From 56535dbcb0792d4d12b6724c4ada40967bc42c03 Mon Sep 17 00:00:00 2001 From: Peanut Date: Sun, 24 May 2026 11:43:40 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20AI=20=E8=B0=83=E7=94=A8=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AF=A6=E6=83=85=E6=9F=A5=E7=9C=8B=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E6=96=BD=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .../plans/2026-05-24-ai-call-log-detail.md | 1035 +++++++++++++++++ 1 file changed, 1035 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-24-ai-call-log-detail.md diff --git a/docs/superpowers/plans/2026-05-24-ai-call-log-detail.md b/docs/superpowers/plans/2026-05-24-ai-call-log-detail.md new file mode 100644 index 0000000..d5125af --- /dev/null +++ b/docs/superpowers/plans/2026-05-24-ai-call-log-detail.md @@ -0,0 +1,1035 @@ +# AI 调用日志详情查看功能实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 在 web-admin AI 配置管理的调用日志页签中增加展开行预览、详情弹窗、分页筛选搜索功能,后端提供 POST 分页查询接口。 + +**Architecture:** 前端新增 JsonViewer 通用组件和 AiCallLogDetailDialog 弹窗组件,改造 AiRoutingList 的调用日志 Tab 增加筛选栏、展开行、分页。后端新增 AiCallLogQueryRequest 和 query 分页查询方法,Controller 增加 POST /call-logs 接口,保留原 GET 接口。 + +**Tech Stack:** Vue 3 + TypeScript + Element Plus(前端),Spring Boot 2.7 + MyBatis-Plus(后端) + +--- + +## 文件结构 + +| 文件 | 动作 | 职责 | +|------|------|------| +| `web-admin/src/types/common.ts` | 修改 | 新增 `LogQueryParams` 接口 | +| `web-admin/src/api/aiconfig.ts` | 修改 | 新增 `queryAiCallLogs` 接口,保留旧接口 | +| `web-admin/src/components/JsonViewer.vue` | 创建 | 通用 JSON 格式化高亮 + 复制组件 | +| `web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue` | 创建 | 调用日志详情弹窗 | +| `web-admin/src/views/aiconfig/AiRoutingList.vue` | 修改 | 调用日志 Tab 增加筛选栏、展开行、分页 | +| `backend-single/src/main/java/com/emotion/dto/request/ai/AiCallLogQueryRequest.java` | 创建 | 日志查询请求 DTO | +| `backend-single/src/main/java/com/emotion/service/AiCallLogService.java` | 修改 | 新增 `query` 方法签名 | +| `backend-single/src/main/java/com/emotion/service/impl/AiCallLogServiceImpl.java` | 修改 | 实现 `query` 分页查询 | +| `backend-single/src/main/java/com/emotion/controller/AiRoutingController.java` | 修改 | 新增 POST /call-logs 接口 | + +--- + +## Task 1: 前端类型定义(LogQueryParams) + +**Files:** +- Modify: `web-admin/src/types/common.ts` + +- [ ] **Step 1: 在 common.ts 中追加 LogQueryParams 接口** + +在 `common.ts` 文件末尾追加: + +```typescript +export interface LogQueryParams { + status?: string + sceneCode?: string + providerCode?: string + endpointCode?: string + startTime?: string + endTime?: string + keyword?: string + pageNum?: number + pageSize?: number +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add web-admin/src/types/common.ts +git commit -m "feat: 添加 AI 调用日志查询参数类型" +``` + +--- + +## Task 2: 前端 API 层(queryAiCallLogs) + +**Files:** +- Modify: `web-admin/src/api/aiconfig.ts` + +- [ ] **Step 1: 导入 LogQueryParams 和 PageResult** + +在 `aiconfig.ts` 的 import 语句中追加: + +```typescript +import type { LogQueryParams } from '@/types/common' +import type { PageResult } from '@/types/common' +``` + +- [ ] **Step 2: 新增 queryAiCallLogs 接口函数** + +在 `listAiCallLogs` 函数下方追加: + +```typescript +export function queryAiCallLogs(params: LogQueryParams) { + return request>({ + url: '/ai/call-logs', + method: 'post', + data: params + }) +} +``` + +**注意:** 保留原有的 `listAiCallLogs` 函数不动,新旧接口共存。 + +- [ ] **Step 3: Commit** + +```bash +git add web-admin/src/api/aiconfig.ts +git commit -m "feat: 新增 AI 调用日志分页查询接口" +``` + +--- + +## Task 3: JsonViewer 通用组件 + +**Files:** +- Create: `web-admin/src/components/JsonViewer.vue` + +- [ ] **Step 1: 创建 JsonViewer.vue** + +```vue + + + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add web-admin/src/components/JsonViewer.vue +git commit -m "feat: 新增 JsonViewer 通用 JSON 高亮组件" +``` + +--- + +## Task 4: AiCallLogDetailDialog 详情弹窗 + +**Files:** +- Create: `web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue` + +- [ ] **Step 1: 创建 AiCallLogDetailDialog.vue** + +```vue + + + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue +git commit -m "feat: 新增 AI 调用日志详情弹窗组件" +``` + +--- + +## Task 5: 后端 AiCallLogQueryRequest DTO + +**Files:** +- Create: `backend-single/src/main/java/com/emotion/dto/request/ai/AiCallLogQueryRequest.java` + +- [ ] **Step 1: 确认 dto/request/ai 目录存在** + +```bash +ls backend-single/src/main/java/com/emotion/dto/request/ai/ +``` + +如果不存在则创建: + +```bash +mkdir -p backend-single/src/main/java/com/emotion/dto/request/ai +``` + +- [ ] **Step 2: 创建 AiCallLogQueryRequest.java** + +```java +package com.emotion.dto.request.ai; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; +import java.time.LocalDateTime; + +/** + * AI 调用日志查询请求 + */ +@Data +@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; +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add backend-single/src/main/java/com/emotion/dto/request/ai/AiCallLogQueryRequest.java +git commit -m "feat: 新增 AI 调用日志查询请求 DTO" +``` + +--- + +## Task 6: 后端 Service 接口和实现 + +**Files:** +- Modify: `backend-single/src/main/java/com/emotion/service/AiCallLogService.java` +- Modify: `backend-single/src/main/java/com/emotion/service/impl/AiCallLogServiceImpl.java` + +- [ ] **Step 1: 在 AiCallLogService 接口中新增 query 方法** + +在 `AiCallLogService.java` 的 `latest` 方法下方添加: + +```java +import com.emotion.common.PageResult; +import com.emotion.dto.request.ai.AiCallLogQueryRequest; + +// ... 在接口中 +PageResult query(AiCallLogQueryRequest request); +``` + +- [ ] **Step 2: 在 AiCallLogServiceImpl 中实现 query 方法** + +在 `AiCallLogServiceImpl.java` 的 `latest` 方法下方添加: + +```java +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.emotion.common.PageResult; +import com.emotion.dto.request.ai.AiCallLogQueryRequest; +import org.apache.commons.lang3.StringUtils; + +// ... 在类中 +@Override +public PageResult query(AiCallLogQueryRequest request) { + Page pageParam = new Page<>(request.getPageNum(), request.getPageSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(AiCallLog::getIsDeleted, 0) + .eq(StringUtils.isNotBlank(request.getStatus()), AiCallLog::getStatus, request.getStatus()) + .eq(StringUtils.isNotBlank(request.getSceneCode()), AiCallLog::getSceneCode, request.getSceneCode()) + .eq(StringUtils.isNotBlank(request.getProviderCode()), AiCallLog::getProviderCode, request.getProviderCode()) + .eq(StringUtils.isNotBlank(request.getEndpointCode()), AiCallLog::getEndpointCode, request.getEndpointCode()) + .ge(request.getStartTime() != null, AiCallLog::getCreateTime, request.getStartTime()) + .le(request.getEndTime() != null, AiCallLog::getCreateTime, request.getEndTime()) + .orderByDesc(AiCallLog::getCreateTime); + + if (StringUtils.isNotBlank(request.getKeyword())) { + wrapper.and(w -> w.like(AiCallLog::getInputText, request.getKeyword()) + .or() + .like(AiCallLog::getOutputText, request.getKeyword())); + } + + IPage page = page(pageParam, wrapper); + return PageResult.of(page); +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add backend-single/src/main/java/com/emotion/service/AiCallLogService.java +git add backend-single/src/main/java/com/emotion/service/impl/AiCallLogServiceImpl.java +git commit -m "feat: AI 调用日志 Service 增加分页查询方法" +``` + +--- + +## Task 7: 后端 Controller 新增 POST 接口 + +**Files:** +- Modify: `backend-single/src/main/java/com/emotion/controller/AiRoutingController.java` + +- [ ] **Step 1: 在 AiRoutingController 中导入新类型** + +在 imports 区域添加: + +```java +import com.emotion.common.PageResult; +import com.emotion.dto.request.ai.AiCallLogQueryRequest; +import javax.validation.Valid; +``` + +- [ ] **Step 2: 在 callLogs GET 方法下方新增 POST 方法** + +在 `callLogs` 方法(第 157-161 行)下方添加: + +```java +@Operation(summary = "分页查询调用日志", description = "分页查询 AI 调用日志,支持多条件筛选和关键词搜索。") +@PostMapping("/call-logs") +public Result> queryCallLogs(@RequestBody @Valid AiCallLogQueryRequest request) { + return Result.success(callLogService.query(request)); +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add backend-single/src/main/java/com/emotion/controller/AiRoutingController.java +git commit -m "feat: Controller 新增 AI 调用日志分页查询接口" +``` + +--- + +## Task 8: 前端 AiRoutingList 改造(核心) + +**Files:** +- Modify: `web-admin/src/views/aiconfig/AiRoutingList.vue` + +这是最大的改动任务。需要在调用日志 Tab 中增加:筛选栏、展开行、分页、详情弹窗。 + +- [ ] **Step 1: 导入新增依赖** + +在 `