Files
x-server-mobile/docs/APPROVAL_NOTIFICATION_ARCHITECTURE.md
T

21 KiB
Raw Blame History

审批与消息模块架构文档

版本: 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

节点类型

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. 实现业务逻辑
    • 审批通过后的业务处理
    • 审批拒绝后的业务处理

示例:

// 请假审批
$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. 定义卡片构建方法
private function buildXXXCard(array $params): array
{
    // 返回卡片数据结构
}
  1. 添加发送方法
public function sendXXXNotification(array $params): bool
{
    // 构建卡片
    $card = $this->buildXXXCard($params);
    // 发送消息
    return $this->sendMessage($feishuUserId, $card);
}
  1. 添加更新方法(如需要)
public function updateXXXCards(...): bool
{
    // 更新逻辑
}

🔗 模块协作

协作流程

┌─────────────────────────────────────────────────────────────┐
│                    业务场景(如出差申请)                     │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
        ┌──────────────────────────────────────┐
        │   调用 ApprovalService::submit()     │
        │   - 创建审批订单                       │
        │   - 返回 approval_order               │
        └──────────────────────────────────────┘
                           │
                           ▼
        ┌──────────────────────────────────────┐
        │   调用 FeishuNotificationService     │
        │   - 发送消息给审批人                   │
        │   - 保存消息映射                       │
        │   - 记录日志                           │
        └──────────────────────────────────────┘
                           │
                           ▼
        ┌──────────────────────────────────────┐
        │   用户点击消息卡片操作                 │
        │   - 飞书回调到后端                     │
        │   - 调用 ApprovalService::approve()  │
        └──────────────────────────────────────┘
                           │
                           ▼
        ┌──────────────────────────────────────┐
        │   审批完成后                          │
        │   - 判断是否需要更新其他人的消息       │
        │   - 或签/单人:调用 updateMessageCards │
        │   - 会签:等待其他人审批               │
        └──────────────────────────────────────┘
                           │
                           ▼
        ┌──────────────────────────────────────┐
        │   流程结束                            │
        │   - 通知申请人结果                     │
        └──────────────────────────────────────┘

接口定义

审批模块 → 消息模块

// 发送审批通知
$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. 创建数据库表
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,
    ...
);
  1. 创建模型
class PurchaseRequest extends Model
{
    public function approvalOrder()
    {
        return $this->belongsTo(ApprovalOrder::class);
    }
}
  1. 创建服务
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;
        });
    }
}
  1. 创建控制器
class PurchaseController
{
    public function submit(Request $request): Response
    {
        $result = $this->service->submit($request->post());
        return $this->success($result);
    }
}

添加新的消息类型

示例:添加催办通知

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. 流程流转                                            │
│    - 移动到下一节点 或 完成流程                        │
│    - 通知下一节点审批人 / 通知申请人结果                │
└───────────────────────────────────────────────────────┘

🔍 调试与监控

消息日志查询

# 查看最近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. 异常处理:记录所有失败日志,方便排查问题

📚 相关文档


维护人: 开发团队 最后更新: 2026-02-28