diff --git a/ai_knowledge_base_v2/apps/admin-web/src/App.vue b/ai_knowledge_base_v2/apps/admin-web/src/App.vue index 647fc98..0859ffa 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/App.vue +++ b/ai_knowledge_base_v2/apps/admin-web/src/App.vue @@ -53,6 +53,7 @@ const previewKnowledgeId = ref(null); const chatDetail = ref(null); const chatDetailOpen = ref(false); const chatDetailLoading = ref(false); +const chatMessagesLoading = ref(false); const chatDetailSessionId = ref(null); const selectedAiLog = ref(null); const aiLogDetailOpen = ref(false); @@ -773,7 +774,21 @@ async function loadChatDetail(sessionId = chatDetailSessionId.value, page = chat } async function changeChatDetailMessagePage(page: number, pageSize: number) { - await loadChatDetail(chatDetailSessionId.value, page, pageSize); + if (!chatDetailSessionId.value || !chatDetail.value) return; + chatMessagesLoading.value = true; + try { + const result = await api.chatMessages(chatDetailSessionId.value, { page, pageSize }); + chatDetail.value = { + ...chatDetail.value, + messages: result.items, + messagesPage: result, + }; + Object.assign(chatDetailMessagePager, { page: result.page, pageSize: result.pageSize, total: result.total }); + } catch (error) { + ElMessage.error(error instanceof Error ? error.message : "聊天消息加载失败"); + } finally { + chatMessagesLoading.value = false; + } } async function openAiLogDetail(row: AiLogRecord) { @@ -1244,7 +1259,7 @@ function formatRecordDateTime(value: string, boundary: "start" | "end") {

完整对话

-
+
request>(`/admin/chat/list${queryString(query)}`), chatDetail: (sessionId: number, query: { messagePage?: number; messagePageSize?: number } = {}) => request(`/admin/chat/${sessionId}${queryString(query)}`), + chatMessages: (sessionId: number, query: { page?: number; pageSize?: number } = {}) => + request>(`/admin/chat/${sessionId}/messages${queryString(query)}`), exportChats: (query: ChatRecordQuery = {}) => download(`/admin/chat/export${queryString(query)}`, "chat_records.csv"), aiLogs: (query: { sessionId?: number; userId?: number; status?: string; page?: number; pageSize?: number } = {}) => request>(`/admin/ai-log/list${queryString(query)}`), diff --git a/ai_knowledge_base_v2/apps/backend/app/api/admin_records.py b/ai_knowledge_base_v2/apps/backend/app/api/admin_records.py index d8c8b57..220dad4 100644 --- a/ai_knowledge_base_v2/apps/backend/app/api/admin_records.py +++ b/ai_knowledge_base_v2/apps/backend/app/api/admin_records.py @@ -80,6 +80,28 @@ def export_chats( ) +@router.get("/chat/{session_id}/messages") +def chat_messages( + session_id: int, + page: int = Query(default=1, ge=1), + pageSize: int = Query(default=20, ge=10, le=100), + db: Session = Depends(get_db), + current_admin: Admin = Depends(get_current_admin), +) -> dict: + exists_session = db.scalar(select(ChatSession.id).where(ChatSession.id == session_id)) + if exists_session is None: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="会话不存在") + message_query = select(ChatMessage).where(ChatMessage.session_id == session_id) + total = db.scalar(select(func.count()).select_from(message_query.subquery())) or 0 + messages = db.scalars( + message_query + .order_by(ChatMessage.created_at.asc(), ChatMessage.id.asc()) + .offset((page - 1) * pageSize) + .limit(pageSize) + ).all() + return api_success(page_result([_message_dict(item) for item in messages], total=total, page=page, page_size=pageSize)) + + @router.get("/chat/{session_id}") def chat_detail( session_id: int, diff --git a/ai_knowledge_base_v2/apps/backend/tests/test_admin_pagination.py b/ai_knowledge_base_v2/apps/backend/tests/test_admin_pagination.py index 5fbb762..cbbbe4f 100644 --- a/ai_knowledge_base_v2/apps/backend/tests/test_admin_pagination.py +++ b/ai_knowledge_base_v2/apps/backend/tests/test_admin_pagination.py @@ -3,7 +3,7 @@ from sqlalchemy.orm import Session from sqlalchemy.pool import StaticPool from app.api.admin_agent_records import attention_list, retrieval_logs -from app.api.admin_records import ai_logs, chat_detail +from app.api.admin_records import ai_logs, chat_detail, chat_messages from app.api.admin_users import list_users from app.models import Base from app.models.chat import ChatMessage, ChatSession @@ -65,6 +65,27 @@ def test_chat_detail_messages_are_paginated(): assert data["messages"][0]["content"] == "消息11" +def test_chat_messages_endpoint_returns_only_message_page(): + with _database() as db: + user = User(id=1, phone="13800000000", name="学员", daily_chat_limit=10) + session = ChatSession(id=1, user_id=1, title="长会话", message_count=12) + db.add_all([user, session]) + db.add_all([ + ChatMessage(id=index + 1, session_id=1, user_id=1, role="user", content=f"消息{index + 1}") + for index in range(12) + ]) + db.add(AiRequestLog(session_id=1, status="success", prompt="p" * 5000, retrieved_chunks='[{"content":"large"}]')) + db.commit() + + response = chat_messages(1, page=2, pageSize=10, db=db, current_admin=object()) + + data = response["data"] + assert data["total"] == 12 + assert len(data["items"]) == 2 + assert data["items"][0]["content"] == "消息11" + assert "aiLogs" not in data + + def test_retrieval_and_attention_lists_are_paginated(): with _database() as db: for index in range(21):