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

258 lines
8.4 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 (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"hyapp-admin-server/internal/integration/userclient"
"hyapp-admin-server/internal/middleware"
"hyapp-admin-server/internal/response"
gamev1 "hyapp.local/api/proto/game/v1"
"github.com/gin-gonic/gin"
)
func (h *Handler) ListSelfGames(c *gin.Context) {
resp, err := h.game.ListSelfGames(c.Request.Context(), &gamev1.ListSelfGamesRequest{Meta: requestMeta(c)})
if err != nil {
response.ServerError(c, "获取自研游戏失败")
return
}
items := make([]diceConfigDTO, 0, len(resp.GetGames()))
for _, item := range resp.GetGames() {
items = append(items, diceConfigFromProto(item))
}
response.OK(c, gin.H{"items": items, "serverTimeMs": resp.GetServerTimeMs()})
}
func (h *Handler) UpdateDiceConfig(c *gin.Context) {
gameID := strings.TrimSpace(c.Param("game_id"))
if gameID == "" {
response.BadRequest(c, "游戏 ID 参数不正确")
return
}
var req diceConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "骰子配置参数不正确")
return
}
resp, err := h.game.UpdateDiceConfig(c.Request.Context(), &gamev1.UpdateDiceConfigRequest{
Meta: requestMeta(c),
Config: req.toProto(gameID),
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
item := diceConfigFromProto(resp.GetConfig())
h.auditLog(c, "update-dice-config", "game_self_game_configs", item.GameID, item.Status)
response.OK(c, item)
}
func (h *Handler) AdjustDicePool(c *gin.Context) {
gameID := strings.TrimSpace(c.Param("game_id"))
if gameID == "" {
response.BadRequest(c, "游戏 ID 参数不正确")
return
}
var req dicePoolAdjustRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "奖池调整参数不正确")
return
}
resp, err := h.game.AdjustDicePool(c.Request.Context(), &gamev1.AdjustDicePoolRequest{
Meta: requestMeta(c),
GameId: gameID,
AmountCoin: req.AmountCoin,
Direction: strings.TrimSpace(req.Direction),
Reason: strings.TrimSpace(req.Reason),
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
h.auditLog(c, "adjust-dice-pool", "game_self_game_pools", gameID, fmt.Sprintf("%s:%d", req.Direction, req.AmountCoin))
response.OK(c, gin.H{"adjustment": dicePoolAdjustmentFromProto(resp.GetAdjustment()), "config": diceConfigFromProto(resp.GetConfig())})
}
func (h *Handler) ListDiceRobots(c *gin.Context) {
pageSize := int32(parsePositiveInt(firstQuery(c, "pageSize", "page_size"), 50))
requestID := middleware.CurrentRequestID(c)
resp, err := h.game.ListDiceRobots(c.Request.Context(), &gamev1.ListDiceRobotsRequest{
Meta: requestMeta(c),
GameId: strings.TrimSpace(firstQuery(c, "gameId", "game_id")),
Status: strings.TrimSpace(c.Query("status")),
PageSize: pageSize,
Cursor: strings.TrimSpace(c.Query("cursor")),
})
if err != nil {
response.ServerError(c, "获取全站机器人失败")
return
}
items := make([]diceRobotDTO, 0, len(resp.GetRobots()))
for _, item := range resp.GetRobots() {
items = append(items, diceRobotFromProto(item))
}
if err := h.enrichDiceRobotProfiles(c.Request.Context(), requestID, items); err != nil {
response.ServerError(c, "获取机器人用户资料失败")
return
}
response.OK(c, gin.H{"items": items, "nextCursor": resp.GetNextCursor(), "pageSize": pageSize, "serverTimeMs": resp.GetServerTimeMs()})
}
func (h *Handler) GenerateDiceRobots(c *gin.Context) {
if h.user == nil {
response.ServerError(c, "用户服务不可用")
return
}
var req diceGenerateRobotsRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "机器人生成参数不正确")
return
}
if req.Count <= 0 {
req.Count = defaultRobotGenerateCount
}
if req.Count > maxRobotGenerateCount {
req.Count = maxRobotGenerateCount
}
country := strings.TrimSpace(req.Country)
if country == "" {
country = "SA"
}
if h.robotProfiles == nil {
response.ServerError(c, "likei 机器人资料源未配置")
return
}
// 新机器人资料只从 likei 线上用户库随机抽取;后台不再使用本地昵称池、随机头像站或 COS 转存,
// 这样每次批量创建都面对真实用户资料池,避免少量固定预设反复出现在匹配列表里。
profiles, err := h.robotProfiles.RandomRobotProfiles(c.Request.Context(), int(req.Count))
if err != nil {
response.BadRequest(c, "获取 likei 机器人资料失败: "+err.Error())
return
}
if len(profiles) < int(req.Count) {
response.BadRequest(c, "likei 机器人资料数量不足")
return
}
userIDs := make([]int64, 0, req.Count)
requestID := middleware.CurrentRequestID(c)
accountLanguage := robotAccountLanguage(req.NicknameLanguage)
for index := int32(0); index < req.Count; index++ {
profile := profiles[index]
created, err := h.user.QuickCreateAccount(c.Request.Context(), userclient.QuickCreateAccountRequest{
RequestID: requestID,
Caller: "admin-server",
Password: randomRobotPassword(),
Username: profile.Nickname,
Avatar: profile.Avatar,
Gender: robotGender(int(index), req.Gender),
Country: country,
DeviceID: "game-robot-" + randomRobotHex(8),
Source: "game_robot",
InstallChannel: "admin",
Platform: "android",
Language: accountLanguage,
Timezone: "UTC",
})
if err != nil {
response.BadRequest(c, "创建机器人用户失败: "+err.Error())
return
}
userIDs = append(userIDs, created.UserID)
}
resp, err := h.game.RegisterDiceRobots(c.Request.Context(), &gamev1.RegisterDiceRobotsRequest{
Meta: requestMeta(c),
GameId: strings.TrimSpace(firstQuery(c, "gameId", "game_id")),
UserIds: userIDs,
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
items := make([]diceRobotDTO, 0, len(resp.GetRobots()))
for _, item := range resp.GetRobots() {
items = append(items, diceRobotFromProto(item))
}
if err := h.enrichDiceRobotProfiles(c.Request.Context(), requestID, items); err != nil {
response.ServerError(c, "获取机器人用户资料失败")
return
}
h.auditLog(c, "generate-dice-robots", "game_self_game_robots", "universal", fmt.Sprintf("count=%d", len(userIDs)))
response.OK(c, gin.H{"created": len(userIDs), "items": items})
}
func (h *Handler) SetDiceRobotStatus(c *gin.Context) {
gameID := strings.TrimSpace(firstQuery(c, "gameId", "game_id"))
if gameID == "" {
gameID = "dice"
}
userID, err := parseInt64Param(c.Param("user_id"))
if err != nil || userID <= 0 {
response.BadRequest(c, "机器人用户 ID 参数不正确")
return
}
var req diceRobotStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "机器人状态参数不正确")
return
}
resp, err := h.game.SetDiceRobotStatus(c.Request.Context(), &gamev1.SetDiceRobotStatusRequest{
Meta: requestMeta(c),
GameId: gameID,
UserId: userID,
Status: strings.TrimSpace(req.Status),
})
if err != nil {
response.BadRequest(c, err.Error())
return
}
item := diceRobotFromProto(resp.GetRobot())
h.auditLog(c, "set-dice-robot-status", "game_self_game_robots", item.UserID, item.Status)
response.OK(c, item)
}
// DeleteDiceRobot 删除当前自研游戏的机器人登记,后台只移除可匹配机器人池,不触碰真实用户资料。
func (h *Handler) DeleteDiceRobot(c *gin.Context) {
gameID := strings.TrimSpace(firstQuery(c, "gameId", "game_id"))
if gameID == "" {
gameID = "dice"
}
userID, err := parseInt64Param(c.Param("user_id"))
if err != nil || userID <= 0 {
response.BadRequest(c, "机器人用户 ID 参数不正确")
return
}
// 后台删除只撤销 game-service 的机器人登记;真实用户仍保留在 user-service历史对局和钱包流水可以继续按 user_id 追溯。
if _, err := h.game.DeleteDiceRobot(c.Request.Context(), &gamev1.DeleteDiceRobotRequest{
Meta: requestMeta(c),
GameId: gameID,
UserId: userID,
}); err != nil {
response.BadRequest(c, err.Error())
return
}
h.auditLog(c, "delete-dice-robot", "game_self_game_robots", fmt.Sprintf("%d", userID), gameID)
response.OK(c, gin.H{"deleted": true})
}
func randomRobotPassword() string {
return "Robot#" + randomRobotHex(12)
}
func randomRobotHex(size int) string {
buf := make([]byte, size)
if _, err := rand.Read(buf); err != nil {
return "fallback"
}
return hex.EncodeToString(buf)
}
func parseInt64Param(raw string) (int64, error) {
var value int64
_, err := fmt.Sscan(strings.TrimSpace(raw), &value)
return value, err
}