feat: 更新玩法配置管理,简化字段并增强功能
- 将玩法相关的显示名称字段统一为 `display_name`,移除多语言字段。 - 在 `PlayTypePatchController` 中新增即时切换玩法开关的功能,并推送大厅更新。 - 优化多个控制器和服务中的权限检查与数据处理逻辑,提升代码可读性与维护性。
This commit is contained in:
@@ -28,6 +28,180 @@ final class AdminReportQueryService
|
||||
return ['date_from' => $dateFrom, 'date_to' => $dateTo];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{date_from: string, date_to: string}
|
||||
*/
|
||||
public function resolveDashboardPeriod(string $period, ?string $dateFrom, ?string $dateTo): array
|
||||
{
|
||||
$today = now()->toDateString();
|
||||
|
||||
$range = match ($period) {
|
||||
'today' => ['date_from' => $today, 'date_to' => $today],
|
||||
'last_7_days' => [
|
||||
'date_from' => now()->subDays(6)->toDateString(),
|
||||
'date_to' => $today,
|
||||
],
|
||||
'last_30_days' => [
|
||||
'date_from' => now()->subDays(29)->toDateString(),
|
||||
'date_to' => $today,
|
||||
],
|
||||
'this_month' => [
|
||||
'date_from' => now()->startOfMonth()->toDateString(),
|
||||
'date_to' => $today,
|
||||
],
|
||||
'lifetime' => $this->lifetimeBusinessDateBounds(),
|
||||
'custom' => [
|
||||
'date_from' => $dateFrom !== null && $dateFrom !== '' ? $dateFrom : $today,
|
||||
'date_to' => $dateTo !== null && $dateTo !== '' ? $dateTo : $today,
|
||||
],
|
||||
default => ['date_from' => $today, 'date_to' => $today],
|
||||
};
|
||||
|
||||
$from = $range['date_from'];
|
||||
$to = $range['date_to'];
|
||||
if ($from > $to) {
|
||||
[$from, $to] = [$to, $from];
|
||||
}
|
||||
|
||||
return ['date_from' => $from, 'date_to' => $to];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{date_from: string, date_to: string}
|
||||
*/
|
||||
private function lifetimeBusinessDateBounds(): array
|
||||
{
|
||||
$today = now()->toDateString();
|
||||
$bounds = DB::table('draws as d')
|
||||
->join('ticket_orders as o', 'o.draw_id', '=', 'd.id')
|
||||
->selectRaw('MIN(d.business_date) as date_from')
|
||||
->selectRaw('MAX(d.business_date) as date_to')
|
||||
->first();
|
||||
|
||||
$from = $this->formatBusinessDateValue($bounds?->date_from) ?? $today;
|
||||
$to = $this->formatBusinessDateValue($bounds?->date_to) ?? $today;
|
||||
|
||||
return ['date_from' => $from, 'date_to' => $to];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* total_bet_minor: int,
|
||||
* total_payout_minor: int,
|
||||
* approx_house_gross_minor: int,
|
||||
* draw_count: int,
|
||||
* business_day_count: int
|
||||
* }
|
||||
*/
|
||||
public function periodFinanceTotals(string $dateFrom, string $dateTo): array
|
||||
{
|
||||
$rows = $this->dailyProfitRows($dateFrom, $dateTo);
|
||||
$totalBet = 0;
|
||||
$totalPayout = 0;
|
||||
$totalGross = 0;
|
||||
foreach ($rows as $row) {
|
||||
$totalBet += (int) $row['total_bet_minor'];
|
||||
$totalPayout += (int) $row['total_payout_minor'];
|
||||
$totalGross += (int) $row['approx_house_gross_minor'];
|
||||
}
|
||||
|
||||
$activity = DB::table('draws as d')
|
||||
->join('ticket_orders as o', 'o.draw_id', '=', 'd.id')
|
||||
->whereBetween('d.business_date', [$dateFrom, $dateTo])
|
||||
->selectRaw('COUNT(DISTINCT d.id) as draw_count')
|
||||
->selectRaw('COUNT(DISTINCT d.business_date) as business_day_count')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'total_bet_minor' => $totalBet,
|
||||
'total_payout_minor' => $totalPayout,
|
||||
'approx_house_gross_minor' => $totalGross,
|
||||
'draw_count' => (int) ($activity->draw_count ?? 0),
|
||||
'business_day_count' => (int) ($activity->business_day_count ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续业务日序列(无数据日补零),用于趋势图。
|
||||
*
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public function dailyProfitSeriesFilled(string $dateFrom, string $dateTo, int $maxDays = 90): array
|
||||
{
|
||||
$from = Carbon::parse($dateFrom)->startOfDay();
|
||||
$to = Carbon::parse($dateTo)->startOfDay();
|
||||
$spanDays = (int) $from->diffInDays($to) + 1;
|
||||
|
||||
$chartFrom = $dateFrom;
|
||||
$chartTo = $dateTo;
|
||||
$truncated = false;
|
||||
if ($spanDays > $maxDays) {
|
||||
$chartFrom = $to->copy()->subDays($maxDays - 1)->format('Y-m-d');
|
||||
$truncated = true;
|
||||
}
|
||||
|
||||
$indexed = collect($this->dailyProfitRows($chartFrom, $chartTo))->keyBy('business_date');
|
||||
$cursor = Carbon::parse($chartFrom)->startOfDay();
|
||||
$end = Carbon::parse($chartTo)->startOfDay();
|
||||
$series = [];
|
||||
|
||||
while ($cursor <= $end) {
|
||||
$key = $cursor->format('Y-m-d');
|
||||
$series[] = $indexed[$key] ?? [
|
||||
'business_date' => $key,
|
||||
'total_bet_minor' => 0,
|
||||
'total_payout_minor' => 0,
|
||||
'approx_house_gross_minor' => 0,
|
||||
];
|
||||
$cursor->addDay();
|
||||
}
|
||||
|
||||
return [
|
||||
'series' => $series,
|
||||
'chart_date_from' => $chartFrom,
|
||||
'chart_date_to' => $chartTo,
|
||||
'truncated' => $truncated,
|
||||
'span_days' => $spanDays,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public function playDimensionBreakdownRows(
|
||||
string $dateFrom,
|
||||
string $dateTo,
|
||||
?string $playCode = null,
|
||||
int $limit = 12,
|
||||
): array {
|
||||
return $this->playDimensionBaseQuery($playCode, $dateFrom, $dateTo)
|
||||
->orderByDesc('total_bet_minor')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(static function (object $row): array {
|
||||
return [
|
||||
'play_code' => (string) $row->play_code,
|
||||
'dimension' => (int) $row->dimension,
|
||||
'total_bet_minor' => (int) $row->total_bet_minor,
|
||||
'total_payout_minor' => (int) $row->total_payout_minor,
|
||||
'approx_house_gross_minor' => (int) $row->approx_house_gross_minor,
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function resolvePeriodCurrencyCode(string $dateFrom, string $dateTo): ?string
|
||||
{
|
||||
$currencyCode = (string) (DB::table('ticket_orders as o')
|
||||
->join('draws as d', 'd.id', '=', 'o.draw_id')
|
||||
->whereBetween('d.business_date', [$dateFrom, $dateTo])
|
||||
->orderByDesc('o.id')
|
||||
->value('o.currency_code') ?? '');
|
||||
|
||||
return $currencyCode !== '' ? $currencyCode : null;
|
||||
}
|
||||
|
||||
public function dailyProfitPaginated(string $dateFrom, string $dateTo, int $page, int $perPage): LengthAwarePaginator
|
||||
{
|
||||
$rows = $this->dailyProfitRows($dateFrom, $dateTo);
|
||||
@@ -80,6 +254,57 @@ final class AdminReportQueryService
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全平台历史累计投注/派彩/盈亏(与 daily-profit 同口径,不限业务日)。
|
||||
*
|
||||
* @return array{
|
||||
* currency_code: ?string,
|
||||
* total_bet_minor: int,
|
||||
* total_payout_minor: int,
|
||||
* approx_house_gross_minor: int,
|
||||
* draw_count: int,
|
||||
* business_day_count: int,
|
||||
* date_from: ?string,
|
||||
* date_to: ?string
|
||||
* }
|
||||
*/
|
||||
public function platformLifetimeTotals(): array
|
||||
{
|
||||
$totalBetMinor = (int) DB::table('ticket_orders')->sum('total_actual_deduct');
|
||||
|
||||
$payoutAgg = DB::table('ticket_items')
|
||||
->selectRaw('COALESCE(SUM(win_amount), 0) as win_minor, COALESCE(SUM(jackpot_win_amount), 0) as jackpot_minor')
|
||||
->first();
|
||||
$totalPayoutMinor = (int) ($payoutAgg->win_minor ?? 0) + (int) ($payoutAgg->jackpot_minor ?? 0);
|
||||
|
||||
$activity = DB::table('draws as d')
|
||||
->join('ticket_orders as o', 'o.draw_id', '=', 'd.id')
|
||||
->selectRaw('COUNT(DISTINCT d.id) as draw_count')
|
||||
->selectRaw('COUNT(DISTINCT d.business_date) as business_day_count')
|
||||
->selectRaw('MIN(d.business_date) as date_from')
|
||||
->selectRaw('MAX(d.business_date) as date_to')
|
||||
->first();
|
||||
|
||||
$drawCount = (int) ($activity->draw_count ?? 0);
|
||||
$businessDayCount = (int) ($activity->business_day_count ?? 0);
|
||||
|
||||
$dateFrom = $this->formatBusinessDateValue($activity?->date_from);
|
||||
$dateTo = $this->formatBusinessDateValue($activity?->date_to);
|
||||
|
||||
$currencyCode = (string) (DB::table('ticket_orders')->orderByDesc('id')->value('currency_code') ?? '');
|
||||
|
||||
return [
|
||||
'currency_code' => $currencyCode !== '' ? $currencyCode : null,
|
||||
'total_bet_minor' => $totalBetMinor,
|
||||
'total_payout_minor' => $totalPayoutMinor,
|
||||
'approx_house_gross_minor' => $totalBetMinor - $totalPayoutMinor,
|
||||
'draw_count' => $drawCount,
|
||||
'business_day_count' => $businessDayCount,
|
||||
'date_from' => $dateFrom,
|
||||
'date_to' => $dateTo,
|
||||
];
|
||||
}
|
||||
|
||||
public function playerWinLossPaginated(
|
||||
?int $playerId,
|
||||
string $dateFrom,
|
||||
@@ -340,4 +565,26 @@ final class AdminReportQueryService
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function formatBusinessDateValue(mixed $value): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof Carbon) {
|
||||
return $value->format('Y-m-d');
|
||||
}
|
||||
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}/', $raw, $m) === 1) {
|
||||
return substr($m[0], 0, 10);
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user