2026-06-15 23:53:30 +08:00

127 lines
4.5 KiB
Go

package service
import (
"context"
"time"
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
roomv1 "hyapp.local/api/proto/room/v1"
"hyapp/pkg/tencentim"
"hyapp/pkg/xerr"
"hyapp/services/room-service/internal/room/command"
"hyapp/services/room-service/internal/room/outbox"
"hyapp/services/room-service/internal/room/rank"
"hyapp/services/room-service/internal/room/state"
)
// RobotMicUpInput 是内部机器人编排器占麦输入;调用方必须只传 robot-service 登记过的机器人用户。
type RobotMicUpInput struct {
Meta *roomv1.RequestMeta
TargetUserID int64
SeatNo int32
}
// RobotVirtualMicUp 把机器人放到静音虚拟麦位,不要求真实 RTC 推流。
func (s *Service) RobotVirtualMicUp(ctx context.Context, input RobotMicUpInput) (*roomv1.MicUpResponse, error) {
ctx = contextFromMeta(ctx, input.Meta)
cmd := command.RobotMicUp{
Base: baseFromMeta(input.Meta),
TargetUserID: input.TargetUserID,
SeatNo: input.SeatNo,
}
if cmd.TargetUserID <= 0 {
cmd.TargetUserID = cmd.ActorUserID()
}
cmd.MicSessionID = micSessionIDForCommand(cmd.RoomID(), cmd.ID())
result, err := s.mutateRoom(ctx, cmd, true, func(now time.Time, current *state.RoomState, _ *rank.LocalRank) (mutationResult, []outbox.Record, error) {
if err := requireActiveRoom(current); err != nil {
return mutationResult{}, nil, err
}
if _, exists := current.OnlineUsers[cmd.TargetUserID]; !exists {
return mutationResult{}, nil, xerr.New(xerr.NotFound, "robot not in room")
}
if _, exists := current.SeatByUser(cmd.TargetUserID); exists {
return mutationResult{}, nil, xerr.New(xerr.Conflict, "robot already on seat")
}
index := current.SeatIndex(cmd.SeatNo)
if index < 0 {
return mutationResult{}, nil, xerr.New(xerr.InvalidArgument, "seat does not exist")
}
if current.MicSeats[index].Locked {
return mutationResult{}, nil, xerr.New(xerr.PermissionDenied, "seat is locked")
}
if current.MicSeats[index].UserID != 0 {
return mutationResult{}, nil, xerr.New(xerr.Conflict, "seat is occupied")
}
current.Version++
current.MicSeats[index].UserID = cmd.TargetUserID
current.MicSeats[index].PublishState = state.MicPublishPublishing
current.MicSeats[index].MicSessionID = cmd.MicSessionID
current.MicSeats[index].PublishDeadlineMS = 0
current.MicSeats[index].MicSessionRoomVersion = current.Version
current.MicSeats[index].LastPublishEventTimeMS = now.UnixMilli()
current.MicSeats[index].MicHeartbeatAtMS = now.UnixMilli()
current.MicSeats[index].MicMuted = true
seatStatus := current.MicSeats[index].SeatStatus()
targetGiftValue := roomUserGiftValue(current, cmd.TargetUserID)
micEvent, err := outbox.Build(current.RoomID, "RoomMicChanged", current.Version, now, &roomeventsv1.RoomMicChanged{
ActorUserId: cmd.ActorUserID(),
TargetUserId: cmd.TargetUserID,
FromSeat: 0,
ToSeat: cmd.SeatNo,
Action: "robot_up",
MicSessionId: cmd.MicSessionID,
PublishState: state.MicPublishPublishing,
PublishDeadlineMs: 0,
SeatStatus: seatStatus,
TargetGiftValue: targetGiftValue,
})
if err != nil {
return mutationResult{}, nil, err
}
return mutationResult{
snapshot: current.ToProto(),
seatNo: cmd.SeatNo,
micSessionID: cmd.MicSessionID,
publishDeadlineMS: 0,
syncEvent: &tencentim.RoomEvent{
EventID: micEvent.EventID,
RoomID: current.RoomID,
EventType: "room_mic_publish_confirmed",
ActorUserID: cmd.ActorUserID(),
TargetUserID: cmd.TargetUserID,
SeatNo: cmd.SeatNo,
RoomVersion: current.Version,
Attributes: map[string]string{
"action": "robot_up",
"publish_state": state.MicPublishPublishing,
"mic_session_id": cmd.MicSessionID,
"seat_status": seatStatus,
"mic_muted": "true",
"target_gift_value": int64String(targetGiftValue),
},
},
}, []outbox.Record{micEvent}, nil
})
if err != nil {
return nil, err
}
if result.micSessionID == "" {
if seat := protoSeatByUser(result.snapshot, cmd.TargetUserID); seat != nil {
result.seatNo = seat.GetSeatNo()
result.micSessionID = seat.GetMicSessionId()
}
}
return &roomv1.MicUpResponse{
Result: commandResult(result.applied, result.snapshot.GetVersion(), s.clock.Now()),
SeatNo: result.seatNo,
Room: result.snapshot,
MicSessionId: result.micSessionID,
PublishDeadlineMs: 0,
}, nil
}