调整备货单

This commit is contained in:
mzl
2025-09-08 20:00:12 +08:00
parent 28368e27a7
commit 84c588496b
14 changed files with 945 additions and 247 deletions
@@ -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";
}
}