From f465c12d39482d611187b1976ed09bc5fd8da166 Mon Sep 17 00:00:00 2001 From: zhx Date: Wed, 17 Jun 2026 11:30:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=BF=E9=97=B4=E6=9C=BA=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integration/robotclient/client.go | 71 +++++++++++++++---- .../internal/modules/roomadmin/robot_room.go | 29 ++++---- .../modules/roomadmin/service_test.go | 25 +++++++ 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/server/admin/internal/integration/robotclient/client.go b/server/admin/internal/integration/robotclient/client.go index 8936c4a3..19fba880 100644 --- a/server/admin/internal/integration/robotclient/client.go +++ b/server/admin/internal/integration/robotclient/client.go @@ -13,6 +13,7 @@ import ( ) type Client interface { + ListGameRobots(ctx context.Context, req ListGameRobotsRequest) (ListGameRobotsResult, error) ListRoomRobots(ctx context.Context, req ListRoomRobotsRequest) (ListRoomRobotsResult, error) } @@ -33,32 +34,60 @@ type ListRoomRobotsRequest struct { Cursor string } +type ListGameRobotsRequest struct { + GameID string + Status string + PageSize int32 + Cursor string +} + type ListRoomRobotsResult struct { Robots []Robot NextCursor string ServerTimeMS int64 } +type ListGameRobotsResult struct { + Robots []Robot + NextCursor string + ServerTimeMS int64 +} + type GRPCClient struct { - client robotv1.RoomRobotServiceClient + gameClient robotv1.GameRobotServiceClient + roomClient robotv1.RoomRobotServiceClient } func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient { - return &GRPCClient{client: robotv1.NewRoomRobotServiceClient(conn)} + return &GRPCClient{ + gameClient: robotv1.NewGameRobotServiceClient(conn), + roomClient: robotv1.NewRoomRobotServiceClient(conn), + } +} + +func (c *GRPCClient) ListGameRobots(ctx context.Context, req ListGameRobotsRequest) (ListGameRobotsResult, error) { + if c == nil || c.gameClient == nil { + return ListGameRobotsResult{}, fmt.Errorf("game robot service client is not configured") + } + resp, err := c.gameClient.ListGameRobots(ctx, &robotv1.ListGameRobotsRequest{ + Meta: requestMeta(ctx, "admin-game-robots"), + GameId: strings.TrimSpace(req.GameID), + Status: strings.TrimSpace(req.Status), + PageSize: req.PageSize, + Cursor: strings.TrimSpace(req.Cursor), + }) + if err != nil { + return ListGameRobotsResult{}, err + } + return ListGameRobotsResult{Robots: robotsFromProto(resp.GetRobots()), NextCursor: resp.GetNextCursor(), ServerTimeMS: resp.GetServerTimeMs()}, nil } func (c *GRPCClient) ListRoomRobots(ctx context.Context, req ListRoomRobotsRequest) (ListRoomRobotsResult, error) { - if c == nil || c.client == nil { + if c == nil || c.roomClient == nil { return ListRoomRobotsResult{}, fmt.Errorf("robot service client is not configured") } - resp, err := c.client.ListRoomRobots(ctx, &robotv1.ListRoomRobotsRequest{ - Meta: &robotv1.RequestMeta{ - RequestId: fmt.Sprintf("admin-room-robots-%d", time.Now().UnixMilli()), - Caller: "admin-server", - AppCode: appctx.FromContext(ctx), - SentAtMs: time.Now().UnixMilli(), - ActorUserId: 0, - }, + resp, err := c.roomClient.ListRoomRobots(ctx, &robotv1.ListRoomRobotsRequest{ + Meta: requestMeta(ctx, "admin-room-robots"), RoomScene: strings.TrimSpace(req.RoomScene), Status: strings.TrimSpace(req.Status), PageSize: req.PageSize, @@ -67,8 +96,22 @@ func (c *GRPCClient) ListRoomRobots(ctx context.Context, req ListRoomRobotsReque if err != nil { return ListRoomRobotsResult{}, err } - items := make([]Robot, 0, len(resp.GetRobots())) - for _, item := range resp.GetRobots() { + return ListRoomRobotsResult{Robots: robotsFromProto(resp.GetRobots()), NextCursor: resp.GetNextCursor(), ServerTimeMS: resp.GetServerTimeMs()}, nil +} + +func requestMeta(ctx context.Context, prefix string) *robotv1.RequestMeta { + return &robotv1.RequestMeta{ + RequestId: fmt.Sprintf("%s-%d", prefix, time.Now().UnixMilli()), + Caller: "admin-server", + AppCode: appctx.FromContext(ctx), + SentAtMs: time.Now().UnixMilli(), + ActorUserId: 0, + } +} + +func robotsFromProto(source []*robotv1.Robot) []Robot { + items := make([]Robot, 0, len(source)) + for _, item := range source { items = append(items, Robot{ UserID: item.GetUserId(), Status: item.GetStatus(), @@ -79,5 +122,5 @@ func (c *GRPCClient) ListRoomRobots(ctx context.Context, req ListRoomRobotsReque UsedCount: item.GetUsedCount(), }) } - return ListRoomRobotsResult{Robots: items, NextCursor: resp.GetNextCursor(), ServerTimeMS: resp.GetServerTimeMs()}, nil + return items } diff --git a/server/admin/internal/modules/roomadmin/robot_room.go b/server/admin/internal/modules/roomadmin/robot_room.go index 60d46b24..a135b957 100644 --- a/server/admin/internal/modules/roomadmin/robot_room.go +++ b/server/admin/internal/modules/roomadmin/robot_room.go @@ -11,8 +11,6 @@ import ( "hyapp-admin-server/internal/modules/shared" ) -const defaultRoomRobotScene = "voice" - type RobotRoom struct { AppCode string `json:"appCode"` RoomID string `json:"roomId"` @@ -89,10 +87,9 @@ func (s *Service) ListAvailableRoomRobots(ctx context.Context) ([]AvailableRoomR if s.robotClient == nil || s.roomClient == nil { return nil, fmt.Errorf("robot or room service client is not configured") } - result, err := s.robotClient.ListRoomRobots(ctx, robotclient.ListRoomRobotsRequest{ - RoomScene: defaultRoomRobotScene, - Status: "active", - PageSize: 200, + result, err := s.robotClient.ListGameRobots(ctx, robotclient.ListGameRobotsRequest{ + Status: "active", + PageSize: 200, }) if err != nil { return nil, err @@ -103,20 +100,26 @@ func (s *Service) ListAvailableRoomRobots(ctx context.Context) ([]AvailableRoomR userIDs = append(userIDs, item.UserID) } } + // 全站机器人是后台统一维护的机器人账号池;房间机器人下拉只把这批账号交给 room-service 做占用过滤, + // 避免新增了全站机器人却因为旧的 room-scene 专用池为空而无法选择房主机器人。 available, err := s.roomClient.FilterAvailableRoomRobots(ctx, userIDs) if err != nil { return nil, err } - allowed := make(map[int64]bool, len(available.AvailableUserIDs)) - for _, userID := range available.AvailableUserIDs { - allowed[userID] = true - } owners, err := s.queryRoomOwners(ctx, available.AvailableUserIDs) if err != nil { return nil, err } - items := make([]AvailableRoomRobot, 0, len(available.AvailableUserIDs)) - for _, robot := range result.Robots { + return availableRoomRobotsFromGamePool(result.Robots, available.AvailableUserIDs, owners), nil +} + +func availableRoomRobotsFromGamePool(robots []robotclient.Robot, availableUserIDs []int64, owners map[int64]RoomOwner) []AvailableRoomRobot { + allowed := make(map[int64]bool, len(availableUserIDs)) + for _, userID := range availableUserIDs { + allowed[userID] = true + } + items := make([]AvailableRoomRobot, 0, len(availableUserIDs)) + for _, robot := range robots { if !allowed[robot.UserID] { continue } @@ -129,7 +132,7 @@ func (s *Service) ListAvailableRoomRobots(ctx context.Context) ([]AvailableRoomR User: owners[robot.UserID], }) } - return items, nil + return items } func (s *Service) CreateRobotRoom(ctx context.Context, req createRobotRoomRequest, actor shared.Actor) (RobotRoom, error) { diff --git a/server/admin/internal/modules/roomadmin/service_test.go b/server/admin/internal/modules/roomadmin/service_test.go index 544d04a2..b5938713 100644 --- a/server/admin/internal/modules/roomadmin/service_test.go +++ b/server/admin/internal/modules/roomadmin/service_test.go @@ -3,6 +3,8 @@ package roomadmin import ( "errors" "testing" + + "hyapp-admin-server/internal/integration/robotclient" ) func TestNormalizeRoomListSortKeepsOnlyContributionSort(t *testing.T) { @@ -74,3 +76,26 @@ func TestRobotRoomProfileFromOwnerRequiresNameAndAvatar(t *testing.T) { }) } } + +func TestAvailableRoomRobotsFromGamePoolKeepsOnlyRoomAvailableUsers(t *testing.T) { + robots := []robotclient.Robot{ + {UserID: 101, Status: "active", LastUsedAtMS: 1000, UsedCount: 2}, + {UserID: 102, Status: "active", LastUsedAtMS: 2000, UsedCount: 3}, + {UserID: 103, Status: "active", LastUsedAtMS: 3000, UsedCount: 4}, + } + owners := map[int64]RoomOwner{ + 101: {UserID: "101", Username: "Robot A"}, + 103: {UserID: "103", Username: "Robot C"}, + } + + items := availableRoomRobotsFromGamePool(robots, []int64{103, 101}, owners) + if len(items) != 2 { + t.Fatalf("available robots length mismatch: got %d want 2", len(items)) + } + if items[0].UserID != "101" || items[0].User.Username != "Robot A" || items[0].UsedCount != 2 { + t.Fatalf("first available robot mismatch: %+v", items[0]) + } + if items[1].UserID != "103" || items[1].User.Username != "Robot C" || items[1].LastUsedAtMS != 3000 { + t.Fatalf("second available robot mismatch: %+v", items[1]) + } +}