# 绩效模块测试后修复工作报告

## 概要

- **日期**: 2026-03-20
- **范围**: 测试完成后的人工验证 + Bug 修复 + UI 优化
- **总修复项**: 30+
- **核心发现**: 七层自动化测试覆盖的是"API 能调通、页面能打开"，但**真实用户操作链路中的权限、数据关联、UI 状态联动**几乎全部漏掉

---

## 一、修复项分类汇总

### 1. 权限/安全问题（7 项）

| # | 问题 | 根因 | 为什么测试没发现 |
|---|------|------|----------------|
| 1 | 360 summary 员工查自己返回 403 | `@RequirePermissions` 一刀切，不区分"自己的数据" | L1 全用 admin 跑，admin 自动跳过权限 |
| 2 | 360 summary 管理员查员工返回 403 | 手动 `permissions.includes()` 没考虑 Admin 角色 bypass | 同上 |
| 3 | PUT /kpi/assignments 员工编辑自己的 KPI 返回 403 | `@RequirePermissions(KPI_ASSIGN)` 员工没有 | 同上 |
| 4 | overall-comment 经理身份无校验 | manager 路径没验证是否真是该员工经理 | L1 没有多角色测试 |
| 5 | overall-comment 任意人可读他人评语 | 读取接口无隔离 | 同上 |
| 6 | POST /kpi/assignments 可替他人创建 KPI | 没校验 employeeId = 当前用户 | L1 没有越权测试 |
| 7 | 360 模板/战略目标用错权限码 | 复用了 grade/cycle 的权限 | L0a/L0b 不检查权限码 |

**根因模式**: L1 集成测试全部用 Administrator 角色跑，所有权限检查自动通过。**从未用 Employee/Leader 角色执行过任何 API**。

### 2. 数据关联/契约问题（6 项）

| # | 问题 | 根因 |
|---|------|------|
| 1 | 设置依赖人不生效 | DTO whitelist 静默丢弃未声明的 `dependentUserId` 字段 |
| 2 | 依赖确认显示"未命名指标""未知用户" | 查询没 include 关联数据 |
| 3 | KPI 删除按钮调错 API（删指标库而非删分配） | 前端 ✕ 按钮绑错了接口 |
| 4 | 战略目标 assigneeIds 静默丢弃 | service 层 create() 没接 assigneeIds |
| 5 | 360 模板创建返回不完整对象 | 后端返回 `{ id }` 但前端当完整对象用 |
| 6 | KPI 得分明细字段名不匹配 | 后端返回 `indicatorName`，前端读 `name` |

**根因模式**: L0a/L0b 契约校验只比对字段名存在性，不验证**运行时值是否正确传递**和**关联数据是否完整**。

### 3. 状态/流程问题（5 项）

| # | 问题 | 根因 |
|---|------|------|
| 1 | 校准阶段无数据 | `startCalibration` 没生成 performance_result |
| 2 | 结果确认中无确认按钮 | `startConfirming` 生成 result 但 isPublished=false |
| 3 | 自评完成后 360 区域消失 | 前端条件 `!allSelfEvalDone` 过严 |
| 4 | 校准阶段提前显示等级 | 前端硬算等级，结果发布前不应暴露 |
| 5 | 360 模板 update 可打成多默认 | update() 没撤销旧默认 |

**根因模式**: L1 测试验证了"API 返回 200"但**不验证状态推进的副作用**（推进后 result 是否生成、isPublished 是否正确）。

### 4. UI/交互问题（12+ 项）

| # | 问题 | 修复 |
|---|------|------|
| 1 | KPI 保存后整页 reload（7 处） | 改为局部 `setState` 更新 |
| 2 | 依赖描述 ✓✕ 按钮不显示 | 修复条件判断 + 始终显示 |
| 3 | 依赖人选择器无取消按钮 | 加 ✕ 按钮 |
| 4 | 日期显示原始 ISO 格式 | 格式化为本地日期 |
| 5 | 自评后 360 配置仍可编辑 | 加 `readonly` 属性锁定 |
| 6 | 校准阶段显示等级 | 改为"待发布" |
| 7 | 结果确认页分项得分重复 | 删除（大圆环已有） |
| 8 | KPI 明细和展开明细重复 | 删除重复的展开区域 |
| 9 | 360 tab 样式粗糙 | 改为 pill 风格 |
| 10 | 评语/360/KPI 明细三处重复渲染 | 抽取 `PerformanceResultDetail` 统一组件 |
| 11 | 团队页 360 提示不显示 | 加 360 未完成提示横幅 |
| 12 | 申诉无处理入口 | 新增 resolve-appeal API + UI |

**根因模式**: L2 E2E 只做页面级检查（能打开、有数据），**从未执行交互操作**（点按钮、填表单、切换角色验证状态变化）。

---

## 二、问题分布

```
权限/安全     ████████ 7 项 (23%)
数据/契约     ██████   6 项 (20%)
状态/流程     █████    5 项 (17%)
UI/交互       ████████████ 12 项 (40%)
```

## 三、测试盲区分析

| 测试层 | 能发现 | 发现不了 | 本次漏掉的 |
|--------|--------|---------|-----------|
| L0a/L0b | 字段名不匹配 | 运行时字段丢失（whitelist）、权限码对错 | 6 项 |
| L1 | API 返回码（用 admin） | 非 admin 角色权限、状态推进副作用 | 12 项 |
| L1.5 | Schema 完整性 | 种子关系正确性（manager 关系） | 1 项 |
| L2 | 页面能打开 | 交互操作、多角色流程、UI 状态联动 | 12 项 |

**一句话**: 所有问题都发生在**非管理员角色的交互操作链路**上。

## 四、架构改进

### 已完成

| 改进 | 效果 |
|------|------|
| `PerformanceResultDetail` 统一组件 | 评语+360+KPI 明细一处维护，个人/团队页共用 |
| KPI 保存局部更新（7处） | 不再整页 reload，保留滚动位置和展开状态 |
| 360 配置自评后锁定 | 提交后评估人不可增删 |
| 团队页 360 未完成提示 | 经理评分前知道 360 进度 |
| 校准阶段等级显示"待发布" | 不提前暴露等级 |

### 需要补充到测试规则

1. **L1 必须多角色**: 至少 Admin + Employee + Leader 三个角色各跑一遍核心 API
2. **L1 验证副作用**: 状态推进后检查关联表变化
3. **L0b 验证关键字段非空**: 不只检查字段名，还要检查关联字段在有数据时不为 null
4. **L2 必须执行交互**: 页面级检查不算通过，每步必须有操作+断言
5. **种子数据验证 manager 关系**: L1.5 应检查测试用户间的管理链路

---

## 五、代码改动统计

| 分类 | 文件数 | 估计行数 |
|------|--------|---------|
| 后端权限修复 | 5 | ~120 |
| 后端 API/逻辑修复 | 6 | ~200 |
| 前端 Bug 修复 | 4 | ~150 |
| 前端 UI 优化 | 5 | ~300 |
| 组件抽取 | 1（新建） | ~200 |
| 种子数据修复 | 2 | ~30 |
| Skill/文档更新 | 4 | ~200 |
| **合计** | **~27** | **~1200** |
