diff --git a/app/Http/Controllers/Business/CourierAgreementSignatures.php b/app/Http/Controllers/Business/CourierAgreementSignatures.php
index 60e336cf..8ab884b0 100644
--- a/app/Http/Controllers/Business/CourierAgreementSignatures.php
+++ b/app/Http/Controllers/Business/CourierAgreementSignatures.php
@@ -63,7 +63,10 @@ class CourierAgreementSignatures extends Controller
$request->merge(['counterparty_signer_type' => 'organization']);
}
- $data = $request->validate($this->rules());
+ $data = $request->validate($this->rules(), [
+ 'operator_name.required_if' => '请填写快递方经办人的 E 签宝实名认证姓名。',
+ 'secondary_operator_name.required_if' => '请填写我方 E 签宝账号绑定的实名认证姓名。',
+ ]);
$type = $data['document_type'];
if ($type === 'authorization' && count($data['authorization_items'] ?? []) === 0) {
throw ValidationException::withMessages(['authorization_items' => '授权委托书至少选择一项授权事项。']);
@@ -100,6 +103,17 @@ class CourierAgreementSignatures extends Controller
$data['operator_name'] = trim((string) $data['counterparty_organization_name']);
}
}
+ if ($type === 'cooperation-contract') {
+ $companyNames = [trim($data['cooperation_party_a_name']), trim($data['cooperation_party_b_name'])];
+ foreach (['operator_name' => '快递方', 'secondary_operator_name' => '我方'] as $field => $party) {
+ $data[$field] = trim($data[$field]);
+ if (in_array($data[$field], $companyNames, true)) {
+ throw ValidationException::withMessages([
+ $field => "{$party}实名认证姓名应填写账号绑定的个人姓名,不能填写甲乙方公司名称。",
+ ]);
+ }
+ }
+ }
$this->validateEsignAccount($data['esign_account'], 'esign_account');
if (! empty($data['secondary_esign_account'])) {
$this->validateEsignAccount($data['secondary_esign_account'], 'secondary_esign_account');
@@ -303,7 +317,8 @@ class CourierAgreementSignatures extends Controller
'signing_deadline' => $date,
'esign_account' => ['required', 'string', 'max:255'],
'secondary_esign_account' => ['required_if:document_type,entity-change,cooperation-contract,authorization', 'nullable', 'string', 'max:255'],
- 'operator_name' => ['nullable', 'string', 'min:2', 'max:100'],
+ 'operator_name' => ['required_if:document_type,cooperation-contract', 'nullable', 'string', 'min:2', 'max:100'],
+ 'secondary_operator_name' => ['required_if:document_type,cooperation-contract', 'nullable', 'string', 'min:2', 'max:100'],
'counterparty_organization_name' => ['required_if:document_type,account-proof,account-change', 'nullable', 'string', 'min:2', 'max:255'],
'counterparty_signer_type' => ['required_if:document_type,account-proof,account-change', 'nullable', 'in:organization,person'],
'archive_mode' => ['required', 'in:auto,manual'],
diff --git a/app/Support/CourierAgreementSignatureService.php b/app/Support/CourierAgreementSignatureService.php
index f10dfa51..f0f6fe46 100644
--- a/app/Support/CourierAgreementSignatureService.php
+++ b/app/Support/CourierAgreementSignatureService.php
@@ -307,13 +307,13 @@ final class CourierAgreementSignatureService
[
'signer_type' => 1,
'organization_name' => $payload['cooperation_party_a_name'] ?? $signature->company_full_name,
- 'operator_name' => $payload['cooperation_party_a_name'] ?? $signature->company_full_name,
+ 'operator_name' => $payload['secondary_operator_name'],
'operator_account' => $payload['secondary_esign_account'] ?? $signature->operator_account,
],
[
'signer_type' => 1,
'organization_name' => $payload['cooperation_party_b_name'] ?? $signature->courier_name,
- 'operator_name' => $signature->operator_name ?: ($payload['cooperation_party_b_name'] ?? $signature->courier_name),
+ 'operator_name' => $signature->operator_name,
'operator_account' => $signature->operator_account,
],
];
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 d97fda09..5b7b529e 100644
--- a/resources/views/business/pages/courier-payables/agreement-signing.blade.php
+++ b/resources/views/business/pages/courier-payables/agreement-signing.blade.php
@@ -1528,6 +1528,11 @@
主体变更、合作合同和授权委托书需要双方分别实名签署;合作合同统一使用我方账号 {{ $agreementCooperationSecondaryEsignAccount }}。
+
+
+
+
双方实名认证姓名均填写各自账号绑定的个人姓名;甲乙方公司名称在下方合同资料中填写。
+
@@ -2004,6 +2009,8 @@
var accountSignerHelp = root.querySelector('[data-account-signer-help]');
var operatorNameInput = root.querySelector('[data-account-operator-name]');
var operatorRequiredMark = root.querySelector('[data-account-operator-required]');
+ var cooperationOperatorField = root.querySelector('[data-cooperation-operator-field]');
+ var secondaryOperatorInput = root.querySelector('input[name="secondary_operator_name"]');
var sharedCourierList = root.querySelector('[data-shared-courier-list]');
var sharedCourierAdd = root.querySelector('[data-shared-courier-add]');
var quoteVersionField = root.querySelector('[data-quote-confirmation-field]');
@@ -2704,6 +2711,7 @@
var courier = fieldValue('[name="courier_name"]', '快递公司');
var companyName = fieldValue('[name="company_full_name"]', '待填写公司全称');
var accountAgreement = templateType === 'account-proof' || templateType === 'account-change';
+ var cooperationContract = templateType === 'cooperation-contract';
var accountSignerTypeInput = accountSignerTypeInputs.find(function (input) {
return input.checked;
});
@@ -2724,14 +2732,20 @@
: '填写对方在 E 签宝认证的企业全称';
}
if (operatorNameInput) {
- operatorNameInput.disabled = !accountAgreement || accountSignerIsPerson;
- operatorNameInput.required = accountAgreement && !accountSignerIsPerson;
- if (accountSignerIsPerson) {
+ var needsOperatorName = cooperationContract || (accountAgreement && !accountSignerIsPerson);
+ operatorNameInput.disabled = !needsOperatorName;
+ operatorNameInput.required = needsOperatorName;
+ if (accountAgreement && accountSignerIsPerson) {
operatorNameInput.value = accountOrganizationInput ? accountOrganizationInput.value : '';
}
}
if (operatorRequiredMark) {
- operatorRequiredMark.hidden = !accountAgreement || accountSignerIsPerson;
+ operatorRequiredMark.hidden = !operatorNameInput.required;
+ }
+ if (cooperationOperatorField) {
+ cooperationOperatorField.hidden = !cooperationContract;
+ secondaryOperatorInput.disabled = !cooperationContract;
+ secondaryOperatorInput.required = cooperationContract;
}
if (accountSignerLabel) {
accountSignerLabel.textContent = accountSignerIsPerson ? '对方签署个人姓名' : '对方签署企业全称';
diff --git a/tests/Feature/Business/AgreementSigningPageTest.php b/tests/Feature/Business/AgreementSigningPageTest.php
index 1643fc1a..a5f1a90b 100644
--- a/tests/Feature/Business/AgreementSigningPageTest.php
+++ b/tests/Feature/Business/AgreementSigningPageTest.php
@@ -93,6 +93,8 @@ class AgreementSigningPageTest extends FeatureTestCase
->assertSee("setPreviewValue('proofSigner', '公司盖章')", false)
->assertSee("setPreviewValue('changeSigner', '公司盖章')", false)
->assertSeeText('合作合同统一使用我方账号 18052733960')
+ ->assertSeeText('我方 E 签宝实名认证姓名')
+ ->assertSee('name="secondary_operator_name"', false)
->assertSeeText('填写变量')
->assertSeeText('签署完成后自动归档')
->assertSeeText('人工确认后归档')
diff --git a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php
index 3bcd66e3..a49cfdab 100644
--- a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php
+++ b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php
@@ -181,25 +181,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
public function test_cooperation_contract_maps_party_variables_and_creates_two_signers(): void
{
$this->fakeEsign();
- $payload = [
- 'idempotency_key' => 'cooperation-001',
- 'courier_name' => '新北中通',
- 'document_type' => 'cooperation-contract',
- 'signing_subject' => $this->company->name,
- 'company_full_name' => $this->company->name,
- 'signature_date' => '2026-09-21',
- 'signing_deadline' => now()->addDays(7)->toDateString(),
- 'esign_account' => '13800138000',
- 'secondary_esign_account' => '13900139000',
- 'operator_name' => '张经理',
- 'archive_mode' => 'auto',
- 'cooperation_party_a_name' => $this->company->name,
- 'cooperation_party_a_phone' => '0519-88886666',
- 'cooperation_party_b_name' => '新北中通快递有限公司',
- 'cooperation_party_b_phone' => '18112896651',
- 'cooperation_start_date' => '2026-01-01',
- 'cooperation_end_date' => '2026-12-31',
- ];
+ $payload = $this->cooperationContractPayload('cooperation-001');
$this->loginAs()
->post(route('business.courier-agreement-signatures.store'), $payload)
@@ -209,6 +191,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
$this->assertSame('cooperation-contract', $signature->document_type);
$this->assertSame('TPL-COOPERATION-CONTRACT-V1', $signature->template_code);
$this->assertSame('18052733960', $signature->payload['secondary_esign_account']);
+ $this->assertSame('李经理', $signature->payload['secondary_operator_name']);
$this->assertSame('0519-88886666', $signature->payload['cooperation_party_a_phone']);
$this->assertSame('2026-12-31', $signature->payload['cooperation_end_date']);
@@ -223,10 +206,41 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
return count($signers) === 2
&& ($signers[0]['orgSignerInfo']['orgName'] ?? null) === $this->company->name
&& ($signers[0]['orgSignerInfo']['transactorInfo']['psnAccount'] ?? null) === '18052733960'
- && ($signers[1]['orgSignerInfo']['orgName'] ?? null) === '新北中通快递有限公司';
+ && ($signers[0]['orgSignerInfo']['transactorInfo']['psnInfo']['psnName'] ?? null) === '李经理'
+ && ($signers[1]['orgSignerInfo']['orgName'] ?? null) === '新北中通快递有限公司'
+ && ($signers[1]['orgSignerInfo']['transactorInfo']['psnAccount'] ?? null) === '13800138000'
+ && ($signers[1]['orgSignerInfo']['transactorInfo']['psnInfo']['psnName'] ?? null) === '张经理';
});
}
+ public function test_cooperation_contract_requires_both_real_names_before_creating_a_task(): void
+ {
+ $this->fakeEsign();
+ foreach (['operator_name', 'secondary_operator_name'] as $field) {
+ $payload = $this->cooperationContractPayload('missing-' . $field);
+ $payload[$field] = ' ';
+ $this->withExceptionHandling()->loginAs()
+ ->post(route('business.courier-agreement-signatures.store'), $payload)
+ ->assertSessionHasErrors($field);
+ }
+ $this->assertSame(0, CourierAgreementSignature::query()->count());
+ Http::assertNothingSent();
+ }
+
+ public function test_cooperation_contract_rejects_company_names_as_personal_names(): void
+ {
+ $this->fakeEsign();
+ foreach (['operator_name' => 'cooperation_party_b_name', 'secondary_operator_name' => 'cooperation_party_a_name'] as $field => $companyField) {
+ $payload = $this->cooperationContractPayload('company-' . $field);
+ $payload[$field] = $payload[$companyField];
+ $this->withExceptionHandling()->loginAs()
+ ->post(route('business.courier-agreement-signatures.store'), $payload)
+ ->assertSessionHasErrors($field);
+ }
+ $this->assertSame(0, CourierAgreementSignature::query()->count());
+ Http::assertNothingSent();
+ }
+
public function test_shared_contract_accepts_multiple_couriers_and_preserves_note_in_pdf(): void
{
$this->fakeEsign();
@@ -716,6 +730,30 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
];
}
+ private function cooperationContractPayload(string $idempotencyKey): array
+ {
+ return [
+ 'idempotency_key' => $idempotencyKey,
+ 'courier_name' => '新北中通',
+ 'document_type' => 'cooperation-contract',
+ 'signing_subject' => $this->company->name,
+ 'company_full_name' => $this->company->name,
+ 'signature_date' => '2026-09-21',
+ 'signing_deadline' => now()->addDays(7)->toDateString(),
+ 'esign_account' => '13800138000',
+ 'secondary_esign_account' => '13900139000',
+ 'operator_name' => '张经理',
+ 'secondary_operator_name' => '李经理',
+ 'archive_mode' => 'auto',
+ 'cooperation_party_a_name' => $this->company->name,
+ 'cooperation_party_a_phone' => '0519-88886666',
+ 'cooperation_party_b_name' => '新北中通快递有限公司',
+ 'cooperation_party_b_phone' => '18112896651',
+ 'cooperation_start_date' => '2026-01-01',
+ 'cooperation_end_date' => '2026-12-31',
+ ];
+ }
+
private function accountChangePayload(string $idempotencyKey): array
{
return [