feat: 扩展玩法配置快照字段并切换目录生效来源
This commit is contained in:
@@ -4,7 +4,6 @@ namespace App\Services\Config;
|
||||
|
||||
use App\Models\Currency;
|
||||
use App\Models\OddsItem;
|
||||
use App\Models\PlayType;
|
||||
use App\Models\OddsVersion;
|
||||
use App\Models\RiskCapItem;
|
||||
use App\Models\PlayConfigItem;
|
||||
@@ -37,8 +36,6 @@ final class EffectivePlayCatalogService
|
||||
->where('status', ConfigVersionStatus::Active->value)
|
||||
->firstOrFail();
|
||||
|
||||
$playTypes = PlayType::query()->orderBy('sort_order')->orderBy('play_code')->get();
|
||||
|
||||
/** @var Collection<string, PlayConfigItem> $configByCode */
|
||||
$configByCode = PlayConfigItem::query()
|
||||
->where('version_id', $playVersion->id)
|
||||
@@ -58,26 +55,30 @@ final class EffectivePlayCatalogService
|
||||
->orderBy('normalized_number')
|
||||
->get();
|
||||
|
||||
$plays = $playTypes->map(function (PlayType $pt) use ($configByCode, $oddsByPlay): array {
|
||||
$c = $configByCode->get($pt->play_code);
|
||||
$items = $oddsByPlay->get($pt->play_code, collect());
|
||||
$o = $this->pickPrimaryOddsItem($items);
|
||||
$plays = $configByCode->values()
|
||||
->sortBy([
|
||||
['display_order', 'asc'],
|
||||
['play_code', 'asc'],
|
||||
])
|
||||
->map(function (PlayConfigItem $c) use ($oddsByPlay): array {
|
||||
$items = $oddsByPlay->get($c->play_code, collect());
|
||||
$o = $this->pickPrimaryOddsItem($items);
|
||||
|
||||
return [
|
||||
'play_code' => $pt->play_code,
|
||||
'category' => $pt->category,
|
||||
'dimension' => $pt->dimension,
|
||||
'bet_mode' => $pt->bet_mode,
|
||||
'display_name_zh' => $pt->display_name_zh,
|
||||
'display_name_en' => $pt->display_name_en,
|
||||
'display_name_ne' => $pt->display_name_ne,
|
||||
'sort_order' => (int) $pt->sort_order,
|
||||
'supports_multi_number' => (bool) $pt->supports_multi_number,
|
||||
'master_enabled' => (bool) $pt->is_enabled,
|
||||
'config' => $c === null ? null : $this->serializePlayConfigItem($c),
|
||||
'odds' => $o === null ? null : $this->serializeOddsItem($o),
|
||||
];
|
||||
})->values()->all();
|
||||
return [
|
||||
'play_code' => $c->play_code,
|
||||
'category' => $c->category,
|
||||
'dimension' => $c->dimension === null ? null : (int) $c->dimension,
|
||||
'bet_mode' => $c->bet_mode,
|
||||
'display_name_zh' => $c->display_name_zh,
|
||||
'display_name_en' => $c->display_name_en,
|
||||
'display_name_ne' => $c->display_name_ne,
|
||||
'sort_order' => (int) $c->display_order,
|
||||
'supports_multi_number' => (bool) $c->supports_multi_number,
|
||||
'master_enabled' => (bool) $c->is_enabled,
|
||||
'config' => $this->serializePlayConfigItem($c),
|
||||
'odds' => $o === null ? null : $this->serializeOddsItem($o),
|
||||
];
|
||||
})->values()->all();
|
||||
|
||||
return [
|
||||
'currency_code' => $currency->code,
|
||||
@@ -144,10 +145,18 @@ final class EffectivePlayCatalogService
|
||||
private function serializePlayConfigItem(PlayConfigItem $r): array
|
||||
{
|
||||
return [
|
||||
'category' => $r->category,
|
||||
'dimension' => $r->dimension === null ? null : (int) $r->dimension,
|
||||
'bet_mode' => $r->bet_mode,
|
||||
'display_name_zh' => $r->display_name_zh,
|
||||
'display_name_en' => $r->display_name_en,
|
||||
'display_name_ne' => $r->display_name_ne,
|
||||
'is_enabled' => (bool) $r->is_enabled,
|
||||
'min_bet_amount' => (int) $r->min_bet_amount,
|
||||
'max_bet_amount' => (int) $r->max_bet_amount,
|
||||
'display_order' => (int) $r->display_order,
|
||||
'supports_multi_number' => (bool) $r->supports_multi_number,
|
||||
'reserved_rule_json' => $r->reserved_rule_json,
|
||||
'rule_text_zh' => $r->rule_text_zh,
|
||||
'rule_text_en' => $r->rule_text_en,
|
||||
'rule_text_ne' => $r->rule_text_ne,
|
||||
|
||||
@@ -54,10 +54,18 @@ final class PlayConfigStreamService
|
||||
PlayConfigItem::query()->create([
|
||||
'version_id' => $draft->id,
|
||||
'play_code' => $row->play_code,
|
||||
'category' => $row->category,
|
||||
'dimension' => $row->dimension,
|
||||
'bet_mode' => $row->bet_mode,
|
||||
'display_name_zh' => $row->display_name_zh,
|
||||
'display_name_en' => $row->display_name_en,
|
||||
'display_name_ne' => $row->display_name_ne,
|
||||
'is_enabled' => $row->is_enabled,
|
||||
'min_bet_amount' => $row->min_bet_amount,
|
||||
'max_bet_amount' => $row->max_bet_amount,
|
||||
'display_order' => $row->display_order,
|
||||
'supports_multi_number' => $row->supports_multi_number,
|
||||
'reserved_rule_json' => $row->reserved_rule_json,
|
||||
'rule_text_zh' => $row->rule_text_zh,
|
||||
'rule_text_en' => $row->rule_text_en,
|
||||
'rule_text_ne' => $row->rule_text_ne,
|
||||
@@ -69,10 +77,18 @@ final class PlayConfigStreamService
|
||||
PlayConfigItem::query()->create([
|
||||
'version_id' => $draft->id,
|
||||
'play_code' => $pt->play_code,
|
||||
'category' => $pt->category,
|
||||
'dimension' => $pt->dimension,
|
||||
'bet_mode' => $pt->bet_mode,
|
||||
'display_name_zh' => $pt->display_name_zh,
|
||||
'display_name_en' => $pt->display_name_en,
|
||||
'display_name_ne' => $pt->display_name_ne,
|
||||
'is_enabled' => (bool) $pt->is_enabled,
|
||||
'min_bet_amount' => 100,
|
||||
'max_bet_amount' => 500_000_000,
|
||||
'display_order' => (int) $pt->sort_order,
|
||||
'supports_multi_number' => (bool) $pt->supports_multi_number,
|
||||
'reserved_rule_json' => $pt->reserved_rule_json,
|
||||
'rule_text_zh' => null,
|
||||
'rule_text_en' => null,
|
||||
'rule_text_ne' => null,
|
||||
@@ -97,10 +113,18 @@ final class PlayConfigStreamService
|
||||
PlayConfigItem::query()->create([
|
||||
'version_id' => $draft->id,
|
||||
'play_code' => (string) $row['play_code'],
|
||||
'category' => $row['category'] ?? null,
|
||||
'dimension' => $row['dimension'] ?? null,
|
||||
'bet_mode' => $row['bet_mode'] ?? null,
|
||||
'display_name_zh' => $row['display_name_zh'] ?? null,
|
||||
'display_name_en' => $row['display_name_en'] ?? null,
|
||||
'display_name_ne' => $row['display_name_ne'] ?? null,
|
||||
'is_enabled' => (bool) ($row['is_enabled'] ?? true),
|
||||
'min_bet_amount' => (int) ($row['min_bet_amount'] ?? 0),
|
||||
'max_bet_amount' => (int) ($row['max_bet_amount'] ?? 0),
|
||||
'display_order' => (int) ($row['display_order'] ?? 0),
|
||||
'supports_multi_number' => (bool) ($row['supports_multi_number'] ?? false),
|
||||
'reserved_rule_json' => $row['reserved_rule_json'] ?? null,
|
||||
'rule_text_zh' => isset($row['rule_text_zh']) ? (string) $row['rule_text_zh'] : null,
|
||||
'rule_text_en' => isset($row['rule_text_en']) ? (string) $row['rule_text_en'] : null,
|
||||
'rule_text_ne' => isset($row['rule_text_ne']) ? (string) $row['rule_text_ne'] : null,
|
||||
@@ -221,6 +245,14 @@ final class PlayConfigStreamService
|
||||
if ($maxBet < $minBet) {
|
||||
$errors["items.$index.max_bet_amount"][] = '最大下注额不能小于最小下注额';
|
||||
}
|
||||
|
||||
if ($row->display_name_zh === null || $row->display_name_zh === '') {
|
||||
$errors["items.$index.display_name_zh"][] = '显示名称不能为空';
|
||||
}
|
||||
|
||||
if ($row->display_order === null) {
|
||||
$errors["items.$index.display_order"][] = '排序不能为空';
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors !== []) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Services\Ticket;
|
||||
|
||||
use App\Models\OddsItem;
|
||||
use App\Models\PlayType;
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Models\OddsVersion;
|
||||
use App\Models\RiskCapItem;
|
||||
@@ -80,18 +79,10 @@ final class PlayCatalogResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{play_type: PlayType, play_config: PlayConfigItem, odds_items: Collection<int, OddsItem>}
|
||||
* @return array{play_config: PlayConfigItem, odds_items: Collection<int, OddsItem>}
|
||||
*/
|
||||
public function resolve(string $playCode, string $currencyCode): array
|
||||
{
|
||||
$playType = PlayType::query()->where('play_code', $playCode)->first();
|
||||
if ($playType === null) {
|
||||
throw new TicketOperationException('play_not_found', ErrorCode::BetPlayUnsupported->value);
|
||||
}
|
||||
if (! $playType->is_enabled) {
|
||||
throw new TicketOperationException('play_master_disabled', ErrorCode::PlayModeClosed->value);
|
||||
}
|
||||
|
||||
$playVersion = PlayConfigVersion::query()
|
||||
->where('status', ConfigVersionStatus::Active->value)
|
||||
->firstOrFail();
|
||||
@@ -120,7 +111,6 @@ final class PlayCatalogResolver
|
||||
}
|
||||
|
||||
return [
|
||||
'play_type' => $playType,
|
||||
'play_config' => $playConfig,
|
||||
'odds_items' => $oddsItems,
|
||||
];
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Services\Ticket;
|
||||
|
||||
use App\Models\OddsItem;
|
||||
use App\Models\PlayType;
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Models\PlayConfigItem;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -20,7 +19,7 @@ final class PlayRuleEngine
|
||||
* @param Collection<int, OddsItem> $oddsItems
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function evaluateLine(array $line, PlayType $playType, PlayConfigItem $playConfig, Collection $oddsItems): array
|
||||
public function evaluateLine(array $line, PlayConfigItem $playConfig, Collection $oddsItems): array
|
||||
{
|
||||
$playCode = (string) $line['play_code'];
|
||||
$dimension = $line['dimension'] ?? null;
|
||||
@@ -61,9 +60,9 @@ final class PlayRuleEngine
|
||||
'original_number' => (string) $line['number'],
|
||||
'normalized_number' => $number,
|
||||
'play_code' => $playCode,
|
||||
'dimension' => $this->toDimensionInt(is_string($dimension) ? $dimension : null, $playType),
|
||||
'dimension' => $this->toDimensionInt(is_string($dimension) ? $dimension : null, $playConfig),
|
||||
'digit_slot' => $digitSlotInt,
|
||||
'bet_mode' => $playType->bet_mode,
|
||||
'bet_mode' => $playConfig->bet_mode,
|
||||
'unit_bet_amount' => $unitBetAmount,
|
||||
'total_bet_amount' => $totalBetAmount,
|
||||
'rebate_rate_snapshot' => number_format($rebateRate, 4, '.', ''),
|
||||
@@ -300,13 +299,13 @@ final class PlayRuleEngine
|
||||
return $oddsItems->firstOrFail();
|
||||
}
|
||||
|
||||
private function toDimensionInt(?string $dimension, PlayType $playType): ?int
|
||||
private function toDimensionInt(?string $dimension, PlayConfigItem $playConfig): ?int
|
||||
{
|
||||
return match ($dimension) {
|
||||
'D2' => 2,
|
||||
'D3' => 3,
|
||||
'D4' => 4,
|
||||
default => $playType->dimension === null ? null : (int) $playType->dimension,
|
||||
default => $playConfig->dimension === null ? null : (int) $playConfig->dimension,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ final class TicketPlacementService
|
||||
}
|
||||
$evaluated = $this->ruleEngine->evaluateLine(
|
||||
(array) $line,
|
||||
$resolved['play_type'],
|
||||
$resolved['play_config'],
|
||||
$resolved['odds_items'],
|
||||
);
|
||||
|
||||
@@ -54,7 +54,6 @@ final class TicketPreviewService
|
||||
}
|
||||
$evaluated = $this->ruleEngine->evaluateLine(
|
||||
(array) $line,
|
||||
$resolved['play_type'],
|
||||
$resolved['play_config'],
|
||||
$resolved['odds_items'],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user