diff --git a/common/js/ai-service.js b/common/js/ai-service.js index 7ebc9f6..4b68cea 100644 --- a/common/js/ai-service.js +++ b/common/js/ai-service.js @@ -914,8 +914,12 @@ export default { */ async clearConversation(conversationId) { try { + // PR-AI-CLEAR-CONV-FIX (2026-09-19): URL 必须用 camelCase + // ThinkPHP 后端方法名 = clearConversation, URL 不做 kebab→camel 转换 + // 之前 'clear-conversation' 拼出 /api/kefu/clear-conversation → 404 method not exists + // 改 'clearConversation' → /api/kefu/clearConversation → 200 const response = await http.sendRequest({ - url: resolveApiUrl('clear-conversation'), + url: resolveApiUrl('clearConversation'), data: { agent_id: getAgentId(), conversation_id: conversationId, @@ -958,11 +962,16 @@ export default { return true; // #endif // #ifdef H5 - // PR-ELICIT-RESPONSE-HTTP (2026-09-18): H5 端改 fetch POST 新端点 - // 后端 InitRoute 已注册 /aiagent/api/chat/elicit-response 和 /aikefu/api/chat/elicit-response - // 复用 resolveApiUrl 走 agent_id 路由,与 chat/info 等接口一致 + // PR-ELICIT-RESPONSE-HTTP (2026-09-18, 修 2026-09-19): H5 端改 fetch POST 新端点 + // 后端 InitRoute 注册的是 addon 模式 URL /{addon}/api/chat/elicit-response + // (aikefu/api/... 或 aiagent/api/...),不能用 resolveApiUrl 因为它走 /api/{short}/... + // 会拆出 module=api, controller=kefu/aiagent, action=chat → 命中 chat() 而非 elicitResponse() + // (PR-WB-MP-MEDIA 注释里记录的同款 thinkphp 路由陷阱) + // 必须用 resolveAddonName() 拿 addon 全名直接拼 URL try { - const resp = await fetch(Config.baseUrl + resolveApiUrl('chat/elicit-response'), { + const addon = resolveAddonName(currentAgentId); // aikefu / aiagent + const url = Config.baseUrl + '/' + addon + '/api/chat/elicit-response'; + const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -1132,20 +1141,37 @@ export default { /** * Set current AI Agent (default aikefu; link system passes agent_id). * PR-7: 切换 agent 时, 如果跨 addon 则断开 WebSocket 重连 (路径变了) + * + * PR-AGENT-LIFECYCLE-CLEANUP (2026-09-19): agent 真的"变了"(从 A → B) 时 + * 主动清当前会话 ID 缓存, 不展示陈旧会话 → "无痛苦无疑惑" + * - 后端 L2 会兜底: 老 agent 被删 → 静默开新会话 (前端什么都不用做也对) + * - 但前端主动清更安全: 即便后端没回 404, 用户也不看到"我换 agent 了为什么还是上次的对话" + * * @param {string} agentId + * @param {object} opts { silent: bool } true = 不回调, 默认 false */ - setAgentId(agentId) { + setAgentId(agentId, opts) { + var oldAgentId = currentAgentId; var oldAddon = resolveAddonName(currentAgentId); setAgentId(agentId); var newAddon = resolveAddonName(currentAgentId); if (oldAddon !== newAddon) { try { disconnectWebSocket(); } catch (e) { /* ignore */ } } - // 切 agent 后恢复该 agent 的会话 ID (per-agent 隔离) + // PR-AGENT-LIFECYCLE-CLEANUP: agent 字符串真变化 (A → B) → 清 conv_id 缓存 + // 不变化 (aikefu → aikefu 同 string) → 保留缓存, 让用户继续之前的多轮对话 + var agentChanged = oldAgentId && oldAgentId !== agentId; currentConversationId = null; try { const saved = uni.getStorageSync(getConversationKey(currentAgentId)); - if (saved) currentConversationId = saved; + if (saved && !agentChanged) currentConversationId = saved; + else if (saved && agentChanged) { + // agent 变了, 把老 agent 的 conv_id 也清掉, 避免老 user 换个 product page 又用回 + try { + const oldKey = getConversationKey(oldAgentId); + if (oldKey) uni.removeStorageSync(oldKey); + } catch (e) {} + } } catch (e) { /* ignore */ } },