189 lines
5.8 KiB
Go
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")
|
|
}
|