- 将玩法相关的显示名称字段统一为 `display_name`,移除多语言字段。 - 在 `PlayTypePatchController` 中新增即时切换玩法开关的功能,并推送大厅更新。 - 优化多个控制器和服务中的权限检查与数据处理逻辑,提升代码可读性与维护性。
144 lines
4.1 KiB
PHP
144 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use App\Models\AdminUser;
|
|
use App\Services\AuditLogger;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
/**
|
|
* 对 admin_api_resources.is_audit_required=true 的变更类请求,在成功响应后写入审计日志。
|
|
*
|
|
* 若请求已设置 {@see Request::ATTRIBUTE_AUDIT_RECORDED}(业务层已写更细快照),则跳过。
|
|
*/
|
|
final class RecordAdminApiAudit
|
|
{
|
|
public const ATTRIBUTE_AUDIT_RECORDED = 'admin_audit_recorded';
|
|
|
|
private const MUTATING_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'];
|
|
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$response = $next($request);
|
|
|
|
if (! $this->shouldRecord($request, $response)) {
|
|
return $response;
|
|
}
|
|
|
|
$admin = $request->user();
|
|
if (! $admin instanceof AdminUser) {
|
|
return $response;
|
|
}
|
|
|
|
$resource = $this->resolveResource($request);
|
|
if ($resource === null || ! (bool) $resource->is_audit_required) {
|
|
return $response;
|
|
}
|
|
|
|
$targetId = $this->resolveTargetId($request);
|
|
$actionCode = $this->resolveActionCode((string) $resource->code);
|
|
|
|
AuditLogger::recordForAdmin(
|
|
$admin,
|
|
$request,
|
|
moduleCode: (string) $resource->module_code,
|
|
actionCode: $actionCode,
|
|
targetType: (string) $resource->code,
|
|
targetId: $targetId,
|
|
beforeJson: null,
|
|
afterJson: [
|
|
'http_method' => $request->method(),
|
|
'route_name' => $this->normalizeRouteName((string) ($request->route()?->getName() ?? '')),
|
|
'status' => $response->getStatusCode(),
|
|
'payload' => $this->sanitizedPayload($request),
|
|
],
|
|
);
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function shouldRecord(Request $request, Response $response): bool
|
|
{
|
|
if ($request->attributes->get(self::ATTRIBUTE_AUDIT_RECORDED) === true) {
|
|
return false;
|
|
}
|
|
|
|
if (! in_array(strtoupper($request->method()), self::MUTATING_METHODS, true)) {
|
|
return false;
|
|
}
|
|
|
|
$status = $response->getStatusCode();
|
|
|
|
return $status >= 200 && $status < 300;
|
|
}
|
|
|
|
private function resolveResource(Request $request): ?object
|
|
{
|
|
$routeName = $request->route()?->getName();
|
|
if (! is_string($routeName) || $routeName === '') {
|
|
return null;
|
|
}
|
|
|
|
return DB::table('admin_api_resources')
|
|
->where('route_name', $this->normalizeRouteName($routeName))
|
|
->where('status', 1)
|
|
->first(['code', 'module_code', 'is_audit_required']);
|
|
}
|
|
|
|
private function normalizeRouteName(string $routeName): string
|
|
{
|
|
return preg_replace('/^(api\.v1\.admin\.)+/', 'api.v1.admin.', $routeName) ?? $routeName;
|
|
}
|
|
|
|
private function resolveActionCode(string $resourceCode): string
|
|
{
|
|
$pos = strrpos($resourceCode, '.');
|
|
if ($pos === false) {
|
|
return $resourceCode;
|
|
}
|
|
|
|
return substr($resourceCode, $pos + 1);
|
|
}
|
|
|
|
private function resolveTargetId(Request $request): ?string
|
|
{
|
|
$route = $request->route();
|
|
if ($route === null) {
|
|
return null;
|
|
}
|
|
|
|
foreach (['batch', 'draw', 'transfer_no', 'player', 'admin_user', 'admin_role', 'id', 'play_code', 'number_4d', 'key'] as $key) {
|
|
$value = $route->parameter($key);
|
|
if ($value === null) {
|
|
continue;
|
|
}
|
|
|
|
if (is_object($value) && method_exists($value, 'getKey')) {
|
|
return (string) $value->getKey();
|
|
}
|
|
|
|
return (string) $value;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function sanitizedPayload(Request $request): ?array
|
|
{
|
|
$data = $request->except([
|
|
'password',
|
|
'password_confirmation',
|
|
'current_password',
|
|
'token',
|
|
]);
|
|
|
|
return $data === [] ? null : $data;
|
|
}
|
|
}
|