调整备货单
This commit is contained in:
@@ -34,36 +34,56 @@ class ApprovalFlowService
|
||||
throw new BusinessException("未找到可执行的审批步骤");
|
||||
}
|
||||
|
||||
// 3. 创建审批实例
|
||||
// 3. 计算截止时间
|
||||
$deadlineAt = null;
|
||||
if ($flow->timeout_hours > 0) {
|
||||
$deadlineAt = date('Y-m-d H:i:s', time() + $flow->timeout_hours * 3600);
|
||||
}
|
||||
|
||||
// 4. 创建审批实例
|
||||
$instanceId = Db::table('approval_instances')->insertGetId([
|
||||
'flow_type' => $flowType,
|
||||
'biz_type' => $bizType,
|
||||
'biz_id' => $bizId,
|
||||
'form_data' => json_encode($formData, JSON_UNESCAPED_UNICODE),
|
||||
'initiator_id' => $initiatorId,
|
||||
'status' => 'processing',
|
||||
'status' => 'processing', // processing 表示正在进行中
|
||||
'current_step' => 0,
|
||||
'deadline_at' => $deadlineAt,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
// 4. 创建审批记录(待办)
|
||||
$approverIds = $this->resolveApprovers($firstStep, (object)['initiator_id' => $initiatorId]);
|
||||
// 5. 创建审批记录(待办)
|
||||
$approverIds = $this->resolveApprovers($firstStep, (object)[
|
||||
'initiator_id' => $initiatorId,
|
||||
'form_data' => json_encode($formData)
|
||||
]);
|
||||
|
||||
foreach ($approverIds as $approverId) {
|
||||
Db::table('approval_records')->insert([
|
||||
'instance_id' => $instanceId,
|
||||
'step_name' => $firstStep['name'],
|
||||
'approver_id' => $approverId,
|
||||
'step_index' => 0,
|
||||
'action' => '', // 未操作时为空
|
||||
'status' => 'pending',
|
||||
'approved_at' => date('Y-m-d H:i:s', 0), // 未审批时设置为初始时间
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
// 5. 通知审批人
|
||||
$this->sendNotification($approverIds, (object)['id' => $instanceId, 'flow_type' => $flowType], 'pending', false);
|
||||
// 6. 通知审批人
|
||||
$instance = (object)[
|
||||
'id' => $instanceId,
|
||||
'flow_type' => $flowType,
|
||||
'form_data' => json_encode($formData),
|
||||
'initiator_id' => $initiatorId
|
||||
];
|
||||
$this->sendNotification($approverIds, $instance, 'pending', false);
|
||||
|
||||
// 6. 通知抄送人(当前步骤)
|
||||
$this->notifyCCs($firstStep, (object)['id' => $instanceId, 'flow_type' => $flowType, 'form_data' => json_encode($formData)], 'pending');
|
||||
// 7. 通知抄送人(当前步骤)
|
||||
$this->notifyCCs($firstStep, $instance, 'pending');
|
||||
|
||||
return ['instance_id' => $instanceId];
|
||||
}
|
||||
@@ -73,13 +93,28 @@ class ApprovalFlowService
|
||||
*/
|
||||
public function approve(int $instanceId, int $approverId, string $action, string $comment = '')
|
||||
{
|
||||
// 验证操作是否有效
|
||||
if (!in_array($action, ['approve', 'reject'])) {
|
||||
throw new BusinessException("无效的审批操作");
|
||||
}
|
||||
|
||||
$instance = Db::table('approval_instances')->where('id', $instanceId)->first();
|
||||
if (!$instance) {
|
||||
throw new BusinessException("审批实例未找到");
|
||||
}
|
||||
|
||||
$steps = json_decode($instance->steps, true) ?: json_decode(Db::table('approval_flows')->where('type', $instance->flow_type)->value('steps'), true);
|
||||
$currentStep = $steps[$instance->current_step] ?? null;
|
||||
// 获取流程定义
|
||||
$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;
|
||||
$currentStep = $steps[$currentStepIndex] ?? null;
|
||||
|
||||
if (!$currentStep) {
|
||||
throw new BusinessException("当前步骤配置错误");
|
||||
@@ -90,20 +125,33 @@ class ApprovalFlowService
|
||||
throw new BusinessException("您无权审批此步骤");
|
||||
}
|
||||
|
||||
// 检查用户是否已经审批过
|
||||
$existingRecord = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('approver_id', $approverId)
|
||||
->where('step_index', $currentStepIndex)
|
||||
->first();
|
||||
|
||||
if ($existingRecord && $existingRecord->status !== 'pending') {
|
||||
throw new BusinessException("您已经审批过此步骤");
|
||||
}
|
||||
|
||||
// 更新审批记录
|
||||
Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('approver_id', $approverId)
|
||||
->where('step_index', $currentStepIndex)
|
||||
->where('status', 'pending')
|
||||
->update([
|
||||
'status' => $action,
|
||||
'action' => $action,
|
||||
'status' => $action, // 状态与操作一致
|
||||
'comment' => $comment,
|
||||
'approved_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
// 检查当前步骤是否完成(根据 type: any, all, sequential)
|
||||
$stepCompleted = $this->isStepCompleted($instanceId, $instance->current_step, $currentStep['type']);
|
||||
// 检查当前步骤是否完成(根据 type: any, all)
|
||||
$stepCompleted = $this->isStepCompleted($instanceId, $currentStepIndex, $currentStep['type']);
|
||||
|
||||
if (!$stepCompleted) {
|
||||
// 步骤未完成,不进入下一步
|
||||
@@ -111,12 +159,13 @@ class ApprovalFlowService
|
||||
}
|
||||
|
||||
// 步骤已完成,进入下一步或结束
|
||||
$nextStep = $this->getNextValidStep($steps, json_decode($instance->form_data, true), $instance->current_step + 1);
|
||||
$nextStepIndex = $this->getNextStepIndex($steps, json_decode($instance->form_data, true), $currentStepIndex);
|
||||
|
||||
if ($nextStep === null) {
|
||||
if ($nextStepIndex === null) {
|
||||
// 流程结束
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'status' => $action === 'approve' ? 'approved' : 'rejected',
|
||||
'status' => $action, // 最终状态与最后一次操作一致
|
||||
'finished_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
@@ -127,21 +176,26 @@ class ApprovalFlowService
|
||||
}
|
||||
|
||||
// 进入下一步
|
||||
$nextStepIndex = array_search($nextStep, $steps);
|
||||
Db::table('approval_instances')->where('id', $instanceId)->update([
|
||||
'current_step' => $nextStepIndex,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
// 获取下一步信息
|
||||
$nextStep = $steps[$nextStepIndex];
|
||||
|
||||
// 创建下一步的审批记录
|
||||
$nextApprovers = $this->resolveApprovers($nextStep, $instance);
|
||||
foreach ($nextApprovers as $approverId) {
|
||||
Db::table('approval_records')->insert([
|
||||
'instance_id' => $instanceId,
|
||||
'step_name' => $nextStep['name'],
|
||||
'approver_id' => $approverId,
|
||||
'step_index' => $nextStepIndex,
|
||||
'action' => '',
|
||||
'status' => 'pending',
|
||||
'approved_at' => date('Y-m-d H:i:s', 0),
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -233,6 +287,26 @@ class ApprovalFlowService
|
||||
$ccUserIds = array_unique(array_filter($ccUserIds));
|
||||
|
||||
if (!empty($ccUserIds)) {
|
||||
// 记录抄送信息到数据库
|
||||
foreach ($ccUserIds as $userId) {
|
||||
// 检查是否已存在抄送记录
|
||||
$exists = Db::table('approval_ccs')
|
||||
->where('instance_id', $instance->id)
|
||||
->where('user_id', $userId)
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
Db::table('approval_ccs')->insert([
|
||||
'instance_id' => $instance->id,
|
||||
'user_id' => $userId,
|
||||
'notified_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'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送通知
|
||||
$this->sendNotification($ccUserIds, $instance, $action, true);
|
||||
}
|
||||
}
|
||||
@@ -264,7 +338,7 @@ class ApprovalFlowService
|
||||
case '==': return $actualValue == $value;
|
||||
case '!=': return $actualValue != $value;
|
||||
case '>': return $actualValue > $value;
|
||||
case '<': $actualValue < $value;
|
||||
case '<': return $actualValue < $value;
|
||||
case '>=': return $actualValue >= $value;
|
||||
case '<=': return $actualValue <= $value;
|
||||
default: throw new BusinessException("不支持的操作符: {$op}");
|
||||
@@ -272,14 +346,14 @@ class ApprovalFlowService
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个有效步骤(考虑 condition)
|
||||
* 获取下一个有效步骤索引(考虑 condition)
|
||||
*/
|
||||
private function getNextValidStep(array $steps, array $formData, int $startIndex = 0): ?array
|
||||
private function getNextStepIndex(array $steps, array $formData, int $currentStepIndex): ?int
|
||||
{
|
||||
for ($i = $startIndex; $i < count($steps); $i++) {
|
||||
for ($i = $currentStepIndex + 1; $i < count($steps); $i++) {
|
||||
$step = $steps[$i];
|
||||
if (!isset($step['condition']) || $this->evaluateCondition($step['condition'], $formData)) {
|
||||
return $step;
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -299,20 +373,38 @@ class ApprovalFlowService
|
||||
*/
|
||||
private function isStepCompleted(int $instanceId, int $stepIndex, string $type): bool
|
||||
{
|
||||
$pendingCount = Db::table('approval_records')
|
||||
// 获取当前步骤的所有审批记录
|
||||
$records = Db::table('approval_records')
|
||||
->where('instance_id', $instanceId)
|
||||
->where('status', 'pending')
|
||||
->count();
|
||||
->where('step_index', $stepIndex)
|
||||
->get();
|
||||
|
||||
if ($type === 'any') {
|
||||
return $pendingCount === 0; // 任一通过即可
|
||||
if ($records->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($type === 'all' || $type === 'sequential') {
|
||||
return $pendingCount === 0; // 所有人都必须审批
|
||||
switch ($type) {
|
||||
case 'any':
|
||||
// 任意一人审批即可(approve或reject都算完成)
|
||||
foreach ($records as $record) {
|
||||
if ($record->status !== 'pending') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case 'all':
|
||||
// 所有人都必须审批
|
||||
foreach ($records as $record) {
|
||||
if ($record->status === 'pending') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,21 +433,12 @@ class ApprovalFlowService
|
||||
*/
|
||||
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')
|
||||
return Db::table('approval_records as ar')
|
||||
->join('approval_instances as ai', 'ar.instance_id', '=', 'ai.id')
|
||||
->where('ar.approver_id', $userId)
|
||||
->where('ar.status', 'pending')
|
||||
->where('ai.status', 'processing')
|
||||
->select('ai.*')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
@@ -406,12 +489,7 @@ class ApprovalFlowService
|
||||
if ($hasReject) {
|
||||
$status = 'rejected';
|
||||
} else {
|
||||
if ($step['type'] === 'parallel') {
|
||||
// 简化处理
|
||||
$status = 'approved';
|
||||
} else {
|
||||
$status = 'approved';
|
||||
}
|
||||
$status = 'approved';
|
||||
}
|
||||
} elseif ($index < $instance->current_step) {
|
||||
$status = 'approved';
|
||||
@@ -449,172 +527,40 @@ class ApprovalFlowService
|
||||
'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');
|
||||
$updateData['status'] = 'reject';
|
||||
} elseif ($flow->timeout_action === 'auto_approve') {
|
||||
$updateData['status'] = 'approved';
|
||||
Db::table('approval_instances')->where('id', $instance->id)->update($updateData);
|
||||
$this->notifyInitiator($instance, 'approved_timeout');
|
||||
$updateData['status'] = 'approve';
|
||||
}
|
||||
|
||||
Db::table('approval_instances')->where('id', $instance->id)->update($updateData);
|
||||
$this->notifyInitiator($instance, $flow->timeout_action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 辅助方法 ---
|
||||
|
||||
// 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 字段
|
||||
->where('r.code', $roleCode)
|
||||
->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";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user