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