202 lines
6.1 KiB
Go
202 lines
6.1 KiB
Go
package gamemanagement
|
||
|
||
import (
|
||
"strconv"
|
||
"strings"
|
||
|
||
"hyapp-admin-server/internal/integration/gameclient"
|
||
"hyapp-admin-server/internal/integration/userclient"
|
||
"hyapp-admin-server/internal/modules/shared"
|
||
"hyapp-admin-server/internal/response"
|
||
gamev1 "hyapp.local/api/proto/game/v1"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
type Handler struct {
|
||
game gameclient.Client
|
||
user userclient.Client
|
||
audit shared.OperationLogger
|
||
robotProfiles RobotProfileSource
|
||
}
|
||
|
||
// Option 只开放模块初始化时的基础设施注入点,避免 handler 方法里散落环境配置读取。
|
||
type Option func(*Handler)
|
||
|
||
func New(game gameclient.Client, user userclient.Client, audit shared.OperationLogger, options ...Option) *Handler {
|
||
h := &Handler{game: game, user: user, audit: audit}
|
||
for _, option := range options {
|
||
if option != nil {
|
||
option(h)
|
||
}
|
||
}
|
||
return h
|
||
}
|
||
|
||
// ListPlatforms 返回后台游戏平台筛选项和平台配置列表。
|
||
func (h *Handler) ListPlatforms(c *gin.Context) {
|
||
resp, err := h.game.ListPlatforms(c.Request.Context(), &gamev1.ListPlatformsRequest{
|
||
Meta: requestMeta(c),
|
||
Status: strings.TrimSpace(c.Query("status")),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取游戏平台失败")
|
||
return
|
||
}
|
||
items := make([]platformDTO, 0, len(resp.GetPlatforms()))
|
||
for _, item := range resp.GetPlatforms() {
|
||
items = append(items, platformFromProto(item))
|
||
}
|
||
response.OK(c, gin.H{"items": items, "serverTimeMs": resp.GetServerTimeMs()})
|
||
}
|
||
|
||
func (h *Handler) CreatePlatform(c *gin.Context) {
|
||
h.upsertPlatform(c, "")
|
||
}
|
||
|
||
func (h *Handler) UpdatePlatform(c *gin.Context) {
|
||
h.upsertPlatform(c, strings.TrimSpace(c.Param("platform_code")))
|
||
}
|
||
|
||
// upsertPlatform 创建或更新第三方游戏平台配置;callbackSecret 非空时才会轮换厂商密钥。
|
||
func (h *Handler) upsertPlatform(c *gin.Context, platformCode string) {
|
||
var req platformRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "游戏平台参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.game.UpsertPlatform(c.Request.Context(), &gamev1.UpsertPlatformRequest{
|
||
Meta: requestMeta(c),
|
||
Platform: req.toProto(platformCode),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := platformFromProto(resp.GetPlatform())
|
||
h.auditLog(c, "upsert-game-platform", "game_platforms", item.PlatformCode, item.PlatformName)
|
||
response.OK(c, item)
|
||
}
|
||
|
||
// ListCatalog 返回游戏目录;当前 game-service 使用 cursor 扩展点,后台先按 pageSize 截断。
|
||
func (h *Handler) ListCatalog(c *gin.Context) {
|
||
pageSize := int32(parsePositiveInt(firstQuery(c, "pageSize", "page_size"), 50))
|
||
resp, err := h.game.ListCatalog(c.Request.Context(), &gamev1.ListCatalogRequest{
|
||
Meta: requestMeta(c),
|
||
PlatformCode: strings.TrimSpace(firstQuery(c, "platformCode", "platform_code")),
|
||
Status: strings.TrimSpace(c.Query("status")),
|
||
PageSize: pageSize,
|
||
Cursor: strings.TrimSpace(c.Query("cursor")),
|
||
})
|
||
if err != nil {
|
||
response.ServerError(c, "获取游戏列表失败")
|
||
return
|
||
}
|
||
items := make([]catalogDTO, 0, len(resp.GetGames()))
|
||
for _, item := range resp.GetGames() {
|
||
items = append(items, catalogFromProto(item))
|
||
}
|
||
response.OK(c, gin.H{
|
||
"items": items,
|
||
"nextCursor": resp.GetNextCursor(),
|
||
"pageSize": pageSize,
|
||
"serverTimeMs": resp.GetServerTimeMs(),
|
||
})
|
||
}
|
||
|
||
func (h *Handler) CreateCatalog(c *gin.Context) {
|
||
h.upsertCatalog(c, "")
|
||
}
|
||
|
||
func (h *Handler) UpdateCatalog(c *gin.Context) {
|
||
h.upsertCatalog(c, strings.TrimSpace(c.Param("game_id")))
|
||
}
|
||
|
||
// upsertCatalog 创建或更新内部 game_id 到 provider_game_id 的映射。
|
||
func (h *Handler) upsertCatalog(c *gin.Context, gameID string) {
|
||
var req catalogRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "游戏参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.game.UpsertCatalog(c.Request.Context(), &gamev1.UpsertCatalogRequest{
|
||
Meta: requestMeta(c),
|
||
Game: req.toProto(gameID),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := catalogFromProto(resp.GetGame())
|
||
h.auditLog(c, "upsert-game-catalog", "game_catalog", item.GameID, item.GameName)
|
||
response.OK(c, item)
|
||
}
|
||
|
||
// SetGameStatus 只处理 active/maintenance/disabled 状态,不删除历史订单关联的游戏目录。
|
||
func (h *Handler) SetGameStatus(c *gin.Context) {
|
||
gameID := strings.TrimSpace(c.Param("game_id"))
|
||
if gameID == "" {
|
||
response.BadRequest(c, "游戏 ID 参数不正确")
|
||
return
|
||
}
|
||
var req statusRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "状态参数不正确")
|
||
return
|
||
}
|
||
resp, err := h.game.SetGameStatus(c.Request.Context(), &gamev1.SetGameStatusRequest{
|
||
Meta: requestMeta(c),
|
||
GameId: gameID,
|
||
Status: strings.TrimSpace(req.Status),
|
||
})
|
||
if err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
item := catalogFromProto(resp.GetGame())
|
||
h.auditLog(c, "set-game-status", "game_catalog", item.GameID, item.Status)
|
||
response.OK(c, item)
|
||
}
|
||
|
||
// DeleteCatalog 删除后台游戏目录记录,同时由 game-service 清理展示规则;历史订单和启动会话保留用于审计。
|
||
func (h *Handler) DeleteCatalog(c *gin.Context) {
|
||
gameID := strings.TrimSpace(c.Param("game_id"))
|
||
if gameID == "" {
|
||
response.BadRequest(c, "游戏 ID 参数不正确")
|
||
return
|
||
}
|
||
if _, err := h.game.DeleteCatalog(c.Request.Context(), &gamev1.DeleteCatalogRequest{
|
||
Meta: requestMeta(c),
|
||
GameId: gameID,
|
||
}); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
h.auditLog(c, "delete-game-catalog", "game_catalog", gameID, "deleted")
|
||
response.OK(c, gin.H{"gameId": gameID})
|
||
}
|
||
|
||
func (h *Handler) auditLog(c *gin.Context, action string, resource string, resourceID string, detail string) {
|
||
shared.OperationLogWithResourceID(c, h.audit, action, resource, resourceID, "success", detail)
|
||
}
|
||
|
||
func parsePositiveInt(raw string, fallback int) int {
|
||
value, err := strconv.Atoi(strings.TrimSpace(raw))
|
||
if err != nil || value <= 0 {
|
||
return fallback
|
||
}
|
||
if value > 200 {
|
||
return 200
|
||
}
|
||
return value
|
||
}
|
||
|
||
func firstQuery(c *gin.Context, keys ...string) string {
|
||
for _, key := range keys {
|
||
if value := c.Query(key); value != "" {
|
||
return value
|
||
}
|
||
}
|
||
return ""
|
||
}
|