/** * AI 调用明细页面。 * * - 顶部筛选器:app_type / status / trigger_type / site_id / 日期范围 * - 主体:分页表格(app_type、trigger_type、member_id、tokens、延迟、状态) * - 点击行:Drawer 展示完整 prompt / response / error_message */ import React, { useEffect, useState, useCallback } from "react"; import { Card, Table, Tag, Select, Button, DatePicker, Row, Space, Drawer, Descriptions, message, Typography, } from "antd"; import { ReloadOutlined } from "@ant-design/icons"; import type { ColumnsType } from "antd/es/table"; import { getRunLogs, getRunLogDetail, type RunLogItem, type RunLogDetailResponse, type RunLogQuery, } from "../api/adminAI"; const { RangePicker } = DatePicker; const { Title } = Typography; const STATUS_COLOR: Record = { success: "green", failed: "red", timeout: "orange", circuit_open: "volcano", pending: "default", running: "processing", }; function fmtTime(raw: string | null): string { if (!raw) return "—"; const d = new Date(raw); return Number.isNaN(d.getTime()) ? raw : d.toLocaleString("zh-CN"); } const AIRunLogs: React.FC = () => { const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1); const [pageSize] = useState(20); // 筛选状态 const [appType, setAppType] = useState(); const [status, setStatus] = useState(); const [triggerType, setTriggerType] = useState(); const [siteId, setSiteId] = useState(); const [dateRange, setDateRange] = useState<[string, string] | null>(null); // Drawer 详情 const [drawerVisible, setDrawerVisible] = useState(false); const [detail, setDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const load = useCallback(async () => { setLoading(true); try { const params: RunLogQuery = { page, page_size: pageSize, app_type: appType, status, trigger_type: triggerType, site_id: siteId, date_from: dateRange?.[0], date_to: dateRange?.[1], }; const res = await getRunLogs(params); setItems(res.items); setTotal(res.total); } catch { message.error("加载调用记录失败"); } finally { setLoading(false); } }, [page, pageSize, appType, status, triggerType, siteId, dateRange]); useEffect(() => { load(); }, [load]); const handleRowClick = async (id: number) => { setDetailLoading(true); setDrawerVisible(true); try { const res = await getRunLogDetail(id); setDetail(res); } catch { message.error("加载详情失败"); } finally { setDetailLoading(false); } }; const APP_TYPE_OPTIONS = RUN_LOG_APP_TYPE_OPTIONS; const columns: ColumnsType = [ { title: "ID", dataIndex: "id", key: "id", width: 70 }, { title: "App 类型", dataIndex: "app_type", key: "app_type", width: 160 }, { title: "触发方式", dataIndex: "trigger_type", key: "trigger_type", width: 110 }, { title: "会员 ID", dataIndex: "member_id", key: "member_id", width: 100, render: (v) => v ?? "—" }, { title: "Tokens", dataIndex: "tokens_used", key: "tokens_used", width: 90, align: "right" }, { title: "延迟", dataIndex: "latency_ms", key: "latency_ms", width: 90, align: "right", render: (v: number | null) => v != null ? `${v}ms` : "—", }, { title: "状态", dataIndex: "status", key: "status", width: 110, render: (v: string) => {v}, }, { title: "时间", dataIndex: "created_at", key: "created_at", width: 170, render: fmtTime }, ]; return (
AI 调用明细 {/* 筛选器行 */} { setStatus(v); setPage(1); }} options={[ { label: "success", value: "success" }, { label: "failed", value: "failed" }, { label: "timeout", value: "timeout" }, { label: "circuit_open", value: "circuit_open" }, ]} /> { setSiteId(v); setPage(1); }} options={[{ label: "默认门店", value: 2790685415443269 }]} /> { setDateRange(dateStrings[0] ? [dateStrings[0], dateStrings[1]] : null); setPage(1); }} /> {/* 主体表格 */} columns={columns} dataSource={items} rowKey="id" loading={loading} scroll={{ x: 1000 }} onRow={(record) => ({ onClick: () => handleRowClick(record.id), style: { cursor: "pointer" }, })} pagination={{ current: page, pageSize, total, onChange: (p) => setPage(p), showTotal: (t) => `共 ${t} 条`, }} /> {/* 详情 Drawer */} { setDrawerVisible(false); setDetail(null); }} width={640} loading={detailLoading} > {detail && ( <> {detail.app_type} {detail.trigger_type} {detail.member_id ?? "—"} {detail.site_id} {detail.tokens_used} {detail.latency_ms != null ? `${detail.latency_ms}ms` : "—"} {detail.status} {detail.session_id ?? "—"} {fmtTime(detail.created_at)} {fmtTime(detail.finished_at)} {detail.error_message && (
                  {detail.error_message}
                
)}
                {detail.request_prompt ?? "(无)"}
              
                {detail.response_text ?? "(无)"}
              
)}
); }; export default AIRunLogs; export const RUN_LOG_APP_TYPE_OPTIONS = [ "app1_chat", "app2_finance", "app2a_finance_area", "app3_clue", "app4_analysis", "app5_tactics", "app6_note", "app7_customer", "app8_consolidate", "app8_consolidation", ].map((v) => ({ label: v, value: v }));