package router import ( "net/http" "strconv" "strings" "chatapp3-golang/internal/config" "chatapp3-golang/internal/service/gameking" "github.com/gin-gonic/gin" ) // registerGameKingRoutes 注册 Yumi H5、运营后台和外部 cron 内部触发接口。 func registerGameKingRoutes(engine *gin.Engine, cfg config.Config, javaClient authGateway, service *gameking.Service) { if service == nil { return } h5 := engine.Group("/app/h5/game-king/yumi") h5.Use(authMiddleware(javaClient)) h5.GET("/detail", func(c *gin.Context) { resp, err := service.Detail(c.Request.Context(), mustAuthUser(c), parseGameKingID(c.Query("activityId"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) h5.GET("/me", func(c *gin.Context) { resp, err := service.Me(c.Request.Context(), mustAuthUser(c), parseGameKingID(c.Query("activityId"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) h5.POST("/draw", func(c *gin.Context) { var req gameking.DrawRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, gameking.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := service.Draw(c.Request.Context(), mustAuthUser(c), req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) h5.GET("/ranking", func(c *gin.Context) { limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "30"))) resp, err := service.Ranking(c.Request.Context(), mustAuthUser(c), parseGameKingID(c.Query("activityId")), c.Query("rankingType"), c.Query("period"), limit) if err != nil { writeError(c, err) return } writeOK(c, resp) }) h5.GET("/draw-records", func(c *gin.Context) { limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20"))) resp, err := service.DrawRecords(c.Request.Context(), mustAuthUser(c), parseGameKingID(c.Query("activityId")), limit) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin := engine.Group("/resident-activity/game-king") admin.Use(consoleAuthMiddleware(javaClient), consoleMenuMiddleware(javaClient, "ResidentGameKingActivity")) admin.GET("/list", func(c *gin.Context) { resp, err := service.ListActivities(c.Request.Context()) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.GET("/detail/:id", func(c *gin.Context) { resp, err := service.ManageDetail(c.Request.Context(), parseGameKingID(c.Param("id"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.POST("/save", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:edit"), func(c *gin.Context) { var req gameking.SaveActivityRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, gameking.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := service.SaveActivity(c.Request.Context(), req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.PUT("/enable/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:enable"), func(c *gin.Context) { raw, exists := c.GetQuery("enabled") if !exists { writeError(c, gameking.NewAppError(http.StatusBadRequest, "enabled_required", "enabled query is required")) return } enabled, err := parseGameKingBool(raw, "enabled") if err != nil { writeError(c, err) return } if err := service.EnableActivity(c.Request.Context(), parseGameKingID(c.Param("id")), enabled); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.GET("/prizes/:id", func(c *gin.Context) { resp, err := service.ListPrizes(c.Request.Context(), parseGameKingID(c.Param("id"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.PUT("/prizes/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:edit"), func(c *gin.Context) { var req gameking.SavePrizesRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, gameking.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } if err := service.SavePrizes(c.Request.Context(), parseGameKingID(c.Param("id")), req); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.GET("/rank-rewards/:id", func(c *gin.Context) { resp, err := service.ListRankRewards(c.Request.Context(), parseGameKingID(c.Param("id"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.PUT("/rank-rewards/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:edit"), func(c *gin.Context) { var req gameking.SaveRankRewardsRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, gameking.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } if err := service.SaveRankRewards(c.Request.Context(), parseGameKingID(c.Param("id")), req); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.GET("/draw-records/:id", func(c *gin.Context) { limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "100"))) resp, err := service.ManageDrawRecords(c.Request.Context(), parseGameKingID(c.Param("id")), c.Query("deliveryStatus"), limit) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.POST("/draw/retry/:recordId", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:reconcile"), func(c *gin.Context) { if err := service.RetryDraw(c.Request.Context(), parseGameKingID(c.Param("recordId"))); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.GET("/settlement-records/:id", func(c *gin.Context) { resp, err := service.SettlementRecords(c.Request.Context(), parseGameKingID(c.Param("id"))) if err != nil { writeError(c, err) return } writeOK(c, resp) }) admin.POST("/settlement/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:settle"), func(c *gin.Context) { if err := service.Settle(c.Request.Context(), parseGameKingID(c.Param("id"))); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.POST("/settlement/retry/:recordId", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:reconcile"), func(c *gin.Context) { if err := service.RetrySettlement(c.Request.Context(), parseGameKingID(c.Param("recordId"))); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.POST("/delivery-item/resolve/:itemId", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:reconcile"), func(c *gin.Context) { raw, exists := c.GetQuery("delivered") if !exists { writeError(c, gameking.NewAppError(http.StatusBadRequest, "delivered_required", "delivered query is required")) return } delivered, err := parseGameKingBool(raw, "delivered") if err != nil { writeError(c, err) return } if err := service.ResolveUnknownDeliveryItem(c.Request.Context(), parseGameKingID(c.Param("itemId")), delivered); err != nil { writeError(c, err) return } writeOK(c, gin.H{"success": true}) }) admin.POST("/backfill/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-game-king:backfill"), func(c *gin.Context) { lastID := parseGameKingID(c.DefaultQuery("lastId", "0")) limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "200"))) resp, err := service.Backfill(c.Request.Context(), parseGameKingID(c.Param("id")), lastID, limit) if err != nil { writeError(c, err) return } writeOK(c, resp) }) internal := engine.Group("/internal/game-king") // 结算入口会冻结榜单并产生真实奖励副作用,必须与 Yumi 礼物挑战保持相同的 // fail-closed 边界:服务端未配置密钥时直接拒绝,不能沿用历史内部接口的空密钥兼容行为。 internal.Use(requiredInternalSecretMiddleware(cfg.HTTP.InternalCallbackSecret)) internal.POST("/settle-due", func(c *gin.Context) { resp, err := service.SettleDue(c.Request.Context()) if err != nil { writeError(c, err) return } writeOK(c, resp) }) } func parseGameKingID(value string) int64 { parsed, _ := strconv.ParseInt(strings.TrimSpace(value), 10, 64) return parsed } func parseGameKingBool(value, field string) (bool, error) { switch strings.ToLower(strings.TrimSpace(value)) { case "true", "1": return true, nil case "false", "0": return false, nil default: // resolve delivered=false 会立即重发,因此不能把拼写错误或空值默认为 false。 return false, gameking.NewAppError(http.StatusBadRequest, field+"_invalid", field+" must be true, false, 1 or 0") } }