feat: enhance admin role management and reconciliation features
- Updated AGENTS.md to clarify site admin roles and their associated permissions. - Refactored reconciliation controllers to include admin user validation and improved access control based on admin roles. - Enhanced AdminReconcileJobService to support player-specific reconciliation and site-based job creation. - Removed deprecated rebate commission report functionality from the API and related services. - Improved dashboard overview builders to accommodate new site operator roles and their specific functionalities.
This commit is contained in:
206
app/Support/AdminReconcileScope.php
Normal file
206
app/Support/AdminReconcileScope.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Models\AdminSite;
|
||||
use App\Models\AdminUser;
|
||||
use App\Models\AgentNode;
|
||||
use App\Models\Player;
|
||||
use App\Models\ReconcileJob;
|
||||
use App\Support\ApiMessage;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
/** 对账任务:按接入站点(及绑定代理子树)收敛可见范围。 */
|
||||
final class AdminReconcileScope
|
||||
{
|
||||
/**
|
||||
* @param Builder<ReconcileJob> $query
|
||||
*/
|
||||
public static function applyToJobsQuery(Builder $query, AdminUser $admin): void
|
||||
{
|
||||
if ($admin->isSuperAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$siteIds = $admin->accessibleAdminSiteIds();
|
||||
if ($siteIds === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($siteIds === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$siteCodes = AdminSite::query()
|
||||
->whereIn('id', $siteIds)
|
||||
->pluck('code')
|
||||
->map(static fn ($code): string => (string) $code)
|
||||
->all();
|
||||
|
||||
$agent = AdminAgentScope::primaryAgentNode($admin);
|
||||
|
||||
$query->where(function (Builder $outer) use ($siteIds, $siteCodes, $agent): void {
|
||||
$outer->whereIn('admin_site_id', $siteIds);
|
||||
|
||||
if ($siteCodes !== []) {
|
||||
$outer->orWhere(function (Builder $legacy) use ($siteCodes, $agent): void {
|
||||
$legacy->whereNull('admin_site_id')
|
||||
->whereHas('items', function (Builder $items) use ($siteCodes, $agent): void {
|
||||
self::applyItemsTransferPlayerScope($items, $siteCodes, $agent);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ($agent instanceof AgentNode) {
|
||||
$query->whereHas('items', function (Builder $items) use ($agent): void {
|
||||
self::applyItemsAgentSubtree($items, $agent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static function jobAccessible(AdminUser $admin, ReconcileJob $job): bool
|
||||
{
|
||||
if ($admin->isSuperAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$siteIds = $admin->accessibleAdminSiteIds();
|
||||
if ($siteIds === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($siteIds === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$jobSiteId = $job->admin_site_id !== null ? (int) $job->admin_site_id : null;
|
||||
if ($jobSiteId !== null) {
|
||||
if (! in_array($jobSiteId, $siteIds, true)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$siteCodes = AdminSite::query()
|
||||
->whereIn('id', $siteIds)
|
||||
->pluck('code')
|
||||
->map(static fn ($code): string => (string) $code)
|
||||
->all();
|
||||
|
||||
$hasScopedItem = $job->items()
|
||||
->where(function (Builder $items) use ($siteCodes, $admin): void {
|
||||
$agent = AdminAgentScope::primaryAgentNode($admin);
|
||||
self::applyItemsTransferPlayerScope($items, $siteCodes, $agent);
|
||||
})
|
||||
->exists();
|
||||
|
||||
if (! $hasScopedItem) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$agent = AdminAgentScope::primaryAgentNode($admin);
|
||||
if ($agent === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $job->items()
|
||||
->where(function (Builder $items) use ($agent): void {
|
||||
self::applyItemsAgentSubtree($items, $agent);
|
||||
})
|
||||
->exists();
|
||||
}
|
||||
|
||||
public static function resolveAdminSiteIdForJob(AdminUser $admin, ?Player $player = null): ?int
|
||||
{
|
||||
if ($player !== null) {
|
||||
$siteId = AdminSite::query()
|
||||
->where('code', (string) $player->site_code)
|
||||
->value('id');
|
||||
|
||||
return $siteId !== null ? (int) $siteId : null;
|
||||
}
|
||||
|
||||
if ($admin->isSuperAdmin()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$agent = AdminAgentScope::primaryAgentNode($admin);
|
||||
if ($agent !== null) {
|
||||
return (int) $agent->admin_site_id;
|
||||
}
|
||||
|
||||
$siteIds = $admin->accessibleAdminSiteIds();
|
||||
|
||||
return $siteIds[0] ?? null;
|
||||
}
|
||||
|
||||
public static function denyUnlessJobAccessible(AdminUser $admin, ReconcileJob $job): ?JsonResponse
|
||||
{
|
||||
if (! self::jobAccessible($admin, $job)) {
|
||||
return ApiMessage::errorResponse(
|
||||
request(),
|
||||
'admin.reconcile_job_access_denied',
|
||||
ErrorCode::AdminForbidden->value,
|
||||
null,
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<\App\Models\ReconcileItem> $query
|
||||
* @param list<string> $siteCodes
|
||||
*/
|
||||
public static function applyItemsTransferPlayerScope(
|
||||
Builder $query,
|
||||
array $siteCodes,
|
||||
?AgentNode $agent = null,
|
||||
): void {
|
||||
if ($siteCodes === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$query->whereExists(function ($sub) use ($siteCodes, $agent): void {
|
||||
$sub->selectRaw('1')
|
||||
->from('transfer_orders')
|
||||
->join('players', 'players.id', '=', 'transfer_orders.player_id')
|
||||
->whereColumn('transfer_orders.transfer_no', 'reconcile_items.side_a_ref');
|
||||
|
||||
$sub->whereIn('players.site_code', $siteCodes);
|
||||
|
||||
if ($agent instanceof AgentNode) {
|
||||
$sub->whereIn('players.agent_node_id', function ($nodes) use ($agent): void {
|
||||
$nodes->select('id')
|
||||
->from('agent_nodes')
|
||||
->where('path', 'like', $agent->path.'%');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<\App\Models\ReconcileItem> $query
|
||||
*/
|
||||
private static function applyItemsAgentSubtree(Builder $query, AgentNode $agent): void
|
||||
{
|
||||
$query->whereExists(function ($sub) use ($agent): void {
|
||||
$sub->selectRaw('1')
|
||||
->from('transfer_orders')
|
||||
->join('players', 'players.id', '=', 'transfer_orders.player_id')
|
||||
->whereColumn('transfer_orders.transfer_no', 'reconcile_items.side_a_ref')
|
||||
->whereIn('players.agent_node_id', function ($nodes) use ($agent): void {
|
||||
$nodes->select('id')
|
||||
->from('agent_nodes')
|
||||
->where('path', 'like', $agent->path.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user