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