feat(server): 到账通知四接缝(邀请注册/首充/TG/购买开通)——与发放同事务零孤儿
This commit is contained in:
@@ -16,14 +16,21 @@ type Granter interface {
|
||||
GrantRewardTx(ctx context.Context, tx *sql.Tx, userID int64, days int, source, auditAction, ref string) (int64, time.Time, error)
|
||||
}
|
||||
|
||||
// Noticer 抽象 notices.Store 的事务内插入入口(测试可替身;生产传 notices.NewStore(db))。
|
||||
// reward → notices 单向依赖,不引入 import cycle,故不需要适配器,直接实现即可。
|
||||
type Noticer interface {
|
||||
InsertNoticeTx(ctx context.Context, tx *sql.Tx, userID int64, typ, titleZH, titleEN, bodyZH, bodyEN, link string, now time.Time) error
|
||||
}
|
||||
|
||||
type Config struct{ RegDays, PaidDays, TgDays, RegMonthlyCap int }
|
||||
|
||||
type Service struct {
|
||||
db *sql.DB
|
||||
st *Store
|
||||
g Granter
|
||||
cfg Config
|
||||
now func() time.Time
|
||||
db *sql.DB
|
||||
st *Store
|
||||
g Granter
|
||||
cfg Config
|
||||
now func() time.Time
|
||||
noticer Noticer
|
||||
|
||||
rdb *redis.Client
|
||||
tg tgConfig
|
||||
@@ -32,6 +39,22 @@ type Service struct {
|
||||
memTok map[string]int64
|
||||
}
|
||||
|
||||
// SetNoticer 注入到账通知钩子(notices.Store 满足此接口);为 nil 时四接缝跳过(装配前兼容)。
|
||||
func (s *Service) SetNoticer(n Noticer) { s.noticer = n }
|
||||
|
||||
// rewardNoticeTitles 按事件渲染双语标题(reward 类通知无正文,一行标题即可)。
|
||||
func rewardNoticeTitles(kind string, days int) (zh, en string) {
|
||||
switch kind {
|
||||
case "invite_reg":
|
||||
return fmt.Sprintf("邀请奖励 +%d 天已到账", days), fmt.Sprintf("Invite reward +%d days credited", days)
|
||||
case "invite_paid":
|
||||
return fmt.Sprintf("好友首购奖励 +%d 天已到账", days), fmt.Sprintf("Friend's first purchase: +%d days credited", days)
|
||||
case "task_tg":
|
||||
return fmt.Sprintf("任务奖励 +%d 天已到账", days), fmt.Sprintf("Task reward +%d days credited", days)
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func NewService(db *sql.DB, st *Store, g Granter, cfg Config, now func() time.Time) *Service {
|
||||
if now == nil {
|
||||
now = func() time.Time { return time.Now().UTC() }
|
||||
@@ -119,6 +142,17 @@ func (s *Service) OnRegister(ctx context.Context, inviteeID int64, inviteCode, d
|
||||
slog.Warn("reward: grant invitee reg failed", "err", err)
|
||||
return
|
||||
}
|
||||
if s.noticer != nil {
|
||||
zh, en := rewardNoticeTitles("invite_reg", s.cfg.RegDays)
|
||||
if err := s.noticer.InsertNoticeTx(ctx, tx, inviterID, "reward", zh, en, "", "", "", now); err != nil {
|
||||
slog.Warn("reward: insert inviter notice failed", "err", err)
|
||||
return
|
||||
}
|
||||
if err := s.noticer.InsertNoticeTx(ctx, tx, inviteeID, "reward", zh, en, "", "", "", now); err != nil {
|
||||
slog.Warn("reward: insert invitee notice failed", "err", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
slog.Warn("reward: commit failed", "err", err)
|
||||
@@ -142,6 +176,15 @@ func (s *Service) OnFirstPaidTx(ctx context.Context, tx *sql.Tx, inviteeID int64
|
||||
if _, _, err := s.g.GrantRewardTx(ctx, tx, inviteeID, s.cfg.PaidDays, "invite", "invite_paid_invitee", fmt.Sprintf("invite:paid:inviter=%d", inviterID)); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.noticer != nil {
|
||||
zh, en := rewardNoticeTitles("invite_paid", s.cfg.PaidDays)
|
||||
if err := s.noticer.InsertNoticeTx(ctx, tx, inviterID, "reward", zh, en, "", "", "", now); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.noticer.InsertNoticeTx(ctx, tx, inviteeID, "reward", zh, en, "", "", "", now); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return s.st.MarkPaidRewardedTx(ctx, tx, inviteeID, now)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user