fix(funds): 加固转账冲正、结算收付幂等与坏账核销
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled

- 转入 main_site_timeout 等不可冲正场景直接拒绝,避免假结案
- payment_records / settlement_adjustments 增加 partial unique 索引
- 坏账核销行锁 + meta 幂等回放;补差单记录 result_bill_id
- 新增 FundOperationsHardeningTest 覆盖关键路径
This commit is contained in:
2026-06-26 15:19:17 +08:00
parent 3f04b4ebe3
commit 7ec8f4c5a2
8 changed files with 535 additions and 104 deletions

View File

@@ -384,6 +384,14 @@ final class LotteryTransferService
);
}
if (! $this->isEligibleForReverse($locked)) {
throw new WalletOperationException(
'reverse_not_eligible',
ErrorCode::WalletExternalRejected->value,
422,
);
}
if ($locked->direction === self::DIR_OUT) {
$idempotentKey = 'reversal:'.$locked->transfer_no;
$alreadyCredited = WalletTxn::query()
@@ -566,6 +574,20 @@ final class LotteryTransferService
&& $this->isEligibleForCompleteCredit($order);
}
/** 后台冲正:转出 pending_reconcile 或转入 lottery_credit_failed 且主站已扣款。 */
public function isEligibleForReverse(TransferOrder $order): bool
{
if ($order->status !== self::ST_PENDING_RECONCILE) {
return false;
}
if ($order->direction === self::DIR_OUT) {
return true;
}
return $this->isEligibleForTransferInReverse($order);
}
public function isEligibleForManualProcess(TransferOrder $order): bool
{
if (! in_array($order->status, [self::ST_PROCESSING, self::ST_FAILED, self::ST_PENDING_RECONCILE], true)) {