├── .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
├── favicon.ico
├── index.php
├── robots.txt
├── router.php
└── static
│ └── .gitignore
├── route
└── route.php
├── runtime
└── .gitignore
├── schema.sql
└── think
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /.vscode
3 | /vendor
4 | *.log
5 | thinkphp
6 | .env
7 | .DS_Store
8 | composer.lock
9 | /public/uploads
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: php
4 |
5 | branches:
6 | only:
7 | - stable
8 |
9 | cache:
10 | directories:
11 | - $HOME/.composer/cache
12 |
13 | before_install:
14 | - composer self-update
15 |
16 | install:
17 | - composer install --no-dev --no-interaction --ignore-platform-reqs
18 | - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
19 | - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
20 | - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
21 | - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
22 | - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
23 | - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
24 | - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
25 | - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
26 | - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
27 | - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
28 | - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
29 |
30 | script:
31 | - php think unit
32 |
33 | deploy:
34 | provider: releases
35 | api_key:
36 | secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
37 | file:
38 | - ThinkPHP_Core.zip
39 | - ThinkPHP_Full.zip
40 | skip_cleanup: true
41 | on:
42 | tags: true
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
2 | 版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
3 | All rights reserved。
4 | ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
5 |
6 | Apache Licence是著名的非盈利开源组织Apache采用的协议。
7 | 该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
8 | 允许代码修改,再作为开源或商业软件发布。需要满足
9 | 的条件:
10 | 1. 需要给代码的用户一份Apache Licence ;
11 | 2. 如果你修改了代码,需要在被修改的文件中说明;
12 | 3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
13 | 带有原来代码中的协议,商标,专利声明和其他原来作者规
14 | 定需要包含的说明;
15 | 4. 如果再发布的产品中包含一个Notice文件,则在Notice文
16 | 件中需要带有本协议内容。你可以在Notice中增加自己的
17 | 许可,但不可以表现为对Apache Licence构成更改。
18 | 具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 | POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Lin-CMS-TP5
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | # 简介
15 |
16 | ## 预防针
17 |
18 | * 本项目非官方团队出品,仅出于学习、研究目的丰富下官方项目的语言支持,目前已被收录至官方团队仓库,[点击查看](https://github.com/TaleLin)
19 | * 本项目采取后跟进官方团队功能的形式,即官方团队出什么功能,这边就跟进开发什么功能,开发者不必担心前端适配问题。
20 | * 在上一点的基础上,我们会尝试加入一些自己的想法并实现。
21 | * 局限于本人水平,有些地方还需重构,已经纳入了计划中,当然也会有我没考虑到的,希望有更多人参与进来一起完善,毕竟PHP作为世界上最好的语言不能缺席。
22 |
23 | ## 专栏教程
24 |
25 | * [《Lin CMS PHP&Vue教程》](https://course.7yue.pro/lin/lin-cms-php/)专栏教程连载更新中,通过实战开源前后端分离CMS——Lin CMS全家桶(lin-cms-vue & lin-cms-tp5)为一个前端应用实现内容管理系统。一套教程入门上手vue、ThinkPHP两大框架,自用、工作、私单一次打通。
26 |
27 | * 读者反馈:[《Lin CMS PHP&Vue教程》读者反馈贴](https://github.com/ChenJinchuang/lin-cms-tp5/issues/47)
28 |
29 | ## 线上文档地址(完善中)
30 |
31 | [http://chenjinchuang.gitee.io/lin-cms-book/](http://chenjinchuang.gitee.io/lin-cms-book/)
32 |
33 | ## 线上 Demo
34 |
35 | 可直接参考官方团队的线上Demo:[http://face.cms.7yue.pro/](http://face.cms.7yue.pro/),用户名:super,密码:123456
36 |
37 | ## 什么是 Lin CMS?
38 |
39 | > Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套**内容管理系统框架**。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。
40 |
41 | 本项目是基于ThinkPHP 5.1的 Lin CMS 后端实现。
42 |
43 | 官方团队产品了解请访问[TaleLin](https://github.com/TaleLin)
44 |
45 | ## Lin CMS 的特点
46 |
47 | Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。
48 |
49 | **Lin CMS 是一个前后端分离的 CMS 解决方案**
50 |
51 | 这意味着,Lin 既提供后台的支撑,也有一套对应的前端系统,当然双端分离的好处不仅仅在于此,我们会在后续提供NodeJS和PHP版本的 Lin。如果你心仪 Lin,却又因为技术栈的原因无法即可使用,没关系,我们会在后续提供更多的语言版本。为什么 Lin 要选择前后端分离的单页面架构呢?
52 |
53 | 首先,传统的网站开发更多的是采用服务端渲染的方式,需用使用一种模板语言在服务端完成页面渲染:比如 JinJa2、Jade 等。 服务端渲染的好处在于可以比较好的支持 SEO,但作为内部使用的 CMS 管理系统,SEO 并不重要。
54 |
55 | 但一个不可忽视的事实是,服务器渲染的页面到底是由前端开发者来完成,还是由服务器开发者来完成?其实都不太合适。现在已经没有多少前端开发者是了解这些服务端模板语言的,而服务器开发者本身是不太擅长开发页面的。那还是分开吧,前端用最熟悉的 Vue 写 JS 和 CSS,而服务器只关注自己的 API 即可。
56 |
57 | 其次,单页面应用程序的体验本身就要好于传统网站。
58 |
59 | 更多关于Lin CMS的介绍请访问[Lin CMS线上文档](http://doc.cms.7yue.pro/)
60 |
61 | **框架本身已内置了 CMS 常用的功能**
62 |
63 | Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需要集中精力开发自己的 CMS 业务即可
64 |
65 | ## Lin CMS TP5 的特点
66 |
67 | 在当前项目的版本`(0.0.1)`中,特点更多来自于`ThinkPHP 5.1`框架本身带来的特点。通过充分利用框架的特性,实现高效的后端使用、开发,也就是说,只要你熟悉`ThinkPHP`框架,那么对于理解使用和二次开发本项目是没有难度的,即便对于框架的某些功能存在疑问也完全可以通过ThinkPHP官方的开发手册找到答案。当然我们更欢迎你通过[Issues](https://github.com/ChenJinchuang/lin-cms-tp5/issues)来向我们提问:)
68 |
69 | 在下一个版本中`(>0.0.1)`,我们会在框架的基础上融入一些自己的东西来增强或者优化框架的使用、开发体验。
70 |
71 | ## 所需基础
72 |
73 | 由于 Lin 采用的是前后端分离的架构,所以你至少需要熟悉 PHP 和 Vue。
74 |
75 | Lin 的服务端框架是基于 ThinkPHP5.1的,所以如果你比较熟悉ThinkPHP的开发模式,那将可以更好的使用本项目。但如果你并不熟悉ThinkPHP,我们认为也没有太大的关系,因为框架本身已经提供了一套完整的开发机制,你只需要在框架下用 PHP 来编写自己的业务代码即可。照葫芦画瓢应该就是这种感觉。
76 |
77 | 但前端不同,前端还是需要开发者比较熟悉 Vue 的。但我想以 Vue 在国内的普及程度,绝大多数的开发者是没有问题的。这也正是我们选择 Vue 作为前端框架的原因。如果你喜欢 React Or Angular,那么加入我们,为 Lin 开发一个对应的版本吧。
78 |
79 | # 快速开始
80 |
81 | ## Server 端必备环境
82 |
83 | * 安装MySQL(version: 5.7+)
84 |
85 | * 安装PHP环境(version: 7.1+)
86 |
87 | ## 获取工程项目
88 |
89 | ```bash
90 | git clone https://github.com/ChenJinchuang/lin-cms-tp5.git
91 | ```
92 |
93 | > 执行完毕后会生成lin-cms-tp5目录
94 |
95 | ## 安装依赖包
96 |
97 | 执行命令前请确保你已经安装了composer工具
98 |
99 | ```bash
100 | # 进入项目根目录
101 | cd lin-cms-tp5
102 | # 先执行以下命令,全局替换composer源,解决墙的问题
103 | composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
104 | # 接着执行以下命令安装依赖包
105 | composer install
106 | ```
107 |
108 | ## 数据库配置
109 |
110 | Lin 需要你自己在 MySQL 中新建一个数据库,名字由你自己决定。例如,新建一个名为` lin-cms `的数据库。接着,我们需要在工程中进行一项简单的配置。使用编辑器打开 Lin 工程根目录下``/config/database.php``,找到如下配置项:
111 |
112 | ```php
113 | // 服务器地址
114 | 'hostname' => '',
115 | // 数据库名
116 | 'database' => 'lin-cms',
117 | // 用户名
118 | 'username' => 'root',
119 | // 密码
120 | 'password' => '',
121 |
122 | //省略后面一堆的配置项
123 | ```
124 |
125 | **请务必根据自己的实际情况修改此配置项**
126 |
127 | ## 导入数据
128 |
129 | 接下来使用你本机上任意一款数据库可视化工具,为已经创建好的`lin-cms`数据库运行lin-cms-tp5根目录下的`schema.sql`文件,这个SQL脚本文件将为为你生成一些基础的数据库表和数据。
130 |
131 | ## 运行
132 |
133 | 如果前面的过程一切顺利,项目所需的准备工作就已经全部完成,这时候你就可以试着让工程运行起来了。在工程的根目录打开命令行,输入:
134 |
135 | ```bash
136 | php think run --port 5000 //启动thinkPHP内置的Web服务器
137 | ```
138 |
139 | 启动成功后会看到如下提示:
140 |
141 | ```php
142 | ThinkPHP Development server is started On
143 | You can exit with `CTRL-C`
144 | ```
145 |
146 | 打开浏览器,访问``http://127.0.0.1:5000``,你会看到一个欢迎界面,至此,Lin-cms-tp5部署完毕,可搭配[lin-cms-vue](https://github.com/TaleLin/lin-cms-vue)使用了。
147 |
148 | ## 更新日志
149 |
150 | [查看日志](http://chenjinchuang.gitee.io/lin-cms-book/log/)
151 |
152 | ## 常见问题
153 |
154 | [查看常见问题](http://chenjinchuang.gitee.io/lin-cms-book/qa/)
155 |
156 | ## 讨论交流
157 |
158 | ### QQ 交流群
159 |
160 | QQ 群号:643205479
161 |
162 |
163 |
164 | ### 微信公众号
165 |
166 | 微信搜索:林间有风
167 |
168 |
169 |
--------------------------------------------------------------------------------
/application/.htaccess:
--------------------------------------------------------------------------------
1 | deny from all
--------------------------------------------------------------------------------
/application/api/behavior/Logger.php:
--------------------------------------------------------------------------------
1 | '日志信息不能为空'
31 | ]);
32 | }
33 |
34 | if (is_array($params)) {
35 | list('uid' => $uid, 'username' => $username, 'msg' => $message) = $params;
36 | } else {
37 | $tokenService = LoginToken::getInstance();
38 | $uid = $tokenService->getCurrentUid();
39 | $username = $tokenService->getCurrentUserName();
40 | $message = $params;
41 | }
42 |
43 | $data = [
44 | 'message' => $username . $message,
45 | 'user_id' => $uid,
46 | 'username' => $username,
47 | 'status_code' => Response::getCode(),
48 | 'method' => Request::method(),
49 | 'path' => '/' . Request::path(),
50 | 'permission' => null
51 | ];
52 |
53 | LinLog::create($data);
54 |
55 | }
56 | }
--------------------------------------------------------------------------------
/application/api/controller/cms/Admin.php:
--------------------------------------------------------------------------------
1 | get('page/d', 0);
56 | $count = $request->get('count/d', 10);
57 | $groupId = $request->get('group_id/d');
58 |
59 | return AdminService::getUsers($page, $count, $groupId);
60 | }
61 |
62 | /**
63 | * @adminRequired
64 | * @permission('修改用户密码','管理员','hidden')
65 | * @validate('ResetPasswordValidator')
66 | * @param Request $request
67 | * @param $id
68 | * @return Json
69 | * @throws NotFoundException
70 | */
71 | public function changeUserPassword(Request $request, $id)
72 | {
73 | $newPassword = $request->put('new_password');
74 | AdminService::changeUserPassword($id, $newPassword);
75 | Hook::listen('logger', "修改了用户ID为{$id}的密码");
76 |
77 | return writeJson(200, null, '修改成功', 4);
78 | }
79 |
80 | /**
81 | * @adminRequired
82 | * @permission('删除用户','管理员','hidden')
83 | * @param int $id
84 | * @param('id','用户id','require|integer')
85 | * @return Json
86 | * @throws NotFoundException
87 | * @throws OperationException
88 | */
89 | public function deleteUser(int $id)
90 | {
91 | AdminService::deleteUser($id);
92 | Hook::listen('logger', "删除了用户ID为:{$id}的用户");
93 | return writeJson(201, $id, '删除用户成功', 5);
94 | }
95 |
96 | /**
97 | * @adminRequired
98 | * @permission('管理员更新用户信息','管理员','hidden')
99 | * @param Request $request
100 | * @param('id','用户id','require|integer')
101 | * @param('group_ids','分组id','require|array|min:1')
102 | * @return Json
103 | * @throws NotFoundException
104 | * @throws OperationException
105 | * @throws ForbiddenException
106 | * @throws DataNotFoundException
107 | * @throws ModelNotFoundException
108 | * @throws DbException
109 | */
110 | public function updateUser(Request $request, $id)
111 | {
112 | $groupIds = $request->put('group_ids');
113 | AdminService::updateUserInfo($id, $groupIds);
114 |
115 | Hook::listen('logger', "更新了用户:{$id}的所属分组");
116 | return writeJson(201, $id, '更新用户成功', 6);
117 | }
118 |
119 | /**
120 | * @adminRequired
121 | * @permission('查询所有分组','管理员','hidden')
122 | * @return array|PDOStatement|string|\think\Collection|Collection
123 | * @throws DataNotFoundException
124 | * @throws DbException
125 | * @throws ModelNotFoundException
126 | * @throws NotFoundException
127 | */
128 | public function getGroupAll()
129 | {
130 | return AdminService::getAllGroups();
131 | }
132 |
133 | /**
134 | * @adminRequired
135 | * @permission('查询一个权限组及其权限','管理员','hidden')
136 | * @param int $id
137 | * @param('id','分组id','require|integer')
138 | * @return Query
139 | * @throws DbException
140 | * @throws NotFoundException
141 | */
142 | public function getGroup(int $id)
143 | {
144 | return AdminService::getGroup($id);
145 | }
146 |
147 | /**
148 | * @adminRequired
149 | * @permission('新建一个权限组','管理员','hidden')
150 | * @param Request $request
151 | * @param('name','分组名字','require')
152 | * @param('permission_ids','权限id','require|array|min:1')
153 | * @return Json
154 | * @throws DataNotFoundException
155 | * @throws DbException
156 | * @throws ModelNotFoundException
157 | * @throws NotFoundException
158 | * @throws OperationException
159 | */
160 | public function createGroup(Request $request)
161 | {
162 | $name = $request->post('name');
163 | $info = $request->post('info');
164 | $permissionIds = $request->post('permission_ids');
165 |
166 | $groupId = AdminService::createGroup($name, $info, $permissionIds);
167 |
168 | Hook::listen('logger', "创建了分组:{$name}");
169 | return writeJson(201, $groupId, '新增分组成功', 15);
170 | }
171 |
172 | /**
173 | * @adminRequired
174 | * @permission('更新一个权限组','管理员','hidden')
175 | * @param Request $request
176 | * @param int $id
177 | * @param('id','分组id','require|integer')
178 | * @param('info','分组信息','require')
179 | * @param('name','分组名字','require')
180 | * @return Json
181 | * @throws DataNotFoundException
182 | * @throws DbException
183 | * @throws ModelNotFoundException
184 | * @throws NotFoundException
185 | */
186 | public function updateGroup(Request $request, int $id)
187 | {
188 | $name = $request->put('name');
189 | $info = $request->put('info');
190 |
191 | $res = AdminService::updateGroup($id, $name, $info);
192 |
193 | Hook::listen('logger', "更新了id为{$id}的分组");
194 | return writeJson(200, $res, '更新分组信息成功', 7);
195 | }
196 |
197 | /**
198 | * @adminRequired
199 | * @permission('更新一个权限组','管理员','hidden')
200 | * @param int $id
201 | * @param('id','分组id','require|integer')
202 | * @return Json
203 | * @throws ForbiddenException
204 | * @throws NotFoundException
205 | * @throws OperationException
206 | */
207 | public function deleteGroup(int $id)
208 | {
209 | AdminService::deleteGroup($id);
210 |
211 | Hook::listen('logger', "删除了id为{$id}的分组");
212 | return writeJson(200, null, '删除分组成功', 8);
213 | }
214 |
215 | /**
216 | * @adminRequired
217 | * @permission('分配多个权限','管理员','hidden')
218 | * @param Request $request
219 | * @param('group_id','分组id','require|integer')
220 | * @param('permission_ids','权限id','require|array|min:1')
221 | * @return Json
222 | * @throws DbException
223 | * @throws NotFoundException
224 | * @throws OperationException
225 | */
226 | public function dispatchPermissions(Request $request)
227 | {
228 | $groupId = $request->post('group_id');
229 | $permissionIds = $request->post('permission_ids');
230 |
231 | AdminService::dispatchPermissions($groupId, $permissionIds);
232 |
233 | Hook::listen('logger', "修改了分组ID为{$groupId}的权限");
234 | return writeJson(200, null, '分配权限成功', 9);
235 | }
236 |
237 | /**
238 | * @adminRequired
239 | * @permission('删除多个权限','管理员','hidden')
240 | * @param Request $request
241 | * @param('group_id','分组id','require|integer')
242 | * @param('permission_ids','权限id','require|array|min:1')
243 | * @return Json
244 | * @throws DbException
245 | * @throws NotFoundException
246 | */
247 | public function removePermissions(Request $request)
248 | {
249 | $groupId = $request->post('group_id');
250 | $permissionIds = $request->post('permission_ids');
251 |
252 | $deleted = AdminService::removePermissions($groupId, $permissionIds);
253 |
254 | Hook::listen('logger', "修改了分组ID为{$groupId}的权限");
255 | return writeJson(200, $deleted, '删除权限成功', 10);
256 | }
257 | }
--------------------------------------------------------------------------------
/application/api/controller/cms/File.php:
--------------------------------------------------------------------------------
1 | '字段中含有非法字符',
32 | ]);
33 | }
34 | $file = (new LocalUploader($request))->upload();
35 | return $file;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/application/api/controller/cms/Log.php:
--------------------------------------------------------------------------------
1 | get('start');
32 | $end = $request->get('end');
33 | $name = $request->get('name');
34 | $page = $request->get('page/d', 0);
35 | $count = $request->get('count/d', 10);
36 |
37 | return LogService::getLogs($page, $count, $start, $end, $name);
38 | }
39 |
40 | /**
41 | * @groupRequired
42 | * @permission('搜索日志','日志')
43 | * @param Request $request
44 | * @param('page','分页数','integer')
45 | * @param('count','分页值','integer')
46 | * @param('start','开始日期','date')
47 | * @param('end','结束日期','date')
48 | * @return array
49 | * @throws ParameterException
50 | */
51 | public function getUserLogs(Request $request)
52 | {
53 | $start = $request->get('start');
54 | $end = $request->get('end');
55 | $name = $request->get('name');
56 | $keyword = $request->get('keyword');
57 | $page = $request->get('page/d', 0);
58 | $count = $request->get('count/d', 10);
59 |
60 | return LogService::searchLogs($page, $count, $start, $end, $name, $keyword);
61 | }
62 |
63 | /**
64 | * @groupRequired
65 | * @permission('查询日志记录的用户','日志')
66 | * @param Request $request
67 | * @param('page','分页数','integer')
68 | * @param('count','分页值','integer')
69 | * @return array
70 | * @throws ParameterException
71 | */
72 | public function getUsers(Request $request)
73 | {
74 | $page = $request->get('page/d', 0);
75 | $count = $request->get('count/d', 10);
76 |
77 | return LogService::getUserNames($page, $count);
78 | }
79 | }
--------------------------------------------------------------------------------
/application/api/controller/cms/User.php:
--------------------------------------------------------------------------------
1 | loginTokenService = LoginToken::getInstance();
36 | }
37 |
38 |
39 | /**
40 | * @adminRequired
41 | * @permission('注册','管理员','hidden')
42 | * @param Request $request
43 | * @validate('RegisterForm')
44 | * @return Json
45 | * @throws NotFoundException
46 | * @throws OperationException
47 | * @throws RepeatException
48 | * @throws ForbiddenException
49 | * @throws DataNotFoundException
50 | * @throws ModelNotFoundException
51 | * @throws DbException
52 | */
53 | public function register(Request $request)
54 | {
55 | $params = $request->post();
56 | $user = UserService::createUser($params);
57 |
58 | Hook::listen('logger', "新建了用户:{$user['username']}");
59 | return writeJson(201, $user['id'], '注册用户成功');
60 | }
61 |
62 | /**
63 | * @param Request $request
64 | * @validate('LoginForm')
65 | * @return array
66 | * @throws DataNotFoundException
67 | * @throws DbException
68 | * @throws ModelNotFoundException
69 | * @throws NotFoundException
70 | * @throws AuthFailedException
71 | */
72 | public function userLogin(Request $request)
73 | {
74 | $username = $request->post('username');
75 | $password = $request->post('password');
76 | $user = UserService::verify($username, $password);
77 |
78 | $tokenExtend = UserService::generateTokenExtend($user);
79 |
80 | $token = $this->loginTokenService->getToken($tokenExtend);
81 |
82 | Hook::listen('logger', array('uid' => $user->id, 'username' => $user->identifier, 'msg' => '登陆成功获取了令牌'));
83 | return [
84 | 'access_token' => $token['accessToken'],
85 | 'refresh_token' => $token['refreshToken']
86 | ];
87 | }
88 |
89 | /**
90 | * @return array
91 | * @throws TokenException
92 | */
93 | public function refreshToken()
94 | {
95 | $token = $this->loginTokenService->getTokenFromHeaders();
96 | $token = $this->loginTokenService->refresh($token);
97 | return [
98 | 'access_token' => $token['accessToken']
99 | ];
100 | }
101 |
102 | /**
103 | * @loginRequired
104 | */
105 | public function getAllowedApis()
106 | {
107 | $uid = $this->loginTokenService->getCurrentUid();
108 | return UserService::getPermissions($uid);
109 | }
110 |
111 | /**
112 | * @loginRequired
113 | * @return mixed
114 | */
115 | public function getInformation()
116 | {
117 | $uid = $this->loginTokenService->getCurrentUid();
118 | return UserService::getInformation($uid);
119 | }
120 |
121 | /**
122 | * @loginRequired
123 | * @param Request $request
124 | * @validate('UpdateUserForm')
125 | * @return Json
126 | * @throws RepeatException
127 | */
128 | public function update(Request $request)
129 | {
130 | $params = $request->put();
131 | $row = UserService::updateUser($params);
132 | return writeJson(200, $row, '用户信息更新成功');
133 | }
134 |
135 | /**
136 | * @loginRequired
137 | * @validate('ChangePasswordForm')
138 | * @param Request $request
139 | * @return Json
140 | * @throws AuthFailedException
141 | * @throws NotFoundException
142 | */
143 | public function changePassword(Request $request)
144 | {
145 | $oldPassword = $request->put('old_password');
146 | $newPassword = $request->put('new_password');
147 |
148 | $row = UserService::changePassword($oldPassword, $newPassword);
149 |
150 | Hook::listen('logger', '修改了自己的密码');
151 | return writeJson(200, $row, '密码修改成功');
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/application/api/controller/v1/Book.php:
--------------------------------------------------------------------------------
1 | post();
56 | BookModel::create($params);
57 | return writeJson(201, '', '新建图书成功');
58 | }
59 |
60 | public function update(Request $request)
61 | {
62 | $params = $request->put();
63 | $bookModel = new BookModel();
64 | $bookModel->save($params, ['id' => $params['id']]);
65 | return writeJson(201, '', '更新图书成功');
66 | }
67 |
68 | /**
69 | * @groupRequired
70 | * @permission('删除图书','图书')
71 | * @param $bid
72 | * @return Json
73 | */
74 | public function delete($bid)
75 | {
76 | BookModel::destroy($bid);
77 | Hook::listen('logger', '删除了id为' . $bid . '的图书');
78 | return writeJson(201, '', '删除图书成功');
79 | }
80 | }
--------------------------------------------------------------------------------
/application/api/model/BaseModel.php:
--------------------------------------------------------------------------------
1 | belongsToMany('LinUser', 'Lin_user_group', 'user_id', 'group_id');
26 | }
27 |
28 | public function permissions()
29 | {
30 | return $this->belongsToMany('LinPermission', 'lin_group_permission', 'permission_id', 'group_id');
31 | }
32 | }
--------------------------------------------------------------------------------
/application/api/model/admin/LinGroupPermission.php:
--------------------------------------------------------------------------------
1 | count();
27 | $logList = $logList->limit($start, $count)
28 | ->order('create_time desc')
29 | ->select();
30 | return [
31 | 'logList' => $logList,
32 | 'total' => $total
33 | ];
34 | }
35 |
36 | public static function searchLogs(int $start, int $count, $params = [])
37 | {
38 | $logList = self::withSearch(['name', 'start', 'end', 'keyword'], $params);
39 |
40 | $total = $logList->count();
41 | $logList = $logList->limit($start, $count)
42 | ->order('create_time desc')
43 | ->select();
44 | return [
45 | 'logList' => $logList,
46 | 'total' => $total
47 | ];
48 | }
49 |
50 |
51 | public static function getUserNames(int $start, int $count)
52 | {
53 | $users = self::field('username');
54 |
55 | $total = $users->count();
56 | $users = $users->limit($start, $count)
57 | ->group('username')
58 | ->select();
59 |
60 | return [
61 | 'userList' => $users,
62 | 'total' => $total
63 | ];
64 | }
65 |
66 | public function searchNameAttr($query, $value)
67 | {
68 | if ($value) {
69 | $query->where('username', $value);
70 | }
71 | }
72 |
73 | public
74 | function searchStartAttr($query, $value)
75 | {
76 | if ($value) {
77 | $query->where('create_time', '>= time', $value);
78 | }
79 | }
80 |
81 | public
82 | function searchEndAttr($query, $value)
83 | {
84 | if ($value) {
85 | $query->where('create_time', '<= time', $value);
86 | }
87 | }
88 |
89 | public
90 | function searchKeywordAttr($query, $value)
91 | {
92 | if ($value) {
93 | $query->whereLike('message', "%{$value}%");
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/application/api/model/admin/LinPermission.php:
--------------------------------------------------------------------------------
1 | where('username', '<>', 'root');
27 | $total = $userList->count();
28 |
29 | $userList = $userList
30 | ->limit($start, $count)
31 | ->with('groups')
32 | ->select();
33 |
34 | return [
35 | 'userList' => $userList,
36 | 'total' => $total
37 | ];
38 | }
39 |
40 | public function groups()
41 | {
42 | return $this->belongsToMany('LinGroup', 'lin_user_group', 'group_id', 'user_id');
43 | }
44 |
45 | public function identity()
46 | {
47 | return $this->hasMany('LinUserIdentity', 'user_id');
48 | }
49 |
50 | public function searchGroupIdAttr($query, $value)
51 | {
52 | if ($value) {
53 | $query->join('lin_group g', 'g.id=' . $value)->where('g.id', '<>', 1);
54 | }
55 | }
56 |
57 | public function getAvatarAttr($value)
58 | {
59 | if ($value) {
60 | $host = Config::get('file.host') ?? "http://127.0.0.1:5000/";
61 | $dir = Config::get('file.store_dir');
62 | return $host . $dir . '/' . $value;
63 | }
64 | return $value;
65 | }
66 | }
--------------------------------------------------------------------------------
/application/api/model/admin/LinUserGroup.php:
--------------------------------------------------------------------------------
1 | where('identifier', $currentUser->getAttr('username'))
28 | ->find();
29 |
30 | if (!$user) {
31 | throw new NotFoundException();
32 | }
33 |
34 | $user->credential = md5($newPassword);
35 | $user->save();
36 | }
37 |
38 | public function checkPassword(string $password): bool
39 | {
40 | return $this->getAttr('credential') === md5($password);
41 | }
42 | }
--------------------------------------------------------------------------------
/application/api/service/admin/Admin.php:
--------------------------------------------------------------------------------
1 | run();
45 | foreach ($permissionList as $permission) {
46 | $model = LinPermissionModel::where('name', $permission['name'])
47 | ->where('module', $permission['module'])
48 | ->find();
49 | if (!$model) {
50 | self::createPermission($permission['name'], $permission['module']);
51 | }
52 | }
53 |
54 | $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
55 | ->select()->toArray();
56 | $result = [];
57 | foreach ($permissions as $permission) {
58 | $result[$permission['module']][] = $permission;
59 | }
60 | return $result;
61 | }
62 |
63 | /**
64 | * @param int $page
65 | * @param int $count
66 | * @param int $groupId
67 | * @return array
68 | * @throws ParameterException
69 | */
70 | public static function getUsers(int $page, int $count, int $groupId = null): array
71 | {
72 | list($start, $count) = paginate($count, $page);
73 | $params = $groupId ? ['group_id' => $groupId] : [];
74 | $users = LinUserModel::getUsers($start, $count, $params);
75 |
76 | return [
77 | 'items' => $users['userList'],
78 | 'count' => $count,
79 | 'page' => $page,
80 | 'total' => $users['total']
81 | ];
82 | }
83 |
84 | /**
85 | * @param int $uid
86 | * @param string $newPassword
87 | * @throws NotFoundException
88 | */
89 | public static function changeUserPassword(int $uid, string $newPassword): void
90 | {
91 | $user = LinUserModel::get($uid);
92 | if (!$user) {
93 | throw new NotFoundException();
94 | }
95 |
96 | LinUserIdentityModel::resetPassword($user, $newPassword);
97 |
98 | }
99 |
100 | /**
101 | * @param int $uid
102 | * @throws NotFoundException
103 | * @throws OperationException
104 | */
105 | public static function deleteUser(int $uid): void
106 | {
107 | $user = LinUserModel::get($uid, 'identity');
108 | if (!$user) {
109 | throw new NotFoundException();
110 | }
111 |
112 | Db::startTrans();
113 | try {
114 | $user->groups()->detach();
115 | $user->together('identity')->delete();
116 | Db::commit();
117 | } catch (Exception $ex) {
118 | DB::rollback();
119 | throw new OperationException(['msg' => "删除用户失败"]);
120 | }
121 | }
122 |
123 | /**
124 | * @param int $uid
125 | * @param array $groupIds
126 | * @throws ForbiddenException
127 | * @throws NotFoundException
128 | * @throws OperationException
129 | * @throws DataNotFoundException
130 | * @throws ModelNotFoundException
131 | * @throws DbException
132 | */
133 | public static function updateUserInfo(int $uid, array $groupIds): void
134 | {
135 | $user = LinUserModel::get($uid);
136 | if (!$user) {
137 | throw new NotFoundException();
138 | }
139 |
140 | $userGroupIds = LinUserGroupModel::where('user_id', $uid)->column('group_id');
141 | $isAdmin = LinGroupModel::where('level', GroupLevelEnum::ROOT)
142 | ->whereIn('id', $userGroupIds)
143 | ->find();
144 | if ($isAdmin) {
145 | throw new ForbiddenException(['code' => 10078, 'msg' => '不允许调整root分组信息']);
146 | }
147 |
148 | foreach ($userGroupIds as $groupId) {
149 | $group = LinGroupModel::get($groupId);
150 | if ($group['level'] === GroupLevelEnum::ROOT) {
151 | throw new ForbiddenException(['code' => 10073, 'msg' => '不允许添加用户到root分组']);
152 | }
153 |
154 | if (!$group) {
155 | throw new NotFoundException(['code' => 10077]);
156 | }
157 | }
158 |
159 | Db::startTrans();
160 | try {
161 | $user->groups()->detach();
162 | $user->groups()->attach($groupIds);
163 | Db::commit();
164 | } catch (Exception $ex) {
165 | DB::rollback();
166 | throw new OperationException(['msg' => "更新用户分组失败"]);
167 | }
168 | }
169 |
170 | /**
171 | * @return array|PDOStatement|string|\think\Collection|Collection
172 | * @throws DataNotFoundException
173 | * @throws DbException
174 | * @throws ModelNotFoundException
175 | * @throws NotFoundException
176 | */
177 | public static function getAllGroups()
178 | {
179 | $groups = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)->select();
180 | if ($groups->isEmpty()) {
181 | throw new NotFoundException();
182 | }
183 | return $groups;
184 | }
185 |
186 | /**
187 | * @param int $id
188 | * @return \think\db\Query
189 | * @throws DbException
190 | * @throws NotFoundException
191 | */
192 | public static function getGroup(int $id)
193 | {
194 | $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)
195 | ->get($id, 'permissions');
196 | if (!$group) {
197 | throw new NotFoundException();
198 | }
199 |
200 | return $group;
201 | }
202 |
203 | /**
204 | * @param string $name
205 | * @param string $info
206 | * @param array $permissionIds
207 | * @return int
208 | * @throws DataNotFoundException
209 | * @throws DbException
210 | * @throws ModelNotFoundException
211 | * @throws NotFoundException
212 | * @throws OperationException
213 | */
214 | public static function createGroup(string $name, string $info, array $permissionIds): int
215 | {
216 | $isExist = LinGroupModel::where('name', $name)->find();
217 | if ($isExist) {
218 | throw new OperationException(['msg' => '分组名已存在']);
219 | }
220 |
221 | foreach ($permissionIds as $permissionId) {
222 | $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
223 | ->get($permissionId);
224 | if (!$permission) {
225 | throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']);
226 | }
227 | }
228 |
229 | Db::startTrans();
230 | try {
231 | $group = LinGroupModel::create(['name' => $name, 'info' => $info], true);
232 | $group->permissions()->saveAll($permissionIds);
233 | Db::commit();
234 | return $group->getAttr('id');
235 | } catch (\Exception $ex) {
236 | Db::rollback();
237 | throw new OperationException(['msg' => "新增分组失败:{$ex->getMessage()}"]);
238 | }
239 |
240 | }
241 |
242 | /**
243 | * @param int $id
244 | * @param string $name
245 | * @param string $info
246 | * @return int
247 | * @throws DataNotFoundException
248 | * @throws DbException
249 | * @throws ModelNotFoundException
250 | * @throws NotFoundException
251 | */
252 | public static function updateGroup(int $id, string $name, string $info): int
253 | {
254 | $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)
255 | ->find($id);
256 |
257 | if (!$group) {
258 | throw new NotFoundException();
259 | }
260 |
261 | return $group->save(['name' => $name, 'info' => $info]);
262 | }
263 |
264 | /**
265 | * @param int $id
266 | * @throws ForbiddenException
267 | * @throws NotFoundException
268 | * @throws OperationException
269 | */
270 | public static function deleteGroup(int $id): void
271 | {
272 | $group = LinGroupModel::find($id);
273 |
274 | if (!$group) {
275 | throw new NotFoundException();
276 | }
277 |
278 | if ($group->getAttr('level') === GroupLevelEnum::ROOT) {
279 | throw new ForbiddenException(['msg' => '不允许删除root分组']);
280 | }
281 |
282 | if ($group->getAttr('level') === GroupLevelEnum::GUEST) {
283 | throw new ForbiddenException(['msg' => '不允许删除guest分组']);
284 | }
285 |
286 | Db::startTrans();
287 | try {
288 | $group->permissions()->detach();
289 | $group->users()->detach();
290 | Db::commit();
291 | } catch (Exception $ex) {
292 | Db::rollback();
293 | throw new OperationException(['msg' => "删除分组失败:{$ex->getMessage()}"]);
294 | }
295 | }
296 |
297 | /**
298 | * @param int $id
299 | * @param array $permissionIds
300 | * @throws DbException
301 | * @throws NotFoundException
302 | * @throws OperationException
303 | */
304 | public static function dispatchPermissions(int $id, array $permissionIds)
305 | {
306 | $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)
307 | ->get($id);
308 | if (!$group) {
309 | throw new NotFoundException();
310 | }
311 |
312 | foreach ($permissionIds as $permissionId) {
313 | $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
314 | ->get($permissionId);
315 | if (!$permission) {
316 | throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']);
317 | }
318 | }
319 |
320 | try {
321 | $group->permissions()->attach($permissionIds);
322 | } catch (Exception $ex) {
323 | throw new OperationException(['msg' => '权限分配失败']);
324 | }
325 | }
326 |
327 | /**
328 | * @param int $id
329 | * @param array $permissionIds
330 | * @return int
331 | * @throws DbException
332 | * @throws NotFoundException
333 | */
334 | public static function removePermissions(int $id, array $permissionIds): int
335 | {
336 | $group = LinGroupModel::where('level', '<>', GroupLevelEnum::ROOT)
337 | ->get($id);
338 | if (!$group) {
339 | throw new NotFoundException();
340 | }
341 |
342 | foreach ($permissionIds as $permissionId) {
343 | $permission = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
344 | ->get($permissionId);
345 | if (!$permission) {
346 | throw new NotFoundException(['error_code' => 10231, 'msg' => '分配了不存在的权限']);
347 | }
348 | }
349 |
350 | return $group->permissions()->detach($permissionIds);
351 | }
352 |
353 | public static function createPermission(string $name, string $module): LinPermissionModel
354 | {
355 | return LinPermissionModel::create(['name' => $name, 'module' => $module, 'mount' => 1]);
356 | }
357 | }
--------------------------------------------------------------------------------
/application/api/service/admin/Log.php:
--------------------------------------------------------------------------------
1 | $start, 'end' => $end, 'name' => $name];
29 | $logsRes = LinLogModel::getLogs($offset, $count, $params);
30 |
31 | return [
32 | 'items' => $logsRes['logList'],
33 | 'count' => $count,
34 | 'page' => $page,
35 | 'total' => $logsRes['total']
36 | ];
37 | }
38 |
39 | /**
40 | * @param int $page
41 | * @param int $count
42 | * @param string|null $start
43 | * @param string|null $end
44 | * @param string|null $name
45 | * @param string|null $keyword
46 | * @return array
47 | * @throws ParameterException
48 | */
49 | public static function searchLogs(int $page, int $count, string $start = null,
50 | string $end = null, string $name = null, string $keyword = null)
51 | {
52 | list($offset, $count) = paginate($count, $page);
53 | $params = ['start' => $start, 'end' => $end, 'name' => $name, 'keyword' => $keyword];
54 |
55 | $logsRes = LinLogModel::searchLogs($offset, $count, $params);
56 |
57 | return [
58 | 'items' => $logsRes['logList'],
59 | 'count' => $count,
60 | 'page' => $page,
61 | 'total' => $logsRes['total']
62 | ];
63 | }
64 |
65 | /**
66 | * @param int $page
67 | * @param int $count
68 | * @return array
69 | * @throws ParameterException
70 | */
71 | public static function getUserNames(int $page, int $count)
72 | {
73 | list($start, $count) = paginate($count, $page);
74 | $usersRes = LinLogModel::getUserNames($start, $count);
75 | $items = array_map(function ($item) {
76 | return $item['username'];
77 | }, $usersRes['userList']->toArray());
78 |
79 | return [
80 | 'items' => $items,
81 | 'count' => $count,
82 | 'page' => $page,
83 | 'total' => $usersRes['total']
84 | ];
85 | }
86 | }
--------------------------------------------------------------------------------
/application/api/service/admin/User.php:
--------------------------------------------------------------------------------
1 | find();
50 | if ($user) {
51 | throw new RepeatException(['msg' => '用户名已存在']);
52 | }
53 |
54 | if (isset($params['email'])) {
55 | $user = LinUserModel::where('email', $params['email'])->find();
56 | if ($user) {
57 | throw new RepeatException(['msg' => '邮箱地址已存在']);
58 | }
59 | }
60 |
61 | if (isset($params['group_ids'])) {
62 | $groups = LinGroupModel::select($params['group_ids']);
63 | foreach ($groups as $group) {
64 | if ($group['level'] === GroupLevelEnum::ROOT) {
65 | throw new ForbiddenException(['msg' => '不允许分配用户到root分组']);
66 | }
67 | }
68 |
69 | if ($groups->isEmpty()) {
70 | throw new NotFoundException();
71 | }
72 | }
73 |
74 | return self::registerUser($params);
75 | }
76 |
77 | /**
78 | * @param string $username
79 | * @param string $password
80 | * @return Model
81 | * @throws AuthFailedException
82 | * @throws DataNotFoundException
83 | * @throws DbException
84 | * @throws ModelNotFoundException
85 | * @throws NotFoundException
86 | */
87 | public static function verify(string $username, string $password): Model
88 | {
89 | $user = new LinUserIdentityModel();
90 |
91 | $user = $user->where('identifier', $username)
92 | ->where('identity_type', IdentityTypeEnum::PASSWORD)
93 | ->find();
94 |
95 | if (!$user) {
96 | throw new NotFoundException(['msg' => '用户不存在']);
97 | }
98 |
99 | if (!$user->checkPassword($password)) {
100 | throw new AuthFailedException();
101 | }
102 | return $user;
103 | }
104 |
105 | public static function generateTokenExtend(Model $linUserIdentityModel)
106 | {
107 | $user = LinUserModel::get($linUserIdentityModel['user_id']);
108 | $userPermissions = self::getPermissions($user->getAttr('id'));
109 | return [
110 | 'id' => $user->getAttr('id'),
111 | 'identifier' => $linUserIdentityModel->getAttr('identifier'),
112 | 'email' => $user->getAttr('email'),
113 | 'admin' => $userPermissions['admin'],
114 | 'permissions' => $userPermissions['permissions'],
115 | ];
116 | }
117 |
118 | public static function getPermissions(int $uid): array
119 | {
120 | $user = LinUserModel::get($uid);
121 |
122 | $groupIds = LinUserGroupModel::where('user_id', $uid)
123 | ->column('group_id');
124 |
125 | $root = LinGroupModel::where('level', GroupLevelEnum::ROOT)
126 | ->whereIn('id', $groupIds)->find();
127 |
128 | $user = $user->hidden(['username'])->toArray();
129 | $user['admin'] = $root ? true : false;
130 |
131 | if ($root) {
132 | $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
133 | ->select()
134 | ->toArray();
135 | $user['permissions'] = formatPermissions($permissions);
136 | } else {
137 | $permissionIds = LinGroupPermissionModel::whereIn('group_id', $groupIds)
138 | ->column('permission_id');
139 | $permissions = LinPermissionModel::where('mount', MountTypeEnum::MOUNT)
140 | ->select($permissionIds)->toArray();
141 |
142 | $user['permissions'] = formatPermissions($permissions);
143 |
144 | }
145 |
146 | return $user;
147 | }
148 |
149 | public static function getInformation(int $uid)
150 | {
151 | return LinUser::get($uid, 'groups');
152 | }
153 |
154 | public static function updateUser(array $params): int
155 | {
156 | $user = LoginToken::getInstance()->getTokenExtend();
157 | if (isset($params['username']) && $params['username'] !== $user['username']) {
158 | $isExit = LinUserModel::where('username', $params['username'])
159 | ->find();
160 | if ($isExit) {
161 | throw new RepeatException(['msg' => "用户名已被占用"]);
162 | }
163 | }
164 |
165 | if (isset($params['email']) && $params['email'] !== $user['email']) {
166 | $isExit = LinUserModel::where('email', $params['email'])
167 | ->find();
168 | if ($isExit) {
169 | throw new RepeatException(['msg' => "邮箱已被占用"]);
170 | }
171 | }
172 |
173 | $user = LinUserModel::get($user['id']);
174 | return $user->allowField(true)->save($params);
175 | }
176 |
177 | public static function changePassword(string $oldPassword, string $newPassword): int
178 | {
179 | $currentUser = LoginToken::getInstance()->getTokenExtend();
180 | $user = new LinUserIdentityModel();
181 |
182 | $user = $user::where('identity_type', IdentityTypeEnum::PASSWORD)
183 | ->where('identifier', $currentUser['identifier'])
184 | ->find();
185 |
186 | if (!$user) {
187 | throw new NotFoundException();
188 | }
189 |
190 | if (!$user->checkPassword($oldPassword)) {
191 | throw new AuthFailedException();
192 | }
193 |
194 | $user->credential = md5($newPassword);
195 | return $user->save();
196 | }
197 |
198 | /**
199 | * @param array $params
200 | * @return LinUserModel
201 | * @throws OperationException
202 | */
203 | private static function registerUser(array $params): LinUserModel
204 | {
205 | Db::startTrans();
206 | try {
207 | $user = LinUserModel::create($params, true);
208 | $user->identity()->save([
209 | 'identity_type' => IdentityTypeEnum::PASSWORD,
210 | 'identifier' => $user['username'],
211 | 'credential' => md5($params['password'])
212 | ]);
213 |
214 | // 判断是否同时分配了分组
215 | if (isset($params['group_ids']) && count($params['group_ids']) > 0) {
216 | $user->groups()->attach($params['group_ids']);
217 | } else {
218 | // 没有分配分组,添加到游客分组
219 | $group = LinGroupModel::where('level', GroupLevelEnum::GUEST)->find();
220 | $user->groups()->attach([$group['id']]);
221 | }
222 | Db::commit();
223 | return $user;
224 | } catch (Exception $ex) {
225 | Db::rollback();
226 | throw new OperationException(['msg' => "注册用户失败:{$ex->getMessage()}"]);
227 | }
228 |
229 | }
230 |
231 | // private static function formatPermissions(array $permissions)
232 | // {
233 | // $groupPermission = [];
234 | // foreach ($permissions as $permission) {
235 | // $item = [
236 | // 'name' => $permission['name'],
237 | // 'module' => $permission['module']
238 | // ];
239 | // $groupPermission[$permission['module']][] = $item;
240 | // }
241 | //
242 | // $result[] = array_map(function ($item) {
243 | // return $item;
244 | // }, $groupPermission);
245 | // return $result;
246 | // }
247 | }
--------------------------------------------------------------------------------
/application/api/service/token/LoginToken.php:
--------------------------------------------------------------------------------
1 | tokenConfig = (new TokenConfig())
42 | ->dualToken($config['enable_dual_token'])
43 | ->setAlgorithms($config['algorithms'])
44 | ->setIat(time())
45 | ->setIss($config['issuer'])
46 | ->setAccessSecretKey($config['access_secret_key'])
47 | ->setAccessExp($config['access_expire_time'])
48 | ->setRefreshSecretKey($config['refresh_secret_key'])
49 | ->setRefreshExp($config['refresh_expire_time']);
50 | }
51 |
52 | public static function getInstance(): LoginToken
53 | {
54 | if (!self::$instance instanceof self) {
55 | self::$instance = new self();
56 | }
57 | return self::$instance;
58 | }
59 |
60 | /**
61 | * 获取令牌
62 | * @param array $extend 要插入到令牌扩展字段中的信息
63 | * @return array
64 | * @throws \Exception
65 | */
66 | public function getToken(array $extend): array
67 | {
68 | $this->tokenConfig->setExtend($extend);
69 | return TokenUtils::makeToken($this->tokenConfig);
70 | }
71 |
72 | /**
73 | * 令牌刷新
74 | * @param string $refreshToken 当开启了双令牌后颁发的refreshToken,用于刷新accessToken
75 | * @return array
76 | * @throws TokenException
77 | */
78 | public function refresh(string $refreshToken): array
79 | {
80 | try {
81 | return TokenUtils::refresh($refreshToken, $this->tokenConfig);
82 | } catch (SignatureInvalidException $signatureInvalidException) {
83 | throw new TokenException(['msg' => '令牌签名错误']);
84 | } catch (BeforeValidException $beforeValidException) {
85 | throw new TokenException();
86 | } catch (ExpiredException $expiredException) {
87 | throw new TokenException(['error_code' => 10042, 'msg' => '令牌已过期,请重新登录']);
88 | } catch (UnexpectedValueException $unexpectedValueException) {
89 | throw new TokenException();
90 | }
91 | }
92 |
93 | /**
94 | * @param string|null $token
95 | * @param string $tokenType
96 | * @return array
97 | * @throws TokenException
98 | */
99 | public function verify(string $token = null, string $tokenType = 'access')
100 | {
101 | $token = $token ?: $this->getTokenFromHeaders();
102 | try {
103 | return TokenUtils::verifyToken($token, $tokenType, $this->tokenConfig);
104 | } catch (SignatureInvalidException $signatureInvalidException) {
105 | throw new TokenException(['msg' => '令牌签名错误']);
106 | } catch (BeforeValidException $beforeValidException) {
107 | throw new TokenException();
108 | } catch (ExpiredException $expiredException) {
109 | throw new TokenException(['error_code' => 10041, 'msg' => '令牌已过期']);
110 | } catch (UnexpectedValueException $unexpectedValueException) {
111 | throw new TokenException();
112 | }
113 | }
114 |
115 | /**
116 | * 获取令牌扩展字段内容
117 | * @param string|null $token
118 | * @param string $tokenType
119 | * @return array
120 | * @throws TokenException
121 | */
122 | public function getTokenExtend(string $token = null, string $tokenType = 'access'): array
123 | {
124 | return (array)$this->verify($token, $tokenType)['extend'];
125 | }
126 |
127 | /**
128 | * 获取指定令牌扩展内容字段的值
129 | * @param string $val
130 | * @return mixed
131 | * @throws TokenException
132 | */
133 | public function getExtendVal(string $val)
134 | {
135 | return $this->getTokenExtend()[$val];
136 | }
137 |
138 | public function getCurrentUid()
139 | {
140 | return $this->getExtendVal('id');
141 | }
142 |
143 | public function getCurrentUserName()
144 | {
145 | return $this->getExtendVal('identifier');
146 | }
147 |
148 | public function getTokenFromHeaders(): string
149 | {
150 | $authorization = Request::header('authorization');
151 |
152 | if (!$authorization) {
153 | throw new TokenException(['msg' => '请求未携带Authorization信息']);
154 | }
155 |
156 | list($type, $token) = explode(' ', $authorization);
157 |
158 | if ($type !== 'Bearer') throw new TokenException(['msg' => '接口认证方式需为Bearer']);
159 |
160 | if (!$token || $token === 'undefined') {
161 | throw new TokenException(['msg' => '尝试获取的Authorization信息不存在']);
162 | }
163 |
164 | return $token;
165 | }
166 | }
--------------------------------------------------------------------------------
/application/api/validate/user/ChangePasswordForm.php:
--------------------------------------------------------------------------------
1 | 'require',
13 | 'new_password|新密码' => 'require|confirm:confirm_password',
14 | 'confirm_password|确认密码' => 'require',
15 | ];
16 | }
--------------------------------------------------------------------------------
/application/api/validate/user/LoginForm.php:
--------------------------------------------------------------------------------
1 | 'require',
18 | 'password' => 'require',
19 | ];
20 |
21 | protected $message = [
22 | 'username' => '用户名不能为空',
23 | 'password' => '密码不能为空'
24 | ];
25 | }
--------------------------------------------------------------------------------
/application/api/validate/user/RegisterForm.php:
--------------------------------------------------------------------------------
1 | 'require|confirm:confirm_password',
18 | 'confirm_password' => 'require',
19 | 'username' => 'require|length:2,10',
20 | 'group_ids' => 'array',
21 | 'email' => 'email'
22 | ];
23 | }
--------------------------------------------------------------------------------
/application/api/validate/user/ResetPasswordValidator.php:
--------------------------------------------------------------------------------
1 | 'require|integer',
18 | 'new_password|新密码' => 'require|confirm:confirm_password',
19 | 'confirm_password|确认密码' => 'require',
20 | ];
21 | }
--------------------------------------------------------------------------------
/application/api/validate/user/UpdateUserForm.php:
--------------------------------------------------------------------------------
1 | 'length:2,10',
18 | 'email' => 'email',
19 | 'nickname' => 'length:2,10',
20 | ];
21 | }
--------------------------------------------------------------------------------
/application/command.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | return [];
13 |
--------------------------------------------------------------------------------
/application/common.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // 应用公共文件
13 | use LinCmsTp5\exception\ParameterException;
14 | use think\response\Json;
15 |
16 | /**
17 | * 统一响应包装函数
18 | * @param $code
19 | * @param $errorCode
20 | * @param $data
21 | * @param $msg
22 | * @return Json
23 | */
24 | function writeJson($code, $data, $msg = 'ok', $errorCode = 0)
25 | {
26 | $data = [
27 | 'code' => $errorCode,
28 | 'result' => $data,
29 | 'message' => $msg
30 | ];
31 | return json($data, $code);
32 | }
33 |
34 | /**
35 | * 分页参数处理函数
36 | * @param int $count
37 | * @param int $page
38 | * @return array
39 | * @throws ParameterException
40 | */
41 | function paginate(int $count = 10, int $page = 0)
42 | {
43 | // $count = intval(Request::get('count', $count));
44 | // $start = intval(Request::get('page', $page));
45 | // $page = $start;
46 | $count = $count >= 15 ? 15 : $count;
47 | $start = $page * $count;
48 |
49 | if ($start < 0 || $count < 0) throw new ParameterException();
50 |
51 | return [$start, $count];
52 | }
53 |
54 | /**
55 | * 权限数组格式化函数
56 | * @param array $permissions
57 | * @return array
58 | */
59 | function formatPermissions(array $permissions)
60 | {
61 | $groupPermission = [];
62 | foreach ($permissions as $permission) {
63 | $item = [
64 | 'permission' => $permission['name'],
65 | 'module' => $permission['module']
66 | ];
67 | $groupPermission[$permission['module']][] = $item;
68 | }
69 | $result = [];
70 | foreach ($groupPermission as $key => $item) {
71 | array_push($result, [$key => $item]);
72 | }
73 |
74 | return $result;
75 | }
--------------------------------------------------------------------------------
/application/http/middleware/Authentication.php:
--------------------------------------------------------------------------------
1 | check();
23 |
24 | if (!$auth) {
25 | throw new ForbiddenException();
26 | }
27 |
28 | return $next($request);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/application/index/controller/Index.php:
--------------------------------------------------------------------------------
1 | *{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor:
21 | pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family:
22 | "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal;
23 | margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }');
25 | }
26 | }
--------------------------------------------------------------------------------
/application/lib/authenticator/Authenticator.php:
--------------------------------------------------------------------------------
1 | controller();
29 | // 控制层下有二级目录,需要解析下。如controller/cms/Admin,获取到的是Cms.Admin
30 | $controllerPath = explode('.', $controller);
31 | // 获取当前请求的方法
32 | $action = $request->action();
33 | // 反射获取当前请求的控制器类
34 | $class = new ReflectionClass('app\\api\\controller\\' . strtolower($controllerPath[0]) . '\\' . $controllerPath[1]);
35 | $this->parsedClass = (new Reflex($class->newInstance()))->setMethod($action);
36 | }
37 |
38 | /**
39 | * 入口方法
40 | * @return bool
41 | * @throws DeployException
42 | * @throws ReflectionException
43 | */
44 | public function check(): bool
45 | {
46 | //判断是否开启加载文件函数注释
47 | if (ini_get('opcache.save_comments') === '0' || ini_get('opcache.save_comments') === '') {
48 | throw new DeployException();
49 | }
50 | // 获取方法权限控制等级
51 | $actionPermissionLevel = $this->actionAuthorityLevel();
52 | // 没有等级标识,直接通过
53 | if (!$actionPermissionLevel) {
54 | return true;
55 | }
56 |
57 | // 执行校验并返回校验结果
58 | return $this->execute($actionPermissionLevel);
59 |
60 | }
61 |
62 | /**
63 | * 执行各权限等级校验
64 | * @param string $actionPermissionLevel
65 | * @return bool
66 | * @throws ReflectionException
67 | */
68 | public function execute(string $actionPermissionLevel): bool
69 | {
70 | // 账户信息,包含所拥有的权限列表
71 | $userInfo = $this->getUserInfo();
72 | //账户属于超级管理员,直接通过
73 | if ($userInfo['admin'] === true) return true;
74 | $actionPermissionName = $this->actionPermission();
75 |
76 | return AuthenticatorExecutorFactory::getInstance($actionPermissionLevel)->handle($userInfo, $actionPermissionName);
77 |
78 | }
79 |
80 | /**
81 | * 获取接口权限等级注解
82 | * @return string
83 | * @throws Exception
84 | */
85 | protected function actionAuthorityLevel(): string
86 | {
87 | $permissionLevel = null;
88 |
89 | if ($this->parsedClass->isExist(PermissionLevelEnum::LOGIN_REQUIRED)) {
90 | $permissionLevel = PermissionLevelEnum::LOGIN_REQUIRED;
91 | return $permissionLevel;
92 | }
93 |
94 | if ($this->parsedClass->isExist(PermissionLevelEnum::GROUP_REQUIRED)) {
95 | $permissionLevel = PermissionLevelEnum::GROUP_REQUIRED;
96 | return $permissionLevel;
97 | }
98 | if ($this->parsedClass->isExist(PermissionLevelEnum::ADMIN_REQUIRED)) {
99 | $permissionLevel = PermissionLevelEnum::ADMIN_REQUIRED;
100 | return $permissionLevel;
101 | }
102 | return '';
103 | }
104 |
105 | protected function getUserInfo(): array
106 | {
107 | return LoginToken::getInstance()->getTokenExtend();
108 | }
109 |
110 | /**
111 | * 获取接口权限注解内容
112 | * @return string
113 | * @throws Exception
114 | */
115 | protected function actionPermission(): string
116 | {
117 | $actionAuthContent = $this->parsedClass->get('permission');
118 |
119 | $actionAuthContent = empty($actionAuthContent) ? '' : $actionAuthContent[0] . '/' . $actionAuthContent[1];
120 | return $actionAuthContent;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/application/lib/authenticator/AuthenticatorExecutorFactory.php:
--------------------------------------------------------------------------------
1 | namespaceList = (new Scan())->scanController();
25 | }
26 |
27 | /**
28 | * @throws ReflectionException
29 | */
30 | public function run()
31 | {
32 | return $this->getPermissionList();
33 | }
34 |
35 | /**
36 | * @return array
37 | * @throws ReflectionException
38 | */
39 | private function getPermissionList()
40 | {
41 | $permissionList = [];
42 | // 遍历需要解析@permission注解的控制器类
43 | foreach ($this->namespaceList as $value) {
44 | // 反射控制器类
45 | $class = new ReflectionClass($value);
46 | // 类下面的所有方法的数组
47 | $methods = $class->getMethods();
48 | // 类下面所有含有@permission注解的方法的注解内容数组
49 | $methodPermissionList = $this->getPermissionByMethods($class->newInstance(), $methods);
50 |
51 | if (!empty($methodPermissionList)) {
52 | // 插入类权限数组
53 | if (empty($permissionList)) {
54 | $permissionList = $methodPermissionList;
55 | } else {
56 | $permissionList = array_merge($permissionList, $methodPermissionList);
57 | }
58 | }
59 | }
60 | return $permissionList;
61 | }
62 |
63 | /**
64 | * @param $class
65 | * @param $methods
66 | * @param string $annotationField
67 | * @return array
68 | * @throws Exception
69 | */
70 | private function getPermissionByMethods($class, $methods, $annotationField = 'permission')
71 | {
72 | $data = [];
73 | $re = new Reflex($class);
74 | foreach ($methods as $value) {
75 | $re->setMethod($value->name);
76 | $permissionAnnotationArray = $re->get($annotationField);
77 |
78 | if (!empty($permissionAnnotationArray) && !in_array('hidden', $permissionAnnotationArray)) {
79 | $permission = $this->handleAnnotation($permissionAnnotationArray);
80 | array_push($data, $permission);
81 | }
82 | }
83 |
84 | return $data;
85 | }
86 |
87 | public function handleAnnotation(array $annotation)
88 | {
89 | return [
90 | 'name' => $annotation[0],
91 | 'module' => $annotation[1],
92 | 'mount' => MountTypeEnum::MOUNT
93 | ];
94 | }
95 | }
--------------------------------------------------------------------------------
/application/lib/authenticator/Scan.php:
--------------------------------------------------------------------------------
1 | controller_namespace = 'app\\api\\controller\\';
22 | // 拼接出当前应用模块下的控制器层目录在服务器上的绝对路径
23 | $this->controller_path = Env::get('module_path') . 'controller';
24 | // 初始化需权限扫描的命名空间列表
25 | $this->authScanNamespaceList = [];
26 | }
27 |
28 | /**
29 | * 入口方法,调用scanControllerLayerDir()扫描控制器层
30 | * @return array 控制器层下所有类的完整命名空间数组
31 | */
32 | public function scanController()
33 | {
34 | return $this->scanControllerLayerDir($this->controller_path);
35 | }
36 |
37 | /**
38 | * 递归扫描控制器目录,扫描到类文件的时候push命名空间到$this->authScanNamespaceList
39 | * @param string $path 扫描的目标目录
40 | * @param string $subModule 可空,目标目录的子目录
41 | * @return array 控制器层下所有类的完整命名空间数组
42 | */
43 | private function scanControllerLayerDir(string $path, string $subModule = '')
44 | {
45 | $files = scandir($path);
46 | foreach ($files as $file) {
47 | if ($file !== '.' && $file !== '..') {
48 | if (strpos($file, '.php')) {
49 | $classFileName = substr($file, 0, -4);
50 | $module = $subModule ? $subModule . '\\' : '';
51 | $completeNamespace = $this->controller_namespace . $module . $classFileName;
52 | array_push($this->authScanNamespaceList, $completeNamespace);
53 | } else {
54 | $ds = PHP_OS === 'WINNT' ? '\\' : DIRECTORY_SEPARATOR;
55 | $subDir = $path . $ds . $file;
56 | $this->scanControllerLayerDir($subDir, $file);
57 | }
58 | }
59 | }
60 | return $this->authScanNamespaceList;
61 | }
62 | }
--------------------------------------------------------------------------------
/application/lib/authenticator/executor/IExecutor.php:
--------------------------------------------------------------------------------
1 | verify();
21 | return true;
22 | }
23 | }
--------------------------------------------------------------------------------
/application/lib/enum/GroupLevelEnum.php:
--------------------------------------------------------------------------------
1 | files as $key => $file) {
31 | $md5 = $this->generateMd5($file);
32 | $exists = LinFile::get(['md5' => $md5]);
33 | if ($exists) {
34 | array_push($ret, [
35 | 'id' => $exists['id'],
36 | 'key' => $key,
37 | 'path' => $exists['path'],
38 | 'url' => $host . '/' . $this->storeDir . '/' . $exists['path']
39 | ]);
40 | } else {
41 | $size = $this->getSize($file);
42 | $info = $file->move(Env::get('root_path') . '/' . 'public' . '/' . $this->storeDir);
43 | if ($info) {
44 | $extension = '.' . $info->getExtension();
45 | $path = str_replace('\\', '/', $info->getSaveName());
46 | $name = $info->getFilename();
47 | } else {
48 | throw new FileException([
49 | 'msg' => "存储本地文件失败",
50 | 'error_code' => 60001
51 | ]);
52 | }
53 | $linFile = LinFile::create([
54 | 'name' => $name,
55 | 'path' => $path,
56 | 'size' => $size,
57 | 'extension' => $extension,
58 | 'md5' => $md5,
59 | 'type' => 1
60 | ]);
61 | array_push($ret, [
62 | 'id' => $linFile->id,
63 | 'key' => $key,
64 | 'path' => $path,
65 | 'url' => $host . '/' . $this->storeDir . '/' . $path
66 | ]);
67 |
68 | }
69 |
70 | }
71 | return $ret;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/application/provider.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // 应用容器绑定定义
13 | return [
14 | ];
15 |
--------------------------------------------------------------------------------
/application/tags.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // 应用行为扩展定义文件
13 | return [
14 | // 应用初始化
15 | 'app_init' => [
16 | ],
17 | // 应用开始
18 | 'app_begin' => [],
19 | // 模块初始化
20 | 'module_init' => [],
21 | // 操作开始执行
22 | 'action_begin' => [],
23 | // 视图内容过滤
24 | 'view_filter' => [],
25 | // 日志写入
26 | 'log_write' => [],
27 | // 应用结束
28 | 'app_end' => [],
29 | // api日志
30 | 'logger' => [
31 | 'app\\api\\behavior\\Logger',
32 | ]
33 | ];
34 |
--------------------------------------------------------------------------------
/build.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | return [
13 | // 生成应用公共文件
14 | '__file__' => ['common.php'],
15 |
16 | // 定义demo模块的自动生成 (按照实际定义的文件名生成)
17 | 'demo' => [
18 | '__file__' => ['common.php'],
19 | '__dir__' => ['behavior', 'controller', 'model', 'view'],
20 | 'controller' => ['Index', 'Test', 'UserType'],
21 | 'model' => ['User', 'UserType'],
22 | 'view' => ['index/index'],
23 | ],
24 |
25 | // 其他更多的模块定义
26 | ];
27 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "topthink/think",
3 | "description": "the new thinkphp framework",
4 | "type": "project",
5 | "keywords": [
6 | "framework",
7 | "thinkphp",
8 | "ORM"
9 | ],
10 | "homepage": "http://thinkphp.cn/",
11 | "license": "Apache-2.0",
12 | "authors": [
13 | {
14 | "name": "liu21st",
15 | "email": "liu21st@gmail.com"
16 | }
17 | ],
18 | "require": {
19 | "php": ">=7.1.0",
20 | "topthink/framework": "5.1.36",
21 | "topthink/think-migration": "2.*",
22 | "lin-cms-tp5/base-core": "dev-master",
23 | "lin-cms-tp/validate-core": "dev-master",
24 | "lin-cms-tp/utils-core": "dev-master",
25 | "qinchen/web-utils": ">0.0.1"
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "app\\": "application"
30 | }
31 | },
32 | "extra": {
33 | "think-path": "thinkphp"
34 | },
35 | "config": {
36 | "preferred-install": "dist"
37 | },
38 | "repositories": {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 应用设置
14 | // +----------------------------------------------------------------------
15 |
16 | return [
17 | // 应用名称
18 | 'app_name' => '',
19 | // 应用地址
20 | 'app_host' => '',
21 | // 应用调试模式
22 | 'app_debug' => true,
23 | // 应用Trace
24 | 'app_trace' => false,
25 | // 是否支持多模块
26 | 'app_multi_module' => true,
27 | // 入口自动绑定模块
28 | 'auto_bind_module' => false,
29 | // 注册的根命名空间
30 | 'root_namespace' => [],
31 | // 默认输出类型
32 | 'default_return_type' => 'json',
33 | // 默认AJAX 数据返回格式,可选json xml ...
34 | 'default_ajax_return' => 'json',
35 | // 默认JSONP格式返回的处理方法
36 | 'default_jsonp_handler' => 'jsonpReturn',
37 | // 默认JSONP处理方法
38 | 'var_jsonp_handler' => 'callback',
39 | // 默认时区
40 | 'default_timezone' => 'Asia/Shanghai',
41 | // 是否开启多语言
42 | 'lang_switch_on' => false,
43 | // 默认全局过滤方法 用逗号分隔多个
44 | 'default_filter' => '',
45 | // 默认语言
46 | 'default_lang' => 'zh-cn',
47 | // 应用类库后缀
48 | 'class_suffix' => false,
49 | // 控制器类后缀
50 | 'controller_suffix' => false,
51 |
52 | // +----------------------------------------------------------------------
53 | // | 模块设置
54 | // +----------------------------------------------------------------------
55 |
56 | // 默认模块名
57 | 'default_module' => 'index',
58 | // 禁止访问模块
59 | 'deny_module_list' => ['common'],
60 | // 默认控制器名
61 | 'default_controller' => 'Index',
62 | // 默认操作名
63 | 'default_action' => 'index',
64 | // 默认验证器
65 | 'default_validate' => '',
66 | // 默认的空模块名
67 | 'empty_module' => '',
68 | // 默认的空控制器名
69 | 'empty_controller' => 'Error',
70 | // 操作方法前缀
71 | 'use_action_prefix' => false,
72 | // 操作方法后缀
73 | 'action_suffix' => '',
74 | // 自动搜索控制器
75 | 'controller_auto_search' => false,
76 |
77 | // +----------------------------------------------------------------------
78 | // | URL设置
79 | // +----------------------------------------------------------------------
80 |
81 | // PATHINFO变量名 用于兼容模式
82 | 'var_pathinfo' => 's',
83 | // 兼容PATH_INFO获取
84 | 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
85 | // pathinfo分隔符
86 | 'pathinfo_depr' => '/',
87 | // HTTPS代理标识
88 | 'https_agent_name' => '',
89 | // IP代理获取标识
90 | 'http_agent_ip' => 'X-REAL-IP',
91 | // URL伪静态后缀
92 | 'url_html_suffix' => 'html',
93 | // URL普通方式参数 用于自动生成
94 | 'url_common_param' => false,
95 | // URL参数方式 0 按名称成对解析 1 按顺序解析
96 | 'url_param_type' => 0,
97 | // 是否开启路由延迟解析
98 | 'url_lazy_route' => false,
99 | // 是否强制使用路由
100 | 'url_route_must' => false,
101 | // 合并路由规则
102 | 'route_rule_merge' => false,
103 | // 路由是否完全匹配
104 | 'route_complete_match' => true,
105 | // 使用注解路由
106 | 'route_annotation' => false,
107 | // 域名根,如thinkphp.cn
108 | 'url_domain_root' => '',
109 | // 是否自动转换URL中的控制器和操作名
110 | 'url_convert' => true,
111 | // 默认的访问控制器层
112 | 'url_controller_layer' => 'controller',
113 | // 表单请求类型伪装变量
114 | 'var_method' => '_method',
115 | // 表单ajax伪装变量
116 | 'var_ajax' => '_ajax',
117 | // 表单pjax伪装变量
118 | 'var_pjax' => '_pjax',
119 | // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
120 | 'request_cache' => false,
121 | // 请求缓存有效期
122 | 'request_cache_expire' => null,
123 | // 全局请求缓存排除规则
124 | 'request_cache_except' => [],
125 | // 是否开启路由缓存
126 | 'route_check_cache' => false,
127 | // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5
128 | 'route_check_cache_key' => '',
129 | // 路由缓存类型及参数
130 | 'route_cache_option' => [],
131 |
132 | // 默认跳转页面对应的模板文件
133 | 'dispatch_success_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl',
134 | 'dispatch_error_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl',
135 |
136 | // 异常页面的模板文件
137 | 'exception_tmpl' => Env::get('think_path') . 'tpl/think_exception.tpl',
138 |
139 | // 错误显示信息,非调试模式有效
140 | 'error_message' => '页面错误!请稍后再试~',
141 | // 显示错误信息
142 | 'show_error_msg' => false,
143 | // 异常处理handle类 留空使用 \think\exception\Handle
144 | 'exception_handle' => 'LinCmsTp5\exception\ExceptionHandler',
145 |
146 | ];
147 |
--------------------------------------------------------------------------------
/config/cache.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 缓存设置
14 | // +----------------------------------------------------------------------
15 |
16 | return [
17 | // 驱动方式
18 | 'type' => 'File',
19 | // 缓存保存目录
20 | 'path' => '',
21 | // 缓存前缀
22 | 'prefix' => '',
23 | // 缓存有效期 0表示永久缓存
24 | 'expire' => 0,
25 | ];
26 |
--------------------------------------------------------------------------------
/config/console.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 控制台配置
14 | // +----------------------------------------------------------------------
15 | return [
16 | 'name' => 'Think Console',
17 | 'version' => '0.1',
18 | 'user' => null,
19 | 'auto_path' => env('app_path') . 'command' . DIRECTORY_SEPARATOR,
20 | ];
21 |
--------------------------------------------------------------------------------
/config/cookie.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | Cookie设置
14 | // +----------------------------------------------------------------------
15 | return [
16 | // cookie 名称前缀
17 | 'prefix' => '',
18 | // cookie 保存时间
19 | 'expire' => 0,
20 | // cookie 保存路径
21 | 'path' => '/',
22 | // cookie 有效域名
23 | 'domain' => '',
24 | // cookie 启用安全传输
25 | 'secure' => false,
26 | // httponly设置
27 | 'httponly' => '',
28 | // 是否使用 setcookie
29 | 'setcookie' => true,
30 | ];
31 |
--------------------------------------------------------------------------------
/config/database.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | return [
13 | // 数据库类型
14 | 'type' => 'mysql',
15 | // 服务器地址
16 | 'hostname' => '127.0.0.1',
17 | // 数据库名
18 | 'database' => 'lin_cms',
19 | // 用户名
20 | 'username' => 'root',
21 | // 密码
22 | 'password' => '',
23 | // 端口
24 | 'hostport' => '',
25 | // 连接dsn
26 | 'dsn' => '',
27 | // 数据库连接参数
28 | 'params' => [],
29 | // 数据库编码默认采用utf8
30 | 'charset' => 'utf8',
31 | // 数据库表前缀
32 | 'prefix' => '',
33 | // 数据库调试模式
34 | 'debug' => true,
35 | // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
36 | 'deploy' => 0,
37 | // 数据库读写是否分离 主从式有效
38 | 'rw_separate' => false,
39 | // 读写分离后 主服务器数量
40 | 'master_num' => 1,
41 | // 指定从服务器序号
42 | 'slave_no' => '',
43 | // 自动读取主库数据
44 | 'read_master' => false,
45 | // 是否严格检查字段是否存在
46 | 'fields_strict' => true,
47 | // 数据集返回类型
48 | 'resultset_type' => 'collection',
49 | // 自动写入时间戳字段
50 | 'auto_timestamp' => false,
51 | // 时间字段取出后的默认时间格式
52 | 'datetime_format' => 'Y-m-d H:i:s',
53 | // 是否需要进行SQL性能分析
54 | 'sql_explain' => false,
55 | // Builder类
56 | 'builder' => '',
57 | // Query类
58 | 'query' => '\\think\\db\\Query',
59 | // 是否需要断线重连
60 | 'break_reconnect' => false,
61 | // 断线标识字符串
62 | 'break_match_str' => []
63 | ];
64 |
--------------------------------------------------------------------------------
/config/file.php:
--------------------------------------------------------------------------------
1 | 'uploads', # 文件的存储路径
5 | "single_limit" => 1024 * 1024 * 2, # 单个文件的大小限制,默认2M
6 | "total_limit"=> 1024 * 1024 * 20, # 所有文件的大小限制,默认20M
7 | "nums" => 10, # 文件数量限制,默认10
8 | "include" => [], # 文件后缀名的排除项,默认排除[],即允许所有类型的文件上传
9 | "exclude" => [] # 文件后缀名的包括项
10 | ];
11 |
--------------------------------------------------------------------------------
/config/log.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 日志设置
14 | // +----------------------------------------------------------------------
15 | return [
16 | // 日志记录方式,内置 file socket 支持扩展
17 | 'type' => 'File',
18 | // 日志保存目录
19 | 'path' => '',
20 | // 日志记录级别
21 | 'level' => [],
22 | // 单文件日志写入
23 | 'single' => false,
24 | // 独立日志级别
25 | 'apart_level' => [],
26 | // 最大日志文件数量
27 | 'max_files' => 0,
28 | // 是否关闭日志写入
29 | 'close' => true,
30 |
31 | ];
32 |
--------------------------------------------------------------------------------
/config/middleware.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 中间件配置
14 | // +----------------------------------------------------------------------
15 | return [
16 | // 默认中间件命名空间
17 | 'default_namespace' => 'app\\http\\middleware\\',
18 | 'ReflexValidate' => LinCmsTp\Param::class // 开启注释验证器,需要的中间件配置,请勿胡乱关闭
19 | ];
20 |
--------------------------------------------------------------------------------
/config/secure.php:
--------------------------------------------------------------------------------
1 | 'SDJxjkxc9o',
10 | 'refresh_token_salt' => 'SKTxigxc9o',
11 | ];
--------------------------------------------------------------------------------
/config/session.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 会话设置
14 | // +----------------------------------------------------------------------
15 |
16 | return [
17 | 'id' => '',
18 | // SESSION_ID的提交变量,解决flash上传跨域
19 | 'var_session_id' => '',
20 | // SESSION 前缀
21 | 'prefix' => 'think',
22 | // 驱动方式 支持redis memcache memcached
23 | 'type' => '',
24 | // 是否自动开启 SESSION
25 | 'auto_start' => true,
26 | ];
27 |
--------------------------------------------------------------------------------
/config/template.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | 模板设置
14 | // +----------------------------------------------------------------------
15 |
16 | return [
17 | // 模板引擎类型 支持 php think 支持扩展
18 | 'type' => 'Think',
19 | // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
20 | 'auto_rule' => 1,
21 | // 模板路径
22 | 'view_path' => '',
23 | // 模板后缀
24 | 'view_suffix' => 'html',
25 | // 模板文件名分隔符
26 | 'view_depr' => DIRECTORY_SEPARATOR,
27 | // 模板引擎普通标签开始标记
28 | 'tpl_begin' => '{',
29 | // 模板引擎普通标签结束标记
30 | 'tpl_end' => '}',
31 | // 标签库标签开始标记
32 | 'taglib_begin' => '{',
33 | // 标签库标签结束标记
34 | 'taglib_end' => '}',
35 | ];
36 |
--------------------------------------------------------------------------------
/config/token.php:
--------------------------------------------------------------------------------
1 | true,
11 | # 令牌算法类型
12 | 'algorithms' => 'HS256',
13 | # 令牌签发者
14 | 'issuer' => 'lin-cms-tp5',
15 | # accessToken秘钥
16 | 'access_secret_key' => 'w.kx(c82jkA',
17 | # accessToken过期时间,单位秒
18 | 'access_expire_time' => 7200,
19 | # refreshToken秘钥
20 | 'refresh_secret_key' => 'xUh.@3s8A8',
21 | # refreshToken过期时间,建议设置较长时间
22 | # 在有效期内可用于刷新accessToken,单位秒
23 | 'refresh_expire_time' => 604800,
24 | ];
--------------------------------------------------------------------------------
/config/trace.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // +----------------------------------------------------------------------
13 | // | Trace设置 开启 app_trace 后 有效
14 | // +----------------------------------------------------------------------
15 | return [
16 | // 内置Html Console 支持扩展
17 | 'type' => 'Html',
18 | ];
19 |
--------------------------------------------------------------------------------
/error_msg.md:
--------------------------------------------------------------------------------
1 | # 错误码字典
2 |
3 | ## 参数类错误
4 | 99999 参数校验不通过
5 |
6 | ## 通用错误
7 | 10001 资源操作失败
8 | 10021 资源不存在
9 | 10071 资源已存在
10 |
11 | ## 令牌类错误
12 | 10000 令牌解析失败
13 | 10041 access令牌过期
14 | 10042 refresh令牌过期
15 | 10002 权限不足
16 | 10073 不允许添加用户到root分组
17 | 10078 不允许调整root分组信息
18 |
19 | ## 用户类错误
20 |
21 | 10030 密码错误
22 |
23 | ## 分组类错误
24 | ### 3000x
25 | 30001 分组创建失败
26 | 30002 分组权限编辑失败
27 | 30003 分组不存在
28 | 30004 分组已存在
29 |
30 | ## 服务端类错误
31 | ### 5000x
32 | 50000 服务环境配置错误
33 |
34 | ## 上传类错误
35 | ### 6000x
36 | 60000 不符合上传条件
37 | 60001 上传文件失败
38 |
--------------------------------------------------------------------------------
/extend/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Options +FollowSymlinks -Multiviews
3 | RewriteEngine On
4 |
5 | RewriteCond %{REQUEST_FILENAME} !-d
6 | RewriteCond %{REQUEST_FILENAME} !-f
7 | RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
8 |
9 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TaleLin/lin-cms-tp5/27f7429abc1f0e1f44ac9ca4a8a3450ea52ee18d/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | // [ 应用入口文件 ]
13 | namespace think;
14 |
15 | // 加载基础文件
16 | require __DIR__ . '/../thinkphp/base.php';
17 |
18 | // 支持事先使用静态方法设置Request对象和Config对象
19 |
20 | // 执行应用并响应
21 | Container::get('app')->run()->send();
22 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/public/router.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 | // $Id$
12 |
13 | if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
14 | return false;
15 | } else {
16 | require __DIR__ . "/index.php";
17 | }
18 |
--------------------------------------------------------------------------------
/public/static/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/route/route.php:
--------------------------------------------------------------------------------
1 |
10 | // +----------------------------------------------------------------------
11 |
12 | use think\facade\Route;
13 |
14 | Route::get('v1/test', 'api/v1.Test/index');
15 |
16 | Route::group('', function () {
17 | Route::group('cms', function () {
18 | // 账户相关接口分组
19 | Route::group('user', function () {
20 | // 登陆接口
21 | Route::post('login', 'api/cms.User/userLogin');
22 | // 刷新令牌
23 | Route::get('refresh', 'api/cms.User/refreshToken');
24 | // 查询自己拥有的权限
25 | Route::get('permissions', 'api/cms.User/getAllowedApis');
26 | // 注册一个用户
27 | Route::post('register', 'api/cms.User/register');
28 | // 查询自己信息
29 | Route::get('information', 'api/cms.User/getInformation');
30 | // 用户更新信息
31 | Route::put('', 'api/cms.User/update');
32 | // 修改自己密码
33 | Route::put('change_password', 'api/cms.User/changePassword');
34 | });
35 | // 管理类接口
36 | Route::group('admin', function () {
37 | // 查询所有可分配的权限
38 | Route::get('permission', 'api/cms.Admin/getAllPermissions');
39 | // 查询所有用户
40 | Route::get('users', 'api/cms.Admin/getAdminUsers');
41 | // 修改用户密码
42 | Route::put('user/:id/password', 'api/cms.Admin/changeUserPassword');
43 | // 删除用户
44 | Route::delete('user/:id', 'api/cms.Admin/deleteUser');
45 | // 更新用户信息
46 | Route::put('user/:id', 'api/cms.Admin/updateUser');
47 | // 查询所有权限组
48 | Route::get('group/all', 'api/cms.Admin/getGroupAll');
49 | // 新增权限组
50 | Route::post('group', 'api/cms.Admin/createGroup');
51 | // 查询指定分组及其权限
52 | Route::get('group/:id', 'api/cms.Admin/getGroup');
53 | // 更新一个权限组
54 | Route::put('group/:id', 'api/cms.Admin/updateGroup');
55 | // 删除一个分组
56 | Route::delete('group/:id', 'api/cms.Admin/deleteGroup');
57 | // 删除多个权限
58 | Route::post('permission/remove', 'api/cms.Admin/removePermissions');
59 | // 分配多个权限
60 | Route::post('permission/dispatch/batch', 'api/cms.Admin/dispatchPermissions');
61 |
62 | });
63 | // 日志类接口
64 | Route::group('log', function () {
65 | Route::get('', 'api/cms.Log/getLogs');
66 | Route::get('users', 'api/cms.Log/getUsers');
67 | Route::get('search', 'api/cms.Log/getUserLogs');
68 | });
69 | //上传文件类接口
70 | Route::post('file', 'api/cms.File/postFile');
71 | });
72 | Route::group('v1', function () {
73 | Route::group('book', function () {
74 | // 查询所有图书
75 | Route::get('', 'api/v1.Book/getBooks');
76 | // 新建图书
77 | Route::post('', 'api/v1.Book/create');
78 | // 查询指定bid的图书
79 | Route::get(':bid', 'api/v1.Book/getBook');
80 | // 搜索图书
81 |
82 | // 更新图书
83 | Route::put(':bid', 'api/v1.Book/update');
84 | // 删除图书
85 | Route::delete(':bid', 'api/v1.Book/delete');
86 | });
87 |
88 | });
89 | })->middleware(['Authentication', 'ReflexValidate'])->allowCrossDomain();
90 |
91 |
--------------------------------------------------------------------------------
/runtime/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/schema.sql:
--------------------------------------------------------------------------------
1 | SET NAMES utf8mb4;
2 | SET FOREIGN_KEY_CHECKS = 0;
3 |
4 | -- ----------------------------
5 | -- 文件表
6 | -- ----------------------------
7 | DROP TABLE IF EXISTS lin_file;
8 | CREATE TABLE lin_file
9 | (
10 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
11 | path varchar(500) NOT NULL,
12 | type varchar(10) NOT NULL DEFAULT 'LOCAL' COMMENT 'LOCAL 本地,REMOTE 远程',
13 | name varchar(100) NOT NULL,
14 | extension varchar(50) DEFAULT NULL,
15 | size int(11) DEFAULT NULL,
16 | md5 varchar(40) DEFAULT NULL COMMENT 'md5值,防止上传重复文件',
17 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
18 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
19 | delete_time datetime(3) DEFAULT NULL,
20 | PRIMARY KEY (id),
21 | UNIQUE KEY md5_del (md5, delete_time)
22 | ) ENGINE = InnoDB
23 | DEFAULT CHARSET = utf8mb4
24 | COLLATE = utf8mb4_general_ci;
25 |
26 | -- ----------------------------
27 | -- 日志表
28 | -- ----------------------------
29 | DROP TABLE IF EXISTS lin_log;
30 | CREATE TABLE lin_log
31 | (
32 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
33 | message varchar(450) DEFAULT NULL,
34 | user_id int(10) unsigned NOT NULL,
35 | username varchar(24) DEFAULT NULL,
36 | status_code int(11) DEFAULT NULL,
37 | method varchar(20) DEFAULT NULL,
38 | path varchar(50) DEFAULT NULL,
39 | permission varchar(100) DEFAULT NULL,
40 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
41 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
42 | delete_time datetime(3) DEFAULT NULL,
43 | PRIMARY KEY (id)
44 | ) ENGINE = InnoDB
45 | DEFAULT CHARSET = utf8mb4
46 | COLLATE = utf8mb4_general_ci;
47 |
48 | -- ----------------------------
49 | -- 权限表
50 | -- ----------------------------
51 | DROP TABLE IF EXISTS lin_permission;
52 | CREATE TABLE lin_permission
53 | (
54 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
55 | name varchar(60) NOT NULL COMMENT '权限名称,例如:访问首页',
56 | module varchar(50) NOT NULL COMMENT '权限所属模块,例如:人员管理',
57 | mount tinyint(1) NOT NULL DEFAULT 1 COMMENT '0:关闭 1:开启',
58 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
59 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
60 | delete_time datetime(3) DEFAULT NULL,
61 | PRIMARY KEY (id)
62 | ) ENGINE = InnoDB
63 | DEFAULT CHARSET = utf8mb4
64 | COLLATE = utf8mb4_general_ci;
65 |
66 | -- ----------------------------
67 | -- 分组表
68 | -- ----------------------------
69 | DROP TABLE IF EXISTS lin_group;
70 | CREATE TABLE lin_group
71 | (
72 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
73 | name varchar(60) NOT NULL COMMENT '分组名称,例如:搬砖者',
74 | info varchar(255) DEFAULT NULL COMMENT '分组信息:例如:搬砖的人',
75 | level tinyint(2) NOT NULL DEFAULT 3 COMMENT '分组级别 1:root 2:guest 3:user(root、guest分组只能存在一个)',
76 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
77 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
78 | delete_time datetime(3) DEFAULT NULL,
79 | PRIMARY KEY (id),
80 | UNIQUE KEY name_del (name, delete_time)
81 | ) ENGINE = InnoDB
82 | DEFAULT CHARSET = utf8mb4
83 | COLLATE = utf8mb4_general_ci;
84 |
85 | -- ----------------------------
86 | -- 分组-权限表
87 | -- ----------------------------
88 | DROP TABLE IF EXISTS lin_group_permission;
89 | CREATE TABLE lin_group_permission
90 | (
91 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
92 | group_id int(10) unsigned NOT NULL COMMENT '分组id',
93 | permission_id int(10) unsigned NOT NULL COMMENT '权限id',
94 | PRIMARY KEY (id),
95 | KEY group_id_permission_id (group_id, permission_id) USING BTREE COMMENT '联合索引'
96 | ) ENGINE = InnoDB
97 | DEFAULT CHARSET = utf8mb4
98 | COLLATE = utf8mb4_general_ci;
99 |
100 | -- ----------------------------
101 | -- 用户基本信息表
102 | -- ----------------------------
103 | DROP TABLE IF EXISTS lin_user;
104 | CREATE TABLE lin_user
105 | (
106 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
107 | username varchar(24) NOT NULL COMMENT '用户名,唯一',
108 | nickname varchar(24) DEFAULT NULL COMMENT '用户昵称',
109 | avatar varchar(500) DEFAULT NULL COMMENT '头像url',
110 | email varchar(100) DEFAULT NULL COMMENT '邮箱',
111 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
112 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
113 | delete_time datetime(3) DEFAULT NULL,
114 | PRIMARY KEY (id),
115 | UNIQUE KEY username_del (username, delete_time),
116 | UNIQUE KEY email_del (email, delete_time)
117 | ) ENGINE = InnoDB
118 | DEFAULT CHARSET = utf8mb4
119 | COLLATE = utf8mb4_general_ci;
120 |
121 | -- ----------------------------
122 | -- 用户授权信息表
123 | # id
124 | # user_id
125 | # identity_type 登录类型(手机号 邮箱 用户名)或第三方应用名称(微信 微博等)
126 | # identifier 标识(手机号 邮箱 用户名或第三方应用的唯一标识)
127 | # credential 密码凭证(站内的保存密码,站外的不保存或保存token)
128 | -- ----------------------------
129 | DROP TABLE IF EXISTS lin_user_identity;
130 | CREATE TABLE lin_user_identity
131 | (
132 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
133 | user_id int(10) unsigned NOT NULL COMMENT '用户id',
134 | identity_type varchar(100) NOT NULL,
135 | identifier varchar(100),
136 | credential varchar(100),
137 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
138 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
139 | delete_time datetime(3) DEFAULT NULL,
140 | PRIMARY KEY (id)
141 | ) ENGINE = InnoDB
142 | DEFAULT CHARSET = utf8mb4
143 | COLLATE = utf8mb4_general_ci;
144 |
145 | DROP TABLE IF EXISTS book;
146 | CREATE TABLE book
147 | (
148 | id int(11) NOT NULL AUTO_INCREMENT,
149 | title varchar(50) NOT NULL,
150 | author varchar(30) DEFAULT NULL,
151 | summary varchar(1000) DEFAULT NULL,
152 | image varchar(100) DEFAULT NULL,
153 | create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
154 | update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
155 | delete_time datetime(3) DEFAULT NULL,
156 | PRIMARY KEY (id)
157 | ) ENGINE = InnoDB
158 | DEFAULT CHARSET = utf8mb4
159 | COLLATE = utf8mb4_general_ci;
160 |
161 |
162 | -- ----------------------------
163 | -- 用户-分组表
164 | -- ----------------------------
165 | DROP TABLE IF EXISTS lin_user_group;
166 | CREATE TABLE lin_user_group
167 | (
168 | id int(10) unsigned NOT NULL AUTO_INCREMENT,
169 | user_id int(10) unsigned NOT NULL COMMENT '用户id',
170 | group_id int(10) unsigned NOT NULL COMMENT '分组id',
171 | PRIMARY KEY (id),
172 | KEY user_id_group_id (user_id, group_id) USING BTREE COMMENT '联合索引'
173 | ) ENGINE = InnoDB
174 | DEFAULT CHARSET = utf8mb4
175 | COLLATE = utf8mb4_general_ci;
176 |
177 | SET FOREIGN_KEY_CHECKS = 1;
178 |
179 | -- ----------------------------
180 | -- 插入超级管理员
181 | -- 插入root分组
182 | -- ----------------------------
183 | BEGIN;
184 | INSERT INTO lin_user(id, username, nickname)
185 | VALUES (1, 'root', 'root');
186 |
187 | INSERT INTO lin_user_identity (id, user_id, identity_type, identifier, credential)
188 |
189 | VALUES (1, 1, 'USERNAME_PASSWORD', 'root',
190 | 'e10adc3949ba59abbe56e057f20f883e');
191 |
192 | INSERT INTO lin_group(id, name, info, level)
193 | VALUES (1, 'root', '超级用户组', 1);
194 |
195 | INSERT INTO lin_group(id, name, info, level)
196 | VALUES (2, 'guest', '游客组', 2);
197 |
198 | INSERT INTO lin_user_group(id, user_id, group_id)
199 | VALUES (1, 1, 1);
200 |
201 | COMMIT;
202 |
--------------------------------------------------------------------------------
/think:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
11 | // +----------------------------------------------------------------------
12 |
13 | namespace think;
14 |
15 | // 加载基础文件
16 | require __DIR__ . '/thinkphp/base.php';
17 |
18 | // 应用初始化
19 | Container::get('app')->path(__DIR__ . '/application/')->initialize();
20 |
21 | // 控制台初始化
22 | Console::init();
--------------------------------------------------------------------------------