feat(#4): 删除抽奖活动接口 DELETE /admin/lottery/activities/:id

- 软删活动 + 硬删其奖品(同事务)+ 审计日志
- 运行中的活动禁止删除(需先暂停)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 00:16:34 -07:00
parent e8e3a3a72b
commit 13bafd5847
4 changed files with 71 additions and 0 deletions
+49
View File
@@ -318,6 +318,55 @@ func (l *PauseLotteryActivityLogic) PauseLotteryActivity(req *types.AdminActivit
return t.run(req.Id)
}
// ---- DeleteLotteryActivity -------------------------------------------------
type DeleteLotteryActivityLogic struct {
logger.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDeleteLotteryActivityLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteLotteryActivityLogic {
return &DeleteLotteryActivityLogic{Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}
// DeleteLotteryActivity 软删活动 + 硬删其奖品(同事务)。运行中的活动禁止删除,
// 需先暂停,避免误删正在进行的抽奖。历史抽奖记录/快照保留(独立于奖品行)。
func (l *DeleteLotteryActivityLogic) DeleteLotteryActivity(req *types.AdminActivityIdRequest) error {
actor := currentAdminId(l.ctx)
if actor == 0 {
return xerr.NewErrCode(xerr.ErrorTokenInvalid)
}
return l.svcCtx.DB.WithContext(l.ctx).Transaction(func(tx *gorm.DB) error {
var a modelLottery.Activity
if err := tx.Where("id = ?", req.Id).First(&a).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return xerr.NewErrCode(xerr.LotteryActivityEnded)
}
return errors.Wrap(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error())
}
if a.Status == modelLottery.ActivityStatusRunning {
return xerr.NewErrCodeMsg(xerr.InvalidParams, "运行中的活动请先暂停再删除")
}
// 软删活动(Activity 有 gorm.DeletedAt)。
if err := tx.Delete(&modelLottery.Activity{}, req.Id).Error; err != nil {
return errors.Wrap(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
}
// 硬删奖品(Prize 无软删字段),避免残留孤儿奖品。
if err := tx.Where("activity_id = ?", req.Id).Delete(&modelLottery.Prize{}).Error; err != nil {
return errors.Wrap(xerr.NewErrCode(xerr.DatabaseDeletedError), err.Error())
}
ip, ua := requestMeta(l.ctx)
return audit.WriteAdminAction(l.ctx, tx, audit.Entry{
ActorUserId: actor,
Action: audit.ActionLotteryActivityDelete,
TargetIds: int64ToStr(req.Id),
IP: ip,
UserAgent: ua,
})
})
}
// ---- UpdateLotteryRules (with caps) ----------------------------------------
type UpdateLotteryRulesLogic struct {