diff --git a/pages/contract/index.vue b/pages/contract/index.vue index 138f6f8..f913c79 100644 --- a/pages/contract/index.vue +++ b/pages/contract/index.vue @@ -560,7 +560,7 @@

⚠️ 积分不足,当前余额 {{ pointsBalance }},需要 {{ reviewPoints }} 积分且审查后余额需保留至少 200 积分。 - 去充值 + 去充值

@@ -1712,6 +1712,17 @@ return html; }, // ===== 积分相关 ===== + // 积分不足时跳转到「积分管理」并刷新余额与明细 + goRecharge() { + this.menuIndex = '6'; + this.isSign = false; + this.isSignIntro = false; + this.isSignLog = false; + this.isSignFlow = false; + this.isSignTips = false; + this.getPointsBalance(); + this.getPointsLog(); + }, async getPointsBalance() { try { const res = await vk.callFunction({ diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js index c43baba..23c0e5e 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js @@ -25,22 +25,52 @@ module.exports = { } }); - let answer = ''; + // 扣子 v3 流式事件说明: + // conversation.message.delta —— 增量片段(应当拼接) + // conversation.message.completed —— 该条消息的「完整内容」(不能再次拼接,否则重复) + // 因此必须按事件类型区分,只累加 delta;completed 仅用于兜底。 let conversationId = ''; + const msgMap = new Map(); // messageId -> { text } + let curEvent = ''; const text = res.data || ''; const lines = text.split('\n'); for (const line of lines) { + if (line.startsWith('event:')) { + curEvent = line.slice(6).trim(); + continue; + } if (!line.startsWith('data:')) continue; const d = line.slice(5).trim(); if (d === '[DONE]' || d === '"[DONE]"') continue; try { const p = JSON.parse(d); if (p.conversation_id) conversationId = p.conversation_id; - if (p.type === 'answer' && p.content) { - answer += p.content; // 拼接,不是覆盖 + // 只取正文,忽略 verbose(工具调用日志)等其他类型 + if (p.type !== 'answer' || typeof p.content !== 'string') continue; + const key = p.id || '__default__'; + const rec = msgMap.get(key) || { text: '' }; + + if (curEvent === 'conversation.message.delta') { + // 防御:万一上游发的是累计内容而非增量,用长度+前缀判断避免重复 + if (p.content.length > rec.text.length && p.content.indexOf(rec.text) === 0) { + rec.text = p.content; + } else { + rec.text += p.content; + } + msgMap.set(key, rec); + } else if (curEvent === 'conversation.message.completed') { + // 已有 delta 拼出的内容就丢掉 completed,避免重复;没有则用它兜底 + if (!rec.text && p.content) { + rec.text = p.content; + msgMap.set(key, rec); + } } } catch (e) { /* 跳过 */ } } + + // 按消息首次出现顺序拼接(多段消息之间自带换行,直接相连) + let answer = ''; + for (const rec of msgMap.values()) answer += rec.text; if (!answer) answer = '暂时无法回复,请拨打 400-686-5559 咨询'; return { diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js index 86b3b98..c1c27f7 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js @@ -73,8 +73,13 @@ module.exports = { if (p.conversation_id) conversationId = p.conversation_id; // 处理回答内容 + // 注意:conversation.message.completed 携带的是该条消息的完整内容, + // 若前面已经通过 delta 拼过,再拼一次就会重复,这里做去重 if (p.type === 'answer' && p.content) { - answer += p.content; + const isDup = eventName === 'conversation.message.completed' && answer.indexOf(p.content) >= 0; + if (!isDup) { + answer += p.content; + } // 每积累 200 字更新一次数据库(前端轮询能看到进度) if (answer.length - lastUpdateLen >= 200) {