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

@@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('settlement_adjustments', function (Blueprint $table): void {
$table->foreignId('result_bill_id')
->nullable()
->after('original_bill_id')
->constrained('settlement_bills')
->nullOnDelete();
});
Schema::table('payment_records', function (Blueprint $table): void {
$table->dropIndex(['settlement_bill_id', 'idempotency_key']);
});
Schema::table('settlement_adjustments', function (Blueprint $table): void {
$table->dropIndex(['original_bill_id', 'idempotency_key']);
});
DB::statement(
'CREATE UNIQUE INDEX payment_records_bill_idempotency_unique '
.'ON payment_records (settlement_bill_id, idempotency_key) '
."WHERE idempotency_key IS NOT NULL AND idempotency_key <> ''",
);
DB::statement(
'CREATE UNIQUE INDEX settlement_adjustments_bill_idempotency_unique '
.'ON settlement_adjustments (original_bill_id, idempotency_key) '
."WHERE idempotency_key IS NOT NULL AND idempotency_key <> ''",
);
}
public function down(): void
{
DB::statement('DROP INDEX IF EXISTS payment_records_bill_idempotency_unique');
DB::statement('DROP INDEX IF EXISTS settlement_adjustments_bill_idempotency_unique');
Schema::table('payment_records', function (Blueprint $table): void {
$table->index(['settlement_bill_id', 'idempotency_key']);
});
Schema::table('settlement_adjustments', function (Blueprint $table): void {
$table->index(['original_bill_id', 'idempotency_key']);
$table->dropConstrainedForeignId('result_bill_id');
});
}
};