Files
lotteryLaravel/app/Support/OddsStandardScopes.php
kang c6c10f1397
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
feat(odds): 支持基于开注商的赔率区分与优先级处理
- 在赔率数据表中新增 provider_code 字段,默认值为 GLOBAL
- 赔率接口与服务中新增 provider_code 参数支持,实现开注商特定赔率加载
- 解析赔率集合时,根据 provider_code 优先级进行排序与筛选,保证特定开注商赔率优先
- 优化赔率唯一索引,新增 provider_code 列以防止不同开注商赔率重复冲突
- 调整赔率相关验证规则,支持开注商代码的合法性与可选性检查
- 玩法解析与出票相关逻辑改造,支持多开注商环境下的投注处理
- 管理界面赔率替换接口增加 provider_code 字段支持
- 同步和测试用例覆盖 provider_code 相关功能,验证开注商赔率隔离与优先切换
- PlayEffectiveCatalogController 支持按 provider_code 返回赔率目录数据
- TicketPlacementService 与 TicketPreviewService 修改为支持 provider_code 多开注商玩法解析
- 维护 OddsStandardScopes 同步逻辑,保证开注商维度佣金与赔率正确同步
- AdminConfigPresenter 增加了对应数据的 provider_code 与 dimension 输出字段
- SDK接口添加 odds_snapshot_json 中开注商字段,完善赔率快照信息
- 路由配置调整管理文档地址,无关改动细微修改
2026-07-09 10:52:05 +08:00

132 lines
5.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Support;
use App\Models\Currency;
use App\Models\OddsItem;
use App\Models\PlayType;
use App\Models\OddsVersion;
use Illuminate\Support\Facades\DB;
/**
* 界面文档 §5.5:每个玩法需具备的五档 prize_scopeodds_value = 乘数×10000。
*/
final class OddsStandardScopes
{
/** @var array<string, int> */
public const PRESET_ODDS_BY_SCOPE = [
'first' => 250_000,
'second' => 110_000,
'third' => 55_000,
'starter' => 22_000,
'consolation' => 6_500,
];
/** @var list<string> */
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(['provider_code', '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) [
'provider_code' => 'GLOBAL',
'play_code' => $pt->play_code,
'dimension' => $pt->dimension,
'currency_code' => $currencyCode,
]);
}
// 按维度分组,获取每个维度的佣金率
$dimensionCommissions = [];
foreach ($pairs as $pair) {
$providerCode = strtoupper((string) ($pair->provider_code ?? 'GLOBAL'));
$dimension = $pair->dimension ?? null;
$currencyCode = strtoupper((string) $pair->currency_code);
$key = $providerCode.'|'.$dimension.'|'.$currencyCode;
if (!isset($dimensionCommissions[$key])) {
// 从现有记录中获取该维度的佣金率
$anchor = OddsItem::query()
->where('version_id', $vid)
->where('provider_code', $providerCode)
->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) {
$providerCode = strtoupper((string) ($pair->provider_code ?? 'GLOBAL'));
$playCode = (string) $pair->play_code;
$dimension = $pair->dimension ?? null;
$currencyCode = strtoupper((string) $pair->currency_code);
$key = $providerCode.'|'.$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('provider_code', $providerCode)
->where('play_code', $playCode)
->where('currency_code', $currencyCode)
->where('prize_scope', $scope)
->exists();
if ($exists) {
continue;
}
OddsItem::query()->create([
'version_id' => $vid,
'provider_code' => $providerCode,
'play_code' => $playCode,
'prize_scope' => $scope,
'dimension' => $dimension,
'odds_value' => $oddsValue,
'rebate_rate' => $rebate,
'commission_rate' => $commission,
'currency_code' => $currencyCode,
'extra_config_json' => null,
]);
}
}
});
}
}