后台管理系统实战

RBAC 权限模型

sql 复制代码
-- 用户表  
CREATE TABLE admins (  
    id INT PRIMARY KEY AUTO_INCREMENT,  
    username VARCHAR(50) UNIQUE,  
    password VARCHAR(255),  
    nickname VARCHAR(50),  
    status TINYINT DEFAULT 1,  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  
);  
  
-- 角色表  
CREATE TABLE roles (  
    id INT PRIMARY KEY AUTO_INCREMENT,  
    name VARCHAR(50),  
    code VARCHAR(50) UNIQUE,  
    description VARCHAR(255)  
);  
  
-- 用户角色关联  
CREATE TABLE admin_roles (  
    admin_id INT,  
    role_id INT,  
    PRIMARY KEY (admin_id, role_id)  
);  
  
-- 菜单/权限表  
CREATE TABLE permissions (  
    id INT PRIMARY KEY AUTO_INCREMENT,  
    parent_id INT DEFAULT 0,  
    name VARCHAR(50),  
    code VARCHAR(100),  
    type TINYINT,  -- 1菜单 2按钮  
    path VARCHAR(200),  
    icon VARCHAR(50),  
    sort INT DEFAULT 0  
);  
  
-- 角色权限关联  
CREATE TABLE role_permissions (  
    role_id INT,  
    permission_id INT,  
    PRIMARY KEY (role_id, permission_id)  
);

权限验证

php 复制代码
<?php  
class PermissionService  
{  
    // 获取用户所有权限  
    public function getUserPermissions(int $adminId): array  
    {  
        $cacheKey = "admin:permissions:{$adminId}";  
          
        return Cache::remember($cacheKey, 3600, function () use ($adminId) {  
            return DB::table('admin_roles as ar')  
                ->join('role_permissions as rp', 'ar.role_id', '=', 'rp.role_id')  
                ->join('permissions as p', 'rp.permission_id', '=', 'p.id')  
                ->where('ar.admin_id', $adminId)  
                ->pluck('p.code')  
                ->unique()  
                ->toArray();  
        });  
    }  
      
    // 检查权限  
    public function hasPermission(int $adminId, string $code): bool  
    {  
        $permissions = $this->getUserPermissions($adminId);  
        return in_array($code, $permissions);  
    }  
      
    // 获取用户菜单  
    public function getUserMenus(int $adminId): array  
    {  
        $permissions = DB::table('admin_roles as ar')  
            ->join('role_permissions as rp', 'ar.role_id', '=', 'rp.role_id')  
            ->join('permissions as p', 'rp.permission_id', '=', 'p.id')  
            ->where('ar.admin_id', $adminId)  
            ->where('p.type', 1)  
            ->orderBy('p.sort')  
            ->get();  
          
        return $this->buildTree($permissions);  
    }  
      
    private function buildTree($items, $parentId = 0): array  
    {  
        $tree = [];  
        foreach ($items as $item) {  
            if ($item->parent_id == $parentId) {  
                $children = $this->buildTree($items, $item->id);  
                $node = (array) $item;  
                if ($children) {  
                    $node['children'] = $children;  
                }  
                $tree[] = $node;  
            }  
        }  
        return $tree;  
    }  
}

权限中间件

php 复制代码
<?php  
class CheckPermission  
{  
    public function handle(Request $request, Closure $next, string $permission)  
    {  
        $admin = auth('admin')->user();  
          
        if (!$this->permissionService->hasPermission($admin->id, $permission)) {  
            return response()->json(['error' => '无权限'], 403);  
        }  
          
        return $next($request);  
    }  
}  
  
// 路由使用  
Route::get('/users', [UserController::class, 'index'])  
    ->middleware('permission:user.list');  
Route::post('/users', [UserController::class, 'store'])  
    ->middleware('permission:user.create');

操作日志

php 复制代码
<?php  
class OperationLog  
{  
    public static function record(string $module, string $action, $data = null): void  
    {  
        DB::table('operation_logs')->insert([  
            'admin_id' => auth('admin')->id(),  
            'module' => $module,  
            'action' => $action,  
            'data' => json_encode($data),  
            'ip' => request()->ip(),  
            'user_agent' => request()->userAgent(),  
            'created_at' => now()  
        ]);  
    }  
}  
  
// 使用  
OperationLog::record('用户管理', '创建用户', ['username' => $user->username]);

数据字典

php 复制代码
<?php  
class DictService  
{  
    public function getByType(string $type): array  
    {  
        return Cache::remember("dict:{$type}", 3600, function () use ($type) {  
            return DB::table('dicts')  
                ->where('type', $type)  
                ->where('status', 1)  
                ->orderBy('sort')  
                ->get()  
                ->toArray();  
        });  
    }  
      
    public function getValue(string $type, $key): ?string  
    {  
        $items = $this->getByType($type);  
        foreach ($items as $item) {  
            if ($item->key == $key) {  
                return $item->value;  
            }  
        }  
        return null;  
    }  
}  
  
// 使用  
$statusText = $dictService->getValue('order_status', $order->status);

通用 CRUD

php 复制代码
<?php  
abstract class BaseController  
{  
    protected string $model;  
    protected array $searchFields = [];  
      
    public function index(Request $request)  
    {  
        $query = $this->model::query();  
          
        // 搜索  
        foreach ($this->searchFields as $field) {  
            if ($request->filled($field)) {  
                $query->where($field, 'like', "%{$request->input($field)}%");  
            }  
        }  
          
        // 排序  
        $query->orderBy($request->input('sort_field', 'id'), $request->input('sort_order', 'desc'));  
          
        // 分页  
        return $query->paginate($request->input('page_size', 15));  
    }  
      
    public function store(Request $request)  
    {  
        $data = $request->validated();  
        return $this->model::create($data);  
    }  
      
    public function update(Request $request, int $id)  
    {  
        $model = $this->model::findOrFail($id);  
        $model->update($request->validated());  
        return $model;  
    }  
      
    public function destroy(int $id)  
    {  
        $this->model::findOrFail($id)->delete();  
        return response()->json(['message' => '删除成功']);  
    }  
}

导出 Excel

php 复制代码
<?php  
use PhpOffice\PhpSpreadsheet\Spreadsheet;  
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;  
  
class ExportService  
{  
    public function export(array $headers, array $data, string $filename): string  
    {  
        $spreadsheet = new Spreadsheet();  
        $sheet = $spreadsheet->getActiveSheet();  
          
        // 写入表头  
        $col = 'A';  
        foreach ($headers as $header) {  
            $sheet->setCellValue($col . '1', $header);  
            $col++;  
        }  
          
        // 写入数据  
        $row = 2;  
        foreach ($data as $item) {  
            $col = 'A';  
            foreach ($item as $value) {  
                $sheet->setCellValue($col . $row, $value);  
                $col++;  
            }  
            $row++;  
        }  
          
        $path = storage_path("exports/{$filename}.xlsx");  
        $writer = new Xlsx($spreadsheet);  
        $writer->save($path);  
          
        return $path;  
    }  
}

总结

模块 关键点
权限 RBAC 模型
菜单 树形结构
日志 操作记录
字典 配置管理

后台系统的核心是权限管理,其他功能根据业务需求扩展。

相关推荐
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530141 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908401 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金2 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒2 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境2 小时前
提升 Python 代码健壮性的方法大盘点
后端·python