199 lines
6.8 KiB
PHP
199 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Business;
|
|
|
|
use App\Abstracts\Model;
|
|
use App\Support\EsignErrorMessage;
|
|
use Illuminate\Contracts\Encryption\DecryptException;
|
|
|
|
class CourierAgreementSignature extends Model
|
|
{
|
|
public const STATUS_PREPARED = 'prepared';
|
|
public const STATUS_SENDING = 'sending';
|
|
public const STATUS_SIGNING = 'signing';
|
|
public const STATUS_COMPLETED = 'completed';
|
|
public const STATUS_ARCHIVED = 'archived';
|
|
public const STATUS_REVOKED = 'revoked';
|
|
public const STATUS_EXPIRED = 'expired';
|
|
public const STATUS_REJECTED = 'rejected';
|
|
public const STATUS_FAILED = 'failed';
|
|
public const STATUS_UNKNOWN = 'unknown';
|
|
|
|
public const DOCUMENTS = [
|
|
'account-proof' => [
|
|
'code' => 'TPL-ACCOUNT-CERT-V2',
|
|
'version' => '2',
|
|
'name' => '收款账户证明',
|
|
'file' => '收款账户证明文件.doc',
|
|
],
|
|
'account-change' => [
|
|
'code' => 'TPL-ACCOUNT-CHANGE-V1',
|
|
'version' => '1',
|
|
'name' => '收款账户变更',
|
|
'file' => '收款账户变更证明.doc',
|
|
],
|
|
'entity-change' => [
|
|
'code' => 'TPL-ENTITY-CHANGE-V1',
|
|
'version' => '1',
|
|
'name' => '公司主体变更',
|
|
'file' => '公司主体变更证明.docx',
|
|
],
|
|
'shared-contract' => [
|
|
'code' => 'TPL-SHARED-CONTRACT-V1',
|
|
'version' => '1',
|
|
'name' => '共用合同证明',
|
|
'file' => '共用合同证明.docx',
|
|
],
|
|
'cooperation-contract' => [
|
|
'code' => 'TPL-COOPERATION-CONTRACT-V1',
|
|
'version' => '1',
|
|
'name' => '合作合同',
|
|
'file' => '保丟不保损-合同发快递2024.8.doc',
|
|
],
|
|
'authorization' => [
|
|
'code' => 'TPL-AUTHORIZATION-V1',
|
|
'version' => '1',
|
|
'name' => '授权委托书',
|
|
'file' => '授权委托书(快递).docx',
|
|
],
|
|
'quote-confirmation' => [
|
|
'code' => 'TPL-QUOTE-CONFIRMATION-V1',
|
|
'version' => '1',
|
|
'name' => '报价确认单',
|
|
'file' => '报价确认单(报价版本快照).pdf',
|
|
],
|
|
];
|
|
|
|
protected $table = 'business_courier_agreement_signatures';
|
|
|
|
protected $fillable = [
|
|
'company_id', 'business_no', 'idempotency_key', 'document_type', 'template_code',
|
|
'template_version', 'source_file_name', 'status', 'courier_name', 'company_full_name',
|
|
'organization_name', 'operator_name', 'operator_account', 'payload', 'environment',
|
|
'expires_at', 'source_pdf_path', 'source_pdf_sha256', 'esign_file_id',
|
|
'esign_sign_flow_id', 'remote_status', 'signed_pdf_path', 'signed_pdf_sha256',
|
|
'certificate_path', 'certificate_sha256', 'sent_at', 'completed_at', 'archived_at',
|
|
'last_synced_at', 'last_error', 'callback_payload_hash', 'created_by', 'updated_by',
|
|
];
|
|
|
|
protected $casts = [
|
|
'company_id' => 'integer',
|
|
'expires_at' => 'datetime',
|
|
'sent_at' => 'datetime',
|
|
'completed_at' => 'datetime',
|
|
'archived_at' => 'datetime',
|
|
'last_synced_at' => 'datetime',
|
|
'payload' => 'encrypted:array',
|
|
'operator_account' => 'encrypted',
|
|
];
|
|
|
|
public function partnerTypeLabel(): string
|
|
{
|
|
// Records created before partner types were introduced are courier tasks.
|
|
try {
|
|
$partnerType = $this->payload['partner_type'] ?? 'courier';
|
|
} catch (DecryptException) {
|
|
// Keep legacy rows with unreadable encrypted payloads renderable.
|
|
$partnerType = 'courier';
|
|
}
|
|
|
|
return $partnerType === 'supplier' ? '供应商' : '快递公司';
|
|
}
|
|
|
|
public function credentialDocuments(): array
|
|
{
|
|
try {
|
|
return $this->payload['credential_documents'] ?? [];
|
|
} catch (\Throwable) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public function statusLabel(): string
|
|
{
|
|
return match ($this->status) {
|
|
self::STATUS_PREPARED => '待推送',
|
|
self::STATUS_SENDING => '推送中',
|
|
self::STATUS_SIGNING => '待签署',
|
|
self::STATUS_COMPLETED => '签署完成,待归档',
|
|
self::STATUS_ARCHIVED => '已签署并归档',
|
|
self::STATUS_REVOKED => '已撤销',
|
|
self::STATUS_EXPIRED => '已过期',
|
|
self::STATUS_REJECTED => '已拒签',
|
|
self::STATUS_FAILED => '处理失败',
|
|
self::STATUS_UNKNOWN => $this->hasDefinitiveFailure() ? '处理失败' : '状态未知,请人工核对',
|
|
default => $this->status,
|
|
};
|
|
}
|
|
|
|
public function hasDefinitiveFailure(): bool
|
|
{
|
|
return EsignErrorMessage::isDefinitiveFailure($this->last_error);
|
|
}
|
|
|
|
public function lastErrorSummary(): ?string
|
|
{
|
|
return EsignErrorMessage::sanitize($this->last_error);
|
|
}
|
|
|
|
public function canSend(): bool
|
|
{
|
|
return in_array($this->status, [self::STATUS_PREPARED, self::STATUS_FAILED], true)
|
|
&& blank($this->esign_sign_flow_id);
|
|
}
|
|
|
|
public function documentLabel(): string
|
|
{
|
|
foreach (\App\Support\SupplierCooperationTemplates::TEMPLATES as $template) {
|
|
if ($this->template_code === $template['code']) {
|
|
return $template['name'];
|
|
}
|
|
}
|
|
return self::DOCUMENTS[$this->document_type]['name'] ?? $this->document_type;
|
|
}
|
|
|
|
/**
|
|
* The company that initiated the request is kept in the encrypted request
|
|
* snapshot. It is separate from organization_name, which is the E-sign
|
|
* signing organization for the counterparty.
|
|
*/
|
|
public function initiatorName(): string
|
|
{
|
|
try {
|
|
$payloadName = trim((string) data_get($this->payload, 'signing_subject', ''));
|
|
} catch (\Throwable) {
|
|
// 历史记录可能由已轮换的 APP_KEY 加密。列表仍应可读,
|
|
// 使用未加密的公司快照作为发起人回退值。
|
|
$payloadName = '';
|
|
}
|
|
|
|
return $payloadName !== ''
|
|
? $payloadName
|
|
: (trim((string) $this->company_full_name) ?: '—');
|
|
}
|
|
|
|
/**
|
|
* Return the archive mode without allowing an unreadable historical
|
|
* payload to turn the signing task list into a 500 response.
|
|
*/
|
|
public function archiveMode(): string
|
|
{
|
|
try {
|
|
$mode = (string) data_get($this->payload, 'archive_mode', 'auto');
|
|
} catch (\Throwable) {
|
|
$mode = 'auto';
|
|
}
|
|
|
|
return in_array($mode, ['auto', 'manual'], true) ? $mode : 'auto';
|
|
}
|
|
|
|
public function archiveReason(): string
|
|
{
|
|
try {
|
|
return trim((string) data_get($this->payload, 'archive_reason', ''));
|
|
} catch (\Throwable) {
|
|
return '';
|
|
}
|
|
}
|
|
}
|