CaiWuXiTong/app/Models/Business/CourierAgreementSignature.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 '';
}
}
}