后台管理系统实战

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 模型
菜单 树形结构
日志 操作记录
字典 配置管理

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

相关推荐
卷福同学1 天前
【养虾日记】Openclaw操作浏览器自动化发文
人工智能·后端·算法
江湖十年1 天前
Go 并发控制:sync.Pool 详解
后端·面试·go
jwn9991 天前
Spring Boot 整合 Keycloak
java·spring boot·后端
mldlds1 天前
SpringBoot详解
java·spring boot·后端
kang_jin1 天前
Spring Boot 自动配置
java·spring boot·后端
yuweiade1 天前
Spring Boot中使用Server-Sent Events (SSE) 实现实时数据推送教程
java·spring boot·后端
小箌1 天前
springboot_03
spring boot·后端·状态模式
冬奇Lab1 天前
一天一个开源项目(第54篇):Supabase - 开源的 Postgres 开发平台,Firebase 替代方案
后端·开源·资讯
中年程序员一枚1 天前
spring-cloud-starter-openfeign现实中的运行逻辑
java·spring boot·后端
why技术1 天前
我拿到了腾讯QClaw的内测码,然后沉默了。
前端·后端