docs: 调用日志列表增加用户字段实现计划

This commit is contained in:
2026-05-25 21:45:43 +08:00
parent 763e1df662
commit e3e1903d93
@@ -0,0 +1,330 @@
# 调用日志列表增加用户字段实现计划
> **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 行)下方新增:
```java
// 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`:
```sql
-- 调用日志表新增 user_name 字段
ALTER TABLE t_ai_call_log ADD COLUMN user_name VARCHAR(100) DEFAULT NULL COMMENT '用户昵称' AFTER user_id;
```
- [ ] **Step 3: 编译验证后端代码**
```bash
cd backend-single
mvn clean compile -DskipTests
```
预期:BUILD SUCCESS
- [ ] **Step 4: 提交**
```bash
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(...)` 之前,新增:
```java
// 在 callLog.setUserId(resolveUserId(request)); 之后新增
callLog.setUserName(request.getUserName());
```
修改后的上下文(第 68-74 行)变为:
```java
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(...)` 之前,新增:
```java
// 在 callLog.setUserId(request.getUserId()); 之后新增
callLog.setUserName(request.getUserName());
```
修改后的上下文(第 219-226 行)变为:
```java
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 的验证:
```java
// 在 request.setRequestId("client-request-1"); 之后添加
request.setUserName("测试用户");
// 在已有的 assertEquals("完整输出", savedLog.getOutputText()); 之后添加
assertEquals("测试用户", savedLog.getUserName());
```
- [ ] **Step 4: 运行测试验证**
```bash
cd backend-single
mvn test -Dtest=AiRuntimeServiceImplTest
```
预期:Tests run: 1, Failures: 0, Errors: 0
- [ ] **Step 5: 编译验证 + 提交**
```bash
cd backend-single
mvn clean compile -DskipTests
```
```bash
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 行),在其下方新增:
```typescript
// 在 userId?: string 之后新增
userName?: string
```
- [ ] **Step 2: AiRoutingList.vue 新增"调用用户"列**
在调用日志表格中,找到 `调用时间` 列(`<el-table-column prop="createTime" label="调用时间" width="175" />`)之后,`场景` 列之前,新增:
```vue
<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 行),新增:
```typescript
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: 前端类型检查**
```bash
cd web-admin
npm run type-check
```
预期:无错误
- [ ] **Step 5: 提交**
```bash
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 行):
```vue
<div class="info-item">
<span class="info-label">用户 ID</span>
<span class="info-value">{{ log.userId || '-' }}</span>
</div>
```
替换为:
```vue
<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 行)之后,新增:
```typescript
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: 前端类型检查**
```bash
cd web-admin
npm run type-check
```
预期:无错误
- [ ] **Step 4: 提交**
```bash
git add web-admin/src/views/aiconfig/components/AiCallLogDetailDialog.vue
git commit -m "feat: 详情弹窗用户信息改为昵称+ID格式"
```
---
### Task 5: 浏览器验证
- [ ] **Step 1: 启动管理后台**
```bash
cd web-admin
npm run dev
```
- [ ] **Step 2: 浏览器验证**
1. 打开管理后台页面(通常 `http://localhost:5174`
2. 导航至 AI 配置管理页面 (`/aiconfig/list`)
3. 切换到"调用日志" tab
4. 验证:列表中出现"调用用户"列,显示格式为 `昵称(ID)``-(ID: xxx)``-`
5. 点击任意日志的"详情"按钮,验证弹窗中"调用用户"行显示格式与列表一致
6. 打开浏览器 DevTools Console,确认无报错
- [ ] **Step 3: 最终提交(如有必要的小修复)**
```bash
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` → Java `String`,前端 `userName?: string` ✅ 一致
- `userDisplay` 函数在 AiRoutingList.vue 和 AiCallLogDetailDialog.vue 中实现相同逻辑 ✅ 一致
- 显示格式统一为 `昵称(ID)` / `-(ID: xxx)` / `-` ✅ 一致