From 254f0073dcda2fab1db8e60133024fd1fbea2894 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 28 Feb 2026 22:57:48 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=B8=8E=E6=B6=88=E6=81=AF=E6=A8=A1=E5=9D=97=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md | 548 +++++++++++++++++++++ 1 file changed, 548 insertions(+) create mode 100644 docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md diff --git a/docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md b/docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md new file mode 100644 index 0000000..9cace2e --- /dev/null +++ b/docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md @@ -0,0 +1,548 @@ +# 审批与消息模块架构文档 + +**版本**: 1.0.0 +**更新时间**: 2026-02-28 +**状态**: 设计中 + +--- + +## 📋 概述 + +系统采用**模块化设计**,审批模块和消息模块是两个独立但可联合使用的核心模块。 + +### 设计原则 + +1. **模块独立**:各模块可独立部署和使用 +2. **松耦合**:模块间通过接口通信,不直接依赖 +3. **可扩展**:支持新增业务场景(如请假、采购、报销等) +4. **统一协议**:所有业务场景遵循相同的审批和消息协议 + +--- + +## 🏗️ 模块架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 业务场景层 │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 出差审批 │ │ 请假审批 │ │ 采购审批 │ │ 费用报销 │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 审批引擎层(ApprovalService) │ +│ - 流程管理 - 节点管理 - 订单管理 - 记录管理 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 消息服务层(FeishuNotificationService) │ +│ - 消息发送 - 卡片构建 - 消息更新 - 日志记录 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 飞书平台 │ +│ - 消息推送 - 卡片交互 - 回调处理 │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🔧 审批模块(Approval Module) + +### 核心模型 + +| 模型 | 说明 | 关键字段 | +|------|------|----------| +| `ApprovalFlow` | 审批流程模板 | business_type, name, status | +| `ApprovalNode` | 审批节点 | type, approvers, order | +| `ApprovalOrder` | 审批订单实例 | business_type, business_id, form_data, current_nodes, status | +| `ApprovalRecord` | 审批操作记录 | order_id, node_id, approver_id, action | + +### 节点类型 + +```php +const TYPE_APPROVER = 'approver'; // 单人审批:1人通过即可 +const TYPE_AND_APPROVE = 'and_approve'; // 会签:所有人通过 +const TYPE_OR_APPROVE = 'or_approve'; // 或签:1人通过即可 +const TYPE_NOTIFY = 'notify'; // 通知节点:无需审批 +``` + +### 审批状态流转 + +``` +draft(草稿) + ↓ submit +pending(审批中) + ↓ + ├─→ approved(已通过) + ├─→ rejected(已拒绝) + └─→ cancelled(已撤销) +``` + +### 核心方法 + +| 方法 | 说明 | +|------|------| +| `submit()` | 提交审批 | +| `approve()` | 审批通过 | +| `reject()` | 审批拒绝 | +| `cancel()` | 撤销申请 | +| `isNodeCompleted()` | 判断节点是否完成 | + +### 扩展新业务场景 + +**步骤:** + +1. **创建业务模型**(如 `LeaveRequest`) +2. **定义流程类型**(`business_type = 'leave'`) +3. **实现审批逻辑** + - 调用 `ApprovalService::submit()` 创建审批订单 + - 在 `form_data` 中存储业务数据 +4. **实现业务逻辑** + - 审批通过后的业务处理 + - 审批拒绝后的业务处理 + +**示例:** +```php +// 请假审批 +$approvalOrder = $approvalService->submit([ + 'business_type' => 'leave', + 'business_id' => $leaveRequest->id, + 'title' => '请假申请 - ' . $user->name, + 'applicant_id' => $user->id, + 'flow_id' => $flow->id, + 'form_data' => [ + 'reason' => $leaveRequest->reason, + 'start_date' => $leaveRequest->start_date, + 'end_date' => $leaveRequest->end_date, + 'approvers' => $selectedApprovers, + 'cc_users' => $selectedCcUsers, + ], +]); +``` + +--- + +## 📨 消息模块(Notification Module) + +### 核心模型 + +| 模型 | 说明 | 关键字段 | +|------|------|----------| +| `FeishuMessage` | 消息映射表 | order_id, node_id, user_id, message_id, status | +| `FeishuMessageLog` | 消息日志表 | action, order_id, message_id, status, request_data, response_data | + +### 消息类型 + +| 类型 | 场景 | 卡片样式 | +|------|------|----------| +| 审批通知 | 发送给审批人 | 带通过/拒绝按钮 | +| 系统通知 | 发送给抄送人 | 带查看详情按钮 | +| 文本通知 | 自审批场景 | 纯文本,无按钮 | +| 结果通知 | 审批结果通知 | 显示操作结果 | + +### 核心方法 + +| 方法 | 说明 | +|------|------| +| `sendApprovalNotification()` | 发送审批通知(带操作按钮) | +| `sendSystemNotification()` | 发送系统通知(只读) | +| `updateMessageCards()` | 更新消息卡片(移除按钮) | +| `buildApprovalCard()` | 构建审批卡片 | +| `buildSystemCard()` | 构建系统通知卡片 | + +### 消息生命周期 + +``` +1. 发送消息 + - 调用飞书 API + - 获取 message_id + - 保存映射到 feishu_message + - 记录日志到 feishu_message_log + +2. 用户操作 + - 点击卡片按钮 + - 飞书回调处理 + - 执行审批逻辑 + +3. 更新消息(或签/单人场景) + - 节点完成后触发 + - 更新其他审批人的卡片 + - 移除操作按钮 + - 记录日志 +``` + +### 扩展新消息类型 + +**步骤:** + +1. **定义卡片构建方法** +```php +private function buildXXXCard(array $params): array +{ + // 返回卡片数据结构 +} +``` + +2. **添加发送方法** +```php +public function sendXXXNotification(array $params): bool +{ + // 构建卡片 + $card = $this->buildXXXCard($params); + // 发送消息 + return $this->sendMessage($feishuUserId, $card); +} +``` + +3. **添加更新方法**(如需要) +```php +public function updateXXXCards(...): bool +{ + // 更新逻辑 +} +``` + +--- + +## 🔗 模块协作 + +### 协作流程 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 业务场景(如出差申请) │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ 调用 ApprovalService::submit() │ + │ - 创建审批订单 │ + │ - 返回 approval_order │ + └──────────────────────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ 调用 FeishuNotificationService │ + │ - 发送消息给审批人 │ + │ - 保存消息映射 │ + │ - 记录日志 │ + └──────────────────────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ 用户点击消息卡片操作 │ + │ - 飞书回调到后端 │ + │ - 调用 ApprovalService::approve() │ + └──────────────────────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ 审批完成后 │ + │ - 判断是否需要更新其他人的消息 │ + │ - 或签/单人:调用 updateMessageCards │ + │ - 会签:等待其他人审批 │ + └──────────────────────────────────────┘ + │ + ▼ + ┌──────────────────────────────────────┐ + │ 流程结束 │ + │ - 通知申请人结果 │ + └──────────────────────────────────────┘ +``` + +### 接口定义 + +**审批模块 → 消息模块** + +```php +// 发送审批通知 +$notificationService->sendApprovalNotification([ + 'user_id' => $approverId, + 'applicant_id' => $applicantId, + 'title' => '新的审批待办', + 'content' => '...', + 'url' => $detailUrl, + 'order_id' => $orderId, + 'node_id' => $nodeId, +]); + +// 更新消息卡片(或签/单人场景) +$notificationService->updateMessageCards( + $orderId, + $nodeId, + $excludeUserId, + 'approved', + $operatorName +); +``` + +--- + +## 🚧 扩展指南 + +### 添加新的审批业务场景 + +**示例:添加采购审批** + +1. **创建数据库表** +```sql +CREATE TABLE purchase_request ( + id bigint unsigned PRIMARY KEY, + user_id bigint unsigned NOT NULL, + reason varchar(500), + amount decimal(10,2), + status enum('draft', 'pending', 'approved', 'rejected'), + approval_order_id bigint unsigned, + ... +); +``` + +2. **创建模型** +```php +class PurchaseRequest extends Model +{ + public function approvalOrder() + { + return $this->belongsTo(ApprovalOrder::class); + } +} +``` + +3. **创建服务** +```php +class PurchaseService +{ + private $approvalService; + + public function submit(array $data): PurchaseRequest + { + return DB::transaction(function () use ($data) { + // 创建采购申请 + $purchase = PurchaseRequest::create($data); + + // 创建审批订单 + $approvalOrder = $this->approvalService->submit([ + 'business_type' => 'purchase', + 'business_id' => $purchase->id, + 'title' => '采购申请 - ' . $data['item_name'], + 'applicant_id' => $data['user_id'], + 'flow_id' => $this->getFlowId(), + 'form_data' => [ + 'item_name' => $data['item_name'], + 'amount' => $data['amount'], + 'approvers' => $data['approvers'], + 'cc_users' => $data['cc_users'], + ], + ]); + + // 关联审批订单 + $purchase->approval_order_id = $approvalOrder->id; + $purchase->status = 'pending'; + $purchase->save(); + + // 发送通知 + $this->sendNotifications($purchase, $approvalOrder); + + return $purchase; + }); + } +} +``` + +4. **创建控制器** +```php +class PurchaseController +{ + public function submit(Request $request): Response + { + $result = $this->service->submit($request->post()); + return $this->success($result); + } +} +``` + +### 添加新的消息类型 + +**示例:添加催办通知** + +```php +class FeishuNotificationService +{ + /** + * 发送催办通知 + */ + public function sendReminderNotification(array $params): bool + { + $userId = $params['user_id']; + $card = $this->buildReminderCard($params); + + // 保存消息映射(如需要) + if (isset($params['order_id']) && isset($params['node_id'])) { + \app\model\FeishuMessage::saveMessage( + $params['order_id'], + $params['node_id'], + $userId, + $messageId + ); + } + + return $this->sendMessage($feishuUserId, $card); + } + + private function buildReminderCard(array $params): array + { + return [ + 'header' => [ + 'title' => ['content' => '⏰ 审批催办', 'tag' => 'plain_text'], + 'template' => 'orange', + ], + 'elements' => [ + [ + 'tag' => 'div', + 'text' => [ + 'content' => $params['content'], + 'tag' => 'lark_md', + ], + ], + [ + 'tag' => 'action', + 'actions' => [ + [ + 'tag' => 'button', + 'text' => ['content' => '立即处理', 'tag' => 'plain_text'], + 'type' => 'default', + 'url' => $params['url'], + ], + ], + ], + ], + ]; + } +} +``` + +--- + +## 📊 数据流向图 + +### 完整审批流程数据流 + +``` +用户提交申请 + ↓ +┌───────────────────────────────────────────────────────┐ +│ 1. 业务表(business_trip) │ +│ - 创建业务记录 │ +│ - status = 'draft' │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 2. 审批订单(approval_order) │ +│ - business_type = 'business_trip' │ +│ - business_id = 业务记录ID │ +│ - form_data = { │ +│ approvers: [用户选择的审批人], │ +│ cc_users: [用户选择的抄送人], │ +│ ...业务数据 │ +│ } │ +│ - current_nodes = [第一个节点ID] │ +│ - status = 'pending' │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 3. 消息映射(feishu_message) │ +│ - order_id + node_id + user_id → message_id │ +│ - 每个审批人一条记录 │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 4. 飞书平台 │ +│ - 发送卡片消息给审批人 │ +└───────────────────────────────────────────────────────┘ + ↓ +审批人操作 + ↓ +┌───────────────────────────────────────────────────────┐ +│ 5. 审批记录(approval_record) │ +│ - order_id, node_id, approver_id │ +│ - action = 'approve' 或 'reject' │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 6. 节点完成判断 │ +│ - 检查 form_data.approvers(实际选择的审批人) │ +│ - 节点类型决定完成条件 │ +│ - 或签/单人:立即完成,触发消息更新 │ +│ - 会签:等待所有人 │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 7. 消息更新(或签/单人场景) │ +│ - 更新其他审批人的消息卡片 │ +│ - 移除操作按钮 │ +│ - 标记消息状态 = 'updated' │ +└───────────────────────────────────────────────────────┘ + ↓ +┌───────────────────────────────────────────────────────┐ +│ 8. 流程流转 │ +│ - 移动到下一节点 或 完成流程 │ +│ - 通知下一节点审批人 / 通知申请人结果 │ +└───────────────────────────────────────────────────────┘ +``` + +--- + +## 🔍 调试与监控 + +### 消息日志查询 + +```bash +# 查看最近50条日志 +curl "http://localhost:8787/debug/message-logs?debug=1&limit=50" + +# 查看指定订单的日志 +curl "http://localhost:8787/debug/message-logs?debug=1&order_id=123" + +# 直接查询数据库 +docker exec 1Panel-mysql-409s mysql -uroot -pmysql_xEsdPa xserver -e " +SELECT * FROM feishu_message_log +WHERE order_id = 123 +ORDER BY created_at DESC; +" +``` + +### 日志字段说明 + +| 字段 | 说明 | +|------|------| +| `action` | send(发送)、update(更新)、update_card(更新卡片) | +| `status` | success(成功)、failed(失败) | +| `request_data` | 发送的数据(JSON) | +| `response_data` | 飞书返回的数据(JSON) | +| `error_message` | 错误信息(如有) | + +--- + +## 📝 注意事项 + +1. **审批人来源**:始终从 `form_data.approvers` 获取,不是从 `node.approvers` +2. **消息更新**:仅在或签/单人审批完成时触发 +3. **防止重复**:审批前检查是否已操作过 +4. **事务处理**:业务逻辑和审批创建在同一事务中 +5. **异常处理**:记录所有失败日志,方便排查问题 + +--- + +## 📚 相关文档 + +- [数据库结构文档](./DATABASE_SCHEMA.md) +- [项目文档](./PROJECT.md) + +--- + +**维护人**: 开发团队 +**最后更新**: 2026-02-28