wanghongjun 1 year ago
parent
commit
695f866ec3
  1. 17
      .env
  2. 5
      .gitignore
  3. 42
      .travis.yml
  4. 21
      LICENSE
  5. 127
      README.md
  6. BIN
      apiadmin_72Dnz (1).tar.gz
  7. 1
      app/.htaccess
  8. 22
      app/AppService.php
  9. 94
      app/BaseController.php
  10. 58
      app/ExceptionHandle.php
  11. 8
      app/Request.php
  12. 26
      app/command/ApiAdmin.php
  13. 112
      app/command/AutoBuildFile.php
  14. 31
      app/command/FreshAdminRouter.php
  15. 120
      app/command/Install.php
  16. 2
      app/common.php
  17. 218
      app/controller/admin/App.php
  18. 140
      app/controller/admin/AppGroup.php
  19. 277
      app/controller/admin/Auth.php
  20. 105
      app/controller/admin/Base.php
  21. 285
      app/controller/admin/Fields.php
  22. 51
      app/controller/admin/Index.php
  23. 158
      app/controller/admin/InterfaceGroup.php
  24. 201
      app/controller/admin/InterfaceList.php
  25. 65
      app/controller/admin/Log.php
  26. 173
      app/controller/admin/Login.php
  27. 112
      app/controller/admin/Menu.php
  28. 18
      app/controller/admin/Miss.php
  29. 350
      app/controller/admin/ThirdLogin.php
  30. 277
      app/controller/admin/User.php
  31. 56
      app/controller/api/Base.php
  32. 87
      app/controller/api/BuildToken.php
  33. 26
      app/controller/api/Miss.php
  34. 188
      app/controller/wiki/Api.php
  35. 43
      app/controller/wiki/Base.php
  36. 17
      app/event.php
  37. 10
      app/middleware.php
  38. 45
      app/middleware/AdminAuth.php
  39. 45
      app/middleware/AdminLog.php
  40. 96
      app/middleware/AdminPermission.php
  41. 14
      app/middleware/AdminResponse.php
  42. 145
      app/middleware/ApiAuth.php
  43. 31
      app/middleware/ApiLog.php
  44. 33
      app/middleware/ApiPermission.php
  45. 13
      app/middleware/ApiResponse.php
  46. 128
      app/middleware/RequestFilter.php
  47. 47
      app/middleware/WikiAuth.php
  48. 7
      app/model/AdminApp.php
  49. 12
      app/model/AdminAppGroup.php
  50. 17
      app/model/AdminAuthGroup.php
  51. 12
      app/model/AdminAuthGroupAccess.php
  52. 12
      app/model/AdminAuthRule.php
  53. 12
      app/model/AdminFields.php
  54. 13
      app/model/AdminGroup.php
  55. 11
      app/model/AdminList.php
  56. 7
      app/model/AdminMenu.php
  57. 19
      app/model/AdminUser.php
  58. 12
      app/model/AdminUserAction.php
  59. 11
      app/model/AdminUserData.php
  60. 14
      app/model/Base.php
  61. 9
      app/provider.php
  62. 9
      app/service.php
  63. 138
      app/util/ApiLogTool.php
  64. 203
      app/util/AutoBuild.php
  65. 22
      app/util/DataType.php
  66. 50
      app/util/ReturnCode.php
  67. 107
      app/util/RouterTool.php
  68. 185
      app/util/StrRandom.php
  69. 254
      app/util/Strs.php
  70. 192
      app/util/Tools.php
  71. 46
      composer.json
  72. 38
      config/apiadmin.php
  73. 32
      config/app.php
  74. 39
      config/cache.php
  75. 13
      config/console.php
  76. 18
      config/cookie.php
  77. 62
      config/database.php
  78. 24
      config/filesystem.php
  79. 25
      config/lang.php
  80. 45
      config/log.php
  81. 8
      config/middleware.php
  82. 45
      config/route.php
  83. 19
      config/session.php
  84. 10
      config/trace.php
  85. 25
      config/view.php
  86. 85
      database/migrations/20190425073802_admin_app.php
  87. 61
      database/migrations/20190425094427_admin_app_group.php
  88. 56
      database/migrations/20190508070533_admin_auth_group.php
  89. 54
      database/migrations/20190508100337_admin_auth_group_access.php
  90. 64
      database/migrations/20190508101122_admin_auth_rule.php
  91. 88
      database/migrations/20190508152801_admin_fields.php
  92. 81
      database/migrations/20190508153800_admin_group.php
  93. 88
      database/migrations/20190513065521_admin_list.php
  94. 82
      database/migrations/20190513070628_admin_menu.php
  95. 84
      database/migrations/20190513081034_admin_user.php
  96. 71
      database/migrations/20190513082503_admin_user_action.php
  97. 67
      database/migrations/20190513085755_admin_user_data.php
  98. 718
      database/migrations/20190513155519_ini_admin_menu.php
  99. 47
      database/migrations/20190514034923_ini_admin_group.php
  100. 53
      database/migrations/20190515031308_ini_admin_user.php

17
.env

@ -0,0 +1,17 @@
APP_DEBUG = true
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = invotpdb
USERNAME = invotpdb
PASSWORD = JJYzfsbrNwjn7P3E
HOSTPORT = 3306
CHARSET = utf8mb4
DEBUG = false
[LANG]
default_lang = zh-cn

5
.gitignore

@ -0,0 +1,5 @@
/.idea
/.vscode
/vendor
*.log
composer.lock

42
.travis.yml

@ -0,0 +1,42 @@
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

21
LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Zhao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

127
README.md

@ -0,0 +1,127 @@
> 站在巨人的肩膀上,并不是高的表现,反而使自己变得渺小~只有吸收了巨人的营养,茁壮自己才是真正的高大! --笔者
# ApiAdmin
[![ApiAdmin](https://img.shields.io/hexpm/l/plug.svg)](http://www.apiadmin.org/)
[![ApiAdmin](https://img.shields.io/badge/release-5.0.0-blue.svg)](http://www.apiadmin.org/)
[![ApiAdmin](https://img.shields.io/badge/build-passing-brightgreen.svg)](http://www.apiadmin.org/)
[![ApiAdmin](https://img.shields.io/badge/ApiAdmin-5.0.0-brightgreen.svg)](http://www.apiadmin.org/)
## 前端页面
ApiAdmin5.0是一个前后端完全分离的项目,前端采用Vue构建,如需要可视化配置的请移步:[ApiAdmin-WEB](https://gitee.com/apiadmin/ApiAdmin-WEB)
## 快速安装
> 第一步:安装代码
```
composer create-project apiadmin/apiadmin
```
```
你也可以:先获取基础代码 git clone https://gitee.com/apiadmin/ApiAdmin.git 再使用composer安装 composer install
```
> 第二步:检测环境以及配置数据库
```
php think apiadmin:install
```
> 第三步:完成数据迁移
```
php think migrate:run
* 如出现报错:There are no commands defined in the "migrate" namespace.
* 请先更新下think版本:composer update topthink/framework
* 再执行:php think migrate:run
* 特别鸣谢:@孙晔华
```
> 第四步:构建后端路由
```
php think apiadmin:adminRouter
```
> 第五步:获取管理后台账号密码
```
cat install/lock.ini
```
## 灵 感
首先自我介绍下吧,我是一个PHP程序员,目前就职于某上市集团。我第一份工作是做微信开发的,这也是我入行以来第一次做的商业上线项目,虽然我只是充当了其中一个不是太重要的角色,但是感谢它让我第一次接触了API,也让我第一次对于API产生了浓厚的兴趣。之后的一段时间内甚至疯狂的收集过各种免费的API接口!然而一直只是在用API,却没有为API贡献过些什么。
开源框架用了很多,开源代码看了很多,github、git@osc、Stack Overflow这些优秀的平台帮助了我很多,所以,我觉得是时候为开源做点什么。更是给开源项目PhalApi贡献过代码,也正是这一个契机使得我正式迈向开源社区。随着时间的推移,PhalApi的战绩赫赫,它的壮大更加坚定了Api的地位,既然未来的互联网世界中API占了很重要的地位,既然越来越多的人开始开发API,那么无状态的API如何去管理呢?因此**ApiAdmin**来了~
## 愿 景
> 希望有人用它,希望更多的人用它。
> 希望它能帮助到你,希望它能帮助到更多的你。
## 项目简介
**系统需求**
- PHP >= 7.2.5
- MySQL >= 5.5.3
- Redis
**项目构成**
- ThinkPHP v6.0.*
- Vue 2.*
- ...
**功能简介**
1. 接口文档自动生成
2. 接口输入参数自动检查
3. 接口输出参数数据类型自动规整
4. 灵活的参数规则设定
5. 支持三方Api无缝融合
6. 本地二次开发友好
7. ...
```
ApiAdmin(PHP部分)
├─ 系统维护
| ├─ 菜单管理 - 编辑访客权限,处理菜单父子关系,被权限系统依赖(极为重要)
| ├─ 用户管理 - 添加新用户,封号,删号以及给账号分配权限组
| ├─ 权限管理 - 权限组管理,给权限组添加权限,将用户提出权限组
| └─ 操作日志 - 记录管理员的操作,用于追责,回溯和备案
| ...
```
**页面截图**
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095358_19cb42d0_110856.png "api.png")
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095410_55dc23e1_110856.png "app.png")
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095420_bddff990_110856.png "auth1.png")
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095427_fa86e42d_110856.png "auth2.png")
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095436_3600de17_110856.png "lock.png")
![输入图片说明](https://gitee.com/uploads/images/2018/0224/095444_d2a88da0_110856.png "user.png")
**项目特性**
- 开放源码
- 保持生机
- 不断更新
- 响应市场
**开源,我们在路上!**
## 鸣谢
ApiAdmin走到今天,也正式迈入4.1时代了,我们怀着激动的心情迎来这次发布。在新版本发布之际,我们真诚的感谢从1.0到5.0陪我们一路走来的朋友们。感谢你们的支持和信任!当然也感谢#开源中国#给大陆本土开源提供这样一个优秀的平台。
## 附:升级指南
很抱歉的告诉大家,虽然我们尽可能的和往期版本进行了兼容,但是,由于整体架构变化很大,所以想要零成本升级有点困难。我们建议大家可以使用5.0做新接口,慢慢的将4.1版本的接口移植到5.0。

BIN
apiadmin_72Dnz (1).tar.gz

Binary file not shown.

1
app/.htaccess

@ -0,0 +1 @@
deny from all

22
app/AppService.php

@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

94
app/BaseController.php

@ -0,0 +1,94 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
[$validate, $scene] = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}

58
app/ExceptionHandle.php

@ -0,0 +1,58 @@
<?php
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 其他错误交给系统处理
return parent::render($request, $e)->header(config('apiadmin.CROSS_DOMAIN'));
}
}

8
app/Request.php

@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

26
app/command/ApiAdmin.php

@ -0,0 +1,26 @@
<?php
declare (strict_types=1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class ApiAdmin extends Command {
protected function configure() {
// 指令配置
$this->setName('apiadmin:test')
->setDescription('ApiAdmin默认命令行脚本,主要用于内部测试和研究');
}
protected function execute(Input $input, Output $output): void {
$a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
foreach ($a as $k => &$v) {
if ($v === 5) {
$v = 55;
}
}
dump($a);
}
}

112
app/command/AutoBuildFile.php

@ -0,0 +1,112 @@
<?php
declare (strict_types=1);
namespace app\command;
use app\util\AutoBuild;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class AutoBuildFile extends Command {
protected function configure() {
// 指令配置
$this->setName('apiadmin:autoBuild')->setDescription('ApiAdmin自动构建文件');
}
/**
* 自动构建
* @param Input $input
* @param Output $output
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
protected function execute(Input $input, Output $output): void {
$config = $this->parseConfig($output);
(new AutoBuild())->run($config);
$output->info('Build files successful');
}
/**
* 获取cli配置输入
* @param $output
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function parseConfig($output): array {
$output->comment('Do you need to build a control? 1 or 0 (default 1)');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['control'] = strlen($input) ? $input : 1;
if ($dsn['control']) {
$dsn['name'] = $this->getControlName($output);
$output->comment('Please choose module (1:admin;2:api, default 1):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['module'] = strlen($input) ? $input : 1;
$output->comment('Do you need to build a menu? 1 or 0 (default 1):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['menu'] = strlen($input) ? $input : 1;
if ($dsn['menu']) {
$output->comment('Please input menu fid (default 0):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['fid'] = strlen($input) ? $input : 0;
$output->comment('Do you need to create a route? 1 or 0 (default 0):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['route'] = strlen($input) ? $input : 0;
}
}
$output->comment('Do you need to build a model? 1 or 0 (default 0):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['model'] = strlen($input) ? $input : 0;
if ($dsn['model']) {
$dsn['modelName'] = $this->getModelName($output);
$output->comment('Do you need to create a table? 1 or 0 (default 0):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['table'] = strlen($input) ? $input : 0;
}
return $dsn;
}
/**
* 递归获取控制器名称
* @param $output
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function getModelName($output): string {
$output->comment('Please input model name');
$input = trim(fgets(fopen('php://stdin', 'r')));
if ($input) {
return $input;
} else {
return $this->getModelName($output);
}
}
/**
* 递归获取控制器名称
* @param $output
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function getControlName($output): string {
$output->comment('Please input controller name');
$input = trim(fgets(fopen('php://stdin', 'r')));
if ($input) {
return $input;
} else {
return $this->getControlName($output);
}
}
}

31
app/command/FreshAdminRouter.php

@ -0,0 +1,31 @@
<?php
declare (strict_types=1);
namespace app\command;
use app\util\RouterTool;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class FreshAdminRouter extends Command {
protected function configure(): void {
// 指令配置
$this->setName('apiadmin:adminRouter')->setDescription('自动构建后端路由');
}
/**
* php think apiadmin:adminRouter
* @param Input $input
* @param Output $output
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
protected function execute(Input $input, Output $output): void {
RouterTool::buildAdminRouter();
$output->info('路由构建成功');
}
}

120
app/command/Install.php

@ -0,0 +1,120 @@
<?php
declare (strict_types=1);
namespace app\command;
use app\util\Strs;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class Install extends Command {
protected function configure(): void {
$this->setName('apiadmin:install')->setDescription('ApiAdmin安装脚本');
}
/**
* php think apiadmin:install --db mysql://root:123456@127.0.0.1:3306/apiadmin#utf8mb4
* @param Input $input
* @param Output $output
* @return int|void|null
* @throws \think\Exception
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
protected function execute(Input $input, Output $output) {
$tplPath = root_path() . 'install' . DIRECTORY_SEPARATOR;
$lockFile = $tplPath . 'lock.ini';
if (file_exists($lockFile)) {
$output->highlight("You have already installed it, please do not reinstall!");
$output->highlight("If necessary, delete the install/lock.ini and try again");
exit;
}
if (!is_writable($tplPath)) {
$output->highlight($tplPath . 'cannot be modified!');
exit;
}
$tempPath = runtime_path();
if (!is_writable($tempPath)) {
$output->highlight($tempPath . 'cannot be modified!');
exit;
}
if (!extension_loaded('redis')) {
$output->highlight('Redis extension missing!');
exit;
}
try {
$options = $this->parseDsnConfig($output);
$dsn = "{$options['type']}:dbname={$options['database']};host={$options['hostname']};port={$options['hostport']};charset={$options['charset']}";
new \PDO($dsn, $options['username'], $options['password']);
//处理数据库配置文件
$dbConf = str_replace([
'{$DB_TYPE}', '{$DB_HOST}', '{$DB_NAME}',
'{$DB_USER}', '{$DB_PASSWORD}', '{$DB_PORT}',
'{$DB_CHAR}'
], [
$options['type'], $options['hostname'], $options['database'],
$options['username'], $options['password'], $options['hostport'],
$options['charset']
], file_get_contents($tplPath . 'db.tpl'));
file_put_contents(root_path() . '.env', $dbConf);
$output->info('Database configuration updated successfully');
//处理ApiAdmin自定义配置
$authKey = substr(Strs::uuid(), 1, -1);
$apiConf = str_replace('{$AUTH_KEY}', $authKey, file_get_contents($tplPath . 'apiadmin.tpl'));
file_put_contents(config_path() . 'apiadmin.php', $apiConf);
$output->info('ApiAdmin configuration updated successfully');
//生成lock文件,并且写入用户名密码
file_put_contents($lockFile, $authKey);
$output->info('Lock file initialization successful');
} catch (\PDOException $e) {
$output->highlight($e->getMessage());
}
}
/**
* DSN解析
* @param $output
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function parseDsnConfig($output): array {
$output->comment('please input database type(default mysql):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['type'] = $input ? $input : 'mysql';
$output->comment('please input database username(default root):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['username'] = $input ? $input : 'root';
$output->comment('please input database password(default 123456):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['password'] = $input ? $input : '123456';
$output->comment('please input database host(default 127.0.0.1):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['hostname'] = $input ? $input : '127.0.0.1';
$output->comment('please input database port(default 3306):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['hostport'] = $input ? $input : '3306';
$output->comment('please input database name(default apiadmin):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['database'] = $input ? $input : 'apiadmin';
$output->comment('please input database charset(default utf8mb4):');
$input = trim(fgets(fopen('php://stdin', 'r')));
$dsn['charset'] = $input ? $input : 'utf8mb4';
return $dsn;
}
}

2
app/common.php

@ -0,0 +1,2 @@
<?php
// 应用公共文件

218
app/controller/admin/App.php

@ -0,0 +1,218 @@
<?php
declare (strict_types=1);
/**
* 应用管理
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminApp;
use app\model\AdminList;
use app\model\AdminGroup;
use app\util\ReturnCode;
use app\util\Strs;
use app\util\Tools;
use think\Response;
class App extends Base {
/**
* 获取应用列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$keywords = $this->request->get('keywords', '');
$type = $this->request->get('type', '');
$status = $this->request->get('status', '');
$obj = new AdminApp();
if (strlen($status)) {
$obj = $obj->where('app_status', $status);
}
if ($type) {
switch ($type) {
case 1:
$obj = $obj->where('app_id', $keywords);
break;
case 2:
$obj = $obj->whereLike('app_name', "%{$keywords}%");
break;
}
}
$listObj = $obj->order('app_add_time', 'DESC')->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 获取AppId,AppSecret,接口列表,应用接口权限细节
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAppInfo(): Response {
$apiArr = (new AdminList())->select();
foreach ($apiArr as $api) {
$res['apiList'][$api['group_hash']][] = $api;
}
$groupArr = (new AdminGroup())->select();
$groupArr = Tools::buildArrFromObj($groupArr);
$res['groupInfo'] = array_column($groupArr, 'name', 'hash');
$id = $this->request->get('id', 0);
if ($id) {
$appInfo = (new AdminApp())->where('id', $id)->find()->toArray();
$res['app_detail'] = json_decode($appInfo['app_api_show'], true);
} else {
$res['app_id'] = mt_rand(1, 9) . Strs::randString(7, 1);
$res['app_secret'] = Strs::randString(32);
}
return $this->buildSuccess($res);
}
/**
* 刷新APPSecret
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function refreshAppSecret(): Response {
$data['app_secret'] = Strs::randString(32);
return $this->buildSuccess($data);
}
/**
* 新增应用
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
$data = [
'app_id' => $postData['app_id'],
'app_secret' => $postData['app_secret'],
'app_name' => $postData['app_name'],
'app_info' => $postData['app_info'],
'app_group' => $postData['app_group'],
'app_add_time' => time(),
'app_api' => '',
'app_api_show' => ''
];
if (isset($postData['app_api']) && $postData['app_api']) {
$appApi = [];
$data['app_api_show'] = json_encode($postData['app_api']);
foreach ($postData['app_api'] as $value) {
$appApi = array_merge($appApi, $value);
}
$data['app_api'] = implode(',', $appApi);
}
$res = AdminApp::create($data);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 应用状态编辑
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminApp::update([
'id' => $id,
'app_status' => $status
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
$appInfo = (new AdminApp())->where('id', $id)->find();
cache('AccessToken:Easy:' . $appInfo['app_secret'], null);
if ($oldWiki = cache('WikiLogin:' . $id)) {
cache('WikiLogin:' . $oldWiki, null);
}
return $this->buildSuccess();
}
/**
* 编辑应用
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
$data = [
'app_secret' => $postData['app_secret'],
'app_name' => $postData['app_name'],
'app_info' => $postData['app_info'],
'app_group' => $postData['app_group'],
'app_api' => '',
'app_api_show' => ''
];
if (isset($postData['app_api']) && $postData['app_api']) {
$appApi = [];
$data['app_api_show'] = json_encode($postData['app_api']);
foreach ($postData['app_api'] as $value) {
$appApi = array_merge($appApi, $value);
}
$data['app_api'] = implode(',', $appApi);
}
$res = AdminApp::update($data, ['id' => $postData['id']]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
$appInfo = (new AdminApp())->where('id', $postData['id'])->find();
cache('AccessToken:Easy:' . $appInfo['app_secret'], null);
if ($oldWiki = cache('WikiLogin:' . $postData['id'])) {
cache('WikiLogin:' . $oldWiki, null);
}
return $this->buildSuccess();
}
/**
* 删除应用
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$id = $this->request->get('id');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$appInfo = (new AdminApp())->where('id', $id)->find();
cache('AccessToken:Easy:' . $appInfo['app_secret'], null);
AdminApp::destroy($id);
if ($oldWiki = cache('WikiLogin:' . $id)) {
cache('WikiLogin:' . $oldWiki, null);
}
return $this->buildSuccess();
}
}

140
app/controller/admin/AppGroup.php

@ -0,0 +1,140 @@
<?php
declare (strict_types=1);
/**
*
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminApp;
use app\model\AdminAppGroup;
use app\util\ReturnCode;
use think\Response;
class AppGroup extends Base {
/**
* 获取应用组列表
* @return \think\Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$keywords = $this->request->get('keywords', '');
$type = $this->request->get('type', '');
$status = $this->request->get('status', '');
$obj = new AdminAppGroup();
if (strlen($status)) {
$obj = $obj->where('status', $status);
}
if ($type) {
switch ($type) {
case 1:
if (strlen($keywords)) {
$obj = $obj->where('hash', $keywords);
}
break;
case 2:
$obj = $obj->whereLike('name', "%{$keywords}%");
break;
}
}
$listObj = $obj->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 获取全部有效的应用组
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAll(): Response {
$listInfo = (new AdminAppGroup())->where(['status' => 1])->select();
return $this->buildSuccess([
'list' => $listInfo
]);
}
/**
* 应用组状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminAppGroup::update([
'id' => $id,
'status' => $status
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 添加应用组
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
$res = AdminAppGroup::create($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 应用组编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
$res = AdminAppGroup::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 应用组删除
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$hash = $this->request->get('hash');
if (!$hash) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$has = (new AdminApp())->where(['app_group' => $hash])->count();
if ($has) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '当前分组存在' . $has . '个应用,禁止删除');
}
AdminAppGroup::destroy(['hash' => $hash]);
return $this->buildSuccess();
}
}

277
app/controller/admin/Auth.php

@ -0,0 +1,277 @@
<?php
declare (strict_types=1);
/**
* 权限相关配置
* @since 2018-02-06
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminAuthGroup;
use app\model\AdminAuthGroupAccess;
use app\model\AdminAuthRule;
use app\model\AdminMenu;
use app\util\ReturnCode;
use app\util\Tools;
use think\Response;
class Auth extends Base {
/**
* 获取权限组列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$keywords = $this->request->get('keywords', '');
$status = $this->request->get('status', '');
$obj = new AdminAuthGroup();
if (strlen($status)) {
$obj = $obj->where('status', $status);
}
if ($keywords) {
$obj = $obj->whereLike('name', "%{$keywords}%");
}
$listObj = $obj->order('id', 'DESC')->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 获取全部已开放的可选组
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getGroups(): Response {
$listInfo = (new AdminAuthGroup())->where(['status' => 1])->order('id', 'DESC')->select();
$count = count($listInfo);
$listInfo = Tools::buildArrFromObj($listInfo);
return $this->buildSuccess([
'list' => $listInfo,
'count' => $count
]);
}
/**
* 获取组所在权限列表
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getRuleList(): Response {
$groupId = $this->request->get('group_id', 0);
$list = (new AdminMenu)->order('sort', 'ASC')->select();
$list = Tools::buildArrFromObj($list);
$list = Tools::listToTree($list);
$rules = [];
if ($groupId) {
$rules = (new AdminAuthRule())->where(['group_id' => $groupId])->select();
$rules = Tools::buildArrFromObj($rules);
$rules = array_column($rules, 'url');
}
$newList = $this->buildList($list, $rules);
return $this->buildSuccess([
'list' => $newList
]);
}
/**
* 新增组
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$res = AdminAuthGroup::create([
'name' => $this->request->post('name', ''),
'description' => $this->request->post('description', '')
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 权限组状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminAuthGroup::update([
'id' => $id,
'status' => $status
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 编辑用户
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$res = AdminAuthGroup::update([
'id' => $this->request->post('id', 0),
'name' => $this->request->post('name', ''),
'description' => $this->request->post('description', '')
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 删除组
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$id = $this->request->get('id');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$listInfo = (new AdminAuthGroupAccess())->where('find_in_set("' . $id . '", `group_id`)')->select();
if ($listInfo) {
foreach ($listInfo as $value) {
$oldGroupArr = explode(',', $value->group_id);
$key = array_search($id, $oldGroupArr);
unset($oldGroupArr[$key]);
$newData = implode(',', $oldGroupArr);
$value->group_id = $newData;
$value->save();
}
}
AdminAuthGroup::destroy($id);
AdminAuthRule::destroy(['group_id' => $id]);
return $this->buildSuccess();
}
/**
* 从指定组中删除指定用户
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function delMember(): Response {
$gid = $this->request->get('gid', 0);
$uid = $this->request->get('uid', 0);
if (!$gid || !$uid) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$oldInfo = (new AdminAuthGroupAccess())->where('uid', $uid)->find()->toArray();
$oldGroupArr = explode(',', $oldInfo['group_id']);
$key = array_search($gid, $oldGroupArr);
unset($oldGroupArr[$key]);
$newData = implode(',', $oldGroupArr);
$res = AdminAuthGroupAccess::update([
'group_id' => $newData
], [
'uid' => $uid
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 构建适用前端的权限数据
* @param $list
* @param $rules
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function buildList($list, $rules): array {
$newList = [];
foreach ($list as $key => $value) {
$newList[$key]['title'] = $value['title'];
$newList[$key]['key'] = $value['url'];
if (isset($value['children'])) {
$newList[$key]['expand'] = true;
$newList[$key]['children'] = $this->buildList($value['children'], $rules);
} else {
if (in_array($value['url'], $rules)) {
$newList[$key]['checked'] = true;
}
}
}
return $newList;
}
/**
* 编辑权限细节
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function editRule(): Response {
$id = $this->request->post('id', 0);
$rules = $this->request->post('rules', []);
if (is_array($rules)) {
$needAdd = [];
$has = (new AdminAuthRule())->where(['group_id' => $id])->select();
$has = Tools::buildArrFromObj($has);
$hasRule = array_column($has, 'url');
$needDel = array_flip($hasRule);
foreach ($rules as $key => $value) {
if (!empty($value)) {
if (!in_array($value, $hasRule)) {
$data['url'] = $value;
$data['group_id'] = $id;
$needAdd[] = $data;
} else {
unset($needDel[$value]);
}
}
}
if (count($needAdd)) {
(new AdminAuthRule())->saveAll($needAdd);
}
if (count($needDel)) {
$urlArr = array_keys($needDel);
(new AdminAuthRule())->whereIn('url', $urlArr)->where('group_id', $id)->delete();
}
}
return $this->buildSuccess();
}
}

105
app/controller/admin/Base.php

@ -0,0 +1,105 @@
<?php
declare (strict_types=1);
/**
* 工程基类
* @since 2017/02/28 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminUser;
use app\model\AdminUserData;
use app\util\ReturnCode;
use app\BaseController;
use think\App;
use think\facade\Env;
use think\Response;
class Base extends BaseController {
private $debug = [];
protected $userInfo;
public function __construct(App $app) {
parent::__construct($app);
$this->userInfo = $this->request->API_ADMIN_USER_INFO;
}
/**
* 成功的返回
* @param array $data
* @param string $msg
* @param int $code
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function buildSuccess(array $data = [], string $msg = '操作成功', int $code = ReturnCode::SUCCESS): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
/**
* 更新用户信息
* @param array $data
* @param bool $isDetail
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function updateUserInfo(array $data, bool $isDetail = false): void {
$apiAuth = $this->request->header('Api-Auth');
if ($isDetail) {
AdminUserData::update($data, ['uid' => $this->userInfo['id']]);
$this->userInfo['userData'] = (new AdminUserData())->where('uid', $this->userInfo['id'])->find();
} else {
AdminUser::update($data, ['id' => $this->userInfo['id']]);
$detail = $this->userInfo['userData'];
$this->userInfo = (new AdminUser())->where('id', $this->userInfo['id'])->find();
$this->userInfo['userData'] = $detail;
}
cache('Login:' . $apiAuth, json_encode($this->userInfo), config('apiadmin.ONLINE_TIME'));
}
/**
* 错误的返回
* @param int $code
* @param string $msg
* @param array $data
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function buildFailed(int $code, string $msg = '操作失败', array $data = []): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
/**
* debug参数收集
* @param $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
protected function debug($data): void {
if ($data) {
$this->debug[] = $data;
}
}
}

285
app/controller/admin/Fields.php

@ -0,0 +1,285 @@
<?php
declare (strict_types=1);
/**
* 接口输入输出字段维护
* @since 2018-02-21
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminFields;
use app\model\AdminList;
use app\util\DataType;
use app\util\ReturnCode;
use app\util\Tools;
use think\Response;
class Fields extends Base {
private $dataType = [
DataType::TYPE_INTEGER => 'Integer',
DataType::TYPE_STRING => 'String',
DataType::TYPE_BOOLEAN => 'Boolean',
DataType::TYPE_ENUM => 'Enum',
DataType::TYPE_FLOAT => 'Float',
DataType::TYPE_FILE => 'File',
DataType::TYPE_MOBILE => 'Mobile',
DataType::TYPE_OBJECT => 'Object',
DataType::TYPE_ARRAY => 'Array'
];
/**
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
return $this->buildSuccess($this->dataType);
}
/**
* 获取请求参数
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function request(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$hash = $this->request->get('hash', '');
if (empty($hash)) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$listObj = (new AdminFields())->where('hash', $hash)->where('type', 0)
->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
$apiInfo = (new AdminList())->where('hash', $hash)->find();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total'],
'dataType' => $this->dataType,
'apiInfo' => $apiInfo
]);
}
/**
* 获取返回参数
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function response(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$hash = $this->request->get('hash', '');
if (empty($hash)) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$listObj = (new AdminFields())->where('hash', $hash)->where('type', 1)
->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
$apiInfo = (new AdminList())->where('hash', $hash)->find();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total'],
'dataType' => $this->dataType,
'apiInfo' => $apiInfo
]);
}
/**
* 新增字段
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
$postData['show_name'] = $postData['field_name'];
$postData['default'] = $postData['defaults'];
unset($postData['defaults']);
$res = AdminFields::create($postData);
cache('RequestFields:NewRule:' . $postData['hash'], null);
cache('RequestFields:Rule:' . $postData['hash'], null);
cache('ResponseFieldsRule:' . $postData['hash'], null);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 字段编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
$postData['show_name'] = $postData['field_name'];
$postData['default'] = $postData['defaults'];
unset($postData['defaults']);
$res = AdminFields::update($postData);
cache('RequestFields:NewRule:' . $postData['hash'], null);
cache('RequestFields:Rule:' . $postData['hash'], null);
cache('ResponseFieldsRule:' . $postData['hash'], null);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 字段删除
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$id = $this->request->get('id');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$fieldsInfo = (new AdminFields())->where('id', $id)->find();
cache('RequestFields:NewRule:' . $fieldsInfo->hash, null);
cache('RequestFields:Rule:' . $fieldsInfo->hash, null);
cache('ResponseFieldsRule:' . $fieldsInfo->hash, null);
AdminFields::destroy($id);
return $this->buildSuccess();
}
/**
* 批量上传返回字段
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function upload(): Response {
$hash = $this->request->post('hash');
$type = $this->request->post('type');
$jsonStr = $this->request->post('jsonStr');
$jsonStr = html_entity_decode($jsonStr);
$data = json_decode($jsonStr, true);
if ($data === null) {
return $this->buildFailed(ReturnCode::EXCEPTION, 'JSON数据格式有误');
}
AdminList::update(['return_str' => json_encode($data)], ['hash' => $hash]);
$this->handle($data['data'], $dataArr);
$old = (new AdminFields())->where('hash', $hash)->where('type', $type)->select();
$old = Tools::buildArrFromObj($old);
$oldArr = array_column($old, 'show_name');
$newArr = array_column($dataArr, 'show_name');
$addArr = array_diff($newArr, $oldArr);
$delArr = array_diff($oldArr, $newArr);
if ($delArr) {
$delArr = array_values($delArr);
(new AdminFields())->whereIn('show_name', $delArr)->delete();
}
if ($addArr) {
$addData = [];
foreach ($dataArr as $item) {
if (in_array($item['show_name'], $addArr)) {
$addData[] = $item;
}
}
(new AdminFields())->insertAll($addData);
}
cache('RequestFields:NewRule:' . $hash, null);
cache('RequestFields:Rule:' . $hash, null);
cache('ResponseFieldsRule:' . $hash, null);
return $this->buildSuccess();
}
/**
* @param $data
* @param $dataArr
* @param string $prefix
* @param string $index
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function handle(array $data, &$dataArr, string $prefix = 'data', string $index = 'data'): void {
if (!$this->isAssoc($data)) {
$addArr = [
'field_name' => $index,
'show_name' => $prefix,
'hash' => $this->request->post('hash'),
'is_must' => 1,
'data_type' => DataType::TYPE_ARRAY,
'type' => $this->request->post('type')
];
$dataArr[] = $addArr;
$prefix .= '[]';
if (isset($data[0]) && is_array($data[0])) {
$this->handle($data[0], $dataArr, $prefix);
}
} else {
$addArr = [
'field_name' => $index,
'show_name' => $prefix,
'hash' => $this->request->post('hash'),
'is_must' => 1,
'data_type' => DataType::TYPE_OBJECT,
'type' => $this->request->post('type')
];
$dataArr[] = $addArr;
$prefix .= '{}';
foreach ($data as $index => $datum) {
$myPre = $prefix . $index;
$addArr = array(
'field_name' => $index,
'show_name' => $myPre,
'hash' => $this->request->post('hash'),
'is_must' => 1,
'data_type' => DataType::TYPE_STRING,
'type' => $this->request->post('type')
);
if (is_numeric($datum)) {
if (preg_match('/^\d*$/', $datum)) {
$addArr['data_type'] = DataType::TYPE_INTEGER;
} else {
$addArr['data_type'] = DataType::TYPE_FLOAT;
}
$dataArr[] = $addArr;
} elseif (is_array($datum)) {
$this->handle($datum, $dataArr, $myPre, $index);
} else {
$addArr['data_type'] = DataType::TYPE_STRING;
$dataArr[] = $addArr;
}
}
}
}
/**
* 判断是否是关联数组(true表示是关联数组)
* @param array $arr
* @return bool
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function isAssoc(array $arr): bool {
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
}

51
app/controller/admin/Index.php

@ -0,0 +1,51 @@
<?php
declare (strict_types=1);
namespace app\controller\admin;
use app\util\ReturnCode;
use think\Response;
class Index extends Base {
public function upload(): Response {
$path = '/upload/' . date('Ymd', time()) . '/';
$name = $_FILES['file']['name'];
$tmp_name = $_FILES['file']['tmp_name'];
$error = $_FILES['file']['error'];
//过滤错误
if ($error) {
switch ($error) {
case 1:
$error_message = '您上传的文件超过了PHP.INI配置文件中UPLOAD_MAX-FILESIZE的大小';
break;
case 2:
$error_message = '您上传的文件超过了PHP.INI配置文件中的post_max_size的大小';
break;
case 3:
$error_message = '文件只被部分上传';
break;
case 4:
$error_message = '文件不能为空';
break;
default:
$error_message = '未知错误';
}
die($error_message);
}
$arr_name = explode('.', $name);
$hz = array_pop($arr_name);
$new_name = md5(time() . uniqid()) . '.' . $hz;
if (!file_exists($_SERVER['DOCUMENT_ROOT'] . $path)) {
mkdir($_SERVER['DOCUMENT_ROOT'] . $path, 0755, true);
}
if (move_uploaded_file($tmp_name, $_SERVER['DOCUMENT_ROOT'] . $path . $new_name)) {
return $this->buildSuccess([
'fileName' => $new_name,
'fileUrl' => $this->request->domain() . $path . $new_name
]);
} else {
return $this->buildFailed(ReturnCode::FILE_SAVE_ERROR, '文件上传失败');
}
}
}

158
app/controller/admin/InterfaceGroup.php

@ -0,0 +1,158 @@
<?php
declare (strict_types=1);
/**
* 接口组维护
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminApp;
use app\model\AdminGroup;
use app\model\AdminList;
use app\util\ReturnCode;
use think\Response;
class InterfaceGroup extends Base {
/**
* 获取接口组列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$keywords = $this->request->get('keywords', '');
$type = $this->request->get('type', '');
$status = $this->request->get('status', '');
$obj = new AdminGroup();
if (strlen($status)) {
$obj = $obj->where('status', $status);
}
if ($type) {
switch ($type) {
case 1:
$obj = $obj->where('hash', $keywords);
break;
case 2:
$obj = $obj->whereLike('name', "%{$keywords}%");
break;
}
}
$listObj = $obj->order('create_time', 'desc')->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 获取全部有效的接口组
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAll(): Response {
$listInfo = (new AdminGroup())->where(['status' => 1])->select();
return $this->buildSuccess([
'list' => $listInfo
]);
}
/**
* 接口组状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminGroup::update([
'id' => $id,
'status' => $status,
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 添加接口组
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
$res = AdminGroup::create($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 接口组编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
$res = AdminGroup::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 接口组删除
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$hash = $this->request->get('hash');
if (!$hash) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
if ($hash === 'default') {
return $this->buildFailed(ReturnCode::INVALID, '系统预留关键数据,禁止删除!');
}
AdminList::update(['group_hash' => 'default'], ['group_hash' => $hash]);
$hashRule = (new AdminApp())->whereLike('app_api_show', "%$hash%")->select();
if ($hashRule) {
foreach ($hashRule as $rule) {
$appApiShowArr = json_decode($rule->app_api_show, true);
if (!empty($appApiShowArr[$hash])) {
if (isset($appApiShowArr['default'])) {
$appApiShowArr['default'] = array_merge($appApiShowArr['default'], $appApiShowArr[$hash]);
} else {
$appApiShowArr['default'] = $appApiShowArr[$hash];
}
}
unset($appApiShowArr[$hash]);
$rule->app_api_show = json_encode($appApiShowArr);
$rule->save();
}
}
AdminGroup::destroy(['hash' => $hash]);
return $this->buildSuccess();
}
}

201
app/controller/admin/InterfaceList.php

@ -0,0 +1,201 @@
<?php
declare (strict_types=1);
/**
* 接口管理
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminApp;
use app\model\AdminFields;
use app\model\AdminList;
use app\util\ReturnCode;
use think\facade\Env;
use think\Response;
class InterfaceList extends Base {
/**
* 获取接口列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$keywords = $this->request->get('keywords', '');
$type = $this->request->get('type', '');
$status = $this->request->get('status', '');
$obj = new AdminList();
if (strlen($status)) {
$obj = $obj->where('status', $status);
}
if ($type) {
switch ($type) {
case 1:
$obj = $obj->where('hash', $keywords);
break;
case 2:
$obj = $obj->whereLike('info', "%{$keywords}%");
break;
case 3:
$obj = $obj->whereLike('api_class', "%{$keywords}%");
break;
}
}
$listObj = $obj->order('id', 'DESC')->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 获取接口唯一标识
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getHash(): Response {
$res['hash'] = uniqid();
return $this->buildSuccess($res);
}
/**
* 新增接口
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
if (!preg_match("/^[A-Za-z0-9_\/]+$/", $postData['api_class'])) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR, '真实类名只允许填写字母,数字和/');
}
$res = AdminList::create($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 接口状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$hash = $this->request->get('hash');
$status = $this->request->get('status');
$res = AdminList::update([
'status' => $status
], [
'hash' => $hash
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
cache('ApiInfo:' . $hash, null);
return $this->buildSuccess();
}
/**
* 编辑接口
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
if (!preg_match("/^[A-Za-z0-9_\/]+$/", $postData['api_class'])) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR, '真实类名只允许填写字母,数字和/');
}
$res = AdminList::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
cache('ApiInfo:' . $postData['hash'], null);
return $this->buildSuccess();
}
/**
* 删除接口
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$hash = $this->request->get('hash');
if (!$hash) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$hashRule = (new AdminApp())->whereLike('app_api', "%$hash%")->select();
if ($hashRule) {
$oldInfo = (new AdminList())->where('hash', $hash)->find();
foreach ($hashRule as $rule) {
$appApiArr = explode(',', $rule->app_api);
$appApiIndex = array_search($hash, $appApiArr);
array_splice($appApiArr, $appApiIndex, 1);
$rule->app_api = implode(',', $appApiArr);
$appApiShowArrOld = json_decode($rule->app_api_show, true);
$appApiShowArr = $appApiShowArrOld[$oldInfo->group_hash];
$appApiShowIndex = array_search($hash, $appApiShowArr);
array_splice($appApiShowArr, $appApiShowIndex, 1);
$appApiShowArrOld[$oldInfo->group_hash] = $appApiShowArr;
$rule->app_api_show = json_encode($appApiShowArrOld);
$rule->save();
}
}
AdminList::destroy(['hash' => $hash]);
AdminFields::destroy(['hash' => $hash]);
cache('ApiInfo:' . $hash, null);
return $this->buildSuccess();
}
/**
* 刷新接口路由
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function refresh(): Response {
$rootPath = root_path();
$apiRoutePath = $rootPath . 'route/apiRoute.php';
$tplPath = $rootPath . 'install/apiRoute.tpl';
$methodArr = ['*', 'POST', 'GET'];
$tplOriginStr = file_get_contents($tplPath);
$listInfo = (new AdminList())->where('status', 1)->select();
$tplStr = [];
foreach ($listInfo as $value) {
if ($value['hash_type'] === 1) {
array_push($tplStr, 'Route::rule(\'' . addslashes($value->api_class) . '\',\'api.' . addslashes($value->api_class) . '\', \'' . $methodArr[$value->method] . '\')->middleware([app\middleware\ApiAuth::class, app\middleware\ApiPermission::class, app\middleware\RequestFilter::class, app\middleware\ApiLog::class]);');
} else {
array_push($tplStr, 'Route::rule(\'' . addslashes($value->hash) . '\',\'api.' . addslashes($value->api_class) . '\', \'' . $methodArr[$value->method] . '\')->middleware([app\middleware\ApiAuth::class, app\middleware\ApiPermission::class, app\middleware\RequestFilter::class, app\middleware\ApiLog::class]);');
}
}
$tplOriginStr = str_replace(['{$API_RULE}'], [implode(PHP_EOL . ' ', $tplStr)], $tplOriginStr);
file_put_contents($apiRoutePath, $tplOriginStr);
return $this->buildSuccess();
}
}

65
app/controller/admin/Log.php

@ -0,0 +1,65 @@
<?php
declare (strict_types=1);
/**
* 后台操作日志管理
* @since 2018-02-06
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminUserAction;
use app\util\ReturnCode;
use think\Response;
class Log extends Base {
/**
* 获取操作日志列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$type = $this->request->get('type', '');
$keywords = $this->request->get('keywords', '');
$obj = new AdminUserAction();
if ($type) {
switch ($type) {
case 1:
$obj = $obj->whereLike('url', "%{$keywords}%");
break;
case 2:
$obj = $obj->whereLike('nickname', "%{$keywords}%");
break;
case 3:
$obj = $obj->where('uid', $keywords);
break;
}
}
$listObj = $obj->order('add_time', 'DESC')->paginate(['page' => $start, 'list_rows' => $limit])->toArray();
return $this->buildSuccess([
'list' => $listObj['data'],
'count' => $listObj['total']
]);
}
/**
* 删除日志
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$id = $this->request->get('id');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
AdminUserAction::destroy($id);
return $this->buildSuccess();
}
}

173
app/controller/admin/Login.php

@ -0,0 +1,173 @@
<?php
declare (strict_types=1);
/**
* 登录登出
* @since 2017-11-02
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminAuthGroupAccess;
use app\model\AdminAuthRule;
use app\model\AdminMenu;
use app\model\AdminUser;
use app\model\AdminUserData;
use app\util\ReturnCode;
use app\util\RouterTool;
use app\util\Tools;
use think\Response;
class Login extends Base {
/**
* 用户登录【账号密码登录】
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$username = $this->request->post('username');
$password = $this->request->post('password');
if (!$username) {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '缺少用户名!');
}
if (!$password) {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '缺少密码!');
} else {
$password = Tools::userMd5($password);
}
$userInfo = (new AdminUser())->where('username', $username)->where('password', $password)->find();
if (!empty($userInfo)) {
if ($userInfo['status']) {
//更新用户数据
$userData = $userInfo->userData;
$data = [];
if ($userData) {
$userData->login_times++;
$userData->last_login_ip = sprintf("%u", ip2long($this->request->ip()));
$userData->last_login_time = time();
$userData->save();
} else {
$data['login_times'] = 1;
$data['uid'] = $userInfo['id'];
$data['last_login_ip'] = sprintf("%u", ip2long($this->request->ip()));
$data['last_login_time'] = time();
$data['head_img'] = '';
AdminUserData::create($data);
$userInfo['userData'] = $data;
}
} else {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '用户已被封禁,请联系管理员');
}
} else {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '用户名密码不正确');
}
$userInfo['access'] = $this->getAccess($userInfo['id']);
$userInfo['menu'] = $this->getAccessMenuData($userInfo['id']);
$apiAuth = md5(uniqid() . time());
cache('Login:' . $apiAuth, json_encode($userInfo), config('apiadmin.ONLINE_TIME'));
cache('Login:' . $userInfo['id'], $apiAuth, config('apiadmin.ONLINE_TIME'));
$userInfo['apiAuth'] = $apiAuth;
return $this->buildSuccess($userInfo->toArray(), '登录成功');
}
/**
* 获取用户信息
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getUserInfo(): Response {
return $this->buildSuccess($this->userInfo);
}
/**
* 用户登出
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function logout(): Response {
$ApiAuth = $this->request->header('Api-Auth');
cache('Login:' . $ApiAuth, null);
cache('Login:' . $this->userInfo['id'], null);
return $this->buildSuccess([], '登出成功');
}
/**
* 获取当前用户的允许菜单
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAccessMenu(): Response {
return $this->buildSuccess($this->getAccessMenuData($this->userInfo['id']));
}
/**
* 获取当前用户的允许菜单
* @param int $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAccessMenuData(int $uid): array {
$returnData = [];
$isSupper = Tools::isAdministrator($uid);
if ($isSupper) {
$access = (new AdminMenu())->where('router', '<>', '')->select();
$returnData = Tools::listToTree(Tools::buildArrFromObj($access));
} else {
$groups = (new AdminAuthGroupAccess())->where('uid', $uid)->find();
if (isset($groups) && $groups->group_id) {
$access = (new AdminAuthRule())->whereIn('group_id', $groups->group_id)->select();
$access = array_unique(array_column(Tools::buildArrFromObj($access), 'url'));
array_push($access, "");
$menus = (new AdminMenu())->whereIn('url', $access)->where('show', 1)->select();
$returnData = Tools::listToTree(Tools::buildArrFromObj($menus));
RouterTool::buildVueRouter($returnData);
}
}
return array_values($returnData);
}
/**
* 获取用户权限数据
* @param $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAccess(int $uid): array {
$isSupper = Tools::isAdministrator($uid);
if ($isSupper) {
$access = (new AdminMenu())->select();
$access = Tools::buildArrFromObj($access);
return array_values(array_filter(array_column($access, 'url')));
} else {
$groups = (new AdminAuthGroupAccess())->where('uid', $uid)->find();
if (isset($groups) && $groups->group_id) {
$access = (new AdminAuthRule())->whereIn('group_id', $groups->group_id)->select();
$access = Tools::buildArrFromObj($access);
return array_values(array_unique(array_column($access, 'url')));
} else {
return [];
}
}
}
}

112
app/controller/admin/Menu.php

@ -0,0 +1,112 @@
<?php
declare (strict_types=1);
/**
* 目录管理
* @since 2018-01-16
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminMenu;
use app\util\ReturnCode;
use app\util\Tools;
use think\Response;
class Menu extends Base {
/**
* 获取菜单列表
* @return \think\Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$keywords = $this->request->get('keywords', '');
$obj = new AdminMenu();
if ($keywords) {
$obj = $obj->whereLike('title', "%{$keywords}%");
}
$obj = $obj->order('sort', 'ASC')->select();
$list = Tools::buildArrFromObj($obj);
if (!$keywords) {
$list = Tools::listToTree($list);
}
return $this->buildSuccess([
'list' => $list
]);
}
/**
* 新增菜单
* @return \think\Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add(): Response {
$postData = $this->request->post();
if ($postData['url']) {
$postData['url'] = 'admin/' . $postData['url'];
}
$res = AdminMenu::create($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
} else {
return $this->buildSuccess();
}
}
/**
* 菜单状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus(): Response {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminMenu::update([
'id' => $id,
'show' => $status
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 编辑菜单
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit(): Response {
$postData = $this->request->post();
if ($postData['url']) {
$postData['url'] = 'admin/' . $postData['url'];
}
$res = AdminMenu::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
return $this->buildSuccess();
}
/**
* 删除菜单
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del(): Response {
$id = $this->request->get('id');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
(new AdminMenu())->whereIn('id', $id)->delete();
return $this->buildSuccess();
}
}

18
app/controller/admin/Miss.php

@ -0,0 +1,18 @@
<?php
declare (strict_types=1);
namespace app\controller\admin;
use app\util\ReturnCode;
use think\Response;
class Miss extends Base {
public function index(): Response {
if ($this->request->isOptions()) {
return $this->buildSuccess();
} else {
return $this->buildFailed(ReturnCode::INVALID, '接口地址异常');
}
}
}

350
app/controller/admin/ThirdLogin.php

@ -0,0 +1,350 @@
<?php
declare (strict_types=1);
/**
* 三方一键登录平台
* @since 2018-03-28
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminAuthGroupAccess;
use app\model\AdminUser;
use app\util\ReturnCode;
use app\util\Strs;
use app\util\Tools;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
use think\facade\Cache;
use think\facade\Env;
use think\Response;
class ThirdLogin extends Base {
/**
* QQ一键登录配置
* @var array
*/
private $qqConfig = [
'appId' => '',
'appSecret' => '',
'redirectUri' => 'https://admin.apiadmin.org/#/login/qq'
];
/**
* 微信认证服务号一键登录配置
* @var array
*/
private $wxConfig = [
'appId' => '',
'appSecret' => ''
];
/**
* 微信开放平台一键登录配置
* @var array
*/
private $wxOpenConfig = [
'appId' => '',
'appSecret' => '',
'redirectUri' => 'https://admin.apiadmin.org/#/login/wx'
];
/**
* 使用微信开放平台登录
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function wx(): Response {
$state = $this->request->get('state', '');
$code = $this->request->get('code', '');
//验证合法性
$cacheData = Cache::has($state);
if (!$cacheData) {
return $this->buildFailed(ReturnCode::SESSION_TIMEOUT, 'state已过期');
} else {
cache($state, null);
}
//获取AccessToken
$getAccessTokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' .
$this->wxOpenConfig['appId'] . '&secret=' . $this->wxOpenConfig['appSecret'] . '&code=' . $code .
'&grant_type=authorization_code';
$tokenArr = file_get_contents($getAccessTokenUrl);
$accessTokenArr = json_decode($tokenArr, true);
//获取openId
$getUserIdUrl = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $accessTokenArr['access_token'] . '&openid=' . $accessTokenArr['openid'];
$userIdArr = file_get_contents($getUserIdUrl);
$userIdArr = json_decode($userIdArr, true);
return $this->doLogin($userIdArr['openid'], [
'nickname' => $userIdArr['nickname'],
'head_img' => $userIdArr['headimgurl']
]);
}
/**
* 获取授权登录的二维码
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getQr(): Response {
$state = uniqid();
$query = [
'appid' => $this->wxConfig['appId'],
'redirect_uri' => 'https://api.apiadmin.org/admin/ThirdLogin/loginByWx',
'response_type' => 'code',
'scope' => 'snsapi_userinfo',
'state' => $state
];
$authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($query) . '#wechat_redirect';
$qrCode = new QrCode($authUrl);
$qrCode->setSize(300);
$qrCode->setWriterByName('png');
$qrCode->setMargin(10);
$qrCode->setEncoding('UTF-8');
$qrCode->setErrorCorrectionLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::HIGH));
$qrCode->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]);
$qrCode->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]);
$qrCode->setRoundBlockSize(true);
$qrCode->setValidateResult(false);
$qrCode->writeFile(Env::get('root_path') . 'public/qr/' . $state . '.png');
cache($state, 1, 300);
return $this->buildSuccess([
'qrUrl' => 'https://api.apiadmin.org/qr/' . $state . '.png',
'state' => $state
]);
}
/**
* 接受微信回调,处理用户登录
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function loginByWx(): Response {
$code = $this->request->get('code');
$state = $this->request->get('state');
$auth = cache($state);
if (!$auth) {
return view('wiki@index/login_res', [
'info' => '当前二维码已失效',
'code' => ReturnCode::RECORD_NOT_FOUND
]);
}
$query = [
'appid' => $this->wxConfig['appId'],
'secret' => $this->wxConfig['appSecret'],
'grant_type' => 'authorization_code',
'code' => $code
];
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($query);
$accessToken = json_decode(file_get_contents($url), true);
$getUserInfoQuery = [
'access_token' => $accessToken['access_token'],
'openid' => $accessToken['openid'],
'lang' => 'zh_CN'
];
$getUserInfoUrl = 'https://api.weixin.qq.com/sns/userinfo?' . http_build_query($getUserInfoQuery);
$userInfoArr = file_get_contents($getUserInfoUrl);
$userInfoArr = json_decode($userInfoArr, true);
if ($userInfoArr) {
cache($state, [
'nickname' => $userInfoArr['nickname'],
'head_img' => $userInfoArr['headimgurl'],
'openid' => $accessToken['openid']
], 300);
return view('wiki@index/login_res', [
'info' => '登录成功',
'code' => ReturnCode::SUCCESS
]);
} else {
return view('wiki@index/login_res', [
'info' => '操作失败',
'code' => ReturnCode::DB_SAVE_ERROR
]);
}
}
/**
* 处理微信用户登录
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function checkWxLogin(): Response {
$state = $this->request->get('state');
$userInfo = cache($state);
if (is_numeric($userInfo)) {
return $this->buildFailed(666, '等待扫码');
} else {
@unlink(Env::get('root_path') . 'public/qr/' . $state . '.png');
if (is_array($userInfo)) {
cache($state, null);
return $this->doLogin($userInfo['openid'], [
'nickname' => $userInfo['nickname'],
'head_img' => $userInfo['head_img']
]);
} else {
return $this->buildFailed(ReturnCode::INVALID, '登录状态已失效,请重新登录');
}
}
}
/**
* 获取qq登录必要参数
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getWxCode(): Response {
$state = md5(uniqid() . time());
cache($state, $state, 300);
return $this->buildSuccess([
'appId' => $this->wxOpenConfig['appId'],
'redirectUri' => urlencode($this->wxOpenConfig['redirectUri']),
'state' => $state
]);
}
/**
* 获取qq登录必要参数
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getQQCode(): Response {
$state = md5(uniqid() . time());
cache($state, $state, 300);
return $this->buildSuccess([
'appId' => $this->qqConfig['appId'],
'redirectUri' => urlencode($this->qqConfig['redirectUri']),
'state' => $state
]);
}
/**
* 使用QQ登录
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function loginByQQ(): Response {
$state = $this->request->get('state', '');
$code = $this->request->get('code', '');
//验证合法性
$cacheData = Cache::has($state);
if (!$cacheData) {
return $this->buildFailed(ReturnCode::SESSION_TIMEOUT, 'state已过期');
} else {
cache($state, null);
}
//获取AccessToken
$getAccessTokenUrl = 'https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=' .
$this->qqConfig['appId'] . '&client_secret=' . $this->qqConfig['appSecret'] . '&code=' . $code .
'&redirect_uri=' . urlencode($this->qqConfig['redirectUri']);
$tokenArr = file_get_contents($getAccessTokenUrl);
parse_str($tokenArr, $accessTokenArr);
//获取openId
$getUserIdUrl = 'https://graph.qq.com/oauth2.0/me?access_token=' . $accessTokenArr['access_token'];
$userIdArr = file_get_contents($getUserIdUrl);
$userIdArr = str_replace('callback( ', '', $userIdArr);
$userIdArr = str_replace(' );', '', $userIdArr);
$userIdArr = json_decode($userIdArr, true);
$getUserInfoUrl = 'https://graph.qq.com/user/get_user_info?access_token=' . $accessTokenArr['access_token'] . '&oauth_consumer_key=' .
$this->qqConfig['appId'] . '&openid=' . $userIdArr['openid'];
$userInfoArr = file_get_contents($getUserInfoUrl);
$userInfoArr = json_decode($userInfoArr, true);
return $this->doLogin($userIdArr['openid'], [
'nickname' => $userInfoArr['nickname'],
'head_img' => $userInfoArr['figureurl_qq_2']
]);
}
/**
* 统一处理用户登录
* @param $openid
* @param $userDetail
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function doLogin(string $openid, array $userDetail): Response {
$userInfo = (new AdminUser())->where('openid', $openid)->find();
if (empty($userInfo)) {
$userInfo = AdminUser::create([
'nickname' => $userDetail['nickname'],
'username' => 'ApiAdmin_qq_' . Strs::randString(8),
'openid' => $openid,
'create_ip' => sprintf("%u", ip2long($this->request->ip())),
'status' => 1,
'create_time' => time(),
'password' => Tools::userMd5('ApiAdmin')
]);
$userDataArr = [
'login_times' => 1,
'uid' => $userInfo->id,
'last_login_ip' => sprintf("%u", ip2long($this->request->ip())),
'last_login_time' => time(),
'head_img' => $userDetail['head_img']
];
$userInfo->userData()->save($userDataArr);
$userInfo['userData'] = $userDataArr;
AdminAuthGroupAccess::create([
'uid' => $userInfo->id,
'group_id' => 1
]);
} else {
if ($userInfo['status']) {
//更新用户数据
$userInfo->userData->login_times++;
$userInfo->userData->last_login_ip = sprintf("%u", ip2long($this->request->ip()));
$userInfo->userData->last_login_time = time();
$userInfo->userData->save();
} else {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '用户已被封禁,请联系管理员');
}
}
$userInfo['access'] = (new Login(App()))->getAccess(intval($userInfo['id']));
$userInfo['menu'] = (new Login(App()))->getAccessMenuData(intval($userInfo['id']));
$apiAuth = md5(uniqid() . time());
cache('Login:' . $apiAuth, json_encode($userInfo), config('apiadmin.ONLINE_TIME'));
cache('Login:' . $userInfo['id'], $apiAuth, config('apiadmin.ONLINE_TIME'));
$userInfo['apiAuth'] = $apiAuth;
return $this->buildSuccess($userInfo->toArray(), '登录成功');
}
}

277
app/controller/admin/User.php

@ -0,0 +1,277 @@
<?php
declare (strict_types=1);
/**
* 用户管理
* @since 2018-02-06
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\admin;
use app\model\AdminAuthGroupAccess;
use app\model\AdminUser;
use app\model\AdminUserData;
use app\util\ReturnCode;
use app\util\Tools;
use think\facade\Db;
use think\Response;
class User extends Base {
/**
* 获取用户列表
* @return Response
* @throws \think\db\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function index(): Response {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$start = $this->request->get('page', 1);
$type = $this->request->get('type', '', 'intval');
$keywords = $this->request->get('keywords', '');
$status = $this->request->get('status', '');
$obj = new AdminUser();
if (strlen($status)) {
$obj = $obj->where('status', $status);
}
if ($type) {
switch ($type) {
case 1:
$obj = $obj->whereLike('username', "%{$keywords}%");
break;
case 2:
$obj = $obj->whereLike('nickname', "%{$keywords}%");
break;
}
}
$listObj = $obj->order('create_time', 'DESC')
->paginate(['page' => $start, 'list_rows' => $limit])->each(function($item, $key) {
$item->userData;
})->toArray();
$listInfo = $listObj['data'];
$idArr = array_column($listInfo, 'id');
$userGroup = (new AdminAuthGroupAccess())->whereIn('uid', $idArr)->select();
$userGroup = Tools::buildArrFromObj($userGroup);
$userGroup = Tools::buildArrByNewKey($userGroup, 'uid');
foreach ($listInfo as $key => &$value) {
if ($value['userData']) {
$value['userData']['last_login_ip'] = long2ip($value['userData']['last_login_ip']);
$value['userData']['last_login_time'] = date('Y-m-d H:i:s', $value['userData']['last_login_time']);
$value['create_ip'] = long2ip($value['create_ip']);
}
if (isset($userGroup[$value['id']])) {
$value['group_id'] = explode(',', $userGroup[$value['id']]['group_id']);
} else {
$value['group_id'] = [];
}
}
return $this->buildSuccess([
'list' => $listInfo,
'count' => $listObj['total']
]);
}
/**
* 新增用户
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function add() {
$groups = '';
$postData = $this->request->post();
$postData['create_ip'] = sprintf("%u", ip2long($this->request->ip()));
$postData['password'] = Tools::userMd5($postData['password']);
if (isset($postData['group_id']) && $postData['group_id']) {
$groups = trim(implode(',', $postData['group_id']), ',');
unset($postData['group_id']);
}
$res = AdminUser::create($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
AdminAuthGroupAccess::create([
'uid' => $res->id,
'group_id' => $groups
]);
return $this->buildSuccess();
}
/**
* 获取当前组的全部用户
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getUsers() {
$limit = $this->request->get('size', config('apiadmin.ADMIN_LIST_DEFAULT'));
$page = $this->request->get('page', 1);
$gid = $this->request->get('gid', 0);
if (!$gid) {
return $this->buildFailed(ReturnCode::PARAM_INVALID, '非法操作');
}
$totalNum = (new AdminAuthGroupAccess())->where('find_in_set("' . $gid . '", `group_id`)')->count();
$start = $limit * ($page - 1);
$sql = "SELECT au.* FROM admin_user as au LEFT JOIN admin_auth_group_access as aaga " .
" ON aaga.`uid` = au.`id` WHERE find_in_set('{$gid}', aaga.`group_id`) " .
" ORDER BY au.create_time DESC LIMIT {$start}, {$limit}";
$userInfo = Db::query($sql);
$uidArr = array_column($userInfo, 'id');
$userData = (new AdminUserData())->whereIn('uid', $uidArr)->select();
$userData = Tools::buildArrByNewKey($userData, 'uid');
foreach ($userInfo as $key => $value) {
if (isset($userData[$value['id']])) {
$userInfo[$key]['last_login_ip'] = long2ip($userData[$value['id']]['last_login_ip']);
$userInfo[$key]['login_times'] = $userData[$value['id']]['login_times'];
$userInfo[$key]['last_login_time'] = date('Y-m-d H:i:s', $userData[$value['id']]['last_login_time']);
}
$userInfo[$key]['create_ip'] = long2ip($userInfo[$key]['create_ip']);
}
return $this->buildSuccess([
'list' => $userInfo,
'count' => $totalNum
]);
}
/**
* 用户状态编辑
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function changeStatus() {
$id = $this->request->get('id');
$status = $this->request->get('status');
$res = AdminUser::update([
'id' => $id,
'status' => $status
]);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
if ($oldAdmin = cache('Login:' . $id)) {
cache('Login:' . $oldAdmin, null);
}
return $this->buildSuccess();
}
/**
* 编辑用户
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function edit() {
$groups = '';
$postData = $this->request->post();
if ($postData['password'] === 'ApiAdmin') {
unset($postData['password']);
} else {
$postData['password'] = Tools::userMd5($postData['password']);
}
if (isset($postData['group_id']) && $postData['group_id']) {
$groups = trim(implode(',', $postData['group_id']), ',');
unset($postData['group_id']);
}
$res = AdminUser::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
$has = (new AdminAuthGroupAccess())->where('uid', $postData['id'])->select();
if ($has) {
AdminAuthGroupAccess::update([
'group_id' => $groups
], [
'uid' => $postData['id'],
]);
} else {
AdminAuthGroupAccess::create([
'uid' => $postData['id'],
'group_id' => $groups
]);
}
if ($oldAdmin = cache('Login:' . $postData['id'])) {
cache('Login:' . $oldAdmin, null);
}
return $this->buildSuccess();
}
/**
* 修改自己的信息
* @return Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function own() {
$postData = $this->request->post();
$headImg = $postData['head_img'];
if ($postData['password'] && $postData['oldPassword']) {
$oldPass = Tools::userMd5($postData['oldPassword']);
unset($postData['oldPassword']);
if ($oldPass === $this->userInfo['password']) {
$postData['password'] = Tools::userMd5($postData['password']);
} else {
return $this->buildFailed(ReturnCode::INVALID, '原始密码不正确');
}
} else {
unset($postData['password']);
unset($postData['oldPassword']);
}
$postData['id'] = $this->userInfo['id'];
unset($postData['head_img']);
$res = AdminUser::update($postData);
if ($res === false) {
return $this->buildFailed(ReturnCode::DB_SAVE_ERROR);
}
$userData = (new AdminUserData())->where('uid', $postData['id'])->find();
$userData->head_img = $headImg;
$userData->save();
if ($oldWiki = cache('WikiLogin:' . $postData['id'])) {
cache('WikiLogin:' . $oldWiki, null);
}
return $this->buildSuccess();
}
/**
* 删除用户
* @return Response
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function del() {
$id = $this->request->get('id/d');
if (!$id) {
return $this->buildFailed(ReturnCode::EMPTY_PARAMS, '缺少必要参数');
}
$isAdmin = Tools::isAdministrator($id);
if ($isAdmin) {
return $this->buildFailed(ReturnCode::INVALID, '超级管理员不能被删除');
}
AdminUser::destroy($id);
AdminAuthGroupAccess::destroy(['uid' => $id]);
if ($oldAdmin = cache('Login:' . $id)) {
cache('Login:' . $oldAdmin, null);
}
return $this->buildSuccess();
}
}

56
app/controller/api/Base.php

@ -0,0 +1,56 @@
<?php
declare (strict_types=1);
/**
* 工程基类
* @since 2017/02/28 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\api;
use app\BaseController;
use app\util\ReturnCode;
use think\facade\Env;
use think\Response;
class Base extends BaseController {
private $debug = [];
protected $userInfo = [];
public function _initialize() {
// $this->userInfo = ''; 这部分初始化用户信息可以参考admin模块下的Base去自行处理
}
public function buildSuccess(array $data = [], string $msg = '操作成功', int $code = ReturnCode::SUCCESS): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
public function buildFailed(int $code, string $msg = '操作失败', array $data = []): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
if (Env::get('APP_DEBUG') && $this->debug) {
$return['debug'] = $this->debug;
}
return json($return);
}
protected function debug($data): void {
if ($data) {
$this->debug[] = $data;
}
}
}

87
app/controller/api/BuildToken.php

@ -0,0 +1,87 @@
<?php
/**
*
* @since 2017-10-26
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\api;
use app\model\AdminApp;
use app\util\ReturnCode;
use app\util\Strs;
class BuildToken extends Base {
/**
* 构建AccessToken
* @return \think\Response
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function getAccessToken() {
$param = $this->request->param();
$appInfo = (new AdminApp())->where(['app_id' => $param['app_id'], 'app_status' => 1])->find();
if (empty($appInfo)) {
return $this->buildFailed(ReturnCode::INVALID, '应用ID非法');
} else {
$appInfo = $appInfo->toArray();
}
$signature = $param['signature'];
unset($param['signature']);
unset($param['Access-Token']);
$sign = $this->getAuthToken($appInfo['app_secret'], $param);
$this->debug($sign);
if ($sign !== $signature) {
return $this->buildFailed(ReturnCode::INVALID, '身份令牌验证失败');
}
$expires = config('apiadmin.ACCESS_TOKEN_TIME_OUT');
$accessToken = cache('AccessToken:' . $param['device_id']);
if ($accessToken) {
cache('AccessToken:' . $accessToken, null);
cache('AccessToken:' . $param['device_id'], null);
}
$accessToken = $this->buildAccessToken($appInfo['app_id'], $appInfo['app_secret']);
$appInfo['device_id'] = $param['device_id'];
cache('AccessToken:' . $accessToken, $appInfo, $expires);
cache('AccessToken:' . $param['device_id'], $accessToken, $expires);
$return['access_token'] = $accessToken;
$return['expires_in'] = $expires;
return $this->buildSuccess($return);
}
/**
* 根据AppSecret和数据生成相对应的身份认证秘钥
* @param $appSecret
* @param $data
* @return string
*/
private function getAuthToken($appSecret, $data) {
if (empty($data)) {
return '';
} else {
unset($data['APP_CONF_DETAIL'], $data['API_CONF_DETAIL']);
$preArr = array_merge($data, ['app_secret' => $appSecret]);
ksort($preArr);
$preStr = http_build_query($preArr);
return md5($preStr);
}
}
/**
* 计算出唯一的身份令牌
* @param $appId
* @param $appSecret
* @return string
*/
private function buildAccessToken($appId, $appSecret) {
$preStr = $appSecret . $appId . time() . Strs::keyGen();
return md5($preStr);
}
}

26
app/controller/api/Miss.php

@ -0,0 +1,26 @@
<?php
declare (strict_types=1);
namespace app\controller\api;
use think\Exception;
use think\facade\App;
use think\Response;
class Miss extends Base {
public function index(): Response {
$version = config('apiadmin.APP_VERSION');
if (!$version) {
throw new Exception('请先执行安装脚本,完成项目初始化!');
} else {
return $this->buildSuccess([
'Product' => config('apiadmin.APP_NAME'),
'ApiVersion' => $version,
'TpVersion' => App::version(),
'Company' => config('apiadmin.COMPANY_NAME'),
'ToYou' => "I'm glad to meet you(终于等到你!)"
]);
}
}
}

188
app/controller/wiki/Api.php

@ -0,0 +1,188 @@
<?php
declare (strict_types=1);
/**
* @since 2019-08-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\wiki;
use app\model\AdminApp;
use app\model\AdminFields;
use app\model\AdminGroup;
use app\model\AdminList;
use app\util\DataType;
use app\util\ReturnCode;
use app\util\Tools;
use think\Response;
class Api extends Base {
public function errorCode(): Response {
$codeArr = ReturnCode::getConstants();
$codeArr = array_flip($codeArr);
$result = [];
$errorInfo = [
ReturnCode::SUCCESS => '请求成功',
ReturnCode::INVALID => '非法操作',
ReturnCode::DB_SAVE_ERROR => '数据存储失败',
ReturnCode::DB_READ_ERROR => '数据读取失败',
ReturnCode::CACHE_SAVE_ERROR => '缓存存储失败',
ReturnCode::CACHE_READ_ERROR => '缓存读取失败',
ReturnCode::FILE_SAVE_ERROR => '文件读取失败',
ReturnCode::LOGIN_ERROR => '登录失败',
ReturnCode::NOT_EXISTS => '不存在',
ReturnCode::JSON_PARSE_FAIL => 'JSON数据格式错误',
ReturnCode::TYPE_ERROR => '类型错误',
ReturnCode::NUMBER_MATCH_ERROR => '数字匹配失败',
ReturnCode::EMPTY_PARAMS => '丢失必要数据',
ReturnCode::DATA_EXISTS => '数据已经存在',
ReturnCode::AUTH_ERROR => '权限认证失败',
ReturnCode::OTHER_LOGIN => '别的终端登录',
ReturnCode::VERSION_INVALID => 'API版本非法',
ReturnCode::CURL_ERROR => 'CURL操作异常',
ReturnCode::RECORD_NOT_FOUND => '记录未找到',
ReturnCode::DELETE_FAILED => '删除失败',
ReturnCode::ADD_FAILED => '添加记录失败',
ReturnCode::UPDATE_FAILED => '更新记录失败',
ReturnCode::PARAM_INVALID => '数据类型非法',
ReturnCode::ACCESS_TOKEN_TIMEOUT => '身份令牌过期',
ReturnCode::SESSION_TIMEOUT => 'SESSION过期',
ReturnCode::UNKNOWN => '未知错误',
ReturnCode::EXCEPTION => '系统异常',
];
foreach ($errorInfo as $key => $value) {
$result[] = [
'en_code' => $codeArr[$key],
'code' => $key,
'chinese' => $value,
];
}
return $this->buildSuccess([
'data' => $result,
'co' => config('apiadmin.APP_NAME') . ' ' . config('apiadmin.APP_VERSION')
]);
}
public function login(): Response {
$appId = $this->request->post('username');
$appSecret = $this->request->post('password');
$appInfo = (new AdminApp())->where('app_id', $appId)->where('app_secret', $appSecret)->find();
if (!empty($appInfo)) {
if ($appInfo->app_status) {
//保存用户信息和登录凭证
$appInfo = $appInfo->toArray();
$apiAuth = md5(uniqid() . time());
cache('WikiLogin:' . $apiAuth, $appInfo, config('apiadmin.ONLINE_TIME'));
cache('WikiLogin:' . $appInfo['id'], $apiAuth, config('apiadmin.ONLINE_TIME'));
$appInfo['apiAuth'] = $apiAuth;
return $this->buildSuccess($appInfo, '登录成功');
} else {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, '当前应用已被封禁,请联系管理员');
}
} else {
return $this->buildFailed(ReturnCode::LOGIN_ERROR, 'AppId或AppSecret错误');
}
}
public function groupList(): Response {
$groupInfo = (new AdminGroup())->select();
$apiInfo = (new AdminList())->select();
$listInfo = [];
if ($this->appInfo['app_id'] === -1) {
$_apiInfo = [];
foreach ($apiInfo as $aVal) {
$_apiInfo[$aVal['group_hash']][] = $aVal;
}
foreach ($groupInfo as $gVal) {
if (isset($_apiInfo[$gVal['hash']])) {
$gVal['api_info'] = $_apiInfo[$gVal['hash']];
}
$listInfo[] = $gVal;
}
} else {
$apiInfo = Tools::buildArrFromObj($apiInfo, 'hash');
$groupInfo = Tools::buildArrFromObj($groupInfo, 'hash');
$app_api_show = json_decode($this->appInfo['app_api_show'], true);
foreach ($app_api_show as $key => $item) {
$_listInfo = $groupInfo[$key];
foreach ($item as $apiItem) {
$_listInfo['api_info'][] = $apiInfo[$apiItem];
}
if (isset($_listInfo['api_info'])) {
$listInfo[] = $_listInfo;
}
}
}
return $this->buildSuccess([
'data' => $listInfo,
'co' => config('apiadmin.APP_NAME') . ' ' . config('apiadmin.APP_VERSION')
]);
}
public function detail(): Response {
$hash = $this->request->get('hash');
if (!$hash) {
return $this->buildFailed(ReturnCode::NOT_EXISTS, '缺少必要参数');
}
$apiList = (new AdminList())->whereIn('hash', $hash)->find();
if (!$apiList) {
return $this->buildFailed(ReturnCode::NOT_EXISTS, '接口hash非法');
}
$request = (new AdminFields())->where('hash', $hash)->where('type', 0)->select();
$response = (new AdminFields())->where('hash', $hash)->where('type', 1)->select();
$dataType = array(
DataType::TYPE_INTEGER => 'Integer',
DataType::TYPE_STRING => 'String',
DataType::TYPE_BOOLEAN => 'Boolean',
DataType::TYPE_ENUM => 'Enum',
DataType::TYPE_FLOAT => 'Float',
DataType::TYPE_FILE => 'File',
DataType::TYPE_ARRAY => 'Array',
DataType::TYPE_OBJECT => 'Object',
DataType::TYPE_MOBILE => 'Mobile'
);
$groupInfo = (new AdminGroup())->where('hash', $apiList['group_hash'])->find();
$groupInfo->hot = $groupInfo->hot + 1;
$groupInfo->save();
if ($apiList['hash_type'] === 1) {
$url = $this->request->domain() . '/api/' . $apiList['api_class'];
} else {
$url = $this->request->domain() . '/api/' . $hash;
}
return $this->buildSuccess([
'request' => $request,
'response' => $response,
'dataType' => $dataType,
'apiList' => $apiList,
'url' => $url,
'co' => config('apiadmin.APP_NAME') . ' ' . config('apiadmin.APP_VERSION')
]);
}
public function logout(): Response {
$ApiAuth = $this->request->header('ApiAuth');
cache('WikiLogin:' . $ApiAuth, null);
cache('WikiLogin:' . $this->appInfo['id'], null);
$oldAdmin = cache('Login:' . $ApiAuth);
if ($oldAdmin) {
$oldAdmin = json_decode($oldAdmin, true);
cache('Login:' . $ApiAuth, null);
cache('Login:' . $oldAdmin['id'], null);
}
return $this->buildSuccess([], '登出成功');
}
}

43
app/controller/wiki/Base.php

@ -0,0 +1,43 @@
<?php
declare (strict_types=1);
/**
* 工程基类
* @since 2017/02/28 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\controller\wiki;
use app\BaseController;
use app\util\ReturnCode;
use think\Response;
class Base extends BaseController {
protected $appInfo;
public function __construct() {
parent::__construct(App());
$this->appInfo = $this->request->API_WIKI_USER_INFO;
}
public function buildSuccess($data = [], $msg = '操作成功', $code = ReturnCode::SUCCESS): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
return json($return);
}
public function buildFailed($code, $msg = '操作失败', $data = []): Response {
$return = [
'code' => $code,
'msg' => $msg,
'data' => $data
];
return json($return);
}
}

17
app/event.php

@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

10
app/middleware.php

@ -0,0 +1,10 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
];

45
app/middleware/AdminAuth.php

@ -0,0 +1,45 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\util\ReturnCode;
use think\Response;
class AdminAuth {
/**
* ApiAuth鉴权
* @param \think\facade\Request $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next): Response {
$header = config('apiadmin.CROSS_DOMAIN');
$ApiAuth = $request->header('Api-Auth', '');
if ($ApiAuth) {
$userInfo = cache('Login:' . $ApiAuth);
if ($userInfo) {
$userInfo = json_decode($userInfo, true);
}
if (!$userInfo || !isset($userInfo['id'])) {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => 'ApiAuth不匹配',
'data' => []
])->header($header);
} else {
$request->API_ADMIN_USER_INFO = $userInfo;
}
return $next($request);
} else {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => '缺少ApiAuth',
'data' => []
])->header($header);
}
}
}

45
app/middleware/AdminLog.php

@ -0,0 +1,45 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\model\AdminMenu;
use app\model\AdminUserAction;
use app\util\ReturnCode;
use think\Response;
class AdminLog {
/**
* @param \think\facade\Request $request
* @param \Closure $next
* @return \think\response\Json
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next): Response {
$userInfo = $request->API_ADMIN_USER_INFO;
$menuInfo = (new AdminMenu())->where('url', $request->pathinfo())->find();
if ($menuInfo) {
$menuInfo = $menuInfo->toArray();
} else {
return json([
'code' => ReturnCode::INVALID,
'msg' => '当前路由非法:' . $request->pathinfo(),
'data' => []
])->header(config('apiadmin.CROSS_DOMAIN'));
}
AdminUserAction::create([
'action_name' => $menuInfo['title'],
'uid' => $userInfo['id'],
'nickname' => $userInfo['nickname'],
'add_time' => time(),
'url' => $request->pathinfo(),
'data' => json_encode($request->param())
]);
return $next($request);
}
}

96
app/middleware/AdminPermission.php

@ -0,0 +1,96 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\model\AdminAuthGroup;
use app\model\AdminAuthGroupAccess;
use app\model\AdminAuthRule;
use app\util\ReturnCode;
use app\util\Tools;
use think\Response;
class AdminPermission {
/**
* 用户权限检测
* @param \think\facade\Request $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next): Response {
$userInfo = $request->API_ADMIN_USER_INFO;
// rule里包含了rule(路由规则), ruoter(完整路由)
if (!$this->checkAuth($userInfo['id'], $request->rule()->getRule())) {
return json([
'code' => ReturnCode::INVALID,
'msg' => '非常抱歉,您没有权限这么做!',
'data' => []
])->header(config('apiadmin.CROSS_DOMAIN'));
}
return $next($request);
}
/**
* 检测用户权限
* @param $uid
* @param $route
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function checkAuth($uid, $route) {
$isSupper = Tools::isAdministrator($uid);
if (!$isSupper) {
$rules = $this->getAuth($uid);
return in_array($route, $rules);
} else {
return true;
}
}
/**
* 根据用户ID获取全部权限节点
* @param $uid
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function getAuth($uid) {
$groups = (new AdminAuthGroupAccess())->where('uid', $uid)->find();
if (isset($groups) && $groups->group_id) {
$openGroup = (new AdminAuthGroup())->whereIn('id', $groups->group_id)->where(['status' => 1])->select();
if (isset($openGroup)) {
$openGroupArr = [];
foreach ($openGroup as $group) {
$openGroupArr[] = $group->id;
}
$allRules = (new AdminAuthRule())->whereIn('group_id', $openGroupArr)->select();
if (isset($allRules)) {
$rules = [];
foreach ($allRules as $rule) {
$rules[] = $rule->url;
}
$rules = array_unique($rules);
return $rules;
} else {
return [];
}
} else {
return [];
}
} else {
return [];
}
}
}

14
app/middleware/AdminResponse.php

@ -0,0 +1,14 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\Config;
use think\Response;
class AdminResponse {
public function handle($request, \Closure $next): Response {
return $next($request)->header(Config::get('apiadmin.CROSS_DOMAIN'));
}
}

145
app/middleware/ApiAuth.php

@ -0,0 +1,145 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\model\AdminApp;
use app\model\AdminList;
use app\util\ReturnCode;
use think\facade\Cache;
use think\Request;
class ApiAuth {
/**
* 获取接口基本配置参数,校验接口Hash是否合法,校验APP_ID是否合法等
* @param Request $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next) {
$header = config('apiadmin.CROSS_DOMAIN');
$pathParam = [];
$pathParamStr = str_replace($request->rule()->getRule() . '/', '', $request->pathinfo());
$pathArr = explode('/', $pathParamStr);
$pathArrLen = count($pathArr);
for ($index = 0; $index < $pathArrLen; $index += 2) {
if ($index + 1 < $pathArrLen) {
$pathParam[$pathArr[$index]] = $pathArr[$index + 1];
}
}
$apiHash = str_replace('api/', '', $request->rule()->getRule());
if ($apiHash) {
$cached = Cache::has('ApiInfo:' . $apiHash);
if ($cached) {
$apiInfo = Cache::get('ApiInfo:' . $apiHash);
} else {
$apiInfo = (new AdminList())->where('hash', $apiHash)->where('hash_type', 2)->find();
if ($apiInfo) {
$apiInfo = $apiInfo->toArray();
Cache::delete('ApiInfo:' . $apiInfo['api_class']);
Cache::set('ApiInfo:' . $apiHash, $apiInfo);
} else {
$apiInfo = (new AdminList())->where('api_class', $apiHash)->where('hash_type', 1)->find();
if ($apiInfo) {
$apiInfo = $apiInfo->toArray();
Cache::delete('ApiInfo:' . $apiInfo['hash']);
Cache::set('ApiInfo:' . $apiHash, $apiInfo);
} else {
return json([
'code' => ReturnCode::DB_READ_ERROR,
'msg' => '获取接口配置数据失败',
'data' => []
])->header($header);
}
}
}
$accessToken = $request->header('Access-Token', '');
if (!$accessToken) {
$accessToken = $request->post('Access-Token', '');
}
if (!$accessToken) {
$accessToken = $request->get('Access-Token', '');
}
if (!$accessToken && !empty($pathParam['Access-Token'])) {
$accessToken = $pathParam['Access-Token'];
}
if (!$accessToken) {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => '缺少必要参数Access-Token',
'data' => []
])->header($header);
}
if ($apiInfo['access_token']) {
$appInfo = $this->doCheck($accessToken);
} else {
$appInfo = $this->doEasyCheck($accessToken);
}
if ($appInfo === false) {
return json([
'code' => ReturnCode::ACCESS_TOKEN_TIMEOUT,
'msg' => 'Access-Token已过期',
'data' => []
])->header($header);
}
$request->APP_CONF_DETAIL = $appInfo;
$request->API_CONF_DETAIL = $apiInfo;
return $next($request);
} else {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => '缺少接口Hash',
'data' => []
])->header($header);
}
}
/**
* 简易鉴权,更具APP_SECRET获取应用信息
* @param $accessToken
* @return array|false|mixed|object|\think\App
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function doEasyCheck($accessToken) {
$appInfo = cache('AccessToken:Easy:' . $accessToken);
if (!$appInfo) {
$appInfo = (new AdminApp())->where('app_secret', $accessToken)->find();
if (!$appInfo) {
return false;
} else {
$appInfo = $appInfo->toArray();
cache('AccessToken:Easy:' . $accessToken, $appInfo);
}
}
return $appInfo;
}
/**
* 复杂鉴权,需要先通过接口获取AccessToken
* @param $accessToken
* @return bool|mixed
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function doCheck($accessToken) {
$appInfo = cache('AccessToken:' . $accessToken);
if (!$appInfo) {
return false;
} else {
return $appInfo;
}
}
}

31
app/middleware/ApiLog.php

@ -0,0 +1,31 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\util\ApiLogTool;
class ApiLog {
/**
* @param $request
* @param \Closure $next
* @return mixed
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next) {
$response = $next($request);
$requestInfo = $request->param();
unset($requestInfo['API_CONF_DETAIL']);
unset($requestInfo['APP_CONF_DETAIL']);
ApiLogTool::setApiInfo((array)$request->API_CONF_DETAIL);
ApiLogTool::setAppInfo((array)$request->APP_CONF_DETAIL);
ApiLogTool::setRequest($requestInfo);
ApiLogTool::setResponse($response->getData(), isset($response->getData()['code']) ? strval($response->getData()['code']) : 'null');
ApiLogTool::setHeader((array)$request->header());
ApiLogTool::save();
return $response;
}
}

33
app/middleware/ApiPermission.php

@ -0,0 +1,33 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\util\ReturnCode;
class ApiPermission {
/**
* 校验当前App是否有请求当前接口的权限
* @param $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next) {
$header = config('apiadmin.CROSS_DOMAIN');
$appInfo = $request->APP_CONF_DETAIL;
$apiInfo = $request->API_CONF_DETAIL;
$allRules = explode(',', $appInfo['app_api']);
if (!in_array($apiInfo['hash'], $allRules)) {
return json([
'code' => ReturnCode::INVALID,
'msg' => '非常抱歉,您没有权限这么做!',
'data' => []
])->header($header);
}
return $next($request);
}
}

13
app/middleware/ApiResponse.php

@ -0,0 +1,13 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\Config;
class ApiResponse {
public function handle($request, \Closure $next) {
return $next($request)->header(Config::get('apiadmin.CROSS_DOMAIN'));
}
}

128
app/middleware/RequestFilter.php

@ -0,0 +1,128 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\model\AdminFields;
use app\util\DataType;
use app\util\ReturnCode;
use think\facade\Cache;
use think\facade\Validate;
class RequestFilter {
/**
* 接口请求字段过滤【只验证数据的合法性,不再过滤数据】
* @param $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next) {
$apiInfo = $request->API_CONF_DETAIL;
$data = $request->param();
$has = Cache::has('RequestFields:NewRule:' . $apiInfo['hash']);
if ($has) {
$newRule = cache('RequestFields:NewRule:' . $apiInfo['hash']);
} else {
$rule = (new AdminFields())->where('hash', $apiInfo['hash'])->where('type', 0)->select();
$newRule = $this->buildValidateRule($rule);
cache('RequestFields:NewRule:' . $apiInfo['hash'], $newRule);
}
if ($newRule) {
$validate = Validate::rule($newRule);
if (!$validate->check($data)) {
return json(['code' => ReturnCode::PARAM_INVALID, 'msg' => $validate->getError(), 'data' => []]);
}
}
return $next($request);
}
/**
* 将数据库中的规则转换成TP_Validate使用的规则数组
* @param array $rule
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function buildValidateRule($rule = []) {
$newRule = [];
if ($rule) {
foreach ($rule as $value) {
if ($value['is_must']) {
$newRule[$value['field_name'] . '|' . $value['info']][] = 'require';
}
switch ($value['data_type']) {
case DataType::TYPE_INTEGER:
$newRule[$value['field_name'] . '|' . $value['info']][] = 'number';
if ($value['range']) {
$range = htmlspecialchars_decode($value['range']);
$range = json_decode($range, true);
if (isset($range['min'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['egt'] = $range['min'];
}
if (isset($range['max'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['elt'] = $range['max'];
}
}
break;
case DataType::TYPE_STRING:
if ($value['range']) {
$range = htmlspecialchars_decode($value['range']);
$range = json_decode($range, true);
if (isset($range['min'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['min'] = $range['min'];
}
if (isset($range['max'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['max'] = $range['max'];
}
}
break;
case DataType::TYPE_ENUM:
if ($value['range']) {
$range = htmlspecialchars_decode($value['range']);
$range = json_decode($range, true);
$newRule[$value['field_name'] . '|' . $value['info']]['in'] = implode(',', $range);
}
break;
case DataType::TYPE_FLOAT:
$newRule[$value['field_name'] . '|' . $value['info']][] = 'float';
if ($value['range']) {
$range = htmlspecialchars_decode($value['range']);
$range = json_decode($range, true);
if (isset($range['min'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['egt'] = $range['min'];
}
if (isset($range['max'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['elt'] = $range['max'];
}
}
break;
case DataType::TYPE_ARRAY:
$newRule[$value['field_name']][] = 'array';
if ($value['range']) {
$range = htmlspecialchars_decode($value['range']);
$range = json_decode($range, true);
if (isset($range['min'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['min'] = $range['min'];
}
if (isset($range['max'])) {
$newRule[$value['field_name'] . '|' . $value['info']]['max'] = $range['max'];
}
}
break;
case DataType::TYPE_MOBILE:
$newRule[$value['field_name'] . '|' . $value['info']]['regex'] = '/^1[3456789]\d{9}$/';
break;
}
}
}
return $newRule;
}
}

47
app/middleware/WikiAuth.php

@ -0,0 +1,47 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use app\util\ReturnCode;
class WikiAuth {
/**
* ApiAuth鉴权
* @param \think\facade\Request $request
* @param \Closure $next
* @return mixed|\think\response\Json
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function handle($request, \Closure $next) {
$header = config('apiadmin.CROSS_DOMAIN');
$ApiAuth = $request->header('Api-Auth', '');
if ($ApiAuth) {
$userInfo = cache('Login:' . $ApiAuth);
if (!$userInfo) {
$userInfo = cache('WikiLogin:' . $ApiAuth);
} else {
$userInfo = json_decode($userInfo, true);
$userInfo['app_id'] = -1;
}
if (!$userInfo || !isset($userInfo['id'])) {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => 'ApiAuth不匹配',
'data' => []
])->header($header);
} else {
$request->API_WIKI_USER_INFO = $userInfo;
}
return $next($request);
} else {
return json([
'code' => ReturnCode::AUTH_ERROR,
'msg' => '缺少ApiAuth',
'data' => []
])->header($header);
}
}
}

7
app/model/AdminApp.php

@ -0,0 +1,7 @@
<?php
namespace app\model;
class AdminApp extends Base {
}

12
app/model/AdminAppGroup.php

@ -0,0 +1,12 @@
<?php
/**
*
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminAppGroup extends Base {
}

17
app/model/AdminAuthGroup.php

@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
/**
*
* @since 2018-02-08
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
use think\model\relation\HasMany;
class AdminAuthGroup extends Base {
public function rules(): HasMany {
return $this->hasMany('AdminAuthRule', 'group_id', 'id');
}
}

12
app/model/AdminAuthGroupAccess.php

@ -0,0 +1,12 @@
<?php
/**
*
* @since 2018-02-08
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminAuthGroupAccess extends Base {
}

12
app/model/AdminAuthRule.php

@ -0,0 +1,12 @@
<?php
/**
*
* @since 2018-02-08
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminAuthRule extends Base {
}

12
app/model/AdminFields.php

@ -0,0 +1,12 @@
<?php
/**
* 接口字段规则模型
* @since 2017/07/25 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminFields extends Base {
}

13
app/model/AdminGroup.php

@ -0,0 +1,13 @@
<?php
/**
*
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminGroup extends Base {
protected $autoWriteTimestamp = true;
}

11
app/model/AdminList.php

@ -0,0 +1,11 @@
<?php
/**
* @since 2017-07-30
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminList extends Base {
}

7
app/model/AdminMenu.php

@ -0,0 +1,7 @@
<?php
namespace app\model;
class AdminMenu extends Base {
}

19
app/model/AdminUser.php

@ -0,0 +1,19 @@
<?php
declare (strict_types=1);
/**
* @since 2017-11-02
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
use think\model\relation\HasOne;
class AdminUser extends Base {
protected $autoWriteTimestamp = true;
public function userData(): HasOne {
return $this->hasOne('AdminUserData', 'uid', 'id');
}
}

12
app/model/AdminUserAction.php

@ -0,0 +1,12 @@
<?php
/**
*
* @since 2018-02-11
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminUserAction extends Base {
}

11
app/model/AdminUserData.php

@ -0,0 +1,11 @@
<?php
/**
* @since 2017-11-02
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
class AdminUserData extends Base {
}

14
app/model/Base.php

@ -0,0 +1,14 @@
<?php
/**
*
* @since 2019-04-23
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\model;
use think\Model;
class Base extends Model {
}

9
app/provider.php

@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

9
app/service.php

@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

138
app/util/ApiLogTool.php

@ -0,0 +1,138 @@
<?php
declare (strict_types=1);
/**
* @since 2017-04-14
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
use think\facade\Env;
class ApiLogTool {
private static $appInfo = 'null';
private static $apiInfo = 'null';
private static $request = 'null';
private static $response = 'null';
private static $header = 'null';
private static $userInfo = 'null';
private static $separator = ' | ';
/**
* 设置应用信息
* @param array $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
* @desc appId|appName|deviceId
*/
public static function setAppInfo(array $data): void {
self::$appInfo =
($data['app_id'] ?? 'null') . self::$separator .
($data['app_name'] ?? 'null') . self::$separator .
($data['device_id'] ?? 'null');
}
/**
* 设置请求头日志数据
* @param array $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
* @desc accessToken|version
*/
public static function setHeader(array $data): void {
$accessToken = (isset($data['access-token']) && !empty($data['access-token'])) ? $data['access-token'] : 'null';
$version = (isset($data['version']) && !empty($data['version'])) ? $data['version'] : 'null';
self::$header = $accessToken . self::$separator . $version;
}
/**
* 设置Api日志数据
* @param array $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
* @desc hash|apiClass
*/
public static function setApiInfo(array $data): void {
self::$apiInfo =
($data['hash'] ?? 'null') . self::$separator .
($data['api_class'] ?? 'null');
}
/**
* 这部分的日志其实很关键,但是由于不再强制检测UserToken,所以这部分日志暂时不生效,请大家各自适配
* @param $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function setUserInfo($data): void {
if (is_array($data) || is_object($data)) {
$data = json_encode($data);
self::$userInfo = $data;
}
}
/**
* 设置请求信息
* @param $data
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function setRequest($data): void {
if (is_array($data) || is_object($data)) {
$data = json_encode($data);
}
self::$request = $data;
}
/**
* 设置返回的信息
* @param $data
* @param string $code
* @author zhaoxiang <zhaoxiang051405@gmail.com>
* @desc 返回码|数据
*/
public static function setResponse($data, string $code = ''): void {
if (is_array($data) || is_object($data)) {
$data = json_encode($data);
}
self::$response = $code . self::$separator . $data;
}
/**
* 保存接口日志数据
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function save(): void {
$logPath = runtime_path() . 'ApiLog' . DIRECTORY_SEPARATOR;
$logStr = implode(self::$separator, array(
'[' . date('Y-m-d H:i:s') . ']',
self::$apiInfo,
self::$request,
self::$header,
self::$response,
self::$appInfo,
self::$userInfo
));
if (!file_exists($logPath)) {
mkdir($logPath, 0755, true);
}
@file_put_contents($logPath . date('YmdH') . '.log', $logStr . "\n", FILE_APPEND);
}
/**
* 保存日志文件
* @param string $log 被记录的内容
* @param string $type 日志文件名称
* @param string $filePath
*/
public static function writeLog(string $log, string $type = 'sql', string $filePath = ''): void {
if (!$filePath) {
$filePath = runtime_path() . $type . DIRECTORY_SEPARATOR;
} else {
$filePath = $filePath . $type . DIRECTORY_SEPARATOR;
}
$filename = $filePath . date("YmdH") . ".log";
if (!file_exists($filePath)) {
mkdir($filePath, 0755, true);
}
@$handle = fopen($filename, "a+");
@fwrite($handle, date('Y-m-d H:i:s') . "\t" . $log . "\r\n");
@fclose($handle);
}
}

203
app/util/AutoBuild.php

@ -0,0 +1,203 @@
<?php
/**
*
* @since 2021-02-18
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
use app\model\AdminMenu;
use think\facade\Db;
class AutoBuild {
private $config = [
'model' => 0, // 是否需要构建模型
'control' => 1, // 是否需要构建控制器
'menu' => 1, // 是否需要构建目录
'route' => 1, // 是否需要构建路由
'name' => '', // 唯一标识
'module' => 1, // 构建类型 1:admin;2:api
'table' => 0, // 是否创建表
'modelName' => '', // 表名称
'fid' => 0 // 父级ID
];
/**
* 自动构建
* @param array $config
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function run($config = []) {
$this->config = array_merge($this->config, $config);
if ($this->config['model'] == 1) {
$this->buildModel();
if ($this->config['table'] == 1) {
$this->createTable();
}
}
if ($this->config['control'] && $this->config['name']) {
$this->buildControl();
if ($this->config['menu'] && $this->config['module'] == 1) {
$this->buildMenu();
}
if ($this->config['route'] && $this->config['module'] == 1) {
$this->buildRoute();
}
}
}
/**
* 驼峰命名转下划线命名
* @param $camelCaps
* @param string $separator
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function unCamelize($camelCaps, $separator = '_'): string {
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}
/**
* 构建控制器
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function buildControl() {
$tplPath = root_path() . 'install' . DIRECTORY_SEPARATOR;
if ($this->config['module'] == 1) {
$module = 'admin';
} else {
$module = 'api';
}
$controlStr = str_replace(
['{$MODULE}', '{$NAME}'],
[$module, $this->config['name']],
file_get_contents($tplPath . 'control.tpl')
);
file_put_contents(
base_path() . 'controller' . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . $this->config['name'] . '.php',
$controlStr
);
}
/**
* 构建模型
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function buildModel() {
$modelStr = '<?php' . PHP_EOL;
$modelStr .= '/**' . PHP_EOL;
$modelStr .= ' * 由ApiAdmin自动构建' . PHP_EOL;
$modelStr .= ' * @author apiadmin <apiadmin.org>' . PHP_EOL;
$modelStr .= ' */' . PHP_EOL;
$modelStr .= 'namespace app\model;' . PHP_EOL;
$modelStr .= 'class ' . $this->config['modelName'] . ' extends Base {' . PHP_EOL;
$modelStr .= '}' . PHP_EOL;
file_put_contents(
base_path() . 'model' . DIRECTORY_SEPARATOR . $this->config['modelName'] . '.php',
$modelStr
);
}
/**
* 构建表
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function createTable() {
$tableName = $this->unCamelize($this->config['modelName']);
$cmd = "CREATE TABLE `{$tableName}` (`id` int NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)) COMMENT='由ApiAdmin自动构建';";
Db::execute($cmd);
}
/**
* 构建菜单
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function buildMenu() {
$menus = [
[
'title' => '新增',
'fid' => $this->config['fid'],
'url' => "admin/{$this->config['name']}/add",
'auth' => 1,
'sort' => 0,
'show' => 1,
'icon' => '',
'level' => 3,
'component' => '',
'router' => '',
'log' => 1,
'permission' => 1,
'method' => 2
],
[
'title' => '编辑',
'fid' => $this->config['fid'],
'url' => "admin/{$this->config['name']}/edit",
'auth' => 1,
'sort' => 0,
'show' => 1,
'icon' => '',
'level' => 3,
'component' => '',
'router' => '',
'log' => 1,
'permission' => 1,
'method' => 2
],
[
'title' => '删除',
'fid' => $this->config['fid'],
'url' => "admin/{$this->config['name']}/del",
'auth' => 1,
'sort' => 0,
'show' => 1,
'icon' => '',
'level' => 3,
'component' => '',
'router' => '',
'log' => 1,
'permission' => 1,
'method' => 1
],
[
'title' => '列表',
'fid' => $this->config['fid'],
'url' => "admin/{$this->config['name']}/index",
'auth' => 1,
'sort' => 0,
'show' => 1,
'icon' => '',
'level' => 3,
'component' => '',
'router' => '',
'log' => 1,
'permission' => 1,
'method' => 1
]
];
(new AdminMenu())->insertAll($menus);
}
/**
* 构建路由
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private function buildRoute() {
RouterTool::buildAdminRouter();
}
}

22
app/util/DataType.php

@ -0,0 +1,22 @@
<?php
/**
* 数据类型维护
* 特别注意:这里的数据类型包含但不限于常规数据类型,可能会存在系统自己定义的数据类型
* @since 2017/03/01 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
class DataType {
const TYPE_INTEGER = 1;
const TYPE_STRING = 2;
const TYPE_ARRAY = 3;
const TYPE_FLOAT = 4;
const TYPE_BOOLEAN = 5;
const TYPE_FILE = 6;
const TYPE_ENUM = 7;
const TYPE_MOBILE = 8;
const TYPE_OBJECT = 9;
}

50
app/util/ReturnCode.php

@ -0,0 +1,50 @@
<?php
declare (strict_types=1);
/**
* 错误码统一维护
* @since 2017/02/28 创建
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
class ReturnCode {
const SUCCESS = 1;
const INVALID = -1;
const DB_SAVE_ERROR = -2;
const DB_READ_ERROR = -3;
const CACHE_SAVE_ERROR = -4;
const CACHE_READ_ERROR = -5;
const FILE_SAVE_ERROR = -6;
const LOGIN_ERROR = -7;
const NOT_EXISTS = -8;
const JSON_PARSE_FAIL = -9;
const TYPE_ERROR = -10;
const NUMBER_MATCH_ERROR = -11;
const EMPTY_PARAMS = -12;
const DATA_EXISTS = -13;
const AUTH_ERROR = -14;
const OTHER_LOGIN = -16;
const VERSION_INVALID = -17;
const CURL_ERROR = -18;
const RECORD_NOT_FOUND = -19; // 记录未找到
const DELETE_FAILED = -20; // 删除失败
const ADD_FAILED = -21; // 添加记录失败
const UPDATE_FAILED = -22; // 添加记录失败
const PARAM_INVALID = -995; // 参数无效
const ACCESS_TOKEN_TIMEOUT = -996;
const SESSION_TIMEOUT = -997;
const UNKNOWN = -998;
const EXCEPTION = -999;
public static function getConstants(): array {
$oClass = new \ReflectionClass(__CLASS__);
return $oClass->getConstants();
}
}

107
app/util/RouterTool.php

@ -0,0 +1,107 @@
<?php
declare (strict_types=1);
/**
*
* @since 2020-05-14
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
use app\model\AdminMenu;
use think\App;
class RouterTool {
/**
* 构建后端路由
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function buildAdminRouter(): void {
$methodArr = ['*', 'get', 'post', 'put', 'delete'];
$routePath = (new App())->getRootPath() . 'route' . DIRECTORY_SEPARATOR . 'app.php';
$bakPath = (new App())->getRootPath() . 'route' . DIRECTORY_SEPARATOR . 'app.bak';
if (file_exists($bakPath)) {
unlink($bakPath);
}
if (file_exists($routePath)) {
rename($routePath, $bakPath);
}
$context = '<?php' . PHP_EOL;
$context .= 'use think\facade\Route;' . PHP_EOL;
$context .= "Route::group('admin', function() {" . PHP_EOL;
$menus = (new AdminMenu())->select();
if ($menus) {
foreach ($menus as $menu) {
$menu = $menu->toArray();
$menuUrl = str_replace('admin/', '', $menu['url']);
if ($menu['url']) {
$context .= " Route::rule('{$menuUrl}', 'admin.{$menuUrl}', '"
. $methodArr[$menu['method']] . "')" . self::getAdminMiddleware($menu) . PHP_EOL;
}
}
}
$context .= " Route::miss('admin.Miss/index');" . PHP_EOL . "});" . PHP_EOL;
file_put_contents($routePath, $context);
}
/**
* 构建前端路由
* TODO::待算法优化
* @param $menus
* @return mixed
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function buildVueRouter(array &$menus): void {
foreach ($menus as $key => $menu) {
if (isset($menu['children'])) {
foreach ($menu['children'] as $cKey => $child) {
if (!isset($child['children'])) {
unset($menus[$key]['children'][$cKey]);
} else {
$menus[$key]['children'][$cKey]['children'] = [];
}
}
} else {
unset($menus[$key]);
}
}
foreach ($menus as $k => $m) {
if (isset($m['children']) && !empty($m['children'])) {
$menus[$k]['children'] = array_values($m['children']);
} else {
unset($menus[$k]);
}
}
}
/**
* 构建菜单权限细节
* @param $menu
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
private static function getAdminMiddleware(array $menu): string {
$middle = ['app\middleware\AdminResponse::class'];
if ($menu['log']) {
array_unshift($middle, 'app\middleware\AdminLog::class');
}
if ($menu['permission']) {
array_unshift($middle, 'app\middleware\AdminPermission::class');
}
if ($menu['auth']) {
array_unshift($middle, 'app\middleware\AdminAuth::class');
}
return '->middleware([' . implode(', ', $middle) . ']);';
}
}

185
app/util/StrRandom.php

@ -0,0 +1,185 @@
<?php
declare (strict_types=1);
/**
* 构建各类有意义的随机数
* @since 2018-08-07
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
class StrRandom {
/**
* 构建一个随机浮点数
* @param int $min 整数部分的最小值,默认值为-999999999
* @param int $max 整数部分的最大值,默认值为999999999
* @param int $dmin 小数部分位数的最小值,默认值为 0
* @param int $dmax 小数部分位数的最大值,默认值为 8
* @return float
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomFloat(int $min = -999999999, int $max = 999999999, int $dmin = 0, int $dmax = 8): float {
$rand = '';
$intNum = mt_rand($min, $max);
$floatLength = mt_rand($dmin, $dmax);
if ($floatLength > 1) {
$rand = Strs::randString($floatLength - 1, 1);
}
$floatEnd = mt_rand(1, 9);
return floatval($intNum . '.' . $rand . $floatEnd);
}
/**
* 获取随机的时间
* @param string $format PHP的时间日期格式化字符
* @return false|string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomDate(string $format = 'Y-m-d H:i:s'): string {
$timestamp = time() - mt_rand(0, 86400 * 3650);
return date($format, $timestamp);
}
/**
* 构建随机IP地址
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomIp(): string {
$ipLong = [
['607649792', '608174079'], // 36.56.0.0-36.63.255.255
['1038614528', '1039007743'], // 61.232.0.0-61.237.255.255
['1783627776', '1784676351'], // 106.80.0.0-106.95.255.255
['2035023872', '2035154943'], // 121.76.0.0-121.77.255.255
['2078801920', '2079064063'], // 123.232.0.0-123.235.255.255
['-1950089216', '-1948778497'], // 139.196.0.0-139.215.255.255
['-1425539072', '-1425014785'], // 171.8.0.0-171.15.255.255
['-1236271104', '-1235419137'], // 182.80.0.0-182.92.255.255
['-770113536', '-768606209'], // 210.25.0.0-210.47.255.255
['-569376768', '-564133889'], // 222.16.0.0-222.95.255.255
];
$randKey = mt_rand(0, 9);
return $ip = long2ip(mt_rand($ipLong[$randKey][0], $ipLong[$randKey][1]));
}
/**
* 随机生成一个 URL 协议
* @return mixed
* @author zhaoxiang <zhaoxiang051405@gmail','com>
*/
public static function randomProtocol(): string {
$proArr = [
'http',
'ftp',
'gopher',
'mailto',
'mid',
'cid',
'news',
'nntp',
'prospero',
'telnet',
'rlogin',
'tn3270',
'wais'
];
shuffle($proArr);
return $proArr[0];
}
/**
* 随机生成一个顶级域名
* @author zhaoxiang <zhaoxiang051405@gmail','com>
*/
public static function randomTld(): string {
$tldArr = [
'com', 'cn', 'xin', 'net', 'top', '在线',
'xyz', 'wang', 'shop', 'site', 'club', 'cc',
'fun', 'online', 'biz', 'red', 'link', 'ltd',
'mobi', 'info', 'org', 'edu', 'com.cn', 'net.cn',
'org.cn', 'gov.cn', 'name', 'vip', 'pro', 'work',
'tv', 'co', 'kim', 'group', 'tech', 'store', 'ren',
'ink', 'pub', 'live', 'wiki', 'design', '中文网',
'我爱你', '中国', '网址', '网店', '公司', '网络', '集团', 'app'
];
shuffle($tldArr);
return $tldArr[0];
}
/**
* 获取一个随机的域名
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomDomain(): string {
$len = mt_rand(6, 16);
return strtolower(Strs::randString($len)) . '.' . self::randomTld();
}
/**
* 随机生成一个URL
* @param string $protocol 协议名称,可以不用指定
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomUrl($protocol = ''): string {
$protocol = $protocol ? $protocol : self::randomProtocol();
return $protocol . '://' . self::randomDomain();
}
/**
* 随机生成一个邮箱地址
* @param string $domain 可以指定邮箱域名
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomEmail($domain = ''): string {
$len = mt_rand(6, 16);
$domain = $domain ? $domain : self::randomDomain();
return Strs::randString($len) . '@' . $domain;
}
public static function randomPhone(): string {
$prefixArr = [133, 153, 173, 177, 180, 181, 189, 199, 134, 135,
136, 137, 138, 139, 150, 151, 152, 157, 158, 159, 172, 178,
182, 183, 184, 187, 188, 198, 130, 131, 132, 155, 156, 166,
175, 176, 185, 186, 145, 147, 149, 170, 171];
shuffle($prefixArr);
return $prefixArr[0] . Strs::randString(8, 1);
}
/**
* 随机创建一个身份证号码
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function randomId(): string {
$prefixArr = [
11, 12, 13, 14, 15,
21, 22, 23,
31, 32, 33, 34, 35, 36, 37,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54,
61, 62, 63, 64, 65,
71, 81, 82
];
shuffle($prefixArr);
$suffixArr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'X'];
shuffle($suffixArr);
return $prefixArr[0] . '0000' . self::randomDate('Ymd') . Strs::randString(3, 1) . $suffixArr[0];
}
}

254
app/util/Strs.php

@ -0,0 +1,254 @@
<?php
declare (strict_types=1);
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\util;
class Strs {
/**
* 生成UUID 单机使用
* @access public
* @return string
*/
public static function uuid(): string {
$charId = md5(uniqid(strval(mt_rand()), true));
$hyphen = chr(45);
$uuid = chr(123)
. substr($charId, 0, 8) . $hyphen
. substr($charId, 8, 4) . $hyphen
. substr($charId, 12, 4) . $hyphen
. substr($charId, 16, 4) . $hyphen
. substr($charId, 20, 12)
. chr(125);
return $uuid;
}
/**
* 生成Guid主键
* @return Boolean
*/
public static function keyGen(): string {
return str_replace('-', '', substr(self::uuid(), 1, -1));
}
/**
* 检查字符串是否是UTF8编码
* @param string $string 字符串
* @return Boolean
*/
public static function isUtf8($string): bool {
$len = strlen($string);
for ($i = 0; $i < $len; $i++) {
$c = ord($string[$i]);
if ($c > 128) {
if (($c >= 254)) return false;
elseif ($c >= 252) $bits = 6;
elseif ($c >= 248) $bits = 5;
elseif ($c >= 240) $bits = 4;
elseif ($c >= 224) $bits = 3;
elseif ($c >= 192) $bits = 2;
else return false;
if (($i + $bits) > $len) return false;
while ($bits > 1) {
$i++;
$b = ord($string[$i]);
if ($b < 128 || $b > 191) return false;
$bits--;
}
}
}
return true;
}
/**
* 字符串截取,支持中文和其他编码
* @param string $str
* @param float $start
* @param int $length
* @param string $charset
* @param bool $suffix
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function mSubStr(string $str, int $length, float $start = 0, string $charset = "utf-8", bool $suffix = true): string {
if (function_exists("mb_substr"))
$slice = mb_substr($str, $start, $length, $charset);
elseif (function_exists('iconv_substr')) {
$slice = iconv_substr($str, $start, $length, $charset);
} else {
$re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
$re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
$re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
preg_match_all($re[$charset], $str, $match);
$slice = join("", array_slice($match[0], $start, $length));
}
return $suffix ? $slice . '...' : $slice;
}
/**
* 产生随机字串,可用来自动生成密码
* 默认长度6位 字母和数字混合 支持中文
* @param integer $len 长度
* @param int $type 字串类型
* 0 字母 1 数字 其它 混合
* @param string $addChars 额外字符
* @return string
*/
public static function randString(int $len = 6, int $type = 0, string $addChars = ''): string {
$str = '';
switch ($type) {
case 0:
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars;
break;
case 1:
$chars = str_repeat('0123456789', 3);
break;
case 2:
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
break;
case 3:
$chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
break;
case 4:
$chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借" . $addChars;
break;
default :
// 默认去掉了容易混淆的字符oOLl和数字01,要添加请使用addChars参数
$chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
break;
}
if ($len > 10) {//位数过长重复字符串一定次数
$chars = $type == 1 ? str_repeat($chars, $len) : str_repeat($chars, 5);
}
if ($type != 4) {
$chars = str_shuffle($chars);
$str = substr($chars, 0, $len);
} else {
// 中文随机字
for ($i = 0; $i < $len; $i++) {
$str .= self::msubstr($chars, 1, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 'utf-8', false);
}
}
return $str;
}
/**
* 生成一定数量的随机数,并且不重复
* @param integer $number 数量
* @param integer $length 长度
* @param integer $mode 字串类型
* 0 字母 1 数字 其它 混合
* @return array
*/
public static function buildCountRand(int $number, int $length = 4, int $mode = 1): array {
if ($mode == 1 && $length < strlen($number)) {
//不足以生成一定数量的不重复数字
return [];
}
$rand = [];
for ($i = 0; $i < $number; $i++) {
$rand[] = self::randString($length, $mode);
}
$unique = array_unique($rand);
if (count($unique) == count($rand)) {
return $rand;
}
$count = count($rand) - count($unique);
for ($i = 0; $i < $count * 3; $i++) {
$rand[] = self::randString($length, $mode);
}
$rand = array_slice(array_unique($rand), 0, $number);
return $rand;
}
/**
* 带格式生成随机字符 支持批量生成
* 但可能存在重复
* @param string $format 字符格式
* # 表示数字 * 表示字母和数字 $ 表示字母
* @param integer $number 生成数量
* @return array
*/
public static function buildFormatRand(string $format, int $number = 1): array {
$str = [];
$length = strlen($format);
for ($j = 0; $j < $number; $j++) {
$strTemp = '';
for ($i = 0; $i < $length; $i++) {
$char = substr($format, $i, 1);
switch ($char) {
case "*"://字母和数字混合
$strTemp .= self::randString(1);
break;
case "#"://数字
$strTemp .= self::randString(1, 1);
break;
case "$"://大写字母
$strTemp .= self::randString(1, 2);
break;
default://其他格式均不转换
$strTemp .= $char;
break;
}
}
$str[] = $strTemp;
}
return $str;
}
/**
* 获取一定范围内的随机数字 位数不足补零
* @param integer $min 最小值
* @param integer $max 最大值
* @return string
*/
public static function randNumber(int $min, int $max): string {
return sprintf("%0" . strlen($max) . "d", mt_rand($min, $max));
}
// 自动转换字符集 支持数组转换
public static function autoCharset(string $string, string $from = 'gbk', string $to = 'utf-8'): string {
$from = strtoupper($from) == 'UTF8' ? 'utf-8' : $from;
$to = strtoupper($to) == 'UTF8' ? 'utf-8' : $to;
if (strtoupper($from) === strtoupper($to) || empty($string) || (is_scalar($string) && !is_string($string))) {
//如果编码相同或者非字符串标量则不转换
return $string;
}
if (is_string($string)) {
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($string, $to, $from);
} elseif (function_exists('iconv')) {
return iconv($from, $to, $string);
} else {
return $string;
}
} elseif (is_array($string)) {
foreach ($string as $key => $val) {
$_key = self::autoCharset($key, $from, $to);
$string[$_key] = self::autoCharset($val, $from, $to);
if ($key != $_key)
unset($string[$key]);
}
return $string;
} else {
return $string;
}
}
}

192
app/util/Tools.php

@ -0,0 +1,192 @@
<?php
declare (strict_types=1);
/**
*
* @since 2017-11-01
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
namespace app\util;
class Tools {
/**
* 获取相对时间
* @param int $timestamp
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function getDate(int $timestamp): string {
$now = time();
$diff = $now - $timestamp;
if ($diff <= 60) {
return $diff . '秒前';
} elseif ($diff <= 3600) {
return floor($diff / 60) . '分钟前';
} elseif ($diff <= 86400) {
return floor($diff / 3600) . '小时前';
} elseif ($diff <= 2592000) {
return floor($diff / 86400) . '天前';
} else {
return '一个月前';
}
}
/**
* 二次封装的密码加密
* @param $str
* @param string $auth_key
* @return string
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function userMd5(string $str, string $auth_key = ''): string {
if (!$auth_key) {
$auth_key = config('apiadmin.AUTH_KEY');
}
return '' === $str ? '' : md5(sha1($str) . $auth_key);
}
/**
* 判断当前用户是否是超级管理员
* @param int $uid
* @return bool
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function isAdministrator(int $uid = 0): bool {
if (!empty($uid)) {
$adminConf = config('apiadmin.USER_ADMINISTRATOR');
if (is_array($adminConf)) {
if (is_array($uid)) {
$m = array_intersect($adminConf, $uid);
if (count($m)) {
return true;
}
} else {
if (in_array($uid, $adminConf)) {
return true;
}
}
} else {
if (is_array($uid)) {
if (in_array($adminConf, $uid)) {
return true;
}
} else {
if ($uid == $adminConf) {
return true;
}
}
}
}
return false;
}
/**
* 将查询的二维对象转换成二维数组
* @param $res
* @param string $key 允许指定索引值
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function buildArrFromObj($res, string $key = ''): array {
$arr = [];
foreach ($res as $value) {
$value = $value->toArray();
if ($key) {
$arr[$value[$key]] = $value;
} else {
$arr[] = $value;
}
}
return $arr;
}
/**
* 将二维数组变成指定key
* @param $array
* @param string $keyName
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function buildArrByNewKey($array, string $keyName = 'id'): array {
$list = [];
foreach ($array as $item) {
$list[$item[$keyName]] = $item;
}
return $list;
}
/**
* 把返回的数据集转换成Tree
* @param $list
* @param string $pk
* @param string $pid
* @param string $child
* @param string $root
* @return array
*/
public static function listToTree(
array $list,
string $pk = 'id',
string $pid = 'fid',
string $child = 'children',
string $root = '0'
): array {
$tree = [];
if (is_array($list)) {
$refer = [];
foreach ($list as $key => $data) {
$refer[$data[$pk]] = &$list[$key];
}
foreach ($list as $key => $data) {
$parentId = $data[$pid];
if ($root == $parentId) {
$tree[] = &$list[$key];
} else {
if (isset($refer[$parentId])) {
$parent = &$refer[$parentId];
$parent[$child][] = &$list[$key];
}
}
}
}
return $tree;
}
/**
* 将层级数组遍历成一维数组
* @param array $list
* @param int $lv
* @param string $title
* @return array
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public static function formatTree(array $list, int $lv = 0, string $title = 'title'): array {
$formatTree = [];
foreach ($list as $key => $val) {
$title_prefix = '';
for ($i = 0; $i < $lv; $i++) {
$title_prefix .= "|---";
}
$val['lv'] = $lv;
$val['namePrefix'] = $lv == 0 ? '' : $title_prefix;
$val['showName'] = $lv == 0 ? $val[$title] : $title_prefix . $val[$title];
if (!array_key_exists('children', $val)) {
array_push($formatTree, $val);
} else {
$child = $val['children'];
unset($val['children']);
array_push($formatTree, $val);
$middle = self::formatTree($child, $lv + 1, $title); //进行下一层递归
$formatTree = array_merge($formatTree, $middle);
}
}
return $formatTree;
}
}

46
composer.json

@ -0,0 +1,46 @@
{
"name": "apiadmin/apiadmin",
"description": "Just For Api",
"type": "project",
"keywords": [
"framework",
"thinkphp",
"api",
"apiadmin"
],
"homepage": "http://www.apiadmin.org/",
"license": "Apache-2.0",
"authors": [
{
"name": "zhaoxiang",
"email": "zhaoxiang051405@gmail.com"
}
],
"require": {
"php": ">=7.2.5",
"topthink/framework": "^6.0",
"topthink/think-orm": "^2.0",
"topthink/think-migration": "^3.0"
},
"require-dev": {
"symfony/var-dumper": "^4.2",
"topthink/think-trace":"^1.0"
},
"autoload": {
"psr-4": {
"app\\": "app"
},
"psr-0": {
"": "extend/"
}
},
"config": {
"preferred-install": "dist"
},
"scripts": {
"post-autoload-dump": [
"@php think service:discover",
"@php think vendor:publish"
]
}
}

38
config/apiadmin.php

@ -0,0 +1,38 @@
<?php
// +---------------------------------------------------------------------------------
// | ApiAdmin [ JUST FOR API ]
// +---------------------------------------------------------------------------------
// | Copyright (c) 2017~2020 https://www.apiadmin.org/ All rights reserved.
// +---------------------------------------------------------------------------------
// | Licensed ( https://gitee.com/apiadmin/ApiAdmin/blob/master/LICENSE.txt )
// +---------------------------------------------------------------------------------
// | Author: zhaoxiang <zhaoxiang051405@gmail.com>
// +---------------------------------------------------------------------------------
return [
'APP_VERSION' => '5.0',
'APP_NAME' => 'ApiAdmin',
//鉴权相关
'USER_ADMINISTRATOR' => [1],
//安全秘钥
'AUTH_KEY' => '84719e84-ab81-1db8-d8ed-af59f2cc5afb',
//后台登录状态维持时间[目前只有登录和解锁会重置登录时间]
'ONLINE_TIME' => 86400,
//AccessToken失效时间
'ACCESS_TOKEN_TIME_OUT' => 86400,
'COMPANY_NAME' => 'ApiAdmin开发维护团队',
//跨域配置
'CROSS_DOMAIN' => [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST,PUT,GET,DELETE',
'Access-Control-Allow-Headers' => 'Version, Access-Token, User-Token, Api-Auth, User-Agent, Keep-Alive, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With',
'Access-Control-Allow-Credentials' => 'true'
],
//后台列表默认一页显示数量
'ADMIN_LIST_DEFAULT' => 20,
];

32
config/app.php

@ -0,0 +1,32 @@
<?php
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
return [
// 应用地址
'app_host' => env('app.host', ''),
// 应用的命名空间
'app_namespace' => '',
// 是否启用路由
'with_route' => true,
// 默认应用
'default_app' => 'index',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
// 应用映射(自动多应用模式有效)
'app_map' => [],
// 域名绑定(自动多应用模式有效)
'domain_bind' => [],
// 禁止URL访问的应用列表(自动多应用模式有效)
'deny_app_list' => [],
// 异常页面的模板文件
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg' => true,
];

39
config/cache.php

@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
// 默认缓存驱动
'default' => env('cache.driver', 'redis'),
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => '',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => []
],
'redis' => [
// 驱动方式
'type' => 'Redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'prefix' => '',
'serialize' => []
]
// 更多的缓存连接
],
];

13
config/console.php

@ -0,0 +1,13 @@
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
// 指令定义
'commands' => [
'apiadmin:adminRouter' => 'app\command\FreshAdminRouter',
'apiadmin:install' => 'app\command\Install',
'apiadmin:test' => 'app\command\ApiAdmin',
'apiadmin:autoBuild' => 'app\command\AutoBuildFile'
],
];

18
config/cookie.php

@ -0,0 +1,18 @@
<?php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
return [
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
];

62
config/database.php

@ -0,0 +1,62 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => env('database.driver', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
// true为自动识别类型 false关闭
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', '127.0.0.1'),
// 数据库名
'database' => env('database.database', ''),
// 用户名
'username' => env('database.username', 'root'),
// 密码
'password' => env('database.password', ''),
// 端口
'hostport' => env('database.hostport', '3306'),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => env('database.charset', 'utf8'),
// 数据库表前缀
'prefix' => env('database.prefix', ''),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('app_debug', true),
// 开启字段缓存
'fields_cache' => false,
// 字段缓存路径
'schema_cache_path' => app()->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR,
],
// 更多的数据库配置信息
],
];

24
config/filesystem.php

@ -0,0 +1,24 @@
<?php
return [
// 默认磁盘
'default' => env('filesystem.driver', 'local'),
// 磁盘列表
'disks' => [
'local' => [
'type' => 'local',
'root' => app()->getRuntimePath() . 'storage',
],
'public' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'public/storage',
// 磁盘路径对应的外部URL路径
'url' => '/storage',
// 可见性
'visibility' => 'public',
],
// 更多的磁盘配置信息
],
];

25
config/lang.php

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | 多语言设置
// +----------------------------------------------------------------------
return [
// 默认语言
'default_lang' => env('lang.default_lang', 'zh-cn'),
// 允许的语言列表
'allow_lang_list' => [],
// 多语言自动侦测变量名
'detect_var' => 'lang',
// 是否使用Cookie记录
'use_cookie' => true,
// 多语言cookie变量
'cookie_var' => 'think_lang',
// 扩展语言包
'extend_list' => [],
// Accept-Language转义为对应语言包名称
'accept_language' => [
'zh-hans-cn' => 'zh-cn',
],
// 是否支持语言分组
'allow_group' => false,
];

45
config/log.php

@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => env('log.channel', 'file'),
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
],
];

8
config/middleware.php

@ -0,0 +1,8 @@
<?php
// 中间件配置
return [
// 别名或分组
'alias' => [],
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
'priority' => [],
];

45
config/route.php

@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// pathinfo分隔符
'pathinfo_depr' => '/',
// URL伪静态后缀
'url_html_suffix' => 'html',
// URL普通方式参数 用于自动生成
'url_common_param' => true,
// 是否开启路由延迟解析
'url_lazy_route' => false,
// 是否强制使用路由
'url_route_must' => false,
// 合并路由规则
'route_rule_merge' => false,
// 路由是否完全匹配
'route_complete_match' => false,
// 访问控制器层名称
'controller_layer' => 'controller',
// 空控制器名
'empty_controller' => 'Error',
// 是否使用控制器后缀
'controller_suffix' => false,
// 默认的路由变量规则
'default_route_pattern' => '[\w\.]+',
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
'request_cache_key' => false,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则
'request_cache_except' => [],
// 默认控制器名
'default_controller' => 'api.Miss',
// 默认操作名
'default_action' => 'index',
// 操作方法后缀
'action_suffix' => '',
// 默认JSONP格式返回的处理方法
'default_jsonp_handler' => 'jsonpReturn',
// 默认JSONP处理方法
'var_jsonp_handler' => 'callback',
];

19
config/session.php

@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
return [
// session name
'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// 驱动方式 支持file cache
'type' => 'file',
// 存储连接标识 当type使用cache的时候有效
'store' => null,
// 过期时间
'expire' => 1440,
// 前缀
'prefix' => '',
];

10
config/trace.php

@ -0,0 +1,10 @@
<?php
// +----------------------------------------------------------------------
// | Trace设置 开启调试模式后有效
// +----------------------------------------------------------------------
return [
// 内置Html和Console两种方式 支持扩展
'type' => 'Html',
// 读取的日志通道名
'channel' => '',
];

25
config/view.php

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
return [
// 模板引擎类型使用Think
'type' => 'Think',
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
'auto_rule' => 1,
// 模板目录名
'view_dir_name' => 'view',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DIRECTORY_SEPARATOR,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
];

85
database/migrations/20190425073802_admin_app.php

@ -0,0 +1,85 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminApp extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_app` (
* `id` int(11) unsigned NOT NULL,
* `app_id` varchar(50) NOT NULL DEFAULT '' COMMENT '应用id',
* `app_secret` varchar(50) NOT NULL DEFAULT '' COMMENT '应用密码',
* `app_name` varchar(50) NOT NULL DEFAULT '' COMMENT '应用名称',
* `app_status` int(2) NOT NULL DEFAULT '1' COMMENT '应用状态:0表示禁用,1表示启用',
* `app_info` text COMMENT '应用说明',
* `app_api` text COMMENT '当前应用允许请求的全部API接口',
* `app_group` varchar(128) NOT NULL DEFAULT 'default' COMMENT '当前应用所属的应用组唯一标识',
* `app_addTime` int(11) NOT NULL DEFAULT '0' COMMENT '应用创建时间',
* `app_api_show` text COMMENT '前台样式显示所需数据格式',
* PRIMARY KEY (`id`),
* UNIQUE KEY `app_id` (`app_id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='appId和appSecret表';
*/
public function change() {
$table = $this->table('admin_app', [
'comment' => 'appId和appSecret表',
])->setCollation('utf8mb4_general_ci');
$table->addColumn('app_id', 'string', [
'limit' => 50,
'default' => '',
'comment' => '应用id'
])->addColumn('app_secret', 'string', [
'limit' => 50,
'default' => '',
'comment' => '应用密码'
])->addColumn('app_name', 'string', [
'limit' => 50,
'default' => '',
'comment' => '应用名称'
])->addColumn('app_status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '应用状态:0表示禁用,1表示启用'
])->addColumn('app_info', 'text', [
'comment' => '应用说明',
'null' => true
])->addColumn('app_api', 'text', [
'comment' => '当前应用允许请求的全部API接口',
'null' => true
])->addColumn('app_group', 'string', [
'limit' => 128,
'default' => 'default',
'comment' => '当前应用所属的应用组唯一标识'
])->addColumn('app_add_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '应用创建时间'
])->addColumn('app_api_show', 'text', [
'comment' => '前台样式显示所需数据格式',
'null' => true
])->addIndex(['app_id'], ['unique' => true])->create();
}
}

61
database/migrations/20190425094427_admin_app_group.php

@ -0,0 +1,61 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminAppGroup extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_app_group` (
* `id` int(11) unsigned NOT NULL,
* `name` varchar(128) NOT NULL DEFAULT '' COMMENT '组名称',
* `description` text COMMENT '组说明',
* `status` int(2) NOT NULL DEFAULT '1' COMMENT '组状态:0表示禁用,1表示启用',
* `hash` varchar(128) NOT NULL DEFAULT '' COMMENT '组标识',
* PRIMARY KEY (`id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用组,目前只做管理使用,没有实际权限控制';
*/
public function change() {
$table = $this->table('admin_app_group', [
'comment' => '应用组,目前只做管理使用,没有实际权限控制'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('name', 'string', [
'limit' => 128,
'default' => '',
'comment' => '组名称'
])->addColumn('description', 'text', [
'comment' => '组说明',
'null' => true
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '组状态:0表示禁用,1表示启用'
])->addColumn('hash', 'string', [
'limit' => 128,
'default' => '',
'comment' => '组标识'
])->create();
}
}

56
database/migrations/20190508070533_admin_auth_group.php

@ -0,0 +1,56 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminAuthGroup extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_auth_group` (
* `id` int(11) unsigned NOT NULL,
* `name` varchar(50) NOT NULL DEFAULT '' COMMENT '组名称',
* `description` text COMMENT '组描述',
* `status` int(2) NOT NULL DEFAULT '1' COMMENT '组状态:为1正常,为0禁用',
* PRIMARY KEY (`id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限组';
*/
public function change() {
$table = $this->table('admin_auth_group', [
'comment' => '权限组'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('name', 'string', [
'limit' => 50,
'default' => '',
'comment' => '组名称'
])->addColumn('description', 'text', [
'comment' => '组描述',
'null' => true
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '组状态:为1正常,为0禁用'
])->create();
}
}

54
database/migrations/20190508100337_admin_auth_group_access.php

@ -0,0 +1,54 @@
<?php
use think\migration\Migrator;
class AdminAuthGroupAccess extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_auth_group_access` (
* `id` int(11) unsigned NOT NULL,
* `uid` int(11) unsigned NOT NULL DEFAULT '0',
* `groupId` varchar(255) NOT NULL DEFAULT '',
* PRIMARY KEY (`id`),
* KEY `uid` (`uid`),
* KEY `groupId` (`groupId`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和组的对应关系';
*/
public function change() {
$table = $this->table('admin_auth_group_access', [
'comment' => '用户和组的对应关系'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('uid', 'integer', [
'limit' => 11,
'default' => 0,
'signed' => false,
'comment' => ''
])->addColumn('group_id', 'string', [
'limit' => 255,
'default' => '',
'comment' => ''
])->addIndex(['uid'])->addIndex(['group_id'])->create();
}
}

64
database/migrations/20190508101122_admin_auth_rule.php

@ -0,0 +1,64 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminAuthRule extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_auth_rule` (
* `id` int(11) unsigned NOT NULL,
* `url` varchar(80) NOT NULL DEFAULT '' COMMENT '规则唯一标识',
* `groupId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限所属组的ID',
* `auth` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限数值',
* `status` int(1) NOT NULL DEFAULT '1' COMMENT '状态:为1正常,为0禁用',
* PRIMARY KEY (`id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限细节';
*/
public function change() {
$table = $this->table('admin_auth_rule', [
'comment' => '权限细节'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('url', 'string', [
'limit' => 80,
'default' => '',
'comment' => '规则唯一标识'
])->addColumn('group_id', 'integer', [
'limit' => 11,
'default' => 0,
'signed' => false,
'comment' => '权限所属组的ID'
])->addColumn('auth', 'integer', [
'limit' => 11,
'default' => 0,
'signed' => false,
'comment' => '权限数值'
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '状态:为1正常,为0禁用'
])->create();
}
}

88
database/migrations/20190508152801_admin_fields.php

@ -0,0 +1,88 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminFields extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_fields` (
* `id` int(11) unsigned NOT NULL,
* `field_name` varchar(50) NOT NULL DEFAULT '' COMMENT '字段名称',
* `hash` varchar(50) NOT NULL DEFAULT '' COMMENT '权限所属组的ID',
* `data_type` int(2) NOT NULL DEFAULT '0' COMMENT '数据类型,来源于DataType类库',
* `default` varchar(500) NOT NULL DEFAULT '' COMMENT '默认值',
* `is_must` int(2) NOT NULL DEFAULT '0' COMMENT '是否必须 0为不必须,1为必须',
* `range` varchar(500) NOT NULL DEFAULT '' COMMENT '范围,Json字符串,根据数据类型有不一样的含义',
* `info` varchar(500) NOT NULL DEFAULT '' COMMENT '字段说明',
* `type` int(2) NOT NULL DEFAULT '0' COMMENT '字段用处:0为request,1为response',
* `show_name` varchar(50) NOT NULL DEFAULT '' COMMENT 'wiki显示用字段',
* PRIMARY KEY (`id`),
* KEY `hash` (`hash`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用于保存各个API的字段规则';
*/
public function change() {
$table = $this->table('admin_fields', [
'comment' => '用于保存各个API的字段规则'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('field_name', 'string', [
'limit' => 50,
'default' => '',
'comment' => '字段名称'
])->addColumn('hash', 'string', [
'limit' => 50,
'default' => '',
'comment' => '权限所属组的ID'
])->addColumn('data_type', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '数据类型,来源于DataType类库'
])->addColumn('default', 'string', [
'limit' => 500,
'default' => '',
'comment' => '默认值'
])->addColumn('is_must', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '是否必须 0为不必须,1为必须'
])->addColumn('range', 'string', [
'limit' => 500,
'default' => '',
'comment' => '范围,Json字符串,根据数据类型有不一样的含义'
])->addColumn('info', 'string', [
'limit' => 500,
'default' => '',
'comment' => '字段说明'
])->addColumn('type', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '字段用处:0为request,1为response'
])->addColumn('show_name', 'string', [
'limit' => 50,
'default' => '',
'comment' => 'wiki显示用字段'
])->addIndex(['hash'])->create();
}
}

81
database/migrations/20190508153800_admin_group.php

@ -0,0 +1,81 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminGroup extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_group` (
* `id` int(11) unsigned NOT NULL,
* `name` varchar(128) NOT NULL DEFAULT '' COMMENT '组名称',
* `description` text COMMENT '组说明',
* `status` int(1) NOT NULL DEFAULT '1' COMMENT '状态:为1正常,为0禁用',
* `hash` varchar(128) NOT NULL DEFAULT '' COMMENT '组标识',
* `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
* `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间',
* `image` varchar(256) DEFAULT NULL COMMENT '分组封面图',
* `hot` int(11) NOT NULL DEFAULT '0' COMMENT '分组热度',
* PRIMARY KEY (`id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='接口组管理';
*/
public function change() {
$table = $this->table('admin_group', [
'comment' => '接口组管理'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('name', 'string', [
'limit' => 128,
'default' => '',
'comment' => '组名称'
])->addColumn('description', 'text', [
'comment' => '组说明',
'null' => true
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '状态:为1正常,为0禁用'
])->addColumn('hash', 'string', [
'limit' => 128,
'default' => '',
'comment' => '组标识'
])->addColumn('create_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '创建时间'
])->addColumn('update_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '修改时间'
])->addColumn('image', 'string', [
'limit' => 256,
'null' => true,
'comment' => '分组封面图'
])->addColumn('hot', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '分组热度'
])->create();
}
}

88
database/migrations/20190513065521_admin_list.php

@ -0,0 +1,88 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminList extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_list` (
* `id` int(11) unsigned NOT NULL,
* `api_class` varchar(50) NOT NULL DEFAULT '' COMMENT 'api索引,保存了类和方法',
* `hash` varchar(50) NOT NULL DEFAULT '' COMMENT 'api唯一标识',
* `access_token` int(2) NOT NULL DEFAULT '1' COMMENT '是否需要认证AccessToken 1:需要,0:不需要',
* `need_login` int(2) NOT NULL DEFAULT '1' COMMENT '是否需要认证用户token 1:需要 0:不需要',
* `status` int(2) NOT NULL DEFAULT '1' COMMENT 'API状态:0表示禁用,1表示启用',
* `method` int(2) NOT NULL DEFAULT '2' COMMENT '请求方式0:不限1:Post,2:Get',
* `info` varchar(500) NOT NULL DEFAULT '' COMMENT 'api中文说明',
* `is_test` int(2) NOT NULL DEFAULT '0' COMMENT '是否是测试模式:0:生产模式,1:测试模式',
* `return_str` text COMMENT '返回数据示例',
* `group_hash` varchar(64) NOT NULL DEFAULT 'default' COMMENT '当前接口所属的接口分组',
* PRIMARY KEY (`id`),
* KEY `hash` (`hash`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用于维护接口信息';
*/
public function change() {
$table = $this->table('admin_list', [
'comment' => '用于维护接口信息'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('api_class', 'string', [
'limit' => 50,
'default' => '',
'comment' => 'api索引,保存了类和方法'
])->addColumn('hash', 'string', [
'limit' => 50,
'default' => '',
'comment' => 'api唯一标识'
])->addColumn('access_token', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => '认证方式 1:复杂认证,0:简易认证'
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 1,
'comment' => 'API状态:0表示禁用,1表示启用'
])->addColumn('method', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 2,
'comment' => '请求方式0:不限1:Post,2:Get'
])->addColumn('info', 'string', [
'limit' => 500,
'default' => '',
'comment' => 'api中文说明'
])->addColumn('is_test', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '是否是测试模式:0:生产模式,1:测试模式'
])->addColumn('return_str', 'text', [
'null' => true,
'comment' => '返回数据示例'
])->addColumn('group_hash', 'string', [
'limit' => 64,
'default' => 'default',
'comment' => '当前接口所属的接口分组'
])->addIndex(['hash'])->create();
}
}

82
database/migrations/20190513070628_admin_menu.php

@ -0,0 +1,82 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminMenu extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_menu` (
* `id` int(11) unsigned NOT NULL,
* `name` varchar(50) NOT NULL DEFAULT '' COMMENT '菜单名',
* `fid` int(11) NOT NULL DEFAULT '0' COMMENT '父级菜单ID',
* `url` varchar(50) NOT NULL DEFAULT '' COMMENT '链接',
* `auth` int(2) NOT NULL DEFAULT '0' COMMENT '访客权限',
* `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
* `hide` int(2) NOT NULL DEFAULT '0' COMMENT '是否显示',
* `icon` varchar(50) NOT NULL DEFAULT '' COMMENT '菜单图标',
* `level` int(2) NOT NULL DEFAULT '0' COMMENT '菜单认证等级',
* PRIMARY KEY (`id`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='目录信息';
*/
public function change() {
$table = $this->table('admin_menu', [
'comment' => '目录信息'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('name', 'string', [
'limit' => 50,
'default' => '',
'comment' => '菜单名'
])->addColumn('fid', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '父级菜单ID'
])->addColumn('url', 'string', [
'limit' => 50,
'default' => '',
'comment' => '链接'
])->addColumn('auth', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '访客权限'
])->addColumn('sort', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '排序'
])->addColumn('hide', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '是否显示'
])->addColumn('icon', 'string', [
'limit' => 50,
'default' => '',
'comment' => '菜单图标'
])->addColumn('level', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '菜单认证等级'
])->create();
}
}

84
database/migrations/20190513081034_admin_user.php

@ -0,0 +1,84 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminUser extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_user` (
* `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
* `username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
* `nickname` varchar(64) NOT NULL DEFAULT '' COMMENT '用户昵称',
* `password` char(32) NOT NULL DEFAULT '' COMMENT '用户密码',
* `create_time` int(10) NOT NULL DEFAULT '0' COMMENT '注册时间',
* `create_ip` bigint(11) NOT NULL COMMENT '注册IP',
* `update_time` int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
* `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '账号状态 0封号 1正常',
* `openid` varchar(100) DEFAULT NULL COMMENT '三方登录唯一ID',
* PRIMARY KEY (`id`),
* KEY `create_time` (`create_time`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员认证信息';
*/
public function change() {
$table = $this->table('admin_user', [
'comment' => '管理员认证信息'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('username', 'string', [
'limit' => 64,
'default' => '',
'comment' => '用户名'
])->addColumn('nickname', 'string', [
'limit' => 64,
'default' => '',
'comment' => '用户昵称'
])->addColumn('password', 'char', [
'limit' => 32,
'default' => '',
'comment' => '用户密码'
])->addColumn('create_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '注册时间'
])->addColumn('create_ip', 'integer', [
'limit' => MysqlAdapter::INT_BIG,
'default' => 0,
'comment' => '注册IP'
])->addColumn('update_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '更新时间'
])->addColumn('status', 'integer', [
'limit' => MysqlAdapter::INT_TINY,
'default' => 0,
'comment' => '账号状态 0封号 1正常'
])->addColumn('openid', 'string', [
'limit' => 100,
'null' => true,
'default' => '',
'comment' => '三方登录唯一ID'
])->addIndex(['create_time'])->create();
}
}

71
database/migrations/20190513082503_admin_user_action.php

@ -0,0 +1,71 @@
<?php
use think\migration\Migrator;
class AdminUserAction extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_user_action` (
* `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
* `action_name` varchar(50) NOT NULL DEFAULT '' COMMENT '行为名称',
* `uid` int(11) NOT NULL DEFAULT '0' COMMENT '操作用户ID',
* `nickname` varchar(50) NOT NULL DEFAULT '' COMMENT '用户昵称',
* `add_time` int(11) NOT NULL DEFAULT '0' COMMENT '操作时间',
* `data` text COMMENT '用户提交的数据',
* `url` varchar(200) NOT NULL DEFAULT '' COMMENT '操作URL',
* PRIMARY KEY (`id`),
* KEY `uid` (`uid`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户操作日志';
*/
public function change() {
$table = $this->table('admin_user_action', [
'comment' => '用户操作日志'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('action_name', 'string', [
'limit' => 50,
'default' => '',
'comment' => '行为名称'
])->addColumn('uid', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '操作用户ID'
])->addColumn('nickname', 'string', [
'limit' => 50,
'default' => '',
'comment' => '用户昵称'
])->addColumn('add_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '操作时间'
])->addColumn('data', 'text', [
'null' => true,
'comment' => '用户提交的数据'
])->addColumn('url', 'string', [
'limit' => 200,
'default' => 0,
'comment' => '操作URL'
])->addIndex(['uid'])->create();
}
}

67
database/migrations/20190513085755_admin_user_data.php

@ -0,0 +1,67 @@
<?php
use think\migration\Migrator;
use Phinx\Db\Adapter\MysqlAdapter;
class AdminUserData extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/**
* CREATE TABLE `admin_user_data` (
* `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
* `login_times` int(11) NOT NULL DEFAULT '0' COMMENT '账号登录次数',
* `last_login_ip` bigint(11) NOT NULL DEFAULT '0' COMMENT '最后登录IP',
* `last_login_time` int(11) NOT NULL DEFAULT '0' COMMENT '最后登录时间',
* `uid` int(11) NOT NULL DEFAULT '' COMMENT '用户ID',
* `head_img` text COMMENT '用户头像',
* PRIMARY KEY (`id`)
* KEY `uid` (`uid`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员数据表';
*/
public function change() {
$table = $this->table('admin_user_data', [
'comment' => '管理员数据表'
])->setCollation('utf8mb4_general_ci');
$table->addColumn('login_times', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '账号登录次数'
])->addColumn('last_login_ip', 'integer', [
'limit' => MysqlAdapter::INT_BIG,
'default' => 0,
'comment' => '最后登录IP'
])->addColumn('last_login_time', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '最后登录时间'
])->addColumn('uid', 'integer', [
'limit' => 11,
'default' => 0,
'comment' => '用户ID'
])->addColumn('head_img', 'text', [
'null' => true,
'comment' => '用户头像'
])->addIndex(['uid'])->create();
}
}

718
database/migrations/20190513155519_ini_admin_menu.php

@ -0,0 +1,718 @@
<?php
use think\migration\Migrator;
class IniAdminMenu extends Migrator {
/**
* 初始化数据
* @author zhaoxiang <zhaoxiang051405@gmail.com>
*/
public function up() {
$data = [
[
'id' => 1,
'name' => '用户登录',
'fid' => 0,
'url' => 'admin/Login/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 2,
'name' => '用户登出',
'fid' => 0,
'url' => 'admin/Login/logout',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 3,
'name' => '系统管理',
'fid' => 0,
'url' => '',
'auth' => 0,
'sort' => 1,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 4,
'name' => '菜单维护',
'fid' => 3,
'url' => '',
'auth' => 0,
'sort' => 1,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 5,
'name' => '菜单状态修改',
'fid' => 4,
'url' => 'admin/Menu/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 6,
'name' => '新增菜单',
'fid' => 4,
'url' => 'admin/Menu/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 7,
'name' => '编辑菜单',
'fid' => 4,
'url' => 'admin/Menu/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 8,
'name' => '菜单删除',
'fid' => 4,
'url' => 'admin/Menu/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 9,
'name' => '用户管理',
'fid' => 3,
'url' => '',
'auth' => 0,
'sort' => 2,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 10,
'name' => '获取当前组的全部用户',
'fid' => 9,
'url' => 'admin/User/getUsers',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 11,
'name' => '用户状态修改',
'fid' => 9,
'url' => 'admin/User/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 12,
'name' => '新增用户',
'fid' => 9,
'url' => 'admin/User/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 13,
'name' => '用户编辑',
'fid' => 9,
'url' => 'admin/User/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 14,
'name' => '用户删除',
'fid' => 9,
'url' => 'admin/User/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 15,
'name' => '权限管理',
'fid' => 3,
'url' => '',
'auth' => 0,
'sort' => 3,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 16,
'name' => '权限组状态编辑',
'fid' => 15,
'url' => 'admin/Auth/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 17,
'name' => '从指定组中删除指定用户',
'fid' => 15,
'url' => 'admin/Auth/delMember',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 18,
'name' => '新增权限组',
'fid' => 15,
'url' => 'admin/Auth/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 19,
'name' => '权限组编辑',
'fid' => 15,
'url' => 'admin/Auth/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 20,
'name' => '删除权限组',
'fid' => 15,
'url' => 'admin/Auth/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 21,
'name' => '获取全部已开放的可选组',
'fid' => 15,
'url' => 'admin/Auth/getGroups',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 22,
'name' => '获取组所有的权限列表',
'fid' => 15,
'url' => 'admin/Auth/getRuleList',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 23,
'name' => '应用接入',
'fid' => 0,
'url' => '',
'auth' => 0,
'sort' => 2,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 24,
'name' => '应用管理',
'fid' => 23,
'url' => '',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 25,
'name' => '应用状态编辑',
'fid' => 24,
'url' => 'admin/App/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 26,
'name' => '获取AppId,AppSecret,接口列表,应用接口权限细节',
'fid' => 24,
'url' => 'admin/App/getAppInfo',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 27,
'name' => '新增应用',
'fid' => 24,
'url' => 'admin/App/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 28,
'name' => '编辑应用',
'fid' => 24,
'url' => 'admin/App/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 29,
'name' => '删除应用',
'fid' => 24,
'url' => 'admin/App/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 30,
'name' => '接口管理',
'fid' => 0,
'url' => '',
'auth' => 0,
'sort' => 3,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 31,
'name' => '接口维护',
'fid' => 30,
'url' => '',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 32,
'name' => '接口状态编辑',
'fid' => 31,
'url' => 'admin/InterfaceList/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 33,
'name' => '获取接口唯一标识',
'fid' => 31,
'url' => 'admin/InterfaceList/getHash',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 34,
'name' => '添加接口',
'fid' => 31,
'url' => 'admin/InterfaceList/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 35,
'name' => '编辑接口',
'fid' => 31,
'url' => 'admin/InterfaceList/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 36,
'name' => '删除接口',
'fid' => 31,
'url' => 'admin/InterfaceList/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 37,
'name' => '获取接口请求字段',
'fid' => 31,
'url' => 'admin/Fields/request',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 38,
'name' => '获取接口返回字段',
'fid' => 31,
'url' => 'admin/Fields/response',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 39,
'name' => '添加接口字段',
'fid' => 31,
'url' => 'admin/Fields/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 40,
'name' => '上传接口返回字段',
'fid' => 31,
'url' => 'admin/Fields/upload',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 41,
'name' => '编辑接口字段',
'fid' => 31,
'url' => 'admin/Fields/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 42,
'name' => '删除接口字段',
'fid' => 31,
'url' => 'admin/Fields/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 43,
'name' => '接口分组',
'fid' => 30,
'url' => '',
'auth' => 0,
'sort' => 1,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 44,
'name' => '添加接口组',
'fid' => 43,
'url' => 'admin/InterfaceGroup/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 45,
'name' => '编辑接口组',
'fid' => 43,
'url' => 'admin/InterfaceGroup/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 46,
'name' => '删除接口组',
'fid' => 43,
'url' => 'admin/InterfaceGroup/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 47,
'name' => '获取全部有效的接口组',
'fid' => 43,
'url' => 'admin/InterfaceGroup/getAll',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 48,
'name' => '接口组状态维护',
'fid' => 43,
'url' => 'admin/InterfaceGroup/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 49,
'name' => '应用分组',
'fid' => 23,
'url' => '',
'auth' => 0,
'sort' => 1,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 50,
'name' => '添加应用组',
'fid' => 49,
'url' => 'admin/AppGroup/add',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 51,
'name' => '编辑应用组',
'fid' => 49,
'url' => 'admin/AppGroup/edit',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 52,
'name' => '删除应用组',
'fid' => 49,
'url' => 'admin/AppGroup/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 53,
'name' => '获取全部可用应用组',
'fid' => 49,
'url' => 'admin/AppGroup/getAll',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 54,
'name' => '应用组状态编辑',
'fid' => 49,
'url' => 'admin/AppGroup/changeStatus',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 55,
'name' => '菜单列表',
'fid' => 4,
'url' => 'admin/Menu/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 56,
'name' => '用户列表',
'fid' => 9,
'url' => 'admin/User/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 57,
'name' => '权限列表',
'fid' => 15,
'url' => 'admin/Auth/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 58,
'name' => '应用列表',
'fid' => 24,
'url' => 'admin/App/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 59,
'name' => '应用分组列表',
'fid' => 49,
'url' => 'admin/AppGroup/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 60,
'name' => '接口列表',
'fid' => 31,
'url' => 'admin/InterfaceList/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 61,
'name' => '接口分组列表',
'fid' => 43,
'url' => 'admin/InterfaceGroup/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 62,
'name' => '日志管理',
'fid' => 3,
'url' => '',
'auth' => 0,
'sort' => 4,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 63,
'name' => '获取操作日志列表',
'fid' => 62,
'url' => 'admin/Log/index',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 64,
'name' => '删除单条日志记录',
'fid' => 62,
'url' => 'admin/Log/del',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 65,
'name' => '刷新路由',
'fid' => 31,
'url' => 'admin/InterfaceList/refresh',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 67,
'name' => '文件上传',
'fid' => 0,
'url' => 'admin/Index/upload',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 68,
'name' => '更新个人信息',
'fid' => 9,
'url' => 'admin/User/own',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 69,
'name' => '刷新AppSecret',
'fid' => 24,
'url' => 'admin/App/refreshAppSecret',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 70,
'name' => '获取用户信息',
'fid' => 9,
'url' => 'admin/Login/getUserInfo',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
], [
'id' => 71,
'name' => '编辑权限细节',
'fid' => 15,
'url' => 'admin/Auth/editRule',
'auth' => 0,
'sort' => 0,
'hide' => 0,
'icon' => '',
'level' => 0
]
];
$this->table('admin_menu')->insert($data)->saveData();
}
}

47
database/migrations/20190514034923_ini_admin_group.php

@ -0,0 +1,47 @@
<?php
use think\migration\Migrator;
class IniAdminGroup extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
/** INSERT INTO `admin_group` (`id`, `name`, `description`, `status`, `hash`, `addTime`, `updateTime`, `image`, `hot`)
* VALUES
* (1, '默认分组', '默认分组', 1, 'default', 0, 0, '', 0);
*/
public function up() {
$data = [
'name' => '默认分组',
'description' => '默认分组',
'status' => 1,
'hash' => 'default',
'create_time' => time(),
'update_time' => time(),
'image' => '',
'hot' => 0
];
$this->table('admin_group')->insert($data)->saveData();
}
}

53
database/migrations/20190515031308_ini_admin_user.php

@ -0,0 +1,53 @@
<?php
use think\facade\Env;
use think\migration\Migrator;
use \app\util\Strs;
use \app\util\Tools;
class IniAdminUser extends Migrator {
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function up() {
$pass = Strs::randString(8);
$lockFile = Env::get('app_path') . 'install' . DIRECTORY_SEPARATOR . 'lock.ini';
$authKey = file_get_contents($lockFile);
$data = [
'id' => 1,
'username' => 'root',
'nickname' => 'root',
'password' => Tools::userMd5($pass, $authKey),
'create_time' => time(),
'create_ip' => ip2long('127.0.0.1'),
'update_time' => time(),
'status' => 1,
'openid' => null
];
$this->table('admin_user')->insert($data)->saveData();
$lockFile = Env::get('app_path') . 'install' . DIRECTORY_SEPARATOR . 'lock.ini';
file_put_contents($lockFile, "username:root, password:{$pass}" . PHP_EOL);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save