refactor:拆分 API 路由与请求校验,统一 final 类和代码风格

This commit is contained in:
2026-05-13 11:54:40 +08:00
parent 5d2dbdbe1d
commit 805847954d
281 changed files with 1886 additions and 1308 deletions

View File

@@ -2,10 +2,10 @@
namespace App\Console\Commands;
use App\Services\Draw\DrawTickService;
use Illuminate\Console\Command;
use App\Services\Draw\DrawTickService;
class LotteryDrawTickCommand extends Command
final class LotteryDrawTickCommand extends Command
{
protected $signature = 'lottery:draw-tick';

View File

@@ -2,10 +2,10 @@
namespace App\Console\Commands;
use App\Services\Draw\LotteryHallRealtimeBroadcaster;
use Illuminate\Console\Command;
use App\Services\Draw\LotteryHallRealtimeBroadcaster;
class LotteryHallCountdownCommand extends Command
final class LotteryHallCountdownCommand extends Command
{
protected $signature = 'lottery:hall-countdown';

View File

@@ -3,13 +3,13 @@
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
/** 界面文档 §2.1`draw.countdown` */
class DrawCountdownBroadcast implements ShouldBroadcastNow
final class DrawCountdownBroadcast implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;

View File

@@ -3,13 +3,13 @@
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
/** 界面文档 §2.1`result.published` */
class DrawResultPublishedBroadcast implements ShouldBroadcastNow
final class DrawResultPublishedBroadcast implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;

View File

@@ -3,13 +3,13 @@
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
/** 界面文档 §2.1`draw.status_change` */
class DrawStatusChangeBroadcast implements ShouldBroadcastNow
final class DrawStatusChangeBroadcast implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;

View File

@@ -2,8 +2,8 @@
namespace App\Exceptions;
use App\Lottery\ErrorCode;
use RuntimeException;
use App\Lottery\ErrorCode;
/**
* 玩家端 Bearer 鉴权失败时抛出,由 EnsurePlayerApi 捕获并转为 JSON。

View File

@@ -2,9 +2,9 @@
namespace App\Exceptions;
use RuntimeException;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use RuntimeException;
/**
* 玩家钱包划转等业务失败时抛出,由控制器捕获并转为 {@see ApiResponse} JSON。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Audit;
use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/audit-logs 运营/客服查询审计留痕。

View File

@@ -2,9 +2,9 @@
namespace App\Http\Controllers\Api\V1\Admin\Auth;
use App\Services\AdminCaptchaService;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Services\AdminCaptchaService;
/**
* GET /api/v1/admin/auth/captcha

View File

@@ -2,38 +2,28 @@
namespace App\Http\Controllers\Api\V1\Admin\Auth;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Services\AdminCaptchaService;
use App\Lottery\ErrorCode;
use Illuminate\Support\Str;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Services\AdminCaptchaService;
use App\Http\Requests\Admin\AdminLoginRequest;
/**
* POST /api/v1/admin/auth/login
*
* Body: account登录账号 username 对应ASCII 仅存小写比对、password、captcha_key、captcha_code。
*/
final class LoginController
final class LoginController extends Controller
{
public function __invoke(Request $request, AdminCaptchaService $captcha): JsonResponse
public function __invoke(AdminLoginRequest $request, AdminCaptchaService $captcha): JsonResponse
{
$locale = $request->lotteryLocale();
/** @var array{account:string,password:string,captcha_key:string,captcha_code:string} $data */
$data = validator($request->all(), [
'account' => ['required', 'string', 'min:2', 'max:64', 'regex:/^[a-zA-Z0-9._-]+$/u'],
'password' => ['required', 'string', 'max:256'],
'captcha_key' => ['required', 'string', 'uuid'],
'captcha_code' => ['required', 'string', 'max:32'],
], [], [
'account' => 'account',
'password' => 'password',
'captcha_key' => 'captcha_key',
'captcha_code' => 'captcha_code',
])->validate();
$data = $request->validated();
if (! $captcha->verify($data['captcha_key'], $data['captcha_code'])) {
return ApiResponse::error(

View File

@@ -2,17 +2,17 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Models\OddsVersion;
use App\Services\Config\OddsStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Support\AdminConfigPresenter;
use App\Services\Config\OddsStreamService;
/** PUT /api/v1/admin/config/odds-versions/{id}/items */
final class OddsItemsReplaceController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\OddsVersion;
use App\Services\Config\OddsStreamService;
use App\Support\AdminApiList;
use App\Support\AdminConfigPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\OddsStreamService;
/** GET /api/v1/admin/config/odds-versions */
final class OddsVersionIndexController extends Controller

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Models\OddsVersion;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\OddsVersion;
use App\Services\Config\OddsStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\Config\OddsStreamService;
/** POST /api/v1/admin/config/odds-versions/{id}/publish */
final class OddsVersionPublishController extends Controller

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\OddsVersion;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
/** GET /api/v1/admin/config/odds-versions/{id} */
final class OddsVersionShowController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\Config\OddsStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\OddsStreamService;
/** POST /api/v1/admin/config/odds-versions */
final class OddsVersionStoreController extends Controller

View File

@@ -2,17 +2,17 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\PlayConfigVersion;
use App\Services\Config\PlayConfigStreamService;
use App\Support\AdminConfigPresenter;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Models\PlayConfigVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Support\AdminConfigPresenter;
use App\Services\Config\PlayConfigStreamService;
/** PUT /api/v1/admin/config/play-versions/{id}/items */
final class PlayConfigItemsReplaceController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\PlayConfigVersion;
use App\Services\Config\PlayConfigStreamService;
use App\Support\AdminApiList;
use App\Support\AdminConfigPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\PlayConfigVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\PlayConfigStreamService;
/** GET /api/v1/admin/config/play-versions */
final class PlayConfigVersionIndexController extends Controller

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Models\PlayConfigVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\PlayConfigVersion;
use App\Services\Config\PlayConfigStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\Config\PlayConfigStreamService;
/** POST /api/v1/admin/config/play-versions/{id}/publish */
final class PlayConfigVersionPublishController extends Controller

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\PlayConfigVersion;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use App\Models\PlayConfigVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
/** GET /api/v1/admin/config/play-versions/{id} */
final class PlayConfigVersionShowController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\Config\PlayConfigStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\PlayConfigStreamService;
/** POST /api/v1/admin/config/play-versions — 新建草稿(默认克隆当前 active若无则按 play_types 生成)。 */
final class PlayConfigVersionStoreController extends Controller

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Models\RiskCapVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\RiskCapVersion;
use App\Services\Config\RiskCapStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\Config\RiskCapStreamService;
/** PUT /api/v1/admin/config/risk-cap-versions/{id}/items */
final class RiskCapItemsReplaceController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\RiskCapVersion;
use App\Services\Config\RiskCapStreamService;
use App\Support\AdminApiList;
use App\Support\AdminConfigPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\RiskCapVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\RiskCapStreamService;
/** GET /api/v1/admin/config/risk-cap-versions */
final class RiskCapVersionIndexController extends Controller

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Models\RiskCapVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\RiskCapVersion;
use App\Services\Config\RiskCapStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\Config\RiskCapStreamService;
/** POST /api/v1/admin/config/risk-cap-versions/{id}/publish */
final class RiskCapVersionPublishController extends Controller

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\RiskCapVersion;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use App\Models\RiskCapVersion;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
/** GET /api/v1/admin/config/risk-cap-versions/{id} */
final class RiskCapVersionShowController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Config;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\Config\RiskCapStreamService;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
use App\Services\Config\RiskCapStreamService;
/** POST /api/v1/admin/config/risk-cap-versions */
final class RiskCapVersionStoreController extends Controller

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Dashboard;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Services\Admin\AdminDashboardSnapshotBuilder;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Admin\AdminDashboardSnapshotBuilder;
/**
* GET /api/v1/admin/dashboard 首页仪表盘聚合数据(需登录;按权限返回子块)。

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\SettlementBatch;
use App\Models\TicketItem;
use App\Models\TicketOrder;
use App\Support\ApiResponse;
use App\Models\SettlementBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/draws/{draw}/finance-summary 单期投注/派彩汇总(客服/财务视角PRD §15.4)。

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Support\AdminApiList;
use Carbon\Carbon;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use App\Models\Draw;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* GET /api/v1/admin/draws 期号列表。

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Models\DrawResultItem;
use App\Support\ApiResponse;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/draws/{draw}/result-batches 开奖批次与号码(审核/结果核对)。

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use Carbon\Carbon;
use App\Models\Draw;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\DrawResultBatchStatus;
use App\Models\Draw;
use App\Services\Draw\DrawHallSnapshotBuilder;
use App\Support\ApiResponse;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
/**
* GET /api/v1/admin/draws/{draw} 当期状态明细(后台查看 DB 为准 + 大厅展示态预览)。

View File

@@ -2,20 +2,20 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Services\Draw\DrawPublishService;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Models\DrawResultBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Draw\DrawPublishService;
/**
* POST /api/v1/admin/draws/{draw}/result-batches/{batch}/publish 人工审核发布 RNG 批次。
*/
class DrawResultBatchPublishController extends Controller
final class DrawResultBatchPublishController extends Controller
{
public function __construct(
private readonly DrawPublishService $publishService,

View File

@@ -2,14 +2,14 @@
namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Models\Draw;
use App\Services\Settlement\SettlementOrchestrator;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Settlement\SettlementOrchestrator;
/**
* POST /api/v1/admin/draws/{draw}/settlement/run `settling` 期号执行结算(可关自动结算时手工触发)。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotContribution;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Models\JackpotContribution;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/jackpot/contributions Jackpot 蓄水流水。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotPayoutLog;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\JackpotPayoutLog;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/jackpot/payout-logs Jackpot 派彩(爆池)记录。

View File

@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotPool;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/jackpot/pools Jackpot 奖池配置列表。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotPool;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* PUT /api/v1/admin/jackpot/pools/{pool} 更新奖池运营参数(蓄水比例、阈值等)。

View File

@@ -2,15 +2,15 @@
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* Bearer Token 必填({@see EnsureAdminApi} + Sanctum确认 `/api/v1/admin` 鉴权链路可达。
* 路由GET /api/v1/admin/ping
*/
class PingController extends Controller
final class PingController extends Controller
{
public function __invoke(): JsonResponse
{

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use App\Models\PlayType;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
/** GET /api/v1/admin/play-types */
final class PlayTypeIndexController extends Controller

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use App\Models\PlayType;
use App\Support\AdminConfigPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminConfigPresenter;
/** PATCH /api/v1/admin/play-types/{play_code} — 主目录层开关与展示名(不等同于版本化 items。 */
final class PlayTypePatchController extends Controller

View File

@@ -2,12 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Player;
use App\Http\Controllers\Controller;
use App\Models\Player;
use App\Models\TicketItem;
use App\Support\ApiResponse;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminPlayerTicketItemsRequest;
/**
* GET /api/v1/admin/players/{player}/ticket-items 客服/财务按玩家查注单PRD §15.4)。
@@ -16,17 +17,16 @@ use Illuminate\Http\Request;
*/
final class AdminPlayerTicketItemsIndexController extends Controller
{
public function __invoke(Request $request, Player $player): JsonResponse
{
$validated = validator($request->query(), [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:50'],
'draw_no' => ['sometimes', 'nullable', 'string', 'max:32'],
])->validate();
use PaginationTrait;
$perPage = max(1, min(50, (int) ($validated['per_page'] ?? 20)));
$page = max(1, (int) ($validated['page'] ?? 1));
$drawNo = isset($validated['draw_no']) ? trim((string) $validated['draw_no']) : '';
public function __invoke(AdminPlayerTicketItemsRequest $request, Player $player): JsonResponse
{
$perPage = $this->perPage($request, 'per_page', 20, 50);
$page = $this->page($request);
$drawNo = $request->validated('draw_no');
if (is_string($drawNo)) {
$drawNo = trim($drawNo);
}
$query = TicketItem::query()
->where('ticket_items.player_id', $player->id)

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Player;
use App\Http\Controllers\Controller;
use App\Models\Player;
use App\Models\PlayerWallet;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* 后台:按玩家查询钱包余额(`player_wallets` 全币种)。

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\ReconcileItem;
use App\Models\ReconcileJob;
use Illuminate\Http\Request;
use App\Models\ReconcileItem;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/reconcile-jobs/{reconcile_job}/items */
final class ReconcileItemIndexController extends Controller

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\ReconcileJob;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/reconcile-jobs */
final class ReconcileJobIndexController extends Controller

View File

@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\ReconcileJob;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/reconcile-jobs/{reconcile_job} */
final class ReconcileJobShowController extends Controller

View File

@@ -2,32 +2,23 @@
namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\Admin\AdminReconcileJobService;
use App\Support\ApiResponse;
use Carbon\Carbon;
use App\Models\AdminUser;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\Admin\AdminReconcileJobService;
use App\Http\Requests\Admin\ReconcileJobStoreRequest;
/** POST /api/v1/admin/reconcile-jobs */
final class ReconcileJobStoreController extends Controller
{
public function __invoke(Request $request, AdminReconcileJobService $service): JsonResponse
public function __invoke(ReconcileJobStoreRequest $request, AdminReconcileJobService $service): JsonResponse
{
/** @var AdminUser $admin */
$admin = $request->lotteryAdmin();
$data = validator($request->all(), [
'reconcile_type' => ['required', 'string', 'max:32'],
'period_start' => ['nullable', 'date'],
'period_end' => ['nullable', 'date', 'after_or_equal:period_start'],
'items' => ['nullable', 'array', 'max:5000'],
'items.*.side_a_ref' => ['nullable', 'string', 'max:128'],
'items.*.side_b_ref' => ['nullable', 'string', 'max:128'],
'items.*.difference_amount' => ['nullable', 'integer'],
'items.*.status' => ['nullable', 'string', 'max:32'],
])->validate();
$data = $request->validated();
$job = $service->createJob(
$admin,

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Reports;
use App\Http\Controllers\Controller;
use App\Models\ReportJob;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/report-jobs */
final class ReportJobIndexController extends Controller

View File

@@ -2,10 +2,10 @@
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;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/report-jobs/{report_job} */
final class ReportJobShowController extends Controller

View File

@@ -2,26 +2,22 @@
namespace App\Http\Controllers\Api\V1\Admin\Reports;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\Admin\AdminReportJobService;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\Admin\AdminReportJobService;
use App\Http\Requests\Admin\ReportJobStoreRequest;
/** POST /api/v1/admin/report-jobs */
final class ReportJobStoreController extends Controller
{
public function __invoke(Request $request, AdminReportJobService $service): JsonResponse
public function __invoke(ReportJobStoreRequest $request, AdminReportJobService $service): JsonResponse
{
/** @var AdminUser $admin */
$admin = $request->lotteryAdmin();
$data = validator($request->all(), [
'report_type' => ['required', 'string', 'max:64'],
'export_format' => ['sometimes', 'string', 'in:csv,xlsx'],
'filter_json' => ['nullable', 'array'],
])->validate();
$data = $request->validated();
$job = $service->enqueue(
$admin,

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Risk;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\RiskPool;
use App\Support\AdminApiList;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* GET /api/v1/admin/draws/{draw}/risk-pools 按期号分页查询赔付池事实(售罄筛选、排序)。

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Admin\Risk;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\RiskPoolLockLog;
use App\Support\AdminApiList;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\RiskPoolLockLog;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* GET /api/v1/admin/draws/{draw}/risk-pool-lock-logs 风险池占用/释放流水(审计与监控)。

View File

@@ -21,7 +21,12 @@ final class AdminRiskPoolShowController extends Controller
public function __invoke(Request $request, Draw $draw, string $number_4d): JsonResponse
{
if (preg_match('/^[0-9]{4}$/', $number_4d) !== 1) {
return ApiResponse::error('normalized_number 须为 4 位数字', ErrorCode::ValidationFailed->value, null, 422);
return ApiResponse::error(
trans('api.validation_failed', [], $request->lotteryLocale()),
ErrorCode::ValidationFailed->value,
null,
422,
);
}
$pool = RiskPool::query()
@@ -30,7 +35,12 @@ final class AdminRiskPoolShowController extends Controller
->first();
if ($pool === null) {
return ApiResponse::error('该期尚无此号码的风险池记录', ErrorCode::NotFound->value, null, 404);
return ApiResponse::error(
trans('api.not_found', [], $request->lotteryLocale()),
ErrorCode::NotFound->value,
null,
404,
);
}
$p = AdminApiList::readPaging($request, 20, AdminApiList::MAX_PER_PAGE);

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\Settlement;
use App\Http\Controllers\Controller;
use App\Models\SettlementBatch;
use App\Models\TicketSettlementDetail;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\SettlementBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Models\TicketSettlementDetail;
/**
* GET /api/v1/admin/settlement-batches/{batch}/details 该批次下注单结算明细分页。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\Settlement;
use App\Http\Controllers\Controller;
use App\Models\SettlementBatch;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use App\Models\SettlementBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/settlement-batches 结算批次分页列表。

View File

@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\V1\Admin\Settlement;
use App\Http\Controllers\Controller;
use App\Models\SettlementBatch;
use App\Support\ApiResponse;
use App\Models\SettlementBatch;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* GET /api/v1/admin/settlement-batches/{batch} 单批次摘要。

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminRole;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
/** GET /api/v1/admin/admin-user-permission-catalog */
final class AdminPermissionCatalogController extends Controller

View File

@@ -2,14 +2,14 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Services\AuditLogger;
use App\Support\AdminUserApiPresenter;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\AuditLogger;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminUserApiPresenter;
/** DELETE /api/v1/admin/admin-users/{admin_user} */
final class AdminUserDestroyController extends Controller

View File

@@ -2,12 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Support\AdminApiList;
use App\Support\AdminUserApiPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminUserApiPresenter;
/** GET /api/v1/admin/admin-users */
final class AdminUserIndexController extends Controller

View File

@@ -2,27 +2,20 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Support\AdminPermissionBridge;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
use App\Support\AdminPermissionBridge;
use App\Http\Requests\Admin\AdminUserPermissionSyncRequest;
/** PUT /api/v1/admin/admin-users/{admin_user}/permissions */
final class AdminUserPermissionSyncController extends Controller
{
public function __invoke(Request $request, AdminUser $admin_user): JsonResponse
public function __invoke(AdminUserPermissionSyncRequest $request, AdminUser $admin_user): JsonResponse
{
/** @var array{permission_slugs:list<string>} $data */
$data = validator($request->all(), [
'permission_slugs' => ['required', 'array'],
'permission_slugs.*' => ['string', 'max:128', 'distinct', Rule::in(AdminPermissionBridge::allLegacySlugs())],
])->validate();
$slugs = array_values(array_unique($data['permission_slugs']));
$slugs = array_values(array_unique($request->validated('permissions')));
$siteId = AdminUser::defaultAdminSiteId();
$codes = [];

View File

@@ -2,25 +2,18 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminUserRoleSyncRequest;
/** PUT /api/v1/admin/admin-users/{admin_user}/roles */
final class AdminUserRoleSyncController extends Controller
{
public function __invoke(Request $request, AdminUser $admin_user): JsonResponse
public function __invoke(AdminUserRoleSyncRequest $request, AdminUser $admin_user): JsonResponse
{
/** @var array{role_slugs:list<string>} $data */
$data = validator($request->all(), [
'role_slugs' => ['required', 'array'],
'role_slugs.*' => ['string', 'max:64', 'distinct', Rule::exists('admin_roles', 'slug')],
])->validate();
$slugs = array_values(array_unique($data['role_slugs']));
$slugs = array_values(array_unique($request->validated('role_slugs')));
$admin_user->syncRoleSlugsForDefaultSite($slugs);
$admin_user->load('roles');

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Support\AdminUserApiPresenter;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminUserApiPresenter;
/** GET /api/v1/admin/admin-users/{admin_user} */
final class AdminUserShowController extends Controller

View File

@@ -2,55 +2,40 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\AuditLogger;
use App\Support\AdminUserApiPresenter;
use App\Support\ApiResponse;
use App\Services\AuditLogger;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Http\Controllers\Controller;
use App\Support\AdminUserApiPresenter;
use App\Http\Requests\Admin\AdminUserStoreRequest;
/** POST /api/v1/admin/admin-users */
/**
* 管理员用户创建。
*
* POST /api/v1/admin/admin-users
*/
final class AdminUserStoreController extends Controller
{
public function __invoke(Request $request): JsonResponse
public function __invoke(AdminUserStoreRequest $request): JsonResponse
{
/** @var AdminUser $actor */
$actor = $request->lotteryAdmin();
$payload = $request->all();
if (isset($payload['username']) && is_string($payload['username'])) {
$payload['username'] = Str::lower(trim($payload['username']));
}
if (array_key_exists('email', $payload) && $payload['email'] === '') {
$payload['email'] = null;
}
$data = validator($payload, [
'username' => ['required', 'string', 'min:2', 'max:64', 'regex:/^[a-zA-Z0-9._-]+$/u', 'unique:admin_users,username'],
'nickname' => ['required', 'string', 'max:128'],
'email' => ['nullable', 'string', 'email', 'max:255'],
'password' => ['required', 'string', 'min:8', 'max:256'],
'status' => ['sometimes', 'integer', 'in:0,1'],
'role_slugs' => ['required', 'array', 'min:1'],
'role_slugs.*' => ['string', 'max:64', 'distinct', 'exists:admin_roles,slug'],
])->validate();
$email = is_string($data['email'] ?? null) && trim($data['email']) !== ''
? trim($data['email'])
$email = is_string($request->validated('email'))
? trim($request->validated('email'))
: null;
$roleSlugs = array_values(array_unique($data['role_slugs']));
$roleSlugs = array_values(array_unique($request->validated('role_slugs')));
$user = DB::transaction(function () use ($data, $email, $roleSlugs): AdminUser {
$user = DB::transaction(function () use ($request, $email, $roleSlugs): AdminUser {
$created = AdminUser::query()->create([
'username' => $data['username'],
'name' => $data['nickname'],
'username' => $request->validated('username'),
'name' => $request->validated('nickname'),
'email' => $email,
'password' => $data['password'],
'status' => array_key_exists('status', $data) ? (int) $data['status'] : 0,
'password' => $request->validated('password'),
'status' => $request->validated('status', 0),
]);
$created->syncRoleSlugsForDefaultSite($roleSlugs);

View File

@@ -2,19 +2,18 @@
namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Services\AuditLogger;
use App\Support\AdminUserApiPresenter;
use App\Support\ApiResponse;
use App\Services\AuditLogger;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
use App\Support\AdminUserApiPresenter;
use App\Http\Requests\Admin\AdminUserUpdateRequest;
/** PUT /api/v1/admin/admin-users/{admin_user} */
final class AdminUserUpdateController extends Controller
{
public function __invoke(Request $request, AdminUser $admin_user): JsonResponse
public function __invoke(AdminUserUpdateRequest $request, AdminUser $admin_user): JsonResponse
{
/** @var AdminUser $actor */
$actor = $request->lotteryAdmin();
@@ -22,18 +21,7 @@ final class AdminUserUpdateController extends Controller
$admin_user->load('roles');
$before = AdminUserApiPresenter::listItem($admin_user);
$payload = $request->all();
if (array_key_exists('email', $payload) && $payload['email'] === '') {
$payload['email'] = null;
}
/** @var array{nickname?:string,email?:?string,password?:?string,status?:int} $data */
$data = validator($payload, [
'nickname' => ['sometimes', 'string', 'max:128'],
'email' => ['sometimes', 'nullable', 'string', 'email', 'max:255', Rule::unique('admin_users', 'email')->ignore($admin_user->id)],
'password' => ['sometimes', 'nullable', 'string', 'min:8', 'max:256'],
'status' => ['sometimes', 'integer', Rule::in([0, 1])],
])->validate();
$data = $request->validated();
$updates = [];
if (array_key_exists('nickname', $data)) {

View File

@@ -2,11 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\Wallet;
use App\Http\Controllers\Controller;
use App\Models\TransferOrder;
use App\Support\ApiResponse;
use App\Models\TransferOrder;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\TransferOrderListRequest;
/**
* 后台:转账单列表(主站 彩票 {@see transfer_orders})。
@@ -25,26 +26,16 @@ use Illuminate\Http\Request;
*/
final class TransferOrderListController extends Controller
{
use PaginationTrait;
private const ALLOWED_STATUS = ['processing', 'success', 'failed', 'pending_reconcile'];
public function __invoke(Request $request): JsonResponse
public function __invoke(TransferOrderListRequest $request): JsonResponse
{
$validated = validator($request->query(), [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
'size' => ['sometimes', 'integer', 'min:1', 'max:100'],
'player_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'player_account' => ['sometimes', 'nullable', 'string', 'max:128'],
'transfer_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'external_ref_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'created_from' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'created_to' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'status' => ['sometimes', 'nullable', 'string', 'max:256'],
])->validate();
$validated = $request->validated();
$perPage = (int) ($validated['per_page'] ?? $validated['size'] ?? 20);
$perPage = min(100, max(1, $perPage));
$page = max(1, (int) ($validated['page'] ?? 1));
$perPage = $this->perPage($request, 'per_page', 20, 100);
$page = $this->page($request);
$query = TransferOrder::query()
->with(['player:id,site_code,site_player_id,username,nickname'])

View File

@@ -2,11 +2,12 @@
namespace App\Http\Controllers\Api\V1\Admin\Wallet;
use App\Http\Controllers\Controller;
use App\Models\WalletTxn;
use App\Support\ApiResponse;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\WalletTransactionListRequest;
/**
* 后台:彩票钱包流水列表 {@see wallet_txns}
@@ -25,27 +26,16 @@ use Illuminate\Http\Request;
*/
final class WalletTransactionListController extends Controller
{
use PaginationTrait;
private const ALLOWED_STATUS = ['posted', 'pending_reconcile'];
public function __invoke(Request $request): JsonResponse
public function __invoke(WalletTransactionListRequest $request): JsonResponse
{
$validated = validator($request->query(), [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
'size' => ['sometimes', 'integer', 'min:1', 'max:100'],
'player_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'player_account' => ['sometimes', 'nullable', 'string', 'max:128'],
'txn_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'external_ref_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'created_from' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'created_to' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'biz_type' => ['sometimes', 'nullable', 'string', 'max:64'],
'status' => ['sometimes', 'nullable', 'string', 'max:128'],
])->validate();
$validated = $request->validated();
$perPage = (int) ($validated['per_page'] ?? $validated['size'] ?? 20);
$perPage = min(100, max(1, $perPage));
$page = max(1, (int) ($validated['page'] ?? 1));
$perPage = $this->perPage($request, 'per_page', 20, 100);
$page = $this->page($request);
$query = WalletTxn::query()
->with(['player:id,site_code,site_player_id,username,nickname'])

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Draw;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Draw\DrawHallSnapshotBuilder;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* 下注大厅:`GET /api/v1/draw/current`
*/
class DrawCurrentController extends Controller
final class DrawCurrentController extends Controller
{
public function __construct(
private readonly DrawHallSnapshotBuilder $snapshot,

View File

@@ -2,18 +2,18 @@
namespace App\Http\Controllers\Api\V1\Draw;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\Draw;
use App\Services\Draw\DrawResultViewService;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Draw\DrawResultViewService;
/**
* `GET /api/v1/draw/results/{draw_no}` 单期详情(便于玩家端[< >]切换)。
*/
class DrawResultShowController extends Controller
final class DrawResultShowController extends Controller
{
public function __construct(
private readonly DrawResultViewService $viewer,

View File

@@ -2,29 +2,32 @@
namespace App\Http\Controllers\Api\V1\Draw;
use App\Models\Draw;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Models\DrawResultBatch;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Lottery\DrawResultBatchStatus;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Services\Draw\DrawResultViewService;
use App\Support\ApiResponse;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* `GET /api/v1/draw/results` 已发布开奖往期(公开;对齐 PRD `/api/v1/results`)。
*/
class DrawResultsIndexController extends Controller
final class DrawResultsIndexController extends Controller
{
use PaginationTrait;
public function __construct(
private readonly DrawResultViewService $viewer,
) {}
public function __invoke(Request $request): JsonResponse
{
$perPage = max(1, min(50, (int) $request->query('size', $request->query('per_page', 15))));
$page = max(1, (int) $request->query('page', 1));
$perPage = $this->perPage($request, 'size', 15, 50);
$page = $this->page($request);
/** @var string|null $bizDate query `business_date` 或旧的 `date` */
$bizDate = $request->query('business_date') ?? $request->query('date');

View File

@@ -2,11 +2,11 @@
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
class HealthController extends Controller
final class HealthController extends Controller
{
/**
* 健康检查Next / 网关探活。路径GET /api/v1/health

View File

@@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api\V1\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotPool;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* `GET /api/v1/jackpot/summary` 当前奖池水位(公开;玩家端开奖区展示)。
*/
class JackpotSummaryController extends Controller
final class JackpotSummaryController extends Controller
{
public function __invoke(Request $request): JsonResponse
{

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Play;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Services\Config\EffectivePlayCatalogService;
use App\Support\ApiResponse;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Config\EffectivePlayCatalogService;
use Illuminate\Database\Eloquent\ModelNotFoundException;
/**
* GET /api/v1/play/effective 当前生效的玩法目录 + 赔率 + 封顶(公开,无需登录)。

View File

@@ -2,10 +2,10 @@
namespace App\Http\Controllers\Api\V1\Player;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* 鉴权自检:返回当前 Token 对应的玩家公开字段(不含密码)。
@@ -15,7 +15,7 @@ use Illuminate\Http\Request;
* 补充字段与 PRD「识别玩家」及前端引导一致`locale` 为当次 API 实际使用的语言(与 `NegotiateLotteryLocale` 一致);
* 时间类为 ISO 8601 字符串,便于 H5 展示与排错。
*/
class MeController extends Controller
final class MeController extends Controller
{
public function __invoke(Request $request): JsonResponse
{

View File

@@ -2,15 +2,15 @@
namespace App\Http\Controllers\Api\V1\Player;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* 无需登录:仅供网关/前端确认「玩家 API 前缀」可达。
* 路由GET /api/v1/player/ping
*/
class PingController extends Controller
final class PingController extends Controller
{
public function __invoke(): JsonResponse
{

View File

@@ -2,20 +2,20 @@
namespace App\Http\Controllers\Api\V1\Ticket;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\Player;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Draw\DrawResultViewService;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Models\TicketCombination;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Draw\DrawResultViewService;
/**
* `GET /api/v1/ticket/draws/{draw_no}/my-match` 当期本人号码与已发布开奖 23 格的交集(用于开奖页高亮)。
*/
class TicketDrawMyMatchController extends Controller
final class TicketDrawMyMatchController extends Controller
{
public function __construct(
private readonly DrawResultViewService $drawResultView,

View File

@@ -2,19 +2,19 @@
namespace App\Http\Controllers\Api\V1\Ticket;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Lottery\ErrorCode;
use App\Models\TicketItem;
use App\Services\Draw\DrawResultViewService;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Services\Draw\DrawResultViewService;
/**
* `GET /api/v1/ticket/items/{ticket_no}` 注单详情(单注项 + 组合 + 结算摘要)。
*/
class TicketItemShowController extends Controller
final class TicketItemShowController extends Controller
{
public function __construct(
private readonly DrawResultViewService $drawResultView,

View File

@@ -2,25 +2,28 @@
namespace App\Http\Controllers\Api\V1\Ticket;
use App\Http\Controllers\Controller;
use App\Models\Player;
use App\Models\TicketItem;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* `GET /api/v1/ticket/items` 我的注单(注项列表,支持 `draw_no` 筛选)。
*/
class TicketItemsIndexController extends Controller
final class TicketItemsIndexController extends Controller
{
use PaginationTrait;
public function __invoke(Request $request): JsonResponse
{
/** @var Player $player */
$player = $request->attributes->get('lottery_player');
$perPage = max(1, min(50, (int) $request->query('per_page', 20)));
$page = max(1, (int) $request->query('page', 1));
$perPage = $this->perPage($request, 'per_page', 20, 50);
$page = $this->page($request);
$drawNo = $request->query('draw_no');
$query = TicketItem::query()

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Ticket;
use App\Exceptions\TicketOperationException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Ticket\TicketPlaceRequest;
use App\Services\Ticket\TicketPlacementService;
use App\Support\ApiResponse;
use App\Support\LotteryMessage;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Exceptions\TicketOperationException;
use App\Services\Ticket\TicketPlacementService;
use App\Http\Requests\Ticket\TicketPlaceRequest;
final class TicketPlaceController extends Controller
{

View File

@@ -2,13 +2,13 @@
namespace App\Http\Controllers\Api\V1\Ticket;
use App\Exceptions\TicketOperationException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Ticket\TicketPreviewRequest;
use App\Services\Ticket\TicketPreviewService;
use App\Support\ApiResponse;
use App\Support\LotteryMessage;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Exceptions\TicketOperationException;
use App\Services\Ticket\TicketPreviewService;
use App\Http\Requests\Ticket\TicketPreviewRequest;
final class TicketPreviewController extends Controller
{

View File

@@ -2,13 +2,14 @@
namespace App\Http\Controllers\Api\V1\Wallet;
use App\Http\Controllers\Controller;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Lottery\ErrorCode;
use App\Models\PlayerWallet;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Support\CurrencyResolver;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* 【玩家】查询彩票侧钱包余额。
@@ -22,7 +23,7 @@ use Illuminate\Http\Request;
* - `available_balance``balance - frozen_balance`,表示当前可用于下注的整数最小货币单位(不为负)
* - `main_balance`:主站钱包余额占位,接入主站 API 后再返回实数;当前固定 `null`
*/
class WalletBalanceController extends Controller
final class WalletBalanceController extends Controller
{
private const WALLET_TYPE_LOTTERY = 'lottery';
@@ -68,16 +69,9 @@ class WalletBalanceController extends Controller
*/
private function resolveCurrencyCode(Request $request, Player $player): string|JsonResponse
{
$raw = $request->query('currency');
if (is_string($raw) && $raw !== '') {
$code = strtoupper(substr(trim($raw), 0, 16));
} else {
$fallback = $player->default_currency ?? config('lottery.default_currency', 'NPR');
$code = strtoupper(substr(trim((string) $fallback), 0, 16));
}
$code = CurrencyResolver::resolve($request, $player, 'currency');
// 币种码:字母数字,长度 116与 migrations 字段一致
if (! preg_match('/^[A-Z0-9]{1,16}$/', $code)) {
if (! CurrencyResolver::isValid($code)) {
return ApiResponse::error(
__('wallet.invalid_currency'),
ErrorCode::WalletInvalidCurrency->value,

View File

@@ -2,21 +2,24 @@
namespace App\Http\Controllers\Api\V1\Wallet;
use App\Http\Controllers\Controller;
use App\Models\TransferOrder;
use App\Models\WalletTxn;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Models\TransferOrder;
use App\Support\PaginationTrait;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
/**
* PRD §10.1.1`GET /api/v1/wallet/logs` 钱包流水。
*
* Query`page``size`(每页条数,默认 20)、`type`逗号分隔transfer_in,transfer_out,bet,prize,refund
*/
class WalletLogsController extends Controller
final class WalletLogsController extends Controller
{
use PaginationTrait;
/** PRD 对外类型 → 本地 biz_type */
private const TYPE_TO_BIZ = [
'transfer_in' => ['transfer_in'],
@@ -31,8 +34,8 @@ class WalletLogsController extends Controller
$player = $request->lotteryPlayer();
abort_if($player === null, 500, 'lottery_player missing');
$perPage = min(100, max(1, (int) $request->query('size', $request->query('per_page', 20))));
$page = max(1, (int) $request->query('page', 1));
$perPage = $this->perPage($request, 'size', 20, 100);
$page = $this->page($request);
$pendingPayload = $this->pendingReconcilePayload((int) $player->id);

View File

@@ -2,23 +2,23 @@
namespace App\Http\Controllers\Api\V1\Wallet;
use App\Exceptions\WalletOperationException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Wallet\WalletTransferRequest;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Services\Wallet\LotteryTransferService;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use App\Support\LotteryMessage;
use App\Support\CurrencyResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Exceptions\WalletOperationException;
use App\Services\Wallet\LotteryTransferService;
use App\Http\Requests\Wallet\WalletTransferRequest;
/**
* 转入:主站扣款 彩票钱包加款。
*
* `POST /api/v1/wallet/transfer-in` body JSON`amount`(最小货币单位整数), `idempotent_key`, `currency`(可选)
*/
class WalletTransferInController extends Controller
final class WalletTransferInController extends Controller
{
public function __construct(
private readonly LotteryTransferService $transferService,
@@ -53,17 +53,11 @@ class WalletTransferInController extends Controller
return ApiResponse::success($data);
}
private function resolveCurrencyCode(Request $request, Player $player): string|JsonResponse
private function resolveCurrencyCode(WalletTransferRequest $request, Player $player): string|JsonResponse
{
$raw = $request->input('currency');
if (is_string($raw) && $raw !== '') {
$code = strtoupper(substr(trim($raw), 0, 16));
} else {
$fallback = $player->default_currency ?? config('lottery.default_currency', 'NPR');
$code = strtoupper(substr(trim((string) $fallback), 0, 16));
}
$code = CurrencyResolver::resolve($request, $player, 'currency');
if (! preg_match('/^[A-Z0-9]{1,16}$/', $code)) {
if (! CurrencyResolver::isValid($code)) {
return ApiResponse::error(
__('wallet.invalid_currency'),
ErrorCode::WalletInvalidCurrency->value,

View File

@@ -2,23 +2,23 @@
namespace App\Http\Controllers\Api\V1\Wallet;
use App\Exceptions\WalletOperationException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Wallet\WalletTransferRequest;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Services\Wallet\LotteryTransferService;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use App\Support\LotteryMessage;
use App\Support\CurrencyResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Exceptions\WalletOperationException;
use App\Services\Wallet\LotteryTransferService;
use App\Http\Requests\Wallet\WalletTransferRequest;
/**
* 转出:彩票钱包扣款 主站加款;主站失败则冲正彩票余额。
*
* `POST /api/v1/wallet/transfer-out`
*/
class WalletTransferOutController extends Controller
final class WalletTransferOutController extends Controller
{
public function __construct(
private readonly LotteryTransferService $transferService,
@@ -53,17 +53,11 @@ class WalletTransferOutController extends Controller
return ApiResponse::success($data);
}
private function resolveCurrencyCode(Request $request, Player $player): string|JsonResponse
private function resolveCurrencyCode(WalletTransferRequest $request, Player $player): string|JsonResponse
{
$raw = $request->input('currency');
if (is_string($raw) && $raw !== '') {
$code = strtoupper(substr(trim($raw), 0, 16));
} else {
$fallback = $player->default_currency ?? config('lottery.default_currency', 'NPR');
$code = strtoupper(substr(trim((string) $fallback), 0, 16));
}
$code = CurrencyResolver::resolve($request, $player, 'currency');
if (! preg_match('/^[A-Z0-9]{1,16}$/', $code)) {
if (! CurrencyResolver::isValid($code)) {
return ApiResponse::error(
__('wallet.invalid_currency'),
ErrorCode::WalletInvalidCurrency->value,

View File

@@ -2,10 +2,10 @@
namespace App\Http\Middleware;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Support\ApiResponse;
use Closure;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
* 后台 API`auth:sanctum` 之后执行,校验为 {@link AdminUser} 且未禁用;
* 上下文可通过 `$request->lotteryAdmin()` 读取。
*/
class EnsureAdminApi
final class EnsureAdminApi
{
public function handle(Request $request, Closure $next): Response
{

View File

@@ -2,10 +2,10 @@
namespace App\Http\Middleware;
use App\Lottery\ErrorCode;
use App\Models\AdminUser;
use App\Support\ApiResponse;
use Closure;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
* 后台 RBAC {@see EnsureAdminApi} 之后校验 `prd.*` 等功能权限 slug {@see AdminUser::hasAdminPermission} 一致)。
* 路由参数支持 `slug` `slug1|slug2`(满足其一即可)。
*/
class EnsureAdminPermission
final class EnsureAdminPermission
{
public function handle(Request $request, Closure $next, string $permissionSlugs): Response
{

View File

@@ -2,14 +2,14 @@
namespace App\Http\Middleware;
use App\Exceptions\PlayerAuthenticationException;
use App\Lottery\ErrorCode;
use App\Services\PlayerTokenResolver;
use App\Support\ApiResponse;
use App\Support\LotteryMessage;
use Closure;
use App\Lottery\ErrorCode;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use App\Support\LotteryMessage;
use App\Services\PlayerTokenResolver;
use Symfony\Component\HttpFoundation\Response;
use App\Exceptions\PlayerAuthenticationException;
/**
* 【玩家端 API 鉴权中间件】
@@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response;
*
* PlayerAuthenticationException getMessage() 仅作开发与日志用语,可与 API msg 语种不一致。
*/
class EnsurePlayerApi
final class EnsurePlayerApi
{
public function handle(Request $request, Closure $next): Response
{

View File

@@ -2,9 +2,9 @@
namespace App\Http\Middleware;
use App\Support\LotteryLocale;
use Closure;
use Illuminate\Http\Request;
use App\Support\LotteryLocale;
use Symfony\Component\HttpFoundation\Response;
/**
@@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response;
*
* 【与异常 JSON】{@see LotteryLocale} middleware 同源,在未命中路由、`lottery_locale` 未写入时仍可从 Header 推导。
*/
class NegotiateLotteryLocale
final class NegotiateLotteryLocale
{
public function handle(Request $request, Closure $next): Response
{

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员登录请求。
*
* @see LoginController
*/
final class AdminLoginRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'account' => ['required', 'string', 'min:2', 'max:64', 'regex:/^[a-zA-Z0-9._-]+$/u'],
'password' => ['required', 'string', 'max:256'],
'captcha_key' => ['required', 'string', 'uuid'],
'captcha_code' => ['required', 'string', 'max:32'],
];
}
/**
* @return array<string, string>
*/
public function attributes(): array
{
return [
'account' => 'account',
'password' => 'password',
'captcha_key' => 'captcha_key',
'captcha_code' => 'captcha_code',
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员查看玩家注单列表请求。
*
* @see AdminPlayerTicketItemsIndexController
*/
final class AdminPlayerTicketItemsRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:50'],
'draw_no' => ['sometimes', 'nullable', 'string', 'max:32'],
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员用户权限同步请求。
*
* @see AdminUserPermissionSyncController
*/
final class AdminUserPermissionSyncRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'permissions' => ['required', 'array'],
'permissions.*' => ['string', 'max:128'],
];
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员用户角色同步请求。
*
* @see AdminUserRoleSyncController
*/
final class AdminUserRoleSyncRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'role_slugs' => ['required', 'array', 'min:1'],
'role_slugs.*' => ['string', 'max:64', 'distinct', 'exists:admin_roles,slug'],
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Support\Str;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员用户创建请求。
*
* @see AdminUserStoreController
*/
final class AdminUserStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Prepare the data for validation.
*/
protected function prepareForValidation(): void
{
$username = $this->input('username');
if (is_string($username)) {
$this->merge([
'username' => Str::lower(trim($username)),
]);
}
if ($this->input('email') === '') {
$this->merge([
'email' => null,
]);
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'username' => ['required', 'string', 'min:2', 'max:64', 'regex:/^[a-zA-Z0-9._-]+$/u', 'unique:admin_users,username'],
'nickname' => ['required', 'string', 'max:128'],
'email' => ['nullable', 'string', 'email', 'max:255'],
'password' => ['required', 'string', 'min:8', 'max:256'],
'status' => ['sometimes', 'integer', 'in:0,1'],
'role_slugs' => ['required', 'array', 'min:1'],
'role_slugs.*' => ['string', 'max:64', 'distinct', 'exists:admin_roles,slug'],
];
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
/**
* 管理员用户更新请求。
*
* @see AdminUserUpdateController
*/
final class AdminUserUpdateRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
protected function prepareForValidation(): void
{
if ($this->input('email') === '') {
$this->merge(['email' => null]);
}
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
$adminUser = $this->route('admin_user');
return [
'nickname' => ['sometimes', 'string', 'max:128'],
'email' => ['sometimes', 'nullable', 'string', 'email', 'max:255', Rule::unique('admin_users', 'email')->ignore($adminUser?->id)],
'password' => ['sometimes', 'nullable', 'string', 'min:8', 'max:256'],
'status' => ['sometimes', 'integer', Rule::in([0, 1])],
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 对账任务创建请求。
*
* @see ReconcileJobStoreController
*/
final class ReconcileJobStoreRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'date_from' => ['required', 'date_format:Y-m-d'],
'date_to' => ['required', 'date_format:Y-m-d', 'after_or_equal:date_from'],
'player_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 报表任务创建请求。
*
* @see ReportJobStoreController
*/
final class ReportJobStoreRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'report_type' => ['required', 'string', 'max:64'],
'parameters' => ['sometimes', 'array'],
'parameters.date_from' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'parameters.date_to' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 转账单列表查询请求。
*
* @see TransferOrderListController
*/
final class TransferOrderListRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
'size' => ['sometimes', 'integer', 'min:1', 'max:100'],
'player_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'player_account' => ['sometimes', 'nullable', 'string', 'max:128'],
'transfer_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'external_ref_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'created_from' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'created_to' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'status' => ['sometimes', 'nullable', 'string', 'max:256'],
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
/**
* 钱包流水列表查询请求。
*
* @see WalletTransactionListController
*/
final class WalletTransactionListRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, mixed>>
*/
public function rules(): array
{
return [
'page' => ['sometimes', 'integer', 'min:1'],
'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'],
'size' => ['sometimes', 'integer', 'min:1', 'max:100'],
'player_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'player_account' => ['sometimes', 'nullable', 'string', 'max:128'],
'txn_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'external_ref_no' => ['sometimes', 'nullable', 'string', 'max:96'],
'created_from' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'created_to' => ['sometimes', 'nullable', 'date_format:Y-m-d'],
'biz_type' => ['sometimes', 'nullable', 'string', 'max:64'],
'status' => ['sometimes', 'nullable', 'string', 'max:128'],
];
}
}

View File

@@ -2,7 +2,7 @@
namespace App\Http\Requests\Ticket;
class TicketPlaceRequest extends TicketPreviewRequest
final class TicketPlaceRequest extends TicketPreviewRequest
{
/**
* @return array<string, mixed>

View File

@@ -4,7 +4,7 @@ namespace App\Http\Requests\Ticket;
use Illuminate\Foundation\Http\FormRequest;
class TicketPreviewRequest extends FormRequest
final class TicketPreviewRequest extends FormRequest
{
public function authorize(): bool
{

View File

@@ -7,7 +7,7 @@ use Illuminate\Foundation\Http\FormRequest;
/**
* 转入 / 转出共用请求体:最小货币单位金额、幂等键、可选币种。
*/
class WalletTransferRequest extends FormRequest
final class WalletTransferRequest extends FormRequest
{
public function authorize(): bool
{

View File

@@ -4,7 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AdminMenu extends Model
final class AdminMenu extends Model
{
protected $table = 'admin_menus';
}

View File

@@ -5,7 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AdminMenuAction extends Model
final class AdminMenuAction extends Model
{
protected $table = 'admin_menu_actions';

View File

@@ -2,18 +2,18 @@
namespace App\Models;
use Illuminate\Support\Facades\DB;
use App\Support\AdminPermissionBridge;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Facades\DB;
class AdminRole extends Model
final class AdminRole extends Model
{
protected $table = 'admin_roles';
protected static function booted(): void
{
static::creating(function (AdminRole $role): void {
self::creating(function (AdminRole $role): void {
if (($role->code ?? '') === '' && is_string($role->slug) && $role->slug !== '') {
$role->code = $role->slug;
}

View File

@@ -2,14 +2,14 @@
namespace App\Models;
use App\Support\AdminPermissionBridge;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\DB;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Facades\DB;
use App\Support\AdminPermissionBridge;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class AdminUser extends Authenticatable
final class AdminUser extends Authenticatable
{
use HasApiTokens;
use Notifiable;

View File

@@ -6,7 +6,7 @@ use App\Services\AuditLogger;
use Illuminate\Database\Eloquent\Model;
/** 运维/后台动作留痕表 audit_logs写入请走 {@see AuditLogger})。 */
class AuditLog extends Model
final class AuditLog extends Model
{
public const UPDATED_AT = null;

View File

@@ -5,7 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/** 币种枚举表 {@see currencies} */
class Currency extends Model
final class Currency extends Model
{
protected $fillable = [
'code',

View File

@@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/** 彩票期号 {@see draws} */
class Draw extends Model
final class Draw extends Model
{
protected $fillable = [
'draw_no',

View File

@@ -4,11 +4,11 @@ namespace App\Models;
use App\Lottery\DrawResultBatchStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 开奖结果批次(含 RNG 种子摘要、审核人) {@see draw_result_batches} */
class DrawResultBatch extends Model
final class DrawResultBatch extends Model
{
public $timestamps = true;

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 单条中奖号码行(与界面文档奖项分区对应) {@see draw_result_items} */
class DrawResultItem extends Model
final class DrawResultItem extends Model
{
public const UPDATED_AT = null;

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 单注对 Jackpot 的蓄水 {@see jackpot_contributions} */
class JackpotContribution extends Model
final class JackpotContribution extends Model
{
protected $fillable = [
'jackpot_pool_id',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** Jackpot 爆池派发记录 {@see jackpot_payout_logs} */
class JackpotPayoutLog extends Model
final class JackpotPayoutLog extends Model
{
public const UPDATED_AT = null;

View File

@@ -3,11 +3,11 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** Jackpot 奖池配置与当前水位 {@see jackpot_pools} */
class JackpotPool extends Model
final class JackpotPool extends Model
{
protected $fillable = [
'currency_code',

View File

@@ -6,7 +6,7 @@ use App\Services\LotterySettings;
use Illuminate\Database\Eloquent\Model;
/** 运行期 KV 配置行,对应表 lottery_settings读业务请优先用 {@see LotterySettings})。 */
class LotterySetting extends Model
final class LotterySetting extends Model
{
protected $fillable = [
'setting_key',

View File

@@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*
* `odds_value`:整数,**赔率乘数 × 10000**(例如 19500 表示 1.9500 倍)。
*/
class OddsItem extends Model
final class OddsItem extends Model
{
protected $fillable = [
'version_id',

View File

@@ -4,11 +4,11 @@ namespace App\Models;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** {@see odds_versions} */
class OddsVersion extends Model
final class OddsVersion extends Model
{
protected $fillable = [
'version_no',
@@ -29,7 +29,7 @@ class OddsVersion extends Model
protected static function booted(): void
{
static::saving(function (OddsVersion $m): void {
self::saving(function (OddsVersion $m): void {
if ($m->status === null || $m->status === '') {
$m->status = ConfigVersionStatus::Draft->value;
}

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** {@see play_config_items} — 与 {@see PlayType::play_code} 对齐。 */
class PlayConfigItem extends Model
final class PlayConfigItem extends Model
{
protected $fillable = [
'version_id',

View File

@@ -4,11 +4,11 @@ namespace App\Models;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** {@see play_config_versions} */
class PlayConfigVersion extends Model
final class PlayConfigVersion extends Model
{
protected $fillable = [
'version_no',
@@ -29,7 +29,7 @@ class PlayConfigVersion extends Model
protected static function booted(): void
{
static::saving(function (PlayConfigVersion $m): void {
self::saving(function (PlayConfigVersion $m): void {
if ($m->status === null || $m->status === '') {
$m->status = ConfigVersionStatus::Draft->value;
}

View File

@@ -5,7 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/** 玩法主数据目录 {@see play_types}(与版本化配置 items 通过 play_code 关联)。 */
class PlayType extends Model
final class PlayType extends Model
{
protected $fillable = [
'play_code',

View File

@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* 主站玩家在本地映射账号(表 players SSO JWT site_code + site_player_id 对应。
*/
class Player extends Model
final class Player extends Model
{
protected $fillable = [
'site_code',

View File

@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* 彩票侧余额(表 player_wallets player + wallet_type + currency 唯一。
*/
class PlayerWallet extends Model
final class PlayerWallet extends Model
{
protected $fillable = [
'player_id',

View File

@@ -5,7 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ReconcileItem extends Model
final class ReconcileItem extends Model
{
protected $table = 'reconcile_items';

View File

@@ -3,10 +3,10 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ReconcileJob extends Model
final class ReconcileJob extends Model
{
protected $table = 'reconcile_jobs';

View File

@@ -5,7 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ReportJob extends Model
final class ReportJob extends Model
{
protected $table = 'report_jobs';

View File

@@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*
* `cap_amount`:与钱包一致的最小货币单位(整数)。
*/
class RiskCapItem extends Model
final class RiskCapItem extends Model
{
protected $fillable = [
'version_id',

View File

@@ -4,11 +4,11 @@ namespace App\Models;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** {@see risk_cap_versions} */
class RiskCapVersion extends Model
final class RiskCapVersion extends Model
{
protected $fillable = [
'version_no',
@@ -29,7 +29,7 @@ class RiskCapVersion extends Model
protected static function booted(): void
{
static::saving(function (RiskCapVersion $m): void {
self::saving(function (RiskCapVersion $m): void {
if ($m->status === null || $m->status === '') {
$m->status = ConfigVersionStatus::Draft->value;
}

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 赔付池事实表 {@see risk_pools} */
class RiskPool extends Model
final class RiskPool extends Model
{
protected $fillable = [
'draw_id',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 风险池占用日志 {@see risk_pool_lock_logs} */
class RiskPoolLockLog extends Model
final class RiskPoolLockLog extends Model
{
public $timestamps = false;

View File

@@ -4,11 +4,11 @@ namespace App\Models;
use App\Lottery\SettlementBatchStatus;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 单期单次结算批次 {@see settlement_batches} */
class SettlementBatch extends Model
final class SettlementBatch extends Model
{
protected $fillable = [
'draw_id',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 注项展开组合 {@see ticket_combinations} */
class TicketCombination extends Model
final class TicketCombination extends Model
{
public $timestamps = false;

View File

@@ -3,12 +3,12 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 注项明细 {@see ticket_items} */
class TicketItem extends Model
final class TicketItem extends Model
{
protected $fillable = [
'ticket_no',

View File

@@ -3,11 +3,11 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 下注订单头 {@see ticket_orders} */
class TicketOrder extends Model
final class TicketOrder extends Model
{
protected $fillable = [
'order_no',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 注单项结算明细 {@see ticket_settlement_details} */
class TicketSettlementDetail extends Model
final class TicketSettlementDetail extends Model
{
protected $fillable = [
'settlement_batch_id',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 主站 ↔ 彩票划转订单 {@see transfer_orders} */
class TransferOrder extends Model
final class TransferOrder extends Model
{
protected $fillable = [
'transfer_no',

View File

@@ -6,7 +6,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/** 彩票钱包流水 {@see wallet_txns} */
class WalletTxn extends Model
final class WalletTxn extends Model
{
protected $fillable = [
'txn_no',

View File

@@ -2,17 +2,17 @@
namespace App\Providers;
use App\Models\AdminUser;
use App\Models\Player;
use App\Services\Wallet\HttpMainSiteWalletGateway;
use App\Services\Wallet\MainSiteWalletGateway;
use App\Services\Wallet\StubMainSiteWalletGateway;
use Illuminate\Cache\RateLimiting\Limit;
use App\Models\AdminUser;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use App\Services\Wallet\MainSiteWalletGateway;
use App\Services\Wallet\HttpMainSiteWalletGateway;
use App\Services\Wallet\StubMainSiteWalletGateway;
class AppServiceProvider extends ServiceProvider
final class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.

View File

@@ -2,16 +2,16 @@
namespace App\Services\Admin;
use App\Lottery\DrawResultBatchStatus;
use App\Models\AdminUser;
use Carbon\Carbon;
use App\Models\Draw;
use App\Models\RiskPool;
use App\Models\SettlementBatch;
use App\Models\AdminUser;
use App\Models\TicketItem;
use App\Models\TicketOrder;
use App\Models\TransferOrder;
use App\Models\SettlementBatch;
use App\Lottery\DrawResultBatchStatus;
use App\Services\Draw\DrawHallSnapshotBuilder;
use Carbon\Carbon;
/**
* 后台首页仪表盘:聚合大厅快照、当期财务、期号面板、风控摘要、异常转账计数。

View File

@@ -2,14 +2,14 @@
namespace App\Services\Admin;
use App\Models\AdminUser;
use App\Models\ReconcileItem;
use App\Models\ReconcileJob;
use App\Services\AuditLogger;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\AdminUser;
use Illuminate\Support\Str;
use App\Models\ReconcileJob;
use Illuminate\Http\Request;
use App\Models\ReconcileItem;
use App\Services\AuditLogger;
use Illuminate\Support\Facades\DB;
/**
* 对账任务:落库 `reconcile_jobs` / `reconcile_items`(阶段 7;差异引擎可后续替换)。

View File

@@ -4,10 +4,10 @@ namespace App\Services\Admin;
use App\Models\AdminUser;
use App\Models\ReportJob;
use App\Services\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Services\AuditLogger;
use Illuminate\Support\Facades\DB;
/**
* 报表导出任务:落库 `report_jobs`(阶段 7;异步生成可后续接队列)。

View File

@@ -2,13 +2,13 @@
namespace App\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cache;
/**
* 后台登录图形验证码SVG 产出 + Cache 短时保存答案摘要(单行文本,便于前台用 img[src=data:...] 展示)。
*/
class AdminCaptchaService
final class AdminCaptchaService
{
private const PREFIX = 'admin_captcha:';

View File

@@ -2,9 +2,9 @@
namespace App\Services;
use App\Models\AdminUser;
use App\Models\AuditLog;
use App\Models\Player;
use App\Models\AuditLog;
use App\Models\AdminUser;
use Illuminate\Http\Request;
/**

View File

@@ -2,16 +2,16 @@
namespace App\Services\Config;
use App\Lottery\ConfigVersionStatus;
use App\Models\Currency;
use App\Models\OddsItem;
use App\Models\OddsVersion;
use App\Models\PlayConfigItem;
use App\Models\PlayConfigVersion;
use App\Models\PlayType;
use App\Models\OddsVersion;
use App\Models\RiskCapItem;
use App\Models\PlayConfigItem;
use App\Models\RiskCapVersion;
use App\Models\PlayConfigVersion;
use Illuminate\Support\Collection;
use App\Lottery\ConfigVersionStatus;
/**
* 玩家端:当前生效的玩法目录 + 三套版本快照(只读)。

View File

@@ -2,17 +2,17 @@
namespace App\Services\Config;
use App\Lottery\ConfigVersionStatus;
use App\Models\AdminUser;
use App\Models\Currency;
use App\Models\OddsItem;
use App\Models\OddsVersion;
use App\Models\PlayType;
use App\Services\AuditLogger;
use App\Support\OddsStandardScopes;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use App\Models\AdminUser;
use App\Models\OddsVersion;
use Illuminate\Http\Request;
use App\Services\AuditLogger;
use Illuminate\Support\Facades\DB;
use App\Support\OddsStandardScopes;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/** 后台:赔率版本({@see odds_versions} / {@see odds_items} */
final class OddsStreamService

View File

@@ -2,15 +2,15 @@
namespace App\Services\Config;
use App\Lottery\ConfigVersionStatus;
use App\Models\PlayType;
use App\Models\AdminUser;
use Illuminate\Http\Request;
use App\Services\AuditLogger;
use App\Models\PlayConfigItem;
use App\Models\PlayConfigVersion;
use App\Models\PlayType;
use App\Services\AuditLogger;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/** 后台:玩法配置版本({@see play_config_versions} / {@see play_config_items} */
final class PlayConfigStreamService

View File

@@ -2,14 +2,14 @@
namespace App\Services\Config;
use App\Lottery\ConfigVersionStatus;
use App\Models\AdminUser;
use App\Models\RiskCapItem;
use App\Models\RiskCapVersion;
use App\Services\AuditLogger;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use App\Services\AuditLogger;
use App\Models\RiskCapVersion;
use Illuminate\Support\Facades\DB;
use App\Lottery\ConfigVersionStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/** 后台:风控封顶版本({@see risk_cap_versions} / {@see risk_cap_items} */
final class RiskCapStreamService

View File

@@ -2,12 +2,12 @@
namespace App\Services\Draw;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Models\DrawResultItem;
use Carbon\Carbon;
use App\Models\Draw;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use App\Lottery\DrawResultBatchStatus;
/**
* `GET draw/current` 与大厅 WS 快照共用数据结构。

View File

@@ -2,11 +2,11 @@
namespace App\Services\Draw;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use Carbon\Carbon;
use Illuminate\Database\QueryException;
use App\Models\Draw;
use App\Lottery\DrawStatus;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\QueryException;
/**
* 按计划生成未来的 `draws` 行(期号、时间表)。

View File

@@ -2,12 +2,12 @@
namespace App\Services\Draw;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\DrawStatus;
use App\Models\AdminUser;
use App\Models\Draw;
use App\Models\AdminUser;
use App\Lottery\DrawStatus;
use App\Models\DrawResultBatch;
use Illuminate\Support\Facades\DB;
use App\Lottery\DrawResultBatchStatus;
/**
* 人工审核通过后发布结果;或 RNG 自动生成路径内联调用同一事务字段更新。

View File

@@ -2,13 +2,13 @@
namespace App\Services\Draw;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use App\Models\DrawResultBatch;
use Illuminate\Support\Collection;
use App\Lottery\DrawResultBatchStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* 将已发布的 {@see DrawResultItem} 聚合成前端/文档约定结构。

View File

@@ -2,14 +2,14 @@
namespace App\Services\Draw;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\DrawResultSourceType;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Models\DrawResultItem;
use Carbon\Carbon;
use App\Models\Draw;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use Illuminate\Support\Facades\DB;
use App\Lottery\DrawResultSourceType;
use App\Lottery\DrawResultBatchStatus;
/**
* 按配置执行 RNG写入 {@see DrawResultBatch} / {@see DrawResultItem}

View File

@@ -2,11 +2,11 @@
namespace App\Services\Draw;
use App\Lottery\DrawStatus;
use Carbon\Carbon;
use App\Models\Draw;
use App\Lottery\DrawStatus;
use App\Services\LotterySettings;
use App\Services\Settlement\SettlementOrchestrator;
use Carbon\Carbon;
/**
* 每分钟调度:期号状态推进 RNG若到期号 冷静期结束时进入结算态 补齐未来缓冲。

View File

@@ -3,8 +3,8 @@
namespace App\Services\Draw;
use App\Events\DrawCountdownBroadcast;
use App\Events\DrawResultPublishedBroadcast;
use App\Events\DrawStatusChangeBroadcast;
use App\Events\DrawResultPublishedBroadcast;
/**
* 对齐界面文档 §2.1`draw.countdown``draw.status_change``result.published`(频道 `lottery-hall`)。

View File

@@ -2,11 +2,11 @@
namespace App\Services\Jackpot;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use App\Models\JackpotPayoutLog;
use App\Models\JackpotPool;
use App\Models\TicketItem;
use App\Lottery\DrawStatus;
use App\Models\JackpotPool;
use App\Models\JackpotPayoutLog;
use Illuminate\Support\Collection;
/**

View File

@@ -3,9 +3,9 @@
namespace App\Services\Jackpot;
use App\Models\Draw;
use App\Models\JackpotContribution;
use App\Models\JackpotPool;
use App\Models\TicketItem;
use App\Models\JackpotPool;
use App\Models\JackpotContribution;
/**
* 产品文档 §5.11.1:每笔有效注单按比例蓄水(在下注成功路径调用,非结算)。

View File

@@ -2,13 +2,13 @@
namespace App\Services;
use App\Exceptions\PlayerAuthenticationException;
use App\Lottery\ErrorCode;
use App\Models\Player;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Illuminate\Database\QueryException;
use App\Models\Player;
use App\Lottery\ErrorCode;
use Illuminate\Http\Request;
use Illuminate\Database\QueryException;
use App\Exceptions\PlayerAuthenticationException;
/**
* 从请求头解析玩家身份,返回已落库的 {@see Player}

View File

@@ -2,10 +2,10 @@
namespace App\Services\Settlement\Contracts;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\PublishedDrawResultBoard;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\PublishedDrawResultBoard;
interface SettlementPlayMatcher
{

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/**
* Big / 包号展开类:命中 23 档中**最优档**计奖(产品文档 Big / iBox / mBox / Box

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/**
* head / tail / odd / even / digit_big / digit_small展开组合中若有与**头奖 4D** 完全一致则中奖(赔率档 first

View File

@@ -3,9 +3,9 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\PublishedDrawResultBoard;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/**
* 阶段 6 首轮未实现的玩法:不派奖(后续补位置类、单双等匹配器)。

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_2abc后二位命中头/二/三任意一档。 */
final class Pos2AbcSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_2a / pos_2b / pos_2c后二位命中对应档。 */
final class Pos2TierSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_3abc后三位命中头/二/三任意一档;取最优档赔率。 */
final class Pos3AbcSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_3a / pos_3b / pos_3c后三位命中对应档。头奖命中时 `matched_prize_tier` 为 firstJackpot 口径)。 */
final class Pos3TierSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_4a / pos_4b / pos_4c与对应档完整 4D 一致。 */
final class Pos4ExactTierSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** pos_4d特别奖/ pos_4e安慰奖命中任意一组即中奖。 */
final class Pos4ListTierSettlementMatcher implements SettlementPlayMatcher

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/**
* Small仅头 / / 三奖(产品文档 Small

View File

@@ -2,12 +2,12 @@
namespace App\Services\Settlement\Matchers;
use App\Models\TicketCombination;
use App\Models\TicketItem;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use Illuminate\Support\Collection;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/**
* 直选类:仅与**头奖**号码完全一致中奖(产品文档 Straight / 头奖口径)。

View File

@@ -2,18 +2,18 @@
namespace App\Services\Settlement;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Services\Settlement\Matchers\BigSpreadSettlementMatcher;
use App\Services\Settlement\Matchers\FirstPrizeComboSettlementMatcher;
use App\Services\Settlement\Matchers\NoopSettlementMatcher;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
use App\Services\Settlement\Matchers\Pos2AbcSettlementMatcher;
use App\Services\Settlement\Matchers\Pos2TierSettlementMatcher;
use App\Services\Settlement\Matchers\Pos3AbcSettlementMatcher;
use App\Services\Settlement\Matchers\Pos2TierSettlementMatcher;
use App\Services\Settlement\Matchers\Pos3TierSettlementMatcher;
use App\Services\Settlement\Matchers\Pos4ExactTierSettlementMatcher;
use App\Services\Settlement\Matchers\Pos4ListTierSettlementMatcher;
use App\Services\Settlement\Matchers\BigSpreadSettlementMatcher;
use App\Services\Settlement\Matchers\SmallSpreadSettlementMatcher;
use App\Services\Settlement\Matchers\Pos4ListTierSettlementMatcher;
use App\Services\Settlement\Matchers\StraightLikeSettlementMatcher;
use App\Services\Settlement\Matchers\Pos4ExactTierSettlementMatcher;
use App\Services\Settlement\Matchers\FirstPrizeComboSettlementMatcher;
final class SettlementMatcherRegistry
{

View File

@@ -2,22 +2,22 @@
namespace App\Services\Settlement;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\DrawStatus;
use App\Lottery\SettlementBatchStatus;
use App\Models\Draw;
use App\Models\DrawResultBatch;
use App\Models\DrawResultItem;
use App\Models\JackpotPool;
use App\Models\Player;
use App\Models\SettlementBatch;
use App\Models\TicketItem;
use App\Lottery\DrawStatus;
use App\Models\JackpotPool;
use App\Models\TicketOrder;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use App\Models\SettlementBatch;
use Illuminate\Support\Facades\DB;
use App\Lottery\DrawResultBatchStatus;
use App\Lottery\SettlementBatchStatus;
use App\Models\TicketSettlementDetail;
use App\Services\Jackpot\JackpotBurstAllocator;
use App\Services\Ticket\RiskPoolService;
use App\Services\Ticket\TicketWalletService;
use Illuminate\Support\Facades\DB;
use App\Services\Jackpot\JackpotBurstAllocator;
/**
* 阶段 6:对已发布开奖、处于 `settling` 的期号执行结算(匹配 回水派彩调整 Jackpot 爆池分配 明细 风险池释放 入账)。

View File

@@ -2,8 +2,8 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\ErrorCode;
use App\Exceptions\TicketOperationException;
final class NumberNormalizer
{

View File

@@ -2,17 +2,17 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\ConfigVersionStatus;
use App\Lottery\ErrorCode;
use App\Models\OddsItem;
use App\Models\OddsVersion;
use App\Models\PlayConfigItem;
use App\Models\PlayConfigVersion;
use App\Models\PlayType;
use App\Lottery\ErrorCode;
use App\Models\OddsVersion;
use App\Models\RiskCapItem;
use App\Models\PlayConfigItem;
use App\Models\RiskCapVersion;
use App\Models\PlayConfigVersion;
use Illuminate\Support\Collection;
use App\Lottery\ConfigVersionStatus;
use App\Exceptions\TicketOperationException;
final class PlayCatalogResolver
{

View File

@@ -2,12 +2,12 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\ErrorCode;
use App\Models\OddsItem;
use App\Models\PlayConfigItem;
use App\Models\PlayType;
use App\Lottery\ErrorCode;
use App\Models\PlayConfigItem;
use Illuminate\Support\Collection;
use App\Exceptions\TicketOperationException;
final class PlayRuleEngine
{

View File

@@ -2,11 +2,11 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\ErrorCode;
use App\Models\RiskPool;
use App\Models\RiskPoolLockLog;
use App\Lottery\ErrorCode;
use App\Models\TicketItem;
use App\Models\RiskPoolLockLog;
use App\Exceptions\TicketOperationException;
final class RiskPoolService
{

View File

@@ -2,16 +2,16 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\DrawStatus;
use App\Lottery\ErrorCode;
use App\Models\Draw;
use App\Models\Player;
use App\Models\TicketCombination;
use App\Lottery\ErrorCode;
use App\Models\TicketItem;
use App\Lottery\DrawStatus;
use App\Models\TicketOrder;
use App\Services\Jackpot\JackpotContributionService;
use App\Models\TicketCombination;
use Illuminate\Support\Facades\DB;
use App\Exceptions\TicketOperationException;
use App\Services\Jackpot\JackpotContributionService;
final class TicketPlacementService
{

View File

@@ -2,10 +2,10 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\DrawStatus;
use App\Lottery\ErrorCode;
use App\Models\Draw;
use App\Lottery\ErrorCode;
use App\Lottery\DrawStatus;
use App\Exceptions\TicketOperationException;
final class TicketPreviewService
{

View File

@@ -2,12 +2,12 @@
namespace App\Services\Ticket;
use App\Exceptions\TicketOperationException;
use App\Lottery\ErrorCode;
use App\Models\Player;
use App\Models\PlayerWallet;
use App\Models\TicketOrder;
use App\Models\WalletTxn;
use App\Lottery\ErrorCode;
use App\Models\TicketOrder;
use App\Models\PlayerWallet;
use App\Exceptions\TicketOperationException;
final class TicketWalletService
{

View File

@@ -3,8 +3,8 @@
namespace App\Services\Wallet;
use App\Models\Player;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Facades\Http;
use GuzzleHttp\Exception\ConnectException;
/**
* 通过 HTTP 调用主站钱包 API路径见 config lottery.main_site.wallet_*_path

View File

@@ -2,17 +2,17 @@
namespace App\Services\Wallet;
use App\Exceptions\WalletOperationException;
use App\Lottery\ErrorCode;
use App\Models\Currency;
use App\Models\Player;
use App\Models\Currency;
use App\Models\WalletTxn;
use App\Lottery\ErrorCode;
use Illuminate\Support\Str;
use App\Models\PlayerWallet;
use App\Models\TransferOrder;
use App\Models\WalletTxn;
use App\Services\LotterySettings;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Database\QueryException;
use App\Exceptions\WalletOperationException;
/**
* 主站 彩票钱包:转入 / 转出(幂等键 + 流水 + 订单)。

View File

@@ -2,9 +2,9 @@
namespace App\Support;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* 后台标准列表:`items` + `meta`current_page / per_page / total / last_page

View File

@@ -3,12 +3,12 @@
namespace App\Support;
use App\Models\OddsItem;
use App\Models\OddsVersion;
use App\Models\PlayConfigItem;
use App\Models\PlayConfigVersion;
use App\Models\PlayType;
use App\Models\OddsVersion;
use App\Models\RiskCapItem;
use App\Models\PlayConfigItem;
use App\Models\RiskCapVersion;
use App\Models\PlayConfigVersion;
/** 后台 API阶段 4 运营配置序列化(与其它 Admin*Controller 手写数组风格一致)。 */
final class AdminConfigPresenter

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Support;
use App\Lottery\ErrorCode;
use Illuminate\Http\Request;
/**
* 【管理后台业务文案翻译辅助类】
*
* lang/{locale}/admin.php 等语言包取字符串,供 JSON 里「msg」字段使用。
* LotteryMessage 对应,专门处理管理端错误消息。
*/
final class AdminMessage
{
/**
* 取通用管理后台错误的用户可见文案lang/{locale}/admin.php
*
* @param string $key 语言包键名,如 'unauthenticated', 'permission_denied'
*/
public static function get(Request $request, string $key): string
{
$fallback = (string) config('lottery.locales.fallback', 'en');
$locale = (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request));
$fullKey = 'admin.'.$key;
$msg = trans($fullKey, [], $locale);
if ($msg !== $fullKey) {
return $msg;
}
return trans($fullKey, [], $fallback);
}
/**
* 取错误码对应的用户可见文案。
*
* @param int $code {@see ErrorCode} 管理端段81108114
*/
public static function errorCode(Request $request, int $code): string
{
$fallback = (string) config('lottery.locales.fallback', 'en');
$locale = (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request));
$key = 'admin.'.$code;
$msg = trans($key, [], $locale);
if ($msg !== $key) {
return $msg;
}
return trans($key, [], $fallback);
}
}

View File

@@ -2,12 +2,13 @@
namespace App\Support;
use App\Lottery\ErrorCode;
use Illuminate\Http\JsonResponse;
/**
* 对外 API 统一 JSON 结构:{ code, msg, data }
*
* - `code=0` {@see \App\Lottery\ErrorCode::Success};非 0 `ErrorCode` docs/04 §10。
* - `code=0` {@see ErrorCode::Success};非 0 `ErrorCode` docs/04 §10。
* - error() HTTP 状态可与 code 独立(如鉴权失败 401 + code 8001)。
*/
final class ApiResponse

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace App\Support;
use App\Models\Player;
use Illuminate\Http\Request;
/**
* 币种码解析工具。
*
* 统一处理 Query/Body 中的 currency 参数,支持回退到玩家默认币种或全局配置。
*/
final class CurrencyResolver
{
/**
* 从请求或玩家/配置回退解析币种码。
*
* @param Request $request 请求对象
* @param Player|null $player 玩家对象(用于回退)
* @param string $key 参数名(默认 currency
* @param string|null $default 默认币种码(不传则使用 player->default_currency config
*/
public static function resolve(
Request $request,
?Player $player = null,
string $key = 'currency',
?string $default = null,
): string {
$raw = $request->input($key) ?? $request->query($key);
if (is_string($raw) && $raw !== '') {
$code = strtoupper(substr(trim($raw), 0, 16));
} else {
$fallback = $default
?? $player?->default_currency
?? config('lottery.default_currency', 'NPR');
$code = strtoupper(substr(trim((string) $fallback), 0, 16));
}
return $code;
}
/**
* 验证币种码格式是否合法。
*
* 合法格式1-16 位字母数字,全大写。
*/
public static function isValid(string $code): bool
{
return preg_match('/^[A-Z0-9]{1,16}$/', $code) === 1;
}
/**
* 解析并验证币种码,无效时返回 null
*/
public static function resolveOrNull(
Request $request,
?Player $player = null,
string $key = 'currency',
?string $default = null,
): ?string {
$code = self::resolve($request, $player, $key, $default);
return self::isValid($code) ? $code : null;
}
}

View File

@@ -4,8 +4,8 @@ namespace App\Support;
use App\Models\Currency;
use App\Models\OddsItem;
use App\Models\OddsVersion;
use App\Models\PlayType;
use App\Models\OddsVersion;
use Illuminate\Support\Facades\DB;
/**

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Support;
use Illuminate\Http\Request;
/**
* 分页请求参数解析 trait
*
* 统一处理 `page``per_page`/`size` 参数的合法化。
*/
trait PaginationTrait
{
/**
* 解析并限制每页条数。
*
* @param Request $request 请求对象
* @param string $key 参数名(默认 per_page
* @param int $default 默认值
* @param int $max 最大值上限
*/
protected function perPage(Request $request, string $key = 'per_page', int $default = 20, int $max = 50): int
{
$value = (int) $request->query($key, $request->query('size', $default));
return max(1, min($max, $value));
}
/**
* 解析当前页码(强制至少为 1)。
*/
protected function page(Request $request, string $key = 'page', int $default = 1): int
{
return max(1, (int) $request->query($key, $default));
}
/**
* 解析分页元数据数组。
*
* @return array{page: int, per_page: int}
*/
protected function paginationMeta(Request $request, int $defaultPerPage = 20, int $maxPerPage = 50): array
{
return [
'page' => $this->page($request),
'per_page' => $this->perPage($request, 'per_page', $defaultPerPage, $maxPerPage),
];
}
}