07409eb602
Closes HIF-4 Stage 2 交付:人工奖领奖工单完整闭环。crypto / physical / manual_other 三类奖品从抽中到 mark-paid 的全流程可用。 - 迁移 02159_lottery_claim:UNIQUE(draw_id) + 3 支持索引,状态机 pending_claim→reviewing→paying→paid,rejected 可复活,超时 expired - 3 个 PrizeHandler:Dispatch→ErrDispatchNotSupported 兜底、ClaimSchema 各自形态、ValidateClaim 表驱动 - BuildCryptoClaimSchema:抽中时按奖品 config.networks 注入 enum,前端下拉直接可用 - Draw service dispatchOrEnqueueClaim:人工奖同 tx 插 pending_claim(回滚双清),nonce 重放回读 ExpiresAt + ClaimFormSchema - POST /claim 实装:ownership 校验 → prize 类型校验 → handler.ValidateClaim → crypto network 白名单二次校验 → tx CAS status IN (pending_claim, rejected) AND expires_at > now - Admin CRUD 5 接口:list(IN 批拉 snap + user,无 N+1)、summary(GROUP BY 一次拿计数 + overdue 单查)、approve/reject/mark-paid 全走 CAS + audit - Scheduler @every 1h 扫过期,级联 lottery_draw.dispatch_state → expired - 新增错误码 100005-100011(already_submitted / invalid_claim_data / draw_not_found / not_your_draw / claim_expired / claim_state_invalid) - Rebase 后 Stage 2 测试主动 reuse PR E 的 unmetReasonsNotEmpty + evaluatedAtNotZero matcher,人工奖分支若绕过守卫会立即挂 - Stage 1 全部 4 处 guardrail 后端 rebase 时自检过:UnmetReasons、EvaluatedAt、GrantLedger.Payload、AdminMetaMiddleware 全保留 CI 全绿;28 files, +2442/-126;覆盖率 handler 78.9% / model.lottery 74.3% / draw 68.7% / queue/lottery 76.9%
52 lines
2.8 KiB
Go
52 lines
2.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"github.com/gin-gonic/gin"
|
|
adminLottery "github.com/perfect-panel/server/internal/handler/admin/lottery"
|
|
publicLottery "github.com/perfect-panel/server/internal/handler/public/lottery"
|
|
"github.com/perfect-panel/server/internal/middleware"
|
|
"github.com/perfect-panel/server/internal/svc"
|
|
)
|
|
|
|
// registerLotteryRoutes wires the Stage 1 lottery endpoints. Kept in its own
|
|
// file to avoid ballooning routes.go and to make the lottery surface easy to
|
|
// audit end-to-end. The path prefix "/v1/lottery" is under the user middleware
|
|
// stack (AuthMiddleware + DeviceMiddleware); "/v1/admin/lottery" uses the
|
|
// admin-detecting AuthMiddleware (path contains "admin" segment).
|
|
func registerLotteryRoutes(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|
userGroup := router.Group("/v1/lottery")
|
|
userGroup.Use(middleware.AuthMiddleware(serverCtx), middleware.DeviceMiddleware(serverCtx))
|
|
{
|
|
userGroup.GET("/config", publicLottery.QueryLotteryConfigHandler(serverCtx))
|
|
userGroup.POST("/draw", publicLottery.DrawLotteryHandler(serverCtx))
|
|
userGroup.GET("/records", publicLottery.QueryLotteryRecordsHandler(serverCtx))
|
|
userGroup.POST("/claim", publicLottery.ClaimLotteryPrizeHandler(serverCtx))
|
|
}
|
|
|
|
adminGroup := router.Group("/v1/admin/lottery")
|
|
adminGroup.Use(middleware.AuthMiddleware(serverCtx), middleware.AdminMetaMiddleware())
|
|
{
|
|
adminGroup.POST("/activities", adminLottery.CreateLotteryActivityHandler(serverCtx))
|
|
adminGroup.PUT("/activities", adminLottery.UpdateLotteryActivityHandler(serverCtx))
|
|
adminGroup.GET("/activities", adminLottery.ListLotteryActivitiesHandler(serverCtx))
|
|
adminGroup.GET("/activities/detail", adminLottery.GetLotteryActivityHandler(serverCtx))
|
|
adminGroup.POST("/activities/publish", adminLottery.PublishLotteryActivityHandler(serverCtx))
|
|
adminGroup.POST("/activities/pause", adminLottery.PauseLotteryActivityHandler(serverCtx))
|
|
adminGroup.PUT("/activities/rules", adminLottery.UpdateLotteryRulesHandler(serverCtx))
|
|
|
|
adminGroup.POST("/prizes", adminLottery.CreateLotteryPrizeHandler(serverCtx))
|
|
adminGroup.PUT("/prizes", adminLottery.UpdateLotteryPrizeHandler(serverCtx))
|
|
adminGroup.DELETE("/prizes", adminLottery.DeleteLotteryPrizeHandler(serverCtx))
|
|
adminGroup.GET("/prizes", adminLottery.ListLotteryPrizesHandler(serverCtx))
|
|
|
|
adminGroup.POST("/chances/grant", adminLottery.GrantLotteryChanceHandler(serverCtx))
|
|
|
|
// Stage 2 (HIF-4): 人工奖工单接口
|
|
adminGroup.GET("/claims", adminLottery.ListLotteryClaimsHandler(serverCtx))
|
|
adminGroup.GET("/claims/summary", adminLottery.LotteryClaimsSummaryHandler(serverCtx))
|
|
adminGroup.POST("/claims/approve", adminLottery.ApproveLotteryClaimHandler(serverCtx))
|
|
adminGroup.POST("/claims/reject", adminLottery.RejectLotteryClaimHandler(serverCtx))
|
|
adminGroup.POST("/claims/mark-paid", adminLottery.MarkPaidLotteryClaimHandler(serverCtx))
|
|
}
|
|
}
|