Files
happy-life-star/docs/superpowers/plans/2026-05-25-call-log-add-user-plan.md
T

331 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 调用日志列表增加用户字段实现计划
> **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)` / `-` ✅ 一致