PHP 怎么流程审批

wen PHP项目 1

本文目录导读:

PHP 怎么流程审批

  1. 第一阶段:数据库设计(核心)
  2. 第二阶段:核心业务逻辑(PHP代码)
  3. 第三阶段:进阶优化与注意事项
  4. 第四阶段:使用现成组件(可选)

在 PHP 中实现流程审批(工作流)没有单一的“标准答案”,因为需求差异很大,但核心架构和思路是相通的。

今天的最佳实践是:不要在数据库里硬编码状态机,而是用“节点 + 流转记录”的通用表结构。

以下是详细的实现方案,从数据库设计到代码逻辑,再到进阶优化。


第一阶段:数据库设计(核心)

不需要复杂的 BPMN 引擎,用这三张表就能覆盖 90% 的业务场景。

审批模板表 (approval_template)

定义这条流程有几个环节,以及每个环节是谁来审批。

CREATE TABLE `approval_template` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '流程名称,如:请假审批',
  `node_order` int(11) NOT NULL COMMENT '节点顺序,1,2,3...',
  `node_name` varchar(50) NOT NULL COMMENT '节点名称,如:部门经理审批',
  `approver_type` varchar(20) NOT NULL COMMENT '审批人类型:ROLE(角色) / USER(指定人) / DEPT_LEADER(直属上级)',
  `approver_value` varchar(255) DEFAULT NULL COMMENT '对应ID,如角色ID或用户ID',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

示例数据: | id | name | node_order | node_name | approver_type | approver_value | |----|------|------------|-----------|---------------|----------------| | 1 | 请假审批 | 1 | 直属领导审批 | DEPT_LEADER | null | | 2 | 请假审批 | 2 | 人事经理审批 | ROLE | 1 | | 3 | 请假审批 | 3 | 总经理审批 | USER | 5 |

审批单据表 (每发起一条流程,就插入一条记录)

CREATE TABLE `approval_bill` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `template_name` varchar(50) DEFAULT NULL,
  `bill_no` varchar(32) NOT NULL COMMENT '业务单号,如请假单号',
  `current_node` int(11) DEFAULT '1' COMMENT '当前进行到第几个节点',
  `status` tinyint(4) DEFAULT '0' COMMENT '状态:0-审批中,1-通过,2-驳回,3-撤回',
  `submitter_id` int(11) NOT NULL COMMENT '发起人ID',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

审批记录表 (谁在什么时候做了什么操作)

CREATE TABLE `approval_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `bill_id` int(11) NOT NULL COMMENT '单据ID',
  `node_order` int(11) DEFAULT NULL COMMENT '操作的是第几节点',
  `action` varchar(10) DEFAULT NULL COMMENT '操作:AGREE(同意) / REJECT(驳回) / RETURN(退回上一步)',
  `comment` varchar(500) DEFAULT NULL COMMENT '审批意见',
  `operator_id` int(11) NOT NULL COMMENT '操作人ID',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

第二阶段:核心业务逻辑(PHP代码)

发起审批(写入流程)

public function startApproval($userId, $billNo, $templateId) {
    // 1. 查询模板所有节点
    $nodes = $this->db->query("SELECT * FROM approval_template 
                               WHERE name = (SELECT name FROM approval_template WHERE id = ?) 
                               ORDER BY node_order", [$templateId]);
    // 2. 创建审批单,当前节点为第一个节点
    $billId = $this->db->insert('approval_bill', [
        'bill_no' => $billNo,
        'current_node' => 1,
        'status' => 0,
        'submitter_id' => $userId
    ]);
    // 3. 初始化日志
    $this->db->insert('approval_log', [
        'bill_id' => $billId,
        'node_order' => 1,
        'action' => 'START',
        'operator_id' => $userId
    ]);
    // 4. 发送待办通知给第一个节点的审批人(详见下方“找审批人”)
    return $billId;
}

寻找下一个审批人(关键)

根据模板的 approver_type 动态匹配对应的人员。

public function getNextApprover($templateNode, $bill) {
    $type = $templateNode['approver_type'];
    if ($type == 'ROLE') {
        // 根据角色ID查用户(多对多,通常取一个)
        return $this->db->query("SELECT user_id FROM role_user WHERE role_id = ?", [$templateNode['approver_value']]);
    } elseif ($type == 'USER') {
        // 直接指定人
        return [$templateNode['approver_value']];
    } elseif ($type == 'DEPT_LEADER') {
        // 查询发起人的直属上级(假设用户表有 manager_id)
        $submitter = $this->db->query("SELECT manager_id FROM users WHERE id = ?", [$bill['submitter_id']]);
        return [$submitter['manager_id']];
    }
}

执行审批操作(同意/驳回/退回)

public function doApprove($billId, $operatorId, $action, $comment) {
    $bill = $this->db->query("SELECT * FROM approval_bill WHERE id = ?", [$billId]);
    $currentNode = $bill['current_node'];
    // 权限校验:确认该操作员是否在“当前节点”的审批人列表中(略)
    // 记录日志
    $this->db->insert('approval_log', [
        'bill_id' => $billId,
        'node_order' => $currentNode,
        'action' => $action,
        'comment' => $comment,
        'operator_id' => $operatorId
    ]);
    if ($action == 'REJECT') {
        // 驳回:流程直接终止,状态变为 驳回
        $this->db->update('approval_bill', ['status' => 2], $billId);
        return '驳回完成';
    }
    if ($action == 'RETURN') {
        // 退回上一级(如果上一级存在)
        if ($currentNode > 1) {
            $this->db->update('approval_bill', ['current_node' => $currentNode - 1], $billId);
            // 通知退回的审批人
        }
        return '已退回上一步';
    }
    // 同意操作
    // 检查是否还有下一节点
    $nextNodeOrder = $currentNode + 1;
    $nextNode = $this->db->query("SELECT * FROM approval_template WHERE node_order = ?", [$nextNodeOrder]);
    if (empty($nextNode)) {
        // 没有下一节点了,流程全部通过
        $this->db->update('approval_bill', ['status' => 1], $billId);
        // 通知发起人审批通过
        return '审批通过';
    } else {
        // 有下一节点,推进流程
        $this->db->update('approval_bill', ['current_node' => $nextNodeOrder], $billId);
        // 通知下一个审批人
        return '已流转到下一节点';
    }
}

第三阶段:进阶优化与注意事项

并发控制(防止重复审批)

使用乐观锁悲观锁防止两个管理员同时操作同一个单据。

// 使用数据库的悲观锁(事务中)
$this->db->query("SELECT * FROM approval_bill WHERE id = ? FOR UPDATE", [$billId]);
// 再执行后续更新逻辑

网关(条件分支)

金额大于5000需要总经理审批,否则只要经理审批。 可以在模板表中加一个 condition 字段(JSON格式),或者在代码中判断:

if ($bill['amount'] > 5000) {
    // 跳到第3节点
    $this->db->update('approval_bill', ['current_node' => 3], $billId);
} else {
    // 跳到第2节点
    $this->db->update('approval_bill', ['current_node' => 2], $billId);
}

会签(需要多个人同时审批才算通过)

这里涉及到队列和票数,可以加一张 approval_assignee 表:

-- 记录某个节点的具体处理人
CREATE TABLE `approval_task` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `bill_id` INT NOT NULL,
  `node_order` INT NOT NULL,
  `assignee_id` INT NOT NULL COMMENT '待办人ID',
  `status` TINYINT DEFAULT 0 COMMENT '0待办 1已完成',
  UNIQUE KEY `unique_bill_node_user` (`bill_id`, `node_order`, `assignee_id`)
);
  • 某个节点的待办人全部点击“同意”后,才推进到下一个节点。
  • 只要有一个点击“驳回”,流程即终止。

通知机制(实时推送)

如果使用 WebSocket(如 Workerman 或 Swoole),审批人可以通过接口拉取自己的待办列表,如果是简单的邮件/短信通知,在 doApprove 的下一步循环里调用发送逻辑即可。


第四阶段:使用现成组件(可选)

如果你不想自己造轮子,且项目技术栈兼容,可以考虑以下方案:

  1. Symfony Workflow Component:这是 Symfony 的核心组件,可以单独使用,它基于 StateMachine(状态机)的概念,用 YAML 定义转换逻辑,适合后端逻辑层使用,配合 Doctrine ORM 处理状态持久化。
    // workflow.yaml
    blog_publishing:
        type: 'state_machine'
        marking_store:
            type: 'method'
            property: 'currentPlace'
        supports:  App\Entity\BlogPost
        places:
            - draft
            - reviewed
            - rejected
        transitions:
            submit_review:
                from: draft
                to: reviewed
  2. PHP 微服务 + Buildup(自建):适合大型企业,将流程引擎独立成微服务,通过 HTTP/API 调用。

不要试图用 PHP 去实现复杂的 BPMN 画布,采用“模板 + 单据 + 日志”的轻量级设计,配合 Laravel(推荐)或 ThinkPHP 的队列和事务机制,完全能够支撑中小型项目的审批需求。

重点代码:

  • 绑定下一个审批人逻辑(getNextApprover)。
  • 处理并行节点时的“会签”集合。
  • 加锁防止并发重复提交。

抱歉,评论功能暂时关闭!