434 lines
14 KiB
Go
434 lines
14 KiB
Go
package router
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"chatapp3-golang/internal/config"
|
|
"chatapp3-golang/internal/service/baishun"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// registerBaishunRoutes 注册百顺游戏相关路由。
|
|
func registerBaishunRoutes(
|
|
engine *gin.Engine,
|
|
cfg config.Config,
|
|
javaClient authGateway,
|
|
baishunService *baishun.BaishunService,
|
|
) {
|
|
if baishunService == nil {
|
|
return
|
|
}
|
|
|
|
appGroup := engine.Group("/app/game")
|
|
appGroup.Use(authMiddleware(javaClient))
|
|
appGroup.GET("/baishun/state", func(c *gin.Context) {
|
|
resp, err := baishunService.GetRoomState(c.Request.Context(), mustAuthUser(c), c.Query("roomId"))
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
appGroup.POST("/baishun/launch", func(c *gin.Context) {
|
|
var req baishun.BaishunLaunchAppRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.LaunchGame(c.Request.Context(), mustAuthUser(c), req, resolveClientIP(c))
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
appGroup.POST("/baishun/close", func(c *gin.Context) {
|
|
var req baishun.BaishunCloseAppRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.CloseGame(c.Request.Context(), mustAuthUser(c), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
|
|
internalGroup := engine.Group("/internal/game/baishun")
|
|
internalGroup.Use(internalSecretMiddleware(cfg.HTTP.InternalCallbackSecret))
|
|
internalGroup.POST("/sync-catalog", func(c *gin.Context) {
|
|
var req baishun.SyncCatalogRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.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, baishun.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)
|
|
})
|
|
|
|
consoleGroup := engine.Group("/operate/baishun-game")
|
|
consoleGroup.Use(consoleAuthMiddleware(javaClient))
|
|
consoleGroup.GET("/config", func(c *gin.Context) {
|
|
resp, err := baishunService.GetProviderConfig(c.Request.Context(), c.Query("sysOrigin"), c.Query("profile"))
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.POST("/config", func(c *gin.Context) {
|
|
var req baishun.SaveBaishunProviderConfigRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.SaveProviderConfig(c.Request.Context(), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.POST("/config/activate", func(c *gin.Context) {
|
|
var req baishun.ActivateBaishunProviderProfileRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.ActivateProviderProfile(c.Request.Context(), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.GET("/page", func(c *gin.Context) {
|
|
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
|
|
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20")))
|
|
resp, err := baishunService.PageAdminGames(
|
|
c.Request.Context(),
|
|
c.Query("sysOrigin"),
|
|
c.Query("profile"),
|
|
c.Query("keyword"),
|
|
parseOptionalBool(c.Query("showcase")),
|
|
cursor,
|
|
limit,
|
|
)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.GET("/catalog/page", func(c *gin.Context) {
|
|
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
|
|
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20")))
|
|
resp, err := baishunService.PageCatalog(
|
|
c.Request.Context(),
|
|
c.Query("sysOrigin"),
|
|
c.Query("profile"),
|
|
c.Query("keyword"),
|
|
cursor,
|
|
limit,
|
|
)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.POST("/save", func(c *gin.Context) {
|
|
var req baishun.SaveAdminBaishunGameRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.SaveAdminGame(c.Request.Context(), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.DELETE("", func(c *gin.Context) {
|
|
id, _ := strconv.ParseInt(strings.TrimSpace(c.Query("id")), 10, 64)
|
|
if err := baishunService.DeleteAdminGame(c.Request.Context(), c.Query("sysOrigin"), c.Query("profile"), id); err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, gin.H{"deleted": true})
|
|
})
|
|
consoleGroup.POST("/catalog/fetch", func(c *gin.Context) {
|
|
var req baishun.SyncCatalogRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.FetchAdminCatalog(c.Request.Context(), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.POST("/import-catalog", func(c *gin.Context) {
|
|
var req baishun.SyncCatalogRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
defaultShowcase := true
|
|
if req.DefaultShowcase != nil {
|
|
defaultShowcase = *req.DefaultShowcase
|
|
}
|
|
resp, err := baishunService.ImportCatalogGames(c.Request.Context(), req.SysOrigin, req.Profile, req.VendorGameIDs, defaultShowcase)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
consoleGroup.POST("/sync", func(c *gin.Context) {
|
|
var req baishun.SyncCatalogRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
|
return
|
|
}
|
|
resp, err := baishunService.SyncAdminGames(c.Request.Context(), req)
|
|
if err != nil {
|
|
writeError(c, err)
|
|
return
|
|
}
|
|
writeOK(c, resp)
|
|
})
|
|
|
|
callbackGroup := engine.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(), mapToReportRequest(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))
|
|
})
|
|
}
|
|
|
|
// writeBaishunJSON 输出百顺回调约定的 JSON 结构。
|
|
func writeBaishunJSON(c *gin.Context, status int, payload any) {
|
|
c.JSON(status, payload)
|
|
}
|
|
|
|
// readRawPayload 同时返回原始报文和 map 结构,方便百顺回调复用。
|
|
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
|
|
}
|
|
|
|
// mapToTokenRequest 把百顺 token 回调报文映射成服务层 DTO。
|
|
func mapToTokenRequest(payload map[string]any) baishun.BaishunTokenRequest {
|
|
return baishun.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"]),
|
|
}
|
|
}
|
|
|
|
// mapToProfileRequest 把百顺 profile 回调报文映射成服务层 DTO。
|
|
func mapToProfileRequest(payload map[string]any) baishun.BaishunProfileRequest {
|
|
return baishun.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"]),
|
|
}
|
|
}
|
|
|
|
// mapToUpdateTokenRequest 把百顺 update-token 回调报文映射成服务层 DTO。
|
|
func mapToUpdateTokenRequest(payload map[string]any) baishun.BaishunUpdateTokenRequest {
|
|
return baishun.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"]),
|
|
}
|
|
}
|
|
|
|
// mapToChangeBalanceRequest 把百顺 change-balance 回调报文映射成服务层 DTO。
|
|
func mapToChangeBalanceRequest(payload map[string]any) baishun.BaishunChangeBalanceRequest {
|
|
var currencyType *int
|
|
if rawValue, exists := payload["currency_type"]; exists && rawValue != nil {
|
|
parsed := int(asInt64(rawValue))
|
|
currencyType = &parsed
|
|
}
|
|
return baishun.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"]),
|
|
}
|
|
}
|
|
|
|
// mapToReportRequest 把百顺 report 回调报文映射成服务层 DTO。
|
|
func mapToReportRequest(payload map[string]any) baishun.BaishunReportRequest {
|
|
return baishun.BaishunReportRequest{
|
|
ReportType: asString(payload["report_type"]),
|
|
ReportMsg: asMap(payload["report_msg"]),
|
|
UserID: asString(payload["user_id"]),
|
|
SSToken: asString(payload["ss_token"]),
|
|
}
|
|
}
|
|
|
|
// mapToBalanceInfoRequest 把百顺 balance-info 回调报文映射成服务层 DTO。
|
|
func mapToBalanceInfoRequest(payload map[string]any) baishun.BaishunBalanceInfoRequest {
|
|
return baishun.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"]),
|
|
}
|
|
}
|
|
|
|
// asMap 宽松读取对象字段。
|
|
func asMap(value any) map[string]any {
|
|
switch typed := value.(type) {
|
|
case map[string]any:
|
|
return typed
|
|
case string:
|
|
result := map[string]any{}
|
|
_ = json.Unmarshal([]byte(typed), &result)
|
|
return result
|
|
default:
|
|
body, err := json.Marshal(typed)
|
|
if err != nil {
|
|
return map[string]any{}
|
|
}
|
|
result := map[string]any{}
|
|
_ = json.Unmarshal(body, &result)
|
|
return result
|
|
}
|
|
}
|
|
|
|
// asString 宽松读取字符串字段。
|
|
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))
|
|
}
|
|
}
|
|
|
|
// asInt64 宽松读取数值字段。
|
|
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
|
|
}
|
|
}
|
|
|
|
func parseOptionalBool(raw string) *bool {
|
|
switch strings.ToLower(strings.TrimSpace(raw)) {
|
|
case "0", "false":
|
|
value := false
|
|
return &value
|
|
case "1", "true":
|
|
value := true
|
|
return &value
|
|
default:
|
|
return nil
|
|
}
|
|
}
|