init.
This commit is contained in:
commit
a9c2455857
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
*.log
|
||||||
|
.env
|
||||||
|
composer.phar
|
||||||
|
composer.lock
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
/vendor
|
||||||
|
/.settings
|
||||||
|
/.buildpath
|
||||||
|
/.project
|
||||||
42
.travis.yml
Normal file
42
.travis.yml
Normal file
@ -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
|
||||||
32
LICENSE.txt
Normal file
32
LICENSE.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||||
|
版权所有Copyright © 2006-2025 by ThinkPHP (http://thinkphp.cn)
|
||||||
|
All rights reserved。
|
||||||
|
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||||
|
|
||||||
|
Apache Licence是著名的非盈利开源组织Apache采用的协议。
|
||||||
|
该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
|
||||||
|
允许代码修改,再作为开源或商业软件发布。需要满足
|
||||||
|
的条件:
|
||||||
|
1. 需要给代码的用户一份Apache Licence ;
|
||||||
|
2. 如果你修改了代码,需要在被修改的文件中说明;
|
||||||
|
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
|
||||||
|
带有原来代码中的协议,商标,专利声明和其他原来作者规
|
||||||
|
定需要包含的说明;
|
||||||
|
4. 如果再发布的产品中包含一个Notice文件,则在Notice文
|
||||||
|
件中需要带有本协议内容。你可以在Notice中增加自己的
|
||||||
|
许可,但不可以表现为对Apache Licence构成更改。
|
||||||
|
具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
77
README.md
Normal file
77
README.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|

|
||||||
|
|
||||||
|
ThinkPHP 8
|
||||||
|
===============
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
* 基于PHP`8.0+`重构
|
||||||
|
* 升级`PSR`依赖
|
||||||
|
* 依赖`think-orm`3.0+版本
|
||||||
|
* 全新的`think-dumper`服务,支持远程调试
|
||||||
|
* 支持`6.0`/`6.1`无缝升级
|
||||||
|
|
||||||
|
> ThinkPHP8的运行环境要求PHP8.0+
|
||||||
|
|
||||||
|
现在开始,你可以使用官方提供的[ThinkChat](https://chat.topthink.com/),让你在学习ThinkPHP的旅途中享受私人AI助理服务!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
ThinkPHP生态服务由[顶想云](https://www.topthink.com)(TOPThink Cloud)提供,为生态提供专业的开发者服务和价值之选。
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
[完全开发手册](https://doc.thinkphp.cn)
|
||||||
|
|
||||||
|
|
||||||
|
## 赞助
|
||||||
|
|
||||||
|
全新的[赞助计划](https://www.thinkphp.cn/sponsor)可以让你通过我们的网站、手册、欢迎页及GIT仓库获得巨大曝光,同时提升企业的品牌声誉,也更好保障ThinkPHP的可持续发展。
|
||||||
|
|
||||||
|
[](https://www.thinkphp.cn/sponsor/special)
|
||||||
|
|
||||||
|
[](https://www.thinkphp.cn/sponsor)
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
~~~
|
||||||
|
composer create-project topthink/think tp
|
||||||
|
~~~
|
||||||
|
|
||||||
|
启动服务
|
||||||
|
|
||||||
|
~~~
|
||||||
|
cd tp
|
||||||
|
php think run
|
||||||
|
~~~
|
||||||
|
|
||||||
|
然后就可以在浏览器中访问
|
||||||
|
|
||||||
|
~~~
|
||||||
|
http://localhost:8000
|
||||||
|
~~~
|
||||||
|
|
||||||
|
如果需要更新框架使用
|
||||||
|
~~~
|
||||||
|
composer update topthink/framework
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## 命名规范
|
||||||
|
|
||||||
|
`ThinkPHP`遵循PSR-2命名规范和PSR-4自动加载规范。
|
||||||
|
|
||||||
|
## 参与开发
|
||||||
|
|
||||||
|
直接提交PR或者Issue即可
|
||||||
|
|
||||||
|
## 版权信息
|
||||||
|
|
||||||
|
ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
|
||||||
|
|
||||||
|
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||||
|
|
||||||
|
版权所有Copyright © 2006-2024 by ThinkPHP (http://thinkphp.cn) All rights reserved。
|
||||||
|
|
||||||
|
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
|
||||||
|
|
||||||
|
更多细节参阅 [LICENSE.txt](LICENSE.txt)
|
||||||
1
app/.htaccess
Normal file
1
app/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
deny from all
|
||||||
18
app/AppService.php
Normal file
18
app/AppService.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
declare (strict_types=1);
|
||||||
|
|
||||||
|
namespace app;
|
||||||
|
|
||||||
|
use app\http\middleware\ContextMiddleware;
|
||||||
|
use think\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用服务类
|
||||||
|
*/
|
||||||
|
class AppService extends Service
|
||||||
|
{
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$this->app->middleware->unshift(ContextMiddleware::class, 'route');
|
||||||
|
}
|
||||||
|
}
|
||||||
145
app/BaseController.php
Normal file
145
app/BaseController.php
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
declare (strict_types=1);
|
||||||
|
|
||||||
|
namespace app;
|
||||||
|
|
||||||
|
use app\http\HttpStatus;
|
||||||
|
use stdClass;
|
||||||
|
use think\App;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use think\Paginator;
|
||||||
|
use think\response\Json;
|
||||||
|
use think\Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 控制器基础类
|
||||||
|
*/
|
||||||
|
abstract class BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Request实例
|
||||||
|
* @var \app\Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用实例
|
||||||
|
* @var \think\App
|
||||||
|
*/
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否批量验证
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $batchValidate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 控制器中间件
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middleware = [];
|
||||||
|
readonly protected ?http\HttpAuth $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
* @access public
|
||||||
|
* @param App $app 应用对象
|
||||||
|
*/
|
||||||
|
public function __construct(App $app)
|
||||||
|
{
|
||||||
|
$this->app = $app;
|
||||||
|
$this->request = $this->app->request;
|
||||||
|
$this->auth = $this->app->request->getAuth();
|
||||||
|
|
||||||
|
|
||||||
|
// 控制器初始化
|
||||||
|
$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, string|array $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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口调用成功
|
||||||
|
* @param string $message
|
||||||
|
* @param mixed $data
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
protected function writeSuccess(string $message = '', mixed $data = new stdClass()): Json
|
||||||
|
{
|
||||||
|
return $this->writeJson(HttpStatus::API_SUCCESS->value, $message ?: HttpStatus::message(HttpStatus::API_SUCCESS), $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口调用失败
|
||||||
|
* @param string $message
|
||||||
|
* @param mixed $data
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
protected function writeError(string $message = '', mixed $data = new stdClass()): Json
|
||||||
|
{
|
||||||
|
return $this->writeJson(HttpStatus::API_ERROR->value, $message ?: HttpStatus::message(HttpStatus::API_ERROR), $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
* @param int $code
|
||||||
|
* @param string $message
|
||||||
|
* @param mixed $data
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
protected function writeJson(int $code = 200, string $message = '', mixed $data = null): Json
|
||||||
|
{
|
||||||
|
if($data instanceof Paginator) {
|
||||||
|
$data = [
|
||||||
|
'count'=> $data->total(),
|
||||||
|
'list' => $data->items()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return json(array(
|
||||||
|
"code" => $code,
|
||||||
|
"data" => $data,
|
||||||
|
"message" => $message,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/BaseModel.php
Normal file
19
app/BaseModel.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
declare (strict_types=1);
|
||||||
|
|
||||||
|
namespace app;
|
||||||
|
|
||||||
|
use think\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型基础类
|
||||||
|
*/
|
||||||
|
abstract class BaseModel extends Model
|
||||||
|
{
|
||||||
|
protected function getOptions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'convertNameToCamel'=> true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/ExceptionHandle.php
Normal file
58
app/ExceptionHandle.php
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Request.php
Normal file
59
app/Request.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app;
|
||||||
|
|
||||||
|
// 应用请求对象类
|
||||||
|
use app\http\HttpAuth;
|
||||||
|
use app\http\HttpClient;
|
||||||
|
use think\helper\Macroable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method static HttpClient getClient() 获取客户端信息
|
||||||
|
*/
|
||||||
|
class Request extends \think\Request
|
||||||
|
{
|
||||||
|
use Macroable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public string $contextId = '';
|
||||||
|
/**
|
||||||
|
* @var HttpAuth
|
||||||
|
*/
|
||||||
|
private readonly HttpAuth $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Auth
|
||||||
|
* @param HttpAuth $auth
|
||||||
|
* @return Request
|
||||||
|
*/
|
||||||
|
public function setAuth(HttpAuth $auth): self
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuth(): ?HttpAuth
|
||||||
|
{
|
||||||
|
// var_dump($this->auth);
|
||||||
|
return $this->auth ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getBrowser(): string
|
||||||
|
{
|
||||||
|
return get_agent_browser($this->server('HTTP_USER_AGENT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDevice(): string
|
||||||
|
{
|
||||||
|
return get_agent_device($this->server('HTTP_USER_AGENT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOs(): string
|
||||||
|
{
|
||||||
|
return get_agent_os($this->server('HTTP_USER_AGENT'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
53
app/RequestClient.php
Normal file
53
app/RequestClient.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app;
|
||||||
|
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use app\validate\auth\ClientValidate;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求客户端信息
|
||||||
|
*/
|
||||||
|
readonly class RequestClient
|
||||||
|
{
|
||||||
|
const string CLIENT_KEY = "KOhjymFZ2Ofcwvlp";
|
||||||
|
|
||||||
|
public string $name;
|
||||||
|
public string $id;
|
||||||
|
public string $version;
|
||||||
|
|
||||||
|
public array $data;
|
||||||
|
|
||||||
|
private string $sign;
|
||||||
|
|
||||||
|
public function __construct(string $clientName, string $clientId, string $clientVersion = '', string $sign = '')
|
||||||
|
{
|
||||||
|
$this->name = $clientName; // 客户端名称(PAdmin/IAdmin)
|
||||||
|
$this->id = $clientId; // 客户端ID(前端生成的唯一标识)
|
||||||
|
$this->version = $clientVersion; // 客户端版本号
|
||||||
|
$this->sign = $sign; // 携带签名数据
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new ValidateException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validate(): void
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
if ($this->sign) {
|
||||||
|
$encryptedData = base64_decode($this->sign);
|
||||||
|
$dataStr = openssl_decrypt($encryptedData, 'AES-128-ECB', RequestClient::CLIENT_KEY, OPENSSL_RAW_DATA);
|
||||||
|
$data = json_decode($dataStr, true);
|
||||||
|
}
|
||||||
|
$this->data = $data;
|
||||||
|
validate(ClientValidate::class)->check([
|
||||||
|
'name' => $this->name,
|
||||||
|
'id' => $this->id,
|
||||||
|
'version' => $this->version,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
221
app/common.php
Normal file
221
app/common.php
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
// 应用公共文件
|
||||||
|
use app\service\DictionaryService;
|
||||||
|
use Hashids\Hashids;
|
||||||
|
use think\Collection;
|
||||||
|
use think\facade\Event;
|
||||||
|
use think\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入日志
|
||||||
|
* @param array|string $event
|
||||||
|
* @param string $message
|
||||||
|
* @param mixed|null $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function security_log_record(array|string $event, string $message, mixed $data = null): void
|
||||||
|
{
|
||||||
|
if (is_string($event)) {
|
||||||
|
$event = explode('.', $event);
|
||||||
|
}
|
||||||
|
if ($data instanceof Model) {
|
||||||
|
$data = $data->toArray();
|
||||||
|
}
|
||||||
|
if ($data instanceof Collection) {
|
||||||
|
$data = $data->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::trigger('sys:securityLogRecord', [$event, $message, $data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典内容
|
||||||
|
* @param string $name
|
||||||
|
* @param $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
function dict_get(string $name, $default = null): mixed
|
||||||
|
{
|
||||||
|
return dict()->get($name, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典服务
|
||||||
|
* @return DictionaryService
|
||||||
|
*/
|
||||||
|
function dict(): DictionaryService
|
||||||
|
{
|
||||||
|
return app()->dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将扁平数组列表转换为树形结构
|
||||||
|
* @param array $items 原始数组
|
||||||
|
* @param string $idKey ID字段名
|
||||||
|
* @param string $parentKey 父ID字段名
|
||||||
|
* @param string $childrenKey 子节点字段名
|
||||||
|
* @return array 树形结构
|
||||||
|
*/
|
||||||
|
function list_build_tree(array $items, string $idKey = 'id', string $parentKey = 'pid', string $childrenKey = 'children'): array
|
||||||
|
{
|
||||||
|
// 创建ID映射和结果树
|
||||||
|
$tree = [];
|
||||||
|
$map = [];
|
||||||
|
|
||||||
|
// 创建ID到数组元素的引用映射
|
||||||
|
foreach ($items as &$item) {
|
||||||
|
$map[$item[$idKey]] = &$item;
|
||||||
|
$item[$childrenKey] = []; // 初始化children
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建树结构
|
||||||
|
foreach ($items as &$item) {
|
||||||
|
$parentId = $item[$parentKey];
|
||||||
|
|
||||||
|
if (isset($map[$parentId])) {
|
||||||
|
// 当前项有父节点,添加到父节点的children
|
||||||
|
$map[$parentId][$childrenKey][] = &$item;
|
||||||
|
} else {
|
||||||
|
// 当前项是根节点,添加到树
|
||||||
|
$tree[] = &$item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理引用
|
||||||
|
unset($item);
|
||||||
|
|
||||||
|
return $tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $minHashLength
|
||||||
|
* @return Hashids
|
||||||
|
*/
|
||||||
|
function hashids(int $minHashLength = 6): Hashids
|
||||||
|
{
|
||||||
|
$salt = (string)config('app.default_salt', '');
|
||||||
|
return new Hashids($salt, $minHashLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snowflake_id(int $machineId = null): int
|
||||||
|
{
|
||||||
|
// 时间戳 42字节
|
||||||
|
$time = floor(microtime(true) * 1000);
|
||||||
|
// 当前时间 与 开始时间 差值
|
||||||
|
$time -= 1641862815726;
|
||||||
|
$base = decbin(1099511627775 + $time);
|
||||||
|
if ($machineId) {
|
||||||
|
$machineId = str_pad(decbin($machineId), 10, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
$random = str_pad(decbin(mt_rand(0, 4095)), 12, '0', STR_PAD_LEFT);
|
||||||
|
return bindec($base . $machineId . $random);
|
||||||
|
}
|
||||||
|
|
||||||
|
function guid($namespace = ''): string
|
||||||
|
{
|
||||||
|
|
||||||
|
$uid = uniqid("", true);
|
||||||
|
$data = $namespace;
|
||||||
|
$data .= $_SERVER['REQUEST_TIME'] ?? '';
|
||||||
|
$data .= $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||||
|
$data .= $_SERVER['LOCAL_ADDR'] ?? '';
|
||||||
|
$data .= $_SERVER['LOCAL_PORT'] ?? '';
|
||||||
|
$data .= $_SERVER['REMOTE_ADDR'] ?? '';
|
||||||
|
$data .= $_SERVER['REMOTE_PORT'] ?? '';
|
||||||
|
$hash = strtoupper(hash('ripemd128', $uid . md5($data)));
|
||||||
|
return
|
||||||
|
substr($hash, 0, 8) .
|
||||||
|
'-' .
|
||||||
|
substr($hash, 8, 4) .
|
||||||
|
'-' .
|
||||||
|
substr($hash, 12, 4) .
|
||||||
|
'-' .
|
||||||
|
substr($hash, 16, 4) .
|
||||||
|
'-' .
|
||||||
|
substr($hash, 20, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unique_str(): string
|
||||||
|
{
|
||||||
|
return md5(uniqid());
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_agent_browser($userAgent = ''): string
|
||||||
|
{
|
||||||
|
if (str_contains($userAgent, 'MicroMessenger')) {
|
||||||
|
return 'Wechat';
|
||||||
|
} elseif (str_contains($userAgent, 'QQBrowser')) {
|
||||||
|
return 'QQ';
|
||||||
|
} elseif (str_contains($userAgent, 'MSIE') || str_contains($userAgent, 'Trident/7.0;') || str_contains($userAgent, 'rv:11.0')) {
|
||||||
|
return 'Explorer';
|
||||||
|
} elseif (str_contains($userAgent, 'Firefox')) {
|
||||||
|
return 'Firefox';
|
||||||
|
} elseif (str_contains($userAgent, 'Edg')) {
|
||||||
|
return 'Edge';
|
||||||
|
} elseif (str_contains($userAgent, 'Chrome')) {
|
||||||
|
return 'Chrome';
|
||||||
|
} elseif (str_contains($userAgent, 'Safari')) {
|
||||||
|
return 'Safari';
|
||||||
|
} elseif (str_contains($userAgent, 'Opera Mini') || str_contains($userAgent, 'Opera')) {
|
||||||
|
return 'Opera';
|
||||||
|
} else {
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_agent_device($agent = ''): string
|
||||||
|
{
|
||||||
|
if (stristr($agent, 'iPad')) {
|
||||||
|
$fb_fs = "iPad";
|
||||||
|
} else if (preg_match('/Android (([0-9_.]{1,3})+)/i', $agent, $version)) {
|
||||||
|
$fb_fs = "手机(Android " . $version[1] . ")";
|
||||||
|
} else if (stristr($agent, 'Linux')) {
|
||||||
|
$fb_fs = "电脑(Linux)";
|
||||||
|
} else if (preg_match('/iPhone OS (([0-9_.]{1,3})+)/i', $agent, $version)) {
|
||||||
|
$fb_fs = "手机(iPhone " . $version[1] . ")";
|
||||||
|
} else if (preg_match('/Mac OS X (([0-9_.]{1,5})+)/i', $agent, $version)) {
|
||||||
|
$fb_fs = "电脑(OS X " . $version[1] . ")";
|
||||||
|
} else if (preg_match('/unix/i', $agent)) {
|
||||||
|
$fb_fs = "Unix";
|
||||||
|
} else if (preg_match('/windows/i', $agent)) {
|
||||||
|
$fb_fs = "电脑(Windows)";
|
||||||
|
} else {
|
||||||
|
$fb_fs = "Unknown";
|
||||||
|
}
|
||||||
|
return $fb_fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_agent_os($userAgent): string
|
||||||
|
{
|
||||||
|
if (preg_match('/windows/i', $userAgent)) {
|
||||||
|
return 'Windows';
|
||||||
|
} elseif (preg_match('/iPhone/i', $userAgent)) {
|
||||||
|
return 'iOS';
|
||||||
|
} elseif (preg_match('/HarmonyOS/i', $userAgent)) {
|
||||||
|
return 'Android';
|
||||||
|
} elseif (preg_match('/Android/i', $userAgent)) {
|
||||||
|
return 'Android';
|
||||||
|
} elseif (preg_match('/mac/i', $userAgent)) {
|
||||||
|
return 'Mac';
|
||||||
|
} else {
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function agent_is_crawler($userAgent): bool
|
||||||
|
{
|
||||||
|
$crawlers = [
|
||||||
|
'googlebot', 'bingbot', 'slurp', 'baidu', 'duckduckbot',
|
||||||
|
'yandexbot', 'sogou', 'exabot', 'ia_archiver', 'facebot',
|
||||||
|
'facebookexternalhit'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($crawlers as $crawler) {
|
||||||
|
if (str_contains($userAgent, $crawler)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
30
app/controller/Index.php
Normal file
30
app/controller/Index.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
|
||||||
|
class Index extends BaseController
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
// $user = SysUser::with(['roles.menus'=> function($query) {
|
||||||
|
// $query->where('deleted', 0);
|
||||||
|
// }])->find("1");
|
||||||
|
//
|
||||||
|
// $menus = $user->getMenus();
|
||||||
|
// dump($user);
|
||||||
|
// dump($menus);
|
||||||
|
|
||||||
|
// dd($user->getMenus(true));
|
||||||
|
|
||||||
|
|
||||||
|
return '';
|
||||||
|
return '<style>*{ padding: 0; margin: 0; }</style><iframe src="https://www.thinkphp.cn/welcome?version=' . \think\facade\App::version() . '" width="100%" height="100%" frameborder="0" scrolling="auto"></iframe>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hello($name = 'ThinkPHP8')
|
||||||
|
{
|
||||||
|
return 'hello,' . $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/controller/admin/auth/AuthController.php
Normal file
40
app/controller/admin/auth/AuthController.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\auth;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysUser;
|
||||||
|
use think\db\exception\DataNotFoundException;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\db\exception\ModelNotFoundException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户Auth控制器
|
||||||
|
*/
|
||||||
|
class AuthController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询用户信息
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function user(): Json
|
||||||
|
{
|
||||||
|
$user = SysUser::findOrFail($this->auth->userId);
|
||||||
|
$data = $user->append(['authorities', 'roles'])->toArray();
|
||||||
|
|
||||||
|
return $this->writeSuccess('', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function logout(): Json
|
||||||
|
{
|
||||||
|
return $this->writeSuccess('退出成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/controller/admin/auth/LoginController.php
Normal file
37
app/controller/admin/auth/LoginController.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\auth;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\service\admin\LoginService;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
class LoginController extends BaseController
|
||||||
|
{
|
||||||
|
public function captcha()
|
||||||
|
{
|
||||||
|
$captcha = file_get_contents("https://v2.eleadmin.com/api/captcha");
|
||||||
|
$captchaData = json_decode($captcha, true);
|
||||||
|
|
||||||
|
return $this->writeSuccess('登录成功', [
|
||||||
|
'base64'=> $captchaData['data']['base64'] ?? '',
|
||||||
|
'text'=> $captchaData['data']['text'] ?? '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$username = $this->request->post('username', '');
|
||||||
|
$password = $this->request->post('password', '');
|
||||||
|
$remember = $this->request->post('remember', false);
|
||||||
|
$code = $this->request->post('code', '');
|
||||||
|
|
||||||
|
$loginService = new LoginService();
|
||||||
|
try {
|
||||||
|
$result = $loginService->login($this->request, $this->request->getClient(), $username, $password, $code, $remember);
|
||||||
|
} catch (ValidateException $e) {
|
||||||
|
return $this->writeError($e->getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->writeSuccess('登录成功', ['user' => $result['user'], 'access_token' => $result['access_token']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
135
app/controller/admin/system/DictionaryController.php
Normal file
135
app/controller/admin/system/DictionaryController.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysDictionary;
|
||||||
|
use app\entity\SysDictionaryData;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class DictionaryController extends BaseController
|
||||||
|
{
|
||||||
|
public function lists(): Json
|
||||||
|
{
|
||||||
|
$lists = CurdService::getList($this->request, new SysDictionary(), ['sort_number' => 'desc']);
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $lists);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$data = $this->request->put([
|
||||||
|
'dictId' => 0,
|
||||||
|
'dictCode' => null,
|
||||||
|
'dictName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
SysDictionary::findOrFail($data['dictId'])->save([
|
||||||
|
'dict_code' => $data['dictCode'],
|
||||||
|
'dict_name' => $data['dictName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('修改成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(SysDictionary $model)
|
||||||
|
{
|
||||||
|
$model->delete();
|
||||||
|
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'dictCode' => null,
|
||||||
|
'dictName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
$sysDictionary = new SysDictionary();
|
||||||
|
$sysDictionary->save([
|
||||||
|
'dict_code' => $data['dictCode'],
|
||||||
|
'dict_name' => $data['dictName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataAdd()
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'dictId' => null,
|
||||||
|
'dictDataCode' => null,
|
||||||
|
'dictDataName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
$sysDictionary = new SysDictionaryData();
|
||||||
|
$sysDictionary->save([
|
||||||
|
'dict_id' => $data['dictId'],
|
||||||
|
'dict_data_code' => $data['dictDataCode'],
|
||||||
|
'dict_data_name' => $data['dictDataName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataUpdate()
|
||||||
|
{
|
||||||
|
$data = $this->request->put([
|
||||||
|
'dictDataId' => 0,
|
||||||
|
'dictId' => null,
|
||||||
|
'dictDataCode' => null,
|
||||||
|
'dictDataName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
SysDictionaryData::findOrFail($data['dictDataId'])->save([
|
||||||
|
'dict_id' => $data['dictId'],
|
||||||
|
'dict_data_code' => $data['dictDataCode'],
|
||||||
|
'dict_data_name' => $data['dictDataName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('修改成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataBatchRemove()
|
||||||
|
{
|
||||||
|
$data = $this->request->delete();
|
||||||
|
SysDictionaryData::destroy($data);
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataLists(): Json
|
||||||
|
{
|
||||||
|
$dictCode = $this->request->param('dictCode/s', '');
|
||||||
|
|
||||||
|
$data = SysDictionary::dictCodeData($dictCode);
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典集列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function dataPage(): Json
|
||||||
|
{
|
||||||
|
$paginate = CurdService::getPaginate($this->request, SysDictionaryData::withSearch(['dictId', 'dictDataName', 'dictDataCode'], [
|
||||||
|
'dictId' => $this->request->get('dictId/d', 0),
|
||||||
|
'dictDataName' => $this->request->get('dictDataName/s', ''),
|
||||||
|
'dictDataCode' => $this->request->get('dictDataCode/s', ''),
|
||||||
|
]));
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $paginate);
|
||||||
|
}
|
||||||
|
}
|
||||||
85
app/controller/admin/system/DictionaryDataController.php
Normal file
85
app/controller/admin/system/DictionaryDataController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysDictionary;
|
||||||
|
use app\entity\SysDictionaryData;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class DictionaryDataController extends BaseController
|
||||||
|
{
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'dictId' => null,
|
||||||
|
'dictDataCode' => null,
|
||||||
|
'dictDataName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
$sysDictionary = new SysDictionaryData();
|
||||||
|
$sysDictionary->save([
|
||||||
|
'dict_id' => $data['dictId'],
|
||||||
|
'dict_data_code' => $data['dictDataCode'],
|
||||||
|
'dict_data_name' => $data['dictDataName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$data = $this->request->put([
|
||||||
|
'dictDataId' => 0,
|
||||||
|
'dictId' => null,
|
||||||
|
'dictDataCode' => null,
|
||||||
|
'dictDataName' => null,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'comments' => ''
|
||||||
|
]);
|
||||||
|
SysDictionaryData::findOrFail($data['dictDataId'])->save([
|
||||||
|
'dict_id' => $data['dictId'],
|
||||||
|
'dict_data_code' => $data['dictDataCode'],
|
||||||
|
'dict_data_name' => $data['dictDataName'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'comments' => $data['comments']
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('修改成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchRemove()
|
||||||
|
{
|
||||||
|
$data = $this->request->delete();
|
||||||
|
SysDictionaryData::destroy($data);
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lists(): Json
|
||||||
|
{
|
||||||
|
$dictCode = $this->request->param('dictCode/s', '');
|
||||||
|
|
||||||
|
$data = SysDictionary::dictCodeData($dictCode);
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询字典集列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$paginate = CurdService::getPaginate($this->request, SysDictionaryData::withSearch(['dictId', 'dictDataName', 'dictDataCode'], [
|
||||||
|
'dictId' => $this->request->get('dictId/d', 0),
|
||||||
|
'dictDataName' => $this->request->get('dictDataName/s', ''),
|
||||||
|
'dictDataCode' => $this->request->get('dictDataCode/s', ''),
|
||||||
|
]));
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $paginate);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/controller/admin/system/FileController.php
Normal file
41
app/controller/admin/system/FileController.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysFileRecord;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class FileController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 分页查询账号登录记录
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysFileRecord::with(['createUser'])->withSearch(['name','path','createNickname'], [
|
||||||
|
'name'=> $this->request->param('name/s',''),
|
||||||
|
'path'=> $this->request->param('path/s',''),
|
||||||
|
'createNickname'=> $this->request->param('createNickname/s',''),
|
||||||
|
'createTime' => [
|
||||||
|
$this->request->get('createTimeStart/s', ''),
|
||||||
|
$this->request->get('createTimeEnd/s', '')
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $paginate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchRemove()
|
||||||
|
{
|
||||||
|
$data = $this->request->delete();
|
||||||
|
SysFileRecord::destroy($data);
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/controller/admin/system/LoginRecordController.php
Normal file
34
app/controller/admin/system/LoginRecordController.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysLoginRecord;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class LoginRecordController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 分页查询账号登录记录
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysLoginRecord::withSearch(['username', 'nickname', 'loginType', 'createTime'], [
|
||||||
|
'username' => $this->request->get('username/s', ''),
|
||||||
|
'nickname' => $this->request->get('nickname/s', ''),
|
||||||
|
'loginType' => $this->request->get('loginType/d', 0),
|
||||||
|
'createTime' => [
|
||||||
|
$this->request->get('createTimeStart/s', ''),
|
||||||
|
$this->request->get('createTimeEnd/s', '')
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $paginate);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
app/controller/admin/system/MenuController.php
Normal file
84
app/controller/admin/system/MenuController.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysMenu;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DataNotFoundException;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\db\exception\ModelNotFoundException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class MenuController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询菜单列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function list(): Json
|
||||||
|
{
|
||||||
|
$model = SysMenu::withSearch(['title', 'path', 'authority'], [
|
||||||
|
'title' => $this->request->get('title/s', ''),
|
||||||
|
'path' => $this->request->get('path/s', ''),
|
||||||
|
'authority' => $this->request->get('authority/s', ''),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$lists = CurdService::getList($this->request, $model, ['sort_number' => 'asc']);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $lists);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加菜单列表
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function add(): Json
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'parentId' => 0,
|
||||||
|
'title' => '',
|
||||||
|
'menuType' => '',
|
||||||
|
'openType' => '',
|
||||||
|
'icon' => '',
|
||||||
|
'path' => '',
|
||||||
|
'component' => '',
|
||||||
|
'authority' => '',
|
||||||
|
'sortNumber' => 0,
|
||||||
|
'hide' => 0,
|
||||||
|
'meta' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = new SysMenu();
|
||||||
|
$user->save([
|
||||||
|
'parent_id' => $data['parentId'],
|
||||||
|
'title' => $data['title'],
|
||||||
|
'menu_type' => $data['menuType'],
|
||||||
|
'open_type' => $data['openType'],
|
||||||
|
'icon' => $data['icon'],
|
||||||
|
'path' => $data['path'],
|
||||||
|
'component' => $data['component'],
|
||||||
|
'authority' => $data['authority'],
|
||||||
|
'sortNumber' => $data['sortNumber'],
|
||||||
|
'hide' => $data['hide'],
|
||||||
|
'meta' => $data['meta'],
|
||||||
|
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除菜单
|
||||||
|
* @param SysMenu $model
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function remove(SysMenu $model)
|
||||||
|
{
|
||||||
|
$model->delete();
|
||||||
|
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/controller/admin/system/OperateRecordController.php
Normal file
34
app/controller/admin/system/OperateRecordController.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysOperateRecord;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class OperateRecordController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 分页查询操作日志记录
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysOperateRecord::withSearch([], [
|
||||||
|
'username' => $this->request->get('username/s', ''),
|
||||||
|
'nickname' => $this->request->get('nickname/s', ''),
|
||||||
|
'loginType' => $this->request->get('loginType/d', 0),
|
||||||
|
'createTime' => [
|
||||||
|
$this->request->get('createTimeStart/s', ''),
|
||||||
|
$this->request->get('createTimeEnd/s', '')
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $paginate);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
app/controller/admin/system/OrganizationController.php
Normal file
80
app/controller/admin/system/OrganizationController.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysOrganization;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class OrganizationController extends BaseController
|
||||||
|
{
|
||||||
|
public function lists(): Json
|
||||||
|
{
|
||||||
|
$model = SysOrganization::withSearch(['organizationType', 'organizationName'], [
|
||||||
|
'organizationType' => $this->request->get('organizationType/d', 0),
|
||||||
|
'organizationName' => $this->request->get('organizationName/s', ''),
|
||||||
|
])->append(['organizationTypeName']);
|
||||||
|
|
||||||
|
$lists = CurdService::getList($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $lists);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'parentId' => 0,
|
||||||
|
'organizationType' => 0,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'organizationName' => '',
|
||||||
|
'organizationFullName' => '',
|
||||||
|
'organizationCode' => '',
|
||||||
|
'comments' => '',
|
||||||
|
'organizationId' => ''
|
||||||
|
]);
|
||||||
|
$sysDictionary = SysOrganization::findOrFail($data['organizationId']);
|
||||||
|
$sysDictionary->save([
|
||||||
|
'parent_id' => $data['parentId'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'organization_type' => $data['organizationType'],
|
||||||
|
'organization_name' => $data['organizationName'],
|
||||||
|
'organization_full_name' => $data['organizationFullName'],
|
||||||
|
'organization_code' => $data['organizationCode'],
|
||||||
|
'comments' => $data['comments'],
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('更新成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add()
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'parentId' => 0,
|
||||||
|
'organizationType' => 0,
|
||||||
|
'sortNumber' => 100,
|
||||||
|
'organizationName' => '',
|
||||||
|
'organizationFullName' => '',
|
||||||
|
'organizationCode' => '',
|
||||||
|
'comments' => '',
|
||||||
|
|
||||||
|
]);
|
||||||
|
$sysDictionary = new SysOrganization();
|
||||||
|
$sysDictionary->save([
|
||||||
|
'parent_id' => $data['parentId'],
|
||||||
|
'sort_number' => $data['sortNumber'],
|
||||||
|
'organization_type' => $data['organizationType'],
|
||||||
|
'organization_name' => $data['organizationName'],
|
||||||
|
'organization_full_name' => $data['organizationFullName'],
|
||||||
|
'organization_code' => $data['organizationCode'],
|
||||||
|
'comments' => $data['comments'],
|
||||||
|
]);
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(SysOrganization $model)
|
||||||
|
{
|
||||||
|
$model->delete();
|
||||||
|
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/controller/admin/system/RequestRecordController.php
Normal file
31
app/controller/admin/system/RequestRecordController.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysRequestRecord;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class RequestRecordController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 分页查询请求日志记录
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysRequestRecord::withSearch(['createTime'], [
|
||||||
|
'createTime' => [
|
||||||
|
$this->request->get('createTimeStart/s', ''),
|
||||||
|
$this->request->get('createTimeEnd/s', '')
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $paginate);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
app/controller/admin/system/RoleController.php
Normal file
90
app/controller/admin/system/RoleController.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysMenu;
|
||||||
|
use app\entity\SysRole;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\DataNotFoundException;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\db\exception\ModelNotFoundException;
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class RoleController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询角色列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function list(): Json
|
||||||
|
{
|
||||||
|
$lists = CurdService::getList($this->request, new SysRole());
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $lists);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询角色菜单
|
||||||
|
* @param $role_id
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function getMenu($role_id)
|
||||||
|
{
|
||||||
|
$role = SysRole::findOrFail($role_id);
|
||||||
|
$menuIds = $role->menus->column('menu_id');
|
||||||
|
$menus = SysMenu::select()->each(function (SysMenu $menu) use ($menuIds) {
|
||||||
|
$menu->checked = in_array($menu['menu_id'], $menuIds);
|
||||||
|
});
|
||||||
|
return $this->writeSuccess('ok', $menus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新角色菜单
|
||||||
|
* @param $role_id
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function updateMenu($role_id)
|
||||||
|
{
|
||||||
|
$menus = $this->request->put();
|
||||||
|
$role = SysRole::findOrFail($role_id);
|
||||||
|
$role->menus()->attach($menus);
|
||||||
|
return $this->writeSuccess('操作成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询角色列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysRole::withSearch(['roleName', 'roleCode'], [
|
||||||
|
'roleName' => $this->request->get('roleName/s', ''),
|
||||||
|
'roleCode' => $this->request->get('roleCode/s', ''),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('ok', $paginate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除角色
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function removeRoles(): Json
|
||||||
|
{
|
||||||
|
$data = $this->request->delete();
|
||||||
|
SysRole::destroy($data);
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
170
app/controller/admin/system/UserController.php
Normal file
170
app/controller/admin/system/UserController.php
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller\admin\system;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\entity\SysUser;
|
||||||
|
use app\service\CurdService;
|
||||||
|
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
class UserController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 分页获取用户列表
|
||||||
|
* @return Json
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public function page(): Json
|
||||||
|
{
|
||||||
|
$model = SysUser::with(['roles'])
|
||||||
|
->withSearch(['organizationId', 'username', 'nickname', 'sex'], [
|
||||||
|
'organizationId' => $this->request->get('organizationId/d', 0),
|
||||||
|
'username' => $this->request->get('username/s', ''),
|
||||||
|
'nickname' => $this->request->get('nickname/s', ''),
|
||||||
|
'sex' => $this->request->get('sex/d', 0),
|
||||||
|
])->append(['sexName']);
|
||||||
|
|
||||||
|
$paginate = CurdService::getPaginate($this->request, $model);
|
||||||
|
|
||||||
|
return $this->writeSuccess('success', $paginate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增用户
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function add(): Json
|
||||||
|
{
|
||||||
|
$data = $this->request->post([
|
||||||
|
'birthday' => null,
|
||||||
|
'username' => '',
|
||||||
|
'email' => '',
|
||||||
|
'nickname' => '',
|
||||||
|
'introduction' => '',
|
||||||
|
'organizationId' => 0,
|
||||||
|
'phone' => '',
|
||||||
|
'sex' => '',
|
||||||
|
'status' => 0,
|
||||||
|
'roles' => [],
|
||||||
|
]);
|
||||||
|
$data['roles'] = array_column((array)$data['roles'], 'roleId');
|
||||||
|
|
||||||
|
$user = new SysUser();
|
||||||
|
$user->save([
|
||||||
|
'birthday' => $data['birthday'],
|
||||||
|
'username' => $data['username'],
|
||||||
|
'nickname' => $data['nickname'],
|
||||||
|
'introduction' => $data['introduction'],
|
||||||
|
'organization_id' => $data['organizationId'],
|
||||||
|
'phone' => $data['phone'],
|
||||||
|
'sex' => $data['sex'],
|
||||||
|
'status' => $data['status'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'password' => password_hash($data['password'], PASSWORD_DEFAULT),
|
||||||
|
]);
|
||||||
|
$user->roles()->saveAll($data['roles']);
|
||||||
|
|
||||||
|
return $this->writeSuccess('添加成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function update(): Json
|
||||||
|
{
|
||||||
|
$data = $this->request->put([
|
||||||
|
'userId' => 0,
|
||||||
|
'birthday' => null,
|
||||||
|
'username' => '',
|
||||||
|
'email' => '',
|
||||||
|
'nickname' => '',
|
||||||
|
'introduction' => '',
|
||||||
|
'organizationId' => 0,
|
||||||
|
'phone' => '',
|
||||||
|
'sex' => '',
|
||||||
|
'status' => 0,
|
||||||
|
'roles' => [],
|
||||||
|
]);
|
||||||
|
$data['roles'] = array_column((array)$data['roles'], 'roleId');
|
||||||
|
|
||||||
|
$user = SysUser::findOrFail($data['userId']);
|
||||||
|
$updateOk = $user->save([
|
||||||
|
'birthday' => $data['birthday'],
|
||||||
|
'username' => $data['username'],
|
||||||
|
'nickname' => $data['nickname'],
|
||||||
|
'introduction' => $data['introduction'],
|
||||||
|
'organization_id' => $data['organizationId'],
|
||||||
|
'phone' => $data['phone'],
|
||||||
|
'sex' => $data['sex'],
|
||||||
|
'status' => $data['status'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
]);
|
||||||
|
if (!$updateOk) {
|
||||||
|
return $this->writeError('修改失败');
|
||||||
|
}
|
||||||
|
$user->roles()->attach($data['roles']);
|
||||||
|
|
||||||
|
return $this->writeSuccess('修改成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除用户
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function batchRemove(): Json
|
||||||
|
{
|
||||||
|
$data = $this->request->delete();
|
||||||
|
SysUser::destroy($data);
|
||||||
|
return $this->writeSuccess('删除成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户状态
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function updateStatus(): Json
|
||||||
|
{
|
||||||
|
$userId = $this->request->put('userId/d', 0);
|
||||||
|
$status = $this->request->put('status/d', 0);
|
||||||
|
SysUser::findOrfail($userId)->save(['status' => $status]);
|
||||||
|
return $this->writeSuccess('操作成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户密码
|
||||||
|
* @return Json
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function updatePassword(): Json
|
||||||
|
{
|
||||||
|
$userId = $this->request->put('userId/d', 0);
|
||||||
|
$password = $this->request->put('password/s', 0);
|
||||||
|
|
||||||
|
SysUser::findOrfail($userId)->save(['password' => password_hash($password, PASSWORD_DEFAULT)]);
|
||||||
|
|
||||||
|
return $this->writeSuccess('操作成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断数据是否存在
|
||||||
|
* @return Json
|
||||||
|
*/
|
||||||
|
public function existence(): Json
|
||||||
|
{
|
||||||
|
$field = $this->request->get('field/s', '');
|
||||||
|
$value = $this->request->get('value/s', '');
|
||||||
|
try {
|
||||||
|
SysUser::field('user_id')->where([$field => $value])->findOrFail();
|
||||||
|
} catch (ModelNotFoundException|DataNotFoundException) {
|
||||||
|
return $this->writeError("用户不存在");
|
||||||
|
}
|
||||||
|
return $this->writeSuccess('用户已存在');
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/entity/SysDictionary.php
Normal file
17
app/entity/SysDictionary.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method static static dictCode(string $dictCode)
|
||||||
|
*/
|
||||||
|
class SysDictionary extends Entity
|
||||||
|
{
|
||||||
|
public static function dictCodeData($dictCode)
|
||||||
|
{
|
||||||
|
return self::dictCode($dictCode)->findOrFail()->dictData->order('sortNumber', 'desc');
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/entity/SysDictionaryData.php
Normal file
11
app/entity/SysDictionaryData.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
|
||||||
|
class SysDictionaryData extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysFileRecord.php
Normal file
14
app/entity/SysFileRecord.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统文件实体类
|
||||||
|
* @see \app\model\SysFileRecord
|
||||||
|
*/
|
||||||
|
class SysFileRecord extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysLoginRecord.php
Normal file
14
app/entity/SysLoginRecord.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统登录日志实体类
|
||||||
|
* @see \app\model\SysLoginRecord
|
||||||
|
*/
|
||||||
|
class SysLoginRecord extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysMenu.php
Normal file
14
app/entity/SysMenu.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统菜单实体类
|
||||||
|
* @see \app\model\SysMenu
|
||||||
|
*/
|
||||||
|
class SysMenu extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysOperateRecord.php
Normal file
14
app/entity/SysOperateRecord.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统操作日志实体类
|
||||||
|
* @see \app\model\SysOperateRecord
|
||||||
|
*/
|
||||||
|
class SysOperateRecord extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysOrganization.php
Normal file
14
app/entity/SysOrganization.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统机构实体类
|
||||||
|
* @see \app\model\SysOrganization
|
||||||
|
*/
|
||||||
|
class SysOrganization extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysRequestRecord.php
Normal file
14
app/entity/SysRequestRecord.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统请求记录实体类
|
||||||
|
* @see \app\model\SysRequestRecord
|
||||||
|
*/
|
||||||
|
class SysRequestRecord extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
14
app/entity/SysRole.php
Normal file
14
app/entity/SysRole.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统角色实体类
|
||||||
|
* @see \app\model\SysRole
|
||||||
|
*/
|
||||||
|
class SysRole extends Entity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
45
app/entity/SysUser.php
Normal file
45
app/entity/SysUser.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\entity;
|
||||||
|
|
||||||
|
use app\model\SysRole;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Hashids\Hashids;
|
||||||
|
use think\Collection;
|
||||||
|
use think\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户实体
|
||||||
|
* @see \app\model\SysUser
|
||||||
|
* @property SysRole[]|Collection $roles
|
||||||
|
* @property array $authorities
|
||||||
|
*/
|
||||||
|
class SysUser extends Entity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 获取菜单列表
|
||||||
|
* @param bool $buildTree
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMenus(bool $buildTree = false): array
|
||||||
|
{
|
||||||
|
return $buildTree ? list_build_tree($this->authorities, 'menuId', 'parentId') : $this->authorities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成AccessToken
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAccessToken(): string
|
||||||
|
{
|
||||||
|
$jwtConfig = (array)config('admin.jwt_config', []);
|
||||||
|
$jwtKey = (string)config('admin.jwt_key', '');
|
||||||
|
$hashids = new Hashids($jwtKey, 8);
|
||||||
|
return JWT::encode([
|
||||||
|
...$jwtConfig,
|
||||||
|
'iat' => time(),
|
||||||
|
'exp' => time() + 3600,
|
||||||
|
'uid' => $hashids->encode($this->user_id),
|
||||||
|
], $jwtKey, 'HS256');
|
||||||
|
}
|
||||||
|
}
|
||||||
8
app/enum/ClientEnum.php
Normal file
8
app/enum/ClientEnum.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum ClientEnum: string
|
||||||
|
{
|
||||||
|
case PAdmin = 'PAdmin';// PC版后台
|
||||||
|
}
|
||||||
10
app/enum/FileEnum.php
Normal file
10
app/enum/FileEnum.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum FileEnum: string
|
||||||
|
{
|
||||||
|
case Image = 'image';// 图片
|
||||||
|
case Video = 'video';// 视频
|
||||||
|
case Audio = 'audio';// 音频
|
||||||
|
}
|
||||||
12
app/enum/UserLoginEnum.php
Normal file
12
app/enum/UserLoginEnum.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum UserLoginEnum: int
|
||||||
|
{
|
||||||
|
case USERNAME_ERROR = 11;// 用户名错误
|
||||||
|
case PASSWORD_ERROR = 12;// 密码错误
|
||||||
|
case STATUS_DISABLED = 21; // 用户已被禁用
|
||||||
|
case LOGIN_EXCEPTION = 31; // 登录异常
|
||||||
|
case LOGIN_SUCCESS = 99; // 登录成功
|
||||||
|
}
|
||||||
10
app/enum/UserSexEnum.php
Normal file
10
app/enum/UserSexEnum.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum UserSexEnum: int
|
||||||
|
{
|
||||||
|
case Unknown = 0;// 未知
|
||||||
|
case Man = 1;// 男
|
||||||
|
case Woman = 2;// 女
|
||||||
|
}
|
||||||
9
app/enum/UserStatusEnum.php
Normal file
9
app/enum/UserStatusEnum.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum UserStatusEnum: int
|
||||||
|
{
|
||||||
|
case Disable = 0;// 禁用
|
||||||
|
case Enable = 1;// 启用
|
||||||
|
}
|
||||||
9
app/enum/UserTypeEnum.php
Normal file
9
app/enum/UserTypeEnum.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\enum;
|
||||||
|
|
||||||
|
enum UserTypeEnum: string
|
||||||
|
{
|
||||||
|
case USER = 'user';
|
||||||
|
case VISITOR = 'visitor';
|
||||||
|
}
|
||||||
20
app/event.php
Normal file
20
app/event.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
// 事件定义文件
|
||||||
|
use app\subscribe\SysUserSubscribe;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'bind' => [
|
||||||
|
],
|
||||||
|
|
||||||
|
'listen' => [
|
||||||
|
'AppInit' => [],
|
||||||
|
'HttpRun' => [],
|
||||||
|
'HttpEnd' => [],
|
||||||
|
'LogLevel' => [],
|
||||||
|
'LogWrite' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
'subscribe' => [
|
||||||
|
'sysUser' => SysUserSubscribe::class
|
||||||
|
],
|
||||||
|
];
|
||||||
43
app/http/HttpAuth.php
Normal file
43
app/http/HttpAuth.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\http;
|
||||||
|
|
||||||
|
use app\entity\SysUser;
|
||||||
|
use app\enum\UserTypeEnum;
|
||||||
|
use think\helper\Macroable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method SysUser getUser()
|
||||||
|
*/
|
||||||
|
class HttpAuth
|
||||||
|
{
|
||||||
|
use Macroable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
* >0 已登录用户
|
||||||
|
* =0 未登录
|
||||||
|
* <0 访客
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public int $userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户类型
|
||||||
|
* @var UserTypeEnum
|
||||||
|
* USER 标准用户
|
||||||
|
* VISITOR 未登录访客
|
||||||
|
*/
|
||||||
|
public UserTypeEnum $userType;
|
||||||
|
|
||||||
|
public function __construct($userId, $userType)
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
$this->userType = $userType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLogin(): bool
|
||||||
|
{
|
||||||
|
return $this->userId > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
app/http/HttpClient.php
Normal file
53
app/http/HttpClient.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\http;
|
||||||
|
|
||||||
|
|
||||||
|
use app\validate\auth\ClientValidate;
|
||||||
|
use Exception;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求客户端信息
|
||||||
|
*/
|
||||||
|
readonly class HttpClient
|
||||||
|
{
|
||||||
|
const string CLIENT_KEY = "KOhjymFZ2Ofcwvlp";
|
||||||
|
|
||||||
|
public string $name;
|
||||||
|
public string $id;
|
||||||
|
public string $version;
|
||||||
|
|
||||||
|
public array $data;
|
||||||
|
|
||||||
|
private string $sign;
|
||||||
|
|
||||||
|
public function __construct(string $clientName, string $clientId, string $clientVersion = '', string $sign = '')
|
||||||
|
{
|
||||||
|
$this->name = $clientName; // 客户端名称(PAdmin/IAdmin)
|
||||||
|
$this->id = $clientId; // 客户端ID(前端生成的唯一标识)
|
||||||
|
$this->version = $clientVersion; // 客户端版本号
|
||||||
|
$this->sign = $sign; // 携带签名数据
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new ValidateException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validate(): void
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
if ($this->sign) {
|
||||||
|
$encryptedData = base64_decode($this->sign);
|
||||||
|
$dataStr = openssl_decrypt($encryptedData, 'AES-128-ECB', static::CLIENT_KEY, OPENSSL_RAW_DATA);
|
||||||
|
$data = json_decode($dataStr, true);
|
||||||
|
}
|
||||||
|
$this->data = $data;
|
||||||
|
validate(ClientValidate::class)->check([
|
||||||
|
'name' => $this->name,
|
||||||
|
'id' => $this->id,
|
||||||
|
'version' => $this->version,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/http/HttpStatus.php
Normal file
24
app/http/HttpStatus.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace app\http;
|
||||||
|
|
||||||
|
enum HttpStatus: int
|
||||||
|
{
|
||||||
|
case API_SUCCESS = 0;
|
||||||
|
case API_ERROR = 1;
|
||||||
|
case UNAUTHORIZED = 401;
|
||||||
|
case FORBIDDEN = 403;
|
||||||
|
|
||||||
|
static function message(self $status): string
|
||||||
|
{
|
||||||
|
return match ($status) {
|
||||||
|
self::API_SUCCESS => '操作成功',
|
||||||
|
self::API_ERROR => '操作失败',
|
||||||
|
self::UNAUTHORIZED => '未授权访问',
|
||||||
|
self::FORBIDDEN => '禁止访问',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
60
app/http/middleware/AuthMiddleware.php
Normal file
60
app/http/middleware/AuthMiddleware.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\http\middleware;
|
||||||
|
|
||||||
|
use app\model\SysUser;
|
||||||
|
use app\Request;
|
||||||
|
use app\service\admin\LoginService;
|
||||||
|
use Closure;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use think\Middleware;
|
||||||
|
use think\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验中间件
|
||||||
|
*/
|
||||||
|
class AuthMiddleware extends Middleware
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 请求接口白名单列表
|
||||||
|
*/
|
||||||
|
protected array $whiteList = [];
|
||||||
|
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 权限校验
|
||||||
|
*/
|
||||||
|
$authorization = (string)$request->header('authorization', '');
|
||||||
|
$authorization = str_replace('Bearer ', '', $authorization);
|
||||||
|
$loginSrv = new LoginService();
|
||||||
|
if (!in_array($request->pathinfo(), $this->whiteList, true)) {
|
||||||
|
try{
|
||||||
|
$auth = $loginSrv->checkUserAccessToken($authorization);
|
||||||
|
}catch (ValidateException $e){
|
||||||
|
return json(['code' => 401, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$auth = $loginSrv->getVisitor($request);
|
||||||
|
return json(['code' => 401, 'message' => '禁止访问']);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 注入获取用户信息的function
|
||||||
|
*/
|
||||||
|
$auth::macro('getUser', function () use ($auth) {
|
||||||
|
return SysUser::cache("sysUserInfo:{$auth->userId}", 180, 'sysUserInfoLists')->findOrFail($auth->userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $next($request->setAuth($auth));
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 结束调度
|
||||||
|
// * @中间件支持定义请求结束前的回调机制,你只需要在中间件类中添加end方法。
|
||||||
|
// * @param Response $response
|
||||||
|
// */
|
||||||
|
// public function end(Response $response)
|
||||||
|
// {
|
||||||
|
// // 回调行为
|
||||||
|
// }
|
||||||
|
}
|
||||||
36
app/http/middleware/ClientMiddleware.php
Normal file
36
app/http/middleware/ClientMiddleware.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\http\middleware;
|
||||||
|
|
||||||
|
use app\http\HttpClient;
|
||||||
|
use Closure;
|
||||||
|
use think\Middleware;
|
||||||
|
use think\Request;
|
||||||
|
|
||||||
|
class ClientMiddleware extends Middleware
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
$clientName = $request->header('client', '');
|
||||||
|
if(empty($clientName)) {
|
||||||
|
return response('设备未授权',200);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 客户端信息
|
||||||
|
*/
|
||||||
|
$client = new HttpClient(
|
||||||
|
$clientName,
|
||||||
|
$request->header('clientId', ''),
|
||||||
|
$request->header('clientVersion', ''),
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
* 获取客户端
|
||||||
|
*/
|
||||||
|
\app\Request::macro('getClient', function () use ($client) {
|
||||||
|
return $client;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Next.
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
135
app/http/middleware/ContextMiddleware.php
Normal file
135
app/http/middleware/ContextMiddleware.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\http\middleware;
|
||||||
|
|
||||||
|
use app\entity\SysRequestRecord;
|
||||||
|
use Closure;
|
||||||
|
use think\{file\UploadedFile, middleware, Request, Response};
|
||||||
|
|
||||||
|
class ContextMiddleware extends middleware
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 生成并绑定全局上下文标识ID
|
||||||
|
*/
|
||||||
|
$request->contextId = unique_str();
|
||||||
|
/**
|
||||||
|
* @var Response $response
|
||||||
|
*/
|
||||||
|
$response = $next($request);
|
||||||
|
$response->header([
|
||||||
|
'R-Context-Id' => $request->contextId,
|
||||||
|
]);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束
|
||||||
|
* @param Response $response
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function end(Response $response): void
|
||||||
|
{
|
||||||
|
$request = $this->app->request;
|
||||||
|
$log = (array)$this->app->config->get('admin.httpLog');
|
||||||
|
|
||||||
|
$logOpen = $log['open'] ?? false;
|
||||||
|
|
||||||
|
if (!$logOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$request_start_time = $request->time(true);
|
||||||
|
$request_end_time = microtime(true);
|
||||||
|
$running_time = ($request_end_time - $request_start_time) * 1000;
|
||||||
|
|
||||||
|
$openRecordStartTime = $log['open_start_time'] ?? 0;
|
||||||
|
$openRecordEndTime = $log['open_end_time'] ?? 0;
|
||||||
|
if ($openRecordStartTime && $openRecordEndTime) {
|
||||||
|
if ($request_end_time < $openRecordStartTime || $openRecordEndTime < $request_end_time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = array_merge([
|
||||||
|
'include_methods' => [],
|
||||||
|
'include_responses' => [],
|
||||||
|
'include_codes' => [],
|
||||||
|
'exclude_codes' => [],
|
||||||
|
], $log['recordRules'] ?? []);
|
||||||
|
|
||||||
|
// 请求类型(GET/POST/PUT/DELETE)
|
||||||
|
$request_method = $request->method();
|
||||||
|
if (!in_array($request_method, $rules['include_methods'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 返回类型(JSON/HTML/XML)
|
||||||
|
$response_type = get_class($response);
|
||||||
|
if (!in_array($response_type, $rules['include_responses'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 返回状态码(Http Status Code)
|
||||||
|
$response_code = $response->getCode();
|
||||||
|
if (in_array($response_type, $rules['exclude_codes'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count($rules['include_codes']) > 0) {
|
||||||
|
if (!in_array($response_type, $rules['include_codes'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$request_start_time = explode('.', $request_start_time);
|
||||||
|
$request_end_time = explode('.', $request_end_time);
|
||||||
|
$request_date = date('Y-m-d H:i:s', $request_start_time[0]);
|
||||||
|
$response_data = $response->getContent();
|
||||||
|
|
||||||
|
// 限制response_data记录长度
|
||||||
|
$response_max = $log['response_max'] ?? 0;
|
||||||
|
if ($response_max) {
|
||||||
|
$response_data = mb_substr($response_data, 0, $response_max, 'utf-8');
|
||||||
|
}
|
||||||
|
$request_body = $request->param();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 对文件类型的数据做处理
|
||||||
|
$request_file = $request->file();
|
||||||
|
if ($request_file && count($request_file) > 0) {
|
||||||
|
/**
|
||||||
|
* @var UploadedFile $file
|
||||||
|
*/
|
||||||
|
foreach ($request->file() as $key => $file) {
|
||||||
|
$request_body[$key] = [
|
||||||
|
'md5' => $file->md5(),
|
||||||
|
'name' => $file->getOriginalName(),
|
||||||
|
'size' => $file->getSize(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$route = $request->rule()->getRoute();
|
||||||
|
|
||||||
|
(new SysRequestRecord)->save([
|
||||||
|
'context_id' => $request->contextId,
|
||||||
|
'request_date' => $request_date,
|
||||||
|
'request_time' => $request_date . '.' . ($request_start_time[1] ?? 0),
|
||||||
|
'request_method' => $request_method,
|
||||||
|
'request_end_time' => date('Y-m-d H:i:s', $request_end_time[0]) . '.' . ($request_end_time[1] ?? 0),
|
||||||
|
'request_headers' => json_encode($request->header(), JSON_UNESCAPED_UNICODE),
|
||||||
|
'request_body' => json_encode($request_body, JSON_UNESCAPED_UNICODE),
|
||||||
|
'request_path' => $request->url(),
|
||||||
|
'request_domain' => $request->domain(),
|
||||||
|
'response_type' => $response_type,
|
||||||
|
'response_code' => $response_code,
|
||||||
|
'response_data' => $response_data,
|
||||||
|
'running_time' => (int)$running_time,
|
||||||
|
'running_memory_limit' => ini_get('memory_limit'),
|
||||||
|
'running_memory_usage' => memory_get_peak_usage(),
|
||||||
|
'request_ip' => $request->ip(),
|
||||||
|
'rule_name' => $request->rule()->getName(),
|
||||||
|
'rule_route' => is_array($route) ? implode('@', $route) : @json_encode($route),
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->app->log->error("[ContextMiddleware::end] 日志写入失败->{$e->getMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/middleware.php
Normal file
12
app/middleware.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
// 全局中间件定义文件
|
||||||
|
use app\http\middleware\ContextMiddleware;
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 全局请求缓存
|
||||||
|
// \think\middleware\CheckRequestCache::class,
|
||||||
|
// 多语言加载
|
||||||
|
// \think\middleware\LoadLangPack::class,
|
||||||
|
// Session初始化
|
||||||
|
// \think\middleware\SessionInit::class
|
||||||
|
];
|
||||||
14
app/model/SysConfig.php
Normal file
14
app/model/SysConfig.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
|
||||||
|
class SysConfig extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $table = "sys_config";
|
||||||
|
protected $pk = 'config_id';
|
||||||
|
}
|
||||||
93
app/model/SysDictionary.php
Normal file
93
app/model/SysDictionary.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use app\observer\DictionaryObserver;
|
||||||
|
use think\Model;
|
||||||
|
use think\model\Collection;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
use think\model\relation\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统字典模型
|
||||||
|
* @property int $dict_id
|
||||||
|
* @property string $dict_code
|
||||||
|
* @property string $dict_name
|
||||||
|
* @property string $dict_type
|
||||||
|
* @property int $sort_number
|
||||||
|
* @property string $comments
|
||||||
|
* @property int $is_system
|
||||||
|
* @property int $deleted
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
* @property SysDictionaryData[]|Collection $dictData
|
||||||
|
* @property array $dictDataCodes
|
||||||
|
*/
|
||||||
|
class SysDictionary extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
|
||||||
|
protected $name = "sys_dictionary";
|
||||||
|
protected $pk = "dict_id";
|
||||||
|
protected string $eventObserver = DictionaryObserver::class;
|
||||||
|
|
||||||
|
public function dictData(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(SysDictionaryData::class, 'dict_id', 'dict_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// public static function onAfterInsert(Model|SysDictionary $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionary::class, 'onAfterInsert'], "添加了新字典集", $model);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onAfterDelete(Model|SysDictionary $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionary::class, 'onAfterDelete'], "字典集{$model->dict_name}被删除", $model);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onBeforeUpdate(Model|SysDictionary $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionary::class, 'onBeforeUpdate'], "修改了{$model->dict_name}字典集", $model);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static function getByDictCode(string $dictCode)
|
||||||
|
{
|
||||||
|
return self::with(['dictData'])->where('dict_code', $dictCode)->findOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeDictCode($query, $dictCode)
|
||||||
|
{
|
||||||
|
$query->with(['dictData'])->where('dict_code', $dictCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDictDataCodesAttr()
|
||||||
|
{
|
||||||
|
return $this->dictData->column('dict_data_code');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByDictDataCode(string $dictDataCode, mixed $default = null)
|
||||||
|
{
|
||||||
|
$dictDataMap = $this->dictData->column('dict_data_name', 'dict_data_code');
|
||||||
|
|
||||||
|
return $dictDataMap[$dictDataCode] ?? $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchGroupAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->where('group_tag', '=', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchDictNameAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->where('dict_name', 'like', "%$value%");
|
||||||
|
}
|
||||||
|
// public function searchDictCodeAttr($query, $value): void
|
||||||
|
// {
|
||||||
|
// $query->where('dict_code', '=', $value);
|
||||||
|
// }
|
||||||
|
}
|
||||||
83
app/model/SysDictionaryData.php
Normal file
83
app/model/SysDictionaryData.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use app\observer\DictionaryObserver;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\Model;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统字典模型
|
||||||
|
* @property int $dict_data_id
|
||||||
|
* @property int $dict_id
|
||||||
|
* @property string $dict_data_code
|
||||||
|
* @property string $dict_data_name
|
||||||
|
* @property int $sort_number
|
||||||
|
* @property string $comments
|
||||||
|
* @property int $deleted
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
*/
|
||||||
|
class SysDictionaryData extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $name = "sys_dictionary_data";
|
||||||
|
protected $pk = "dict_data_id";
|
||||||
|
|
||||||
|
protected string $eventObserver = DictionaryObserver::class;
|
||||||
|
|
||||||
|
//
|
||||||
|
// public static function onBeforeRestore(Model|SysDictionaryData $model)
|
||||||
|
// {
|
||||||
|
// $model->set('deleted', 0);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onBeforeDelete(Model|SysDictionaryData $model)
|
||||||
|
// {
|
||||||
|
// $model->set('deleted', 1);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onAfterInsert(Model|SysDictionaryData $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionaryData::class, 'onAfterInsert'], "添加了新字典", $model);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onAfterDelete(Model|SysDictionaryData $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionaryData::class, 'onAfterDelete'], "字典{$model->dict_data_name}被删除", $model);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static function onBeforeUpdate(Model|SysDictionaryData $model): void
|
||||||
|
// {
|
||||||
|
// security_log_record([SysDictionaryData::class, 'onBeforeUpdate'], "修改了{$model->dict_data_name}字典", $model->getChangedDataDA());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function searchDictCodeAttr($query, $value): void
|
||||||
|
// {
|
||||||
|
// $query->where('dict_id', function (Query $query) use ($value) {
|
||||||
|
// return $query->table(SysDictionary::getTable())->where('dict_code', $value)->field('dict_id');
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public function searchDictDataNameAttr($query, string $value, array $data = []): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('dict_data_name', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchDictDataCodeAttr($query, string $value, array $data = []): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('dict_data_code', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchDictIdAttr($query, string|int $value, array $data = []): void
|
||||||
|
{
|
||||||
|
(is_numeric($value) && $value > 0) && $query->where('dict_id', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/model/SysFileRecord.php
Normal file
44
app/model/SysFileRecord.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统文件模型
|
||||||
|
*/
|
||||||
|
class SysFileRecord extends BaseModel
|
||||||
|
{
|
||||||
|
protected $name = "sys_file_record";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
public function createUser(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysUser::class, 'user_id', 'create_user_id')->field('user_id,nickname,avatar,username,status');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchNameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('name', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPathAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('path', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchCreateNicknameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->whereIn('create_user_id', SysUser::where('username', $value)->column('user_id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchCreateTimeAttr(Query $query, array $value, array $data): void
|
||||||
|
{
|
||||||
|
$value = array_filter($value);
|
||||||
|
count($value) >= 2 && $query->whereBetweenTime('request_time', $value[0], $value[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
27
app/model/SysFileRule.php
Normal file
27
app/model/SysFileRule.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\adminapi\model\TenantModel;
|
||||||
|
use app\BaseModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property string $disk 存储磁盘
|
||||||
|
* @property string $path_rule 路径规则
|
||||||
|
* @property string $name_rule 命名规则
|
||||||
|
* @property string $query Query参数携带
|
||||||
|
* @property string $permissions
|
||||||
|
* #1 文件大小限制(单位:KB)
|
||||||
|
* @property int $rule_file_size_limit_min
|
||||||
|
* @property int $rule_file_size_limit_max
|
||||||
|
* #2 文件类型限制
|
||||||
|
* @property string $rule_file_ext
|
||||||
|
* @property string $rule_file_mime
|
||||||
|
* #3 图片类型限制
|
||||||
|
* @property string $rule_image
|
||||||
|
*/
|
||||||
|
class SysFileRule extends BaseModel
|
||||||
|
{
|
||||||
|
protected $name = "sys_file_rule";
|
||||||
|
protected $pk = "id";
|
||||||
|
}
|
||||||
43
app/model/SysLoginRecord.php
Normal file
43
app/model/SysLoginRecord.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
class SysLoginRecord extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $name = "sys_login_record";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
|
||||||
|
public function user(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysUser::class, 'username', 'username')->field('user_id,username,nickname');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchUsernameAttr(Query $query, string $value, array $data): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('username', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchLoginTypeAttr(Query $query, int $value, array $data): void
|
||||||
|
{
|
||||||
|
$value > 0 && $query->where('login_type', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchNicknameAttr(Query $query, string $value, array $data): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->whereIn('username', SysUser::where('username', 'like', "%{$value}%")->column('username'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchCreateTimeAttr(Query $query, array $value, array $data): void
|
||||||
|
{
|
||||||
|
$value = array_filter($value);
|
||||||
|
count($value) >= 2 && $query->whereBetweenTime('create_time', $value[0], $value[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
79
app/model/SysMenu.php
Normal file
79
app/model/SysMenu.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\Model;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
use think\model\relation\BelongsToMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $menu_id
|
||||||
|
* @property int $parent_id
|
||||||
|
* @property string $title
|
||||||
|
* @property string $path
|
||||||
|
* @property string $component
|
||||||
|
* @property int $menu_type
|
||||||
|
* @property int $sort_number
|
||||||
|
* @property string $authority
|
||||||
|
* @property string $icon
|
||||||
|
* @property int $hide
|
||||||
|
* @property string $meta
|
||||||
|
* @property int $deleted
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
*/
|
||||||
|
class SysMenu extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
|
||||||
|
protected $name = "sys_menu";
|
||||||
|
protected $pk = "menu_id";
|
||||||
|
|
||||||
|
public function roles(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(SysRole::class, SysRoleMenu::class, 'menu_id', 'role_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件定义
|
||||||
|
public static function onBeforeRestore(Model|SysMenu $model)
|
||||||
|
{
|
||||||
|
$model->set('deleted', 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onBeforeDelete(Model|SysMenu $model): bool
|
||||||
|
{
|
||||||
|
$model->set('deleted', 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterInsert(Model|SysMenu $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysMenu::class, 'onAfterInsert'], "添加了新菜单", $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterDelete(Model|SysMenu $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysMenu::class, 'onAfterDelete'], "菜单{$model->title}被删除", $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索定义
|
||||||
|
public function searchTitleAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value !== '' && $query->where('title', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchAuthorityAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value !== '' && $query->where('authority', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPathAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value !== '' && $query->where('path', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/model/SysOperateRecord.php
Normal file
34
app/model/SysOperateRecord.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
|
||||||
|
class SysOperateRecord extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $name = "sys_operate_record";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
public function operateUser(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysUser::class, 'user_id', 'operate_user_id')->field('user_id,nickname,avatar,username,status');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysRequestRecord::class, 'context_id', 'context_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function searchCreateTimeAttr(Query $query, array $value, array $data): void
|
||||||
|
{
|
||||||
|
$value = array_filter($value);
|
||||||
|
count($value) >= 2 && $query->whereBetweenTime('create_time', $value[0], $value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
app/model/SysOrganization.php
Normal file
70
app/model/SysOrganization.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\Model;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $organization_id
|
||||||
|
* @property int $parent_id
|
||||||
|
* @property string $organization_name
|
||||||
|
* @property string $organization_full_name
|
||||||
|
* @property string $organization_code
|
||||||
|
* @property string $organization_type
|
||||||
|
* @property int $leader_id
|
||||||
|
* @property int $sort_number
|
||||||
|
* @property string $comments
|
||||||
|
* @property int $deleted
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
*/
|
||||||
|
class SysOrganization extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $name = "sys_organization";
|
||||||
|
protected $pk = "organization_id";
|
||||||
|
|
||||||
|
// 事件定义
|
||||||
|
|
||||||
|
public static function onAfterInsert(Model|SysOrganization $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysOrganization::class, 'onAfterInsert'], "添加了新机构{$model->organization_full_name}", $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterDelete(Model|SysOrganization $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysOrganization::class, 'onAfterDelete'], "机构{$model->organization_full_name}被删除", $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索定义
|
||||||
|
public function searchOrganizationNameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('organization_name', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchOrganizationFullNameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('organization_full_name', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchOrganizationCodeAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('organization_code', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchOrganizationTypeAttr(Query $query, int $value): void
|
||||||
|
{
|
||||||
|
$value > 0 && $query->where('organization_type', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrganizationTypeNameAttr($value, $data): string
|
||||||
|
{
|
||||||
|
return dict_get('organization_type.' . $data['organization_type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/model/SysRequestRecord.php
Normal file
22
app/model/SysRequestRecord.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\db\Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统接口请求记录模型
|
||||||
|
*/
|
||||||
|
class SysRequestRecord extends BaseModel
|
||||||
|
{
|
||||||
|
protected $name = "sys_request_record";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
public function searchCreateTimeAttr(Query $query, array $value, array $data): void
|
||||||
|
{
|
||||||
|
$value = array_filter($value);
|
||||||
|
count($value) >= 2 && $query->whereBetweenTime('request_time', $value[0], $value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/model/SysRole.php
Normal file
71
app/model/SysRole.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\Model;
|
||||||
|
use think\model\Collection;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
use think\model\relation\BelongsToMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统角色模型
|
||||||
|
* @property int $role_id
|
||||||
|
* @property string $role_name
|
||||||
|
* @property string $role_code
|
||||||
|
* @property string $comments
|
||||||
|
* @property int $deleted
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
* @property SysUser[]|Collection $users 角色存在多个用户
|
||||||
|
* @property SysMenu[]|Collection $menus 角色存在多个菜单
|
||||||
|
*/
|
||||||
|
class SysRole extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $name = "sys_role";
|
||||||
|
protected $pk = "role_id";
|
||||||
|
|
||||||
|
|
||||||
|
// 关联定义
|
||||||
|
public function users(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(SysRole::class, SysUserRole::class, 'role_id', 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menus(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(SysMenu::class, SysRoleMenu::class, 'menu_id', 'role_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索定义
|
||||||
|
public function searchRoleNameAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('role_name', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchRoleCodeAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('role_code', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchCommentsAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$value != '' && $query->where('comments', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件定义
|
||||||
|
|
||||||
|
public static function onAfterInsert(Model|SysRole $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysRole::class, 'onAfterInsert'], "添加了新角色", $model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterDelete(SysRole $model): void
|
||||||
|
{
|
||||||
|
security_log_record([SysRole::class, 'onAfterDelete'], "角色{$model->role_name}被删除", $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/model/SysRoleMenu.php
Normal file
23
app/model/SysRoleMenu.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
|
||||||
|
use think\model\Pivot;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
class SysRoleMenu extends Pivot
|
||||||
|
{
|
||||||
|
protected $name = "sys_role_menu";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
public function role(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysRole::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menu(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysMenu::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
126
app/model/SysUser.php
Normal file
126
app/model/SysUser.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\Collection;
|
||||||
|
use think\db\Query;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
use think\model\relation\BelongsToMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户模型
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $username
|
||||||
|
* @property string $password
|
||||||
|
* @property string $nickname
|
||||||
|
* @property string $avatar
|
||||||
|
* @property int $sex
|
||||||
|
* @property string $birthday
|
||||||
|
* @property string $phone
|
||||||
|
* @property string $email
|
||||||
|
* @property integer $email_verified
|
||||||
|
* @property string $real_name
|
||||||
|
* @property string $id_card
|
||||||
|
* @property string $introduction
|
||||||
|
* @property int $organization_id
|
||||||
|
* @property int $status
|
||||||
|
* @property int $deleted
|
||||||
|
* @property int $tenant_id
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
* @property string $delete_time
|
||||||
|
* @property SysRole[]|Collection $roles
|
||||||
|
* @property SysMenu[]|Collection $menus
|
||||||
|
*/
|
||||||
|
class SysUser extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
|
||||||
|
protected $name = "sys_user";
|
||||||
|
protected $pk = "user_id";
|
||||||
|
|
||||||
|
public function roles(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(SysRole::class, SysUserRole::class, 'role_id', 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthoritiesAttr(): array
|
||||||
|
{
|
||||||
|
$menus = [];
|
||||||
|
$this->roles->load(['menus'], true);
|
||||||
|
foreach ($this->roles as $role) {
|
||||||
|
$menus = array_merge($menus, $role->menus->hidden(['pivot'])->where('deleted', 0)->toArray());
|
||||||
|
}
|
||||||
|
$uniqueMenus = [];
|
||||||
|
foreach ($menus as $menu) {
|
||||||
|
$uniqueMenus[$menu['menuId']] = $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
$array = array_values($uniqueMenus);
|
||||||
|
usort($array, function($a, $b) {
|
||||||
|
return $a['sortNumber'] <=> $b['sortNumber'];
|
||||||
|
});
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attr@性别
|
||||||
|
* @param $value
|
||||||
|
* @param $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSexNameAttr($value, $data): string
|
||||||
|
{
|
||||||
|
return (string)dict_get('sex.' . $data['sex'], '未知');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function searchOrganizationIdAttr(Query $query, int $value, array $data): void
|
||||||
|
{
|
||||||
|
($value > 0) && $query->where('organization_id', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function searchCreateTimeAttr($query, array $value): void
|
||||||
|
{
|
||||||
|
$query->whereBetweenTime('create_time', $value[0], $value[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchNicknameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('nickname', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchUsernameAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('username', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchSexAttr(Query $query, int $value): void
|
||||||
|
{
|
||||||
|
($value > 0) && $query->where('sex', '=', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchStatusAttr(Query $query, int $value): void
|
||||||
|
{
|
||||||
|
($value > 0) && $query->where('status', '=', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchEmailAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('email', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPhoneAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('phone', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchIdCardAttr(Query $query, string $value): void
|
||||||
|
{
|
||||||
|
$value != "" && $query->where('id_card', 'like', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
app/model/SysUserFile.php
Normal file
63
app/model/SysUserFile.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use app\adminapi\model\TenantModel;
|
||||||
|
use app\BaseModel;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 系统用户文件
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $name
|
||||||
|
*/
|
||||||
|
class SysUserFile extends BaseModel
|
||||||
|
{
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $name = "sys_user_file";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
public function getThumbnailAttr($thumbnail, $row): string
|
||||||
|
{
|
||||||
|
return $row['is_directory'] ? '' : (string)$row['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDownloadUrlAttr($downloadUrl, $row): string
|
||||||
|
{
|
||||||
|
return $row['is_directory'] ? '' : (string)$row['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUrlAttr($url, $row): string
|
||||||
|
{
|
||||||
|
return $row['is_directory'] ? '' : (string)$row['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createUser(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysUser::class, 'user_id', 'user_id')->field('user_id,username,nickname,avatar');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchNameAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->whereLike('name', '%' . $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchContentTypeAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->whereLike('content_type', $value . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchParentIdAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->where('parent_id', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchUserIdAttr($query, $value): void
|
||||||
|
{
|
||||||
|
$query->where('user_id', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
app/model/SysUserRole.php
Normal file
40
app/model/SysUserRole.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\model;
|
||||||
|
|
||||||
|
use think\model\Pivot;
|
||||||
|
use think\model\relation\HasOne;
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 系统(用户/角色)关联模型
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property int $role_id
|
||||||
|
* @property int $tenant_id
|
||||||
|
* @property string $create_time
|
||||||
|
* @property string $update_time
|
||||||
|
*/
|
||||||
|
class SysUserRole extends Pivot
|
||||||
|
{
|
||||||
|
protected $name = "sys_user_role";
|
||||||
|
protected $pk = "id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
* @return HasOne
|
||||||
|
*/
|
||||||
|
public function role(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysRole::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
* @return HasOne
|
||||||
|
*/
|
||||||
|
public function user(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysUser::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/observer/DictionaryObserver.php
Normal file
45
app/observer/DictionaryObserver.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\observer;
|
||||||
|
|
||||||
|
|
||||||
|
use app\model\SysDictionary;
|
||||||
|
use app\model\SysDictionaryData;
|
||||||
|
|
||||||
|
class DictionaryObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 更新/创建数据事件l
|
||||||
|
* @param SysDictionary|SysDictionaryData $model
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function onAfterWrite(SysDictionary|SysDictionaryData $model)
|
||||||
|
{
|
||||||
|
$this->clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除数据事件
|
||||||
|
* @param SysDictionary|SysDictionaryData $model
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function onAfterDelete(SysDictionary|SysDictionaryData $model)
|
||||||
|
{
|
||||||
|
$this->clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复数据事件
|
||||||
|
* @param SysDictionary|SysDictionaryData $model
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function onAfterRestore(SysDictionary|SysDictionaryData $model)
|
||||||
|
{
|
||||||
|
$this->clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearCache()
|
||||||
|
{
|
||||||
|
dict()->clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
app/provider.php
Normal file
9
app/provider.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
use app\ExceptionHandle;
|
||||||
|
use app\Request;
|
||||||
|
|
||||||
|
// 容器Provider定义文件
|
||||||
|
return [
|
||||||
|
'think\Request' => Request::class,
|
||||||
|
'think\exception\Handle' => ExceptionHandle::class,
|
||||||
|
];
|
||||||
15
app/service.php
Normal file
15
app/service.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use app\AppService;
|
||||||
|
use app\service\ConfigService;
|
||||||
|
use app\service\DictionaryService;
|
||||||
|
use app\service\SecurityService;
|
||||||
|
|
||||||
|
// 系统服务定义文件
|
||||||
|
// 服务在完成全局初始化之后执行
|
||||||
|
return [
|
||||||
|
AppService::class,
|
||||||
|
ConfigService::class,
|
||||||
|
DictionaryService::class,
|
||||||
|
SecurityService::class,
|
||||||
|
];
|
||||||
31
app/service/ConfigService.php
Normal file
31
app/service/ConfigService.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service;
|
||||||
|
|
||||||
|
use app\model\SysConfig;
|
||||||
|
use think\facade\Config;
|
||||||
|
use think\Service;
|
||||||
|
|
||||||
|
class ConfigService extends Service
|
||||||
|
{
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
Config::hook(function ($name, $default = null) {
|
||||||
|
if($name == "admin.jwt_key") {
|
||||||
|
return "123456a";
|
||||||
|
}
|
||||||
|
return $default;
|
||||||
|
// $value = self::getDynamicConfig($name);
|
||||||
|
// // 对配置参数和值进行处理后返回最终的配置值
|
||||||
|
// // ...
|
||||||
|
// return is_null($value) ? $default : $value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDynamicConfig(string $key)
|
||||||
|
{
|
||||||
|
// return SysConfig::where('key', $key)
|
||||||
|
// ->cache(60)
|
||||||
|
// ->value('value');
|
||||||
|
}
|
||||||
|
}
|
||||||
65
app/service/CurdService.php
Normal file
65
app/service/CurdService.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service;
|
||||||
|
|
||||||
|
use app\BaseModel;
|
||||||
|
use app\Request;
|
||||||
|
use think\Collection;
|
||||||
|
use think\db\exception\DataNotFoundException;
|
||||||
|
use think\db\exception\DbException;
|
||||||
|
use think\db\exception\ModelNotFoundException;
|
||||||
|
use think\helper\Str;
|
||||||
|
use think\Paginator;
|
||||||
|
use think\Service;
|
||||||
|
|
||||||
|
class CurdService extends Service
|
||||||
|
{
|
||||||
|
static int $MAX_LIMIT = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param BaseModel $model
|
||||||
|
* @param null $order
|
||||||
|
* @return Collection
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public static function getList(Request $request, mixed $model, $order = null): Collection
|
||||||
|
{
|
||||||
|
$offset = (int)$request->param('offset', 0);
|
||||||
|
$limit = (int)$request->param('limit', 30);
|
||||||
|
$limit = min($limit, self::$MAX_LIMIT);
|
||||||
|
|
||||||
|
return $model->order(!is_null($order) ? $order : (string)$model->getPk(), 'desc')
|
||||||
|
->limit($offset, $limit)
|
||||||
|
->select();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param BaseModel $model
|
||||||
|
* @param null $order
|
||||||
|
* @return Paginator
|
||||||
|
* @throws DbException
|
||||||
|
*/
|
||||||
|
public static function getPaginate(Request $request, mixed $model, $order = null)
|
||||||
|
{
|
||||||
|
$page = (int)$request->param('page', 1);
|
||||||
|
$page = max(1, $page);
|
||||||
|
$limit = (int)$request->param('limit', 30);
|
||||||
|
$limit = min($limit, self::$MAX_LIMIT);
|
||||||
|
// 前端自定义排序条件
|
||||||
|
$sort = $request->param('sort');
|
||||||
|
$sort_order = $request->param('order');
|
||||||
|
if (!empty($sort) && in_array($sort_order, ['desc', 'asc'])) {
|
||||||
|
$order = [Str::snake($sort) => $sort_order];
|
||||||
|
}
|
||||||
|
return $model->order(!is_null($order) ? $order : (string)$model->getPk(), 'desc')
|
||||||
|
->limit($page, $limit)
|
||||||
|
->paginate([
|
||||||
|
'list_rows' => $limit,
|
||||||
|
'page' => $page,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
83
app/service/DictionaryService.php
Normal file
83
app/service/DictionaryService.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service;
|
||||||
|
|
||||||
|
use app\entity\SysDictionary;
|
||||||
|
use think\facade\Cache;
|
||||||
|
use think\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典服务
|
||||||
|
*/
|
||||||
|
class DictionaryService extends Service
|
||||||
|
{
|
||||||
|
readonly protected array $sysDictData;
|
||||||
|
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 注册到全局容器中
|
||||||
|
*/
|
||||||
|
$this->app->bind('dict', $this);
|
||||||
|
/*
|
||||||
|
* 添加应用事件初始化字典内容
|
||||||
|
*/
|
||||||
|
$this->app->event->listen("AppInit", function () {
|
||||||
|
$this->sysDictData = Cache::remember('sysDict', function () {
|
||||||
|
$array = [];
|
||||||
|
SysDictionary::with(['dictData'])->select()->each(function (SysDictionary|\app\model\SysDictionary $dict) use (&$array) {
|
||||||
|
$array[$dict->dict_code] = $dict->dictData->column('dict_data_name', 'dict_data_code');
|
||||||
|
});
|
||||||
|
return $array;
|
||||||
|
}, 3600 * 4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典
|
||||||
|
* @param string $name
|
||||||
|
* @param $default
|
||||||
|
* @param $cache
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $name, $default = null, $cache = true)
|
||||||
|
{
|
||||||
|
if (!$cache) {
|
||||||
|
return $this->getValue($name, $default);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* 通过缓存判断减少计算操作(使用内存换取性能)
|
||||||
|
*/
|
||||||
|
static $cacheValue = [];
|
||||||
|
if (!isset($cacheValue[$name]) || $cacheValue[$name]['0'] !== $default) {
|
||||||
|
$cacheValue[$name] = ['v' => $this->getValue($name, $default), '0' => $default];
|
||||||
|
}
|
||||||
|
return $cacheValue[$name]['v'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典元素
|
||||||
|
* @param string $name
|
||||||
|
* @param $default
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function getValue(string $name, $default = null): mixed
|
||||||
|
{
|
||||||
|
$keys = explode('.', $name);
|
||||||
|
if (count($keys) == 1) {
|
||||||
|
return $this->sysDictData[$name] ?? (is_array($default) ? $default : []);
|
||||||
|
}
|
||||||
|
$code = array_pop($keys);
|
||||||
|
$key = implode('.', $keys);
|
||||||
|
if (isset($this->sysDictData[$key])) {
|
||||||
|
return $this->sysDictData[$key][$code] ?? $default;
|
||||||
|
}
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearCache()
|
||||||
|
{
|
||||||
|
Cache::delete('sysDict');
|
||||||
|
}
|
||||||
|
}
|
||||||
50
app/service/SecurityService.php
Normal file
50
app/service/SecurityService.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service;
|
||||||
|
|
||||||
|
use app\model\SysOperateRecord;
|
||||||
|
use think\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全服务
|
||||||
|
*/
|
||||||
|
class SecurityService extends Service
|
||||||
|
{
|
||||||
|
protected array $securityLogRecords = [];
|
||||||
|
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 添加安全日志记录事件
|
||||||
|
*/
|
||||||
|
$this->app->event->listen("sys:securityLogRecord", function ($trigger) {
|
||||||
|
|
||||||
|
[[$location, $event], $message, $data] = $trigger;
|
||||||
|
$model = new SysOperateRecord();
|
||||||
|
$model->setOption('convertNameToCamel', false);
|
||||||
|
$model->location = $location;
|
||||||
|
$model->event = $event;
|
||||||
|
$model->description = $message;
|
||||||
|
$model->create_time = date('Y-m-d H:i:s');
|
||||||
|
$model->data = json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||||
|
$this->securityLogRecords[] = $model->toArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->event->listen("HttpEnd", function () {
|
||||||
|
$auth = $this->app->request->getAuth();
|
||||||
|
$context_id = $this->app->request->contextId;
|
||||||
|
$operate_user_id = null;
|
||||||
|
if ($auth) {
|
||||||
|
$operate_user_id = $auth->userId;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 保存接口操作记录到日志表
|
||||||
|
*/
|
||||||
|
(new SysOperateRecord)->insertAll(array_map(function ($item) use ($operate_user_id, $context_id) {
|
||||||
|
$item['operate_user_id'] = $operate_user_id;
|
||||||
|
$item['context_id'] = $context_id;
|
||||||
|
return $item;
|
||||||
|
}, $this->securityLogRecords));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
120
app/service/admin/LoginService.php
Normal file
120
app/service/admin/LoginService.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service\admin;
|
||||||
|
|
||||||
|
use app\http\HttpAuth;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
use Hashids\Hashids;
|
||||||
|
use stdClass;
|
||||||
|
use app\entity\{SysLoginRecord, SysUser};
|
||||||
|
use UnexpectedValueException;
|
||||||
|
use app\enum\{UserLoginEnum, UserStatusEnum, UserTypeEnum};
|
||||||
|
use app\http\HttpClient;
|
||||||
|
use app\Request;
|
||||||
|
use app\validate\auth\LoginValidate;
|
||||||
|
use Exception;
|
||||||
|
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
|
||||||
|
class LoginService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 账号登录
|
||||||
|
* @param Request $request
|
||||||
|
* @param HttpClient $client
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @param string $code
|
||||||
|
* @param string $remember
|
||||||
|
* @return array
|
||||||
|
* @throws DataNotFoundException
|
||||||
|
* @throws DbException
|
||||||
|
* @throws ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function login(Request $request, HttpClient $client, string $username, string $password, string $code, string $remember): array
|
||||||
|
{
|
||||||
|
$clientName = $client->name;
|
||||||
|
validate(LoginValidate::class)
|
||||||
|
->scene($clientName . 'Login')
|
||||||
|
->check(compact('username', 'password', 'remember', 'code'));
|
||||||
|
$user = SysUser::where(['username' => $username])->find();
|
||||||
|
if (empty($user)) {
|
||||||
|
$this->saveLoginRecord($request, $username, UserLoginEnum::USERNAME_ERROR, "用户名错误");
|
||||||
|
throw new ValidateException("用户名或密码错误");
|
||||||
|
}
|
||||||
|
if (!password_verify($password, $user->password)) {
|
||||||
|
$this->saveLoginRecord($request, $username, UserLoginEnum::PASSWORD_ERROR, "密码错误");
|
||||||
|
throw new ValidateException("用户名或密码错误");
|
||||||
|
}
|
||||||
|
if ($user->status == UserStatusEnum::Disable->value) {
|
||||||
|
$this->saveLoginRecord($request, $username, UserLoginEnum::STATUS_DISABLED, "账号被禁用");
|
||||||
|
throw new ValidateException("账户不可用");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$access_token = $user->getAccessToken();
|
||||||
|
$user = $user->append(['authorities', 'roles'])->toArray();
|
||||||
|
$this->saveLoginRecord($request, $username, UserLoginEnum::LOGIN_SUCCESS, "登录成功");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->saveLoginRecord($request, $username, UserLoginEnum::LOGIN_EXCEPTION, $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
return compact('user', 'access_token');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存登录记录
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $username
|
||||||
|
* @param UserLoginEnum $loginType
|
||||||
|
* @param string $comments
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function saveLoginRecord(Request $request, string $username, UserLoginEnum $loginType, string $comments): void
|
||||||
|
{
|
||||||
|
$sysLoginRecord = new SysLoginRecord();
|
||||||
|
$sysLoginRecord->save([
|
||||||
|
'username' => $username,
|
||||||
|
'os' => $request->getOs(),
|
||||||
|
'device' => $request->getDevice(),
|
||||||
|
'browser' => $request->getBrowser(),
|
||||||
|
'ip' => $request->ip(),
|
||||||
|
'comments' => $comments,
|
||||||
|
'login_type' => $loginType->value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function checkUserAccessToken(string $token): HttpAuth
|
||||||
|
{
|
||||||
|
if (empty($token)) {
|
||||||
|
throw new ValidateException('凭证错误');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$jwtKey = (string)config('admin.jwt_key', '');
|
||||||
|
$headers = new stdClass();
|
||||||
|
$data = JWT::decode($token, new Key($jwtKey, 'HS256'), $headers);
|
||||||
|
} catch (UnexpectedValueException $e) {
|
||||||
|
if($e->getMessage() == "Expired token") {
|
||||||
|
throw new ValidateException('凭证过期');
|
||||||
|
}
|
||||||
|
throw new ValidateException('校验失败' . $e->getMessage());
|
||||||
|
}
|
||||||
|
if ($data->exp <= (time() - 180)) {
|
||||||
|
// 还要三分钟就要过期
|
||||||
|
throw new ValidateException('凭证过期');
|
||||||
|
}
|
||||||
|
$hashids = new Hashids($jwtKey, 8);
|
||||||
|
|
||||||
|
$user_id = $hashids->decode($data->uid)[0] ?? 0;
|
||||||
|
if (empty($user_id)) {
|
||||||
|
throw new ValidateException('凭证错误');
|
||||||
|
}
|
||||||
|
return new HttpAuth($user_id, UserTypeEnum::USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVisitor(\think\Request $request): HttpAuth
|
||||||
|
{
|
||||||
|
return new HttpAuth(-1, UserTypeEnum::VISITOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/subscribe/SysUserSubscribe.php
Normal file
18
app/subscribe/SysUserSubscribe.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\subscribe;
|
||||||
|
|
||||||
|
use app\model\SysUser;
|
||||||
|
|
||||||
|
class SysUserSubscribe
|
||||||
|
{
|
||||||
|
public function onUserLogin(SysUser $user)
|
||||||
|
{
|
||||||
|
// UserLogin事件响应处理
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUserLogout(SysUser $user)
|
||||||
|
{
|
||||||
|
// UserLogout事件响应处理
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/validate/auth/ClientValidate.php
Normal file
20
app/validate/auth/ClientValidate.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\validate\auth;
|
||||||
|
|
||||||
|
use app\enum\ClientEnum;
|
||||||
|
use think\Validate;
|
||||||
|
|
||||||
|
class ClientValidate extends Validate
|
||||||
|
{
|
||||||
|
protected array $rule = [
|
||||||
|
'name' => ClientEnum::class, // 客户端类型(PAdmin:PC管理端,IAdmin:手机管理端)
|
||||||
|
'uuid' => 'max:64', // 客户端UUID
|
||||||
|
'version' => 'max:10', // 客户端版本号
|
||||||
|
];
|
||||||
|
protected $message = [
|
||||||
|
'name.enum' => '客户端类型错误',
|
||||||
|
'uuid.max' => '客户端UUID错误',
|
||||||
|
'version.max' => '客户端版本号错误',
|
||||||
|
];
|
||||||
|
}
|
||||||
15
app/validate/auth/LoginValidate.php
Normal file
15
app/validate/auth/LoginValidate.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\validate\auth;
|
||||||
|
|
||||||
|
use app\enum\ClientEnum;
|
||||||
|
use think\Validate;
|
||||||
|
|
||||||
|
class LoginValidate extends Validate
|
||||||
|
{
|
||||||
|
protected array $must = ['username', 'password'];
|
||||||
|
protected array $rule = [
|
||||||
|
'username' => 'max:20',
|
||||||
|
'password' => 'min:5|max:64'
|
||||||
|
];
|
||||||
|
}
|
||||||
54
composer.json
Normal file
54
composer.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "topthink/think",
|
||||||
|
"description": "the new thinkphp framework",
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"type": "project",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"thinkphp",
|
||||||
|
"ORM"
|
||||||
|
],
|
||||||
|
"homepage": "https://www.thinkphp.cn/",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "liu21st",
|
||||||
|
"email": "liu21st@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "yunwuxin",
|
||||||
|
"email": "448901948@qq.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0",
|
||||||
|
"topthink/framework": "^8.0",
|
||||||
|
"topthink/think-orm": "^4.0",
|
||||||
|
"topthink/think-filesystem": "^3.0",
|
||||||
|
"firebase/php-jwt": "dev-main",
|
||||||
|
"hashids/hashids": "5.0.x-dev",
|
||||||
|
"phpmailer/phpmailer": "dev-master",
|
||||||
|
"topthink/think-helper": "^3.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"topthink/think-dumper": "^1.0",
|
||||||
|
"topthink/think-trace": "^2.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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
22
config/admin.php
Normal file
22
config/admin.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use think\response\Json;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'jwt_key' => 'oasdasdasd',
|
||||||
|
'httpLog' => [
|
||||||
|
'open' => true,
|
||||||
|
'open_start_time' => 0,
|
||||||
|
'open_end_time' => 0,
|
||||||
|
'recordRules' => [
|
||||||
|
'include_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
|
||||||
|
'include_responses' => [Json::class],
|
||||||
|
'include_codes' => [],
|
||||||
|
'exclude_codes' => [],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'cls' => [
|
||||||
|
'secretId' => 'AKIDz6HXnBZ0Pm5UtEXlv5BThrwvsmcM0a5e',
|
||||||
|
'secretKey' => 'eGoIj6QiYSLBFNTsIdv8GxZOWaWWcn8R'
|
||||||
|
]
|
||||||
|
];
|
||||||
30
config/app.php
Normal file
30
config/app.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 应用设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 应用的命名空间
|
||||||
|
'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' => false,
|
||||||
|
];
|
||||||
29
config/cache.php
Normal file
29
config/cache.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 缓存设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 默认缓存驱动
|
||||||
|
'default' => 'file',
|
||||||
|
|
||||||
|
// 缓存连接方式配置
|
||||||
|
'stores' => [
|
||||||
|
'file' => [
|
||||||
|
// 驱动方式
|
||||||
|
'type' => 'File',
|
||||||
|
// 缓存保存目录
|
||||||
|
'path' => '',
|
||||||
|
// 缓存前缀
|
||||||
|
'prefix' => '',
|
||||||
|
// 缓存有效期 0表示永久缓存
|
||||||
|
'expire' => 0,
|
||||||
|
// 缓存标签前缀
|
||||||
|
'tag_prefix' => 'tag:',
|
||||||
|
// 序列化机制 例如 ['serialize', 'unserialize']
|
||||||
|
'serialize' => [],
|
||||||
|
],
|
||||||
|
// 更多的缓存连接
|
||||||
|
],
|
||||||
|
];
|
||||||
9
config/console.php
Normal file
9
config/console.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 控制台配置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
return [
|
||||||
|
// 指令定义
|
||||||
|
'commands' => [
|
||||||
|
],
|
||||||
|
];
|
||||||
20
config/cookie.php
Normal file
20
config/cookie.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Cookie设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
return [
|
||||||
|
// cookie 保存时间
|
||||||
|
'expire' => 0,
|
||||||
|
// cookie 保存路径
|
||||||
|
'path' => '/',
|
||||||
|
// cookie 有效域名
|
||||||
|
'domain' => '',
|
||||||
|
// cookie 启用安全传输
|
||||||
|
'secure' => false,
|
||||||
|
// httponly设置
|
||||||
|
'httponly' => false,
|
||||||
|
// 是否使用 setcookie
|
||||||
|
'setcookie' => true,
|
||||||
|
// samesite 设置,支持 'strict' 'lax'
|
||||||
|
'samesite' => '',
|
||||||
|
];
|
||||||
63
config/database.php
Normal file
63
config/database.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 默认使用的数据库连接配置
|
||||||
|
'default' => env('DB_DRIVER', 'mysql'),
|
||||||
|
|
||||||
|
// 自定义时间查询规则
|
||||||
|
'time_query_rule' => [],
|
||||||
|
|
||||||
|
// 自动写入时间戳字段
|
||||||
|
// true为自动识别类型 false关闭
|
||||||
|
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
|
||||||
|
'auto_timestamp' => true,
|
||||||
|
|
||||||
|
// 时间字段取出后的默认时间格式
|
||||||
|
'datetime_format' => 'Y-m-d H:i:s',
|
||||||
|
|
||||||
|
// 时间字段配置 配置格式:create_time,update_time
|
||||||
|
'datetime_field' => '',
|
||||||
|
|
||||||
|
// 数据库连接配置信息
|
||||||
|
'connections' => [
|
||||||
|
'mysql' => [
|
||||||
|
// 数据库类型
|
||||||
|
'type' => env('DB_TYPE', 'mysql'),
|
||||||
|
// 服务器地址
|
||||||
|
'hostname' => env('DB_HOST', '127.0.0.1'),
|
||||||
|
// 数据库名
|
||||||
|
'database' => env('DB_NAME', ''),
|
||||||
|
// 用户名
|
||||||
|
'username' => env('DB_USER', 'root'),
|
||||||
|
// 密码
|
||||||
|
'password' => env('DB_PASS', ''),
|
||||||
|
// 端口
|
||||||
|
'hostport' => env('DB_PORT', '3306'),
|
||||||
|
// 数据库连接参数
|
||||||
|
'params' => [],
|
||||||
|
// 数据库编码
|
||||||
|
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||||
|
// 数据库表前缀
|
||||||
|
'prefix' => env('DB_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,
|
||||||
|
],
|
||||||
|
|
||||||
|
// 更多的数据库配置信息
|
||||||
|
],
|
||||||
|
];
|
||||||
24
config/filesystem.php
Normal file
24
config/filesystem.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 默认磁盘
|
||||||
|
'default' => 'local',
|
||||||
|
// 磁盘列表
|
||||||
|
'disks' => [
|
||||||
|
'local' => [
|
||||||
|
'type' => 'local',
|
||||||
|
'root' => app()->getRuntimePath() . 'storage',
|
||||||
|
],
|
||||||
|
'public' => [
|
||||||
|
// 磁盘类型
|
||||||
|
'type' => 'local',
|
||||||
|
// 磁盘路径
|
||||||
|
'root' => app()->getRootPath() . 'public/storage',
|
||||||
|
// 磁盘路径对应的外部URL路径
|
||||||
|
'url' => '/storage',
|
||||||
|
// 可见性
|
||||||
|
'visibility' => 'public',
|
||||||
|
],
|
||||||
|
// 更多的磁盘配置信息
|
||||||
|
],
|
||||||
|
];
|
||||||
29
config/lang.php
Normal file
29
config/lang.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 多语言设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
|
||||||
|
return [
|
||||||
|
// 默认语言
|
||||||
|
'default_lang' => env('DEFAULT_LANG', 'zh-cn'),
|
||||||
|
// 自动侦测浏览器语言
|
||||||
|
'auto_detect_browser' => true,
|
||||||
|
// 允许的语言列表
|
||||||
|
'allow_lang_list' => [],
|
||||||
|
// 多语言自动侦测变量名
|
||||||
|
'detect_var' => 'lang',
|
||||||
|
// 是否使用Cookie记录
|
||||||
|
'use_cookie' => true,
|
||||||
|
// 多语言cookie变量
|
||||||
|
'cookie_var' => 'think_lang',
|
||||||
|
// 多语言header变量
|
||||||
|
'header_var' => 'think-lang',
|
||||||
|
// 扩展语言包
|
||||||
|
'extend_list' => [],
|
||||||
|
// Accept-Language转义为对应语言包名称
|
||||||
|
'accept_language' => [
|
||||||
|
'zh-hans-cn' => 'zh-cn',
|
||||||
|
],
|
||||||
|
// 是否支持语言分组
|
||||||
|
'allow_group' => false,
|
||||||
|
];
|
||||||
45
config/log.php
Normal file
45
config/log.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 日志设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
return [
|
||||||
|
// 默认日志记录通道
|
||||||
|
'default' => '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
Normal file
8
config/middleware.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
// 中间件配置
|
||||||
|
return [
|
||||||
|
// 别名或分组
|
||||||
|
'alias' => [],
|
||||||
|
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
|
||||||
|
'priority' => [],
|
||||||
|
];
|
||||||
55
config/route.php
Normal file
55
config/route.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | 路由设置
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
|
||||||
|
return [
|
||||||
|
// pathinfo分隔符
|
||||||
|
'pathinfo_depr' => '/',
|
||||||
|
// 是否开启路由延迟解析
|
||||||
|
'url_lazy_route' => false,
|
||||||
|
// 是否强制使用路由
|
||||||
|
'url_route_must' => false,
|
||||||
|
// 是否区分大小写
|
||||||
|
'url_case_sensitive' => false,
|
||||||
|
// 自动扫描子目录分组
|
||||||
|
'route_auto_group' => false,
|
||||||
|
// 合并路由规则
|
||||||
|
'route_rule_merge' => false,
|
||||||
|
// 路由是否完全匹配
|
||||||
|
'route_complete_match' => false,
|
||||||
|
// 去除斜杠
|
||||||
|
'remove_slash' => false,
|
||||||
|
// 默认的路由变量规则
|
||||||
|
'default_route_pattern' => '[\w\.]+',
|
||||||
|
// URL伪静态后缀
|
||||||
|
'url_html_suffix' => 'html',
|
||||||
|
// 访问控制器层名称
|
||||||
|
'controller_layer' => 'controller',
|
||||||
|
// 空控制器名
|
||||||
|
'empty_controller' => 'Error',
|
||||||
|
// 是否使用控制器后缀
|
||||||
|
'controller_suffix' => false,
|
||||||
|
// 默认模块名(开启自动多模块有效)
|
||||||
|
'default_module' => 'index',
|
||||||
|
// 默认控制器名
|
||||||
|
'default_controller' => 'Index',
|
||||||
|
// 默认操作名
|
||||||
|
'default_action' => 'index',
|
||||||
|
// 操作方法后缀
|
||||||
|
'action_suffix' => '',
|
||||||
|
// 非路由变量是否使用普通参数方式(用于URL生成)
|
||||||
|
'url_common_param' => true,
|
||||||
|
// 操作方法的参数绑定方式 route get param
|
||||||
|
'action_bind_param' => 'get',
|
||||||
|
// 请求缓存规则 true为自动规则
|
||||||
|
'request_cache_key' => true,
|
||||||
|
// 请求缓存有效期
|
||||||
|
'request_cache_expire' => null,
|
||||||
|
// 全局请求缓存排除规则
|
||||||
|
'request_cache_except' => [],
|
||||||
|
// 请求缓存的Tag
|
||||||
|
'request_cache_tag' => '',
|
||||||
|
// API版本header变量
|
||||||
|
'api_version' => 'Api-Version',
|
||||||
|
];
|
||||||
19
config/session.php
Normal file
19
config/session.php
Normal file
@ -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
Normal file
10
config/trace.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Trace设置 开启调试模式后有效
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
return [
|
||||||
|
// 内置Html和Console两种方式 支持扩展
|
||||||
|
'type' => 'Html',
|
||||||
|
// 读取的日志通道名
|
||||||
|
'channel' => '',
|
||||||
|
];
|
||||||
25
config/view.php
Normal file
25
config/view.php
Normal file
@ -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' => '}',
|
||||||
|
];
|
||||||
2
extend/.gitignore
vendored
Normal file
2
extend/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
8
public/.htaccess
Normal file
8
public/.htaccess
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
Options +FollowSymlinks -Multiviews
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
|
||||||
|
</IfModule>
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
25
public/index.php
Normal file
25
public/index.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Author: liu21st <liu21st@gmail.com>
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
|
||||||
|
use think\App;
|
||||||
|
|
||||||
|
// [ 应用入口文件 ]
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
// 执行HTTP应用并响应
|
||||||
|
$http = (new App())->http;
|
||||||
|
|
||||||
|
$response = $http->run();
|
||||||
|
|
||||||
|
$response->send();
|
||||||
|
|
||||||
|
$http->end($response);
|
||||||
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
19
public/router.php
Normal file
19
public/router.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Author: liu21st <liu21st@gmail.com>
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
|
||||||
|
|
||||||
|
require __DIR__ . "/index.php";
|
||||||
|
}
|
||||||
2
public/static/.gitignore
vendored
Normal file
2
public/static/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
112
route/api.php
Normal file
112
route/api.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use app\controller\admin\system\OrganizationController;
|
||||||
|
use app\entity\SysMenu;
|
||||||
|
use app\entity\SysOrganization;
|
||||||
|
use app\http\middleware\ClientMiddleware;
|
||||||
|
use app\model\SysDictionary;
|
||||||
|
use app\controller\admin\{auth,
|
||||||
|
system\DictionaryController,
|
||||||
|
system\DictionaryDataController,
|
||||||
|
system\FileController,
|
||||||
|
system\LoginRecordController,
|
||||||
|
system\MenuController,
|
||||||
|
system\OperateRecordController,
|
||||||
|
system\RequestRecordController,
|
||||||
|
system\RoleController,
|
||||||
|
system\UserController};
|
||||||
|
use app\http\middleware\AuthMiddleware;
|
||||||
|
use think\facade\Route;
|
||||||
|
use think\middleware\AllowCrossDomain;
|
||||||
|
|
||||||
|
Route::group("adminapi", function () {
|
||||||
|
/*
|
||||||
|
* 用户
|
||||||
|
*/
|
||||||
|
Route::get("captcha", [auth\LoginController::class, "captcha"])->name("admin.LoginCaptcha");
|
||||||
|
|
||||||
|
Route::post("login", [auth\LoginController::class, "index"])->name("admin.SysUserLogin")->middleware([ClientMiddleware::class]);
|
||||||
|
Route::group(function () {
|
||||||
|
|
||||||
|
|
||||||
|
Route::group("auth", function () {
|
||||||
|
Route::post("logout", [auth\AuthController::class, "logout"])->name("admin.SysUserLogout");
|
||||||
|
Route::get("user", [auth\AuthController::class, "user"])->name("admin.SysUserInfo");
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 系统文件管理
|
||||||
|
*/
|
||||||
|
Route::get('file/page', [FileController::class, "page"])->name("system.pageFiles");
|
||||||
|
Route::delete('file/remove/batch', [FileController::class, "batchRemove"])->name("system.batchRemoveFile");
|
||||||
|
|
||||||
|
|
||||||
|
Route::group("system", function () {
|
||||||
|
Route::get('login-record/page', [LoginRecordController::class, "page"])->name("system.pageLoginRecords");
|
||||||
|
Route::get('operate-record/page', [OperateRecordController::class, "page"])->name("system.pageOperateRecords");
|
||||||
|
Route::get('request-record/page', [RequestRecordController::class, "page"])->name("system.pageRequestRecords");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 用户管理
|
||||||
|
*/
|
||||||
|
Route::get("user/page", [UserController::class, "page"])->name("system.pageUsers");
|
||||||
|
Route::delete("user/batch", [UserController::class, "batchRemove"])->name("system.batchRemoveUser");
|
||||||
|
Route::post("user", [UserController::class, "add"])->name("system.addUser");
|
||||||
|
Route::put("user$", [UserController::class, "update"])->name("system.updateUser");
|
||||||
|
Route::get("user/existence", [UserController::class, "existence"])->name("system.userExistence");
|
||||||
|
Route::put("user/status", [UserController::class, "updateStatus"])->name("system.updateUserStatus");
|
||||||
|
Route::put("user/password", [UserController::class, "updatePassword"])->name("system.updateUserPassword");
|
||||||
|
/*
|
||||||
|
* 角色管理
|
||||||
|
*/
|
||||||
|
Route::get("role$", [RoleController::class, "list"])->name("system.listRoles");
|
||||||
|
Route::get("role/page", [RoleController::class, "page"])->name("system.pageRoles");
|
||||||
|
Route::delete("role/batch", [RoleController::class, "removeRoles"])->name("system.removeRoles");
|
||||||
|
Route::get("role-menu/:role_id", [RoleController::class, "getMenu"])->name("system.getRoleMenu");
|
||||||
|
Route::put("role-menu/:role_id", [RoleController::class, "updateMenu"])->name("system.updateRoleMenu");
|
||||||
|
/*
|
||||||
|
* 菜单管理
|
||||||
|
*/
|
||||||
|
Route::get("menu$", [MenuController::class, "list"])->name("system.listMenus");
|
||||||
|
Route::post("menu", [MenuController::class, "add"])->name("system.addMenu");
|
||||||
|
Route::delete("menu/:menu_id", [MenuController::class, "remove"])
|
||||||
|
->model('menu_id', SysMenu::class)
|
||||||
|
->name("system.removeMenu");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 机构管理
|
||||||
|
*/
|
||||||
|
Route::get("organization", [OrganizationController::class, "lists"])->name("system.getOrganizationList");
|
||||||
|
Route::post("organization", [OrganizationController::class, "add"])->name("system.addOrganization");
|
||||||
|
Route::put("organization", [OrganizationController::class, "update"])->name("system.updateOrganization");
|
||||||
|
Route::delete("organization/:organization_id", [OrganizationController::class, "remove"])
|
||||||
|
->model('organization_id', SysOrganization::class)
|
||||||
|
->name("system.removeOrganization");
|
||||||
|
/*
|
||||||
|
* 字典管理
|
||||||
|
*/
|
||||||
|
Route::get("dictionary$", [DictionaryController::class, "lists"])->name("system.getDictionaryList");
|
||||||
|
Route::post("dictionary", [DictionaryController::class, "add"])->name("system.addDictionary");
|
||||||
|
Route::put("dictionary", [DictionaryController::class, "update"])->name("system.updateDictionary");
|
||||||
|
Route::delete("dictionary/:dict_id", [DictionaryController::class, "remove"])->model('dict_id', SysDictionary::class)->name("system.removeDictionary");
|
||||||
|
/*
|
||||||
|
* 字典数据管理
|
||||||
|
*/
|
||||||
|
Route::get("dictionary-data$", [DictionaryDataController::class, "lists"])->name("system.getDictionaryData");
|
||||||
|
Route::get("dictionary-data/page", [DictionaryDataController::class, "page"])->name("system.pageDictionaryData");
|
||||||
|
Route::post("dictionary-data", [DictionaryDataController::class, "add"])->name("system.addDictionaryData");
|
||||||
|
Route::put("dictionary-data", [DictionaryDataController::class, "update"])->name("system.updateDictionaryData");
|
||||||
|
Route::delete("dictionary-data/batch", [DictionaryDataController::class, "batchRemove"])->name("system.batchRemoveDictionaryData");
|
||||||
|
|
||||||
|
})->name('系统接口');
|
||||||
|
|
||||||
|
})->middleware([ClientMiddleware::class, AuthMiddleware::class]);
|
||||||
|
|
||||||
|
})->layer('admin')
|
||||||
|
->middleware([AllowCrossDomain::class], [
|
||||||
|
'Access-Control-Allow-Headers' => 'Client, Client-Version, Client-Id, Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With'
|
||||||
|
]);
|
||||||
|
// AllowCrossDomain 缺一个预检检查.
|
||||||
|
//if($request->isOptions()) {
|
||||||
|
// return \response()->header($header)->code(204);
|
||||||
|
//}
|
||||||
17
route/app.php
Normal file
17
route/app.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
// | Author: liu21st <liu21st@gmail.com>
|
||||||
|
// +----------------------------------------------------------------------
|
||||||
|
use think\facade\Route;
|
||||||
|
|
||||||
|
Route::get('think', function () {
|
||||||
|
return 'hello,ThinkPHP8!';
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::get('hello/:name', 'index/hello');
|
||||||
2
runtime/.gitignore
vendored
Normal file
2
runtime/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user