- 在多个控制器中将权限检查从 hasAdminPermission 更新为 hasPermissionCode,以增强权限管理的灵活性。 - 引入 AdminScopePolicy,优化基于代理节点的权限和数据过滤逻辑,确保管理员能够更精确地控制访问权限。 - 在请求验证中添加 agent_node_id 字段,确保 API 接口支持代理节点的相关操作。 - 更新 AdminUser 模型,新增 hasPermissionCode 方法,以支持更细粒度的权限检查。 - 优化审计日志记录逻辑,确保在处理请求时能够准确记录管理员的操作。
186 lines
5.6 KiB
PHP
186 lines
5.6 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Player;
|
||
use App\Models\AuditLog;
|
||
use App\Models\AdminUser;
|
||
use Illuminate\Http\Request;
|
||
use App\Http\Middleware\RecordAdminApiAudit;
|
||
|
||
/**
|
||
* 审计日志写入入口:落到表 audit_logs,仅 created_at。
|
||
*
|
||
* operator_type:admin / player / system(常量见此类)。
|
||
*/
|
||
final class AuditLogger
|
||
{
|
||
/** 后台账号,operator_id = admin_users.id */
|
||
public const OPERATOR_ADMIN = 'admin';
|
||
|
||
/** 终端玩家,operator_id = players.id */
|
||
public const OPERATOR_PLAYER = 'player';
|
||
|
||
/** 系统任务(定时任务、异步 Job 无自然人操作者时使用) */
|
||
public const OPERATOR_SYSTEM = 'system';
|
||
|
||
/**
|
||
* @param array<string, mixed>|null $beforeJson
|
||
* @param array<string, mixed>|null $afterJson
|
||
*/
|
||
public static function record(
|
||
string $operatorType,
|
||
int $operatorId,
|
||
?string $moduleCode = null,
|
||
?string $actionCode = null,
|
||
?string $targetType = null,
|
||
?string $targetId = null,
|
||
?array $beforeJson = null,
|
||
?array $afterJson = null,
|
||
?string $ip = null,
|
||
?string $userAgent = null,
|
||
): AuditLog {
|
||
return AuditLog::query()->create([
|
||
'operator_type' => $operatorType,
|
||
'operator_id' => $operatorId,
|
||
'module_code' => $moduleCode,
|
||
'action_code' => $actionCode,
|
||
'target_type' => $targetType,
|
||
'target_id' => $targetId,
|
||
'before_json' => $beforeJson,
|
||
'after_json' => $afterJson,
|
||
'ip' => $ip,
|
||
'user_agent' => self::truncateUserAgent($userAgent),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 从当前 HTTP 请求补全 IP、User-Agent(后台 / 玩家 API 调用方便)。
|
||
*
|
||
* @param array<string, mixed>|null $beforeJson
|
||
* @param array<string, mixed>|null $afterJson
|
||
*/
|
||
public static function recordFromRequest(
|
||
Request $request,
|
||
string $operatorType,
|
||
int $operatorId,
|
||
?string $moduleCode = null,
|
||
?string $actionCode = null,
|
||
?string $targetType = null,
|
||
?string $targetId = null,
|
||
?array $beforeJson = null,
|
||
?array $afterJson = null,
|
||
): AuditLog {
|
||
$row = self::record(
|
||
$operatorType,
|
||
$operatorId,
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
$request->ip(),
|
||
$request->userAgent(),
|
||
);
|
||
|
||
self::markAdminApiAuditRecorded($request);
|
||
|
||
return $row;
|
||
}
|
||
|
||
/** 业务层已写审计时,避免 {@see RecordAdminApiAudit} 重复落库(FormRequest 可能与管道中的 Request 非同一实例)。 */
|
||
public static function markAdminApiAuditRecorded(Request $request): void
|
||
{
|
||
$request->attributes->set(RecordAdminApiAudit::ATTRIBUTE_AUDIT_RECORDED, true);
|
||
|
||
try {
|
||
$resolved = request();
|
||
if ($resolved !== $request) {
|
||
$resolved->attributes->set(RecordAdminApiAudit::ATTRIBUTE_AUDIT_RECORDED, true);
|
||
}
|
||
} catch (\Throwable) {
|
||
}
|
||
}
|
||
|
||
public static function recordForAdmin(AdminUser $admin, ?Request $request = null, ?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
|
||
{
|
||
if ($request !== null) {
|
||
return self::recordFromRequest(
|
||
$request,
|
||
self::OPERATOR_ADMIN,
|
||
(int) $admin->getKey(),
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
);
|
||
}
|
||
|
||
return self::record(
|
||
self::OPERATOR_ADMIN,
|
||
(int) $admin->getKey(),
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
);
|
||
}
|
||
|
||
public static function recordForPlayer(Player $player, ?Request $request = null, ?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
|
||
{
|
||
if ($request !== null) {
|
||
return self::recordFromRequest(
|
||
$request,
|
||
self::OPERATOR_PLAYER,
|
||
(int) $player->getKey(),
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
);
|
||
}
|
||
|
||
return self::record(
|
||
self::OPERATOR_PLAYER,
|
||
(int) $player->getKey(),
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
);
|
||
}
|
||
|
||
/** 定时任务或队列内无 Request 时使用。 */
|
||
public static function recordForSystem(?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
|
||
{
|
||
return self::record(
|
||
self::OPERATOR_SYSTEM,
|
||
0,
|
||
$moduleCode,
|
||
$actionCode,
|
||
$targetType,
|
||
$targetId,
|
||
$beforeJson,
|
||
$afterJson,
|
||
);
|
||
}
|
||
|
||
private static function truncateUserAgent(?string $userAgent): ?string
|
||
{
|
||
if ($userAgent === null || $userAgent === '') {
|
||
return null;
|
||
}
|
||
|
||
return mb_substr($userAgent, 0, 255);
|
||
}
|
||
}
|