258 lines
8.4 KiB
Go
258 lines
8.4 KiB
Go
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
|
||
}
|