PHP项目请假审批如何流转更新排班状态数据

wen PHP项目 31

本文目录导读:

PHP项目请假审批如何流转更新排班状态数据

  1. 核心表结构设计
  2. 审批流转逻辑(核心)
  3. 取消/撤回请假时的状态恢复
  4. 动态审批流(可选增强)
  5. 排班状态与请假状态的联动时机
  6. 事务与一致性

在PHP项目中实现请假审批流转并更新排班状态数据,通常需要结合流程引擎设计(或状态机)与排班状态联动更新,下面是一个通用的设计思路和实现步骤,涵盖表结构设计、审批流程流转、以及排班状态的更新逻辑。


核心表结构设计

你需要至少包含以下几张表:

  • leave_requests (请假申请表):存储请假记录。
  • approval_flow (审批流程表):存储流程节点(如 申请人->直属上级->HR)
  • approval_records (审批记录表):存储每一步的审批意见、时间、状态。
  • schedules (排班表):存储员工每天的排班状态(值班/休息/请假/调休等)。

示例表结构(简化版):

-- 请假申请表
CREATE TABLE leave_requests (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,               -- 申请人ID
    leave_type ENUM('annual','sick','personal') NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    total_days DECIMAL(5,1) NOT NULL,   -- 请假总天数
    status ENUM('draft','pending','approved','rejected','cancelled') DEFAULT 'pending',
    current_step INT DEFAULT 1,         -- 当前审批步骤编号
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 审批流程配置表(不同公司可自定义)
CREATE TABLE approval_flow (
    id INT PRIMARY KEY AUTO_INCREMENT,
    step_no INT NOT NULL,               -- 步骤序号 (1,2,3...)
    approver_role VARCHAR(50) NOT NULL,  -- 审批角色 (如 'manager', 'hr', 'ceo')
    condition_sql TEXT DEFAULT NULL      -- 可选,动态条件跳过步骤
);
-- 审批记录表
CREATE TABLE approval_records (
    id INT PRIMARY KEY AUTO_INCREMENT,
    request_id INT NOT NULL,
    step_no INT NOT NULL,
    approver_id INT NOT NULL,
    action ENUM('approved','rejected','returned') NOT NULL,
    comment TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 排班表 (按天记录)
CREATE TABLE schedules (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    date DATE NOT NULL,
    status ENUM('working','off','leave','trip','sick') DEFAULT 'working',
    leave_request_id INT DEFAULT NULL,   -- 关联的请假单ID(可选)
    UNIQUE KEY uk_user_date (user_id, date)
);

审批流转逻辑(核心)

请假审批通常为线性流转(按步骤顺序),每次审批通过后 current_step 加1,直到结束。

(1)提交请假申请

用户提交后,status 由 draftpending,并设置 current_step = 1

(2)审批人处理(同意/驳回)

  • 后端处理函数示例:
// 审批处理(假设从请求取得当前审批人ID,步骤序号等)
public function approveLeave($leaveId, $approverId, $action, $comment) {
    // 1. 验证该审批人是否有权审批当前步骤
    $leave = LeaveRequest::find($leaveId);
    $currentStep = $leave->current_step;
    // 从审批流配置检查该步骤的角色是否符合当前审批人
    $flowStep = ApprovalFlow::where('step_no', $currentStep)->first();
    if (!$this->userHasRole($approverId, $flowStep->approver_role)) {
        throw new \Exception('无权审批此步骤');
    }
    // 2. 记录审批记录
    $record = new ApprovalRecord([
        'request_id' => $leaveId,
        'step_no' => $currentStep,
        'approver_id' => $approverId,
        'action' => $action,
        'comment' => $comment,
    ]);
    $record->save();
    // 3. 根据action更新请假单状态
    if ($action === 'approved') {
        // 获取下一步骤序号
        $nextStep = $currentStep + 1;
        $nextFlowStep = ApprovalFlow::where('step_no', $nextStep)->first();
        if ($nextFlowStep) {
            // 还有下一步 -> 只更新current_step
            $leave->current_step = $nextStep;
            $leave->save();
        } else {
            // 所有步骤已通过 -> 审批完成
            $leave->status = 'approved';
            $leave->current_step = null;
            $leave->save();
            // *** 核心:审批通过后更新排班状态 ***
            $this->updateScheduleForLeave($leave);
        }
    } elseif ($action === 'rejected') {
        $leave->status = 'rejected';
        $leave->current_step = null;
        $leave->save();
    } elseif ($action === 'returned') {
        // 回退到上一步(或者回到申请人)可自定义
        $leave->status = 'pending'; // 或者 'draft'
        $leave->current_step = max(1, $currentStep - 1);
        $leave->save();
    }
}

(3)更新排班状态(关键函数)

在审批最终通过时(或发起的瞬间?根据业务),批量更新排班表:

private function updateScheduleForLeave(LeaveRequest $leave) {
    // 获取请假起止日期
    $start = new \DateTime($leave->start_date);
    $end = new \DateTime($leave->end_date);
    $interval = new \DateInterval('P1D');
    $period = new \DatePeriod($start, $interval, $end->modify('+1 day')); // 包含结束日
    foreach ($period as $date) {
        $dateStr = $date->format('Y-m-d');
        // 检查是否已存在当天的排班(避免覆盖其他类型)
        $schedule = Schedule::updateOrCreate(
            ['user_id' => $leave->user_id, 'date' => $dateStr],
            [
                'status' => 'leave',  // 或 mapping 到具体请假类型
                'leave_request_id' => $leave->id,
            ]
        );
        // 如果原本是 'off' 休息,可选覆盖或不覆盖,根据业务
    }
}

注意:如果请假在审批通过前已经影响了排班(比如提交后立即占用名额),则应该在提交时就用“pending_leave”等中间状态标记排班,最终审批通过后变为“leave”,驳回后恢复。


取消/撤回请假时的状态恢复

如果请假单被取消或驳回,需要恢复排班状态(通常改为 offworking 取决于原始排班,建议在排班表中额外存一个 previous_status 字段以便恢复)。

public function cancelLeave($leaveId) {
    $leave = LeaveRequest::find($leaveId);
    // 先恢复排班
    Schedule::where('leave_request_id', $leaveId)
        ->update(['status' => 'working', 'leave_request_id' => null]); 
        // 如果之前是 'off',则需特殊处理,建议存储原始状态
    $leave->status = 'cancelled';
    $leave->save();
}

动态审批流(可选增强)

如果审批步骤不是固定的(比如不同部门、不同天数),可将 approval_flow 表设计成可配置的,并绑定到 leave_requests 的特定条件。

// 根据请假类型、天数动态计算审批流
public function getApprovalFlow(LeaveRequest $leave) {
    // 规则例子:1-3天经理审批, 3-7天+HR, 7天以上+老板
    if ($leave->total_days <= 3) {
        return [['step'=>1, 'role'=>'manager']];
    }
    return [['step'=>1, 'role'=>'manager'], ['step'=>2, 'role'=>'hr']];
}

在提交时动态设置 current_step 及对应的 approval_flow 表。


排班状态与请假状态的联动时机

动作 排班状态变化
提交请假(未审批) 可选:占位(状态变为 pending_leave)或无变化
审批通过 改为 leave(或具体类型)
审批驳回 恢复为原状态(需记录原始状态)
用户取消(撤回) 同上恢复
假条修改(例如改日期) 先清除旧排班,再按新日期新增(需事务)

事务与一致性

保证 请假单状态排班状态 的一致性,建议使用数据库事务 + 锁(如乐观锁 version 字段)。

DB::transaction(function () use ($leaveId, $approverId, $action) {
    // 锁住 leave_requests 行
    $leave = LeaveRequest::where('id', $leaveId)->lockForUpdate()->first();
    $this->approveLeave($leave, $approverId, $action, $comment);
    // 排班更新等...
});

  • 审批流转:利用 current_step 控制流程前进,每一步记录审批记录。
  • 排班更新:在最终状态变化(审批通过/取消)时触发批量更新 schedules 表。
  • 推荐:使用 状态机模式(如 spatie/laravel-states)封装请假状态变化,并在状态转移时通过事件监听自动更新排班,保持代码清晰扩展。

这样,你的请假审批系统既能灵活流转,又能与排班数据实时联动。

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