# 绩效管理模块 - 状态机文档

> **module**: performance
> **doc_type**: StateMachine
> **status**: Active
> **owner**: FFOA 开发团队
> **upstream_docs**: 01-prd.md, 03-architecture.md
> **last_verified**: 2026-03-19

---

本模块包含多个核心对象，每个对象有独立的状态机。

---

## 1. 绩效周期状态机（PerformanceCycle）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | 绩效周期 |
| 状态字段 | `status` |
| 数据表 | `performance_cycle` |

### 状态列表

| 状态代码 | 说明 | 业务终态 | 生命周期终态 |
|---------|------|---------|------------|
| DRAFT | 草稿，周期创建中，可编辑等级配置 | ❌ | ❌ |
| GOAL_SETTING | 目标设定，员工创建/编辑 KPI，经理审批 | ❌ | ❌ |
| IN_PROGRESS | 执行中，KPI 只读，员工执行目标 | ❌ | ❌ |
| EVALUATING | 评估中，员工自评、配置 360 评估人、填写 360 评估、经理评分 | ❌ | ❌ |
| CALIBRATING | 校准中，HR 调整等级 | ❌ | ❌ |
| CONFIRMING | 结果确认中，员工查看结果并确认或申诉 | ❌ | ❌ |
| COMPLETED | 已完成，所有数据只读，仅允许归档 | ✅ | ❌ |
| ARCHIVED | 已归档，历史数据只读 | ✅ | ✅ |

> COMPLETED 是业务终态（不再允许任何业务修改），但不是生命周期终态（仍可归档）。ARCHIVED 是最终终态。

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| DRAFT | GOAL_SETTING | 发布周期 `publishCycle` | HR | 名称、类型、时间范围、组织、等级配置均已填写 | 409 PERF_CYCLE_004 |
| DRAFT | （删除） | 删除周期 `deleteCycle` | HR | 无关联 KPI 数据 | 409 PERF_CYCLE_005 |
| GOAL_SETTING | IN_PROGRESS | 启动执行 `startExecution` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |
| IN_PROGRESS | EVALUATING | 启动评估 `startEvaluation` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |
| EVALUATING | CALIBRATING | 启动校准 `startCalibration` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |
| CALIBRATING | CONFIRMING | 发布结果 `startConfirming` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |
| CONFIRMING | COMPLETED | 完成周期 `completeCycle` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |
| COMPLETED | ARCHIVED | 归档 `archiveCycle` | HR | HR 确认操作 | 409 PERF_CYCLE_004 |

> 所有非法状态推进（如从 EVALUATING 直接到 CONFIRMING）返回 409 PERF_CYCLE_004。

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> DRAFT: 创建周期
    DRAFT --> GOAL_SETTING: 发布
    DRAFT --> [*]: 删除
    GOAL_SETTING --> IN_PROGRESS: 启动执行
    IN_PROGRESS --> EVALUATING: 启动评估
    EVALUATING --> CALIBRATING: 启动校准
    CALIBRATING --> CONFIRMING: 发布结果
    CONFIRMING --> COMPLETED: 完成周期
    COMPLETED --> ARCHIVED: 归档
    ARCHIVED --> [*]
```

### 各阶段前端功能映射

| 阶段 | 员工可操作 | 经理可操作 | HR 可操作 |
|------|-----------|-----------|-----------|
| DRAFT | - | - | 编辑周期信息、等级配置 |
| GOAL_SETTING | 创建/编辑 KPI、提交审批、设定依赖 | 审批/驳回下属 KPI | 查看目标设定进度 |
| IN_PROGRESS | 查看 KPI（只读） | 查看团队 KPI | 推进到评估阶段 |
| EVALUATING | 自评 KPI、配置 360 评估人、填写 360 评估 | 给下属评分和评语、填写 360 评估 | 查看评估进度 |
| CALIBRATING | - | - | 调整等级 |
| CONFIRMING | 确认结果或申诉 | 查看团队结果 | 查看确认进度 |
| COMPLETED | 查看历史结果 | 查看历史结果 | 可归档 |
| ARCHIVED | 查看历史结果 | 查看历史结果 | 只读 |

---

## 2. KPI 分配状态机（KpiAssignment）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | KPI 分配记录（目标设定阶段） |
| 状态字段 | `status` |
| 数据表 | `kpi_assignment` |

### 状态列表（数据库枚举）

| 状态代码 | 说明 | 终态 | 备注 |
|---------|------|------|------|
| DRAFT | 草稿 | ❌ | 员工编辑中 |
| SUBMITTED | 已提交，等待经理审批 | ❌ | |
| APPROVED | 已批准，经理已通过 | ❌ | |
| REJECTED | 已拒绝，经理已驳回 | ❌ | |
| PENDING_DEPENDENCY | ~~废弃，保留枚举值~~ | — | v4.1 起不再使用，依赖状态由 `dependentUserId` 等字段和业务规则推导 |
| CONFIRMED | ~~废弃，保留枚举值~~ | — | v4.1 起不再使用 |

> **废弃值硬规则**：新写入不得再产生 PENDING_DEPENDENCY 或 CONFIRMED，读取旧数据时仅做兼容映射。

### 前端显示状态（由组合条件决定）

| 数据库 status | 填写完整 | 有依赖人 | 依赖确认状态（推导） | 前端显示 | 颜色 |
|--------------|---------|---------|-----------------|---------|------|
| DRAFT | ❌ | — | — | **草稿** | 灰色 |
| DRAFT | ✅ | 无 | — | **可提交** | 青色 |
| DRAFT | ✅ | 有 | 未确认 | **待确认** | 黄色 |
| DRAFT | ✅ | 有 | CONFIRMED | **可提交** | 青色 |
| SUBMITTED | — | — | — | **待审批** | 蓝色 |
| APPROVED | — | — | — | **已通过** | 绿色 |
| REJECTED | — | — | — | **已驳回** | 红色 |

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| DRAFT | SUBMITTED | 提交 KPI `submitAllKpi` | 员工 | 所有 KPI 填写完整 + 权重 100% + 有依赖的已确认 | 400 PERF_KPI_004（权重）/ 409 PERF_KPI_018（依赖未确认） |
| SUBMITTED | APPROVED | 批准 `approveKpiAssignment` | 经理 | 当前用户是该员工的经理（manager_id） | 403 PERF_COMMON_002 |
| SUBMITTED | REJECTED | 驳回 `rejectKpiAssignment` | 经理 | 填写驳回原因 | 400 PERF_COMMON_003 |
| REJECTED | DRAFT | 重新编辑 | 员工 | 周期仍处于 GOAL_SETTING | 409 PERF_CYCLE_004 |

### 依赖确认机制（独立于主状态）

依赖管理通过 `dependentUserId` 等字段和业务规则推导，不影响 KPI 主状态流转（注意：数据库中无独立的 `dependencyStatus` 字段，依赖确认状态由业务逻辑判定）：

1. 员工设置依赖人（`dependentUserId`），此时 KPI 主状态保持 DRAFT
2. 设置依赖人时要求 KPI 已填写完整
3. 依赖人在"待确认依赖"列表中看到请求
4. 依赖人确认 → 依赖确认状态变为已确认（业务规则推导），KPI 主状态不变（仍为 DRAFT）
5. 员工看到所有依赖已确认后，点"提交目标"→ SUBMITTED
6. **如果存在未确认的依赖，提交时被阻断**（返回 409 PERF_KPI_018）

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> DRAFT: 创建 KPI
    DRAFT --> SUBMITTED: 提交（填完+依赖已确认）
    SUBMITTED --> APPROVED: 经理批准
    SUBMITTED --> REJECTED: 经理驳回
    REJECTED --> DRAFT: 重新编辑
```

### 跨部门依赖机制

员工在创建 KPI 时可指定跨部门依赖：
- `dependentUserId`：依赖方用户 ID
- `dependencyDescription`：依赖描述
- 依赖方可在「我的 KPI」页面看到待确认的依赖请求
- 依赖方拒绝时需填写 `dependencyRejectionReason`

---

## 3. KPI 考核状态机（KpiAssessment）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | KPI 考核记录（评估阶段） |
| 状态字段 | `status` |
| 数据表 | `kpi_assessment` |

### 状态列表

| 状态代码 | 说明 | 终态 |
|---------|------|------|
| PENDING | 待评估，等待自评 | ❌ |
| SELF_EVALUATED | 已自评，等待经理评估 | ❌ |
| MANAGER_EVALUATED | 已他评，经理已评估 | ❌ |
| CONFIRMED | 已确认，最终评分确定 | ✅ |

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| PENDING | SELF_EVALUATED | KPI 自评 `selfEvaluateKpi` | 员工 | 周期处于 EVALUATING + 自评分数和完成情况已填写 | 409 PERF_CYCLE_004 / 400 PERF_COMMON_003 |
| SELF_EVALUATED | MANAGER_EVALUATED | 经理评分 `managerEvaluateKpi` | 经理 | 周期处于 EVALUATING + 员工已自评 + 经理评分和评语已填写 | 400 PERF_KPI_008（员工未自评）/ 400 PERF_KPI_009（评分越界） |
| MANAGER_EVALUATED | CONFIRMED | 冻结结果 | 系统 | 周期推进到 CALIBRATING 时由系统自动冻结 | — |

> MANAGER_EVALUATED → CONFIRMED 由系统在周期从 EVALUATING 推进到 CALIBRATING 时自动触发，不是人工操作。

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> PENDING: KPI 分配
    PENDING --> SELF_EVALUATED: 员工自评
    SELF_EVALUATED --> MANAGER_EVALUATED: 经理评分
    MANAGER_EVALUATED --> CONFIRMED: 系统冻结（周期→CALIBRATING）
    CONFIRMED --> [*]
```

### 评语管理

除逐项 KPI 评分外，系统支持**整体评语**：
- 员工可在自评阶段填写整体自评评语 `saveOverallEvalComment(role='self')`
- 经理可在评估阶段填写整体经理评语 `saveOverallEvalComment(role='manager')`
- 评语通过独立接口存取，不绑定在单个 KPI 上

---

## 4. 绩效结果确认状态机（ResultConfirmStatus）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | 绩效结果确认状态 |
| 状态字段 | `confirmStatus` |
| 数据表 | `performance_result` |

### 状态列表

| 状态代码 | 说明 | 终态 |
|---------|------|------|
| PENDING | 待确认，结果已发布但员工尚未确认 | ❌ |
| CONFIRMED | 已确认，员工已确认绩效结果 | ✅ |
| APPEALED | 已申诉，员工对结果提出异议 | ❌ |
| APPEAL_RESOLVED | 申诉已处理（当前后端保留，前端暂未提供处理申诉入口） | ✅ |

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| PENDING | CONFIRMED | 确认结果 `confirmResult` | 员工 | 周期处于 CONFIRMING + 结果已发布 + 未重复确认 | 409 PERF_RESULT_007（未发布）/ 409 PERF_RESULT_005（重复确认） |
| PENDING | APPEALED | 申诉 `appealResult` | 员工 | 结果已发布 + 申诉原因 ≥ 10 字符 | 400 PERF_RESULT_006（原因过短） |
| APPEALED | APPEAL_RESOLVED | 处理申诉 | HR | 申诉审核完成（前端暂无入口） | — |

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> PENDING: 结果发布
    PENDING --> CONFIRMED: 员工确认
    PENDING --> APPEALED: 员工申诉
    APPEALED --> APPEAL_RESOLVED: 申诉处理
    CONFIRMED --> [*]
    APPEAL_RESOLVED --> [*]
```

---

## 5. 360 评估状态机（Evaluation360）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | 360 评估 |
| 状态字段 | `status` |
| 数据表 | `evaluation_360` |

### 状态列表

| 状态代码 | 说明 | 终态 |
|---------|------|------|
| DRAFT | 草稿，评估配置中 | ❌ |
| IN_PROGRESS | 进行中，评估者正在填写 | ❌ |
| COLLECTING | 收集中，等待更多评估者提交 | ❌ |
| COMPLETED | 已完成，所有评估已收集，结果自动可见（评完即可见，无需 HR 发布） | ✅ |
| CANCELLED | 已取消 | ✅ |

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| DRAFT | IN_PROGRESS | 发布评估 | HR/经理 | 评估关系已配置 + 模板已选择 | 400 PERF_E360_014（无模板）/ 400 PERF_E360_004（人数不足） |
| DRAFT | CANCELLED | 取消 | HR/经理 | 状态为 DRAFT | 400 PERF_E360_003（状态不允许） |
| IN_PROGRESS | COLLECTING | 部分提交 | 系统 | 有评估者提交 | — |
| IN_PROGRESS | CANCELLED | 取消 | HR/经理 | 状态为 IN_PROGRESS | 400 PERF_E360_003 |
| COLLECTING | COMPLETED | 收集完成 | 系统 | 达到最低提交数或截止，结果自动可见 | 400 PERF_E360_010（未达最低提交数） |
| COLLECTING | CANCELLED | 取消 | HR/经理 | 状态为 COLLECTING | 400 PERF_E360_003 |

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> DRAFT: 创建评估
    DRAFT --> IN_PROGRESS: 发布
    DRAFT --> CANCELLED: 取消

    IN_PROGRESS --> COLLECTING: 部分提交
    IN_PROGRESS --> CANCELLED: 取消

    COLLECTING --> COMPLETED: 收集完成（结果自动可见）
    COLLECTING --> CANCELLED: 取消

    CANCELLED --> [*]
    COMPLETED --> [*]
```

---

## 6. 360 评估任务状态机（EvaluationTask）

### 核心对象

| 字段 | 内容 |
|------|------|
| 对象类型 | 360 评估任务（单个评估者的任务） |
| 状态字段 | `status` |
| 数据表 | `evaluation_task` |

### 状态列表

| 状态代码 | 说明 | 终态 |
|---------|------|------|
| PENDING | 待完成，等待评估者填写 | ❌ |
| SUBMITTED | 已提交，评估者已完成 | ✅ |
| EXPIRED | 已过期，超过截止时间未提交 | ✅ |
| CANCELLED | 已取消，评估被取消 | ✅ |

### 合法流转

| 当前状态 | 可流转到 | 触发动作 | 执行者 | 守卫条件 | 违反时错误 |
|---------|---------|---------|--------|---------|-----------|
| PENDING | SUBMITTED | 提交评估 | 评估者 | 必填项已填写 + 同一评估者未重复提交 + 未过截止日期 | 400 PERF_E360_008（必填未完成）/ 409 PERF_E360_015（重复提交）/ 400 PERF_E360_006（已过期） |
| PENDING | EXPIRED | 超时 | 系统 | 超过截止日期 | — |
| PENDING | CANCELLED | 取消 | HR/系统 | 父评估被取消 | — |

### 状态流转图

```mermaid
stateDiagram-v2
    [*] --> PENDING: 分配任务
    PENDING --> SUBMITTED: 提交
    PENDING --> EXPIRED: 超时
    PENDING --> CANCELLED: 取消

    SUBMITTED --> [*]
    EXPIRED --> [*]
    CANCELLED --> [*]
```

---

## 状态机关系概览

```mermaid
graph TB
    subgraph 绩效周期
        PC[PerformanceCycle<br/>8个状态]
    end

    subgraph KPI
        KAsgn[KpiAssignment<br/>分配审批4个有效状态]
        KAsmt[KpiAssessment<br/>考核评分4个状态]
    end

    subgraph 360评估
        E360[Evaluation360<br/>5个状态]
        ET[EvaluationTask<br/>4个状态]
    end

    subgraph 结果确认
        RC[ResultConfirmStatus<br/>4个状态]
    end

    PC --> KAsgn
    KAsgn --> KAsmt
    PC --> E360
    E360 --> ET
    PC --> RC
```

## 设计原则

1. **单向流转为主**：除 KPI 驳回重新编辑外，状态流转基本单向
2. **终态分层**：区分业务终态（不再允许业务修改）和生命周期终态（不再允许任何操作）
3. **人工触发**：周期阶段流转由 HR 手动触发（前端 Admin Overview 页面操作按钮）
4. **权限控制**：每个状态变更都有明确的执行者和守卫条件

## 状态变更审计

所有状态变更需记录审计日志：
- 变更前状态
- 变更后状态
- 变更时间
- 执行者
- 变更原因（如适用）

---

## 异常路径

> 本节显式声明状态机中的异常场景。AI agent 不应假设"合理行为"，而应严格按此实现。

### 不合法的状态转换

| 尝试操作 | 当前状态 | 期望行为 |
|---------|---------|---------|
| 提交 KPI 审批 | 周期非 GOAL_SETTING | 返回 409 PERF_CYCLE_004 |
| 自评打分 | 周期非 EVALUATING | 返回 409 PERF_CYCLE_004 |
| 经理评分 | 周期非 EVALUATING | 返回 409 PERF_CYCLE_004 |
| 校准调整等级 | 周期非 CALIBRATING | 返回 409 PERF_CYCLE_004 |
| 发布结果 | 周期非 CALIBRATING | 返回 409 PERF_CYCLE_004 |
| 确认/申诉结果 | 周期非 CONFIRMING | 返回 409 PERF_CYCLE_004 |
| 删除周期 | 非 DRAFT | 返回 409 PERF_CYCLE_005 |
| 编辑 KPI | 周期非 GOAL_SETTING | 返回 409 PERF_CYCLE_004 |

### 超时与未响应

| 场景 | 超时时间 | 处理方式 |
|------|---------|---------|
| 跨部门 KPI 依赖方未响应 | 当前无自动超时 | 依赖状态保持 PENDING，**未确认依赖阻塞 KPI 提交** |
| 员工未确认结果 | 当前无自动超时 | 结果保持 PENDING 状态，HR 可手动标记 |

### 并发冲突

| 场景 | 处理方式 |
|------|---------|
| 两个 HR 同时推进同一周期阶段 | 数据库事务保证只有一个成功，后者收到 409 PERF_COMMON_006 |
| 两人同时校准同一员工等级 | 后提交者提交时需基于最新状态；若版本冲突返回 409，具体策略以实现为准 |
