# ERR-20260501-004 — 多节点流程审批人解析失败时静默跳过 + 整流程标 APPROVED

## 现象
"L2 多节点会签表单"（4 节点：START → Approver(INITIATOR_MANAGER) → Approver(INITIATOR_MANAGER) → END）。

itadmin 用户没有 manager 配置（`UserDepartment` 里没 manager 关系）。
submit 这张表 → workflow 启动 → 立刻完成 → **整流程 status=APPROVED**。

## 根因
worker 日志：
```
Creating node instance: node_1777539077745_rez7ufcjt for instance 542c2076-...
Resolving approvers: initiator:manager
Skipping node instance: f2b3effd-..., reason: No approvers resolved
Creating node instance: node_1777539091027_jsukyfoc0 for instance 542c2076-...
Resolving approvers: initiator:manager
Skipping node instance: d8acbd22-..., reason: No approvers resolved
Creating node instance: end for instance 542c2076-...
Updating process status: 542c2076-... to APPROVED
Process completed: 542c2076-... with status APPROVED
```

approval engine 的节点处理逻辑：当 approverType 解析后得到空列表（INITIATOR_MANAGER 但 initiator 没 manager），**静默 skip 节点**，继续推进到下一个节点。所有 USER_TASK 节点都 skip 完后到 END，process 标 APPROVED。

**等于"无人审批 = 自动通过"**，违反业务约束（审批必须有真实审批动作）。

## 业务影响
任何多节点流程，如果某个节点的 approverType 解析时不到人（manager/部门长/角色为空 / 用户离职），整流程会**自动通过**。可能场景：
- 新员工提交申请，但他还没分配 manager → 直接通过
- approverType=ROLE 配的角色被禁用 → 该节点 skip
- 部门主管离职后未指派接任人，部门内员工的申请直接通过

## 应有行为（任选其一）
1. **报错挂起**：throw "无法解析审批人" → workflow paused → 管理员介入
2. **fallback 到默认审批人**：流程定义层加 fallback 字段（如部门 admin / 全局 admin）
3. **拒绝**：approverType 解析失败 → workflow REJECT，发起人收到通知
4. **跳过 + 警告标记**：保留当前行为但额外写 audit log + 在 instance 上加"unverified"标志，让审计能追溯

## 处置 — **已修复（PR #214）**
用户给的策略：**流程挂起等管理员**。

### 实现
1. 新 activity `suspendInstanceForUnresolvedApprovers`：
   - ApprovalInstance.status = SUSPENDED + endReason='APPROVER_UNRESOLVED'（**不设 endTime**，可恢复）
   - ApprovalNodeInstance.status = PENDING
   - 向所有 Administrator 发 IN_APP 通知（带 instanceId / nodeInstanceId / initiatorId 让管理员定位）

2. 新 activity `resumeInstanceFromSuspension`：
   - 清 endReason / endComment
   - status SUSPENDED → RUNNING

3. Workflow `generic-approval.workflow.ts`：
   - 新 signal `resumeWithApprovers` (payload: `{ approverIds, resolvedBy }`)
   - approvers 解析失败 → 调 suspendActivity + state.status='SUSPENDED' + `condition()` 等
     `state.resumedApprovers || state.shouldTerminate`
   - shouldTerminate → TERMINATE 终止
   - resumedApprovers → 用注入的列表替代未解析的，调 resumeActivity 恢复 RUNNING，继续 createApprovalTask 等

4. 新 admin API `POST /api/v1/approval/admin/:instanceId/resume-with-approvers`
   - DTO `AdminResumeWithApproversDto { approverIds: UUID[], reason: string }`
   - 状态校验：必须是 SUSPENDED；approverIds 不能为空
   - 调 `temporalService.sendSignal(workflowId, 'resumeWithApprovers', payload)`
   - 写审计日志（action=ADMIN_REASSIGN）

5. L1 测试 `instance-unresolved-approvers.api.test.ts`（5 用例）：
   - suspendActivity 直调：DB 状态 + 通知发送
   - resumeActivity 直调：状态回退
   - API 端到端：成功路径 + RUNNING 状态 400 + 空 approverIds 400

### 留给后续 PR
- 给 frontend 加"挂起流程恢复"管理员页面（点击挂起实例 → 选审批人 → 调本接口）
- 通知模板规范化（当前 subject + content 是硬编码字符串，应走 NotificationTemplate）
- 多个 SUSPENDED 节点串联场景的 e2e（一个流程多次解析失败连续挂起）

## 相关文件
- `backend/src/engines/approval/temporal/activities/approval.activities.ts` 的 `resolveApprovers` + node-skip 逻辑
- `backend/src/engines/approval/temporal/workflows/generic-approval.workflow.ts`

## 适用范围
任何 approval engine 多节点流程的审批人动态解析路径（INITIATOR_MANAGER / DEPT_LEADER / ROLE_BASED 等）。
