Files
Agent-System/internal/server/chat_export_api.go
T
WeiCHENandCursor 28faa4932d fix(agent): Chat Agent 全链路 P0-P5 整改
封堵鉴权旁路与导出 IDOR,DocGov 改服务端身份;对话层 PendingAction 接真实 docgov/taskrun;修复流式租约假死与会话级任务流;加固配置/doc_id/NULL;补齐 slog 与测试;前端 ErrorBoundary 与受控文档重试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 18:22:45 +08:00

93 lines
2.4 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"
"chat-agent-system/internal/replyexport"
)
func (s *Server) handleChatExportMessages(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
user, ok := s.requireAuthenticatedUser(w, r)
if !ok {
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
}
if !s.assertSessionOwnedByUser(w, r, sessionID, user.ID) {
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
}
hasCard := false
if m.Metadata != nil {
if _, exists := m.Metadata["interactive_card"]; exists {
hasCard = true
}
}
_, cap := replyexport.EvaluatePolicy(replyexport.PolicyOptions{
Role: m.Role,
Status: "completed",
Content: m.Content,
HasInteractiveCard: hasCard,
Revision: m.ContentRevision,
})
if cap.Policy == replyexport.PolicyNone {
continue
}
filtered = append(filtered, m)
}
writeJSONResponse(w, http.StatusOK, filtered)
}