diff --git a/docs/APPROVAL_FLOWCHARTS.md b/docs/APPROVAL_FLOWCHARTS.md new file mode 100644 index 0000000..fe424d3 --- /dev/null +++ b/docs/APPROVAL_FLOWCHARTS.md @@ -0,0 +1,717 @@ +# 审批流程图汇总 + +**文档版本**: 1.0 +**更新时间**: 2026-03-01 +**适用版本**: feature/debug-feishu-message + +--- + +## 📋 目录 + +1. [节点类型说明](#节点类型说明) +2. [基础流程图](#基础流程图) +3. [按节点类型的流程图](#按节点类型的流程图) +4. [特殊场景流程图](#特殊场景流程图) +5. [状态流转图](#状态流转图) + +--- + +## 节点类型说明 + +### 节点类型定义 + +| 类型 | 常量 | 说明 | 完成条件 | +|------|------|------|----------| +| 单人审批 | `approver` | 配置1个审批人 | 1人通过即可完成 | +| 会签 | `and_approve` | 配置多个审批人 | 所有人通过才完成 | +| 或签 | `or_approve` | 配置多个审批人 | 任一人通过即可完成 | +| 通知 | `notify` | 无需审批 | 自动跳过 | + +### 流程状态 + +| 状态 | 常量 | 说明 | +|------|------|------| +| 待审批 | `pending` | 流程进行中 | +| 已通过 | `approved` | 流程完成(通过) | +| 已拒绝 | `rejected` | 流程结束(拒绝) | +| 已撤销 | `cancelled` | 申请人撤销 | + +--- + +## 基础流程图 + +### 完整审批流程 + +``` +┌─────────────┐ +│ 用户提交申请 │ +└──────┬──────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 创建 ApprovalOrder │ +│ - status = pending │ +│ - current_nodes = [节点1] │ +└──────┬──────────────────────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 发送飞书消息给审批人 │ +│ - 保存 feishu_message 映射 │ +│ - 记录 feishu_message_log │ +└──────┬──────────────────────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 等待审批操作 │ +│ (通过/拒绝/撤销) │ +└──────┬──────────────────────┘ + │ + ├─────────────┬─────────────┐ + ▼ ▼ ▼ + [通过] [拒绝] [撤销] + │ │ │ + ▼ ▼ ▼ + 检查节点 订单结束 订单结束 + 是否完成 (rejected) (cancelled) + │ + ├──── [是] └──── [否] + ▼ ▼ +移动下一节点 保持pending + │ + ▼ + 有下一节点? + ├──── [是] └──── [否] + │ ▼ + 发送通知 订单结束 + 给下一节点 (approved) + 审批人 + │ + ▼ + ┌─────────┐ + │ 继续等待 │ + │ 审批 │ + └─────────┘ +``` + +--- + +## 按节点类型的流程图 + +### 1. 单人审批 (approver) + +**配置**: 配置1个审批人 +**完成条件**: 1人通过即可 + +``` +提交申请 + │ + ▼ +┌───────────────────┐ +│ 当前节点: 单人审批 │ +│ 审批人: [张三] │ +│ status: pending │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 张三审批操作 │ +└─────┬─────────────┘ + │ + ├─────────────┬─────────────┐ + ▼ ▼ ▼ + [通过] [拒绝] [撤销] + │ │ │ + ▼ ▼ ▼ + 1人通过 任一人拒绝 申请人撤销 + 满足条件 流程终止 流程终止 + │ │ │ + ▼ ▼ ▼ +┌─────────┐ ┌─────────┐ ┌─────────┐ +│approved │ │rejected │ │cancelled│ +│ 已通过 │ │已拒绝 │ │已撤销 │ +└─────────┘ └─────────┘ └─────────┘ + +通知申请人 通知申请人 通知申请人 +``` + +**关键代码逻辑**: +```php +if ($node->type === 'approver') { + // 单人审批:需要一人通过 + return count($records) >= 1; +} +``` + +--- + +### 2. 会签 (and_approve) + +**配置**: 配置多个审批人 +**完成条件**: 所有人通过 + +``` +提交申请 + │ + ▼ +┌───────────────────┐ +│ 当前节点: 会签 │ +│ 审批人: [张三,李四] │ +│ status: pending │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 张三审批操作 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 已审批: 1/2 │ +│ 状态: pending │ ← 仍需其他人审批 +│ ⚠️ 继续等待 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 李四审批操作 │ +└─────┬─────────────┘ + │ + ├─────────────┬─────────────┐ + ▼ ▼ ▼ + [通过] [拒绝] [撤销] + │ │ │ + ▼ ▼ ▼ + 所有人通过 任一人拒绝 申请人撤销 + 2/2 满足条件 流程终止 流程终止 + │ │ │ + ▼ ▼ ▼ +┌─────────┐ ┌─────────┐ ┌─────────┐ +│approved │ │rejected │ │cancelled│ +│ 已通过 │ │已拒绝 │ │已撤销 │ +└─────────┘ └─────────┘ └─────────┘ + +通知申请人 通知申请人 通知申请人 +``` + +**关键代码逻辑**: +```php +if ($node->type === 'and_approve') { + // 会签:需要所有人通过 + return count($records) >= count($approverIds); +} +``` + +**测试结果**: +- 第1人通过 → 状态: pending (1/2) +- 第2人通过 → 状态: approved (2/2) ✅ + +--- + +### 3. 或签 (or_approve) + +**配置**: 配置多个审批人 +**完成条件**: 任一人通过 + +``` +提交申请 + │ + ▼ +┌───────────────────┐ +│ 当前节点: 或签 │ +│ 审批人: [张三,李四] │ +│ status: pending │ +└─────┬─────────────┘ + │ + ├─────────────┐ + ▼ │ +┌───────────────────┐ │ +│ 张三审批操作 │ │ +└─────┬─────────────┘ │ + │ │ + ▼ │ + [通过] │ + │ │ + ▼ │ +┌───────────────────┐ │ +│ 1人通过 │ │ +│ 满足条件 │ │ +│ status: approved │ │ +└─────────┬─────────┘ │ + │ │ + ▼ │ + ┌────────────┐ │ + │订单完成 │ │ + │更新其他人消息│ │ ← 或签/单人特有 + └────────────┘ │ + │ + ▼ (李四无需操作) + ┌─────────┐ + │approved │ + │ 已通过 │ + └─────────┘ +``` + +**关键代码逻辑**: +```php +if ($node->type === 'or_approve') { + // 或签:任一人通过即可 + return count($records) >= 1; +} + +// 或签/单人完成后更新其他人的消息卡片 +if ($node->type === 'or_approve' || $node->type === 'approver') { + $this->notificationService->updateMessageCards(...); +} +``` + +**测试结果**: +- 第1人通过 → 状态: approved (1/2) ✅ + +--- + +### 4. 通知节点 (notify) + +**配置**: 配置通知接收人 +**完成条件**: 无需审批,自动跳过 + +``` +前置节点完成 + │ + ▼ +┌───────────────────┐ +│ 检查下一节点 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 下一节点是通知? │ +└─────┬─────────────┘ + │ + ├──── [是] └──── [否] (审批节点) + │ │ + ▼ ▼ +┌───────────────────┐ 继续等待审批 +│ 自动跳过 │ +│ 发送通知给接收人 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 检查还有后续节点? │ +└─────┬─────────────┘ + │ + ├──── [有] └──── [无] + │ │ + ▼ ▼ + 继续处理 订单完成 + 下一节点 (approved) +``` + +**代码逻辑**: +```php +if ($nextNode && $nextNode->type === 'notify') { + // 下一个是抄送节点,跳过并完成流程 + $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; + $approvalOrder->current_nodes = []; +} +``` + +--- + +## 特殊场景流程图 + +### 1. 拒绝流程 (所有节点类型通用) + +``` +审批进行中 + │ + ▼ +┌───────────────────┐ +│ 任一审批人拒绝 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 记录拒绝操作 │ +│ action: reject │ +│ comment: 原因... │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 订单状态 → rejected│ +│ 流程立即终止 │ +│ 后续节点不执行 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 通知申请人 │ +│ 「您的申请已被拒绝」│ +└───────────────────┘ +``` + +**代码逻辑**: +```php +if ($action === 'reject') { + // 拒绝:直接结束流程 + $approvalOrder->status = ApprovalOrder::STATUS_REJECTED; + $approvalOrder->current_nodes = []; +} +``` + +--- + +### 2. 重复操作防护 + +``` +审批人尝试操作 + │ + ▼ +┌───────────────────┐ +│ 检查是否已审批 │ +│ 查询 approval_record│ +└─────┬─────────────┘ + │ + ├──── [已审批] └──── [未审批] + │ │ + ▼ ▼ +┌───────────────────┐ 继续处理 +│ 抛出异常 │ +│ 「您已经XX过此申请」│ +└───────────────────┘ +``` + +**代码逻辑**: +```php +$existingRecord = ApprovalRecord::where('order_id', $order->id) + ->where('approver_id', $approverId) + ->whereIn('action', [ApprovalRecord::ACTION_APPROVE, ApprovalRecord::ACTION_REJECT]) + ->first(); + +if ($existingRecord) { + throw new \Exception("您已经{$action}过此申请,无需重复操作"); +} +``` + +--- + +### 3. 撤销流程 + +``` +审批进行中 + │ + ▼ +┌───────────────────┐ +│ 申请人撤销申请 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 记录撤销操作 │ +│ action: cancel │ +│ operato: 申请人 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 订单状态 → cancelled│ +│ 流程立即终止 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 通知所有审批人 │ +│ 「申请已被撤销」 │ +└───────────────────┘ +``` + +**代码逻辑**: +```php +public function cancel(int $orderId, int $userId): ApprovalOrder +{ + $order = ApprovalOrder::findOrFail($orderId); + + if ($order->applicant_id !== $userId) { + throw new \Exception('只有申请人可以撤销'); + } + + // 记录撤销操作 + ApprovalRecord::create([ + 'order_id' => $order->id, + 'node_id' => $currentNodeId, + 'action' => 'cancel', + 'operator_id' => $userId, + ]); + + $order->status = ApprovalOrder::STATUS_CANCELLED; + $order->current_nodes = []; + $order->save(); +} +``` + +--- + +### 4. 多节点流转 + +``` +提交申请 + │ + ▼ +┌───────────────────┐ +│ 节点1: 会签 │ +│ [张三, 李四] │ +│ status: pending │ +└─────┬─────────────┘ + │ + ├─────────────┐ + ▼ │ + 所有人通过 │ + │ │ + ▼ │ +┌───────────────────┐ │ +│ 节点1完成 │ │ +│ current_nodes │ │ +│ → [节点2] │ │ +└─────┬─────────────┘ │ + │ │ + ▼ │ +┌───────────────────┐ │ +│ 节点2: 或签 │ │ +│ [王五, 赵六] │ │ +│ status: pending │ │ +└─────┬─────────────┘ │ + │ │ + ├─────────────┐ │ + ▼ │ │ + 任一人通过 │ │ + │ │ │ + ▼ │ │ +┌───────────────────┐ │ +│ 节点2完成 │ │ +│ current_nodes │ │ +│ → [节点3] │ │ +└─────┬─────────────┘ │ + │ │ + ▼ │ +┌───────────────────┐ │ +│ 节点3: 通知 │ │ +│ 自动跳过 │ │ +└─────┬─────────────┘ │ + │ │ + ▼ │ +┌───────────────────┐ │ +│ 流程完成 │ │ +│ status: approved │ │ +│ current_nodes: [] │ │ +└───────────────────┘ │ + │ + ▼ + 通知申请人 +``` + +**关键代码逻辑**: +```php +// 获取当前节点的 order 值 +$currentOrder = $node->order ?? 0; + +// 查找 order 比当前节点大的下一个节点 +$nextNode = ApprovalNode::where('flow_id', $approvalOrder->flow_id) + ->where('order', '>', $currentOrder) + ->orderBy('order') + ->first(); + +if ($nextNode) { + if ($nextNode->type === 'notify') { + // 跳过通知节点,完成流程 + $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; + } else { + // 移动到下一节点 + $approvalOrder->current_nodes = [$nextNode->id]; + } +} else { + // 没有下一节点,流程完成 + $approvalOrder->status = ApprovalOrder::STATUS_APPROVED; +} +``` + +--- + +## 状态流转图 + +### 完整状态机 + +``` + ┌─────────────┐ + │ pending │ ◄─── 初始状态 + │ (待审批) │ + └──────┬──────┘ + │ + ┌─────────────────┼─────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ 节点完成 │ │ 任何人 │ │ 申请人 │ + │检查 │ │ 拒绝 │ │ 撤销 │ + └────┬────┘ └────┬────┘ └────┬────┘ + │ │ │ + │ ▼ ▼ + │ ┌─────────┐ ┌─────────┐ + │ │rejected │ │cancelled│ + │ └─────────┘ └─────────┘ + │ + ▼ + ┌─────────────┐ + │ 所有人通过? │ + └────┬────────┘ + │ + ├──── [是] └──── [否] (继续等待) + │ + ▼ + ┌─────────────┐ + │ 或签/单人 │ + │ 任人通过? │ + └────┬────────┘ + │ + ├──── [是] └──── [否] (继续等待) + │ + ▼ + ┌─────────────┐ + │ 有下一节点? │ + └────┬────────┘ + │ + ├──── [是] └──── [否] + │ │ + ▼ ▼ + ┌─────────┐ ┌─────────┐ + │移动到 │ │approved │ + │下一节点 │ └─────────┘ + └─────────┘ +``` + +### 状态转换条件表 + +| 当前状态 | 触发条件 | 下一状态 | 说明 | +|----------|----------|----------|------| +| pending | 所有人通过(会签) | pending | 还有下一节点 | +| pending | 所有人通过(会签)+ 无下一节点 | approved | 流程完成 | +| pending | 任一人通过(或签/单人) | pending | 还有下一节点 | +| pending | 任一人通过(或签/单人)+ 无下一节点 | approved | 流程完成 | +| pending | 任一人拒绝 | rejected | 流程终止 | +| pending | 申请人撤销 | cancelled | 流程终止 | +| pending | 下一节点是notify | approved | 自动跳过通知节点 | + +--- + +## 消息发送流程图 + +### 消息类型 + +``` +申请提交 + │ + ▼ +┌───────────────────┐ +│ 检查审批人列表 │ +│ form_data.approvers│ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 遍历每个审批人 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 是否是申请人自己? │ +└─────┬─────────────┘ + │ + ├──── [是] └──── [否] + │ │ + ▼ ▼ +┌─────────────┐ ┌─────────────┐ +│发送文本消息 │ │发送卡片消息 │ +│(自审批) │ │(带审批按钮) │ +└─────────────┘ └────┬────────┘ + │ + ▼ + ┌─────────────┐ + │保存消息映射 │ + │feishu_message│ + │message_id │ + └─────────────┘ +``` + +### 消息卡片更新流程 (或签/单人特有) + +``` +订单完成(或签/单人) + │ + ▼ +┌───────────────────┐ +│ 获取该节点的所有 │ +│ 消息映射 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 遍历每个其他审批人 │ +│ (排除已操作的人) │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 更新消息卡片 │ +│ 移除审批按钮 │ +│ 显示「已被XX处理」 │ +└─────┬─────────────┘ + │ + ▼ +┌───────────────────┐ +│ 记录更新日志 │ +│ action: update_card│ +└───────────────────┘ +``` + +--- + +## 附录:数据模型关系 + +### 核心表关系 + +``` +approval_flow (审批流程) + │ + ├── approval_node (审批节点) + │ ├── 1: approver (单人) + │ ├── 2: and_approve (会签) + │ ├── 3: or_approve (或签) + │ └── 4: notify (通知) + │ + └── approval_order (审批订单) + │ + ├── form_data (表单数据) + │ ├── approvers: [1, 2, 3] + │ └── cc_users: [4, 5] + │ + ├── approval_record (审批记录) + │ ├── action: approve/reject/cancel/submit + │ └── approver_id: 用户ID + │ + └── feishu_message (消息映射) + └── message_id: 飞书消息ID + +feishu_message_log (消息日志) + ├── action: send/update/update_card + ├── status: success/failed + └── error_message: 错误信息 +``` + +--- + +**文档维护**: 开发团队 +**最后更新**: 2026-03-01 +**相关文档**: +- `docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md` - 审批通知架构文档 +- `docs/TEST_REPORT_COMPLETE_20260301.md` - 完整测试报告