房间机器人

This commit is contained in:
zhx 2026-06-17 11:30:18 +08:00
parent 72e1fb920d
commit f465c12d39
3 changed files with 98 additions and 27 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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])
}
}