117 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package wallet
import (
"context"
"strings"
"time"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
"hyapp/services/wallet-service/internal/domain/ledger"
)
const vipDailyCoinRebateTaskDayLayout = "2006-01-02"
// ProcessVipDailyCoinRebateBatch 只接受 UTC 日期,不接受时区偏移。空 task_day 使用 wallet owner
// 当前 UTC 日期,使调度方无需也不能自行决定业务切日。
func (s *Service) ProcessVipDailyCoinRebateBatch(ctx context.Context, command ledger.ProcessVipDailyCoinRebateBatchCommand) (ledger.ProcessVipDailyCoinRebateBatchResult, error) {
if s.repository == nil {
return ledger.ProcessVipDailyCoinRebateBatchResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
command.AppCode = appcode.Normalize(command.AppCode)
now := s.now().UTC()
command.NowMS = now.UnixMilli()
command.TaskDay = strings.TrimSpace(command.TaskDay)
if command.TaskDay == "" {
command.TaskDay = now.Format(vipDailyCoinRebateTaskDayLayout)
}
dayStart, err := time.ParseInLocation(vipDailyCoinRebateTaskDayLayout, command.TaskDay, time.UTC)
if err != nil || dayStart.Format(vipDailyCoinRebateTaskDayLayout) != command.TaskDay {
return ledger.ProcessVipDailyCoinRebateBatchResult{}, xerr.New(xerr.InvalidArgument, "task_day must be an UTC date in YYYY-MM-DD")
}
if dayStart.After(now) {
return ledger.ProcessVipDailyCoinRebateBatchResult{}, xerr.New(xerr.InvalidArgument, "future vip rebate task_day is not allowed")
}
if command.BatchSize <= 0 {
command.BatchSize = 100
}
if command.BatchSize > 500 {
command.BatchSize = 500
}
ctx = appcode.WithContext(ctx, command.AppCode)
return s.repository.ProcessVipDailyCoinRebateBatch(ctx, command)
}
// GetMyCurrentVipDailyCoinRebate 查询当前 UTC 日返现;没有记录是正常状态,以 found=false 表达。
func (s *Service) GetMyCurrentVipDailyCoinRebate(ctx context.Context, userID int64) (ledger.VipDailyCoinRebate, bool, error) {
if userID <= 0 {
return ledger.VipDailyCoinRebate{}, false, xerr.New(xerr.InvalidArgument, "user_id is required")
}
if s.repository == nil {
return ledger.VipDailyCoinRebate{}, false, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
nowMS := s.now().UTC().UnixMilli()
ctx = appcode.WithContext(ctx, appcode.FromContext(ctx))
return s.repository.GetMyCurrentVipDailyCoinRebate(ctx, userID, nowMS)
}
// ListMyVipDailyCoinRebateStatuses 返回用户历史记录expired 由 wallet owner 使用当前 UTC 时钟投影。
func (s *Service) ListMyVipDailyCoinRebateStatuses(ctx context.Context, query ledger.ListVipDailyCoinRebateStatusesQuery) ([]ledger.VipDailyCoinRebate, int64, error) {
if query.UserID <= 0 {
return nil, 0, xerr.New(xerr.InvalidArgument, "user_id is required")
}
if s.repository == nil {
return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
query.AppCode = appcode.Normalize(query.AppCode)
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 20
}
if query.PageSize > 100 {
query.PageSize = 100
}
if len(query.RebateIDs) > 100 {
return nil, 0, xerr.New(xerr.InvalidArgument, "rebate_ids cannot contain more than 100 items")
}
seenRebateIDs := make(map[string]struct{}, len(query.RebateIDs))
normalizedRebateIDs := make([]string, 0, len(query.RebateIDs))
for _, rebateID := range query.RebateIDs {
rebateID = strings.TrimSpace(rebateID)
if rebateID == "" || len(rebateID) > 96 {
return nil, 0, xerr.New(xerr.InvalidArgument, "rebate_ids contains an invalid id")
}
if _, exists := seenRebateIDs[rebateID]; exists {
continue
}
seenRebateIDs[rebateID] = struct{}{}
normalizedRebateIDs = append(normalizedRebateIDs, rebateID)
}
query.RebateIDs = normalizedRebateIDs
query.NowMS = s.now().UTC().UnixMilli()
ctx = appcode.WithContext(ctx, query.AppCode)
return s.repository.ListMyVipDailyCoinRebateStatuses(ctx, query)
}
// ClaimVipDailyCoinRebate 只校验命令形状;可领取窗口、金额、付费 VIP 快照和余额变更均在
// repository 的同一行锁事务内裁决,客户端无法提交金额或延长过期时间。
func (s *Service) ClaimVipDailyCoinRebate(ctx context.Context, command ledger.ClaimVipDailyCoinRebateCommand) (ledger.ClaimVipDailyCoinRebateReceipt, error) {
command.AppCode = appcode.Normalize(command.AppCode)
command.CommandID = strings.TrimSpace(command.CommandID)
command.RebateID = strings.TrimSpace(command.RebateID)
if command.CommandID == "" || command.UserID <= 0 || command.RebateID == "" {
return ledger.ClaimVipDailyCoinRebateReceipt{}, xerr.New(xerr.InvalidArgument, "vip daily coin rebate claim command is incomplete")
}
if len(command.CommandID) > 128 || len(command.RebateID) > 96 {
return ledger.ClaimVipDailyCoinRebateReceipt{}, xerr.New(xerr.InvalidArgument, "vip daily coin rebate claim command is invalid")
}
if s.repository == nil {
return ledger.ClaimVipDailyCoinRebateReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
}
command.NowMS = s.now().UTC().UnixMilli()
ctx = appcode.WithContext(ctx, command.AppCode)
return s.repository.ClaimVipDailyCoinRebate(ctx, command)
}