$lockOrder * @param callable(array):?array $lockClaim * @param callable(array,?array):mixed $mutation */ public static function run( callable $lockOrder, callable $lockClaim, callable $mutation, bool $assertMutable = true ): mixed { $order = Closure::fromCallable($lockOrder)(); if ($order === []) { throw new DomainException('订单不存在'); } $claim = Closure::fromCallable($lockClaim)($order); if ($assertMutable) { PharmacyRemoteSnapshotPolicy::assertMutable($order, $claim); } $result = Closure::fromCallable($mutation)($order, $claim); if ($result === false) { throw new DomainException('受保护变更未完成,事务已回滚'); } return $result; } /** @param callable(array,?array):mixed $mutation */ public static function execute( int $orderId, callable $mutation, bool $assertMutable = true, int $revision = 1 ): mixed { return Db::transaction(static fn (): mixed => self::run( static fn (): array => (array) (Db::name('tcm_prescription_order') ->where('id', $orderId) ->whereNull('delete_time') ->lock(true) ->find() ?: []), static fn (): ?array => Db::name('pharmacy_submission_claim') ->where('prescription_order_id', $orderId) ->where('source_revision', max($revision, 1)) ->lock(true) ->find() ?: null, $mutation, $assertMutable )); } /** @param callable(array>):mixed $mutation */ public static function executeForPrescription(int $prescriptionId, callable $mutation): mixed { return Db::transaction(static function () use ($prescriptionId, $mutation): mixed { $orders = Db::name('tcm_prescription_order') ->where('prescription_id', $prescriptionId) ->whereNull('delete_time') ->order('id', 'asc') ->lock(true) ->select() ->toArray(); foreach ($orders as $order) { $claim = Db::name('pharmacy_submission_claim') ->where('prescription_order_id', (int) $order['id']) ->where('source_revision', 1) ->lock(true) ->find(); PharmacyRemoteSnapshotPolicy::assertMutable($order, $claim ?: null); } $result = Closure::fromCallable($mutation)($orders); if ($result === false) { throw new DomainException('受保护变更未完成,事务已回滚'); } return $result; }); } }