Files
lotteryLaravel/app/Support/AuditLogApiPresenter.php
kang 1dcd4716c5 refactor: 更新权限管理与请求验证逻辑
- 在多个控制器中将权限检查从 hasAdminPermission 更新为 hasPermissionCode,以增强权限管理的灵活性。
- 引入 AdminScopePolicy,优化基于代理节点的权限和数据过滤逻辑,确保管理员能够更精确地控制访问权限。
- 在请求验证中添加 agent_node_id 字段,确保 API 接口支持代理节点的相关操作。
- 更新 AdminUser 模型,新增 hasPermissionCode 方法,以支持更细粒度的权限检查。
- 优化审计日志记录逻辑,确保在处理请求时能够准确记录管理员的操作。
2026-06-03 10:07:38 +08:00

228 lines
7.3 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\Support;
use App\Models\AuditLog;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* 审计日志列表展示:模块 / 动作 / 目标中文标签API 资源名 + 业务码映射)。
*/
final class AuditLogApiPresenter
{
/** @var array<string, string> */
private const MODULE_LABELS = [
'agent' => '代理',
'system' => '系统',
'settings' => '系统设置',
'integration' => '对接站点',
'player_manage' => '玩家管理',
'risk_cap' => '风险池',
'odds' => '赔率',
'play_config' => '玩法配置',
'settlement' => '结算',
'report_jobs' => '报表任务',
'reconcile_jobs' => '对账任务',
'draw' => '开奖',
'wallet' => '钱包',
'reconcile' => '对账',
'job' => '任务',
'bootstrap' => '系统',
];
/** @var array<string, string> */
private const ACTION_LABELS = [
'agent_role.sync_permissions' => '同步代理角色权限',
'admin_role.sync_permissions' => '同步平台角色权限',
'admin_role.create' => '创建平台角色',
'admin_role.update' => '更新平台角色',
'admin_role.delete' => '删除平台角色',
'agent_role.create' => '创建代理角色',
'agent_role.update' => '更新代理角色',
'agent_role.destroy' => '删除代理角色',
'agent_admin_user.create' => '创建代理账号',
'agent_admin_user.sync_roles' => '同步代理账号角色',
'agent_node.create' => '创建代理节点',
'agent_node.update' => '更新代理节点',
'agent_node.destroy' => '删除代理节点',
'agent.delegation.sync' => '同步代理授权',
'admin_user.create' => '创建管理员',
'admin_user.update' => '更新管理员',
'admin_user.delete' => '删除管理员',
'sync_permissions' => '同步权限',
'batch_update' => '批量更新设置',
'payout_adjustment' => '派彩调整',
'rotate_secrets' => '轮换密钥',
'toggle_active' => '切换启用状态',
'enqueue' => '提交报表任务',
];
/** @var array<string, string> */
private const TARGET_TYPE_LABELS = [
'admin_role' => '角色',
'admin_user' => '管理员',
'agent_node' => '代理节点',
'admin_site' => '对接站点',
'player' => '玩家',
'lottery_settings' => '系统设置',
'lottery_setting' => '设置项',
'risk_cap_version' => '风险池版本',
'odds_version' => '赔率版本',
'play_config_item' => '玩法',
'play_config_version' => '玩法配置版本',
'settlement_batch_adjustment' => '结算调整单',
'report_job' => '报表任务',
'reconcile_job' => '对账任务',
'transfer_no' => '转账单',
'draw' => '期次',
'batch' => '批次',
];
/** @var array<string, string> */
private const VERB_LABELS = [
'sync' => '同步',
'store' => '创建',
'update' => '更新',
'destroy' => '删除',
'delete' => '删除',
'create' => '创建',
'publish' => '发布',
'reopen' => '重新开奖',
'freeze' => '冻结',
'unfreeze' => '解冻',
'run' => '执行',
'transfer' => '转账',
'start' => '开始',
'test' => '测试',
];
/**
* @param LengthAwarePaginator<AuditLog> $paginator
* @return array{items: list<array<string, mixed>>, meta: array{current_page: int, per_page: int, total: int, last_page: int}}
*/
public static function listPayload(LengthAwarePaginator $paginator): array
{
$items = collect($paginator->items());
$resourceNames = self::loadResourceNames($items);
return AdminApiList::payload(
$paginator,
fn (AuditLog $r) => self::row($r, $resourceNames),
);
}
/**
* @param array<string, string> $resourceNames
* @return array<string, mixed>
*/
public static function row(AuditLog $r, array $resourceNames = []): array
{
return [
'id' => (int) $r->id,
'operator_type' => $r->operator_type,
'operator_id' => (int) $r->operator_id,
'module_code' => $r->module_code,
'action_code' => $r->action_code,
'target_type' => $r->target_type,
'target_id' => $r->target_id,
'module_label' => self::moduleLabel($r->module_code),
'action_label' => self::actionLabel($r, $resourceNames),
'target_label' => self::targetLabel($r, $resourceNames),
'before_json' => $r->before_json,
'after_json' => $r->after_json,
'ip' => $r->ip,
'user_agent' => $r->user_agent,
'created_at' => $r->created_at?->toIso8601String(),
];
}
/**
* @param Collection<int, AuditLog> $items
* @return array<string, string>
*/
private static function loadResourceNames(Collection $items): array
{
$codes = $items
->pluck('target_type')
->filter(fn (?string $type) => self::isApiResourceCode($type))
->unique()
->values()
->all();
if ($codes === []) {
return [];
}
/** @var array<string, string> */
return DB::table('admin_api_resources')
->whereIn('code', $codes)
->pluck('name', 'code')
->all();
}
private static function moduleLabel(?string $code): string
{
if ($code === null || $code === '') {
return '—';
}
return self::MODULE_LABELS[$code] ?? $code;
}
/**
* @param array<string, string> $resourceNames
*/
private static function actionLabel(AuditLog $r, array $resourceNames): string
{
$action = (string) ($r->action_code ?? '');
if ($action === '') {
return '—';
}
if (isset(self::ACTION_LABELS[$action])) {
return self::ACTION_LABELS[$action];
}
$targetType = (string) ($r->target_type ?? '');
if (self::isApiResourceCode($targetType) && isset($resourceNames[$targetType])) {
return $resourceNames[$targetType];
}
if (isset(self::VERB_LABELS[$action])) {
return self::VERB_LABELS[$action];
}
return $action;
}
/**
* @param array<string, string> $resourceNames
*/
private static function targetLabel(AuditLog $r, array $resourceNames): string
{
$type = (string) ($r->target_type ?? '');
$id = trim((string) ($r->target_id ?? ''));
if ($type === '') {
return $id !== '' ? '#'.$id : '—';
}
if (self::isApiResourceCode($type)) {
$name = $resourceNames[$type] ?? $type;
return $id !== '' ? sprintf('%s #%s', $name, $id) : $name;
}
$typeLabel = self::TARGET_TYPE_LABELS[$type] ?? $type;
return $id !== '' ? sprintf('%s #%s', $typeLabel, $id) : $typeLabel;
}
private static function isApiResourceCode(?string $code): bool
{
return is_string($code) && str_starts_with($code, 'admin.');
}
}