feat: 增强代理和玩家管理功能
- 在多个控制器中更新权限检查逻辑,确保管理员能够更灵活地管理代理和玩家。 - 在 AdminPlayerStoreController 中引入对玩家创建能力的验证,确保只有具备相应权限的管理员能够创建玩家。 - 更新请求验证逻辑,新增 credit_limit、rebate_rate 和 extra_rebate_rate 字段,以支持更细粒度的玩家管理。 - 在 AgentNodeProfileController 中添加对父代理能力授予的验证,确保子代理的权限在父代理范围内。 - 引入 AgentProfileFieldRules 以简化代理资料更新请求的规则定义,提升代码复用性。
This commit is contained in:
@@ -16,7 +16,7 @@ use App\Exceptions\IdempotentTicketReplayException;
|
||||
use App\Exceptions\TicketOperationException;
|
||||
use App\Services\Jackpot\JackpotContributionService;
|
||||
use App\Services\Draw\DrawHallSnapshotBuilder;
|
||||
use App\Support\CreditLineMode;
|
||||
use App\Support\PlayerFundingMode;
|
||||
use App\Services\Player\PlayerCreditService;
|
||||
|
||||
final class TicketPlacementService
|
||||
@@ -42,8 +42,10 @@ final class TicketPlacementService
|
||||
? (string) $payload['client_trace_id']
|
||||
: null;
|
||||
|
||||
$drawNo = (string) $payload['draw_id'];
|
||||
$drawIdForIdempotency = Draw::query()->where('draw_no', $drawNo)->value('id');
|
||||
$drawNo = trim((string) ($payload['draw_id'] ?? ''));
|
||||
$drawIdForIdempotency = $drawNo === ''
|
||||
? null
|
||||
: Draw::query()->where('draw_no', $drawNo)->value('id');
|
||||
|
||||
if ($clientTraceId !== null && $drawIdForIdempotency !== null) {
|
||||
$existing = TicketOrder::query()
|
||||
@@ -75,9 +77,10 @@ final class TicketPlacementService
|
||||
$payload,
|
||||
$expectedVersions,
|
||||
$clientTraceId,
|
||||
$drawNo,
|
||||
): array {
|
||||
$draw = Draw::query()
|
||||
->where('draw_no', (string) $payload['draw_id'])
|
||||
->where('draw_no', $drawNo)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
if ($draw === null) {
|
||||
@@ -156,20 +159,24 @@ final class TicketPlacementService
|
||||
);
|
||||
}
|
||||
|
||||
$wallet = PlayerWallet::query()
|
||||
->where('player_id', $player->id)
|
||||
->where('wallet_type', 'lottery')
|
||||
->where('currency_code', $currencyCode)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
if ($wallet !== null && (int) $wallet->status !== 0) {
|
||||
throw new TicketOperationException('wallet_frozen', ErrorCode::WalletLotteryFrozen->value);
|
||||
}
|
||||
$creditLine = PlayerFundingMode::usesCredit($player);
|
||||
|
||||
$walletBalance = $wallet !== null ? (int) $wallet->balance : 0;
|
||||
$walletAvailable = $walletBalance - ($wallet !== null ? (int) $wallet->frozen_balance : 0);
|
||||
if ($walletAvailable < $totalActualDeduct) {
|
||||
throw new TicketOperationException('bet_insufficient_balance', ErrorCode::BetInsufficientBalance->value);
|
||||
if (! $creditLine) {
|
||||
$wallet = PlayerWallet::query()
|
||||
->where('player_id', $player->id)
|
||||
->where('wallet_type', 'lottery')
|
||||
->where('currency_code', $currencyCode)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
if ($wallet !== null && (int) $wallet->status !== 0) {
|
||||
throw new TicketOperationException('wallet_frozen', ErrorCode::WalletLotteryFrozen->value);
|
||||
}
|
||||
|
||||
$walletBalance = $wallet !== null ? (int) $wallet->balance : 0;
|
||||
$walletAvailable = $walletBalance - ($wallet !== null ? (int) $wallet->frozen_balance : 0);
|
||||
if ($walletAvailable < $totalActualDeduct) {
|
||||
throw new TicketOperationException('bet_insufficient_balance', ErrorCode::BetInsufficientBalance->value);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -301,17 +308,17 @@ final class TicketPlacementService
|
||||
'status' => $failedItems === [] ? 'pending_confirm' : 'partial_pending_confirm',
|
||||
])->save();
|
||||
|
||||
if (CreditLineMode::isEnabledForSiteCode((string) $player->site_code)) {
|
||||
$this->playerCreditService->holdForBet($player, $successTotalActualDeduct);
|
||||
if ($creditLine) {
|
||||
$this->playerCreditService->assertMayPlaceBet($player, $successTotalActualDeduct);
|
||||
} else {
|
||||
$this->ticketWalletService->reserveBetDeduct(
|
||||
$player,
|
||||
$currencyCode,
|
||||
$successTotalActualDeduct,
|
||||
$order,
|
||||
);
|
||||
}
|
||||
|
||||
$this->ticketWalletService->reserveBetDeduct(
|
||||
$player,
|
||||
$currencyCode,
|
||||
$successTotalActualDeduct,
|
||||
$order,
|
||||
);
|
||||
|
||||
return [
|
||||
'order' => $order,
|
||||
'draw_id' => (int) $draw->id,
|
||||
@@ -331,13 +338,20 @@ final class TicketPlacementService
|
||||
$order = TicketOrder::query()->whereKey($placement['order']->id)->firstOrFail();
|
||||
$draw = Draw::query()->whereKey((int) $placement['draw_id'])->firstOrFail();
|
||||
|
||||
$creditLine = PlayerFundingMode::usesCredit($player);
|
||||
|
||||
try {
|
||||
$balanceAfter = $this->ticketWalletService->finalizeReservedBetDeduct(
|
||||
$player,
|
||||
(string) $placement['currency_code'],
|
||||
(int) $placement['success_total_actual_deduct'],
|
||||
$order,
|
||||
);
|
||||
$balanceAfter = $creditLine
|
||||
? $this->playerCreditService->availableCreditMinor(
|
||||
$player,
|
||||
(string) $placement['currency_code'],
|
||||
)
|
||||
: $this->ticketWalletService->finalizeReservedBetDeduct(
|
||||
$player,
|
||||
(string) $placement['currency_code'],
|
||||
(int) $placement['success_total_actual_deduct'],
|
||||
$order,
|
||||
);
|
||||
|
||||
DB::transaction(function () use ($order, $draw, $placement): void {
|
||||
$successfulItems = TicketItem::query()
|
||||
@@ -381,7 +395,9 @@ final class TicketPlacementService
|
||||
}
|
||||
|
||||
$order->forceFill(['status' => 'refunded'])->save();
|
||||
$this->ticketWalletService->releaseReservedBetDeduct($order, 'wallet_deduct_failed_release');
|
||||
if (! PlayerFundingMode::usesCredit($player)) {
|
||||
$this->ticketWalletService->releaseReservedBetDeduct($order, 'wallet_deduct_failed_release');
|
||||
}
|
||||
$this->ticketWalletService->reverseBetDeduct($order);
|
||||
});
|
||||
|
||||
@@ -516,7 +532,7 @@ final class TicketPlacementService
|
||||
*/
|
||||
private function applyCreditLineInstantRebatePolicy(Player $player, array $evaluated): array
|
||||
{
|
||||
if (! CreditLineMode::isEnabledForSiteCode((string) $player->site_code)) {
|
||||
if (! PlayerFundingMode::usesCredit($player)) {
|
||||
return $evaluated;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ final class TicketPreviewService
|
||||
*/
|
||||
public function preview(array $payload): array
|
||||
{
|
||||
$draw = Draw::query()->where('draw_no', (string) $payload['draw_id'])->first();
|
||||
$drawNo = trim((string) ($payload['draw_id'] ?? ''));
|
||||
$draw = $drawNo === ''
|
||||
? null
|
||||
: Draw::query()->where('draw_no', $drawNo)->first();
|
||||
if ($draw === null) {
|
||||
throw new TicketOperationException('draw_not_found', ErrorCode::BetInvalidDraw->value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user