# 绩效模块走查修复报告 — 2026-03-20

## 概述

本次走查覆盖了绩效模块从目标设定 → 评估 → 校准 → 结果确认的完整生命周期。共发现并修复 **16 个问题**，涉及前后端契约不一致、前端自算逻辑越权、权限过严、数据未透传、UI 交互缺陷等。

---

## 问题清单

### 一、前端 Demo Mock 残留（1 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 1 | `performance.ts` 中有 100+ 处 `ENABLE_PERFORMANCE_DEMO` 分支和大量 mock 数据 | 开发阶段的 demo 模式代码未清理 | 删除全部 demo 代码，文件从 5356 行缩减到 2057 行 |

**为什么测试没发现**：`ENABLE_PERFORMANCE_DEMO = false`，demo 代码是死代码不影响运行，但增加维护负担和阅读成本。属于代码卫生问题，不是功能缺陷。

---

### 二、前后端契约不一致（4 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 2 | 依赖确认 API 404 | 前端路径 `/kpi/assignments/:id/confirm-dependency`，后端路径 `/kpi/dependencies/:id/confirm` | 修正前端 API 路径 |
| 3 | 依赖确认 Dialog 显示"未命名指标""0%""未知用户" | `depDialogTarget` 实际是 `KpiDependency` 结构，但前端按 `KpiAssignment` 取字段 | 改为从 `sourceAssignment`/`sourceEmployee` 取值 |
| 4 | 依赖需求描述在确认列表中不显示 | 后端 `KpiDependency.description` 未映射为前端期望的 `dependencyDescription` | 后端 `getPendingDependencyConfirmations` 加上字段映射 |
| 5 | 校准时只更新 `gradeCode` 不更新 `gradeName`，导致"A - 待改进" | `saveCalibrationAdjustment` 只写了 `gradeCode`，漏了 `gradeName` | 加上等级名称映射同步更新 |

**为什么测试没发现**：
- **#2-4 依赖对齐**：依赖确认是跨用户交互流程，需要 A 设置依赖人 → B 登录确认，单用户测试难以覆盖。L1 集成测试没有覆盖依赖确认的完整 API 链路。
- **#5 等级名称**：测试验证了 `gradeCode` 正确性但没验证 `gradeName` 是否同步更新。字段级一致性检查缺失。

---

### 三、前端越权自算（3 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 6 | 员工端"我的 KPI"页面校准阶段显示 D 等级，管理员端显示 C | 前端 fallback 自算等级分界线（90/80/70）与数据库标准（95/85/75/60）不一致 | 去掉前端所有等级 fallback 计算，`myResult` 为空时显示"—" |
| 7 | 团队 KPI 页面等级显示"待发布"即使已发布 | 前端自算等级逻辑 `finalScore >= 90 ? 'A' : ...`，不从后端取 | 后端 `getTeamAssessments` 返回 `results` 字段，前端直接用 |
| 8 | 团队 KPI 页面等级名称硬编码缺 S/D | `grade === 'A' ? '优秀' : ...` 没覆盖全 | 改为从后端 `gradeName` 取值 |

**为什么测试没发现**：
- 这是**数据源治理问题**——前端在多个组件中独立实现了等级计算逻辑，与后端标准不同步。测试时可能用的是同一套分界线，不会暴露差异。
- 根本原因：**前端不应该有等级计算逻辑**，所有等级都应由后端 `performance_result` 表提供。这是架构设计缺陷，不是测试盲区。

---

### 四、权限过严（2 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 9 | 团队 KPI 页面主管看不到下属评语 | `getOverallComment` 只允许直属经理（`managerId`），team lead 不是直属经理 | 扩展权限：支持直属经理 + 部门主管 |
| 10 | 团队 KPI 页面等级数据加载失败 | 用 `getResults` API 需要 `performance:result:view` 权限，主管没有 | 改为后端 `getTeamAssessments` 直接返回结果数据，不依赖额外权限 |

**为什么测试没发现**：
- 测试通常用 itadmin（全权限账户），不会遇到权限限制。**缺少角色切换测试**——需要用 leader/manager 角色走完整流程。

---

### 五、数据未生成/未透传（3 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 11 | 校准页面和结果页面无数据 | `ensurePerformanceResults` 幂等检查只看记录是否存在，不看是否有效（totalScore 非空），之前的空记录阻塞了重新生成 | 改为检查 `totalScore IS NOT NULL`，清理空记录后重新生成 |
| 12 | 校准页面展开行无 KPI 明细 | 后端 `getCalibrationOverview` 查了 KPI 数据但没放到返回结果里 | 加上 `assignments` 字段返回 |
| 13 | 校准页面无 360 评估分数列 | 后端已返回 `e360Score` 但前端表格没有展示列 | 加上 360 列 |

**为什么测试没发现**：
- **#11**：需要特定的脏数据场景（空 result 记录），正常流程不会产生。属于异常路径覆盖不足。
- **#12-13**：校准页面的展开功能和 360 数据展示在 L2 E2E 测试范围内，但 E2E 测试没有覆盖到校准页面的细节交互。

---

### 六、UI 交互缺陷（3 项）

| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 14 | KPI 草稿选定依赖人后不显示需求描述输入框 | 草稿卡片没有渲染依赖描述 textarea | 加上依赖描述输入框 |
| 15 | 依赖描述的对勾按钮始终显示 | 缺少 dirty 状态追踪 | 只在内容有变化或首次新增时显示，保存后消失 |
| 16 | 新增 KPI 后顺序被打乱（新的变成第 1 个） | 后端 `orderBy: { createdAt: 'desc' }` | 改为 `createdAt: 'asc'` |

**为什么测试没发现**：
- 这些是**细节交互体验问题**，E2E 测试关注"功能能否走通"，不会验证"对勾按钮何时消失"或"新增 KPI 的排序"。需要 L3 人工验收清单覆盖。

---

## 新增功能

| 功能 | 说明 |
|------|------|
| 360 评估模板适用范围 | 模板新增 `cycleId`（关联周期）和 `scope`（适用范围：全员/按部门/按职级/手动指定），后端匹配逻辑 + 前端设置 UI |
| 360 模板种子数据 | 新增 `performance-seed.ts`，初始化两个默认模板（领导力 + 通用） |
| 校准页面优化 | 去掉校准分编辑（只调等级不改分）、加上 360 分数列、优化列宽 |
| 导航调整 | "绩效校准"放到"结果发布"上面 |

---

## 根因分析与改进建议

### 问题分类统计

| 根因类别 | 数量 | 占比 |
|----------|------|------|
| 前后端契约不一致 | 4 | 25% |
| 前端越权自算 | 3 | 19% |
| 权限设计不足 | 2 | 12% |
| 数据未透传 | 3 | 19% |
| UI 交互细节 | 3 | 19% |
| 代码卫生 | 1 | 6% |

### 测试盲区与改进

| 盲区 | 改进措施 |
|------|----------|
| **单用户测试无法覆盖跨用户交互**（依赖确认） | L1 集成测试增加跨用户场景：A 创建依赖 → B 确认/拒绝 |
| **全权限账户掩盖权限问题** | E2E 测试必须包含角色切换：用 leader/manager 账号走完整流程 |
| **前端自算与后端不同步** | 制定规则：**等级、分数等业务数据禁止前端 fallback 计算**，统一从后端获取 |
| **字段级一致性未验证** | L0a/L0b 契约校验增加：同一实体的多个字段（如 gradeCode + gradeName）必须同步更新 |
| **异常路径覆盖不足** | L1 增加脏数据场景测试：空记录、null 字段、状态不一致等 |
| **UI 细节交互无覆盖** | L3 人工验收清单增加：排序保持、状态指示器、条件展示/隐藏等项 |

### 架构层面的根本改进

1. **禁止前端等级计算**：所有 `score >= X ? 'A' : ...` 逻辑必须删除。等级只来自 `performance_result` 表。
2. **API 返回完整数据**：团队视图的 API 应一次性返回关联的 result 数据，不让前端额外调需要更高权限的 API。
3. **字段映射统一**：后端同一字段在不同 API 返回时必须用同一命名（如 `dependencyDescription` 而非有的用 `description`）。

---

*生成时间：2026-03-20*
