feat: 为赔率项增加 dimension 字段并支持按维度配置佣金,调整后台配置导航权限
This commit is contained in:
@@ -16,6 +16,7 @@ final class OddsItem extends Model
|
|||||||
'version_id',
|
'version_id',
|
||||||
'play_code',
|
'play_code',
|
||||||
'prize_scope',
|
'prize_scope',
|
||||||
|
'dimension',
|
||||||
'odds_value',
|
'odds_value',
|
||||||
'rebate_rate',
|
'rebate_rate',
|
||||||
'commission_rate',
|
'commission_rate',
|
||||||
@@ -27,6 +28,7 @@ final class OddsItem extends Model
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'version_id' => 'integer',
|
'version_id' => 'integer',
|
||||||
|
'dimension' => 'integer',
|
||||||
'odds_value' => 'integer',
|
'odds_value' => 'integer',
|
||||||
'rebate_rate' => 'decimal:4',
|
'rebate_rate' => 'decimal:4',
|
||||||
'commission_rate' => 'decimal:4',
|
'commission_rate' => 'decimal:4',
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ final class OddsStreamService
|
|||||||
'version_id' => $draft->id,
|
'version_id' => $draft->id,
|
||||||
'play_code' => $row->play_code,
|
'play_code' => $row->play_code,
|
||||||
'prize_scope' => $row->prize_scope,
|
'prize_scope' => $row->prize_scope,
|
||||||
|
'dimension' => $row->dimension,
|
||||||
'odds_value' => $row->odds_value,
|
'odds_value' => $row->odds_value,
|
||||||
'rebate_rate' => $row->rebate_rate,
|
'rebate_rate' => $row->rebate_rate,
|
||||||
'commission_rate' => $row->commission_rate,
|
'commission_rate' => $row->commission_rate,
|
||||||
@@ -77,6 +78,7 @@ final class OddsStreamService
|
|||||||
'version_id' => $draft->id,
|
'version_id' => $draft->id,
|
||||||
'play_code' => $pt->play_code,
|
'play_code' => $pt->play_code,
|
||||||
'prize_scope' => $scope,
|
'prize_scope' => $scope,
|
||||||
|
'dimension' => $pt->dimension,
|
||||||
'odds_value' => $oddsValue,
|
'odds_value' => $oddsValue,
|
||||||
'rebate_rate' => 0,
|
'rebate_rate' => 0,
|
||||||
'commission_rate' => 0,
|
'commission_rate' => 0,
|
||||||
@@ -107,6 +109,7 @@ final class OddsStreamService
|
|||||||
'version_id' => $draft->id,
|
'version_id' => $draft->id,
|
||||||
'play_code' => (string) $row['play_code'],
|
'play_code' => (string) $row['play_code'],
|
||||||
'prize_scope' => (string) $row['prize_scope'],
|
'prize_scope' => (string) $row['prize_scope'],
|
||||||
|
'dimension' => isset($row['dimension']) ? (int) $row['dimension'] : null,
|
||||||
'odds_value' => (int) $row['odds_value'],
|
'odds_value' => (int) $row['odds_value'],
|
||||||
'rebate_rate' => (float) ($row['rebate_rate'] ?? 0),
|
'rebate_rate' => (float) ($row['rebate_rate'] ?? 0),
|
||||||
'commission_rate' => (float) ($row['commission_rate'] ?? 0),
|
'commission_rate' => (float) ($row['commission_rate'] ?? 0),
|
||||||
@@ -221,6 +224,7 @@ final class OddsStreamService
|
|||||||
foreach ($items as $index => $row) {
|
foreach ($items as $index => $row) {
|
||||||
$playCode = (string) $row->play_code;
|
$playCode = (string) $row->play_code;
|
||||||
$scope = (string) $row->prize_scope;
|
$scope = (string) $row->prize_scope;
|
||||||
|
$dimension = $row->dimension;
|
||||||
$currencyCode = strtoupper((string) $row->currency_code);
|
$currencyCode = strtoupper((string) $row->currency_code);
|
||||||
$oddsValue = (int) $row->odds_value;
|
$oddsValue = (int) $row->odds_value;
|
||||||
$rebateRate = (float) $row->rebate_rate;
|
$rebateRate = (float) $row->rebate_rate;
|
||||||
@@ -239,6 +243,10 @@ final class OddsStreamService
|
|||||||
$errors["items.$index.currency_code"][] = '币种不可下注';
|
$errors["items.$index.currency_code"][] = '币种不可下注';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($dimension !== null && ! in_array($dimension, [2, 3, 4], true)) {
|
||||||
|
$errors["items.$index.dimension"][] = '维度必须是 2、3 或 4';
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($seenKeys[$key])) {
|
if (isset($seenKeys[$key])) {
|
||||||
$errors["items.$index"][] = '同一玩法、档位、币种存在重复赔率项';
|
$errors["items.$index"][] = '同一玩法、档位、币种存在重复赔率项';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ final class PlayRuleEngine
|
|||||||
|
|
||||||
$unitBetAmount = $this->resolveUnitBetAmount($playCode, $amount, $combinationCount);
|
$unitBetAmount = $this->resolveUnitBetAmount($playCode, $amount, $combinationCount);
|
||||||
$totalBetAmount = $this->resolveTotalBetAmount($playCode, $amount, $unitBetAmount, $combinationCount);
|
$totalBetAmount = $this->resolveTotalBetAmount($playCode, $amount, $unitBetAmount, $combinationCount);
|
||||||
|
$dimensionInt = $this->toDimensionInt(is_string($dimension) ? $dimension : null, $playConfig);
|
||||||
$primaryOdds = $this->pickPrimaryOdds($oddsItems);
|
$primaryOdds = $this->pickPrimaryOdds($oddsItems);
|
||||||
$rebateRate = (float) $primaryOdds->rebate_rate;
|
$rebateRate = (float) $primaryOdds->rebate_rate;
|
||||||
$commissionRate = (float) $primaryOdds->commission_rate;
|
// 佣金按维度(2D/3D/4D)配置:从 odds_items 中查找匹配维度的佣金率
|
||||||
|
$commissionRate = $this->pickCommissionByDimension($oddsItems, $dimensionInt);
|
||||||
$actualDeductAmount = max(0, (int) floor($totalBetAmount * (1 - $rebateRate)));
|
$actualDeductAmount = max(0, (int) floor($totalBetAmount * (1 - $rebateRate)));
|
||||||
$maxOdds = $oddsItems->max(fn (OddsItem $row) => (int) $row->odds_value) ?? 0;
|
$maxOdds = $oddsItems->max(fn (OddsItem $row) => (int) $row->odds_value) ?? 0;
|
||||||
$estimatedPayoutPerCombo = (int) floor($unitBetAmount * ($maxOdds / 10000));
|
$estimatedPayoutPerCombo = (int) floor($unitBetAmount * ($maxOdds / 10000));
|
||||||
@@ -302,6 +304,28 @@ final class PlayRuleEngine
|
|||||||
return $oddsItems->firstOrFail();
|
return $oddsItems->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按维度(2D/3D/4D)获取佣金率
|
||||||
|
*
|
||||||
|
* @param Collection<int, OddsItem> $oddsItems
|
||||||
|
*/
|
||||||
|
private function pickCommissionByDimension(Collection $oddsItems, ?int $dimension): float
|
||||||
|
{
|
||||||
|
if ($dimension === null) {
|
||||||
|
// 如果维度为空,返回第一个 odds item 的佣金率(向后兼容)
|
||||||
|
return (float) ($oddsItems->first()?->commission_rate ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找匹配维度的 odds item
|
||||||
|
$dimensionItem = $oddsItems->firstWhere('dimension', $dimension);
|
||||||
|
if ($dimensionItem !== null) {
|
||||||
|
return (float) $dimensionItem->commission_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到匹配维度的,返回第一个的佣金率(向后兼容)
|
||||||
|
return (float) ($oddsItems->first()?->commission_rate ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
private function toDimensionInt(?string $dimension, PlayConfigItem $playConfig): ?int
|
private function toDimensionInt(?string $dimension, PlayConfigItem $playConfig): ?int
|
||||||
{
|
{
|
||||||
return match ($dimension) {
|
return match ($dimension) {
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ final class AdminAuthorizationRegistry
|
|||||||
['slug' => 'prd.draw_result.view', 'name' => '开奖结果·查看', 'nav_segment' => 'draws', 'permission_codes' => ['draw.results.view', 'risk.monitor.view']],
|
['slug' => 'prd.draw_result.view', 'name' => '开奖结果·查看', 'nav_segment' => 'draws', 'permission_codes' => ['draw.results.view', 'risk.monitor.view']],
|
||||||
['slug' => 'prd.draw_reopen.manage', 'name' => '开奖结果重开·可管理', 'nav_segment' => 'draws', 'permission_codes' => ['draw.review.publish']],
|
['slug' => 'prd.draw_reopen.manage', 'name' => '开奖结果重开·可管理', 'nav_segment' => 'draws', 'permission_codes' => ['draw.review.publish']],
|
||||||
|
|
||||||
['slug' => 'prd.play_switch.manage', 'name' => '玩法开关·可管理', 'nav_segment' => 'config', 'permission_codes' => ['config.play.manage']],
|
['slug' => 'prd.play_switch.manage', 'name' => '玩法开关·可管理', 'nav_segment' => 'rules_plays', 'permission_codes' => ['config.play.manage']],
|
||||||
['slug' => 'prd.odds.manage', 'name' => '赔率配置·可管理', 'nav_segment' => 'config', 'permission_codes' => ['config.odds.manage']],
|
['slug' => 'prd.odds.manage', 'name' => '赔率配置·可管理', 'nav_segment' => 'rules_odds', 'permission_codes' => ['config.odds.manage']],
|
||||||
['slug' => 'prd.risk_cap.manage', 'name' => '封顶配置·可管理', 'nav_segment' => 'config', 'permission_codes' => ['config.risk_cap.manage']],
|
['slug' => 'prd.risk_cap.manage', 'name' => '封顶配置·可管理', 'nav_segment' => 'risk_cap', 'permission_codes' => ['config.risk_cap.manage']],
|
||||||
['slug' => 'prd.risk_cap.view', 'name' => '封顶配置·查看', 'nav_segment' => 'config', 'permission_codes' => ['config.risk_cap.view']],
|
['slug' => 'prd.risk_cap.view', 'name' => '封顶配置·查看', 'nav_segment' => 'risk_cap', 'permission_codes' => ['config.risk_cap.view']],
|
||||||
['slug' => 'prd.rebate.manage', 'name' => '佣金/回水·可管理', 'nav_segment' => 'config', 'permission_codes' => ['config.odds.manage']],
|
['slug' => 'prd.rebate.manage', 'name' => '佣金/回水·可管理', 'nav_segment' => 'rules_odds', 'permission_codes' => ['config.odds.manage']],
|
||||||
['slug' => 'prd.rebate.view', 'name' => '佣金/回水·查看', 'nav_segment' => 'config', 'permission_codes' => ['config.odds.manage']],
|
['slug' => 'prd.rebate.view', 'name' => '佣金/回水·查看', 'nav_segment' => 'rules_odds', 'permission_codes' => ['config.odds.manage']],
|
||||||
['slug' => 'prd.jackpot.manage', 'name' => 'Jackpot 配置·可管理', 'nav_segment' => 'config', 'permission_codes' => ['config.jackpot.manage']],
|
['slug' => 'prd.jackpot.manage', 'name' => 'Jackpot 配置·可管理', 'nav_segment' => 'jackpot', 'permission_codes' => ['config.jackpot.manage']],
|
||||||
['slug' => 'prd.jackpot.view', 'name' => 'Jackpot 配置·查看', 'nav_segment' => 'config', 'permission_codes' => ['config.jackpot.view']],
|
['slug' => 'prd.jackpot.view', 'name' => 'Jackpot 配置·查看', 'nav_segment' => 'jackpot', 'permission_codes' => ['config.jackpot.view']],
|
||||||
|
|
||||||
['slug' => 'prd.payout.manage', 'name' => '派彩确认·可管理', 'nav_segment' => 'settlement', 'permission_codes' => ['settlement.batch.manage', 'settlement.batch.view']],
|
['slug' => 'prd.payout.manage', 'name' => '派彩确认·可管理', 'nav_segment' => 'settlement', 'permission_codes' => ['settlement.batch.manage', 'settlement.batch.view']],
|
||||||
['slug' => 'prd.payout.review', 'name' => '派彩确认·可审核', 'nav_segment' => 'settlement', 'permission_codes' => ['settlement.batch.review', 'settlement.batch.view']],
|
['slug' => 'prd.payout.review', 'name' => '派彩确认·可审核', 'nav_segment' => 'settlement', 'permission_codes' => ['settlement.batch.review', 'settlement.batch.view']],
|
||||||
@@ -73,10 +73,12 @@ final class AdminAuthorizationRegistry
|
|||||||
'currencies' => '币种管理',
|
'currencies' => '币种管理',
|
||||||
'wallet' => '钱包流水',
|
'wallet' => '钱包流水',
|
||||||
'draws' => '期号列表',
|
'draws' => '期号列表',
|
||||||
'config' => '运营配置',
|
'rules_plays' => '投注规则',
|
||||||
|
'rules_odds' => '赔率与回水',
|
||||||
|
'risk_cap' => '限额版本',
|
||||||
'risk' => '风控',
|
'risk' => '风控',
|
||||||
'settlement' => '结算',
|
'settlement' => '结算',
|
||||||
'jackpot' => 'Jackpot',
|
'jackpot' => '奖池',
|
||||||
'reconcile' => '对账',
|
'reconcile' => '对账',
|
||||||
'tickets' => '玩家注单',
|
'tickets' => '玩家注单',
|
||||||
'audit' => '审计日志',
|
'audit' => '审计日志',
|
||||||
@@ -106,18 +108,26 @@ final class AdminAuthorizationRegistry
|
|||||||
public static function navigationDefinitions(): array
|
public static function navigationDefinitions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
// 总览
|
||||||
['segment' => 'dashboard', 'label' => 'Dashboard', 'href' => '/admin'],
|
['segment' => 'dashboard', 'label' => 'Dashboard', 'href' => '/admin'],
|
||||||
['segment' => 'admin_users', 'label' => 'Admin Users', 'href' => '/admin/admin-users', 'requiredAny' => ['prd.admin_user.manage']],
|
// 日常运营:开奖 → 注单 → 玩家
|
||||||
['segment' => 'admin_roles', 'label' => 'Admin Roles', 'href' => '/admin/admin-roles', 'requiredAny' => ['prd.admin_role.manage']],
|
|
||||||
['segment' => 'players', 'label' => 'Players', 'href' => '/admin/players', 'requiredAny' => ['prd.users.manage', 'prd.users.view_finance', 'prd.users.view_cs', 'prd.player_freeze.manage']],
|
|
||||||
['segment' => 'currencies', 'label' => 'Currencies', 'href' => '/admin/currencies', 'requiredAny' => ['prd.currency.manage']],
|
|
||||||
['segment' => 'wallet', 'label' => 'Wallet', 'href' => '/admin/wallet/transactions', 'activeMatchPrefix' => '/admin/wallet', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs', 'prd.wallet_adjust.manage', 'prd.users.manage', 'prd.users.view_finance']],
|
|
||||||
['segment' => 'draws', 'label' => 'Draws', 'href' => '/admin/draws', 'requiredAny' => ['prd.draw_result.manage', 'prd.draw_result.view', 'prd.draw_reopen.manage']],
|
['segment' => 'draws', 'label' => 'Draws', 'href' => '/admin/draws', 'requiredAny' => ['prd.draw_result.manage', 'prd.draw_result.view', 'prd.draw_reopen.manage']],
|
||||||
['segment' => 'config', 'label' => 'Configuration', 'href' => '/admin/config', 'requiredAny' => ['prd.play_switch.manage', 'prd.odds.manage', 'prd.risk_cap.manage', 'prd.risk_cap.view', 'prd.rebate.manage', 'prd.rebate.view', 'prd.jackpot.manage', 'prd.jackpot.view']],
|
['segment' => 'tickets', 'label' => 'Tickets', 'href' => '/admin/tickets', 'requiredAny' => ['prd.users.view_cs', 'prd.users.manage', 'prd.users.view_finance', 'prd.draw_result.view', 'prd.draw_result.manage', 'prd.payout.view', 'prd.payout.review', 'prd.payout.manage']],
|
||||||
['segment' => 'risk', 'label' => 'Risk', 'href' => '/admin/risk', 'requiredAny' => ['prd.draw_result.view', 'prd.draw_result.manage']],
|
['segment' => 'players', 'label' => 'Players', 'href' => '/admin/players', 'requiredAny' => ['prd.users.manage', 'prd.users.view_finance', 'prd.users.view_cs', 'prd.player_freeze.manage']],
|
||||||
|
// 规则与参数
|
||||||
|
['segment' => 'rules_plays', 'label' => 'Play rules', 'href' => '/admin/rules/plays', 'requiredAny' => ['prd.play_switch.manage', 'prd.odds.manage']],
|
||||||
|
['segment' => 'rules_odds', 'label' => 'Odds & rebate', 'href' => '/admin/rules/odds', 'requiredAny' => ['prd.odds.manage', 'prd.rebate.manage', 'prd.rebate.view']],
|
||||||
|
['segment' => 'jackpot', 'label' => 'Jackpot', 'href' => '/admin/jackpot', 'activeMatchPrefix' => '/admin/jackpot', 'requiredAny' => ['prd.jackpot.manage', 'prd.jackpot.view']],
|
||||||
|
['segment' => 'risk_cap', 'label' => 'Risk cap rules', 'href' => '/admin/risk/cap', 'activeMatchPrefix' => '/admin/risk/cap', 'requiredAny' => ['prd.risk_cap.manage', 'prd.risk_cap.view']],
|
||||||
|
// 资金
|
||||||
|
['segment' => 'wallet', 'label' => 'Wallet', 'href' => '/admin/wallet/transactions', 'activeMatchPrefix' => '/admin/wallet', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs', 'prd.wallet_adjust.manage', 'prd.users.manage', 'prd.users.view_finance']],
|
||||||
['segment' => 'settlement', 'label' => 'Settlement', 'href' => '/admin/settlement-batches', 'requiredAny' => ['prd.payout.manage', 'prd.payout.review', 'prd.payout.view']],
|
['segment' => 'settlement', 'label' => 'Settlement', 'href' => '/admin/settlement-batches', 'requiredAny' => ['prd.payout.manage', 'prd.payout.review', 'prd.payout.view']],
|
||||||
['segment' => 'reconcile', 'label' => 'Reconcile', 'href' => '/admin/reconcile', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']],
|
['segment' => 'reconcile', 'label' => 'Reconcile', 'href' => '/admin/reconcile', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']],
|
||||||
['segment' => 'tickets', 'label' => 'Tickets', 'href' => '/admin/tickets', 'requiredAny' => ['prd.users.view_cs', 'prd.users.manage', 'prd.users.view_finance', 'prd.draw_result.view', 'prd.draw_result.manage', 'prd.payout.view', 'prd.payout.review', 'prd.payout.manage']],
|
['segment' => 'currencies', 'label' => 'Currencies', 'href' => '/admin/currencies', 'requiredAny' => ['prd.currency.manage']],
|
||||||
|
// 权限与系统
|
||||||
|
['segment' => 'admin_users', 'label' => 'Admin Users', 'href' => '/admin/admin-users', 'requiredAny' => ['prd.admin_user.manage']],
|
||||||
|
['segment' => 'admin_roles', 'label' => 'Admin Roles', 'href' => '/admin/admin-roles', 'requiredAny' => ['prd.admin_role.manage']],
|
||||||
|
['segment' => 'risk', 'label' => 'Risk', 'href' => '/admin/risk', 'requiredAny' => ['prd.draw_result.view', 'prd.draw_result.manage']],
|
||||||
['segment' => 'audit', 'label' => 'Audit Logs', 'href' => '/admin/audit-logs', 'requiredAny' => ['prd.audit.all', 'prd.audit.self', 'prd.audit.finance']],
|
['segment' => 'audit', 'label' => 'Audit Logs', 'href' => '/admin/audit-logs', 'requiredAny' => ['prd.audit.all', 'prd.audit.self', 'prd.audit.finance']],
|
||||||
['segment' => 'settings', 'label' => 'Settings', 'href' => '/admin/settings', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.currency.manage']],
|
['segment' => 'settings', 'label' => 'Settings', 'href' => '/admin/settings', 'requiredAny' => ['prd.wallet_reconcile.manage', 'prd.currency.manage']],
|
||||||
];
|
];
|
||||||
@@ -179,7 +189,10 @@ final class AdminAuthorizationRegistry
|
|||||||
'currencies' => ['prd.currency.manage'],
|
'currencies' => ['prd.currency.manage'],
|
||||||
'wallet' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs', 'prd.wallet_adjust.manage', 'prd.users.view_finance'],
|
'wallet' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs', 'prd.wallet_adjust.manage', 'prd.users.view_finance'],
|
||||||
'draws' => ['prd.draw_result.manage', 'prd.draw_result.view', 'prd.draw_reopen.manage'],
|
'draws' => ['prd.draw_result.manage', 'prd.draw_result.view', 'prd.draw_reopen.manage'],
|
||||||
'config' => ['prd.play_switch.manage', 'prd.odds.manage', 'prd.risk_cap.manage', 'prd.risk_cap.view', 'prd.rebate.manage', 'prd.rebate.view', 'prd.jackpot.manage', 'prd.jackpot.view'],
|
'rules_plays' => ['prd.play_switch.manage', 'prd.odds.manage'],
|
||||||
|
'rules_odds' => ['prd.odds.manage', 'prd.rebate.manage', 'prd.rebate.view'],
|
||||||
|
'jackpot' => ['prd.jackpot.manage', 'prd.jackpot.view'],
|
||||||
|
'risk_cap' => ['prd.risk_cap.manage', 'prd.risk_cap.view'],
|
||||||
'risk' => ['prd.draw_result.manage', 'prd.draw_result.view'],
|
'risk' => ['prd.draw_result.manage', 'prd.draw_result.view'],
|
||||||
'settlement' => ['prd.payout.manage', 'prd.payout.review', 'prd.payout.view'],
|
'settlement' => ['prd.payout.manage', 'prd.payout.review', 'prd.payout.view'],
|
||||||
'reconcile' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs'],
|
'reconcile' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs'],
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ final class OddsStandardScopes
|
|||||||
*
|
*
|
||||||
* 仅对「该版本里已出现过的 (play_code, currency_code)」补全,避免给从未配置过的玩法凭空加行。
|
* 仅对「该版本里已出现过的 (play_code, currency_code)」补全,避免给从未配置过的玩法凭空加行。
|
||||||
* 若版本下无任何 odds_items,则用当前全部玩法 × 首个可下注币种补全。
|
* 若版本下无任何 odds_items,则用当前全部玩法 × 首个可下注币种补全。
|
||||||
|
*
|
||||||
|
* 佣金按维度(2D/3D/4D)配置:同一维度的所有玩法共享佣金率。
|
||||||
*/
|
*/
|
||||||
public static function syncMissingForVersion(OddsVersion $version): void
|
public static function syncMissingForVersion(OddsVersion $version): void
|
||||||
{
|
{
|
||||||
@@ -54,26 +56,45 @@ final class OddsStandardScopes
|
|||||||
$pairs = PlayType::query()
|
$pairs = PlayType::query()
|
||||||
->orderBy('sort_order')
|
->orderBy('sort_order')
|
||||||
->orderBy('play_code')
|
->orderBy('play_code')
|
||||||
->get(['play_code'])
|
->get(['play_code', 'dimension'])
|
||||||
->map(fn (PlayType $pt) => (object) [
|
->map(fn (PlayType $pt) => (object) [
|
||||||
'play_code' => $pt->play_code,
|
'play_code' => $pt->play_code,
|
||||||
|
'dimension' => $pt->dimension,
|
||||||
'currency_code' => $currencyCode,
|
'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) {
|
foreach ($pairs as $pair) {
|
||||||
$playCode = (string) $pair->play_code;
|
$playCode = (string) $pair->play_code;
|
||||||
|
$dimension = $pair->dimension ?? null;
|
||||||
$currencyCode = strtoupper((string) $pair->currency_code);
|
$currencyCode = strtoupper((string) $pair->currency_code);
|
||||||
|
$key = $dimension.'|'.$currencyCode;
|
||||||
$anchor = OddsItem::query()
|
|
||||||
->where('version_id', $vid)
|
$rebate = $dimensionCommissions[$key]['rebate'] ?? 0;
|
||||||
->where('play_code', $playCode)
|
$commission = $dimensionCommissions[$key]['commission'] ?? 0;
|
||||||
->where('currency_code', $currencyCode)
|
|
||||||
->orderByDesc('id')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
$rebate = (float) ($anchor?->rebate_rate ?? 0);
|
|
||||||
$commission = (float) ($anchor?->commission_rate ?? 0);
|
|
||||||
|
|
||||||
foreach (self::PRESET_ODDS_BY_SCOPE as $scope => $oddsValue) {
|
foreach (self::PRESET_ODDS_BY_SCOPE as $scope => $oddsValue) {
|
||||||
$exists = OddsItem::query()
|
$exists = OddsItem::query()
|
||||||
@@ -90,6 +111,7 @@ final class OddsStandardScopes
|
|||||||
'version_id' => $vid,
|
'version_id' => $vid,
|
||||||
'play_code' => $playCode,
|
'play_code' => $playCode,
|
||||||
'prize_scope' => $scope,
|
'prize_scope' => $scope,
|
||||||
|
'dimension' => $dimension,
|
||||||
'odds_value' => $oddsValue,
|
'odds_value' => $oddsValue,
|
||||||
'rebate_rate' => $rebate,
|
'rebate_rate' => $rebate,
|
||||||
'commission_rate' => $commission,
|
'commission_rate' => $commission,
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('odds_items', function (Blueprint $table) {
|
||||||
|
// 添加 dimension 字段用于按 2D/3D/4D 配置佣金
|
||||||
|
$table->unsignedTinyInteger('dimension')->nullable()->after('prize_scope')->comment('2/3/4 维度,佣金按维度配置');
|
||||||
|
|
||||||
|
// 删除旧的唯一约束
|
||||||
|
$table->dropUnique('uk_odds_items_version_play_prize_currency');
|
||||||
|
|
||||||
|
// 添加新的唯一约束:佣金按 dimension + currency_code 配置
|
||||||
|
// 赔率仍按 play_code + prize_scope + currency_code 配置
|
||||||
|
$table->unique(
|
||||||
|
['version_id', 'play_code', 'prize_scope', 'currency_code', 'dimension'],
|
||||||
|
'uk_odds_items_version_play_prize_currency_dimension'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('odds_items', function (Blueprint $table) {
|
||||||
|
// 删除新的唯一约束
|
||||||
|
$table->dropUnique('uk_odds_items_version_play_prize_currency_dimension');
|
||||||
|
|
||||||
|
// 恢复旧的唯一约束
|
||||||
|
$table->unique(
|
||||||
|
['version_id', 'play_code', 'prize_scope', 'currency_code'],
|
||||||
|
'uk_odds_items_version_play_prize_currency'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 删除 dimension 字段
|
||||||
|
$table->dropColumn('dimension');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -105,6 +105,7 @@ final class OperationalConfigV1Seeder extends Seeder
|
|||||||
'version_id' => $oddsVersion->id,
|
'version_id' => $oddsVersion->id,
|
||||||
'play_code' => $pt->play_code,
|
'play_code' => $pt->play_code,
|
||||||
'prize_scope' => $scope,
|
'prize_scope' => $scope,
|
||||||
|
'dimension' => $pt->dimension,
|
||||||
'odds_value' => $oddsValue,
|
'odds_value' => $oddsValue,
|
||||||
'rebate_rate' => 0,
|
'rebate_rate' => 0,
|
||||||
'commission_rate' => 0,
|
'commission_rate' => 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user