修复旧下麦命令恢复阻断

This commit is contained in:
zhx 2026-07-07 20:05:02 +08:00
parent 967e1912f3
commit d9bfb014d8
2 changed files with 43 additions and 1 deletions

View File

@ -240,7 +240,12 @@ func replay(current *state.RoomState, cmd command.Command, committedAtMS int64)
case *command.MicDown:
seat, exists := current.SeatByUser(typed.TargetUserID)
if !exists {
return fmt.Errorf("mic_down replay target is not on seat: %d", typed.TargetUserID)
// 历史日志里存在下麦/离房/超时清理重复到达后仍被标记 replayable 的旧下麦命令;当前状态已无麦位时恢复保持 no-op。
return nil
}
if typed.MicSessionID != "" && seat.MicSessionID != typed.MicSessionID {
// 超时下麦会携带 mic_session_id如果用户已快速重上麦旧命令不能释放新会话。
return nil
}
index := current.SeatIndex(seat.SeatNo)
current.ClearMicSession(index)

View File

@ -42,6 +42,43 @@ func TestReplayMicHeartbeatSkipsStaleSession(t *testing.T) {
}
}
func TestReplayMicDownSkipsStaleMissingSeat(t *testing.T) {
current := state.NewRoomState("room-replay-stale-down", 7251, 8, "voice")
current.OnlineUsers[7251] = &state.RoomUserState{UserID: 7251, Role: "owner", JoinedAtMS: 1000, LastSeenAtMS: 1000}
if err := replay(current, &command.MicDown{
Base: command.Base{ActorID: 7251, Room: current.RoomID, SentAtMS: 2000},
TargetUserID: 7251,
MicSessionID: "mic-old",
Reason: "publish_timeout",
}, 2000); err != nil {
t.Fatalf("stale mic down without seat must be skipped: %v", err)
}
if current.Version != 0 {
t.Fatalf("stale mic down must not mutate version, got %d", current.Version)
}
}
func TestReplayMicDownSkipsStaleSession(t *testing.T) {
current := state.NewRoomState("room-replay-stale-down-session", 7261, 8, "voice")
current.OnlineUsers[7261] = &state.RoomUserState{UserID: 7261, Role: "owner", JoinedAtMS: 1000, LastSeenAtMS: 1000}
current.MicSeats[0].UserID = 7261
current.MicSeats[0].PublishState = state.MicPublishPublishing
current.MicSeats[0].MicSessionID = "mic-new"
if err := replay(current, &command.MicDown{
Base: command.Base{ActorID: 7261, Room: current.RoomID, SentAtMS: 2000},
TargetUserID: 7261,
MicSessionID: "mic-old",
Reason: "publish_timeout",
}, 2000); err != nil {
t.Fatalf("stale mic down for replaced session must be skipped: %v", err)
}
if current.Version != 0 || current.MicSeats[0].UserID != 7261 || current.MicSeats[0].MicSessionID != "mic-new" {
t.Fatalf("stale mic down must not clear replaced session, version=%d seat=%+v", current.Version, current.MicSeats[0])
}
}
func TestReplayConfirmMicPublishingSkipsStaleMissingSeat(t *testing.T) {
current := state.NewRoomState("room-replay-stale-confirm", 7301, 8, "voice")
current.OnlineUsers[7301] = &state.RoomUserState{UserID: 7301, Role: "owner", JoinedAtMS: 1000, LastSeenAtMS: 1000}