瀏覽代碼

审批支持会签、或签、依次审批模式

moell 8 月之前
父節點
當前提交
b2c6929226

+ 1 - 0
app/Models/Approval.php

@@ -16,6 +16,7 @@ class Approval extends Model
         'object_type',
         'object_id',
         'node_level',
+        'approval_mode',
         'users',
         'remark',
         'created_by',

+ 9 - 0
app/Models/Enums/ObjectAction.php

@@ -88,6 +88,8 @@ enum ObjectAction: string
 
     case APPROVED_CANCELED ='approvedCanceled';
 
+    case APPROVED_WAIT_OTHERS = "approvedWaitOthers";
+
     public function isApproval()
     {
         return in_array($this->value, [
@@ -95,7 +97,13 @@ enum ObjectAction: string
             self::APPROVAL_APPROVED->value,
             self::APPROVAL_REJECTED->value,
             self::APPROVED_TO_NEXT_NODE->value,
+            self::APPROVED_WAIT_OTHERS->value,
         ]);
+
+        /**
+         * 1,发起审批
+         * 2,审批通过等待其他人审批, 依次审批,通知下一个,会签,通知审批人
+         */
     }
 
     public static function messageNotificationItems()
@@ -105,6 +113,7 @@ enum ObjectAction: string
             self::APPROVAL_APPROVED,
             self::APPROVAL_REJECTED,
             self::APPROVED_TO_NEXT_NODE,
+            self::APPROVED_WAIT_OTHERS,
         ];
 
         return [

+ 64 - 16
app/Services/Approval/ActionService.php

@@ -3,10 +3,12 @@
 namespace App\Services\Approval;
 
 use App\Models\Approval;
+use App\Models\Enums\ApprovalMode;
 use App\Models\Enums\ApprovalObjectType;
 use App\Models\Enums\ObjectAction;
 use App\Models\Enums\ObjectApprovalStatus;
 use App\Repositories\ActionRepository;
+use Illuminate\Support\Facades\Auth;
 
 class ActionService
 {
@@ -14,15 +16,23 @@ class ActionService
 
     protected array $actionExtraFields = [];
 
+    protected ?ObjectAction $objectAction = null;
+
     public function action(Approval $approval, int $status, string $comment = null): void
     {
         $this->approval = $approval;
 
+        $this->actionExtraFields = [
+            'now' => $this->approval->node_level
+        ];
+
         match($status) {
             1 => $this->approved(),
             default => $this->rejected(),
         };
 
+        $this->actionExtraFields['approval_mode'] = $this->approval->approval_mode;
+
         $this->triggerAction($comment);
 
         $this->changeApprovalObjectStatus();
@@ -47,15 +57,9 @@ class ActionService
 
     protected function triggerAction (string $comment = null): void
     {
-        $objectAction = match ($this->approval->status) {
-            ObjectApprovalStatus::APPROVED->value => ObjectAction::APPROVAL_APPROVED,
-            ObjectApprovalStatus::DOING->value => ObjectAction::APPROVED_TO_NEXT_NODE,
-            ObjectApprovalStatus::REJECTED->value => ObjectAction::APPROVAL_REJECTED,
-        };
-
         ActionRepository::createByApproval(
             $this->approval,
-            $objectAction,
+            $this->objectAction,
             $comment,
             extraFields: $this->actionExtraFields
         );
@@ -65,20 +69,64 @@ class ActionService
     {
         $approvalFlowNodes = $this->approval->approvalFlow?->nodes;
         $nextNodeIndex = $this->approval->node_level + 1;
-        $nextNodes = $approvalFlowNodes[$nextNodeIndex] ?? [];
+        $nextNode = $approvalFlowNodes[$nextNodeIndex] ?? [];
+
+        $approvalObjectUsers = array_values(array_filter(explode(",", $this->approval->users), function($value) {
+            return $value != Auth::id() && $value;
+        }));
+
+        switch (ApprovalMode::tryFrom($this->approval->approval_mode)) {
+            case ApprovalMode::ALTERNATIVE_APPROVAL:
+                if (! $nextNode) {
+                    $this->approvedHandle();
+                } else {
+                    $this->approvedToNextNodeHandle($nextNodeIndex, $nextNode);
+                }
+                break;
+            case ApprovalMode::COUNTERSIGNATURE:
+            case ApprovalMode::SEQUENTIAL_APPROVAL:
+                if (count($approvalObjectUsers) > 0 ) {
+                    $this->approvedWaitOthers($approvalObjectUsers);
+                } elseif (! $nextNode) {
+                    $this->approvedHandle();
+                } else {
+                    $this->approvedToNextNodeHandle($nextNodeIndex, $nextNode);
+                }
+                break;
+        }
+    }
+
+    protected function approvedWaitOthers(array $approvalObjectUsers)
+    {
+        $this->approval->users = sprintf(",%s,", implode(',', $approvalObjectUsers));
+
+        $this->objectAction = ObjectAction::APPROVED_WAIT_OTHERS;
 
-        if (! $nextNodes) {
-            $this->approval->status = ObjectApprovalStatus::APPROVED->value;
-            return;
+        if ($this->approval->approval_mode == ApprovalMode::SEQUENTIAL_APPROVAL->value) {
+            $this->actionExtraFields['next_sequential_user'] = $approvalObjectUsers[0];
         }
+    }
 
-        $this->actionExtraFields = [
-            'now' => $this->approval->node_level,
-            'next' =>  $nextNodeIndex,
-        ];
+    protected function approvedHandle()
+    {
+        $this->approval->status = ObjectApprovalStatus::APPROVED->value;
+
+        $this->objectAction = ObjectAction::APPROVAL_APPROVED;
+    }
+
+    protected function approvedToNextNodeHandle(int $nextNodeIndex, array $nextNode)
+    {
+        $this->actionExtraFields['next'] = $nextNodeIndex;
 
         $this->approval->node_level = $nextNodeIndex;
-        $this->approval->users = sprintf(",%s,", implode(',', $nextNodes['approval_users']));
+        $this->approval->users = sprintf(",%s,", implode(',', $nextNode['approval_users']));
+        $this->approval->approval_mode = $nextNode['approval_mode'];
+
+        $this->objectAction = ObjectAction::APPROVED_TO_NEXT_NODE;
+
+        if ($this->approval->approval_mode == ApprovalMode::SEQUENTIAL_APPROVAL->value) {
+            $this->actionExtraFields['next_sequential_user'] = $nextNode['approval_users'][0];
+        }
     }
 
     protected function rejected(): void

+ 13 - 4
app/Services/Approval/StoreService.php

@@ -6,6 +6,7 @@ use App\Models\Approval;
 use App\Models\ApprovalFlow;
 use App\Models\Enums\ApprovalFlowObjectType;
 use App\Models\Enums\ApprovalFlowType;
+use App\Models\Enums\ApprovalMode;
 use App\Models\Enums\ApprovalObjectType;
 use App\Models\Enums\ObjectAction;
 use App\Models\Enums\ObjectApprovalStatus;
@@ -46,19 +47,27 @@ class StoreService
             'object_type' => $approvalObjectType->flowType()->value,
             'object_id' => $objectId,
             'node_level' => $firstNode['level'],
+            'approval_mode' => $firstNode['approval_mode'],
             'users' => sprintf(",%s,", implode(',', $firstNode['approval_users'])),
             'remark' => $remark,
             'created_by' => Auth::id(),
             'status' => ObjectApprovalStatus::DOING,
         ]);
 
+        $extraFields = [
+            'now' => 0,
+            'next' => 1,
+            'approval_mode' => $firstNode['approval_mode'],
+        ];
+
+        if ($approval->approval_mode == ApprovalMode::SEQUENTIAL_APPROVAL->value) {
+            $extraFields['next_sequential_user'] = $firstNode['approval_users'][0];
+        }
+
         ActionRepository::createByApproval(
             $approval,
             ObjectAction::APPROVAL_REQUEST,
-            extraFields:  [
-                'now' => 0,
-                'next' => 1,
-            ],
+            extraFields:  $extraFields,
         );
     }
 

+ 9 - 1
app/Services/Notification/Abstracts/ActionBrowserNotificationAbstract.php

@@ -4,6 +4,7 @@ namespace App\Services\Notification\Abstracts;
 
 use App\Models\Action;
 use App\Models\Approval;
+use App\Models\Enums\ApprovalMode;
 use App\Models\Enums\NotificationObjectType;
 use App\Models\Enums\ObjectAction;
 use App\Models\Notification;
@@ -85,7 +86,14 @@ abstract class ActionBrowserNotificationAbstract implements ActionBrowserNotific
             return;
         }
 
-        $userIds = $approvalFlow->nodes[$this->action->extra_fields['next']]['approval_users'] ?? [];
+        $nextNode = $approvalFlow->nodes[$this->action->extra_fields['next']];
+        $approvalObjectUsers = array_values(array_filter(explode(',', $approval->users)));
+
+        $userIds = match (ApprovalMode::tryFrom($nextNode['approval_mode'])) {
+            ApprovalMode::ALTERNATIVE_APPROVAL, ApprovalMode::COUNTERSIGNATURE => $nextNode['approval_users'] ?? [],
+            ApprovalMode::SEQUENTIAL_APPROVAL => isset($approvalObjectUsers[0]) ? [$approvalObjectUsers[0]] : [],
+            default => [],
+        };
 
         $this->storeNotification($userIds, [
             'action' => 'waitingForMyApproval'

+ 2 - 0
app/Services/Notification/ActionEmail/ActionEmailService.php

@@ -89,6 +89,8 @@ class ActionEmailService
 
             $approvalFlow = $approval->approvalFlow()->first();
 
+            dump($this->action->extra_fields);
+
             $nextApprovalUsers = $approvalFlow?->nodes[$this->action->extra_fields['next']]['approval_users'] ?? [];
 
             $userIds = match ($this->objectAction) {

+ 28 - 0
database/migrations/2024_07_03_203237_add_approval_mode_to_approvals_table.php

@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('approvals', function (Blueprint $table) {
+            $table->tinyInteger("approval_mode")->nullable()->after("approval_flow_id");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('approvals', function (Blueprint $table) {
+            $table->dropColumn(['approval_mode']);
+        });
+    }
+};

+ 1 - 0
lang/en/action-labels.php

@@ -48,6 +48,7 @@ return[
         ObjectAction::APPROVAL_REJECTED->value => "approval rejected",
         ObjectAction::APPROVED_CANCELED->value => "approval canceled",
         ObjectAction::APPROVED_TO_NEXT_NODE->value => "approved to next step",
+        ObjectAction::APPROVED_WAIT_OTHERS->value => "Approved pending approval by others",
 
         "waitingForMyApproval" => "Waiting for my approval",
     ]