From 2534e33988eed3531187bf4598b44ca62fb84522 Mon Sep 17 00:00:00 2001 From: limeng <1131177064@qq.com> Date: Sun, 11 Oct 2026 10:29:55 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(=E5=8D=8F=E8=AE=AE=E7=AD=BE=E7=BD=B2):?= =?UTF-8?q?=20=E5=BE=85=E5=A4=84=E7=90=86=E4=BB=BB=E5=8A=A1=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=A1=AE=E8=AE=A4=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Business/CourierAgreementSignatures.php | 35 ++++ .../Business/CourierAgreementSignature.php | 21 ++ .../CourierAgreementSignatureService.php | 4 + .../agreement-signing.blade.php | 136 ++++++++++++- routes/admin.php | 1 + .../CourierAgreementTaskDeletionTest.php | 185 ++++++++++++++++++ 6 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/Business/CourierAgreementTaskDeletionTest.php diff --git a/app/Http/Controllers/Business/CourierAgreementSignatures.php b/app/Http/Controllers/Business/CourierAgreementSignatures.php index 7268a088..1500f9d5 100644 --- a/app/Http/Controllers/Business/CourierAgreementSignatures.php +++ b/app/Http/Controllers/Business/CourierAgreementSignatures.php @@ -309,6 +309,41 @@ class CourierAgreementSignatures extends Controller ->with('success', "{$signature->documentLabel()}已人工确认并归档。"); } + public function destroy(Request $request, CourierAgreementSignature $agreementSignature): JsonResponse|RedirectResponse + { + $this->ensureCanManage(); + abort_unless($agreementSignature->company_id === (int) company_id(), 404); + $request->validate(['delete_confirmed' => ['required', 'accepted']]); + + DB::transaction(function () use ($agreementSignature): void { + $signature = CourierAgreementSignature::query()->disableCache()->lockForUpdate()->findOrFail($agreementSignature->id); + abort_unless($signature->canDelete(), 422, '仅可删除未推送成功、已撤销、已过期或已拒签的任务;签署中、已签署或状态不明的任务不能删除。'); + + if (filled($signature->esign_sign_flow_id)) { + abort_unless(EsignClient::configured() + && $signature->environment === config('services.esign.environment', 'sandbox'), 422, '无法核对 E 签宝流程状态,任务未删除。'); + try { + $detail = app(EsignClient::class)->signFlowDetail($signature->esign_sign_flow_id); + } catch (\Throwable) { + throw ValidationException::withMessages(['signature' => 'E 签宝状态查询失败,任务未删除,请稍后重试。']); + } + abort_unless(in_array((string) ($detail['signFlowStatus'] ?? $detail['status'] ?? ''), ['3', '5', '7'], true), 422, 'E 签宝流程尚未结束或已完成签署,任务未删除,请先同步状态。'); + } + + // Keep the original files and callback audit records for traceability. + $signature->updated_by = user_id(); + $signature->save(); + $signature->delete(); + }); + + $message = '签署任务已删除。'; + + return $request->expectsJson() + ? response()->json(['message' => $message]) + : redirect()->route('business.pages.show', ['section' => 'courier-payables', 'page' => 'agreement-signing', 'agreement_view' => 'tasks']) + ->with('success', $message); + } + public function download(CourierAgreementSignature $agreementSignature, string $kind): StreamedResponse|RedirectResponse { $this->ensureCanManage(); diff --git a/app/Models/Business/CourierAgreementSignature.php b/app/Models/Business/CourierAgreementSignature.php index ce9b63a8..b7e08b98 100644 --- a/app/Models/Business/CourierAgreementSignature.php +++ b/app/Models/Business/CourierAgreementSignature.php @@ -119,6 +119,27 @@ class CourierAgreementSignature extends Model && blank($this->esign_sign_flow_id); } + public function canDelete(): bool + { + if ($this->trashed() || $this->completed_at || $this->archived_at + || filled($this->signed_pdf_path) || filled($this->certificate_path) + || in_array((string) $this->remote_status, ['1', '2'], true)) { + return false; + } + + if (blank($this->esign_sign_flow_id)) { + return in_array($this->status, [self::STATUS_PREPARED, self::STATUS_FAILED, self::STATUS_REVOKED, self::STATUS_EXPIRED, self::STATUS_REJECTED], true) + || ($this->status === self::STATUS_UNKNOWN && $this->hasDefinitiveFailure()); + } + + return match ($this->status) { + self::STATUS_REVOKED => $this->remote_status === '3', + self::STATUS_EXPIRED => $this->remote_status === '5', + self::STATUS_REJECTED => $this->remote_status === '7', + default => false, + }; + } + public function documentLabel(): string { return self::DOCUMENTS[$this->document_type]['name'] ?? $this->document_type; diff --git a/app/Support/CourierAgreementSignatureService.php b/app/Support/CourierAgreementSignatureService.php index 46dbcfca..1d7318bc 100644 --- a/app/Support/CourierAgreementSignatureService.php +++ b/app/Support/CourierAgreementSignatureService.php @@ -35,12 +35,16 @@ final class CourierAgreementSignatureService $signature = DB::transaction(function () use ($companyId, $document, $data, $payload, $idempotencyKey, $businessNo, $userId): CourierAgreementSignature { $existing = CourierAgreementSignature::allCompanies() + ->withTrashed() ->where('company_id', $companyId) ->where('idempotency_key', $idempotencyKey) ->lockForUpdate() ->first(); if ($existing) { + if ($existing->trashed()) { + throw ValidationException::withMessages(['signature' => '该签署请求已删除,请重新打开发起签章页面后提交新的请求。']); + } if ($existing->esign_sign_flow_id || in_array($existing->status, [CourierAgreementSignature::STATUS_SENDING, CourierAgreementSignature::STATUS_SIGNING, CourierAgreementSignature::STATUS_COMPLETED, CourierAgreementSignature::STATUS_ARCHIVED], true)) { return $existing; } diff --git a/resources/views/business/pages/courier-payables/agreement-signing.blade.php b/resources/views/business/pages/courier-payables/agreement-signing.blade.php index d5b52c6a..b92627a5 100644 --- a/resources/views/business/pages/courier-payables/agreement-signing.blade.php +++ b/resources/views/business/pages/courier-payables/agreement-signing.blade.php @@ -447,6 +447,15 @@ background: var(--agreement-accent-hover); } + .agreement-button--danger { + color: var(--agreement-danger, #B80000); + } + + .agreement-button--danger:hover { + border-color: var(--agreement-danger, #B80000); + background: #FFF5F5; + } + .agreement-button:disabled, .agreement-icon-button:disabled { border-color: var(--agreement-border); @@ -1426,7 +1435,7 @@ @foreach ($agreementTasks as $task) @php($taskError = $task->lastErrorSummary()) @php($taskFailed = $task->status === 'failed' || ($task->status === 'unknown' && $task->hasDefinitiveFailure())) - + {{ $task->business_no }} {{ $task->courier_name }} {{ $task->documentLabel() }} @@ -1466,6 +1475,14 @@ @if ($task->certificate_path) @endif + @if ($agreementScope === 'active' && $task->canDelete()) +
+ @csrf + @method('DELETE') + + +
+ @endif @endforeach @@ -2136,6 +2153,26 @@ + +