package http import ( "chatapp3-golang/internal/config" "chatapp3-golang/internal/integration" "chatapp3-golang/internal/service" "encoding/json" "fmt" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" ) func registerBaishunRoutes( router *gin.Engine, cfg config.Config, javaClient *integration.Client, baishunService *service.BaishunService, ) { appGroup := router.Group("/app/game") appGroup.Use(authMiddleware(javaClient)) appGroup.GET("/room/shortcut", func(c *gin.Context) { user := mustAuthUser(c) resp, err := baishunService.ListShortcutGames(c.Request.Context(), user, c.Query("roomId")) if err != nil { writeError(c, err) return } writeOK(c, resp) }) appGroup.GET("/room/list", func(c *gin.Context) { user := mustAuthUser(c) resp, err := baishunService.ListRoomGames(c.Request.Context(), user, c.Query("roomId"), c.Query("category")) if err != nil { writeError(c, err) return } writeOK(c, resp) }) appGroup.GET("/baishun/state", func(c *gin.Context) { user := mustAuthUser(c) resp, err := baishunService.GetRoomState(c.Request.Context(), user, c.Query("roomId")) if err != nil { writeError(c, err) return } writeOK(c, resp) }) appGroup.POST("/baishun/launch", func(c *gin.Context) { user := mustAuthUser(c) var req service.BaishunLaunchAppRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := baishunService.LaunchGame(c.Request.Context(), user, req, resolveClientIP(c)) if err != nil { writeError(c, err) return } writeOK(c, resp) }) appGroup.POST("/baishun/close", func(c *gin.Context) { user := mustAuthUser(c) var req service.BaishunCloseAppRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := baishunService.CloseGame(c.Request.Context(), user, req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) internalGroup := router.Group("/internal/game/baishun") internalGroup.Use(internalSecretMiddleware(cfg.InternalCallbackSecret)) internalGroup.POST("/sync-catalog", func(c *gin.Context) { var req service.SyncCatalogRequest if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := baishunService.SyncCatalog(c.Request.Context(), req) if err != nil { writeError(c, err) return } writeOK(c, resp) }) internalGroup.POST("/refresh-room-state", func(c *gin.Context) { var req struct { SysOrigin string `json:"sysOrigin"` RoomID string `json:"roomId"` } if err := c.ShouldBindJSON(&req); err != nil { writeError(c, service.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } resp, err := baishunService.RefreshRoomState(c.Request.Context(), req.SysOrigin, req.RoomID) if err != nil { writeError(c, err) return } writeOK(c, resp) }) callbackGroup := router.Group("/game/baishun") callbackGroup.POST("/token", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleToken(c.Request.Context(), mapToTokenRequest(payload), raw)) }) callbackGroup.POST("/profile", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleProfile(c.Request.Context(), mapToProfileRequest(payload), raw)) }) callbackGroup.POST("/update-token", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleUpdateToken(c.Request.Context(), mapToUpdateTokenRequest(payload), raw)) }) callbackGroup.POST("/change-balance", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleChangeBalance(c.Request.Context(), mapToChangeBalanceRequest(payload), raw)) }) callbackGroup.POST("/report", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleReport(c.Request.Context(), payload, raw)) }) callbackGroup.POST("/balance-info", func(c *gin.Context) { raw, payload := readRawPayload(c) writeBaishunJSON(c, http.StatusOK, baishunService.HandleBalanceInfo(c.Request.Context(), mapToBalanceInfoRequest(payload), raw)) }) } func internalSecretMiddleware(secret string) gin.HandlerFunc { return func(c *gin.Context) { if strings.TrimSpace(secret) == "" { c.Next() return } token := strings.TrimSpace(c.GetHeader("X-Internal-Token")) if token != secret { writeError(c, service.NewAppError(http.StatusUnauthorized, "invalid_internal_token", "internal token is invalid")) c.Abort() return } c.Next() } } func writeBaishunJSON(c *gin.Context, status int, payload any) { c.JSON(status, payload) } func readRawPayload(c *gin.Context) (string, map[string]any) { rawBytes, _ := c.GetRawData() raw := string(rawBytes) payload := map[string]any{} if len(rawBytes) > 0 { _ = json.Unmarshal(rawBytes, &payload) } return raw, payload } func mapToTokenRequest(payload map[string]any) service.BaishunTokenRequest { return service.BaishunTokenRequest{ AppID: asInt64(payload["app_id"]), UserID: asString(payload["user_id"]), Code: asString(payload["code"]), Signature: asString(payload["signature"]), SignatureNonce: asString(payload["signature_nonce"]), Timestamp: asInt64(payload["timestamp"]), } } func mapToProfileRequest(payload map[string]any) service.BaishunProfileRequest { return service.BaishunProfileRequest{ AppID: asInt64(payload["app_id"]), UserID: asString(payload["user_id"]), SSToken: asString(payload["ss_token"]), ClientIP: asString(payload["client_ip"]), GameID: int(asInt64(payload["game_id"])), Signature: asString(payload["signature"]), SignatureNonce: asString(payload["signature_nonce"]), Timestamp: asInt64(payload["timestamp"]), } } func mapToUpdateTokenRequest(payload map[string]any) service.BaishunUpdateTokenRequest { return service.BaishunUpdateTokenRequest{ AppID: asInt64(payload["app_id"]), UserID: asString(payload["user_id"]), SSToken: asString(payload["ss_token"]), GameID: int(asInt64(payload["game_id"])), Signature: asString(payload["signature"]), SignatureNonce: asString(payload["signature_nonce"]), Timestamp: asInt64(payload["timestamp"]), } } func mapToChangeBalanceRequest(payload map[string]any) service.BaishunChangeBalanceRequest { var currencyType *int if rawValue, exists := payload["currency_type"]; exists && rawValue != nil { parsed := int(asInt64(rawValue)) currencyType = &parsed } return service.BaishunChangeBalanceRequest{ AppID: asInt64(payload["app_id"]), UserID: asString(payload["user_id"]), SSToken: asString(payload["ss_token"]), CurrencyDiff: asInt64(payload["currency_diff"]), DiffMsg: asString(payload["diff_msg"]), GameID: int(asInt64(payload["game_id"])), GameRoundID: asString(payload["game_round_id"]), RoomID: asString(payload["room_id"]), ChangeTimeAt: asInt64(payload["change_time_at"]), OrderID: asString(payload["order_id"]), Extend: asString(payload["extend"]), MsgType: asString(payload["msg_type"]), CurrencyType: currencyType, Signature: asString(payload["signature"]), SignatureNonce: asString(payload["signature_nonce"]), Timestamp: asInt64(payload["timestamp"]), } } func mapToBalanceInfoRequest(payload map[string]any) service.BaishunBalanceInfoRequest { return service.BaishunBalanceInfoRequest{ UserID: asString(payload["user_id"]), AppID: asInt64(payload["app_id"]), AppChannel: asString(payload["app_channel"]), Signature: asString(payload["signature"]), SignatureNonce: asString(payload["signature_nonce"]), Timestamp: asInt64(payload["timestamp"]), } } func asString(value any) string { if value == nil { return "" } switch typed := value.(type) { case string: return strings.TrimSpace(typed) case json.Number: return typed.String() default: return strings.TrimSpace(fmt.Sprint(typed)) } } func asInt64(value any) int64 { if value == nil { return 0 } switch typed := value.(type) { case int64: return typed case int: return int64(typed) case float64: return int64(typed) case json.Number: parsed, _ := typed.Int64() return parsed case string: parsed, _ := strconv.ParseInt(strings.TrimSpace(typed), 10, 64) return parsed default: parsed, _ := strconv.ParseInt(strings.TrimSpace(fmt.Sprint(typed)), 10, 64) return parsed } }