初始化项目

This commit is contained in:
2026-02-08 22:38:13 +08:00
commit 334d2c6312
201 changed files with 32724 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log as LaravelLog;
use App\Services\System\LogService;
use Throwable;
class LogRequestMiddleware
{
protected $logService;
public function __construct(LogService $logService)
{
$this->logService = $logService;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2); // 转换为毫秒
// 异步记录日志,不影响响应速度
$this->logRequest($request, $response, $executionTime);
return $response;
}
/**
* 记录请求日志
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @param float $executionTime
* @return void
*/
protected function logRequest(Request $request, $response, float $executionTime): void
{
try {
// 获取当前用户信息
$user = Auth::guard('admin')->user();
$userId = $user ? $user->id : null;
$username = $user ? $user->username : 'guest';
// 解析模块和操作
$module = $this->parseModule($request->path());
$action = $this->parseAction($request->method(), $request->path());
// 获取请求参数(排除敏感信息)
$params = $this->sanitizeParams($request->all());
// 获取响应数据
$result = null;
if ($response->getStatusCode() >= 400) {
$result = $response->getContent();
}
// 确定日志状态
$status = $response->getStatusCode() < 400 ? 'success' : 'error';
$errorMessage = null;
if ($status === 'error') {
$errorMessage = $this->extractErrorMessage($result);
}
// 构建日志数据
$logData = [
'user_id' => $userId,
'username' => $username,
'module' => $module,
'action' => $action,
'method' => $request->method(),
'url' => $request->fullUrl(),
'ip' => $this->getClientIp($request),
'user_agent' => $request->userAgent(),
'params' => $params,
'result' => $result,
'status_code' => $response->getStatusCode(),
'status' => $status,
'error_message' => $errorMessage,
'execution_time' => $executionTime,
];
// 记录到数据库
$this->logService->create($logData);
// 同时记录到 Laravel 日志(用于错误)
if ($status === 'error') {
LaravelLog::error('API Request Error', [
'url' => $request->fullUrl(),
'method' => $request->method(),
'user_id' => $userId,
'error' => $errorMessage,
]);
}
} catch (Throwable $e) {
// 记录日志失败不影响业务流程
LaravelLog::error('Log request failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
}
}
/**
* 解析模块名称
*
* @param string $path
* @return string
*/
protected function parseModule(string $path): string
{
$segments = explode('/', trim($path, '/'));
// 移除前缀(如 admin, api
$prefixes = ['admin', 'api'];
if (in_array($segments[0], $prefixes)) {
array_shift($segments);
}
return $segments[0] ?? 'unknown';
}
/**
* 解析操作名称
*
* @param string $method
* @param string $path
* @return string
*/
protected function parseAction(string $method, string $path): string
{
$segments = explode('/', trim($path, '/'));
// 获取资源名称
$resource = end($segments);
// 如果资源是 ID则取前一个作为资源名
if (is_numeric($resource)) {
$resource = prev($segments);
}
// 根据方法映射操作
$actionMap = [
'GET' => '查询',
'POST' => '创建',
'PUT' => '更新',
'PATCH' => '更新',
'DELETE' => '删除',
];
$action = $actionMap[$method] ?? '操作';
return $action . ' ' . $resource;
}
/**
* 清理敏感参数
*
* @param array $params
* @return array
*/
protected function sanitizeParams(array $params): array
{
$sensitiveKeys = ['password', 'password_confirmation', 'token', 'secret', 'key'];
array_walk_recursive($params, function (&$value, $key) use ($sensitiveKeys) {
if (in_array(strtolower($key), $sensitiveKeys)) {
$value = '******';
}
});
return $params;
}
/**
* 提取错误信息
*
* @param string|null $content
* @return string|null
*/
protected function extractErrorMessage(?string $content): ?string
{
if (empty($content)) {
return null;
}
try {
$data = json_decode($content, true);
if (isset($data['message'])) {
return $data['message'];
}
} catch (\Exception $e) {
// JSON 解析失败,返回原始内容
}
return $content;
}
/**
* 获取客户端 IP
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function getClientIp(Request $request): string
{
$ip = $request->ip();
// 检查代理头
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_CLIENT_IP',
'HTTP_X_REAL_IP',
'HTTP_CF_CONNECTING_IP',
];
foreach ($headers as $header) {
if ($request->hasHeader($header)) {
$forwardedIps = explode(',', $request->header($header));
$ip = trim($forwardedIps[0]);
break;
}
}
return $ip;
}
}