fix(collab): room_closed 无条件归位、断线重连补状态、学生 socket 重连迁移
Critical:room_closed 不再按 reason 挑着重置 helpStatus——学生自己的 socket 发送失败时服务端删了请求却发不出任何纠正帧,store 会卡在陈旧的 active/pending 上出不来。现在一律先归 idle,老师掉线那条路径服务端会紧跟着补一条 help_status:pending,同一条连接消息严格按序到达,不会被这次重置抢跑。 Critical:断线重连没有补状态。前端 CollabWebSocket 加 onConnected 钩子, 每次连接建立(含重连)都清空本地 requests/helpStatus/room,等服务端补发; 后端 handleCollabOpen 对非老师的重连方,如果这个账号名下还有请求, 补发对应的 help_status(pending 带重算的 queueAhead,active 带 teacherName)。 Critical:重连后请求仍绑在旧 socket 上,导致 handleHelpCancel 的归属校验 认不出新连接、后续通知也写进死连接。handleCollabOpen 里把请求和(如果有) 房间迁移到新 socket,并清掉旧 socket 的 roomOwnerId,防止它稍后的 close 反过来拆掉刚迁移出去的房间。 Minor:disconnect() 补齐 queueAhead/teacherName/notice 的重置,不留陈旧值。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K1d8B3f4SXJwDvUY625eQd
This commit is contained in:
@@ -736,6 +736,7 @@ export interface CollabMessage extends WebSocketMessage {
|
||||
*/
|
||||
export class CollabWebSocket extends BaseWebSocket<CollabMessage> {
|
||||
private binaryHandler: ((data: ArrayBuffer) => void) | null = null
|
||||
private connectHandler: (() => void) | null = null
|
||||
|
||||
constructor() {
|
||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"
|
||||
@@ -746,7 +747,21 @@ export class CollabWebSocket extends BaseWebSocket<CollabMessage> {
|
||||
this.binaryHandler = handler
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次连接**建立**都触发,含重连 —— 不止首次 connect()。用来在重连瞬间
|
||||
* 清掉本地缓存的求助/房间状态:旧连接期间的 pending/active 可能早就过时了,
|
||||
* 服务端会在 handleCollabOpen 里紧接着补发 requests(老师)或 help_status
|
||||
* (还在排队/协作中的学生),补发落地前先归零,好过让过时状态活过一次重连。
|
||||
*/
|
||||
setConnectHandler(handler: (() => void) | null) {
|
||||
this.connectHandler = handler
|
||||
}
|
||||
|
||||
protected override onBinary(data: ArrayBuffer) {
|
||||
this.binaryHandler?.(data)
|
||||
}
|
||||
|
||||
protected override onConnected() {
|
||||
this.connectHandler?.()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,9 +88,15 @@ export const useCollabStore = defineStore("collab", () => {
|
||||
}
|
||||
return
|
||||
case "room_closed":
|
||||
// 不管 reason 一律先归位到 idle:学生这一侧的 socket 发送失败时,
|
||||
// 服务端把它从请求表里摘掉却**发不出**任何纠正性的 help_status
|
||||
// (那正是刚失败的那条 socket),不这样兜底 store 会卡在陈旧的
|
||||
// active/pending 上再也回不来。老师掉线的情况服务端会紧接着另发一条
|
||||
// help_status:pending——teardownRoom 里 room_closed 先发、
|
||||
// requeueAfterTeacherGone 后发,同一条连接上消息严格按发送顺序到达,
|
||||
// 这里先归零,那条 pending 补发会立刻把它纠正回来,不会被这次重置盖掉
|
||||
room.value = null
|
||||
// 老师掉线时服务端会另发一条 help_status:pending,这里不抢着改学生状态
|
||||
if (data.reason === "done") helpStatus.value = "idle"
|
||||
helpStatus.value = "idle"
|
||||
notice.value =
|
||||
data.reason === "peer_offline" ? "对方已断开连接" : "协作已结束"
|
||||
return
|
||||
@@ -102,6 +108,19 @@ export const useCollabStore = defineStore("collab", () => {
|
||||
|
||||
ws.addHandler(handleMessage)
|
||||
|
||||
// 每次连接**建立**都清一遍本地状态,不止首次 connect() —— 重连(掉线重连、
|
||||
// API 重启后的自动重连)同样会触发。旧连接期间的 pending/active/requests
|
||||
// 可能早就过时了:老师端等服务端在 handleCollabOpen 里重新推 requests 补齐;
|
||||
// 学生端等服务端补发的 help_status 补齐(真在排队/协作中会被立刻纠正回来),
|
||||
// 不该让上一条连接的陈旧状态越过重连活下来
|
||||
ws.setConnectHandler(() => {
|
||||
requests.value = []
|
||||
helpStatus.value = "idle"
|
||||
queueAhead.value = 0
|
||||
teacherName.value = ""
|
||||
room.value = null
|
||||
})
|
||||
|
||||
function connect() {
|
||||
ws.connect()
|
||||
}
|
||||
@@ -110,7 +129,10 @@ export const useCollabStore = defineStore("collab", () => {
|
||||
ws.disconnect()
|
||||
requests.value = []
|
||||
helpStatus.value = "idle"
|
||||
queueAhead.value = 0
|
||||
teacherName.value = ""
|
||||
room.value = null
|
||||
notice.value = ""
|
||||
}
|
||||
|
||||
function requestHelp(problemId: string) {
|
||||
|
||||
Reference in New Issue
Block a user