2026-06-17 23:13:08 +08:00

142 lines
4.0 KiB
Go

package integration
import (
"context"
"fmt"
"sort"
"strings"
"time"
"google.golang.org/grpc"
robotv1 "hyapp.local/api/proto/robot/v1"
userv1 "hyapp.local/api/proto/user/v1"
"hyapp/pkg/appcode"
)
const humanRobotPoolPageSize = int32(200)
// HumanRoomRobotPool 是真人房间机器人运行时按国家分组后的账号池。
type HumanRoomRobotPool struct {
CountryCode string
RobotUserIDs []int64
}
// HumanRoomRobotPoolProvider 屏蔽 robot-service 和 user-service 的组合查询细节。
type HumanRoomRobotPoolProvider interface {
ListHumanRoomRobotPools(ctx context.Context, appCode string) ([]HumanRoomRobotPool, error)
}
type GRPCHumanRoomRobotPoolProvider struct {
robots robotv1.GameRobotServiceClient
users userv1.UserServiceClient
}
func NewGRPCHumanRoomRobotPoolProvider(robotConn grpc.ClientConnInterface, userConn grpc.ClientConnInterface) *GRPCHumanRoomRobotPoolProvider {
return &GRPCHumanRoomRobotPoolProvider{
robots: robotv1.NewGameRobotServiceClient(robotConn),
users: userv1.NewUserServiceClient(userConn),
}
}
func (p *GRPCHumanRoomRobotPoolProvider) ListHumanRoomRobotPools(ctx context.Context, appCode string) ([]HumanRoomRobotPool, error) {
if p == nil || p.robots == nil || p.users == nil {
return nil, fmt.Errorf("human room robot pool provider is not configured")
}
app := appcode.Normalize(appCode)
userIDs, err := p.listActiveRobotUserIDs(ctx, app)
if err != nil {
return nil, err
}
if len(userIDs) == 0 {
return nil, nil
}
groups := make(map[string][]int64)
for start := 0; start < len(userIDs); start += int(humanRobotPoolPageSize) {
end := start + int(humanRobotPoolPageSize)
if end > len(userIDs) {
end = len(userIDs)
}
resp, err := p.users.BatchGetUsers(ctx, &userv1.BatchGetUsersRequest{
Meta: userRequestMeta(app, "room-human-robot-users"),
UserIds: userIDs[start:end],
})
if err != nil {
return nil, err
}
for _, userID := range userIDs[start:end] {
user := resp.GetUsers()[userID]
countryCode := normalizePoolCountryCode(user.GetCountry())
if countryCode == "" {
continue
}
groups[countryCode] = append(groups[countryCode], userID)
}
}
pools := make([]HumanRoomRobotPool, 0, len(groups))
for countryCode, ids := range groups {
pools = append(pools, HumanRoomRobotPool{CountryCode: countryCode, RobotUserIDs: ids})
}
sort.Slice(pools, func(i, j int) bool { return pools[i].CountryCode < pools[j].CountryCode })
return pools, nil
}
func (p *GRPCHumanRoomRobotPoolProvider) listActiveRobotUserIDs(ctx context.Context, app string) ([]int64, error) {
userIDs := make([]int64, 0, humanRobotPoolPageSize)
cursor := ""
for {
resp, err := p.robots.ListGameRobots(ctx, &robotv1.ListGameRobotsRequest{
Meta: robotRequestMeta(app, "room-human-robot-pool"),
Status: "active",
PageSize: humanRobotPoolPageSize,
Cursor: cursor,
})
if err != nil {
return nil, err
}
for _, robot := range resp.GetRobots() {
if robot.GetUserId() > 0 {
userIDs = append(userIDs, robot.GetUserId())
}
}
cursor = strings.TrimSpace(resp.GetNextCursor())
if cursor == "" {
return uniquePositiveInt64(userIDs), nil
}
}
}
func robotRequestMeta(appCode string, requestID string) *robotv1.RequestMeta {
return &robotv1.RequestMeta{
RequestId: requestID,
Caller: "room-service",
AppCode: appcode.Normalize(appCode),
SentAtMs: time.Now().UTC().UnixMilli(),
}
}
func userRequestMeta(appCode string, requestID string) *userv1.RequestMeta {
return &userv1.RequestMeta{
RequestId: requestID,
Caller: "room-service",
AppCode: appcode.Normalize(appCode),
SentAtMs: time.Now().UTC().UnixMilli(),
}
}
func normalizePoolCountryCode(value string) string {
return strings.ToUpper(strings.TrimSpace(value))
}
func uniquePositiveInt64(values []int64) []int64 {
seen := make(map[int64]bool, len(values))
out := make([]int64, 0, len(values))
for _, value := range values {
if value <= 0 || seen[value] {
continue
}
seen[value] = true
out = append(out, value)
}
return out
}