93 lines
3.4 KiB
PHP
93 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace app\common\service\pharmacy;
|
|
|
|
use Closure;
|
|
use DomainException;
|
|
use InvalidArgumentException;
|
|
use Throwable;
|
|
|
|
final class PharmacySubmissionClaimWorkflow
|
|
{
|
|
private Closure $acquireClaim;
|
|
private Closure $invokeRemote;
|
|
private Closure $markSuccess;
|
|
private Closure $markFailure;
|
|
private Closure $markReconcile;
|
|
|
|
public function __construct(
|
|
callable $acquireClaim,
|
|
callable $invokeRemote,
|
|
callable $markSuccess,
|
|
callable $markFailure,
|
|
callable $markReconcile
|
|
) {
|
|
$this->acquireClaim = Closure::fromCallable($acquireClaim);
|
|
$this->invokeRemote = Closure::fromCallable($invokeRemote);
|
|
$this->markSuccess = Closure::fromCallable($markSuccess);
|
|
$this->markFailure = Closure::fromCallable($markFailure);
|
|
$this->markReconcile = Closure::fromCallable($markReconcile);
|
|
}
|
|
|
|
/** @return array<string,mixed> */
|
|
public function execute(string $target): array
|
|
{
|
|
if (!in_array($target, ['gancao', 'direct'], true)) {
|
|
throw new InvalidArgumentException('Unsupported pharmacy target');
|
|
}
|
|
|
|
$claim = ($this->acquireClaim)($target);
|
|
if (!empty($claim['idempotent'])) {
|
|
$result = is_array($claim['result'] ?? null) ? $claim['result'] : [];
|
|
return $result + ['target' => $target, 'idempotent' => true];
|
|
}
|
|
|
|
$token = trim((string) ($claim['token'] ?? $claim['claim_token'] ?? ''));
|
|
if ($token === '') {
|
|
throw new DomainException('药房提交凭证缺失');
|
|
}
|
|
$claimStatus = strtoupper(trim((string) ($claim['status'] ?? '')));
|
|
if ($target === 'gancao' && (
|
|
!empty($claim['reconcile'])
|
|
|| in_array($claimStatus, ['UNKNOWN', 'PENDING_RECONCILE'], true)
|
|
)) {
|
|
throw new PharmacyReconciliationRequiredException(
|
|
'甘草药房远端结果待核对,当前禁止重提;请等待人工或后续对账'
|
|
);
|
|
}
|
|
|
|
try {
|
|
$result = ($this->invokeRemote)($target, $token, $claim);
|
|
if (!is_array($result)) {
|
|
throw new DomainException('药房返回数据格式错误');
|
|
}
|
|
} catch (Throwable $exception) {
|
|
if (PharmacyRemoteOutcomeClassifier::isConfirmedNoCreate($exception)) {
|
|
($this->markFailure)($target, $token, $exception->getMessage(), $claim);
|
|
} else {
|
|
($this->markReconcile)($target, $token, $exception->getMessage(), $claim);
|
|
}
|
|
throw $exception;
|
|
}
|
|
|
|
try {
|
|
$finalized = (bool) ($this->markSuccess)($target, $token, $result, $claim);
|
|
} catch (Throwable $exception) {
|
|
($this->markReconcile)($target, $token, '远端成功但本地回写异常:' . $exception->getMessage(), $claim);
|
|
throw new PharmacyReconciliationRequiredException(
|
|
'远端可能已创建订单,本地回写失败,必须对账后再操作',
|
|
0,
|
|
$exception
|
|
);
|
|
}
|
|
if (!$finalized) {
|
|
($this->markReconcile)($target, $token, '远端成功但本地提交凭证无法完成', $claim);
|
|
throw new PharmacyReconciliationRequiredException('远端已返回成功,本地回写未完成,必须对账后再操作');
|
|
}
|
|
|
|
return $result + ['target' => $target, 'idempotent' => false];
|
|
}
|
|
}
|