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

> **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 | `server/src/main/java/com/emotion/entity/AiCallLog.java` | 实体新增 `userName` 字段 |
| Create | `server/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql` | 数据库迁移脚本 |
| Modify | `server/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java:68-73` | invokeStream 写入 userName |
| Modify | `server/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: `server/src/main/java/com/emotion/entity/AiCallLog.java:29-31`
- Create: `server/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql`
- Test: `server/` 目录下执行编译验证

- [ ] **Step 1: 在 AiCallLog 实体中新增 userName 字段**

在 `userId` 字段（第 29-30 行）下方新增：

```java
// AiCallLog.java — 在 @TableField("user_id") private String userId; 之后新增

@TableField("user_name")
private String userName;
```

- [ ] **Step 2: 创建数据库迁移脚本**

创建 `server/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 server
mvn clean compile -DskipTests
```

预期：BUILD SUCCESS

- [ ] **Step 4: 提交**

```bash
git add server/src/main/java/com/emotion/entity/AiCallLog.java
git add server/src/main/resources/db/migration/V20260525__add_user_name_to_ai_call_log.sql
git commit -m "feat: 调用日志实体新增 userName 字段及数据库迁移脚本"
```

---

### Task 2: 后端日志保存逻辑写入 userName

**Files:**
- Modify: `server/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java`
- Test: `server/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 被正确写入**

修改 `server/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 server
mvn test -Dtest=AiRuntimeServiceImplTest
```

预期：Tests run: 1, Failures: 0, Errors: 0

- [ ] **Step 5: 编译验证 + 提交**

```bash
cd server
mvn clean compile -DskipTests
```

```bash
git add server/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java
git add server/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)` / `-` ✅ 一致
