P1-6: WS死路由清理+结单确认断链修复 - 删ai_reply_chunk/quiz_diagnostic_answer死路由 + pending_close_request改为resolve_confirm对齐后端 + 挂载ResolveConfirmCard(修复坐席结单员工端不可见的功能断链)
This commit is contained in:
@@ -86,6 +86,16 @@
|
|||||||
:key="msg.message_id"
|
:key="msg.message_id"
|
||||||
:msg="msg"
|
:msg="msg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 坐席结单确认卡片(v4.0 P1-6 修复:WS resolve_confirm → pendingCloseRequest → 挂载此卡片) -->
|
||||||
|
<ResolveConfirmCard
|
||||||
|
v-if="store.pendingCloseRequest"
|
||||||
|
:resolve-summary="store.pendingCloseRequest.resolve_summary"
|
||||||
|
:conversation-id="store.pendingCloseRequest.conversation_id"
|
||||||
|
@confirmed="store.clearPendingCloseRequest()"
|
||||||
|
@rejected="store.clearPendingCloseRequest()"
|
||||||
|
@timeout="store.clearPendingCloseRequest()"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -139,6 +149,7 @@ import InputBar from './InputBar.vue'
|
|||||||
// 2026-07-12 改造:不再 import CallAgentModal(弹窗动画已移除)
|
// 2026-07-12 改造:不再 import CallAgentModal(弹窗动画已移除)
|
||||||
// import CallAgentModal from './CallAgentModal.vue'
|
// import CallAgentModal from './CallAgentModal.vue'
|
||||||
import TroubleshootFlow from './TroubleshootFlow.vue'
|
import TroubleshootFlow from './TroubleshootFlow.vue'
|
||||||
|
import ResolveConfirmCard from './ResolveConfirmCard.vue'
|
||||||
import ParticipantStrip from './ParticipantStrip.vue'
|
import ParticipantStrip from './ParticipantStrip.vue'
|
||||||
import ParticipantList from './ParticipantList.vue'
|
import ParticipantList from './ParticipantList.vue'
|
||||||
import EvaluationDialog from './EvaluationDialog.vue'
|
import EvaluationDialog from './EvaluationDialog.vue'
|
||||||
|
|||||||
@@ -387,14 +387,10 @@ export function useH5WebSocket() {
|
|||||||
break
|
break
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// AI 回复流式推送(改造方案 A:发送瞬时返回,AI 经 WS 推回)
|
// AI 回复(blocking 模式:发送瞬时返回,AI 经 WS 推回)
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
case 'ai_reply_chunk':
|
// v4.0 P1-6:case 'ai_reply_chunk' 已删除(后端从未推送流式 chunk,
|
||||||
// 打字机:逐 chunk 累积到临时 AI 气泡
|
// blocking 模式下 AI 回复只走 ai_reply 完整消息)
|
||||||
if (msg.data) {
|
|
||||||
store.handleAiReplyChunk(msg.data)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'ai_reply':
|
case 'ai_reply':
|
||||||
// AI 终态:用真实消息替换打字机气泡
|
// AI 终态:用真实消息替换打字机气泡
|
||||||
@@ -474,10 +470,10 @@ export function useH5WebSocket() {
|
|||||||
// ==================================================================
|
// ==================================================================
|
||||||
// 坐席发起结单 — 员工端弹出确认卡片
|
// 坐席发起结单 — 员工端弹出确认卡片
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
case 'pending_close_request':
|
case 'resolve_confirm':
|
||||||
// 坐席发起了结单,会话进入 pending_close 状态
|
// 坐席发起了结单,会话进入 pending_close 状态
|
||||||
// 做什么:在消息流中插入 ResolveConfirmCard 组件
|
// v4.0 P1-6 修复:后端实际推送类型是 resolve_confirm(closing_service._push_resolve_confirm),
|
||||||
// 员工需在 5 分钟内确认或拒绝,否则系统自动关闭
|
// 原 case 'pending_close_request' 从未命中导致结单确认卡片不显示(功能断链)
|
||||||
if (msg.data) {
|
if (msg.data) {
|
||||||
store.handlePendingCloseRequest(msg.data)
|
store.handlePendingCloseRequest(msg.data)
|
||||||
}
|
}
|
||||||
@@ -486,13 +482,7 @@ export function useH5WebSocket() {
|
|||||||
// ==================================================================
|
// ==================================================================
|
||||||
// 诊断答题答案附加到会话上下文
|
// 诊断答题答案附加到会话上下文
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
case 'quiz_diagnostic_answer':
|
// v4.0 P1-6:case 'quiz_diagnostic_answer' 已删除(后端从未推送此类型)
|
||||||
// 诊断题答案已附加到会话上下文
|
|
||||||
// 做什么:在消息流中显示一条系统消息,告知用户诊断信息已传递给坐席
|
|
||||||
if (msg.data) {
|
|
||||||
store.handleQuizDiagnosticAnswer(msg.data)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn(`[H5 WS] 未知消息类型: ${msg.type}`)
|
console.warn(`[H5 WS] 未知消息类型: ${msg.type}`)
|
||||||
|
|||||||
@@ -1067,39 +1067,6 @@ export const useConversationStore = defineStore('conversation', () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 WS 流式 AI 回复 chunk(打字机效果)
|
* 处理 WS 流式 AI 回复 chunk(打字机效果)
|
||||||
* 做什么:首个 chunk 时创建临时 AI 气泡,后续 chunk 累积到该气泡 content
|
|
||||||
* 为什么:后端 AI 推理(Dify)流式返回,逐字推送以获得打字机体验,
|
|
||||||
* 避免整段等待(原同步方案卡"发送中"3~15s)
|
|
||||||
*
|
|
||||||
* @param data - { conversation_id, chunk }
|
|
||||||
*/
|
|
||||||
function handleAiReplyChunk(data: { conversation_id: string; chunk: string }): void {
|
|
||||||
// 仅处理当前会话,避免串台
|
|
||||||
if (currentConversation.value?.conversation_id !== data.conversation_id) return
|
|
||||||
if (!data.chunk) return
|
|
||||||
|
|
||||||
// 首个 chunk:创建占位气泡(不设置 status,避免误显示"发送中")
|
|
||||||
if (!streamingAiMessageId.value) {
|
|
||||||
const tempId = `ai_stream_${data.conversation_id}_${Date.now()}`
|
|
||||||
streamingAiMessageId.value = tempId
|
|
||||||
messages.value.push({
|
|
||||||
message_id: tempId,
|
|
||||||
conversation_id: data.conversation_id,
|
|
||||||
message_type: 'ai',
|
|
||||||
msg_type: 'text',
|
|
||||||
content: '',
|
|
||||||
sender_name: 'Duckula(达寇拉)',
|
|
||||||
created_at: new Date().toISOString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 累积 chunk(Vue3 ref 深层响应式,直接改属性即可触发重渲染 → 打字机)
|
|
||||||
const idx = messages.value.findIndex(m => m.message_id === streamingAiMessageId.value)
|
|
||||||
if (idx !== -1) {
|
|
||||||
messages.value[idx].content += data.chunk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 WS AI 回复终态
|
* 处理 WS AI 回复终态
|
||||||
* 做什么:用真实 AI 消息(含 DB message_id)替换打字机占位气泡,
|
* 做什么:用真实 AI 消息(含 DB message_id)替换打字机占位气泡,
|
||||||
@@ -1457,12 +1424,15 @@ export const useConversationStore = defineStore('conversation', () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理坐席结单请求
|
* 处理坐席结单请求
|
||||||
* WS event: pending_close_request
|
* WS event: resolve_confirm(v4.0 P1-6 修复:对齐后端 closing_service 实际推送类型)
|
||||||
|
* 后端 payload: { conversation_id, agent_id, resolve_summary, auto_close_minutes, timestamp }
|
||||||
*/
|
*/
|
||||||
function handlePendingCloseRequest(data: {
|
function handlePendingCloseRequest(data: {
|
||||||
conversation_id: string
|
conversation_id: string
|
||||||
resolve_summary: string
|
resolve_summary: string
|
||||||
agent_name: string
|
agent_id?: string
|
||||||
|
agent_name?: string
|
||||||
|
auto_close_minutes?: number
|
||||||
}): void {
|
}): void {
|
||||||
if (currentConversation.value?.conversation_id !== data.conversation_id) return
|
if (currentConversation.value?.conversation_id !== data.conversation_id) return
|
||||||
|
|
||||||
@@ -1491,29 +1461,6 @@ export const useConversationStore = defineStore('conversation', () => {
|
|||||||
messages.value.push(systemMsg)
|
messages.value.push(systemMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理诊断答题答案附加到上下文
|
|
||||||
* WS event: quiz_diagnostic_answer
|
|
||||||
*/
|
|
||||||
function handleQuizDiagnosticAnswer(data: {
|
|
||||||
question: string
|
|
||||||
selected_answer: string
|
|
||||||
conversation_id: string
|
|
||||||
}): void {
|
|
||||||
// 添加系统消息,告知用户诊断信息已传递
|
|
||||||
const systemMsg: Message = {
|
|
||||||
message_id: `quiz_diag_${data.conversation_id}_${Date.now()}`,
|
|
||||||
conversation_id: data.conversation_id,
|
|
||||||
message_type: 'system',
|
|
||||||
msg_type: 'text',
|
|
||||||
content: `📝 诊断信息已传递给坐席:${data.question} → ${data.selected_answer}`,
|
|
||||||
sender_name: '系统',
|
|
||||||
created_at: new Date().toISOString(),
|
|
||||||
status: 'sent',
|
|
||||||
}
|
|
||||||
messages.value.push(systemMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 清除结单请求(确认或拒绝后调用) */
|
/** 清除结单请求(确认或拒绝后调用) */
|
||||||
function clearPendingCloseRequest(): void {
|
function clearPendingCloseRequest(): void {
|
||||||
pendingCloseRequest.value = null
|
pendingCloseRequest.value = null
|
||||||
@@ -1710,8 +1657,7 @@ export const useConversationStore = defineStore('conversation', () => {
|
|||||||
// WS-06 消息去重(与 agent 端对齐,WebSocket 接入时使用)
|
// WS-06 消息去重(与 agent 端对齐,WebSocket 接入时使用)
|
||||||
handleNewMessage,
|
handleNewMessage,
|
||||||
|
|
||||||
// 流式 AI 回复(改造方案 A:打字机)
|
// 流式 AI 回复(改造方案 A:blocking 模式,v4.0 P1-6 删除 handleAiReplyChunk)
|
||||||
handleAiReplyChunk,
|
|
||||||
handleAiReply,
|
handleAiReply,
|
||||||
handleAiReplyFailed,
|
handleAiReplyFailed,
|
||||||
cancelStreamingBubble,
|
cancelStreamingBubble,
|
||||||
@@ -1737,7 +1683,6 @@ export const useConversationStore = defineStore('conversation', () => {
|
|||||||
handleQueuePositionUpdate,
|
handleQueuePositionUpdate,
|
||||||
handleConversationResolved,
|
handleConversationResolved,
|
||||||
handlePendingCloseRequest,
|
handlePendingCloseRequest,
|
||||||
handleQuizDiagnosticAnswer,
|
|
||||||
clearPendingCloseRequest,
|
clearPendingCloseRequest,
|
||||||
clearResolvedInfo,
|
clearResolvedInfo,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user