Repository: ChenJinchuang/lin-cms-tp5 Branch: master Commit: 72cea0211be7 Files: 83 Total size: 109.7 KB Directory structure: gitextract_fsxqjcbx/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── application/ │ ├── .htaccess │ ├── api/ │ │ ├── behavior/ │ │ │ └── Logger.php │ │ ├── controller/ │ │ │ ├── cms/ │ │ │ │ ├── Admin.php │ │ │ │ ├── File.php │ │ │ │ ├── Log.php │ │ │ │ └── User.php │ │ │ └── v1/ │ │ │ └── Book.php │ │ ├── model/ │ │ │ ├── BaseModel.php │ │ │ ├── Book.php │ │ │ └── admin/ │ │ │ ├── LinFile.php │ │ │ ├── LinGroup.php │ │ │ ├── LinGroupPermission.php │ │ │ ├── LinLog.php │ │ │ ├── LinPermission.php │ │ │ ├── LinUser.php │ │ │ ├── LinUserGroup.php │ │ │ └── LinUserIdentity.php │ │ ├── service/ │ │ │ ├── admin/ │ │ │ │ ├── Admin.php │ │ │ │ ├── Log.php │ │ │ │ └── User.php │ │ │ └── token/ │ │ │ └── LoginToken.php │ │ └── validate/ │ │ └── user/ │ │ ├── ChangePasswordForm.php │ │ ├── LoginForm.php │ │ ├── RegisterForm.php │ │ ├── ResetPasswordValidator.php │ │ └── UpdateUserForm.php │ ├── command.php │ ├── common.php │ ├── http/ │ │ └── middleware/ │ │ └── Authentication.php │ ├── index/ │ │ └── controller/ │ │ └── Index.php │ ├── lib/ │ │ ├── authenticator/ │ │ │ ├── Authenticator.php │ │ │ ├── AuthenticatorExecutorFactory.php │ │ │ ├── PermissionScan.php │ │ │ ├── Scan.php │ │ │ └── executor/ │ │ │ ├── IExecutor.php │ │ │ └── impl/ │ │ │ ├── AdminRequireExecutorImpl.php │ │ │ ├── GroupRequireExecutorImpl.php │ │ │ └── LoginRequireExecutorImpl.php │ │ ├── enum/ │ │ │ ├── GroupLevelEnum.php │ │ │ ├── IdentityTypeEnum.php │ │ │ ├── MountTypeEnum.php │ │ │ └── PermissionLevelEnum.php │ │ ├── exception/ │ │ │ ├── AuthFailedException.php │ │ │ ├── NotFoundException.php │ │ │ ├── OperationException.php │ │ │ ├── RepeatException.php │ │ │ ├── file/ │ │ │ │ └── FileException.php │ │ │ └── token/ │ │ │ ├── DeployException.php │ │ │ ├── ForbiddenException.php │ │ │ └── TokenException.php │ │ └── file/ │ │ └── LocalUploader.php │ ├── provider.php │ └── tags.php ├── build.php ├── composer.json ├── config/ │ ├── app.php │ ├── cache.php │ ├── console.php │ ├── cookie.php │ ├── database.php │ ├── file.php │ ├── log.php │ ├── middleware.php │ ├── secure.php │ ├── session.php │ ├── template.php │ ├── token.php │ └── trace.php ├── error_msg.md ├── extend/ │ └── .gitignore ├── public/ │ ├── .htaccess │ ├── index.php │ ├── robots.txt │ ├── router.php │ └── static/ │ └── .gitignore ├── route/ │ └── route.php ├── runtime/ │ └── .gitignore ├── schema.sql └── think ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /.idea /.vscode /vendor *.log thinkphp .env .DS_Store composer.lock /public/uploads ================================================ FILE: .travis.yml ================================================ sudo: false language: php branches: only: - stable cache: directories: - $HOME/.composer/cache before_install: - composer self-update install: - composer install --no-dev --no-interaction --ignore-platform-reqs - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip . - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0" - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0" - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip . script: - php think unit deploy: provider: releases api_key: secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw= file: - ThinkPHP_Core.zip - ThinkPHP_Full.zip skip_cleanup: true on: tags: true ================================================ FILE: LICENSE ================================================ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn) All rights reserved。 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 Apache Licence是著名的非盈利开源组织Apache采用的协议。 该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, 允许代码修改,再作为开源或商业软件发布。需要满足 的条件: 1. 需要给代码的用户一份Apache Licence ; 2. 如果你修改了代码,需要在被修改的文件中说明; 3. 在延伸的代码中(修改和有源代码衍生的代码中)需要 带有原来代码中的协议,商标,专利声明和其他原来作者规 定需要包含的说明; 4. 如果再发布的产品中包含一个Notice文件,则在Notice文 件中需要带有本协议内容。你可以在Notice中增加自己的 许可,但不可以表现为对Apache Licence构成更改。 具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================


Lin-CMS-TP5

php version ThinkPHP version LISENCE

# 简介 ## 预防针 * 本项目非官方团队出品,仅出于学习、研究目的丰富下官方项目的语言支持,目前已被收录至官方团队仓库,[点击查看](https://github.com/TaleLin) * 本项目采取后跟进官方团队功能的形式,即官方团队出什么功能,这边就跟进开发什么功能,开发者不必担心前端适配问题。 * 在上一点的基础上,我们会尝试加入一些自己的想法并实现。 * 局限于本人水平,有些地方还需重构,已经纳入了计划中,当然也会有我没考虑到的,希望有更多人参与进来一起完善,毕竟PHP作为世界上最好的语言不能缺席。 ## 专栏教程 * [《Lin CMS PHP&Vue教程》](https://course.7yue.pro/lin/lin-cms-php/)专栏教程连载更新中,通过实战开源前后端分离CMS——Lin CMS全家桶(lin-cms-vue & lin-cms-tp5)为一个前端应用实现内容管理系统。一套教程入门上手vue、ThinkPHP两大框架,自用、工作、私单一次打通。 * 读者反馈:[《Lin CMS PHP&Vue教程》读者反馈贴](https://github.com/ChenJinchuang/lin-cms-tp5/issues/47) ## 线上文档地址(完善中) [http://chenjinchuang.gitee.io/lin-cms-book/](http://chenjinchuang.gitee.io/lin-cms-book/) ## 线上 Demo 可直接参考官方团队的线上Demo:[http://face.cms.7yue.pro/](http://face.cms.7yue.pro/),用户名:super,密码:123456 ## 什么是 Lin CMS? > Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套**内容管理系统框架**。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。 本项目是基于ThinkPHP 5.1的 Lin CMS 后端实现。 官方团队产品了解请访问[TaleLin](https://github.com/TaleLin) ## Lin CMS 的特点 Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。 **Lin CMS 是一个前后端分离的 CMS 解决方案** 这意味着,Lin 既提供后台的支撑,也有一套对应的前端系统,当然双端分离的好处不仅仅在于此,我们会在后续提供NodeJS和PHP版本的 Lin。如果你心仪 Lin,却又因为技术栈的原因无法即可使用,没关系,我们会在后续提供更多的语言版本。为什么 Lin 要选择前后端分离的单页面架构呢? 首先,传统的网站开发更多的是采用服务端渲染的方式,需用使用一种模板语言在服务端完成页面渲染:比如 JinJa2、Jade 等。 服务端渲染的好处在于可以比较好的支持 SEO,但作为内部使用的 CMS 管理系统,SEO 并不重要。 但一个不可忽视的事实是,服务器渲染的页面到底是由前端开发者来完成,还是由服务器开发者来完成?其实都不太合适。现在已经没有多少前端开发者是了解这些服务端模板语言的,而服务器开发者本身是不太擅长开发页面的。那还是分开吧,前端用最熟悉的 Vue 写 JS 和 CSS,而服务器只关注自己的 API 即可。 其次,单页面应用程序的体验本身就要好于传统网站。 更多关于Lin CMS的介绍请访问[Lin CMS线上文档](http://doc.cms.7yue.pro/) **框架本身已内置了 CMS 常用的功能** Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需要集中精力开发自己的 CMS 业务即可 ## Lin CMS TP5 的特点 在当前项目的版本`(0.0.1)`中,特点更多来自于`ThinkPHP 5.1`框架本身带来的特点。通过充分利用框架的特性,实现高效的后端使用、开发,也就是说,只要你熟悉`ThinkPHP`框架,那么对于理解使用和二次开发本项目是没有难度的,即便对于框架的某些功能存在疑问也完全可以通过ThinkPHP官方的开发手册找到答案。当然我们更欢迎你通过[Issues](https://github.com/ChenJinchuang/lin-cms-tp5/issues)来向我们提问:) 在下一个版本中`(>0.0.1)`,我们会在框架的基础上融入一些自己的东西来增强或者优化框架的使用、开发体验。 ## 所需基础 由于 Lin 采用的是前后端分离的架构,所以你至少需要熟悉 PHP 和 Vue。 Lin 的服务端框架是基于 ThinkPHP5.1的,所以如果你比较熟悉ThinkPHP的开发模式,那将可以更好的使用本项目。但如果你并不熟悉ThinkPHP,我们认为也没有太大的关系,因为框架本身已经提供了一套完整的开发机制,你只需要在框架下用 PHP 来编写自己的业务代码即可。照葫芦画瓢应该就是这种感觉。 但前端不同,前端还是需要开发者比较熟悉 Vue 的。但我想以 Vue 在国内的普及程度,绝大多数的开发者是没有问题的。这也正是我们选择 Vue 作为前端框架的原因。如果你喜欢 React Or Angular,那么加入我们,为 Lin 开发一个对应的版本吧。 # 快速开始 ## Server 端必备环境 * 安装MySQL(version: 5.7+) * 安装PHP环境(version: 7.1+) ## 获取工程项目 ```bash git clone https://github.com/ChenJinchuang/lin-cms-tp5.git ``` > 执行完毕后会生成lin-cms-tp5目录 ## 安装依赖包 执行命令前请确保你已经安装了composer工具 ```bash # 进入项目根目录 cd lin-cms-tp5 # 先执行以下命令,全局替换composer源,解决墙的问题 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ # 接着执行以下命令安装依赖包 composer install ``` ## 数据库配置 Lin 需要你自己在 MySQL 中新建一个数据库,名字由你自己决定。例如,新建一个名为` lin-cms `的数据库。接着,我们需要在工程中进行一项简单的配置。使用编辑器打开 Lin 工程根目录下``/config/database.php``,找到如下配置项: ```php // 服务器地址 'hostname' => '', // 数据库名 'database' => 'lin-cms', // 用户名 'username' => 'root', // 密码 'password' => '', //省略后面一堆的配置项 ``` **请务必根据自己的实际情况修改此配置项** ## 导入数据 接下来使用你本机上任意一款数据库可视化工具,为已经创建好的`lin-cms`数据库运行lin-cms-tp5根目录下的`schema.sql`文件,这个SQL脚本文件将为为你生成一些基础的数据库表和数据。 ## 运行 如果前面的过程一切顺利,项目所需的准备工作就已经全部完成,这时候你就可以试着让工程运行起来了。在工程的根目录打开命令行,输入: ```bash php think run --port 5000 //启动thinkPHP内置的Web服务器 ``` 启动成功后会看到如下提示: ```php ThinkPHP Development server is started On You can exit with `CTRL-C` ``` 打开浏览器,访问``http://127.0.0.1:5000``,你会看到一个欢迎界面,至此,Lin-cms-tp5部署完毕,可搭配[lin-cms-vue](https://github.com/TaleLin/lin-cms-vue)使用了。 ## 更新日志 [查看日志](http://chenjinchuang.gitee.io/lin-cms-book/log/) ## 常见问题 [查看常见问题](http://chenjinchuang.gitee.io/lin-cms-book/qa/) ## 讨论交流 ### QQ 交流群 QQ 群号:643205479 ### 微信公众号 微信搜索:林间有风 ================================================ FILE: application/.htaccess ================================================ deny from all ================================================ FILE: application/api/behavior/Logger.php ================================================ '日志信息不能为空' ]); } if (is_array($params)) { list('uid' => $uid, 'username' => $username, 'msg' => $message) = $params; } else { $tokenService = LoginToken::getInstance(); $uid = $tokenService->getCurrentUid(); $username = $tokenService->getCurrentUserName(); $message = $params; } $data = [ 'message' => $username . $message, 'user_id' => $uid, 'username' => $username, 'status_code' => Response::getCode(), 'method' => Request::method(), 'path' => '/' . Request::path(), 'permission' => null ]; LinLog::create($data); } } ================================================ FILE: application/api/controller/cms/Admin.php ================================================ get('page/d', 0); $count = $request->get('count/d', 10); $groupId = $request->get('group_id/d'); return AdminService::getUsers($page, $count, $groupId); } /** * @adminRequired * @permission('修改用户密码','管理员','hidden') * @validate('ResetPasswordValidator') * @param Request $request * @param $id * @return Json * @throws NotFoundException */ public function changeUserPassword(Request $request, $id) { $newPassword = $request->put('new_password'); AdminService::changeUserPassword($id, $newPassword); Hook::listen('logger', "修改了用户ID为{$id}的密码"); return writeJson(200, null, '修改成功', 4); } /** * @adminRequired * @permission('删除用户','管理员','hidden') * @param int $id * @param('id','用户id','require|integer') * @return Json * @throws NotFoundException * @throws OperationException */ public function deleteUser(int $id) { AdminService::deleteUser($id); Hook::listen('logger', "删除了用户ID为:{$id}的用户"); return writeJson(201, $id, '删除用户成功', 5); } /** * @adminRequired * @permission('管理员更新用户信息','管理员','hidden') * @param Request $request * @param('id','用户id','require|integer') * @param('group_ids','分组id','require|array|min:1') * @return Json * @throws NotFoundException * @throws OperationException * @throws ForbiddenException * @throws DataNotFoundException * @throws ModelNotFoundException * @throws DbException */ public function updateUser(Request $request, $id) { $groupIds = $request->put('group_ids'); AdminService::updateUserInfo($id, $groupIds); Hook::listen('logger', "更新了用户:{$id}的所属分组"); return writeJson(201, $id, '更新用户成功', 6); } /** * @adminRequired * @permission('查询所有分组','管理员','hidden') * @return array|PDOStatement|string|\think\Collection|Collection * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException */ public function getGroupAll() { return AdminService::getAllGroups(); } /** * @adminRequired * @permission('查询一个权限组及其权限','管理员','hidden') * @param int $id * @param('id','分组id','require|integer') * @return Query * @throws DbException * @throws NotFoundException */ public function getGroup(int $id) { return AdminService::getGroup($id); } /** * @adminRequired * @permission('新建一个权限组','管理员','hidden') * @param Request $request * @param('name','分组名字','require') * @param('permission_ids','权限id','require|array|min:1') * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException * @throws OperationException */ public function createGroup(Request $request) { $name = $request->post('name'); $info = $request->post('info'); $permissionIds = $request->post('permission_ids'); $groupId = AdminService::createGroup($name, $info, $permissionIds); Hook::listen('logger', "创建了分组:{$name}"); return writeJson(201, $groupId, '新增分组成功', 15); } /** * @adminRequired * @permission('更新一个权限组','管理员','hidden') * @param Request $request * @param int $id * @param('id','分组id','require|integer') * @param('info','分组信息','require') * @param('name','分组名字','require') * @return Json * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException */ public function updateGroup(Request $request, int $id) { $name = $request->put('name'); $info = $request->put('info'); $res = AdminService::updateGroup($id, $name, $info); Hook::listen('logger', "更新了id为{$id}的分组"); return writeJson(200, $res, '更新分组信息成功', 7); } /** * @adminRequired * @permission('更新一个权限组','管理员','hidden') * @param int $id * @param('id','分组id','require|integer') * @return Json * @throws ForbiddenException * @throws NotFoundException * @throws OperationException */ public function deleteGroup(int $id) { AdminService::deleteGroup($id); Hook::listen('logger', "删除了id为{$id}的分组"); return writeJson(200, null, '删除分组成功', 8); } /** * @adminRequired * @permission('分配多个权限','管理员','hidden') * @param Request $request * @param('group_id','分组id','require|integer') * @param('permission_ids','权限id','require|array|min:1') * @return Json * @throws DbException * @throws NotFoundException * @throws OperationException */ public function dispatchPermissions(Request $request) { $groupId = $request->post('group_id'); $permissionIds = $request->post('permission_ids'); AdminService::dispatchPermissions($groupId, $permissionIds); Hook::listen('logger', "修改了分组ID为{$groupId}的权限"); return writeJson(200, null, '分配权限成功', 9); } /** * @adminRequired * @permission('删除多个权限','管理员','hidden') * @param Request $request * @param('group_id','分组id','require|integer') * @param('permission_ids','权限id','require|array|min:1') * @return Json * @throws DbException * @throws NotFoundException */ public function removePermissions(Request $request) { $groupId = $request->post('group_id'); $permissionIds = $request->post('permission_ids'); $deleted = AdminService::removePermissions($groupId, $permissionIds); Hook::listen('logger', "修改了分组ID为{$groupId}的权限"); return writeJson(200, $deleted, '删除权限成功', 10); } } ================================================ FILE: application/api/controller/cms/File.php ================================================ '字段中含有非法字符', ]); } $file = (new LocalUploader($request))->upload(); return $file; } } ================================================ FILE: application/api/controller/cms/Log.php ================================================ get('start'); $end = $request->get('end'); $name = $request->get('name'); $page = $request->get('page/d', 0); $count = $request->get('count/d', 10); return LogService::getLogs($page, $count, $start, $end, $name); } /** * @groupRequired * @permission('搜索日志','日志') * @param Request $request * @param('page','分页数','integer') * @param('count','分页值','integer') * @param('start','开始日期','date') * @param('end','结束日期','date') * @return array * @throws ParameterException */ public function getUserLogs(Request $request) { $start = $request->get('start'); $end = $request->get('end'); $name = $request->get('name'); $keyword = $request->get('keyword'); $page = $request->get('page/d', 0); $count = $request->get('count/d', 10); return LogService::searchLogs($page, $count, $start, $end, $name, $keyword); } /** * @groupRequired * @permission('查询日志记录的用户','日志') * @param Request $request * @param('page','分页数','integer') * @param('count','分页值','integer') * @return array * @throws ParameterException */ public function getUsers(Request $request) { $page = $request->get('page/d', 0); $count = $request->get('count/d', 10); return LogService::getUserNames($page, $count); } } ================================================ FILE: application/api/controller/cms/User.php ================================================ loginTokenService = LoginToken::getInstance(); } /** * @adminRequired * @permission('注册','管理员','hidden') * @param Request $request * @validate('RegisterForm') * @return Json * @throws NotFoundException * @throws OperationException * @throws RepeatException * @throws ForbiddenException * @throws DataNotFoundException * @throws ModelNotFoundException * @throws DbException */ public function register(Request $request) { $params = $request->post(); $user = UserService::createUser($params); Hook::listen('logger', "新建了用户:{$user['username']}"); return writeJson(201, $user['id'], '注册用户成功'); } /** * @param Request $request * @validate('LoginForm') * @return array * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException * @throws AuthFailedException */ public function userLogin(Request $request) { $username = $request->post('username'); $password = $request->post('password'); $user = UserService::verify($username, $password); $tokenExtend = UserService::generateTokenExtend($user); $token = $this->loginTokenService->getToken($tokenExtend); Hook::listen('logger', array('uid' => $user->id, 'username' => $user->identifier, 'msg' => '登陆成功获取了令牌')); return [ 'access_token' => $token['accessToken'], 'refresh_token' => $token['refreshToken'] ]; } /** * @return array * @throws TokenException */ public function refreshToken() { $token = $this->loginTokenService->getTokenFromHeaders(); $token = $this->loginTokenService->refresh($token); return [ 'access_token' => $token['accessToken'] ]; } /** * @loginRequired */ public function getAllowedApis() { $uid = $this->loginTokenService->getCurrentUid(); return UserService::getPermissions($uid); } /** * @loginRequired * @return mixed */ public function getInformation() { $uid = $this->loginTokenService->getCurrentUid(); return UserService::getInformation($uid); } /** * @loginRequired * @param Request $request * @validate('UpdateUserForm') * @return Json * @throws RepeatException */ public function update(Request $request) { $params = $request->put(); $row = UserService::updateUser($params); return writeJson(200, $row, '用户信息更新成功'); } /** * @loginRequired * @validate('ChangePasswordForm') * @param Request $request * @return Json * @throws AuthFailedException * @throws NotFoundException */ public function changePassword(Request $request) { $oldPassword = $request->put('old_password'); $newPassword = $request->put('new_password'); $row = UserService::changePassword($oldPassword, $newPassword); Hook::listen('logger', '修改了自己的密码'); return writeJson(200, $row, '密码修改成功'); } } ================================================ FILE: application/api/controller/v1/Book.php ================================================ post(); BookModel::create($params); return writeJson(201, '', '新建图书成功'); } public function update(Request $request) { $params = $request->put(); $bookModel = new BookModel(); $bookModel->save($params, ['id' => $params['id']]); return writeJson(201, '', '更新图书成功'); } /** * @groupRequired * @permission('删除图书','图书') * @param $bid * @return Json */ public function delete($bid) { BookModel::destroy($bid); Hook::listen('logger', '删除了id为' . $bid . '的图书'); return writeJson(201, '', '删除图书成功'); } } ================================================ FILE: application/api/model/BaseModel.php ================================================ belongsToMany('LinUser', 'Lin_user_group', 'user_id', 'group_id'); } public function permissions() { return $this->belongsToMany('LinPermission', 'lin_group_permission', 'permission_id', 'group_id'); } } ================================================ FILE: application/api/model/admin/LinGroupPermission.php ================================================ count(); $logList = $logList->limit($start, $count) ->order('create_time desc') ->select(); return [ 'logList' => $logList, 'total' => $total ]; } public static function searchLogs(int $start, int $count, $params = []) { $logList = self::withSearch(['name', 'start', 'end', 'keyword'], $params); $total = $logList->count(); $logList = $logList->limit($start, $count) ->order('create_time desc') ->select(); return [ 'logList' => $logList, 'total' => $total ]; } public static function getUserNames(int $start, int $count) { $users = self::field('username'); $total = $users->count(); $users = $users->limit($start, $count) ->group('username') ->select(); return [ 'userList' => $users, 'total' => $total ]; } public function searchNameAttr($query, $value) { if ($value) { $query->where('username', $value); } } public function searchStartAttr($query, $value) { if ($value) { $query->where('create_time', '>= time', $value); } } public function searchEndAttr($query, $value) { if ($value) { $query->where('create_time', '<= time', $value); } } public function searchKeywordAttr($query, $value) { if ($value) { $query->whereLike('message', "%{$value}%"); } } } ================================================ FILE: application/api/model/admin/LinPermission.php ================================================ where('username', '<>', 'root'); $total = $userList->count(); $userList = $userList ->limit($start, $count) ->with('groups') ->select(); return [ 'userList' => $userList, 'total' => $total ]; } public function groups() { return $this->belongsToMany('LinGroup', 'lin_user_group', 'group_id', 'user_id'); } public function identity() { return $this->hasMany('LinUserIdentity', 'user_id'); } public function searchGroupIdAttr($query, $value) { if ($value) { $query->join('lin_group g', 'g.id=' . $value)->where('g.id', '<>', 1); } } public function getAvatarAttr($value) { if ($value) { $host = Config::get('file.host') ?? "http://127.0.0.1:5000/"; $dir = Config::get('file.store_dir'); return $host . $dir . '/' . $value; } return $value; } } ================================================ FILE: application/api/model/admin/LinUserGroup.php ================================================ where('identifier', $currentUser->getAttr('username')) ->find(); if (!$user) { throw new NotFoundException(); } $user->credential = md5($newPassword); $user->save(); } public function checkPassword(string $password): bool { return $this->getAttr('credential') === md5($password); } } ================================================ FILE: application/api/service/admin/Admin.php ================================================ run(); foreach ($permissionList as $permission) { $model = LinPermissionModel::where('name', $permission['name']) ->where('module', $permission['module']) ->find(); if (!$model) { self::createPermission($permission['name'], $permission['module']); } } $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->select()->toArray(); $result = []; foreach ($permissions as $permission) { $result[$permission['module']][] = $permission; } return $result; } /** * @param int $page * @param int $count * @param int $groupId * @return array * @throws ParameterException */ public static function getUsers(int $page, int $count, int $groupId = null): array { list($start, $count) = paginate($count, $page); $params = $groupId ? ['group_id' => $groupId] : []; $users = LinUserModel::getUsers($start, $count, $params); return [ 'items' => $users['userList'], 'count' => $count, 'page' => $page, 'total' => $users['total'] ]; } /** * @param int $uid * @param string $newPassword * @throws NotFoundException */ public static function changeUserPassword(int $uid, string $newPassword): void { $user = LinUserModel::get($uid); if (!$user) { throw new NotFoundException(); } LinUserIdentityModel::resetPassword($user, $newPassword); } /** * @param int $uid * @throws NotFoundException * @throws OperationException */ public static function deleteUser(int $uid): void { $user = LinUserModel::get($uid, 'identity'); if (!$user) { throw new NotFoundException(); } Db::startTrans(); try { $user->groups()->detach(); $user->together('identity')->delete(); Db::commit(); } catch (Exception $ex) { DB::rollback(); throw new OperationException(['msg' => "删除用户失败"]); } } /** * @param int $uid * @param array $groupIds * @throws ForbiddenException * @throws NotFoundException * @throws OperationException * @throws DataNotFoundException * @throws ModelNotFoundException * @throws DbException */ public static function updateUserInfo(int $uid, array $groupIds): void { $user = LinUserModel::get($uid); if (!$user) { throw new NotFoundException(); } $userGroupIds = LinUserGroupModel::where('user_id', $uid)->column('group_id'); $isAdmin = LinGroupModel::where('level', GroupLevelEnum::ROOT) ->whereIn('id', $userGroupIds) ->find(); if ($isAdmin) { throw new ForbiddenException(['code' => 10078, 'msg' => '不允许调整root分组信息']); } foreach ($userGroupIds as $groupId) { $group = LinGroupModel::get($groupId); if ($group['level'] === GroupLevelEnum::ROOT) { throw new ForbiddenException(['code' => 10073, 'msg' => '不允许添加用户到root分组']); } if (!$group) { throw new NotFoundException(['code' => 10077]); } } Db::startTrans(); try { $user->groups()->detach(); $user->groups()->attach($groupIds); Db::commit(); } catch (Exception $ex) { DB::rollback(); throw new OperationException(['msg' => "更新用户分组失败"]); } } /** * @return array|PDOStatement|string|\think\Collection|Collection * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException */ public static function getAllGroups() { $groups = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)->select(); if ($groups->isEmpty()) { throw new NotFoundException(); } return $groups; } /** * @param int $id * @return \think\db\Query * @throws DbException * @throws NotFoundException */ public static function getGroup(int $id) { $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT) ->get($id, 'permissions'); if (!$group) { throw new NotFoundException(); } return $group; } /** * @param string $name * @param string $info * @param array $permissionIds * @return int * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException * @throws OperationException */ public static function createGroup(string $name, string $info, array $permissionIds): int { $isExist = LinGroupModel::where('name', $name)->find(); if ($isExist) { throw new OperationException(['msg' => '分组名已存在']); } foreach ($permissionIds as $permissionId) { $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->get($permissionId); if (!$permission) { throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']); } } Db::startTrans(); try { $group = LinGroupModel::create(['name' => $name, 'info' => $info], true); $group->permissions()->saveAll($permissionIds); Db::commit(); return $group->getAttr('id'); } catch (\Exception $ex) { Db::rollback(); throw new OperationException(['msg' => "新增分组失败:{$ex->getMessage()}"]); } } /** * @param int $id * @param string $name * @param string $info * @return int * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException */ public static function updateGroup(int $id, string $name, string $info): int { $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT) ->find($id); if (!$group) { throw new NotFoundException(); } return $group->save(['name' => $name, 'info' => $info]); } /** * @param int $id * @throws ForbiddenException * @throws NotFoundException * @throws OperationException */ public static function deleteGroup(int $id): void { $group = LinGroupModel::find($id); if (!$group) { throw new NotFoundException(); } if ($group->getAttr('level') === GroupLevelEnum::ROOT) { throw new ForbiddenException(['msg' => '不允许删除root分组']); } if ($group->getAttr('level') === GroupLevelEnum::GUEST) { throw new ForbiddenException(['msg' => '不允许删除guest分组']); } Db::startTrans(); try { $group->permissions()->detach(); $group->users()->detach(); Db::commit(); } catch (Exception $ex) { Db::rollback(); throw new OperationException(['msg' => "删除分组失败:{$ex->getMessage()}"]); } } /** * @param int $id * @param array $permissionIds * @throws DbException * @throws NotFoundException * @throws OperationException */ public static function dispatchPermissions(int $id, array $permissionIds) { $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT) ->get($id); if (!$group) { throw new NotFoundException(); } foreach ($permissionIds as $permissionId) { $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->get($permissionId); if (!$permission) { throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']); } } try { $group->permissions()->attach($permissionIds); } catch (Exception $ex) { throw new OperationException(['msg' => '权限分配失败']); } } /** * @param int $id * @param array $permissionIds * @return int * @throws DbException * @throws NotFoundException */ public static function removePermissions(int $id, array $permissionIds): int { $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT) ->get($id); if (!$group) { throw new NotFoundException(); } foreach ($permissionIds as $permissionId) { $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->get($permissionId); if (!$permission) { throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']); } } return $group->permissions()->detach($permissionIds); } public static function createPermission(string $name, string $module): LinPermissionModel { return LinPermissionModel::create(['name' => $name, 'module' => $module, 'mount' => 1]); } } ================================================ FILE: application/api/service/admin/Log.php ================================================ $start, 'end' => $end, 'name' => $name]; $logsRes = LinLogModel::getLogs($offset, $count, $params); return [ 'items' => $logsRes['logList'], 'count' => $count, 'page' => $page, 'total' => $logsRes['total'] ]; } /** * @param int $page * @param int $count * @param string|null $start * @param string|null $end * @param string|null $name * @param string|null $keyword * @return array * @throws ParameterException */ public static function searchLogs(int $page, int $count, string $start = null, string $end = null, string $name = null, string $keyword = null) { list($offset, $count) = paginate($count, $page); $params = ['start' => $start, 'end' => $end, 'name' => $name, 'keyword' => $keyword]; $logsRes = LinLogModel::searchLogs($offset, $count, $params); return [ 'items' => $logsRes['logList'], 'count' => $count, 'page' => $page, 'total' => $logsRes['total'] ]; } /** * @param int $page * @param int $count * @return array * @throws ParameterException */ public static function getUserNames(int $page, int $count) { list($start, $count) = paginate($count, $page); $usersRes = LinLogModel::getUserNames($start, $count); $items = array_map(function ($item) { return $item['username']; }, $usersRes['userList']->toArray()); return [ 'items' => $items, 'count' => $count, 'page' => $page, 'total' => $usersRes['total'] ]; } } ================================================ FILE: application/api/service/admin/User.php ================================================ find(); if ($user) { throw new RepeatException(['msg' => '用户名已存在']); } if (isset($params['email'])) { $user = LinUserModel::where('email', $params['email'])->find(); if ($user) { throw new RepeatException(['msg' => '邮箱地址已存在']); } } if (isset($params['group_ids'])) { $groups = LinGroupModel::select($params['group_ids']); foreach ($groups as $group) { if ($group['level'] === GroupLevelEnum::ROOT) { throw new ForbiddenException(['msg' => '不允许分配用户到root分组']); } } if ($groups->isEmpty()) { throw new NotFoundException(); } } return self::registerUser($params); } /** * @param string $username * @param string $password * @return Model * @throws AuthFailedException * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException * @throws NotFoundException */ public static function verify(string $username, string $password): Model { $user = new LinUserIdentityModel(); $user = $user->where('identifier', $username) ->where('identity_type', IdentityTypeEnum::PASSWORD) ->find(); if (!$user) { throw new NotFoundException(['msg' => '用户不存在']); } if (!$user->checkPassword($password)) { throw new AuthFailedException(); } return $user; } public static function generateTokenExtend(Model $linUserIdentityModel) { $user = LinUserModel::get($linUserIdentityModel['user_id']); $userPermissions = self::getPermissions($user->getAttr('id')); return [ 'id' => $user->getAttr('id'), 'identifier' => $linUserIdentityModel->getAttr('identifier'), 'email' => $user->getAttr('email'), 'admin' => $userPermissions['admin'], 'permissions' => $userPermissions['permissions'], ]; } public static function getPermissions(int $uid): array { $user = LinUserModel::get($uid); $groupIds = LinUserGroupModel::where('user_id', $uid) ->column('group_id'); $root = LinGroupModel::where('level', GroupLevelEnum::ROOT) ->whereIn('id', $groupIds)->find(); $user = $user->hidden(['username'])->toArray(); $user['admin'] = $root ? true : false; if ($root) { $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->select() ->toArray(); $user['permissions'] = formatPermissions($permissions); } else { $permissionIds = LinGroupPermissionModel::whereIn('group_id', $groupIds) ->column('permission_id'); $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT) ->select($permissionIds)->toArray(); $user['permissions'] = formatPermissions($permissions); } return $user; } public static function getInformation(int $uid) { return LinUser::get($uid, 'groups'); } public static function updateUser(array $params): int { $user = LoginToken::getInstance()->getTokenExtend(); if (isset($params['username']) && $params['username'] !== $user['username']) { $isExit = LinUserModel::where('username', $params['username']) ->find(); if ($isExit) { throw new RepeatException(['msg' => "用户名已被占用"]); } } if (isset($params['email']) && $params['email'] !== $user['email']) { $isExit = LinUserModel::where('email', $params['email']) ->find(); if ($isExit) { throw new RepeatException(['msg' => "邮箱已被占用"]); } } $user = LinUserModel::get($user['id']); return $user->allowField(true)->save($params); } public static function changePassword(string $oldPassword, string $newPassword): int { $currentUser = LoginToken::getInstance()->getTokenExtend(); $user = new LinUserIdentityModel(); $user = $user::where('identity_type', IdentityTypeEnum::PASSWORD) ->where('identifier', $currentUser['identifier']) ->find(); if (!$user) { throw new NotFoundException(); } if (!$user->checkPassword($oldPassword)) { throw new AuthFailedException(); } $user->credential = md5($newPassword); return $user->save(); } /** * @param array $params * @return LinUserModel * @throws OperationException */ private static function registerUser(array $params): LinUserModel { Db::startTrans(); try { $user = LinUserModel::create($params, true); $user->identity()->save([ 'identity_type' => IdentityTypeEnum::PASSWORD, 'identifier' => $user['username'], 'credential' => md5($params['password']) ]); // 判断是否同时分配了分组 if (isset($params['group_ids']) && count($params['group_ids']) > 0) { $user->groups()->attach($params['group_ids']); } else { // 没有分配分组,添加到游客分组 $group = LinGroupModel::where('level', GroupLevelEnum::GUEST)->find(); $user->groups()->attach([$group['id']]); } Db::commit(); return $user; } catch (Exception $ex) { Db::rollback(); throw new OperationException(['msg' => "注册用户失败:{$ex->getMessage()}"]); } } // private static function formatPermissions(array $permissions) // { // $groupPermission = []; // foreach ($permissions as $permission) { // $item = [ // 'name' => $permission['name'], // 'module' => $permission['module'] // ]; // $groupPermission[$permission['module']][] = $item; // } // // $result[] = array_map(function ($item) { // return $item; // }, $groupPermission); // return $result; // } } ================================================ FILE: application/api/service/token/LoginToken.php ================================================ tokenConfig = (new TokenConfig()) ->dualToken($config['enable_dual_token']) ->setAlgorithms($config['algorithms']) ->setIat(time()) ->setIss($config['issuer']) ->setAccessSecretKey($config['access_secret_key']) ->setAccessExp($config['access_expire_time']) ->setRefreshSecretKey($config['refresh_secret_key']) ->setRefreshExp($config['refresh_expire_time']); } public static function getInstance(): LoginToken { if (!self::$instance instanceof self) { self::$instance = new self(); } return self::$instance; } /** * 获取令牌 * @param array $extend 要插入到令牌扩展字段中的信息 * @return array * @throws \Exception */ public function getToken(array $extend): array { $this->tokenConfig->setExtend($extend); return TokenUtils::makeToken($this->tokenConfig); } /** * 令牌刷新 * @param string $refreshToken 当开启了双令牌后颁发的refreshToken,用于刷新accessToken * @return array * @throws TokenException */ public function refresh(string $refreshToken): array { try { return TokenUtils::refresh($refreshToken, $this->tokenConfig); } catch (SignatureInvalidException $signatureInvalidException) { throw new TokenException(['msg' => '令牌签名错误']); } catch (BeforeValidException $beforeValidException) { throw new TokenException(); } catch (ExpiredException $expiredException) { throw new TokenException(['error_code' => 10042, 'msg' => '令牌已过期,请重新登录']); } catch (UnexpectedValueException $unexpectedValueException) { throw new TokenException(); } } /** * @param string|null $token * @param string $tokenType * @return array * @throws TokenException */ public function verify(string $token = null, string $tokenType = 'access') { $token = $token ?: $this->getTokenFromHeaders(); try { return TokenUtils::verifyToken($token, $tokenType, $this->tokenConfig); } catch (SignatureInvalidException $signatureInvalidException) { throw new TokenException(['msg' => '令牌签名错误']); } catch (BeforeValidException $beforeValidException) { throw new TokenException(); } catch (ExpiredException $expiredException) { throw new TokenException(['error_code' => 10041, 'msg' => '令牌已过期']); } catch (UnexpectedValueException $unexpectedValueException) { throw new TokenException(); } } /** * 获取令牌扩展字段内容 * @param string|null $token * @param string $tokenType * @return array * @throws TokenException */ public function getTokenExtend(string $token = null, string $tokenType = 'access'): array { return (array)$this->verify($token, $tokenType)['extend']; } /** * 获取指定令牌扩展内容字段的值 * @param string $val * @return mixed * @throws TokenException */ public function getExtendVal(string $val) { return $this->getTokenExtend()[$val]; } public function getCurrentUid() { return $this->getExtendVal('id'); } public function getCurrentUserName() { return $this->getExtendVal('identifier'); } public function getTokenFromHeaders(): string { $authorization = Request::header('authorization'); if (!$authorization) { throw new TokenException(['msg' => '请求未携带Authorization信息']); } list($type, $token) = explode(' ', $authorization); if ($type !== 'Bearer') throw new TokenException(['msg' => '接口认证方式需为Bearer']); if (!$token || $token === 'undefined') { throw new TokenException(['msg' => '尝试获取的Authorization信息不存在']); } return $token; } } ================================================ FILE: application/api/validate/user/ChangePasswordForm.php ================================================ 'require', 'new_password|新密码' => 'require|confirm:confirm_password', 'confirm_password|确认密码' => 'require', ]; } ================================================ FILE: application/api/validate/user/LoginForm.php ================================================ 'require', 'password' => 'require', ]; protected $message = [ 'username' => '用户名不能为空', 'password' => '密码不能为空' ]; } ================================================ FILE: application/api/validate/user/RegisterForm.php ================================================ 'require|confirm:confirm_password', 'confirm_password' => 'require', 'username' => 'require|length:2,10', 'group_ids' => 'array', 'email' => 'email' ]; } ================================================ FILE: application/api/validate/user/ResetPasswordValidator.php ================================================ 'require|integer', 'new_password|新密码' => 'require|confirm:confirm_password', 'confirm_password|确认密码' => 'require', ]; } ================================================ FILE: application/api/validate/user/UpdateUserForm.php ================================================ 'length:2,10', 'email' => 'email', 'nickname' => 'length:2,10', ]; } ================================================ FILE: application/command.php ================================================ // +---------------------------------------------------------------------- return []; ================================================ FILE: application/common.php ================================================ // +---------------------------------------------------------------------- // 应用公共文件 use LinCmsTp5\exception\ParameterException; use think\response\Json; /** * 统一响应包装函数 * @param $code * @param $errorCode * @param $data * @param $msg * @return Json */ function writeJson($code, $data, $msg = 'ok', $errorCode = 0) { $data = [ 'code' => $errorCode, 'result' => $data, 'message' => $msg ]; return json($data, $code); } /** * 分页参数处理函数 * @param int $count * @param int $page * @return array * @throws ParameterException */ function paginate(int $count = 10, int $page = 0) { // $count = intval(Request::get('count', $count)); // $start = intval(Request::get('page', $page)); // $page = $start; $count = $count >= 15 ? 15 : $count; $start = $page * $count; if ($start < 0 || $count < 0) throw new ParameterException(); return [$start, $count]; } /** * 权限数组格式化函数 * @param array $permissions * @return array */ function formatPermissions(array $permissions) { $groupPermission = []; foreach ($permissions as $permission) { $item = [ 'permission' => $permission['name'], 'module' => $permission['module'] ]; $groupPermission[$permission['module']][] = $item; } $result = []; foreach ($groupPermission as $key => $item) { array_push($result, [$key => $item]); } return $result; } ================================================ FILE: application/http/middleware/Authentication.php ================================================ check(); if (!$auth) { throw new ForbiddenException(); } return $next($request); } } ================================================ FILE: application/index/controller/Index.php ================================================ *{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }

Lin
心上无垢,林间有风。

'); } } ================================================ FILE: application/lib/authenticator/Authenticator.php ================================================ controller(); // 控制层下有二级目录,需要解析下。如controller/cms/Admin,获取到的是Cms.Admin $controllerPath = explode('.', $controller); // 获取当前请求的方法 $action = $request->action(); // 反射获取当前请求的控制器类 $class = new ReflectionClass('app\\api\\controller\\' . strtolower($controllerPath[0]) . '\\' . $controllerPath[1]); $this->parsedClass = (new Reflex($class->newInstance()))->setMethod($action); } /** * 入口方法 * @return bool * @throws DeployException * @throws ReflectionException */ public function check(): bool { //判断是否开启加载文件函数注释 if (ini_get('opcache.save_comments') === '0' || ini_get('opcache.save_comments') === '') { throw new DeployException(); } // 获取方法权限控制等级 $actionPermissionLevel = $this->actionAuthorityLevel(); // 没有等级标识,直接通过 if (!$actionPermissionLevel) { return true; } // 执行校验并返回校验结果 return $this->execute($actionPermissionLevel); } /** * 执行各权限等级校验 * @param string $actionPermissionLevel * @return bool * @throws ReflectionException */ public function execute(string $actionPermissionLevel): bool { // 账户信息,包含所拥有的权限列表 $userInfo = $this->getUserInfo(); //账户属于超级管理员,直接通过 if ($userInfo['admin'] === true) return true; $actionPermissionName = $this->actionPermission(); return AuthenticatorExecutorFactory::getInstance($actionPermissionLevel)->handle($userInfo, $actionPermissionName); } /** * 获取接口权限等级注解 * @return string * @throws Exception */ protected function actionAuthorityLevel(): string { $permissionLevel = null; if ($this->parsedClass->isExist(PermissionLevelEnum::LOGIN_REQUIRED)) { $permissionLevel = PermissionLevelEnum::LOGIN_REQUIRED; return $permissionLevel; } if ($this->parsedClass->isExist(PermissionLevelEnum::GROUP_REQUIRED)) { $permissionLevel = PermissionLevelEnum::GROUP_REQUIRED; return $permissionLevel; } if ($this->parsedClass->isExist(PermissionLevelEnum::ADMIN_REQUIRED)) { $permissionLevel = PermissionLevelEnum::ADMIN_REQUIRED; return $permissionLevel; } return ''; } protected function getUserInfo(): array { return LoginToken::getInstance()->getTokenExtend(); } /** * 获取接口权限注解内容 * @return string * @throws Exception */ protected function actionPermission(): string { $actionAuthContent = $this->parsedClass->get('permission'); $actionAuthContent = empty($actionAuthContent) ? '' : $actionAuthContent[0] . '/' . $actionAuthContent[1]; return $actionAuthContent; } } ================================================ FILE: application/lib/authenticator/AuthenticatorExecutorFactory.php ================================================ namespaceList = (new Scan())->scanController(); } /** * @throws ReflectionException */ public function run() { return $this->getPermissionList(); } /** * @return array * @throws ReflectionException */ private function getPermissionList() { $permissionList = []; // 遍历需要解析@permission注解的控制器类 foreach ($this->namespaceList as $value) { // 反射控制器类 $class = new ReflectionClass($value); // 类下面的所有方法的数组 $methods = $class->getMethods(); // 类下面所有含有@permission注解的方法的注解内容数组 $methodPermissionList = $this->getPermissionByMethods($class->newInstance(), $methods); if (!empty($methodPermissionList)) { // 插入类权限数组 if (empty($permissionList)) { $permissionList = $methodPermissionList; } else { $permissionList = array_merge($permissionList, $methodPermissionList); } } } return $permissionList; } /** * @param $class * @param $methods * @param string $annotationField * @return array * @throws Exception */ private function getPermissionByMethods($class, $methods, $annotationField = 'permission') { $data = []; $re = new Reflex($class); foreach ($methods as $value) { $re->setMethod($value->name); $permissionAnnotationArray = $re->get($annotationField); if (!empty($permissionAnnotationArray) && !in_array('hidden', $permissionAnnotationArray)) { $permission = $this->handleAnnotation($permissionAnnotationArray); array_push($data, $permission); } } return $data; } public function handleAnnotation(array $annotation) { return [ 'name' => $annotation[0], 'module' => $annotation[1], 'mount' => MountTypeEnum::MOUNT ]; } } ================================================ FILE: application/lib/authenticator/Scan.php ================================================ controller_namespace = 'app\\api\\controller\\'; // 拼接出当前应用模块下的控制器层目录在服务器上的绝对路径 $this->controller_path = Env::get('module_path') . 'controller'; // 初始化需权限扫描的命名空间列表 $this->authScanNamespaceList = []; } /** * 入口方法,调用scanControllerLayerDir()扫描控制器层 * @return array 控制器层下所有类的完整命名空间数组 */ public function scanController() { return $this->scanControllerLayerDir($this->controller_path); } /** * 递归扫描控制器目录,扫描到类文件的时候push命名空间到$this->authScanNamespaceList * @param string $path 扫描的目标目录 * @param string $subModule 可空,目标目录的子目录 * @return array 控制器层下所有类的完整命名空间数组 */ private function scanControllerLayerDir(string $path, string $subModule = '') { $files = scandir($path); foreach ($files as $file) { if ($file !== '.' && $file !== '..') { if (strpos($file, '.php')) { $classFileName = substr($file, 0, -4); $module = $subModule ? $subModule . '\\' : ''; $completeNamespace = $this->controller_namespace . $module . $classFileName; array_push($this->authScanNamespaceList, $completeNamespace); } else { $ds = PHP_OS === 'WINNT' ? '\\' : DIRECTORY_SEPARATOR; $subDir = $path . $ds . $file; $this->scanControllerLayerDir($subDir, $file); } } } return $this->authScanNamespaceList; } } ================================================ FILE: application/lib/authenticator/executor/IExecutor.php ================================================ verify(); return true; } } ================================================ FILE: application/lib/enum/GroupLevelEnum.php ================================================ files as $key => $file) { $md5 = $this->generateMd5($file); $exists = LinFile::get(['md5' => $md5]); if ($exists) { array_push($ret, [ 'id' => $exists['id'], 'key' => $key, 'path' => $exists['path'], 'url' => $host . '/' . $this->storeDir . '/' . $exists['path'] ]); } else { $size = $this->getSize($file); $info = $file->move(Env::get('root_path') . '/' . 'public' . '/' . $this->storeDir); if ($info) { $extension = '.' . $info->getExtension(); $path = str_replace('\\', '/', $info->getSaveName()); $name = $info->getFilename(); } else { throw new FileException([ 'msg' => "存储本地文件失败", 'error_code' => 60001 ]); } $linFile = LinFile::create([ 'name' => $name, 'path' => $path, 'size' => $size, 'extension' => $extension, 'md5' => $md5, 'type' => 1 ]); array_push($ret, [ 'id' => $linFile->id, 'key' => $key, 'path' => $path, 'url' => $host . '/' . $this->storeDir . '/' . $path ]); } } return $ret; } } ================================================ FILE: application/provider.php ================================================ // +---------------------------------------------------------------------- // 应用容器绑定定义 return [ ]; ================================================ FILE: application/tags.php ================================================ // +---------------------------------------------------------------------- // 应用行为扩展定义文件 return [ // 应用初始化 'app_init' => [ ], // 应用开始 'app_begin' => [], // 模块初始化 'module_init' => [], // 操作开始执行 'action_begin' => [], // 视图内容过滤 'view_filter' => [], // 日志写入 'log_write' => [], // 应用结束 'app_end' => [], // api日志 'logger' => [ 'app\\api\\behavior\\Logger', ] ]; ================================================ FILE: build.php ================================================ // +---------------------------------------------------------------------- return [ // 生成应用公共文件 '__file__' => ['common.php'], // 定义demo模块的自动生成 (按照实际定义的文件名生成) 'demo' => [ '__file__' => ['common.php'], '__dir__' => ['behavior', 'controller', 'model', 'view'], 'controller' => ['Index', 'Test', 'UserType'], 'model' => ['User', 'UserType'], 'view' => ['index/index'], ], // 其他更多的模块定义 ]; ================================================ FILE: composer.json ================================================ { "name": "topthink/think", "description": "the new thinkphp framework", "type": "project", "keywords": [ "framework", "thinkphp", "ORM" ], "homepage": "http://thinkphp.cn/", "license": "Apache-2.0", "authors": [ { "name": "liu21st", "email": "liu21st@gmail.com" } ], "require": { "php": ">=7.1.0", "topthink/framework": "5.1.36", "topthink/think-migration": "2.*", "lin-cms-tp5/base-core": "dev-master", "lin-cms-tp/validate-core": "dev-master", "lin-cms-tp/utils-core": "dev-master", "qinchen/web-utils": ">0.0.1" }, "autoload": { "psr-4": { "app\\": "application" } }, "extra": { "think-path": "thinkphp" }, "config": { "preferred-install": "dist" }, "repositories": { } } ================================================ FILE: config/app.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 应用设置 // +---------------------------------------------------------------------- return [ // 应用名称 'app_name' => '', // 应用地址 'app_host' => '', // 应用调试模式 'app_debug' => true, // 应用Trace 'app_trace' => false, // 是否支持多模块 'app_multi_module' => true, // 入口自动绑定模块 'auto_bind_module' => false, // 注册的根命名空间 'root_namespace' => [], // 默认输出类型 'default_return_type' => 'json', // 默认AJAX 数据返回格式,可选json xml ... 'default_ajax_return' => 'json', // 默认JSONP格式返回的处理方法 'default_jsonp_handler' => 'jsonpReturn', // 默认JSONP处理方法 'var_jsonp_handler' => 'callback', // 默认时区 'default_timezone' => 'Asia/Shanghai', // 是否开启多语言 'lang_switch_on' => false, // 默认全局过滤方法 用逗号分隔多个 'default_filter' => '', // 默认语言 'default_lang' => 'zh-cn', // 应用类库后缀 'class_suffix' => false, // 控制器类后缀 'controller_suffix' => false, // +---------------------------------------------------------------------- // | 模块设置 // +---------------------------------------------------------------------- // 默认模块名 'default_module' => 'index', // 禁止访问模块 'deny_module_list' => ['common'], // 默认控制器名 'default_controller' => 'Index', // 默认操作名 'default_action' => 'index', // 默认验证器 'default_validate' => '', // 默认的空模块名 'empty_module' => '', // 默认的空控制器名 'empty_controller' => 'Error', // 操作方法前缀 'use_action_prefix' => false, // 操作方法后缀 'action_suffix' => '', // 自动搜索控制器 'controller_auto_search' => false, // +---------------------------------------------------------------------- // | URL设置 // +---------------------------------------------------------------------- // PATHINFO变量名 用于兼容模式 'var_pathinfo' => 's', // 兼容PATH_INFO获取 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], // pathinfo分隔符 'pathinfo_depr' => '/', // HTTPS代理标识 'https_agent_name' => '', // IP代理获取标识 'http_agent_ip' => 'X-REAL-IP', // URL伪静态后缀 'url_html_suffix' => 'html', // URL普通方式参数 用于自动生成 'url_common_param' => false, // URL参数方式 0 按名称成对解析 1 按顺序解析 'url_param_type' => 0, // 是否开启路由延迟解析 'url_lazy_route' => false, // 是否强制使用路由 'url_route_must' => false, // 合并路由规则 'route_rule_merge' => false, // 路由是否完全匹配 'route_complete_match' => true, // 使用注解路由 'route_annotation' => false, // 域名根,如thinkphp.cn 'url_domain_root' => '', // 是否自动转换URL中的控制器和操作名 'url_convert' => true, // 默认的访问控制器层 'url_controller_layer' => 'controller', // 表单请求类型伪装变量 'var_method' => '_method', // 表单ajax伪装变量 'var_ajax' => '_ajax', // 表单pjax伪装变量 'var_pjax' => '_pjax', // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 'request_cache' => false, // 请求缓存有效期 'request_cache_expire' => null, // 全局请求缓存排除规则 'request_cache_except' => [], // 是否开启路由缓存 'route_check_cache' => false, // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5 'route_check_cache_key' => '', // 路由缓存类型及参数 'route_cache_option' => [], // 默认跳转页面对应的模板文件 'dispatch_success_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl', 'dispatch_error_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl', // 异常页面的模板文件 'exception_tmpl' => Env::get('think_path') . 'tpl/think_exception.tpl', // 错误显示信息,非调试模式有效 'error_message' => '页面错误!请稍后再试~', // 显示错误信息 'show_error_msg' => false, // 异常处理handle类 留空使用 \think\exception\Handle 'exception_handle' => 'LinCmsTp5\exception\ExceptionHandler', ]; ================================================ FILE: config/cache.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 缓存设置 // +---------------------------------------------------------------------- return [ // 驱动方式 'type' => 'File', // 缓存保存目录 'path' => '', // 缓存前缀 'prefix' => '', // 缓存有效期 0表示永久缓存 'expire' => 0, ]; ================================================ FILE: config/console.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 控制台配置 // +---------------------------------------------------------------------- return [ 'name' => 'Think Console', 'version' => '0.1', 'user' => null, 'auto_path' => env('app_path') . 'command' . DIRECTORY_SEPARATOR, ]; ================================================ FILE: config/cookie.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | Cookie设置 // +---------------------------------------------------------------------- return [ // cookie 名称前缀 'prefix' => '', // cookie 保存时间 'expire' => 0, // cookie 保存路径 'path' => '/', // cookie 有效域名 'domain' => '', // cookie 启用安全传输 'secure' => false, // httponly设置 'httponly' => '', // 是否使用 setcookie 'setcookie' => true, ]; ================================================ FILE: config/database.php ================================================ // +---------------------------------------------------------------------- return [ // 数据库类型 'type' => 'mysql', // 服务器地址 'hostname' => '127.0.0.1', // 数据库名 'database' => 'lin_cms', // 用户名 'username' => 'root', // 密码 'password' => '', // 端口 'hostport' => '', // 连接dsn 'dsn' => '', // 数据库连接参数 'params' => [], // 数据库编码默认采用utf8 'charset' => 'utf8', // 数据库表前缀 'prefix' => '', // 数据库调试模式 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'deploy' => 0, // 数据库读写是否分离 主从式有效 'rw_separate' => false, // 读写分离后 主服务器数量 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', // 自动读取主库数据 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, // 数据集返回类型 'resultset_type' => 'collection', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 'sql_explain' => false, // Builder类 'builder' => '', // Query类 'query' => '\\think\\db\\Query', // 是否需要断线重连 'break_reconnect' => false, // 断线标识字符串 'break_match_str' => [] ]; ================================================ FILE: config/file.php ================================================ 'uploads', # 文件的存储路径 "single_limit" => 1024 * 1024 * 2, # 单个文件的大小限制,默认2M "total_limit"=> 1024 * 1024 * 20, # 所有文件的大小限制,默认20M "nums" => 10, # 文件数量限制,默认10 "include" => [], # 文件后缀名的排除项,默认排除[],即允许所有类型的文件上传 "exclude" => [] # 文件后缀名的包括项 ]; ================================================ FILE: config/log.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 日志设置 // +---------------------------------------------------------------------- return [ // 日志记录方式,内置 file socket 支持扩展 'type' => 'File', // 日志保存目录 'path' => '', // 日志记录级别 'level' => [], // 单文件日志写入 'single' => false, // 独立日志级别 'apart_level' => [], // 最大日志文件数量 'max_files' => 0, // 是否关闭日志写入 'close' => true, ]; ================================================ FILE: config/middleware.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 中间件配置 // +---------------------------------------------------------------------- return [ // 默认中间件命名空间 'default_namespace' => 'app\\http\\middleware\\', 'ReflexValidate' => LinCmsTp\Param::class // 开启注释验证器,需要的中间件配置,请勿胡乱关闭 ]; ================================================ FILE: config/secure.php ================================================ 'SDJxjkxc9o', 'refresh_token_salt' => 'SKTxigxc9o', ]; ================================================ FILE: config/session.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 会话设置 // +---------------------------------------------------------------------- return [ 'id' => '', // SESSION_ID的提交变量,解决flash上传跨域 'var_session_id' => '', // SESSION 前缀 'prefix' => 'think', // 驱动方式 支持redis memcache memcached 'type' => '', // 是否自动开启 SESSION 'auto_start' => true, ]; ================================================ FILE: config/template.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | 模板设置 // +---------------------------------------------------------------------- return [ // 模板引擎类型 支持 php think 支持扩展 'type' => 'Think', // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法 'auto_rule' => 1, // 模板路径 'view_path' => '', // 模板后缀 'view_suffix' => 'html', // 模板文件名分隔符 'view_depr' => DIRECTORY_SEPARATOR, // 模板引擎普通标签开始标记 'tpl_begin' => '{', // 模板引擎普通标签结束标记 'tpl_end' => '}', // 标签库标签开始标记 'taglib_begin' => '{', // 标签库标签结束标记 'taglib_end' => '}', ]; ================================================ FILE: config/token.php ================================================ true, # 令牌算法类型 'algorithms' => 'HS256', # 令牌签发者 'issuer' => 'lin-cms-tp5', # accessToken秘钥 'access_secret_key' => 'w.kx(c82jkA', # accessToken过期时间,单位秒 'access_expire_time' => 7200, # refreshToken秘钥 'refresh_secret_key' => 'xUh.@3s8A8', # refreshToken过期时间,建议设置较长时间 # 在有效期内可用于刷新accessToken,单位秒 'refresh_expire_time' => 604800, ]; ================================================ FILE: config/trace.php ================================================ // +---------------------------------------------------------------------- // +---------------------------------------------------------------------- // | Trace设置 开启 app_trace 后 有效 // +---------------------------------------------------------------------- return [ // 内置Html Console 支持扩展 'type' => 'Html', ]; ================================================ FILE: error_msg.md ================================================ # 错误码字典 ## 参数类错误 99999 参数校验不通过 ## 通用错误 10001 资源操作失败 10021 资源不存在 10071 资源已存在 ## 令牌类错误 10000 令牌解析失败 10041 access令牌过期 10042 refresh令牌过期 10002 权限不足 10073 不允许添加用户到root分组 10078 不允许调整root分组信息 ## 用户类错误 10030 密码错误 ## 分组类错误 ### 3000x 30001 分组创建失败 30002 分组权限编辑失败 30003 分组不存在 30004 分组已存在 ## 服务端类错误 ### 5000x 50000 服务环境配置错误 ## 上传类错误 ### 6000x 60000 不符合上传条件 60001 上传文件失败 ================================================ FILE: extend/.gitignore ================================================ * !.gitignore ================================================ FILE: public/.htaccess ================================================ Options +FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] ================================================ FILE: public/index.php ================================================ // +---------------------------------------------------------------------- // [ 应用入口文件 ] namespace think; // 加载基础文件 require __DIR__ . '/../thinkphp/base.php'; // 支持事先使用静态方法设置Request对象和Config对象 // 执行应用并响应 Container::get('app')->run()->send(); ================================================ FILE: public/robots.txt ================================================ User-agent: * Disallow: ================================================ FILE: public/router.php ================================================ // +---------------------------------------------------------------------- // $Id$ if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) { return false; } else { require __DIR__ . "/index.php"; } ================================================ FILE: public/static/.gitignore ================================================ * !.gitignore ================================================ FILE: route/route.php ================================================ // +---------------------------------------------------------------------- use think\facade\Route; Route::get('v1/test', 'api/v1.Test/index'); Route::group('', function () { Route::group('cms', function () { // 账户相关接口分组 Route::group('user', function () { // 登陆接口 Route::post('login', 'api/cms.User/userLogin'); // 刷新令牌 Route::get('refresh', 'api/cms.User/refreshToken'); // 查询自己拥有的权限 Route::get('permissions', 'api/cms.User/getAllowedApis'); // 注册一个用户 Route::post('register', 'api/cms.User/register'); // 查询自己信息 Route::get('information', 'api/cms.User/getInformation'); // 用户更新信息 Route::put('', 'api/cms.User/update'); // 修改自己密码 Route::put('change_password', 'api/cms.User/changePassword'); }); // 管理类接口 Route::group('admin', function () { // 查询所有可分配的权限 Route::get('permission', 'api/cms.Admin/getAllPermissions'); // 查询所有用户 Route::get('users', 'api/cms.Admin/getAdminUsers'); // 修改用户密码 Route::put('user/:id/password', 'api/cms.Admin/changeUserPassword'); // 删除用户 Route::delete('user/:id', 'api/cms.Admin/deleteUser'); // 更新用户信息 Route::put('user/:id', 'api/cms.Admin/updateUser'); // 查询所有权限组 Route::get('group/all', 'api/cms.Admin/getGroupAll'); // 新增权限组 Route::post('group', 'api/cms.Admin/createGroup'); // 查询指定分组及其权限 Route::get('group/:id', 'api/cms.Admin/getGroup'); // 更新一个权限组 Route::put('group/:id', 'api/cms.Admin/updateGroup'); // 删除一个分组 Route::delete('group/:id', 'api/cms.Admin/deleteGroup'); // 删除多个权限 Route::post('permission/remove', 'api/cms.Admin/removePermissions'); // 分配多个权限 Route::post('permission/dispatch/batch', 'api/cms.Admin/dispatchPermissions'); }); // 日志类接口 Route::group('log', function () { Route::get('', 'api/cms.Log/getLogs'); Route::get('users', 'api/cms.Log/getUsers'); Route::get('search', 'api/cms.Log/getUserLogs'); }); //上传文件类接口 Route::post('file', 'api/cms.File/postFile'); }); Route::group('v1', function () { Route::group('book', function () { // 查询所有图书 Route::get('', 'api/v1.Book/getBooks'); // 新建图书 Route::post('', 'api/v1.Book/create'); // 查询指定bid的图书 Route::get(':bid', 'api/v1.Book/getBook'); // 搜索图书 // 更新图书 Route::put(':bid', 'api/v1.Book/update'); // 删除图书 Route::delete(':bid', 'api/v1.Book/delete'); }); }); })->middleware(['Authentication', 'ReflexValidate'])->allowCrossDomain(true, $header = [ 'Access-Control-Allow-Credentials' => 'true', 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE', 'Access-Control-Allow-Headers' => 'tag, Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With', ]); ================================================ FILE: runtime/.gitignore ================================================ * !.gitignore ================================================ FILE: schema.sql ================================================ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- 文件表 -- ---------------------------- DROP TABLE IF EXISTS lin_file; CREATE TABLE lin_file ( id int(10) unsigned NOT NULL AUTO_INCREMENT, path varchar(500) NOT NULL, type varchar(10) NOT NULL DEFAULT 'LOCAL' COMMENT 'LOCAL 本地,REMOTE 远程', name varchar(100) NOT NULL, extension varchar(50) DEFAULT NULL, size int(11) DEFAULT NULL, md5 varchar(40) DEFAULT NULL COMMENT 'md5值,防止上传重复文件', create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY md5_del (md5, delete_time) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 日志表 -- ---------------------------- DROP TABLE IF EXISTS lin_log; CREATE TABLE lin_log ( id int(10) unsigned NOT NULL AUTO_INCREMENT, message varchar(450) DEFAULT NULL, user_id int(10) unsigned NOT NULL, username varchar(24) DEFAULT NULL, status_code int(11) DEFAULT NULL, method varchar(20) DEFAULT NULL, path varchar(50) DEFAULT NULL, permission varchar(100) DEFAULT NULL, create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 权限表 -- ---------------------------- DROP TABLE IF EXISTS lin_permission; CREATE TABLE lin_permission ( id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(60) NOT NULL COMMENT '权限名称,例如:访问首页', module varchar(50) NOT NULL COMMENT '权限所属模块,例如:人员管理', mount tinyint(1) NOT NULL DEFAULT 1 COMMENT '0:关闭 1:开启', create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 分组表 -- ---------------------------- DROP TABLE IF EXISTS lin_group; CREATE TABLE lin_group ( id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(60) NOT NULL COMMENT '分组名称,例如:搬砖者', info varchar(255) DEFAULT NULL COMMENT '分组信息:例如:搬砖的人', level tinyint(2) NOT NULL DEFAULT 3 COMMENT '分组级别 1:root 2:guest 3:user(root、guest分组只能存在一个)', create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY name_del (name, delete_time) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 分组-权限表 -- ---------------------------- DROP TABLE IF EXISTS lin_group_permission; CREATE TABLE lin_group_permission ( id int(10) unsigned NOT NULL AUTO_INCREMENT, group_id int(10) unsigned NOT NULL COMMENT '分组id', permission_id int(10) unsigned NOT NULL COMMENT '权限id', PRIMARY KEY (id), KEY group_id_permission_id (group_id, permission_id) USING BTREE COMMENT '联合索引' ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 用户基本信息表 -- ---------------------------- DROP TABLE IF EXISTS lin_user; CREATE TABLE lin_user ( id int(10) unsigned NOT NULL AUTO_INCREMENT, username varchar(24) NOT NULL COMMENT '用户名,唯一', nickname varchar(24) DEFAULT NULL COMMENT '用户昵称', avatar varchar(500) DEFAULT NULL COMMENT '头像url', email varchar(100) DEFAULT NULL COMMENT '邮箱', create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY username_del (username, delete_time), UNIQUE KEY email_del (email, delete_time) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 用户授权信息表 # id # user_id # identity_type 登录类型(手机号 邮箱 用户名)或第三方应用名称(微信 微博等) # identifier 标识(手机号 邮箱 用户名或第三方应用的唯一标识) # credential 密码凭证(站内的保存密码,站外的不保存或保存token) -- ---------------------------- DROP TABLE IF EXISTS lin_user_identity; CREATE TABLE lin_user_identity ( id int(10) unsigned NOT NULL AUTO_INCREMENT, user_id int(10) unsigned NOT NULL COMMENT '用户id', identity_type varchar(100) NOT NULL, identifier varchar(100), credential varchar(100), create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; DROP TABLE IF EXISTS book; CREATE TABLE book ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(50) NOT NULL, author varchar(30) DEFAULT NULL, summary varchar(1000) DEFAULT NULL, image varchar(100) DEFAULT NULL, create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; -- ---------------------------- -- 用户-分组表 -- ---------------------------- DROP TABLE IF EXISTS lin_user_group; CREATE TABLE lin_user_group ( id int(10) unsigned NOT NULL AUTO_INCREMENT, user_id int(10) unsigned NOT NULL COMMENT '用户id', group_id int(10) unsigned NOT NULL COMMENT '分组id', PRIMARY KEY (id), KEY user_id_group_id (user_id, group_id) USING BTREE COMMENT '联合索引' ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci; SET FOREIGN_KEY_CHECKS = 1; -- ---------------------------- -- 插入超级管理员 -- 插入root分组 -- ---------------------------- BEGIN; INSERT INTO lin_user(id, username, nickname) VALUES (1, 'root', 'root'); INSERT INTO lin_user_identity (id, user_id, identity_type, identifier, credential) VALUES (1, 1, 'USERNAME_PASSWORD', 'root', 'e10adc3949ba59abbe56e057f20f883e'); INSERT INTO lin_group(id, name, info, level) VALUES (1, 'root', '超级用户组', 1); INSERT INTO lin_group(id, name, info, level) VALUES (2, 'guest', '游客组', 2); INSERT INTO lin_user_group(id, user_id, group_id) VALUES (1, 1, 1); COMMIT; ================================================ FILE: think ================================================ #!/usr/bin/env php // +---------------------------------------------------------------------- namespace think; // 加载基础文件 require __DIR__ . '/thinkphp/base.php'; // 应用初始化 Container::get('app')->path(__DIR__ . '/application/')->initialize(); // 控制台初始化 Console::init();