醉梦人间三千年 7 månader sedan
förälder
incheckning
e807199b0f
36 ändrade filer med 1768 tillägg och 266 borttagningar
  1. 1 1
      .env
  2. 1 0
      .gitignore
  3. 12 0
      app/Enums/Common/ErrorMessage.php
  4. 12 0
      app/Enums/System/MenuType.php
  5. 6 5
      app/Exceptions/Handler.php
  6. 20 17
      app/Http/Controllers/Backend/Server/System/AuthController.php
  7. 22 3
      app/Http/Controllers/Backend/Server/System/DictDataController.php
  8. 39 5
      app/Http/Controllers/Backend/Server/System/DictTypeController.php
  9. 4 4
      app/Http/Controllers/Backend/Server/System/MenuController.php
  10. 76 0
      app/Http/Controllers/Backend/Server/System/RoleController.php
  11. 59 0
      app/Http/Controllers/Backend/Server/System/UserController.php
  12. 51 2
      app/Http/Services/Backend/Server/System/DictDataService.php
  13. 63 4
      app/Http/Services/Backend/Server/System/DictTypeService.php
  14. 84 33
      app/Http/Services/Backend/Server/System/MenuService.php
  15. 5 0
      app/Models/System/DictType.php
  16. 31 65
      app/Models/System/Menu.php
  17. 66 0
      app/Models/System/Role.php
  18. 7 3
      app/Models/System/User.php
  19. 3 1
      composer.json
  20. 492 103
      composer.lock
  21. 186 0
      config/permission.php
  22. 1 1
      database/migrations/0001_01_01_000000_create_system_users_table.php
  23. 151 0
      database/migrations/2024_08_14_093112_create_permission_tables.php
  24. 33 0
      database/migrations/2024_08_26_111055_create_system_dict_type_table.php
  25. 38 0
      database/migrations/2024_08_27_031435_create_system_dict_data_table.php
  26. 65 0
      database/migrations/2024_08_29_032939_create_system_menus_table.php
  27. 1 0
      database/seeders/DatabaseSeeder.php
  28. 29 0
      database/seeders/SystemDictTableSeeder.php
  29. 60 0
      database/seeders/SystemMenusTableSeeder.php
  30. 21 0
      database/seeders/SystemModelHasRolesTableSeeder.php
  31. 56 0
      database/seeders/SystemPermissionsTableSeeder.php
  32. 28 0
      database/seeders/SystemRoleHasPermissionsTableSeeder.php
  33. 22 0
      database/seeders/SystemRolesTableSeeder.php
  34. 22 0
      database/seeders/SystemUsersTableSeeder.php
  35. 0 19
      tests/Feature/Backend/Server/SystemTest.php
  36. 1 0
      tests/Feature/ExampleTest.php

+ 1 - 1
.env

@@ -23,7 +23,7 @@ LOG_LEVEL=debug
 DB_CONNECTION=mysql
 DB_HOST=127.0.0.1
 DB_PORT=3306
-DB_DATABASE=massage_pro
+DB_DATABASE=massage_new
 DB_USERNAME=root
 DB_PASSWORD=root
 

+ 1 - 0
.gitignore

@@ -5,6 +5,7 @@
 /public/storage
 /storage/*.key
 /vendor
+.env
 .env.backup
 .env.production
 .phpactor.json

+ 12 - 0
app/Enums/Common/ErrorMessage.php

@@ -0,0 +1,12 @@
+<?php declare(strict_types=1);
+
+namespace App\Enums\Common;
+
+use BenSampo\Enum\Enum;
+
+final class ErrorMessage extends Enum
+{
+    const DICT_TYPE_DUPLICATE = '字典类型重复'; // 关闭
+    const DICT_TYPE_EXISTS_DATA = '字典类型存在数据'; // 关闭
+
+}

+ 12 - 0
app/Enums/System/MenuType.php

@@ -0,0 +1,12 @@
+<?php declare(strict_types=1);
+
+namespace App\Enums\System;
+
+use BenSampo\Enum\Enum;
+
+final class MenuType extends Enum
+{
+    const DIR = 1;
+    const MENU = 2;
+    const BUTTON = 3;
+}

+ 6 - 5
app/Exceptions/Handler.php

@@ -47,10 +47,11 @@ class Handler
         }
 
         if ($e instanceof ApiException) {
-            $status = match ($code) {
-                Response::HTTP_UNPROCESSABLE_ENTITY => 200, // 表单验证
-                default => $code,
-            };
+//            $status = match ($code) {
+//                Response::HTTP_UNPROCESSABLE_ENTITY => 200, // 表单验证
+//                default => $code,
+//            };
+            $status = 200;
             // 400 错误请求
             // 401 未授权
             // 403 禁止访问
@@ -60,7 +61,7 @@ class Handler
             // 500 服务器内部错误
 
         }
-        return response()->json(['code' => $code, 'message' => $message], $status);
+        return response()->json(['code' => $code, 'msg' => $message], $status);
     }
 
 }

+ 20 - 17
app/Http/Controllers/Backend/Server/System/AuthController.php

@@ -17,6 +17,7 @@ use App\Models\System\Role;
 use App\Models\System\User;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Auth;
 use function PHPUnit\Framework\isEmpty;
 
 class AuthController extends Controller
@@ -36,8 +37,8 @@ class AuthController extends Controller
         $permissionInfo = ['user' => null, 'roles' => [], 'permissions' => [], 'menus' => []];
 
         // 1.1 获得用户信息
-        $user = request()->user();
-        if (!$user || $user->status !== 0) return self::success($permissionInfo);
+        $user = Auth::user();
+        if (!$user || $user['status'] !== 0) return self::success($permissionInfo);
         $permissionInfo['user'] = $user->only(['id', 'nickname', 'avatar']);
 
         // 1.2 获得角色列表
@@ -54,10 +55,10 @@ class AuthController extends Controller
         // 菜单树
 //        $menus = $allPermissions->where('status', 0)->whereIn('type', [1, 2])->whereIn('pivot.role_id', $roles->pluck('id'))->all();
 //        if (empty($menus)) return self::success($permissionInfo);
-        $permissionInfo['menus'] = self::buildMenuTree($menus->whereIn('type', [1, 2])->all());
+
+        $permissionInfo['menus'] = self::buildMenuTree($menus->toArray());
         // 权限标识信息
-        dd($user->getAllPermissions());
-        $permissions = $user->getAllPermissions()->where('status', 0)->whereIn('pivot.role_id', $roles->pluck('id'))->whereNotNull('permission')->pluck('permission');
+        $permissions = $user->getAllPermissions()->whereIn('pivot.role_id', $roles->pluck('id'))->pluck('name');
         $permissionInfo['permissions'] = $permissions;
 
         return self::success($permissionInfo);
@@ -68,9 +69,11 @@ class AuthController extends Controller
         // 移除按钮
         $removeKeys = [];
         $menuList = collect($menuList);
+
         $menuList->each(function ($value, $key) use (&$removeKeys) {
-            if ($value['type'] === 3) $removeKeys[] = $key;
+            if ($value->type === 3) $removeKeys[] = $key;
         });
+
         // 移除指定的项目
         $menuList = $menuList->forget($removeKeys);
 
@@ -81,17 +84,17 @@ class AuthController extends Controller
         $menuMap = collect([]);
 
         $menuList->each(function ($value) use (&$menuMap) {
-            $menuMap[$value['id']] = [
-                "id" => $value['id'],
-                "parentId" => $value['parent_id'],
-                "name" => $value['name'],
-                "path" => $value['path'],
-                "component" => $value['component'],
-                "componentName" => $value['component_name'],
-                "icon" => $value['icon'],
-                "visible" => !!$value['visible'],
-                "keepAlive" => !!$value['keep_alive'],
-                "alwaysShow" => !!$value['always_show'],
+            $menuMap[$value->id] = [
+                "id" => $value->id,
+                "parentId" => $value->parent_id,
+                "name" => $value->name,
+                "path" => $value->path,
+                "component" => $value->component,
+                "componentName" => $value->component_name,
+                "icon" => $value->icon,
+                "visible" => !!$value->visible,
+                "keepAlive" => !!$value->keep_alive,
+                "alwaysShow" => !!$value->always_show,
                 "children" => null
             ];
         });

+ 22 - 3
app/Http/Controllers/Backend/Server/System/DictDataController.php

@@ -28,9 +28,9 @@ class DictDataController extends Controller
 
     public function index(Request $request)
     {
-        $dictType = $request->get('dict_type');
-        $dictDataPage = DictData::query()->where('dict_type', $dictType)->where('status', 0)->paginate(10);
-        return self::success(['list' => $dictDataPage->items(), 'total' => $dictDataPage->total()]);
+        $params = $request->all();
+        $res = $this->dictDataService->getDictDataList($params);
+        return self::success($res);
     }
 
     public function store(Request $request)
@@ -40,6 +40,25 @@ class DictDataController extends Controller
         return self::success($res);
     }
 
+    public function show(int $id)
+    {
+        $res = $this->dictDataService->getDictData($id);
+        return self::success($res);
+    }
+
+    public function update(Request $request, int $id)
+    {
+        $data = $request->all();
+        $this->dictDataService->updateDictData($data, $id);
+        return self::success(true);
+    }
+
+    public function destroy(int $id)
+    {
+        $res = $this->dictDataService->deleteDictData($id);
+        return self::success($res);
+    }
+
     public function simpleList()
     {
         $select = ["dict_type as dictType", "value", "label", "color_type as colorType", "css_class as cssClass"];

+ 39 - 5
app/Http/Controllers/Backend/Server/System/DictTypeController.php

@@ -11,21 +11,55 @@ namespace App\Http\Controllers\Backend\Server\System;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Request;
 use App\Http\Services\Backend\Server\System\DictTypeService;
+use App\Http\Services\Backend\Server\System\MenuService;
 use App\Models\System\DictType;
 use Illuminate\Http\JsonResponse;
 
 class DictTypeController extends Controller
 {
-    public function index(Request $request)
+    private DictTypeService $dictTypeService;
+
+    function __construct(DictTypeService $dictTypeService)
+    {
+        $this->dictTypeService = $dictTypeService;
+//        $this->middleware('permission:system:menu:query|system:menu:create|system:menu:update|system:menu:delete', ['only' => ['index', 'show']]);
+//        $this->middleware('permission:system:menu:create', ['only' => ['create', 'store']]);
+//        $this->middleware('permission:system:menu:update', ['only' => ['edit', 'update']]);
+//        $this->middleware('permission:system:menu:delete', ['only' => ['destroy']]);
+    }
+
+    public function index(Request $request): JsonResponse
+    {
+        $params = $request->safe()->only(['name', 'type', 'status', 'createTime']);
+        $params['pageNo'] = $request->get('pageNo', 1);
+        $params['pageSize'] = $request->get('pageSize', 10);
+        $res = $this->dictTypeService->getDictTypeList($params);
+        return self::success($res);
+    }
+
+    public function show($id)
     {
-        $dictTypePage = DictType::query()->paginate(10);
-        return self::success(['list' => $dictTypePage->items(),'total' => $dictTypePage->total()]);
+        $res = $this->dictTypeService->getDictType($id);
+        return self::success($res);
+    }
+
+    public function store(Request $request): JsonResponse
+    {
+        $data = $request->only(['name', 'type', 'status', 'remark']);
+        $res = $this->dictTypeService->createDictType($data);
+        return self::success($res);
     }
 
-    public function store(Request $request)
+    public function update(Request $request, int $id)
     {
         $params = $request->all();
-        $res = (new DictTypeService())->createDictType($params);
+        $this->dictTypeService->updateDictType($params, $id);
+        return self::success(true);
+    }
+
+    public function destroy(int $id)
+    {
+        $res = $this->dictTypeService->deleteDictType($id);
         return self::success($res);
     }
 }

+ 4 - 4
app/Http/Controllers/Backend/Server/System/MenuController.php

@@ -17,7 +17,6 @@ use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 
 
-
 class MenuController extends Controller
 {
     private MenuService $menuService;
@@ -25,7 +24,7 @@ class MenuController extends Controller
     function __construct(MenuService $menuService)
     {
         $this->menuService = $menuService;
-        $this->middleware('permission:test|system-menu-query|system-menu-create|system-menu-update|system-menu-delete', ['only' => ['index', 'show']]);
+//        $this->middleware('permission:system:menu:query|system:menu:create|system:menu:update|system:menu:delete', ['only' => ['index', 'show']]);
 //        $this->middleware('permission:system:menu:create', ['only' => ['create', 'store']]);
 //        $this->middleware('permission:system:menu:update', ['only' => ['edit', 'update']]);
 //        $this->middleware('permission:system:menu:delete', ['only' => ['destroy']]);
@@ -56,8 +55,9 @@ class MenuController extends Controller
      */
     public function store(MenuRequest $request): JsonResponse
     {
-        $params = $request->safe()->all();
-        $res = $this->menuService->createMenu($params);
+        $params = $request->safe()->only(['name','type','sort','parentId','path','icon','component','componentName','status','visible','keepAlive','alwaysShow']);
+        $permissions = $request->post('permission', '');
+        $res = $this->menuService->createMenu($params, explode('|', $permissions));
         return self::success($res);
     }
 

+ 76 - 0
app/Http/Controllers/Backend/Server/System/RoleController.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Http\Controllers\Backend\Server\System;
+
+use App\Exceptions\ApiException;
+use App\Http\Controllers\Controller;
+use App\Http\Services\Backend\Server\System\RoleService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+
+class RoleController extends Controller
+{
+    private RoleService $roleService;
+
+    public function __construct(RoleService $roleService)
+    {
+        $this->roleService = $roleService;
+    }
+
+    public function index(Request $request): JsonResponse
+    {
+        // 处理首页逻辑
+        $params = $request->all();
+        $res = $this->roleService->getRoleList($params);
+        return self::success($res);
+    }
+
+    public function show($id)
+    {
+        // 处理显示单个用户的逻辑
+    }
+
+    public function create()
+    {
+        // 显示创建用户的表单
+    }
+
+    /**
+     * @throws ApiException
+     */
+    public function store(Request $request): JsonResponse
+    {
+        $params = $request->all();
+        $res = $this->roleService->createRole($params);
+        return self::success($res);
+    }
+
+    public function edit($id)
+    {
+        // 显示编辑用户的表单
+    }
+
+    /**
+     * @throws ApiException
+     */
+    public function update(Request $request, $id): JsonResponse
+    {
+        // 更新用户逻辑
+        $params = $request->all();
+        $res = $this->roleService->updateRole($params, $id);
+        return self::success(!!$res);
+    }
+
+    public function destroy($id): JsonResponse
+    {
+        // 删除用户逻辑
+        $res = $this->roleService->deleteRole($id);
+        return self::success(!!$res);
+    }
+
+//    public function permissions($id)
+//    {
+//        $res = $this->roleService->getRolePermissions($id);
+//        return self::success($res);
+//    }
+}

+ 59 - 0
app/Http/Controllers/Backend/Server/System/UserController.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace App\Http\Controllers\Backend\Server\System;
+
+use App\Http\Controllers\Backend\Server\Controller;
+use App\Http\Requests\Backend\Server\System\UserRequest;
+use App\Models\System\User;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Hash;
+
+class UserController extends Controller
+{
+    public function index()
+    {
+        // 处理首页逻辑
+        return 'index';
+    }
+
+    public function show($id)
+    {
+        // 处理显示单个用户的逻辑
+    }
+
+    public function create()
+    {
+        // 显示创建用户的表单
+    }
+
+    public function store(UserRequest $request)
+    {
+        // 存储新创建的用户逻辑
+        User::query()->create([
+            'name' => $request->string('name'),
+            'password' => Hash::make($request->string('password')),
+        ]);
+
+        return response()->noContent();
+//        event(new Registered($user));
+//
+//        Auth::login($user);
+//
+//        return response()->noContent();
+    }
+
+    public function edit($id)
+    {
+        // 显示编辑用户的表单
+    }
+
+    public function update(Request $request, $id)
+    {
+        // 更新用户逻辑
+    }
+
+    public function destroy($id)
+    {
+        // 删除用户逻辑
+    }
+}

+ 51 - 2
app/Http/Services/Backend/Server/System/DictDataService.php

@@ -11,18 +11,67 @@ namespace App\Http\Services\Backend\Server\System;
 use App\Http\Services\Service;
 use App\Models\System\DictData;
 use App\Models\System\DictType;
+use Symfony\Component\HttpFoundation\Response;
 
 class DictDataService extends Service
 {
+    protected array $selectColumn = ['id', 'label', 'value', 'sort', 'dict_type as dictType', 'status', 'color_type as colorType', 'css_class as cssClass', 'remark'];
+    protected array $selectAppendColumn = ['created_at as createTime'];
+
+    public function getDictDataList($params)
+    {
+        $dictData = DictData::query();
+        !empty($params['label']) && $dictData->whereLike('label', $params['label']);
+        isset($params['status']) && filled($params['status']) && $dictData->where('status', $params['status']);
+        $dictDataPage = $dictData->where('dict_type', $params['dictType'])->paginate($params['pageSize'], [...$this->selectColumn, ...$this->selectAppendColumn], 'page', $params['pageNo']);
+        return ['list' => $dictDataPage->items(), 'total' => $dictDataPage->total()];
+    }
+
     public function createDictData($data)
     {
         // 校验字典类型有效
-//        validateDictTypeExists(createReqVO.getDictType());
+        $this->validateDictTypeExists($data['dictType']);
         // 校验字典数据的值的唯一性
-//        validateDictDataValueUnique(null, createReqVO.getDictType(), createReqVO.getValue());
+        $this->validateDictDataValueUnique($data['dictType'], $data['value']);
 
         // 插入字典类型
         $dictData = self::toModel($data, DictData::class);
         return $dictData->create($dictData->getAttributes())->id;
     }
+
+    public function getDictData(int $id)
+    {
+        return DictData::query()->select($this->selectColumn)->find($id);
+    }
+
+    public function updateDictData(array $data, int $id)
+    {
+        // 校验字典类型有效
+        $this->validateDictTypeExists($data['dictType']);
+
+        // 校验字典数据的值的唯一性
+        $this->validateDictDataValueUnique($data['dictType'], $data['value'], $id);
+        // 修改字典数据
+        $dictData = self::toModel([...$data, 'id' => $id], DictData::class);
+        $dictData->update($dictData->getAttributes());
+    }
+
+    public function deleteDictData(int $id)
+    {
+        $dictData = DictData::query()->find($id);
+        is_null($dictData) && self::error('DICT_DATA_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY);
+        return $dictData->delete();
+    }
+
+    protected function validateDictTypeExists($dictType)
+    {
+        $isExists = DictType::query()->where('type', $dictType)->exists();
+        !$isExists && self::error('DICT_TYPE_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY);
+    }
+
+    protected function validateDictDataValueUnique(string $type, string $value, int $id = null)
+    {
+        $dictDataId = DictData::query()->where(['dict_type' => $type, 'value' => $value])->value('id');
+        !empty($dictDataId) && $dictDataId !== $id && self::error('DICT_DATA_DUPLICATE', Response::HTTP_UNPROCESSABLE_ENTITY);
+    }
 }

+ 63 - 4
app/Http/Services/Backend/Server/System/DictTypeService.php

@@ -8,18 +8,77 @@
 
 namespace App\Http\Services\Backend\Server\System;
 
+use App\Enums\Common\ErrorMessage;
 use App\Http\Services\Service;
+use App\Models\System\DictData;
 use App\Models\System\DictType;
+use Symfony\Component\HttpFoundation\Response;
 
 class DictTypeService extends Service
 {
+    protected array $selectColumn = ['id', 'name', 'remark', 'status', 'type'];
+
+    public function getDictType($id)
+    {
+        return DictType::query()->select($this->selectColumn)->find($id);
+    }
+
+    public function getDictTypeList($params): array
+    {
+        $dictType = DictType::query();
+        !empty($params['name']) && $dictType->whereLike('name', $params['name']);
+        !empty($params['type']) && $dictType->where('type', $params['type']);
+        !empty($params['status']) && $dictType->where('status', $params['status']);
+        !empty($params['createTime']) && $dictType->whereIn('created_at', $params['createTime']);
+        $select = ['id', 'name', 'remark', 'status', 'type', 'creator', 'created_at'];
+        $dictTypePage = DictType::query()->paginate($params['pageSize'], $select, 'page', $params['pageNo']);
+        return ['list' => $dictTypePage->items(), 'total' => $dictTypePage->total()];
+    }
+
     public function createDictType($data)
     {
-        // 校验字典类型的名字的唯一性
-        // 校验字典类型的类型的唯一性
+        // 校验字典类型的唯一性
+        $this->isExistType($data['name'], $data['type']);
+
+        // 插入字典类型
+        $dictType = self::toModel($data, DictType::class);
+        return $dictType->create($dictType->getAttributes())->id;
+    }
+
+    public function updateDictType($data, $id)
+    {
+        empty($id) && self::error();
+        // 校验字典类型的唯一性
+        $this->isExistType($data['name'], $data['type'], $id);
 
         // 插入字典类型
-        $menu = self::toModel($data, DictType::class);
-        return $menu->create($menu->getAttributes())->id;
+        $dictType = self::toModel($data, DictType::class);
+        $dictType->where('id', $id)->update($dictType->getAttributes());
     }
+
+    public function deleteDictType(int $id)
+    {
+        $dictType = DictType::query()->find($id);
+        is_null($dictType) && self::error('DICT_TYPE_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY);
+
+        // 校验是否存在字典数据
+        $this->validateDictDataExists($dictType->type);
+        return $dictType->delete();
+    }
+
+    protected function isExistType($name, $type, $id = null)
+    {
+        $dict_id = DictType::query()->where('name', $name)->where('type', $type)->value('id');
+        if (!empty($dict_id) && $dict_id !== $id) {
+            // 写入日志
+            self::error(ErrorMessage::DICT_TYPE_DUPLICATE, Response::HTTP_UNPROCESSABLE_ENTITY);
+        }
+    }
+
+    protected function validateDictDataExists(string $dictType)
+    {
+        $isExists = DictData::query()->where('dict_type',$dictType)->count();
+        $isExists && self::error(ErrorMessage::DICT_TYPE_EXISTS_DATA, Response::HTTP_UNPROCESSABLE_ENTITY);
+    }
+
 }

+ 84 - 33
app/Http/Services/Backend/Server/System/MenuService.php

@@ -8,35 +8,57 @@
 
 namespace App\Http\Services\Backend\Server\System;
 
+use App\Enums\Common\Status;
 use App\Enums\System\MenuType;
 use App\Exceptions\ApiException;
 use App\Http\Services\Service;
 use App\Models\System\Menu;
-use Illuminate\Support\Arr;
+use Exception;
+use Illuminate\Support\Facades\DB;
+use Spatie\Permission\Guard;
+use Spatie\Permission\Models\Permission;
 use Symfony\Component\HttpFoundation\Response;
 
 class MenuService extends Service
 {
+    protected array $selectColumn = ['id', 'name', 'type', 'sort', 'parent_id as parentId', 'path', 'icon', 'component', 'component_name as componentName', 'status', 'visible', 'keep_alive as keepAlive', 'always_show as alwaysShow'];
+//    protected array $selectAppendColumn = ['created_at as createTime'];
 
     /**
      * @throws ApiException
      */
-    public function createMenu($data)
+    public function createMenu(array $data, array $permissions)
     {
         // 校验父菜单存在
         self::validateParentMenu($data['parentId'], null);
         // 校验菜单(自己)
         self::validateMenu($data['parentId'], $data['name'], null);
+
+        // 校验权限标识
+        $this->validatePermissions($permissions);
+
         // 插入数据库
         $menu = self::toModel($data, Menu::class);
+
         self::initMenuProperty($menu);
-        return Menu::create($menu->getAttributes())->id;
+
+        DB::beginTransaction();
+        try {
+            $menu_id = $menu->create($menu->getAttributes())->id;
+            $this->createPermissions($permissions, $menu_id);
+            DB::commit();
+        }
+        catch (Exception){
+            DB::rollBack();
+            self::error('添加失败');
+        }
+        return $menu_id;
     }
 
     /**
      * @throws ApiException
      */
-    public function updateMenu($params)
+    public function updateMenu($params): void
     {
         // 校验更新的菜单是否存在
         !self::isExistMenu($params['id']) && self::error('MENU_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY);
@@ -61,7 +83,7 @@ class MenuService extends Service
         self::validateChildrenMenu($id);
 
         // 校验删除的菜单是否存在
-        !self::isExistMenu($id) && self::error('MENU_NOT_EXISTS',Response::HTTP_UNPROCESSABLE_ENTITY);
+        !self::isExistMenu($id) && self::error('MENU_NOT_EXISTS', Response::HTTP_UNPROCESSABLE_ENTITY);
 
         $menu = self::toModel(['id' => $id], Menu::class);
 
@@ -69,27 +91,25 @@ class MenuService extends Service
         return $menu->delete();
     }
 
-    public function getMenu($id)
+    public function getMenu($id): array
     {
         $menu = Menu::query();
-        $select = ['id','name','permission','type','sort','parent_id as parentId','path','icon','component','component_name as componentName','status','visible','keep_alive as keepAlive','always_show as alwaysShow'];
-        return $menu->select($select)->find($id)->toArray();
+        return $menu->select($this->selectColumn)->find($id)->toArray();
     }
+
     public function getMenuList($params = []): array
     {
         $menu = Menu::query();
-        $select = ['id','name','permission','type','sort','parent_id as parentId','path','icon','component','component_name as componentName','status','visible','keep_alive as keepAlive','always_show as alwaysShow'];
-        !empty($params['name']) && $menu->whereLike('name', "%{$params['name']}%");
-        !empty($params['status']) && $menu->where('status', $params['status']);
-        return $menu->orderBy('sort')->select($select)->get()->toArray();
+        isset($params['name']) && filled($params['name']) && $menu->whereLike('name', "%{$params['name']}%");
+        isset($params['status']) && filled($params['status']) && $menu->where('status', $params['status']);
+        return $menu->orderBy('sort')->select($this->selectColumn)->get()->toArray();
     }
 
     public function getSimpleMenuList(): array
     {
         $menus = $this->getMenuList(['status' => 0]);
-        $list = self::filterDisableMenus($menus);
-//        list.sort(Comparator . comparing(MenuDO::getSort));
-        return $list;
+        //        list.sort(Comparator . comparing(MenuDO::getSort));
+        return self::filterDisableMenus($menus);
     }
 
     protected static function isExistMenu(int|array $condition): bool
@@ -115,7 +135,7 @@ class MenuService extends Service
     {
         if (!$parentId) return;
         // 不能设置自己为父菜单
-        $parentId === $childId && self::error('MENU_PARENT_ERROR',Response::HTTP_UNPROCESSABLE_ENTITY);
+        $parentId === $childId && self::error('MENU_PARENT_ERROR', Response::HTTP_UNPROCESSABLE_ENTITY);
 
         $menu = Menu::query()->select('type')->find($parentId);
         // 父菜单不存在
@@ -139,6 +159,46 @@ class MenuService extends Service
         $menu->id !== $id && self::error('MENU_NAME_DUPLICATE', Response::HTTP_UNPROCESSABLE_ENTITY);
     }
 
+    /**
+     * @throws ApiException
+     */
+    protected function validatePermissions(array $permissions, int $menu_id = null): void
+    {
+        if (empty($permissions)) return;
+        $isExists = Permission::query()->whereIn('name', $permissions)->where('guard_name', Guard::getDefaultName(static::class))->exists();
+        $isExists && empty($menu_id) && self::error('权限标识重复');
+    }
+
+    protected function createPermissions(array $permissions, int $menu_id): void
+    {
+        if (empty($permissions)) return;
+        $permissionsData = [];
+        $guardName = Guard::getDefaultName(static::class);
+        $permissionQuery = Permission::query();
+        foreach ($permissions as $permission) {
+            $permissionsData[] = ['name' => $permission, 'guard_name' => $guardName];
+        }
+
+        $permissionQuery->insert($permissionsData);
+        $permissionIds = $permissionQuery->whereIn('name', $permissions)->pluck('id');
+
+        $this->createMenuHasPermissions($menu_id, $permissionIds->toArray());
+    }
+
+    protected function createMenuHasPermissions(int $menu_id, array $permission_ids): void
+    {
+        $tableNames = config('permission.table_names');
+        $menuHasPermissionsData = [];
+        $model_type = Menu::class;
+        foreach ($permission_ids as $permission_id) {
+            $menuHasPermissionsData[] = ['permission_id' => $permission_id, 'model_type' => $model_type, 'model_id' => $menu_id];
+        }
+        DB::table($tableNames['model_has_permissions'])->insert($menuHasPermissionsData);
+
+        // 存在权限并清空权限缓存
+//        Cache::forget(config('permission.cache.key'));
+    }
+
     /**
      * 初始化菜单的通用属性。
      * <p>
@@ -148,34 +208,25 @@ class MenuService extends Service
     private static function initMenuProperty(&$menu): void
     {
         // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空
-        if ($menu->type === MenuType::BUTTON) {
-            $menu->component = null;
-            $menu->componentName = null;
-            $menu->icon = null;
-            $menu->path = null;
+        if ($menu['type'] === MenuType::BUTTON) {
+            $menu['component'] = null;
+            $menu['component_name'] = null;
+            $menu['icon'] = null;
+            $menu['path'] = null;
         }
-        (isset($menu->permission) && empty($menu->permission) || !isset($menu->permission)) && ($menu->permission = null);
     }
 
     private static function filterDisableMenus($menus): array
     {
         if (empty($menus)) return [];
-//        $menuMap = convertMap($menus,'id');
-//        $menuMap = collect($menus)->mapWithKeys(function ($menu) {
-//            return [$menu['id'] => $menu];
-//        })->all();
 
         // 遍历 menu 菜单,查找不是禁用的菜单,添加到 enabledMenus 结果
         $enabledMenus = [];
-        $disabledMenuCache = []; // 存下递归搜索过被禁用的菜单,防止重复的搜索
+//        $disabledMenuCache = []; // 存下递归搜索过被禁用的菜单,防止重复的搜索
 
         foreach ($menus as $menu) {
-            if($menu['status'] !== 0) continue;
-//            if($menu['type'] === 3) continue;
-//            if (isMenuDisabled($menu, $menuMap, $disabledMenuCache)) {
-//                continue;
-//            }
-            $enabledMenus[] = ['id' => $menu['id'],'status' => $menu['status'], 'name' => $menu['name'], 'parentId' => $menu['parentId'], 'type' => $menu['type']];
+            if ($menu['status'] === Status::DISABLE) continue;
+            $enabledMenus[] = collect($menu)->only(['id', 'status', 'name', 'parentId', 'type']); //['id' => $menu['id'], 'status' => $menu['status'], 'name' => $menu['name'], 'parentId' => $menu['parentId'], 'type' => $menu['type']];
         }
 
         return $enabledMenus;

+ 5 - 0
app/Models/System/DictType.php

@@ -5,6 +5,7 @@ namespace App\Models\System;
 use App\Exceptions\ApiException;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use Spatie\Permission\Contracts\Permission as PermissionContract;
@@ -15,6 +16,10 @@ use Symfony\Component\HttpFoundation\Response;
 
 class DictType extends Model
 {
+    use SoftDeletes;
+
     protected $table = 'system_dict_type';
 
+    protected $guarded = [];
+
 }

+ 31 - 65
app/Models/System/Menu.php

@@ -3,106 +3,72 @@
 namespace App\Models\System;
 
 use App\Exceptions\ApiException;
-use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
-use Spatie\Permission\Contracts\Permission as PermissionContract;
-use Spatie\Permission\Exceptions\PermissionAlreadyExists;
 use Spatie\Permission\Guard;
-use Spatie\Permission\Models\Permission;
+use Spatie\Permission\Traits\HasPermissions;
 use Symfony\Component\HttpFoundation\Response;
 
-class Menu extends Permission
+class Menu extends Model
 {
-    protected $attributes = [
-        'title' => 'name'
-//        'name' => 'title',
-//        'permission' => 'name'
-    ];
+//    use HasPermissions;
+
+    protected $table = 'system_menus';
+
+    protected $attributes = [];
+
+    protected $guarded = [];
 
     public static $snakeAttributes = false;
 
-    protected $appends = ['title'];
+    protected $appends = ['permission'];
 
     protected $casts = [
-        'visible' => 'bool',
-        'keepAlive' => 'bool',
-        'alwaysShow' => 'bool',
+//        'visible' => 'bool',
+//        'keepAlive' => 'bool',
+//        'alwaysShow' => 'bool',
 //        'parent_id' => 'camel'
     ];
 
-    /**
-     * @throws ApiException
-     */
-    public static function create(array $attributes = [])
-    {
-        $attributes['guard_name'] = $attributes['guard_name'] ?? Guard::getDefaultName(static::class);
-        if (isset($attributes['permission']) && $attributes['permission']) {
-            $permission = static::getPermission(['permission' => $attributes['permission'], 'guard_name' => $attributes['guard_name']]);
-            if ($permission) {
-                throw new ApiException(['code' => Response::HTTP_UNPROCESSABLE_ENTITY, 'message' => 'PERMISSION_ALREADY_EXISTS']);
-            }
-        }
-        return static::query()->create($attributes);
-    }
-
-    /**
-     * @param array $attributes
-     * @param array $options
-     * @throws ApiException
-     */
-    public function update(array $attributes = [], array $options = []): void
-    {
-        $attributes['guard_name'] = $attributes['guard_name'] ?? Guard::getDefaultName(static::class);
-        if (isset($attributes['permission']) && $attributes['permission']) {
-            $permission = static::getPermission(['permission' => $attributes['permission'], 'guard_name' => $attributes['guard_name']]);
-            if ($permission && $permission->id !== $attributes['id']) {
-                throw new ApiException(['code' => Response::HTTP_UNPROCESSABLE_ENTITY, 'message' => 'PERMISSION_ALREADY_EXISTS']);
-            }
-        }
-        static::query()->where('id', $attributes['id'])->update($attributes, $options);
-        Cache::forget(config('permission.cache.key'));
-    }
-
-    public function setTitleAttribute($value)
+    public function disableAppends()
     {
-        $this->attributes['name'] =  $value;
+        $this->appends = [];
+        return $this;
     }
 
-    public function getTitleAttribute()
+    public function getPermissionAttribute()
     {
-        return $this->attributes['name'];
-    }
-
-    public function setNameAttribute($value)
-    {
-        $this->attributes['permission'] =  $value;
-    }
-
-    public function getNameAttribute()
-    {
-        return $this->attributes['permission'];
+        $tableNames = config('permission.table_names');
+        $permission_ids = DB::table($tableNames['model_has_permissions'])->where('model_type', Menu::class)->where('model_id', $this->attributes['id'])->pluck('permission_id');
+        $permission_names = DB::table($tableNames['permissions'])->whereIn('id', $permission_ids)->pluck('name');
+        return implode('|', $permission_names->toArray());
     }
 
     public function setComponentNameAttribute($value)
     {
-        $this->attributes['component_name'] =  $value;
+        $this->attributes['component_name'] = $value;
     }
 
     public function setVisibleAttribute($value)
     {
-        $this->attributes['visible'] =  $value ? 1 : 0;
+        $this->attributes['visible'] = $value ? 1 : 0;
     }
 
     public function setAlwaysShowAttribute($value)
     {
-        $this->attributes['always_show'] =  $value ? 1 : 0;
+        $this->attributes['always_show'] = $value ? 1 : 0;
     }
 
+
+//    public function getVisibleAttribute($value)
+//    {
+//        $this->attributes['visible'] = !$value;
+//    }
+
     public function setKeepAliveAttribute($value)
     {
-        $this->attributes['keep_alive'] =  $value ? 1 : 0;
+        $this->attributes['keep_alive'] = $value ? 1 : 0;
     }
 
 }

+ 66 - 0
app/Models/System/Role.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Models\System;
+
+use Illuminate\Support\Facades\DB;
+use Spatie\Permission\Models\Role as PermissionRole;
+use Spatie\Permission\Traits\HasPermissions;
+use Spatie\Permission\Traits\RefreshesPermissionCache;
+
+class Role extends PermissionRole
+{
+    use HasPermissions;
+    use RefreshesPermissionCache;
+
+    public static function getMenus(array $role_ids)
+    {
+        $menu_ids = DB::table('system_role_has_menus')->whereIn('role_id',$role_ids)->pluck('menu_id')->all();
+        return DB::table('system_menus')->whereIn('id',$menu_ids)->where('status', 0)->get();
+    }
+//    public function __construct(array $attributes = [])
+//    {
+//        parent::__construct($attributes);
+//    }
+//
+//    /**
+//     * @throws ApiException
+//     */
+//    public static function create(array $attributes = [])
+//    {
+//        // 校验角色的唯一字段是否重复
+//        $role = self::getRoleByParam($attributes);
+//        $role && throw new ApiException(['code' => Response::HTTP_UNPROCESSABLE_ENTITY, 'message' => 'ROLE_CODE_DUPLICATE']);
+//        return static::query()->create($attributes);
+//    }
+//
+//    /**
+//     * @throws ApiException
+//     */
+//    public function update(array $attributes = [], array $options = []): bool|int
+//    {
+//        // 校验角色的唯一字段是否重复
+//        $role = self::getRoleByParam($attributes);
+//        $role && $role->id !== $attributes['id'] && throw new ApiException(['code' => Response::HTTP_UNPROCESSABLE_ENTITY, 'message' => 'ROLE_CODE_DUPLICATE']);
+//        // 获取角色的所有权限
+//        $permissions = $role->getPermissionNames();
+//        // 存在权限并清空权限缓存
+//        count($permissions) && Cache::forget(config('permission.cache.key'));
+//        return static::query()->where('id', $attributes['id'])->update($attributes, $options);
+//    }
+//
+//    private static function getRoleByParam(array &$attributes = []): PermissionRole|null
+//    {
+//        // 校验角色的唯一字段是否重复
+//        $attributes['guard_name'] = $attributes['guard_name'] ?? Guard::getDefaultName(static::class);
+//        $params = ['code' => $attributes['code'], 'guard_name' => $attributes['guard_name']];
+//        if (app(PermissionRegistrar::class)->teams) {
+//            $teamsKey = app(PermissionRegistrar::class)->teamsKey;
+//            if (array_key_exists($teamsKey, $attributes)) {
+//                $params[$teamsKey] = $attributes[$teamsKey];
+//            } else {
+//                $attributes[$teamsKey] = getPermissionsTeamId();
+//            }
+//        }
+//        return static::findByParam($params);
+//    }
+}

+ 7 - 3
app/Models/User.php → app/Models/System/User.php

@@ -1,15 +1,18 @@
 <?php
 
-namespace App\Models;
+namespace App\Models\System;
 
-// use Illuminate\Contracts\Auth\MustVerifyEmail;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Foundation\Auth\User as Authenticatable;
 use Illuminate\Notifications\Notifiable;
+use Laravel\Sanctum\HasApiTokens;
+use Spatie\Permission\Traits\HasRoles;
 
 class User extends Authenticatable
 {
-    use HasFactory, Notifiable;
+    use HasApiTokens, HasRoles, HasFactory, Notifiable;
+
+    protected $table = 'system_users';
 
     /**
      * The attributes that are mass assignable.
@@ -42,6 +45,7 @@ class User extends Authenticatable
         return [
             'email_verified_at' => 'datetime',
             'password' => 'hashed',
+            'last_activity_at' => 'datetime'
         ];
     }
 }

+ 3 - 1
composer.json

@@ -6,9 +6,11 @@
     "license": "MIT",
     "require": {
         "php": "^8.2",
+        "bensampo/laravel-enum": "^6.11",
         "laravel/framework": "^11.9",
         "laravel/sanctum": "^4.0",
-        "laravel/tinker": "^2.9"
+        "laravel/tinker": "^2.9",
+        "spatie/laravel-permission": "^6.9"
     },
     "require-dev": {
         "fakerphp/faker": "^1.23",

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 492 - 103
composer.lock


+ 186 - 0
config/permission.php

@@ -0,0 +1,186 @@
+<?php
+
+return [
+
+    'models' => [
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * Eloquent model should be used to retrieve your permissions. Of course, it
+         * is often just the "Permission" model but you may use whatever you like.
+         *
+         * The model you want to use as a Permission model needs to implement the
+         * `Spatie\Permission\Contracts\Permission` contract.
+         */
+
+        'permission' => Spatie\Permission\Models\Permission::class,
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * Eloquent model should be used to retrieve your roles. Of course, it
+         * is often just the "Role" model but you may use whatever you like.
+         *
+         * The model you want to use as a Role model needs to implement the
+         * `Spatie\Permission\Contracts\Role` contract.
+         */
+
+        'role' => Spatie\Permission\Models\Role::class,
+
+    ],
+
+    'table_names' => [
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your roles. We have chosen a basic
+         * default value but you may easily change it to any table you like.
+         */
+
+        'roles' => 'system_roles',
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * table should be used to retrieve your permissions. We have chosen a basic
+         * default value but you may easily change it to any table you like.
+         */
+
+        'permissions' => 'system_permissions',
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * table should be used to retrieve your models permissions. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'model_has_permissions' => 'model_has_permissions',
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your models roles. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'model_has_roles' => 'model_has_roles',
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your roles permissions. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'role_has_permissions' => 'role_has_permissions',
+    ],
+
+    'column_names' => [
+        /*
+         * Change this if you want to name the related pivots other than defaults
+         */
+        'role_pivot_key' => null, //default 'role_id',
+        'permission_pivot_key' => null, //default 'permission_id',
+
+        /*
+         * Change this if you want to name the related model primary key other than
+         * `model_id`.
+         *
+         * For example, this would be nice if your primary keys are all UUIDs. In
+         * that case, name this `model_uuid`.
+         */
+
+        'model_morph_key' => 'model_id',
+
+        /*
+         * Change this if you want to use the teams feature and your related model's
+         * foreign key is other than `team_id`.
+         */
+
+        'team_foreign_key' => 'team_id',
+    ],
+
+    /*
+     * When set to true, the method for checking permissions will be registered on the gate.
+     * Set this to false if you want to implement custom logic for checking permissions.
+     */
+
+    'register_permission_check_method' => true,
+
+    /*
+     * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
+     * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
+     * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
+     */
+    'register_octane_reset_listener' => false,
+
+    /*
+     * Teams Feature.
+     * When set to true the package implements teams using the 'team_foreign_key'.
+     * If you want the migrations to register the 'team_foreign_key', you must
+     * set this to true before doing the migration.
+     * If you already did the migration then you must make a new migration to also
+     * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
+     * (view the latest version of this package's migration file)
+     */
+
+    'teams' => false,
+
+    /*
+     * Passport Client Credentials Grant
+     * When set to true the package will use Passports Client to check permissions
+     */
+
+    'use_passport_client_credentials' => false,
+
+    /*
+     * When set to true, the required permission names are added to exception messages.
+     * This could be considered an information leak in some contexts, so the default
+     * setting is false here for optimum safety.
+     */
+
+    'display_permission_in_exception' => false,
+
+    /*
+     * When set to true, the required role names are added to exception messages.
+     * This could be considered an information leak in some contexts, so the default
+     * setting is false here for optimum safety.
+     */
+
+    'display_role_in_exception' => false,
+
+    /*
+     * By default wildcard permission lookups are disabled.
+     * See documentation to understand supported syntax.
+     */
+
+    'enable_wildcard_permission' => false,
+
+    /*
+     * The class to use for interpreting wildcard permissions.
+     * If you need to modify delimiters, override the class and specify its name here.
+     */
+    // 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class,
+
+    /* Cache-specific settings */
+
+    'cache' => [
+
+        /*
+         * By default all permissions are cached for 24 hours to speed up performance.
+         * When permissions or roles are updated the cache is flushed automatically.
+         */
+
+        'expiration_time' => \DateInterval::createFromDateString('24 hours'),
+
+        /*
+         * The cache key used to store all permissions.
+         */
+
+        'key' => 'spatie.permission.cache',
+
+        /*
+         * You may optionally indicate a specific cache driver to use for permission and
+         * role caching using any of the `store` drivers listed in the cache.php config
+         * file. Using 'default' here means to use the `default` set in cache.php.
+         */
+
+        'store' => 'default',
+    ],
+];

+ 1 - 1
database/migrations/0001_01_01_000000_create_system_users_table.php

@@ -25,7 +25,7 @@ return new class extends Migration
             $table->string('ip_address', 45)->nullable()->comment('最后登录IP');
             $table->timestamp('last_activity_at')->nullable()->comment('最后登录时间');
             $table->text('remark')->nullable()->comment('备注');
-            $table->tinyInteger('status')->default(0)->comment('账户状态 (1正常 0停用)');
+            $table->tinyInteger('status')->default(0)->comment('账户状态 (0正常 1停用)');
             $table->bigInteger('creator')->nullable()->comment('创建者');
             $table->bigInteger('updater')->nullable()->comment('更新者');
             $table->rememberToken();

+ 151 - 0
database/migrations/2024_08_14_093112_create_permission_tables.php

@@ -0,0 +1,151 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration {
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        $teams = config('permission.teams');
+        $tableNames = config('permission.table_names');
+        $columnNames = config('permission.column_names');
+        $pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
+        $pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
+
+        if (empty($tableNames)) {
+            throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
+        }
+        if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
+            throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
+        }
+
+        Schema::create($tableNames['permissions'], function (Blueprint $table) {
+            //$table->engine('InnoDB');
+            $table->bigIncrements('id'); // permission id
+            $table->string('name')->comment('权限名称');       // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
+            $table->string('guard_name')->default('web'); // For MyISAM use string('guard_name', 25);
+            $table->timestamps();
+
+            $table->unique(['name', 'guard_name']);
+        });
+
+        Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
+            //$table->engine('InnoDB');
+            $table->bigIncrements('id'); // role id
+            if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
+                $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
+                $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
+            }
+            $table->string('name')->comment('角色名称');       // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
+            $table->string('guard_name')->default('web'); // For MyISAM use string('guard_name', 25);
+            $table->string('code', 100)->default('')->comment('角色权限标识');
+            $table->integer('sort')->default(0)->comment('显示顺序');
+            $table->tinyInteger('data_scope')->default(1)->comment('数据范围(1:全部数据 2:指定部门数据 3:所属部门数据 4:所属部门及下级数据)');
+            $table->json('data_scope_dept_ids')->nullable()->comment('数据范围(指定部门)');
+            $table->tinyInteger('status')->default(0)->comment('角色状态');
+            $table->tinyInteger('type')->comment('角色类型');
+            $table->text('remark')->nullable()->comment('备注');
+            $table->string('creator', 64)->nullable()->comment('创建者');
+            $table->string('updater', 64)->nullable()->comment('更新者');
+            $table->timestamps();
+            $table->softDeletes();
+
+            if ($teams || config('permission.testing')) {
+                $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
+            } else {
+                $table->unique(['name', 'guard_name']);
+            }
+
+        });
+
+        Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
+            $table->unsignedBigInteger($pivotPermission);
+
+            $table->string('model_type');
+            $table->unsignedBigInteger($columnNames['model_morph_key']);
+            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
+
+            $table->foreign($pivotPermission)
+                ->references('id') // permission id
+                ->on($tableNames['permissions'])
+                ->onDelete('cascade');
+            if ($teams) {
+                $table->unsignedBigInteger($columnNames['team_foreign_key']);
+                $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
+
+                $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_permissions_permission_model_type_primary');
+            } else {
+                $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_permissions_permission_model_type_primary');
+            }
+
+        });
+
+        Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
+            $table->unsignedBigInteger($pivotRole);
+
+            $table->string('model_type');
+            $table->unsignedBigInteger($columnNames['model_morph_key']);
+            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
+
+            $table->foreign($pivotRole)
+                ->references('id') // role id
+                ->on($tableNames['roles'])
+                ->onDelete('cascade');
+            if ($teams) {
+                $table->unsignedBigInteger($columnNames['team_foreign_key']);
+                $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
+
+                $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_roles_role_model_type_primary');
+            } else {
+                $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_roles_role_model_type_primary');
+            }
+        });
+
+        Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
+            $table->unsignedBigInteger($pivotPermission);
+            $table->unsignedBigInteger($pivotRole);
+
+            $table->foreign($pivotPermission)
+                ->references('id') // permission id
+                ->on($tableNames['permissions'])
+                ->onDelete('cascade');
+
+            $table->foreign($pivotRole)
+                ->references('id') // role id
+                ->on($tableNames['roles'])
+                ->onDelete('cascade');
+
+            $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
+        });
+
+        app('cache')
+            ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
+            ->forget(config('permission.cache.key'));
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        $tableNames = config('permission.table_names');
+
+        if (empty($tableNames)) {
+            throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
+        }
+
+        Schema::drop($tableNames['role_has_permissions']);
+        Schema::drop($tableNames['model_has_roles']);
+        Schema::drop($tableNames['model_has_permissions']);
+        Schema::drop($tableNames['roles']);
+        Schema::drop($tableNames['permissions']);
+    }
+};

+ 33 - 0
database/migrations/2024_08_26_111055_create_system_dict_type_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration {
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('system_dict_type', function (Blueprint $table) {
+            $table->id()->comment('字典主键');
+            $table->string('name', 100)->comment('字典名称');
+            $table->string('type', 100)->comment('字典类型');
+            $table->tinyInteger('status')->default(0)->comment('状态(0正常 1停用)');
+            $table->string('remark', 500)->nullable()->comment('备注');
+            $table->string('creator', 64)->nullable()->comment('创建者');
+            $table->string('updater', 64)->nullable()->comment('更新者');
+            $table->timestamps();
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('system_dict_type');
+    }
+};

+ 38 - 0
database/migrations/2024_08_27_031435_create_system_dict_data_table.php

@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('system_dict_data', function (Blueprint $table) {
+            $table->id()->comment('字典编码');
+            $table->integer('sort')->default(0)->comment('字典排序');
+            $table->string('label', 100)->comment('字典标签');
+            $table->string('value', 100)->comment('字典键值');
+            $table->string('dict_type', 100)->comment('字典类型');
+            $table->tinyInteger('status')->default(0)->comment('状态(0正常 1停用)');
+            $table->string('color_type', 100)->nullable()->comment('颜色类型');
+            $table->string('css_class', 100)->nullable()->comment('css 样式');
+            $table->string('remark', 500)->nullable()->comment('备注');
+            $table->string('creator', 64)->nullable()->comment('创建者');
+            $table->string('updater', 64)->nullable()->comment('更新者');
+            $table->timestamps();
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('system_dict_data');
+    }
+};

+ 65 - 0
database/migrations/2024_08_29_032939_create_system_menus_table.php

@@ -0,0 +1,65 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration {
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        $tableNames = config('permission.table_names');
+
+        if (empty($tableNames)) {
+            throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
+        }
+
+        Schema::create('system_menus', function (Blueprint $table) {
+            $table->bigIncrements('id'); // permission id
+            $table->string('name', 50)->comment('菜单名称');
+            $table->tinyInteger('type')->comment('菜单类型');
+            $table->integer('sort')->default(0)->comment('显示顺序');
+            $table->bigInteger('parent_id')->default(0)->comment('父菜单ID');
+            $table->string('path', 200)->nullable()->comment('路由地址');
+            $table->string('icon', 100)->nullable()->comment('菜单图标');
+            $table->string('component')->nullable()->comment('组件路径');
+            $table->string('component_name')->nullable()->comment('组件名');
+            $table->tinyInteger('status')->default(0)->comment('菜单状态');
+            $table->tinyInteger('visible')->default(1)->comment('是否可见');
+            $table->tinyInteger('keep_alive')->default(1)->comment('是否缓存');
+            $table->tinyInteger('always_show')->default(1)->comment('是否总是显示');
+            $table->string('creator', 64)->nullable()->comment('创建者');
+            $table->string('updater', 64)->nullable()->comment('更新者');
+            $table->timestamps();
+        });
+
+        Schema::create('system_role_has_menus', function (Blueprint $table) use ($tableNames) {
+            $table->bigIncrements('id');
+            $table->unsignedBigInteger('menu_id');
+            $table->unsignedBigInteger('role_id');
+
+            $table->foreign('menu_id')
+                ->references('id')
+                ->on('system_menus')
+                ->onDelete('cascade');
+
+            $table->foreign('role_id')
+                ->references('id')
+                ->on($tableNames['roles'])
+                ->onDelete('cascade');
+
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('system_role_has_menus');
+        Schema::dropIfExists('system_menus');
+    }
+};

+ 1 - 0
database/seeders/DatabaseSeeder.php

@@ -20,6 +20,7 @@ class DatabaseSeeder extends Seeder
             SystemMenusTableSeeder::class,
             SystemPermissionsTableSeeder::class,
             SystemRoleHasPermissionsTableSeeder::class,
+            SystemDictTableSeeder::class
             // 其他Seeder类...
         ]);
     }

+ 29 - 0
database/seeders/SystemDictTableSeeder.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Database\Seeders;
+
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemDictTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        //
+        DB::table('system_dict_type')->insert(['id' => 10, 'name' => '系统状态', 'type' => 'common_status', 'creator' => '系统']);
+        //DB::table('system_dict_type')->insert(['id' => 110, 'name' => '角色类型', 'type' => 'system_role_type', 'creator' => '系统']);
+
+        DB::table('system_dict_data')->insert(['label' => '开启', 'value' => 0, 'dict_type' => 'common_status', 'creator' => '系统']);
+        DB::table('system_dict_data')->insert(['label' => '关闭', 'value' => 1, 'dict_type' => 'common_status', 'creator' => '系统']);
+
+
+        DB::table('system_dict_data')->insert(['label' => '目录', 'value' => 1, 'dict_type' => 'system_menu_type', 'creator' => '系统']);
+        DB::table('system_dict_data')->insert(['label' => '菜单', 'value' => 2, 'dict_type' => 'system_menu_type', 'creator' => '系统']);
+        DB::table('system_dict_data')->insert(['label' => '按钮', 'value' => 3, 'dict_type' => 'system_menu_type', 'creator' => '系统']);
+
+    }
+}

+ 60 - 0
database/seeders/SystemMenusTableSeeder.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\System\Menu;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemMenusTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        // 目录
+        DB::table('system_menus')->insert(['id' => 1, 'name' => '系统菜单', 'type' => 1, 'path' => '/system', 'icon' => 'ep:tools', 'creator' => '系统']);
+
+        // 菜单
+        DB::table('system_menus')->insert(['id' => 100, 'name' => '用户管理', 'type' => 2, 'parent_id' => 1, 'path' => 'user', 'icon' => 'ep:avatar', 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 110, 'name' => '角色管理', 'type' => 2, 'parent_id' => 1, 'path' => 'role', 'icon' => 'ep:user', 'component' => 'system/role/index', 'component_name' => 'SystemRole', 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 120, 'name' => '菜单管理', 'type' => 2, 'parent_id' => 1, 'path' => 'menu', 'icon' => 'ep:menu', 'component' => 'system/menu/index', 'component_name' => 'SystemMenu', 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 130, 'name' => '部门管理', 'type' => 2, 'parent_id' => 1, 'path' => 'menu', 'icon' => 'ep:menu', 'component' => 'system/menu/index', 'component_name' => 'SystemMenu', 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 140, 'name' => '岗位管理', 'type' => 2, 'parent_id' => 1, 'path' => 'menu', 'icon' => 'ep:menu', 'component' => 'system/menu/index', 'component_name' => 'SystemMenu', 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 150, 'name' => '字典管理', 'type' => 2, 'parent_id' => 1, 'path' => 'dict', 'icon' => 'ep:collection', 'component' => 'system/dict/index', 'component_name' => 'SystemDictType', 'creator' => '系统']);
+
+        // 按钮
+        // 菜单管理
+        DB::table('system_menus')->insert(['id' => 121, 'name' => '菜单查询', 'type' => 3, 'parent_id' => 120, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 122, 'name' => '菜单新增', 'type' => 3, 'parent_id' => 120, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 123, 'name' => '菜单修改', 'type' => 3, 'parent_id' => 120, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 124, 'name' => '菜单删除', 'type' => 3, 'parent_id' => 120, 'creator' => '系统']);
+        // 字典管理
+        DB::table('system_menus')->insert(['id' => 151, 'name' => '字典查询', 'type' => 3, 'parent_id' => 150, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 152, 'name' => '字典新增', 'type' => 3, 'parent_id' => 150, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 153, 'name' => '字典修改', 'type' => 3, 'parent_id' => 150, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 154, 'name' => '字典删除', 'type' => 3, 'parent_id' => 150, 'creator' => '系统']);
+        DB::table('system_menus')->insert(['id' => 155, 'name' => '字典导出', 'type' => 3, 'parent_id' => 150, 'creator' => '系统']);
+
+
+        // 角色菜单
+        // 菜单管理
+        DB::table('system_role_has_menus')->insert(['menu_id' => 1, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 120, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 121, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 122, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 123, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 124, 'role_id' => 1]);
+        // 字典管理
+        DB::table('system_role_has_menus')->insert(['menu_id' => 150, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 151, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 152, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 153, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 154, 'role_id' => 1]);
+        DB::table('system_role_has_menus')->insert(['menu_id' => 155, 'role_id' => 1]);
+
+
+    }
+}

+ 21 - 0
database/seeders/SystemModelHasRolesTableSeeder.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\System\User;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemModelHasRolesTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        //
+        DB::table('model_has_roles')->insert(['role_id' => 1, 'model_type' => User::class, 'model_id' => 1]);
+
+    }
+}

+ 56 - 0
database/seeders/SystemPermissionsTableSeeder.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\System\Menu;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemPermissionsTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        //
+        DB::table('system_permissions')->insert(['name' => 'system:user:query']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:create']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:update']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:delete']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:export']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:import']);
+        DB::table('system_permissions')->insert(['name' => 'system:user:update-password']);
+
+        DB::table('system_permissions')->insert(['name' => 'system:role:query']);
+        DB::table('system_permissions')->insert(['name' => 'system:role:create']);
+        DB::table('system_permissions')->insert(['name' => 'system:role:update']);
+        DB::table('system_permissions')->insert(['name' => 'system:role:delete']);
+        DB::table('system_permissions')->insert(['name' => 'system:role:export']);
+
+        DB::table('system_permissions')->insert(['id' => 121,'name' => 'system:menu:query']);
+        DB::table('system_permissions')->insert(['id' => 122,'name' => 'system:menu:create']);
+        DB::table('system_permissions')->insert(['id' => 123,'name' => 'system:menu:update']);
+        DB::table('system_permissions')->insert(['id' => 124,'name' => 'system:menu:delete']);
+
+        DB::table('model_has_permissions')->insert(['permission_id' => 121,'model_type' => Menu::class, 'model_id' => 121]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 122,'model_type' => Menu::class, 'model_id' => 122]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 123,'model_type' => Menu::class, 'model_id' => 123]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 124,'model_type' => Menu::class, 'model_id' => 124]);
+
+        // 字典管理
+        DB::table('system_permissions')->insert(['id' => 151,'name' => 'system:dict:query']);
+        DB::table('system_permissions')->insert(['id' => 152,'name' => 'system:dict:create']);
+        DB::table('system_permissions')->insert(['id' => 153,'name' => 'system:dict:update']);
+        DB::table('system_permissions')->insert(['id' => 154,'name' => 'system:dict:delete']);
+        DB::table('system_permissions')->insert(['id' => 155,'name' => 'system:dict:export']);
+
+        DB::table('model_has_permissions')->insert(['permission_id' => 151,'model_type' => Menu::class, 'model_id' => 151]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 152,'model_type' => Menu::class, 'model_id' => 152]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 153,'model_type' => Menu::class, 'model_id' => 153]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 154,'model_type' => Menu::class, 'model_id' => 154]);
+        DB::table('model_has_permissions')->insert(['permission_id' => 155,'model_type' => Menu::class, 'model_id' => 155]);
+
+    }
+}

+ 28 - 0
database/seeders/SystemRoleHasPermissionsTableSeeder.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Database\Seeders;
+
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemRoleHasPermissionsTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        // 菜单权限
+        DB::table('role_has_permissions')->insert(['permission_id' => 121, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 122, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 123, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 124, 'role_id' => 1]);
+        // 字典管理
+        DB::table('role_has_permissions')->insert(['permission_id' => 151, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 152, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 153, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 154, 'role_id' => 1]);
+        DB::table('role_has_permissions')->insert(['permission_id' => 155, 'role_id' => 1]);
+    }
+}

+ 22 - 0
database/seeders/SystemRolesTableSeeder.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Database\Seeders;
+
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+
+class SystemRolesTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        $tableNames = config('permission.table_names');
+
+        //
+        DB::table($tableNames['roles'])->insert(['id' => 1, 'name' => '超级管理员', 'code' => 'super_admin', 'type' => 1, 'creator' => '系统']);
+
+    }
+}

+ 22 - 0
database/seeders/SystemUsersTableSeeder.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\System\User;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Hash;
+
+class SystemUsersTableSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     */
+    public function run(): void
+    {
+        //
+        DB::table('system_users')->insert(['id' => 1, 'name' => 'administrator', 'password' => '$2y$12$FD.RtwKBvPN.9dHRf00/7O3u447/YACyY.daCb9Ctblm3IQPRosTW']);
+        DB::table('system_users')->insert(['id' => 2, 'name' => 'admin', 'password' => Hash::make('admin')]);
+    }
+}

+ 0 - 19
tests/Feature/Backend/Server/SystemTest.php

@@ -1,19 +0,0 @@
-<?php
-
-namespace Tests\Feature\Backend\Server;
-
-use App\Models\System\Menu;
-use App\Models\User;
-use Illuminate\Foundation\Testing\RefreshDatabase;
-use Tests\TestCase;
-
-class SystemTest extends TestCase
-{
-    use RefreshDatabase;
-
-    public function test_create_menu(): void
-    {
-        $menu = Menu::query()->create(['guard_name' => 'Web']);
-        $this->assertGuest();
-    }
-}

+ 1 - 0
tests/Feature/ExampleTest.php

@@ -13,6 +13,7 @@ class ExampleTest extends TestCase
     public function test_the_application_returns_a_successful_response(): void
     {
         $response = $this->get('/');
+
         $response->assertStatus(200);
     }
 }

Vissa filer visades inte eftersom för många filer har ändrats