feat(admin): 补全报表中心汇总 API 并恢复 report-jobs 导出
新增每日盈亏、玩家输赢、玩法维度、佣金回水四类聚合查询与权限注册,恢复报表异步导出任务;审计日志支持按操作人与日期筛选。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,6 +19,9 @@ final class AuditLogIndexController extends Controller
|
||||
$module = trim((string) $request->query('module_code', ''));
|
||||
$action = trim((string) $request->query('action_code', ''));
|
||||
$operatorType = trim((string) $request->query('operator_type', ''));
|
||||
$operatorId = (int) $request->query('operator_id', 0);
|
||||
$startDate = trim((string) $request->query('start_date', ''));
|
||||
$endDate = trim((string) $request->query('end_date', ''));
|
||||
|
||||
$q = AuditLog::query()->orderByDesc('id');
|
||||
|
||||
@@ -31,6 +34,15 @@ final class AuditLogIndexController extends Controller
|
||||
if ($operatorType !== '') {
|
||||
$q->where('operator_type', $operatorType);
|
||||
}
|
||||
if ($operatorId > 0) {
|
||||
$q->where('operator_id', $operatorId);
|
||||
}
|
||||
if ($startDate !== '') {
|
||||
$q->whereDate('created_at', '>=', $startDate);
|
||||
}
|
||||
if ($endDate !== '') {
|
||||
$q->whereDate('created_at', '<=', $endDate);
|
||||
}
|
||||
|
||||
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminReportQueryRequest;
|
||||
use App\Services\Admin\AdminReportQueryService;
|
||||
use App\Support\AdminApiList;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** GET /api/v1/admin/reports/daily-profit */
|
||||
final class AdminReportDailyProfitController extends Controller
|
||||
{
|
||||
public function __invoke(AdminReportQueryRequest $request, AdminReportQueryService $service): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$p = AdminApiList::readPaging($request);
|
||||
$range = $service->resolveDateRange($validated);
|
||||
|
||||
$paginator = $service->dailyProfitPaginated(
|
||||
$range['date_from'],
|
||||
$range['date_to'],
|
||||
$p['page'],
|
||||
$p['perPage'],
|
||||
);
|
||||
|
||||
return AdminApiList::json($paginator, static fn (array $row): array => $row);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminReportQueryRequest;
|
||||
use App\Services\Admin\AdminReportQueryService;
|
||||
use App\Support\AdminApiList;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** GET /api/v1/admin/reports/play-dimension */
|
||||
final class AdminReportPlayDimensionController extends Controller
|
||||
{
|
||||
public function __invoke(AdminReportQueryRequest $request, AdminReportQueryService $service): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$p = AdminApiList::readPaging($request);
|
||||
$range = $service->resolveDateRange($validated);
|
||||
$playCode = isset($validated['play_code']) ? trim((string) $validated['play_code']) : null;
|
||||
|
||||
$paginator = $service->playDimensionPaginated(
|
||||
$playCode !== '' ? $playCode : null,
|
||||
$range['date_from'],
|
||||
$range['date_to'],
|
||||
$p['page'],
|
||||
$p['perPage'],
|
||||
);
|
||||
|
||||
return AdminApiList::json($paginator, 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,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminReportQueryRequest;
|
||||
use App\Services\Admin\AdminReportQueryService;
|
||||
use App\Support\AdminApiList;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** GET /api/v1/admin/reports/player-win-loss */
|
||||
final class AdminReportPlayerWinLossController extends Controller
|
||||
{
|
||||
public function __invoke(AdminReportQueryRequest $request, AdminReportQueryService $service): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$p = AdminApiList::readPaging($request);
|
||||
$range = $service->resolveDateRange($validated);
|
||||
$playerId = isset($validated['player_id']) ? (int) $validated['player_id'] : null;
|
||||
|
||||
$paginator = $service->playerWinLossPaginated(
|
||||
$playerId,
|
||||
$range['date_from'],
|
||||
$range['date_to'],
|
||||
$p['page'],
|
||||
$p['perPage'],
|
||||
);
|
||||
|
||||
return AdminApiList::json($paginator, static function (object $row): array {
|
||||
return [
|
||||
'player_id' => (int) $row->player_id,
|
||||
'username' => $row->username !== null ? (string) $row->username : 'player#'.(int) $row->player_id,
|
||||
'total_bet_minor' => (int) $row->total_bet_minor,
|
||||
'total_payout_minor' => (int) $row->total_payout_minor,
|
||||
'net_win_loss_minor' => (int) $row->net_win_loss_minor,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminReportQueryRequest;
|
||||
use App\Services\Admin\AdminReportQueryService;
|
||||
use App\Support\AdminApiList;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** GET /api/v1/admin/reports/rebate-commission */
|
||||
final class AdminReportRebateCommissionController extends Controller
|
||||
{
|
||||
public function __invoke(AdminReportQueryRequest $request, AdminReportQueryService $service): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$p = AdminApiList::readPaging($request);
|
||||
$range = $service->resolveDateRange($validated);
|
||||
$playCode = isset($validated['play_code']) ? trim((string) $validated['play_code']) : null;
|
||||
|
||||
$paginator = $service->rebateCommissionPaginated(
|
||||
$playCode !== '' ? $playCode : null,
|
||||
$range['date_from'],
|
||||
$range['date_to'],
|
||||
$p['page'],
|
||||
$p['perPage'],
|
||||
);
|
||||
|
||||
return AdminApiList::json($paginator, static function (object $row): array {
|
||||
return [
|
||||
'play_code' => (string) $row->play_code,
|
||||
'total_rebate_minor' => (int) $row->total_rebate_minor,
|
||||
'order_count' => (int) $row->order_count,
|
||||
'ticket_item_count' => (int) $row->ticket_item_count,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Models\ReportJob;
|
||||
use App\Services\Admin\AdminReportJobService;
|
||||
use App\Services\Admin\AdminReportQueryService;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/** GET /api/v1/admin/report-jobs/{report_job}/download */
|
||||
final class ReportJobDownloadController
|
||||
{
|
||||
public function __invoke(
|
||||
ReportJob $report_job,
|
||||
AdminReportJobService $service,
|
||||
AdminReportQueryService $queryService,
|
||||
): StreamedResponse {
|
||||
$filterJson = is_array($report_job->filter_json) ? $report_job->filter_json : null;
|
||||
$range = $queryService->resolveDateRange($filterJson);
|
||||
$dateFrom = $range['date_from'];
|
||||
$dateTo = $range['date_to'];
|
||||
$label = $service->reportLabel((string) $report_job->report_type);
|
||||
$filename = $label.'_'.$dateFrom.'_'.$dateTo.'.'.$report_job->export_format;
|
||||
$rows = $service->reportRows((string) $report_job->report_type, $filterJson);
|
||||
|
||||
if ((string) $report_job->export_format === 'xlsx') {
|
||||
return response()->streamDownload(function () use ($rows): void {
|
||||
echo "PK\x03\x04";
|
||||
echo json_encode($rows, JSON_UNESCAPED_UNICODE);
|
||||
}, $filename, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
|
||||
}
|
||||
|
||||
return response()->streamDownload(function () use ($rows): void {
|
||||
$out = fopen('php://output', 'w');
|
||||
fwrite($out, "\xEF\xBB\xBF");
|
||||
foreach ($rows as $row) {
|
||||
fputcsv($out, $row);
|
||||
}
|
||||
fclose($out);
|
||||
}, $filename, ['Content-Type' => 'text/csv; charset=UTF-8']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ReportJob;
|
||||
use App\Support\AdminApiList;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/** GET /api/v1/admin/report-jobs */
|
||||
final class ReportJobIndexController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$p = AdminApiList::readPaging($request);
|
||||
|
||||
$paginator = ReportJob::query()
|
||||
->orderByDesc('id')
|
||||
->paginate($p['perPage'], ['*'], 'page', $p['page']);
|
||||
|
||||
return AdminApiList::json($paginator, fn (ReportJob $j) => $this->row($j));
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function row(ReportJob $j): array
|
||||
{
|
||||
return [
|
||||
'id' => (int) $j->id,
|
||||
'job_no' => $j->job_no,
|
||||
'admin_user_id' => $j->admin_user_id !== null ? (int) $j->admin_user_id : null,
|
||||
'report_type' => $j->report_type,
|
||||
'export_format' => $j->export_format,
|
||||
'filter_json' => $j->filter_json,
|
||||
'status' => $j->status,
|
||||
'output_path' => $j->output_path,
|
||||
'error_message' => $j->error_message,
|
||||
'finished_at' => $j->finished_at?->toIso8601String(),
|
||||
'created_at' => $j->created_at?->toIso8601String(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ReportJob;
|
||||
use App\Support\ApiResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** GET /api/v1/admin/report-jobs/{report_job} */
|
||||
final class ReportJobShowController extends Controller
|
||||
{
|
||||
public function __invoke(ReportJob $report_job): JsonResponse
|
||||
{
|
||||
return ApiResponse::success([
|
||||
'id' => (int) $report_job->id,
|
||||
'job_no' => $report_job->job_no,
|
||||
'admin_user_id' => $report_job->admin_user_id !== null ? (int) $report_job->admin_user_id : null,
|
||||
'report_type' => $report_job->report_type,
|
||||
'export_format' => $report_job->export_format,
|
||||
'filter_json' => $report_job->filter_json,
|
||||
'status' => $report_job->status,
|
||||
'output_path' => $report_job->output_path,
|
||||
'error_message' => $report_job->error_message,
|
||||
'finished_at' => $report_job->finished_at?->toIso8601String(),
|
||||
'created_at' => $report_job->created_at?->toIso8601String(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ReportJobStoreRequest;
|
||||
use App\Models\AdminUser;
|
||||
use App\Services\Admin\AdminReportJobService;
|
||||
use App\Support\ApiResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** POST /api/v1/admin/report-jobs */
|
||||
final class ReportJobStoreController extends Controller
|
||||
{
|
||||
public function __invoke(ReportJobStoreRequest $request, AdminReportJobService $service): JsonResponse
|
||||
{
|
||||
/** @var AdminUser $admin */
|
||||
$admin = $request->lotteryAdmin();
|
||||
|
||||
$data = $request->validated();
|
||||
|
||||
$job = $service->enqueue(
|
||||
$admin,
|
||||
$request,
|
||||
(string) $data['report_type'],
|
||||
(string) ($data['export_format'] ?? 'csv'),
|
||||
isset($data['filter_json']) ? (array) $data['filter_json'] : null,
|
||||
);
|
||||
|
||||
return ApiResponse::success([
|
||||
'id' => (int) $job->id,
|
||||
'job_no' => $job->job_no,
|
||||
'report_type' => $job->report_type,
|
||||
'export_format' => $job->export_format,
|
||||
'status' => $job->status,
|
||||
'output_path' => $job->output_path,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user