10 KiB
调用日志列表增加用户字段实现计划
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: 在管理后台调用日志列表中增加"调用用户"列,显示格式为 昵称(ID),方便排查用户反馈问题。
Architecture: 后端 AiCallLog 实体新增 userName 字段,两处日志保存逻辑写入该字段;前端列表和详情弹窗统一展示 昵称(ID) 格式。
Tech Stack: Java (Spring Boot 2.7 + MyBatis-Plus), Vue 3 + TypeScript + Element Plus
Spec: docs/superpowers/specs/2026-05-25-call-log-add-user-design.md
文件清单
| 操作 | 文件 | 说明 |
|---|---|---|
| Modify | backend-single/src/main/java/com/emotion/entity/AiCallLog.java |
实体新增 userName 字段 |
| Create | backend-single/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql |
数据库迁移脚本 |
| Modify | backend-single/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java:68-73 |
invokeStream 写入 userName |
| Modify | backend-single/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java:219-225 |
invokeEndpointStream 写入 userName |
| Modify | web-admin/src/types/aiconfig.ts |
AiCallLog 接口新增 userName |
| Modify | web-admin/src/views/aiconfig/AiRoutingList.vue |
列表新增"调用用户"列 + userDisplay 函数 |
| Modify | web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue |
详情弹窗"用户 ID"改为"调用用户" |
Task 1: 后端实体 + 数据库迁移
Files:
-
Modify:
backend-single/src/main/java/com/emotion/entity/AiCallLog.java:29-31 -
Create:
backend-single/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql -
Test:
backend-single/目录下执行编译验证 -
Step 1: 在 AiCallLog 实体中新增 userName 字段
在 userId 字段(第 29-30 行)下方新增:
// AiCallLog.java — 在 @TableField("user_id") private String userId; 之后新增
@TableField("user_name")
private String userName;
- Step 2: 创建数据库迁移脚本
创建 backend-single/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql:
-- 调用日志表新增 user_name 字段
ALTER TABLE t_ai_call_log ADD COLUMN user_name VARCHAR(100) DEFAULT NULL COMMENT '用户昵称' AFTER user_id;
- Step 3: 编译验证后端代码
cd backend-single
mvn clean compile -DskipTests
预期:BUILD SUCCESS
- Step 4: 提交
git add backend-single/src/main/java/com/emotion/entity/AiCallLog.java
git add backend-single/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql
git commit -m "feat: 调用日志实体新增 userName 字段及数据库迁移脚本"
Task 2: 后端日志保存逻辑写入 userName
Files:
-
Modify:
backend-single/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java -
Test:
backend-single/src/test/java/com/emotion/service/AiRuntimeServiceImplTest.java -
Step 1: 在 invokeStream 方法中写入 userName
找到第 71 行 callLog.setUserId(resolveUserId(request)); 之后,第 72 行 callLog.setInputText(...) 之前,新增:
// 在 callLog.setUserId(resolveUserId(request)); 之后新增
callLog.setUserName(request.getUserName());
修改后的上下文(第 68-74 行)变为:
AiCallLog callLog = new AiCallLog();
callLog.setRequestId(requestId);
callLog.setSceneCode(request.getSceneCode());
callLog.setUserId(resolveUserId(request));
callLog.setUserName(request.getUserName());
callLog.setInputText(JSON.toJSONString(request.getInputs()));
callLog.setStatus("running");
- Step 2: 在 invokeEndpointStream 方法中写入 userName
找到第 223 行 callLog.setUserId(request.getUserId()); 之后,第 224 行 callLog.setInputText(...) 之前,新增:
// 在 callLog.setUserId(request.getUserId()); 之后新增
callLog.setUserName(request.getUserName());
修改后的上下文(第 219-226 行)变为:
AiCallLog callLog = new AiCallLog();
callLog.setRequestId(requestId);
callLog.setEndpointCode(endpoint.getEndpointCode());
callLog.setProviderCode(provider.getProviderCode());
callLog.setUserId(request.getUserId());
callLog.setUserName(request.getUserName());
callLog.setInputText(JSON.toJSONString(request.getInputs()));
callLog.setStatus("running");
- Step 3: 更新单元测试,验证 userName 被正确写入
修改 backend-single/src/test/java/com/emotion/service/AiRuntimeServiceImplTest.java,在 invokeStreamRecoversWhenOutputExists 测试中:
在已有的 request.setRequestId("client-request-1"); 之后添加 request.setUserName("测试用户");,然后在断言部分新增 userName 的验证:
// 在 request.setRequestId("client-request-1"); 之后添加
request.setUserName("测试用户");
// 在已有的 assertEquals("完整输出", savedLog.getOutputText()); 之后添加
assertEquals("测试用户", savedLog.getUserName());
- Step 4: 运行测试验证
cd backend-single
mvn test -Dtest=AiRuntimeServiceImplTest
预期:Tests run: 1, Failures: 0, Errors: 0
- Step 5: 编译验证 + 提交
cd backend-single
mvn clean compile -DskipTests
git add backend-single/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java
git add backend-single/src/test/java/com/emotion/service/AiRuntimeServiceImplTest.java
git commit -m "feat: 日志保存时写入 userName 字段"
Task 3: 前端类型定义 + 列表新增列
Files:
-
Modify:
web-admin/src/types/aiconfig.ts:249-250(AiCallLog 接口) -
Modify:
web-admin/src/views/aiconfig/AiRoutingList.vue(调用日志表格区域) -
Step 1: AiCallLog 接口新增 userName
在 web-admin/src/types/aiconfig.ts 的 AiCallLog 接口中,找到 userId?: string 行(约第 249 行),在其下方新增:
// 在 userId?: string 之后新增
userName?: string
- Step 2: AiRoutingList.vue 新增"调用用户"列
在调用日志表格中,找到 调用时间 列(<el-table-column prop="createTime" label="调用时间" width="175" />)之后,场景 列之前,新增:
<el-table-column label="调用用户" width="180" show-overflow-tooltip>
<template #default="{ row }">
{{ userDisplay(row) }}
</template>
</el-table-column>
- Step 3: AiRoutingList.vue 新增 userDisplay 辅助函数
在 <script setup> 中,找到 function formatMs(ms?: number) 函数之后(约第 640 行),新增:
function userDisplay(row: AiCallLog): string {
const name = row.userName
const id = row.userId
if (name && id) return `${name}(${id})`
if (name) return name
if (id) return `-(ID: ${id})`
return '-'
}
- Step 4: 前端类型检查
cd web-admin
npm run type-check
预期:无错误
- Step 5: 提交
git add web-admin/src/types/aiconfig.ts
git add web-admin/src/views/aiconfig/AiRoutingList.vue
git commit -m "feat: 调用日志列表新增调用用户列"
Task 4: 前端详情弹窗格式统一
Files:
-
Modify:
web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue -
Step 1: 替换"用户 ID"行为"调用用户"
在 AiCallLogDetailDialog.vue 中,找到基本信息区域中的"用户 ID"行(约第 50-53 行):
<div class="info-item">
<span class="info-label">用户 ID</span>
<span class="info-value">{{ log.userId || '-' }}</span>
</div>
替换为:
<div class="info-item">
<span class="info-label">调用用户</span>
<span class="info-value">{{ userDisplay(log) }}</span>
</div>
- Step 2: 添加 userDisplay 辅助函数
在 <script setup> 中,找到 function formatMs(ms?: number) 函数(约第 111 行)之后,新增:
function userDisplay(log: AiCallLog): string {
const name = log.userName
const id = log.userId
if (name && id) return `${name}(${id})`
if (name) return name
if (id) return `-(ID: ${id})`
return '-'
}
- Step 3: 前端类型检查
cd web-admin
npm run type-check
预期:无错误
- Step 4: 提交
git add web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue
git commit -m "feat: 详情弹窗用户信息改为昵称+ID格式"
Task 5: 浏览器验证
- Step 1: 启动管理后台
cd web-admin
npm run dev
- Step 2: 浏览器验证
- 打开管理后台页面(通常
http://localhost:5174) - 导航至 AI 配置管理页面 (
/aiconfig/list) - 切换到"调用日志" tab
- 验证:列表中出现"调用用户"列,显示格式为
昵称(ID)或-(ID: xxx)或- - 点击任意日志的"详情"按钮,验证弹窗中"调用用户"行显示格式与列表一致
- 打开浏览器 DevTools Console,确认无报错
- Step 3: 最终提交(如有必要的小修复)
git add -A
git commit -m "fix: 调用日志用户列浏览器验证修复"
Self-Review: Spec Coverage Check
| Spec 要求 | 对应 Task |
|---|---|
| AiCallLog 实体新增 userName | Task 1 Step 1 |
| 数据库新增 user_name 列 | Task 1 Step 2 |
| invokeStream 写入 userName | Task 2 Step 1 |
| invokeEndpointStream 写入 userName | Task 2 Step 2 |
| 前端类型定义新增 userName | Task 3 Step 1 |
| 列表新增"调用用户"列 | Task 3 Step 2-3 |
| 详情弹窗改为调用用户格式 | Task 4 Step 1-2 |
| 兜底策略(历史数据 NULL) | userDisplay 函数已处理 |
| 不改 AiCallLogQueryRequest | 确认未改 |
Placeholder Scan
计划中无 "TBD"、"TODO"、"fill in"、"add appropriate error handling" 等占位符。所有步骤包含完整代码和命令。
Type Consistency Check
AiCallLog.userName→ JavaString,前端userName?: string✅ 一致userDisplay函数在 AiRoutingList.vue 和 AiCallLogDetailDialog.vue 中实现相同逻辑 ✅ 一致- 显示格式统一为
昵称(ID)/-(ID: xxx)/-✅ 一致