Files
zyt/server/app/common/service/pharmacy/PharmacySubmissionClaimWorkflow.php
T
2026-07-22 14:50:34 +08:00

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];
}
}