# 审批与消息模块架构文档 **版本**: 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