From a8ceeb5b1133e167cceb31ed59878d9e9164afdc Mon Sep 17 00:00:00 2001 From: Shijia Huang Date: Wed, 25 Mar 2026 21:13:08 +1100 Subject: [PATCH] fixed the bug --- backend/src/constants/prompts.ts | 2 +- miniprogram/packageB/pages/chat-room/index.ts | 98 +++++++++++++------ 2 files changed, 67 insertions(+), 33 deletions(-) diff --git a/backend/src/constants/prompts.ts b/backend/src/constants/prompts.ts index afcf7f3..8f59f12 100644 --- a/backend/src/constants/prompts.ts +++ b/backend/src/constants/prompts.ts @@ -39,7 +39,7 @@ export const JUDGMENT_SYSTEM_PROMPT = `你是"清汤大老爷",一位断案风 - 语气要像一个慈祥但毒舌的长辈 ### 4. 惩罚任务 (punishmentTask) -- 为责任占比较高的一方(败诉方)设计一个惩罚任务 +- 为责任占比较高的一方(败诉方)设计一个惩罚任务 Important:责任占比较高的一方为败诉方 - 任务要有趣但不过分,适合情侣/朋友之间互动 - 任务要具体可执行,10-20字左右 - 任务要结合双方争吵的内容,有针对性 diff --git a/miniprogram/packageB/pages/chat-room/index.ts b/miniprogram/packageB/pages/chat-room/index.ts index f181ed7..8ae28b0 100644 --- a/miniprogram/packageB/pages/chat-room/index.ts +++ b/miniprogram/packageB/pages/chat-room/index.ts @@ -104,6 +104,12 @@ type TSpeakerFinal = Pick; type TSpeakerLive = Pick; +type TPendingAsrAction = + | 'sendTurnEnd' + | 'sendTurnEndAndNotify' + | 'showSwitchNotification' + | 'redirect'; + interface IChatRoomCustomOption extends WechatMiniprogram.Page.CustomOption { timerId: number | null; listenerHintTimerId: number | null; @@ -117,7 +123,7 @@ interface IChatRoomCustomOption extends WechatMiniprogram.Page.CustomOption { currentSpeakerUserId: string; // UserId of current speaker isSelfFirstSpeaker: boolean; // Whether self is first speaker (SpeakerA) // ASR 完成后的待执行动作(用于确保录音结果先于控制消息发出) - pendingAfterAsrComplete: 'sendTurnEnd' | 'redirect' | null; + pendingAfterAsrComplete: TPendingAsrAction | null; pendingAfterAsrCompleteTimerId: number | null; } @@ -675,10 +681,8 @@ Page({ // 执行挂起的动作(录音结果已全部发送,现在安全地发控制消息或跳转) const pending = this.pendingAfterAsrComplete; this.pendingAfterAsrComplete = null; - if (pending === 'sendTurnEnd') { - this.sendSpeechTurnEnd(); - } else if (pending === 'redirect') { - this.doRedirectToVerdictWaiting(); + if (pending) { + this.executePendingAction(pending); } }; @@ -857,22 +861,15 @@ Page({ // 根据当前阶段直接计算 liveKey(同步,无竞态问题) const liveKey: 'speakerALive' | 'speakerBLive' = phase === EPhase.SpeakerA ? 'speakerALive' : 'speakerBLive'; + const wasRecording: boolean = this.data.isRecording; - // 强制停止语音识别,并在 ASR 完成后发送控制消息(确保录音结果先到后端) - if (this.asrManager && this.data.isRecording) { + // 强制停止语音识别,并在 ASR 完成后发送控制消息并显示切换提示(确保录音结果先到后端) + if (this.asrManager && wasRecording) { if (canSpeak) { - // 先设置 pending 再 stop,避免回调比赋值先到 - this.pendingAfterAsrComplete = 'sendTurnEnd'; - // 兜底:3 秒内 OnRecognitionComplete 未触发则直接发送 - this.pendingAfterAsrCompleteTimerId = setTimeout(() => { - if (this.pendingAfterAsrComplete === 'sendTurnEnd') { - this.pendingAfterAsrComplete = null; - this.sendSpeechTurnEnd(); - } - this.pendingAfterAsrCompleteTimerId = null; - }, 3000) as unknown as number; + this.stopAsrAndDefer('sendTurnEndAndNotify'); + } else { + this.asrManager.stop(); } - this.asrManager.stop(); } else if (canSpeak) { // 未在录音,直接发送 this.sendSpeechTurnEnd(); @@ -912,8 +909,10 @@ Page({ this.timerId = null; } - // 显示"下一位"切换提示 - this.showSwitchNotification(); + // 显示"下一位"切换提示(录音中则等 OnRecognitionComplete 确认最后一段文本已发出) + if (!(this.asrManager && wasRecording)) { + void this.showSwitchNotification(); + } this.setData({ phase: nextPhase, @@ -935,6 +934,45 @@ Page({ } }, + /** + * 强制停止 ASR 并将指定动作延迟到 OnRecognitionComplete 后执行 + * 确保录音结果先于控制消息/跳转发出。 + * 兜底:3 秒内回调未触发时直接执行该动作。 + */ + stopAsrAndDefer(action: TPendingAsrAction): void { + // 先赋值再 stop,避免回调先于赋值到达 + this.pendingAfterAsrComplete = action; + this.pendingAfterAsrCompleteTimerId = setTimeout(() => { + if (this.pendingAfterAsrComplete === action) { + this.pendingAfterAsrComplete = null; + this.executePendingAction(action); + } + this.pendingAfterAsrCompleteTimerId = null; + }, 3000) as unknown as number; + this.asrManager.stop(); + }, + + /** + * 执行 pendingAfterAsrComplete 挂起的动作 + */ + executePendingAction(action: TPendingAsrAction): void { + switch (action) { + case 'sendTurnEnd': + this.sendSpeechTurnEnd(); + break; + case 'sendTurnEndAndNotify': + this.sendSpeechTurnEnd(); + void this.showSwitchNotification(); + break; + case 'showSwitchNotification': + void this.showSwitchNotification(); + break; + case 'redirect': + this.doRedirectToVerdictWaiting(); + break; + } + }, + /** * 发送 SPEECH_TURN_END 给服务器 * 当本方发言轮次结束时调用 @@ -980,8 +1018,13 @@ Page({ const selfUserId: string = getApp().globalData.selfUserId; const nextCanSpeak: boolean = this.currentSpeakerUserId === selfUserId; - // 显示"下一位"切换提示 - this.showSwitchNotification(); + // 显示"下一位"切换提示(录音中则等 OnRecognitionComplete 确认最后一段文本已发出) + const wasRecording: boolean = this.data.isRecording; + if (this.asrManager && wasRecording) { + this.stopAsrAndDefer('showSwitchNotification'); + } else { + void this.showSwitchNotification(); + } this.setData({ phase: EPhase.SpeakerB, @@ -1038,16 +1081,7 @@ Page({ if (this.asrManager && wasRecording) { // 等 OnRecognitionComplete 回调确认最后一段文本已发出,再跳转 - this.pendingAfterAsrComplete = 'redirect'; - // 兜底:3 秒内未回调则直接跳转 - this.pendingAfterAsrCompleteTimerId = setTimeout(() => { - if (this.pendingAfterAsrComplete === 'redirect') { - this.pendingAfterAsrComplete = null; - this.doRedirectToVerdictWaiting(); - } - this.pendingAfterAsrCompleteTimerId = null; - }, 3000) as unknown as number; - this.asrManager.stop(); + this.stopAsrAndDefer('redirect'); } else { // 未在录音,短暂展示遮罩后跳转 setTimeout(() => {