*/ public const PRESET_ODDS_BY_SCOPE = [ 'first' => 250_000, 'second' => 110_000, 'third' => 55_000, 'starter' => 22_000, 'consolation' => 6_500, ]; /** @var list */ public const SCOPE_KEYS = ['first', 'second', 'third', 'starter', 'consolation']; /** * 为指定赔率版本补全缺失的标准五档行(幂等)。 * * 仅对「该版本里已出现过的 (play_code, currency_code)」补全,避免给从未配置过的玩法凭空加行。 * 若版本下无任何 odds_items,则用当前全部玩法 × 首个可下注币种补全。 * * 佣金按维度(2D/3D/4D)配置:同一维度的所有玩法共享佣金率。 */ public static function syncMissingForVersion(OddsVersion $version): void { DB::transaction(function () use ($version): void { $vid = (int) $version->id; $pairs = OddsItem::query() ->where('version_id', $vid) ->select(['play_code', 'currency_code']) ->distinct() ->get(); if ($pairs->isEmpty()) { $currencyCode = Currency::query() ->where('is_bettable', true) ->where('is_enabled', true) ->orderBy('code') ->value('code'); if ($currencyCode === null || $currencyCode === '') { return; } $pairs = PlayType::query() ->orderBy('sort_order') ->orderBy('play_code') ->get(['play_code', 'dimension']) ->map(fn (PlayType $pt) => (object) [ 'play_code' => $pt->play_code, 'dimension' => $pt->dimension, 'currency_code' => $currencyCode, ]); } // 按维度分组,获取每个维度的佣金率 $dimensionCommissions = []; foreach ($pairs as $pair) { $dimension = $pair->dimension ?? null; $currencyCode = strtoupper((string) $pair->currency_code); $key = $dimension.'|'.$currencyCode; if (!isset($dimensionCommissions[$key])) { // 从现有记录中获取该维度的佣金率 $anchor = OddsItem::query() ->where('version_id', $vid) ->where('currency_code', $currencyCode) ->where('dimension', $dimension) ->orderByDesc('id') ->first(); $dimensionCommissions[$key] = [ 'rebate' => (float) ($anchor?->rebate_rate ?? 0), 'commission' => (float) ($anchor?->commission_rate ?? 0), ]; } } foreach ($pairs as $pair) { $playCode = (string) $pair->play_code; $dimension = $pair->dimension ?? null; $currencyCode = strtoupper((string) $pair->currency_code); $key = $dimension.'|'.$currencyCode; $rebate = $dimensionCommissions[$key]['rebate'] ?? 0; $commission = $dimensionCommissions[$key]['commission'] ?? 0; foreach (self::PRESET_ODDS_BY_SCOPE as $scope => $oddsValue) { $exists = OddsItem::query() ->where('version_id', $vid) ->where('play_code', $playCode) ->where('currency_code', $currencyCode) ->where('prize_scope', $scope) ->exists(); if ($exists) { continue; } OddsItem::query()->create([ 'version_id' => $vid, 'play_code' => $playCode, 'prize_scope' => $scope, 'dimension' => $dimension, 'odds_value' => $oddsValue, 'rebate_rate' => $rebate, 'commission_rate' => $commission, 'currency_code' => $currencyCode, 'extra_config_json' => null, ]); } } }); } }