package registerreward import ( "context" "errors" "net/http" "strconv" "strings" "time" "chatapp3-golang/internal/model" "gorm.io/gorm" ) // GetStatus 返回当前登录用户的注册奖励状态,主要用于后台联调和兜底排查。 func (s *RegisterRewardService) GetStatus(ctx context.Context, user AuthUser) (*RegisterRewardStatusResponse, error) { if user.UserID <= 0 { return nil, NewAppError(http.StatusUnauthorized, "invalid_user", "authenticated user is required") } sysOrigin := strings.ToUpper(strings.TrimSpace(user.SysOrigin)) if sysOrigin == "" { return nil, NewAppError(http.StatusUnauthorized, "invalid_sys_origin", "authenticated sysOrigin is required") } configRow, err := s.loadRegisterRewardConfig(ctx, sysOrigin) if err != nil { return nil, err } userInfo, err := s.loadUserBaseInfo(ctx, user.UserID) if err != nil { return nil, err } logRecord, err := s.loadRegisterRewardLog(ctx, sysOrigin, user.UserID) if err != nil { return nil, err } registerDateMatchesServerDay := false registerTime := "" if userInfo != nil && !userInfo.CreateTime.IsZero() { registerTime = formatRegisterRewardTime(userInfo.CreateTime) registerDateMatchesServerDay = isRegisterRewardCurrentServerDate(userInfo.CreateTime) } goldAmount, rewardGroupID := resolveRegisterRewardPayload(configRow, logRecord) rewardStatus := deriveRegisterRewardStatus(logRecord, registerDateMatchesServerDay, configRow != nil && configRow.Enabled) hasRewardRecord := logRecord != nil errorMessage := "" if logRecord != nil { errorMessage = strings.TrimSpace(logRecord.ErrorMessage) } if !registerDateMatchesServerDay { errorMessage = "" if logRecord == nil { goldAmount = 0 rewardGroupID = nil } } return &RegisterRewardStatusResponse{ UserID: user.UserID, SysOrigin: sysOrigin, Configured: configRow != nil, CampaignEnabled: configRow != nil && configRow.Enabled, IsNewRegister: registerDateMatchesServerDay, HasRewardRecord: hasRewardRecord, RewardStatus: rewardStatus, HasClaimed: rewardStatus == registerRewardStatusSuccess, GoldAmount: goldAmount, RewardGroupID: rewardGroupID, RegisterTime: registerTime, ErrorMessage: errorMessage, }, nil } // loadRegisterRewardLog 按用户读取注册奖励处理日志,优先按标准 eventId 命中,兼容历史数据则回退最近一条日志。 func (s *RegisterRewardService) loadRegisterRewardLog(ctx context.Context, sysOrigin string, userID int64) (*model.ResidentRegisterRewardLog, error) { var record model.ResidentRegisterRewardLog eventID := buildRegisterRewardEventID(sysOrigin, userID) err := s.repo.DB.WithContext(ctx). Where("event_id = ?", eventID). First(&record).Error if err == nil { return &record, nil } if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } err = s.repo.DB.WithContext(ctx). Where("sys_origin = ? AND user_id = ?", sysOrigin, userID). Order("create_time desc"). Order("id desc"). First(&record).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } if err != nil { return nil, err } return &record, nil } // loadUserBaseInfo 读取用户基础资料,用创建时间兜底判断“刚注册但异步日志还没落库”的场景。 func (s *RegisterRewardService) loadUserBaseInfo(ctx context.Context, userID int64) (*model.UserBaseInfo, error) { var info model.UserBaseInfo err := s.repo.DB.WithContext(ctx). Where("id = ?", userID). First(&info).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } if err != nil { return nil, err } return &info, nil } // buildRegisterRewardEventID 按 Java 生产者相同规则拼接注册奖励事件 ID。 func buildRegisterRewardEventID(sysOrigin string, userID int64) string { return "REGISTER_REWARD:" + strings.ToUpper(strings.TrimSpace(sysOrigin)) + ":" + strconv.FormatInt(userID, 10) } // deriveRegisterRewardStatus 统一返回联调接口里的注册奖励状态。 func deriveRegisterRewardStatus( logRecord *model.ResidentRegisterRewardLog, registerDateMatchesServerDay bool, campaignEnabled bool, ) string { // 业务要求:只要注册日期与服务器当天日期不一致,就视为该注册奖励已经处理完成,不再继续引导端上查询。 if !registerDateMatchesServerDay { return registerRewardStatusSuccess } if logRecord != nil { switch status := strings.ToUpper(strings.TrimSpace(logRecord.Status)); status { case registerRewardStatusPending, registerRewardStatusSuccess, registerRewardStatusSkipped, registerRewardStatusFailed: return status default: return registerRewardStatusPending } } if campaignEnabled { return registerRewardStatusProcessing } return registerRewardStatusNotTriggered } // isRegisterRewardCurrentServerDate 判断注册时间是否仍落在“服务器今天”的自然日内。 func isRegisterRewardCurrentServerDate(value time.Time) bool { if value.IsZero() { return false } location, err := time.LoadLocation(registerRewardDailyQuotaTimezone) if err != nil { now := time.Now() return value.Format("2006-01-02") == now.Format("2006-01-02") } return value.In(location).Format("2006-01-02") == time.Now().In(location).Format("2006-01-02") } // resolveRegisterRewardPayload 优先返回日志里已命中的奖励内容,没有日志时回退当前后台配置。 func resolveRegisterRewardPayload( configRow *model.ResidentRegisterRewardConfig, logRecord *model.ResidentRegisterRewardLog, ) (int64, *int64) { if logRecord != nil { return logRecord.GoldAmount, logRecord.RewardGroupID } if configRow == nil { return 0, nil } return configRow.GoldAmount, configRow.RewardGroupID } // formatRegisterRewardTime 统一格式化注册时间展示值。 func formatRegisterRewardTime(value time.Time) string { if value.IsZero() { return "" } return value.Format("2006-01-02 15:04:05") }