diff --git a/app/admin/controller/AdminController.php b/app/admin/controller/AdminController.php new file mode 100644 index 0000000..cf37121 --- /dev/null +++ b/app/admin/controller/AdminController.php @@ -0,0 +1,208 @@ +input('page', 1); + $perPage = $request->input('per_page', 10); + $admin = Admin::with('roles')->paginate($perPage, ['*'], 'page', $page); + return Util::success($admin); + } + // 获取用户角色列表 + public function roles(Request $request, $id) + { + $admin = Admin::with('roles')->findOrFail($id); + $allRoles = Role::all(); + + $res = [ + 'admin' => $admin, + 'all_roles' => $allRoles, + 'assigned_roles' => $admin->roles->pluck('id')->toArray() + ]; + return Util::success($res); + } + + // 更新用户角色 + public function updateRoles(Request $request, $id) + { +// $request->validate([ +// 'roles' => 'required|array', +// 'roles.*' => 'exists:roles,id' +// ]); + // 验证规则 + $rules = [ + 'roles' => 'required|array', + 'roles.*' => 'exists:roles,id' + ]; + + // 自定义错误消息(可选) + $messages = [ + 'roles.required' => '角色不能为空', + 'roles.exists' => '角色id不存在', + ]; + + // 执行验证 + $validator = Validator::make($request->all(), $rules, $messages); + + $admin = Admin::findOrFail($id); + + $res = $admin->roles()->sync($request->input('roles')); + + return Util::success($res,'角色更新成功'); + } + + // 创建管理员 + public function create(Request $request) + { + $rules = [ + 'username' => ['required', 'string', 'min:5', 'regex:/^[a-zA-Z0-9\-\_\.]+$/'], + 'password' => 'required|string|min:6', + 'nickname' => 'required|string', + 'email' => 'nullable|email', + 'status' => 'in:0,1' + ]; + + $messages = [ + 'username.required' => '账号不能为空', + 'username.min' => '账号长度不能少于5位', + 'username.regex' => '账号只能包含字母、数字、下划线、连字符和点号', + 'password.required' => '密码不能为空', + 'password.min' => '密码长度不能少于6位', + 'nickname.required' => '姓名不能为空', + 'email.email' => '邮箱格式不正确', + 'status.in' => '状态值不正确' + ]; + + $validator = Validator::make($request->all(), $rules, $messages); + if ($validator->fails()) { + return Util::fail('',$validator->errors()->first()); + } + + // 检查用户名是否已存在 + if (Admin::where('username', $request->input('username'))->exists()) { + return Util::fail('','账号已存在'); + } + + // 检查邮箱是否已存在 + if ($request->input('email') && Admin::where('email', $request->input('email'))->exists()) { + return Util::fail('','邮箱已存在'); + } + + $admin = new Admin(); + $admin->username = $request->input('username'); + $admin->password = password_hash($request->input('password'), PASSWORD_DEFAULT); + $admin->nickname = $request->input('nickname'); + $admin->email = $request->input('email', null); + $admin->status = $request->input('status', 1); + $admin->save(); + + if ($request->input('roles')) { + $res = $admin->roles()->sync($request->input('roles')); + } + + return Util::success($admin, '管理员创建成功'); + } + + // 修改管理员信息 + public function update(Request $request, $id) + { + $admin = Admin::findOrFail($id); + + $rules = [ + 'password' => ['string', 'min:6', 'regex:/^[a-zA-Z0-9\-\_\.]+$/'], + 'nickname' => 'string', + 'email' => 'email', + 'status' => 'in:0,1', + 'old_password' => 'required_with:password|string', + 'avatar' => 'string', + 'mobile' => 'string' + ]; + + $messages = [ + 'password.min' => '密码长度不能少于6位', + 'password.regex' => '密码只能包含字母、数字、下划线、连字符和点号', + 'email.email' => '邮箱格式不正确', + 'status.in' => '状态值不正确', + 'old_password.required_with' => '请提供旧密码', + 'old_password.string' => '旧密码格式不正确' + ]; + + $validator = Validator::make($request->all(), $rules, $messages); + if ($validator->fails()) { + return Util::fail('',$validator->errors()->first()); + } + + // 检查邮箱是否已存在(排除当前管理员) + if ($request->input('email') && Admin::where('email', $request->input('email'))->where('id', '!=', $id)->exists()) { + return Util::fail('','邮箱已存在'); + } + + // 准备要更新的数据 + $updateData = $request->only(['nickname', 'email', 'status', 'avatar', 'mobile']); + + // 如果修改密码,验证旧密码 + if ($request->input('password')) { + if (!$request->input('old_password')) { + return Util::fail('','请提供旧密码'); + } + if (!password_verify($request->input('old_password'), $admin->password)) { + return Util::fail('','旧密码不正确'); + } + $updateData['password'] = password_hash($request->input('password'), PASSWORD_DEFAULT); + } + + // 只填充前端传递的字段 + $admin->fill($updateData); + $admin->save(); + + return Util::success($admin, '管理员信息更新成功'); + } + + // 禁用/启用管理员 + public function toggleStatus(Request $request, $id) + { + $admin = Admin::findOrFail($id); + $admin->status = $admin->status == 1 ? 0 : 1; + $admin->save(); + + $statusText = $admin->status == 1 ? '启用' : '禁用'; + return Util::success($admin, "管理员{$statusText}成功"); + } + + // 重置管理员密码 + public function resetPassword(Request $request, $id) + { + $rules = [ + 'new_password' => 'required|string|min:6', +// 'confirm_password' => 'required|string|same:new_password' + ]; + + $messages = [ + 'new_password.required' => '新密码不能为空', + 'new_password.min' => '新密码长度不能少于6位', +// 'confirm_password.required' => '确认密码不能为空', +// 'confirm_password.same' => '两次输入的密码不一致' + ]; + + $validator = Validator::make($request->all(), $rules, $messages); + if ($validator->fails()) { + return Util::fail('',$validator->errors()->first()); + } + + $admin = Admin::findOrFail($id); + $admin->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT); + $admin->save(); + + return Util::success(null, '密码重置成功'); + } +} \ No newline at end of file diff --git a/app/admin/controller/AuthController.php b/app/admin/controller/AuthController.php new file mode 100644 index 0000000..7cc6a97 --- /dev/null +++ b/app/admin/controller/AuthController.php @@ -0,0 +1,79 @@ +where('username', $username) + ->first(); + if (!$admin) { + return Util::fail(['user' => $admin], trans('user_none')); + } + + $userPassword = $admin->password; + $user = $admin->toArray(); + + //加密 + $hashedPassword = password_hash($password, PASSWORD_DEFAULT); + + //验证密码 + if (!password_verify($password, $userPassword)) { + return Util::fail([], trans('password_error')); + } + $userId = $user['id']; // 实际从数据库获取 + + + Redis::set('admin_' . $userId, $user); + + //密码验证成功后加密生成 Token + $token = JwtUtil::generateToken('admin_'.$userId); + $result = $user; + unset($result['password']); + $result['token'] = $token; + + return Util::success($result, trans('login_success')); + } + + public function menus(Request $request) + { + $user = $request->user; + $menus = Menu::with(['child.child' => function($query) { + $query->where('status', 1)->orderBy('sort'); + }]) + ->where('parent_id', 0) + ->where('status', 1) + ->orderBy('sort') + ->get() + ->filter(function($menu) use ($user) { + if (empty($menu->permission_slug)) { + return true; + } + return $user->hasPermission($menu->permission_slug); + }); + + return Util::success($menus); + } +} \ No newline at end of file diff --git a/app/admin/controller/CustomerController.php b/app/admin/controller/CustomerController.php new file mode 100644 index 0000000..acf3a90 --- /dev/null +++ b/app/admin/controller/CustomerController.php @@ -0,0 +1,49 @@ +input('page', 1); + $perPage = $request->input('per_page', 10); + $where = []; + $user_id = $request->input('user_id'); + if(empty($user_id) || $user_id == 0){ + $where[] = ['user_id', '=', $request->input('user_id')]; + } + $user = Customer::with('user')->where($where)->paginate($perPage, ['*'], 'page', $page); + $user = $user->toArray(); + foreach ($user['data'] as &$item) { + unset($item['password']); + } + return Util::success($user); + } + + public function follow(Request $request) + { + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $where = []; + if($request->input('user_id')){ + $where[] = ['user_id', '=', $request->input('user_id')]; + } + $user = Customer::with('user')->where($where)->paginate($perPage, ['*'], 'page', $page); + $user = $user->toArray(); + foreach ($user['data'] as &$item) { + unset($item['password']); + } + return Util::success($user); + } +} \ No newline at end of file diff --git a/app/admin/controller/RoleController.php b/app/admin/controller/RoleController.php index b0c1091..da706ae 100644 --- a/app/admin/controller/RoleController.php +++ b/app/admin/controller/RoleController.php @@ -4,10 +4,29 @@ namespace app\admin\controller; use app\admin\model\Permission; use app\admin\model\Role; +use app\util\Util; +use Illuminate\Support\Facades\Validator; use support\Request; class RoleController { + public function all(Request $request,) + { + $admin = Role::with('permissions')->where([['status',1]])->get(); + return Util::success($admin); + } + + /* + * 角色列表 + */ + public function list(Request $request,) + { + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $admin = Role::with('permissions')->paginate($perPage, ['*'], 'page', $page); + return Util::success($admin); + } + // 获取角色权限列表 public function permissions(Request $request, $id) { @@ -27,14 +46,24 @@ class RoleController // 更新角色权限 public function updatePermissions(Request $request, $id) { - $request->validate([ + // 验证规则 + $rules = [ 'permissions' => 'required|array', 'permissions.*' => 'exists:permissions,id' - ]); + ]; - $role = Role::findOrFail($id); + // 自定义错误消息(可选) + $messages = [ + 'permissions.required' => '角色不能为空', + 'permissions.exists' => '角色id不存在', + ]; + + // 执行验证 + $validator = Validator::make($request->all(), $rules, $messages); + + $role = Role::with('permissions')->findOrFail($id); $role->permissions()->sync($request->input('permissions')); - return json(['code' => 0, 'msg' => '权限更新成功']); + return Util::success($role,'角色更新成功'); } } \ No newline at end of file diff --git a/app/admin/controller/UserController.php b/app/admin/controller/UserController.php index a70b8a8..10b47af 100644 --- a/app/admin/controller/UserController.php +++ b/app/admin/controller/UserController.php @@ -2,39 +2,22 @@ namespace app\admin\controller; -use support\Request; use app\model\User; -use app\admin\model\Role; +use app\util\Util; +use support\Request; class UserController { - // 获取用户角色列表 - public function roles(Request $request, $id) + + public function list(Request $request,) { - $user = User::with('roles')->findOrFail($id); - $allRoles = Role::all(); - - return json([ - 'code' => 0, - 'data' => [ - 'user' => $user, - 'all_roles' => $allRoles, - 'assigned_roles' => $user->roles->pluck('id')->toArray() - ] - ]); - } - - // 更新用户角色 - public function updateRoles(Request $request, $id) - { - $request->validate([ - 'roles' => 'required|array', - 'roles.*' => 'exists:roles,id' - ]); - - $user = User::findOrFail($id); - $user->roles()->sync($request->input('roles')); - - return json(['code' => 0, 'msg' => '角色更新成功']); + $page = $request->input('page', 1); + $perPage = $request->input('per_page', 10); + $user = User::paginate($perPage, ['*'], 'page', $page); + $user = $user->toArray(); + foreach ($user['data'] as &$item) { + unset($item['password']); + } + return Util::success($user); } } \ No newline at end of file diff --git a/app/admin/controller/menus.php b/app/admin/controller/menus.php index 2c4269a..1f21c84 100644 --- a/app/admin/controller/menus.php +++ b/app/admin/controller/menus.php @@ -2,6 +2,9 @@ namespace app\admin\controller; +use app\admin\model\Menu; +use support\Request; + class menus { public function menus(Request $request) diff --git a/app/admin/middleware/AuthMiddleware.php b/app/admin/middleware/AuthMiddleware.php new file mode 100644 index 0000000..01c04e1 --- /dev/null +++ b/app/admin/middleware/AuthMiddleware.php @@ -0,0 +1,49 @@ +getTokenFromRequest($request); + + if (!$token) { + return $this->unauthorized('缺少访问令牌'); + } + + if (!$user = auth('api')->setToken($token)->user()) { + return $this->unauthorized('用户不存在'); + } + + // 将用户信息存入请求 + $request->user = $user; + + } catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) { + return $this->unauthorized('令牌已过期'); + } catch (JWTException $e) { + return $this->unauthorized('令牌无效'); + } + + return $handler($request); + } + + private function getTokenFromRequest(Request $request) + { + $header = $request->header('Authorization', ''); + if (preg_match('/Bearer\s+(\S+)/', $header, $matches)) { + return $matches[1]; + } + return $request->input('token'); + } + + private function unauthorized($msg) + { + return json(['code' => 401, 'msg' => $msg]); + } +} \ No newline at end of file diff --git a/app/admin/middleware/PermissionCheck.php b/app/admin/middleware/PermissionCheck.php index 667f392..19701e8 100644 --- a/app/admin/middleware/PermissionCheck.php +++ b/app/admin/middleware/PermissionCheck.php @@ -2,6 +2,8 @@ namespace app\admin\middleware; +use app\util\Util; +use ReflectionClass; use Webman\MiddlewareInterface; use Webman\Http\Response; use Webman\Http\Request; @@ -10,21 +12,40 @@ class PermissionCheck implements MiddlewareInterface { public function process(Request $request, callable $handler): Response { - $user = $request->user(); - $route = $request->route; + $user = $request->user; +// $route = $request->route; + $route = $request->path(); - // 检查角色 - $roles = $route->param('roles'); - if ($roles && !$user->roles()->whereIn('name', (array)$roles)->exists()) { - return json(['code' => 403, 'msg' => '需要特定角色才能访问']); - } + // 通过反射获取控制器哪些方法不需要登录 + $controller = new ReflectionClass($request->controller); + $noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? []; - // 检查权限 - $permission = $route->param('permission'); - if ($permission && !$user->hasPermission($permission)) { - return json(['code' => 403, 'msg' => '无权访问']); + $isLogin = !in_array($request->action, $noNeedLogin); + + $permission = substr($route,7); + + $is_auth = str_starts_with($permission, 'auth'); + $permissions = $user?$user->permissions():true; + if($isLogin && $permission && (empty($permissions) && !$is_auth) && !$user->isPermission($permission)){ + return Util::fail('','无权访问该资源',401); } +// // 检查角色 +// $requiredRoles = $request->route->param('roles', []); +// if (!empty($requiredRoles) && !$user->hasAnyRole($requiredRoles)) { +// return $this->forbidden('需要特定角色才能访问'); +// } +// +// // 检查权限 +// $requiredPermission = $route->param('permission'); +// if ($requiredPermission && !$user->hasPermission($requiredPermission)) { +// return $this->forbidden('无权访问该资源'); +// } return $handler($request); } + + private function forbidden($msg) + { + return json(['code' => 403, 'msg' => $msg]); + } } \ No newline at end of file diff --git a/app/admin/model/Admin.php b/app/admin/model/Admin.php new file mode 100644 index 0000000..d9845fc --- /dev/null +++ b/app/admin/model/Admin.php @@ -0,0 +1,54 @@ +belongsToMany(Role::class, 'admin_roles', 'user_id', 'role_id'); + } + + public function hasRole($role) + { + return $this->roles()->where('name', $role)->exists(); + } + + public function getJWTIdentifier() + { + return $this->getKey(); + } + + public function getJWTCustomClaims() + { + return []; + } + + public function hasAnyRole($roles) + { + return $this->roles()->whereIn('name', (array)$roles)->exists(); + } + + public function hasPermission($permission) + { + return $this->permissions()->contains('slug', $permission); + } + public function isPermission($permission) + { + $permissions = $this->permissions(); + return in_array($permission, array_column($permissions->toArray(), 'slug')); + } + public function permissions() + { + return $this->roles() + ->with('permissions') + ->get() + ->pluck('permissions') + ->flatten() + ->unique('id'); + } +} \ No newline at end of file diff --git a/app/admin/model/Menu.php b/app/admin/model/Menu.php index b04c418..3bfa2c7 100644 --- a/app/admin/model/Menu.php +++ b/app/admin/model/Menu.php @@ -8,7 +8,7 @@ class Menu extends Model { protected $table = 'menus'; - public function children() + public function child() { return $this->hasMany(Menu::class, 'parent_id')->orderBy('sort'); } diff --git a/app/admin/model/Role.php b/app/admin/model/Role.php index f917a19..4cadcd0 100644 --- a/app/admin/model/Role.php +++ b/app/admin/model/Role.php @@ -10,7 +10,7 @@ class Role extends Model public function users() { - return $this->belongsToMany(User::class, 'user_roles', 'role_id', 'user_id'); + return $this->belongsToMany(Admin::class, 'user_roles', 'role_id', 'user_id'); } public function permissions() diff --git a/app/admin/model/User.php b/app/admin/model/User.php deleted file mode 100644 index f314ce4..0000000 --- a/app/admin/model/User.php +++ /dev/null @@ -1,29 +0,0 @@ -belongsToMany(Role::class, 'user_roles', 'user_id', 'role_id'); - } - - public function hasRole($role) - { - return $this->roles()->where('name', $role)->exists(); - } - - public function hasPermission($permission) - { - return $this->roles() - ->whereHas('permissions', function($query) use ($permission) { - $query->where('slug', $permission); - }) - ->exists(); - } -} \ No newline at end of file diff --git a/app/middleware/AuthMiddleware.php b/app/middleware/AuthMiddleware.php index 8306cc3..a0204eb 100644 --- a/app/middleware/AuthMiddleware.php +++ b/app/middleware/AuthMiddleware.php @@ -2,6 +2,7 @@ namespace app\middleware; +use app\admin\model\Admin; use app\model\User; use app\util\JwtUtil; use app\util\Util; @@ -77,17 +78,27 @@ class AuthMiddleware implements MiddlewareInterface } $userId = $decoded->sub; - $user = User::select(['id','name','sex','role','avatar','email','password','language','created_at']) - ->find($userId); - if(!$user){ - return Util::fail(['user'=>$user],trans('user_none')); + if(str_starts_with($userId,'admin_')){ + $userId = substr($userId,6); + $user = Admin::find($userId); + if(!$user){ + return Util::fail(['user'=>$user],trans('user_none')); + } +// $user = $user->toArray(); + + }else{ + $user = User::select(['id','name','sex','role','avatar','email','password','language','created_at']) + ->find($userId); + if(!$user){ + return Util::fail(['user'=>$user],trans('user_none')); + } + $user = $user->toArray(); + locale($user['language']); } - $user = $user->toArray(); // 将用户ID存入请求对象,供后续使用 // $request->user = ['id' => $userId]; $request->user = $user; - locale($user['language']); return $handler($request); } catch (BusinessException $e) { return json(['code' => 401, 'msg' => $e->getMessage()]); diff --git a/app/model/Customer.php b/app/model/Customer.php index 9df2cfa..50804c9 100644 --- a/app/model/Customer.php +++ b/app/model/Customer.php @@ -128,4 +128,10 @@ class Customer extends Model { return $this->hasMany(CustomerHis::class, 'customer_id', 'id'); } + + // 定义反向多对一关联 + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } } \ No newline at end of file diff --git a/app/model/User.php b/app/model/User.php index abdc2ea..ace7d51 100644 --- a/app/model/User.php +++ b/app/model/User.php @@ -50,4 +50,10 @@ class User extends Model ->toArray(); return $user; } + + // 定义与 Customer 的一对多关系 + public function customer() + { + return $this->hasMany(Customer::class, 'user_id', 'id'); + } } \ No newline at end of file diff --git a/composer.json b/composer.json index 0d98800..27a206f 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,9 @@ "vlucas/phpdotenv": "^5.6", "guzzlehttp/guzzle": "^7.9", "webman/cache": "^2.1", - "workerman/crontab": "^1.0" + "workerman/crontab": "^1.0", + "illuminate/validation": "^11.44", + "illuminate/container": "^11.44" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index 24e52de..73f21a6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a1bdf8bacc564776f13b4f4634c1b4c9", + "content-hash": "611cb43c328188aa7f01631e69f47567", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -277,6 +277,162 @@ ], "time": "2024-02-18T20:23:39+00:00" }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "b115554301161fa21467629f1e1391c1936de517" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517", + "reference": "b115554301161fa21467629f1e1391c1936de517", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.3" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2024-12-27T00:36:43+00:00" + }, { "name": "firebase/php-jwt", "version": "v6.11.0", @@ -957,7 +1113,13 @@ "type": "zip", "url": "https://api.github.com/repos/illuminate/container/zipball/4dcc3fcbab92e734fc0c08d9cfd97f5a1aef18ca", "reference": "4dcc3fcbab92e734fc0c08d9cfd97f5a1aef18ca", - "shasum": "" + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] }, "require": { "illuminate/contracts": "^11.0", @@ -1168,6 +1330,79 @@ }, "time": "2025-01-31T17:44:22+00:00" }, + { + "name": "illuminate/filesystem", + "version": "v11.44.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/filesystem.git", + "reference": "c79a54ebc61ad4e1665ca7610f276ff8a92f48f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/c79a54ebc61ad4e1665ca7610f276ff8a92f48f7", + "reference": "c79a54ebc61ad4e1665ca7610f276ff8a92f48f7", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/collections": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2", + "symfony/finder": "^7.0.3" + }, + "suggest": { + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-hash": "Required to use the Filesystem class.", + "illuminate/http": "Required for handling uploaded files (^7.0).", + "league/flysystem": "Required to use the Flysystem local driver (^3.25.1).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", + "symfony/mime": "Required to enable support for guessing extensions (^7.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Illuminate\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Filesystem package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-02-19T20:17:00+00:00" + }, { "name": "illuminate/macroable", "version": "v11.44.2", @@ -1443,6 +1678,130 @@ }, "time": "2025-02-23T14:20:22+00:00" }, + { + "name": "illuminate/translation", + "version": "v11.44.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/translation.git", + "reference": "de64584470b1d4bfc30f2c291585b873ce844ecf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/translation/zipball/de64584470b1d4bfc30f2c291585b873ce844ecf", + "reference": "de64584470b1d4bfc30f2c291585b873ce844ecf", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "illuminate/collections": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/filesystem": "^11.0", + "illuminate/macroable": "^11.0", + "illuminate/support": "^11.0", + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Translation package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-02-14T15:47:05+00:00" + }, + { + "name": "illuminate/validation", + "version": "v11.44.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/validation.git", + "reference": "0abcf721a46d3c2a94f225dce82f3c00070f6121" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/validation/zipball/0abcf721a46d3c2a94f225dce82f3c00070f6121", + "reference": "0abcf721a46d3c2a94f225dce82f3c00070f6121", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "egulias/email-validator": "^3.2.5|^4.0", + "ext-filter": "*", + "ext-mbstring": "*", + "illuminate/collections": "^11.0", + "illuminate/container": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/macroable": "^11.0", + "illuminate/support": "^11.0", + "illuminate/translation": "^11.0", + "php": "^8.2", + "symfony/http-foundation": "^7.0", + "symfony/mime": "^7.0" + }, + "suggest": { + "illuminate/database": "Required to use the database presence verifier (^11.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Validation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Validation package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-03-11T08:51:12+00:00" + }, { "name": "laravel/serializable-closure", "version": "v2.0.3", @@ -2953,6 +3312,250 @@ ], "time": "2024-09-25T14:20:29+00:00" }, + { + "name": "symfony/finder", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-23T06:56:12+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.2.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/62d1a43796ca3fea3f83a8470dfe63a4af3bc588", + "reference": "62d1a43796ca3fea3f83a8470dfe63a4af3bc588", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.2.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:17+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/2fc3b4bd67e4747e45195bc4c98bea4628476204", + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-27T11:08:17+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.31.0", @@ -3038,6 +3641,182 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.31.0", diff --git a/config/bootstrap.php b/config/bootstrap.php index 95d2e87..5b4eed8 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -12,6 +12,24 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +// 初始化 Laravel 容器 +use Illuminate\Container\Container; +use Illuminate\Support\Facades\Facade; + +$container = Container::getInstance(); + +// 将容器设置为 Facade 的根 +Facade::setFacadeApplication($container); + +// 注册验证器服务(关键步骤) +$container->singleton('validator', function () { + return new \Illuminate\Validation\Factory( + new \Illuminate\Translation\Translator( + new \Illuminate\Translation\ArrayLoader(), 'en' // 默认语言 + ) + ); +}); + return [ support\bootstrap\Session::class, ]; diff --git a/config/middleware.php b/config/middleware.php index 8296416..d38a8a0 100644 --- a/config/middleware.php +++ b/config/middleware.php @@ -17,5 +17,6 @@ return [ '' => [ app\middleware\CrossDomain::class, // 全局跨域中间件 app\middleware\AuthMiddleware::class, // 鉴权中间件 + app\admin\middleware\PermissionCheck::class, // 后端权限 ] ]; \ No newline at end of file