refactor:用 AdminApiList 统一后台列表类接口的响应格式

This commit is contained in:
2026-05-13 11:36:24 +08:00
parent edd863764b
commit 5d2dbdbe1d
29 changed files with 622 additions and 293 deletions

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Audit;
use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,8 +15,7 @@ final class AuditLogIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$module = trim((string) $request->query('module_code', ''));
$action = trim((string) $request->query('action_code', ''));
$operatorType = trim((string) $request->query('operator_type', ''));
@@ -33,17 +32,9 @@ final class AuditLogIndexController extends Controller
$q->where('operator_type', $operatorType);
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (AuditLog $r) => $this->row($r))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (AuditLog $r) => $this->row($r));
}
/** @return array<string, mixed> */

View File

@@ -5,8 +5,8 @@ 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 App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,19 +15,11 @@ final class OddsVersionIndexController extends Controller
{
public function __invoke(Request $request, OddsStreamService $service): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$status = trim((string) $request->query('status', ''));
$paginator = $service->paginate($status === '' ? null : $status, $perPage);
$paginator = $service->paginate($status === '' ? null : $status, $p['perPage'], $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (OddsVersion $v) => AdminConfigPresenter::oddsVersionSummary($v))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (OddsVersion $v) => AdminConfigPresenter::oddsVersionSummary($v));
}
}

View File

@@ -5,8 +5,8 @@ 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 App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,19 +15,11 @@ final class PlayConfigVersionIndexController extends Controller
{
public function __invoke(Request $request, PlayConfigStreamService $service): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$status = trim((string) $request->query('status', ''));
$paginator = $service->paginate($status === '' ? null : $status, $perPage);
$paginator = $service->paginate($status === '' ? null : $status, $p['perPage'], $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (PlayConfigVersion $v) => AdminConfigPresenter::playConfigVersionSummary($v))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (PlayConfigVersion $v) => AdminConfigPresenter::playConfigVersionSummary($v));
}
}

View File

@@ -5,8 +5,8 @@ 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 App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,19 +15,11 @@ final class RiskCapVersionIndexController extends Controller
{
public function __invoke(Request $request, RiskCapStreamService $service): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$status = trim((string) $request->query('status', ''));
$paginator = $service->paginate($status === '' ? null : $status, $perPage);
$paginator = $service->paginate($status === '' ? null : $status, $p['perPage'], $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (RiskCapVersion $v) => AdminConfigPresenter::riskCapVersionSummary($v))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (RiskCapVersion $v) => AdminConfigPresenter::riskCapVersionSummary($v));
}
}

View File

@@ -4,8 +4,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Carbon\Carbon;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -16,7 +17,7 @@ final class AdminDrawIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$drawNo = trim((string) $request->query('draw_no', ''));
$status = trim((string) $request->query('status', ''));
@@ -30,18 +31,10 @@ final class AdminDrawIndexController extends Controller
$q->where('status', $status);
}
/** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator */
$paginator = $q->paginate($perPage);
/** @var LengthAwarePaginator $paginator */
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (Draw $row) => $this->row($row))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (Draw $row) => $this->row($row));
}
/** @return array<string, mixed> */

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotContribution;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,8 +15,7 @@ final class AdminJackpotContributionIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$drawNo = trim((string) $request->query('draw_no', ''));
$q = JackpotContribution::query()
@@ -27,28 +26,20 @@ final class AdminJackpotContributionIndexController extends Controller
$q->whereHas('draw', fn ($d) => $d->where('draw_no', 'like', '%'.$drawNo.'%'));
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (JackpotContribution $r) => [
'id' => (int) $r->id,
'draw_id' => (int) $r->draw_id,
'draw_no' => $r->draw?->draw_no,
'jackpot_pool_id' => (int) $r->jackpot_pool_id,
'currency_code' => $r->pool?->currency_code,
'player_id' => (int) $r->player_id,
'player_username' => $r->player?->username,
'ticket_item_id' => $r->ticket_item_id !== null ? (int) $r->ticket_item_id : null,
'ticket_no' => $r->ticketItem?->ticket_no,
'contribution_amount' => (int) $r->contribution_amount,
'created_at' => $r->created_at?->toIso8601String(),
])->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
return AdminApiList::json($paginator, fn (JackpotContribution $r) => [
'id' => (int) $r->id,
'draw_id' => (int) $r->draw_id,
'draw_no' => $r->draw?->draw_no,
'jackpot_pool_id' => (int) $r->jackpot_pool_id,
'currency_code' => $r->pool?->currency_code,
'player_id' => (int) $r->player_id,
'player_username' => $r->player?->username,
'ticket_item_id' => $r->ticket_item_id !== null ? (int) $r->ticket_item_id : null,
'ticket_no' => $r->ticketItem?->ticket_no,
'contribution_amount' => (int) $r->contribution_amount,
'created_at' => $r->created_at?->toIso8601String(),
]);
}
}

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
use App\Http\Controllers\Controller;
use App\Models\JackpotPayoutLog;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,8 +15,7 @@ final class AdminJackpotPayoutLogIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$drawNo = trim((string) $request->query('draw_no', ''));
$q = JackpotPayoutLog::query()
@@ -27,27 +26,19 @@ final class AdminJackpotPayoutLogIndexController extends Controller
$q->whereHas('draw', fn ($d) => $d->where('draw_no', 'like', '%'.$drawNo.'%'));
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (JackpotPayoutLog $r) => [
'id' => (int) $r->id,
'draw_id' => (int) $r->draw_id,
'draw_no' => $r->draw?->draw_no,
'jackpot_pool_id' => (int) $r->jackpot_pool_id,
'currency_code' => $r->pool?->currency_code,
'trigger_type' => $r->trigger_type,
'total_payout_amount' => (int) $r->total_payout_amount,
'winner_count' => (int) $r->winner_count,
'trigger_snapshot_json' => $r->trigger_snapshot_json,
'created_at' => $r->created_at?->toIso8601String(),
])->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
return AdminApiList::json($paginator, fn (JackpotPayoutLog $r) => [
'id' => (int) $r->id,
'draw_id' => (int) $r->draw_id,
'draw_no' => $r->draw?->draw_no,
'jackpot_pool_id' => (int) $r->jackpot_pool_id,
'currency_code' => $r->pool?->currency_code,
'trigger_type' => $r->trigger_type,
'total_payout_amount' => (int) $r->total_payout_amount,
'winner_count' => (int) $r->winner_count,
'trigger_snapshot_json' => $r->trigger_snapshot_json,
'created_at' => $r->created_at?->toIso8601String(),
]);
}
}

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\ReconcileItem;
use App\Models\ReconcileJob;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -14,31 +14,23 @@ final class ReconcileItemIndexController extends Controller
{
public function __invoke(Request $request, ReconcileJob $reconcile_job): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 50), 1), 200);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request, 50, 200);
$paginator = $reconcile_job->items()
->orderBy('id')
->paginate($perPage, ['*'], 'page', $page);
->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
return AdminApiList::jsonWith($paginator, fn (ReconcileItem $r) => [
'id' => (int) $r->id,
'side_a_ref' => $r->side_a_ref,
'side_b_ref' => $r->side_b_ref,
'difference_amount' => (int) $r->difference_amount,
'status' => $r->status,
'resolved_at' => $r->resolved_at?->toIso8601String(),
'created_at' => $r->created_at?->toIso8601String(),
], [
'job_id' => (int) $reconcile_job->id,
'job_no' => $reconcile_job->job_no,
'items' => collect($paginator->items())->map(fn (ReconcileItem $r) => [
'id' => (int) $r->id,
'side_a_ref' => $r->side_a_ref,
'side_b_ref' => $r->side_b_ref,
'difference_amount' => (int) $r->difference_amount,
'status' => $r->status,
'resolved_at' => $r->resolved_at?->toIso8601String(),
'created_at' => $r->created_at?->toIso8601String(),
])->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
}
}

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
use App\Http\Controllers\Controller;
use App\Models\ReconcileJob;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -13,8 +13,7 @@ final class ReconcileJobIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$type = trim((string) $request->query('reconcile_type', ''));
$q = ReconcileJob::query()->orderByDesc('id');
@@ -22,17 +21,9 @@ final class ReconcileJobIndexController extends Controller
$q->where('reconcile_type', $type);
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (ReconcileJob $j) => $this->row($j))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (ReconcileJob $j) => $this->row($j));
}
/** @return array<string, mixed> */

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Reports;
use App\Http\Controllers\Controller;
use App\Models\ReportJob;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -13,22 +13,13 @@ final class ReportJobIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$paginator = ReportJob::query()
->orderByDesc('id')
->paginate($perPage, ['*'], 'page', $page);
->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (ReportJob $j) => $this->row($j))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (ReportJob $j) => $this->row($j));
}
/** @return array<string, mixed> */

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Risk;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\RiskPool;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -19,7 +19,7 @@ final class AdminRiskPoolIndexController extends Controller
{
public function __invoke(Request $request, Draw $draw): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$soldOutOnly = $request->boolean('sold_out_only');
$sort = trim((string) $request->query('sort', 'usage_desc'));
@@ -39,18 +39,11 @@ final class AdminRiskPoolIndexController extends Controller
};
/** @var LengthAwarePaginator $paginator */
$paginator = $q->paginate($perPage);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
return AdminApiList::jsonWith($paginator, fn (RiskPool $row) => $this->row($row), [
'draw_id' => (int) $draw->id,
'draw_no' => $draw->draw_no,
'items' => collect($paginator->items())->map(fn (RiskPool $row) => $this->row($row))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
}

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Risk;
use App\Http\Controllers\Controller;
use App\Models\Draw;
use App\Models\RiskPoolLockLog;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -17,7 +17,7 @@ final class AdminRiskPoolLockLogIndexController extends Controller
{
public function __invoke(Request $request, Draw $draw): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$p = AdminApiList::readPaging($request);
$action = trim((string) $request->query('action_type', ''));
$number = trim((string) $request->query('normalized_number', ''));
@@ -36,18 +36,11 @@ final class AdminRiskPoolLockLogIndexController extends Controller
}
/** @var LengthAwarePaginator $paginator */
$paginator = $q->paginate($perPage);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
return AdminApiList::jsonWith($paginator, fn (RiskPoolLockLog $log) => $this->row($log), [
'draw_id' => (int) $draw->id,
'draw_no' => $draw->draw_no,
'items' => collect($paginator->items())->map(fn (RiskPoolLockLog $log) => $this->row($log))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
}

View File

@@ -7,6 +7,7 @@ use App\Lottery\ErrorCode;
use App\Models\Draw;
use App\Models\RiskPool;
use App\Models\RiskPoolLockLog;
use App\Support\AdminApiList;
use App\Support\ApiResponse;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
@@ -32,7 +33,7 @@ final class AdminRiskPoolShowController extends Controller
return ApiResponse::error('该期尚无此号码的风险池记录', ErrorCode::NotFound->value, null, 404);
}
$perPage = min(max((int) $request->integer('per_page', 20), 1), 100);
$p = AdminApiList::readPaging($request, 20, AdminApiList::MAX_PER_PAGE);
/** @var LengthAwarePaginator $paginator */
$paginator = RiskPoolLockLog::query()
@@ -41,7 +42,7 @@ final class AdminRiskPoolShowController extends Controller
->with(['ticketItem:id,ticket_no,play_code,player_id'])
->orderByDesc('created_at')
->orderByDesc('id')
->paginate($perPage);
->paginate($p['perPage'], ['*'], 'page', $p['page']);
$cap = (int) $pool->total_cap_amount;
$locked = (int) $pool->locked_amount;
@@ -59,15 +60,7 @@ final class AdminRiskPoolShowController extends Controller
'usage_ratio' => $cap > 0 ? round($locked / $cap, 6) : null,
'version' => (int) $pool->version,
],
'logs' => [
'items' => collect($paginator->items())->map(fn (RiskPoolLockLog $log) => $this->logRow($log))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
],
'logs' => AdminApiList::payload($paginator, fn (RiskPoolLockLog $log) => $this->logRow($log)),
]);
}

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Settlement;
use App\Http\Controllers\Controller;
use App\Models\SettlementBatch;
use App\Models\TicketSettlementDetail;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -16,8 +16,7 @@ final class AdminSettlementBatchDetailsController extends Controller
{
public function __invoke(Request $request, SettlementBatch $batch): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$paginator = TicketSettlementDetail::query()
->where('settlement_batch_id', $batch->id)
@@ -26,36 +25,27 @@ final class AdminSettlementBatchDetailsController extends Controller
'ticketItem.player:id,username,site_player_id',
])
->orderBy('id')
->paginate($perPage, ['*'], 'page', $page);
->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'batch_id' => (int) $batch->id,
'items' => collect($paginator->items())->map(function ($row) {
/** @var TicketSettlementDetail $row */
$item = $row->ticketItem;
$player = $item?->player;
return AdminApiList::jsonWith($paginator, function ($row) {
/** @var TicketSettlementDetail $row */
$item = $row->ticketItem;
$player = $item?->player;
return [
'id' => (int) $row->id,
'ticket_item_id' => (int) $row->ticket_item_id,
'ticket_no' => $item?->ticket_no,
'play_code' => $item?->play_code,
'player_id' => $item?->player_id,
'player_username' => $player?->username,
'site_player_id' => $player?->site_player_id,
'matched_prize_tier' => $row->matched_prize_tier,
'win_amount' => (int) $row->win_amount,
'jackpot_allocation_amount' => (int) $row->jackpot_allocation_amount,
'match_detail_json' => $row->match_detail_json,
'created_at' => $row->created_at?->toIso8601String(),
];
})->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return [
'id' => (int) $row->id,
'ticket_item_id' => (int) $row->ticket_item_id,
'ticket_no' => $item?->ticket_no,
'play_code' => $item?->play_code,
'player_id' => $item?->player_id,
'player_username' => $player?->username,
'site_player_id' => $player?->site_player_id,
'matched_prize_tier' => $row->matched_prize_tier,
'win_amount' => (int) $row->win_amount,
'jackpot_allocation_amount' => (int) $row->jackpot_allocation_amount,
'match_detail_json' => $row->match_detail_json,
'created_at' => $row->created_at?->toIso8601String(),
];
}, ['batch_id' => (int) $batch->id]);
}
}

View File

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Settlement;
use App\Http\Controllers\Controller;
use App\Models\SettlementBatch;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -15,8 +15,7 @@ final class AdminSettlementBatchIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$drawNo = trim((string) $request->query('draw_no', ''));
$status = trim((string) $request->query('status', ''));
@@ -32,17 +31,9 @@ final class AdminSettlementBatchIndexController extends Controller
$q->where('status', $status);
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(fn (SettlementBatch $b) => $this->row($b))->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
return AdminApiList::json($paginator, fn (SettlementBatch $b) => $this->row($b));
}
/** @return array<string, mixed> */

View File

@@ -0,0 +1,64 @@
<?php
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\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/** DELETE /api/v1/admin/admin-users/{admin_user} */
final class AdminUserDestroyController extends Controller
{
public function __invoke(Request $request, AdminUser $admin_user): JsonResponse
{
/** @var AdminUser $actor */
$actor = $request->lotteryAdmin();
if ((int) $actor->getKey() === (int) $admin_user->getKey()) {
return ApiResponse::error(
'不能删除当前登录账号',
ErrorCode::ValidationFailed->value,
null,
422,
);
}
$admin_user->load('roles');
if ($admin_user->isSuperAdmin()) {
$hasOther = AdminUser::query()
->whereKeyNot($admin_user->getKey())
->whereHas('roles', static fn ($q) => $q->where('admin_roles.slug', AdminUser::ROLE_SUPER_ADMIN))
->exists();
if (! $hasOther) {
return ApiResponse::error(
'不能删除最后一个超级管理员',
ErrorCode::ValidationFailed->value,
null,
422,
);
}
}
$before = AdminUserApiPresenter::listItem($admin_user);
$id = (int) $admin_user->id;
$admin_user->delete();
AuditLogger::recordForAdmin(
$actor,
$request,
'system',
'admin_user.delete',
'admin_user',
(string) $id,
$before,
null,
);
return ApiResponse::success(['deleted' => true, 'id' => $id]);
}
}

View File

@@ -4,7 +4,8 @@ namespace App\Http\Controllers\Api\V1\Admin\User;
use App\Http\Controllers\Controller;
use App\Models\AdminUser;
use App\Support\ApiResponse;
use App\Support\AdminApiList;
use App\Support\AdminUserApiPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -13,8 +14,7 @@ final class AdminUserIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$perPage = min(max((int) $request->integer('per_page', 25), 1), 100);
$page = max((int) $request->integer('page', 1), 1);
$p = AdminApiList::readPaging($request);
$keyword = trim((string) $request->query('keyword', ''));
$q = AdminUser::query()
@@ -29,33 +29,8 @@ final class AdminUserIndexController extends Controller
});
}
$paginator = $q->paginate($perPage, ['*'], 'page', $page);
$paginator = $q->paginate($p['perPage'], ['*'], 'page', $p['page']);
return ApiResponse::success([
'items' => collect($paginator->items())->map(
fn (AdminUser $user): array => $this->row($user)
)->all(),
'meta' => [
'current_page' => $paginator->currentPage(),
'per_page' => $paginator->perPage(),
'total' => $paginator->total(),
'last_page' => $paginator->lastPage(),
],
]);
}
/** @return array<string, mixed> */
private function row(AdminUser $user): array
{
return [
'id' => (int) $user->id,
'username' => $user->username,
'nickname' => $user->name,
'email' => $user->email,
'status' => (int) $user->status,
'roles' => $user->adminRoleSlugs(),
'direct_permissions' => $user->directLegacyPermissionSlugs(),
'effective_permissions' => $user->adminPermissionSlugs(),
];
return AdminApiList::json($paginator, fn (AdminUser $user): array => AdminUserApiPresenter::listItem($user));
}
}

View File

@@ -7,7 +7,6 @@ use App\Models\AdminUser;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
/** PUT /api/v1/admin/admin-users/{admin_user}/roles */
@@ -22,29 +21,7 @@ final class AdminUserRoleSyncController extends Controller
])->validate();
$slugs = array_values(array_unique($data['role_slugs']));
$siteId = AdminUser::defaultAdminSiteId();
$roleIds = DB::table('admin_roles')
->whereIn('slug', $slugs)
->pluck('id')
->all();
DB::transaction(function () use ($admin_user, $siteId, $roleIds): void {
DB::table('admin_user_site_roles')
->where('admin_user_id', $admin_user->id)
->where('site_id', $siteId)
->delete();
$now = now();
foreach ($roleIds as $rid) {
DB::table('admin_user_site_roles')->insert([
'admin_user_id' => $admin_user->id,
'site_id' => $siteId,
'role_id' => (int) $rid,
'granted_at' => $now,
]);
}
});
$admin_user->syncRoleSlugsForDefaultSite($slugs);
$admin_user->load('roles');

View File

@@ -0,0 +1,20 @@
<?php
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;
/** GET /api/v1/admin/admin-users/{admin_user} */
final class AdminUserShowController extends Controller
{
public function __invoke(AdminUser $admin_user): JsonResponse
{
$admin_user->load('roles');
return ApiResponse::success(AdminUserApiPresenter::listItem($admin_user));
}
}

View File

@@ -0,0 +1,75 @@
<?php
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 Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/** POST /api/v1/admin/admin-users */
final class AdminUserStoreController extends Controller
{
public function __invoke(Request $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'])
: null;
$roleSlugs = array_values(array_unique($data['role_slugs']));
$user = DB::transaction(function () use ($data, $email, $roleSlugs): AdminUser {
$created = AdminUser::query()->create([
'username' => $data['username'],
'name' => $data['nickname'],
'email' => $email,
'password' => $data['password'],
'status' => array_key_exists('status', $data) ? (int) $data['status'] : 0,
]);
$created->syncRoleSlugsForDefaultSite($roleSlugs);
return $created;
});
$user->load('roles');
AuditLogger::recordForAdmin(
$actor,
$request,
'system',
'admin_user.create',
'admin_user',
(string) $user->getKey(),
null,
AdminUserApiPresenter::listItem($user),
);
return ApiResponse::success(AdminUserApiPresenter::listItem($user));
}
}

View File

@@ -0,0 +1,77 @@
<?php
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 Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
/** PUT /api/v1/admin/admin-users/{admin_user} */
final class AdminUserUpdateController extends Controller
{
public function __invoke(Request $request, AdminUser $admin_user): JsonResponse
{
/** @var AdminUser $actor */
$actor = $request->lotteryAdmin();
$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();
$updates = [];
if (array_key_exists('nickname', $data)) {
$updates['name'] = $data['nickname'];
}
if (array_key_exists('email', $data)) {
$updates['email'] = ($data['email'] !== null && trim((string) $data['email']) !== '')
? trim((string) $data['email'])
: null;
}
if (array_key_exists('status', $data)) {
$updates['status'] = (int) $data['status'];
}
if (array_key_exists('password', $data) && is_string($data['password']) && $data['password'] !== '') {
$updates['password'] = $data['password'];
}
if ($updates === []) {
return ApiResponse::success($before);
}
$admin_user->fill($updates);
$admin_user->save();
$admin_user->load('roles');
$after = AdminUserApiPresenter::listItem($admin_user);
AuditLogger::recordForAdmin(
$actor,
$request,
'system',
'admin_user.update',
'admin_user',
(string) $admin_user->getKey(),
$before,
$after,
);
return ApiResponse::success($after);
}
}