# 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: 导入新增依赖** 在 `