feat: 添加新的错误码以支持投注功能,更新数据库填充器以增强玩法和赔率配置,扩展 API 路由以支持风险池管理
This commit is contained in:
@@ -16,80 +16,117 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 阶段 4:写入首套 **active** 玩法配置 / 赔率 / 风控封顶版本(依赖 {@see PlayTypeSeeder}、{@see CurrencySeeder})。
|
||||
*
|
||||
* 幂等:仅当三套版本均已有 active 行时跳过;否则只补缺失的一类(避免「仅有 play active 时整段被跳过」导致 /play/effective 不可用)。
|
||||
*/
|
||||
class OperationalConfigV1Seeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
if (PlayConfigVersion::query()->where('status', ConfigVersionStatus::Active->value)->exists()) {
|
||||
$hasPlay = PlayConfigVersion::query()
|
||||
->where('status', ConfigVersionStatus::Active->value)
|
||||
->exists();
|
||||
$hasOdds = OddsVersion::query()
|
||||
->where('status', ConfigVersionStatus::Active->value)
|
||||
->exists();
|
||||
$hasRisk = RiskCapVersion::query()
|
||||
->where('status', ConfigVersionStatus::Active->value)
|
||||
->exists();
|
||||
|
||||
if ($hasPlay && $hasOdds && $hasRisk) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::transaction(function (): void {
|
||||
$playVersion = PlayConfigVersion::query()->create([
|
||||
'version_no' => 1,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
foreach (PlayType::query()->orderBy('sort_order')->orderBy('play_code')->get() as $pt) {
|
||||
PlayConfigItem::query()->create([
|
||||
'version_id' => $playVersion->id,
|
||||
'play_code' => $pt->play_code,
|
||||
'is_enabled' => (bool) $pt->is_enabled,
|
||||
'min_bet_amount' => 100,
|
||||
'max_bet_amount' => 500_000_000,
|
||||
'display_order' => (int) $pt->sort_order,
|
||||
'rule_text_zh' => null,
|
||||
'rule_text_en' => null,
|
||||
'rule_text_ne' => null,
|
||||
'extra_config_json' => null,
|
||||
]);
|
||||
DB::transaction(function () use ($hasPlay, $hasOdds, $hasRisk): void {
|
||||
if (! $hasPlay) {
|
||||
$this->seedActivePlayConfigVersion();
|
||||
}
|
||||
|
||||
$oddsVersion = OddsVersion::query()->create([
|
||||
'version_no' => 1,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
/** 对齐界面文档 §5.5:头/二/三/特别/安慰;odds_value = 乘数×10000(NPR 基准展示口径) */
|
||||
foreach (PlayType::query()->orderBy('sort_order')->orderBy('play_code')->get() as $pt) {
|
||||
foreach (OddsStandardScopes::PRESET_ODDS_BY_SCOPE as $scope => $oddsValue) {
|
||||
OddsItem::query()->create([
|
||||
'version_id' => $oddsVersion->id,
|
||||
'play_code' => $pt->play_code,
|
||||
'prize_scope' => $scope,
|
||||
'odds_value' => $oddsValue,
|
||||
'rebate_rate' => 0,
|
||||
'commission_rate' => 0,
|
||||
'currency_code' => 'NPR',
|
||||
'extra_config_json' => null,
|
||||
]);
|
||||
}
|
||||
if (! $hasOdds) {
|
||||
$this->seedActiveOddsVersion();
|
||||
}
|
||||
|
||||
$riskVersion = RiskCapVersion::query()->create([
|
||||
'version_no' => 1,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
foreach (['0000', '1234', '9999'] as $num) {
|
||||
RiskCapItem::query()->create([
|
||||
'version_id' => $riskVersion->id,
|
||||
'draw_id' => null,
|
||||
'normalized_number' => $num,
|
||||
'cap_amount' => 50_000_000_000,
|
||||
'cap_type' => 'per_number',
|
||||
]);
|
||||
if (! $hasRisk) {
|
||||
$this->seedActiveRiskCapVersion();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function seedActivePlayConfigVersion(): void
|
||||
{
|
||||
$versionNo = (int) (PlayConfigVersion::query()->max('version_no') ?? 0) + 1;
|
||||
|
||||
$playVersion = PlayConfigVersion::query()->create([
|
||||
'version_no' => $versionNo,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
foreach (PlayType::query()->orderBy('sort_order')->orderBy('play_code')->get() as $pt) {
|
||||
PlayConfigItem::query()->create([
|
||||
'version_id' => $playVersion->id,
|
||||
'play_code' => $pt->play_code,
|
||||
'is_enabled' => (bool) $pt->is_enabled,
|
||||
'min_bet_amount' => 100,
|
||||
'max_bet_amount' => 500_000_000,
|
||||
'display_order' => (int) $pt->sort_order,
|
||||
'rule_text_zh' => null,
|
||||
'rule_text_en' => null,
|
||||
'rule_text_ne' => null,
|
||||
'extra_config_json' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function seedActiveOddsVersion(): void
|
||||
{
|
||||
$versionNo = (int) (OddsVersion::query()->max('version_no') ?? 0) + 1;
|
||||
|
||||
$oddsVersion = OddsVersion::query()->create([
|
||||
'version_no' => $versionNo,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
/** 对齐界面文档 §5.5:头/二/三/特别/安慰;odds_value = 乘数×10000(NPR 基准展示口径) */
|
||||
foreach (PlayType::query()->orderBy('sort_order')->orderBy('play_code')->get() as $pt) {
|
||||
foreach (OddsStandardScopes::PRESET_ODDS_BY_SCOPE as $scope => $oddsValue) {
|
||||
OddsItem::query()->create([
|
||||
'version_id' => $oddsVersion->id,
|
||||
'play_code' => $pt->play_code,
|
||||
'prize_scope' => $scope,
|
||||
'odds_value' => $oddsValue,
|
||||
'rebate_rate' => 0,
|
||||
'commission_rate' => 0,
|
||||
'currency_code' => 'NPR',
|
||||
'extra_config_json' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function seedActiveRiskCapVersion(): void
|
||||
{
|
||||
$versionNo = (int) (RiskCapVersion::query()->max('version_no') ?? 0) + 1;
|
||||
|
||||
$riskVersion = RiskCapVersion::query()->create([
|
||||
'version_no' => $versionNo,
|
||||
'status' => ConfigVersionStatus::Active->value,
|
||||
'effective_at' => now(),
|
||||
'updated_by' => null,
|
||||
'reason' => 'seed:v1',
|
||||
]);
|
||||
|
||||
foreach (['0000', '1234', '9999'] as $num) {
|
||||
RiskCapItem::query()->create([
|
||||
'version_id' => $riskVersion->id,
|
||||
'draw_id' => null,
|
||||
'normalized_number' => $num,
|
||||
'cap_amount' => 50_000_000_000,
|
||||
'cap_type' => 'per_number',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,75 +5,87 @@ namespace Database\Seeders;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 首期玩法占位数据,play_code 与 `docs/04` 玩法编码(小写)一致,便于前端/配置对齐。
|
||||
*/
|
||||
class PlayTypeSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$defaults = ['created_at' => now(), 'updated_at' => now()];
|
||||
|
||||
$rows = [
|
||||
[
|
||||
'play_code' => 'big',
|
||||
'category' => 'standard',
|
||||
'dimension' => 2,
|
||||
'bet_mode' => null,
|
||||
'display_name_zh' => 'Big',
|
||||
'display_name_en' => 'Big',
|
||||
'display_name_ne' => 'Big',
|
||||
'is_enabled' => true,
|
||||
'sort_order' => 10,
|
||||
'supports_multi_number' => false,
|
||||
'reserved_rule_json' => null,
|
||||
],
|
||||
[
|
||||
'play_code' => 'small',
|
||||
'category' => 'standard',
|
||||
'dimension' => 2,
|
||||
'bet_mode' => null,
|
||||
'display_name_zh' => 'Small',
|
||||
'display_name_en' => 'Small',
|
||||
'display_name_ne' => 'Small',
|
||||
'is_enabled' => true,
|
||||
'sort_order' => 20,
|
||||
'supports_multi_number' => false,
|
||||
'reserved_rule_json' => null,
|
||||
],
|
||||
[
|
||||
'play_code' => 'head',
|
||||
'category' => 'digit',
|
||||
'dimension' => 2,
|
||||
'bet_mode' => null,
|
||||
'display_name_zh' => 'Head',
|
||||
'display_name_en' => 'Head',
|
||||
'display_name_ne' => 'Head',
|
||||
'is_enabled' => true,
|
||||
'sort_order' => 30,
|
||||
'supports_multi_number' => false,
|
||||
'reserved_rule_json' => null,
|
||||
],
|
||||
[
|
||||
'play_code' => 'tail',
|
||||
'category' => 'digit',
|
||||
'dimension' => 2,
|
||||
'bet_mode' => null,
|
||||
'display_name_zh' => 'Tail',
|
||||
'display_name_en' => 'Tail',
|
||||
'display_name_ne' => 'Tail',
|
||||
'is_enabled' => true,
|
||||
'sort_order' => 40,
|
||||
'supports_multi_number' => false,
|
||||
'reserved_rule_json' => null,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
foreach ($this->rows() as $row) {
|
||||
DB::table('play_types')->updateOrInsert(
|
||||
['play_code' => $row['play_code']],
|
||||
array_merge($row, $defaults),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private function rows(): array
|
||||
{
|
||||
return [
|
||||
$this->row('big', 'standard', 4, 'single', 'Big', 10),
|
||||
$this->row('small', 'standard', 4, 'single', 'Small', 20),
|
||||
|
||||
$this->row('pos_4a', 'position', 4, 'single', '4A', 30, false, ['prize_scope' => ['first']]),
|
||||
$this->row('pos_4b', 'position', 4, 'single', '4B', 40, false, ['prize_scope' => ['second']]),
|
||||
$this->row('pos_4c', 'position', 4, 'single', '4C', 50, false, ['prize_scope' => ['third']]),
|
||||
$this->row('pos_4d', 'position', 4, 'single', '4D', 60, false, ['prize_scope' => ['starter']]),
|
||||
$this->row('pos_4e', 'position', 4, 'single', '4E', 70, false, ['prize_scope' => ['consolation']]),
|
||||
|
||||
$this->row('pos_3a', 'position', 3, 'single', '3A', 80, false, ['prize_scope' => ['first']]),
|
||||
$this->row('pos_3b', 'position', 3, 'single', '3B', 90, false, ['prize_scope' => ['second']]),
|
||||
$this->row('pos_3c', 'position', 3, 'single', '3C', 100, false, ['prize_scope' => ['third']]),
|
||||
$this->row('pos_3abc', 'position', 3, 'single', '3ABC', 110, false, ['prize_scope' => ['first', 'second', 'third']]),
|
||||
|
||||
$this->row('pos_2a', 'position', 2, 'single', '2A', 120, false, ['prize_scope' => ['first']]),
|
||||
$this->row('pos_2b', 'position', 2, 'single', '2B', 130, false, ['prize_scope' => ['second']]),
|
||||
$this->row('pos_2c', 'position', 2, 'single', '2C', 140, false, ['prize_scope' => ['third']]),
|
||||
$this->row('pos_2abc', 'position', 2, 'single', '2ABC', 150, false, ['prize_scope' => ['first', 'second', 'third']]),
|
||||
|
||||
$this->row('straight', 'box', 4, 'single', 'Straight', 160, false, ['expand_mode' => 'straight']),
|
||||
$this->row('box', 'box', 4, 'single', 'Box', 170, false, ['expand_mode' => 'box']),
|
||||
$this->row('ibox', 'box', 4, 'per_combination', 'iBox', 180, true, ['expand_mode' => 'box']),
|
||||
$this->row('mbox', 'box', 4, 'shared_total', 'mBox', 190, true, ['expand_mode' => 'box']),
|
||||
$this->row('roll', 'box', 4, 'per_combination', 'Roll', 200, true, ['expand_mode' => 'roll']),
|
||||
$this->row('half_box', 'box', 4, 'single', 'Half Box', 210, true, ['reserved' => true], false),
|
||||
|
||||
$this->row('head', 'attribute', 4, 'single', 'Head', 220, false, ['attribute' => 'head']),
|
||||
$this->row('tail', 'attribute', 4, 'single', 'Tail', 230, false, ['attribute' => 'tail']),
|
||||
$this->row('odd', 'attribute', null, 'single', 'Odd', 240, false, ['attribute' => 'odd']),
|
||||
$this->row('even', 'attribute', null, 'single', 'Even', 250, false, ['attribute' => 'even']),
|
||||
$this->row('digit_big', 'attribute', null, 'single', 'Big Digit', 260, false, ['attribute' => 'digit_big']),
|
||||
$this->row('digit_small', 'attribute', null, 'single', 'Small Digit', 270, false, ['attribute' => 'digit_small']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function row(
|
||||
string $playCode,
|
||||
string $category,
|
||||
?int $dimension,
|
||||
?string $betMode,
|
||||
string $name,
|
||||
int $sortOrder,
|
||||
bool $supportsMultiNumber = false,
|
||||
?array $rules = null,
|
||||
bool $isEnabled = true,
|
||||
): array {
|
||||
return [
|
||||
'play_code' => $playCode,
|
||||
'category' => $category,
|
||||
'dimension' => $dimension,
|
||||
'bet_mode' => $betMode,
|
||||
'display_name_zh' => $name,
|
||||
'display_name_en' => $name,
|
||||
'display_name_ne' => $name,
|
||||
'is_enabled' => $isEnabled,
|
||||
'sort_order' => $sortOrder,
|
||||
'supports_multi_number' => $supportsMultiNumber,
|
||||
'reserved_rule_json' => $rules === null ? null : json_encode($rules, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user