package rechargereward import ( "context" "fmt" "net/http" "strings" "time" ) var personalMonthlyRechargeTypes = []string{ "UNDEFINED", "HUAWEI", "GOOGLE", "APPLE", "PAYER_MAX", "SHIPPING_AGENT", "STRIPE", "SALARY_EXCHANGE", "PAY_PAL", "CLIPSPAY", "SELLER_AGENT", } type rechargeRewardClaimRecordRow struct { UserID int64 `gorm:"column:user_id"` RechargeAmount string `gorm:"column:recharge_amount"` LastRechargeTime string `gorm:"column:last_recharge_time"` Account string `gorm:"column:account"` UserAvatar string `gorm:"column:user_avatar"` UserNickname string `gorm:"column:user_nickname"` CountryCode string `gorm:"column:country_code"` CountryName string `gorm:"column:country_name"` } // PageClaimRecords 分页返回当前配置下已达到充值奖励档位的用户。 func (s *Service) PageClaimRecords(ctx context.Context, sysOrigin string, month string, userID int64, cursor int, limit int) (*RechargeRewardClaimRecordPageResponse, error) { sysOrigin = s.normalizeSysOrigin(sysOrigin) cursor, limit = NormalizePage(cursor, limit) bundle, err := s.loadConfigBundle(ctx, sysOrigin) if err != nil { return nil, err } location := s.location if bundle.Config != nil { location = resolveLocation(bundle.Config.Timezone, s.location) } rechargeDate, displayMonth, err := normalizeRechargeRewardRecordMonth(month, time.Now().In(location)) if err != nil { return nil, err } levels := enabledRechargeRewardLevels(bundle.Levels) if len(levels) == 0 { return emptyRechargeRewardClaimRecordPage(displayMonth, rechargeDate, location.String(), cursor, limit), nil } minThresholdCents := levels[0].RechargeAmountCents minThresholdAmount := float64(minThresholdCents) / 100 var total int64 countSQL := ` SELECT COUNT(1) FROM ( SELECT recharge.user_id FROM user_monthly_recharge_v2 AS recharge INNER JOIN user_base_info AS user ON user.id = recharge.user_id WHERE recharge.recharge_date = ? AND recharge.type IN ? AND user.origin_sys = ? AND (? = 0 OR recharge.user_id = ?) GROUP BY recharge.user_id HAVING SUM(recharge.amount) >= ? ) AS qualified` if err := s.db.WithContext(ctx).Raw( countSQL, rechargeDate, personalMonthlyRechargeTypes, sysOrigin, userID, userID, minThresholdAmount, ).Scan(&total).Error; err != nil { return nil, err } records := make([]RechargeRewardClaimRecordView, 0) if total > 0 { var rows []rechargeRewardClaimRecordRow listSQL := ` SELECT recharge.user_id, SUM(recharge.amount) AS recharge_amount, MAX(recharge.update_time) AS last_recharge_time, user.account, user.user_avatar, user.user_nickname, user.country_code, user.country_name FROM user_monthly_recharge_v2 AS recharge INNER JOIN user_base_info AS user ON user.id = recharge.user_id WHERE recharge.recharge_date = ? AND recharge.type IN ? AND user.origin_sys = ? AND (? = 0 OR recharge.user_id = ?) GROUP BY recharge.user_id, user.account, user.user_avatar, user.user_nickname, user.country_code, user.country_name HAVING SUM(recharge.amount) >= ? ORDER BY SUM(recharge.amount) DESC, recharge.user_id ASC LIMIT ? OFFSET ?` if err := s.db.WithContext(ctx).Raw( listSQL, rechargeDate, personalMonthlyRechargeTypes, sysOrigin, userID, userID, minThresholdAmount, limit, (cursor-1)*limit, ).Scan(&rows).Error; err != nil { return nil, err } rewardItems := s.loadRewardItemsMap(ctx, levels) records = make([]RechargeRewardClaimRecordView, 0, len(rows)) for _, row := range rows { records = append(records, rechargeRewardClaimRecordView(row, displayMonth, rechargeDate, levels, rewardItems)) } } return &RechargeRewardClaimRecordPageResponse{ Month: displayMonth, RechargeDate: rechargeDate, Timezone: location.String(), Records: records, Total: total, Current: cursor, Size: limit, }, nil } func emptyRechargeRewardClaimRecordPage(month string, rechargeDate int, timezone string, cursor int, limit int) *RechargeRewardClaimRecordPageResponse { return &RechargeRewardClaimRecordPageResponse{ Month: month, RechargeDate: rechargeDate, Timezone: timezone, Records: []RechargeRewardClaimRecordView{}, Total: 0, Current: cursor, Size: limit, } } func normalizeRechargeRewardRecordMonth(raw string, now time.Time) (int, string, error) { value := strings.TrimSpace(raw) if value == "" { value = now.Format("2006-01") } if len(value) == 6 && !strings.Contains(value, "-") { value = value[:4] + "-" + value[4:] } parsed, err := time.Parse("2006-01", value) if err != nil { return 0, "", NewAppError(http.StatusBadRequest, "invalid_month", "month must be YYYY-MM") } rechargeDate := parsed.Year()*100 + int(parsed.Month()) return rechargeDate, parsed.Format("2006-01"), nil } func enabledRechargeRewardLevels(levels []rechargeRewardLevelSnapshot) []rechargeRewardLevelSnapshot { result := make([]rechargeRewardLevelSnapshot, 0, len(levels)) for _, level := range levels { if !level.Enabled || level.RechargeAmountCents <= 0 { continue } result = append(result, level) } sortLevels(result) return result } func rechargeRewardClaimRecordView( row rechargeRewardClaimRecordRow, month string, rechargeDate int, levels []rechargeRewardLevelSnapshot, rewardItems map[int64][]RechargeRewardRewardItem, ) RechargeRewardClaimRecordView { rechargeCents := parseIntegrationAmountCents(integrationAmount(row.RechargeAmount)) reached := reachedRechargeRewardLevels(levels, rewardItems, rechargeCents) return RechargeRewardClaimRecordView{ RecordKey: fmt.Sprintf("%d-%d", row.UserID, rechargeDate), UserID: row.UserID, Account: strings.TrimSpace(row.Account), UserAvatar: strings.TrimSpace(row.UserAvatar), UserNickname: strings.TrimSpace(row.UserNickname), CountryCode: strings.TrimSpace(row.CountryCode), CountryName: strings.TrimSpace(row.CountryName), RechargeMonth: month, RechargeDate: rechargeDate, RechargeAmount: formatAmountCents(rechargeCents), RechargeAmountCents: rechargeCents, ReachedLevels: reached, ClaimedRewards: rechargeRewardClaimedRewards(reached), LastRechargeTime: formatRechargeRewardRecordTime(row.LastRechargeTime), } } func reachedRechargeRewardLevels( levels []rechargeRewardLevelSnapshot, rewardItems map[int64][]RechargeRewardRewardItem, rechargeCents int64, ) []RechargeRewardLevelPayload { result := make([]RechargeRewardLevelPayload, 0) for _, level := range levels { if rechargeCents < level.RechargeAmountCents { continue } var items []RechargeRewardRewardItem if level.RewardGroupID != nil { items = rewardItems[*level.RewardGroupID] } result = append(result, RechargeRewardLevelPayload{ ID: level.ID, Level: level.Level, RechargeAmount: formatAmountCents(level.RechargeAmountCents), RechargeAmountCents: level.RechargeAmountCents, RewardGold: level.RewardGold, RewardGroupID: level.RewardGroupID, RewardGroupName: level.RewardGroupName, RewardItems: items, Enabled: level.Enabled, Reached: true, }) } return result } func rechargeRewardClaimedRewards(levels []RechargeRewardLevelPayload) []RechargeRewardClaimedRewardPayload { result := make([]RechargeRewardClaimedRewardPayload, 0, len(levels)) for _, level := range levels { result = append(result, RechargeRewardClaimedRewardPayload{ Level: level.Level, RechargeAmount: level.RechargeAmount, RechargeAmountCents: level.RechargeAmountCents, RewardGold: level.RewardGold, RewardGroupID: level.RewardGroupID, RewardGroupName: level.RewardGroupName, RewardItems: level.RewardItems, }) } return result } func formatRechargeRewardRecordTime(raw string) string { value := strings.TrimSpace(raw) if value == "" { return "" } for _, layout := range []string{ "2006-01-02 15:04:05.999999999-07:00", "2006-01-02 15:04:05.999999999", "2006-01-02 15:04:05", time.RFC3339Nano, time.RFC3339, } { if parsed, err := time.Parse(layout, value); err == nil { return formatDateTime(parsed) } } if len(value) > len("2006-01-02 15:04:05") { return value[:len("2006-01-02 15:04:05")] } return value }