233 lines
5.9 KiB
Go

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
}