- 虚拟滚动:接入 @tanstack/react-virtual,封装 useVirtualMessages 动态测高与 overscan 预渲染,迁移 anchorNavigator 锚点定位与 SSR 兼容降级 - 聊天记录导出:基于既有 docx 与 xlsx 依赖实现 chatExporter 与 ChatExportModal,新增服务端 /api/v1/chat/export/messages 支持按条件导出 - 协同未读标记:新增 useUnreadMarks 30s 轮询与服务端 /api/v1/chat/unread 接口,日历格与工具栏支持橙色未读徽标提示 - 测试与验证:新增 chat_unread_and_export_api_test.go,前端全量 126 项单元/组件测试及生产打包 100% 通过
67 lines
1.7 KiB
Go
67 lines
1.7 KiB
Go
package server
|
|
|
|
// chat_export_api.go — P3 聊天记录导出查询 API
|
|
//
|
|
// GET /api/v1/chat/export/messages?session_id=...&date_from=...&date_to=...&category=...
|
|
//
|
|
// 返回按条件过滤后的消息列表,支持客户端导出为 docx/xlsx 等格式。
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"chat-agent-system/internal/ctxmgr"
|
|
)
|
|
|
|
func (s *Server) handleChatExportMessages(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
sessionID := strings.TrimSpace(r.URL.Query().Get("session_id"))
|
|
if sessionID == "" {
|
|
http.Error(w, "missing required query param 'session_id'", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if s.svc == nil || s.svc.ContextManager() == nil {
|
|
http.Error(w, "context manager not initialized", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
dateFrom := strings.TrimSpace(r.URL.Query().Get("date_from"))
|
|
dateTo := strings.TrimSpace(r.URL.Query().Get("date_to"))
|
|
category := strings.TrimSpace(r.URL.Query().Get("category"))
|
|
|
|
// 查询该会话的历史消息(上限 5000 条)
|
|
msgs, err := s.svc.ContextManager().GetChatHistory(r.Context(), sessionID, 5000, 0)
|
|
if err != nil {
|
|
http.Error(w, "query chat history failed: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// 过滤
|
|
filtered := make([]ctxmgr.ChatMessageRecord, 0, len(msgs))
|
|
for _, m := range msgs {
|
|
dk := m.DayKey
|
|
if dk == "" && !m.CreatedAt.IsZero() {
|
|
dk = m.CreatedAt.Format("2006-01-02")
|
|
}
|
|
|
|
if dateFrom != "" && dk < dateFrom {
|
|
continue
|
|
}
|
|
if dateTo != "" && dk > dateTo {
|
|
continue
|
|
}
|
|
if category != "" && category != "all" && m.Category != category {
|
|
continue
|
|
}
|
|
|
|
filtered = append(filtered, m)
|
|
}
|
|
|
|
writeJSONResponse(w, http.StatusOK, filtered)
|
|
}
|