- Added new section in AGENTS.md detailing learned workspace facts for better understanding of settlement processes. - Updated AgentNodeDestroyController to remove unnecessary checks for admin users. - Enhanced AgentSettlement controllers to assert permissions for finance adjustments and bill operations. - Improved query scopes in AgentSettlement services to ensure proper data access based on admin roles. - Refactored methods in SettlementPartyEnrichment for better bill row enrichment and data handling. - Introduced new methods in AdminAgentSettlementScope for managing agent node visibility and finance adjustments.
280 lines
9.2 KiB
PHP
280 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\AgentSettlement;
|
|
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/** 结算列表:玩家 / 代理 / 注单关联字段 enrichment。 */
|
|
final class SettlementPartyEnrichment
|
|
{
|
|
/**
|
|
* @param Collection<int, object> $agents keyed by id
|
|
* @return array{
|
|
* direct_agent_id: int|null,
|
|
* direct_agent_label: string|null,
|
|
* parent_agent_id: int|null,
|
|
* parent_agent_label: string|null,
|
|
* }
|
|
*/
|
|
public function agentLineLabels(?int $directAgentId, Collection $agents): array
|
|
{
|
|
if ($directAgentId === null || $directAgentId <= 0) {
|
|
return [
|
|
'direct_agent_id' => null,
|
|
'direct_agent_label' => null,
|
|
'parent_agent_id' => null,
|
|
'parent_agent_label' => null,
|
|
];
|
|
}
|
|
|
|
$direct = $agents->get($directAgentId);
|
|
$directLabel = $this->formatAgent($direct, $directAgentId);
|
|
$parentId = $direct !== null ? (int) ($direct->parent_id ?? 0) : 0;
|
|
$parent = $parentId > 0 ? $agents->get($parentId) : null;
|
|
|
|
return [
|
|
'direct_agent_id' => $directAgentId,
|
|
'direct_agent_label' => $directLabel,
|
|
'parent_agent_id' => $parentId > 0 ? $parentId : null,
|
|
'parent_agent_label' => $parentId > 0 ? $this->formatAgent($parent, $parentId) : null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $agentIds
|
|
* @return Collection<int, object>
|
|
*/
|
|
public function loadAgents(array $agentIds): Collection
|
|
{
|
|
$ids = array_values(array_unique(array_filter($agentIds, static fn (int $id): bool => $id > 0)));
|
|
if ($ids === []) {
|
|
return collect();
|
|
}
|
|
|
|
$rows = DB::table('agent_nodes')->whereIn('id', $ids)->get()->keyBy('id');
|
|
$parentIds = $rows
|
|
->pluck('parent_id')
|
|
->map(static fn ($id): int => (int) $id)
|
|
->filter(static fn (int $id): bool => $id > 0)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$missingParents = array_diff($parentIds, $ids);
|
|
if ($missingParents !== []) {
|
|
$parents = DB::table('agent_nodes')->whereIn('id', $missingParents)->get()->keyBy('id');
|
|
foreach ($parents as $id => $row) {
|
|
$rows->put((int) $id, $row);
|
|
}
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $ticketItemIds
|
|
* @return array<int, array{play_code: string|null, draw_no: string|null, ticket_item_id: int, actual_deduct_amount: int}>
|
|
*/
|
|
public function loadTicketRefs(array $ticketItemIds): array
|
|
{
|
|
$ids = array_values(array_unique(array_filter($ticketItemIds, static fn (int $id): bool => $id > 0)));
|
|
if ($ids === []) {
|
|
return [];
|
|
}
|
|
|
|
$map = [];
|
|
foreach (DB::table('ticket_items as ti')
|
|
->leftJoin('draws as d', 'd.id', '=', 'ti.draw_id')
|
|
->whereIn('ti.id', $ids)
|
|
->select(['ti.id', 'ti.play_code', 'ti.actual_deduct_amount', 'd.draw_no'])
|
|
->get() as $row) {
|
|
$map[(int) $row->id] = [
|
|
'ticket_item_id' => (int) $row->id,
|
|
'play_code' => $row->play_code !== null ? (string) $row->play_code : null,
|
|
'draw_no' => $row->draw_no !== null ? (string) $row->draw_no : null,
|
|
'actual_deduct_amount' => (int) ($row->actual_deduct_amount ?? 0),
|
|
];
|
|
}
|
|
|
|
return $map;
|
|
}
|
|
|
|
/** 结算展示:仅代理名称;编号为内部标识,无名称时才回退 code。 */
|
|
public function formatAgent(?object $agent, int $fallbackId): string
|
|
{
|
|
if ($agent === null) {
|
|
return "agent#{$fallbackId}";
|
|
}
|
|
|
|
$name = trim((string) ($agent->name ?? ''));
|
|
if ($name !== '') {
|
|
return $name;
|
|
}
|
|
|
|
$code = trim((string) ($agent->code ?? ''));
|
|
|
|
return $code !== '' ? $code : "agent#{$fallbackId}";
|
|
}
|
|
|
|
public function formatPlayerUsername(?object $player): ?string
|
|
{
|
|
if ($player === null) {
|
|
return null;
|
|
}
|
|
|
|
$username = trim((string) ($player->username ?? ''));
|
|
|
|
return $username !== '' ? $username : null;
|
|
}
|
|
|
|
public function formatPlayerSiteId(?object $player): ?string
|
|
{
|
|
if ($player === null) {
|
|
return null;
|
|
}
|
|
|
|
$sitePlayerId = trim((string) ($player->site_player_id ?? ''));
|
|
|
|
return $sitePlayerId !== '' ? $sitePlayerId : null;
|
|
}
|
|
|
|
public function formatCounterpartyLabel(string $type, int $id, Collection $agents): string
|
|
{
|
|
if ($type === 'platform' || $id <= 0) {
|
|
return 'platform';
|
|
}
|
|
|
|
if ($type === 'agent') {
|
|
return $this->formatAgent($agents->get($id), $id);
|
|
}
|
|
|
|
return "{$type}#{$id}";
|
|
}
|
|
|
|
/**
|
|
* @param iterable<int, object> $rows
|
|
* @return list<array<string, mixed>>
|
|
*/
|
|
public function enrichBillRows(iterable $rows): array
|
|
{
|
|
$items = collect($rows);
|
|
if ($items->isEmpty()) {
|
|
return [];
|
|
}
|
|
|
|
$playerIds = [];
|
|
$agentIds = [];
|
|
foreach ($items as $row) {
|
|
if ((string) $row->owner_type === 'player') {
|
|
$playerIds[] = (int) $row->owner_id;
|
|
} elseif ((string) $row->owner_type === 'agent') {
|
|
$agentIds[] = (int) $row->owner_id;
|
|
}
|
|
if ((string) $row->counterparty_type === 'agent' && (int) $row->counterparty_id > 0) {
|
|
$agentIds[] = (int) $row->counterparty_id;
|
|
}
|
|
}
|
|
|
|
$players = $playerIds !== []
|
|
? DB::table('players')
|
|
->whereIn('id', array_unique($playerIds))
|
|
->select(['id', 'username', 'site_player_id', 'agent_node_id', 'funding_mode', 'auth_source'])
|
|
->get()
|
|
->keyBy('id')
|
|
: collect();
|
|
|
|
foreach ($players as $player) {
|
|
$aid = (int) ($player->agent_node_id ?? 0);
|
|
if ($aid > 0) {
|
|
$agentIds[] = $aid;
|
|
}
|
|
}
|
|
|
|
$agents = $this->loadAgents($agentIds);
|
|
|
|
$out = [];
|
|
foreach ($items as $row) {
|
|
$out[] = $this->enrichBillRowWithLookups($row, $players, $agents);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
public function enrichBillRow(object $row): array
|
|
{
|
|
return $this->enrichBillRows([$row])[0];
|
|
}
|
|
|
|
/**
|
|
* @param Collection<int, object> $players
|
|
* @param Collection<int, object> $agents
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function enrichBillRowWithLookups(object $row, Collection $players, Collection $agents): array
|
|
{
|
|
$item = (array) $row;
|
|
$ownerType = (string) $row->owner_type;
|
|
$counterType = (string) $row->counterparty_type;
|
|
$counterId = (int) $row->counterparty_id;
|
|
|
|
$item['owner_label'] = $this->legacyOwnerLabel($ownerType, (int) $row->owner_id, $players, $agents);
|
|
$item['counterparty_label'] = $this->formatCounterpartyLabel($counterType, $counterId, $agents);
|
|
|
|
$item['player_username'] = null;
|
|
$item['player_site_player_id'] = null;
|
|
$item['player_id_display'] = null;
|
|
$item['direct_agent_label'] = null;
|
|
$item['superior_agent_label'] = null;
|
|
$item['owner_party_label'] = null;
|
|
|
|
if ($ownerType === 'player') {
|
|
$player = $players->get((int) $row->owner_id);
|
|
$item['player_username'] = $this->formatPlayerUsername($player);
|
|
$item['player_site_player_id'] = $this->formatPlayerSiteId($player);
|
|
$item['player_id_display'] = (int) $row->owner_id;
|
|
$item['owner_funding_mode'] = $player !== null ? (string) ($player->funding_mode ?? '') : null;
|
|
$item['owner_auth_source'] = $player !== null ? $player->auth_source : null;
|
|
|
|
$directId = $counterType === 'agent' ? $counterId : (int) ($player->agent_node_id ?? 0);
|
|
$line = $this->agentLineLabels($directId > 0 ? $directId : null, $agents);
|
|
$item['direct_agent_label'] = $line['direct_agent_label'];
|
|
$item['superior_agent_label'] = $line['parent_agent_label'];
|
|
} elseif ($ownerType === 'agent') {
|
|
$ownerAgentId = (int) $row->owner_id;
|
|
$item['owner_party_label'] = $this->formatAgent($agents->get($ownerAgentId), $ownerAgentId);
|
|
$item['superior_agent_label'] = $counterType === 'platform'
|
|
? 'platform'
|
|
: $this->formatCounterpartyLabel($counterType, $counterId, $agents);
|
|
}
|
|
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* @param Collection<int, object> $players
|
|
* @param Collection<int, object> $agents
|
|
*/
|
|
private function legacyOwnerLabel(
|
|
string $type,
|
|
int $id,
|
|
Collection $players,
|
|
Collection $agents,
|
|
): string {
|
|
if ($type === 'player') {
|
|
$player = $players->get($id);
|
|
|
|
return $player !== null
|
|
? (string) ($player->username ?: $player->site_player_id ?: "player#{$id}")
|
|
: "player#{$id}";
|
|
}
|
|
|
|
if ($type === 'agent') {
|
|
return $this->formatAgent($agents->get($id), $id);
|
|
}
|
|
|
|
return "{$type}#{$id}";
|
|
}
|
|
}
|