189 lines
5.8 KiB
Go

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")
}