后端 (Laravel 10 + Sanctum): - RBAC 四层权限系统 (users→roles→permissions→menus) - 门店隔离中间件 (BelongsToStore Trait + StoreIsolation Middleware) - 操作日志中间件 (自动记录写操作) - 权限检查中间件 (CheckPermission) - 16张数据库表迁移 (系统基础+RBAC+字典+配置) - 11个 Eloquent Model - Auth API (登录/登出/用户信息) - 系统设置模块 CRUD (门店/部门/职务/用户/角色/菜单/权限/字典/日志) - 45条 RESTful API 路由 - InitSeeder 初始数据 (超管/角色/76权限/31菜单) 前端 (Vue 3 + Element Plus + Vite): - Axios 请求封装 + Token 注入 - Pinia 状态管理 (user + permission store) - 动态路由 (服务端菜单→前端路由自动生成) - 后台布局 (侧边栏+顶栏+主内容区) - 登录页 + 仪表盘首页 - 系统设置 7 个 CRUD 页面 技术方案文档 (7卷): - 技术总览/数据库设计/API规范/RBAC设计/模块详设/小程序设计/部署方案
87 lines
2.2 KiB
PHP
87 lines
2.2 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Middleware;
|
||
|
||
use Closure;
|
||
use Illuminate\Http\Request;
|
||
use App\Models\OperationLog as OperationLogModel;
|
||
|
||
/**
|
||
* 操作日志中间件
|
||
* 自动记录写操作(POST/PUT/PATCH/DELETE)
|
||
*/
|
||
class OperationLog
|
||
{
|
||
/**
|
||
* 不记录的路由
|
||
*/
|
||
private array $except = [
|
||
'api/v1/auth/login',
|
||
'api/v1/auth/me',
|
||
];
|
||
|
||
public function handle(Request $request, Closure $next)
|
||
{
|
||
$response = $next($request);
|
||
|
||
// 仅记录写操作
|
||
if (!in_array($request->method(), ['POST', 'PUT', 'PATCH', 'DELETE'])) {
|
||
return $response;
|
||
}
|
||
|
||
// 排除白名单
|
||
foreach ($this->except as $pattern) {
|
||
if ($request->is($pattern)) {
|
||
return $response;
|
||
}
|
||
}
|
||
|
||
$user = $request->user();
|
||
if (!$user) {
|
||
return $response;
|
||
}
|
||
|
||
try {
|
||
// 从 URL 推断模块和操作
|
||
$path = $request->path();
|
||
$module = $this->parseModule($path);
|
||
$action = $this->parseAction($request->method());
|
||
|
||
OperationLogModel::create([
|
||
'store_id' => $user->store_id,
|
||
'user_id' => $user->id,
|
||
'user_name' => $user->name,
|
||
'module' => $module,
|
||
'action' => $action,
|
||
'target' => $path,
|
||
'ip' => $request->ip(),
|
||
'method' => $request->method(),
|
||
'url' => $request->fullUrl(),
|
||
'created_at' => now(),
|
||
]);
|
||
} catch (\Throwable $e) {
|
||
// 日志记录失败不影响业务
|
||
\Log::warning('操作日志记录失败: ' . $e->getMessage());
|
||
}
|
||
|
||
return $response;
|
||
}
|
||
|
||
private function parseModule(string $path): string
|
||
{
|
||
// api/v1/system/users → system
|
||
$segments = explode('/', $path);
|
||
return $segments[2] ?? 'unknown';
|
||
}
|
||
|
||
private function parseAction(string $method): string
|
||
{
|
||
return match ($method) {
|
||
'POST' => 'create',
|
||
'PUT', 'PATCH' => 'update',
|
||
'DELETE' => 'delete',
|
||
default => $method,
|
||
};
|
||
}
|
||
}
|