package router import ( "net/http" "strconv" "strings" "chatapp3-golang/internal/config" "chatapp3-golang/internal/service/yumigiftchallenge" "github.com/gin-gonic/gin" ) const yumiGiftChallengeMenuAlias = "ResidentYumiGiftChallenge" // registerYumiGiftChallengeRoutes 注册 Yumi H5 用户接口与 Console 配置、结算、核账接口。 func registerYumiGiftChallengeRoutes(engine *gin.Engine, cfg config.Config, javaClient authGateway, service *yumigiftchallenge.Service) { if service == nil { return } internal := engine.Group("/internal/yumi-gift-challenge") // 结算会产生真实奖励副作用;密钥缺失也必须拒绝,不能沿用历史内部接口的空密钥兼容行为。 internal.Use(requiredInternalSecretMiddleware(cfg.HTTP.InternalCallbackSecret)) internal.POST("/settle-due", func(c *gin.Context) { batch, err := parseOptionalInt(c.Query("batch")) if err != nil { writeError(c, err) return } response, err := service.SettleDue(c.Request.Context(), batch) if err != nil { writeError(c, err) return } writeOK(c, response) }) h5 := engine.Group("/app/h5/gift-challenge/yumi") h5.Use(authMiddleware(javaClient)) h5.GET("/detail", func(c *gin.Context) { activityID, err := parseOptionalInt64(c.Query("activityId")) if err != nil { writeError(c, err) return } response, err := service.GetDetail(c.Request.Context(), mustAuthUser(c), activityID) if err != nil { writeError(c, err) return } writeOK(c, response) }) h5.POST("/enter", func(c *gin.Context) { var request yumigiftchallenge.EnterRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } response, err := service.Enter(c.Request.Context(), mustAuthUser(c), request) if err != nil { writeError(c, err) return } writeOK(c, response) }) h5.GET("/ranking", func(c *gin.Context) { activityID, err := parseOptionalInt64(c.Query("activityId")) if err != nil { writeError(c, err) return } limit, err := parseOptionalInt(c.Query("limit")) if err != nil { writeError(c, err) return } period := firstNonEmptyString(c.Query("periodType"), c.Query("period")) response, err := service.GetRanking(c.Request.Context(), mustAuthUser(c), activityID, period, c.Query("statDate"), limit) if err != nil { writeError(c, err) return } writeOK(c, response) }) h5.POST("/tasks/:taskCode/claim", func(c *gin.Context) { var request yumigiftchallenge.ClaimRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } response, err := service.ClaimTask(c.Request.Context(), mustAuthUser(c), c.Param("taskCode"), request) if err != nil { writeError(c, err) return } writeOK(c, response) }) admin := engine.Group("/resident-activity/yumi-gift-challenge") admin.Use(consoleAuthMiddleware(javaClient)) read := admin.Group("") read.Use(consoleMenuMiddleware(javaClient, yumiGiftChallengeMenuAlias)) read.GET("/list", func(c *gin.Context) { page, pageErr := parseOptionalInt(c.Query("page")) size, sizeErr := parseOptionalInt(c.Query("size")) if pageErr != nil || sizeErr != nil { writeError(c, firstParseError(pageErr, sizeErr)) return } response, err := service.ListActivities(c.Request.Context(), c.Query("sysOrigin"), page, size) if err != nil { writeError(c, err) return } writeOK(c, response) }) read.GET("/detail/:id", func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } response, err := service.GetAdminDetail(c.Request.Context(), activityID) if err != nil { writeError(c, err) return } writeOK(c, response) }) read.GET("/tasks/:id", func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } response, err := service.GetAdminDetail(c.Request.Context(), activityID) if err != nil { writeError(c, err) return } writeOK(c, response.Tasks) }) read.GET("/rank-rewards/:id", func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } response, err := service.GetAdminDetail(c.Request.Context(), activityID) if err != nil { writeError(c, err) return } writeOK(c, response.RankRewards) }) read.GET("/ranking/:id", func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } limit, err := parseOptionalInt(c.Query("limit")) if err != nil { writeError(c, err) return } period := firstNonEmptyString(c.Query("periodType"), c.Query("period")) response, err := service.GetAdminRanking(c.Request.Context(), activityID, period, c.Query("statDate"), limit) if err != nil { writeError(c, err) return } writeOK(c, response) }) read.GET("/settlement-records/:id", func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } page, pageErr := parseOptionalInt(c.Query("page")) size, sizeErr := parseOptionalInt(c.Query("size")) if pageErr != nil || sizeErr != nil { writeError(c, firstParseError(pageErr, sizeErr)) return } response, err := service.PageSettlementRecords(c.Request.Context(), activityID, c.Query("periodType"), c.Query("statDate"), c.Query("deliveryStatus"), page, size) if err != nil { writeError(c, err) return } writeOK(c, response) }) admin.POST("/save", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:edit"), func(c *gin.Context) { var request yumigiftchallenge.SaveRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } response, err := service.SaveActivity(c.Request.Context(), request) if err != nil { writeError(c, err) return } writeOK(c, response) }) admin.PUT("/enable/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:enable"), func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } enabled, err := strconv.ParseBool(strings.TrimSpace(c.Query("enabled"))) if err != nil { writeBadRequest(c, err) return } if err := service.SetEnabled(c.Request.Context(), activityID, enabled); err != nil { writeError(c, err) return } writeOK(c, gin.H{"id": strconv.FormatInt(activityID, 10), "enabled": enabled}) }) admin.PUT("/tasks/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:edit"), func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } var request yumigiftchallenge.TaskSaveRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } request.ActivityID = yumigiftchallenge.FlexibleInt64(activityID) response, err := service.SaveTasks(c.Request.Context(), activityID, request) if err != nil { writeError(c, err) return } writeOK(c, response) }) admin.PUT("/rank-rewards/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:edit"), func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } var request yumigiftchallenge.RankRewardSaveRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } request.ActivityID = yumigiftchallenge.FlexibleInt64(activityID) response, err := service.SaveRankRewards(c.Request.Context(), activityID, request) if err != nil { writeError(c, err) return } writeOK(c, response) }) admin.POST("/settlement/:id", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:settle"), func(c *gin.Context) { activityID, ok := requirePathInt64(c, "id") if !ok { return } var request yumigiftchallenge.SettlementRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } if err := service.Settle(c.Request.Context(), activityID, request); err != nil { writeError(c, err) return } writeOK(c, gin.H{"settled": true}) }) admin.POST("/delivery-item/resolve/:itemId", consolePermissionMiddleware(javaClient, "resident-activity:yumi-gift-challenge:reconcile"), func(c *gin.Context) { itemID, ok := requirePathInt64(c, "itemId") if !ok { return } var request yumigiftchallenge.ResolveDeliveryRequest if err := c.ShouldBindJSON(&request); err != nil { writeBadRequest(c, err) return } if err := service.ResolveUnknownDeliveryItem(c.Request.Context(), itemID, request.Delivered); err != nil { writeError(c, err) return } writeOK(c, gin.H{"id": strconv.FormatInt(itemID, 10), "delivered": request.Delivered}) }) } func parseOptionalInt64(value string) (int64, error) { value = strings.TrimSpace(value) if value == "" { return 0, nil } parsed, err := strconv.ParseInt(value, 10, 64) if err != nil || parsed <= 0 { return 0, yumigiftchallenge.NewAppError(http.StatusBadRequest, "invalid_id", "id must be a positive int64 string") } return parsed, nil } func parseOptionalInt(value string) (int, error) { value = strings.TrimSpace(value) if value == "" { return 0, nil } parsed, err := strconv.Atoi(value) if err != nil || parsed < 0 { return 0, yumigiftchallenge.NewAppError(http.StatusBadRequest, "invalid_number", "query number must be a non-negative integer") } return parsed, nil } func requirePathInt64(c *gin.Context, name string) (int64, bool) { value, err := parseOptionalInt64(c.Param(name)) if err != nil || value <= 0 { if err == nil { err = yumigiftchallenge.NewAppError(http.StatusBadRequest, "invalid_id", name+" must be a positive int64 string") } writeError(c, err) return 0, false } return value, true } func firstParseError(values ...error) error { for _, err := range values { if err != nil { return err } } return yumigiftchallenge.NewAppError(http.StatusBadRequest, "bad_request", "invalid query") } func writeBadRequest(c *gin.Context, err error) { writeError(c, yumigiftchallenge.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) }