package weekstar import ( "fmt" "sort" "strconv" "strings" "time" "chatapp3-golang/internal/model" ) // buildGiftPayloads 把礼物配置表转换成对外载体。 func buildGiftPayloads(rows []model.WeekStarActivityGiftConfig) []WeekStarGiftConfigPayload { sort.Slice(rows, func(i, j int) bool { return rows[i].Sort < rows[j].Sort }) result := make([]WeekStarGiftConfigPayload, 0, len(rows)) for _, row := range rows { result = append(result, WeekStarGiftConfigPayload{ GiftID: row.GiftID, GiftName: row.GiftName, GiftPhoto: row.GiftPhoto, GiftCandy: row.GiftCandy, Sort: row.Sort, }) } return result } // calculateGiftScore 按活动口径计算一笔送礼的金币积分。 func calculateGiftScore(event weekStarGiftEvent) int64 { if actual := int64(event.GiftValue.ActualAmount); actual > 0 { return actual } acceptUserSize := int64(countDistinctAcceptUsers(event.Accepts)) if acceptUserSize <= 0 { acceptUserSize = 1 } quantity := int64(event.Quantity) if quantity <= 0 { quantity = 1 } giftCandy := int64(event.GiftConfig.GiftCandy) if giftCandy <= 0 { return 0 } return giftCandy * quantity * acceptUserSize } // countDistinctAcceptUsers 统计不同收礼用户数。 func countDistinctAcceptUsers(users []weekStarGiftEventUser) int { if len(users) == 0 { return 0 } set := make(map[int64]struct{}, len(users)) for _, user := range users { if int64(user.AcceptUserID) <= 0 { continue } set[int64(user.AcceptUserID)] = struct{}{} } return len(set) } // containsGiftID 判断礼物是否命中活动指定礼物列表。 func containsGiftID(rows []model.WeekStarActivityGiftConfig, giftID int64) bool { for _, row := range rows { if row.GiftID == giftID { return true } } return false } // parseRedisMemberInt64 把 Redis member 统一解析为用户 ID。 func parseRedisMemberInt64(member any) (int64, bool) { switch value := member.(type) { case string: parsed, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64) return parsed, err == nil case []byte: parsed, err := strconv.ParseInt(strings.TrimSpace(string(value)), 10, 64) return parsed, err == nil case int64: return value, true case int: return int64(value), true default: return 0, false } } // parseDateTimeInLocation 按指定时区解析后台传入的时间字符串。 func parseDateTimeInLocation(value string, location *time.Location) (time.Time, error) { value = strings.TrimSpace(value) if value == "" { return time.Time{}, fmt.Errorf("time is required") } layouts := []string{ "2006-01-02 15:04:05", "2006-01-02T15:04:05", time.RFC3339, "2006-01-02", } for _, layout := range layouts { if layout == time.RFC3339 { if parsed, err := time.Parse(layout, value); err == nil { return parsed.In(location), nil } continue } if parsed, err := time.ParseInLocation(layout, value, location); err == nil { return parsed, nil } } return time.Time{}, fmt.Errorf("unsupported time format: %s", value) } // resolveMillisTime 把毫秒时间戳解析为时间,失败时回退默认值。 func resolveMillisTime(raw int64, fallback time.Time) time.Time { if raw <= 0 { return fallback } if raw < 1_000_000_000_000 { return time.Unix(raw, 0) } return time.UnixMilli(raw) } // normalizePage 规范分页参数,避免非法页码或 pageSize。 func normalizePage(cursor, limit int) (int, int) { if cursor <= 0 { cursor = 1 } if limit <= 0 { limit = 20 } if limit > 100 { limit = 100 } return cursor, limit } // formatDateTime 把时间格式化成统一展示字符串。 func formatDateTime(t time.Time) string { if t.IsZero() { return "" } return t.Format("2006-01-02 15:04:05") } // formatPeriodText 把周期起止时间格式化成页面展示文案。 func formatPeriodText(start, end time.Time) string { if start.IsZero() || end.IsZero() || !end.After(start) { return "" } return start.Format("2006/01/02") + "-" + end.Format("2006/01/02") } // truncateString 截断字符串,避免超出字段长度限制。 func truncateString(value string, limit int) string { if limit <= 0 || len(value) <= limit { return value } return value[:limit] } // maxTime 返回两个时间中的较大值。 func maxTime(left, right time.Time) time.Time { if left.After(right) { return left } return right } // minTime 返回两个时间中的较小值。 func minTime(left, right time.Time) time.Time { if left.Before(right) { return left } return right } // maxInt64 返回两个整数中的较大值。 func maxInt64(left, right int64) int64 { if left > right { return left } return right } // isDuplicateKeyErr 判断是否是数据库唯一键冲突错误。 func isDuplicateKeyErr(err error) bool { if err == nil { return false } message := strings.ToLower(err.Error()) return strings.Contains(message, "duplicate entry") || strings.Contains(message, "duplicate key") } // toWeekStarRewardRecordView 把发奖记录模型转换成后台展示对象。 func toWeekStarRewardRecordView(row model.WeekStarRewardRecord) WeekStarRewardRecordView { return WeekStarRewardRecordView{ ID: row.ID, ConfigID: row.ConfigID, CycleKey: row.CycleKey, UserID: row.UserID, UserAvatar: row.UserAvatar, UserNickname: row.UserNickname, Account: row.Account, CountryCode: row.CountryCode, CountryName: row.CountryName, Rank: row.Rank, RewardGroupID: row.RewardGroupID, RewardGroupName: row.RewardGroupName, ScoreGold: row.ScoreGold, Status: row.Status, RetryCount: row.RetryCount, LastError: row.LastError, SentAt: formatDateTime(pointerTimeValue(row.SentAt)), UpdateTime: formatDateTime(row.UpdateTime), } } // pointerTimeValue 把可空时间指针转换成非空时间值。 func pointerTimeValue(value *time.Time) time.Time { if value == nil { return time.Time{} } return *value }