Files
fdesk/app/common/utils/approval/service/ApprovalFlowService_bak.php
T

431 lines
16 KiB
PHP
Raw Normal View History

2025-09-04 10:26:28 +08:00
<?php
2025-09-08 11:34:19 +08:00
namespace app\common\utils\approval\service;
2025-09-04 10:26:28 +08:00
use DateTime;
use support\exception\BusinessException;
use support\Db;
use app\common\approval\model\ApprovalCcs;
use app\common\approval\model\ApprovalFlows;
use app\common\approval\model\ApprovalInstances;
use app\common\approval\model\ApprovalRecords;
class ApprovalFlowService_bak
{
// --- 公共方法 ---
/**
* 启动一个审批实例
* @param string $flowType 流程类型标识 (approval_flows.type)
* @param string $bizType 业务类型标识 (如 'leave_application')
* @param string $bizId 业务数据ID (如 leave_applications 表的 ID)
* @param array $formData 提交的表单数据快照
* @param int $initiatorId 发起人ID
* @return array ['success' => true, 'instance_id' => ...] 或抛出异常
*/
public function start(string $flowType, string $bizType, string $bizId, array $formData, int $initiatorId): array
{
// 1. 获取流程定义
$flow = Db::table('approval_flows')
->where('type', $flowType)
->where('is_active', 1)
->first();
if (!$flow) {
throw new BusinessException("审批流程 '{$flowType}' 不存在或已停用");
}
// 2. 创建审批实例
$instanceId = Db::table('approval_instances')->insertGetId([
'flow_type' => $flowType,
'biz_id' => $bizId,
'biz_type' => $bizType,
'form_data' => json_encode($formData),
'initiator_id' => $initiatorId,
'status' => 'processing',
'current_step' => 0,
'deadline_at' => $flow->timeout_hours > 0 ?
(new DateTime())->modify("+{$flow->timeout_hours} hours")->format('Y-m-d H:i:s') : null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
// 3. 通知下一个审批人
$this->notifyNextApprovers($instanceId);
return ['success' => true, 'instance_id' => $instanceId];
}
/**
* 执行审批操作
* @param int $instanceId 审批实例ID
* @param int $approverId 审批人ID
* @param string $action 操作 (approve, reject, revoke)
* @param string $comment 审批意见
* @return array 操作结果
*/
public function approve(int $instanceId, int $approverId, string $action, string $comment = ''): array
{
// 1. 获取实例和流程
$instance = Db::table('approval_instances')->find($instanceId);
if (!$instance) {
throw new BusinessException("审批实例不存在");
}
if ($instance->status !== 'processing') {
throw new BusinessException("审批实例状态不正确");
}
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
if (!$flow) {
throw new BusinessException("关联的审批流程不存在");
}
$steps = json_decode($flow->steps, true);
$currentStepIndex = $instance->current_step;
if ($currentStepIndex >= count($steps)) {
throw new BusinessException("审批流程已结束");
}
$currentStep = $steps[$currentStepIndex];
// 2. 验证权限和条件
if (!$this->canUserApproveStep($currentStep, $approverId, $instance)) {
throw new BusinessException("您无权审批此步骤");
}
// 3. 记录审批行为
Db::table('approval_records')->insert([
'instance_id' => $instanceId,
'approver_id' => $approverId,
'step_index' => $currentStepIndex,
'action' => $action,
'comment' => $comment,
'approved_at' => date('Y-m-d H:i:s'),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
// 4. 处理审批结果
$updateData = ['updated_at' => date('Y-m-d H:i:s')];
if ($action === 'reject') {
$updateData['status'] = 'rejected';
$updateData['finished_at'] = date('Y-m-d H:i:s');
Db::table('approval_instances')->where('id', $instanceId)->update($updateData);
$this->notifyInitiator($instance, 'rejected');
return ['result' => 'rejected'];
}
if ($action === 'revoke') {
if ($instance->initiator_id !== $approverId || $instance->status !== 'processing') {
throw new BusinessException("无权撤回或状态不允许");
}
$updateData['status'] = 'revoked';
$updateData['finished_at'] = date('Y-m-d H:i:s');
Db::table('approval_instances')->where('id', $instanceId)->update($updateData);
$this->notifyAllApprovers($instance, 'revoked');
return ['result' => 'revoked'];
}
// 5. 处理 approve 逻辑
$nextStepIndex = $this->calculateNextStepIndex($instanceId, $currentStepIndex, $currentStep);
$updateData['current_step'] = $nextStepIndex;
if ($nextStepIndex >= count($steps)) {
$updateData['status'] = 'approved';
$updateData['finished_at'] = date('Y-m-d H:i:s');
Db::table('approval_instances')->where('id', $instanceId)->update($updateData);
$this->notifyInitiator($instance, 'approved');
$this->notifyCc($instanceId);
} else {
Db::table('approval_instances')->where('id', $instanceId)->update($updateData);
$this->notifyNextApprovers($instanceId);
}
return ['result' => 'approved', 'next_step' => $nextStepIndex];
}
/**
* 获取用户待审批列表
* @param int $userId 用户ID
* @return array
*/
public function getPendingApprovals(int $userId): array
{
// 简化查询:找到用户需要审批的实例ID
// 更复杂的逻辑(如并行会签)需要更精细的查询
$instanceIds = Db::table('approval_records')
->where('approver_id', $userId)
->pluck('instance_id')
->unique()
->toArray();
if (empty($instanceIds)) {
return [];
}
return Db::table('approval_instances')
->whereIn('id', $instanceIds)
->where('status', 'processing')
->get()
->toArray();
}
/**
* 获取审批历史记录
* @param int $instanceId
* @return array
*/
public function getApprovalHistory(int $instanceId): array
{
return Db::table('approval_records')
->where('instance_id', $instanceId)
->orderBy('step_index')
->orderBy('approved_at')
->get()
->toArray();
}
/**
* 获取审批流程图数据 (简化版)
* @param int $instanceId
* @return array
*/
public function getFlowDiagramData(int $instanceId): array
{
$instance = Db::table('approval_instances')->find($instanceId);
if (!$instance) return [];
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
if (!$flow) return [];
$steps = json_decode($flow->steps, true);
$records = Db::table('approval_records')
->where('instance_id', $instanceId)
->orderBy('step_index')
->orderBy('approved_at')
->get()
->groupBy('step_index');
$diagramData = [];
foreach ($steps as $index => $step) {
$status = 'pending';
$actions = $records->get($index, collect())->toArray();
if (!empty($actions)) {
$hasReject = collect($actions)->contains('action', 'reject');
if ($hasReject) {
$status = 'rejected';
} else {
if ($step['type'] === 'parallel') {
// 简化处理
$status = 'approved';
} else {
$status = 'approved';
}
}
} elseif ($index < $instance->current_step) {
$status = 'approved';
} elseif ($index == $instance->current_step) {
$status = 'processing';
}
$diagramData[] = [
'index' => $index,
'name' => $step['name'],
'type' => $step['type'],
'status' => $status,
'actions' => $actions
];
}
return $diagramData;
}
/**
* 处理超时的审批实例 (定时任务调用)
*/
public function handleTimeouts()
{
$now = date('Y-m-d H:i:s');
$expiredInstances = Db::table('approval_instances')
->where('status', 'processing')
->where('deadline_at', '<=', $now)
->get();
foreach ($expiredInstances as $instance) {
$flow = Db::table('approval_flows')->where('type', $instance->flow_type)->first();
if ($flow && $flow->timeout_action) {
$updateData = [
'finished_at' => $now,
'updated_at' => $now
];
if ($flow->timeout_action === 'auto_reject') {
$updateData['status'] = 'rejected';
Db::table('approval_instances')->where('id', $instance->id)->update($updateData);
$this->notifyInitiator($instance, 'rejected_timeout');
} elseif ($flow->timeout_action === 'auto_approve') {
$updateData['status'] = 'approved';
Db::table('approval_instances')->where('id', $instance->id)->update($updateData);
$this->notifyInitiator($instance, 'approved_timeout');
}
}
}
}
// --- 辅助方法 ---
private function canUserApproveStep(array $step, int $userId, object $instance): bool
{
$approvers = $this->resolveApprovers($step, $instance);
return in_array($userId, $approvers);
}
// private function resolveApprovers(array $step, object $instance): array
// {
// $resolved = [];
// foreach ($step['approvers'] as $approver) {
// if ($approver['type'] === 'role') {
// // 示例:根据角色查找用户ID,你需要替换为实际逻辑
// if ($approver['value'] === 'admin') {
// $resolved[] = 1; // 假设 admin 角色的用户ID是1
// } elseif ($approver['value'] === 'manager') {
// $resolved[] = 2; $resolved[] = 3; // 假设有多个manager
// }
// } elseif ($approver['type'] === 'user') {
// $resolved[] = (int)$approver['value'];
// } elseif ($approver['type'] === 'initiator_leader') {
// // 示例:获取发起人的领导
// $resolved[] = 4; // 假设领导ID是4
// }
// }
// return array_unique($resolved);
// }
private function resolveApprovers(array $step, object $instance): array
{
$resolved = [];
foreach ($step['approvers'] as $approverConfig) {
$approverIds = [];
switch ($approverConfig['type']) {
// 1. 按角色查找
case 'role':
$roleCode = $approverConfig['value'];
// 所有拥有该角色的用户ID
$approverIds = $this->getUserIdsByRole($roleCode);
break;
// 2. 按具体用户ID
case 'user':
$approverIds = [(int)$approverConfig['value']];
break;
// 3. 按发起人的直属领导 (业务一般是组长)
case 'initiator_leader':
// 用户表中 report_to 字段
$approverIds = $this->getLeaderIdByUserId($instance->initiator_id);
if ($approverIds) {
$approverIds = [$approverIds];
} else {
$approverIds = []; // 没有领导,跳过
}
break;
// 4. 按业务字段关联的用户 (高级功能)
case 'field_user':
$fieldName = $approverConfig['value']; // 如 'project_manager_id'
$formData = json_decode($instance->form_data, true);
if (isset($formData[$fieldName])) {
$approverIds = [(int)$formData[$fieldName]];
} else {
$approverIds = [];
}
break;
// 5. 按组织架构层级 (如发起人所在部门的负责人)
case 'dept_head':
$deptId = $approverConfig['value']; // 也可以从 instance 或 formData 中获取
$approverIds = $this->getDeptHeadId($deptId);
if ($approverIds) {
$approverIds = [$approverIds];
} else {
$approverIds = [];
}
break;
default:
throw new BusinessException("不支持的审批人类型: {$approverConfig['type']}");
}
$resolved = array_merge($resolved, $approverIds);
}
return array_unique($resolved);
}
// 角色层
private function getUserIdsByRole(string $roleCode): array
{
// user_roles 表和 roles 表
return Db::table('user_roles as ur')
->join('roles as r', 'ur.role_id', '=', 'r.id')
->where('r.code', $roleCode) // 假设 roles 表有 code 字段
->pluck('ur.user_id')
->toArray();
}
// 领导层
private function getLeaderIdByUserId(int $userId): ?int
{
// users 表 report_to 字段
return Db::table('user')->where('id', $userId)->value('report_to');
}
// 组织架构层
private function getDeptHeadId(int $deptId): ?int
{
// departments 表 head_user_id 字段(部门逻辑暂时不用,根据后续业务实际情况开发)
return Db::table('departments')->where('id', $deptId)->value('head_user_id');
}
private function calculateNextStepIndex(int $instanceId, int $currentStepIndex, array $currentStep): int
{
// 对于 sequential 和 any,单次 approve 即进入下一步
if ($currentStep['type'] === 'sequential' || $currentStep['type'] === 'any') {
return $currentStepIndex + 1;
} elseif ($currentStep['type'] === 'parallel') {
// 对于并行会签,需要检查是否所有审批人都已 approve
// 这里简化处理,实际需要精确计算
// 假设只要有一次 approve 就算完成(不准确,仅为示例)
// 正确做法是查询所有 approvers 是否都 approve 了
return $currentStepIndex + 1;
}
return $currentStepIndex + 1;
}
private function notifyNextApprovers(int $instanceId)
{
// 实现你的通知逻辑,如发送站内信、邮件、企业微信等
// 可以查询 instance_id 对应的当前步骤审批人
echo "通知下一个审批人 (Instance ID: {$instanceId})\n";
}
private function notifyInitiator(object $instance, string $result)
{
// 通知发起人审批结果
echo "通知发起人 {$instance->initiator_id} 审批结果: {$result}\n";
}
private function notifyAllApprovers(object $instance, string $status)
{
// 通知所有相关审批人(如撤回时)
echo "通知所有审批人,实例 {$instance->id} 状态变为: {$status}\n";
}
private function notifyCc(int $instanceId)
{
// 通知抄送人
echo "通知抄送人 (Instance ID: {$instanceId})\n";
}
}