feat: 增强代理和玩家管理功能
- 在多个控制器中更新权限检查逻辑,确保管理员能够更灵活地管理代理和玩家。 - 在 AdminPlayerStoreController 中引入对玩家创建能力的验证,确保只有具备相应权限的管理员能够创建玩家。 - 更新请求验证逻辑,新增 credit_limit、rebate_rate 和 extra_rebate_rate 字段,以支持更细粒度的玩家管理。 - 在 AgentNodeProfileController 中添加对父代理能力授予的验证,确保子代理的权限在父代理范围内。 - 引入 AgentProfileFieldRules 以简化代理资料更新请求的规则定义,提升代码复用性。
This commit is contained in:
232
app/Services/AgentSettlement/AgentPeriodAggregator.php
Normal file
232
app/Services/AgentSettlement/AgentPeriodAggregator.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AgentSettlement;
|
||||
|
||||
use App\Models\AgentNode;
|
||||
use App\Models\Player;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class AgentPeriodAggregator
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ShareSettlementCalculator $calculator,
|
||||
private readonly BetSettlementSnapshotBuilder $snapshotBuilder,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* players: array<int, array<string, mixed>>,
|
||||
* agent_edges: array<string, int>,
|
||||
* agent_subtrees: array<int, array<string, mixed>>,
|
||||
* platform_share_profit: int,
|
||||
* }
|
||||
*/
|
||||
public function aggregate(int $adminSiteId, string $periodStart, string $periodEnd): array
|
||||
{
|
||||
$siteCode = (string) DB::table('admin_sites')->where('id', $adminSiteId)->value('code');
|
||||
$codeToId = AgentNode::query()
|
||||
->where('admin_site_id', $adminSiteId)
|
||||
->pluck('id', 'code')
|
||||
->mapWithKeys(fn ($id, $code): array => [(string) $code => (int) $id])
|
||||
->all();
|
||||
|
||||
$rows = DB::table('share_ledger as sl')
|
||||
->join('players as p', 'p.id', '=', 'sl.player_id')
|
||||
->where('p.site_code', $siteCode)
|
||||
->whereBetween('sl.settled_at', [$periodStart, $periodEnd])
|
||||
->select([
|
||||
'sl.player_id',
|
||||
'sl.ticket_item_id',
|
||||
'sl.agent_node_id',
|
||||
'sl.share_snapshot',
|
||||
'sl.game_win_loss',
|
||||
'sl.basic_rebate',
|
||||
])
|
||||
->orderBy('sl.id')
|
||||
->get();
|
||||
|
||||
$players = [];
|
||||
$agentEdges = [];
|
||||
$agentSubtrees = [];
|
||||
$platformShareProfit = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$playerId = (int) $row->player_id;
|
||||
$snapshot = $this->resolveSnapshotFromLedgerRow($row);
|
||||
if ($snapshot === null) {
|
||||
$player = Player::query()->find($playerId);
|
||||
if ($player === null) {
|
||||
continue;
|
||||
}
|
||||
$built = $this->snapshotBuilder->buildForPlayer($player);
|
||||
$snapshot = [
|
||||
'total_shares' => $built['total_shares'],
|
||||
'chain_codes' => $built['chain_codes'],
|
||||
];
|
||||
}
|
||||
|
||||
$gameWinLoss = (int) $row->game_win_loss;
|
||||
$basicRebate = (int) $row->basic_rebate;
|
||||
$extraRebate = $this->extraRebateForTicketItem(
|
||||
(int) $row->ticket_item_id,
|
||||
$periodStart,
|
||||
$periodEnd,
|
||||
);
|
||||
|
||||
$extraByCode = [];
|
||||
if ($extraRebate > 0 && $snapshot['chain_codes'] !== []) {
|
||||
$extraByCode[$snapshot['chain_codes'][0]] = $extraRebate;
|
||||
}
|
||||
|
||||
$result = $this->calculator->calculate(
|
||||
sharedNetWinLoss: 0,
|
||||
totalSharesByCode: $snapshot['total_shares'],
|
||||
extraRebateByCode: $extraByCode,
|
||||
gameWinLoss: $gameWinLoss,
|
||||
basicRebate: $basicRebate,
|
||||
chainFromPlayer: $snapshot['chain_codes'],
|
||||
);
|
||||
|
||||
$net = (int) round($result->playerNetSettlement);
|
||||
|
||||
if (! isset($players[$playerId])) {
|
||||
$players[$playerId] = [
|
||||
'agent_node_id' => (int) $row->agent_node_id,
|
||||
'game_win_loss' => 0,
|
||||
'basic_rebate' => 0,
|
||||
'extra_rebate' => 0,
|
||||
'net_amount' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$players[$playerId]['game_win_loss'] += $gameWinLoss;
|
||||
$players[$playerId]['basic_rebate'] += $basicRebate;
|
||||
$players[$playerId]['extra_rebate'] += $extraRebate;
|
||||
$players[$playerId]['net_amount'] += $net;
|
||||
|
||||
foreach ($result->tierSettlements as $edge => $amount) {
|
||||
$agentEdges[$edge] = ($agentEdges[$edge] ?? 0) + (int) round($amount);
|
||||
}
|
||||
|
||||
$platformShareProfit += (int) round($result->finalProfits['platform'] ?? 0);
|
||||
|
||||
$pathIds = $this->resolveAgentPathIds($row, $snapshot, $codeToId);
|
||||
foreach ($pathIds as $agentId) {
|
||||
if (! isset($agentSubtrees[$agentId])) {
|
||||
$agentSubtrees[$agentId] = [
|
||||
'gross_win_loss' => 0,
|
||||
'basic_rebate' => 0,
|
||||
'extra_rebate' => 0,
|
||||
'share_profit' => 0,
|
||||
'player_count' => 0,
|
||||
'_players_seen' => [],
|
||||
];
|
||||
}
|
||||
$agentSubtrees[$agentId]['gross_win_loss'] += $gameWinLoss;
|
||||
$agentSubtrees[$agentId]['basic_rebate'] += $basicRebate;
|
||||
$agentSubtrees[$agentId]['extra_rebate'] += $extraRebate;
|
||||
if (! in_array($playerId, $agentSubtrees[$agentId]['_players_seen'], true)) {
|
||||
$agentSubtrees[$agentId]['_players_seen'][] = $playerId;
|
||||
$agentSubtrees[$agentId]['player_count']++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($snapshot['chain_codes'] as $code) {
|
||||
$agentId = $codeToId[$code] ?? 0;
|
||||
if ($agentId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$profit = (int) round($result->finalProfits[$code] ?? 0);
|
||||
$agentSubtrees[$agentId]['share_profit'] = ($agentSubtrees[$agentId]['share_profit'] ?? 0) + $profit;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($agentSubtrees as $id => $subtree) {
|
||||
unset($agentSubtrees[$id]['_players_seen']);
|
||||
}
|
||||
|
||||
return [
|
||||
'players' => $players,
|
||||
'agent_edges' => $agentEdges,
|
||||
'agent_subtrees' => $agentSubtrees,
|
||||
'platform_share_profit' => $platformShareProfit,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{chain_codes: list<string>, total_shares: array<string, float>} $snapshot
|
||||
* @param array<string, int> $codeToId
|
||||
* @return list<int>
|
||||
*/
|
||||
private function resolveAgentPathIds(object $row, array $snapshot, array $codeToId): array
|
||||
{
|
||||
$raw = $row->share_snapshot ?? null;
|
||||
if ($raw !== null && $raw !== '') {
|
||||
$decoded = is_string($raw) ? json_decode($raw, true) : $raw;
|
||||
if (is_array($decoded) && is_array($decoded['agent_path'] ?? null)) {
|
||||
return array_values(array_map(intval(...), $decoded['agent_path']));
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($snapshot['chain_codes'] as $code) {
|
||||
$id = $codeToId[$code] ?? 0;
|
||||
if ($id > 0) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
public function siteIdForPeriod(int $periodId): int
|
||||
{
|
||||
return (int) DB::table('settlement_periods')->where('id', $periodId)->value('admin_site_id');
|
||||
}
|
||||
|
||||
private function extraRebateForTicketItem(int $ticketItemId, string $periodStart, string $periodEnd): int
|
||||
{
|
||||
if ($ticketItemId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) DB::table('rebate_records')
|
||||
->where('ticket_item_id', $ticketItemId)
|
||||
->where('rebate_type', 'extra')
|
||||
->whereIn('status', ['accrued', 'reversed'])
|
||||
->whereBetween('created_at', [$periodStart, $periodEnd])
|
||||
->sum('rebate_amount');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{total_shares: array<string, float>, chain_codes: list<string>}|null
|
||||
*/
|
||||
private function resolveSnapshotFromLedgerRow(object $row): ?array
|
||||
{
|
||||
$raw = $row->share_snapshot ?? null;
|
||||
if ($raw === null || $raw === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = is_string($raw) ? json_decode($raw, true) : $raw;
|
||||
if (! is_array($decoded)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$totalShares = $decoded['total_shares'] ?? null;
|
||||
$chainCodes = $decoded['chain_codes'] ?? null;
|
||||
if (! is_array($totalShares) || ! is_array($chainCodes) || $chainCodes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$shares = [];
|
||||
foreach ($totalShares as $code => $rate) {
|
||||
$shares[(string) $code] = (float) $rate;
|
||||
}
|
||||
|
||||
return [
|
||||
'total_shares' => $shares,
|
||||
'chain_codes' => array_values(array_map(strval(...), $chainCodes)),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user