feat: 切换 schema dump 基线并增强返点结算与管理校验
This commit is contained in:
@@ -38,8 +38,8 @@ final class RiskCapItemsReplaceController extends Controller
|
||||
'items' => ['required', 'array', 'min:1'],
|
||||
'items.*.draw_id' => ['sometimes', 'nullable', 'integer', 'exists:draws,id'],
|
||||
'items.*.normalized_number' => ['required', 'string', 'size:4', 'regex:/^[0-9]{4}$/'],
|
||||
'items.*.cap_amount' => ['required', 'integer', 'min:0'],
|
||||
'items.*.cap_type' => ['required', 'string', 'max:16'],
|
||||
'items.*.cap_amount' => ['required', 'integer', 'min:1'],
|
||||
'items.*.cap_type' => ['required', 'string', 'in:default,per_number'],
|
||||
]);
|
||||
|
||||
$service->replaceItems($version, $data['items'], $admin);
|
||||
|
||||
@@ -50,6 +50,7 @@ final class AdminCurrencyDestroyController extends Controller
|
||||
$checks = [
|
||||
'玩家默认币种' => DB::table('players')->where('default_currency', $code),
|
||||
'玩家钱包' => DB::table('player_wallets')->where('currency_code', $code),
|
||||
'站点默认币种' => DB::table('admin_sites')->where('currency_code', $code),
|
||||
'转账单' => DB::table('transfer_orders')->where('currency_code', $code),
|
||||
'注单' => DB::table('ticket_orders')->where('currency_code', $code),
|
||||
'赔率配置' => DB::table('odds_items')->where('currency_code', $code),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Jackpot;
|
||||
|
||||
use App\Models\AdminUser;
|
||||
use App\Models\Draw;
|
||||
use App\Models\JackpotPool;
|
||||
use App\Support\ApiResponse;
|
||||
use App\Support\ApiMessage;
|
||||
@@ -40,11 +41,21 @@ final class AdminJackpotPoolManualBurstController extends Controller
|
||||
}
|
||||
|
||||
$data = $request->validate([
|
||||
'draw_id' => 'required|integer|exists:draws,id',
|
||||
'draw_id' => ['required'],
|
||||
]);
|
||||
|
||||
$drawId = $this->resolveDrawId(trim((string) $data['draw_id']));
|
||||
if ($drawId === null) {
|
||||
return ApiResponse::error(
|
||||
trans('validation.exists', ['attribute' => 'draw_id'], $request->lotteryLocale()),
|
||||
ErrorCode::ClientHttpError->value,
|
||||
['draw_id' => [trans('validation.exists', ['attribute' => 'draw_id'], $request->lotteryLocale())]],
|
||||
422,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$payload = $this->service->execute($pool, (int) $data['draw_id']);
|
||||
$payload = $this->service->execute($pool, $drawId);
|
||||
} catch (\RuntimeException $e) {
|
||||
return ApiResponse::error(
|
||||
ApiMessage::get($request, 'jackpot_manual_burst_failed', [
|
||||
@@ -58,4 +69,22 @@ final class AdminJackpotPoolManualBurstController extends Controller
|
||||
|
||||
return ApiResponse::success($payload);
|
||||
}
|
||||
|
||||
private function resolveDrawId(string $drawRef): ?int
|
||||
{
|
||||
if ($drawRef === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ctype_digit($drawRef)) {
|
||||
$draw = Draw::query()->whereKey((int) $drawRef)->first();
|
||||
if ($draw !== null) {
|
||||
return (int) $draw->id;
|
||||
}
|
||||
}
|
||||
|
||||
$draw = Draw::query()->where('draw_no', $drawRef)->first();
|
||||
|
||||
return $draw !== null ? (int) $draw->id : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Support\AdminSiteScope;
|
||||
use App\Support\PlayerFundingMode;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/** DELETE /api/v1/admin/players/{player} */
|
||||
final class AdminPlayerDestroyController extends Controller
|
||||
@@ -25,13 +27,36 @@ final class AdminPlayerDestroyController extends Controller
|
||||
return $denied;
|
||||
}
|
||||
|
||||
$hasWallets = Player::query()
|
||||
->whereKey($player->getKey())
|
||||
->whereHas('wallets', static fn (Builder $q) => $q->where('balance', '!=', 0))
|
||||
if (PlayerFundingMode::usesCredit($player)) {
|
||||
$creditRow = DB::table('player_credit_accounts')
|
||||
->where('player_id', $player->getKey())
|
||||
->first();
|
||||
|
||||
$usedCredit = (int) ($creditRow->used_credit ?? 0);
|
||||
$frozenCredit = (int) ($creditRow->frozen_credit ?? 0);
|
||||
if ($usedCredit !== 0 || $frozenCredit !== 0) {
|
||||
return ApiMessage::errorResponse($request, 'admin.player_credit_in_use_blocks_delete', ErrorCode::ValidationFailed->value, null, 422);
|
||||
}
|
||||
} else {
|
||||
$hasWallets = Player::query()
|
||||
->whereKey($player->getKey())
|
||||
->whereHas('wallets', static fn (Builder $q) => $q->where('balance', '!=', 0))
|
||||
->exists();
|
||||
|
||||
if ($hasWallets) {
|
||||
return ApiMessage::errorResponse($request, 'admin.player_wallet_balance_blocks_delete', ErrorCode::ValidationFailed->value, null, 422);
|
||||
}
|
||||
}
|
||||
|
||||
$hasUnpaidSettlementBills = DB::table('settlement_bills')
|
||||
->where('owner_type', 'player')
|
||||
->where('owner_id', $player->getKey())
|
||||
->whereIn('status', ['confirmed', 'partial_paid', 'overdue'])
|
||||
->where('unpaid_amount', '>', 0)
|
||||
->exists();
|
||||
|
||||
if ($hasWallets) {
|
||||
return ApiMessage::errorResponse($request, 'admin.player_wallet_balance_blocks_delete', ErrorCode::ValidationFailed->value, null, 422);
|
||||
if ($hasUnpaidSettlementBills) {
|
||||
return ApiMessage::errorResponse($request, 'admin.player_unpaid_settlement_blocks_delete', ErrorCode::ValidationFailed->value, null, 422);
|
||||
}
|
||||
|
||||
$hasTickets = TicketOrder::query()
|
||||
@@ -42,6 +67,7 @@ final class AdminPlayerDestroyController extends Controller
|
||||
return ApiMessage::errorResponse($request, 'admin.player_has_tickets_blocks_delete', ErrorCode::ValidationFailed->value, null, 422);
|
||||
}
|
||||
|
||||
DB::table('player_credit_accounts')->where('player_id', $player->getKey())->delete();
|
||||
$player->wallets()->delete();
|
||||
$player->delete();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ final class AdminPlayerIndexController extends Controller
|
||||
|
||||
if ($keyword !== '') {
|
||||
$term = '%'.addcslashes($keyword, '%_\\').'%';
|
||||
$q->where(static function ($sub) use ($term): void {
|
||||
$q->where(static function ($sub) use ($term, $keyword): void {
|
||||
$sub->where('site_player_id', 'like', $term)
|
||||
->orWhere('username', 'like', $term)
|
||||
->orWhere('nickname', 'like', $term);
|
||||
|
||||
@@ -55,7 +55,7 @@ final class AdminPlayerStoreController extends Controller
|
||||
if ($isNative) {
|
||||
$sitePlayerId = $sitePlayerId !== ''
|
||||
? $sitePlayerId
|
||||
: 'native:'.Str::lower(Str::ulid());
|
||||
: $this->generateNativeSitePlayerId($siteCode);
|
||||
}
|
||||
|
||||
if ($sitePlayerId === '') {
|
||||
@@ -192,4 +192,20 @@ final class AdminPlayerStoreController extends Controller
|
||||
|
||||
return $rootId !== null ? (int) $rootId : null;
|
||||
}
|
||||
|
||||
private function generateNativeSitePlayerId(string $siteCode): string
|
||||
{
|
||||
$prefix = strtoupper(substr(preg_replace('/[^A-Za-z]/', '', $siteCode) ?: 'LP', 0, 2));
|
||||
$prefix = str_pad($prefix, 2, 'P');
|
||||
|
||||
do {
|
||||
$candidate = sprintf('%s%06d', $prefix, random_int(0, 999999));
|
||||
$exists = Player::query()
|
||||
->where('site_code', $siteCode)
|
||||
->where('site_player_id', $candidate)
|
||||
->exists();
|
||||
} while ($exists);
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin\Reconcile;
|
||||
|
||||
use App\Models\TransferOrder;
|
||||
use App\Models\ReconcileJob;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\ReconcileItem;
|
||||
@@ -20,6 +21,19 @@ final class ReconcileItemIndexController extends Controller
|
||||
->orderBy('id')
|
||||
->paginate($p['perPage'], ['*'], 'page', $p['page']);
|
||||
|
||||
$transferNos = collect($paginator->items())
|
||||
->map(fn (ReconcileItem $item) => $item->side_a_ref)
|
||||
->filter(fn ($value) => is_string($value) && $value !== '')
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$transferStatuses = $transferNos === []
|
||||
? []
|
||||
: TransferOrder::query()
|
||||
->whereIn('transfer_no', $transferNos)
|
||||
->pluck('status', 'transfer_no')
|
||||
->all();
|
||||
|
||||
return AdminApiList::jsonWith($paginator, fn (ReconcileItem $r) => [
|
||||
'id' => (int) $r->id,
|
||||
'side_a_ref' => $r->side_a_ref,
|
||||
@@ -27,6 +41,8 @@ final class ReconcileItemIndexController extends Controller
|
||||
'difference_amount' => (int) $r->difference_amount,
|
||||
'status' => $r->status,
|
||||
'resolved_at' => $r->resolved_at?->toIso8601String(),
|
||||
'is_resolved' => $r->resolved_at !== null || in_array($transferStatuses[$r->side_a_ref ?? ''] ?? null, ['success', 'reversed', 'manually_processed'], true),
|
||||
'current_transfer_status' => $transferStatuses[$r->side_a_ref ?? ''] ?? null,
|
||||
'created_at' => $r->created_at?->toIso8601String(),
|
||||
], [
|
||||
'job_id' => (int) $reconcile_job->id,
|
||||
|
||||
@@ -14,10 +14,16 @@ final class ReportJobIndexController extends Controller
|
||||
public function __invoke(Request $request): JsonResponse
|
||||
{
|
||||
$p = AdminApiList::readPaging($request);
|
||||
$reportType = trim((string) $request->query('report_type', ''));
|
||||
|
||||
$paginator = ReportJob::query()
|
||||
->orderByDesc('id')
|
||||
->paginate($p['perPage'], ['*'], 'page', $p['page']);
|
||||
$query = ReportJob::query()
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($reportType !== '') {
|
||||
$query->where('report_type', $reportType);
|
||||
}
|
||||
|
||||
$paginator = $query->paginate($p['perPage'], ['*'], 'page', $p['page']);
|
||||
|
||||
return AdminApiList::json($paginator, fn (ReportJob $j) => $this->row($j));
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ final class AdminTicketItemIndexController extends Controller
|
||||
->with([
|
||||
'draw:id,draw_no,business_date',
|
||||
'order:id,order_no,currency_code,created_at',
|
||||
'player:id,site_code,site_player_id,username,nickname,agent_node_id',
|
||||
'player:id,site_code,site_player_id,username,nickname,agent_node_id,funding_mode',
|
||||
'player.agentNode:id,code,name',
|
||||
])
|
||||
->orderByDesc('ticket_items.id');
|
||||
@@ -108,6 +108,8 @@ final class AdminTicketItemIndexController extends Controller
|
||||
'site_player_id' => $row->player?->site_player_id,
|
||||
'username' => $row->player?->username,
|
||||
'nickname' => $row->player?->nickname,
|
||||
'funding_mode' => $row->player?->funding_mode,
|
||||
'uses_credit' => $row->player?->funding_mode === 'credit',
|
||||
'order_no' => $row->order?->order_no,
|
||||
'draw_no' => $row->draw?->draw_no,
|
||||
'currency_code' => $row->order?->currency_code,
|
||||
|
||||
@@ -67,7 +67,7 @@ final class WalletTransactionListController extends Controller
|
||||
|
||||
$query = WalletTxn::query()
|
||||
->with([
|
||||
'player:id,site_code,site_player_id,username,nickname,agent_node_id',
|
||||
'player:id,site_code,site_player_id,username,nickname,agent_node_id,funding_mode,auth_source',
|
||||
'player.agentNode:id,code,name',
|
||||
])
|
||||
->orderByDesc('id');
|
||||
|
||||
Reference in New Issue
Block a user