store-manage/app/Admin/Services/WorkFlowService.php

354 lines
12 KiB
PHP

<?php
namespace App\Admin\Services;
use App\Enums\MessageType;
use App\Enums\{CheckStatus, CheckType};
use App\Models\Employee;
use App\Models\EmployeePromotion;
use App\Models\EmployeeSignRepair;
use App\Models\HolidayApply;
use App\Models\Keyword;
use App\Models\OfficalBusiness;
use App\Models\OvertimeApply;
use App\Models\Reimbursement;
use App\Models\Store;
use App\Models\Workflow;
use App\Models\WorkflowCheck;
use App\Models\WorkflowLog;
use App\Services\MessageService;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class WorkFlowService extends BaseService
{
protected array $withRelationships = [];
protected string $modelName = Workflow::class;
protected string $modelFilterName = '';
/**
* 发起审核申请
* 1. 待审核 Model 实现 Checkable
* 2. 生成全部的审核流程
*
* @param WorkflowCheck $check 待审核记录
* @param Employee $user 申请人
*
* @return bool true: 成功, false: 失败, $this->getError(): 错误消息
*/
public function apply(WorkflowCheck $check, Employee $user)
{
if ($check->check_status === CheckStatus::Success) {
admin_abort('已经审核通过');
}
if ($check->check_status === CheckStatus::Processing) {
admin_abort('正在审核中');
}
$workflow = Workflow::where('key', $check->key)->first();
// 没有配置审核流程, 直接通过
if (! $workflow || ! $workflow->config) {
$this->success($check);
return true;
}
$jobs = Keyword::where('parent_key', 'job')->get();
$config = collect($workflow->config)->sortBy('sort');
$batchId = $check->logs()->max('batch_id') + 1;
$check->update([
'subject_data' => $check->subject->toArray(),
'check_status' => CheckStatus::Processing,
'employee_id' => $user->id,
]);
foreach ($config as $item) {
$checkValue = '';
$checkName = '';
// 职位审核
if ($item['type'] == CheckType::Job->value) {
// 没有门店, 则跳过
if (! $user->store_id) {
continue;
}
// 所属门店的职位审核
$store = Store::findOrFail($user->store_id);
$job = $jobs->firstWhere('key', $item['value']);
$checkValue = $store->id.'-'.$job->key;
$checkName = $store->title.'-'.$job->name;
}
// 指定用户审核
elseif ($item['type'] == CheckType::User->value) {
$checkUser = Employee::findOrFail($item['value']);
$checkValue = $checkUser->id;
$checkName = $checkUser->name;
} else {
admin_abort('未知的审核类型: '.$item['type']);
}
$check->logs()->create([
'batch_id' => $batchId,
'check_type' => $item['type'],
'check_value' => $checkValue,
'check_name' => $checkName,
'sort' => $item['sort'],
]);
}
// 开启第一个审核流程
$this->next($check);
return true;
}
public function success(WorkflowCheck $check, array $options = [])
{
$check->update([
'check_status' => CheckStatus::Success,
'checked_at' => data_get($options, 'checked_at', now()),
'check_remarks' => data_get($options, 'remarks'),
]);
$check->subject->checkSuccess();
if ($employee = $check->employee) {
$text = $this->mapSubjectTypeText($check->subject_type);
if ($text !== '') {
(new MessageService())->create(
MessageType::Approval,
'审批提醒',
"您有一条【{$text}】已通过审核。",
[$employee],
[
'workflow_check' => [
'subject_id' => $check->subject_id,
'subject_type' => $check->subject_type,
],
],
);
}
}
return true;
}
public function fail(WorkflowCheck $check, array $options = [])
{
$check->update([
'check_status' => CheckStatus::Fail,
'check_remarks' => data_get($options, 'remarks'),
'checked_at' => data_get($options, 'checked_at', now()),
]);
// 删除未审核的流程
$check->logs()->where('check_status', CheckStatus::None)->delete();
$check->subject->checkFail();
if ($employee = $check->employee) {
$text = $this->mapSubjectTypeText($check->subject_type);
if ($text !== '') {
(new MessageService())->create(
MessageType::Approval,
'审批提醒',
"您有一条【{$text}】未通过审核。",
[$employee],
[
'workflow_check' => [
'subject_id' => $check->subject_id,
'subject_type' => $check->subject_type,
],
],
);
}
}
return true;
}
/**
* 取消审核
* 1. 删除审核的流程
* 2. 更新申请记录的状态
*/
public function cancel(WorkflowCheck $check)
{
$check->logs()->delete();
$check->update([
'check_status' => CheckStatus::Cancel,
'check_value' => null,
'check_name' => null,
'check_type' => null,
]);
$check->subject->checkCancel();
return true;
}
/**
* 审核单个流程
*
* @param Employee $user 审核人
* @param WorkflowLog $log 审核流水记录
* @param bool $status 通过/不通过
* @param array $options {remarks: 不通过原因, time: 审核时间(默认当前时间)}
* @return bool
*/
public function check(Employee $user, WorkflowLog $log, $status, $options = [])
{
if ($log->check_status != CheckStatus::Processing) {
admin_abort('不可操作, 等待前面的审核完成');
}
if (! $this->authCheck($user, $log)) {
admin_abort('没有权限');
}
$attributes = ['check_status' => $status ? CheckStatus::Success : CheckStatus::Fail];
$attributes['checked_at'] = data_get($options, 'time', now());
$attributes['remarks'] = data_get($options, 'remarks');
$attributes['check_user_id'] = $user->id;
$log->update($attributes);
$check = $log->check;
$check->update(['check_name' => $user->name]);
if ($status) {
return $this->next($check);
} else {
return $this->fail($check, $options);
}
}
/**
* 开启下一个审核流程
*
* @return bool
*/
public function next(WorkflowCheck $check)
{
$log = $check->logs()->where('check_status', CheckStatus::None)->orderBy('sort')->first();
if ($log) {
$check->update([
'check_value' => $log->check_value,
'check_name' => $log->check_name,
'check_type' => $log->check_type,
]);
$log->update(['check_status' => CheckStatus::Processing]);
$employees = [];
switch ($log->check_type) {
case CheckType::Job:
[$storeId, $jobId] = explode('-', $log->check_value);
$employees = Employee::where('store_id', $storeId)
->whereHas('jobs', fn ($query) => $query->where('job_id', $jobId))
->pluck('id')
->all();
break;
case CheckType::User:
$employees = [$log->check_value];
break;
}
if (count($employees)) {
$text = $this->mapSubjectTypeText($check->subject_type);
if ($text !== '') {
(new MessageService())->create(
MessageType::Approval,
'审批提醒',
"您有一条【{$text}】待审批。",
$employees,
[
'workflow_log' => ['id' => $log->id],
],
);
}
}
// 自动审核通过
if ($this->authCheck($check->employee, $log)) {
return $this->check($check->employee, $log, true, ['remarks' => '自动审核通过']);
}
} else {
// 没有审核流程了, 审核完成
return $this->success($check);
}
return true;
}
/**
* 是否有权限审核
* 1. 申请人 == 审核人
* 2. 申请人的职位 高于 审核人职位
*/
public function authCheck(Employee $user, WorkflowLog $log)
{
if ($user->adminUser?->isAdministrator()) {
return true;
}
$checkValue = [$user->id];
if ($user->jobs && $user->jobs->count() > 0) {
foreach($user->jobs as $item) {
array_push($checkValue, $user->store_id . '-' . $item->key);
}
}
return in_array($log->check_value, $checkValue);
}
public function resloveData($data, $model = null)
{
if (isset($data['config'])) {
foreach ($data['config'] as $key => &$item) {
if (! $item) {
$data['config'] = null;
break;
}
$item['title'] = match ($item['type']) {
CheckType::Job->value => CheckType::Job->text(),
CheckType::User->value => CheckType::User->text(),
};
$item['subTitle'] = match ($item['type']) {
CheckType::Job->value => Keyword::where('key', $item['value'])->value('name'),
CheckType::User->value => Employee::where('id', $item['value'])->value('name'),
};
$item['sort'] = $key + 1;
}
}
return $data;
}
public function validate($data, $model = null)
{
$createRules = [
'key' => ['required', Rule::unique('workflows', 'key')],
'name' => ['required'],
'config' => ['required', 'array'],
];
$updateRules = [
'key' => [Rule::unique('workflows', 'key')->ignore($model?->id)],
];
$validator = Validator::make($data, $model ? $updateRules : $createRules, [
'key.unique' => ':input 已经存在',
]);
if ($validator->fails()) {
return $validator->errors()->first();
}
return true;
}
protected function mapSubjectTypeText(string $subjectType): string
{
return match (Relation::getMorphedModel($subjectType)) {
EmployeeSignRepair::class => '补卡申请',
HolidayApply::class => '请假申请',
OvertimeApply::class => '加班申请',
OfficalBusiness::class => '出差报备',
EmployeePromotion::class => '升职申请',
Reimbursement::class => '报销申请',
default => '',
};
}
}