Initial commit

This commit is contained in:
php_Team 2023-06-28 18:45:02 +08:00
commit 0de8b9adc0
132 changed files with 19550 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/.idea
/.vscode
/vendor
/public/uploads/**
*.log
.env
*.http
runtime/*
app/admin/controller/Gen/Gen.php

42
.travis.yml Normal file
View 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
View File

@ -0,0 +1,32 @@
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
版权所有Copyright © 2006-2016 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.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
ThinkPHP 6.0
===============
> 运行环境要求PHP7.1+兼容PHP8.0。
[官方应用服务市场](https://market.topthink.com) | [`ThinkAPI`——官方统一API服务](https://docs.topthink.com/think-api)
ThinkPHPV6.0版本由[亿速云](https://www.yisu.com/)独家赞助发布。
## 主要新特性
* 采用`PHP7`强类型(严格模式)
* 支持更多的`PSR`规范
* 原生多应用支持
* 更强大和易用的查询
* 全新的事件系统
* 模型事件和数据库事件统一纳入事件系统
* 模板引擎分离出核心
* 内部功能中间件化
* SESSION/Cookie机制改进
* 对Swoole以及协程支持改进
* 对IDE更加友好
* 统一和精简大量用法
## 安装
~~~
composer create-project topthink/think tp 6.0.*
~~~
如果需要更新框架使用
~~~
composer update topthink/framework
~~~
## 文档
[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
## 参与开发
请参阅 [ThinkPHP 核心框架包](https://github.com/top-think/framework)。
## 版权信息
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2020 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
更多细节参阅 [LICENSE.txt](LICENSE.txt)

1
app/.htaccess Normal file
View File

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

22
app/AppService.php Normal file
View File

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

138
app/BaseController.php Normal file
View File

@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Request;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, $validate, array $message = [], bool $batch = true)
{
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 callable $cb
* @param array $args
* @return mixed
* @date 2022-07-22
* @example
* @author admin
* @since 1.0.0
*/
protected static function transaction(callable $cb, array $args = [])
{
Db::startTrans();
try {
$result = invoke($cb, $args);
Db::commit();
return $result;
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 分页包装器
*
* @param $query
* @date 2022-03-03
* @example
* @author admin
* @since 1.0.0
*/
protected static function pageWrapper($query)
{
return (clone $query)->page(
(int) Request::param('page', 1),
(int) Request::param('limit', 10)
);
}
}

16
app/BaseModel.php Normal file
View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace app;
use app\common\traits\Model as TraitsModel;
use think\Model;
/**
* 模型层基础类
*/
abstract class BaseModel extends Model
{
use TraitsModel;
}

16
app/BasePivot.php Normal file
View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace app;
use app\common\traits\Model as TraitsModel;
use think\model\Pivot;
/**
* 模型层基础类
*/
abstract class BasePivot extends Pivot
{
use TraitsModel;
}

133
app/ExceptionHandle.php Normal file
View File

@ -0,0 +1,133 @@
<?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;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use app\common\exception\LoginTimeOut;
use app\common\exception\NotAuthApi;
use think\facade\Env;
use app\common\arw\adjfut\src\Exception\ErrorMsg as ExceptionErrorMsg;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
LoginTimeOut::class,
NotAuthApi::class,
ErrorMsg::class,
ExceptionErrorMsg::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 \app\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 未授权接口
if ($e instanceof NotAuthApi) {
return $this->respone([
'code' => 40050,
'msg' => $e->getMessage(),
]);
}
// 登录超时
if ($e instanceof LoginTimeOut) {
return $this->respone([
'code' => 40026,
'msg' => $request->isDev() ? $e->getMessage() : '登录超时',
]);
}
// 参数验证不通过
if ($e instanceof ValidateException) {
return $this->respone([
'code' => 40030,
'msg' => $e->getMessage()
]);
}
// 业务异常
if ($e instanceof ErrorMsg || $e instanceof ExceptionErrorMsg) {
$code = $e->getCode();
return $this->respone([
'code' => $code == 0 ? 40052 : $code,
'msg' => $e->getMessage(),
], $e);
}
// 服务器异常
// if ($e instanceof Exception) {
// return $this->respone([
// 'code' => 40051,
// 'msg' => '服务器异常',
// ], $e);
// }
// 其他错误交给系统处理
return parent::render($request, $e);
}
/**
* 返回
*
* @param array $data
* @param Throwable $e
* @return Response
* @date 2022-04-15
* @example
* @author admin
* @since 1.0.0
*/
private function respone(array $data, $e = null): Response
{
$request = $this->app->request;
if ($e && Env::get('app_show_error')) {
$data['__error'] = [
'File' => $e->getFile(),
'Line' => $e->getLine(),
'Message' => $e->getMessage(),
'Trace' => $e->getTrace()
];
$data['__param'] = $request->param();
}
if ($request->isAjax()) {
return Response::create($data, 'json');
} else {
return Response::create(json_encode($data), 'html');
}
}
}

80
app/Request.php Normal file
View File

@ -0,0 +1,80 @@
<?php
namespace app;
use app\common\model\Token;
use app\common\model\User\User;
use think\facade\Env;
// 应用请求对象类
class Request extends \think\Request
{
/**
* 获取当前用户
*
* @return User
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public static function getCurrentUser(): User
{
return Token::getCurrentUser();
}
/**
* 获取当前token
*
* @return Token
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public static function getCurrentToken(): Token
{
return Token::getCurrent();
}
/**
* 是否登陆
*
* @return boolean
* @date 2022-12-30
* @example
* @author admin
* @since 1.0.0
*/
public static function isLogin(): bool
{
return Token::isLogin();
}
/**
* 是否开发模式
*
* @date 2022-04-07
* @example
* @author admin
* @since 1.0.0
*/
public static function isDev(): bool
{
return !self::isProd();
}
/**
* 是否生产环境
*
* @date 2022-04-07
* @example
* @author admin
* @since 1.0.0
*/
public static function isProd(): bool
{
return Env::get('app_mode') === 'production';
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace app\admin\controller\ChatGpt;
use app\Request;
use app\common\model\Flow\Flow as ModelFlow;
use think\db\Where;
use think\facade\Validate;
use app\BaseController;
use app\exception\ErrorMsg;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use Goutte\Client;
use Orhanerday\OpenAi\OpenAi;
class ChatGpt extends BaseController
{
/**
* chatGpt测试
*/
public function Test(Request $request)
{
$api_key = '';
$message = "问题内容";
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.openai.com/v1/completions',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $api_key
],
CURLOPT_POSTFIELDS => json_encode([
'prompt' => $message, //你的内容
'model' => 'text-davinci-003', //机器人3代
'max_tokens' => 4000, //最大字符串
'temperature' => 0.5 //回答精准度
])
]);
// 请求结束
$domain = curl_exec($curl);
curl_close($curl);
//处理请求的数据
$domain_array = json_decode($domain, true);
$data = $domain_array['choices'][0]['text']; //该数组输出最后回答的内容F
return [
'code' => 0,
'data' => $data
];
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace app\admin\controller;
use app\model\Banner as ModelBanner;
use app\BaseController;
use app\Request;
use app\exception\ErrorMsg;
use think\facade\Validate;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use think\facade\Db;
use think\facade\Env;
class Common extends BaseController
{
/**
* 上传图片
*/
public function uploadImg(Request $request)
{
$dirName = $request->param('dirName');
$upload = new UploadFile('uploads', 'file');
$path = $upload->putFile($dirName . 'Img');
$url = "/uploads/" . $path;
//图片大小>500k压缩图片质量
$absolute_path = public_path() . $url;
if (ceil(filesize($absolute_path) / 1000) > 500) {
$image = \think\Image::open($absolute_path);
$image->save($absolute_path, null, 80);
}
return [
'code' => 0,
'data' => [
"name" => $path,
"url" => $url,
],
'msg' => '上传成功!'
];
}
/**
* 上传文件
*/
public function uploadFile(Request $request)
{
$dirName = $request->param('dirName');
$upload = new UploadFile('uploads', 'file');
$path = $upload->putFile($dirName . 'File');
return [
'code' => 0,
'data' => [
"name" => $path,
"url" => "/uploads/" . $path,
],
'msg' => '上传成功!'
];
}
/**
* 上传视频
*/
public function uploadVideo(Request $request)
{
$dirName = $request->param('dirName');
$upload = new UploadFile('uploads', 'file');
$path = $upload->putFile($dirName . 'File');
return [
'code' => 0,
'data' => [
"name" => $path,
"url" => "/uploads/" . $path,
],
'msg' => '上传成功!'
];
}
}

View File

@ -0,0 +1,234 @@
<?php
namespace app\admin\controller\Dictionary;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use app\common\arw\adjfut\src\Traverse;
use app\BaseController;
use app\exception\ErrorMsg;
use app\common\model\Dictionary\Dictionary as ModelDictionary;
use app\Request;
class Dictionary extends BaseController
{
/**
* 获取字典列表
*
* @param Request $request
* @date 2022-03-14
* @example
* @author admin
* @since 1.0.0
*/
public function getDictionaryList(Request $request): array
{
$select = ModelDictionary::where('dictionary_parent_guid', 0)->order([
'dictionary_index',
'dictionary_order' => 'desc',
])->select();
return [
'code' => 0,
'msg' => 'ok',
'data' => $select
];
}
/**
* 获取菜单树
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function getDictionaryTree(Request $request)
{
$dictionary = ModelDictionary::withoutGlobalScope(['status'])->order([
'dictionary_index',
'dictionary_order' => 'desc',
])->field([
'dictionary_parent_guid',
'dictionary_name',
'dictionary_index',
'dictionary_value',
'dictionary_order',
'dictionary_status',
'dictionary_allow_update',
'dictionary_list_class',
'dictionary_guid',
])->select()->toArray();
array_unshift($dictionary, [
'dictionary_guid' => '0',
'dictionary_parent_guid' => '-1',
'dictionary_name' => '系统字典',
'dictionary_index' => 0,
'dictionary_value' => '',
'dictionary_order' => 0,
'dictionary_status' => 0,
'dictionary_allow_update' => 2,
'dictionary_list_class' => '',
]);
$Traverse = new Traverse('dictionary_guid', 'dictionary_parent_guid');
$dictionaryTree = $Traverse->tree($dictionary, '-1', function ($v) {
return [
'label' => $v['dictionary_name'],
'id' => $v['dictionary_guid'],
'parent_id' => $v['dictionary_parent_guid'],
'index' => $v['dictionary_index'],
'order' => $v['dictionary_order'],
'status' => $v['dictionary_status'],
'allow' => $v['dictionary_allow_update'],
'value' => $v['dictionary_value'],
'list_class' => $v['dictionary_list_class']
];
});
return [
'code' => 0,
'msg' => 'ok',
'data' => $dictionaryTree
];
}
/**
* 添加字典
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function addDictionary(Request $request)
{
$params = $request->param();
$this->validate($params, [
'dictionary_name' => 'require',
'dictionary_index' => 'require',
'dictionary_order' => 'require',
'dictionary_parent_guid' => 'require',
]);
if (isset($params['dictionary_guid'])) {
unset($params['dictionary_guid']);
}
ModelDictionary::create($params);
return [
'code' => 0,
'msg' => '添加成功'
];
}
/**
* 更新部门
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function updateDictionary(Request $request)
{
$params = $request->param();
$this->validate($params, [
'dictionary_guid' => 'require'
]);
ModelDictionary::withoutGlobalScope(['status'])->update($params, [
'dictionary_guid' => $params['dictionary_guid']
]);
return [
'code' => 0,
'msg' => '更新成功'
];
}
/**
* 删除菜单
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function deleteDictionary(Request $request)
{
$params = $request->param();
$this->validate($params, [
'dictionary_guid' => 'require'
]);
ModelDictionary::find($params['dictionary_guid'])->delete();
return [
'code' => 0,
'msg' => '删除成功'
];
}
// 获取字典
public function getDictionary(Request $request)
{
$dictionary_value = $request->param('dictionary_value');
$dictionary_guid = ModelDictionary::where('dictionary_value', $dictionary_value)->find()['dictionary_guid'];
$res = ModelDictionary::field([
'dictionary_guid',
'dictionary_parent_guid',
'dictionary_name',
'dictionary_value',
'dictionary_list_class',
])->where('dictionary_parent_guid', $dictionary_guid)
->where('dictionary_status', 1)->select();
if (!$res) throwErrorMsg("字典不存在");
else return $res;
}
/**
* 下载导入模板
*/
public function downloadTemplate(Request $request)
{
$params = $request->param();
$data = [
[
'上级字典',
'名称',
'值',
'层级',
'排序',
'回显',
]
];
$data[] = ['','状态','status','1','0','',];
$data[] = ['状态','启用','1','2','1','primary'];
$data[] = ['状态','禁用','2','2','2','danger',];
$excel = (new Excel())->exporTsheet($data);
$excel->save('字典导入模板.xlsx');
}
/**
* 导入excel
*/
public function importExcel(Request $request)
{
$file = new UploadFile('uploads', 'fileExt:xlsx');
$file->putFile('Dictionary');
$msg = ModelDictionary::importExcel($file);
return [
'code' => 0,
'msg' => $msg
];
}
}

View File

@ -0,0 +1,308 @@
<?php
namespace app\admin\controller\Flow;
use app\Request;
use app\common\model\Flow\Flow as ModelFlow;
use think\db\Where;
use think\facade\Validate;
use app\BaseController;
use app\exception\ErrorMsg;
use think\facade\Filesystem;
class Flow extends BaseController
{
// (new ModelFlow)->track();
/**
* 查询流量访问记录
* @throws \think\db\exception\DbException
*/
public function getFlowRecord(Request $request): array
{
$query = ModelFlow::where([]);
$select = self::pageWrapper($query)
->field([
'flow_id',
'flow_record_no',
'flow_visitor_ip',
'flow_location',
'flow_create_time',
'flow_source',
'flow_target',
'flow_os',
'flow_browser'
])
->order('flow_id', 'desc')
->select();
$count = $query->count();
return [
'msg' => '查询成功',
'code' => 0,
'data' => $select,
'count' => $count
];
}
/**
* 查询流量来源统计
*/
public function getFlowSourceCount(): array
{
$query = ModelFlow::where([]);
$count = $query->count();
$select = $query->field([
'flow_id',
'flow_source',
'flow_target',
'flow_os',
])->select();
$GDP = $query->field([
'flow_source',
])->group('flow_source')->select();
foreach ($GDP as $k => &$i) {
$i['number'] = 0;
$i['percentage'] = 0;
}
foreach ($select as $key => &$item) {
foreach ($GDP as $k => &$i) {
if ($item['flow_source'] === $i['flow_source']) {
$i['number'] += 1;
}
}
}
foreach ($GDP as $k => $i) {
$i['percentage'] = round($i['number'] / $count * 100);
}
return [
'msg' => '查询完成',
'code' => 0,
'count' => $count,
'data' => [
'GDP' => $GDP,
'select' => $select
]
];
}
/**
* 查询流量浏览器统计
*/
public function getFlowBrowserCount(): array
{
$query = ModelFlow::where([]);
$count = $query->count();
$select = $query->field([
'flow_id',
'flow_browser',
])->select();
$GDP = $query->field([
'flow_browser',
])->group('flow_browser')->select();
foreach ($GDP as $k => &$i) {
$i['number'] = 0;
$i['percentage'] = 0;
}
foreach ($select as $key => &$item) {
foreach ($GDP as $k => &$i) {
if ($item['flow_browser'] === $i['flow_browser']) {
$i['number'] += 1;
}
}
}
foreach ($GDP as $k => $i) {
$i['percentage'] = round($i['number'] / $count * 100);
}
return [
'msg' => '查询完成',
'code' => 0,
'count' => $count,
'data' => [
'select' => $select,
'GDP' => $GDP
]
];
}
/**
* 查询流量月统计
*/
public function getFlowMonthCount(Request $request): array
{
$current_year = input('post.current_year') ?? 0;
$query = ModelFlow::where([]);
$current_year = date('Y', strtotime("$current_year year"));
$count = $query->whereYear('flow_create_time', $current_year)
->field([
'flow_create_time',
])
->select()->count();
$select = $query->whereYear('flow_create_time', $current_year)
->field([
'flow_id',
'flow_record_no',
'flow_visitor_ip',
'flow_location',
'flow_create_time',
'flow_source',
'flow_target',
'flow_os',
'flow_browser'
])
->order('flow_id', 'desc')
->select()->toArray();
$res_select = [];
for ($i = 1; $i <= 12; $i++) {
array_push($res_select, [
'flow_Date' => $current_year .
'-' .
date('m', strtotime($current_year . '-' . $i)),
'number' => 0,
'percentage' => 0
]);
}
foreach ($select as $key => &$item) {
$date_format = explode('-', explode(' ', $item['flow_create_time'])[0]);
$item['flow_Date'] = $date_format[0] . '-' . $date_format[1];
foreach ($res_select as $k => &$it) {
if ($item['flow_Date'] == $it['flow_Date']) {
$it['number'] += 1;
}
}
foreach ($res_select as $k => &$it) {
$it['percentage'] = round($it['number'] / $count * 100);
}
};
foreach ($res_select as $k => &$it) {
foreach ($select as $key => &$item) {
if ($it['flow_Date'] === $item['flow_Date']) {
$it = array_merge($it, $item);
}
}
}
return [
'code' => 0,
'msg' => '查询成功',
'count' => $count,
'data' => [
'current_date' => ['y' => $current_year],
'select' => $res_select
]
];
}
/**
* 查询流量日统计
*/
public function getFlowDayCount(Request $request)
{
$current_month = input('post.current_month') ?? 0;
// return ['a'=>$current_month];
$query = ModelFlow::where([]);
$current_month_date = date('Y-m', strtotime(date('Y-m-01') . "$current_month month"));
// return ['a'=>$current_month_date];
$count = $query->whereMonth('flow_create_time', $current_month_date)
->field([
'flow_create_time',
])
->select()->count();
// $select = $query->whereMonth('flow_create_time', $current_month_date)
// ->field([
// 'flow_create_time',
// ])
// ->order('flow_id', 'desc')
// ->select()->toArray();
$select = $query->whereMonth('flow_create_time', $current_month_date)
->field([
'flow_create_time',
])
->buildSql();
return $select;
$GDP = [];
$res_select = [];
for ($i = 1; $i <= date('t', strtotime(date('Y-m-01') . "$current_month month")); $i++) {
array_push($res_select, [
'week' => $this->getWeek(date('Y-m', strtotime(date('Y-m-01') . "$current_month month"))
. '-'
. date('d', strtotime(date('Y-m', strtotime(date('Y-m-01') . "$current_month month")) . "-$i"))),
'flow_Date' => date('Y-m', strtotime(date('Y-m-01') . "$current_month month"))
. '-'
. date('d', strtotime(date('Y-m', strtotime(date('Y-m-01') . "$current_month month")) . "-$i")),
'number' => 0, 'percentage' => 0
]);
}
foreach ($select as $key => &$item) {
$item['flow_Date'] = explode(' ', $item['flow_create_time'])[0];
foreach ($res_select as $k => &$it) {
if ($item['flow_Date'] == $it['flow_Date']) {
$it['number'] += 1;
}
}
foreach ($res_select as $k => &$it) {
$it['percentage'] = round($it['number'] / $count * 100);
}
};
foreach ($res_select as $k => &$it) {
foreach ($select as $key => &$item) {
if ($it['flow_Date'] === $item['flow_Date']) {
$it = array_merge($it, $item);
}
}
}
return [
'code' => 0,
'msg' => '查询成功',
'count' => $count,
'data' => [
'current_date' => ['y' => date('Y', strtotime(date('Y-m-01') . "$current_month month")), 'm' => date('m', strtotime(date('Y-m-01') . "$current_month month"))],
'select' => $res_select
]
];
}
/**
* @param $date
* @return string
* @descript 获取指定日期星期
*/
private function getWeek($date): string
{
//强制转换日期格式
$date_str = date('Y-m-d', strtotime($date));
//封装成数组
$arr = explode("-", $date_str);
//参数赋值
//年
$year = $arr[0];
//月输出2位整型不够2位右对齐
$month = sprintf('%02d', $arr[1]);
//日输出2位整型不够2位右对齐
$day = sprintf('%02d', $arr[2]);
//时分秒默认赋值为0
$hour = $minute = $second = 0;
//转换成时间戳
$strap = mktime($hour, $minute, $second, $month, $day, $year);
//获取数字型星期几
$number_wk = date("w", $strap);
//自定义星期数组
$weekArr = array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");
//获取数字对应的星期
return $weekArr[$number_wk];
}
}

View File

@ -0,0 +1,517 @@
<?php
namespace app\admin\controller\Gen\GenApi;
use app\admin\controller\Gen\Gen;
/**
* 后端生成类
*/
class GenApi
{
/**
* 项目根目录路径
* @var string
*/
protected $root;
/**
* 字段信息
* @var array
*/
protected $fields = [];
/**
* 生成信息
* @var array
*/
protected $table = [];
/**
* 实体类名
* @var string User
*/
protected $class_name;
/**
* 模块名
* @var string User
*/
protected $module_name;
/**
* 业务名 user
* @var string
*/
protected $business_name;
/**
* 方法名
* @var string 例:用户
*/
protected $function_name;
/**
* 后端生成模板文件路径配置
* @var array
*/
protected const TEMP_PATH_CONFIG = [
'crud' => [ //单表(增删改查)
'api' => 'resources/view/api/controller.tpl',
'admin' => 'resources/view/admin/controller.tpl',
'model' => 'resources/view/admin/model.tpl',
],
'crud_sort' => [ //单表(增删改查+排序处理)
'api' => 'resources/view/api/controller.tpl',
'admin' => 'resources/view/business/sort/sortAdminController.tpl',
'model' => 'resources/view/business/sort/sortModel.tpl',
],
'web_static' => [ //门户静态
'api' => 'resources/view/business/webApiController.tpl',
'admin' => 'resources/view/business/webController.tpl',
'model' => 'resources/view/admin/model.tpl',
],
];
/**
* 模型层模板读取路径
* @var string
*/
protected $model_temp_path;
/**
* 模型层模块生成路径
* @var string
*/
protected $model_module_path;
/**
* 模型层生成路径
* @var string
*/
protected $model_path;
/**
* 后台控制器模板读取路径
* @var string
*/
protected $admin_con_temp_path;
/**
* 后台控制器模块生成路径
* @var string
*/
protected $admin_con_module_path;
/**
* 后台控制器生成路径
* @var string
*/
protected $admin_con_path;
/**
* 前台控制器模板读取路径
* @var string
*/
protected $api_con_temp_path;
/**
* 前台控制器生成路径
* @var string
*/
protected $api_con_path;
/**
* 前台控制器模块生成路径
* @var string
*/
protected $api_con_module_path;
/**
* 新增功能必要字段
* @var array
*/
protected $add_required_fields = ['guid', 'create_user_guid', 'update_user_guid'];
/**
* 新增允许字段模板字符串
* @var string :['user_name','user_id',...]
*/
protected $add_fields_temp;
/**
* 新增必填验证模板字符串
* @var string : ['user_name|用户名称' => 'require',...]
*/
protected $add_required_verify_temp;
/**
* 修改功能必要字段
* @var array
*/
protected $edit_required_fields = ['update_user_guid'];
/**
* 编辑允许字段模板字符串
* @var string :['user_name','user_id',...]
*/
protected $edit_fields_temp;
/**
* 编辑必填验证模板字符串
* @var string : ['user_name|用户名称' => 'require',...]
*/
protected $edit_required_verify_temp;
/**
* 业务字段
* @var array :[ 'user_name' => '用户名称',...]
*/
protected $business_fields = [];
/**
* 是否导入
* @var bool
*/
protected $is_import = false;
/**
* 是否导出
* @var bool
*/
protected $is_export = false;
/**
* 是否排序处理
* @var bool
*/
protected $is_sort = false;
/**
* 图片字段数组
* @var array
*/
protected $img_fields = [];
/**
* 生成后端模板类型
* @var string crud:: 单表(增删改查) | web_static: 门户静态模块(查询修改)
*/
protected $gen_api_type;
/**
* 查询列表显示字段
* @var array :['user_name','user_id',...]
*/
protected $query_list_fields = [];
/**
* 排序字段
* @var string
*/
protected $sort_field;
/**
* 排序类型
* @var string
*/
protected $sort_type;
/**
* 查询列表条件模板字符串
* @var string 例子:Tool::getOptionalQuery(['user_name', 'LIKE'])
*/
protected $query_list_where_temp;
/**
* 构造器
*
* @param array $fields 字段信息
* @param array $table 生成信息
*/
public function __construct(array $fields, array $table)
{
//基本参数初始化
$this->root = base_path();
$this->fields = $fields;
$this->table = $table;
$this->class_name = $this->table['className'];
$this->module_name = $this->table['moduleName'];
$this->business_name = $this->table['businessName'];
$this->function_name = $this->table['functionName'];
//后端生成通用数据初始化
$this->initBusinessFields();
$this->initRequiredFields('add_required_fields');
$this->initRequiredFields('edit_required_fields');
$this->initImportExportStatus();
$this->initImgFields();
$this->buildAddFieldsTemp();
$this->buildEditFieldsTemp();
$this->initGenApiType();
$this->initQueryListDisplayFields();
$this->initSortInfo();
$this->initRequiredVerifyFieldsTemp();
$this->buildQueryWhereContentTemp();
$this->initIsSortStatus();
//后端生成所需路径初始化
$this->initModelGenPath();
$this->initAdminControllerGenPath();
$this->initApiControllerGenPath();
}
/**
* 构建查询列表条件模板字符串
*/
public function buildQueryWhereContentTemp(): void
{
$where_content_arr = [];
foreach ($this->fields as $val) {
if ($val['isQuery']) {
$where_content_arr[] = [$val['columnName'], $val['queryType']];
}
};
$this->query_list_where_temp = self::toFormTempStr($where_content_arr, 3);
}
/**
* 初始化排序信息
*/
private function initSortInfo(): void
{
$this->sort_field = $this->table['options']->SortField ?? "{$this->business_name}_update_time";
$this->sort_type = $this->table['options']->SortType ?? 'desc';
}
/**
* 初始化查询列表可显字段
*/
private function initQueryListDisplayFields(): void
{
foreach ($this->fields as $val) {
if ($val['isList']) {
$this->query_list_fields[] = $val['columnName'];
}
};
}
/**
* 初始化后端模板类型
*/
private function initGenApiType(): void
{
$this->gen_api_type = $this->table['tplCategory'];
}
/**
* 初始化模型层生成所需路径
*/
private function initModelGenPath(): void
{
$this->model_temp_path = self::initFilePath($this->getTempPath('model'));
$this->model_module_path = self::initFilePath("{$this->root}common/model/{$this->module_name}");
$this->model_path = self::initFilePath("{$this->model_module_path}/{$this->class_name}.php");
}
/**
* 初始化后台控制器生成所需路径
*/
private function initAdminControllerGenPath(): void
{
$this->admin_con_temp_path = self::initFilePath($this->getTempPath('admin'));
$this->admin_con_module_path = self::initFilePath("{$this->root}admin/controller/{$this->module_name}");
$this->admin_con_path = self::initFilePath("{$this->admin_con_module_path}/{$this->class_name}.php");
}
/**
* 初始化前台控制器生成所需路径
*/
private function initApiControllerGenPath(): void
{
$this->api_con_temp_path = self::initFilePath($this->getTempPath('api'));
$this->api_con_module_path = self::initFilePath("{$this->root}api/controller/{$this->module_name}");
$this->api_con_path = self::initFilePath("{$this->api_con_module_path}/{$this->class_name}.php");
}
/**
* 获取生成模板文件路径
*
* @param string $type 生成模板文件类型 admin:后台控制器 | api:前台控制器 | model:模型层
*/
private function getTempPath(string $type): string
{
//模板类型
$temp_type = $this->gen_api_type;
//若排序处理开启并且模板类型为crud则使用crud_sort单表(增删改查+排序处理)
if ($this->is_sort) {
$temp_type = ($temp_type == 'crud') ? 'crud_sort' : $temp_type;
}
return $this->root . self::TEMP_PATH_CONFIG[$temp_type][$type];
}
/**
* 初始化图片字段变量
*/
private function initImgFields(): void
{
foreach ($this->fields as $val) {
if ($val['htmlType'] == 'imageUpload') {
$this->img_fields[] = $val['columnName'];
}
}
}
/**
* 初始化导入导出状态
*/
private function initImportExportStatus(): void
{
//其他选项 4导出 6导入
$checked_btn_arr = $this->table['options']->CheckedBtn;
$this->is_import = in_array('6', $checked_btn_arr);
$this->is_export = in_array('4', $checked_btn_arr);
}
/**
* 初始化是否排序处理状态
*/
private function initIsSortStatus(): void
{
$this->is_sort = ($this->table['isSort'] == 1);
}
/**
* 构建新增允许字段模板字符
*/
private function buildAddFieldsTemp(): void
{
$field_arr = array_merge(array_keys($this->business_fields), $this->add_required_fields);
$this->add_fields_temp = self::toFormTempStr($field_arr);
}
/**
* 构建编辑允许字段模板字符
*/
private function buildEditFieldsTemp(): void
{
$field_arr = array_merge(array_keys($this->business_fields), $this->edit_required_fields);
$this->edit_fields_temp = self::toFormTempStr($field_arr);
}
/**
* 初始化功能必填验证模板字符
*/
private function initRequiredVerifyFieldsTemp(): void
{
$edit_require_fields = [];
$add_require_fields = [];
foreach ($this->fields as $val) {
$column_name = $val['columnName'];
if ($val['isEdit']) {
if ($val['isRequired']) {
$edit_require_fields[] = $column_name;
}
}
if ($val['isInsert']) {
if ($val['isRequired']) {
$add_require_fields[] = $column_name;
}
}
};
$this->edit_required_verify_temp = self::toFormTempStr($edit_require_fields, 2);
$this->add_required_verify_temp = self::toFormTempStr($add_require_fields, 2);
}
/**
* 构建业务字段名
*/
private function initBusinessFields()
{
foreach ($this->fields as $val) {
if (!$val['isInit']) {
$this->business_fields[$val['columnName']] = $val['columnComment'];
}
}
}
/**
* 生成模块文件夹
*
* @param string $path 模块文件夹路径
*/
protected function createModuleMkdir(string $module_path): void
{
self::mkdir($module_path);
}
/**
* 初始化文件路径
*
* @param string $path 文件路径
* @return string 初始化后的文件路径
*/
protected static function initFilePath(string $path): string
{
//系统分隔符替换
return str_replace('/', DIRECTORY_SEPARATOR, $path);
}
/**
* 获取模板文件字符串
*
* @param string $temp_path 模板文件路径
*/
protected static function getTempStr(string $temp_path): string
{
//打开该文件资源(r只读方式打开),读取模板文件内容
return fread(fopen($temp_path, "r"), filesize($temp_path));
}
/**
* 写入文件
*
* @param string $path 文件路径
* @param string $content 写入内容
*/
protected static function writeFile(string $path, string $content): void
{
//打开文件资源(w写入方式打开),进行写入
fwrite(fopen($path, 'w'), $content);
}
/**
* 初始化必要字段
*
* @param string 必要字段变量名
*
*/
protected function initRequiredFields(string $op_name): void
{
$init_data = [];
foreach ($this->$op_name as $val) {
$init_data[] = "{$this->business_name}_{$val}";
}
$this->$op_name = $init_data;
}
/**
* 创建文件夹
*
* @param string $path 文件夹创建所属路径
*/
protected static function mkdir(string $path): void
{
if (!(new Gen(new \think\App()))->mkdir($path)) {
throwErrorMsg(__METHOD__ . "创建文件夹失败!");
}
}
/**
* 数组转换为模板字符串
* @param array $arr 数组
* @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
*/
protected function toFormTempStr(array $arr, int $type = 1): string
{
return (new Gen(new \think\App()))->toFormTempStr($arr, $this->business_fields, $type);
}
}

View File

@ -0,0 +1,122 @@
<?php
namespace app\admin\controller\Gen\GenApi\GenController;
use app\admin\controller\Gen\GenApi\GenApi;
use app\common\exception\Tool;
/**
* 后端后台接口生成类
*/
class Admin extends GenApi
{
/**
* 生成后台控制器
*/
public function createAdminController(): void
{
//创建后台控制器层模块目录
self::createModuleMkdir($this->admin_con_module_path);
//模板文件字符串替换
$temp_str = Tool::strReplacePlus(
self::getTempStr($this->admin_con_temp_path),
[
//实体类名
'{$className}' => $this->class_name,
//模块名
'{$moduleName}' => $this->module_name,
//业务名
'{$businessName}' => $this->business_name,
//方法名
'{$functionName}' => $this->function_name,
//查询列表显示字段
'{$queryFields}' => self::toFormTempStr($this->query_list_fields),
//排序字段
'{$orderField}' => $this->sort_field,
//排序类型
'{$orderMode}' => $this->sort_type,
//新增允许字段
'{$addAllowFields}' => $this->add_fields_temp,
//编辑允许字段
'{$editAllowFields}' => $this->edit_fields_temp,
//列表查询条件
'{$whereContent}' => $this->query_list_where_temp,
//导出接口
'{$exportExcelContent}' => $this->buildExportExcelApiTemp(),
//导入接口
'{$importExcelContent}' => $this->buildImportExcelApiTemp(),
//下载模板接口
'{$downloadTempContent}' => $this->buildDownloadTemplateApiTemp(),
//编辑必填验证
'{$editRequireFields}' => $this->edit_required_verify_temp,
//新增必填验证
'{$addRequireFields}' => $this->add_required_verify_temp,
]
);
//文件写入
self::writeFile($this->admin_con_path, $temp_str);
}
/**
* 构建Excel导出接口模板字符串
*/
private function buildExportExcelApiTemp(): string
{
if (!$this->is_export) return '';
//模板字符串构建并返回
return "/**\n* 导出Excel\n*/
public function exportExcel(Request \$request):void
{
Model{$this->class_name}::exportExcel(self::get{$this->class_name}List(\$request, true));
}";
}
/**
* 构建Excel导入接口模板字符串
*/
private function buildImportExcelApiTemp(): string
{
if (!$this->is_import) return '';
//模板字符串构建并返回
return "/**\n* 导入excel\n*/
public function importExcel(Request \$request):array
{
\$file = new UploadFile('uploads', 'fileExt:xlsx');
\$file->putFile('{$this->business_name}');
\$msg = Model{$this->class_name}::importExcel(\$file);
return [
'code' => 0,
'msg' => \$msg
];
}";
}
/**
* 构建Excel下载模板接口模板字符串
*/
private function buildDownloadTemplateApiTemp(): string
{
if (!$this->is_import) return '';
$initial_value_temp = '';
for($i =0 ;$i<count($this->business_fields);$i++){
$index =$i+1;
$initial_value_temp .= "'默认值{$index}',";
}
//模板字符串构建并返回
return "/**\n* 下载导入模板\n*/
public function downloadTemplate(Request \$request):void
{
\$params = \$request->param();
\$data = [
array_values(Model{$this->class_name}::EXCELFIELD),
[{$initial_value_temp}]
];
\$excel = (new Excel())->exporTsheet(\$data);
\$excel->save('{$this->function_name}导入模板.xlsx');
}";
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace app\admin\controller\Gen\GenApi\GenController;
use app\admin\controller\Gen\GenApi\GenApi;
use app\common\exception\Tool;
/**
* 后端前台接口生成类
*/
class Api extends GenApi
{
/**
* 生成前台控制器
*/
public function createApiController(): void
{
//创建前台控制器层模块目录
self::createModuleMkdir($this->api_con_module_path);
//模板文件字符串替换
$temp_str = Tool::strReplacePlus(
self::getTempStr($this->api_con_temp_path),
[
//实体类名
'{$className}' => $this->class_name,
//模块名
'{$moduleName}' => $this->module_name,
//业务名
'{$businessName}' => $this->business_name,
//方法名
'{$functionName}' => $this->function_name,
//查询列表显示字段
'{$queryFields}' => self::toFormTempStr($this->query_list_fields),
//排序字段
'{$orderField}' => $this->sort_field,
//排序类型
'{$orderMode}' => $this->sort_type,
//新增允许字段
'{$addAllowFields}' => $this->add_fields_temp,
//编辑允许字段
'{$editAllowFields}' => $this->edit_fields_temp,
//列表查询条件
'{$whereContent}' => $this->query_list_where_temp,
]
);
//文件写入
self::writeFile($this->api_con_path, $temp_str);
}
}

View File

@ -0,0 +1,209 @@
<?php
namespace app\admin\controller\Gen\GenApi\GenModel;
use app\admin\controller\Gen\GenApi\GenApi;
use app\common\exception\Tool;
/**
* 后端模型层生成类
*/
class Model extends GenApi
{
/**
* 生成模型层
*/
public function createNewModel(): void
{
//创建模型层模块目录
self::createModuleMkdir($this->model_module_path);
//模板文件字符串替换
$temp_str = Tool::strReplacePlus(
self::getTempStr($this->model_temp_path),
[
//实体类名
'{$className}' => $this->class_name,
//模块名
'{$moduleName}' => $this->module_name,
//业务名
'{$businessName}' => $this->business_name,
//模型层字段信息
'{$fields}' => $this->buildFieldsInfoTemp(),
//模型层导出Excel方法
'{$exportExcelContent}' => $this->buildExportFun(),
//模型层导入/下载模板Excel表头
'{$importExcelField}' => $this->buildImportDownloadFieldsTemp(),
//模型层导入Excel方法
'{$importExcelContent}' => $this->buildImportFunTemp(),
//模型层导入Excel初始化方法
'{$importExcelInitContent}' => $this->buildImportInitFunTemp(),
//排序字段
'{$orderField}' => $this->sort_field,
]
);
//文件写入
self::writeFile($this->model_path, $temp_str);
}
/**
* 构建模型层字段信息模板字符串
*/
public function buildFieldsInfoTemp(): string
{
$str = "";
foreach ($this->fields as $value) {
$str .= " '{$value['columnName']}' => '{$value['columnType']}' , \n";
}
return $str;
}
/**
* 构建模型层导出方法模板字符串
*/
private function buildExportFun(): string
{
if (!$this->is_export) return '';
//excel表头字符串构建
$excel_header = [];
foreach ($this->business_fields as $name) {
$excel_header[] = $name;
};
$excel_header_temp = $this->toFormTempStr($excel_header);
//导出数据分配
$data_str = '';
foreach ($this->business_fields as $field => $name) {
//图片字段处理
if (in_array($field, $this->img_fields)) {
$data_str .= "Excel::ExportImgFiled(\$val['$field']),\n";
} else {
$data_str .= "\$val['{$field}'],\n";
}
}
$data_str = "[\n{$data_str}]";
//模板字符串构建并返回
return "
/**
* 导出Excel
*
* @param array \$select 导出的数据
*/
public static function exportExcel(array \$select): void
{
\$data = [{$excel_header_temp}];
foreach (\$select as \$key => \$val) {
\$data[] = {$data_str};
}
\$excel = (new Excel())->exporTsheet(\$data);
\$excel->save('{$this->function_name}.xlsx');
}";
}
/**
* 构建导入/下载模板表头模板字符串
*/
private function buildImportDownloadFieldsTemp(): string
{
if (!$this->is_import) return '';
$init_fields_str = $this->toFormTempStr($this->business_fields, 4);
$init_fields_str = substr($init_fields_str, 0, strlen($init_fields_str) - 1); //末尾的逗号去除
return " // excel导入/下载模板表头
public const EXCELFIELD = {$init_fields_str};";
}
/**
* 构建模型层导入方法模板字符串
*/
private function buildImportFunTemp(): string
{
if (!$this->is_import) return '';
//模板字符串构建并返回
return "
/**
* 导入excel
*
* @param \app\common\arw\adjfut\src\UploadFile \$file excel
*/
public static function importExcel(\app\common\arw\adjfut\src\UploadFile \$file): string
{
\$msg = [];
Db::startTrans();
try {
\$excel = new Excel(\$file);
\$data = \$excel->parseExcel(
Tool::getExcelRule(self::EXCELFIELD),
['titleLine' => [1]]);
if (!\$data) throwErrorMsg('excel无数据', 1);
\$msg = [];
foreach (\$data as \$line => \$value) {
try {
\$model = self::importExcelInit(\$value);
\$msg[] = \"{\$line} <span style='color:#27af49'>新增成功!</span><br>\";
} catch (\Throwable \$th) {
\$msg[] = \"{\$line} <span style='color:red'>{\$th->getMessage()}</span><br>\";
}
}
Db::commit();
return implode(', ', \$msg);
} catch (\Throwable \$th) {
Db::rollback();
throw \$th;
}
}";
}
/**
* 构建模型层导入初始化方法模板字符串
*/
private function buildImportInitFunTemp(): string
{
if (!$this->is_import) return '';
/**
* (匿名函数)获取excel每行导入数据变量分配-模板字符串
*/
$getImportAllocationTemp = function () {
$str = "";
foreach ($this->business_fields as $field => $name) {
$str .= "\${$field} = \$value['{$field}'];";
};
return $str;
};
$import_allocation_temp = $getImportAllocationTemp();
/**
* (匿名函数)获取新增的字段值们-模板字符串
*/
$getAddFieldsTemp = function () {
$str = "";
foreach ($this->business_fields as $field => $name) {
$str .= "'{$field}' => \${$field},\n";
};
return $str;
};
$add_allocation_temp = $getAddFieldsTemp();
//模板字符串构建并返回
return "
/**
* 导入excel初始化
*
* @param array \$value excel每行数据
*/
public static function importExcelInit(array \$value):void
{
{$import_allocation_temp}
self::create(
[{$add_allocation_temp}],
{$this->add_fields_temp}
);
}
";
}
}

View File

@ -0,0 +1,223 @@
<?php
namespace app\admin\controller\Gen\GenVue;
use app\admin\controller\Gen\Gen;
/**
* 前端生成类
*/
class GenDto
{
/**
* 项目根目录路径
* @var string
*/
protected $root;
/**
* 字段信息
* @var array
*/
protected $fields = [];
/**
* 生成信息
* @var array
*/
protected $table = [];
/**
* 实体类名
* @var string User
*/
protected $class_name;
/**
* 模块名
* @var string User
*/
protected $module_name;
/**
* 业务名 user
* @var string
*/
protected $business_name;
/**
* 方法名
* @var string 例:用户
*/
protected $function_name;
/**
* vue基础生成路径
* @var string
*/
protected $genPath;
/**
* 模型层模板读取路径
* @var string
*/
protected $vue_index_temp_path;
/**
* 模型层模块生成路径
* @var string
*/
protected $model_module_path;
/**
* 模型层生成路径
* @var string
*/
protected $model_path;
/**
* 业务字段
* @var array [ 'user_name' => '用户名称',...]
*/
protected $business_fields = [];
/**
* 新增允许字段模板字符
* @var string
*/
protected $add_fields_temp = "";
/**
* 是否导入
* @var bool
*/
protected $is_import = false;
/**
* 是否导出
* @var bool
*/
protected $is_export = false;
/**
* 图片字段数组
* @var array
*/
protected $img_fields = [];
/**
* 构造器
*
* @param array $fields 字段信息
* @param array $table 生成信息
*/
public function __construct(array $fields, array $table)
{
$this->root = base_path();
$this->fields = $fields;
$this->table = $table;
$this->class_name = $this->table['className'];
$this->module_name = $this->table['moduleName'];
$this->business_name = $this->table['businessName'];
$this->function_name = $this->table['functionName'];
$this->genPath = $this->table['genPath'];
$this->vue_index_temp_path = self::initFilePath("{$this->root}resources/view/admin/model.tpl");
$this->model_module_path = self::initFilePath("{$this->root}common/model/{$this->module_name}");
$this->model_path = self::initFilePath("{$this->model_module_path}/{$this->class_name}.php");
$this->mkdirModelModule();
$this->buildBusinessFields();
$this->buildAddFieldsTemp();
$this->initImportExportStatus();
$this->initImgFields();
}
/**
* 初始化图片字段变量
*/
private function initImgFields(): void
{
foreach ($this->fields as $val) {
if ($val['htmlType'] == 'imageUpload') {
$this->img_fields[] = $val['columnName'];
}
};
}
/**
* 初始化导入导出状态
*/
private function initImportExportStatus(): void
{
//其他选项 4导出 6导入
$checked_btn_arr = $this->table['options']->CheckedBtn;
$this->is_import = in_array('6', $checked_btn_arr);
$this->is_export = in_array('4', $checked_btn_arr);
}
/**
* 构建新增允许字段模板字符
*/
private function buildAddFieldsTemp(): void
{
$this->add_fields_temp .= '[';
foreach ($this->business_fields as $key => $val) {
$this->add_fields_temp .= "'{$key}',";
};
$this->add_fields_temp .= ']';
}
/**
* 构建业务字段名
*/
private function buildBusinessFields()
{
foreach ($this->fields as $val) {
if (!$val['isInit']) {
$this->business_fields[$val['columnName']] = $val['columnComment'];
}
};
}
/**
* 生成模型层模块文件夹
*/
private function mkdirModelModule()
{
self::mkdir($this->model_module_path);
}
/**
* 初始化文件路径
*
* @param string $path 文件路径
* @return string 初始化后的文件路径
*/
protected static function initFilePath(string $path): string
{
//系统分隔符替换
return str_replace('/', DIRECTORY_SEPARATOR, $path);
}
/**
* 创建文件夹
*
* @param string $path 文件夹创建所属路径
*/
protected static function mkdir(string $path): void
{
if (!(new Gen(new \think\App()))->mkdir($path)) {
throwErrorMsg(__METHOD__ . "创建文件夹失败!");
};
}
/**
* 数组转换为模板字符串
* @param array $arr 数组
* @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
*/
protected function toFormTempStr(array $arr, int $type = 1): string
{
return (new Gen(new \think\App()))->toFormTempStr($arr, $this->business_fields, $type);
}
}

View File

@ -0,0 +1,212 @@
<?php
namespace app\admin\controller\Gen\GenApi\GenIndex;
use app\admin\controller\Gen\GenVue\GenDto;
use app\common\exception\Tool;
/**
* 前端列表页生成类
*/
class Index extends GenDto
{
/**
* 生成Index.vue
*/
public function createJsVue(): void
{
//打开Index.vue模板文件拿到该文件资源(r只读方式打开)
$tem_f = fopen($this->vue_index_temp_path, "r");
//读取Index.vue模板文件拿到模板文件的全部字符串
$temp_str = fread($tem_f, filesize($this->vue_index_temp_path));
//模板文件字符串替换
$temp_str = Tool::strReplacePlus($temp_str, [
//实体类名
'{$className}' => $this->class_name,
//模块名
'{$moduleName}' => $this->module_name,
//业务名
'{$businessName}' => $this->business_name,
//Index.vue字段信息
'{$fields}' => $this->buildFieldsInfoTemp(),
//Index.vue导出Excel方法
'{$exportExcelContent}' => $this->buildExportFun(),
//Index.vue导入/下载模板Excel表头
'{$importExcelField}' => $this->buildImportDownloadFieldsTemp(),
//Index.vue导入Excel方法
'{$importExcelContent}' => $this->buildImportFunTemp(),
//Index.vue导入Excel初始化方法
'{$importExcelInitContent}' => $this->buildImportInitFunTemp(),
]);
//打开Index.vue文件拿到该文件资源(w写入方式打开)
$gen_model = fopen($this->model_path, 'w');
//写入该Index.vue文件
fwrite($gen_model, $temp_str);
}
/**
* 构建Index.vue字段信息模板字符
*/
public function buildFieldsInfoTemp(): string
{
$str = "";
foreach ($this->fields as $value) {
$str .= " '{$value['columnName']}' => '{$value['columnType']}' , \n";
}
return $str;
}
/**
* 构建Index.vue导出方法模板字符
*/
private function buildExportFun(): string
{
if (!$this->is_export) return '';
//excel表头字符构建
$excel_header = [];
foreach ($this->business_fields as $name) {
$excel_header[] = $name;
};
$excel_header_temp = $this->toFormTempStr($excel_header);
//导出数据分配
$data_str = '';
foreach ($this->business_fields as $field => $name) {
if (in_array($name, $this->img_fields)) {
$data_str .= "Excel::ExportImgFiled(\$val['$field']),\n";
} else {
$data_str .= "\$val['{$field}'],\n";
}
}
$data_str = "[\n{$data_str}]";
//模板字符构建并返回
return "
/**
* 导出Excel
*
* @param array \$select 导出的数据
*/
public static function exportExcel(array \$select): void
{
\$data = [{$excel_header_temp}];
foreach (\$select as \$key => \$val) {
\$data[] = {$data_str};
}
\$excel = (new Excel())->exporTsheet(\$data);
\$excel->save('{$this->function_name}.xlsx');
}";
}
/**
* 构建导入/下载模板表头模板字符
*/
private function buildImportDownloadFieldsTemp(): string
{
if (!$this->is_import) return '';
$init_fields_str = $this->toFormTempStr($this->business_fields, 4);
$init_fields_str = substr($init_fields_str, 0, strlen($init_fields_str) - 1); //末尾的逗号去除
return "
// excel导入/下载模板表头
public const EXCELFIELD = {$init_fields_str};
";
}
/**
* 构建Index.vue导入方法模板字符
*/
private function buildImportFunTemp(): string
{
if (!$this->is_import) return '';
//模板字符构建并返回
return "
/**
* 导入excel
*
* @param \app\common\arw\adjfut\src\UploadFile \$file excel
*/
public static function importExcel(\app\common\arw\adjfut\src\UploadFile \$file): string
{
\$msg = [];
Db::startTrans();
try {
\$excel = new Excel(\$file);
\$data = \$excel->parseExcel(
Tool::getExcelRule(self::EXCELFIELD),
[
'titleLine' => [1]
]);
if (!\$data) throwErrorMsg('excel无数据', 1);
\$msg = [];
foreach (\$data as \$line => \$value) {
try {
\$model = self::importExcelInit(\$value);
\$msg[] = \"{\$line} <span style='color:#27af49'>新增成功!</span><br>\";
} catch (\Throwable \$th) {
\$msg[] = \"{\$line} <span style='color:red'>{\$th->getMessage()}</span><br>\";
}
}
Db::commit();
return implode(', ', \$msg);
} catch (\Throwable \$th) {
Db::rollback();
throw \$th;
}
}";
}
/**
* 构建Index.vue导入初始化方法模板字符
*/
private function buildImportInitFunTemp(): string
{
if (!$this->is_import) return '';
/**
* (匿名函数)获取excel每行导入数据变量分配-模板字符
*/
$getImportAllocationTemp = function () {
$str = "";
foreach ($this->business_fields as $field => $name) {
$str .= "\${$field} = \$value['{$field}'];";
};
return $str;
};
$import_allocation_temp = $getImportAllocationTemp();
/**
* (匿名函数)获取新增的字段值们-模板字符
*/
$getAddFieldsTemp = function () {
$str = "";
foreach ($this->business_fields as $field => $name) {
$str .= "'{$field}' => \${$field},\n";
};
return $str;
};
$add_allocation_temp = $getAddFieldsTemp();
//模板字符构建并返回
return "
/**
* 导入excel初始化
*
* @param array \$value excel每行数据
*/
public static function importExcelInit(array \$value):void
{
{$import_allocation_temp}
self::create(
[{$add_allocation_temp}],
{$this->add_fields_temp}
);
}
";
}
}

View File

@ -0,0 +1,145 @@
<?php
namespace app\admin\controller;
use app\common\arw\adjfut\src\Validate;
use app\BaseController;
use app\common\logic\Login as LogicLogin;
use app\Request;
use think\captcha\facade\Captcha;
use think\middleware\SessionInit;
use think\Response;
class Login extends BaseController
{
/**
* 初始化
*
* @date 2022-03-15
* @example
* @author admin
* @since 1.0.0
*/
protected function initialize(): void
{
$this->middleware[SessionInit::class] = [
'only' => ['accountLogin', 'getCaptcha']
];
}
/**
* 验证token
*
* @param Request $request
* @return void
*/
public function validateToken(Request $request)
{
$token = $request->getCurrentToken();
return [
'code' => 0,
'data' => [
'exp_time' => $token->token_exp_time,
],
'msg' => 'ok'
];
}
/**
* 西北政法大学单点登陆
*
* @param Request $request
* @return Response
* @date 2023-01-04
* @example
* @author admin
* @since 1.0.0
*/
public function casOauthLogin(Request $request): Response
{
$url = $request->param('url');
return LogicLogin::casOauthLogin(
LogicLogin::casOauthLoginHandle($url)
);
}
/**
* 西北政法大学单点登出
*
* @param Request $request
* @return Response
* @date 2023-01-04
* @example
* @author admin
* @since 1.0.0
*/
public function casOauthLogout(Request $request): Response
{
$url = $request->param('url');
return LogicLogin::casOauthLogout($url);
}
/**
* 用户账号登录
*
* @param Request $request
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function accountLogin(Request $request): array
{
$param = Validate::param([
'account|账号' => 'require',
'password|密码' => 'require',
// 'captcha|验证码' => $request->isProd() ? 'require|captcha' : false
]);
$token = LogicLogin::accountLogin(
$param['account'],
$param['password']
);
return [
'code' => 0,
'data' => [
'token' => $token->token_content,
'exp_time' => $token->token_exp_time,
],
'msg' => 'ok'
];
}
/**
* 生成验证码
*
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function getCaptcha()
{
return Captcha::create();
}
/**
* 用户登出
*
* @param Request $request
* @date 2022-03-09
* @example
* @author admin
* @since 1.0.0
*/
public function userLogout(Request $request): array
{
$token = $request->getCurrentToken();
$token->logout();
return [
'code' => 0,
'msg' => '登出成功'
];
}
}

View File

@ -0,0 +1,368 @@
<?php
namespace app\admin\controller\Menu;
use app\common\arw\adjfut\src\Traverse;
use app\BaseController;
use app\common\model\Menu\Menu as ModelMenu;
use app\common\model\Menu\MenuApi;
use app\common\model\Role\Role;
use app\common\model\Role\RoleMenu;
use app\Request;
use think\facade\Db;
class Menu extends BaseController
{
/**
* 获取菜单接口树
*
* @param Request $request
* @return array
* @date 2022-03-07
* @example
* @author admin
* @since 1.0.0
*/
public function getMenuApiTree(Request $request)
{
$menu = ModelMenu::order([
'menu_index',
'menu_order' => 'desc',
])->field([
'menu_guid',
'menu_parent_guid',
'menu_name',
'menu_url',
])->select()->toArray();
$menuApi = [];
foreach (MenuApi::select() as $value) {
if (!isset($menuApi[$value->menu_guid])) {
$menuApi[$value->menu_guid] = [];
}
$menuApi[$value->menu_guid][] = $value->menu_api_url;
}
$Traverse = new Traverse('menu_guid', 'menu_parent_guid');
$menuTree = $Traverse->tree($menu, '0', function ($v) use ($menuApi) {
return [
'menu_api_url' => isset($menuApi[$v['menu_guid']]) ? $menuApi[$v['menu_guid']] : [],
'menu_name' => $v['menu_name'],
'menu_guid' => $v['menu_guid'],
'menu_parent_guid' => $v['menu_parent_guid'],
'menu_url' => $v['menu_url'],
];
}, function ($v) {
$v['hasChildren'] = isset($value['children']) && count($value['children']);
return $v;
});
return [
'code' => 0,
'msg' => 'ok',
'data' => $menuTree,
];
}
/**
* 添加菜单接口
*
* @param Request $request
* @return array
* @date 2022-03-08
* @example
* @author admin
* @since 1.0.0
*/
public function addMenuApi(Request $request)
{
Db::startTrans();
try {
$params = $request->param();
$this->validate($params, [
'menu_guid' => 'require',
'menu_api_url' => 'require'
]);
foreach (explode(',', $params['menu_api_url']) as $menu_api_url) {
MenuApi::create([
'menu_guid' => $params['menu_guid'],
'menu_api_url' => $menu_api_url,
]);
}
Db::commit();
return [
'code' => 0,
'msg' => '添加成功',
];
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 删除菜单接口
*
* @param Request $request
* @return array
* @date 2022-03-08
* @example
* @author admin
* @since 1.0.0
*/
public function deleteMenuApi(Request $request)
{
$params = $request->param();
$this->validate($params, [
'menu_api_guid' => 'require'
]);
MenuApi::where([
'menu_api_guid' => explode(',', $params['menu_api_guid'])
])->select()->delete();
return [
'code' => 0,
'msg' => "删除成功"
];
}
/**
* 获取菜单接口列表
*
* @param Request $request
* @return array
* @date 2022-03-07
* @example
* @author admin
* @since 1.0.0
*/
public function getMenuApiList(Request $request)
{
$params = $request->param();
$this->validate($params, [
'menu_guid' => 'require'
]);
$query = ModelMenu::find($params['menu_guid'])->apis()->append([
'menu_api_status_text'
]);
$data = self::pageWrapper($query)->select();
$count = $query->count();
return [
'code' => 0,
'msg' => 'ok',
'data' => $data,
'count' => $count
];
}
/**
* 获取菜单树
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function getMenuTree(Request $request)
{
$menu = ModelMenu::order([
'menu_index',
'menu_order' => 'desc',
])->field([
'menu_parent_guid',
'menu_name',
'menu_url',
'menu_index',
'menu_order',
'menu_status',
'menu_show',
'menu_guid',
])->select()->toArray();
array_unshift($menu, [
'menu_guid' => '0',
'menu_parent_guid' => '-1',
'menu_name' => '系统菜单',
'menu_index' => 0,
'menu_order' => 0,
'menu_status' => 0,
'menu_show' => 0,
'menu_url' => '',
]);
$Traverse = new Traverse('menu_guid', 'menu_parent_guid');
$menuTree = $Traverse->tree($menu, '-1', function ($v) {
return [
'label' => $v['menu_name'],
'id' => $v['menu_guid'],
'parent_id' => $v['menu_parent_guid'],
'index' => $v['menu_index'],
'order' => $v['menu_order'],
'status' => $v['menu_status'],
'show' => $v['menu_show'],
'url' => $v['menu_url'],
];
});
return [
'code' => 0,
'msg' => 'ok',
'data' => $menuTree
];
}
/**
* 获取角色菜单
*
* @param Request $request
* @return array
* @date 2022-03-09
* @example
* @author admin
* @since 1.0.0
*/
public function getRoleMenu(Request $request)
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require'
]);
$menus = Role::getByRoleGuid($params['role_guid'])->menus;
$menu_guids = [];
$menu_parent_guids = [];
foreach ($menus as $menu) {
if (!in_array($menu->menu_guid, $menu_guids)) {
$menu_guids[] = $menu->menu_guid;
}
if (!in_array($menu->menu_parent_guid, $menu_parent_guids)) {
$menu_parent_guids[] = $menu->menu_parent_guid;
}
}
// 去除父id 存在父id会全选子节点
$temp = [];
foreach ($menu_guids as $menu_guid) {
if (!in_array($menu_guid, $menu_parent_guids)) {
$temp[] = $menu_guid;
}
}
return [
'code' => 0,
'data' => $temp,
'msg' => 'ok'
];
}
/**
* 绑定角色菜单
*
* @param Request $request
* @return array
* @date 2022-03-09
* @example
* @author admin
* @since 1.0.0
*/
public function bindRoleMenu(Request $request)
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require',
'menu_guid' => 'require'
]);
$menus = array_map(function ($v) {
return ModelMenu::find($v);
}, explode(',', $params['menu_guid']));
RoleMenu::rebindRoleMenu(
[
Role::getByRoleGuid($params['role_guid']),
],
array_filter($menus)
);
return [
'code' => 0,
'msg' => '绑定成功'
];
}
/**
* 添加菜单
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function addMenu(Request $request)
{
$params = $request->param();
$this->validate($params, [
'menu_name' => 'require',
'menu_index' => 'require',
'menu_order' => 'require',
'menu_show' => 'require',
'menu_parent_guid' => 'require',
]);
if (isset($params['menu_guid'])) {
unset($params['menu_guid']);
}
ModelMenu::create($params);
return [
'code' => 0,
'msg' => '添加成功'
];
}
/**
* 更新菜单
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function updateMenu(Request $request)
{
$params = $request->param();
$this->validate($params, [
'menu_guid' => 'require'
]);
ModelMenu::withoutGlobalScope(['status'])->update($params, [
'menu_guid' => $params['menu_guid']
]);
return [
'code' => 0,
'msg' => '更新成功'
];
}
/**
* 删除菜单
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function deleteMenu(Request $request)
{
$params = $request->param();
$this->validate($params, [
'menu_guid' => 'require'
]);
ModelMenu::where([
'menu_guid' => explode(',', $params['menu_guid'])
])->select()->delete();
return [
'code' => 0,
'msg' => '删除成功'
];
}
}

View File

@ -0,0 +1,198 @@
<?php
namespace app\admin\controller\Role;
use app\BaseController;
use app\common\model\Role\Role as ModelRole;
use app\Request;
class Role extends BaseController
{
/**
* 获取用户角色列表
*
* @param Request $request
* @return array
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function getUserRoleList(Request $request)
{
}
/**
* 获取角色列表
*
* @param Request $request
* @date 2022-02-25
* @example
* @author admin
* @since 1.0.0
*/
public function getRoleList(Request $request): array
{
$role_status = $request->param('role_status');
$role = $request->param('role');
$scope = explode(',', $request->param('scope', ''));
$con = [
// 'role_status' => 1
];
if ($role) {
$con['role_name'] = $role;
}
if ($role_status) {
$con['role_status'] = $role_status;
}
$query = ModelRole::scope($scope)->withSearch(['role_name'], $con)->where($con);
$select = self::pageWrapper($query)->field([
'role_name',
'role_status',
'role_guid',
])->append([
'role_status_text'
])->order([
'role_id' => 'desc'
])->select();
$count = $query->count();
return [
'code' => 0,
'data' => $select,
'count' => $count,
'msg' => 'ok'
];
}
/**
* 获取角色菜单
*
* @param Request $request
* @date 2022-03-08
* @example
* @author admin
* @since 1.0.0
*/
public function getRoleMenu(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require'
]);
dump(ModelRole::getByRoleGuid($params['role_guid'])->menus);
return [
'code' => 0,
'msg' => 'ok',
// 'data' => $data
];
}
/**
* 编辑角色
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function editRole(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require',
'role_name' => 'require',
]);
$role = ModelRole::where([
'role_guid' => $params['role_guid']
])->find();
if (!$role) {
throwErrorMsg("角色不存在", 1);
}
$role->save([
'role_name' => $params['role_name'],
]);
return [
'code' => 0,
'msg' => '编辑成功'
];
}
/**
* 添加角色
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function addRole(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'role_name' => 'require',
]);
ModelRole::create([
'role_name' => $params['role_name'],
]);
return [
'code' => 0,
'msg' => '添加成功'
];
}
/**
* 删除角色
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function deleteRole(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require',
]);
$roles = ModelRole::where([
'role_guid' => ['in', $params['role_guid']]
])->select();
$roles->delete();
return [
'code' => 0,
'msg' => "删除成功"
];
}
/**
* 更新角色状态
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function updateRoleStatus(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'role_guid' => 'require',
'role_status' => 'require|in:1,2',
]);
$role_status = $params['role_status'];
$roles = ModelRole::where([
'role_guid' => explode(',', $params['role_guid'])
])->select();
$roles->update([
'role_status' => $role_status
]);
return [
'code' => 0,
'msg' => "更新成功"
];
}
}

View File

@ -0,0 +1,150 @@
<?php
namespace app\admin\controller\Tdk;
use app\BaseController;
use app\common\model\Tdk\Tdk as ModelTdk;
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use think\facade\Db;
use think\facade\Env;
class Tdk extends BaseController
{
/**
* 获取网站tdk列表
*/
public function getTdkList(Request $request): array
{
$params = $request->param();
$con = [];
$con = Tool::getOptionalQuery(['tdk_type', '='], ['tdk_title', 'LIKE'],);
$query = ModelTdk::where($con)
->field([
'tdk_id',
'tdk_guid',
'tdk_type',
'tdk_title',
'tdk_description',
'tdk_keyword'
])
->order('tdk_update_time', 'desc');
return msg("获取网站tdk列表成功!", $query);
}
/**
* 编辑网站tdk
*/
public function editTdk(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'tdk_type|tdk所属模块' => 'require',
'tdk_title|网页标题' => 'require',
'tdk_description|网页简介' => 'require',
'tdk_keyword|网页关键词' => 'require'
]);
$model = ModelTdk::where('tdk_guid', $params['tdk_guid'])->find();
if (!$model) throwErrorMsg("该网站tdk不存在", 1);
$model->allowField([
'tdk_update_user_guid',
'tdk_type',
'tdk_title',
'tdk_description',
'tdk_keyword'
])->save($params);
return msg('编辑成功!');
}
/**
* 添加网站tdk
*/
public function addTdk(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'tdk_type|tdk所属模块' => 'require',
'tdk_title|网页标题' => 'require',
'tdk_description|网页简介' => 'require',
'tdk_keyword|网页关键词' => 'require'
]);
$model = ModelTdk::create($params, [
'tdk_guid',
'tdk_create_user_guid',
'tdk_update_user_guid',
'tdk_type',
'tdk_title',
'tdk_description',
'tdk_keyword'
]);
return msg('添加成功!');
}
/**
* 删除网站tdk
*/
public function deleteTdk(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'tdk_guid' => 'require',
]);
$tdk = ModelTdk::where([
'tdk_guid' => explode(',', $params['tdk_guid'])
])->select();
$tdk->delete();
return msg('删除成功!');
}
/**
* 导出Excel
*/
public function exportExcel(Request $request)
{
$params = $request->param();
$select = ModelTdk::field([
'tdk_type',
'tdk_title',
'tdk_description',
'tdk_keyword'
])
->order('tdk_update_time', 'desc')
->select();
return ModelTdk::exportExcel($select);
}
/**
* 下载导入模板
*/
public function downloadTemplate(Request $request)
{
$params = $request->param();
$data = array_values(ModelTdk::EXCELFIELD);
$excel = (new Excel())->exporTsheet($data);
$excel->save('网站tdk导入模板.xlsx');
}
/**
* 导入excel
*/
public function importExcel(Request $request)
{
$file = new UploadFile('uploads', 'fileExt:xlsx');
$file->putFile('tdk');
$msg = ModelTdk::importExcel($file);
return [
'code' => 0,
'msg' => $msg
];
}
}

View File

@ -0,0 +1,371 @@
<?php
namespace app\admin\controller\User;
use app\common\arw\adjfut\src\Traverse;
use app\BaseController;
use app\exception\ErrorMsg;
use app\model\Role;
use app\common\model\Token;
use app\common\model\User\User as ModelUser;
use app\common\model\Role\Role as ModelRole;
use app\common\model\User\UserRole as ModelUserRole;
use app\model\UserRole;
use app\Request;
use think\db\Query;
use think\Exception;
use think\facade\Db;
use app\common\arw\adjfut\src\Validate;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
class User extends BaseController
{
/**
* 获取用户信息
*
* @param Request $request
* @date 2022-03-09
* @example
* @author admin
* @since 1.0.0
*/
public function getUserInfo(Request $request): array
{
$user = $request->getCurrentUser();
return [
'code' => 0,
'data' => [
'user_name' => $user['user_name']
],
'msg' => 'ok'
];
}
/**
* 获取用户菜单
*
* @param Request $request
* @return array
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function getUserMenu(Request $request)
{
// $user = $request->getCurrentUser();
// $menus = [];
// foreach ($user->roles as $role) {
// $menus = array_merge($menus, $role->menus->toArray());
// }
// array_multisort(array_column($menus, 'menu_order'), SORT_DESC, $menus);
// var_dump($menus);
$token = Token::getCurrent();
$menus = $token->token_menu;
$Traverse = new Traverse('menu_guid', 'menu_parent_guid');
$tree = $Traverse->tree($menus, '0', function ($v) {
return [
'key' => $v['menu_guid'],
'name' => $v['menu_name'],
'url' => $v['menu_url'],
'show' => $v['menu_show'],
'icon' => $v['menu_icon'],
];
});
return [
'code' => 0,
'msg' => 'ok',
'data' => $tree
];
}
/**
* 获取用户列表
*
* @param Request $request
* @date 2022-02-25
* @example
* @author admin
* @since 1.0.0
*/
public function getUserList(Request $request): array
{
$user = $request->param('user');
$con = [
// 'user_status' => 1
];
$search = [];
if ($user) {
$search['user_name'] = $user;
}
$query = ModelUser::scope(['admin'])
->withSearch(array_keys($search), $search)->where($con)
->leftjoin('user_role', 'user_role.user_guid = user.user_guid')
->join('role', join(' AND ', [
'role.role_guid = user_role.role_guid',
'role.role_status = 1',
'role.role_delete_time IS NULL',
]), 'left')->where($con);
$select = self::pageWrapper($query)->field([
'user.user_guid',
'user.user_name',
'user.user_phone',
'user.user_position',
'user.user_department',
'user.user_img',
'user.user_status',
'role.role_name',
])
->append([
'roles',
])
->group('user.user_guid')->order([
'user.user_update_time' => 'desc'
])->select();
$count = $query->count();
return [
'code' => 0,
'data' => $select,
'count' => $count,
'msg' => 'ok'
];
}
/**
* 编辑用户
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function editUser(Request $request): array
{
$params = Validate::param([
'user_guid' => 'require',
'user_name' => 'require',
'roles|角色' => 'require',
]);
$model = ModelUser::where([
'user_guid' => $params['user_guid']
])->find();
if (!$model) {
throwErrorMsg("用户不存在", 1);
}
$model->save($params);
ModelUser::editUserRole($params);
return [
'code' => 0,
'msg' => '编辑成功'
];
}
/**
* 添加用户
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function addUser(Request $request): array
{
$params = Validate::param([
'user_name' => 'require',
'user_password' => 'require',
'roles|角色' => 'require',
]);
$params['user_status'] = 1;
$model = ModelUser::create($params);
$user_guid = $model->user_guid;
ModelUser::addUserRole($user_guid, $params);
return [
'code' => 0,
'msg' => '添加成功'
];
}
/**
* 删除用户
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function deleteUser(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'user_guid' => 'require',
]);
$users = ModelUser::where([
'user_guid' => explode(',', $params['user_guid'])
])->select();
$users->delete();
return [
'code' => 0,
'msg' => "删除成功"
];
}
/**
* 更新用户状态
*
* @param Request $request
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function updateUserStatus(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'user_guid' => 'require',
'user_status' => 'require|in:1,2',
]);
$user_status = $params['user_status'];
$users = ModelUser::where([
'user_guid' => explode(',', $params['user_guid'])
])->select();
$users->update([
'user_status' => $user_status
]);
return [
'code' => 0,
'msg' => "更新成功"
];
}
/**
* 重置用户密码
*
* @param Request $request
* @date 2022-02-25
* @example
* @author admin
* @since 1.0.0
*/
public function resetUserPassword(Request $request): array
{
$params = Validate::param([
'user_guid' => 'require',
'password' => 'require',
]);
$password = $params['password'];
$users = ModelUser::select(explode(',', $params['user_guid']));
// $users = ModelUser::where([
// 'user_guid' => ['in', $params['user_guid']]
// ])->select();
$users->update([
'user_password' => $password
]);
return [
'code' => 0,
'msg' => "密码已重置为:$password ,请及时修改密码"
];
}
/**
* 导出Excel
*/
public function exportExcel(Request $request)
{
$user = $request->param('user');
$con = [
// 'user_status' => 1
];
$search = [];
if ($user) {
$search['user_name'] = $user;
}
$select = ModelUser::scope(['admin'])
->where($con)->field([
'user.user_guid',
'user.user_name',
'user.user_phone',
'user.user_position',
'user.user_department',
'user.user_img',
'role.role_name',
])
->append([
'roles',
])
->withSearch(array_keys($search), $search)->where($con)
->leftjoin('user_role', 'user_role.user_guid = user.user_guid')
->join('role', join(' AND ', [
'role.role_guid = user_role.role_guid',
'role.role_status = 1',
'role.role_delete_time IS NULL',
]), 'left')
->group('user.user_guid')->order([
'user.user_update_time' => 'desc'
])->select();
return ModelUser::exportExcel($select);
}
/**
* 下载导入模板
*/
public function downloadTemplate(Request $request)
{
$params = $request->param();
$data = [
[
'用户名',
'头像',
'角色',
'手机号',
'密码',
]
];
$data[] = [
'负责人',
'https://img13.360buyimg.com/n5/jfs/t1/195344/2/24691/95759/6295e145E6fae20b9/1172b44c351eeaab.jpg.avif',
'管理员,部门负责人',
'10086',
'123456@aerwen',
];
$excel = (new Excel())->exporTsheet($data);
$excel->save('用户导入模板.xlsx');
}
/**
* 导入excel
*/
public function importExcel(Request $request)
{
$file = new UploadFile('uploads', 'fileExt:xlsx');
$file->putFile('User');
$msg = ModelUser::importExcel($file);
return [
'code' => 0,
'msg' => $msg
];
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace app\admin\controller\User;
use app\BaseController;
use app\common\model\Role\Role as ModelRole;
use app\common\model\User\User as ModelUser;
use app\common\model\User\UserRole as ModelUserRole;
use app\Request;
class UserRole extends BaseController
{
/**
* 获取未绑定角色用户列表
*
* @param Request $request
* @return void
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function getUnbindUserList(Request $request): array
{
$role_guid = $request->param('role_guid');
$user = $request->param('user');
$this->validate($request->param(), [
'role_guid' => 'require'
]);
$user_guids = [];
$con = [];
$users = ModelRole::getByRoleGuid($role_guid)->users;
if ($users) {
$user_guids = $users->column('user_guid');
}
if ($user) {
$con['user_name'] = $user;
}
$query = ModelUser::withSearch(['user_name'], $con)->where($con)->where('user_guid', 'not in', $user_guids);
$select = self::pageWrapper($query)->field([
'user_name',
'user_guid',
])->order([
'user_id' => 'desc'
])->select();
$count = $query->count();
return [
'code' => 0,
'data' => $select,
'count' => $count,
'msg' => 'ok'
];
}
/**
* 获取绑定角色用户列表
*
* @param Request $request
* @return void
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function getBindUserList(Request $request): array
{
$role_guid = $request->param('role_guid');
$user = $request->param('user');
$this->validate($request->param(), [
'role_guid' => 'require'
]);
$user_ids = [];
$con = [];
$users = ModelRole::getByRoleGuid($role_guid)->users;
if ($users) {
$user_ids = $users->column('user_id');
}
if ($user) {
$con['user_name'] = $user;
}
$query = ModelUser::withSearch(['user_name'], $con)->where($con)->where('user_id', 'in', $user_ids);
$select = self::pageWrapper($query)->field([
'user_name',
'user_guid',
])->order([
'user_id' => 'desc'
])->select();
$count = $query->count();
return [
'code' => 0,
'data' => $select,
'count' => $count,
'msg' => 'ok'
];
}
/**
* 绑定用户角色
*
* @param Request $request
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function bindUserRole(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'user_guid' => 'require',
'role_guid' => 'require',
]);
$users = array_map(function ($v) {
return ModelUser::getByUserGuid($v);
}, explode(',', $params['user_guid']));
$roles = array_map(function ($v) {
return ModelRole::getByRoleGuid($v);
}, explode(',', $params['role_guid']));
ModelUserRole::bindUserRole($users, $roles);
return [
'code' => 0,
'msg' => '绑定成功'
];
}
/**
* 绑定用户角色
*
* @param Request $request
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function unBindUserRole(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'user_guid' => 'require',
'role_guid' => 'require',
]);
$users = array_map(function ($v) {
return ModelUser::getByUserGuid($v);
}, explode(',', $params['user_guid']));
$roles = array_map(function ($v) {
return ModelRole::getByRoleGuid($v);
}, explode(',', $params['role_guid']));
ModelUserRole::unbindUserRole($users, $roles);
return [
'code' => 0,
'msg' => '解绑成功'
];
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace app\admin\middleware;
use app\common\exception\NotAuthApi;
use app\common\model\Token;
use app\common\traits\Auth as TraitsAuth;
use app\Request;
use think\Response;
class Auth
{
use TraitsAuth;
/**
* 忽略登录
*
* @var array
* @date 2023-01-07
* @example
* @author admin
* @since 1.0.0
*/
private $ignoreLogin = [
'Login' => ['*'],
'admin' => ['*'],
];
/**
* 处理请求
*
* @param \app\Request $request
* @param \Closure $next
* @return Response
*/
public function handle(Request $request, \Closure $next): Response
{
$this->request = $request;
if ($this->isIgnoreLogin()) {
return $next($request);
}
if ($request->isLogin()) {
$user = $request->getCurrentUser();
if ($user->user_admin) {
return $next($request);
}
}
$api = join('/', [
$request->controller(),
$request->action()
]);
$token = Token::getCurrent();
/**
* 缓存接口权限验证
*/
$validate = $this->validateApi($token->token_api);
/**
* 缓存权限验证不通过
*/
if ($validate === false) {
/**
* 自定义验证权限
* 如果验证通过刷新缓存接口
*/
$validate = $this->validateUser();
if ($validate) {
Token::getCurrentUser()->login();
}
}
/**
* 权限验证通过
*/
if ($validate) {
return $next($request);
}
throw new NotAuthApi("未授权接口 [ $api ]", 1);
}
}

12
app/admin/route.php Normal file
View File

@ -0,0 +1,12 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// 中间件
'middleware' => [
// 接口鉴权
\app\admin\middleware\Auth::class
],
];

View File

@ -0,0 +1,32 @@
<?php
namespace app\api\controller\CommonApi;
use app\BaseController;
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use think\facade\Db;
use think\facade\Env;
use think\captcha\facade\Captcha;
use think\helper\Arr;
class CommonApi extends BaseController
{
/**
* 获取验证码接口
*
* @return \think\response\Html
* @date 2023-04-26
* @author xjh
* @since 1.0.0
*/
public function getCaptcha(): \think\response\Html
{
return Captcha::create('verify');
}
}

View File

@ -0,0 +1,112 @@
<?php
namespace app\api\controller\Crawler;
use app\Request;
use app\common\model\Flow\Flow as ModelFlow;
use think\db\Where;
use think\facade\Validate;
use app\BaseController;
use app\exception\ErrorMsg;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use Goutte\Client;
class Crawler extends BaseController
{
/**
* 爬虫测试
*/
public function Test(Request $request)
{
// return 1;
// 创建Goutte客户端对象
$client = new Client();
// 获取目标网站的URL
$base_url = 'http://www.zs521.com/';
$url = 'http://www.zs521.com/article_categories/guangzhou/articles';
// return $url;
// 发起请求并获取响应对象
$crawler = $client->request('GET', $url);
// 选择要解析的HTML元素
$titles = $crawler->filter('.productsBody .productItem .products');
$imgs = $crawler->filter('.productsBody .productItem .products .productImg');
// return $titles;
// dump($titles);
// die;
$title111 = $titles->filter('.productType')->text();
$img111 = $titles->filter('.productImg')->eq(2)->attr('lazyload');
// return $img111;
// return $title111;
// 在全部元素中筛选
// $products = $crawler->filter('.productsBody .productItem .products');
// foreach ($products as $key => $value) {
// // dump($value->filter('.productType')->eq($key)->text());
// // die;
// foreach ($titles->filter('.productType')->eq($key) as $key => $value) {
// $title = $value->textContent;
// }
// // return $title;
// }
$titleArr = [];
// 遍历元素并输出结果
foreach ($titles as $title) {
$titleArr[] = $title->textContent;
}
return $titleArr;
// $imgArr = [];
foreach ($imgs as $img) {
// var_dump($img->getAttribute('lazyload'));
// die;
// return $img->getAttribute('lazyload');
$src = $img->getAttribute('lazyload');
if (strpos($src, 'http') === 0) {
$img_url = $src;
} else {
$img_url = $base_url . $src;
}
// return $img_url;
// 文件夹名称
$dirName = "product" . "Img";
// 文件保存位置
$fileSaveLocation = public_path('uploads') . $dirName . "\\";
// return $fileSaveLocation;
if (true !== $res = Tool::mkdir($fileSaveLocation)) {
return $res;
}
// 获取图片二进制数据
$imageContent = file_get_contents($img_url);
// var_dump($imageContent);
// die;
// return $imageContent;
// 保存图片到本地文件系统
file_put_contents($fileSaveLocation . basename($img_url), $imageContent);
$res_img_url = "/uoloads" . "/" . $dirName . "/" . basename($img_url);
return $res_img_url;
}
// return $imgArr;
return $titleArr;
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace app\api\controller\Crawler;
use app\Request;
use app\common\model\Flow\Flow as ModelFlow;
use app\common\model\Home\HomeEnv as ModelHomeEnv;
use app\common\model\Home\HomeWorks as ModelHomeWorks;
use think\db\Where;
use think\facade\Validate;
use app\BaseController;
use app\exception\ErrorMsg;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use Goutte\Client;
class CrawlerHoude extends BaseController
{
/**
* 爬虫测试
*/
public function Test(Request $request)
{
// return 1;
// 创建Goutte客户端对象
$client = new Client();
// 获取目标网站的URL
// $base_url = 'http://www.zs521.com/';
// $url = 'http://www.zs521.com/article_categories/guangzhou/articles';
$base_url = 'http://www.zs521.com/';
$url = 'http://www.zs521.com/article_categories/guangzhou/articles';
// return $url;
// 发起请求并获取响应对象
$crawler = $client->request('GET', $url);
// 选择要解析的HTML元素
$imgs = $crawler->filter('.grid img');
// return $titles;
// dump($titles);
// die;
// 在全部元素中筛选
// $products = $crawler->filter('.productsBody .productItem .products');
// foreach ($products as $key => $value) {
// // dump($value->filter('.productType')->eq($key)->text());
// // die;
// foreach ($titles->filter('.productType')->eq($key) as $key => $value) {
// $title = $value->textContent;
// }
// // return $title;
// }
$titleArr = [];
// 遍历元素并输出结果
// foreach ($titles as $title) {
// $titleArr[] = $title->textContent;
// }
// return $titleArr;
// $imgArr = [];
foreach ($imgs as $img) {
// var_dump($img->getAttribute('lazyload'));
// die;
// return $img->getAttribute('lazyload');
$src = $img->getAttribute('src');
if (strpos($src, 'http') === 0) {
$img_url = $src;
} else {
$img_url = $base_url . $src;
}
// return $img_url;
// 文件夹名称
$dirName = "HomeWorks" . "Img";
// 文件保存位置
$fileSaveLocation = public_path('uploads') . $dirName . "\\";
// return $fileSaveLocation;
if (true !== $res = Tool::mkdir($fileSaveLocation)) {
return $res;
}
$saveFileSaveLocation = $fileSaveLocation . date('Ymd') . "\\";
if (true !== $res = Tool::mkdir($saveFileSaveLocation)) {
return $res;
}
// 获取图片二进制数据
$imageContent = file_get_contents($img_url);
// var_dump($imageContent);
// die;
// return $imageContent;
// return $saveFileSaveLocation;
// 保存图片到本地文件系统
file_put_contents($saveFileSaveLocation . basename($img_url), $imageContent);
$res_img_url = "/uploads" . "/" . $dirName . "/" . date('Ymd') . "/" . basename($img_url);
// return $res_img_url;
// $model = ModelHomeEnv::create([
// 'home_env_img' => $res_img_url,
// 'home_env_sort' => 1
// ]);
$model = ModelHomeWorks::create([
'home_works_img' => $res_img_url,
'home_works_sort' => 1
]);
// return $res_img_url;
}
// return $imgArr;
return $titleArr;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace app\api\controller\Flow;
use app\Request;
use app\common\model\Flow\Flow as ModelFlow;
use think\db\Where;
use think\facade\Validate;
use app\BaseController;
use app\exception\ErrorMsg;
use think\facade\Filesystem;
class Flow extends BaseController
{
/**
* 添加流量访问记录
*/
public function AddFlowRecord(Request $request)
{
$params = $request->param();
$this->validate($params, ['flow_target' => 'require']);
$flow_target = $params['flow_target'];
try {
return (new ModelFlow)->track($flow_target);
return json(msg("添加流量访问记录成功"));
} catch (\Throwable $th) {
throwErrorMsg("错误信息:" . $th);
}
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace app\api\controller;
use app\common\arw\adjfut\src\Validate;
use app\BaseController;
use app\common\logic\Login as LogicLogin;
use app\Request;
use think\captcha\facade\Captcha;
use think\middleware\SessionInit;
use think\Response;
class Login extends BaseController
{
/**
* 初始化
*
* @date 2022-03-15
* @example
* @author admin
* @since 1.0.0
*/
protected function initialize(): void
{
$this->middleware[SessionInit::class] = [
'only' => ['accountLogin', 'getCaptcha']
];
}
/**
* 验证token
*
* @param Request $request
* @return void
*/
public function validateToken(Request $request)
{
$token = $request->getCurrentToken();
return [
'code' => 0,
'data' => [
'exp_time' => $token->token_exp_time,
],
'msg' => 'ok'
];
}
/**
* 西北政法大学单点登陆
*
* @param Request $request
* @return Response
* @date 2023-01-04
* @example
* @author admin
* @since 1.0.0
*/
public function casOauthLogin(Request $request): Response
{
$url = $request->param('url');
return LogicLogin::casOauthLogin(
LogicLogin::casOauthLoginHandle($url)
);
}
/**
* 西北政法大学单点登出
*
* @param Request $request
* @return Response
* @date 2023-01-04
* @example
* @author admin
* @since 1.0.0
*/
public function casOauthLogout(Request $request): Response
{
$url = $request->param('url');
return LogicLogin::casOauthLogout($url);
}
/**
* 用户账号登录
*
* @param Request $request
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function accountLogin(Request $request): array
{
$param = Validate::param([
'account|账号' => 'require',
'password|密码' => 'require',
'captcha|验证码' => $request->isProd() ? 'require|captcha' : false
]);
$token = LogicLogin::accountLogin(
$param['account'],
$param['password']
);
return [
'code' => 0,
'data' => [
'token' => $token->token_content,
'exp_time' => $token->token_exp_time,
],
'msg' => 'ok'
];
}
/**
* 生成验证码
*
* @date 2022-03-05
* @example
* @author admin
* @since 1.0.0
*/
public function getCaptcha()
{
return Captcha::create();
}
/**
* 用户登出
*
* @param Request $request
* @date 2022-03-09
* @example
* @author admin
* @since 1.0.0
*/
public function userLogout(Request $request): array
{
$token = $request->getCurrentToken();
$token->logout();
return [
'code' => 0,
'msg' => '登出成功'
];
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace app\api\controller\Tdk;
use app\BaseController;
use app\common\model\Tdk\Tdk as ModelTdk;
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use think\facade\Db;
use app\common\exception\Tool;
use think\facade\Env;
class Tdk extends BaseController
{
public function createXmd()
{
$table_all = Db::query('SHOW TABLES');
$arr = [];
$match = ['_copy', '_copy1', 'demo', '_lpf', '_20'];
$res_str = '';
foreach ($table_all as $key => $it) {
$item = $it['Tables_in_shop_uniapp'];
$currentMatch = '';
foreach ($match as $matchKey => $matchItem) {
if (stripos($item, $matchItem) === false) {
$currentMatch = '';
} else {
$currentMatch = $item;
break;
}
}
if ($item != $currentMatch) {
$arr[$item] = [
'comment' => $table_info = Db::query('SHOW TABLE STATUS LIKE ' . "'" . $item . "'")[0]['Comment'],
'children' => Db::query('SHOW FULL COLUMNS FROM `' . $item . '`')
];
}
}
function createXmdStr(&$res_str, $arr)
{
foreach ($arr as $key => &$item) {
if ($item['comment'] != '') {
$res_str .= "\n{$key}\n\t" . $item['comment'];
if ($item['children']) {
foreach ($item['children'] as $childKey => &$childItem) {
if ($childItem['Comment'] == '') {
$res_str .= "\n\t\t{$childItem['Field']}";
} else {
$childItem['Comment'] = str_replace(",", '', $childItem['Comment']);
$childItem['Comment'] = str_replace("]", ')', $childItem['Comment']);
$childItem['Comment'] = str_replace("[", '(', $childItem['Comment']);
$res_str .= "\n\t\t{$childItem['Field']}\n\t\t\t{$childItem['Comment']}";
}
}
}
} else {
$res_str .= "\n{$key}";
if ($item['children']) {
foreach ($item['children'] as $childKey => &$childItem) {
if ($childItem['Comment'] == '') {
$res_str .= "\n\t{$childItem['Field']}";
} else {
$childItem['Comment'] = str_replace(",", '', $childItem['Comment']);
$childItem['Comment'] = str_replace("]", ')', $childItem['Comment']);
$childItem['Comment'] = str_replace("[", '(', $childItem['Comment']);
$res_str .= "\n\t{$childItem['Field']}\n\t\t{$childItem['Comment']}";
}
}
}
}
}
}
createXmdStr($res_str, $arr);
echo $res_str;
return '';
}
/**
* 获取网站tdk详情
*/
public function getTdkInfo(Request $request): array
{
$params = $request->param();
$this->validate($params, ['tdk_type' => 'require']);
$find = ModelTdk::field([
'tdk_id',
'tdk_type',
'tdk_title',
'tdk_description',
'tdk_keyword'
])
->where('tdk_type', $params['tdk_type'])
->find();
return msg(0, '获取网站tdk详情成功!', ['data' => $find]);
}
}

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace app\api\middleware;
use app\common\exception\NotAuthApi;
use app\common\model\Token;
use app\common\traits\Auth as TraitsAuth;
use app\Request;
use think\Response;
class Auth
{
use TraitsAuth;
/**
* 忽略登录
*
* @var array
* @date 2023-01-07
* @example
* @author admin
* @since 1.0.0
*/
private $ignoreLogin = [
'Login' => ['*'],
'admin' => ['*'],
'Consult' => ['*'],
];
/**
* 处理请求
*
* @param \app\Request $request
* @param \Closure $next
* @return Response
*/
public function handle(Request $request, \Closure $next): Response
{
$this->request = $request;
if ($this->isIgnoreLogin()) {
return $next($request);
}
if ($request->isLogin()) {
$user = $request->getCurrentUser();
if ($user->user_admin) {
return $next($request);
}
}
$api = join('/', [
$request->controller(),
$request->action()
]);
$token = Token::getCurrent();
/**
* 缓存接口权限验证
*/
$validate = $this->validateApi($token->token_api);
/**
* 缓存权限验证不通过
*/
if ($validate === false) {
/**
* 自定义验证权限
* 如果验证通过刷新缓存接口
*/
$validate = $this->validateUser();
if ($validate) {
Token::getCurrentUser()->login();
}
}
/**
* 权限验证通过
*/
if ($validate) {
return $next($request);
}
throw new NotAuthApi("未授权接口 [ $api ]", 1);
}
}

12
app/api/route.php Normal file
View File

@ -0,0 +1,12 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// 中间件
'middleware' => [
// 接口鉴权
// \app\api\middleware\Auth::class
],
];

178
app/common.php Normal file
View File

@ -0,0 +1,178 @@
<?php
// 应用公共文件
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use app\BaseController;
use think\facade\Request;
use think\facade\Validate;
use think\Collection;
use think\db\Query;
use think\Validate as ThinkValidate;
const DS = DIRECTORY_SEPARATOR;
function fump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
{
$label = (null === $label) ? '' : rtrim($label) . ':';
ob_start();
var_dump($var);
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean());
if (!extension_loaded('xdebug')) {
$output = htmlspecialchars($output, $flags);
}
$output = '<pre>' . $label . $output . '</pre>';
if ($echo) {
echo ($output);
return;
}
return $output;
}
/**
* 抛出业务异常
*
* @param string $msg
* @param integer $code
* @return void
* @throws ErrorMsg
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
function throwErrorMsg(string $msg, int $code = 1): void
{
throw new ErrorMsg($msg, $code);
}
/**
* 计算年龄
*
* @param string $birthday Y-m-d
* @return int
* @date 2022-03-11
* @example
* @author admin
* @since 1.0.0
*/
function sumAge($birthday): int
{
$age = 0;
try {
if (!is_string($birthday)) {
throw new ErrorMsg("非法日期", 1);
}
$birthday = explode("-", date('Y-m-d', strtotime($birthday)));
$date = explode("-", date('Y-m-d'));
if (count($birthday) === 3) {
list($y, $m, $d) = $birthday;
} else {
list($y, $m, $d) = $date;
}
// list($y, $m, $d) = $birthday;
list($dy, $dm, $dd) = $date;
$age = $dy - $y;
if ($dm >= $m) {
if ($dm == $m) {
if ($dd >= $d) {
} else {
$age--;
}
}
} else {
$age--;
}
return $age;
} catch (\Throwable $th) {
}
return $age;
}
Validate::maker(function (ThinkValidate $validate) {
$validate->extend(
'string',
function ($value) {
return is_string($value);
},
':attribute 数据类型非法 不是字符串'
);
});
/**
* 接口返回封装
*/
function msg(...$arr)
{
$msg_data = ['code' => 0];
$default_code_msg = [0 => '操作成功!', 1 => '操作失败!'];
//data数据构建
function constructData(&$msg_data, $code, $msg_str, &$arr2)
{
$msg_data['code'] = $code;
$msg_data['msg'] = $msg_str;
if (is_array($arr2)) { //数组
$msg_data['data'] = $arr2;
}
if (is_object($arr2)) { //查询对象
$obj = $arr2;
$soft_delete_field = '_delete_time'; //软删除默认后缀
//模型层 || Db层
if ($obj instanceof think\db\Query || $obj instanceof think\Collection) {
//join软删除字段过滤补全(暂时只适应软删除为 【前缀名_delete_time】存储为datetime 的设计)
if (isset($obj->getOptions()['join']) && $join_data = $obj->getOptions('join')) {
$join_soft_delete = [];
foreach ($join_data as $key => $join) {
$join_table_name = is_array($join[0]) ? array_keys($join[0])[0] : $join[0]; //联表表名
$join_soft_delete[] = [$join_table_name . $soft_delete_field, 'NULL', null];
}
$obj = $obj->where($join_soft_delete);
}
//select()、count()补全
$msg_data['data'] = $obj->page((int) Request::param('page', 1), (int) Request::param('limit', 10))->select();
$msg_data['count'] = $obj->count();
} else {
return ['code' => 444, 'msg' => '对象只允许传递来自think\db\Query类和instanceof think\Collection类的实例!'];
}
}
};
switch (count($arr)) {
case 1: //单参数
constructData($msg_data, 0, '查询成功!', $arr[0],);
if (is_string($arr[0])) $msg_data['msg'] = $arr[0];
if (is_int($arr[0])) {
$msg_data['code'] = $arr[0];
$msg_data['msg'] = $default_code_msg[$arr[0]];
}
break;
case 2: //双参数
if (is_int($arr[0]) && is_string($arr[1])) {
$msg_data['code'] = $arr[0];
$msg_data['msg'] = $arr[1];
} else {
constructData($msg_data, 0, $arr[0], $arr[1]);
}
break;
case 3: //三参数
$msg_data['code'] = $arr[0];
$msg_data['msg'] = $arr[1];
if (is_object($arr[2])) {
$msg_data['data'] = $arr[2];
} else {
foreach ($arr[2] as $key => $val)
$msg_data[$key] = $val;
}
break;
default:
return ['code' => 444, 'msg' => 'msg()最多参数只允许3个!'];
}
return $msg_data;
}

View File

@ -0,0 +1,39 @@
{
"name": "arw/adjfut",
"type": "library",
"autoload": {
"psr-4": {
"arw\\adjfut\\": "src/"
}
},
"authors": [{
"name": "arw",
"email": "2679599887@qq.com"
}],
"require": {
"php": ">=7.1.0",
"phpoffice/phpspreadsheet": "^1.12",
"topthink/framework": "^6.0.0",
"topthink/think-filesystem": "^2.0"
},
"config": {
"sort-packages": true
},
"repositories": {
"packagist": {
"type": "composer",
"url": "https://mirrors.aliyun.com/composer/"
}
},
"extra": {
"think": {
"services": [
"arw\\adjfut\\Service\\Validate"
],
"config": {
"wechat": "src/Config/wechat.php",
"chunkUpload": "src/Config/chunkUpload.php"
}
}
}
}

View File

@ -0,0 +1,170 @@
<?php
namespace app\common\arw\adjfut\src;
use ArrayAccess;
class ArrayFilter implements ArrayAccess
{
/**
* 数据
*
* @var array
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
private $data = [];
/**
* 初始化
*
* @param array $data
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(array $data)
{
$this->data = $data;
}
/**
* 二维数组排除字段
*
* @param array $array
* @param array $except
* @return array
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public static function exceptFields(array $array, array $except): array
{
foreach ($array as &$value) {
$self = new self($value);
$value = $self->except($except)->toArray();
unset($value, $self);
}
return $array;
}
/**
* 二维数组指定字段
*
* @param array $array
* @param array $only
* @return array
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public static function onlyFields(array $array, array $only): array
{
foreach ($array as &$value) {
$self = new self($value);
$value = $self->only($only)->toArray();
unset($value, $self);
}
return $array;
}
/**
* 排除某些变量
*
* @param array $fields
* @return self
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public function except(array $fields): self
{
$data = $this->data;
// $fields = [id,name]
foreach ($fields as $field) {
if (isset($data[$field])) {
unset($data[$field]);
}
}
return new self($data);
}
/**
* 获取部分变量
*
* @param array $fields
* @return self
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public function only(array $fields): self
{
$data = $this->data;
$result = [];
// $fields = [id,name]
// $fields = [id=>0,name=>asdasd]
foreach ($fields as $key => $value) {
if (is_int($key)) {
if (isset($data[$value])) {
$result[$value] = $data[$value];
}
} else {
$result[$key] = isset($data[$key]) ? $data[$key] : $value;
}
}
return new self($result);
}
/**
* 获取数据
*
* @return array
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
public function toArray(): array
{
return $this->data;
}
public function __get($name)
{
return $this->offsetGet($name);
}
public function __set($name, $value)
{
return $this->offsetSet($name, $value);
}
public function offsetExists($offset): bool
{
return isset($this->data[$offset]);
}
public function offsetGet($offset)
{
return $this->data[$offset];
}
public function offsetSet($offset, $value): void
{
$this->data[$offset] = $value;
}
public function offsetUnset($offset): void
{
unset($this->data[$offset]);
}
}

View File

@ -0,0 +1,174 @@
<?php
namespace app\common\arw\adjfut\src;
use think\helper\Arr;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
class Base64
{
/**
* 类型
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $type = '';
/**
* base64内容
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $body = '';
/**
* base64
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $base64 = '';
/**
* 文件后缀映射
*/
private const FILE_EXT_MAP = [
'text/html' => 'html',
'text/css' => 'css',
'text/javascript' => 'js',
'image/gif' => 'gif',
'image/png' => 'png',
'image/jpeg' => 'jpg',
'image/x-icon' => 'ico',
];
/**
* 实例化
*
* @param string $base64
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(string $base64)
{
$parse = self::parse($base64);
$this->base64 = $base64;
$this->type = $parse['type'];
$this->body = $parse['body'];
}
/**
* 获取文件后缀
*
* @return string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function getFileExt(): string
{
return Arr::get(self::FILE_EXT_MAP, $this->type, '');
}
/**
* 判断是否是base64图片字符串
*
* @return boolean
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function isImage(): bool
{
return in_array($this->type, [
'image/gif',
'image/png',
'image/jpeg',
'image/x-icon',
]);
}
/**
* 保存base64图片
*
* @param string $path
* @return void
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function saveImage(string $path): void
{
file_put_contents($path, base64_decode($this->body));
}
/**
* 判断是否是base64图片字符串
*
* @param string $base64
* @return boolean
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public static function isBase64Image(string $base64): bool
{
$ins = new self($base64);
return $ins->isImage();
}
/**
* 保存base64图片
*
* @param string $base64
* @param string $path
* @return void
*/
public static function saveBase64Image(string $base64, string $path): void
{
$ins = new self($base64);
$ins->saveImage($path);
}
/**
* 解析base64
*
* @param string $base64
* @return array
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private static function parse(string $base64): array
{
$prefix = 'data:';
$validate = substr($base64, 0, strlen($prefix)) === $prefix;
if (!$validate) {
throw new ErrorMsg("非法base64 开头data:", 1);
}
$explode = explode(';base64,', $base64);
if (count($explode) != 2) {
throw new ErrorMsg("非法base64 不存在;base64,", 1);
}
list($type, $body) = $explode;
$type = str_replace('data:', '', $type);
return [
'type' => $type,
'body' => $body
];
}
}

View File

@ -0,0 +1,258 @@
<?php
namespace app\common\arw\adjfut\src;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use think\facade\Filesystem;
use think\facade\Request;
use think\file\UploadedFile;
use think\filesystem\Driver;
use think\helper\Arr;
class ChunkUpload
{
/**
* 磁盘
*
* @var string
*/
private static $disk = null;
/**
* id
*
* @var string
*/
private $id = null;
/**
* 上传文件
*
* @var UploadedFile
*/
private $file = null;
/**
* 合并文件
*
* @var UploadedFile
*/
private $mergeFile = null;
/**
* 初始化
*
* @param string $id 上传id
* @param string $name 上传id
* @param UploadedFile|string|array $file 文件
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(string $id, $file = 'file')
{
$this->id = $id;
if (is_string($file)) {
$file = Request::file($file);
}
if (is_array($file)) {
$file = new UploadedFile(...$file);
}
if (!($file instanceof UploadedFile)) {
throw new ErrorMsg("非法文件", 1);
}
$this->file = $file;
}
/**
* 获取存储配置
*
* @return Driver
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public function getDisk(): Driver
{
return Filesystem::disk(self::$disk);
}
/**
* 分片是否上传完成
*
* @return boolean
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public function isDone(): bool
{
return $this->isDone;
}
/**
* 合并文件
*
* @return self
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public function merge(string $name): self
{
if (!$this->isDone()) {
throw new ErrorMsg("文件未上传完成 禁止合并", 1);
}
$id = $this->id;
$root = self::getDiskConfig('root');
$path = join('', [
$root,
$id,
'.' . pathinfo($name, PATHINFO_EXTENSION)
]);
for ($i = 0; $i <= $this->total; $i++) {
$_path = $root . $id . DIRECTORY_SEPARATOR . $i;
file_put_contents(
$path,
file_get_contents($_path),
FILE_APPEND
);
unlink($_path);
}
rmdir($root . $id);
$this->mergeFile = new UploadedFile($path, $name);
return $this;
}
/**
* 获取合并文件
*
* @return UploadedFile
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public function getMergeFile(): UploadedFile
{
if (!$this->mergeFile) {
throw new ErrorMsg("未合并文件", 1);
}
return $this->mergeFile;
}
/**
* 获取上传文件
*
* @param string $diskName
* @param string $validate
* @return UploadFile
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public function getUploadFile(string $diskName, string $validate = ''): UploadFile
{
return new UploadFile(
$diskName,
$validate,
$this->getMergeFile()
);
}
/**
* 保存文件
*
* @param integer $index
* @param integer $total
* @return self
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public function put(int $index, int $total): self
{
$this->total = $total;
$this->getDisk()->putFileAs(
$this->id,
$this->file,
$index
);
$this->isDone = $index === $total;
return $this;
}
/**
* 删除文件
*
* @return void
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public function delete(): void
{
$this->getDisk()->delete($this->getMergeFile()->getFilename());
}
/**
* 初始化配置
*
* @param array $config
* @return void
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public static function init(array $config): void
{
self::$disk = Arr::get($config, 'disk', '');
$type = self::getDiskConfig('type');
if ($type != 'local') {
throw new ErrorMsg("分片上传仅支持 local类型磁盘", 1);
}
}
/**
* 获取磁盘配置
*
* @param string $name
* @param mixed $default
* @return mixed
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public static function getDiskConfig($name = null, $default = null)
{
return Filesystem::getDiskConfig(self::$disk, $name, $default);
}
/**
* 生成上传唯一id
*
* @return string
* @date 2022-08-21
* @example
* @author arw
* @since 1.0.0
*/
public static function generateUploadId(): string
{
return md5(join('-', [
Request::ip(),
time()
]));
}
}

View File

@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | ChunkUpload配置文件
// +----------------------------------------------------------------------
return [
// 磁盘
'disk' => ''
];

View File

@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | Wechat配置文件
// +----------------------------------------------------------------------
return [
// 中控层 access_key
'access_key' => '',
// 中控层地址
'server_base' => 'http://clique.dszjjt.com:7001/',
// 公众号配置
'gzh' => [
// 缓存key
'cache_key' => 'wechat.gzh',
// 日志通道
'log_channel' => 'wechat/gzh',
// 公众号模板推送
'template' => [
// {{first.DATA}}
// 申请人:{{keyword1.DATA}}
// 申请时间:{{keyword2.DATA}}
// 调课时间:{{keyword3.DATA}}
// {{remark.DATA}}
'申请审核通知' => 'iLuqzUrQM8-v_7ilxageUZoi9v-SQWipkTYVZXB1GeI'
],
'appid' => 'wx99caf571c9fb1cc5',
'secret' => '61100d9c4fff5acf97517f0c8799e2e2',
],
// 小程序配置
'xcx' => [
// 缓存key
'cache_key' => 'wechat.xcx',
// 日志通道
'log_channel' => 'wechat/xcx',
'appid' => 'wx2495492e15854b02',
'secret' => '5b4fcb27324398083fba4a3c39e8a557',
],
];

View File

@ -0,0 +1,790 @@
<?php
namespace app\common\arw\adjfut\src;
use \CurlHandle;
use think\Exception;
class Curl
{
/**
* 请求前缀
*
* @var string
*/
private $baseUrl = '';
/**
* 返回数据类型
*
* @var string
*/
private $dataType = 'json';
/**
* 事件
*
* @var array
*/
private $event = [];
/**
* 绑定
*
* @var array
*/
private $bind = [];
/**
* 配置项
*
* @var array
*/
private $config = [
/**
* http header
*/
CURLOPT_HTTPHEADER => [],
/**
* http 超时
*/
CURLOPT_TIMEOUT => 0,
/**
* http 代理
*/
CURLOPT_PROXY => '',
/**
* http 代理端口
*/
CURLOPT_PROXYPORT => '',
/**
* 来源页面
*/
CURLOPT_REFERER => '',
/**
* 用户代理
*/
CURLOPT_USERAGENT => '',
/**
* 响应中是否显示header
*/
CURLOPT_HEADER => 0
];
/**
* Get请求参数
*
* @var null
*/
private $httpParams = null;
/**
* Post请求参数
*
* @var null
*/
private $httpData = null;
/**
* 异常信息
*
* @var string
*/
private $error = '';
/**
* url是否编码
*
* @var boolean
*/
private $urlEncode = false;
/**
* 验证http请求状态码
*
* @var boolean
*/
private $httpCode = true;
/**
* 请求路径
*
* @var string
*/
private $path = '';
/**
* 请求url
*
* @var string
*/
private $url = '';
/**
* 请求类型
*
* @var string
*/
private $method = 'get';
/**
* 返回内容
*
* @var string
*/
private $content = '';
/**
* 请求信息
*
* @var array
*/
private $status = [];
/**
* 调试模式
*
* @var boolean
*/
private $debug = false;
/**
* 是否上传文件
*
* @var boolean
*/
private $isUploadFile = false;
/**
* 初始化
*
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
}
/**
* 初始化请求
*
* @return $this
*/
public static function instance()
{
return new self;
}
/**
* 获取请求url
*/
public function getUrl(): string
{
return $this->url;
}
/**
* 获取请求路径
*/
public function getPath(): string
{
return $this->path;
}
/**
* 获取返回内容
*
* @return string
*/
public function getContent(): string
{
return $this->content;
}
/**
* 格式化返回
*
* @param string $type 返回类型(默认使用 $this->dataType
* @return mixed
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function formatContent(string $type = '')
{
$content = $this->content;
if (!$type) {
$type = $this->dataType;
}
switch ($type) {
case 'json':
case 'object':
return json_decode($content, $type === 'json');
break;
default:
return $content;
break;
}
}
/**
* 获取请求信息
*/
public function getStatus(): array
{
return $this->status;
}
/**
* 设置是否上传文件
*/
public function setIsUploadFile(bool $isUploadFile): self
{
$this->isUploadFile = $isUploadFile;
return $this;
}
/**
* 设置请求路径
*/
public function setPath(string $path): self
{
if (count(explode('?', $path)) != 1) {
throw new Exception("请求路径不能携带参数 请使用setParams设置请求参数", 1);
}
$this->path = $path;
return $this;
}
/**
* 设置调试模式
*/
public function setDeBug(bool $debug): self
{
$this->debug = $debug;
return $this;
}
/**
* 验证http状态码
*
* @param boolean|int $code
*/
public function setHttpCode($code): self
{
$this->httpCode = $code;
return $this;
}
/**
* 设置url是否编码
*/
public function setUrlEncode(bool $urlEncode): self
{
$this->urlEncode = $urlEncode;
return $this;
}
/**
* 设置请求前缀
*/
public function setBaseUrl(string $baseUrl): self
{
if (count(explode('?', $baseUrl)) != 1) {
throw new Exception("请求前缀不能携带参数 请使用setParams设置请求参数", 1);
}
$this->baseUrl = $baseUrl;
return $this;
}
/**
* 设置返回数据类型
*/
public function setDataType(string $dataType): self
{
$this->dataType = $dataType;
return $this;
}
/**
* 设置http header
*/
public function setHeader(array $header): self
{
$_header = [];
foreach ($header as $key => $value) {
if (is_numeric($key)) {
$_header[] = $value;
} else {
$_header[] = "$key: $value";
}
}
$this->config[CURLOPT_HTTPHEADER] = array_merge($this->config[CURLOPT_HTTPHEADER], $_header);
return $this;
}
/**
* 设置http 超时
*/
public function setTimeout(int $time): self
{
$this->config[CURLOPT_TIMEOUT] = $time <= 0 ? 5 : $time;
return $this;
}
/**
* 设置http 代理
*/
public function setProxy(string $proxy): self
{
$this->config[CURLOPT_PROXY] = $proxy;
return $this;
}
/**
* 设置http 代理端口
*/
public function setProxyPort(int $port): self
{
$this->config[CURLOPT_PROXYPORT] = $port;
return $this;
}
/**
* 设置来源页面
*/
public function setReferer(string $referer = ""): self
{
$this->config[CURLOPT_REFERER] = $referer;
return $this;
}
/**
* 设置用户代理
*/
public function setUserAgent(string $agent = ""): self
{
$this->config[CURLOPT_USERAGENT] = $agent;
return $this;
}
/**
* http响应中是否显示header
*/
public function showResponseHeader(bool $show): self
{
$this->config[CURLOPT_HEADER] = $show ? 1 : 0;
return $this;
}
/**
* 获取get请求参数
*/
public function getParams()
{
return $this->httpParams;
}
/**
* 设置get请求的参数
*/
public function setParams($params): self
{
$_params = $this->trigger('beforeSetParams', [$params]);
$this->httpParams = $_params ? $_params : $params;
return $this;
}
/**
* 获取post请求参数
*/
public function getData()
{
return $this->httpData;
}
/**
* 设置post请求的参数
*/
public function setData($data): self
{
$_data = $this->trigger('beforeSetData', [$data]);
$this->httpData = $_data ? $_data : $data;
return $this;
}
/**
* 设置http请求的cookie信息
*/
public function setCookie(array $cookie): self
{
$_cookie = [];
foreach ($cookie as $key => $value) {
$_cookie[] = "$key=$value";
}
$this->config[CURLOPT_COOKIE] = join(';', $_cookie);
return $this;
}
/**
* 设置证书路径
*/
public function setCainfo(string $file): self
{
$this->config[CURLOPT_CAINFO] = $file;
return $this;
}
/**
* 获取请求异常
*/
public function getError(): string
{
return $this->error;
}
/**
* 绑定
*
* @param string $name
* @param callable $cb
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function bind(string $name, callable $cb): self
{
$this->bind[$name] = $cb;
return $this;
}
/**
* GET请求
* @param string $path
* @return mixed
*/
public function get(string $path = '')
{
if ($path) {
$this->setPath($path);
}
$this->method = 'get';
$respone = $this->fetch();
if (!$path) {
return $this;
}
return $respone;
}
/**
* POST请求
*
* @param string $path
* @return mixed
*/
public function post(string $path = '')
{
if ($path) {
$this->setPath($path);
}
$this->method = 'post';
$respone = $this->fetch();
if (!$path) {
return $this;
}
return $respone;
}
/**
* PUT请求
*
* @param string $path
* @return mixed
*/
public function put(string $path = '')
{
if ($path) {
$this->setPath($path);
}
$this->method = 'put';
$respone = $this->fetch();
if (!$path) {
return $this;
}
return $respone;
}
/**
* DELETE请求
*
* @param string $path
* @return mixed
*/
public function delete(string $path = '')
{
if ($path) {
$this->setPath($path);
}
$this->method = 'delete';
$respone = $this->fetch();
if (!$path) {
return $this;
}
return $respone;
}
/**
* PATCH请求
*
* @param string $path
* @return mixed
*/
public function patch(string $path = '')
{
if ($path) {
$this->setPath($path);
}
$this->method = 'patch';
$respone = $this->fetch();
if (!$path) {
return $this;
}
return $respone;
}
/**
* 设置get传参前
*
* @param callable $cb($params)
* @param mixed $params 参数
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function onBeforeSetParams(callable $cb): self
{
return $this->event('beforeSetParams', $cb);
}
/**
* 设置post传参前
*
* @param callable $cb($data)
* @param mixed $data 参数
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function onBeforeSetData(callable $cb): self
{
return $this->event('beforeSetData', $cb);
}
/**
* 请求前
*
* @param callable $cb($ch)
* @param self $ch
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function onBeforeFetch(callable $cb): self
{
return $this->event('beforeFetch', $cb);
}
/**
* 请求后
*
* @param callable $cb($ch,$info)
* @param self $ch
* @param array $info
* @param string $info[url] 请求完整url
* @param string $info[content] 返回信息
* @param array $info[status] 请求信息
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function onAfterFetch(callable $cb)
{
return $this->event('afterFetch', $cb);
}
/**
* 请求异常(返回true阻止抛出异常)
*
* @param callable $cb($ch,$th)
* @param self $ch
* @param \Throwable $th
* @date 2022-03-26
* @example
* @author arw
* @since 1.0.0
*/
public function onFetchError(callable $cb)
{
return $this->event('fetchError', $cb);
}
/**
* 发起请求
*
* @return mixed
* @date 2022-03-25
* @example
* @author arw
* @since 1.0.0
*/
public function fetch()
{
$this->url = $url = $this->baseUrl . $this->path;
$this->trigger('beforeFetch', [$this]);
try {
$httpParams = $this->httpParams;
if (!empty($httpParams) && is_array($httpParams)) {
$url .= (strpos($url, '?') === false ? '?' : '') . http_build_query($httpParams);
}
if ($this->urlEncode === false) {
$url = urldecode($url);
}
$ch = $this->curlInit($url);
// 设为TRUE把curl_exec()结果转化为字串,而不是直接输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$this->content = $content = curl_exec($ch);
$this->status = $status = curl_getinfo($ch);
if ($content === false) {
$this->error = curl_error($ch);
}
if ($this->debug) {
dump([
'ch' => $ch,
'url' => $url,
'httpParams' => $this->httpParams,
'httpData' => $this->httpData,
'contentArray' => json_decode($content, true),
'content' => $content,
'status' => $status
]);
}
curl_close($ch);
$httpCode = $this->httpCode;
if ($httpCode === true) {
$httpCode = 200;
}
if (is_numeric($httpCode)) {
if (!isset($status['http_code'])) {
throw new Exception("服务器未返回状态码", 1);
}
if ($status['http_code'] !== $httpCode) {
throw new Exception(sprintf("服务器返回状态码异常[ %s ]", $status['http_code']), 1);
}
}
$this->trigger('afterFetch', [$this, [
'url' => $url,
'content' => $content,
'status' => $status
]]);
return $this->formatContent();
} catch (\Throwable $th) {
if (!$this->trigger('fetchError', [$this, $th])) {
throw $th;
}
}
}
public function __call($name, $arguments)
{
array_unshift($arguments, $this);
call_user_func_array($this->bind[$name], $arguments);
}
/**
* 事件
*
* @param string $type beforeSetParams | beforeSetData | beforeFetch | AfterFetch
* @param callable $cb
*/
private function event(string $type, callable $cb): self
{
$this->event[strtoupper($type)] = $cb;
return $this;
}
/**
* 事件触发器
*
* @param string $type
* @return mixed
* @date 2022-03-25
* @example
* @author arw
* @since 1.0.0
*/
private function trigger(string $type, array $args = [])
{
$type = strtoupper($type);
if (isset($this->event[$type])) {
return call_user_func_array($this->event[$type], $args);
}
}
/**
* 初始化请求
*
* @param string $url
* @return resource|CurlHandle
* @date 2022-04-24
* @example
* @author arw
* @since 1.0.0
*/
private function curlInit(string $url)
{
$ch = curl_init($url);
if (stripos($url, 'https://') !== false) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
}
foreach ($this->config as $option => $value) {
if (!$value && $option != CURLOPT_HEADER) {
continue;
}
curl_setopt($ch, $option, $value);
}
switch ($this->method) {
case 'get':
break;
case 'post':
curl_setopt($ch, CURLOPT_POST, true);
break;
default:
// delete put patch
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method));
break;
}
$httpData = $this->httpData;
// 设置post body
if ($httpData) {
$data = null;
if (is_array($httpData)) {
if ($this->isUploadFile) {
$data = $httpData;
} else {
$data = http_build_query($httpData);
}
} else if (is_string($httpData)) {
$data = $httpData;
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
return $ch;
}
}

View File

@ -0,0 +1,519 @@
<?php
namespace app\common\arw\adjfut\src;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use think\helper\Arr;
use think\helper\Str;
use think\Validate;
use app\common\arw\adjfut\src\Traits\Event;
use think\facade\Env;
class Excel
{
use Event;
/**
* Spreadsheet
*
* @var Spreadsheet
*/
private $spreadsheet = null;
/**
* 工作表样式
*
* @var array
*/
private $worksheetStyle = [];
/**
* 文件路径
*
* @var string
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
private $path = '';
/**
* 初始化
*
* @param string|UploadFile $path
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
public function __construct($path = '')
{
if ($path) {
if ($path instanceof UploadFile) {
$path = $path->getRealPath();
}
$this->spreadsheet = self::loadExcel($path);
$this->path = $path;
} else {
$this->spreadsheet = new Spreadsheet();
}
}
/**
* 设置工作表样式
*
* @param array $options
* @param boolean $options[autoBorder] 是否自动添加边框
* @param boolean $options[freezeLine] 是否自动冻结首行
* @return self
*/
public function setWorksheetStyle(array $options): self
{
$this->worksheetStyle = $options;
return $this;
}
/**
* 获取当前Spreadsheet
*
* @return Spreadsheet
* @date 2020-11-23
* @example
* @author arw
* @since 1.0.0
*/
public function getSpreadsheet(): Spreadsheet
{
return $this->spreadsheet;
}
/**
* 读取excel
*
* @param string $path excel路径
* @return Spreadsheet
*/
public static function loadExcel(string $path): Spreadsheet
{
if (!is_file($path)) {
throw new ErrorMsg("文件不存在 $path", 1);
}
$ext = pathinfo($path, PATHINFO_EXTENSION);
return IOFactory::createReader(ucfirst($ext))->load($path);
}
/**
* 获取工作表
* 默认获取当前激活的
*
* @param string $sheet
* @return Worksheet
*/
public function getWorksheet(string $sheet = 'active'): Worksheet
{
$Spreadsheet = $this->getSpreadsheet();
if ($sheet === 'active') {
$Worksheet = $Spreadsheet->getActiveSheet();
} else {
$Worksheet = $Spreadsheet->getSheet($sheet);
}
return $Worksheet;
}
/**
* 获取excel数据
*
* @param string $sheet
* @return array
*/
public function getExcelData(string $sheet = 'active'): array
{
return $this->getWorksheet($sheet)->toArray();
}
/**
* 解析excel
*
* @param array $rules
* @param string $rules[][title] 表头
* @param string $rules[][validate] 验证器
* @param string $rules[][field] 字段名
* @param string $rules[][type] 数据类型 date:日期格式(Y-m-d H:i:s)
* @param array $options
* @param array $options[titleLine] 表头行
* @param boolean $options[autoRemove] 解析后移除文件
* @param boolean $options[ignoreNullRow] 忽略空行
* @return array
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public function parseExcel(array $rules, array $options = []): array
{
$titleLine = Arr::get($options, 'titleLine', [1]);
$autoRemove = Arr::get($options, 'autoRemove', true);
$ignoreNullRow = Arr::get($options, 'ignoreNullRow', true);
if ($autoRemove) {
if (is_file($this->path)) {
unlink($this->path);
}
}
$title = [];
$_title = [];
$_field = [];
$_type = [];
$_validate = [];
foreach ($rules as $rule) {
$_title[] = $rule['title'];
if (isset($rule['field'])) {
$_field[] = $rule['field'];
if (isset($rule['validate'])) {
$_validate[join('|', [
$rule['field'],
$rule['title']
])] = $rule['validate'];
}
} else {
$_field[] = '';
}
if (isset($rule['type'])) {
$_type[] = $rule['type'];
} else {
$_type[] = '';
}
}
$Worksheet = $this->getWorksheet();
$data = $Worksheet->toArray();
// 获取表头 合并复杂表头
foreach ($titleLine as $line) {
$values = $data[$line - 1];
foreach ($values as $key => $value) {
if ($value) {
$title[$key] = $value;
}
}
}
// 验证表头
if ($title != $_title) {
throw new ErrorMsg("表头不一致 请确认excel是否正确", 1);
}
// 去除非数据字段
$i = max($titleLine) - 1;
do {
unset($data[$i]);
$i--;
} while ($i >= 0);
$validate = new Validate;
$validate->rule($_validate)->batch(true)->failException(true);
$result = [];
$error = [];
foreach ($data as $row => $value) {
$index = $row + 1;
$line = "${index}";
$_value = [];
if ($ignoreNullRow && self::isNullRow($value)) {
continue;
}
foreach ($_field as $column => $field) {
if ($field) {
if ($_type[$column]) {
$type = $_type[$column];
if (Str::startsWith($type, 'date') && $value[$column]) {
$format = 'Y-m-d H:i:s';
$types = explode(':', $type);
if (isset($types[1])) {
$format = $types[1];
}
$value[$column] = Tool::conversionDateTime($value[$column], $format);
}
}
$_value[$field] = $value[$column];
}
}
// 验证数据
if ($_validate) {
try {
$validate->check($_value);
} catch (\Throwable $th) {
$error[] = $line . '' . $th->getMessage();
}
}
$result[$line] = $_value;
}
if ($error) {
throw new ErrorMsg(join(PHP_EOL, $error), 1);
}
return $result;
}
/**
* 导出excel
*
* @param array $data 数据
* @param array $options
* @param boolean $options[autoBorder] 是否自动添加边框
* @param string $options[freezeLine] 是否自动冻结首行
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public static function exportSheet(array $data, array $options = [])
{
$excel = new self;
$excel->setWorksheetStyle($options);
$spreadsheet = $excel->getSpreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray($data);
return $excel;
}
/**
* 导出处理图片字段
*/
public static function ExportImgFiled($value)
{
if(strpos($value,"http") !== false){
$base_url = "";
}else{
$base_url = Env::get('APP.DEFAULT_IMG_URL');
}
return $base_url . $value;
}
/**
* 导出excel 多sheet
*
* @param array $data 数据
* @param array $options
* @param boolean $options[autoBorder] 是否自动添加边框
* @param string $options[freezeLine] 是否自动冻结首行
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public static function exportMoreSheet(array $data, array $options = [])
{
$excel = new self;
$excel->setWorksheetStyle($options);
$spreadsheet = $excel->getSpreadsheet();
$spreadsheet->removeSheetByIndex(0);
foreach ($data as $key => $value) {
$worksheet = new Worksheet($spreadsheet, $key);
$worksheet->fromArray($value);
$spreadsheet->addSheet($worksheet);
}
$spreadsheet->setActiveSheetIndex(0);
return $excel;
}
/**
* 保存excel
*
* @param string $name 文件名
* @param string $save 保存路径
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public function save(string $name, string $save = 'php://output')
{
$spreadsheet = $this->getSpreadsheet();
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$_name = $name;
if (!preg_match("/\./", $name)) {
$_name .= '.Xlsx';
}
$worksheets = $spreadsheet->getAllSheets();
foreach ($worksheets as $worksheet) {
$this->_setWorksheetStyle($worksheet);
}
// 保存前
$this->trigger('beforeSave', [$spreadsheet]);
// 浏览器下载
if ($save === 'php://output') {
Tool::obEndClean();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Cache-Control: max-age=0');
header('Content-Disposition:inline;filename="' . $_name . '"');
$writer->save($save);
} else {
Tool::mkdir($save);
// 保存到本地目录
$writer->save(join(DIRECTORY_SEPARATOR, [
$save, $_name,
]));
}
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
// 保存后
$this->trigger('AfterSave');
}
/**
* 保存前
*
* @param callable $cb
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public function onBeforeSave(callable $cb): self
{
return $this->event('beforeSave', $cb);
}
/**
* 保存后
*
* @param callable $cb
* @date 2022-04-11
* @example
* @author arw
* @since 1.0.0
*/
public function onAfterSave(callable $cb): self
{
return $this->event('afterSave', $cb);
}
/**
* 判断是否空行
*
* @param array $rows
* @return boolean
* @date 2021-11-27
* @example
* @author arw
* @since 1.0.0
*/
public static function isNullRow(array $rows)
{
$rows = array_unique($rows);
$rows = array_filter($rows, function ($v) {
return !is_null($v);
});
return count($rows) == 0;
}
/**
* 快捷设置excel样式
* 首行冻结 自动添加边框
*
* @param Worksheet $worksheet
* @return Worksheet $worksheet
* @date 2021-01-25
* @author arw
* @since 1.0.0
*/
private function _setWorksheetStyle(Worksheet $worksheet): Worksheet
{
$options = $this->worksheetStyle;
$autoBorder = Arr::get($options, 'autoBorder', true);
$freezeLine = Arr::get($options, 'freezeLine', 'A2');
$autoBackground = Arr::get($options, 'autoBackground', true);
if ($freezeLine) {
$worksheet->freezePane($freezeLine);
}
if ($autoBorder) {
$worksheet->getStyle(join(':', [
'A1',
$worksheet->getHighestDataColumn() . $worksheet->getHighestDataRow()
]))->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN, //细边框
],
],
]);
}
if($autoBackground){
$style = join(':', [
'A1',
$worksheet->getHighestDataColumn() . "1"
]);
// 列宽
$worksheet -> getDefaultColumnDimension() -> setWidth(25);
// $worksheet -> getColumnDimension('A') -> setWidth(30); //设置A列宽度为30
//行高
// $worksheet -> getRowDimension(1) -> setRowHeight(25);
$worksheet -> getDefaultRowDimension() -> setRowHeight(25);
//样式设置 - 字体
$worksheet -> getStyle($style) -> getFont()
// -> setBold(true)
->setName('微软雅黑')
-> setSize(13);
//样式设置 - 字体颜色
// $worksheet -> getStyle('A1') -> getFont()
// -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); //设置单元格A1的字体颜色
// $font = $worksheet -> getCell('B1') -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);
// if(substr($font,0,1) == "*"){
// }
// $worksheet -> setCellValue('A2', substr($font,0,1) );
//样式设置 - 水平、垂直居中
$styleArray = [
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
],
];
$worksheet -> getStyle($style) -> applyFromArray($styleArray);
//样式设置 - 单元格背景颜色
$worksheet->getStyle($style)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('FFFFFF00');
}
return $worksheet;
}
/**
* 数字转字母
* 1 => A
*
* @param integer $index
* @return string
* @date 2021-01-23
* @author arw
* @since 1.0.0
*/
public static function stringToIndex(int $index)
{
return Coordinate::stringFromColumnIndex($index);
}
/**
* 字母转数字
* A => 1
*
* @param string $string
* @return int
* @date 2021-01-23
* @author arw
* @since 1.0.0
*/
public static function indexToString(string $string)
{
return Coordinate::columnIndexFromString($string);
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace app\common\arw\adjfut\src\Exception;
/**
* 业务异常
*/
class ErrorMsg extends \Exception
{
}

View File

@ -0,0 +1,258 @@
<?php
namespace app\common\app\common\arw\adjfut\src\Model;
use think\model\Collection;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
/**
* 中间表关联
*
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
class Pivot
{
/**
* 中间表
*
* @var string
*/
private $pivotClass = '';
/**
* 主表
*
* @var string
*/
private $masterClass = '';
/**
* 子表
*
* @var string
*/
private $subClass = '';
/**
* 中间表主键
*
* @var string
*/
private $pivotPk = '';
/**
* 主表主键
*
* @var string
*/
private $masterPk = '';
/**
* 子表主键
*
* @var string
*/
private $subPk = '';
/**
* 是否软删除
*
* @var boolean
*/
private $softDelete = false;
/**
* 初始化
*
* @param string $pivotClass 中间表
* @param string $masterClass 主表
* @param string $subClass 子表
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(
string $pivotClass,
string $masterClass,
string $subClass,
bool $softDelete = false
) {
$this->pivotClass = $pivotClass;
$this->masterClass = $masterClass;
$this->subClass = $subClass;
$this->softDelete = $softDelete;
$this->pivotPk = (new $pivotClass)->getPk();
$this->masterPk = (new $masterClass)->getPk();
$this->subPk = (new $subClass)->getPk();
}
/**
* 绑定
*
* @param array|Collection $masters
* @param array|Collection $subs
* @param array $extra 中间表额外数据
* @return void
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
public function bind($masters, $subs, array $extra = []): void
{
$this->_bind($masters, $subs, $extra, false);
}
/**
* 解绑
*
* @param array|Collection $masters
* @param array|Collection $subs
* @return void
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
public function unbind(array $masters, array $subs): void
{
$masters = self::formatModel($masters, $this->masterClass);
$subs = self::formatModel($subs, $this->subClass);
$pivotClass = $this->pivotClass;
$pivotPk = $this->pivotPk;
$masterPk = $this->masterPk;
$subPk = $this->subPk;
foreach ($masters as $master) {
// 查询已绑定的数据
$subPks = $pivotClass::where([
$masterPk => $master->$masterPk
])->column($subPk);
foreach ($subs as $sub) {
if (in_array($sub->$subPk, $subPks)) {
$pivotClass::where([
$subPk => $sub->$subPk,
$masterPk => $master->$masterPk
])->field([
$pivotPk
])->select()->delete();
}
}
}
}
/**
* 重新绑定
*
* @param array|Collection $masters
* @param array|Collection $subs
* @param array $extra 中间表额外数据
* @return void
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
public function rebind($masters, $subs, array $extra = []): void
{
$this->_bind($masters, $subs, $extra, true);
}
/**
* 格式化模型
*
* @param array|Collection $models
* @param string $class
* @return Collection
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
private static function formatModel($models, string $class): Collection
{
if ($models instanceof Collection) {
return $models;
} else if (is_array($models)) {
$Collection = new Collection();
foreach ($models as $model) {
if ($model instanceof $class) {
} else if (is_string($model) || is_numeric($model)) {
$model = $class::find($model);
}
$Collection->push($model);
}
return $Collection;
} else {
throw new ErrorMsg("类型异常", 1);
}
}
/**
* 绑定数据
*
* @param array|Collection $masters
* @param array|Collection $subs
* @param array $extra
* @param boolean $rebind
* @return void
* @date 2022-12-29
* @example
* @author arw
* @since 1.0.0
*/
private function _bind($masters, $subs, array $extra = [], bool $rebind = false)
{
$masters = self::formatModel($masters, $this->masterClass);
$subs = self::formatModel($subs, $this->subClass);
$pivotClass = $this->pivotClass;
$softDelete = $this->softDelete;
$pivotPk = $this->pivotPk;
$masterPk = $this->masterPk;
$subPk = $this->subPk;
foreach ($masters as $master) {
if ($rebind) {
$pivots = $pivotClass::where([
[$masterPk, '=', $master->$masterPk],
[$subPk, 'not in', $subs->column($subPk)],
])->select();
if (!$pivots->isEmpty()) {
// 重新绑定
$this->unbind([$master], $pivots->column($subPk));
}
}
foreach ($subs as $sub) {
$pivot = null;
if ($softDelete) {
$pivot = $pivotClass::withTrashed()->where([
$subPk => $sub->$subPk,
$masterPk => $master->$masterPk
])->find();
} else {
$pivot = $pivotClass::where([
$subPk => $sub->$subPk,
$masterPk => $master->$masterPk
])->find();
}
if (is_null($pivot) || $pivot->isEmpty()) {
$pivotClass::create($extra + [
$subPk => $sub->$subPk,
$masterPk => $master->$masterPk
]);
} else {
if ($softDelete) {
$pivot->restore();
}
if ($extra) {
$pivotClass::update($extra, [
$pivotPk => $pivot->$pivotPk
]);
}
}
}
}
}
}

View File

@ -0,0 +1,261 @@
<?php
namespace app\common\arw\adjfut\src;
use think\Collection;
use think\db\Query;
use think\facade\Db;
use think\helper\Arr;
/**
* 分割数据表
*
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
class PartitionTable
{
/**
* 源表名
*
* @var string
*/
private $originTable = null;
/**
* 目标表名
*
* @var string
*/
private $targetTable = null;
/**
* 唯一字段
*
* @var string
*/
private $uniqueField = null;
/**
* 查询条件
*
* @var think\Db
*/
private $query = null;
/**
* 是否锁表
*
* @var boolean|string
*/
private $lockTable = false;
/**
* 初始化
*
* @param string $table 原数据表
* @param string $suffix 分割表后缀
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(string $table, string $suffix)
{
$this->originTable = $table;
$this->targetTable = join('_', [$table, $suffix]);
$this->checkTargetTable();
}
/**
* 是否锁表
*
* @param boolean|string $lock
* @return self
* @date 2023-01-04
* @example
* @author arw
* @since 1.0.0
*/
public function setLockTable($lock): self
{
$this->lockTable = $lock;
return $this;
}
/**
* 设置唯一字段(设置后将删除原表数据)
*
* @param string $uniqueField
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function setUniqueField(string $uniqueField): self
{
$this->uniqueField = $uniqueField;
return $this;
}
/**
* 设置查询条件
*
* @param callable $cb
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function setQuery(callable $cb): self
{
$this->query = call_user_func($cb, $this->getOriginDb());
return $this;
}
/**
* 跨月查询
*
* @param string $table
* @param string $field 字段
* @param array $option
* @param array $option[start] 开始时间 Y-m-d H:i:s
* @param array $option[end] 结束时间 Y-m-d H:i:s
* @date 2022-04-06
* @example
* @author arw
* @since 1.0.0
*/
public static function queryForMonth(string $table, string $field, array $option): Query
{
$start = Arr::get($option, 'start');
$end = Arr::get($option, 'end');
if (!$start || !$end) {
throw new \Exception("缺少时间限制", 1);
}
$result = Db::query("SHOW TABLES LIKE '${table}_%'");
$tables = [];
foreach ($result as $value) {
$tables = array_merge($tables, array_values($value));
}
$startYm = date('Ym', strtotime($start));
$endYm = date('Ym', strtotime($end));
$months = [];
$months[] = $startYm;
if ($startYm != $endYm) {
$current = $startYm;
while (true) {
$current = date('Ym', strtotime($current . "01 +1month"));
$months[] = $current;
if ($current === $endYm) {
break;
}
}
// $months[] = $endYm;
}
$temp = array_filter($tables, function ($v) use ($table, $months) {
return in_array(explode("${table}_", $v)[1], $months);
});
$query = Db::table(in_array(date('Ym'), $months) ? $table : array_shift($temp));
$query->union(array_map(function ($v) {
return Db::name($v)->fetchSql()->select();
}, $temp));
return Db::table(join(' ', [
$query->buildSql(),
$table
]))->whereBetweenTime($field, $start, $end);
}
/**
* 迁移数据
*
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public function save()
{
Db::startTrans();
try {
$lockTable = $this->lockTable;
if ($lockTable) {
$this->getOriginDb()->lock($lockTable)->select();
$this->getTargetDb()->lock($lockTable)->select();
}
$target = $this->targetTable;
$originSql = $this->query->fetchSql()->select();
$result = Db::execute("REPLACE INTO `$target` $originSql;");
if ($result === false) {
throw new \Exception("迁移数据失败", 1);
}
$uniqueField = $this->uniqueField;
if ($uniqueField) {
$this->getTargetDb()->field([
$uniqueField
])->chunk(500, function (Collection $ids) use ($uniqueField) {
$this->getOriginDb()->whereIn(
$uniqueField,
$ids->column($uniqueField)
)->delete();
}, $uniqueField);
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 获取原始表
*
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
private function getOriginDb(): Query
{
return Db::name($this->originTable);
}
/**
* 获取目标表
*
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
private function getTargetDb(): Query
{
return Db::name($this->targetTable);
}
/**
* 检查目标表
*
* @return void
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
private function checkTargetTable(): void
{
if (!in_array($this->targetTable, Db::getTables())) {
$origin = $this->originTable;
$target = $this->targetTable;
$result = Db::execute("CREATE TABLE `$target` LIKE `$origin`");
if ($result === false) {
throw new \Exception("创建迁移表失败", 1);
}
}
}
}

View File

@ -0,0 +1,250 @@
<?php
namespace app\common\arw\adjfut\src;
use app\common\arw\adjfut\src\Traits\Event;
class ReTry
{
use Event;
/**
* 执行器
*
* @var callable
*/
private $handle = null;
/**
* 是否执行成功
*
* @var boolean
*/
private $success = false;
/**
* 执行上限
*
* @var integer
*/
private $limit = 3;
/**
* 上次执行原始上限
*
* @var integer
*/
private $last_limit = 3;
/**
* 执行次数
*
* @var integer
*/
private $count = 1;
/**
* 包装器
*
* @param callable $handle
* @param integer $limit
* @return self
* @date 2022-12-30
* @example
* @author arw
* @since 1.0.0
*/
public static function wrapper(callable $handle, int $limit = 3): self
{
$ins = new self;
$ins->setHandle($handle);
$ins->setLimit($limit);
return $ins;
}
/**
* 自增
*
* @param integer $step 步长
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function inc(int $step = 1): self
{
$this->limit += $step;
return $this;
}
/**
* 自减
*
* @param integer $step
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function dec(int $step = 1): self
{
$this->limit -= $step;
return $this;
}
/**
* 获取执行次数
*
* @return integer
* @date 2022-12-30
* @example
* @author arw
* @since 1.0.0
*/
public function getCount(): int
{
return $this->count;
}
/**
* 获取执行上限
*
* @return integer
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function getLimit(): int
{
return $this->limit;
}
/**
* 设置执行上限
*
* @param integer $limit
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function setLimit(int $limit): self
{
$this->limit = $limit;
$this->last_limit = $limit;
return $this;
}
/**
* 设置执行器
*
* @param callable $cb
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function setHandle(callable $cb): self
{
$this->handle = $cb;
return $this;
}
/**
* 执行是否成功
*
* @param boolean $success
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function success(bool $success): self
{
$this->success = $success;
return $this;
}
/**
* 重试上限
*
* @param callable $cb
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
public function onRetryMax(callable $cb): self
{
$this->event('reTryMax', $cb);
return $this;
}
/**
* 执行异常
*
* @param callable $cb
* @return self
* @date 2022-12-30
* @example
* @author arw
* @since 1.0.0
*/
public function onError(callable $cb): self
{
$this->event('error', $cb);
return $this;
}
/**
* 执行
*
* @param mixed ...$args
* @return void
* @date 2022-12-30
* @example
* @author arw
* @since 1.0.0
*/
public function run(...$args)
{
$this->reset();
array_unshift($args, $this);
$handle = $this->handle;
$res = null;
while (true) {
try {
$res = call_user_func_array($handle, $args);
} catch (\Throwable $th) {
$this->trigger('error', [$th]);
}
$this->count++;
// 执行成功
if ($this->success) {
break;
}
// 执行上限
if ($this->count > $this->limit) {
// 不减1会导致 获取到的执行次数多1
$this->count--;
$this->trigger('reTryMax');
break;
}
}
return $res;
}
/**
* 重置执行器
*
* @return void
* @date 2022-12-30
* @example
* @author arw
* @since 1.0.0
*/
private function reset(): void
{
$this->count = 1;
$this->setLimit($this->last_limit);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace app\common\arw\adjfut\src\Service;
use think\helper\Arr;
use think\Model;
use think\Service;
use think\Validate as ThinkValidate;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
class Validate extends Service
{
/**
* 启动方法
* 在所有的系统服务注册完成之后调用,用于定义启动某个系统服务之前需要做的操作
*
* @return void
* @date 2022-07-25
* @example
* @author arw
* @since 1.0.0
*/
public function boot(): void
{
ThinkValidate::maker(function (ThinkValidate $validate) {
/**
* 验证数据
* 验证规则
* 全部数据(数组)
* 字段名
* 字段描述
*/
$validate->extend('string', function ($value) {
return is_string($value);
}, ':attribute 数据类型非法 不是字符串');
$validate->extend('json', function ($value) {
try {
$value = json_decode($value, true);
return is_array($value);
} catch (\Throwable $th) {
// throw $th;
}
return false;
}, ':attribute 数据格式错误 不是json字符串');
$validate->extend('modelHas', function ($value, $rule) {
$rules = explode(',', $rule);
$class = Arr::get($rules, 0);
$pk = Arr::get($rules, 1);
if (!class_exists($class)) {
throw new ErrorMsg("$class 类不存在");
}
/**
* @var \think\Model
*/
$model = new $class;
if (!($model instanceof Model)) {
throw new ErrorMsg("$class 该类未继承 \\think\\Model");
}
if (!$pk) {
$pk = $model->getPk();
}
return (bool) $model->where($pk, $value)->value($pk);
}, ':attribute 数据不存在');
});
}
}

View File

@ -0,0 +1,196 @@
<?php
namespace app\common\arw\adjfut\src;
use DateTime;
use think\facade\Log;
use think\helper\Arr;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
class Tool
{
/**
* 生成配置项
*
* @param array $config
* @param callable|string $cb 自定义合并策略
* @return callable
* @date 2022-07-29
* @example
* @author arw
* @since 1.0.0
*/
public static function generateConfig(array $config, $cb = 'array_merge'): callable
{
return function (...$argv) use ($cb, $config) {
array_unshift($argv, $config);
return call_user_func_array($cb, $argv);
};
}
/**
* 构建url
*
* @param string $url
* @param array $query
* @param boolean $decode
* @return string
* @date 2023-01-04
* @example
* @author arw
* @since 1.0.0
*/
public static function buildUrl(string $url, array $query = [], bool $decode = true): string
{
$parse = parse_url($url);
$parse_query = Arr::get($parse, 'query', '');
$_query = [];
parse_str($parse_query, $_query);
if (!is_array($_query)) {
$_query = [];
}
$_query = array_merge($_query, $query);
$_url = str_replace('?' . $parse_query, '', $url);
$_url .= '?';
$_url .= http_build_query($_query);
if ($decode) {
$_url = urldecode($_url);
}
return $_url;
}
/**
* 写入通道日志
*
* @param string $channel
* @param string $type
* @param array $data
* @return void
* @date 2022-07-29
* @example
* @author arw
* @since 1.0.0
*/
public static function writeChannelLog(string $channel, string $type, array $data): void
{
Log::channel($channel)->$type(json_encode($data, JSON_UNESCAPED_UNICODE));
}
/**
* 字段映射
*
* @param array $data
* @param array $map
* @param array $options
* @return array
* @date 2022-06-23
* @example
* @author arw
* @since 1.0.0
*/
public static function fieldMap(array $data, array $map, array $options = []): array
{
$prefix = Arr::get($options, 'prefix', '');
$result = [];
foreach ($data as $key => $value) {
if (isset($map[$key])) {
$key = $map[$key];
}
if ($prefix) {
$key = $prefix . $key;
}
$result[$key] = $value;
}
return $result;
}
/**
* 转换日期格式
*
* @param string $datetime
* @param string $format
* @return string
* @date 2022-04-20
* @example
* @author arw
* @since 1.0.0
*/
public static function conversionDateTime($datetime, string $format): string
{
try {
$date = new DateTime($datetime);
return $date->format($format);
} catch (\Throwable $th) {
throw new ErrorMsg('日期格式非法', 1);
}
}
/**
* 生成唯一id
*
* @param string|array $value
* @return string
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public static function generateGuid($value = ''): string
{
$str = '';
if (is_array($value)) {
$str = json_encode($value);
}
$charid = strtolower(md5($str . uniqid('', true)));
$hyphen = chr(45); // '-'
$uuid = //chr(123)// '{'
substr($charid, 0, 8) . $hyphen
. substr($charid, 8, 4) . $hyphen
. substr($charid, 12, 4) . $hyphen
. substr($charid, 16, 4) . $hyphen
. substr($charid, 20, 12);
//.chr(125);// '}'
return $uuid;
}
/**
* 清空(擦除)缓冲区并关闭输出缓冲
*
* @return void
*/
public static function obEndClean(): void
{
try {
while (ob_get_level() > 0) {
ob_end_clean();
}
} catch (\Throwable $th) {
//throw $th;
}
}
/**
* 自动创建文件夹
*
* @param string $path 文件夹路径
* @param integer $mode 权限
* @return boolean
*/
public static function mkdir(string $path, int $mode = 0755): bool
{
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
$temp = explode(DIRECTORY_SEPARATOR, $path);
if (preg_match("/\./", end($temp))) {
// pathinfo 不支持中文
// $path = pathinfo($path, PATHINFO_DIRNAME);
array_splice($temp, count($temp) - 1, 1);
$path = join(DIRECTORY_SEPARATOR, $temp);
}
if (!file_exists($path)) {
mkdir($path, $mode, true);
}
chmod($path, $mode);
return true;
}
}

View File

@ -0,0 +1,223 @@
<?php
declare(strict_types=1);
namespace app\common\arw\adjfut\src\Traits;
use think\exception\ValidateException;
use think\helper\Arr;
use think\Model;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
/**
* 字典类
* @mixin Model
*/
trait Dictionary
{
// /**
// * 字典映射
// *
// * @var array
// * @date 2023-01-10
// * @example
// * @author arw
// * @since 1.0.0
// */
// public static $dictionaryMap = [];
/**
* 字典缓存
*
* @var array
* @date 2023-01-10
* @example
* @author arw
* @since 1.0.0
*/
private static $dictionaryCache = [];
/**
* 获取字典模型映射
*
* @param string $guid
* @return array
* @date 2023-01-10
* @example
* @author arw
* @since 1.0.0
*/
private static function getDictionaryModelMap(string $guid): array
{
return [];
}
/**
* 验证字典
*
* @param bool $ignoreNull 忽略null值
* @param array $ignoreField 忽略字段验证
* @return void
* @throws ValidateException
* @date 2023-01-11
* @example
* @author arw
* @since 1.0.0
*/
public function validateDictionary(bool $ignoreNull = false, array $ignoreField = []): void
{
$map = self::getDictionaryMap();
$errors = [];
foreach ($map as $field => &$dictionary) {
list($field, $title) = self::getDictionaryField($field);
if (in_array($field, $ignoreField)) {
continue;
}
$value = $this->$field;
if (is_null($value) && $ignoreNull) {
continue;
}
$dictionary = self::getDictionaryFieldMap($field);
$keys = array_keys($dictionary);
if (!in_array($value, $keys)) {
$values = array_values($dictionary);
$msg = join(',', $values);
$errors[] = "$title 应在[ $msg ]";
}
unset($dictionary);
}
if ($errors) {
throw new ValidateException($errors);
}
}
/**
* 获取字典值
*
* @param string $field 字段
* @param mixed $value 设置器内调用无法会无法获取当前值 需要手动传入
* @return mixed
* @date 2023-01-11
* @example
* @author arw
* @since 1.0.0
*/
public function getDictionaryValue(string $field, $value = null)
{
$map = self::getDictionaryFieldMap($field);
if (is_null($value)) {
$value = $this->$field;
}
if (isset($map[$value])) {
return $value;
}
$key = array_search($value, $map);
if ($key === false) {
$msg = join(',', array_values($map));
throw new ErrorMsg("$field 应在[ $msg ]", 1);
}
return $key;
}
/**
* 获取字典名称
*
* @param string $field 字段
* @param mixed $default 默认值 如果默认值在字典值内 则返回字典名称
* @return mixed
* @date 2023-01-11
* @example
* @author arw
* @since 1.0.0
*/
public function getDictionaryName(string $field, $default = null)
{
$map = self::getDictionaryFieldMap($field);
if (isset($map[$default])) {
$default = $map[$default];
}
return Arr::get($map, $this->$field, $default);
}
/**
* 获取字典名称(弃用保留兼容)
* 请调用 getDictionaryName
*
* @param string $field 字段
* @param mixed $default 默认值
* @deprecated 1.0.0
* @return mixed
* @date 2023-01-10
* @example
* @author arw
* @since 1.0.0
*/
public function getDictionaryText(string $field, $default = null)
{
return $this->getDictionaryName($field, $default);
}
/**
* 获取字典字段映射
*
* @param string $field 字段
* @return array
* @date 2023-01-10
* @example
* @author arw
* @since 1.0.0
*/
public static function getDictionaryFieldMap(string $field): array
{
list($field) = self::getDictionaryField($field);
$cache = Arr::get(self::$dictionaryCache, $field);
if (is_array($cache)) {
return $cache;
}
$map = Arr::get(self::getDictionaryMap(), $field);
if (!$map) {
throw new ErrorMsg("未定义字段映射", 1);
}
if (is_array($map)) {
self::$dictionaryCache[$field] = $map;
return self::$dictionaryCache[$field];
}
if (is_string($map)) {
self::$dictionaryCache[$field] = self::getDictionaryModelMap($map);
return self::$dictionaryCache[$field];
}
return [];
}
/**
* 获取字典映射
*
* @return array
* @date 2023-01-10
* @example
* @author arw
* @since 1.0.0
*/
public static function getDictionaryMap(): array
{
$self = new self;
$exists = property_exists($self, 'dictionaryMap') && isset($self::$dictionaryMap);
return $exists ? $self::$dictionaryMap : [];
}
/**
* 获取字典字段
*
* @param string $value
* @return array
* @date 2023-01-13
* @example
* @author arw
* @since 1.0.0
*/
private static function getDictionaryField(string $value): array
{
$temp = explode('|', $value);
$field = Arr::get($temp, '0');
$title = Arr::get($temp, '1', $field);
return [$field, $title];
}
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace app\common\arw\adjfut\src\Traits;
/**
* 事件类
*/
trait Event
{
/**
* 事件
*
* @var array
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
private $event = [];
/**
* 事件
*
* @param string $type
* @param callable $cb
* @return self
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
private function event(string $type, callable $cb): self
{
$this->event[strtoupper($type)] = $cb;
return $this;
}
/**
* 事件触发器
*
* @param string $type
* @param array $args
* @param boolean $withThis
* @return mixed
* @date 2022-12-28
* @example
* @author arw
* @since 1.0.0
*/
private function trigger(string $type, array $args = [], bool $withThis = true)
{
$type = strtoupper($type);
if (isset($this->event[$type])) {
if ($withThis) {
array_unshift($args, $this);
}
return call_user_func_array($this->event[$type], $args);
}
}
}

View File

@ -0,0 +1,240 @@
<?php
namespace app\common\arw\adjfut\src;
class Traverse
{
// 当前id key名称
private $current = 'id';
// 父id key名称
private $parent = 'pid';
// 子集名称
private $children = 'children';
// 数据
private $data = [];
// 格式化函数
private $format = null;
// 是否递归循环
private $loop = false;
public function __construct(string $current, string $parent, string $children = 'children')
{
$this->current = $current;
$this->parent = $parent;
$this->children = $children;
}
/**
* 获取子集
* @access public
* @param array $data 要遍历的数组
* @param integer|string $id 要查找哪个id下的子集
* @param bool $loop 是否循环遍历
* @return array
*/
public function next(array $data = [], $id = 0, bool $loop = false): array
{
$this->setData($data);
$this->setLoop($loop);
return $this->generateNext($id);
}
/**
* 获取父集
* @access public
* @param array $data 要遍历的数组
* @param integer|string $id 要查找哪个id下的父集
* @param bool $loop 是否循环遍历
* @return array
*/
public function prev(array $data = [], $id = 0, bool $loop = false): array
{
$this->setData($data);
$this->setLoop($loop);
return $this->generatePrev($id);
}
/**
* 获取无限层级树状图
*
* @param array $data 要遍历的数组
* @param integer|string $parent_id 起点id
* @param callable $format 自定义树数据
* @return array
* @date 2020-04-09
* @example
* @author arw
* @since 1.0.0
*/
public function tree(array $data, $parent_id, callable $format): array
{
$this->setData($data);
$this->setFormat($format);
return $this->generateTree($parent_id);
}
/**
* 获取全部子集数据
*
* @param integer|string $id
* @return array
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function getCurrentData($id): array
{
$data = array_filter($this->data, function ($v) use ($id) {
return $v[$this->current] == $id;
});
$data = array_values($data);
return $data;
}
/**
* 获取全部父集数据
*
* @param integer|string $id
* @return array
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function getParentData($id): array
{
$data = array_filter($this->data, function ($v) use ($id) {
return $v[$this->parent] == $id;
});
$data = array_values($data);
return $data;
}
/**
* 设置循环
*
* @param boolean $loop
* @return self
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function setLoop(bool $loop): self
{
$this->loop = $loop;
return $this;
}
/**
* 设置数据
*
* @param array $data
* @return self
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function setData(array $data): self
{
$this->data = $data;
return $this;
}
/**
* 设置格式化函数
*
* @param callable $cb
* @return self
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function setFormat(callable $cb): self
{
$this->format = $cb;
return $this;
}
/**
* 生成树
*
* @param integer|string $parent_id
* @return array
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function generateTree($parent_id): array
{
$data = $this->getParentData($parent_id);
foreach ($data as &$value) {
$children = $this->generateTree($value[$this->current]);
$value[$this->children] = $children;
if (is_callable($this->format)) {
$formatReturn = call_user_func($this->format, $value);
if (!isset($formatReturn[$this->children])) {
$formatReturn[$this->children] = $children;
}
if ($formatReturn) {
$value = $formatReturn;
}
}
unset($value);
}
return $data;
}
/**
* 获取子集
*
* @param integer|string $id
* @return array
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function generateNext($id): array
{
$re = [];
$data = $this->getParentData($id);
foreach ($data as $value) {
$current = $value[$this->current];
$re[] = $current;
if ($this->loop) {
$re = array_merge($re, $this->generateNext($current));
}
}
return array_unique($re);
}
/**
* 获取父集
*
* @param integer|string $id
* @return array
* @date 2022-08-08
* @example
* @author arw
* @since 1.0.0
*/
public function generatePrev($id): array
{
$re = [];
$data = $this->getCurrentData($id);
foreach ($data as $value) {
$parent = $value[$this->parent];
$re[] = $parent;
if ($this->loop) {
$re = array_merge($re, $this->generatePrev($parent));
}
}
return array_unique($re);
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace app\common\arw\adjfut\src\Unit;
use think\helper\Arr;
use think\helper\Str;
/**
* @method static string b2kb(string $number,?int $scale)
* @method static string b2mb(string $number,?int $scale)
* @method static string b2gb(string $number,?int $scale)
* @method static string b2tb(string $number,?int $scale)
*
* @method static string kb2b(string $number,?int $scale)
* @method static string kb2mb(string $number,?int $scale)
* @method static string kb2gb(string $number,?int $scale)
* @method static string kb2tb(string $number,?int $scale)
*
* @method static string mb2b(string $number,?int $scale)
* @method static string mb2kb(string $number,?int $scale)
* @method static string mb2gb(string $number,?int $scale)
* @method static string mb2tb(string $number,?int $scale)
*
* @method static string gb2b(string $number,?int $scale)
* @method static string gb2mb(string $number,?int $scale)
* @method static string gb2kb(string $number,?int $scale)
* @method static string gb2tb(string $number,?int $scale)
*
* @method static string tb2b(string $number,?int $scale)
* @method static string tb2mb(string $number,?int $scale)
* @method static string tb2kb(string $number,?int $scale)
* @method static string tb2gb(string $number,?int $scale)
*/
class File
{
/**
* 间隔
*/
const INTERVAL = 1024;
/**
* 顺序
*/
const ORDER = ['b', 'kb', 'mb', 'gb', 'tb'];
/**
* 获取字典单位
*
* @param string $size
* @return string
* @date 2022-07-23
* @example
* @author arw
* @since 1.0.0
*/
public static function getByteUnit(string $size): string
{
for ($i = 0; $size >= self::INTERVAL && $i < 4; $i++) {
$size = bcdiv($size, self::INTERVAL);
}
return round($size, 2) . self::ORDER[$i];
}
public static function __callStatic($name, $arguments)
{
$number = Arr::get($arguments, 0, 0);
$scale = Arr::get($arguments, 1, 0);
$name = Str::lower($name);
$a = null;
$b = null;
if (Str::contains($name, 2)) {
list($a, $b) = explode('2', $name);
} else {
list($a, $b) = explode('to', $name);
}
$a_index = array_search($a, self::ORDER);
$b_index = array_search($b, self::ORDER);
$type = $a_index < $b_index;
for ($i = 0; $i < abs(bcsub($a_index, $b_index, 0)); $i++) {
if ($type) {
$number = bcdiv($number, self::INTERVAL, $scale);
} else {
$number = bcmul($number, self::INTERVAL, $scale);
}
}
return $number;
}
}

View File

@ -0,0 +1,289 @@
<?php
namespace app\common\arw\adjfut\src;
use think\exception\ValidateException;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use think\facade\Filesystem;
use think\facade\Request;
use think\file\UploadedFile;
use think\filesystem\Driver;
use think\helper\Arr;
class UploadFile
{
/**
* 磁盘
*
* @var string
*/
private $diskName = '';
/**
* 文件
*
* @var UploadedFile
*/
private $file = null;
/**
* 文件路径
*
* @var string
*/
private $path = '';
/**
* 初始化
*
* @param string $diskName 磁盘
* @param string $validate 验证
* @param UploadedFile|string|array $file 文件
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(
string $diskName,
string $validate = '',
$file = 'file'
) {
if (is_string($file)) {
$file = Request::file($file);
}
if (is_array($file)) {
$file = new UploadedFile(...$file);
}
if (!($file instanceof UploadedFile)) {
throw new ErrorMsg("非法文件", 1);
}
if ($validate) {
try {
Validate::check([
'file' => $file
], [
'file|文件' => $validate
]);
} catch (ValidateException $th) {
$this->getDisk()->delete($file->getFilename());
throw $th;
}
}
$this->diskName = $diskName;
$this->file = $file;
}
/**
* 上传base64图片
*
* @param string $diskName 磁盘
* @param string $base64 base64
* @return self
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public static function uploadBase64Image(
string $diskName,
string $base64
): self {
$Base64 = new Base64($base64);
if (!$Base64->isImage()) {
throw new ErrorMsg("非法图片base64", 1);
}
$path = tempnam(sys_get_temp_dir(), 'saveBase64Image');
$Base64->saveImage($path);
$name = md5(md5_file($path) . Tool::generateGuid() . time()) . '.' . $Base64->getFileExt();
return new self($diskName, '', [
$path,
$name
]);
}
/**
* 获取文件
*
* @return UploadedFile
* @date 2022-07-05
* @example
* @author arw
* @since 1.0.0
*/
public function getFile(): UploadedFile
{
return $this->file;
}
/**
* 获取磁盘名称
*
* @return string
* @date 2022-07-05
* @example
* @author arw
* @since 1.0.0
*/
public function getDiskName(): string
{
return $this->diskName;
}
/**
* 获取配置项
*
* @param string $key
* @param mixed $default
* @return mixed
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
public function getConfig(string $key, $default = null)
{
return Arr::get(
Filesystem::getDiskConfig($this->diskName),
$key,
$default
);
}
/**
* 获取文件相对路径
*
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
public function getPath(): string
{
if (!$this->path) {
throw new ErrorMsg("请保存文件后使用", 1);
}
return $this->path;
}
/**
* 获取文件真实路径
*
* @return string
* @date 2022-07-19
* @example
* @author arw
* @since 1.0.0
*/
public function getRealPath(): string
{
$path = $this->getPath();
if ($this->getConfig('type') != 'local') {
throw new ErrorMsg("非本地文件类型 无法获取真实路径", 1);
}
return str_replace(['\\', '/'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $this->getConfig('root') . DIRECTORY_SEPARATOR . $path);
}
/**
* 获取文件绝对路径
*
* @date 2022-04-15
* @deprecated 1.0.0
* @example
* @author arw
* @since 1.0.0
*/
public function getFullPath(): string
{
return $this->getRealPath();
}
/**
* 保存文件
* @param string $path 路径
* @param null|string|\Closure $rule 文件名规则
* @param array $options 参数
* @return string
*/
public function putFile(
string $path,
$rule = null,
array $options = []
): string {
$this->path = $this->getDisk()->putFile(
$path,
$this->file,
$rule,
$options
);
if (!$this->path) {
throw new ErrorMsg("文件保存失败", 1);
}
return $this->path;
}
/**
* 指定文件名保存文件
* @param string $path 路径
* @param string $name 文件名
* @param array $options 参数
* @return string
*/
public function putFileAs(
string $path,
string $name = '',
array $options = []
): string {
$this->path = $this->getDisk()->putFileAs(
$path,
$this->file,
$name,
$options
);
if (!$this->path) {
throw new ErrorMsg("文件保存失败", 1);
}
return $this->path;
}
/**
* 删除文件
*
* @return void
* @date 2022-08-22
* @example
* @author arw
* @since 1.0.0
*/
public function delete(): void
{
$this->getDisk()->delete($this->file->getFilename());
}
/**
* 移除文件
*
* @date 2022-04-15
* @deprecated 1.0.0
* @example
* @author arw
* @since 1.0.0
*/
public function unlink(): void
{
$this->delete();
}
/**
* 获取存储配置
*
* @return Driver
* @date 2022-04-15
* @example
* @author arw
* @since 1.0.0
*/
public function getDisk(): Driver
{
return Filesystem::disk($this->diskName);
}
}

View File

@ -0,0 +1,254 @@
<?php
namespace app\common\arw\adjfut\src;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use think\facade\Request;
use think\helper\Arr;
use think\Validate as ThinkValidate;
class Validate
{
private $data = [];
/**
* 设置参数s
*
* @param array $data
* @return self
*/
public function setData(array $data): self
{
$this->data = $data;
return $this;
}
/**
* 当条件满足时 验证
*
* @param array $when
* @param array $validate
* @param array $message
* @param bool $batch
* @return void
*/
public function when(array $when, array $validate, array $message = [], bool $batch = true): void
{
$data = $this->data;
$whenCount = 0;
foreach ($when as $field => $value) {
if (!isset($data[$field])) {
continue;
}
if ($data[$field] == $value) {
$whenCount++;
}
}
$_validate = $whenCount == count($when);
if ($_validate) {
self::check($data, $validate, $message, $batch);
}
}
/**
* 验证是否唯一
*
* @param string $model 模型类
* @param string|int $id 排除主键id
* @param array $rules[field] => name,validateField,pk|其他规则
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return void
* @throws ValidateException
*/
public static function unique(
string $model,
$id,
array $data,
array $rules = [],
array $message = [],
bool $batch = true
): void {
$_rules = [];
foreach ($rules as $field => $validate) {
$validates = explode('|', $validate);
$_rule = explode(',', $validates[0]);
$name = Arr::get($_rule, 0);
$validateField = Arr::get($_rule, 1);
$pk = Arr::get($_rule, 2);
$validates[0] = self::createUniqueRule($model, [
'field' => $validateField ?: $field,
'id' => $id,
'pk' => $pk,
]);
$_rules[$field . ($name ? "|$name" : '')] = $validates;
}
self::check($data, $_rules, $message, $batch);
}
/**
* 创建数据表唯一验证
*
* @param string $class 模型类
* @param array $options 配置项
* @param string $options[field] 字段名
* @param string $options[id] 排除ID
* @param string $options[pk] 主键名
* @return string
* @date 2022-04-16
* @example
* @author arw
* @since 1.0.0
*/
public static function createUniqueRule(string $class, array $options = []): string
{
$field = Arr::get($options, 'field');
$id = Arr::get($options, 'id');
$pk = Arr::get($options, 'pk');
if (!$field) {
throw new ErrorMsg("字段名不能为空", 1);
}
return 'unique:' . join(',', [
$class,
$field,
$id,
$pk
]);
}
/**
* 类场景验证
*
* @param string $validate
* @param string $scene
* @param boolean $batch
* @return void
* @throws ValidateException
*/
public static function classScene(array $validate, array $data, bool $batch = true): void
{
list($class, $scene) = $validate;
/**
* @var ThinkValidate
*/
$validate = new $class;
$validate->scene($scene)->batch($batch);
$validate->failException(true)->check($data);
}
/**
* 类场景验证 param参数
*
* @param array $validate
* @param boolean $batch
* @return array
* @throws ValidateException
*/
public static function classSceneParam(array $validate, bool $batch = true): array
{
$data = Request::param();
self::classScene($validate, $data, $batch);
return $data;
}
/**
* 类场景验证 get参数
*
* @param array $validate
* @param boolean $batch
* @return array
* @throws ValidateException
*/
public static function classSceneGet(array $validate, bool $batch = true): array
{
$data = Request::get();
self::classScene($validate, $data, $batch);
return $data;
}
/**
* 类场景验证 post参数
*
* @param array $validate
* @param boolean $batch
* @return array
* @throws ValidateException
*/
public static function classScenePost(array $validate, bool $batch = true): array
{
$data = Request::post();
self::classScene($validate, $data, $batch);
return $data;
}
/**
* param参数验证
*
* @param array $validate 验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array
* @throws ValidateException
*/
public static function param(array $validate, array $message = [], bool $batch = true): array
{
$data = Request::param();
self::check($data, $validate, $message, $batch);
return $data;
}
/**
* get参数验证
*
* @param array $validate 验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array
* @throws ValidateException
*/
public static function get(array $validate, array $message = [], bool $batch = true): array
{
$data = Request::get();
self::check($data, $validate, $message, $batch);
return $data;
}
/**
* post参数验证
*
* @param array $validate 验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array
* @throws ValidateException
*/
public static function post(array $validate, array $message = [], bool $batch = true): array
{
$data = Request::post();
self::check($data, $validate, $message, $batch);
return $data;
}
/**
* 验证数据
*
* @param array $data 数据
* @param array $validate 验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return void
* @throws ValidateException
*/
public static function check(array $data, array $validate, array $message = [], bool $batch = true): void
{
$v = new ThinkValidate();
$v->rule($validate);
$v->message($message);
$v->batch($batch);
$v->failException(true)->check($data);
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace app\common\arw\adjfut\src\WeChat;
use think\facade\Config as FacadeConfig;
use think\helper\Arr;
class Config
{
/**
* 实例
*
* @var $this
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
private static $instance = null;
/**
* 配置项
*
* @var array
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
private $config = [
// 中控层配置
'access_key' => '',
'server_base' => '',
// 小程序配置
'xcx' => [
'appid' => '',
'secret' => '',
'cache_key' => '',
'log_channel' => '',
],
// 公众号配置
'gzh' => [
'appid' => '',
'secret' => '',
// 推送模板
'template' => [
'模板名称' => '模板id'
],
'cache_key' => '',
'log_channel' => '',
],
];
public function __construct()
{
$setConfig = function (string $key) {
$this->setConfig($key, FacadeConfig::get("wechat.$key"));
};
foreach ([
'access_key', 'server_base',
'xcx.cache_key', 'xcx.log_channel',
'xcx.appid', 'xcx.secret',
'gzh.cache_key', 'gzh.log_channel',
'gzh.appid', 'gzh.secret', 'gzh.template'
] as $key) {
$setConfig($key);
}
}
/**
* 实例
*
* @return self
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
public static function instance(): self
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* 设置配置项
*
* @param string $key
* @param mixed $value
* @return void
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
public function setConfig(string $key, $value)
{
Arr::set($this->config, $key, $value);
}
/**
* 获取配置项
*
* @param string $key
* @param mixed $default
* @return mixed
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
public static function get(string $key, $default = null)
{
return Arr::get(
self::instance()->config,
$key,
$default
);
}
}

View File

@ -0,0 +1,348 @@
<?php
namespace app\common\arw\adjfut\src\WeChat;
use app\common\arw\adjfut\src\Curl;
use app\common\arw\adjfut\src\WeChat\GzhCommon;
use think\facade\Config;
use think\facade\Request;
use think\helper\Arr;
use think\Response;
class Gzh extends GzhCommon
{
/**
* 当前实例
*
* @var $this
*/
private static $instance = null;
/**
* 推送缓存 防止重复推送同样的数据
*
* @var array
* @date 2021-10-11
* @example
* @author arw
* @since 1.0.0
*/
private static $TemplateMessageInterfaceCache = [];
/**
* 初始化
*
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
parent::__construct();
}
/**
* 实例
*
* @return self
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public static function instance(): self
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* 获取AccessToken
*
* @param boolean $refresh 是否强制刷新
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
public function GetAccessToken($refresh = false)
{
return $this->_AccessToken($refresh);
}
/**
* 获取JsApiTicket
*
* @param boolean $refresh 是否强制刷新
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
public function GetJsApiTicket($refresh = false)
{
return $this->_JsApiTicket($refresh);
}
/**
* 网页授权/获取用户基本信息
* https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
* @param array $config 配置项 [
* "scope" => "snsapi_base", 应用授权作用域,
* snsapi_base 不弹出授权页面直接跳转只能获取用户openid
* snsapi_userinfo 弹出授权页面可通过openid拿到昵称、性别、所在地。并且 即使在未关注的情况下,只要用户授权,也能获取其信息
* "state" => "", 重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值最多128字节
* "lang" => "zh_CN", 当scope等于snsapi_userinfo才生效
* 返回国家地区语言版本zh_CN 简体zh_TW 繁体en 英语
* ]
* @param callable $success 回调用户授权成功调用函数 ($userinfo,$state)
* @param callable $error 执行错误回调 ($msg)
* @return null
*/
public function authorize(array $config, callable $success, callable $error)
{
try {
$config["scope"] = Arr::get($config, "scope", "snsapi_base");
$config["state"] = Arr::get($config, "state", "");
$config["lang"] = Arr::get($config, "lang", "zh_CN");
$config["redirect_uri"] = Arr::get($config, "redirect_uri", Request::url(true));
$code = Arr::get($_GET, "code");
if (!$code) {
$url = "https://open.weixin.qq.com/connect/oauth2/authorize";
$url .= "?" . http_build_query([
"appid" => $this->appid,
"redirect_uri" => $config["redirect_uri"],
"response_type" => "code",
"scope" => $config["scope"],
"state" => $config["state"],
]);
$url .= "#wechat_redirect";
return Response::create($url, 'redirect', 302);
}
$curl = Curl::instance();
$param = [];
$param["appid"] = $this->appid;
$param["secret"] = $this->secret;
$param["code"] = $code;
$param["grant_type"] = "authorization_code";
$url = "https://api.weixin.qq.com/sns/oauth2/access_token";
$access_token = $curl->setParams($param)->get($url);
$judge = (Arr::get($access_token, "access_token") && Arr::get($access_token, "openid"));
if (!$judge || !$access_token) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
return $error("获取access_token失败");
}
if ($config["scope"] === "snsapi_userinfo") {
$curl = Curl::instance();
$url = "https://api.weixin.qq.com/sns/userinfo";
$param = [];
$param["access_token"] = Arr::get($access_token, "access_token");
$param["openid"] = Arr::get($access_token, "openid");
$param["lang"] = $config["lang"];
$user_info = $curl->setParams($param)->get($url);
$errcode = Arr::get($user_info, "errcode", false);
if ($errcode !== false) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'result' => $curl->getContent(),
'msg' => '获取用户信息失败',
], 'error');
return $error("获取微信用户信息失败");
}
$access_token = array_merge($access_token, $user_info);
}
return $success($access_token, $_GET["state"]);
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
return $error($th->getMessage());
}
}
/**
* 微信模板消息推送
* 微信返回errcode等于40001会更新缓存AccessToken
* 并重新推送一次
* https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5
* @param string $touser 推送给谁
* @param string $template_id 模板名称
* @param array $data 推送数据
* [
* "touser" => "openid",
* "template_id" => "template_id",
* "url" => "url",
* "miniprogram" => [
* "appid" => "appid",
* "pagepath" => "pagepath",
* ],
* "data" => [
* "first" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword1" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword2" => [
* "value" => "value",
* "color" => "color",
* ],
* "keyword3" => [
* "value" => "value",
* "color" => "color",
* ],
* "remark" => [
* "value" => "value",
* "color" => "color",
* ],
* ],
* ]
* @param boolean $refresh 是否刷新【 access_token
* @param integer $limit 调用上限(允许自动重试次数)
* @return bool
* @date 2020-05-26
* @example
* @author arw
* @since 1.1.1
*/
public function TemplateMessageInterface($touser = null, $template_id = null, array $data, $refresh = false, $limit = 3)
{
$this->log([
'action' => __FUNCTION__,
'touser' => $touser,
'template_id' => $template_id,
'data' => $data,
], 'log');
try {
$template = $this->template;
if (!$touser) {
if (isset($data["touser"])) {
$touser = $data["touser"];
}
}
if (!$template_id) {
if (isset($data["template_id"])) {
$template_id = $data["template_id"];
}
}
if (empty($touser)) {
throw new \Exception("data.touser不能为空", 1);
}
if (empty($template_id)) {
throw new \Exception("data.template_id不能为空", 1);
} else {
if (isset($template[$template_id])) {
$template_id = $template[$template_id];
}
}
$data["touser"] = $touser;
$data["template_id"] = $template_id;
$limit--;
if ($limit <= 0) {
throw new \Exception("超过调用上限", 1);
}
$access_token = $this->_AccessToken($refresh);
if (!$access_token) {
return false;
}
$appid = Arr::get($data, "miniprogram.appid");
if ($appid == 'APPID') {
Arr::set($data, 'miniprogram.appid', Config::get('xcx.appid'));
}
$param = json_encode($data);
if (!isset(self::$TemplateMessageInterfaceCache[$param])) {
self::$TemplateMessageInterfaceCache[$param] = [];
}
if (in_array($touser, self::$TemplateMessageInterfaceCache[$param])) {
throw new \Exception("重复推送", 1);
}
$url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
$url .= "?access_token=" . $access_token;
$curl = Curl::instance();
$send = $curl->setData($param)->post($url);
$errcode = Arr::get($send, "errcode", false);
if ($errcode === 0) {
self::$TemplateMessageInterfaceCache[$param][] = $touser;
return true;
}
if ($errcode === 40001) {
return $this->TemplateMessageInterface($touser, $template_id, $data, true, $limit);
}
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getData(),
'content' => $curl->getContent(),
'msg' => '模板消息推送失败',
], 'error');
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* Jssdk授权
* @param string $url 授权url
* @return null
*/
public function JsSdk(string $url = "")
{
$jsapiTicket = $this->_JsApiTicket();
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
if (!$url) {
$url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
}
$timestamp = time();
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$nonceStr = "";
for ($i = 0; $i < 16; $i++) {
$nonceStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
$signature = sha1($string);
$signPackage = [
"appId" => $this->appid,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string,
];
return $signPackage;
}
}

View File

@ -0,0 +1,459 @@
<?php
namespace app\common\arw\adjfut\src\WeChat;
use think\facade\Cache;
use app\common\arw\adjfut\src\Curl;
use think\facade\Log;
use think\helper\Arr;
class GzhCommon
{
/**
* 微信公众号 appid
*
* @var string
*/
protected $appid = '';
/**
* 微信公众号 secret
*
* @var string
*/
protected $secret = '';
/**
* 微信推送模板
*
* @var array
*/
protected $template = [];
/**
* 缓存key
*
* @var string
*/
protected $cache_key = '';
/**
* 日志通道
*
* @var string
*/
protected $log_channel = '';
/**
* 异常
*
* @var string
*/
protected $error = '';
/**
* 中控层服务器 access_key
*
* @var string
*/
private $access_key = '';
/**
* 中控层服务器 server_base
*
* @var string
*/
private $server_base = '';
/**
* access_token 缓存
*
* @var string
*/
private $access_token = '';
/**
* 初始化
*
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
$this->access_key = Config::get('access_key');
$this->server_base = Config::get('server_base');
$this->appid = Config::get('gzh.appid');
$this->secret = Config::get('gzh.secret');
$this->template = Config::get('gzh.template');
$this->cache_key = Config::get('gzh.cache_key');
$this->log_channel = Config::get('gzh.log_channel');
}
/**
* 获取access_token
* 如果不刷新并且有缓存【 access_token 】的话
* 直接返回缓存的【 access_token
* 否则将看情况请求【 access_token
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.2
*/
protected function _AccessToken($refresh = false)
{
$cache_key = $this->getAccessTokenCacheKey();
if ($refresh && $cache_key) {
Cache::delete($cache_key);
}
if ($refresh === false && $this->access_token) {
return $this->access_token;
}
$access_token = $this->_GetAccessToken($refresh);
if ($access_token) {
$this->access_token = $access_token;
return $access_token;
} else {
$access_token = $this->_GetCacheAccessToken();
if (!$access_token) {
$access_token = $this->_CurlAccessToken();
}
$this->access_token = $access_token;
return $access_token;
}
return false;
}
/**
* 获取jsapiticket
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.0
*/
protected function _JsApiTicket($refresh = false)
{
$cache_key = $this->getJsApiTicketCacheKey();
if ($refresh && $cache_key) {
Cache::delete($cache_key);
}
$ticket = $this->_GetJsApiTicket($refresh);
if ($ticket) {
return $ticket;
} else {
$ticket = $this->_GetCacheJsApiTicket();
if (!$ticket) {
$ticket = $this->_CurlJsApiTicket();
}
return $ticket;
}
return false;
}
/**
* 异常记录
*
* @param array $content
* @param string $level alert | error | warning | notice | info | debug
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
protected function log(array $data, string $level = 'log')
{
$log_channel = $this->log_channel;
if ($log_channel) {
Log::channel($log_channel)->log($level, json_encode($data));
}
}
/**
* 请求【中控层服务器】 获取access_token
* @param boolean $refresh
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 2.0.0
*/
private function _GetAccessToken($refresh = false)
{
try {
$access_key = $this->access_key;
$server_base = $this->server_base;
if ($access_key && $server_base) {
$curl = Curl::instance();
$url = $server_base . "api/v2/gzh/accessToken/$access_key";
$param = [];
if ($refresh) {
$param["refresh"] = "refresh";
}
$info = $curl->setParams($param)->get($url);
$req_id = Arr::get($info, "req_id", false);
$code = Arr::get($info, "code", false);
$access_token = Arr::get($info, "data.access_token", false);
if (!$access_token) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
}
return $access_token;
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 获取本地缓存【access_token】
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function _GetCacheAccessToken()
{
try {
$cache_key = $this->getAccessTokenCacheKey();
if ($cache_key) {
$access_token = Cache::get($cache_key);
if ($access_token) {
return $access_token;
}
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 获取本地缓存【jsapiticket】
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function _GetCacheJsApiTicket()
{
try {
$cache_key = $this->getJsApiTicketCacheKey();
if ($cache_key) {
$ticket = Cache::get($cache_key);
if ($ticket) {
return $ticket;
}
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 请求【微信服务器】 获取access_token
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.0
*/
private function _CurlAccessToken()
{
try {
$cache_key = $this->getAccessTokenCacheKey();
$curl = Curl::instance();
$url = "https://api.weixin.qq.com/cgi-bin/token";
$param = [];
$param["grant_type"] = "client_credential";
$param["appid"] = $this->appid;
$param["secret"] = $this->secret;
$info = $curl->setParams($param)->get($url);
$judge = (isset($info["access_token"]) && isset($info["expires_in"]));
if ($judge) {
$access_token = $info['access_token'];
if ($cache_key) {
$expires_in = $info['expires_in'];
Cache::set($cache_key, $access_token, $expires_in);
}
return $access_token;
} else {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 请求【中控层服务器】 获取jsapiticket
* @param boolean $refresh
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 2.0.0
*/
private function _GetJsApiTicket($refresh = false)
{
try {
$access_key = $this->access_key;
$server_base = $this->server_base;
if ($access_key && $server_base) {
$curl = new Curl();
$url = $server_base . "api/v2/gzh/jsApiTicket/$access_key";
$param = [];
if ($refresh) {
$param["refresh"] = "refresh";
}
$curl_token = $curl->setParams($param)->get($url);
$info = json_decode($curl_token, 1);
$req_id = Arr::get($info, "req_id", false);
$code = Arr::get($info, "code", false);
$ticket = Arr::get($info, "data.ticket", false);
if (!$ticket) {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取jsapiticket失败',
], 'error');
}
return $ticket;
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 请求【微信服务器】 获取jsapiticket
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.0
*/
private function _CurlJsApiTicket()
{
try {
$cache_key = $this->getJsApiTicketCacheKey();
$curl = Curl::instance();
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
$param = [];
$param["access_token"] = $this->_AccessToken();
$param["type"] = "jsapi";
$info = $curl->setParams($param)->get($url);
$judge = (isset($info["ticket"]) && isset($info["expires_in"]));
if ($judge) {
$ticket = $info['ticket'];
if ($cache_key) {
$expires_in = $info['expires_in'];
Cache::set($cache_key, $ticket, $expires_in);
}
return $ticket;
} else {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取jsapiticket失败',
], 'error');
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 获取access_key缓存key
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function getAccessTokenCacheKey()
{
if ($this->cache_key) {
return $this->cache_key . '.access_token';
}
return false;
}
/**
* 获取js api ticket缓存key
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function getJsApiTicketCacheKey()
{
if ($this->cache_key) {
return $this->cache_key . '.js_api_ticket';
}
return false;
}
}

View File

@ -0,0 +1,232 @@
<?php
namespace app\common\arw\adjfut\src\WeChat;
use app\common\arw\adjfut\src\Curl;
use app\common\arw\adjfut\src\WeChat\XcxCommon;
use think\helper\Arr;
class Xcx extends XcxCommon
{
/**
* 当前实例
*
* @var $this
*/
private static $instance = null;
/**
* 初始化
*
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
parent::__construct();
}
/**
* 实例
*
* @return self
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public static function instance(): self
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* 获取AccessToken
*
* @param boolean $refresh 是否强制刷新
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
public function GetAccessToken($refresh = false)
{
return $this->_AccessToken($refresh);
}
/**
* 获取微信小程序用户openid
*
* @param string $code 微信小程序传过来的code
* @return array|bool $res
* @return string $res[session_key]
* @return string $res[expores_in]
* @return string $res[openid]
* @return string $res[unionid]
*/
public function GetOpenId(string $code)
{
try {
$curl = Curl::instance();
$url = 'https://api.weixin.qq.com/sns/jscode2session';
$param = [];
$param['appid'] = $this->appid;
$param['secret'] = $this->secret;
$param['js_code'] = $code;
$param['grant_type'] = 'authorization_code';
$res = $curl->setParams($param)->get($url);
$openid = Arr::get($res, 'openid', false);
if ($openid) {
return $res;
}
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取小程序用户openid失败微信服务器无返回openid',
], 'error');
$this->error = '获取用户openid失败';
return false;
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
$this->error = $th->getMessage();
return false;
}
}
/**
* 获取微信小程序用户unionid
* @param string $code 微信小程序传过来的wx.login 返回的code
* @param string $encryptedData 微信小程序传过来的wx.getUserInfo 返回的encryptedData
* @param string $iv 微信小程序传过来的wx.getUserInfo 返回的iv
*/
public function GetUnionid(string $code, string $encryptedData, string $iv)
{
try {
$result = $this->GetOpenId($code);
if (!$result) {
return false;
}
$openid = Arr::get($result, 'openid');
$session_key = Arr::get($result, 'session_key');
if (!$openid || !$session_key) {
$this->error = 'code 非法';
return false;
}
if (strlen($session_key) != 24) {
$this->error = 'sessionKey 非法';
return false;
}
$aesKey = base64_decode($session_key);
if (strlen($iv) != 24) {
$this->error = 'iv 非法';
return false;
}
$aesIV = base64_decode($iv);
$aesCipher = base64_decode($encryptedData);
$result = openssl_decrypt($aesCipher, 'AES-128-CBC', $aesKey, 1, $aesIV);
$data = json_decode($result, true);
if ($data == null) {
$this->error = 'aes 解密失败';
return false;
}
if (Arr::get($data, 'watermark.appid') != $this->appid) {
$this->error = 'appid不一致';
return false;
}
return $data;
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
}
/**
* 生成微信小程序码
* @param string $type 接口名称 createQRCode/get/getUnlimited
* @param array $data 请求参数
*/
public function QrCode(string $type, array $data)
{
return $this->_QrCode($type, $data);
}
private function _QrCode(string $type, array $data, bool $refresh = false, int $limit = 3)
{
try {
$limit--;
if ($limit <= 0) {
throw new \Exception('超过调用上限', 1);
}
$urls = [
'createQRCode' => 'https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode',
];
$curl = Curl::instance();
$url = $urls[$type];
$url .= '?access_token=' . $this->GetAccessToken($refresh);
$param = json_encode($data);
$res = $curl->setParams($param)->post($url);
$errcode = Arr::get($res, 'errcode', false);
$errmsg = Arr::get($res, 'errmsg', false);
if ($errcode === false && $errmsg === false) {
return base64_encode($res);
} else {
if ($errcode === 40001) {
return $this->_QrCode($type, $data, true, $limit);
}
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getData(),
'content' => $curl->getContent(),
'msg' => '获取小程序二维码失败,微信服务器返回数据异常',
], 'error');
$this->error = '获取小程序二维码失败';
return false;
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
$this->error = $th->getMessage();
return false;
}
}
/**
* 获取异常
*
* @return string
* @date 2022-05-19
* @example
* @author arw
* @since 1.0.0
*/
public function getError()
{
return $this->error;
}
}

View File

@ -0,0 +1,279 @@
<?php
namespace app\common\arw\adjfut\src\WeChat;
use think\facade\Cache;
use app\common\arw\adjfut\src\Curl;
use think\facade\Log;
use think\helper\Arr;
class XcxCommon
{
/**
* 微信小程序 appid
*
* @var string
*/
protected $appid = '';
/**
* 微信小程序 secret
*
* @var string
*/
protected $secret = '';
/**
* 缓存key
*
* @var string
*/
protected $cache_key = '';
/**
* 日志通道
*
* @var string
*/
protected $log_channel = '';
/**
* 异常
*
* @var string
*/
protected $error = '';
/**
* 中控层服务器 access_key
*
* @var string
*/
private $access_key = '';
/**
* 中控层服务器 server_base
*
* @var string
*/
private $server_base = '';
/**
* access_token 缓存
*
* @var string
*/
private $access_token = '';
/**
* 初始化
*
* @date 2022-05-18
* @example
* @author arw
* @since 1.0.0
*/
public function __construct()
{
$this->access_key = Config::get('access_key');
$this->server_base = Config::get('server_base');
$this->appid = Config::get('xcx.appid');
$this->secret = Config::get('xcx.secret');
$this->cache_key = Config::get('xcx.cache_key');
$this->log_channel = Config::get('xcx.log_channel');
}
/**
* 获取access_token
* 如果不刷新并且有缓存【 access_token 】的话
* 直接返回缓存的【 access_token
* 否则将看情况请求【 access_token
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.2
*/
protected function _AccessToken($refresh = false)
{
$cache_key = $this->getAccessTokenCacheKey();
if ($refresh && $cache_key) {
Cache::delete($cache_key);
}
if ($refresh === false && $this->access_token) {
return $this->access_token;
}
$access_token = $this->_GetAccessToken($refresh);
if ($access_token) {
$this->access_token = $access_token;
return $access_token;
} else {
$access_token = $this->_GetCacheAccessToken();
if (!$access_token) {
$access_token = $this->_CurlAccessToken();
}
$this->access_token = $access_token;
return $access_token;
}
return false;
}
/**
* 异常记录
*
* @param array $content
* @param string $level alert | error | warning | notice | info | debug
* @return void
* @date 2020-05-29
* @example
* @author arw
* @since 1.0.0
*/
protected function log(array $data, string $level = 'log')
{
$log_channel = $this->log_channel;
if ($log_channel) {
Log::channel($log_channel)->log($level, json_encode($data));
}
}
/**
* 请求【中控层服务器】 获取access_token
* @param boolean $refresh
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 2.0.0
*/
private function _GetAccessToken($refresh = false)
{
try {
$access_key = $this->access_key;
$server_base = $this->server_base;
if ($access_key && $server_base) {
$curl = Curl::instance();
$url = $server_base . "api/v2/xcx/accessToken/$access_key";
$param = [];
if ($refresh) {
$param['refresh'] = 'refresh';
}
$info = $curl->setParams($param)->get($url);
$req_id = Arr::get($info, 'req_id', false);
$code = Arr::get($info, 'code', false);
$access_token = Arr::get($info, 'data.access_token', false);
if ($access_token) {
return $access_token;
}
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 获取本地缓存【access_token】
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function _GetCacheAccessToken()
{
try {
$cache_key = $this->getAccessTokenCacheKey();
if ($cache_key) {
$access_token = Cache::get($cache_key);
if ($access_token) {
return $access_token;
}
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 请求【微信服务器】 获取access_token
*
* @return string/false
* @date 2020-04-20
* @example
* @author arw
* @since 1.0.0
*/
private function _CurlAccessToken()
{
try {
$cache_key = $this->getAccessTokenCacheKey();
$curl = Curl::instance();
$url = 'https://api.weixin.qq.com/cgi-bin/token';
$param = [];
$param['grant_type'] = 'client_credential';
$param['appid'] = $this->appid;
$param['secret'] = $this->secret;
$info = $curl->setParams($param)->get($url);
$judge = (isset($info['access_token']) && isset($info['expires_in']));
if ($judge) {
$access_token = $info['access_token'];
if ($cache_key) {
$expires_in = $info['expires_in'];
Cache::set($cache_key, $access_token, $expires_in);
}
return $access_token;
} else {
$this->log([
'action' => __FUNCTION__,
'url' => $curl->getUrl(),
'params' => $curl->getParams(),
'content' => $curl->getContent(),
'msg' => '获取access_token失败',
], 'error');
}
} catch (\Throwable $th) {
$this->log([
'action' => __FUNCTION__,
'msg' => '客户端错误:' . $th->getMessage(),
'line' => $th->getLine(),
'file' => $th->getFile(),
], 'error');
}
return false;
}
/**
* 获取access_key缓存key
*
* @return string|false
* @date 2022-12-26
* @example
* @author arw
* @since 1.0.0
*/
private function getAccessTokenCacheKey()
{
if ($this->cache_key) {
return $this->cache_key . '.access_token';
}
return false;
}
}

View File

@ -0,0 +1,174 @@
<?php
namespace app\common\exception;
use think\helper\Arr;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
class Base64
{
/**
* 类型
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $type = '';
/**
* base64内容
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $body = '';
/**
* base64
*
* @var string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private $base64 = '';
/**
* 文件后缀映射
*/
private const FILE_EXT_MAP = [
'text/html' => 'html',
'text/css' => 'css',
'text/javascript' => 'js',
'image/gif' => 'gif',
'image/png' => 'png',
'image/jpeg' => 'jpg',
'image/x-icon' => 'ico',
];
/**
* 实例化
*
* @param string $base64
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function __construct(string $base64)
{
$parse = self::parse($base64);
$this->base64 = $base64;
$this->type = $parse['type'];
$this->body = $parse['body'];
}
/**
* 获取文件后缀
*
* @return string
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function getFileExt(): string
{
return Arr::get(self::FILE_EXT_MAP, $this->type, '');
}
/**
* 判断是否是base64图片字符串
*
* @return boolean
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function isImage(): bool
{
return in_array($this->type, [
'image/gif',
'image/png',
'image/jpeg',
'image/x-icon',
]);
}
/**
* 保存base64图片
*
* @param string $path
* @return void
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public function saveImage(string $path): void
{
file_put_contents($path, base64_decode($this->body));
}
/**
* 判断是否是base64图片字符串
*
* @param string $base64
* @return boolean
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
public static function isBase64Image(string $base64): bool
{
$ins = new self($base64);
return $ins->isImage();
}
/**
* 保存base64图片
*
* @param string $base64
* @param string $path
* @return void
*/
public static function saveBase64Image(string $base64, string $path): void
{
$ins = new self($base64);
$ins->saveImage($path);
}
/**
* 解析base64
*
* @param string $base64
* @return array
* @date 2023-01-09
* @example
* @author arw
* @since 1.0.0
*/
private static function parse(string $base64): array
{
$prefix = 'data:';
$validate = substr($base64, 0, strlen($prefix)) === $prefix;
if (!$validate) {
throw new ErrorMsg("非法base64 开头data:", 1);
}
$explode = explode(';base64,', $base64);
if (count($explode) != 2) {
throw new ErrorMsg("非法base64 不存在;base64,", 1);
}
list($type, $body) = $explode;
$type = str_replace('data:', '', $type);
return [
'type' => $type,
'body' => $body
];
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace app\common\exception;
/**
* 登录超时
*/
class LoginTimeOut extends \Exception
{
}

View File

@ -0,0 +1,37 @@
<?php
namespace app\common\exception;
/**
* 地图扩展类
*/
class Map extends \Exception
{
/**
* 通过地址名获取经纬度
* @address 地址
*/
public static function getLongitudeAndLatitude($address)
{
//创建应用生成的key
$key = '5f059c7c7346c00474804e260979e0b3';
//根据开发文档获取经纬度
$url = "https://restapi.amap.com/v3/geocode/geo?key=" . $key . "&address=" . $address;
$json = file_get_contents($url);
$GaoDeArr = json_decode($json, true);
if ($GaoDeArr['infocode'] != '10000') {
return throwErrorMsg("{$address} 地址信息输入错误");
}
$location = explode(',', $GaoDeArr['geocodes'][0]['location']);
//经度
$longitude = $location[0];
//纬度
$latitude = $location[1];
return [
'longitude' => $longitude,
'latitude' => $latitude,
];
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace app\common\exception;
/**
* 未授权接口
*/
class NotAuthApi extends \Exception
{
}

View File

@ -0,0 +1,49 @@
<?php
namespace app\common\exception;
/**
* 正则验证类
*/
class RegularVerification extends \Exception
{
/**
* 正则匹配验证字符串
* @params string $str 需要匹配验证的字符串
* @params string $type 匹配类型 手机号:mobile(默认),邮箱:email,网址:url,身份证号:idcard,邮编:post,ip地址:ip
*/
public static function checkPreg($str, $type = "")
{
if ($str) {
if ($type == '' || $type == 'mobile') {
//验证手机号 默认
return preg_match('#^1[3,8]{1}\d{9}$|^14[5-9]{1}\d{8}$|/^15[\d^4]{1}\d{8}$/|^16[5,6]{1}\d{8}$|^17[0-8]{1}\d{8}$|^19[0,2,6,7,8,9]{1}\d{8}$#', $str) ? true : false;
} else if ($type == 'email') {
//验证邮箱
return preg_match('#[a-z0-9&\-_.]+@[\w\-_]+([\w\-.]+)?\.[\w\-]+#is', $str) ? true : false;
} else if ($type == 'url') {
//验证url
return preg_match('#(http|https|ftp|ftps)://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?#i', $str) ? true : false;
} else if ($type == 'idcard') {
//验证身份证号
return preg_match('#(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)#', $str) ? true : false;
} else if ($type == 'post') {
//验证邮编
return preg_match('#^[0-9][\d]{5}$#', $str) ? true : false;
} else if ($type == 'ip') {
//验证ip地址
if (preg_match('#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $str)) {
$ipArray = explode('.', $str);
//真实的ip地址每个数字不能大于2550-255
return $ipArray[0] <= 255 && $ipArray[1] <= 255 && $ipArray[2] <= 255 && $ipArray[3] <= 255 ? true : false;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
}

View File

@ -0,0 +1,211 @@
<?php
namespace app\common\exception;
use DateTime;
use think\facade\Request;
use think\facade\Log;
use think\helper\Arr;
use think\facade\Db;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
/**
* 排序类(调用此类的方法需要进行事务与锁表操作)
*/
class Sort
{
/**
* 模型层实例
* @var \think\model
*/
private $model;
/**
* 主键字段名
* @var string
*/
private $pk;
/**
* 排序字段名
* @var string
*/
private $sort_field;
/**
* 表名
* @var string
*/
private $table_name;
/**
* 构造器
*
* @param string $model 模型命名空间
*/
public function __construct(string $model)
{
//模型实例化
$this->model = new $model();
//主键字段名初始化
$this->pk = $this->model->db()->getPk();
//表名初始化
$this->table_name = $this->model->db()->getTable();
//排序字段名初始化
$this->initSortField();
}
/**
* 获取排序字段名
*/
public function getSortField(): string
{
return $this->sort_field;
}
/**
* 获取主键字段名
*/
public function getPk(): string
{
return $this->pk;
}
/**
* 排序号新增处理
*
* @param array $extra_wheres 关联类型 :["xxx_type" => 1]
* @return int 返回新增所需的最新排序号
*/
public function add(array $extra_wheres = []): int
{
$sort_field = $this->sort_field;
$max_sort = $this->model->where($extra_wheres)->order($sort_field, 'desc')->value($sort_field) ?? 0;
return ++$max_sort;
}
/**
* 排序号互换处理(数据A <=> 数据B)
*
* @param string $guid 主键值
* @param int $sort 新排序号
* @param array $extra_wheres 关联类型 :["xxx_type" => 1]
*/
public function swap(string $guid, int $sort, array $extra_wheres = []): void
{
$model = $this->model;
$table_name = $this->table_name;
$pk = $this->pk;
$sort_field = $this->sort_field;
//数据A
$data_a = $model->where($pk, $guid)->find();
if (!$data_a) {
throwErrorMsg('Tool::sortEditProc() : 找不到该数据原排序号', 444);
}
//数据B
//查找当前数据所想更换的新排序号是否已有数据占用(排除自己)这个占用数据则视为数据B
$data_b = $model->where($sort_field, $sort)->where($pk, '<>', $guid)->where($extra_wheres)->find();
//数据B存在开始进行互换处理
if ($data_b) {
//【互换内容初始化】
$update_data = [];
//检查模型是否有父祖级主键,有则也加入互换
if ($this->isExistFathersAndAncestors()) {
$update_data[$model->parent_guid_field] = $data_a[$model->parent_guid_field];
$update_data[$model->ancestors_guid_field] = $data_a[$model->ancestors_guid_field];
}
//原字段排序加入互换
$update_data = [$sort_field => $data_a[$sort_field]];
//关联类型也加入互换
foreach ($extra_wheres as $key => $value) {
$update_data[$key] = $data_a[$key];
};
//【数据逻辑处理】
//互换数据A与B的关联类型不相同时候,则代表为数据互换跨类型情况
if (self::isAssociationTypeDataSame($data_a, $data_b, $extra_wheres)) {
//数据互换不同类型情况-正常互换操作
Db::name($table_name)->where($sort_field, $sort)->where($extra_wheres)->update($update_data);
} else {
//数据互换跨类型情况-腾位操作
$this->vacate($sort, $extra_wheres);
}
}
}
/**
* 排序号腾位处理
*
* @param int|null $sort 当前排序号
* @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) :["xxx_type" => 1,...]
*/
public function vacate($sort, array $wheres = []): void
{
$sort_field = $this->sort_field;
$table_name = $this->table_name;
//新增数据的所属排序号已有数据占用,则腾位处理(已占用的数据及其后面所有数据一起排序号腾位+1)
if ($this->model->where($sort_field, $sort)->where($wheres)->value($sort_field) !== null) {
Db::name($table_name)->where($wheres)->where($sort_field, '>=', $sort)->inc($sort_field)->update();
}
}
/**
* 排序号退位处理
*
* @param string $guid 当前主键值
* @param array $correl_fields 关联类型字段 :["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(:所属xxx类型、所属xxx系列)来进行排序处理
*/
public function back(string $guid, array $correl_fields): void
{
$model = $this->model;
$sort_field = $this->sort_field;
//在所删除的数据之后的数据所属排序号将进行减位处理减位-1
$deleted_find = $model->where($this->pk, $guid)->find();
if ($deleted_find) {
$con = [];
foreach ($correl_fields as $correl_field) {
$con[$correl_field] = $deleted_find[$correl_field];
}
$model->where($sort_field, '>', $deleted_find[$sort_field])->where($con)->dec($sort_field)->save();
}
}
/**
* 对比数据关联类型是否相同【排序号互换处理】
*
* @param object $data_a 互换数据A
* @param object $data_b 互换数据B
* @param array $extra_wheres 关联类型数组 :["xxx_type" => 1]
*/
private static function isAssociationTypeDataSame(object $data_a, object $data_b, array $extra_wheres): bool
{
foreach ($extra_wheres as $key => $val) {
if ($data_a[$key] != $data_b[$key]) {
return false;
}
};
return true;
}
/**
* 检查模型实例父祖级主键字段是否存在【排序号互换处理】
*/
private function isExistFathersAndAncestors(): bool
{
return isset($this->model->parent_guid_field) && isset($this->model->ancestors_guid_field);
}
/**
*初始化排序字段名
*/
private function initSortField(): void
{
if (!isset($this->model->order_field)) {
throwErrorMsg('排序处理失败!模型层未定义排序字段$order_field');
}
$this->sort_field = $this->model->order_field;
}
}

View File

@ -0,0 +1,539 @@
<?php
namespace app\common\exception;
use DateTime;
use think\facade\Request;
use think\facade\Log;
use think\helper\Arr;
use think\facade\Db;
use app\common\exception\Sort;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
class Tool
{
/**
* 生成配置项
*
* @param array $config
* @param callable|string $cb 自定义合并策略
* @return callable
* @date 2022-07-29
* @example
* @author arw
* @since 1.0.0
*/
public static function generateConfig(array $config, $cb = 'array_merge'): callable
{
return function (...$argv) use ($cb, $config) {
array_unshift($argv, $config);
return call_user_func_array($cb, $argv);
};
}
/**
* 构建url
*
* @param string $url
* @param array $query
* @param boolean $decode
* @return string
* @date 2023-01-04
* @example
* @author arw
* @since 1.0.0
*/
public static function buildUrl(string $url, array $query = [], bool $decode = true): string
{
$parse = parse_url($url);
$parse_query = Arr::get($parse, 'query', '');
$_query = [];
parse_str($parse_query, $_query);
if (!is_array($_query)) {
$_query = [];
}
$_query = array_merge($_query, $query);
$_url = str_replace('?' . $parse_query, '', $url);
$_url .= '?';
$_url .= http_build_query($_query);
if ($decode) {
$_url = urldecode($_url);
}
return $_url;
}
/**
* 写入通道日志
*
* @param string $channel
* @param string $type
* @param array $data
* @return void
* @date 2022-07-29
* @example
* @author arw
* @since 1.0.0
*/
public static function writeChannelLog(string $channel, string $type, array $data): void
{
Log::channel($channel)->$type(json_encode($data, JSON_UNESCAPED_UNICODE));
}
/**
* 字段映射
*
* @param array $data
* @param array $map
* @param array $options
* @return array
* @date 2022-06-23
* @example
* @author arw
* @since 1.0.0
*/
public static function fieldMap(array $data, array $map, array $options = []): array
{
$prefix = Arr::get($options, 'prefix', '');
$result = [];
foreach ($data as $key => $value) {
if (isset($map[$key])) {
$key = $map[$key];
}
if ($prefix) {
$key = $prefix . $key;
}
$result[$key] = $value;
}
return $result;
}
/**
* 转换日期格式
*
* @param string $datetime
* @param string $format
* @return string
* @date 2022-04-20
* @example
* @author arw
* @since 1.0.0
*/
public static function conversionDateTime($datetime, string $format): string
{
try {
$date = new DateTime($datetime);
return $date->format($format);
} catch (\Throwable $th) {
throw new ErrorMsg('日期格式非法', 1);
}
}
/**
* 生成唯一id
*
* @param string|array $value
* @return string
* @date 2022-03-24
* @example
* @author arw
* @since 1.0.0
*/
public static function generateGuid($value = ''): string
{
$str = '';
if (is_array($value)) {
$str = json_encode($value);
}
$charid = strtolower(md5($str . uniqid('', true)));
$hyphen = chr(45); // '-'
$uuid = //chr(123)// '{'
substr($charid, 0, 8) . $hyphen
. substr($charid, 8, 4) . $hyphen
. substr($charid, 12, 4) . $hyphen
. substr($charid, 16, 4) . $hyphen
. substr($charid, 20, 12);
//.chr(125);// '}'
return $uuid;
}
/**
* 清空(擦除)缓冲区并关闭输出缓冲
*
* @return void
*/
public static function obEndClean(): void
{
try {
while (ob_get_level() > 0) {
ob_end_clean();
}
} catch (\Throwable $th) {
//throw $th;
}
}
/**
* 自动创建文件夹
*
* @param string $path 文件夹路径
* @param integer $mode 权限
* @return boolean
*/
public static function mkdir(string $path, int $mode = 0755): bool
{
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
$temp = explode(DIRECTORY_SEPARATOR, $path);
if (preg_match("/\./", end($temp))) {
// pathinfo 不支持中文
// $path = pathinfo($path, PATHINFO_DIRNAME);
array_splice($temp, count($temp) - 1, 1);
$path = join(DIRECTORY_SEPARATOR, $temp);
}
if (!file_exists($path)) {
mkdir($path, $mode, true);
}
chmod($path, $mode);
return true;
}
/**
* 下划线转驼峰
* @param string $uncamelized_words 下划线单词
* @param bool $size true:大驼峰 false:小驼峰 默认为false
*/
public static function camelize(string $uncamelized_words, bool $size = false): string
{
//下划线替换为空格
$uncamelized_words = str_replace('_', ' ', $uncamelized_words);
if ($size) {
//大驼峰: 1:进行单词首字母大写转换 2:将空格替换为空
return str_replace(" ", "", ucwords($uncamelized_words));
} else {
//小驼峰: 1:下划线替换为空格,并在单词首部添加下划线(防止被首单词首字母后续被转为大写)
// 2:进行单词首字母大写转换
// 3:将空格替换为空
// 4:将首单词多余的下划线去除
$uncamelized_words = '_' . $uncamelized_words;
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), '_');
}
}
/**
* 驼峰命名转下划线命名
* @param string $uncamelize 驼峰单词
*/
public static function uncamelize($camelCaps): string
{
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . '_' . "$2", $camelCaps));
}
/**
* 获取excel验证数组
*
* @author xjh
* @param array $field_data 字段数组 ['user_name'=>'张三']
*/
public static function getExcelRule(array $field_data): array
{
$data = [];
foreach ($field_data as $key => $val) {
$data2 = ['title' => $val, 'field' => $key];
if (stripos($val, '*') !== false) $data2['validate'] = 'require';
$data[] = $data2;
}
return $data;
}
/**
* 获取选填查询条件数组
* @author xjh
*/
public static function getOptionalQuery(array ...$data)
{
$params = [];
$con = [];
//请求参数获取
if (isset($data[0]['params']) && $data[0]['params']) {
//自定义
$params = $data[0]['params'];
unset($data[0]);
$data = array_merge($data);
} else {
//自动获取
$params = Request::param();
}
//处理联表字段
$dealJoinField = function (string $field_name): string {
$stripos_val = stripos($field_name, '.');
return $stripos_val !== false ? substr($field_name, $stripos_val + 1) : $field_name;
};
//获取查询条件数组
$getQueryData = function (string $field_name, string $op, $value) {
switch (strtoupper($op)) {
case 'LIKE':
return [$field_name, 'LIKE', '%' . $value . '%'];
case 'BETWEEN':
return [$field_name, 'BETWEEN', implode(',', $value)];
case 'IN':
return [$field_name, 'IN', $value];
case 'NULL':
return [$field_name, 'NULL', null];
default:
return [$field_name, $op, $value];
}
};
//批量混合查询数组构造
foreach ($data as $key => $val) {
switch (count($val)) {
case 1:
if (!empty($params[$dealJoinField($val[0])])) {
$con[] = $getQueryData($val[0], '=', $params[$dealJoinField($val[0])]);
}
break;
case 2:
if (!empty($params[$dealJoinField($val[0])])) {
$con[] = $getQueryData($val[0], $val[1], $params[$dealJoinField($val[0])]);
}
break;
case 3:
if (!empty($params[$dealJoinField($val[2])])) {
$con[] = $getQueryData($val[0], $val[1], $params[$val[2]]);
}
break;
}
}
return $con;
}
/**
* 上下个数据返回
*
* @author xjh
* @param string $model 模型层命名空间地址
* @param int|array $sort_field_info 排序字段信息 :当前排序值(排序字段默认走模型层定义好的) | [排序字段字段名,当前排序值]
* @param array $extends 扩展
* @param array $extends["field"] 数据结果集标识要返回的字段(默认全部字段返回)
* @param string $extends["type"] 返回类型 all(默认):[上个数据,下个数据] | last:上个数据 | next:下个数据
* @param array $extends["extraWhere"] 额外查询条件(批量混合查询格式: [ ['user_name','=','name'],... ]
* @return string|array
*/
public static function getLastNextData(string $model, $sort_field_info, array $extends = [])
{
//模型对象初始化
$model = new $model();
//排序字段信息初始化
$order_field_name = null;
$order_field_val = null;
if (is_array($sort_field_info)) {
$order_field_name = $sort_field_info[0];
$order_field_val = $sort_field_info[1];
} else if (is_int($sort_field_info)) {
if (!isset($model->order_field)) {
throwErrorMsg(__METHOD__ . "若排序字段信息为字符串,则必须要在模型层定义排序字段");
}
$order_field_name = $model->order_field;
$order_field_val = $sort_field_info;
}
//非必传参数初始化
$extra_where = isset($extends['extraWhere']) ? $extends['extraWhere'] : [];
$field = isset($extends['field']) ? $extends['field'] : null;
$type = isset($extends['type']) ? $extends['type'] : 'all';
//闭包查询函数
$query = function ($op) use ($model, $order_field_name, $order_field_val, $field, $extra_where) {
$order = $op == '<' ? 'desc' : 'asc';
if ($field) $model = $model->field($field);
return $model->where($order_field_name, $op, $order_field_val)
->where($extra_where)
->order($order_field_name, $order)
->find();
};
//获取指定类型数据
switch ($type) {
case 'all':
return [$query('<'), $query('>')];
case 'last':
return $query('<');
case 'next':
return $query('>');
default:
throwErrorMsg('Tool::getLastNextId() : type无此类型', 444);
}
}
/**
* 后台接口锁表(排他锁/写锁/独占锁)操作
*
* @param string|array $table 被锁表名(可批量)
*/
public static function adminLockTableWrite($table)
{
if(is_string($table)){
$table = [$table];
}
$lock_table =[];
foreach ($table as $table_name)
{
$lock_table[] = "{$table_name} WRITE";
};
$lock_table = implode(',',$lock_table);
Db::execute("LOCK TABLES {$lock_table},token WRITE,user WRITE");
}
/**
* 解除表锁
*/
public static function unlockTable()
{
Db::execute("UNLOCK TABLES");
}
/**
* 数据修改前排序处理
*
* 注意:(调用此类的方法需要进行事务与锁表操作)
* @param \think\model &$model 模型实例引用
* @param array $extra_wheres 关联类型 :["xxx_type" => 1]
*/
public static function dataEditSortProc(\think\model &$model, array $extra_wheres = []): void
{
$sort = new Sort(get_class($model));
$sort->swap($model[$sort->getPk()], $model[$sort->getSortField()], $extra_wheres);
}
/**
* 数据新增前排序处理
*
* 注意:(调用此类的方法需要进行事务与锁表操作)
* @param \think\model &$model 模型实例引用
* @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) :["xxx_type" => 1,...]
*/
public static function dataAddSortProc(\think\model &$model, array $wheres = []): void
{
$sort = new Sort(get_class($model));
if ($model[$sort->getSortField()]) {
$sort->vacate($model[$sort->getSortField()], $wheres);
} else {
$model[$sort->getSortField()] = $sort->add($wheres);
}
}
/**
* 数据删除前排序处理
*
* 注意:(调用此类的方法需要进行事务与锁表操作)
* @param \think\model &$model 模型实例引用
* @param array $correl_fields 关联类型字段 :["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(:所属xxx类型、所属xxx系列)来进行排序处理
*/
public static function dataDeleteSortProc(\think\model &$model, array $correl_fields = []): void
{
$sort = new Sort(get_class($model));
$sort->back($model[$sort->getPk()], $correl_fields);
}
/**
* 处理(树形数据父子)伦理关系
*
* @param \think\Model $model 模型层对象
*/
public static function handleEthicalRel(\think\Model $model): void
{
if (!isset($model->parent_guid_field) || !isset($model->ancestors_guid_field)) {
throwErrorMsg(__METHOD__
. "方法:"
. get_class($model)
. "模型层必须定义public \$parent_guid_field,public \$works_type_ancestors_guid");
}
//获取当前的主键字段名
$guld_field = $model->db()->getPk();
//当前父级主键字段名
$parent_guid_field = $model->parent_guid_field;
//当前祖级级主键字段名
$ancestors_guid_field = $model->ancestors_guid_field;
//处理一
if ($model[$guld_field] == $model[$parent_guid_field]) {
throwErrorMsg("不可以当自己的子级!");
}
//处理二
$is_children = $model->where([
[$guld_field, '=', $model[$parent_guid_field]],
[$ancestors_guid_field, 'REGEXP', $model[$guld_field]],
])->find();
if ($is_children) {
throwErrorMsg("不可以当自己孩子们的子级!");
}
}
/**
* 祖级guid构建
* @param \think\Model $model 模型层命名空间地址
* @param bool $is_unipolar 是否为单极结构
*/
public static function buildAncestorsGuid(\think\Model &$model, bool $is_unipolar = false): void
{
//获取最大父级guid
$first_parent_guid = isset($model->first_parent_guid) ? $model->first_parent_guid : "0";
//获取当前的主键字段名
$guld_field_name = $model->db()->getPk();
//获取当前父级主键集字段名
$parent_guid_field = $model->parent_guid_field;
$parent_guid = $model[$parent_guid_field];
//获取当前祖级主键集字段名
$ancestors_guid_field = $model->ancestors_guid_field;
//单极结构或父级guid已经为最大父级guid时直接返回最大父级guid
if ($is_unipolar || $parent_guid == $first_parent_guid) {
$model[$ancestors_guid_field] = $first_parent_guid;
return;
}
//开始构建祖级guid
$parent = $model->where($guld_field_name, $parent_guid)->find();
if (!$parent) throwErrorMsg('该父级数据不存在!');
$model[$ancestors_guid_field] = $parent[$ancestors_guid_field] . ',' . $parent_guid;
}
/**
* 初始化模型字段值
*
* @param \think\Model &$model 模型层对象(引用传递)
* @param array $field_values 初始化字段信息 : ['user_name'=>'张三',...]
*/
public static function initModelFieldValue(\think\Model &$model, array $field_values): void
{
foreach ($field_values as $field => $value) {
$model[$field] = $value;
}
}
/**
* 字符串替换优化版
*
* @param string $subject 执行替换字符串
* @param array $search_and_replace 替换信息 :[替换目标=>替换值,替换目标=>替换值,...]
* @return string 该函数返回替换后的字符串。
*/
public static function strReplacePlus(string $subject, array $search_and_replace): string
{
$search = [];
$replace = [];
foreach ($search_and_replace as $key => $val) {
$search[] = $key;
$replace[] = $val;
};
return str_replace($search, $replace, $subject);
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace app\common\listener;
use app\common\model\Token;
class DelayToken
{
/**
* 事件监听处理
*
* @return mixed
*/
public function handle($event)
{
Token::delay();
}
}

170
app/common/logic/Login.php Normal file
View File

@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace app\common\logic;
use app\common\model\Token;
use app\common\model\User\User;
use think\facade\Config;
use think\facade\Request;
use think\helper\Arr;
use think\Response;
use think\response\Redirect;
use app\common\arw\adjfut\src\Curl;
use app\common\arw\adjfut\src\Tool;
class Login
{
/**
* 账号登陆
*
* @param string $account
* @param string $password
* @return Token
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public static function accountLogin(string $account, string $password = ''): Token
{
if (!$account) {
throwErrorMsg('账号不能未空');
}
/**
* @var User
*/
$user = User::where([
'user_phone|user_name' => $account,
])->find();
if (!$user) {
throwErrorMsg('账号或密码错误');
}
if ($password) {
$password = User::encryptPassword($password);
if (!self::isOpPassword($password)) {
if ($user->user_password != $password) {
throwErrorMsg('账号或密码错误');
}
}
}
return $user->login();
}
/**
* 西北政法大学单点登陆
*
* @param callable $cb
* @return Redirect
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public static function casOauthLogin(callable $cb): Redirect
{
$ticket = Request::param('ticket');
$service = Request::url(true);
$baseUrl = "https://ip.nwupl.edu.cn/cas/login?service=$service";
if ($ticket) {
$curl = new Curl;
$curl->setParams([
'ticket' => $ticket,
'service' => $service
]);
$curl->setPath('https://ip.nwupl.edu.cn/cas/serviceValidate');
$curl->get();
$data = $curl->getContent();
if (!$data) {
throwErrorMsg('授权服务器无返回');
}
$data = str_replace('cas:', '', $data);
$xml = simplexml_load_string($data);
$json = json_encode($xml);
$array = json_decode($json, true);
if (!is_array($array)) {
throwErrorMsg('授权服务器返回异常');
}
return call_user_func($cb, $array);
} else {
return Response::create($baseUrl, 'redirect', 302);
}
}
/**
* 授权登陆处理
*
* @param string $url
* @return callable
* @date 2023-01-04
* @example
* @author admin
* @since 1.0.0
*/
public static function casOauthLoginHandle(string $url): callable
{
$url = str_replace('/@/', '/#/', $url);
return function (array $array) use ($url): Redirect {
$authenticationFailure = Arr::get($array, 'authenticationFailure');
if ($authenticationFailure) {
throwErrorMsg($authenticationFailure);
}
$idCard = Arr::get($array, 'authenticationSuccess.attributes.idCard');
if (!$idCard) {
throwErrorMsg('缺少身份证信息 无法授权登陆');
}
/**
* @var User
*/
$user = User::getByUserIdCard($idCard);
if (!$user) {
throwErrorMsg('用户不存在 请联系管理员');
}
/**
* @var Token
*/
$token = $user->login();
$url = Tool::buildUrl($url, [
'token' => $token->token_content
]);
return Response::create($url, 'redirect', 302);
};
}
/**
* 西北政法大学单点登出
*
* @param string $service
* @return Redirect
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public static function casOauthLogout(string $service): Redirect
{
if (Token::isLogin()) {
$token = Token::getCurrent();
$token->logout();
}
$baseUrl = "https://ip.nwupl.edu.cn/cas/logout?service=$service";
return Response::create($baseUrl, 'redirect', 302);
}
/**
* 是否超级密码
*
* @param string $password
* @return boolean
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
private static function isOpPassword(string $password): bool
{
return md5(Config::get('app.op_key') . date('Y-m-d')) == $password;
}
}

View File

@ -0,0 +1,290 @@
<?php
namespace app\common\model\Dictionary;
use app\BaseModel;
use think\Exception;
use think\facade\Config;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use think\facade\Db;
class Dictionary extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'dictionary_delete_time';
// 设置主键名
protected $pk = 'dictionary_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'dictionary_id' => 'int',
'dictionary_parent_guid' => 'string',
'dictionary_name' => 'string',
'dictionary_value' => 'string',
'dictionary_index' => 'int',
'dictionary_order' => 'int',
'dictionary_status' => 'int',
'dictionary_allow_update' => 'int',
'dictionary_list_class' => 'string',
'dictionary_create_time' => 'datetime',
'dictionary_create_user_guid' => 'string',
'dictionary_update_time' => 'datetime',
'dictionary_update_user_guid' => 'string',
'dictionary_delete_time' => 'datetime',
'dictionary_delete_user_guid' => 'string',
'dictionary_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'dictionary_create_time';
// 修改时间
protected $updateTime = 'dictionary_update_time';
// 状态查询范围
public function scopeStatus($query, $status = 1)
{
$query->where('dictionary_status', $status);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->dictionary_status = 1;
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 获取状态
*
* @param mixed $value
* @param array $data
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function getDictionaryStatusTextAttr($value, $data): string
{
return [
1 => '启用',
2 => '停用'
][$data['dictionary_status']];
}
/**
* 创建人
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function createUser(): HasOne
{
return $this->hasOne(User::class, 'user_create_user_id');
}
/**
* 更新人
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function updateUser(): HasOne
{
return $this->hasOne(User::class, 'user_update_user_id');
}
/**
* 删除人
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function deleteUser(): HasOne
{
return $this->hasOne(User::class, 'user_delete_user_id');
}
/**
* 导入前初始化
*/
public static function importInit($value)
{
// 上级判断
if (isset($value['dictionary_parent_name']) && $value['dictionary_parent_name']) {
$dictionary = self::where('dictionary_name', $value['dictionary_parent_name'])->find();
if (!$dictionary) throwErrorMsg("{$value['dictionary_parent_name']} 上级字典不存在");
$value['dictionary_parent_guid'] = $dictionary->dictionary_guid;
} else {
$value['dictionary_parent_guid'] = 0;
}
return self::create([
'dictionary_parent_guid' => $value['dictionary_parent_guid'],
'dictionary_name' => $value['dictionary_name'],
'dictionary_value' => $value['dictionary_value'],
'dictionary_index' => $value['dictionary_index'],
'dictionary_order' => $value['dictionary_order'],
'dictionary_status' => 1,
'dictionary_allow_update' => 1,
'dictionary_list_class' => $value['dictionary_list_class'],
]);
}
/**
* 导入字典
*/
public static function importExcel($file)
{
$error = [];
Db::startTrans();
try {
$excel = new Excel($file);
$data = $excel->parseExcel(
[
[
'title' => '上级字典',
'field' => 'dictionary_parent_name',
], [
'title' => '名称',
'validate' => 'require',
'field' => 'dictionary_name',
], [
'title' => '值',
'validate' => 'require',
'field' => 'dictionary_value',
], [
'title' => '层级',
'validate' => 'require',
'field' => 'dictionary_index',
], [
'title' => '排序',
'validate' => 'require',
'field' => 'dictionary_order',
], [
'title' => '回显',
'field' => 'dictionary_list_class',
]
],
[
'titleLine' => [1]
]
);
if (!$data) throwErrorMsg('excel无数据', 1);
$error = [];
foreach ($data as $line => $value) {
try {
$model = self::importInit($value);
$error[] = "{$line} 字典:【{$value['dictionary_name']}】<span style='color:#27af49'>新增成功!</span><br>";
} catch (\Throwable $th) {
$error[] = "{$line} 字典:【{$value['dictionary_name']}】<span style='color:red'>{$th->getMessage()}</span><br>";
}
}
Db::commit();
return implode(', ', $error);
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 获取字典数据(二级)
*
* @param string $dictionary_value 字典模块字段值
* @param array $field 字段筛选
*/
public static function getDictionaryData(string $dictionary_value, array $field = ['dictionary_name', 'dictionary_value']): array
{
$dictionary = self::where('dictionary_value', $dictionary_value)->find();
if (!$dictionary) throwErrorMsg("字典数据不存在dictionary_value值为{$dictionary_value}的数据!");
return self::field($field)->where('dictionary_parent_guid', $dictionary->dictionary_guid)->select()->toArray();
}
/**
* 获取字典集合(二级)指定字典值的名称
*
* @param array $dictionary_data 字典集合
* @param string $dictionary_value 字典值
* @return string|null
*/
public static function getDataDictionaryName(array $dictionary_data, string $dictionary_value)
{
$dictionary_name = null;
foreach ($dictionary_data as $dictionary) {
if ($dictionary['dictionary_value'] == $dictionary_value) {
$dictionary_name = $dictionary['dictionary_name'];
break;
}
};
return $dictionary_name;
}
/**
* 获取字典集合(二级)指定字名称的值
*
* @param array $dictionary_data 字典集合
* @param string $dictionary_name 字典名称
* @return string|null
*/
public static function getDataDictionaryValue(array $dictionary_data, string $dictionary_name)
{
$dictionary_value = null;
foreach ($dictionary_data as $dictionary) {
if ($dictionary['dictionary_name'] == $dictionary_name) {
$dictionary_value = $dictionary['dictionary_value'];
break;
}
};
return $dictionary_value;
}
}

View File

@ -0,0 +1,279 @@
<?php
namespace app\common\model\Flow;
use app\Request;
use think\Db;
use Throwable;
use Zhiyun\Adjfut\Traverse;
use app\BaseModel;
use app\model\Product as ModelProduct;
use think\db\Query;
use think\Exception;
use think\facade\Config;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class Flow extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'flow_delete_time';
// 设置主键名
protected $pk = 'flow_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'flow_id' => 'int',
'flow_visitor_ip' => 'string',
'flow_location' => 'string',
'flow_source' => 'string',
'flow_browser' => 'string',
'flow_record_no' => 'string',
'flow_os'=>'string',
'flow_target'=>'string',
'flow_create_user_guid' => 'string',
'flow_create_time' => 'datetime',
'flow_update_time' => 'datetime',
'flow_update_user_guid' => 'string',
'flow_delete_time' => 'datetime',
'flow_delete_user_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'flow_create_time';
// 修改时间
protected $updateTime = 'flow_update_time';
/**
* 新增前
*/
public static function onBeforeInsert(self $model): void
{
// self::checkRepeatData($model);
// $model->completeCreateField();
}
/**
* 更新前
*/
public static function onBeforeUpdate(self $model): void
{
// self::checkRepeatData($model);
// $model->completeUpdateField();
}
/**
* 删除前
*/
public static function onBeforeDelete(self $model): void
{
// $model->completeDeleteField();
}
/**
* @throws Throwable
*/
public function track($flow_target)
{
$res = [];
//获取访问者ip
$res['flow_visitor_ip'] = $this->getIp();
//获取访问者操作系统
$res['flow_os'] = $this->getOs();
//获取访问者浏览器
$res['flow_browser'] = $this->getBrowser();
//获取访问者地址
$res['flow_location'] = $this->getLocation();
//获取访问者来源
$res['flow_source'] = $this->getSource();
//生成记录号
$res['flow_record_no'] = $this->createRecordNumber();
//访问目的地
$res['flow_target'] = $this->getTarget($flow_target);
$res['flow_create_time'] = date("Y-m-d H:i:s");
// return json($_SERVER);
// return json($res);
try {
$this::create($res);
}catch (Throwable $th){
throw $th;
}
}
private function getTarget($flow_target):string
{
$url = $flow_target;
// $res_url = explode('/',$url)[1];
$match_res_pool = [
'index'=>'首页',
'about-intro-idx'=>'关于我们-厚德简介',
'about-env-idx-type'=>'关于我们-教学环境',
'about-history-idx'=>'关于我们-发展历程',
'signUp-signUp_introduction-idx-page'=>'招生报名-招生简介',
'signUp-signUp_introduction-details-id'=>'招生报名-招生简介-详情页',
'signUp-classes_intro-idx-page'=>'招生报名-班型介绍',
'signUp-classes_intro-details-id'=>'招生报名-班型介绍-详情页',
'signUp-enrol_aq-idx'=>'招生报名-招生回答',
'signUp-signup_way-idx'=>'招生报名-报名方式',
'teachers-idx'=>'师资力量',
'teachers-details-id'=>'师资力量-详情页',
'achievement-school_achievement-idx'=>'荣誉成绩-录取院校',
'achievement-joint_achievement-idx-page'=>'荣誉成绩-联考成绩',
'achievement-joint_achievement-details-id'=>'荣誉成绩-联考成绩-详情页',
'works-idx'=>'作品欣赏',
'news-idx-page'=>'艺考咨询',
'news-details-id'=>'艺考咨询',
'contactUs-contact_info-idx'=>'联系我们-联系方式',
'contactUs-signup-idx'=>'联系我们-在线报名',
'contactUs-leave_message-idx'=>'联系我们-用户留言',
'contactUs-join_apply-idx'=>'联系我们-合作加盟',
];
if(!isset($match_res_pool[$url])){
return '未知页面';
}
return $match_res_pool[$url];
}
private function createRecordNumber(): string
{
return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
}
private function getSource(){
if(isset($_SERVER['HTTP_REFERER'])){
$colon_index = stripos($_SERVER['HTTP_REFERER'], ':');
$ds = '/';
$ds_count = 0; //冒号后斜杠的数量
for ($i = $colon_index + 1; $i < strlen($_SERVER['HTTP_REFERER']); $i++) {
if ($_SERVER['HTTP_REFERER'][$i] != $ds) break;
$ds_count++;
}
$url_data = explode('/', $_SERVER['HTTP_REFERER']);
$url = "";
foreach ($url_data as $key => $val) {
if ($key > $ds_count-1) $url .= $ds . $val;
};
return explode('/',$url)[1];
}
return '直接访问';
}
private function getOs(): string
{
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
$OS = $_SERVER['HTTP_USER_AGENT'];
if (preg_match('/win/i', $OS)) {
$OS = 'window';
} elseif (preg_match('/mac/i', $OS)) {
$OS = 'mac';
} elseif (preg_match('/linux/i', $OS)) {
$OS = 'linux';
} elseif (preg_match('/unix/i', $OS)) {
$OS = 'unix';
} elseif (preg_match('/bsd/i', $OS)) {
$OS = 'bsd';
} else {
$OS = '其他操作系统';
}
return $OS;
} else {
return "获取访客操作系统信息失败!";
}
}
private function getBrowser()
{
if (isset($_SERVER["HTTP_USER_AGENT"])) {
$user_agent = strtolower($_SERVER["HTTP_USER_AGENT"]);
} else {
return null;
}
$user_agent=strtolower($_SERVER["HTTP_USER_AGENT"]);
// 固定检测
if (strrpos($user_agent, 'micromessenger')) {
$user_bs = 'Weixin';
} elseif (strrpos($user_agent, 'qq')) {
$user_bs = 'QQ';
} elseif (strrpos($user_agent, 'weibo')) {
$user_bs = 'Weibo';
} elseif (strrpos($user_agent, 'alipayclient')) {
$user_bs = 'Alipay';
} elseif (strrpos($user_agent, 'trident/7.0')) {
$user_bs = 'IE11';
// 新版本IE优先避免360等浏览器的兼容模式检测错误
} elseif (strrpos($user_agent, 'trident/6.0')) {
$user_bs = 'IE10';
} elseif (strrpos($user_agent, 'trident/5.0')) {
$user_bs = 'IE9';
} elseif (strrpos($user_agent, 'trident/4.0')) {
$user_bs = 'IE8';
} elseif (strrpos($user_agent, 'msie 7.0')) {
$user_bs = 'IE7';
} elseif (strrpos($user_agent, 'msie 6.0')) {
$user_bs = 'IE6';
} elseif (strrpos($user_agent, 'edg')) {
$user_bs = 'Edge';
} elseif (strrpos($user_agent, 'firefox')) {
$user_bs = 'Firefox';
} elseif (strrpos($user_agent, 'chrome') || strrpos($user_agent, 'android')) {
$user_bs = 'Chrome';
} elseif (strrpos($user_agent, 'safari')) {
$user_bs = 'Safari';
} elseif (strrpos($user_agent, 'mj12bot')) {
$user_bs = 'MJ12bot';
} else {
$user_bs = '其他浏览器';
}
return $user_bs;
}
private function getIp() {
if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP") , "unknown")) {
$ip = getenv("HTTP_CLIENT_IP");
} else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR") , "unknown")) {
$ip = getenv("HTTP_X_FORWARDED_FOR");
} else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR") , "unknown")) {
$ip = getenv("REMOTE_ADDR");
} else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = "unknown";
}
return $ip;
}
private function getLocation($ip = '') {
empty($ip) && $ip = $this->getIp();
$location = '';
if ($ip == "127.0.0.1") return ("本机地址");
$bLocalIp = !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
if($bLocalIp)return '局域网IP';
try {
$api = "https://www.fkcoder.com/ip?ip=$ip"; //请求新浪ip地址库
$json = @file_get_contents($api);
$res = json_decode($json, true);
$location = $res['country'].$res['province'].$res['city'];
return $location;
}catch (Throwable $th){
return 'ip获取异常';
}
}
}

View File

@ -0,0 +1,128 @@
<?php
namespace app\common\model\Menu;
use app\BaseModel;
use think\Exception;
use think\facade\Config;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class Menu extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'menu_delete_time';
// 设置主键名
protected $pk = 'menu_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'menu_id' => 'int',
'menu_parent_guid' => 'string',
'menu_name' => 'string',
'menu_url' => 'string',
'menu_index' => 'int',
'menu_order' => 'int',
'menu_status' => 'int',
'menu_show' => 'int',
'menu_create_time' => 'datetime',
'menu_create_user_guid' => 'string',
'menu_update_time' => 'datetime',
'menu_update_user_guid' => 'string',
'menu_delete_time' => 'datetime',
'menu_delete_user_guid' => 'string',
'menu_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'menu_create_time';
// 修改时间
protected $updateTime = 'menu_update_time';
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus($query, $status = self::STATUS_ENABLE)
{
$query->where('menu_status', $status);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->menu_status = self::STATUS_ENABLE;
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 获取状态
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function getMenuStatusTextAttr(): string
{
return [
self::STATUS_ENABLE => '启用',
self::STATUS_DISABLE => '停用'
][$this->menu_status];
}
/**
* 菜单接口
*
* @date 2022-03-07
* @example
* @author admin
* @since 1.0.0
*/
public function apis(): HasMany
{
return $this->hasMany(MenuApi::class, 'menu_guid');
}
}

View File

@ -0,0 +1,142 @@
<?php
namespace app\common\model\Menu;
use app\BaseModel;
use app\exception\ErrorMsg;
use think\Exception;
use think\facade\App;
use think\facade\Config;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class MenuApi extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'menu_api_delete_time';
// 设置主键名
protected $pk = 'menu_api_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'menu_api_id' => 'int',
'menu_api_guid' => 'string',
'menu_guid' => 'string',
'menu_api_url' => 'string',
'menu_api_status' => 'int',
'menu_api_create_time' => 'datetime',
'menu_api_create_user_guid' => 'string',
'menu_api_update_time' => 'datetime',
'menu_api_update_user_guid' => 'string',
'menu_api_delete_time' => 'datetime',
'menu_api_delete_user_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'menu_api_create_time';
// 修改时间
protected $updateTime = 'menu_api_update_time';
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus($query, $status = self::STATUS_ENABLE)
{
$query->where('menu_api_status', $status);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->menu_api_status = self::STATUS_ENABLE;
$menu_api_urls = explode('/', $model->menu_api_url);
if (count($menu_api_urls) == 3) {
$app = isset($menu_api_urls[0]) ? $menu_api_urls[0] : '';
$controller = isset($menu_api_urls[1]) ? $menu_api_urls[1] : '';
$action = isset($menu_api_urls[2]) ? $menu_api_urls[2] : '';
} else {
$controller = isset($menu_api_urls[0]) ? $menu_api_urls[0] : '';
$action = isset($menu_api_urls[1]) ? $menu_api_urls[1] : '';
}
if (!$controller) {
throwErrorMsg("缺少controller", 1);
}
if (!$action) {
throwErrorMsg("缺少action", 1);
}
$controller = str_replace('.', '\\', $controller);
if ($app) {
$class = "\\app\\$app\\controller\\$controller";
} else {
$class = "\\app\\controller\\$controller";
}
if (!class_exists($class)) {
throwErrorMsg("controller不存在 $controller", 1);
}
if (!method_exists($class, $action)) {
throwErrorMsg("action不存在 $action", 1);
}
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 获取状态
*
* @param mixed $value
* @param array $data
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function getMenuApiStatusTextAttr(): string
{
return [
self::STATUS_ENABLE => '启用',
self::STATUS_DISABLE => '停用'
][$this->menu_api_status];
}
}

View File

@ -0,0 +1,146 @@
<?php
namespace app\common\model\Role;
use app\BaseModel;
use app\common\model\Menu\Menu;
use app\common\model\User\User;
use app\common\model\User\UserRole;
use think\db\Query;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
class Role extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'role_delete_time';
// 设置主键名
protected $pk = 'role_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'role_id' => 'int',
'role_name' => 'string',
'role_status' => 'int',
'role_create_time' => 'datetime',
'role_create_user_guid' => 'string',
'role_update_time' => 'datetime',
'role_update_user_guid' => 'string',
'role_delete_time' => 'datetime',
'role_delete_user_guid' => 'string',
'role_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'role_create_time';
// 修改时间
protected $updateTime = 'role_update_time';
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus(Query $query, $status = self::STATUS_ENABLE)
{
$query->where('role_status', $status);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->role_status = self::STATUS_ENABLE;
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 获取状态
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function getRoleStatusTextAttr(): string
{
return [
self::STATUS_ENABLE => '启用',
self::STATUS_DISABLE => '停用'
][$this->role_status];
}
/**
* 获取角色用户
*
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(
User::class,
UserRole::class,
'user_guid',
'role_guid'
);
}
/**
* 获取角色菜单
*
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function menus(): BelongsToMany
{
return $this->belongsToMany(
Menu::class,
RoleMenu::class,
'menu_guid',
'role_guid'
);
}
}

View File

@ -0,0 +1,162 @@
<?php
namespace app\common\model\Role;
use app\BasePivot;
use app\common\model\Menu\Menu;
use think\db\Query;
use think\facade\Db;
use think\model\Collection;
use think\model\concern\SoftDelete;
use think\model\Pivot;
use think\model\relation\HasMany;
use app\common\arw\adjfut\src\Model\Pivot as ModelPivot;
class RoleMenu extends BasePivot
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'role_menu_delete_time';
// 设置主键名
protected $pk = 'role_menu_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'role_menu_id' => 'int',
'menu_guid' => 'string',
'role_guid' => 'string',
'role_menu_status' => 'int',
'role_menu_create_time' => 'datetime',
'role_menu_create_user_guid' => 'string',
'role_menu_update_time' => 'datetime',
'role_menu_update_user_guid' => 'string',
'role_menu_delete_time' => 'datetime',
'role_menu_delete_user_guid' => 'string',
'role_menu_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'role_menu_create_time';
// 修改时间
protected $updateTime = 'role_menu_update_time';
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus($query, $status = self::STATUS_ENABLE)
{
$query->where('role_menu_status', $status);
}
/**
* 中间关联
*
* @return ModelPivot
* @date 2022-12-30
* @example
* @author admin
* @since 1.0.0
*/
public static function pivot(): ModelPivot
{
return new ModelPivot(
self::class,
Role::class,
Menu::class,
true
);
}
/**
* 绑定角色菜单
*
* @param $roles
* @param $menus
* @param array $extra
* @return void
* @date 2022-12-29
* @example
* @author admin
* @since 1.0.0
*/
public static function bindRoleMenu($roles, $menus, array $extra = []): void
{
self::pivot()->bind($roles, $menus, $extra);
}
/**
* 解绑角色菜单
*
* @param $roles
* @param $menus
* @return void
* @date 2022-12-30
* @example
* @author admin
* @since 1.0.0
*/
public static function unbindRoleMenu($roles, $menus): void
{
self::pivot()->unbind($roles, $menus);
}
/**
* 绑定角色菜单
*
* @param $roles
* @param $menus
* @param array $extra
* @return void
* @date 2022-12-29
* @example
* @author admin
* @since 1.0.0
*/
public static function rebindRoleMenu($roles, $menus, array $extra = []): void
{
self::pivot()->rebind($roles, $menus, $extra);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->role_menu_status = self::STATUS_ENABLE;
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
}

View File

@ -0,0 +1,184 @@
<?php
namespace app\common\model\Tdk;
use app\common\arw\adjfut\src\Validate;
use app\BaseModel;
use think\model\concern\SoftDelete;
use app\common\arw\adjfut\src\Excel;
use app\Request;
use app\common\model\Dictionary\Dictionary as ModelDictionary;
use app\common\exception\Tool;
use think\facade\Db;
class Tdk extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'tdk_delete_time';
// 设置主键名
protected $pk = 'tdk_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
"tdk_id" => "int",
"tdk_guid" => "string",
"tdk_type" => "string",
"tdk_title" => "string",
"tdk_description" => "string",
"tdk_keyword" => "string",
"tdk_create_time" => "datetime",
"tdk_create_user_guid" => "string",
"tdk_update_time" => "datetime",
"tdk_update_user_guid" => "string",
"tdk_delete_time" => "datetime",
"tdk_delete_user_guid" => "string",
];
// 设置json类型字段
protected $json = [''];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'tdk_create_time';
// 修改时间
protected $updateTime = 'tdk_update_time';
// excel导入/下载模板表头
public const EXCELFIELD = [
'tdk_type' => 'tdk所属模块',
'tdk_title' => '网页标题',
'tdk_description' => '网页简介',
'tdk_keyword' => '网页关键词',
];
/**
* 新增前
*/
public static function onBeforeInsert(self $model): void
{
Validate::unique(
self::class,
$model->tdk_guid,
$model->getData(),
['tdk_type' => 'tdk模块',],
['tdk_type' => '一个tdk模块只能拥有一个tdk数据']
);
$model->completeCreateField();
}
/**
* 更新前
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 导出Excel
*/
public static function exportExcel($select)
{
$data = [[
'tdk所属模块',
'网页标题',
'网页简介',
'网页关键词'
]];
foreach ($select as $key => $val) {
// 字典取值
$tdk_type = ModelDictionary::getDictionaryData('tdk_type');
$val['tdk_type'] = ModelDictionary::getDataDictionaryName($tdk_type, $val['tdk_type']);
$data[] = [
$val['tdk_type'],
$val['tdk_title'],
$val['tdk_description'],
$val['tdk_keyword'],
];
}
$excel = (new Excel())->exporTsheet($data);
$excel->save('网站tdk.xlsx');
}
/**
* 导入excel
*/
public static function importExcel($file)
{
$msg = [];
Db::startTrans();
try {
$excel = new Excel($file);
$data = $excel->parseExcel(
Tool::getExcelRule(self::EXCELFIELD),
[
'titleLine' => [1]
]
);
if (!$data) throwErrorMsg('excel无数据', 1);
$msg = [];
foreach ($data as $line => $value) {
try {
$model = self::importExcelInit($value);
$msg[] = "{$line} <span style='color:#27af49'>新增成功!</span><br>";
} catch (\Throwable $th) {
$msg[] = "{$line} <span style='color:red'>{$th->getMessage()}</span><br>";
}
}
Db::commit();
return implode(', ', $msg);
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 导入excel初始化
*/
public static function importExcelInit($value)
{
$tdk_type = $value['tdk_type'];
$tdk_title = $value['tdk_title'];
$tdk_description = $value['tdk_description'];
$tdk_keyword = $value['tdk_keyword'];
// 字典设值
$dic_tdk_type = ModelDictionary::getDictionaryData('tdk_type');
$tdk_type = ModelDictionary::getDataDictionaryValue($dic_tdk_type, $value['tdk_type']);
return self::create([
'tdk_type' => $tdk_type,
'tdk_title' => $tdk_title,
'tdk_description' => $tdk_description,
'tdk_keyword' => $tdk_keyword,
]);
}
}

View File

@ -0,0 +1,168 @@
<?php
namespace app\common\model\Test;
use app\common\arw\adjfut\src\Validate;
use app\BaseModel;
use think\model\concern\SoftDelete;
use app\common\arw\adjfut\src\Excel;
use app\Request;
use app\common\exception\Tool;
use think\facade\Db;
class Test extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = 'test_delete_time';
// 设置主键名
protected $pk = 'test_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'test_id' => 'int' ,
'test_guid' => 'string' ,
'test_name' => 'string' ,
'test_show_status' => 'string' ,
'test_score' => '' ,
'test_sort' => 'int' ,
'test_create_time' => 'datetime' ,
'test_create_user_guid' => 'string' ,
'test_update_time' => 'datetime' ,
'test_update_user_guid' => 'string' ,
'test_delete_time' => 'datetime' ,
'test_delete_user_guid' => 'string' ,
];
// 设置json类型字段
protected $json = [''];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'test_create_time';
// 修改时间
protected $updateTime = 'test_update_time';
// excel导入/下载模板表头
public const EXCELFIELD = [
'test_name' => '名称',
'test_show_status' => '首页是否展示',
'test_score' => '评分',
'test_sort' => '排序',
];
/**
* 新增前
*/
public static function onBeforeInsert(self $model): void
{
// self::checkRepeatData($model);
$model->completeCreateField();
}
/**
* 更新前
*/
public static function onBeforeUpdate(self $model): void
{
// self::checkRepeatData($model);
$model->completeUpdateField();
}
/**
* 删除前
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 导出Excel
*
* @param array $select 导出的数据
*/
public static function exportExcel(array $select): void
{
$data = [[
'名称',
'首页是否展示',
'评分',
'排序'
]];
foreach ($select as $key => $val) {
$data[] = [
$val['test_name'],
$val['test_show_status'],
$val['test_score'],
$val['test_sort'],
];
}
$excel = (new Excel())->exporTsheet($data);
$excel->save('测试.xlsx');
}
/**
* 导入excel
*
* @param \app\common\arw\adjfut\src\UploadFile $file excel
*/
public static function importExcel(\app\common\arw\adjfut\src\UploadFile $file): string
{
$msg = [];
Db::startTrans();
try {
$excel = new Excel($file);
$data = $excel->parseExcel(
Tool::getExcelRule(self::EXCELFIELD),
[
'titleLine' => [1]
]);
if (!$data) throwErrorMsg('excel无数据', 1);
$msg = [];
foreach ($data as $line => $value) {
try {
$model = self::importExcelInit($value);
$msg[] = "{$line} <span style='color:#27af49'>新增成功!</span><br>";
} catch (\Throwable $th) {
$msg[] = "{$line} <span style='color:red'>{$th->getMessage()}</span><br>";
}
}
Db::commit();
return implode(', ', $msg);
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
/**
* 导入excel初始化
*
* @param array $value excel每行数据
*/
public static function importExcelInit(array $value):void
{
$test_name = $value['test_name'];$test_show_status = $value['test_show_status'];$test_score = $value['test_score'];$test_sort = $value['test_sort'];
self::create(
['test_name' => $test_name,
'test_show_status' => $test_show_status,
'test_score' => $test_score,
'test_sort' => $test_sort,
],
['test_name','test_show_status','test_score','test_sort',]
);
}
}

337
app/common/model/Token.php Normal file
View File

@ -0,0 +1,337 @@
<?php
namespace app\common\model;
use app\BaseModel;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use app\common\exception\LoginTimeOut;
use app\common\model\User\User;
use think\facade\Request;
use think\helper\Arr;
use think\model\relation\HasOne;
/**
* Token
*
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
class Token extends BaseModel
{
// 设置主键名
protected $pk = 'token_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'token_id' => 'int',
'token_guid' => 'string',
'user_guid' => 'string',
'token_menu' => 'json',
'token_api' => 'json',
'token_exp_time' => 'datetime',
'token_content' => 'string',
'token_create_time' => 'datetime',
'token_update_time' => 'datetime',
];
// json数据字段
protected $json = ['token_menu', 'token_api'];
// 设置JSON数据返回数组
protected $jsonAssoc = true;
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'token_create_time';
// 修改时间
protected $updateTime = 'token_update_time';
/**
* 当前用户
*
* @var User
*/
private static $user = null;
/**
* 当前model
*
* @var self
*/
private static $current = null;
/**
* 用户
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function user(): hasOne
{
return $this->hasOne(User::class, 'user_guid', 'user_guid');
}
/**
* 设置过期时间
*
* @param mixed $value
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public function setTokenExpTimeAttr($value): string
{
return date('Y-m-d H:i:s', $value);
}
/**
* 判断是否过期
*
* @return boolean
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public function getIsExpAttr(): bool
{
return time() >= $this->token_exp_timestamp;
}
/**
* 获取过期时间戳
*
* @return integer
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public function getTokenExpTimestampAttr(): int
{
return strtotime($this->token_exp_time);
}
/**
* 新增前
*
* @param self $model
* @return void
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->token_content = $model->generateContent();
$model->token_guid = self::generateGuid();
}
/**
* 登陆
* 如果当前存在有效token 更新过期时间
* 如果token已过期 更新token和过期时间
* 如果不存在token 新增数据
*
* @param string $guid 用户id
* @param array $options 配置项
* @param int $options[expTime] 过期时间(时间戳) 不传默认两小时
* @param array $options[menu] 菜单
* @param int $options[api] 接口
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function login(string $guid, array $options = []): self
{
$expTime = 0;
$menu = [];
$api = [];
if (is_array($options)) {
$expTime = Arr::get($options, 'expTime', 0);
$menu = Arr::get($options, 'menu', []);
$api = Arr::get($options, 'api', []);
}
if ($expTime == 0) {
$expTime = time() + (24 * 60 * 60);
}
$data = [
'token_exp_time' => $expTime
];
if ($menu) {
$data['token_menu'] = $menu;
}
if ($api) {
$data['token_api'] = $api;
}
/**
* @var Token
*/
$model = self::where([
'user_guid' => $guid,
])->find();
// 数据不存在
if (!$model) {
return self::create($data + [
'user_guid' => $guid,
]);
}
// token已过期
if ($model->is_exp) {
$data['token_content'] = $model->generateContent($model);
}
$model->save($data);
return $model;
}
/**
* 登出
*
* @date 2022-03-15
* @example
* @author admin
* @since 1.0.0
*/
public static function logout(): void
{
self::getCurrent()->delete();
}
/**
* 获取请求token
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function getRequestToken(): string
{
$tokenKey = 'token';
$token = '';
if (!$token && isset($_GET[$tokenKey])) {
$token = $_GET[$tokenKey];
}
if (!$token && isset($_POST[$tokenKey])) {
$token = $_POST[$tokenKey];
}
if (!$token && Request::param($tokenKey)) {
$token = Request::param($tokenKey);
}
if (!$token && Request::header($tokenKey)) {
$token = Request::header($tokenKey);
}
return $token;
}
/**
* 获取当前用户信息
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function getCurrentUser(): User
{
if (!self::$user) {
$model = self::getCurrent();
self::$user = $model->user;
if (!self::$user) {
throw new ErrorMsg("用户不存在", 1);
}
}
return self::$user;
}
/**
* 获取当前有效token
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function getCurrent(): self
{
if (!self::$current) {
$content = self::getRequestToken();
if (!$content) {
throw new LoginTimeOut("缺少token", 1);
}
$model = self::where([
'token_content' => $content,
])->find();
if (!$model) {
throw new LoginTimeOut("未登录", 1);
}
if ($model->is_exp) {
throw new LoginTimeOut("登录超时", 1);
}
self::$current = $model;
}
return self::$current;
}
/**
* 判断是否登录
*
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
public static function isLogin(): bool
{
try {
self::getCurrent();
return true;
} catch (LoginTimeOut $th) {
return false;
}
}
/**
* 延长token
* 过期前30分钟才会延期
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function delay(): void
{
try {
// 半小时
$delay = (60 * 30);
$model = self::getCurrent();
$token_exp_time = $model->token_exp_timestamp;
if (time() >= ($token_exp_time - $delay)) {
self::login($model->user_guid);
}
} catch (LoginTimeOut $th) {
}
}
/**
* 生成token内容
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
private function generateContent(): string
{
return md5(join('-', [
$this->token_exp_time,
$this->user_guid,
]) . time());
}
}

View File

@ -0,0 +1,548 @@
<?php
namespace app\common\model\User;
use app\common\arw\adjfut\src\Validate;
use app\BaseModel;
use app\common\model\Menu\Menu;
use app\common\model\Role\Role;
use app\common\model\User\UserRole as ModelUserRole;
use app\common\model\Token;
use think\db\Query;
use think\facade\Db;
use think\facade\Config;
use think\facade\Request;
use think\model\concern\SoftDelete;
use think\model\relation\BelongsToMany;
use app\common\arw\adjfut\src\Traits\Dictionary;
use app\common\arw\adjfut\src\Excel;
class User extends BaseModel
{
use Dictionary;
use SoftDelete;
// 删除字段
protected $deleteTime = 'user_delete_time';
// 设置主键名
protected $pk = 'user_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'user_id' => 'int',
'user_guid' => 'string',
'user_img' => 'string',
'user_name' => 'string',
'user_password' => 'string',
'user_phone' => 'string',
'user_status' => 'int',
'user_department' => 'string',
'user_position' => 'string',
'user_create_time' => 'datetime',
'user_create_user_guid' => 'string',
'user_update_time' => 'datetime',
'user_update_user_guid' => 'string',
'user_delete_time' => 'datetime',
'user_delete_user_guid' => 'string',
];
// 只读
protected $readonly = [
'user_id',
'user_guid',
'user_create_time',
'user_create_user_guid'
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'user_create_time';
// 修改时间
protected $updateTime = 'user_update_time';
public static $dictionaryMap = [
'user_status' => [
self::STATUS_ENABLE => '启用',
self::STATUS_DISABLE => '停用'
],
];
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus(Query $query, $status = self::STATUS_ENABLE)
{
$query->where('user_status', $status);
}
/**
* 写入前
*
* @param self $model
* @return void
* @date 2023-01-11
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeWrite(self $model): void
{
self::checkRepeatData($model);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
/**
* 用户名搜索器
*
* @param Query $query
* @param [type] $value
* @param [type] $data
* @return void
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public function searchUserNameAttr(Query $query, $value, $data): void
{
if ($value) {
$query->whereLike(join('|', [
'user_name',
// 'user_phone'
]), "%$value%");
}
}
/**
* 设置用户密码
*
* @param string $value
* @return string
* @date 2022-12-28
* @example
* @author admin
* @since 1.0.0
*/
public function setUserPasswordAttr(string $value): string
{
return self::encryptPassword($value);
}
/**
* 获取用户管理员
*
* @date 2022-03-08
* @example
* @author admin
* @since 1.0.0
*/
public function getUserAdminAttr(): bool
{
return $this->user_id == 1;
}
/**
* 用户登录
*
* @return Token
* @date 2023-01-03
* @example
* @author admin
* @since 1.0.0
*/
public function login(): Token
{
if ($this->user_status != self::STATUS_ENABLE) {
throwErrorMsg('该用户已被停用');
}
$menus = $this->getUserMenu();
$api = [];
foreach ($menus as $menu) {
$api = array_merge($api, explode(',', $menu['menu_api_url']));
}
$api = array_values(array_filter(array_unique($api)));
if (!$api) {
throwErrorMsg('无权登录');
}
return Token::login($this->user_guid, [
'menu' => $menus,
'api' => $api,
]);
}
/**
* 获取用户菜单
*
* @date 2022-03-15
* @example
* @author admin
* @since 1.0.0
*/
public function getUserMenu(): array
{
$result = [];
$query = Menu::join('menu_api', 'menu_api.menu_guid = menu.menu_guid', 'left')->field([
'menu.menu_guid',
'menu.menu_parent_guid',
'menu.menu_name',
'menu.menu_url',
'menu.menu_show',
'menu.menu_icon',
'group_concat(menu_api.menu_api_url)' => 'menu_api_url'
])->order([
'menu.menu_index',
'menu.menu_order' => 'desc',
])->group('menu.menu_guid');
if ($this->user_admin) {
$result = $query->select()->toArray();
} else {
$menus = [];
$roles = $this->roles()->with([
'menus'
])->select();
foreach ($roles as $role) {
$menus = array_merge($menus, $role->menus->column('menu_guid'));
}
$result = $query->where([
['menu.menu_guid', 'in', $menus]
])->select()->toArray();
}
return $result;
}
/**
* 数据查重
*
* @param self $model
* @return void
* @date 2022-03-11
* @example
* @author admin
* @since 1.0.0
*/
private static function checkRepeatData(self $model)
{
Validate::unique(self::class, $model->user_guid, $model->getData(), [
'user_account' => '账户',
'user_phone' => '手机号',
]);
}
/**
* 密码加密
*
* @param string $password
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function encryptPassword(string $password): string
{
return md5($password);
}
/**
* 获取角色
*
* @date 2022-03-11
* @example
* @author admin
* @since 1.0.0
*/
public function roles(): BelongsToMany
{
return $this->belongsToMany(
Role::class,
UserRole::class,
'role_guid',
'user_guid'
);
}
/**
* 新增用户角色
*/
public static function addUserRole($user_guid, $params): void
{
$user_role_arr = $params['roles'];
foreach ($user_role_arr as $role => $item) {
Db::startTrans();
try {
$add_data = [
'user_guid' => $user_guid,
'role_guid' => $item,
];
$user_role = ModelUserRole::create($add_data);
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
}
/**
* 编辑用户角色
*/
public static function editUserRole($params): void
{
// 从学生服务(副表)查询出所有当前学生的服务,进行删除
ModelUserRole::where('user_guid', $params['user_guid'])->select()->delete();
// 再把传值的数据,写入副表
$user_role_arr = $params['roles'];
foreach ($user_role_arr as $key => $item) {
Db::startTrans();
try {
$add_data = [
'user_guid' => $params['user_guid'],
'role_guid' => $item,
];
ModelUserRole::create($add_data);
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
}
/**
* 用户角色guid获取器
*/
public function getRolesAttr($value, $data)
{
$user_guid = $data['user_guid'];
$user_role_guid_arr = ModelUserRole::field([
'role_guid',
])->where('user_guid', $user_guid)->select();
$arr = [];
foreach ($user_role_guid_arr as $key => $value) {
$arr[] = $value['role_guid'];
}
return $arr;
// return array_values($user_key_guid_arr);
}
/**
* 用户角色名称获取器
*/
public function getRoleNameAttr($value, $data)
{
$user_guid = $data['user_guid'];
$user_role_guid_arr = ModelUserRole::field([
'role_guid',
])->where('user_guid', $user_guid)->select();
$arr = [];
foreach ($user_role_guid_arr as $key => $value) {
$model = Role::where('role_guid', $value['role_guid'])->find()->role_name;
$arr[] = $model;
}
return implode(",", $arr);
// return array_values($user_key_guid_arr);
}
/**
* 用户角色名
*/
public static function getRoleName($data)
{
$user_guid = $data['user_guid'];
$user_role_guid_arr = ModelUserRole::field([
'role_guid',
])->where('user_guid', $user_guid)->select();
$arr = [];
foreach ($user_role_guid_arr as $key => $value) {
$model = Role::where('role_guid', $value['role_guid'])->find()->role_name;
$arr[] = $model;
}
return implode(",", $arr);
// return array_values($user_key_guid_arr);
}
/**
* 角色名称转角色guid数组
*/
public static function RoleNameToRoleGuidArr($params)
{
$roles = explode(",", $params['roles']);
$user_role_guid_arr = [];
foreach ($roles as $key => $item) {
$role = Role::where('role_name', $item)->find();
if (!$role) throwErrorMsg('角色不存在!');
$user_role_guid_arr[] = $role->role_guid;
}
return $user_role_guid_arr;
}
/**
* 导出Excel
*/
public static function exportExcel($select)
{
$data = [[
'用户名',
'头像',
'角色',
'手机号',
]];
foreach ($select as $key => $val) {
$val['user_img'] = Excel::ExportImgFiled($val['user_img']);
$user_role_name = self::getRoleName($val);
$data[] = [
$val['user_name'],
$val['user_img'],
$user_role_name,
$val['user_phone'],
];
}
$excel = (new Excel())->exporTsheet($data);
$excel->save('用户.xlsx');
}
/**
* 导入数据处理
*/
public static function HanleImportData($value)
{
// 判断用户是否存在
$user_name = $value['user_name'];
$user_type = self::where('user_name', $user_name)->find();
if ($user_type) throwErrorMsg("{$user_name} 用户已存在,导入失败!");
// 判断角色是否存在
$roles_arr = explode(",", $value['roles']);
foreach ($roles_arr as $key => $item) {
$role = Role::where('role_name', $item)->find();
if (!$role) throwErrorMsg("{$item} 角色不存在,导入失败!");
}
$password = md5($value['user_password']);
$model = self::create([
'user_name' => $value['user_name'],
'user_img' => $value['user_img'],
'user_phone' => $value['user_phone'],
'user_password' => $password,
'user_status' => 1,
]);
$user_guid = $model->user_guid;
$value['roles'] = self::RoleNameToRoleGuidArr($value);
self::addUserRole($user_guid, $value);
}
/**
* 导入Excel
*/
public static function importExcel($file)
{
Db::startTrans();
try {
$excel = new Excel($file);
// 表头
$data = $excel->parseExcel(
[
[
'title' => '用户名',
'validate' => 'require',
'field' => 'user_name',
], [
'title' => '头像',
'field' => 'user_img',
], [
'title' => '角色',
'validate' => 'require',
'field' => 'roles',
], [
'title' => '手机号',
'field' => 'user_phone',
], [
'title' => '密码',
'field' => 'user_password',
]
],
[
'titleLine' => [1]
]
);
if (!$data) throwErrorMsg('excel无数据', 1);
$error = [];
foreach ($data as $line => $value) {
try {
self::HanleImportData($value);
$error[] = "{$line} 用户:【{$value['user_name']}】<span style='color:#27af49'>新增成功!</span><br>";
} catch (\Throwable $th) {
$error[] = "{$line} 用户:【{$value['user_name']}】<span style='color:red'>{$th->getMessage()}</span><br>";
}
}
Db::commit();
return implode(', ', $error);
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
}

View File

@ -0,0 +1,157 @@
<?php
namespace app\common\model\User;
use app\BasePivot;
use app\common\model\Role\Role;
use think\model\concern\SoftDelete;
use app\common\arw\adjfut\src\Model\Pivot as ModelPivot;
class UserRole extends BasePivot
{
// use SoftDelete;
// 删除字段
protected $deleteTime = 'user_role_delete_time';
// 设置主键名
protected $pk = 'user_role_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
'user_role_id' => 'int',
'user_guid' => 'string',
'role_guid' => 'string',
'user_role_status' => 'int',
'user_role_create_time' => 'datetime',
'user_role_create_user_guid' => 'string',
'user_role_update_time' => 'datetime',
'user_role_update_user_guid' => 'string',
'user_role_delete_time' => 'datetime',
'user_role_delete_user_guid' => 'string',
'user_role_guid' => 'string',
];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = 'user_role_create_time';
// 修改时间
protected $updateTime = 'user_role_update_time';
/**
* 状态 启用
*/
const STATUS_ENABLE = 1;
/**
* 状态 禁用
*/
const STATUS_DISABLE = 2;
// 状态查询范围
public function scopeStatus($query, $status = self::STATUS_ENABLE)
{
$query->where('user_role_status', $status);
}
/**
* 中间关联
*
* @return ModelPivot
* @date 2022-12-30
* @example
* @author admin
* @since 1.0.0
*/
public static function pivot(): ModelPivot
{
return new ModelPivot(
self::class,
User::class,
Role::class,
true
);
}
/**
* 绑定用户角色
*
* @param $users
* @param $roles
* @param array $extra
* @return void
* @date 2022-12-29
* @example
* @author admin
* @since 1.0.0
*/
public static function bindUserRole($users, $roles, array $extra = []): void
{
self::pivot()->bind($users, $roles, $extra);
}
/**
* 解绑用户角色
*
* @param $users
* @param $roles
* @return void
* @date 2022-12-30
* @example
* @author admin
* @since 1.0.0
*/
public static function unbindUserRole($users, $roles): void
{
self::pivot()->unbind($users, $roles);
}
/**
* 绑定用户角色
*
* @param $users
* @param $roles
* @param array $extra
* @return void
* @date 2022-12-29
* @example
* @author admin
* @since 1.0.0
*/
public static function rebindUserRole($users, $roles, array $extra = []): void
{
self::pivot()->rebind($users, $roles, $extra);
}
/**
* 新增前
*
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeInsert(self $model): void
{
$model->user_role_status = self::STATUS_ENABLE;
$model->completeCreateField();
}
/**
* 更新前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeUpdate(self $model): void
{
$model->completeUpdateField();
}
/**
* 删除前
*
* @date 2022-02-28
* @example
* @author admin
* @since 1.0.0
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
}

110
app/common/traits/Auth.php Normal file
View File

@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace app\common\traits;
use app\common\model\Token;
use think\helper\Arr;
trait Auth
{
/**
* 请求
*
* @var \app\Request
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
private $request = null;
/**
* 忽略登录
*
* @var array
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
private $ignoreLogin = [];
/**
* 是否忽略登录
*
* @return boolean
*/
private function isIgnoreLogin(): bool
{
$request = $this->request;
$gc = $request->controller();
$ga = $request->action();
$ignoreLogin = Arr::get($this->ignoreLogin, $gc, []);
if (!is_array($ignoreLogin)) {
$ignoreLogin = [];
}
foreach ($ignoreLogin as $action) {
if ($action == '*' || $action == $ga) {
return true;
}
}
return false;
}
/**
* 验证用户权限
*
* @return boolean
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
private function validateUser(): bool
{
return $this->validateMenuApi(
Token::getCurrentUser()->getUserMenu()
);
}
/**
* 验证菜单接口
*
* @return boolean
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
private function validateMenuApi(array $menus): bool
{
$apis = [];
foreach ($menus as $menu) {
$apis = array_merge($apis, explode(',', $menu['menu_api_url'] ?? ''));
}
return $this->validateApi($apis);
}
/**
* 验证接口
*
* @param array $apis
* @return boolean
* @date 2022-05-14
* @example
* @author admin
* @since 1.0.0
*/
private function validateApi(array $apis): bool
{
$request = $this->request;
$apis = array_values(array_filter(array_unique($apis)));
$api = join('/', [
$request->controller(),
$request->action()
]);
return in_array($api, $apis);
}
}

254
app/common/traits/Model.php Normal file
View File

@ -0,0 +1,254 @@
<?php
declare(strict_types=1);
namespace app\common\traits;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use app\common\model\User\User;
use app\Request;
use think\Model as ThinkModel;
use app\common\arw\adjfut\src\Tool;
use app\common\arw\adjfut\src\Validate;
/**
* 模型层
* @mixin ThinkModel
*/
trait Model
{
private static $cacheUserGuid = '';
/**
* 设置用户guid
*
* @param string|bool $id
* @return void
* @date 2022-04-25
* @example
* @author admin
* @since 1.0.0
*/
public static function setUserGuid($id)
{
self::$cacheUserGuid = $id;
}
/**
* 完善添加字段
*
* @param string $user
* @param string $timeType
* @return void
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public function completeCreateField($user = 'user', string $timeType = 'datetime'): void
{
$this->completeBaseField('create', $user, $timeType);
}
/**
* 完善更新字段
*
* @param string $user
* @param string $timeType
* @return void
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public function completeUpdateField($user = 'user', string $timeType = 'datetime'): void
{
$this->completeBaseField('update', $user, $timeType);
}
/**
* 完善删除字段
*
* @param string $user
* @param string $timeType
* @return void
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
public function completeDeleteField($user = 'user', string $timeType = 'datetime'): void
{
$this->completeBaseField('delete', $user, $timeType);
}
/**
* 完善基础字段
*
* @param string $type create|update|delete
* @param User|string $user
* @param string $timeType datetime|timestamp
* @return void
* @date 2022-03-02
* @example
* @author admin
* @since 1.0.0
*/
public function completeBaseField($type, $user = 'user', string $timeType = 'datetime'): void
{
$table = $this->getTable();
$createFieldTime = "${table}_create_time";
$createFieldUserGuId = "${table}_create_user_guid";
$updateFieldTime = "${table}_update_time";
$updateFieldUserGuId = "${table}_update_user_guid";
$deleteFieldTime = "${table}_delete_time";
$deleteFieldUserGuId = "${table}_delete_user_guid";
$guidField = "${table}_guid";
$time = self::getCompleteBaseFieldTimeType($timeType);
$user_guid = self::getCompleteBaseFieldUser($user);
if (!$type) {
throw new ErrorMsg("时间类型异常", 1);
}
$this->$updateFieldTime = $time;
$this->$updateFieldUserGuId = $user_guid;
if ($type === 'create') {
$this->$createFieldTime = $time;
$this->$createFieldUserGuId = $user_guid;
$this->$guidField = self::generateGuid();
}
if ($type === 'delete') {
$this->$deleteFieldTime = $time;
$this->$deleteFieldUserGuId = $user_guid;
}
}
// /**
// * 设置不使用的全局查询范围更新
// * 支持 onBeforeUpdate|onAfterUpdate|onBeforeWrite|onAfterWrite 事件
// *
// * @return void
// * @date 2022-03-12
// * @example
// * @author admin
// * @since 1.0.0
// */
// public static function withoutGlobalScopeUpdate(array $scope, array $data)
// {
// $model = new static;
// $pk = $model->getPk();
// if (!isset($data[$pk])) {
// throw new ErrorMsg("缺少主键", 1);
// }
// $query = $model::withoutGlobalScope($scope)->find($data[$pk]);
// if ($query === null) {
// throw new ErrorMsg("数据不存在", 1);
// }
// if (
// $model::onBeforeWrite($model) === false ||
// $model::onBeforeUpdate($model) === false
// ) {
// return false;
// }
// $model::withoutGlobalScope($scope)->save(array_merge($data, $model->getData()));
// $model::onAfterUpdate($model);
// $model::onAfterWrite($model);
// }
/**
* 生成guid
*
* @param array|string $value
* @return string
* @date 2022-02-22
* @example
* @author admin
* @since 1.0.0
*/
protected static function generateGuid($value = '')
{
return Tool::generateGuid($value);
}
/**
* 数据验证
* 除非是require字段 有数据才验证
*
* @param array $data 数据
* @param array $rules 规则
* @param array $message 提示信息
* @return void
* @throws ValidateException
*/
protected static function dataValidate(array $data, array $rules, array $message = [])
{
$_rules = [];
$dataKeys = array_keys($data);
foreach ($rules as $key => $rule) {
list($field) = explode('|', $key);
// 字段存在规则并且有值 或者 必传
if ((in_array($field, $dataKeys) && !is_null($data[$field])) || preg_match('/require/', $rule)) {
$_rules[$key] = $rule;
}
}
Validate::check($data, $_rules, $message);
}
/**
* 获取完善基础字段用户
*
* @param User|string $user
* @return string
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
private static function getCompleteBaseFieldUser($user): string
{
$cacheUserGuid = self::$cacheUserGuid;
$user_guid = '';
switch (true) {
case (is_string($cacheUserGuid) && $cacheUserGuid):
$user_guid = $cacheUserGuid;
break;
case (is_bool($cacheUserGuid) && $cacheUserGuid === false):
// 无需验证 获取用户Guid
break;
case (is_bool($user) && $user === false):
// 无需验证 获取用户Guid
break;
case ($user === 'user'):
case ($user instanceof User):
$user = $user === 'user' ? Request::getCurrentUser() : $user;
$user_guid = $user->user_guid;
break;
}
return $user_guid;
}
/**
* 获取模型时间类型
*
* @param string $timeType datetime|timestamp
* @return string
* @date 2022-12-27
* @example
* @author admin
* @since 1.0.0
*/
private static function getCompleteBaseFieldTimeType($timeType): string
{
$time = '';
switch ($timeType) {
case 'datetime':
$time = date('Y-m-d H:i:s');
break;
case 'timestamp':
$time = time();
break;
}
return $time;
}
}

20
app/event.php Normal file
View File

@ -0,0 +1,20 @@
<?php
// 事件定义文件
use app\common\listener\DelayToken;
return [
'bind' => [],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [
DelayToken::class
],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [],
];

10
app/middleware.php Normal file
View File

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

9
app/provider.php Normal file
View File

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

View File

@ -0,0 +1,83 @@
<?php
namespace app\admin\controller\{$moduleName};
use app\BaseController;
use app\common\model\{$moduleName}\{$className} as Model{$className};
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use think\facade\Db;
use think\facade\Env;
class {$className} extends BaseController
{
/**
* 获取{$functionName}列表
*/
public function get{$className}List(Request $request, $isExport = false): array
{
$params = $request->param();
$con = [];
{$whereContent}
$query = Model{$className}::where($con)
->field({$queryFields})
->order('{$orderField}', '{$orderMode}');
return $isExport ? $query->select()->toArray() : msg("获取{$functionName}列表成功!",$query);
}
/**
* 添加{$functionName}
*/
public function add{$className}(Request $request): array
{
$params = $request->param();
$this->validate($params, {$addRequireFields});
$model = Model{$className}::create($params,{$addAllowFields});
return msg('添加成功!');
}
/**
* 编辑{$functionName}
*/
public function edit{$className}(Request $request): array
{
$params = $request->param();
$this->validate($params, {$editRequireFields});
$model = Model{$className}::where('{$businessName}_guid',$params['{$businessName}_guid'])->find();
if (!$model) throwErrorMsg("该{$functionName}不存在", 1);
$model->allowField({$editAllowFields})->save($params);
return msg('编辑成功!');
}
/**
* 删除{$functionName}
*/
public function delete{$className}(Request $request): array
{
$params = $request->param();
$this->validate($params, [
'{$businessName}_guid' => 'require',
]);
${$businessName} = Model{$className}::where([
'{$businessName}_guid' => explode(',', $params['{$businessName}_guid'])
])->select();
${$businessName}->delete();
return msg('删除成功!');
}
{$exportExcelContent}
{$downloadTempContent}
{$importExcelContent}
}

View File

@ -0,0 +1,70 @@
<?php
namespace app\common\model\{$moduleName};
use app\common\arw\adjfut\src\Validate;
use app\BaseModel;
use think\model\concern\SoftDelete;
use app\common\arw\adjfut\src\Excel;
use app\Request;
use app\common\exception\Tool;
use think\facade\Db;
class {$className} extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = '{$businessName}_delete_time';
// 设置主键名
protected $pk = '{$businessName}_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
{$fields}
];
// 设置json类型字段
protected $json = [''];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = '{$businessName}_create_time';
// 修改时间
protected $updateTime = '{$businessName}_update_time';
{$importExcelField}
/**
* 新增前
*/
public static function onBeforeInsert(self $model): void
{
// self::checkRepeatData($model);
$model->completeCreateField();
}
/**
* 更新前
*/
public static function onBeforeUpdate(self $model): void
{
// self::checkRepeatData($model);
$model->completeUpdateField();
}
/**
* 删除前
*/
public static function onBeforeDelete(self $model): void
{
$model->completeDeleteField();
}
{$exportExcelContent}
{$importExcelContent}
{$importExcelInitContent}
}

View File

@ -0,0 +1,57 @@
<?php
namespace app\api\controller\{$moduleName};
use app\BaseController;
use app\common\model\{$moduleName}\{$className} as Model{$className};
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use think\facade\Db;
use app\common\exception\Tool;
use think\facade\Env;
class {$className} extends BaseController
{
/**
* 获取{$functionName}列表
*/
public function get{$className}List(Request $request): array
{
$params = $request->param();
$con = [];
{$whereContent}
$query = Model{$className}::where($con)
->field({$queryFields})
->order('{$orderField}', '{$orderMode}')
->select();
return msg(0, "获取{$functionName}列表成功!", [
'data' => $query,
'count' => count($query)
]);
}
/**
* 获取{$functionName}详情
*/
public function get{$className}Info(Request $request): array
{
$params = $request->param();
$this->validate($params, ['{$businessName}_guid' => 'require']);
$find = Model{$className}::field({$queryFields})
->where('{$businessName}_guid',$params['{$businessName}_guid'])
->find();
return msg(0,'获取{$functionName}详情成功!',['data' => $find]);
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace app\admin\controller\{$moduleName};
use app\BaseController;
use app\common\model\{$moduleName}\{$className} as Model{$className};
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\arw\adjfut\src\Excel;
use app\common\arw\adjfut\src\UploadFile;
use app\common\exception\Tool;
use think\facade\Db;
use think\facade\Env;
class {$className} extends BaseController
{
/**
* 获取{$functionName}列表
*/
public function get{$className}List(Request $request, $isExport = false): array
{
$params = $request->param();
$con = [];
{$whereContent}
$query = Model{$className}::where($con)
->field({$queryFields})
->order('{$orderField}', '{$orderMode}');
return $isExport ? $query->select()->toArray() : msg("获取{$functionName}列表成功!",$query);
}
/**
* 添加{$functionName}
*/
public function add{$className}(Request $request): array
{
Db::startTrans();
Tool::adminLockTableWrite('{$businessName}');
try {
$params = $request->param();
$this->validate($params, {$addRequireFields});
$model = Model{$className}::create($params,{$addAllowFields});
Db::commit();
Tool::unlockTable();
return msg('添加成功!');
} catch (\Throwable $th) {
Db::rollback();
Tool::unlockTable();
throw $th;
}
}
/**
* 编辑{$functionName}
*/
public function edit{$className}(Request $request): array
{
Db::startTrans();
Tool::adminLockTableWrite('{$businessName}');
try {
$params = $request->param();
$this->validate($params, {$editRequireFields});
$model = Model{$className}::where('{$businessName}_guid',$params['{$businessName}_guid'])->find();
if (!$model) throwErrorMsg("该{$functionName}不存在", 1);
$model->allowField({$editAllowFields})->save($params);
Db::commit();
Tool::unlockTable();
return msg('编辑成功!');
} catch (\Throwable $th) {
Db::rollback();
Tool::unlockTable();
throw $th;
}
}
/**
* 删除{$functionName}
*/
public function delete{$className}(Request $request): array
{
Db::startTrans();
Tool::adminLockTableWrite('{$businessName}');
try {
$params = $request->param();
$this->validate($params, [
'{$businessName}_guid' => 'require',
]);
${$businessName} = Model{$className}::where([
'{$businessName}_guid' => explode(',', $params['{$businessName}_guid'])
])->select();
${$businessName}->delete();
Db::commit();
Tool::unlockTable();
return msg('删除成功!');
} catch (\Throwable $th) {
Db::rollback();
Tool::unlockTable();
throw $th;
}
}
{$exportExcelContent}
{$downloadTempContent}
{$importExcelContent}
}

View File

@ -0,0 +1,74 @@
<?php
namespace app\common\model\{$moduleName};
use app\common\arw\adjfut\src\Validate;
use app\BaseModel;
use think\model\concern\SoftDelete;
use app\common\arw\adjfut\src\Excel;
use app\Request;
use app\common\exception\Tool;
use think\facade\Db;
class {$className} extends BaseModel
{
use SoftDelete;
// 删除字段
protected $deleteTime = '{$businessName}_delete_time';
// 设置主键名
protected $pk = '{$businessName}_guid';
// 设置废弃字段
protected $disuse = [];
// 设置字段信息
protected $schema = [
{$fields}
];
// 设置json类型字段
protected $json = [''];
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'datetime';
// 创建时间
protected $createTime = '{$businessName}_create_time';
// 修改时间
protected $updateTime = '{$businessName}_update_time';
//排序字段
public $order_field = '{$orderField}';
{$importExcelField}
/**
* 新增前
*/
public static function onBeforeInsert(self $model): void
{
Tool::dataAddSortProc($model);
$model->completeCreateField();
}
/**
* 更新前
*/
public static function onBeforeUpdate(self $model): void
{
Tool::dataEditSortProc($model);
$model->completeUpdateField();
}
/**
* 删除前
*/
public static function onBeforeDelete(self $model): void
{
Tool::dataDeleteSortProc($model);
$model->completeDeleteField();
}
{$exportExcelContent}
{$importExcelContent}
{$importExcelInitContent}
}

View File

@ -0,0 +1,23 @@
import { api} from '~/utils/axios';
/**
* 获取${functionName}内容
* @param {Object} data
* @return {Promise} api
*/
export function get${className}(data) {
return api.post('${moduleName}.${className}/get${className}', data);
}
/**
* 编辑${functionName}
* @param {Object} data
* @return {Promise} api
*/
export function edit${className}(data) {
return api.post('${moduleName}.${className}/edit${className}', data, {
isTransformResponse: true,
isShowSuccessMessage: true,
errorMessageText: '编辑失败'
});
}

View File

@ -0,0 +1,32 @@
<?php
namespace app\api\controller\{$moduleName};
use app\BaseController;
use app\common\model\{$moduleName}\{$className} as Model{$className};
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\exception\Tool;
class {$className} extends BaseController
{
/**
* 获取{$functionName}内容
*/
public function get{$className}(Request $request): array
{
$params = $request->param();
$query = Model{$className}::field({$queryFields})->where(1)->find();
return [
'code' => 0,
'data' => $query,
'msg' => 'ok'
];
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace app\admin\controller\{$moduleName};
use app\BaseController;
use app\common\model\{$moduleName}\{$className} as Model{$className};
use app\Request;
use think\Validate;
use think\exception\ValidateException;
use think\facade\Filesystem;
use app\common\exception\Tool;
class {$className} extends BaseController
{
/**
* 获取{$functionName}内容
*/
public function get{$className}(Request $request): array
{
$params = $request->param();
$query = Model{$className}::field({$queryFields})->where(1)->find();
return [
'code' => 0,
'data' => $query,
'msg' => 'ok'
];
}
/**
* 编辑{$functionName}
*/
public function edit{$className}(Request $request): array
{
$params = $request->param();
$this->validate($params, {$editRequireFields});
$model = Model{$className}::where(1)->find();
$model->allowField({$editAllowFields})->save($params);
return msg('编辑成功!');
}
}

View File

@ -0,0 +1,90 @@
<template>
<!-- 面包屑 -->
<el-breadcrumb>
<el-breadcrumb-item>${functionName}</el-breadcrumb-item>
<el-breadcrumb-item to="/${className}'">${functionName}</el-breadcrumb-item>
</el-breadcrumb>
<el-form ref="formRef" :model="formData" :rules="rules">
<el-row>
${col}
</el-row>
<el-row>
<el-button type="primary" class="sus_button" @click="save(formRef)">
保存
</el-button>
</el-row>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { get${className} , edit${className} } from "~/service/${businessName}";
import { useLoginStore } from "~/store";
const store = useLoginStore();
const headers = {
Accept: "application/json",
...store.headers,
};
let formData = ref({});
${mapParm}
const uoloadData = ref({
dirName: "${className}"
})
//验证规则
const rules = reactive({
${rules}
});
const labelWidth = 200;
const formRef = ref();
//获取
const getContent = async () => {
const { code, data } = await get${className}();
if (code == 0) {
formData.value = data;
${mapOpen}
}
};
${mapOpenFun}
//保存
const save = async (formEl) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) {
return;
}
${mapFun}
const { code } = await edit${className}(formData.value);
});
};
</script>
<style lang="scss" scoped>
.sus_button {
width: 300px;
height: 50px;
margin: 20px auto;
}
.upLoadEl{
:deep(.el-upload-list__item){
height: 200px;
width: 300px;
}
:deep(.el-upload-list__item-thumbnail){
object-fit: contain;
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<el-dialog v-model="dialogVisible" title="添加${functionName}" width="900px" @closed="closeDialog" @open="openDialog">
<el-form ref="formRef" :model="formData" :rules="rules">
<el-row>
${col}
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="handleAddClick(formRef)">添加</el-button>
<el-button @click="handleResetClick(formRef)">重置</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
import { add${className} ${dictFunName} } from "~/service/${businessName}";
import { useLoginStore } from "~/store";
// --业务参数
${mapParm}
// --业务方法
${dictFun}
// --基础参数
const store = useLoginStore();
const headers = {
Accept: "application/json",
...store.headers,
};
const isBtnLod = ref(false);
const formRef = ref();
const labelWidth = 90;
const props = defineProps({
modelValue: Boolean,
done: Function,
});
const emits = defineEmits(["update:modelValue"]);
const dialogVisible = ref(props.modelValue);
const formData = reactive({});
const uoloadData = ref({
dirName: "${className}"
})
watch(props, (v) => {
dialogVisible.value = v.modelValue;
});
const rules = reactive({
${rules}
});
// --基础方法
// 打开弹窗时执行
const openDialog = () => {
${dictFunName2}
};
const closeDialog = () => {
handleResetClick(formRef.value);
dialogVisible.value = false;
emits("update:modelValue", false);
};
const handleAddClick = async (formEl) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) {
return;
}
isBtnLod.value = true;
${mapFun}
const { code } = await add${className}(formData);
if (code == 0) {
closeDialog();
props.done();
}
isBtnLod.value = flase;
});
};
const handleResetClick = async (formEl) => {
if (!formEl) return;
formEl.resetFields();
};
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,56 @@
import { api, downloadFile, createApiUrl} from '~/utils/axios';
${excelFun}
${imageFun}
${fileFun}
${dictFun}
/**
* 获取${functionName}列表
* @param {Object} data
* @return {Promise} api
*/
export function get${className}List(data) {
return api.post('${moduleName}.${className}/get${className}List', data);
}
/**
* 删除${functionName}
* @param {Object} data
* @return {Promise} api
*/
export function delete${className}(data) {
return api.post('${moduleName}.${className}/delete${className}', data, {
isTransformResponse: true,
isShowSuccessMessage: true,
errorMessageText: '删除失败'
});
}
/**
* 添加${functionName}
* @param {Object} data
* @return {Promise} api
*/
export function add${className}(data) {
return api.post('${moduleName}.${className}/add${className}', data, {
isTransformResponse: true,
isShowSuccessMessage: true,
errorMessageText: '添加失败'
});
}
/**
* 编辑${functionName}
* @param {Object} data
* @return {Promise} api
*/
export function edit${className}(data) {
return api.post('${moduleName}.${className}/edit${className}', data, {
isTransformResponse: true,
isShowSuccessMessage: true,
errorMessageText: '编辑失败'
});
}

View File

@ -0,0 +1,62 @@
<template>
<el-dialog v-model="props.modelValue" title="${functionName}详情" width="900px" @closed="closeDialog" @open="openDialog">
<el-form ref="formRef" :model="formData" :disabled="true">
<el-row>
${col}
</el-row>
</el-form>
</el-dialog>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
import { isEmptyObject } from "~/utils/index";
${dictFunName}
// --业务参数
${mapParm}
// --业务方法
${dictFun}
// --基础参数
const formRef = ref();
const labelWidth = 100;
const props = defineProps({
modelValue: Boolean,
data: Object,
done: Function,
});
const emits = defineEmits(["update:modelValue"]);
const formData = ref({
...props.data,
});
// --基础方法
watch(props, (v) => {
formData.value = v.data;
${mapOpen}
});
// 打开弹窗时执行
const openDialog = () => {
${mapOpen}
${dictFunName2}
};
const closeDialog = () => {
emits("update:modelValue", false);
};
</script>
<style lang="less" scoped>
</style>

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