From d5c55a8dbbf950794cb81a400a2a48e732b2a9cc Mon Sep 17 00:00:00 2001 From: yueyang <2495544247@qq.com> Date: Thu, 29 Oct 2026 22:41:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=B2=BE=E7=A1=AE=E9=9A=94=E7=A6=BBOA?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E6=8E=A5=E5=8F=A3=E6=95=85=E9=9A=9C=20(#778)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DingTalkProcessInstanceException.php | 22 ++++++++++ .../DingProcessDetailMirrorSynchronizer.php | 19 +++------ .../DingTalkProcessInstanceClient.php | 40 ++++++++++++++----- ...ingProcessDetailMirrorSynchronizerTest.php | 3 +- 4 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 modules/EcommerceFinance/Exceptions/DingTalkProcessInstanceException.php diff --git a/modules/EcommerceFinance/Exceptions/DingTalkProcessInstanceException.php b/modules/EcommerceFinance/Exceptions/DingTalkProcessInstanceException.php new file mode 100644 index 00000000..73202422 --- /dev/null +++ b/modules/EcommerceFinance/Exceptions/DingTalkProcessInstanceException.php @@ -0,0 +1,22 @@ +stopsRun; + } +} diff --git a/modules/EcommerceFinance/Services/DingProcessDetailMirrorSynchronizer.php b/modules/EcommerceFinance/Services/DingProcessDetailMirrorSynchronizer.php index 25566751..7c0a28af 100644 --- a/modules/EcommerceFinance/Services/DingProcessDetailMirrorSynchronizer.php +++ b/modules/EcommerceFinance/Services/DingProcessDetailMirrorSynchronizer.php @@ -8,6 +8,7 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; +use Modules\EcommerceFinance\Exceptions\DingTalkProcessInstanceException; use RuntimeException; use Throwable; @@ -27,8 +28,6 @@ class DingProcessDetailMirrorSynchronizer private const APPROVAL_API_BACKLOG_BATCH_LIMIT = 750; - private const APPROVAL_API_MAX_CONSECUTIVE_FAILURES = 5; - private const BUSINESS_ID_SYNC_LIMIT = 50; private readonly ConnectionInterface $connection; @@ -37,8 +36,6 @@ class DingProcessDetailMirrorSynchronizer private bool $approvalApiAvailable = true; - private int $approvalApiFailureStreak = 0; - /** @var array> */ private array $approvalSnapshots = []; @@ -67,7 +64,6 @@ class DingProcessDetailMirrorSynchronizer { $this->approvalApiAttempts = 0; $this->approvalApiAvailable = true; - $this->approvalApiFailureStreak = 0; $this->approvalSnapshots = []; $scope = $this->scope(); $state = $this->state(); @@ -177,7 +173,6 @@ class DingProcessDetailMirrorSynchronizer $this->approvalApiAttempts = 0; $this->approvalApiAvailable = true; - $this->approvalApiFailureStreak = 0; $this->approvalSnapshots = []; $scope = $this->scope(); $state = $this->state(); @@ -1310,20 +1305,16 @@ class DingProcessDetailMirrorSynchronizer $this->approvalApiAttempts++; try { - $snapshot = $this->approvalClient->processSnapshot($processInstanceId); - $this->approvalApiFailureStreak = 0; - - return $this->approvalSnapshots[$processInstanceId] = $snapshot; + return $this->approvalSnapshots[$processInstanceId] = + $this->approvalClient->processSnapshot($processInstanceId); } catch (Throwable $exception) { - $this->approvalApiFailureStreak++; - if ($this->approvalApiFailureStreak - >= self::APPROVAL_API_MAX_CONSECUTIVE_FAILURES) { + if ($exception instanceof DingTalkProcessInstanceException + && $exception->stopsRun()) { $this->approvalApiAvailable = false; } Log::warning('钉钉官方审批接口读取流程状态失败。', [ 'process_instance_id' => $processInstanceId, 'exception' => $exception::class, - 'consecutive_failures' => $this->approvalApiFailureStreak, ]); return null; diff --git a/modules/EcommerceFinance/Services/DingTalkProcessInstanceClient.php b/modules/EcommerceFinance/Services/DingTalkProcessInstanceClient.php index 1d7de1ae..3e740127 100644 --- a/modules/EcommerceFinance/Services/DingTalkProcessInstanceClient.php +++ b/modules/EcommerceFinance/Services/DingTalkProcessInstanceClient.php @@ -2,9 +2,10 @@ namespace Modules\EcommerceFinance\Services; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Client\RequestException; use Illuminate\Support\Facades\Http; -use RuntimeException; +use Modules\EcommerceFinance\Exceptions\DingTalkProcessInstanceException; class DingTalkProcessInstanceClient { @@ -48,7 +49,9 @@ class DingTalkProcessInstanceClient { $processInstanceId = trim($processInstanceId); if ($processInstanceId === '') { - throw new RuntimeException('DingTalk process instance ID is required.'); + throw new DingTalkProcessInstanceException( + 'DingTalk process instance ID is required.', + ); } if (isset($this->snapshots[$processInstanceId])) { return $this->snapshots[$processInstanceId]; @@ -63,23 +66,28 @@ class DingTalkProcessInstanceClient ->get(self::PROCESS_INSTANCE_URL, [ 'processInstanceId' => $processInstanceId, ]); - } catch (RequestException $exception) { - throw new RuntimeException( + } catch (ConnectionException|RequestException $exception) { + throw new DingTalkProcessInstanceException( 'DingTalk process instance lookup failed.', - 0, + true, $exception, ); } if (! $response->successful()) { - throw new RuntimeException( - 'DingTalk process instance lookup returned HTTP ' . $response->status() . '.', + $status = $response->status(); + + throw new DingTalkProcessInstanceException( + 'DingTalk process instance lookup returned HTTP ' . $status . '.', + in_array($status, [401, 403, 429], true) || $status >= 500, ); } $result = $response->json('result'); if (! is_array($result)) { - throw new RuntimeException('DingTalk process instance response was incomplete.'); + throw new DingTalkProcessInstanceException( + 'DingTalk process instance response was incomplete.', + ); } $values = []; @@ -114,7 +122,10 @@ class DingTalkProcessInstanceClient return $this->accessToken; } if (! $this->isConfigured()) { - throw new RuntimeException('DingTalk process API credentials are not configured.'); + throw new DingTalkProcessInstanceException( + 'DingTalk process API credentials are not configured.', + true, + ); } try { @@ -126,12 +137,19 @@ class DingTalkProcessInstanceClient ]) ->throw(); } catch (RequestException $exception) { - throw new RuntimeException('DingTalk application token lookup failed.', 0, $exception); + throw new DingTalkProcessInstanceException( + 'DingTalk application token lookup failed.', + true, + $exception, + ); } $token = trim((string) $response->json('access_token')); if ((int) $response->json('errcode', -1) !== 0 || $token === '') { - throw new RuntimeException('DingTalk application token was unavailable.'); + throw new DingTalkProcessInstanceException( + 'DingTalk application token was unavailable.', + true, + ); } $expiresIn = max(300, (int) $response->json('expires_in', 7200)); diff --git a/tests/Unit/EcommerceFinance/DingProcessDetailMirrorSynchronizerTest.php b/tests/Unit/EcommerceFinance/DingProcessDetailMirrorSynchronizerTest.php index 06c68dcd..fa77a077 100644 --- a/tests/Unit/EcommerceFinance/DingProcessDetailMirrorSynchronizerTest.php +++ b/tests/Unit/EcommerceFinance/DingProcessDetailMirrorSynchronizerTest.php @@ -5,6 +5,7 @@ namespace Tests\Unit\EcommerceFinance; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; +use Modules\EcommerceFinance\Exceptions\DingTalkProcessInstanceException; use Modules\EcommerceFinance\Repositories\DingProcessDetailMirrorRepository; use Modules\EcommerceFinance\Services\DingProcessDetailMirrorSynchronizer; use Modules\EcommerceFinance\Services\DingTalkProcessInstanceClient; @@ -1174,7 +1175,7 @@ class FakeDingTalkProcessInstanceClient extends DingTalkProcessInstanceClient { $this->calls[] = $processInstanceId; if (in_array($processInstanceId, $this->failingInstances, true)) { - throw new RuntimeException('审批接口模拟失败'); + throw new DingTalkProcessInstanceException('审批接口模拟失败'); } $cashierStatus = $this->cashierStatusByInstance[$processInstanceId] ?? 'not_started'; $tasks = $this->tasksByInstance[$processInstanceId] ?? [];