130 lines
4.0 KiB
Go
130 lines
4.0 KiB
Go
package registerreward
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// registerRewardGrantPlan 表示当前注册奖励日志最终命中的发奖决策。
|
|
type registerRewardGrantPlan struct {
|
|
ClaimDate string // 领取日期,按每日限额口径记录
|
|
GrantMode string // 发奖决策模式
|
|
SkipDispatch bool // 是否跳过真实发奖动作
|
|
}
|
|
|
|
// resolveGrantPlan 负责确定本次注册奖励是否还能占用当天名额。
|
|
func (s *RegisterRewardService) resolveGrantPlan(
|
|
ctx context.Context,
|
|
logRecord *model.ResidentRegisterRewardLog,
|
|
event registerRewardEvent,
|
|
resolved registerRewardResolvedConfig,
|
|
) (registerRewardGrantPlan, error) {
|
|
// 已经有过发奖决策的日志,失败重试时必须沿用原始结果,避免重试把配额重新算乱。
|
|
if strings.TrimSpace(logRecord.ClaimDate) != "" && strings.TrimSpace(logRecord.GrantMode) != "" {
|
|
return registerRewardGrantPlan{
|
|
ClaimDate: logRecord.ClaimDate,
|
|
GrantMode: logRecord.GrantMode,
|
|
SkipDispatch: strings.ToUpper(strings.TrimSpace(logRecord.GrantMode)) == registerRewardGrantModeQuotaExhausted,
|
|
}, nil
|
|
}
|
|
|
|
claimDate := currentRegisterRewardClaimDate()
|
|
if resolved.DailyLimit <= 0 {
|
|
if err := updateRegisterRewardGrantDecision(
|
|
s.repo.DB.WithContext(ctx),
|
|
logRecord.ID,
|
|
claimDate,
|
|
registerRewardGrantModeIssueReward,
|
|
); err != nil {
|
|
return registerRewardGrantPlan{}, err
|
|
}
|
|
return registerRewardGrantPlan{
|
|
ClaimDate: claimDate,
|
|
GrantMode: registerRewardGrantModeIssueReward,
|
|
}, nil
|
|
}
|
|
|
|
plan := registerRewardGrantPlan{
|
|
ClaimDate: claimDate,
|
|
GrantMode: registerRewardGrantModeIssueReward,
|
|
}
|
|
|
|
err := s.repo.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
// 通过锁定当前系统的配置行,串行化每日限额检查,避免并发用户同时穿透当天名额。
|
|
var configRow model.ResidentRegisterRewardConfig
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where("sys_origin = ?", strings.ToUpper(strings.TrimSpace(event.SysOrigin))).
|
|
First(&configRow).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
dailyLimit := configRow.DailyLimit
|
|
if dailyLimit <= 0 {
|
|
plan.GrantMode = registerRewardGrantModeIssueReward
|
|
return updateRegisterRewardGrantDecision(tx, logRecord.ID, claimDate, plan.GrantMode)
|
|
}
|
|
|
|
var occupiedCount int64
|
|
if err := tx.Model(&model.ResidentRegisterRewardLog{}).
|
|
Where("sys_origin = ? AND claim_date = ? AND grant_mode = ?", event.SysOrigin, claimDate, registerRewardGrantModeIssueReward).
|
|
Count(&occupiedCount).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if occupiedCount >= dailyLimit {
|
|
plan.GrantMode = registerRewardGrantModeQuotaExhausted
|
|
plan.SkipDispatch = true
|
|
}
|
|
|
|
return updateRegisterRewardGrantDecision(tx, logRecord.ID, claimDate, plan.GrantMode)
|
|
})
|
|
if err != nil {
|
|
return registerRewardGrantPlan{}, err
|
|
}
|
|
|
|
return plan, nil
|
|
}
|
|
|
|
// updateRegisterRewardGrantDecision 回写日志的领取日期和发奖决策结果。
|
|
func updateRegisterRewardGrantDecision(
|
|
db *gorm.DB,
|
|
logID int64,
|
|
claimDate string,
|
|
grantMode string,
|
|
) error {
|
|
return db.Model(&model.ResidentRegisterRewardLog{}).
|
|
Where("id = ?", logID).
|
|
Updates(map[string]any{
|
|
"claim_date": strings.TrimSpace(claimDate),
|
|
"grant_mode": strings.ToUpper(strings.TrimSpace(grantMode)),
|
|
"update_time": time.Now(),
|
|
}).Error
|
|
}
|
|
|
|
// currentRegisterRewardClaimDate 返回“每日限额”口径下的当前日期。
|
|
func currentRegisterRewardClaimDate() string {
|
|
location, err := time.LoadLocation(registerRewardDailyQuotaTimezone)
|
|
if err != nil {
|
|
return time.Now().Format("2006-01-02")
|
|
}
|
|
return time.Now().In(location).Format("2006-01-02")
|
|
}
|
|
|
|
// normalizeRegisterRewardClaimDate 规范后台查询传入的日期;未传时默认看今天。
|
|
func normalizeRegisterRewardClaimDate(raw string) string {
|
|
trimmed := strings.TrimSpace(raw)
|
|
if trimmed == "" {
|
|
return currentRegisterRewardClaimDate()
|
|
}
|
|
if _, err := time.Parse("2006-01-02", trimmed); err == nil {
|
|
return trimmed
|
|
}
|
|
return currentRegisterRewardClaimDate()
|
|
}
|