From 257c29c49c2cb4d3f2ea3a93e7ca95b16694430a Mon Sep 17 00:00:00 2001 From: Jing Li Date: Mon, 29 Apr 2024 00:07:18 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AB=99=E5=86=85=E6=B6=88=E6=81=AF=E5=92=8C?= =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Admin/Services/WorkFlowService.php | 95 ++----------------- app/Events/WorkflowCheckFailed.php | 20 ++++ app/Events/WorkflowCheckNext.php | 20 ++++ app/Events/WorkflowCheckSuccess.php | 20 ++++ app/Exceptions/Handler.php | 12 ++- .../CreateWorkflowCheckFailedMessage.php | 44 +++++++++ .../CreateWorkflowCheckNextMessage.php | 61 ++++++++++++ .../CreateWorkflowCheckSuccessMessage.php | 44 +++++++++ .../SendWorkflowCheckFailedNotification.php | 59 ++++++++++++ .../SendWorkflowCheckNextNotification.php | 76 +++++++++++++++ .../SendWorkflowCheckSuccessNotification.php | 59 ++++++++++++ app/Models/WorkflowCheck.php | 17 +++- app/Models/WorkflowLog.php | 6 +- app/Providers/EventServiceProvider.php | 15 ++- composer.json | 1 + composer.lock | 58 ++++++++++- config/easysms.php | 29 ++++++ ..._add_apply_at_to_workflow_checks_table.php | 28 ++++++ 18 files changed, 569 insertions(+), 95 deletions(-) create mode 100644 app/Events/WorkflowCheckFailed.php create mode 100644 app/Events/WorkflowCheckNext.php create mode 100644 app/Events/WorkflowCheckSuccess.php create mode 100644 app/Listeners/CreateWorkflowCheckFailedMessage.php create mode 100644 app/Listeners/CreateWorkflowCheckNextMessage.php create mode 100644 app/Listeners/CreateWorkflowCheckSuccessMessage.php create mode 100644 app/Listeners/SendWorkflowCheckFailedNotification.php create mode 100644 app/Listeners/SendWorkflowCheckNextNotification.php create mode 100644 app/Listeners/SendWorkflowCheckSuccessNotification.php create mode 100644 config/easysms.php create mode 100644 database/migrations/2024_04_28_230319_add_apply_at_to_workflow_checks_table.php diff --git a/app/Admin/Services/WorkFlowService.php b/app/Admin/Services/WorkFlowService.php index 33be5cd..071793e 100644 --- a/app/Admin/Services/WorkFlowService.php +++ b/app/Admin/Services/WorkFlowService.php @@ -2,22 +2,16 @@ namespace App\Admin\Services; -use App\Enums\MessageType; use App\Enums\{CheckStatus, CheckType}; +use App\Events\WorkflowCheckFailed; +use App\Events\WorkflowCheckNext; +use App\Events\WorkflowCheckSuccess; 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; @@ -48,6 +42,10 @@ class WorkFlowService extends BaseService admin_abort('正在审核中'); } + $check->update([ + 'apply_at' => now(), + ]); + $workflow = Workflow::where('key', $check->key)->first(); // 没有配置审核流程, 直接通过 if (! $workflow || ! $workflow->config) { @@ -110,23 +108,7 @@ class WorkFlowService extends BaseService $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, - ], - ], - ); - } - } + WorkflowCheckSuccess::dispatch($check->withoutRelations()); return true; } @@ -144,23 +126,7 @@ class WorkFlowService extends BaseService $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, - ], - ], - ); - } - } + WorkflowCheckFailed::dispatch($check->withoutRelations()); return true; } @@ -233,35 +199,7 @@ class WorkFlowService extends BaseService ]); $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], - ], - ); - } - } + WorkflowCheckNext::dispatch($log); // 自动审核通过 if ($this->authCheck($check->employee, $log)) { @@ -343,17 +281,4 @@ class WorkFlowService extends BaseService 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 => '', - }; - } } diff --git a/app/Events/WorkflowCheckFailed.php b/app/Events/WorkflowCheckFailed.php new file mode 100644 index 0000000..60aa66b --- /dev/null +++ b/app/Events/WorkflowCheckFailed.php @@ -0,0 +1,20 @@ +renderable(function (NotFoundHttpException $e, Request $request) { return response(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND); }); + + $this->reportable(function (NoGatewayAvailableException $e) { + foreach ($e->getExceptions() as $exception) { + $this->report($exception); + } + return false; + }); } protected function invalidJson($request, ValidationException $exception) diff --git a/app/Listeners/CreateWorkflowCheckFailedMessage.php b/app/Listeners/CreateWorkflowCheckFailedMessage.php new file mode 100644 index 0000000..d4701f3 --- /dev/null +++ b/app/Listeners/CreateWorkflowCheckFailedMessage.php @@ -0,0 +1,44 @@ +workflowCheck->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + if (is_null($event->workflowCheck->employee)) { + return; + } + + $this->messageService->create( + MessageType::Approval, + '审批提醒', + "您有一条【{$subjectTypeText}】未通过审核。", + [$event->workflowCheck->employee], + ['workflow_check' => $event->workflowCheck], + ); + } +} diff --git a/app/Listeners/CreateWorkflowCheckNextMessage.php b/app/Listeners/CreateWorkflowCheckNextMessage.php new file mode 100644 index 0000000..687098d --- /dev/null +++ b/app/Listeners/CreateWorkflowCheckNextMessage.php @@ -0,0 +1,61 @@ +workflowLog->check->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + $employees = collect(); + + switch ($event->workflowLog->check_type) { + case CheckType::Job: + [$storeId, $jobId] = explode('-', $event->workflowLog->check_value); + $employees = Employee::where('store_id', $storeId) + ->whereHas('jobs', fn ($query) => $query->where('job_id', $jobId)) + ->pluck('id'); + break; + + case CheckType::User: + $employees->push($event->workflowLog->check_value); + break; + } + + if ($employees->isEmpty()) { + return; + } + + $this->messageService->create( + MessageType::Approval, + '审批提醒', + "您有一条【{$subjectTypeText}】待审批。", + $employees->all(), + ['workflow_log' => $event->workflowLog], + ); + } +} diff --git a/app/Listeners/CreateWorkflowCheckSuccessMessage.php b/app/Listeners/CreateWorkflowCheckSuccessMessage.php new file mode 100644 index 0000000..141c0ee --- /dev/null +++ b/app/Listeners/CreateWorkflowCheckSuccessMessage.php @@ -0,0 +1,44 @@ +workflowCheck->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + if (is_null($event->workflowCheck->employee)) { + return; + } + + $this->messageService->create( + MessageType::Approval, + '审批提醒', + "您有一条【{$subjectTypeText}】已通过审核。", + [$event->workflowCheck->employee], + ['workflow_check' => $event->workflowCheck], + ); + } +} diff --git a/app/Listeners/SendWorkflowCheckFailedNotification.php b/app/Listeners/SendWorkflowCheckFailedNotification.php new file mode 100644 index 0000000..9a983f7 --- /dev/null +++ b/app/Listeners/SendWorkflowCheckFailedNotification.php @@ -0,0 +1,59 @@ +workflowCheck->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + if (empty($phone = $event->workflowCheck->employee?->phone)) { + return; + } + + if (is_null($applyAt = $event->workflowCheck->apply_at)) { + $applyAt = $event->workflowCheck->created_at; + } + + $client = new EasySms(config('easysms')); + + $client->send($phone, [ + 'content' => function($gateway) use ($applyAt, $subjectTypeText) { + return "您于{$applyAt->toDateString()}提交的{$subjectTypeText}未通过审批,请登录查看。"; + }, + 'template' => function($gateway) { + if ($gateway->getName() == 'aliyun') { + return 'SMS_465890235'; + } + }, + 'data' => function($gateway) use ($applyAt, $subjectTypeText) { + return [ + 'time' => $applyAt->toDateString(), + 'work_msg' => $subjectTypeText, + ]; + }, + ]); + } +} diff --git a/app/Listeners/SendWorkflowCheckNextNotification.php b/app/Listeners/SendWorkflowCheckNextNotification.php new file mode 100644 index 0000000..75e0b2a --- /dev/null +++ b/app/Listeners/SendWorkflowCheckNextNotification.php @@ -0,0 +1,76 @@ +workflowLog->check->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + $employees = collect(); + + switch ($event->workflowLog->check_type) { + case CheckType::Job: + [$storeId, $jobId] = explode('-', $event->workflowLog->check_value); + $employees = Employee::where('store_id', $storeId) + ->whereHas('jobs', fn ($query) => $query->where('job_id', $jobId)) + ->get(); + break; + + case CheckType::User: + if ($employee = Employee::find($event->workflowLog->check_value)) { + $employees->push($employee); + } + break; + } + + $client = new EasySms(config('easysms')); + + foreach ($employees as $employee) { + try { + $client->send($employee->phone, [ + 'content' => function($gateway) use ($subjectTypeText) { + return "您有一条{$subjectTypeText}待处理,请尽快登录处理。"; + }, + 'template' => function($gateway) { + if ($gateway->getName() == 'aliyun') { + return 'SMS_465885234'; + } + }, + 'data' => function($gateway) use ($subjectTypeText) { + return [ + 'work_msg' => $subjectTypeText, + ]; + }, + ]); + } catch (Throwable $e) { + report($e); + } + } + } +} diff --git a/app/Listeners/SendWorkflowCheckSuccessNotification.php b/app/Listeners/SendWorkflowCheckSuccessNotification.php new file mode 100644 index 0000000..2752b4a --- /dev/null +++ b/app/Listeners/SendWorkflowCheckSuccessNotification.php @@ -0,0 +1,59 @@ +workflowCheck->subjectTypeText(); + if ($subjectTypeText === '') { + return; + } + + if (empty($phone = $event->workflowCheck->employee?->phone)) { + return; + } + + if (is_null($applyAt = $event->workflowCheck->apply_at)) { + $applyAt = $event->workflowCheck->created_at; + } + + $client = new EasySms(config('easysms')); + + $client->send($phone, [ + 'content' => function($gateway) use ($applyAt, $subjectTypeText) { + return "您于{$applyAt->toDateString()}提交的{$subjectTypeText}已通过审批,请登录查看。"; + }, + 'template' => function($gateway) { + if ($gateway->getName() == 'aliyun') { + return 'SMS_465915258'; + } + }, + 'data' => function($gateway) use ($applyAt, $subjectTypeText) { + return [ + 'time' => $applyAt->toDateString(), + 'work_msg' => $subjectTypeText, + ]; + }, + ]); + } +} diff --git a/app/Models/WorkflowCheck.php b/app/Models/WorkflowCheck.php index 125553c..62c3d56 100644 --- a/app/Models/WorkflowCheck.php +++ b/app/Models/WorkflowCheck.php @@ -6,6 +6,7 @@ use App\Enums\{CheckStatus, CheckType}; use App\Traits\HasDateTimeFormatter; use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\Relation; /** * 审核申请 @@ -18,10 +19,11 @@ class WorkflowCheck extends Model 'check_status' => CheckStatus::None, ]; - protected $fillable = ['subject_type', 'subject_id', 'subject_data', 'key', 'employee_id', 'check_status', 'checked_at', 'check_remarks', 'check_type', 'check_value', 'check_name']; + protected $fillable = ['subject_type', 'subject_id', 'subject_data', 'key', 'employee_id', 'check_status', 'checked_at', 'check_remarks', 'check_type', 'check_value', 'check_name', 'apply_at']; protected $casts = [ 'subject_data' => 'json', + 'apply_at' => 'datetime', 'check_status' => CheckStatus::class, 'checked_at' => 'datetime', 'check_type' => CheckType::class, @@ -64,4 +66,17 @@ class WorkflowCheck extends Model { return $this->check_status === CheckStatus::Fail; } + + public function subjectTypeText(): string + { + return match (Relation::getMorphedModel($this->subject_type)) { + EmployeeSignRepair::class => '补卡申请', + HolidayApply::class => '请假申请', + OvertimeApply::class => '加班申请', + OfficalBusiness::class => '出差报备', + EmployeePromotion::class => '升职申请', + Reimbursement::class => '报销申请', + default => '', + }; + } } diff --git a/app/Models/WorkflowLog.php b/app/Models/WorkflowLog.php index 838f78e..438802f 100644 --- a/app/Models/WorkflowLog.php +++ b/app/Models/WorkflowLog.php @@ -5,7 +5,6 @@ namespace App\Models; use App\Enums\CheckStatus; use App\Enums\CheckType; use Illuminate\Database\Eloquent\Model; -use App\Traits\HasDateTimeFormatter; /** * 审核流水 @@ -46,4 +45,9 @@ class WorkflowLog extends Model } return $builder->whereIn('check_value', $checkValue); } + + public function isCheckProcessing(): bool + { + return $this->check_status === CheckStatus::Processing; + } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 2d65aac..b0a04d2 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,8 +2,6 @@ namespace App\Providers; -use Illuminate\Auth\Events\Registered; -use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; @@ -15,8 +13,17 @@ class EventServiceProvider extends ServiceProvider * @var array> */ protected $listen = [ - Registered::class => [ - SendEmailVerificationNotification::class, + \App\Events\WorkflowCheckNext::class => [ + \App\Listeners\CreateWorkflowCheckNextMessage::class, + \App\Listeners\SendWorkflowCheckNextNotification::class, + ], + \App\Events\WorkflowCheckSuccess::class => [ + \App\Listeners\CreateWorkflowCheckSuccessMessage::class, + \App\Listeners\SendWorkflowCheckSuccessNotification::class, + ], + \App\Events\WorkflowCheckFailed::class => [ + \App\Listeners\CreateWorkflowCheckFailedMessage::class, + \App\Listeners\SendWorkflowCheckFailedNotification::class, ], ]; diff --git a/composer.json b/composer.json index dace068..8522d13 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "laravel/framework": "^10.10", "laravel/sanctum": "^3.3", "laravel/tinker": "^2.8", + "overtrue/easy-sms": "^3.0", "rap2hpoutre/fast-excel": "^5.4", "slowlyo/owl-admin": "^3.5", "tucker-eric/eloquentfilter": "^3.2" diff --git a/composer.lock b/composer.lock index 8f54f86..61aa110 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6ce90738fadc40a447dfead6b131ec62", + "content-hash": "e50204833e31b832f0bb8b3a5438e5ab", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -2087,6 +2087,60 @@ ], "time": "2024-01-09T09:30:37+00:00" }, + { + "name": "overtrue/easy-sms", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/overtrue/easy-sms.git", + "reference": "fa43b42ab936f5fbd6684f6469d16f0ab409c0d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/easy-sms/zipball/fa43b42ab936f5fbd6684f6469d16f0ab409c0d8", + "reference": "fa43b42ab936f5fbd6684f6469d16f0ab409c0d8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "php": ">=8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.54", + "jetbrains/phpstorm-attributes": "^1.0", + "mockery/mockery": "^1.4.2", + "phpunit/phpunit": "^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Overtrue\\EasySms\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ], + "description": "The easiest way to send short message.", + "support": { + "issues": "https://github.com/overtrue/easy-sms/issues", + "source": "https://github.com/overtrue/easy-sms/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2024-04-18T08:21:47+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.2", @@ -6634,5 +6688,5 @@ "php": "^8.1" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/config/easysms.php b/config/easysms.php new file mode 100644 index 0000000..a411c9b --- /dev/null +++ b/config/easysms.php @@ -0,0 +1,29 @@ + 5.0, + + // 默认发送配置 + 'default' => [ + // 网关调用策略,默认:顺序调用 + 'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class, + + // 默认可用的发送网关 + 'gateways' => [ + 'aliyun', + ], + ], + // 可用的网关配置 + 'gateways' => [ + 'errorlog' => [ + 'file' => storage_path('logs/easysms.log'), + ], + + 'aliyun' => [ + 'access_key_id' => env('ALIYUN_SMS_ACCESS_KEY_ID'), + 'access_key_secret' => env('ALIYUN_SMS_ACCESS_KEY_SECRET'), + 'sign_name' => env('ALIYUN_SMS_SIGN_NAME'), + ], + ], +]; diff --git a/database/migrations/2024_04_28_230319_add_apply_at_to_workflow_checks_table.php b/database/migrations/2024_04_28_230319_add_apply_at_to_workflow_checks_table.php new file mode 100644 index 0000000..1e78f11 --- /dev/null +++ b/database/migrations/2024_04_28_230319_add_apply_at_to_workflow_checks_table.php @@ -0,0 +1,28 @@ +timestamp('apply_at')->nullable()->comment('审批申请时间'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('workflow_checks', function (Blueprint $table) { + $table->dropColumn(['apply_at']); + }); + } +};