# 审批流程图汇总 **文档版本**: 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` - 完整测试报告