docs: 添加审批流程图汇总文档

包含:
1. 节点类型说明 (approver/and_approve/or_approve/notify)
2. 基础流程图 (完整审批流程)
3. 按节点类型的详细流程图
4. 特殊场景流程图 (拒绝/重复操作/撤销/多节点)
5. 状态流转图 (状态机)
6. 消息发送流程图
This commit is contained in:
root
2026-03-01 09:24:57 +08:00
parent ed985ff40d
commit 7e88c28cc5
+717
View File
@@ -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` - 完整测试报告