2026-06-12 01:21:33 +08:00

202 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 ""
}