142 lines
4.0 KiB
Go
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
|
|
}
|