2026-06-12 00:35:14 +08:00

263 lines
8.5 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
}
nicknameLanguage := normalizeRobotNicknameLanguage(req.NicknameLanguage)
country := strings.TrimSpace(req.Country)
if country == "" {
country = "SA"
}
userIDs := make([]int64, 0, req.Count)
requestID := middleware.CurrentRequestID(c)
actorID := middleware.CurrentUserID(c)
avatarCache := make(map[string]string)
for index := int32(0); index < req.Count; index++ {
// 批量机器人必须一次性写齐完整资料;同一请求内相同头像源只转存一次,避免 200 个账号重复下载上传同一张图。
avatarSource := robotAvatarSource(int(index), req.AvatarURLs)
avatarURL := avatarCache[avatarSource]
if avatarURL == "" {
var err error
avatarURL, err = h.uploadRobotAvatarFromSource(c.Request.Context(), actorID, avatarSource)
if err != nil {
response.BadRequest(c, "上传机器人头像失败: "+err.Error())
return
}
avatarCache[avatarSource] = avatarURL
}
created, err := h.user.QuickCreateAccount(c.Request.Context(), userclient.QuickCreateAccountRequest{
RequestID: requestID,
Caller: "admin-server",
Password: randomRobotPassword(),
Username: robotNickname(int(index), nicknameLanguage),
Avatar: avatarURL,
Gender: robotGender(int(index), req.Gender),
Country: country,
DeviceID: "game-robot-" + randomRobotHex(8),
Source: "game_robot",
InstallChannel: "admin",
Platform: "android",
Language: robotAccountLanguage(nicknameLanguage),
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 pickString(values []string, index int) string {
if len(values) == 0 {
return ""
}
return strings.TrimSpace(values[index%len(values)])
}
func parseInt64Param(raw string) (int64, error) {
var value int64
_, err := fmt.Sscan(strings.TrimSpace(raw), &value)
return value, err
}