Browse Source

修复历史记录无法存储

zkn 1 day ago
parent
commit
3a8c1a02d1

+ 33 - 2
shudao-vue-frontend/src/utils/chatHistoryPersistence.js

@@ -36,19 +36,50 @@ export const buildPersistedAIMessageContent = (message) => {
   const reports = normalizeReportsForPersistence(message.reports || [])
   const reports = normalizeReportsForPersistence(message.reports || [])
   const webSearchRaw = message.webSearchRaw || null
   const webSearchRaw = message.webSearchRaw || null
   const webSearchSummary = message._fullWebSearchSummary || message.webSearchSummary || null
   const webSearchSummary = message._fullWebSearchSummary || message.webSearchSummary || null
-  const summary = message.summary || message._fullSummary || ''
+  const summary = message._fullSummary || message.summary || ''
+  const thinkingContent = message.thinkingContent || ''
   const directContent = message.content || ''
   const directContent = message.content || ''
   const hasStructuredPayload = reports.length > 0 || webSearchRaw || webSearchSummary
   const hasStructuredPayload = reports.length > 0 || webSearchRaw || webSearchSummary
 
 
   if (hasStructuredPayload || (!directContent && summary)) {
   if (hasStructuredPayload || (!directContent && summary)) {
-    return JSON.stringify({
+    const payload = {
       reports,
       reports,
       webSearchRaw,
       webSearchRaw,
       webSearchSummary,
       webSearchSummary,
       hasWebSearchResults: message.hasWebSearchResults || false,
       hasWebSearchResults: message.hasWebSearchResults || false,
       summary
       summary
+    }
+
+    if (thinkingContent) {
+      payload.thinkingContent = thinkingContent
+    }
+
+    return JSON.stringify(payload)
+  }
+
+  if (thinkingContent) {
+    return JSON.stringify({
+      answer: directContent || summary,
+      thinkingContent
     })
     })
   }
   }
 
 
   return directContent || summary
   return directContent || summary
 }
 }
+
+export const buildAIMessageUpdatePayload = (message) => {
+  const aiMessageId = message?.ai_message_id || message?.rawData?.id
+  if (!aiMessageId) {
+    return null
+  }
+
+  const content = buildPersistedAIMessageContent(message)
+  if (!content || !content.trim()) {
+    return null
+  }
+
+  return {
+    aiMessageId,
+    content
+  }
+}

+ 66 - 0
shudao-vue-frontend/src/utils/chatHistoryPersistence.test.js

@@ -1,6 +1,7 @@
 import { describe, expect, it } from 'vitest'
 import { describe, expect, it } from 'vitest'
 
 
 import {
 import {
+  buildAIMessageUpdatePayload,
   buildPersistedAIMessageContent,
   buildPersistedAIMessageContent,
   hydratePersistedReports,
   hydratePersistedReports,
   normalizeReportsForPersistence
   normalizeReportsForPersistence
@@ -101,6 +102,71 @@ describe('chatHistoryPersistence', () => {
     })
     })
   })
   })
 
 
+  it('persists the full summary instead of the partial typewriter text', () => {
+    const content = buildPersistedAIMessageContent({
+      reports: [],
+      summary: '好的!我理解您提出的问题,',
+      _fullSummary: '好的!我理解您提出的问题,这个问题主要围绕满堂支架施工的技术与安全管控。',
+      webSearchRaw: null,
+      webSearchSummary: null,
+      hasWebSearchResults: false,
+      content: ''
+    })
+
+    expect(JSON.parse(content).summary).toBe('好的!我理解您提出的问题,这个问题主要围绕满堂支架施工的技术与安全管控。')
+  })
+
+  it('keeps thinking content in structured professional payloads', () => {
+    const content = buildPersistedAIMessageContent({
+      reports: [
+        {
+          status: 'completed',
+          report: { display_name: 'file.pdf', summary: '概述', analysis: '', clauses: '' }
+        }
+      ],
+      summary: '',
+      _fullSummary: '完整概述',
+      thinkingContent: '检索到规范并完成分析',
+      content: ''
+    })
+
+    expect(JSON.parse(content).thinkingContent).toBe('检索到规范并完成分析')
+  })
+
+  it('builds an update payload for completed non-typing messages', () => {
+    const payload = buildAIMessageUpdatePayload({
+      type: 'ai',
+      isTyping: false,
+      ai_message_id: 26911,
+      reports: [],
+      summary: '',
+      _fullSummary: '架桥机施工流程应按设备检查、拼装调试、试吊、架梁和复核验收执行。',
+      content: ''
+    })
+
+    expect(payload).toEqual({
+      aiMessageId: 26911,
+      content: JSON.stringify({
+        reports: [],
+        webSearchRaw: null,
+        webSearchSummary: null,
+        hasWebSearchResults: false,
+        summary: '架桥机施工流程应按设备检查、拼装调试、试吊、架梁和复核验收执行。'
+      })
+    })
+  })
+
+  it('does not build an update payload for blank AI content', () => {
+    expect(buildAIMessageUpdatePayload({
+      type: 'ai',
+      isTyping: false,
+      ai_message_id: 26911,
+      reports: [],
+      summary: '',
+      content: ''
+    })).toBeNull()
+  })
+
   it('returns plain text for direct AI answers without structured report data', () => {
   it('returns plain text for direct AI answers without structured report data', () => {
     expect(buildPersistedAIMessageContent({
     expect(buildPersistedAIMessageContent({
       reports: [],
       reports: [],

+ 27 - 14
shudao-vue-frontend/src/views/Chat.vue

@@ -682,6 +682,7 @@ import { useSpeechRecognition } from '@/composables/useSpeechRecognition'
 // ===== 已删除:getUserId - 不再需要,改用token =====
 // ===== 已删除:getUserId - 不再需要,改用token =====
 // import { getUserId } from '@/utils/userManager.js'
 // import { getUserId } from '@/utils/userManager.js'
 import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
 import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
+import { buildAIMessageUpdatePayload, hydratePersistedReports, normalizeReportsForPersistence } from '@/utils/chatHistoryPersistence.js'
 import { getToken, getTokenType } from '@/utils/auth.js'
 import { getToken, getTokenType } from '@/utils/auth.js'
 import { renderMarkdown } from '@/utils/markdown'
 import { renderMarkdown } from '@/utils/markdown'
 import 'katex/dist/katex.min.css'
 import 'katex/dist/katex.min.css'
@@ -1525,7 +1526,7 @@ const getConversationMessages = async (conversationId) => {
                 
                 
                 // 检查是否是新格式(包含reports和webSearch数据)
                 // 检查是否是新格式(包含reports和webSearch数据)
                 if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
                 if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
-                  reports = parsedContent.reports
+                  reports = hydratePersistedReports(parsedContent.reports)
                   // 恢复网络搜索数据
                   // 恢复网络搜索数据
                   if (parsedContent.webSearchRaw) {
                   if (parsedContent.webSearchRaw) {
                     message.webSearchRaw = parsedContent.webSearchRaw
                     message.webSearchRaw = parsedContent.webSearchRaw
@@ -1543,7 +1544,7 @@ const getConversationMessages = async (conversationId) => {
                   }
                   }
                 } else if (Array.isArray(parsedContent)) {
                 } else if (Array.isArray(parsedContent)) {
                   // 旧格式,直接是reports数组
                   // 旧格式,直接是reports数组
-                  reports = parsedContent
+                  reports = hydratePersistedReports(parsedContent)
                 } else if (parsedContent.answer || parsedContent.content || parsedContent.thinkingContent) {
                 } else if (parsedContent.answer || parsedContent.content || parsedContent.thinkingContent) {
                   if (parsedContent.thinkingContent) {
                   if (parsedContent.thinkingContent) {
                     thinkingContent = parsedContent.thinkingContent
                     thinkingContent = parsedContent.thinkingContent
@@ -2840,19 +2841,19 @@ const handleSSEMessage = (data, aiMessageIndex) => {
         // 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
         // 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
         if (aiMessage.ai_message_id) {
         if (aiMessage.ai_message_id) {
           const contentData = {
           const contentData = {
-            reports: aiMessage.reports || [],
+            reports: normalizeReportsForPersistence(aiMessage.reports || []),
             webSearchRaw: aiMessage.webSearchRaw || null,
             webSearchRaw: aiMessage.webSearchRaw || null,
             webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
             webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
             hasWebSearchResults: true,
             hasWebSearchResults: true,
             thinkingContent: aiMessage.thinkingContent || '',
             thinkingContent: aiMessage.thinkingContent || '',
             // ===== 🔧 修复:将summary也包含在content JSON中 =====
             // ===== 🔧 修复:将summary也包含在content JSON中 =====
-            summary: aiMessage.summary || aiMessage._fullSummary || ''
+            summary: aiMessage._fullSummary || aiMessage.summary || ''
           }
           }
           
           
           const collectedContent = JSON.stringify(contentData)
           const collectedContent = JSON.stringify(contentData)
           
           
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = aiMessage.summary || aiMessage._fullSummary || ''
+          const summaryToSave = aiMessage._fullSummary || aiMessage.summary || ''
           
           
           updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
             .then((response) => {
             .then((response) => {
@@ -2897,6 +2898,11 @@ const handleSSEMessage = (data, aiMessageIndex) => {
       if (aiMessage.progress < 100) {
       if (aiMessage.progress < 100) {
         updateMessageStatus(aiMessage, 'completed')
         updateMessageStatus(aiMessage, 'completed')
       }
       }
+      const completedPayload = buildAIMessageUpdatePayload(aiMessage)
+      if (completedPayload) {
+        updateAIMessageContent(completedPayload.aiMessageId, completedPayload.content)
+          .catch(err => console.error('completed事件回写AI消息失败:', err))
+      }
       
       
       ElMessage.success('报告生成完成')
       ElMessage.success('报告生成完成')
       break
       break
@@ -2946,14 +2952,14 @@ const handleSSEComplete = () => {
       if (message.ai_message_id) {
       if (message.ai_message_id) {
         // 构建完整的内容数据,包含报告、网络搜索结果和summary
         // 构建完整的内容数据,包含报告、网络搜索结果和summary
         const contentData = {
         const contentData = {
-          reports: message.reports || [],
+          reports: normalizeReportsForPersistence(message.reports || []),
           webSearchRaw: message.webSearchRaw || null,
           webSearchRaw: message.webSearchRaw || null,
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           thinkingContent: message.thinkingContent || '',
           thinkingContent: message.thinkingContent || '',
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
@@ -2968,7 +2974,7 @@ const handleSSEComplete = () => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))
@@ -2976,6 +2982,13 @@ const handleSSEComplete = () => {
       }
       }
     }
     }
   })
   })
+
+  const lastAIMessageForPersistence = chatMessages.value.filter(msg => msg.type === 'ai').pop()
+  const finalPayload = buildAIMessageUpdatePayload(lastAIMessageForPersistence)
+  if (finalPayload) {
+    updateAIMessageContent(finalPayload.aiMessageId, finalPayload.content)
+      .catch(err => console.error('SSE完成时回写AI消息失败:', err))
+  }
   
   
   isAIReplyProcessComplete.value = true
   isAIReplyProcessComplete.value = true
   
   
@@ -3107,14 +3120,14 @@ const handleSSEInterrupted = (data) => {
       if (message.ai_message_id) {
       if (message.ai_message_id) {
         // 构建完整的内容数据,包含报告、网络搜索结果和summary
         // 构建完整的内容数据,包含报告、网络搜索结果和summary
         const contentData = {
         const contentData = {
-          reports: message.reports || [],
+          reports: normalizeReportsForPersistence(message.reports || []),
           webSearchRaw: message.webSearchRaw || null,
           webSearchRaw: message.webSearchRaw || null,
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           thinkingContent: message.thinkingContent || '',
           thinkingContent: message.thinkingContent || '',
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
@@ -3129,7 +3142,7 @@ const handleSSEInterrupted = (data) => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))
@@ -3183,14 +3196,14 @@ const handleStopGeneration = async () => {
       // 回写数据到后端,包含网络搜索结果和summary
       // 回写数据到后端,包含网络搜索结果和summary
       if (message.ai_message_id) {
       if (message.ai_message_id) {
         const contentData = {
         const contentData = {
-          reports: message.reports || [],
+          reports: normalizeReportsForPersistence(message.reports || []),
           webSearchRaw: message.webSearchRaw || null,
           webSearchRaw: message.webSearchRaw || null,
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           // 使用完整的webSearchSummary,而不是打字机过程中的部分内容
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           thinkingContent: message.thinkingContent || '',
           thinkingContent: message.thinkingContent || '',
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
         const plainAnswer = message.content || message._fullSummary || message.summary || ''
@@ -3205,7 +3218,7 @@ const handleStopGeneration = async () => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))

+ 24 - 11
shudao-vue-frontend/src/views/mobile/m-Chat.vue

@@ -470,6 +470,7 @@ import { createSSEConnection, closeSSEConnection } from '@/utils/sse'
 import { getApiPrefix, BACKEND_API_PREFIX } from '@/utils/apiConfig'
 import { getApiPrefix, BACKEND_API_PREFIX } from '@/utils/apiConfig'
 import { renderMarkdown } from '@/utils/markdown'
 import { renderMarkdown } from '@/utils/markdown'
 import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
 import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
+import { buildAIMessageUpdatePayload, hydratePersistedReports, normalizeReportsForPersistence } from '@/utils/chatHistoryPersistence.js'
 import { getToken, getTokenType, getUserName, getAccountId } from '@/utils/auth.js'
 import { getToken, getTokenType, getUserName, getAccountId } from '@/utils/auth.js'
 import { initNativeNavForSubPage } from '@/utils/nativeBridge.js'
 import { initNativeNavForSubPage } from '@/utils/nativeBridge.js'
 import Vditor from 'vditor'
 import Vditor from 'vditor'
@@ -1268,7 +1269,7 @@ const getConversationMessages = async (conversationId) => {
                 
                 
                 // 检查是否是新格式(包含reports和webSearch数据)
                 // 检查是否是新格式(包含reports和webSearch数据)
                 if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
                 if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
-                  reports = parsedContent.reports
+                  reports = hydratePersistedReports(parsedContent.reports)
                   // 恢复网络搜索数据
                   // 恢复网络搜索数据
                   if (parsedContent.webSearchRaw) {
                   if (parsedContent.webSearchRaw) {
                     message.webSearchRaw = parsedContent.webSearchRaw
                     message.webSearchRaw = parsedContent.webSearchRaw
@@ -1283,7 +1284,7 @@ const getConversationMessages = async (conversationId) => {
                   }
                   }
                 } else if (Array.isArray(parsedContent)) {
                 } else if (Array.isArray(parsedContent)) {
                   // 旧格式,直接是reports数组
                   // 旧格式,直接是reports数组
-                  reports = parsedContent
+                  reports = hydratePersistedReports(parsedContent)
                 } else {
                 } else {
                   throw new Error('Not an array or valid format')
                   throw new Error('Not an array or valid format')
                 }
                 }
@@ -2858,18 +2859,18 @@ const handleSSEMessage = (data, aiMessageIndex) => {
         // 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
         // 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
         if (aiMessage.ai_message_id) {
         if (aiMessage.ai_message_id) {
           const contentData = {
           const contentData = {
-            reports: aiMessage.reports || [],
+            reports: normalizeReportsForPersistence(aiMessage.reports || []),
             webSearchRaw: aiMessage.webSearchRaw || null,
             webSearchRaw: aiMessage.webSearchRaw || null,
             webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
             webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
             hasWebSearchResults: true,
             hasWebSearchResults: true,
             // ===== 🔧 修复:将summary也包含在content JSON中 =====
             // ===== 🔧 修复:将summary也包含在content JSON中 =====
-            summary: aiMessage.summary || aiMessage._fullSummary || ''
+            summary: aiMessage._fullSummary || aiMessage.summary || ''
           }
           }
           
           
           const collectedContent = JSON.stringify(contentData)
           const collectedContent = JSON.stringify(contentData)
           
           
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = aiMessage.summary || aiMessage._fullSummary || ''
+          const summaryToSave = aiMessage._fullSummary || aiMessage.summary || ''
           
           
           updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
             .then((response) => {
             .then((response) => {
@@ -2914,6 +2915,11 @@ const handleSSEMessage = (data, aiMessageIndex) => {
       if (aiMessage.progress < 100) {
       if (aiMessage.progress < 100) {
         updateMessageStatus(aiMessage, 'completed')
         updateMessageStatus(aiMessage, 'completed')
       }
       }
+      const completedPayload = buildAIMessageUpdatePayload(aiMessage)
+      if (completedPayload) {
+        updateAIMessageContent(completedPayload.aiMessageId, completedPayload.content)
+          .catch(err => console.error('completed事件回写AI消息失败:', err))
+      }
       
       
       showToastMessage('报告生成完成', 2000)
       showToastMessage('报告生成完成', 2000)
       break
       break
@@ -2981,7 +2987,7 @@ const handleSSEComplete = () => {
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const collectedContent = message.reports && message.reports.length > 0 
         const collectedContent = message.reports && message.reports.length > 0 
@@ -2990,7 +2996,7 @@ const handleSSEComplete = () => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))
@@ -2998,6 +3004,13 @@ const handleSSEComplete = () => {
       }
       }
     }
     }
   })
   })
+
+  const lastAIMessageForPersistence = chatMessages.value.filter(msg => msg.type === 'ai').pop()
+  const finalPayload = buildAIMessageUpdatePayload(lastAIMessageForPersistence)
+  if (finalPayload) {
+    updateAIMessageContent(finalPayload.aiMessageId, finalPayload.content)
+      .catch(err => console.error('SSE完成时回写AI消息失败:', err))
+  }
   
   
   isAIReplyProcessComplete.value = true
   isAIReplyProcessComplete.value = true
   
   
@@ -3137,7 +3150,7 @@ const handleSSEInterrupted = (data) => {
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const collectedContent = message.reports && message.reports.length > 0 
         const collectedContent = message.reports && message.reports.length > 0 
@@ -3146,7 +3159,7 @@ const handleSSEInterrupted = (data) => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))
@@ -3196,7 +3209,7 @@ const handleStopGeneration = async () => {
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
           hasWebSearchResults: message.hasWebSearchResults || false,
           hasWebSearchResults: message.hasWebSearchResults || false,
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
           // ===== 🔧 修复:将summary也包含在content JSON中 =====
-          summary: message.summary || message._fullSummary || ''
+          summary: message._fullSummary || message.summary || ''
         }
         }
         
         
         const collectedContent = message.reports && message.reports.length > 0 
         const collectedContent = message.reports && message.reports.length > 0 
@@ -3205,7 +3218,7 @@ const handleStopGeneration = async () => {
         
         
         if (collectedContent) {
         if (collectedContent) {
           // 同时保存summary字段(作为单独字段)
           // 同时保存summary字段(作为单独字段)
-          const summaryToSave = message.summary || message._fullSummary || ''
+          const summaryToSave = message._fullSummary || message.summary || ''
           
           
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
           updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
             .catch(err => console.error('回写AI消息失败:', err))
             .catch(err => console.error('回写AI消息失败:', err))