Files
lotteryLaravel/app/Http/Controllers/Api/V1/Wallet/WalletSettlementBillsController.php
kang 992195b00c
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
feat(agent-profile): 限制代理及玩家返点和分成范围,增强权限校验
- 添加ensureSuperAdmin方法,限制管理员设置操作权限
- 在AdminSettingController接口中新增权限检查,防止非超级管理员操作
- AdminPlayerIndexController新增direct agent筛选支持
- AdminPlayerUpdateController新增对信用玩家默认币别变更的拒绝逻辑
- 新增WalletSettlementBillsController,实现玩家信用盘账期账单摘要接口
- AgentProfileService调整,新增返点限额和分享比例自动下调机制,保持子代理及玩家配置不超父级
- AgentProfileService增加can_grant_extra_rebate权限继承限制,阻止无权限代理开启
- AgentSettlementPeriodCloseService增加玩家账单回水信用释放逻辑,确保信用额度同步
- PlayerCreditService新增释放账单回水对应信用逻辑,维护账期信用一致性
- TicketPlacementService和TicketPreviewService新增信用玩家投注币别匹配校验,防止币别不符
- PlayerLedgerLogsService优化信用额度计算,增加可用额度上下限限制,防止负值和超限
- 调整后台管理导航,仅超管可见设置入口,强化权限隔离
- 路由新增玩家信用账单查询接口
- 补充多项AgentProfile相关单元测试覆盖额度限制、返点继承、返点下调场景及权限限制
- 增加站点管理员登录测试,验证系统设置菜单不可见,提升用户权限体验
2026-07-01 15:35:46 +08:00

108 lines
4.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers\Api\V1\Wallet;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use App\Support\CurrencyFormatter;
use App\Support\PlayerFundingMode;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/** GET /api/v1/wallet/settlement-bills信用盘玩家自己的账期账单摘要。 */
final class WalletSettlementBillsController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$player = $request->lotteryPlayer();
abort_if($player === null, 500, 'lottery_player missing');
if (! PlayerFundingMode::usesCredit($player)) {
return ApiResponse::success([
'items' => [],
'summary' => [
'pending_receivable' => 0,
'pending_payable' => 0,
'net_pending' => 0,
'pending_count' => 0,
],
'funding_mode' => (string) ($player->funding_mode ?? PlayerFundingMode::WALLET),
]);
}
$rows = DB::table('settlement_bills as sb')
->join('settlement_periods as sp', 'sp.id', '=', 'sb.settlement_period_id')
->where('sb.bill_type', 'player')
->where('sb.owner_type', 'player')
->where('sb.owner_id', (int) $player->id)
->whereIn('sb.status', ['pending_confirm', 'confirmed', 'partial_paid', 'overdue'])
->orderByDesc('sp.period_end')
->orderByDesc('sb.id')
->limit(5)
->get([
'sb.id',
'sb.settlement_period_id',
'sb.gross_win_loss',
'sb.rebate_amount',
'sb.net_amount',
'sb.paid_amount',
'sb.unpaid_amount',
'sb.status',
'sp.period_start',
'sp.period_end',
]);
$pendingReceivable = 0;
$pendingPayable = 0;
$items = $rows->map(function (object $row) use (&$pendingReceivable, &$pendingPayable): array {
$netAmount = (int) $row->net_amount;
$unpaidAmount = (int) $row->unpaid_amount;
$direction = $netAmount > 0 ? 'payable' : 'receivable';
if ($direction === 'payable') {
$pendingPayable += $unpaidAmount;
} else {
$pendingReceivable += $unpaidAmount;
}
return [
'id' => (int) $row->id,
'settlement_period_id' => (int) $row->settlement_period_id,
'period_start' => $this->isoTimestamp($row->period_start),
'period_end' => $this->isoTimestamp($row->period_end),
'gross_win_loss' => (int) $row->gross_win_loss,
'rebate_amount' => (int) $row->rebate_amount,
'net_amount' => $netAmount,
'paid_amount' => (int) $row->paid_amount,
'unpaid_amount' => $unpaidAmount,
'direction' => $direction,
'status' => (string) $row->status,
];
})->all();
return ApiResponse::success([
'items' => $items,
'summary' => [
'pending_receivable' => $pendingReceivable,
'pending_receivable_formatted' => CurrencyFormatter::fromMinor($pendingReceivable),
'pending_payable' => $pendingPayable,
'pending_payable_formatted' => CurrencyFormatter::fromMinor($pendingPayable),
'net_pending' => $pendingReceivable - $pendingPayable,
'net_pending_formatted' => CurrencyFormatter::fromMinor(abs($pendingReceivable - $pendingPayable)),
'pending_count' => count($items),
],
'funding_mode' => PlayerFundingMode::CREDIT,
]);
}
private function isoTimestamp(mixed $value): ?string
{
if ($value === null || $value === '') {
return null;
}
return \Carbon\Carbon::parse((string) $value)->toIso8601String();
}
}