feat(ctrt): 完成 CTRT-01~03 响应契约统一 - 三端拦截器+portal_token清理+调用点适配
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
getCurrentConversation,
|
||||
sendMessage,
|
||||
pollMessages,
|
||||
getMessages,
|
||||
shake,
|
||||
getApprovalLinks,
|
||||
getSoftwareDownloads,
|
||||
@@ -211,7 +212,8 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
/** 是否有活跃会话(会话未结单) */
|
||||
const hasActiveConversation = computed(() => {
|
||||
if (!currentConversation.value) return false
|
||||
return currentConversation.value.status !== 'closed'
|
||||
// resolved: 已结单(后端状态)
|
||||
return currentConversation.value.status !== 'resolved'
|
||||
})
|
||||
|
||||
/** 当前用户是否为被邀请的参与者(非原始员工) */
|
||||
@@ -613,6 +615,56 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息列表(历史消息)
|
||||
* 获取当前会话的完整消息历史,用于首次加载或翻页
|
||||
* @param params 查询参数(limit 和 before)
|
||||
*/
|
||||
async function fetchMessages(params?: { limit?: number; before?: string }): Promise<void> {
|
||||
// 未登录或无活跃会话时不获取
|
||||
if (!isLoggedIn.value || !hasActiveConversation.value) return
|
||||
|
||||
try {
|
||||
const data = await getMessages(params)
|
||||
if (data.items && data.items.length > 0) {
|
||||
// 消息去重
|
||||
const uniqueMessages = data.items.filter(msg => {
|
||||
if (processedMessageIds.value.has(msg.message_id)) {
|
||||
return false
|
||||
}
|
||||
trackProcessedMessageId(msg.message_id)
|
||||
return true
|
||||
})
|
||||
|
||||
if (uniqueMessages.length > 0) {
|
||||
// 如果没有 before 参数(全量加载),直接替换消息列表
|
||||
// 如果有 before 参数(翻页),追加到列表末尾
|
||||
if (!params?.before) {
|
||||
messages.value = uniqueMessages
|
||||
} else {
|
||||
messages.value.push(...uniqueMessages)
|
||||
}
|
||||
|
||||
// 更新最后消息 ID
|
||||
const lastMsg = uniqueMessages[uniqueMessages.length - 1]
|
||||
if (lastMsg) {
|
||||
lastMessageId.value = lastMsg.message_id
|
||||
}
|
||||
|
||||
console.log('[Store] 获取到历史消息:', uniqueMessages.length, '条')
|
||||
|
||||
// 保存到缓存
|
||||
const convId = currentConversation.value?.conversation_id
|
||||
if (convId) {
|
||||
saveMessagesToCache(convId, messages.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Store] 获取历史消息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动消息轮询
|
||||
* 使用 setInterval 每 3 秒轮询一次新消息
|
||||
@@ -644,10 +696,11 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
* 招手/敲桌子 — 呼叫 IT 坐席
|
||||
* 调用后端招手接口,返回趣味话术
|
||||
* 将话术以系统消息形式插入对话列表
|
||||
* @returns 分配结果: assigned(已分配坐席) / queued(排队中) / assign_failed(分配失败)
|
||||
*/
|
||||
async function shakeAgent(): Promise<void> {
|
||||
async function shakeAgent(): Promise<string> {
|
||||
// 防止重复点击
|
||||
if (shaking.value) return
|
||||
if (shaking.value) return 'pending'
|
||||
|
||||
shaking.value = true
|
||||
try {
|
||||
@@ -670,12 +723,28 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
}
|
||||
messages.value.push(systemMsg)
|
||||
|
||||
// 如果已分配坐席,更新话术内容
|
||||
if (data.assign_result === 'assigned' && data.assigned_agent_id) {
|
||||
systemMsg.content = `${data.funny_phrase}\n\n🎉 坐席已为您服务,请稍候...`
|
||||
// 更新会话状态
|
||||
if (currentConversation.value) {
|
||||
currentConversation.value.status = 'serving'
|
||||
}
|
||||
} else if (data.assign_result === 'queued') {
|
||||
// 进入排队
|
||||
systemMsg.content = `${data.funny_phrase}\n\n⏳ 当前无空闲坐席,您已进入排队,请耐心等待...`
|
||||
}
|
||||
|
||||
// 如果招手后坐席已接入(status === 'serving'),刷新会话信息
|
||||
if (data.conversation?.status === 'serving') {
|
||||
await fetchCurrentConversation()
|
||||
}
|
||||
|
||||
// 返回分配结果
|
||||
return data.assign_result || 'queued'
|
||||
} catch (error) {
|
||||
console.error('[Store] 招手失败:', error)
|
||||
return 'error'
|
||||
} finally {
|
||||
shaking.value = false
|
||||
}
|
||||
@@ -774,11 +843,11 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
// (后端 /h5/conversations/current 理论上应返回刚加入的会话)
|
||||
}
|
||||
|
||||
// 清空消息列表,重新加载
|
||||
// 清空消息列表,重新加载历史消息
|
||||
messages.value = []
|
||||
lastMessageId.value = ''
|
||||
// 立即拉取一次消息,避免等3秒轮询
|
||||
await pollNewMessages()
|
||||
// 获取完整的历史消息
|
||||
await fetchMessages()
|
||||
console.log('[Store] 已切换到邀请会话:', conversationId)
|
||||
} catch (error) {
|
||||
console.error('[Store] 切换会话失败:', error)
|
||||
@@ -896,8 +965,9 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
* 初始化应用
|
||||
* 1. 获取用户信息
|
||||
* 2. 获取当前会话
|
||||
* 3. 加载审批链接和软件下载
|
||||
* 4. 启动消息轮询
|
||||
* 3. 获取消息历史(新增 fetchMessages)
|
||||
* 4. 加载审批链接和软件下载
|
||||
* 5. 启动消息轮询
|
||||
*/
|
||||
async function initialize(): Promise<void> {
|
||||
if (initialized.value) return
|
||||
@@ -905,7 +975,7 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
try {
|
||||
console.log('[Store] 开始初始化应用...')
|
||||
|
||||
// ===== 步骤1:并行加载用户信息和会话(不等待消息) =====
|
||||
// ===== 步骤1:并行加载用户信息和会话 =====
|
||||
await Promise.all([
|
||||
fetchUserInfo(),
|
||||
fetchCurrentConversation(),
|
||||
@@ -918,32 +988,52 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
if (cached && cached.length > 0) {
|
||||
console.log(`[Store] 加载缓存消息 ${cached.length} 条`)
|
||||
messages.value = cached
|
||||
// 从缓存更新最后消息ID
|
||||
const lastCached = cached[cached.length - 1]
|
||||
if (lastCached) {
|
||||
lastMessageId.value = lastCached.message_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 步骤3:后台加载后端消息(不阻塞UI) =====
|
||||
// 使用 Promise.resolve().then() 让它在下一次事件循环执行,不阻塞主线程
|
||||
// ===== 步骤3:获取历史消息(新增,使用 getMessages API) =====
|
||||
// 后台加载,不阻塞 UI
|
||||
Promise.resolve().then(async () => {
|
||||
try {
|
||||
const freshMessages = await pollMessages()
|
||||
if (freshMessages.length > 0) {
|
||||
// 合并缓存和最新消息
|
||||
if (convId) {
|
||||
messages.value = mergeMessages(messages.value, freshMessages)
|
||||
const data = await getMessages({ limit: 50 })
|
||||
if (data.items && data.items.length > 0) {
|
||||
// 消息去重
|
||||
const uniqueMessages = data.items.filter(msg => {
|
||||
if (processedMessageIds.value.has(msg.message_id)) {
|
||||
return false
|
||||
}
|
||||
trackProcessedMessageId(msg.message_id)
|
||||
return true
|
||||
})
|
||||
|
||||
if (uniqueMessages.length > 0) {
|
||||
// 合并缓存和历史消息
|
||||
if (convId && messages.value.length > 0) {
|
||||
messages.value = mergeMessages(messages.value, uniqueMessages)
|
||||
} else {
|
||||
messages.value = uniqueMessages
|
||||
}
|
||||
|
||||
// 更新最后消息ID
|
||||
const lastMsg = uniqueMessages[uniqueMessages.length - 1]
|
||||
if (lastMsg) {
|
||||
lastMessageId.value = lastMsg.message_id
|
||||
}
|
||||
|
||||
// 保存到缓存
|
||||
saveMessagesToCache(convId, messages.value)
|
||||
} else {
|
||||
messages.value = freshMessages
|
||||
if (convId) {
|
||||
saveMessagesToCache(convId, messages.value)
|
||||
}
|
||||
console.log(`[Store] 历史消息已加载,共 ${messages.value.length} 条`)
|
||||
}
|
||||
// 更新最后消息ID
|
||||
const lastMsg = freshMessages[freshMessages.length - 1]
|
||||
if (lastMsg) {
|
||||
lastMessageId.value = lastMsg.message_id
|
||||
}
|
||||
console.log(`[Store] 后端消息已合并,共 ${messages.value.length} 条`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Store] 加载后端消息失败:', e)
|
||||
console.warn('[Store] 加载历史消息失败:', e)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1016,6 +1106,7 @@ export const useConversationStore = defineStore('conversation', () => {
|
||||
handleOAuthCallback,
|
||||
fetchUserInfo,
|
||||
fetchCurrentConversation,
|
||||
fetchMessages,
|
||||
sendNewMessage,
|
||||
pollNewMessages,
|
||||
startPolling,
|
||||
|
||||
Reference in New Issue
Block a user