From b9cf39f562fbb5f68bafa1d536bcb6dd47135b84 Mon Sep 17 00:00:00 2001 From: Peanut Date: Sun, 24 May 2026 11:32:50 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=20AI=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E6=97=A5=E5=BF=97=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=AF=84=E5=AE=A1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 补充 Request 校验注解和 @Schema - 修正 keyword 搜索的 MyBatis-Plus 嵌套逻辑 - 明确旧接口废弃策略 - 弹窗布局增加 userId/requestId - 展开行预览截断逻辑优化 - 补充性能量化阈值 - 增加 JsonViewer props 定义 - 明确时间筛选交互和非法 JSON 处理 Co-Authored-By: Claude Opus 4.7 --- .../2026-05-24-ai-call-log-detail-design.md | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/specs/2026-05-24-ai-call-log-detail-design.md b/docs/superpowers/specs/2026-05-24-ai-call-log-detail-design.md index 6ed4241..987858f 100644 --- a/docs/superpowers/specs/2026-05-24-ai-call-log-detail-design.md +++ b/docs/superpowers/specs/2026-05-24-ai-call-log-detail-design.md @@ -43,6 +43,7 @@ purpose: AI 调用日志详情查看功能设计文档 **展开行交互:** - 点击行首展开/收起图标切换展开状态 - 展开区域显示入参和出参的前 200 字符预览(超长截断 + `...`) +- 截断逻辑确保不在 Unicode 转义序列中间截断(如 `"中"`),优先在空白字符处截断,保证预览可读性 - 提供「查看完整详情」按钮,点击打开详情弹窗 ### 详情弹窗 @@ -55,6 +56,7 @@ purpose: AI 调用日志详情查看功能设计文档 │ ┌────────────┬────────────┬────────────┬────────────┐ │ │ │ 调用时间 │ 场景 │ 服务商 │ 接口 │ │ │ │ 状态 │ 首字耗时 │ 总耗时 │ 片段数 │ │ +│ │ 用户 ID │ 请求 ID │ │ │ │ │ └────────────┴────────────┴────────────┴────────────┘ │ │ │ │ 入参 [📋 复制] │ @@ -100,8 +102,19 @@ purpose: AI 调用日志详情查看功能设计文档 ### JSON 高亮方案 -不引入额外依赖,使用原生 `JSON.stringify(obj, null, 2)` + CSS 样式实现代码高亮: +不引入额外依赖,使用原生 `JSON.stringify(obj, null, 2)` + CSS 样式实现代码高亮。 +**`JsonViewer.vue` Props 接口:** +```typescript +interface JsonViewerProps { + data: string | object // JSON 字符串或对象 + title?: string // 标题,如"入参""出参" + showCopy?: boolean // 是否显示复制按钮,默认 true + maxHeight?: string // 最大高度,默认 "300px" +} +``` + +**配色方案:** - 字符串值 → `#a5d6a7`(浅绿) - 数字/布尔 → `#90caf9`(浅蓝) - key → `#ce93d8`(浅紫) @@ -109,6 +122,8 @@ purpose: AI 调用日志详情查看功能设计文档 背景使用深色主题(`#1e1e1e`),与当前管理后台暗色风格一致。 +**非法 JSON 处理:** 若 `inputText`/`outputText` 不是合法 JSON,直接展示原始文本(等宽字体、自动换行),不报错。 + ### 筛选栏参数 ```typescript @@ -117,14 +132,16 @@ interface LogQueryParams { sceneCode?: string providerCode?: string endpointCode?: string - startTime?: string - endTime?: string + startTime?: string // ISO 8601 格式,如 "2026-05-17T00:00:00" + endTime?: string // ISO 8601 格式,如 "2026-05-24T23:59:59" keyword?: string // 搜索入参/出参内容 pageNum?: number pageSize?: number } ``` +**时间筛选交互:** 前端筛选栏提供快捷选项(近7天、近30天、自定义范围),由前端组件将快捷选项转换为 `startTime`/`endTime` 后传给后端。自定义范围时使用 Element Plus `el-date-picker` 选择起止时间。 + ### 展开行预览逻辑 ```typescript @@ -137,7 +154,7 @@ function previewText(jsonStr: string, maxLen = 200): string { ### API 变更 ```typescript -// 原接口 +// 原接口(废弃,仅 AiRoutingList.vue 中使用,直接替换) export function listAiCallLogs(limit = 50) { ... } // 新接口 @@ -146,6 +163,8 @@ export function queryAiCallLogs(params: LogQueryParams) { } ``` +**兼容性说明:** 经检查,`listAiCallLogs` 仅在 `AiRoutingList.vue` 的调用日志 Tab 中使用,无其他调用方。本次直接替换,不保留旧接口。 + ## 后端 API 设计 ### 接口定义 @@ -159,15 +178,38 @@ public Result> queryCallLogs(@RequestBody @Valid AiCallLogQueryR ### Request 对象 ```java +@Schema(description = "AI 调用日志查询请求") public class AiCallLogQueryRequest { + + @Schema(description = "状态:running / success / failed") private String status; + + @Schema(description = "场景编码") private String sceneCode; + + @Schema(description = "服务商编码") private String providerCode; + + @Schema(description = "接口编码") private String endpointCode; + + @Schema(description = "开始时间") private LocalDateTime startTime; + + @Schema(description = "结束时间") private LocalDateTime endTime; + + @Schema(description = "入参/出参关键词搜索") + @Size(max = 200, message = "关键词长度不能超过 200") private String keyword; + + @Schema(description = "页码", example = "1") + @Min(value = 1, message = "页码必须大于 0") private Integer pageNum = 1; + + @Schema(description = "每页条数", example = "20") + @Min(value = 1, message = "每页条数必须大于 0") + @Max(value = 100, message = "每页条数不能超过 100") private Integer pageSize = 20; } ``` @@ -190,9 +232,9 @@ public Page query(AiCallLogQueryRequest request) { .orderByDesc(AiCallLog::getCreateTime); if (StringUtils.isNotBlank(request.getKeyword())) { - wrapper.and(w -> w.like(AiCallLog::getInputText, request.getKeyword()) - .or() - .like(AiCallLog::getOutputText, request.getKeyword())); + wrapper.and(w -> w.nested(i -> i.like(AiCallLog::getInputText, request.getKeyword()) + .or() + .like(AiCallLog::getOutputText, request.getKeyword()))); } return page(page, wrapper); @@ -202,9 +244,10 @@ public Page query(AiCallLogQueryRequest request) { ## 性能考虑 - `input_text` 和 `output_text` 可能存储大 JSON(聊天记录),LIKE 搜索在数据量极大时会变慢 -- 当前日志量不大,先使用 LIKE。后续如果数据量增长,可升级为: - - MySQL 全文索引(FULLTEXT) - - 或限制 keyword 搜索只查询近 N 天数据 +- **量化阈值:** 当日志表数据量超过 10 万条,或 `keyword` 搜索响应时间超过 500ms 时,启动优化评估: + - 方案 1:为 `input_text`、`output_text` 增加 MySQL 全文索引(FULLTEXT) + - 方案 2:限制 keyword 搜索只查询近 30 天的数据(配合 `create_time` 索引) +- 当前日志量不大,先使用 LIKE。 ## 数据模型