- 扩大玩家ID随机数位宽降低碰撞概率 - 授信额度变更加 lockForUpdate 防并发下调 - 结算冲正与入账利用唯一索引实现幂等闸门 - 失败登录计数加锁防丢失 - 代理子树查询改用子查询替代全表 pluck - 修复返点默认值百分比单位转换 - 测试中全量 fake 广播事件防 CI 500
This commit is contained in:
@@ -15,31 +15,34 @@ return new class extends Migration
|
||||
$defaults = config('agent_line_defaults', []);
|
||||
$now = now();
|
||||
|
||||
$roots = DB::table('agent_nodes')->where('depth', 0)->get(['id']);
|
||||
// 整批包在事务中:避免中途某条失败时部分根代理已补 profile、部分缺失的中间态。
|
||||
DB::transaction(function () use ($defaults, $now): void {
|
||||
$roots = DB::table('agent_nodes')->where('depth', 0)->get(['id']);
|
||||
|
||||
foreach ($roots as $root) {
|
||||
$nodeId = (int) $root->id;
|
||||
if (DB::table('agent_profiles')->where('agent_node_id', $nodeId)->exists()) {
|
||||
continue;
|
||||
foreach ($roots as $root) {
|
||||
$nodeId = (int) $root->id;
|
||||
if (DB::table('agent_profiles')->where('agent_node_id', $nodeId)->exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB::table('agent_profiles')->insert([
|
||||
'agent_node_id' => $nodeId,
|
||||
'total_share_rate' => (float) ($defaults['total_share_rate'] ?? 100),
|
||||
'credit_limit' => (int) ($defaults['credit_limit'] ?? 0) > 0
|
||||
? (int) $defaults['credit_limit']
|
||||
: 1_000_000,
|
||||
'allocated_credit' => 0,
|
||||
'used_credit' => 0,
|
||||
'rebate_limit' => (float) ($defaults['rebate_limit'] ?? 0.005),
|
||||
'default_player_rebate' => (float) ($defaults['default_player_rebate'] ?? 0.005),
|
||||
'can_grant_extra_rebate' => true,
|
||||
'can_create_child_agent' => true,
|
||||
'can_create_player' => true,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::table('agent_profiles')->insert([
|
||||
'agent_node_id' => $nodeId,
|
||||
'total_share_rate' => (float) ($defaults['total_share_rate'] ?? 100),
|
||||
'credit_limit' => (int) ($defaults['credit_limit'] ?? 0) > 0
|
||||
? (int) $defaults['credit_limit']
|
||||
: 1_000_000,
|
||||
'allocated_credit' => 0,
|
||||
'used_credit' => 0,
|
||||
'rebate_limit' => (float) ($defaults['rebate_limit'] ?? 0.005),
|
||||
'default_player_rebate' => (float) ($defaults['default_player_rebate'] ?? 0.005),
|
||||
'can_grant_extra_rebate' => true,
|
||||
'can_create_child_agent' => true,
|
||||
'can_create_player' => true,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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
|
||||
{
|
||||
// 业务幂等保护:
|
||||
// - credit_ledger: 同一 (ref_type, ref_id, reason) 三元组只允许一条;同 ticket_item 可存在
|
||||
// 多 reason 流水(bet_hold_release + game_settlement_loss),但同一笔业务动作(同一 reason)
|
||||
// 重复插入会被拒。注:ref_id IS NULL 的行(如 bet 占位)不参与唯一约束。
|
||||
// - share_ledger.reversal_of_id: 同一原账只允许一条反转(业务语义「补差冲正」每张原票仅一次)。
|
||||
$driver = Schema::getConnection()->getDriverName();
|
||||
|
||||
if (Schema::hasTable('credit_ledger') && ! $this->hasIndex('credit_ledger', 'uk_credit_ledger_ref_reason')) {
|
||||
if ($driver === 'pgsql') {
|
||||
DB::statement('CREATE UNIQUE INDEX uk_credit_ledger_ref_reason ON credit_ledger (ref_type, ref_id, reason) WHERE ref_id IS NOT NULL');
|
||||
} elseif ($driver === 'sqlite') {
|
||||
DB::statement('CREATE UNIQUE INDEX uk_credit_ledger_ref_reason ON credit_ledger (ref_type, ref_id, reason) WHERE ref_id IS NOT NULL');
|
||||
} else {
|
||||
Schema::table('credit_ledger', function (Blueprint $table): void {
|
||||
$table->unique(['ref_type', 'ref_id', 'reason'], 'uk_credit_ledger_ref_reason');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Schema::hasTable('share_ledger') && ! $this->hasIndex('share_ledger', 'uk_share_ledger_reversal_of')) {
|
||||
if ($driver === 'pgsql') {
|
||||
DB::statement('CREATE UNIQUE INDEX uk_share_ledger_reversal_of ON share_ledger (reversal_of_id) WHERE reversal_of_id IS NOT NULL');
|
||||
} elseif ($driver === 'sqlite') {
|
||||
DB::statement('CREATE UNIQUE INDEX uk_share_ledger_reversal_of ON share_ledger (reversal_of_id) WHERE reversal_of_id IS NOT NULL');
|
||||
} else {
|
||||
Schema::table('share_ledger', function (Blueprint $table): void {
|
||||
$table->unique('reversal_of_id', 'uk_share_ledger_reversal_of');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$driver = Schema::getConnection()->getDriverName();
|
||||
|
||||
if (Schema::hasTable('share_ledger') && $this->hasIndex('share_ledger', 'uk_share_ledger_reversal_of')) {
|
||||
if (in_array($driver, ['pgsql', 'sqlite'], true)) {
|
||||
DB::statement('DROP INDEX IF EXISTS uk_share_ledger_reversal_of');
|
||||
} else {
|
||||
Schema::table('share_ledger', function (Blueprint $table): void {
|
||||
$table->dropUnique('uk_share_ledger_reversal_of');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Schema::hasTable('credit_ledger') && $this->hasIndex('credit_ledger', 'uk_credit_ledger_ref_reason')) {
|
||||
if (in_array($driver, ['pgsql', 'sqlite'], true)) {
|
||||
DB::statement('DROP INDEX IF EXISTS uk_credit_ledger_ref_reason');
|
||||
} else {
|
||||
Schema::table('credit_ledger', function (Blueprint $table): void {
|
||||
$table->dropUnique('uk_credit_ledger_ref_reason');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function hasIndex(string $table, string $indexName): bool
|
||||
{
|
||||
return Schema::hasIndex($table, $indexName);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user