mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-11-09 20:40:49 +08:00
增加扩展机制定位文件;将common模块实现扩展模式;发布新版本;
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
use app\commno\exception\EventException;
|
use app\commno\exception\EventException;
|
||||||
use app\common\service\AuthService;
|
use app\common\service\AuthService;
|
||||||
use think\exception\HttpResponseException;
|
use think\exception\HttpResponseException;
|
||||||
|
use think\facade\App;
|
||||||
use think\facade\Cache;
|
use think\facade\Cache;
|
||||||
use think\facade\Env;
|
use think\facade\Env;
|
||||||
use think\facade\Event;
|
use think\facade\Event;
|
||||||
@@ -306,3 +307,19 @@ function event_response($name, $params = [])
|
|||||||
|
|
||||||
throw new HttpResponseException($response);
|
throw new HttpResponseException($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以扩展的架构定位app下的文件位置
|
||||||
|
*
|
||||||
|
* @param string $file_path 文件路径,不要以/开头,不需要以app开头,会自动定位app或extend/base。
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function app_file_path($file_path)
|
||||||
|
{
|
||||||
|
$app_file_path = App::getRootPath() . 'app' . DIRECTORY_SEPARATOR . $file_path;
|
||||||
|
if (!is_file($app_file_path)) {
|
||||||
|
$app_file_path = App::getRootPath() . 'extend' . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . $file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $app_file_path;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,146 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\command;
|
namespace app\common\command;
|
||||||
|
|
||||||
|
use base\common\command\CurdBase;
|
||||||
|
|
||||||
use app\admin\service\curd\BuildCurdService;
|
class Curd extends CurdBase
|
||||||
use app\common\tools\PathTools;
|
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\Exception;
|
|
||||||
use think\facade\App;
|
|
||||||
|
|
||||||
class Curd extends Command
|
|
||||||
{
|
{
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->setName('curd')
|
|
||||||
->addOption('table', 't', Option::VALUE_REQUIRED, '主表名', null)
|
|
||||||
->addOption('controllerFilename', 'c', Option::VALUE_REQUIRED, '控制器文件名', null)
|
|
||||||
->addOption('modelFilename', 'm', Option::VALUE_REQUIRED, '主表模型文件名', null)
|
|
||||||
#
|
|
||||||
->addOption('force', 'f', Option::VALUE_NONE, '强制覆盖模式')
|
|
||||||
->addOption('delete', 'd', Option::VALUE_NONE, '删除模式')
|
|
||||||
->addOption('runtime', 'r', Option::VALUE_NONE, '临时生成')
|
|
||||||
->setDescription('一键curd命令服务');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
|
|
||||||
$table = $input->getOption('table');
|
|
||||||
$controllerFilename = $input->getOption('controllerFilename');
|
|
||||||
$modelFilename = $input->getOption('modelFilename');
|
|
||||||
|
|
||||||
$force = 0;
|
|
||||||
$delete = 0;
|
|
||||||
|
|
||||||
if ($input->hasOption('force')) {
|
|
||||||
$force = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($input->hasOption('delete')) {
|
|
||||||
$delete = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($table)) {
|
|
||||||
$output->error('请设置主表');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$build = (new BuildCurdService())
|
|
||||||
->setTable($table)
|
|
||||||
->setForce($force);
|
|
||||||
|
|
||||||
if ($input->hasOption('runtime')) {
|
|
||||||
|
|
||||||
$runtime_path = App::getRuntimePath() . 'source/build/' . date('YmdHis') . '/';
|
|
||||||
dump($runtime_path);
|
|
||||||
PathTools::intiDir($runtime_path . 'a.temp');
|
|
||||||
|
|
||||||
$build->setRootDir($runtime_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$columns = $build->getTableColumns();
|
|
||||||
|
|
||||||
$relations = [];
|
|
||||||
|
|
||||||
foreach ($columns as $field => $column) {
|
|
||||||
|
|
||||||
if (isset($column['formType']) && $column['formType'] == 'relation') {
|
|
||||||
$define = $column['define'];
|
|
||||||
|
|
||||||
if (!isset($define['table'])) {
|
|
||||||
$output->error("关联字段{$field}没有设置关联表名称");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$relations[] = [
|
|
||||||
'table' => $define['table'],
|
|
||||||
'foreignKey' => $field,
|
|
||||||
'primaryKey' => $define['primaryKey'] ?? null,
|
|
||||||
'modelFilename' => $define['modelFilename'] ?? null,
|
|
||||||
'onlyFileds' => isset($define['onlyFileds']) ? explode("|", $define['onlyFileds']) : [],
|
|
||||||
'relationBindSelect' => $define['relationBindSelect'] ?? null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
!empty($controllerFilename) && $build = $build->setControllerFilename($controllerFilename);
|
|
||||||
!empty($modelFilename) && $build = $build->setModelFilename($modelFilename);
|
|
||||||
|
|
||||||
|
|
||||||
foreach ($relations as $relation) {
|
|
||||||
$build = $build->setRelation($relation['table'], $relation['foreignKey'], $relation['primaryKey'], $relation['modelFilename'], $relation['onlyFileds'], $relation['relationBindSelect']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$build = $build->render();
|
|
||||||
$fileList = $build->getFileList();
|
|
||||||
|
|
||||||
if (!$delete) {
|
|
||||||
if ($force) {
|
|
||||||
$output->writeln(">>>>>>>>>>>>>>>");
|
|
||||||
foreach ($fileList as $key => $val) {
|
|
||||||
$output->writeln($key);
|
|
||||||
}
|
|
||||||
$output->writeln(">>>>>>>>>>>>>>>");
|
|
||||||
|
|
||||||
$ask_force_delete_result = $output->confirm($input,'确定强制生成上方所有文件? 如果文件存在会直接覆盖。');
|
|
||||||
|
|
||||||
if (!$ask_force_delete_result) {
|
|
||||||
throw new Exception("取消文件CURD生成操作");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$result = $build->create();
|
|
||||||
$output->info('自动生成CURD成功');
|
|
||||||
} else {
|
|
||||||
$output->writeln(">>>>>>>>>>>>>>>");
|
|
||||||
foreach ($fileList as $key => $val) {
|
|
||||||
$output->writeln($key);
|
|
||||||
}
|
|
||||||
$output->writeln(">>>>>>>>>>>>>>>");
|
|
||||||
|
|
||||||
$ask_force_delete_result = $output->confirm($input,'确定删除上方所有文件? ');
|
|
||||||
|
|
||||||
if (!$ask_force_delete_result) {
|
|
||||||
throw new Exception("取消删除文件操作");
|
|
||||||
}
|
|
||||||
$result = $build->delete();
|
|
||||||
$output->info('>>>>>>>>>>>>>>>');
|
|
||||||
$output->info('删除自动生成CURD文件成功');
|
|
||||||
}
|
|
||||||
$output->info('>>>>>>>>>>>>>>>');
|
|
||||||
foreach ($result as $vo) {
|
|
||||||
$output->info($vo);
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$output->error($e->getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\command;
|
namespace app\common\command;
|
||||||
|
|
||||||
|
use base\common\command\NodeBase;
|
||||||
|
|
||||||
use app\admin\model\SystemNode;
|
class Node extends NodeBase
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use app\admin\service\NodeService;
|
|
||||||
|
|
||||||
class Node extends Command
|
|
||||||
{
|
{
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->setName('node')
|
|
||||||
->addOption('force', null, Option::VALUE_REQUIRED, '是否强制刷新', 0)
|
|
||||||
->setDescription('系统节点刷新服务');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
$force = $input->getOption('force');
|
|
||||||
$output->writeln("========正在刷新节点服务:=====" . date('Y-m-d H:i:s'));
|
|
||||||
$check = $this->refresh($force);
|
|
||||||
$check !== true && $output->writeln("节点刷新失败:" . $check);
|
|
||||||
$output->writeln("刷新完成:" . date('Y-m-d H:i:s'));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function refresh($force)
|
|
||||||
{
|
|
||||||
$nodeList = (new NodeService())->getNodelist();
|
|
||||||
if (empty($nodeList)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$model = new SystemNode();
|
|
||||||
try {
|
|
||||||
if ($force == 1) {
|
|
||||||
$updateNodeList = $model->whereIn('node', array_column($nodeList, 'node'))->select();
|
|
||||||
$formatNodeList = array_format_key($nodeList, 'node');
|
|
||||||
foreach ($updateNodeList as $vo) {
|
|
||||||
isset($formatNodeList[$vo['node']]) && $model->where('id', $vo['id'])->update([
|
|
||||||
'title' => $formatNodeList[$vo['node']]['title'],
|
|
||||||
'is_auth' => $formatNodeList[$vo['node']]['is_auth'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$existNodeList = $model->field('node,title,type,is_auth')->select();
|
|
||||||
foreach ($nodeList as $key => $vo) {
|
|
||||||
foreach ($existNodeList as $v) {
|
|
||||||
if ($vo['node'] == $v->node) {
|
|
||||||
unset($nodeList[$key]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$model->insertAll($nodeList);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\command;
|
namespace app\common\command;
|
||||||
|
|
||||||
use app\common\service\UploadService;
|
use base\common\command\OssStaticBase;
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\facade\Filesystem;
|
|
||||||
use think\File;
|
|
||||||
|
|
||||||
class OssStatic extends Command
|
class OssStatic extends OssStaticBase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->setName('OssStatic')
|
|
||||||
->setDescription('将静态资源上传到oss上');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
$output->writeln("========正在上传静态资源到OSS上:========" . date('Y-m-d H:i:s'));
|
|
||||||
|
|
||||||
$list = Filesystem::disk('local_static')->listContents('/', true);
|
|
||||||
$upload_service = new UploadService();
|
|
||||||
$uploadPrefix = config('app.oss_static_prefix', 'oss_static_prefix');
|
|
||||||
|
|
||||||
foreach ($list as $file_item) {
|
|
||||||
|
|
||||||
if ($file_item['type'] != 'file') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$file_path = $file_item['path'];
|
|
||||||
|
|
||||||
$file_path = Filesystem::disk('local_static')->path($file_path);
|
|
||||||
|
|
||||||
$file = new File($file_path, false);
|
|
||||||
|
|
||||||
$save_name = $file_item['path'];
|
|
||||||
try {
|
|
||||||
$model_file = $upload_service->save($file, $save_name, true, $uploadPrefix, true);
|
|
||||||
$output->info('文件上传成功:' . $save_name . '。上传地址:' . $model_file['url']);
|
|
||||||
} catch (\Throwable $th) {
|
|
||||||
$output->error('文件上传失败:' . $save_name . '。错误信息:' . $th->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$output->writeln("========已完成静态资源上传到OSS上:========" . date('Y-m-d H:i:s'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,130 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command;
|
namespace app\common\command;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use base\common\command\TimerBase;
|
||||||
use GuzzleHttp\Promise\Utils;
|
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Argument;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\facade\Cache;
|
|
||||||
use think\facade\Log;
|
|
||||||
|
|
||||||
class Timer extends Command
|
class Timer extends TimerBase
|
||||||
{
|
{
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('timer')
|
|
||||||
->addOption('temp', null, Option::VALUE_NONE)
|
|
||||||
->addOption('quit', null, Option::VALUE_NONE)
|
|
||||||
->setDescription('内置秒级定时器');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->writeln('start timer');
|
|
||||||
|
|
||||||
|
|
||||||
$site_domain = sysconfig('site', 'site_domain');
|
|
||||||
|
|
||||||
if (empty($site_domain)) {
|
|
||||||
$output->writeln('请前往后台设置站点域名(site_domain)配置项');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$output->writeln('站点域名:'.$site_domain);
|
|
||||||
|
|
||||||
$client = new Client([
|
|
||||||
'base_uri' => $site_domain,
|
|
||||||
'verify' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
|
|
||||||
$config_list = include __DIR__ . '/timer/config.php';
|
|
||||||
|
|
||||||
$list_promises = [];
|
|
||||||
foreach ($config_list as $config_item) {
|
|
||||||
|
|
||||||
$config_item = static::initConfigItem($config_item);
|
|
||||||
|
|
||||||
|
|
||||||
$name = $config_item['name'];
|
|
||||||
|
|
||||||
if ($name == 'http_demo' && !env('adminsystem.is_demo', false)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cache_key = 'timer_' . $name;
|
|
||||||
$cache_tag = 'system_timer';
|
|
||||||
|
|
||||||
$last_exec_time = Cache::get($cache_key, 0);
|
|
||||||
|
|
||||||
if ($last_exec_time >= time() - $config_item['frequency']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::tag($cache_tag)->set($cache_key, time());
|
|
||||||
|
|
||||||
$type = $config_item['type'];
|
|
||||||
|
|
||||||
switch ($type) {
|
|
||||||
case 'site':
|
|
||||||
$output->writeln(date('Y-m-d H:i:s') . ': build site request async:' . $config_item['target']);
|
|
||||||
$list_promises[$config_item['name']] = $client->getAsync($config_item['target']);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$output->writeln(date('Y-m-d H:i:s') . 'unsupport type:' . $type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($list_promises)) {
|
|
||||||
if(!$input->hasOption('quit')){
|
|
||||||
$output->writeln(date('Y-m-d H:i:s') . ' no request');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$results = Utils::unwrap($list_promises);
|
|
||||||
$output->writeln(date('Y-m-d H:i:s') . ': request all finished');
|
|
||||||
}
|
|
||||||
} catch (\Throwable $th) {
|
|
||||||
// throw $th;
|
|
||||||
$output->writeln('error:' . $th->getMessage());
|
|
||||||
Log::error($th->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ($input->hasOption('temp')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function initConfigItem($config)
|
|
||||||
{
|
|
||||||
$default = [
|
|
||||||
'name' => 'http_demo',
|
|
||||||
'type' => 'site',
|
|
||||||
'target' => '',
|
|
||||||
'frequency' => 600
|
|
||||||
];
|
|
||||||
|
|
||||||
$data = array_merge($default, $config);
|
|
||||||
|
|
||||||
if ($data['frequency'] < 0) {
|
|
||||||
$data['frequency'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,49 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command\admin;
|
namespace app\common\command\admin;
|
||||||
|
|
||||||
use think\console\Command;
|
use base\common\command\admin\ClearBase;
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Argument;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\facade\App;
|
|
||||||
|
|
||||||
class Clear extends Command
|
class Clear extends ClearBase
|
||||||
{
|
{
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('admin:clear')
|
|
||||||
->setDescription('删除开发临时生成目录');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->writeln('删除测试目录');
|
|
||||||
|
|
||||||
|
|
||||||
$command_line = '';
|
|
||||||
|
|
||||||
$dir = App::getRootPath() . '/runtime/source/';
|
|
||||||
|
|
||||||
if (!is_dir($dir)) {
|
|
||||||
$output->writeln('删除成功');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos(strtolower(PHP_OS), 'win') === 0) {
|
|
||||||
$command_line = implode(' ', ['rd', '/s', '/q', str_replace('/', '\\', $dir)]);
|
|
||||||
} else {
|
|
||||||
$command_line = implode(' ', ['rm', '-rf', $dir]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$output->info('删除目录:' . $command_line);
|
|
||||||
|
|
||||||
$output->info('run command: ' . $command_line);
|
|
||||||
|
|
||||||
exec($command_line);
|
|
||||||
|
|
||||||
$output->info('删除成功');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,47 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command\admin;
|
namespace app\common\command\admin;
|
||||||
|
|
||||||
use app\admin\model\SystemAdmin;
|
use base\common\command\admin\ResetPasswordBase;
|
||||||
use app\common\constants\AdminConstant;
|
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Argument;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
|
|
||||||
class ResetPassword extends Command
|
class ResetPassword extends ResetPasswordBase
|
||||||
{
|
{
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('admin:reset:password')
|
|
||||||
->addOption('password','p', Option::VALUE_OPTIONAL)
|
|
||||||
->setDescription('重置超管密码');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->writeln('admin:reset:password');
|
|
||||||
|
|
||||||
|
|
||||||
$model_admin = SystemAdmin::find(AdminConstant::SUPER_ADMIN_ID);
|
|
||||||
if (empty($model_admin)) {
|
|
||||||
$output->writeln('管理员不存在');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$password = $input->getOption('password');
|
|
||||||
|
|
||||||
if(is_null($password)){
|
|
||||||
$password = uniqid();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$model_admin->save([
|
|
||||||
'password' => password($password)
|
|
||||||
]);
|
|
||||||
|
|
||||||
$output->writeln('密码修改为:' . $password);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,309 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command\admin;
|
namespace app\common\command\admin;
|
||||||
|
|
||||||
use app\common\tools\PathTools;
|
use base\common\command\admin\UpdateBase;
|
||||||
use CzProject\GitPhp\Git;
|
|
||||||
use League\Flysystem\Filesystem;
|
|
||||||
use League\Flysystem\Local\LocalFilesystemAdapter;
|
|
||||||
use League\Flysystem\StorageAttributes;
|
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\facade\App;
|
|
||||||
use think\facade\Env;
|
|
||||||
|
|
||||||
class Update extends Command
|
class Update extends UpdateBase
|
||||||
{
|
{
|
||||||
public const REPO = 'https://gitee.com/ulthon/ulthon_admin.git';
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('admin:update')
|
|
||||||
->addOption('reinstall', null, Option::VALUE_NONE, '重装版本')
|
|
||||||
->setDescription('the admin:update command');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->writeln('admin:update');
|
|
||||||
|
|
||||||
$this->cleanWorkpaceDir();
|
|
||||||
|
|
||||||
$current_version = Version::VERSION;
|
|
||||||
|
|
||||||
$current_version_dir = App::getRuntimePath() . '/update/' . $current_version;
|
|
||||||
|
|
||||||
$last_version_dir = App::getRuntimePath() . '/update/last';
|
|
||||||
|
|
||||||
$version_file_regx = "/\bconst VERSION\s*=\s*'[\d\.a-z]+'/";
|
|
||||||
|
|
||||||
$output->writeln('获取最新代码');
|
|
||||||
$last_version_git = new Git();
|
|
||||||
|
|
||||||
$last_version_repo = $last_version_git->cloneRepository(self::REPO, $last_version_dir);
|
|
||||||
|
|
||||||
$tags = $last_version_repo->getTags();
|
|
||||||
|
|
||||||
$update_level = Env::get('adminsystem.update_level', 'production');
|
|
||||||
|
|
||||||
if ($update_level == 'production') {
|
|
||||||
$tags = array_filter($tags, function ($value) {
|
|
||||||
if (strpos($value, '-')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
usort($tags, function ($a, $b) {
|
|
||||||
return version_compare($a, $b);
|
|
||||||
});
|
|
||||||
|
|
||||||
$last_version = $tags[count($tags) - 1];
|
|
||||||
|
|
||||||
if ($last_version == $current_version) {
|
|
||||||
$output->writeln('当前版本为最新版本');
|
|
||||||
$this->cleanWorkpaceDir();
|
|
||||||
|
|
||||||
if ($input->hasOption('reinstall')) {
|
|
||||||
$output->writeln('重装代码');
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将最新代码切换到最新版本,因为最新的提交可能没有发布版本
|
|
||||||
$output->writeln('切换最新代码的最新版本');
|
|
||||||
$last_version_repo->checkout($last_version);
|
|
||||||
|
|
||||||
$current_version_git = new Git();
|
|
||||||
$output->writeln('获取当前版本代码');
|
|
||||||
$current_version_repo = $current_version_git->cloneRepository(self::REPO, $current_version_dir);
|
|
||||||
$output->writeln('切换版本' . $current_version);
|
|
||||||
$current_version_repo->checkout($current_version);
|
|
||||||
|
|
||||||
// 获取当前版本需要跳过的文件
|
|
||||||
$current_version_update_config = include $current_version_dir . '/config/update.php';
|
|
||||||
|
|
||||||
// 获取当前版本要替换的文件
|
|
||||||
$current_version_filesystem = new Filesystem(new LocalFilesystemAdapter($current_version_dir));
|
|
||||||
$current_version_list_files = $current_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
|
||||||
->filter(function (StorageAttributes $attributes) use ($current_version_update_config) {
|
|
||||||
if ($attributes->isDir()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $attributes->path();
|
|
||||||
|
|
||||||
$skip_files = $current_version_update_config['skip_files'] ?? [];
|
|
||||||
|
|
||||||
if (in_array($path, $skip_files)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$skip_dir = $current_version_update_config['skip_dir'] ?? [];
|
|
||||||
$skip_dir[] = '.git';
|
|
||||||
|
|
||||||
foreach ($skip_dir as $dir) {
|
|
||||||
if (str_starts_with($path, $dir)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
// 对比现在的代码,检查是否有定制修改
|
|
||||||
|
|
||||||
$output->writeln('对比源码是否被定制');
|
|
||||||
$now_dir = App::getRootPath();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<bool|string>
|
|
||||||
*/
|
|
||||||
$changed_files = [];
|
|
||||||
|
|
||||||
foreach ($current_version_list_files as $file_path) {
|
|
||||||
$now_file_path = $now_dir . '/' . $file_path;
|
|
||||||
|
|
||||||
$current_version_file_path = $current_version_dir . '/' . $file_path;
|
|
||||||
|
|
||||||
$compare_result = PathTools::compareFiles($now_file_path, $current_version_file_path, true);
|
|
||||||
|
|
||||||
if (!$compare_result || is_string($compare_result)) {
|
|
||||||
$changed_files[$file_path] = $compare_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 有定制修改则退出
|
|
||||||
|
|
||||||
if (!empty($changed_files)) {
|
|
||||||
$output->warning('无法自动更新,以下文件被定制,请还原或手动升级:');
|
|
||||||
|
|
||||||
foreach ($changed_files as $file_path => $compare_result) {
|
|
||||||
$output->warning($file_path);
|
|
||||||
|
|
||||||
if (is_string($compare_result)) {
|
|
||||||
$output->writeln($compare_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$file_count = count($changed_files);
|
|
||||||
|
|
||||||
$ask_result = $output->ask($input, "发现{$file_count}个文件被修改,如果您认为以上文件可以被覆盖或为检测错误,可以强制覆盖。\n是否强制继续更新?(y/n)");
|
|
||||||
|
|
||||||
if (!$ask_result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取最新版本需要跳过的文件
|
|
||||||
|
|
||||||
$last_version_skip_config = include $last_version_dir . '/config/update.php';
|
|
||||||
// 获取最新版本要替换的文件
|
|
||||||
$last_version_filesystem = new Filesystem(new LocalFilesystemAdapter($last_version_dir));
|
|
||||||
$last_version_list_files = $last_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
|
||||||
->filter(function (StorageAttributes $attributes) use ($last_version_skip_config) {
|
|
||||||
if ($attributes->isDir()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $attributes->path();
|
|
||||||
|
|
||||||
$skip_files = $last_version_skip_config['skip_files'] ?? [];
|
|
||||||
|
|
||||||
if (in_array($path, $skip_files)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$skip_dir = $last_version_skip_config['skip_dir'] ?? [];
|
|
||||||
$skip_dir[] = '.git';
|
|
||||||
|
|
||||||
foreach ($skip_dir as $dir) {
|
|
||||||
if (str_starts_with($path, $dir)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
$delete_files = array_diff($current_version_list_files, $last_version_list_files);
|
|
||||||
|
|
||||||
foreach ($delete_files as $file_path) {
|
|
||||||
$now_file_path = $now_dir . '/' . $file_path;
|
|
||||||
unlink($now_file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($last_version_list_files as $file_path) {
|
|
||||||
$now_file_path = $now_dir . '/' . $file_path;
|
|
||||||
$last_file_path = $last_version_dir . '/' . $file_path;
|
|
||||||
|
|
||||||
$file_content = file_get_contents($last_file_path);
|
|
||||||
|
|
||||||
PathTools::intiDir($now_file_path);
|
|
||||||
file_put_contents($now_file_path, $file_content);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理append的文件
|
|
||||||
$last_version_list_skip_files = $last_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
|
||||||
->filter(function (StorageAttributes $attributes) use ($last_version_skip_config) {
|
|
||||||
if ($attributes->isDir()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $attributes->path();
|
|
||||||
|
|
||||||
if (str_starts_with($path, '.git')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$skip_files = $last_version_skip_config['skip_files'] ?? [];
|
|
||||||
|
|
||||||
if (in_array($path, $skip_files)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$skip_dir = $last_version_skip_config['skip_dir'] ?? [];
|
|
||||||
|
|
||||||
foreach ($skip_dir as $dir) {
|
|
||||||
if (str_starts_with($path, $dir)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
$last_version_list_append_files = [];
|
|
||||||
|
|
||||||
foreach ($last_version_list_skip_files as $file_path) {
|
|
||||||
if (in_array($file_path, $last_version_skip_config['append_files'])) {
|
|
||||||
$last_version_list_append_files[] = $file_path;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($last_version_skip_config['append_dir'] as $dir) {
|
|
||||||
if (str_starts_with($file_path, $dir)) {
|
|
||||||
$last_version_list_append_files[] = $file_path;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($last_version_list_append_files as $file_path) {
|
|
||||||
$now_file_path = $now_dir . '/' . $file_path;
|
|
||||||
$last_file_path = $last_version_dir . '/' . $file_path;
|
|
||||||
|
|
||||||
if (file_exists($now_file_path)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$file_content = file_get_contents($last_file_path);
|
|
||||||
|
|
||||||
PathTools::intiDir($now_file_path);
|
|
||||||
file_put_contents($now_file_path, $file_content);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测now的composer依赖和最新的composer依赖
|
|
||||||
|
|
||||||
$last_composer_json = file_get_contents($last_version_dir . '/composer.json');
|
|
||||||
|
|
||||||
$output->writeln($last_composer_json);
|
|
||||||
$output->writeln('请参考以上最新composer文件调整您的依赖');
|
|
||||||
|
|
||||||
// 分析出最新需要的但now没有的
|
|
||||||
|
|
||||||
// 为用户整理出要手动调整的composer命令
|
|
||||||
|
|
||||||
$this->cleanWorkpaceDir();
|
|
||||||
$output->writeln('更新完成');
|
|
||||||
// 更新完成
|
|
||||||
|
|
||||||
if (!empty(Version::UPDATE_TIPS)) {
|
|
||||||
foreach (Version::UPDATE_TIPS as $content) {
|
|
||||||
$output->writeln($content);
|
|
||||||
}
|
|
||||||
$output->writeln('删除对应文件之后,可以通过以下命令重新安装');
|
|
||||||
$output->writeln('php think admin:update --resinstall');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cleanWorkpaceDir()
|
|
||||||
{
|
|
||||||
$dir = App::getRuntimePath() . '/update/';
|
|
||||||
|
|
||||||
$this->output->writeln('清理目录 ' . $dir);
|
|
||||||
PathTools::removeDir($dir);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,69 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command\admin;
|
namespace app\common\command\admin;
|
||||||
|
|
||||||
use think\App as ThinkApp;
|
use base\common\command\admin\VersionBase;
|
||||||
use think\console\Command;
|
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
|
|
||||||
class Version extends Command
|
class Version extends VersionBase
|
||||||
{
|
{
|
||||||
public const VERSION = 'v2.0.45';
|
|
||||||
|
|
||||||
public const LAYUI_VERSION = '2.8.16';
|
|
||||||
|
|
||||||
public const COMMENT = [
|
|
||||||
'实现view的扩展架构',
|
|
||||||
'调整think-view依赖',
|
|
||||||
'发布新版本',
|
|
||||||
];
|
|
||||||
|
|
||||||
public const UPDATE_TIPS = [
|
|
||||||
'本次更新调整了composer依赖,请根据实际情况调整',
|
|
||||||
'可以删除app下admin的view目录了',
|
|
||||||
'删除 think-view',
|
|
||||||
'引入 topthink/think-template-view',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('admin:version')
|
|
||||||
->addOption('push-tag', null, Option::VALUE_NONE, '使用git命令生成tag并推送')
|
|
||||||
->setDescription('查看当前ulthon_admin的版本号');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->info('当前版本号为:' . $this::VERSION);
|
|
||||||
$output->info('当前Layui版本号为:' . $this::LAYUI_VERSION);
|
|
||||||
$output->info('当前ThinkPHP版本号为:' . ThinkApp::VERSION);
|
|
||||||
|
|
||||||
$output->writeln('当前的修改说明:');
|
|
||||||
$output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
|
|
||||||
|
|
||||||
foreach ($this::COMMENT as $comment) {
|
|
||||||
$output->info($comment);
|
|
||||||
}
|
|
||||||
$output->writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
|
|
||||||
|
|
||||||
$output->highlight('代码托管地址:https://gitee.com/ulthon/ulthon_admin');
|
|
||||||
$output->highlight('开发文档地址:http://doc.ulthon.com/home/read/ulthon_admin/home.html');
|
|
||||||
|
|
||||||
$is_push_tag = $input->hasOption('push-tag');
|
|
||||||
|
|
||||||
if ($is_push_tag) {
|
|
||||||
$output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
|
|
||||||
|
|
||||||
$version = $this::VERSION;
|
|
||||||
$comment = implode(';', $this::COMMENT);
|
|
||||||
$output->info('生成标签:' . $version);
|
|
||||||
$output->info('标签描述:' . $comment);
|
|
||||||
exec("git tag -a $version -m \"$comment\"");
|
|
||||||
$output->info('推送到远程仓库');
|
|
||||||
exec('git push --tags');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,220 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\command\curd;
|
namespace app\common\command\curd;
|
||||||
|
|
||||||
use think\console\Command;
|
use base\common\command\curd\MigrateBase;
|
||||||
use think\console\Input;
|
|
||||||
use think\console\input\Argument;
|
|
||||||
use think\console\input\Option;
|
|
||||||
use think\console\Output;
|
|
||||||
use think\facade\App;
|
|
||||||
use think\facade\Db;
|
|
||||||
use think\facade\View;
|
|
||||||
|
|
||||||
class Migrate extends Command
|
class Migrate extends MigrateBase
|
||||||
{
|
{
|
||||||
protected $table;
|
|
||||||
protected $database;
|
|
||||||
protected $tablePrefix;
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
// 指令配置
|
|
||||||
$this->setName('curd:migrate')
|
|
||||||
->addOption('table', 't', Option::VALUE_REQUIRED, '主表名')
|
|
||||||
->addOption('tableName', '', Option::VALUE_OPTIONAL, '要生成的表名')
|
|
||||||
->addOption('fileName', '', Option::VALUE_OPTIONAL, '要生成的文件名')
|
|
||||||
->addOption('force', 'f', Option::VALUE_NONE, '强制生成')
|
|
||||||
->setDescription('the curd:migrate command');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(Input $input, Output $output)
|
|
||||||
{
|
|
||||||
// 指令输出
|
|
||||||
$output->writeln('curd:migrate');
|
|
||||||
|
|
||||||
$table = $input->getOption('table');
|
|
||||||
$file_name = $input->getOption('fileName');
|
|
||||||
$table_name = $input->getOption('tableName');
|
|
||||||
$force = $input->getOption('force');
|
|
||||||
|
|
||||||
if (empty($table)) {
|
|
||||||
$output->error('请输入表名');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($table_name)) {
|
|
||||||
$table_name = $table;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($file_name)) {
|
|
||||||
$file_name = $table_name;
|
|
||||||
}
|
|
||||||
$this->table = $table;
|
|
||||||
|
|
||||||
|
|
||||||
$this->database = config('database.connections.mysql.database');
|
|
||||||
$this->tablePrefix = config('database.connections.mysql.prefix');
|
|
||||||
|
|
||||||
$table_info_sql = "select * from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='{$this->database}' and TABLE_NAME='{$this->tablePrefix}{$this->table}'";
|
|
||||||
|
|
||||||
$table_info = Db::query($table_info_sql);
|
|
||||||
|
|
||||||
if (empty($table_info)) {
|
|
||||||
$output->error('表不存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dist_file_path = App::getRootPath() . '/database/migrations/' . date('YmdHis') . '_' . $file_name . '.php';
|
|
||||||
|
|
||||||
$patt_path = App::getRootPath() . '/database/migrations/*' . $file_name . '.php';
|
|
||||||
|
|
||||||
$patt_files = glob($patt_path);
|
|
||||||
|
|
||||||
|
|
||||||
$is_extis = false;
|
|
||||||
foreach ($patt_files as $patt_file) {
|
|
||||||
|
|
||||||
|
|
||||||
$patt = '/.*database\/migrations\/\d+_' . $file_name . '.php$/';
|
|
||||||
|
|
||||||
$preg_result = preg_match($patt, $patt_file);
|
|
||||||
|
|
||||||
|
|
||||||
if ($preg_result) {
|
|
||||||
$is_extis = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($is_extis) {
|
|
||||||
$output->error('文件已存在:' . $patt_files[0]);
|
|
||||||
if (!$force) {
|
|
||||||
$confirm_force = $output->confirm($input, '确定要覆盖文件吗?', false);
|
|
||||||
|
|
||||||
if (!$confirm_force) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$output->highlight('执行覆盖操作');
|
|
||||||
|
|
||||||
$dist_file_path = $patt_files[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$columns = Db::query("SHOW FULL COLUMNS FROM {$this->tablePrefix}{$this->table}");
|
|
||||||
|
|
||||||
$data['class_name'] = \think\helper\Str::studly($file_name);
|
|
||||||
|
|
||||||
|
|
||||||
$table_columns = [];
|
|
||||||
|
|
||||||
$table_keys = [];
|
|
||||||
$table_keys_text = [];
|
|
||||||
$table_keys_uni = [];
|
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
|
||||||
|
|
||||||
if ($column['Field'] == 'id') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$column_item = [];
|
|
||||||
$column_item['options'] = [];
|
|
||||||
|
|
||||||
$column_item['field'] = $column['Field'];
|
|
||||||
|
|
||||||
$column_item['type'] = '';
|
|
||||||
|
|
||||||
$type = $column['Type'];
|
|
||||||
|
|
||||||
if (strpos($type, '(') !== false) {
|
|
||||||
// 带有长度
|
|
||||||
|
|
||||||
$type_info = explode('(', $type);
|
|
||||||
$column_item['type'] = $type_info[0];
|
|
||||||
|
|
||||||
$length = substr($type_info[1], 0, strpos($type_info[1], ')'));
|
|
||||||
|
|
||||||
if (strpos($length, ',') !== false) {
|
|
||||||
|
|
||||||
$length_info = explode(',', $length);
|
|
||||||
|
|
||||||
$column_item['options']['precision'] = $length_info[0];
|
|
||||||
$column_item['options']['scale'] = $length_info[1];
|
|
||||||
} else {
|
|
||||||
|
|
||||||
$column_item['options']['limit'] = $length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($type, 'unsigned') !== false) {
|
|
||||||
// 无符号
|
|
||||||
$column_item['options']['signed'] = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$column_item['type'] = $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
$column_item['options']['null'] = $column['Null'] == 'YES' ? 1 : 0;
|
|
||||||
if ($column['Default'] !== null) {
|
|
||||||
$column_item['options']['default'] = $column['Default'];
|
|
||||||
}
|
|
||||||
$column_item['options']['comment'] = $column['Comment'];
|
|
||||||
|
|
||||||
$key = $column['Key'];
|
|
||||||
|
|
||||||
if (!empty($key)) {
|
|
||||||
if ($key == 'MUL') {
|
|
||||||
if ($type == 'text' || $type == 'longtext') {
|
|
||||||
|
|
||||||
$table_keys_text[] = $column['Field'];
|
|
||||||
} else {
|
|
||||||
$table_keys[] = $column['Field'];
|
|
||||||
}
|
|
||||||
} elseif ($key == 'UNI') {
|
|
||||||
$table_keys_uni[] = $column['Field'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$table_columns[] = $column_item;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type_map = [
|
|
||||||
'bigint' => 'biginteger',
|
|
||||||
'int' => 'integer',
|
|
||||||
'varchar' => 'string',
|
|
||||||
'smallint' => 'integer',
|
|
||||||
'tinyint' => 'integer',
|
|
||||||
'longtext' => 'text',
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($table_columns as &$column_item_set) {
|
|
||||||
|
|
||||||
if (isset($type_map[$column_item_set['type']])) {
|
|
||||||
$column_item_set['type'] = $type_map[$column_item_set['type']];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($column_item_set['options'] as $key => $option) {
|
|
||||||
|
|
||||||
if(is_array($option)) {
|
|
||||||
|
|
||||||
$column_item_set['options'][$key] = "[".implode(',', $option)."]";
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$column_item_set['options'][$key] = "'{$option}'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$data['table_info'] = $table_info[0];
|
|
||||||
$data['table'] = $table_name;
|
|
||||||
$data['table_columns'] = $table_columns;
|
|
||||||
$data['table_keys'] = $table_keys;
|
|
||||||
$data['table_keys_uni'] = $table_keys_uni;
|
|
||||||
$data['table_keys_text'] = $table_keys_text;
|
|
||||||
|
|
||||||
|
|
||||||
$migrate_content = View::fetch(__DIR__ . '/migrate.tpl', $data);
|
|
||||||
|
|
||||||
file_put_contents(__DIR__ . '/migrate_output.php', "<?php\n\n" . $migrate_content);
|
|
||||||
file_put_contents($dist_file_path, "<?php\n\n" . $migrate_content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\constants;
|
namespace app\common\constants;
|
||||||
|
|
||||||
|
use base\common\constants\AdminConstantBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理员常量
|
* 管理员常量
|
||||||
* Class AdminConstant
|
* Class AdminConstant.
|
||||||
* @package app\common\constants
|
|
||||||
*/
|
*/
|
||||||
class AdminConstant
|
class AdminConstant extends AdminConstantBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* 超级管理员,不受权限控制
|
|
||||||
*/
|
|
||||||
const SUPER_ADMIN_ID = 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\constants;
|
namespace app\common\constants;
|
||||||
|
|
||||||
|
use base\common\constants\MenuConstantBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜单常量
|
* 菜单常量
|
||||||
* Class MenuConstant
|
* Class MenuConstant.
|
||||||
* @package app\common\constants
|
|
||||||
*/
|
*/
|
||||||
class MenuConstant
|
class MenuConstant extends MenuConstantBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* 首页的PID
|
|
||||||
*/
|
|
||||||
const HOME_PID = 99999999;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块名前缀
|
|
||||||
*/
|
|
||||||
const MODULE_PREFIX = 'ulthon_admin_';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,442 +2,11 @@
|
|||||||
|
|
||||||
namespace app\common\controller;
|
namespace app\common\controller;
|
||||||
|
|
||||||
use app\admin\model\SystemAdmin;
|
use base\common\controller\AdminControllerBase;
|
||||||
use app\BaseController;
|
|
||||||
use app\common\constants\AdminConstant;
|
|
||||||
use app\common\service\AuthService;
|
|
||||||
use think\facade\Env;
|
|
||||||
use think\facade\View;
|
|
||||||
use think\Model;
|
|
||||||
use think\template\exception\TemplateNotFoundException;
|
|
||||||
use think\Validate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AdminController.
|
* Class AdminController.
|
||||||
*/
|
*/
|
||||||
class AdminController extends BaseController
|
class AdminController extends AdminControllerBase
|
||||||
{
|
{
|
||||||
use \app\common\traits\JumpTrait;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前模型.
|
|
||||||
* @Model
|
|
||||||
* @var object
|
|
||||||
*/
|
|
||||||
protected $model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字段排序.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $sort = [
|
|
||||||
'id' => 'desc',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 允许修改的字段.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $allowModifyFields = [
|
|
||||||
'status',
|
|
||||||
'sort',
|
|
||||||
'remark',
|
|
||||||
'is_delete',
|
|
||||||
'is_auth',
|
|
||||||
'title',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 不导出的字段信息.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $noExportFields = ['delete_time', 'update_time'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下拉选择条件.
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $selectWhere = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否关联查询.
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $relationSearch = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模板布局, false取消.
|
|
||||||
* @var string|bool
|
|
||||||
*/
|
|
||||||
protected $layout = 'layout/default';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否为演示环境.
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $isDemo = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多元传参
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $dataBrage = [];
|
|
||||||
|
|
||||||
protected $validateRule = null;
|
|
||||||
|
|
||||||
protected $validateMessage = [];
|
|
||||||
|
|
||||||
protected $batchValidate = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出文件的名称,支持中文,为空则获取模型名称.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $exportFileName = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前登陆的管理员.
|
|
||||||
* @var SystemAdmin
|
|
||||||
*/
|
|
||||||
protected $sessionAdmin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化方法.
|
|
||||||
*/
|
|
||||||
protected function initialize()
|
|
||||||
{
|
|
||||||
parent::initialize();
|
|
||||||
$this->layout && $this->app->view->engine()->layout($this->layout);
|
|
||||||
$this->isDemo = Env::get('adminsystem.is_demo', false);
|
|
||||||
$this->viewInit();
|
|
||||||
$this->checkAuth();
|
|
||||||
|
|
||||||
$this->initSort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initSort()
|
|
||||||
{
|
|
||||||
$sort = $this->request->param('sort');
|
|
||||||
|
|
||||||
if (!empty($sort)) {
|
|
||||||
$this->sort = $sort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模板变量赋值
|
|
||||||
* @param string|array $name 模板变量
|
|
||||||
* @param mixed $value 变量值
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function assign($name, $value = null, $isAppendToDataBrage = false)
|
|
||||||
{
|
|
||||||
if ($isAppendToDataBrage) {
|
|
||||||
$this->dataBrage[$name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->app->view->assign($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fetchJS($template = '')
|
|
||||||
{
|
|
||||||
$content_js = '';
|
|
||||||
|
|
||||||
$common_template = '_common';
|
|
||||||
|
|
||||||
if (!empty($template)) {
|
|
||||||
$template_arr = explode('/', $template);
|
|
||||||
unset($template_arr[count($template_arr) - 1]);
|
|
||||||
$template_arr[] = '_common';
|
|
||||||
$common_template = implode('/', $template_arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$content_js .= View::layout(false)->fetchJS($common_template);
|
|
||||||
$content_js .= View::layout(false)->fetchJS($template);
|
|
||||||
} catch (TemplateNotFoundException $th) {
|
|
||||||
if (Env::get('adminsystem.strict_view_js', true)) {
|
|
||||||
throw $th;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "<script>{$content_js}</script>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析和获取模板内容 用于输出.
|
|
||||||
* @param string $template
|
|
||||||
* @param array $vars
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function fetch($template = '', $vars = [])
|
|
||||||
{
|
|
||||||
$this->assign('data_brage', json_encode($this->dataBrage));
|
|
||||||
|
|
||||||
$vars['content_js'] = $this->fetchJS($template);
|
|
||||||
|
|
||||||
$content_main = View::layout($this->layout)
|
|
||||||
->config([
|
|
||||||
'view_suffix' => 'html',
|
|
||||||
])->fetch($template, $vars);
|
|
||||||
|
|
||||||
$html = '';
|
|
||||||
$html .= $content_main;
|
|
||||||
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置dataBrage数据.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDataBrage($name, $value)
|
|
||||||
{
|
|
||||||
$this->dataBrage[$name] = $value;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重写验证规则.
|
|
||||||
* @param array $data
|
|
||||||
* @param array|string $validate
|
|
||||||
* @param array $message
|
|
||||||
* @param bool|null $batch
|
|
||||||
* @return array|bool|string|true
|
|
||||||
*/
|
|
||||||
public function validate(array $data, $validate, array $message = [], bool $batch = null)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$message = array_merge($this->validateMessage, $message);
|
|
||||||
|
|
||||||
if (is_null($batch)) {
|
|
||||||
$batch = $this->batchValidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->validateRule instanceof Validate) {
|
|
||||||
$this->validateRule->message($message);
|
|
||||||
|
|
||||||
// 是否批量验证
|
|
||||||
if ($batch) {
|
|
||||||
$this->validateRule->batch(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->validateRule->failException(true)->check($data);
|
|
||||||
} elseif (is_array($this->validateRule)) {
|
|
||||||
parent::validate($data, $this->validateRule, $message, $batch);
|
|
||||||
} else {
|
|
||||||
parent::validate($data, $validate, $message, $batch);
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建请求参数.
|
|
||||||
* @param array $excludeFields 忽略构建搜索的字段
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function buildTableParames($excludeFields = [])
|
|
||||||
{
|
|
||||||
$get = $this->request->get('', null);
|
|
||||||
|
|
||||||
$page = isset($get['page']) && !empty($get['page']) ? $get['page'] : 1;
|
|
||||||
$limit = isset($get['limit']) && !empty($get['limit']) ? $get['limit'] : 15;
|
|
||||||
$group = isset($get['group']) && !empty($get['group']) ? $get['group'] : null;
|
|
||||||
$filters = isset($get['filter']) && !empty($get['filter']) ? $get['filter'] : '{}';
|
|
||||||
$ops = isset($get['op']) && !empty($get['op']) ? $get['op'] : '{}';
|
|
||||||
// json转数组
|
|
||||||
$filters = json_decode($filters, true) ?? [];
|
|
||||||
$ops = json_decode($ops, true);
|
|
||||||
$where = [];
|
|
||||||
$excludes = [];
|
|
||||||
|
|
||||||
$request_options = [];
|
|
||||||
|
|
||||||
// 判断是否关联查询
|
|
||||||
$tableName = \think\helper\Str::snake(lcfirst($this->model->getName()));
|
|
||||||
|
|
||||||
foreach ($filters as $key => $val) {
|
|
||||||
if (in_array($key, $excludeFields)) {
|
|
||||||
$excludes[$key] = $val;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$op = isset($ops[$key]) && !empty($ops[$key]) ? $ops[$key] : '%*%';
|
|
||||||
|
|
||||||
if (strpos($key, '[') === 0) {
|
|
||||||
$key = str_replace('[', '', $key);
|
|
||||||
|
|
||||||
$key = explode(']', $key)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->relationSearch && count(explode('.', $key)) == 1) {
|
|
||||||
$key = "{$tableName}.{$key}";
|
|
||||||
}
|
|
||||||
switch (strtolower($op)) {
|
|
||||||
case '=':
|
|
||||||
$where[] = [$key, '=', $val];
|
|
||||||
break;
|
|
||||||
case '%*%':
|
|
||||||
$where[] = [$key, 'LIKE', "%{$val}%"];
|
|
||||||
break;
|
|
||||||
case '*%':
|
|
||||||
$where[] = [$key, 'LIKE', "{$val}%"];
|
|
||||||
break;
|
|
||||||
case '%*':
|
|
||||||
$where[] = [$key, 'LIKE', "%{$val}"];
|
|
||||||
case 'in':
|
|
||||||
$where[] = [$key, 'IN', "{$val}"];
|
|
||||||
break;
|
|
||||||
case 'min':
|
|
||||||
$where[] = [$key, '>=', $val];
|
|
||||||
break;
|
|
||||||
case 'max':
|
|
||||||
$where[] = [$key, '<=', $val];
|
|
||||||
break;
|
|
||||||
case 'min_date':
|
|
||||||
$where[] = [$key, '>=', strtotime($val)];
|
|
||||||
break;
|
|
||||||
case 'max_date':
|
|
||||||
$where[] = [$key, '<=', strtotime($val)];
|
|
||||||
break;
|
|
||||||
case 'range':
|
|
||||||
[$beginTime, $endTime] = explode(' - ', $val);
|
|
||||||
$where[] = [$key, '>=', strtotime($beginTime)];
|
|
||||||
$where[] = [$key, '<=', strtotime($endTime)];
|
|
||||||
break;
|
|
||||||
case 'none':
|
|
||||||
$request_options[$key] = $val;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$where[] = [$key, $op, "%{$val}"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$page, $limit, $where, $excludes, $request_options, $group];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下拉选择列表.
|
|
||||||
* @return \think\response\Json
|
|
||||||
*/
|
|
||||||
public function selectList()
|
|
||||||
{
|
|
||||||
$fields = input('selectFields');
|
|
||||||
$data = $this->model
|
|
||||||
->where($this->selectWhere)
|
|
||||||
->field($fields)
|
|
||||||
->select();
|
|
||||||
$this->success(null, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化视图参数.
|
|
||||||
*/
|
|
||||||
private function viewInit()
|
|
||||||
{
|
|
||||||
$request = app()->request;
|
|
||||||
list($thisModule, $thisController, $thisAction) = [app('http')->getName(), app()->request->controller(), $request->action()];
|
|
||||||
list($thisControllerArr, $jsPath) = [explode('.', $thisController), null];
|
|
||||||
foreach ($thisControllerArr as $vo) {
|
|
||||||
empty($jsPath) ? $jsPath = parse_name($vo) : $jsPath .= '/' . parse_name($vo);
|
|
||||||
}
|
|
||||||
$autoloadJs = file_exists(root_path('public') . "static/{$thisModule}/js/{$jsPath}.js") ? true : false;
|
|
||||||
$thisControllerJsPath = "{$thisModule}/js/{$jsPath}.js";
|
|
||||||
$adminModuleName = config('app.admin_alias_name');
|
|
||||||
$isSuperAdmin = session('admin.id') == AdminConstant::SUPER_ADMIN_ID ? true : false;
|
|
||||||
$data = [
|
|
||||||
'adminModuleName' => $adminModuleName,
|
|
||||||
'thisController' => parse_name($thisController),
|
|
||||||
'thisAction' => $thisAction,
|
|
||||||
'thisRequest' => parse_name("{$thisModule}/{$thisController}/{$thisAction}"),
|
|
||||||
'thisControllerJsPath' => "{$thisControllerJsPath}",
|
|
||||||
'autoloadJs' => $autoloadJs,
|
|
||||||
'isSuperAdmin' => $isSuperAdmin,
|
|
||||||
'version' => env('app_debug') ? time() : sysconfig('site', 'site_version'),
|
|
||||||
];
|
|
||||||
|
|
||||||
View::assign($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检测权限.
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
protected function checkAuth($currentNode = null, $haltRequest = true)
|
|
||||||
{
|
|
||||||
$adminConfig = config('admin');
|
|
||||||
$adminId = session('admin.id');
|
|
||||||
$expireTime = session('admin.expire_time');
|
|
||||||
/** @var AuthService $authService */
|
|
||||||
$authService = app(AuthService::class, ['adminId' => $adminId]);
|
|
||||||
|
|
||||||
$currentNode = $authService->getCurrentNode();
|
|
||||||
if (is_null($currentNode)) {
|
|
||||||
$currentNode = $authService->getCurrentNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentController = parse_name(app()->request->controller());
|
|
||||||
|
|
||||||
// 验证登录
|
|
||||||
if (
|
|
||||||
!in_array($currentController, $adminConfig['no_login_controller']) &&
|
|
||||||
!in_array($currentNode, $adminConfig['no_login_node'])
|
|
||||||
) {
|
|
||||||
empty($adminId) && $this->error('请先登录后台', [], __url('admin/login/index'));
|
|
||||||
|
|
||||||
// 判断是否登录过期
|
|
||||||
if ($expireTime !== true && time() > $expireTime) {
|
|
||||||
session('admin', null);
|
|
||||||
$this->error('登录已过期,请重新登录', [], __url('admin/login/index'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证权限
|
|
||||||
if (
|
|
||||||
!in_array($currentController, $adminConfig['no_auth_controller']) &&
|
|
||||||
!in_array($currentNode, $adminConfig['no_auth_node'])
|
|
||||||
) {
|
|
||||||
$check = $authService->checkNode($currentNode);
|
|
||||||
|
|
||||||
if ($haltRequest) {
|
|
||||||
if (!$check) {
|
|
||||||
$this->error('无权限访问');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return $check;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否为演示环境
|
|
||||||
if (env('adminsystem.is_demo', false) && app()->request->isPost()) {
|
|
||||||
$this->error('演示环境下不允许修改');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$model_admin = SystemAdmin::autoCache('read', $adminId)->find($adminId);
|
|
||||||
|
|
||||||
$this->sessionAdmin = $model_admin;
|
|
||||||
|
|
||||||
$this->assign('session_admin', $model_admin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 严格校验接口是否为POST请求
|
|
||||||
*/
|
|
||||||
protected function checkPostRequest()
|
|
||||||
{
|
|
||||||
if (!$this->request->isPost()) {
|
|
||||||
$this->error('当前请求不合法!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\controller;
|
namespace app\common\controller;
|
||||||
|
|
||||||
use think\facade\Cache;
|
use base\common\controller\TimerControllerBase;
|
||||||
|
|
||||||
class TimerController extends ToolsController
|
class TimerController extends TimerControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $frequency = null;
|
|
||||||
|
|
||||||
public function initialize()
|
|
||||||
{
|
|
||||||
|
|
||||||
parent::initialize();
|
|
||||||
|
|
||||||
if (is_integer($this->frequency)) {
|
|
||||||
$this->protectVisit($this->frequency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function protectVisit(int $frequency)
|
|
||||||
{
|
|
||||||
|
|
||||||
$cache_tag = 'timer_protect';
|
|
||||||
|
|
||||||
$cache_key = 'timer_protect_' . md5($this->request->url());
|
|
||||||
|
|
||||||
$last_exec_time = Cache::get($cache_key, 0);
|
|
||||||
|
|
||||||
if ($last_exec_time >= time() - $frequency) {
|
|
||||||
|
|
||||||
return $this->error('请不要频繁请求');
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::tag($cache_tag)->set($cache_key, time());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\controller;
|
namespace app\common\controller;
|
||||||
|
|
||||||
use app\BaseController;
|
use base\common\controller\ToolsControllerBase;
|
||||||
use app\common\traits\JumpTrait;
|
|
||||||
|
|
||||||
class ToolsController extends BaseController
|
class ToolsController extends ToolsControllerBase
|
||||||
{
|
{
|
||||||
use JumpTrait;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\event\AdminLoginSuccess;
|
namespace app\common\event\AdminLoginSuccess;
|
||||||
|
|
||||||
use app\admin\model\SystemAdmin;
|
use base\common\event\AdminLoginSuccess\LogEventBase;
|
||||||
use think\facade\Log;
|
|
||||||
|
|
||||||
class LogEvent
|
class LogEvent extends LogEventBase
|
||||||
{
|
{
|
||||||
public function handle(SystemAdmin $system_admin)
|
|
||||||
{
|
|
||||||
// 事件监听处理
|
|
||||||
Log::report("admin login success,{$system_admin->username}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\event\AdminLoginType;
|
namespace app\common\event\AdminLoginType;
|
||||||
|
|
||||||
use think\facade\Env;
|
use base\common\event\AdminLoginType\DemoEventBase;
|
||||||
use think\facade\View;
|
|
||||||
|
|
||||||
class DemoEvent
|
class DemoEvent extends DemoEventBase
|
||||||
{
|
{
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$content = '';
|
|
||||||
|
|
||||||
if (Env::get('adminsystem.is_demo', false)) {
|
|
||||||
$content = View::layout(false)->fetch('login/ext/demo');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件监听处理
|
|
||||||
return [
|
|
||||||
'view_content' => $content,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace app\commno\exception;
|
namespace app\commno\exception;
|
||||||
|
|
||||||
use Exception;
|
use extend\base\commno\exception\EventExceptionBase;
|
||||||
|
|
||||||
class EventException extends Exception
|
class EventException extends EventExceptionBase
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,106 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\model;
|
namespace app\common\model;
|
||||||
|
|
||||||
use think\facade\Cache;
|
use base\common\model\BaseModelBase;
|
||||||
use think\facade\Env;
|
|
||||||
use think\facade\Log;
|
|
||||||
use think\Model;
|
|
||||||
use think\helper\Str;
|
|
||||||
|
|
||||||
class BaseModel extends Model
|
class BaseModel extends BaseModelBase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* 自动清除的缓存值
|
|
||||||
*
|
|
||||||
* [
|
|
||||||
* 'name'=>'',
|
|
||||||
* 'type'=>'', // tag/key
|
|
||||||
* 'field'=>'' // 为空则不做拼接
|
|
||||||
* ]
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $autoCache = [];
|
|
||||||
|
|
||||||
public static function onAfterWrite($model)
|
|
||||||
{
|
|
||||||
static::autoRemoveCache($model);
|
|
||||||
}
|
|
||||||
public static function onAfterDelete($model)
|
|
||||||
{
|
|
||||||
static::autoRemoveCache($model);
|
|
||||||
}
|
|
||||||
public static function onAfterRestore($model)
|
|
||||||
{
|
|
||||||
static::autoRemoveCache($model);
|
|
||||||
}
|
|
||||||
public static function autoRemoveCache($model)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if(!isset(static::$autoCache['table'])){
|
|
||||||
static::$autoCache['table'] = [
|
|
||||||
'name' => 'table',
|
|
||||||
'type' => 'tag'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset(static::$autoCache['read'])){
|
|
||||||
static::$autoCache['read'] = [
|
|
||||||
'name' => 'read',
|
|
||||||
'field' => 'id'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (static::$autoCache as $cache_item) {
|
|
||||||
$type = $cache_item['type'] ?? 'key';
|
|
||||||
|
|
||||||
$field = $cache_item['field'] ?? '';
|
|
||||||
|
|
||||||
$cache_key = $cache_item['name'] ?? '';
|
|
||||||
|
|
||||||
if (empty($cache_key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cache_key = Str::snake($model->getName()) . '_' . $cache_key;
|
|
||||||
|
|
||||||
|
|
||||||
if (!empty($field) && !is_null($model->$field)) {
|
|
||||||
// 用字段新的值拼接key
|
|
||||||
$cache_key_attr = $cache_key . '_' . $model->getAttr($field);
|
|
||||||
|
|
||||||
static::doRemoveCache($type, $cache_key_attr);
|
|
||||||
|
|
||||||
// 用字段旧的值拼接key
|
|
||||||
$cache_key_original = $cache_key . '_' . $model->getOrigin($field);
|
|
||||||
|
|
||||||
if ($cache_key_original != $cache_key_attr) {
|
|
||||||
static::doRemoveCache($type, $cache_key_original);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static::doRemoveCache($type, $cache_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function doRemoveCache($type, $cache_key)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (Env::get('app.auto_cache_log')) {
|
|
||||||
Log::debug("清除 ORM 自动缓存 {$type}:{$cache_key}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type == 'key') {
|
|
||||||
Cache::delete($cache_key);
|
|
||||||
} else {
|
|
||||||
Cache::tag($cache_key)->clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getClassNameKey()
|
|
||||||
{
|
|
||||||
return str_replace('\\', '_', static::class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\model;
|
namespace app\common\model;
|
||||||
|
|
||||||
|
use base\common\model\TimeModelBase;
|
||||||
use think\model\concern\SoftDelete;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 有关时间的模型
|
* 有关时间的模型
|
||||||
* Class TimeModel
|
* Class TimeModel.
|
||||||
* @package app\common\model
|
|
||||||
*/
|
*/
|
||||||
class TimeModel extends BaseModel
|
class TimeModel extends TimeModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* 自动时间戳类型
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $autoWriteTimestamp = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加时间
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $createTime = 'create_time';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新时间
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $updateTime = 'update_time';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 软删除
|
|
||||||
*/
|
|
||||||
use SoftDelete;
|
|
||||||
|
|
||||||
protected $deleteTime = 'delete_time';
|
|
||||||
|
|
||||||
protected $defaultSoftDelete = 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace app\common\provider;
|
namespace app\common\provider;
|
||||||
|
|
||||||
use think\db\exception\DataNotFoundException;
|
use base\common\provider\ExceptionHandleBase;
|
||||||
use think\db\exception\ModelNotFoundException;
|
|
||||||
use think\exception\Handle;
|
|
||||||
use think\exception\HttpException;
|
|
||||||
use think\exception\HttpResponseException;
|
|
||||||
use think\exception\ValidateException;
|
|
||||||
use think\Response;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用异常处理类
|
* 应用异常处理类.
|
||||||
*/
|
*/
|
||||||
class ExceptionHandle extends Handle
|
class ExceptionHandle extends ExceptionHandleBase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* 不需要记录信息(日志)的异常类列表
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $ignoreReport = [
|
|
||||||
HttpException::class,
|
|
||||||
HttpResponseException::class,
|
|
||||||
ModelNotFoundException::class,
|
|
||||||
DataNotFoundException::class,
|
|
||||||
ValidateException::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录异常信息(包括日志或者其它方式记录)
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param Throwable $exception
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function report(Throwable $exception): void
|
|
||||||
{
|
|
||||||
// 使用内置的方式记录异常日志
|
|
||||||
parent::report($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render an exception into an HTTP response.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param \think\Request $request
|
|
||||||
* @param Throwable $e
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
public function render($request, Throwable $e): Response
|
|
||||||
{
|
|
||||||
// 添加自定义异常处理机制
|
|
||||||
|
|
||||||
// 其他错误交给系统处理
|
|
||||||
return parent::render($request, $e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace app\common\provider;
|
namespace app\common\provider;
|
||||||
|
|
||||||
|
use base\common\provider\RequestBase;
|
||||||
|
|
||||||
// 应用请求对象类
|
// 应用请求对象类
|
||||||
class Request extends \think\Request
|
class Request extends RequestBase
|
||||||
{
|
{
|
||||||
protected $filter = [];
|
protected $filter = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,82 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\provider;
|
namespace app\common\provider;
|
||||||
|
|
||||||
use think\View as ThinkView;
|
use base\common\provider\ViewBase;
|
||||||
|
|
||||||
class View extends ThinkView
|
class View extends ViewBase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* 设置布局
|
|
||||||
* ! 注意,layout并非view类的标准方法,仅仅是think-view和think-template的特性方法
|
|
||||||
* ! 但是,如果在provider中定制,则view的所有操作预期会发生错误
|
|
||||||
* ! 同时,由于后台从设计支出就完全依赖think-view,所以专门为此扩展和特性定义方法时没有问题的.
|
|
||||||
* @param bool|string $name 布局模板名称 false 则关闭布局
|
|
||||||
* @param string $replace 布局模板内容替换标识
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function layout(bool|string $name, string $replace = ''): static
|
|
||||||
{
|
|
||||||
if (false === $name) {
|
|
||||||
// 关闭布局
|
|
||||||
$this->config(['layout_on' => false]);
|
|
||||||
} else {
|
|
||||||
// 开启布局
|
|
||||||
$this->config(['layout_on' => true]);
|
|
||||||
|
|
||||||
// 名称必须为字符串
|
|
||||||
if (is_string($name)) {
|
|
||||||
$this->config(['layout_name' => $name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($replace)) {
|
|
||||||
$this->config(['layout_item' => $replace]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置模板引擎.
|
|
||||||
* @param array $config 参数
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function config(array $config)
|
|
||||||
{
|
|
||||||
$this->driver()->config($config);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析和获取模板内容 用于输出.
|
|
||||||
* @param string $template 模板文件名或者内容
|
|
||||||
* @param array $vars 模板变量
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function fetch(string $template = '', array $vars = []): string
|
|
||||||
{
|
|
||||||
return $this->config([
|
|
||||||
'view_suffix' => 'html',
|
|
||||||
])->getContent(function () use ($vars, $template) {
|
|
||||||
$this->engine()->fetch($template, array_merge($this->data, $vars));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析和获取模板内容 用于输出.
|
|
||||||
* @param string $template 模板文件名或者内容
|
|
||||||
* @param array $vars 模板变量
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function fetchJS(string $template = '', array $vars = []): string
|
|
||||||
{
|
|
||||||
return $this->config([
|
|
||||||
'view_suffix' => 'js',
|
|
||||||
])->getContent(function () use ($vars, $template) {
|
|
||||||
$this->engine()->fetch($template, array_merge($this->data, $vars));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,59 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\provider\db;
|
namespace app\common\provider\db;
|
||||||
|
|
||||||
use think\db\Query as DbQuery;
|
use base\common\provider\db\QueryBase;
|
||||||
use think\facade\Env;
|
|
||||||
use think\facade\Log;
|
|
||||||
use think\helper\Str;
|
|
||||||
|
|
||||||
class Query extends DbQuery
|
class Query extends QueryBase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* autoCache 自动生成缓存.
|
|
||||||
*
|
|
||||||
* @param null|string $key
|
|
||||||
* @param null|string|int $field_key_value
|
|
||||||
* @param null|string $tag
|
|
||||||
* @param null|string|int $field_tag_value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function autoCache($key = null, $field_key_value = null, $tag = null, $field_tag_value = null)
|
|
||||||
{
|
|
||||||
$table_name = Str::snake($this->getName());
|
|
||||||
|
|
||||||
if (is_null($key)) {
|
|
||||||
$key = $this->getOptionsMd5();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($field_key_value)) {
|
|
||||||
$key = $key . '_' . $field_key_value;
|
|
||||||
}
|
|
||||||
$key = $table_name . '_' . $key;
|
|
||||||
|
|
||||||
if (!is_null($tag)) {
|
|
||||||
if (!is_null($field_tag_value)) {
|
|
||||||
$tag = $tag . '_' . $field_tag_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tag = $table_name . '_' . $tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Env::get('app.auto_cache_log')) {
|
|
||||||
Log::debug('use auto cache:' . $key);
|
|
||||||
Log::debug('use auto cache tag:' . $tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cache($key, null, $tag);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOptionsMd5()
|
|
||||||
{
|
|
||||||
$options = $this->getOptions();
|
|
||||||
|
|
||||||
// TODO:支持获取回调函数的设置
|
|
||||||
|
|
||||||
return md5(json_encode($options));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,211 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\service;
|
namespace app\common\service;
|
||||||
|
|
||||||
use app\common\constants\AdminConstant;
|
use base\common\service\AuthServiceBase;
|
||||||
use think\facade\Config;
|
|
||||||
use think\facade\Db;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限验证服务
|
* 权限验证服务
|
||||||
* Class AuthService
|
* Class AuthService.
|
||||||
* @package app\common\service
|
|
||||||
*/
|
*/
|
||||||
class AuthService
|
class AuthService extends AuthServiceBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户ID
|
|
||||||
* @var null
|
|
||||||
*/
|
|
||||||
protected $adminId = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认配置
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $config = [
|
|
||||||
'auth_on' => true, // 权限开关
|
|
||||||
'system_admin' => 'system_admin', // 用户表
|
|
||||||
'system_auth' => 'system_auth', // 权限表
|
|
||||||
'system_node' => 'system_node', // 节点表
|
|
||||||
'system_auth_node' => 'system_auth_node', // 权限-节点表
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理员信息
|
|
||||||
* @var array|\think\Model|null
|
|
||||||
*/
|
|
||||||
protected $adminInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有节点信息
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $nodeList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理员所有授权节点
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $adminNode;
|
|
||||||
|
|
||||||
/***
|
|
||||||
* 构造方法
|
|
||||||
* AuthService constructor.
|
|
||||||
* @param null $adminId
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
public function __construct($adminId = null)
|
|
||||||
{
|
|
||||||
$this->adminId = $adminId;
|
|
||||||
$this->adminInfo = $this->getAdminInfo();
|
|
||||||
$this->nodeList = $this->getNodeList();
|
|
||||||
$this->adminNode = $this->getAdminNode();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检测检测权限
|
|
||||||
* @param null $node
|
|
||||||
* @return bool
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
public function checkNode($node = null)
|
|
||||||
{
|
|
||||||
// 判断是否为超级管理员
|
|
||||||
if ($this->adminId == AdminConstant::SUPER_ADMIN_ID) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 判断权限验证开关
|
|
||||||
if ($this->config['auth_on'] == false) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 判断是否需要获取当前节点
|
|
||||||
if (empty($node)) {
|
|
||||||
$node = $this->getCurrentNode();
|
|
||||||
} else {
|
|
||||||
$node = $this->parseNodeStr($node);
|
|
||||||
}
|
|
||||||
// 判断是否加入节点控制,优先获取缓存信息
|
|
||||||
if (!isset($this->nodeList[$node])) {
|
|
||||||
|
|
||||||
return Config::get('admin.default_auth_check');
|
|
||||||
}
|
|
||||||
$nodeInfo = $this->nodeList[$node];
|
|
||||||
if ($nodeInfo['is_auth'] == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 用户验证,优先获取缓存信息
|
|
||||||
if (empty($this->adminInfo) || $this->adminInfo['status'] != 1 || empty($this->adminInfo['auth_ids'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 判断该节点是否允许访问
|
|
||||||
if (in_array($node, $this->adminNode)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前节点
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getCurrentNode()
|
|
||||||
{
|
|
||||||
$node = $this->parseNodeStr(request()->controller() . '/' . request()->action());
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前管理员所有节点
|
|
||||||
* @return array
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
public function getAdminNode()
|
|
||||||
{
|
|
||||||
$nodeList = [];
|
|
||||||
$adminInfo = $this->getAdminInfo();
|
|
||||||
|
|
||||||
if (!empty($adminInfo) && $adminInfo['status'] != 1) {
|
|
||||||
return $nodeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
|
|
||||||
$buildAuthSql = Db::name($this->config['system_auth'])
|
|
||||||
->distinct(true)
|
|
||||||
->whereIn('id', $adminInfo['auth_ids'])
|
|
||||||
->field('id')
|
|
||||||
->buildSql(true);
|
|
||||||
$buildAuthNodeSql = Db::name($this->config['system_auth_node'])
|
|
||||||
->distinct(true)
|
|
||||||
->where("auth_id IN {$buildAuthSql}")
|
|
||||||
->field('node_id')
|
|
||||||
->buildSql(true);
|
|
||||||
$nodeList = Db::name($this->config['system_node'])
|
|
||||||
->distinct(true)
|
|
||||||
->where("id IN {$buildAuthNodeSql}")
|
|
||||||
->column('node');
|
|
||||||
}
|
|
||||||
return $nodeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有节点信息
|
|
||||||
* @time 2021-01-07
|
|
||||||
* @return array
|
|
||||||
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
|
||||||
*/
|
|
||||||
public function getNodeList()
|
|
||||||
{
|
|
||||||
return Db::name($this->config['system_node'])
|
|
||||||
->autoCache(null, null, 'table')
|
|
||||||
->column('id,node,title,type,is_auth', 'node');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取管理员信息
|
|
||||||
* @time 2021-01-07
|
|
||||||
* @return array|\think\Model|null
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
|
||||||
*/
|
|
||||||
public function getAdminInfo()
|
|
||||||
{
|
|
||||||
return Db::name($this->config['system_admin'])
|
|
||||||
->where('id', $this->adminId)
|
|
||||||
->autoCache('info', $this->adminId)
|
|
||||||
->find();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 驼峰转下划线规则
|
|
||||||
* @param string $node
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function parseNodeStr($node)
|
|
||||||
{
|
|
||||||
$array = explode('/', $node);
|
|
||||||
foreach ($array as $key => $val) {
|
|
||||||
if ($key == 0) {
|
|
||||||
$val = explode('.', $val);
|
|
||||||
foreach ($val as &$vo) {
|
|
||||||
$vo = \think\helper\Str::snake(lcfirst($vo));
|
|
||||||
}
|
|
||||||
$val = implode('.', $val);
|
|
||||||
$array[$key] = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$node = implode('/', $array);
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace app\common\service;
|
namespace app\common\service;
|
||||||
|
|
||||||
use app\common\constants\MenuConstant;
|
use base\common\service\MenuServiceBase;
|
||||||
use think\facade\Db;
|
|
||||||
|
|
||||||
class MenuService
|
class MenuService extends MenuServiceBase
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* 管理员ID
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $adminId;
|
|
||||||
|
|
||||||
public function __construct($adminId)
|
|
||||||
{
|
|
||||||
$this->adminId = $adminId;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取首页信息
|
|
||||||
* @return array|\think\Model|null
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
public function getHomeInfo()
|
|
||||||
{
|
|
||||||
$data = Db::name('system_menu')
|
|
||||||
->field('title,icon,href')
|
|
||||||
->where("delete_time", 0)
|
|
||||||
->where('pid', MenuConstant::HOME_PID)
|
|
||||||
->find();
|
|
||||||
!empty($data) && $data['href'] = __url($data['href']);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取后台菜单树信息
|
|
||||||
* @return mixed
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
public function getMenuTree()
|
|
||||||
{
|
|
||||||
/** @var AuthService $authService */
|
|
||||||
$authServer = app(AuthService::class, ['adminId' => $this->adminId]);
|
|
||||||
return $this->buildMenuChild(0, $this->getMenuData(), $authServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildMenuChild($pid, $menuList, AuthService $authServer)
|
|
||||||
{
|
|
||||||
$treeList = [];
|
|
||||||
foreach ($menuList as &$v) {
|
|
||||||
$check = false;
|
|
||||||
if (!empty($v['auth_node'])) {
|
|
||||||
$check = $authServer->checkNode($v['auth_node']);
|
|
||||||
} else if (!empty($v['href'])) {
|
|
||||||
$check = $authServer->checkNode($v['href']);
|
|
||||||
} else {
|
|
||||||
$check = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
!empty($v['href']) && $v['href'] = __url($v['href']);
|
|
||||||
if ($pid == $v['pid'] && $check) {
|
|
||||||
$node = $v;
|
|
||||||
$child = $this->buildMenuChild($v['id'], $menuList, $authServer);
|
|
||||||
if (!empty($child)) {
|
|
||||||
$node['child'] = $child;
|
|
||||||
}
|
|
||||||
if (!empty($v['href']) || !empty($child)) {
|
|
||||||
$treeList[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $treeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有菜单数据
|
|
||||||
* @return \think\Collection
|
|
||||||
* @throws \think\db\exception\DataNotFoundException
|
|
||||||
* @throws \think\db\exception\DbException
|
|
||||||
* @throws \think\db\exception\ModelNotFoundException
|
|
||||||
*/
|
|
||||||
protected function getMenuData()
|
|
||||||
{
|
|
||||||
$menuData = Db::name('system_menu')
|
|
||||||
->field('id,pid,title,icon,href,target')
|
|
||||||
->where("delete_time", 0)
|
|
||||||
->where([
|
|
||||||
['status', '=', '1'],
|
|
||||||
['pid', '<>', MenuConstant::HOME_PID],
|
|
||||||
])
|
|
||||||
->order([
|
|
||||||
'sort' => 'desc',
|
|
||||||
'id' => 'asc',
|
|
||||||
])
|
|
||||||
->select();
|
|
||||||
return $menuData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,173 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\service;
|
namespace app\common\service;
|
||||||
|
|
||||||
use app\admin\model\SystemUploadfile;
|
use base\common\service\UploadServiceBase;
|
||||||
use app\common\tools\PathTools;
|
|
||||||
use think\exception\ValidateException;
|
|
||||||
use think\facade\App;
|
|
||||||
use think\facade\Filesystem;
|
|
||||||
use think\facade\Validate;
|
|
||||||
use think\File;
|
|
||||||
use think\file\UploadedFile;
|
|
||||||
|
|
||||||
class UploadService
|
class UploadService extends UploadServiceBase
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $uploadType = 'local_public';
|
|
||||||
|
|
||||||
public function __construct($upload_type = null)
|
|
||||||
{
|
|
||||||
$uploadConfig = sysconfig('upload');
|
|
||||||
|
|
||||||
empty($upload_type) && $upload_type = $uploadConfig['upload_type'];
|
|
||||||
|
|
||||||
$this->uploadType = $upload_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validate(File $file, $allow_ext = null, $allow_size = null, $fail_exception = false)
|
|
||||||
{
|
|
||||||
$uploadConfig = sysconfig('upload');
|
|
||||||
|
|
||||||
if (!is_null($allow_ext)) {
|
|
||||||
$uploadConfig['upload_allow_ext'] = $allow_ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($allow_size)) {
|
|
||||||
$uploadConfig['upload_allow_size'] = $allow_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
$rule = [
|
|
||||||
'upload_type|指定上传类型有误' => "in:{$uploadConfig['upload_allow_type']}",
|
|
||||||
'file|文件' => "require|file|fileExt:{$uploadConfig['upload_allow_ext']}|fileSize:{$uploadConfig['upload_allow_size']}",
|
|
||||||
];
|
|
||||||
|
|
||||||
$validat_result = Validate::failException($fail_exception)->check([
|
|
||||||
'upload_type' => $this->uploadType,
|
|
||||||
'file' => $file
|
|
||||||
], $rule);
|
|
||||||
|
|
||||||
if (!$validat_result) {
|
|
||||||
return $validat_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 出于性能原因,您可以注释掉下面的代码
|
|
||||||
$file_path = $file->getRealPath();
|
|
||||||
|
|
||||||
if (strpos(file_get_contents($file_path), '<?php') !== false) {
|
|
||||||
if ($fail_exception) {
|
|
||||||
throw new ValidateException("文件含有PHP注入代码");
|
|
||||||
} else {
|
|
||||||
return '文件含有PHP注入代码';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateException($file, $allow_ext = null, $allow_size = null)
|
|
||||||
{
|
|
||||||
return $this->validate($file, $allow_ext, $allow_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function url($save_name)
|
|
||||||
{
|
|
||||||
$url = Filesystem::disk($this->uploadType)->url($save_name);
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 存储文件
|
|
||||||
*
|
|
||||||
* @param File $file
|
|
||||||
* @param string|null $save_name
|
|
||||||
* @param boolean $force_save 指定$save_name才可以用,设为true强制写入(不报错,如果存在则删除),否则是驱动的默认行为(覆盖、失败、异常)
|
|
||||||
* @param string $upload_dir
|
|
||||||
* @param bool $disable_model
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function save(File $file, string $save_name = null, $force_save = false, $upload_dir = 'upload', $disable_model = false)
|
|
||||||
{
|
|
||||||
|
|
||||||
$model_file = null;
|
|
||||||
|
|
||||||
if ($force_save && !is_null($save_name)) {
|
|
||||||
$file_path = $upload_dir . '/' . $save_name;
|
|
||||||
if (Filesystem::disk($this->uploadType)->has($file_path)) {
|
|
||||||
$model_file = SystemUploadfile::where('save_name', $save_name)->where('upload_type', $this->uploadType)->find();
|
|
||||||
Filesystem::disk($this->uploadType)->delete($file_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($model_file)) {
|
|
||||||
$model_file = new SystemUploadfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
$model_file->upload_type = $this->uploadType;
|
|
||||||
$model_file->file_ext = strtolower($file->extension());
|
|
||||||
|
|
||||||
|
|
||||||
if ($file instanceof UploadedFile) {
|
|
||||||
|
|
||||||
$model_file->original_name = $file->getOriginalName();
|
|
||||||
$model_file->mime_type = $file->getOriginalMime();
|
|
||||||
} else {
|
|
||||||
$model_file->original_name = $file->getFilename();
|
|
||||||
$model_file->mime_type = $file->getMime();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$save_name = Filesystem::disk($this->uploadType)->putFile($upload_dir, $file, function () use ($save_name, $file) {
|
|
||||||
if (!is_null($save_name)) {
|
|
||||||
|
|
||||||
$ext_name = $file->extension();
|
|
||||||
|
|
||||||
if (empty($ext_name)) {
|
|
||||||
return $save_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
$list_name = explode('.', $save_name);
|
|
||||||
|
|
||||||
array_pop($list_name);
|
|
||||||
|
|
||||||
return implode('.', $list_name);
|
|
||||||
}
|
|
||||||
return date('Ymd') . '/' . uniqid();
|
|
||||||
});
|
|
||||||
|
|
||||||
$url = $this->url($save_name);
|
|
||||||
|
|
||||||
$model_file->url = $url;
|
|
||||||
$model_file->save_name = $save_name;
|
|
||||||
|
|
||||||
$model_file->sha1 = $file->sha1();
|
|
||||||
|
|
||||||
$model_file->file_size = $file->getSize();
|
|
||||||
|
|
||||||
if (!$disable_model) {
|
|
||||||
$model_file->save();
|
|
||||||
}
|
|
||||||
return $model_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function saveUrlFile($url, string $save_name = null, $force_save = false, $upload_dir = 'upload', $disable_model = false)
|
|
||||||
{
|
|
||||||
|
|
||||||
$runtime_path = App::getRuntimePath() . 'upload/temp/' . basename($url);
|
|
||||||
|
|
||||||
PathTools::intiDir($runtime_path);
|
|
||||||
|
|
||||||
|
|
||||||
$file_content = file_get_contents($url);
|
|
||||||
|
|
||||||
file_put_contents($runtime_path, $file_content);
|
|
||||||
|
|
||||||
$file = new File($runtime_path);
|
|
||||||
|
|
||||||
$response = $this->save($file, $save_name, $force_save, $upload_dir, $disable_model);
|
|
||||||
|
|
||||||
unlink($runtime_path);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,113 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\tools;
|
namespace app\common\tools;
|
||||||
|
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
use base\common\tools\ExportToolsBase;
|
||||||
|
|
||||||
class ExportTools
|
class ExportTools extends ExportToolsBase
|
||||||
{
|
{
|
||||||
public static function excel($model, $where = [], $fields = [], $image_fields = [], $select_fields = [], $date_fields = [])
|
|
||||||
{
|
|
||||||
|
|
||||||
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
|
||||||
$sheet = $spreadsheet->getActiveSheet();
|
|
||||||
|
|
||||||
$write_col = 1;
|
|
||||||
$write_line = 1;
|
|
||||||
foreach ($fields as $field_key => $field_name) {
|
|
||||||
$col_key = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($write_col);
|
|
||||||
$sheet->setCellValue($col_key . $write_line, $field_name);
|
|
||||||
$sheet->getColumnDimension($col_key)->setWidth(mb_strlen($field_name) * 3);
|
|
||||||
|
|
||||||
$write_col++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$runtime_file_list = [];
|
|
||||||
|
|
||||||
$limit = 1000;
|
|
||||||
|
|
||||||
$page = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
$page++;
|
|
||||||
$list = $model
|
|
||||||
->where($where)->page($page, $limit)->select();
|
|
||||||
foreach ($list as $list_index => $item) {
|
|
||||||
$write_line++;
|
|
||||||
$write_col = 1;
|
|
||||||
|
|
||||||
foreach ($fields as $field_key => $field_name) {
|
|
||||||
$col_key = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($write_col);
|
|
||||||
|
|
||||||
$cel = null;
|
|
||||||
|
|
||||||
$value = \think\helper\Arr::get($item, $field_key);
|
|
||||||
|
|
||||||
if (in_array($field_key, $image_fields)) {
|
|
||||||
// 是图片
|
|
||||||
$cel = $value;
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (filter_var($value, FILTER_VALIDATE_URL)) {
|
|
||||||
|
|
||||||
$runtime_file = PathTools::tempBuildPath(uniqid());
|
|
||||||
|
|
||||||
$runtime_file_list[] = $runtime_file;
|
|
||||||
|
|
||||||
file_put_contents($runtime_file, file_get_contents($value));
|
|
||||||
|
|
||||||
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
|
||||||
$drawing->setName($field_name);
|
|
||||||
$drawing->setDescription($field_key);
|
|
||||||
$drawing->setPath($runtime_file);
|
|
||||||
$drawing->setHeight(36);
|
|
||||||
|
|
||||||
$drawing->setCoordinates($col_key . $write_line);
|
|
||||||
$drawing->setWorksheet($sheet);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $th) {
|
|
||||||
$message = $th->getMessage();
|
|
||||||
|
|
||||||
$cel .= "\n" . $message;
|
|
||||||
}
|
|
||||||
} else if (array_key_exists($field_key, $select_fields)) {
|
|
||||||
// 需要设置选项
|
|
||||||
|
|
||||||
$cel = $select_fields[$field_key][$value] ?? $value;
|
|
||||||
} else if (in_array($field_key, $date_fields)) {
|
|
||||||
if (empty($value)) {
|
|
||||||
$cel = '';
|
|
||||||
} else {
|
|
||||||
$cel = date('Y-m-d H:i:s', $value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$cel = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sheet->setCellValueExplicit($col_key . $write_line, $cel, DataType::TYPE_STRING);
|
|
||||||
$write_col++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($list) < $limit) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$writer->save('php://output');
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_clean();
|
|
||||||
|
|
||||||
foreach ($runtime_file_list as $runtime_file) {
|
|
||||||
if (file_exists($runtime_file)) {
|
|
||||||
unlink($runtime_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,161 +2,8 @@
|
|||||||
|
|
||||||
namespace app\common\tools;
|
namespace app\common\tools;
|
||||||
|
|
||||||
use Localheinz\Diff\Differ;
|
use base\common\tools\PathToolsBase;
|
||||||
use think\facade\App;
|
|
||||||
|
|
||||||
class PathTools
|
class PathTools extends PathToolsBase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* 系统生成的文件,这些文件应当是可以任意删除的.
|
|
||||||
*
|
|
||||||
* @param string $file_name
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function publicBuildPath($file_name)
|
|
||||||
{
|
|
||||||
$file_path = App::getRootPath() . 'public/build/' . $file_name;
|
|
||||||
|
|
||||||
return self::intiDir($file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function publicBuildSaveName($file_name)
|
|
||||||
{
|
|
||||||
return '/build/' . $file_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function safeBuildPath($save_name)
|
|
||||||
{
|
|
||||||
$file_path = App::getRootPath() . 'storage/' . $save_name;
|
|
||||||
|
|
||||||
return self::intiDir($file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function tempBuildPath($file_name)
|
|
||||||
{
|
|
||||||
$runtime_path = App::getRuntimePath() . 'temp/' . $file_name;
|
|
||||||
|
|
||||||
return self::intiDir($runtime_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function intiDir($file_path, $is_dirname = false)
|
|
||||||
{
|
|
||||||
if (!$is_dirname) {
|
|
||||||
$dir_name = dirname($file_path);
|
|
||||||
} else {
|
|
||||||
$dir_name = $file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir($dir_name)) {
|
|
||||||
mkdir($dir_name, 0777, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function removeDir($dir_name)
|
|
||||||
{
|
|
||||||
if (!is_dir($dir_name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos(strtolower(PHP_OS), 'win') === 0) {
|
|
||||||
$dir_name = static::formatWinPath($dir_name);
|
|
||||||
exec("rd /s /q {$dir_name}", $output);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$handle = opendir($dir_name);
|
|
||||||
while (false !== ($file = readdir($handle))) {
|
|
||||||
if ($file != '.' && $file != '..') {
|
|
||||||
is_dir("$dir_name/$file") ? self::removeDir("$dir_name/$file") : unlink("$dir_name/$file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir($handle);
|
|
||||||
|
|
||||||
return rmdir($dir_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function mapDir($dir, $callback = null)
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
$cdir = scandir($dir);
|
|
||||||
foreach ($cdir as $key => $value) {
|
|
||||||
if (!in_array($value, ['.', '..'])) {
|
|
||||||
$current_path = $dir . DS . $value;
|
|
||||||
if (is_dir($current_path)) {
|
|
||||||
$result[$value] = self::mapDir($current_path, $callback);
|
|
||||||
} else {
|
|
||||||
if (is_callable($callback)) {
|
|
||||||
$result[$value] = $callback($current_path, $value, $dir);
|
|
||||||
} else {
|
|
||||||
$result[$value] = $current_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function formatWinPath($content)
|
|
||||||
{
|
|
||||||
return str_replace('/', '\\', $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function compareFiles($a, $b, $return_diff = false):bool|string
|
|
||||||
{
|
|
||||||
$result = true;
|
|
||||||
|
|
||||||
// Check if filesize is different
|
|
||||||
if (filesize($a) !== filesize($b)) {
|
|
||||||
$result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result) {
|
|
||||||
// Check if content is different
|
|
||||||
$ah = fopen($a, 'rb');
|
|
||||||
$bh = fopen($b, 'rb');
|
|
||||||
|
|
||||||
while (!feof($ah)) {
|
|
||||||
if (fread($ah, 8192) != fread($bh, 8192)) {
|
|
||||||
$result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($ah);
|
|
||||||
fclose($bh);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
// 如果前面的方法认为文件变化,那么以文本变化的方式识别是否变化,并给出变化的内容
|
|
||||||
$text_mime_type = [];
|
|
||||||
$text_mime_type[] = 'text/html';
|
|
||||||
$text_mime_type[] = 'text/plain';
|
|
||||||
$text_mime_type[] = 'text/css';
|
|
||||||
$text_mime_type[] = 'image/svg+xml';
|
|
||||||
$text_mime_type[] = 'text/x-php';
|
|
||||||
$text_mime_type[] = 'application/json';
|
|
||||||
$text_mime_type[] = 'application/x-wine-extension-ini';
|
|
||||||
|
|
||||||
if (in_array(mime_content_type($a), $text_mime_type) && in_array(mime_content_type($b), $text_mime_type)) {
|
|
||||||
$a_content = file_get_contents($a);
|
|
||||||
$b_content = file_get_contents($b);
|
|
||||||
|
|
||||||
$diff = new Differ();
|
|
||||||
|
|
||||||
$diff_content = $diff->diff($a_content, $b_content);
|
|
||||||
|
|
||||||
if (!empty($diff_content)) {
|
|
||||||
$result = $return_diff ? $diff_content : false;
|
|
||||||
} else {
|
|
||||||
$result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,148 +2,12 @@
|
|||||||
|
|
||||||
namespace app\common\traits;
|
namespace app\common\traits;
|
||||||
|
|
||||||
use think\exception\HttpResponseException;
|
use base\common\traits\JumpTraitBase;
|
||||||
use think\Response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait JumpTrait
|
* Trait JumpTrait.
|
||||||
* @package app\common\traits
|
|
||||||
*/
|
*/
|
||||||
trait JumpTrait
|
trait JumpTrait
|
||||||
{
|
{
|
||||||
|
use JumpTraitBase;
|
||||||
/**
|
|
||||||
* 操作成功跳转的快捷方法
|
|
||||||
* @access protected
|
|
||||||
* @param mixed $msg 提示信息
|
|
||||||
* @param mixed $data 返回的数据
|
|
||||||
* @param string $url 跳转的 URL 地址
|
|
||||||
* @param int $wait 跳转等待时间
|
|
||||||
* @param array $header 发送的 Header 信息
|
|
||||||
* @return void
|
|
||||||
* @throws HttpResponseException
|
|
||||||
*/
|
|
||||||
protected function success($msg = '', $data = '', $url = null, $wait = 3, array $header = [])
|
|
||||||
{
|
|
||||||
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
|
|
||||||
$url = $_SERVER["HTTP_REFERER"];
|
|
||||||
} elseif ($url) {
|
|
||||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : app('route')->buildUrl($url)->__toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = [
|
|
||||||
'code' => 1,
|
|
||||||
'msg' => $msg,
|
|
||||||
'data' => $data,
|
|
||||||
'url' => $url,
|
|
||||||
'wait' => $wait,
|
|
||||||
];
|
|
||||||
|
|
||||||
$type = $this->getResponseType();
|
|
||||||
if ($type == 'html') {
|
|
||||||
|
|
||||||
if (is_null($result['url'])) {
|
|
||||||
$result['url'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = view(app('config')->get('app.dispatch_success_tmpl'), $result);
|
|
||||||
} elseif ($type == 'json') {
|
|
||||||
$response = json($result);
|
|
||||||
}
|
|
||||||
throw new HttpResponseException($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作错误跳转的快捷方法
|
|
||||||
* @access protected
|
|
||||||
* @param mixed $msg 提示信息
|
|
||||||
* @param mixed $data 返回的数据
|
|
||||||
* @param string $url 跳转的 URL 地址
|
|
||||||
* @param int $wait 跳转等待时间
|
|
||||||
* @param array $header 发送的 Header 信息
|
|
||||||
* @return void
|
|
||||||
* @throws HttpResponseException
|
|
||||||
*/
|
|
||||||
protected function error($msg = '', $data = '', $url = null, $wait = 3, array $header = [])
|
|
||||||
{
|
|
||||||
if (is_null($url)) {
|
|
||||||
$url = request()->isAjax() ? '' : 'javascript:history.back(-1);';
|
|
||||||
} elseif ($url) {
|
|
||||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : app('route')->buildUrl($url)->__toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = $this->getResponseType();
|
|
||||||
$result = [
|
|
||||||
'code' => 0,
|
|
||||||
'msg' => $msg,
|
|
||||||
'data' => $data,
|
|
||||||
'url' => $url,
|
|
||||||
'wait' => $wait,
|
|
||||||
];
|
|
||||||
if ($type == 'html') {
|
|
||||||
if (is_null($result['url'])) {
|
|
||||||
$result['url'] = '';
|
|
||||||
}
|
|
||||||
$response = view(app('config')->get('app.dispatch_error_tmpl'), $result);
|
|
||||||
} elseif ($type == 'json') {
|
|
||||||
$response = json($result);
|
|
||||||
}
|
|
||||||
throw new HttpResponseException($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回封装后的 API 数据到客户端
|
|
||||||
* @access protected
|
|
||||||
* @param mixed $data 要返回的数据
|
|
||||||
* @param int $code 返回的 code
|
|
||||||
* @param mixed $msg 提示信息
|
|
||||||
* @param string $type 返回数据格式
|
|
||||||
* @param array $header 发送的 Header 信息
|
|
||||||
* @return void
|
|
||||||
* @throws HttpResponseException
|
|
||||||
*/
|
|
||||||
protected function result($data, $code = 0, $msg = '', $type = '', array $header = [])
|
|
||||||
{
|
|
||||||
$result = [
|
|
||||||
'code' => $code,
|
|
||||||
'msg' => $msg,
|
|
||||||
'time' => time(),
|
|
||||||
'data' => $data,
|
|
||||||
];
|
|
||||||
$type = $type ?: $this->getResponseType();
|
|
||||||
$response = Response::create($result, $type)->header($header);
|
|
||||||
|
|
||||||
throw new HttpResponseException($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL 重定向
|
|
||||||
* @access protected
|
|
||||||
* @param string $url 跳转的 URL 表达式
|
|
||||||
* @param array|int $params 其它 URL 参数
|
|
||||||
* @param int $code http code
|
|
||||||
* @param array $with 隐式传参
|
|
||||||
* @return void
|
|
||||||
* @throws HttpResponseException
|
|
||||||
*/
|
|
||||||
protected function redirect($url = [], $params = [], $code = 302)
|
|
||||||
{
|
|
||||||
if (is_integer($params)) {
|
|
||||||
$code = $params;
|
|
||||||
$params = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = Response::create($url, 'redirect', $code);
|
|
||||||
throw new HttpResponseException($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前的 response 输出类型
|
|
||||||
* @access protected
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function getResponseType()
|
|
||||||
{
|
|
||||||
return (request()->isJson() || request()->isAjax() || request()->isPost()) ? 'json' : 'html';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// +----------------------------------------------------------------------
|
// +----------------------------------------------------------------------
|
||||||
// | 应用设置
|
// | 应用设置
|
||||||
// +----------------------------------------------------------------------
|
// +----------------------------------------------------------------------
|
||||||
@@ -7,39 +8,39 @@ use think\facade\Env;
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
// 应用地址
|
// 应用地址
|
||||||
'app_host' => Env::get('app.host', ''),
|
'app_host' => Env::get('app.host', ''),
|
||||||
// 应用的命名空间
|
// 应用的命名空间
|
||||||
'app_namespace' => '',
|
'app_namespace' => '',
|
||||||
// 是否启用路由
|
// 是否启用路由
|
||||||
'with_route' => true,
|
'with_route' => true,
|
||||||
// 是否启用事件
|
// 是否启用事件
|
||||||
'with_event' => true,
|
'with_event' => true,
|
||||||
// 开启应用快速访问
|
// 开启应用快速访问
|
||||||
'app_express' => true,
|
'app_express' => true,
|
||||||
// 默认应用
|
// 默认应用
|
||||||
'default_app' => 'index',
|
'default_app' => 'index',
|
||||||
// 默认时区
|
// 默认时区
|
||||||
'default_timezone' => 'Asia/Shanghai',
|
'default_timezone' => 'Asia/Shanghai',
|
||||||
// 应用映射(自动多应用模式有效)
|
// 应用映射(自动多应用模式有效)
|
||||||
'app_map' => [
|
'app_map' => [
|
||||||
Env::get('adminsystem.admin', 'admin') => 'admin',
|
Env::get('adminsystem.admin', 'admin') => 'admin',
|
||||||
],
|
],
|
||||||
// 后台别名
|
// 后台别名
|
||||||
'admin_alias_name' => Env::get('adminsystem.admin', 'admin'),
|
'admin_alias_name' => Env::get('adminsystem.admin', 'admin'),
|
||||||
// 域名绑定(自动多应用模式有效)
|
// 域名绑定(自动多应用模式有效)
|
||||||
'domain_bind' => [],
|
'domain_bind' => [],
|
||||||
// 禁止URL访问的应用列表(自动多应用模式有效)
|
// 禁止URL访问的应用列表(自动多应用模式有效)
|
||||||
'deny_app_list' => ['common'],
|
'deny_app_list' => ['common'],
|
||||||
// 异常页面的模板文件
|
// 异常页面的模板文件
|
||||||
'exception_tmpl' => Env::get('app_debug') == 1 ? app()->getThinkPath() . 'tpl/think_exception.tpl' : app()->getBasePath() . 'common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'think_exception.tpl',
|
'exception_tmpl' => Env::get('app_debug') == 1 ? app()->getThinkPath() . 'tpl/think_exception.tpl' : app_file_path('common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'think_exception.tpl'),
|
||||||
// 跳转页面的成功模板文件
|
// 跳转页面的成功模板文件
|
||||||
'dispatch_success_tmpl' => app()->getBasePath() . 'common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'dispatch_jump.tpl',
|
'dispatch_success_tmpl' => app_file_path('common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'dispatch_jump.tpl'),
|
||||||
// 跳转页面的失败模板文件
|
// 跳转页面的失败模板文件
|
||||||
'dispatch_error_tmpl' => app()->getBasePath() . 'common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'dispatch_jump.tpl',
|
'dispatch_error_tmpl' => app_file_path('common' . DIRECTORY_SEPARATOR . 'tpl' . DIRECTORY_SEPARATOR . 'dispatch_jump.tpl'),
|
||||||
// 错误显示信息,非调试模式有效
|
// 错误显示信息,非调试模式有效
|
||||||
'error_message' => '页面错误!请稍后再试~',
|
'error_message' => '页面错误!请稍后再试~',
|
||||||
// 显示错误信息
|
// 显示错误信息
|
||||||
'show_error_msg' => false,
|
'show_error_msg' => false,
|
||||||
// 静态资源上传到OSS前缀
|
// 静态资源上传到OSS前缀
|
||||||
'oss_static_prefix' => Env::get('adminsystem.oss_static_prefix', 'static_ulthon_admin'),
|
'oss_static_prefix' => Env::get('adminsystem.oss_static_prefix', 'static_ulthon_admin'),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -14,11 +14,16 @@ $skip_files[] = 'README.md';
|
|||||||
$skip_files[] = 'README.en.md';
|
$skip_files[] = 'README.en.md';
|
||||||
$skip_files[] = 'composer.json';
|
$skip_files[] = 'composer.json';
|
||||||
|
|
||||||
|
$skip_files[] = 'app/common/event/AdminLoginSuccess/LogEvent.php';
|
||||||
|
$skip_files[] = 'app/common/event/AdminLoginType/DemoEvent.php';
|
||||||
|
|
||||||
$config['skip_files'] = $skip_files;
|
$config['skip_files'] = $skip_files;
|
||||||
|
|
||||||
$skip_dir = [];
|
$skip_dir = [];
|
||||||
|
|
||||||
$skip_dir[] = 'runtime';
|
$skip_dir[] = 'runtime';
|
||||||
$skip_dir[] = 'vendor';
|
$skip_dir[] = 'vendor';
|
||||||
|
|
||||||
$skip_dir[] = 'app/admin/controller';
|
$skip_dir[] = 'app/admin/controller';
|
||||||
$skip_dir[] = 'app/admin/middleware';
|
$skip_dir[] = 'app/admin/middleware';
|
||||||
$skip_dir[] = 'app/admin/model';
|
$skip_dir[] = 'app/admin/model';
|
||||||
@@ -26,20 +31,37 @@ $skip_dir[] = 'app/admin/service';
|
|||||||
$skip_dir[] = 'app/admin/traits';
|
$skip_dir[] = 'app/admin/traits';
|
||||||
$skip_dir[] = 'app/admin/view';
|
$skip_dir[] = 'app/admin/view';
|
||||||
|
|
||||||
|
$skip_dir[] = 'app/common/controller';
|
||||||
|
$skip_dir[] = 'app/common/command';
|
||||||
|
$skip_dir[] = 'app/common/constants';
|
||||||
|
$skip_dir[] = 'app/common/exception';
|
||||||
|
$skip_dir[] = 'app/common/model';
|
||||||
|
$skip_dir[] = 'app/common/provider';
|
||||||
|
$skip_dir[] = 'app/common/service';
|
||||||
|
$skip_dir[] = 'app/common/tools';
|
||||||
|
$skip_dir[] = 'app/common/traits';
|
||||||
|
$skip_dir[] = 'app/common/tpl';
|
||||||
|
|
||||||
$config['skip_dir'] = $skip_dir;
|
$config['skip_dir'] = $skip_dir;
|
||||||
|
|
||||||
// append 如果当前版本不存在,则追加,如果存在,则不应当覆盖
|
// append 如果当前版本不存在,则追加,如果存在,则不应当覆盖
|
||||||
// append 的文件应当在skip内部
|
// append 的文件应当在skip内部
|
||||||
|
|
||||||
$append_files = [];
|
$append_files = [];
|
||||||
|
|
||||||
$append_files[] = 'app/common/app/functions.php';
|
$append_files[] = 'app/common/app/functions.php';
|
||||||
$append_files[] = 'app/common/app/listen.php';
|
$append_files[] = 'app/common/app/listen.php';
|
||||||
$append_files[] = 'app/common/app/middleware.php';
|
$append_files[] = 'app/common/app/middleware.php';
|
||||||
$append_files[] = 'app/common/app/service.php';
|
$append_files[] = 'app/common/app/service.php';
|
||||||
$append_files[] = 'app/common/app/provider.php';
|
$append_files[] = 'app/common/app/provider.php';
|
||||||
|
|
||||||
|
$append_files[] = 'app/common/event/AdminLoginSuccess/LogEvent.php';
|
||||||
|
$append_files[] = 'app/common/event/AdminLoginType/DemoEvent.php';
|
||||||
|
|
||||||
$config['append_files'] = $append_files;
|
$config['append_files'] = $append_files;
|
||||||
|
|
||||||
$append_dir = [];
|
$append_dir = [];
|
||||||
|
|
||||||
$append_dir[] = 'app/admin/controller';
|
$append_dir[] = 'app/admin/controller';
|
||||||
$append_dir[] = 'app/admin/middleware';
|
$append_dir[] = 'app/admin/middleware';
|
||||||
$append_dir[] = 'app/admin/model';
|
$append_dir[] = 'app/admin/model';
|
||||||
@@ -47,6 +69,17 @@ $append_dir[] = 'app/admin/service';
|
|||||||
$append_dir[] = 'app/admin/traits';
|
$append_dir[] = 'app/admin/traits';
|
||||||
$append_dir[] = 'app/admin/view';
|
$append_dir[] = 'app/admin/view';
|
||||||
|
|
||||||
|
$append_dir[] = 'app/common/controller';
|
||||||
|
$append_dir[] = 'app/common/command';
|
||||||
|
$append_dir[] = 'app/common/constants';
|
||||||
|
$append_dir[] = 'app/common/exception';
|
||||||
|
$append_dir[] = 'app/common/model';
|
||||||
|
$append_dir[] = 'app/common/provider';
|
||||||
|
$append_dir[] = 'app/common/service';
|
||||||
|
$append_dir[] = 'app/common/tools';
|
||||||
|
$append_dir[] = 'app/common/traits';
|
||||||
|
$append_dir[] = 'app/common/tpl';
|
||||||
|
|
||||||
$config['append_dir'] = $append_dir;
|
$config['append_dir'] = $append_dir;
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|||||||
142
extend/base/common/command/CurdBase.php
Normal file
142
extend/base/common/command/CurdBase.php
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\command;
|
||||||
|
|
||||||
|
use app\admin\service\curd\BuildCurdService;
|
||||||
|
use app\common\tools\PathTools;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\Exception;
|
||||||
|
use think\facade\App;
|
||||||
|
|
||||||
|
class CurdBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('curd')
|
||||||
|
->addOption('table', 't', Option::VALUE_REQUIRED, '主表名', null)
|
||||||
|
->addOption('controllerFilename', 'c', Option::VALUE_REQUIRED, '控制器文件名', null)
|
||||||
|
->addOption('modelFilename', 'm', Option::VALUE_REQUIRED, '主表模型文件名', null)
|
||||||
|
//
|
||||||
|
->addOption('force', 'f', Option::VALUE_NONE, '强制覆盖模式')
|
||||||
|
->addOption('delete', 'd', Option::VALUE_NONE, '删除模式')
|
||||||
|
->addOption('runtime', 'r', Option::VALUE_NONE, '临时生成')
|
||||||
|
->setDescription('一键curd命令服务');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
$table = $input->getOption('table');
|
||||||
|
$controllerFilename = $input->getOption('controllerFilename');
|
||||||
|
$modelFilename = $input->getOption('modelFilename');
|
||||||
|
|
||||||
|
$force = 0;
|
||||||
|
$delete = 0;
|
||||||
|
|
||||||
|
if ($input->hasOption('force')) {
|
||||||
|
$force = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->hasOption('delete')) {
|
||||||
|
$delete = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($table)) {
|
||||||
|
$output->error('请设置主表');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$build = (new BuildCurdService())
|
||||||
|
->setTable($table)
|
||||||
|
->setForce($force);
|
||||||
|
|
||||||
|
if ($input->hasOption('runtime')) {
|
||||||
|
$runtime_path = App::getRuntimePath() . 'source/build/' . date('YmdHis') . '/';
|
||||||
|
dump($runtime_path);
|
||||||
|
PathTools::intiDir($runtime_path . 'a.temp');
|
||||||
|
|
||||||
|
$build->setRootDir($runtime_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $build->getTableColumns();
|
||||||
|
|
||||||
|
$relations = [];
|
||||||
|
|
||||||
|
foreach ($columns as $field => $column) {
|
||||||
|
if (isset($column['formType']) && $column['formType'] == 'relation') {
|
||||||
|
$define = $column['define'];
|
||||||
|
|
||||||
|
if (!isset($define['table'])) {
|
||||||
|
$output->error("关联字段{$field}没有设置关联表名称");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relations[] = [
|
||||||
|
'table' => $define['table'],
|
||||||
|
'foreignKey' => $field,
|
||||||
|
'primaryKey' => $define['primaryKey'] ?? null,
|
||||||
|
'modelFilename' => $define['modelFilename'] ?? null,
|
||||||
|
'onlyFileds' => isset($define['onlyFileds']) ? explode('|', $define['onlyFileds']) : [],
|
||||||
|
'relationBindSelect' => $define['relationBindSelect'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
!empty($controllerFilename) && $build = $build->setControllerFilename($controllerFilename);
|
||||||
|
!empty($modelFilename) && $build = $build->setModelFilename($modelFilename);
|
||||||
|
|
||||||
|
foreach ($relations as $relation) {
|
||||||
|
$build = $build->setRelation($relation['table'], $relation['foreignKey'], $relation['primaryKey'], $relation['modelFilename'], $relation['onlyFileds'], $relation['relationBindSelect']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$build = $build->render();
|
||||||
|
$fileList = $build->getFileList();
|
||||||
|
|
||||||
|
if (!$delete) {
|
||||||
|
if ($force) {
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>');
|
||||||
|
foreach ($fileList as $key => $val) {
|
||||||
|
$output->writeln($key);
|
||||||
|
}
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>');
|
||||||
|
|
||||||
|
$ask_force_delete_result = $output->confirm($input, '确定强制生成上方所有文件? 如果文件存在会直接覆盖。');
|
||||||
|
|
||||||
|
if (!$ask_force_delete_result) {
|
||||||
|
throw new Exception('取消文件CURD生成操作');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result = $build->create();
|
||||||
|
$output->info('自动生成CURD成功');
|
||||||
|
} else {
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>');
|
||||||
|
foreach ($fileList as $key => $val) {
|
||||||
|
$output->writeln($key);
|
||||||
|
}
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>');
|
||||||
|
|
||||||
|
$ask_force_delete_result = $output->confirm($input, '确定删除上方所有文件? ');
|
||||||
|
|
||||||
|
if (!$ask_force_delete_result) {
|
||||||
|
throw new Exception('取消删除文件操作');
|
||||||
|
}
|
||||||
|
$result = $build->delete();
|
||||||
|
$output->info('>>>>>>>>>>>>>>>');
|
||||||
|
$output->info('删除自动生成CURD文件成功');
|
||||||
|
}
|
||||||
|
$output->info('>>>>>>>>>>>>>>>');
|
||||||
|
foreach ($result as $vo) {
|
||||||
|
$output->info($vo);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->error($e->getMessage());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
extend/base/common/command/NodeBase.php
Normal file
64
extend/base/common/command/NodeBase.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\command;
|
||||||
|
|
||||||
|
use app\admin\model\SystemNode;
|
||||||
|
use app\admin\service\NodeService;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
|
||||||
|
class NodeBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('node')
|
||||||
|
->addOption('force', null, Option::VALUE_REQUIRED, '是否强制刷新', 0)
|
||||||
|
->setDescription('系统节点刷新服务');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
$force = $input->getOption('force');
|
||||||
|
$output->writeln('========正在刷新节点服务:=====' . date('Y-m-d H:i:s'));
|
||||||
|
$check = $this->refresh($force);
|
||||||
|
$check !== true && $output->writeln('节点刷新失败:' . $check);
|
||||||
|
$output->writeln('刷新完成:' . date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function refresh($force)
|
||||||
|
{
|
||||||
|
$nodeList = (new NodeService())->getNodelist();
|
||||||
|
if (empty($nodeList)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$model = new SystemNode();
|
||||||
|
try {
|
||||||
|
if ($force == 1) {
|
||||||
|
$updateNodeList = $model->whereIn('node', array_column($nodeList, 'node'))->select();
|
||||||
|
$formatNodeList = array_format_key($nodeList, 'node');
|
||||||
|
foreach ($updateNodeList as $vo) {
|
||||||
|
isset($formatNodeList[$vo['node']]) && $model->where('id', $vo['id'])->update([
|
||||||
|
'title' => $formatNodeList[$vo['node']]['title'],
|
||||||
|
'is_auth' => $formatNodeList[$vo['node']]['is_auth'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$existNodeList = $model->field('node,title,type,is_auth')->select();
|
||||||
|
foreach ($nodeList as $key => $vo) {
|
||||||
|
foreach ($existNodeList as $v) {
|
||||||
|
if ($vo['node'] == $v->node) {
|
||||||
|
unset($nodeList[$key]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$model->insertAll($nodeList);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
extend/base/common/command/OssStaticBase.php
Normal file
49
extend/base/common/command/OssStaticBase.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\command;
|
||||||
|
|
||||||
|
use app\common\service\UploadService;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\facade\Filesystem;
|
||||||
|
use think\File;
|
||||||
|
|
||||||
|
class OssStaticBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('OssStatic')
|
||||||
|
->setDescription('将静态资源上传到oss上');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
$output->writeln('========正在上传静态资源到OSS上:========' . date('Y-m-d H:i:s'));
|
||||||
|
|
||||||
|
$list = Filesystem::disk('local_static')->listContents('/', true);
|
||||||
|
$upload_service = new UploadService();
|
||||||
|
$uploadPrefix = config('app.oss_static_prefix', 'oss_static_prefix');
|
||||||
|
|
||||||
|
foreach ($list as $file_item) {
|
||||||
|
if ($file_item['type'] != 'file') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_path = $file_item['path'];
|
||||||
|
|
||||||
|
$file_path = Filesystem::disk('local_static')->path($file_path);
|
||||||
|
|
||||||
|
$file = new File($file_path, false);
|
||||||
|
|
||||||
|
$save_name = $file_item['path'];
|
||||||
|
try {
|
||||||
|
$model_file = $upload_service->save($file, $save_name, true, $uploadPrefix, true);
|
||||||
|
$output->info('文件上传成功:' . $save_name . '。上传地址:' . $model_file['url']);
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
$output->error('文件上传失败:' . $save_name . '。错误信息:' . $th->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output->writeln('========已完成静态资源上传到OSS上:========' . date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
}
|
||||||
125
extend/base/common/command/TimerBase.php
Normal file
125
extend/base/common/command/TimerBase.php
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Promise\Utils;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\facade\Cache;
|
||||||
|
use think\facade\Log;
|
||||||
|
|
||||||
|
class TimerBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('timer')
|
||||||
|
->addOption('temp', null, Option::VALUE_NONE)
|
||||||
|
->addOption('quit', null, Option::VALUE_NONE)
|
||||||
|
->setDescription('内置秒级定时器');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->writeln('start timer');
|
||||||
|
|
||||||
|
$site_domain = sysconfig('site', 'site_domain');
|
||||||
|
|
||||||
|
if (empty($site_domain)) {
|
||||||
|
$output->writeln('请前往后台设置站点域名(site_domain)配置项');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$output->writeln('站点域名:' . $site_domain);
|
||||||
|
|
||||||
|
$client = new Client([
|
||||||
|
'base_uri' => $site_domain,
|
||||||
|
'verify' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$config_list = include __DIR__ . '/timer/config.php';
|
||||||
|
|
||||||
|
$list_promises = [];
|
||||||
|
foreach ($config_list as $config_item) {
|
||||||
|
$config_item = static::initConfigItem($config_item);
|
||||||
|
|
||||||
|
$name = $config_item['name'];
|
||||||
|
|
||||||
|
if ($name == 'http_demo' && !env('adminsystem.is_demo', false)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_key = 'timer_' . $name;
|
||||||
|
$cache_tag = 'system_timer';
|
||||||
|
|
||||||
|
$last_exec_time = Cache::get($cache_key, 0);
|
||||||
|
|
||||||
|
if ($last_exec_time >= time() - $config_item['frequency']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::tag($cache_tag)->set($cache_key, time());
|
||||||
|
|
||||||
|
$type = $config_item['type'];
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'site':
|
||||||
|
$output->writeln(date('Y-m-d H:i:s') . ': build site request async:' . $config_item['target']);
|
||||||
|
$list_promises[$config_item['name']] = $client->getAsync($config_item['target']);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$output->writeln(date('Y-m-d H:i:s') . 'unsupport type:' . $type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($list_promises)) {
|
||||||
|
if (!$input->hasOption('quit')) {
|
||||||
|
$output->writeln(date('Y-m-d H:i:s') . ' no request');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$results = Utils::unwrap($list_promises);
|
||||||
|
$output->writeln(date('Y-m-d H:i:s') . ': request all finished');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
// throw $th;
|
||||||
|
$output->writeln('error:' . $th->getMessage());
|
||||||
|
Log::error($th->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->hasOption('temp')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function initConfigItem($config)
|
||||||
|
{
|
||||||
|
$default = [
|
||||||
|
'name' => 'http_demo',
|
||||||
|
'type' => 'site',
|
||||||
|
'target' => '',
|
||||||
|
'frequency' => 600,
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = array_merge($default, $config);
|
||||||
|
|
||||||
|
if ($data['frequency'] < 0) {
|
||||||
|
$data['frequency'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
extend/base/common/command/admin/ClearBase.php
Normal file
50
extend/base/common/command/admin/ClearBase.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command\admin;
|
||||||
|
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\facade\App;
|
||||||
|
|
||||||
|
class ClearBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('admin:clear')
|
||||||
|
->setDescription('删除开发临时生成目录');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->writeln('删除测试目录');
|
||||||
|
|
||||||
|
$command_line = '';
|
||||||
|
|
||||||
|
$dir = App::getRootPath() . '/runtime/source/';
|
||||||
|
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
$output->writeln('删除成功');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos(strtolower(PHP_OS), 'win') === 0) {
|
||||||
|
$command_line = implode(' ', ['rd', '/s', '/q', str_replace('/', '\\', $dir)]);
|
||||||
|
} else {
|
||||||
|
$command_line = implode(' ', ['rm', '-rf', $dir]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->info('删除目录:' . $command_line);
|
||||||
|
|
||||||
|
$output->info('run command: ' . $command_line);
|
||||||
|
|
||||||
|
exec($command_line);
|
||||||
|
|
||||||
|
$output->info('删除成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
48
extend/base/common/command/admin/ResetPasswordBase.php
Normal file
48
extend/base/common/command/admin/ResetPasswordBase.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command\admin;
|
||||||
|
|
||||||
|
use app\admin\model\SystemAdmin;
|
||||||
|
use app\common\constants\AdminConstant;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
|
||||||
|
class ResetPasswordBase extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('admin:reset:password')
|
||||||
|
->addOption('password', 'p', Option::VALUE_OPTIONAL)
|
||||||
|
->setDescription('重置超管密码');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->writeln('admin:reset:password');
|
||||||
|
|
||||||
|
$model_admin = SystemAdmin::find(AdminConstant::SUPER_ADMIN_ID);
|
||||||
|
if (empty($model_admin)) {
|
||||||
|
$output->writeln('管理员不存在');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$password = $input->getOption('password');
|
||||||
|
|
||||||
|
if (is_null($password)) {
|
||||||
|
$password = uniqid();
|
||||||
|
}
|
||||||
|
|
||||||
|
$model_admin->save([
|
||||||
|
'password' => password($password),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$output->writeln('密码修改为:' . $password);
|
||||||
|
}
|
||||||
|
}
|
||||||
313
extend/base/common/command/admin/UpdateBase.php
Normal file
313
extend/base/common/command/admin/UpdateBase.php
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command\admin;
|
||||||
|
|
||||||
|
use app\common\command\admin\Version;
|
||||||
|
use app\common\tools\PathTools;
|
||||||
|
use CzProject\GitPhp\Git;
|
||||||
|
use League\Flysystem\Filesystem;
|
||||||
|
use League\Flysystem\Local\LocalFilesystemAdapter;
|
||||||
|
use League\Flysystem\StorageAttributes;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\facade\App;
|
||||||
|
use think\facade\Env;
|
||||||
|
|
||||||
|
class UpdateBase extends Command
|
||||||
|
{
|
||||||
|
public const REPO = 'https://gitee.com/ulthon/ulthon_admin.git';
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('admin:update')
|
||||||
|
->addOption('reinstall', null, Option::VALUE_NONE, '重装版本')
|
||||||
|
->setDescription('the admin:update command');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->writeln('admin:update');
|
||||||
|
|
||||||
|
$this->cleanWorkpaceDir();
|
||||||
|
|
||||||
|
$current_version = Version::VERSION;
|
||||||
|
|
||||||
|
$current_version_dir = App::getRuntimePath() . '/update/' . $current_version;
|
||||||
|
|
||||||
|
$last_version_dir = App::getRuntimePath() . '/update/last';
|
||||||
|
|
||||||
|
$version_file_regx = "/\bconst VERSION\s*=\s*'[\d\.a-z]+'/";
|
||||||
|
|
||||||
|
$output->writeln('获取最新代码');
|
||||||
|
$last_version_git = new Git();
|
||||||
|
|
||||||
|
$last_version_repo = $last_version_git->cloneRepository(self::REPO, $last_version_dir);
|
||||||
|
|
||||||
|
$tags = $last_version_repo->getTags();
|
||||||
|
|
||||||
|
$update_level = Env::get('adminsystem.update_level', 'production');
|
||||||
|
|
||||||
|
if ($update_level == 'production') {
|
||||||
|
$tags = array_filter($tags, function ($value) {
|
||||||
|
if (strpos($value, '-')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($tags, function ($a, $b) {
|
||||||
|
return version_compare($a, $b);
|
||||||
|
});
|
||||||
|
|
||||||
|
$last_version = $tags[count($tags) - 1];
|
||||||
|
|
||||||
|
if ($last_version == $current_version) {
|
||||||
|
$output->writeln('当前版本为最新版本');
|
||||||
|
$this->cleanWorkpaceDir();
|
||||||
|
|
||||||
|
if ($input->hasOption('reinstall')) {
|
||||||
|
$output->writeln('重装代码');
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将最新代码切换到最新版本,因为最新的提交可能没有发布版本
|
||||||
|
$output->writeln('切换最新代码的最新版本');
|
||||||
|
$last_version_repo->checkout($last_version);
|
||||||
|
|
||||||
|
$current_version_git = new Git();
|
||||||
|
$output->writeln('获取当前版本代码');
|
||||||
|
$current_version_repo = $current_version_git->cloneRepository(self::REPO, $current_version_dir);
|
||||||
|
$output->writeln('切换版本' . $current_version);
|
||||||
|
$current_version_repo->checkout($current_version);
|
||||||
|
|
||||||
|
// 获取当前版本需要跳过的文件
|
||||||
|
$current_version_update_config = include $current_version_dir . '/config/update.php';
|
||||||
|
|
||||||
|
// 获取当前版本要替换的文件
|
||||||
|
$current_version_filesystem = new Filesystem(new LocalFilesystemAdapter($current_version_dir));
|
||||||
|
$current_version_list_files = $current_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
||||||
|
->filter(function (StorageAttributes $attributes) use ($current_version_update_config) {
|
||||||
|
if ($attributes->isDir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $attributes->path();
|
||||||
|
|
||||||
|
$skip_files = $current_version_update_config['skip_files'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($path, $skip_files)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skip_dir = $current_version_update_config['skip_dir'] ?? [];
|
||||||
|
$skip_dir[] = '.git';
|
||||||
|
|
||||||
|
foreach ($skip_dir as $dir) {
|
||||||
|
if (str_starts_with($path, $dir)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
// 对比现在的代码,检查是否有定制修改
|
||||||
|
|
||||||
|
$output->writeln('对比源码是否被定制');
|
||||||
|
$now_dir = App::getRootPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<bool|string>
|
||||||
|
*/
|
||||||
|
$changed_files = [];
|
||||||
|
|
||||||
|
foreach ($current_version_list_files as $file_path) {
|
||||||
|
$now_file_path = $now_dir . '/' . $file_path;
|
||||||
|
|
||||||
|
$current_version_file_path = $current_version_dir . '/' . $file_path;
|
||||||
|
|
||||||
|
$compare_result = PathTools::compareFiles($now_file_path, $current_version_file_path, true);
|
||||||
|
|
||||||
|
if (!$compare_result || is_string($compare_result)) {
|
||||||
|
$changed_files[$file_path] = $compare_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有定制修改则退出
|
||||||
|
|
||||||
|
if (!empty($changed_files)) {
|
||||||
|
$output->warning('无法自动更新,以下文件被定制,请还原或手动升级:');
|
||||||
|
|
||||||
|
foreach ($changed_files as $file_path => $compare_result) {
|
||||||
|
$output->warning($file_path);
|
||||||
|
|
||||||
|
if (is_string($compare_result)) {
|
||||||
|
$output->writeln($compare_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_count = count($changed_files);
|
||||||
|
|
||||||
|
$ask_result = $output->ask($input, "发现{$file_count}个文件被修改,如果您认为以上文件可以被覆盖或为检测错误,可以强制覆盖。\n是否强制继续更新?(y/n)");
|
||||||
|
|
||||||
|
if (!$ask_result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新版本需要跳过的文件
|
||||||
|
|
||||||
|
$last_version_skip_config = include $last_version_dir . '/config/update.php';
|
||||||
|
// 获取最新版本要替换的文件
|
||||||
|
$last_version_filesystem = new Filesystem(new LocalFilesystemAdapter($last_version_dir));
|
||||||
|
$last_version_list_files = $last_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
||||||
|
->filter(function (StorageAttributes $attributes) use ($last_version_skip_config) {
|
||||||
|
if ($attributes->isDir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $attributes->path();
|
||||||
|
|
||||||
|
$skip_files = $last_version_skip_config['skip_files'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($path, $skip_files)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skip_dir = $last_version_skip_config['skip_dir'] ?? [];
|
||||||
|
$skip_dir[] = '.git';
|
||||||
|
|
||||||
|
foreach ($skip_dir as $dir) {
|
||||||
|
if (str_starts_with($path, $dir)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$delete_files = array_diff($current_version_list_files, $last_version_list_files);
|
||||||
|
|
||||||
|
foreach ($delete_files as $file_path) {
|
||||||
|
$now_file_path = $now_dir . '/' . $file_path;
|
||||||
|
unlink($now_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($last_version_list_files as $file_path) {
|
||||||
|
$now_file_path = $now_dir . '/' . $file_path;
|
||||||
|
$last_file_path = $last_version_dir . '/' . $file_path;
|
||||||
|
|
||||||
|
$file_content = file_get_contents($last_file_path);
|
||||||
|
|
||||||
|
PathTools::intiDir($now_file_path);
|
||||||
|
file_put_contents($now_file_path, $file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理append的文件
|
||||||
|
$last_version_list_skip_files = $last_version_filesystem->listContents('/', Filesystem::LIST_DEEP)
|
||||||
|
->filter(function (StorageAttributes $attributes) use ($last_version_skip_config) {
|
||||||
|
if ($attributes->isDir()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $attributes->path();
|
||||||
|
|
||||||
|
if (str_starts_with($path, '.git')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skip_files = $last_version_skip_config['skip_files'] ?? [];
|
||||||
|
|
||||||
|
if (in_array($path, $skip_files)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skip_dir = $last_version_skip_config['skip_dir'] ?? [];
|
||||||
|
|
||||||
|
foreach ($skip_dir as $dir) {
|
||||||
|
if (str_starts_with($path, $dir)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
->map(fn (StorageAttributes $attributes) => $attributes->path())
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
$last_version_list_append_files = [];
|
||||||
|
|
||||||
|
foreach ($last_version_list_skip_files as $file_path) {
|
||||||
|
if (in_array($file_path, $last_version_skip_config['append_files'])) {
|
||||||
|
$last_version_list_append_files[] = $file_path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($last_version_skip_config['append_dir'] as $dir) {
|
||||||
|
if (str_starts_with($file_path, $dir)) {
|
||||||
|
$last_version_list_append_files[] = $file_path;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($last_version_list_append_files as $file_path) {
|
||||||
|
$now_file_path = $now_dir . '/' . $file_path;
|
||||||
|
$last_file_path = $last_version_dir . '/' . $file_path;
|
||||||
|
|
||||||
|
if (file_exists($now_file_path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file_content = file_get_contents($last_file_path);
|
||||||
|
|
||||||
|
PathTools::intiDir($now_file_path);
|
||||||
|
file_put_contents($now_file_path, $file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测now的composer依赖和最新的composer依赖
|
||||||
|
|
||||||
|
$last_composer_json = file_get_contents($last_version_dir . '/composer.json');
|
||||||
|
|
||||||
|
$output->writeln($last_composer_json);
|
||||||
|
$output->writeln('请参考以上最新composer文件调整您的依赖');
|
||||||
|
|
||||||
|
// 分析出最新需要的但now没有的
|
||||||
|
|
||||||
|
// 为用户整理出要手动调整的composer命令
|
||||||
|
|
||||||
|
$this->cleanWorkpaceDir();
|
||||||
|
$output->writeln('更新完成');
|
||||||
|
// 更新完成
|
||||||
|
|
||||||
|
if (!empty(Version::UPDATE_TIPS)) {
|
||||||
|
foreach (Version::UPDATE_TIPS as $content) {
|
||||||
|
$output->writeln($content);
|
||||||
|
}
|
||||||
|
$output->writeln('删除对应文件之后,可以通过以下命令重新安装');
|
||||||
|
$output->writeln('php think admin:update --resinstall');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function cleanWorkpaceDir()
|
||||||
|
{
|
||||||
|
$dir = App::getRuntimePath() . '/update/';
|
||||||
|
|
||||||
|
$this->output->writeln('清理目录 ' . $dir);
|
||||||
|
PathTools::removeDir($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
extend/base/common/command/admin/VersionBase.php
Normal file
72
extend/base/common/command/admin/VersionBase.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command\admin;
|
||||||
|
|
||||||
|
use think\App as ThinkApp;
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
|
||||||
|
class VersionBase extends Command
|
||||||
|
{
|
||||||
|
public const VERSION = 'v2.0.46';
|
||||||
|
|
||||||
|
public const LAYUI_VERSION = '2.8.16';
|
||||||
|
|
||||||
|
public const COMMENT = [
|
||||||
|
'增加扩展机制定位文件',
|
||||||
|
'将common模块实现扩展模式',
|
||||||
|
'发布新版本',
|
||||||
|
];
|
||||||
|
|
||||||
|
public const UPDATE_TIPS = [
|
||||||
|
'本次将common下的代码实现了扩展模式',
|
||||||
|
'可以删除app下common代码',
|
||||||
|
'然后重新执行更新命令,删除后会自动初始化新的代码',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('admin:version')
|
||||||
|
->addOption('push-tag', null, Option::VALUE_NONE, '使用git命令生成tag并推送')
|
||||||
|
->setDescription('查看当前ulthon_admin的版本号');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->info('当前版本号为:' . $this::VERSION);
|
||||||
|
$output->info('当前Layui版本号为:' . $this::LAYUI_VERSION);
|
||||||
|
$output->info('当前ThinkPHP版本号为:' . ThinkApp::VERSION);
|
||||||
|
|
||||||
|
$output->writeln('当前的修改说明:');
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
|
||||||
|
|
||||||
|
foreach ($this::COMMENT as $comment) {
|
||||||
|
$output->info($comment);
|
||||||
|
}
|
||||||
|
$output->writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
|
||||||
|
|
||||||
|
$output->highlight('代码托管地址:https://gitee.com/ulthon/ulthon_admin');
|
||||||
|
$output->highlight('开发文档地址:http://doc.ulthon.com/home/read/ulthon_admin/home.html');
|
||||||
|
|
||||||
|
$is_push_tag = $input->hasOption('push-tag');
|
||||||
|
|
||||||
|
if ($is_push_tag) {
|
||||||
|
$output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
|
||||||
|
|
||||||
|
$version = $this::VERSION;
|
||||||
|
$comment = implode(';', $this::COMMENT);
|
||||||
|
$output->info('生成标签:' . $version);
|
||||||
|
$output->info('标签描述:' . $comment);
|
||||||
|
exec("git tag -a $version -m \"$comment\"");
|
||||||
|
$output->info('推送到远程仓库');
|
||||||
|
exec('git push --tags');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
207
extend/base/common/command/curd/MigrateBase.php
Normal file
207
extend/base/common/command/curd/MigrateBase.php
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace base\common\command\curd;
|
||||||
|
|
||||||
|
use think\console\Command;
|
||||||
|
use think\console\Input;
|
||||||
|
use think\console\input\Option;
|
||||||
|
use think\console\Output;
|
||||||
|
use think\facade\App;
|
||||||
|
use think\facade\Db;
|
||||||
|
use think\facade\View;
|
||||||
|
|
||||||
|
class MigrateBase extends Command
|
||||||
|
{
|
||||||
|
protected $table;
|
||||||
|
protected $database;
|
||||||
|
protected $tablePrefix;
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
// 指令配置
|
||||||
|
$this->setName('curd:migrate')
|
||||||
|
->addOption('table', 't', Option::VALUE_REQUIRED, '主表名')
|
||||||
|
->addOption('tableName', '', Option::VALUE_OPTIONAL, '要生成的表名')
|
||||||
|
->addOption('fileName', '', Option::VALUE_OPTIONAL, '要生成的文件名')
|
||||||
|
->addOption('force', 'f', Option::VALUE_NONE, '强制生成')
|
||||||
|
->setDescription('the curd:migrate command');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(Input $input, Output $output)
|
||||||
|
{
|
||||||
|
// 指令输出
|
||||||
|
$output->writeln('curd:migrate');
|
||||||
|
|
||||||
|
$table = $input->getOption('table');
|
||||||
|
$file_name = $input->getOption('fileName');
|
||||||
|
$table_name = $input->getOption('tableName');
|
||||||
|
$force = $input->getOption('force');
|
||||||
|
|
||||||
|
if (empty($table)) {
|
||||||
|
$output->error('请输入表名');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($table_name)) {
|
||||||
|
$table_name = $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($file_name)) {
|
||||||
|
$file_name = $table_name;
|
||||||
|
}
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
$this->database = config('database.connections.mysql.database');
|
||||||
|
$this->tablePrefix = config('database.connections.mysql.prefix');
|
||||||
|
|
||||||
|
$table_info_sql = "select * from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='{$this->database}' and TABLE_NAME='{$this->tablePrefix}{$this->table}'";
|
||||||
|
|
||||||
|
$table_info = Db::query($table_info_sql);
|
||||||
|
|
||||||
|
if (empty($table_info)) {
|
||||||
|
$output->error('表不存在');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dist_file_path = App::getRootPath() . '/database/migrations/' . date('YmdHis') . '_' . $file_name . '.php';
|
||||||
|
|
||||||
|
$patt_path = App::getRootPath() . '/database/migrations/*' . $file_name . '.php';
|
||||||
|
|
||||||
|
$patt_files = glob($patt_path);
|
||||||
|
|
||||||
|
$is_extis = false;
|
||||||
|
foreach ($patt_files as $patt_file) {
|
||||||
|
$patt = '/.*database\/migrations\/\d+_' . $file_name . '.php$/';
|
||||||
|
|
||||||
|
$preg_result = preg_match($patt, $patt_file);
|
||||||
|
|
||||||
|
if ($preg_result) {
|
||||||
|
$is_extis = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_extis) {
|
||||||
|
$output->error('文件已存在:' . $patt_files[0]);
|
||||||
|
if (!$force) {
|
||||||
|
$confirm_force = $output->confirm($input, '确定要覆盖文件吗?', false);
|
||||||
|
|
||||||
|
if (!$confirm_force) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output->highlight('执行覆盖操作');
|
||||||
|
|
||||||
|
$dist_file_path = $patt_files[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = Db::query("SHOW FULL COLUMNS FROM {$this->tablePrefix}{$this->table}");
|
||||||
|
|
||||||
|
$data['class_name'] = \think\helper\Str::studly($file_name);
|
||||||
|
|
||||||
|
$table_columns = [];
|
||||||
|
|
||||||
|
$table_keys = [];
|
||||||
|
$table_keys_text = [];
|
||||||
|
$table_keys_uni = [];
|
||||||
|
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
if ($column['Field'] == 'id') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$column_item = [];
|
||||||
|
$column_item['options'] = [];
|
||||||
|
|
||||||
|
$column_item['field'] = $column['Field'];
|
||||||
|
|
||||||
|
$column_item['type'] = '';
|
||||||
|
|
||||||
|
$type = $column['Type'];
|
||||||
|
|
||||||
|
if (strpos($type, '(') !== false) {
|
||||||
|
// 带有长度
|
||||||
|
|
||||||
|
$type_info = explode('(', $type);
|
||||||
|
$column_item['type'] = $type_info[0];
|
||||||
|
|
||||||
|
$length = substr($type_info[1], 0, strpos($type_info[1], ')'));
|
||||||
|
|
||||||
|
if (strpos($length, ',') !== false) {
|
||||||
|
$length_info = explode(',', $length);
|
||||||
|
|
||||||
|
$column_item['options']['precision'] = $length_info[0];
|
||||||
|
$column_item['options']['scale'] = $length_info[1];
|
||||||
|
} else {
|
||||||
|
$column_item['options']['limit'] = $length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($type, 'unsigned') !== false) {
|
||||||
|
// 无符号
|
||||||
|
$column_item['options']['signed'] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$column_item['type'] = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$column_item['options']['null'] = $column['Null'] == 'YES' ? 1 : 0;
|
||||||
|
if ($column['Default'] !== null) {
|
||||||
|
$column_item['options']['default'] = $column['Default'];
|
||||||
|
}
|
||||||
|
$column_item['options']['comment'] = $column['Comment'];
|
||||||
|
|
||||||
|
$key = $column['Key'];
|
||||||
|
|
||||||
|
if (!empty($key)) {
|
||||||
|
if ($key == 'MUL') {
|
||||||
|
if ($type == 'text' || $type == 'longtext') {
|
||||||
|
$table_keys_text[] = $column['Field'];
|
||||||
|
} else {
|
||||||
|
$table_keys[] = $column['Field'];
|
||||||
|
}
|
||||||
|
} elseif ($key == 'UNI') {
|
||||||
|
$table_keys_uni[] = $column['Field'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_columns[] = $column_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_map = [
|
||||||
|
'bigint' => 'biginteger',
|
||||||
|
'int' => 'integer',
|
||||||
|
'varchar' => 'string',
|
||||||
|
'smallint' => 'integer',
|
||||||
|
'tinyint' => 'integer',
|
||||||
|
'longtext' => 'text',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($table_columns as &$column_item_set) {
|
||||||
|
if (isset($type_map[$column_item_set['type']])) {
|
||||||
|
$column_item_set['type'] = $type_map[$column_item_set['type']];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($column_item_set['options'] as $key => $option) {
|
||||||
|
if (is_array($option)) {
|
||||||
|
$column_item_set['options'][$key] = '[' . implode(',', $option) . ']';
|
||||||
|
} else {
|
||||||
|
$column_item_set['options'][$key] = "'{$option}'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['table_info'] = $table_info[0];
|
||||||
|
$data['table'] = $table_name;
|
||||||
|
$data['table_columns'] = $table_columns;
|
||||||
|
$data['table_keys'] = $table_keys;
|
||||||
|
$data['table_keys_uni'] = $table_keys_uni;
|
||||||
|
$data['table_keys_text'] = $table_keys_text;
|
||||||
|
|
||||||
|
$migrate_content = View::fetch(__DIR__ . '/migrate.tpl', $data);
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/migrate_output.php', "<?php\n\n" . $migrate_content);
|
||||||
|
file_put_contents($dist_file_path, "<?php\n\n" . $migrate_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
extend/base/common/command/curd/migrate.tpl
Normal file
40
extend/base/common/command/curd/migrate.tpl
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use think\migration\Migrator;
|
||||||
|
use think\migration\db\Column;
|
||||||
|
|
||||||
|
class {$class_name} extends Migrator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Change Method.
|
||||||
|
*
|
||||||
|
* Write your reversible migrations using this method.
|
||||||
|
*
|
||||||
|
* More information on writing migrations is available here:
|
||||||
|
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
|
||||||
|
*
|
||||||
|
* The following commands can be used in this method and Phinx will
|
||||||
|
* automatically reverse them when rolling back:
|
||||||
|
*
|
||||||
|
* createTable
|
||||||
|
* renameTable
|
||||||
|
* addColumn
|
||||||
|
* renameColumn
|
||||||
|
* addIndex
|
||||||
|
* addForeignKey
|
||||||
|
*
|
||||||
|
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||||
|
* with the Table class.
|
||||||
|
*/
|
||||||
|
public function change()
|
||||||
|
{
|
||||||
|
$table = $this->table('{$table}')
|
||||||
|
->setComment('{$table_info.TABLE_COMMENT}')
|
||||||
|
{volist name="table_columns" id="column"}->addColumn('{$column.field}', '{$column.type}', [{volist name="column.options" id="option"}'{$key}' => {$option|raw}, {/volist}])
|
||||||
|
{/volist}
|
||||||
|
{volist name="table_keys_uni" id="vo"}->addIndex('{$vo}',['unique'=>true])
|
||||||
|
{/volist}
|
||||||
|
{volist name="table_keys_text" id="vo"}->addIndex('{$vo}',['type'=>'fulltext'])
|
||||||
|
{/volist}
|
||||||
|
{volist name="table_keys" id="vo"}->addIndex('{$vo}')
|
||||||
|
{/volist}->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
extend/base/common/command/timer/config.php
Normal file
16
extend/base/common/command/timer/config.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'http_demo', // 定时任务的名称,不能重复
|
||||||
|
'type' => 'site', // 定时任务的类型,默认只支持site,你也可以重写定时器命令行以支持其他命令
|
||||||
|
'target' => '/tools/timer.ResetPassword/do', // 要访问的地址,如果不是以https开头,那么以后台的系统配置中读取相关配置,如果没有配置则不执行
|
||||||
|
'frequency' => 600 // 执行频率,单位:秒,填写10,则每10秒过后执行一次
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'clear_log', // 定时任务的名称,不能重复
|
||||||
|
'type' => 'site', // 定时任务的类型,默认只支持site,你也可以重写定时器命令行以支持其他命令
|
||||||
|
'target' => '/tools/timer.ClearLog/do', // 要访问的地址,如果不是以https开头,那么以后台的系统配置中读取相关配置,如果没有配置则不执行
|
||||||
|
'frequency' => 600 // 执行频率,单位:秒,填写10,则每10秒过后执行一次
|
||||||
|
],
|
||||||
|
];
|
||||||
15
extend/base/common/constants/AdminConstantBase.php
Normal file
15
extend/base/common/constants/AdminConstantBase.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员常量
|
||||||
|
* Class AdminConstant.
|
||||||
|
*/
|
||||||
|
class AdminConstantBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 超级管理员,不受权限控制.
|
||||||
|
*/
|
||||||
|
const SUPER_ADMIN_ID = 1;
|
||||||
|
}
|
||||||
20
extend/base/common/constants/MenuConstantBase.php
Normal file
20
extend/base/common/constants/MenuConstantBase.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单常量
|
||||||
|
* Class MenuConstant.
|
||||||
|
*/
|
||||||
|
class MenuConstantBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 首页的PID.
|
||||||
|
*/
|
||||||
|
const HOME_PID = 99999999;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块名前缀
|
||||||
|
*/
|
||||||
|
const MODULE_PREFIX = 'ulthon_admin_';
|
||||||
|
}
|
||||||
443
extend/base/common/controller/AdminControllerBase.php
Normal file
443
extend/base/common/controller/AdminControllerBase.php
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\controller;
|
||||||
|
|
||||||
|
use app\admin\model\SystemAdmin;
|
||||||
|
use app\BaseController;
|
||||||
|
use app\common\constants\AdminConstant;
|
||||||
|
use app\common\service\AuthService;
|
||||||
|
use think\facade\Env;
|
||||||
|
use think\facade\View;
|
||||||
|
use think\Model;
|
||||||
|
use think\template\exception\TemplateNotFoundException;
|
||||||
|
use think\Validate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AdminController.
|
||||||
|
*/
|
||||||
|
class AdminControllerBase extends BaseController
|
||||||
|
{
|
||||||
|
use \app\common\traits\JumpTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前模型.
|
||||||
|
* @Model
|
||||||
|
* @var object
|
||||||
|
*/
|
||||||
|
protected $model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段排序.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $sort = [
|
||||||
|
'id' => 'desc',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许修改的字段.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $allowModifyFields = [
|
||||||
|
'status',
|
||||||
|
'sort',
|
||||||
|
'remark',
|
||||||
|
'is_delete',
|
||||||
|
'is_auth',
|
||||||
|
'title',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不导出的字段信息.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $noExportFields = ['delete_time', 'update_time'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下拉选择条件.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $selectWhere = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否关联查询.
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $relationSearch = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板布局, false取消.
|
||||||
|
* @var string|bool
|
||||||
|
*/
|
||||||
|
protected $layout = 'layout/default';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为演示环境.
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $isDemo = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多元传参
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $dataBrage = [];
|
||||||
|
|
||||||
|
protected $validateRule = null;
|
||||||
|
|
||||||
|
protected $validateMessage = [];
|
||||||
|
|
||||||
|
protected $batchValidate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出文件的名称,支持中文,为空则获取模型名称.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $exportFileName = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前登陆的管理员.
|
||||||
|
* @var SystemAdmin
|
||||||
|
*/
|
||||||
|
protected $sessionAdmin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化方法.
|
||||||
|
*/
|
||||||
|
protected function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
$this->layout && $this->app->view->engine()->layout($this->layout);
|
||||||
|
$this->isDemo = Env::get('adminsystem.is_demo', false);
|
||||||
|
$this->viewInit();
|
||||||
|
$this->checkAuth();
|
||||||
|
|
||||||
|
$this->initSort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initSort()
|
||||||
|
{
|
||||||
|
$sort = $this->request->param('sort');
|
||||||
|
|
||||||
|
if (!empty($sort)) {
|
||||||
|
$this->sort = $sort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板变量赋值
|
||||||
|
* @param string|array $name 模板变量
|
||||||
|
* @param mixed $value 变量值
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function assign($name, $value = null, $isAppendToDataBrage = false)
|
||||||
|
{
|
||||||
|
if ($isAppendToDataBrage) {
|
||||||
|
$this->dataBrage[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->app->view->assign($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchJS($template = '')
|
||||||
|
{
|
||||||
|
$content_js = '';
|
||||||
|
|
||||||
|
$common_template = '_common';
|
||||||
|
|
||||||
|
if (!empty($template)) {
|
||||||
|
$template_arr = explode('/', $template);
|
||||||
|
unset($template_arr[count($template_arr) - 1]);
|
||||||
|
$template_arr[] = '_common';
|
||||||
|
$common_template = implode('/', $template_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$content_js .= View::layout(false)->fetchJS($common_template);
|
||||||
|
$content_js .= View::layout(false)->fetchJS($template);
|
||||||
|
} catch (TemplateNotFoundException $th) {
|
||||||
|
if (Env::get('adminsystem.strict_view_js', true)) {
|
||||||
|
throw $th;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<script>{$content_js}</script>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析和获取模板内容 用于输出.
|
||||||
|
* @param string $template
|
||||||
|
* @param array $vars
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function fetch($template = '', $vars = [])
|
||||||
|
{
|
||||||
|
$this->assign('data_brage', json_encode($this->dataBrage));
|
||||||
|
|
||||||
|
$vars['content_js'] = $this->fetchJS($template);
|
||||||
|
|
||||||
|
$content_main = View::layout($this->layout)
|
||||||
|
->config([
|
||||||
|
'view_suffix' => 'html',
|
||||||
|
])->fetch($template, $vars);
|
||||||
|
|
||||||
|
$html = '';
|
||||||
|
$html .= $content_main;
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置dataBrage数据.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setDataBrage($name, $value)
|
||||||
|
{
|
||||||
|
$this->dataBrage[$name] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写验证规则.
|
||||||
|
* @param array $data
|
||||||
|
* @param array|string $validate
|
||||||
|
* @param array $message
|
||||||
|
* @param bool|null $batch
|
||||||
|
* @return array|bool|string|true
|
||||||
|
*/
|
||||||
|
public function validate(array $data, $validate, array $message = [], bool $batch = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$message = array_merge($this->validateMessage, $message);
|
||||||
|
|
||||||
|
if (is_null($batch)) {
|
||||||
|
$batch = $this->batchValidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->validateRule instanceof Validate) {
|
||||||
|
$this->validateRule->message($message);
|
||||||
|
|
||||||
|
// 是否批量验证
|
||||||
|
if ($batch) {
|
||||||
|
$this->validateRule->batch(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->validateRule->failException(true)->check($data);
|
||||||
|
} elseif (is_array($this->validateRule)) {
|
||||||
|
parent::validate($data, $this->validateRule, $message, $batch);
|
||||||
|
} else {
|
||||||
|
parent::validate($data, $validate, $message, $batch);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建请求参数.
|
||||||
|
* @param array $excludeFields 忽略构建搜索的字段
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function buildTableParames($excludeFields = [])
|
||||||
|
{
|
||||||
|
$get = $this->request->get('', null);
|
||||||
|
|
||||||
|
$page = isset($get['page']) && !empty($get['page']) ? $get['page'] : 1;
|
||||||
|
$limit = isset($get['limit']) && !empty($get['limit']) ? $get['limit'] : 15;
|
||||||
|
$group = isset($get['group']) && !empty($get['group']) ? $get['group'] : null;
|
||||||
|
$filters = isset($get['filter']) && !empty($get['filter']) ? $get['filter'] : '{}';
|
||||||
|
$ops = isset($get['op']) && !empty($get['op']) ? $get['op'] : '{}';
|
||||||
|
// json转数组
|
||||||
|
$filters = json_decode($filters, true) ?? [];
|
||||||
|
$ops = json_decode($ops, true);
|
||||||
|
$where = [];
|
||||||
|
$excludes = [];
|
||||||
|
|
||||||
|
$request_options = [];
|
||||||
|
|
||||||
|
// 判断是否关联查询
|
||||||
|
$tableName = \think\helper\Str::snake(lcfirst($this->model->getName()));
|
||||||
|
|
||||||
|
foreach ($filters as $key => $val) {
|
||||||
|
if (in_array($key, $excludeFields)) {
|
||||||
|
$excludes[$key] = $val;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$op = isset($ops[$key]) && !empty($ops[$key]) ? $ops[$key] : '%*%';
|
||||||
|
|
||||||
|
if (strpos($key, '[') === 0) {
|
||||||
|
$key = str_replace('[', '', $key);
|
||||||
|
|
||||||
|
$key = explode(']', $key)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->relationSearch && count(explode('.', $key)) == 1) {
|
||||||
|
$key = "{$tableName}.{$key}";
|
||||||
|
}
|
||||||
|
switch (strtolower($op)) {
|
||||||
|
case '=':
|
||||||
|
$where[] = [$key, '=', $val];
|
||||||
|
break;
|
||||||
|
case '%*%':
|
||||||
|
$where[] = [$key, 'LIKE', "%{$val}%"];
|
||||||
|
break;
|
||||||
|
case '*%':
|
||||||
|
$where[] = [$key, 'LIKE', "{$val}%"];
|
||||||
|
break;
|
||||||
|
case '%*':
|
||||||
|
$where[] = [$key, 'LIKE', "%{$val}"];
|
||||||
|
case 'in':
|
||||||
|
$where[] = [$key, 'IN', "{$val}"];
|
||||||
|
break;
|
||||||
|
case 'min':
|
||||||
|
$where[] = [$key, '>=', $val];
|
||||||
|
break;
|
||||||
|
case 'max':
|
||||||
|
$where[] = [$key, '<=', $val];
|
||||||
|
break;
|
||||||
|
case 'min_date':
|
||||||
|
$where[] = [$key, '>=', strtotime($val)];
|
||||||
|
break;
|
||||||
|
case 'max_date':
|
||||||
|
$where[] = [$key, '<=', strtotime($val)];
|
||||||
|
break;
|
||||||
|
case 'range':
|
||||||
|
[$beginTime, $endTime] = explode(' - ', $val);
|
||||||
|
$where[] = [$key, '>=', strtotime($beginTime)];
|
||||||
|
$where[] = [$key, '<=', strtotime($endTime)];
|
||||||
|
break;
|
||||||
|
case 'none':
|
||||||
|
$request_options[$key] = $val;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$where[] = [$key, $op, "%{$val}"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$page, $limit, $where, $excludes, $request_options, $group];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下拉选择列表.
|
||||||
|
* @return \think\response\Json
|
||||||
|
*/
|
||||||
|
public function selectList()
|
||||||
|
{
|
||||||
|
$fields = input('selectFields');
|
||||||
|
$data = $this->model
|
||||||
|
->where($this->selectWhere)
|
||||||
|
->field($fields)
|
||||||
|
->select();
|
||||||
|
$this->success(null, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图参数.
|
||||||
|
*/
|
||||||
|
private function viewInit()
|
||||||
|
{
|
||||||
|
$request = app()->request;
|
||||||
|
list($thisModule, $thisController, $thisAction) = [app('http')->getName(), app()->request->controller(), $request->action()];
|
||||||
|
list($thisControllerArr, $jsPath) = [explode('.', $thisController), null];
|
||||||
|
foreach ($thisControllerArr as $vo) {
|
||||||
|
empty($jsPath) ? $jsPath = parse_name($vo) : $jsPath .= '/' . parse_name($vo);
|
||||||
|
}
|
||||||
|
$autoloadJs = file_exists(root_path('public') . "static/{$thisModule}/js/{$jsPath}.js") ? true : false;
|
||||||
|
$thisControllerJsPath = "{$thisModule}/js/{$jsPath}.js";
|
||||||
|
$adminModuleName = config('app.admin_alias_name');
|
||||||
|
$isSuperAdmin = session('admin.id') == AdminConstant::SUPER_ADMIN_ID ? true : false;
|
||||||
|
$data = [
|
||||||
|
'adminModuleName' => $adminModuleName,
|
||||||
|
'thisController' => parse_name($thisController),
|
||||||
|
'thisAction' => $thisAction,
|
||||||
|
'thisRequest' => parse_name("{$thisModule}/{$thisController}/{$thisAction}"),
|
||||||
|
'thisControllerJsPath' => "{$thisControllerJsPath}",
|
||||||
|
'autoloadJs' => $autoloadJs,
|
||||||
|
'isSuperAdmin' => $isSuperAdmin,
|
||||||
|
'version' => env('app_debug') ? time() : sysconfig('site', 'site_version'),
|
||||||
|
];
|
||||||
|
|
||||||
|
View::assign($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测权限.
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
protected function checkAuth($currentNode = null, $haltRequest = true)
|
||||||
|
{
|
||||||
|
$adminConfig = config('admin');
|
||||||
|
$adminId = session('admin.id');
|
||||||
|
$expireTime = session('admin.expire_time');
|
||||||
|
/** @var AuthService $authService */
|
||||||
|
$authService = app(AuthService::class, ['adminId' => $adminId]);
|
||||||
|
|
||||||
|
$currentNode = $authService->getCurrentNode();
|
||||||
|
if (is_null($currentNode)) {
|
||||||
|
$currentNode = $authService->getCurrentNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentController = parse_name(app()->request->controller());
|
||||||
|
|
||||||
|
// 验证登录
|
||||||
|
if (
|
||||||
|
!in_array($currentController, $adminConfig['no_login_controller']) &&
|
||||||
|
!in_array($currentNode, $adminConfig['no_login_node'])
|
||||||
|
) {
|
||||||
|
empty($adminId) && $this->error('请先登录后台', [], __url('admin/login/index'));
|
||||||
|
|
||||||
|
// 判断是否登录过期
|
||||||
|
if ($expireTime !== true && time() > $expireTime) {
|
||||||
|
session('admin', null);
|
||||||
|
$this->error('登录已过期,请重新登录', [], __url('admin/login/index'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证权限
|
||||||
|
if (
|
||||||
|
!in_array($currentController, $adminConfig['no_auth_controller']) &&
|
||||||
|
!in_array($currentNode, $adminConfig['no_auth_node'])
|
||||||
|
) {
|
||||||
|
$check = $authService->checkNode($currentNode);
|
||||||
|
|
||||||
|
if ($haltRequest) {
|
||||||
|
if (!$check) {
|
||||||
|
$this->error('无权限访问');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $check;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为演示环境
|
||||||
|
if (env('adminsystem.is_demo', false) && app()->request->isPost()) {
|
||||||
|
$this->error('演示环境下不允许修改');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$model_admin = SystemAdmin::autoCache('read', $adminId)->find($adminId);
|
||||||
|
|
||||||
|
$this->sessionAdmin = $model_admin;
|
||||||
|
|
||||||
|
$this->assign('session_admin', $model_admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 严格校验接口是否为POST请求
|
||||||
|
*/
|
||||||
|
protected function checkPostRequest()
|
||||||
|
{
|
||||||
|
if (!$this->request->isPost()) {
|
||||||
|
$this->error('当前请求不合法!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
extend/base/common/controller/TimerControllerBase.php
Normal file
35
extend/base/common/controller/TimerControllerBase.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\controller;
|
||||||
|
|
||||||
|
use app\common\controller\ToolsController;
|
||||||
|
use think\facade\Cache;
|
||||||
|
|
||||||
|
class TimerControllerBase extends ToolsController
|
||||||
|
{
|
||||||
|
protected $frequency = null;
|
||||||
|
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
parent::initialize();
|
||||||
|
|
||||||
|
if (is_int($this->frequency)) {
|
||||||
|
$this->protectVisit($this->frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function protectVisit(int $frequency)
|
||||||
|
{
|
||||||
|
$cache_tag = 'timer_protect';
|
||||||
|
|
||||||
|
$cache_key = 'timer_protect_' . md5($this->request->url());
|
||||||
|
|
||||||
|
$last_exec_time = Cache::get($cache_key, 0);
|
||||||
|
|
||||||
|
if ($last_exec_time >= time() - $frequency) {
|
||||||
|
return $this->error('请不要频繁请求');
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::tag($cache_tag)->set($cache_key, time());
|
||||||
|
}
|
||||||
|
}
|
||||||
11
extend/base/common/controller/ToolsControllerBase.php
Normal file
11
extend/base/common/controller/ToolsControllerBase.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\controller;
|
||||||
|
|
||||||
|
use app\BaseController;
|
||||||
|
use app\common\traits\JumpTrait;
|
||||||
|
|
||||||
|
class ToolsControllerBase extends BaseController
|
||||||
|
{
|
||||||
|
use JumpTrait;
|
||||||
|
}
|
||||||
15
extend/base/common/event/AdminLoginSuccess/LogEventBase.php
Normal file
15
extend/base/common/event/AdminLoginSuccess/LogEventBase.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\event\AdminLoginSuccess;
|
||||||
|
|
||||||
|
use app\admin\model\SystemAdmin;
|
||||||
|
use think\facade\Log;
|
||||||
|
|
||||||
|
class LogEventBase
|
||||||
|
{
|
||||||
|
public function handle(SystemAdmin $system_admin)
|
||||||
|
{
|
||||||
|
// 事件监听处理
|
||||||
|
Log::report("admin login success,{$system_admin->username}");
|
||||||
|
}
|
||||||
|
}
|
||||||
23
extend/base/common/event/AdminLoginType/DemoEventBase.php
Normal file
23
extend/base/common/event/AdminLoginType/DemoEventBase.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\event\AdminLoginType;
|
||||||
|
|
||||||
|
use think\facade\Env;
|
||||||
|
use think\facade\View;
|
||||||
|
|
||||||
|
class DemoEventBase
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
if (Env::get('adminsystem.is_demo', false)) {
|
||||||
|
$content = View::layout(false)->fetch('login/ext/demo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件监听处理
|
||||||
|
return [
|
||||||
|
'view_content' => $content,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
9
extend/base/common/exception/EventExceptionBase.php
Normal file
9
extend/base/common/exception/EventExceptionBase.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace extend\base\commno\exception;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class EventExceptionBase extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
105
extend/base/common/model/BaseModelBase.php
Normal file
105
extend/base/common/model/BaseModelBase.php
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\model;
|
||||||
|
|
||||||
|
use think\facade\Cache;
|
||||||
|
use think\facade\Env;
|
||||||
|
use think\facade\Log;
|
||||||
|
use think\helper\Str;
|
||||||
|
use think\Model;
|
||||||
|
|
||||||
|
class BaseModelBase extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 自动清除的缓存值
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* 'name'=>'',
|
||||||
|
* 'type'=>'', // tag/key
|
||||||
|
* 'field'=>'' // 为空则不做拼接
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $autoCache = [];
|
||||||
|
|
||||||
|
public static function onAfterWrite($model)
|
||||||
|
{
|
||||||
|
static::autoRemoveCache($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterDelete($model)
|
||||||
|
{
|
||||||
|
static::autoRemoveCache($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function onAfterRestore($model)
|
||||||
|
{
|
||||||
|
static::autoRemoveCache($model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function autoRemoveCache($model)
|
||||||
|
{
|
||||||
|
if (!isset(static::$autoCache['table'])) {
|
||||||
|
static::$autoCache['table'] = [
|
||||||
|
'name' => 'table',
|
||||||
|
'type' => 'tag',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset(static::$autoCache['read'])) {
|
||||||
|
static::$autoCache['read'] = [
|
||||||
|
'name' => 'read',
|
||||||
|
'field' => 'id',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (static::$autoCache as $cache_item) {
|
||||||
|
$type = $cache_item['type'] ?? 'key';
|
||||||
|
|
||||||
|
$field = $cache_item['field'] ?? '';
|
||||||
|
|
||||||
|
$cache_key = $cache_item['name'] ?? '';
|
||||||
|
|
||||||
|
if (empty($cache_key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_key = Str::snake($model->getName()) . '_' . $cache_key;
|
||||||
|
|
||||||
|
if (!empty($field) && !is_null($model->$field)) {
|
||||||
|
// 用字段新的值拼接key
|
||||||
|
$cache_key_attr = $cache_key . '_' . $model->getAttr($field);
|
||||||
|
|
||||||
|
static::doRemoveCache($type, $cache_key_attr);
|
||||||
|
|
||||||
|
// 用字段旧的值拼接key
|
||||||
|
$cache_key_original = $cache_key . '_' . $model->getOrigin($field);
|
||||||
|
|
||||||
|
if ($cache_key_original != $cache_key_attr) {
|
||||||
|
static::doRemoveCache($type, $cache_key_original);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static::doRemoveCache($type, $cache_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function doRemoveCache($type, $cache_key)
|
||||||
|
{
|
||||||
|
if (Env::get('app.auto_cache_log')) {
|
||||||
|
Log::debug("清除 ORM 自动缓存 {$type}:{$cache_key}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type == 'key') {
|
||||||
|
Cache::delete($cache_key);
|
||||||
|
} else {
|
||||||
|
Cache::tag($cache_key)->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getClassNameKey()
|
||||||
|
{
|
||||||
|
return str_replace('\\', '_', static::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
extend/base/common/model/TimeModelBase.php
Normal file
40
extend/base/common/model/TimeModelBase.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\model;
|
||||||
|
|
||||||
|
use app\common\model\BaseModel;
|
||||||
|
use think\model\concern\SoftDelete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有关时间的模型
|
||||||
|
* Class TimeModel.
|
||||||
|
*/
|
||||||
|
class TimeModelBase extends BaseModel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 自动时间戳类型.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $autoWriteTimestamp = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加时间.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $createTime = 'create_time';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $updateTime = 'update_time';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 软删除.
|
||||||
|
*/
|
||||||
|
use SoftDelete;
|
||||||
|
|
||||||
|
protected $deleteTime = 'delete_time';
|
||||||
|
|
||||||
|
protected $defaultSoftDelete = 0;
|
||||||
|
}
|
||||||
57
extend/base/common/provider/ExceptionHandleBase.php
Normal file
57
extend/base/common/provider/ExceptionHandleBase.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\provider;
|
||||||
|
|
||||||
|
use think\db\exception\DataNotFoundException;
|
||||||
|
use think\db\exception\ModelNotFoundException;
|
||||||
|
use think\exception\Handle;
|
||||||
|
use think\exception\HttpException;
|
||||||
|
use think\exception\HttpResponseException;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use think\Response;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用异常处理类.
|
||||||
|
*/
|
||||||
|
class ExceptionHandleBase extends Handle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 不需要记录信息(日志)的异常类列表.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $ignoreReport = [
|
||||||
|
HttpException::class,
|
||||||
|
HttpResponseException::class,
|
||||||
|
ModelNotFoundException::class,
|
||||||
|
DataNotFoundException::class,
|
||||||
|
ValidateException::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录异常信息(包括日志或者其它方式记录).
|
||||||
|
*
|
||||||
|
* @param Throwable $exception
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function report(Throwable $exception): void
|
||||||
|
{
|
||||||
|
// 使用内置的方式记录异常日志
|
||||||
|
parent::report($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an exception into an HTTP response.
|
||||||
|
*
|
||||||
|
* @param \think\Request $request
|
||||||
|
* @param Throwable $e
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function render($request, Throwable $e): Response
|
||||||
|
{
|
||||||
|
// 添加自定义异常处理机制
|
||||||
|
|
||||||
|
// 其他错误交给系统处理
|
||||||
|
return parent::render($request, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
extend/base/common/provider/RequestBase.php
Normal file
9
extend/base/common/provider/RequestBase.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\provider;
|
||||||
|
|
||||||
|
// 应用请求对象类
|
||||||
|
class RequestBase extends \think\Request
|
||||||
|
{
|
||||||
|
protected $filter = [];
|
||||||
|
}
|
||||||
83
extend/base/common/provider/ViewBase.php
Normal file
83
extend/base/common/provider/ViewBase.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\provider;
|
||||||
|
|
||||||
|
use think\View as ThinkView;
|
||||||
|
|
||||||
|
class ViewBase extends ThinkView
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 设置布局
|
||||||
|
* ! 注意,layout并非view类的标准方法,仅仅是think-view和think-template的特性方法
|
||||||
|
* ! 但是,如果在provider中定制,则view的所有操作预期会发生错误
|
||||||
|
* ! 同时,由于后台从设计支出就完全依赖think-view,所以专门为此扩展和特性定义方法时没有问题的.
|
||||||
|
* @param bool|string $name 布局模板名称 false 则关闭布局
|
||||||
|
* @param string $replace 布局模板内容替换标识
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function layout(bool|string $name, string $replace = ''): static
|
||||||
|
{
|
||||||
|
if (false === $name) {
|
||||||
|
// 关闭布局
|
||||||
|
$this->config(['layout_on' => false]);
|
||||||
|
} else {
|
||||||
|
// 开启布局
|
||||||
|
$this->config(['layout_on' => true]);
|
||||||
|
|
||||||
|
// 名称必须为字符串
|
||||||
|
if (is_string($name)) {
|
||||||
|
$this->config(['layout_name' => $name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($replace)) {
|
||||||
|
$this->config(['layout_item' => $replace]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置模板引擎.
|
||||||
|
* @param array $config 参数
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function config(array $config)
|
||||||
|
{
|
||||||
|
$this->driver()->config($config);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析和获取模板内容 用于输出.
|
||||||
|
* @param string $template 模板文件名或者内容
|
||||||
|
* @param array $vars 模板变量
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function fetch(string $template = '', array $vars = []): string
|
||||||
|
{
|
||||||
|
return $this->config([
|
||||||
|
'view_suffix' => 'html',
|
||||||
|
])->getContent(function () use ($vars, $template) {
|
||||||
|
$this->engine()->fetch($template, array_merge($this->data, $vars));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析和获取模板内容 用于输出.
|
||||||
|
* @param string $template 模板文件名或者内容
|
||||||
|
* @param array $vars 模板变量
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function fetchJS(string $template = '', array $vars = []): string
|
||||||
|
{
|
||||||
|
return $this->config([
|
||||||
|
'view_suffix' => 'js',
|
||||||
|
])->getContent(function () use ($vars, $template) {
|
||||||
|
$this->engine()->fetch($template, array_merge($this->data, $vars));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
60
extend/base/common/provider/db/QueryBase.php
Normal file
60
extend/base/common/provider/db/QueryBase.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\provider\db;
|
||||||
|
|
||||||
|
use think\db\Query as DbQuery;
|
||||||
|
use think\facade\Env;
|
||||||
|
use think\facade\Log;
|
||||||
|
use think\helper\Str;
|
||||||
|
|
||||||
|
class QueryBase extends DbQuery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* autoCache 自动生成缓存.
|
||||||
|
*
|
||||||
|
* @param null|string $key
|
||||||
|
* @param null|string|int $field_key_value
|
||||||
|
* @param null|string $tag
|
||||||
|
* @param null|string|int $field_tag_value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function autoCache($key = null, $field_key_value = null, $tag = null, $field_tag_value = null)
|
||||||
|
{
|
||||||
|
$table_name = Str::snake($this->getName());
|
||||||
|
|
||||||
|
if (is_null($key)) {
|
||||||
|
$key = $this->getOptionsMd5();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($field_key_value)) {
|
||||||
|
$key = $key . '_' . $field_key_value;
|
||||||
|
}
|
||||||
|
$key = $table_name . '_' . $key;
|
||||||
|
|
||||||
|
if (!is_null($tag)) {
|
||||||
|
if (!is_null($field_tag_value)) {
|
||||||
|
$tag = $tag . '_' . $field_tag_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag = $table_name . '_' . $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Env::get('app.auto_cache_log')) {
|
||||||
|
Log::debug('use auto cache:' . $key);
|
||||||
|
Log::debug('use auto cache tag:' . $tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache($key, null, $tag);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptionsMd5()
|
||||||
|
{
|
||||||
|
$options = $this->getOptions();
|
||||||
|
|
||||||
|
// TODO:支持获取回调函数的设置
|
||||||
|
|
||||||
|
return md5(json_encode($options));
|
||||||
|
}
|
||||||
|
}
|
||||||
212
extend/base/common/service/AuthServiceBase.php
Normal file
212
extend/base/common/service/AuthServiceBase.php
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\service;
|
||||||
|
|
||||||
|
use app\common\constants\AdminConstant;
|
||||||
|
use think\facade\Config;
|
||||||
|
use think\facade\Db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限验证服务
|
||||||
|
* Class AuthService.
|
||||||
|
*/
|
||||||
|
class AuthServiceBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 用户ID.
|
||||||
|
* @var null
|
||||||
|
*/
|
||||||
|
protected $adminId = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认配置.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $config = [
|
||||||
|
'auth_on' => true, // 权限开关
|
||||||
|
'system_admin' => 'system_admin', // 用户表
|
||||||
|
'system_auth' => 'system_auth', // 权限表
|
||||||
|
'system_node' => 'system_node', // 节点表
|
||||||
|
'system_auth_node' => 'system_auth_node', // 权限-节点表
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员信息.
|
||||||
|
* @var array|\think\Model|null
|
||||||
|
*/
|
||||||
|
protected $adminInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有节点信息.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $nodeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员所有授权节点.
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $adminNode;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 构造方法
|
||||||
|
* AuthService constructor.
|
||||||
|
* @param null $adminId
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function __construct($adminId = null)
|
||||||
|
{
|
||||||
|
$this->adminId = $adminId;
|
||||||
|
$this->adminInfo = $this->getAdminInfo();
|
||||||
|
$this->nodeList = $this->getNodeList();
|
||||||
|
$this->adminNode = $this->getAdminNode();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测检测权限.
|
||||||
|
* @param null $node
|
||||||
|
* @return bool
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function checkNode($node = null)
|
||||||
|
{
|
||||||
|
// 判断是否为超级管理员
|
||||||
|
if ($this->adminId == AdminConstant::SUPER_ADMIN_ID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 判断权限验证开关
|
||||||
|
if ($this->config['auth_on'] == false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 判断是否需要获取当前节点
|
||||||
|
if (empty($node)) {
|
||||||
|
$node = $this->getCurrentNode();
|
||||||
|
} else {
|
||||||
|
$node = $this->parseNodeStr($node);
|
||||||
|
}
|
||||||
|
// 判断是否加入节点控制,优先获取缓存信息
|
||||||
|
if (!isset($this->nodeList[$node])) {
|
||||||
|
return Config::get('admin.default_auth_check');
|
||||||
|
}
|
||||||
|
$nodeInfo = $this->nodeList[$node];
|
||||||
|
if ($nodeInfo['is_auth'] == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 用户验证,优先获取缓存信息
|
||||||
|
if (empty($this->adminInfo) || $this->adminInfo['status'] != 1 || empty($this->adminInfo['auth_ids'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 判断该节点是否允许访问
|
||||||
|
if (in_array($node, $this->adminNode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前节点.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCurrentNode()
|
||||||
|
{
|
||||||
|
$node = $this->parseNodeStr(request()->controller() . '/' . request()->action());
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前管理员所有节点.
|
||||||
|
* @return array
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function getAdminNode()
|
||||||
|
{
|
||||||
|
$nodeList = [];
|
||||||
|
$adminInfo = $this->getAdminInfo();
|
||||||
|
|
||||||
|
if (!empty($adminInfo) && $adminInfo['status'] != 1) {
|
||||||
|
return $nodeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
|
||||||
|
$buildAuthSql = Db::name($this->config['system_auth'])
|
||||||
|
->distinct(true)
|
||||||
|
->whereIn('id', $adminInfo['auth_ids'])
|
||||||
|
->field('id')
|
||||||
|
->buildSql(true);
|
||||||
|
$buildAuthNodeSql = Db::name($this->config['system_auth_node'])
|
||||||
|
->distinct(true)
|
||||||
|
->where("auth_id IN {$buildAuthSql}")
|
||||||
|
->field('node_id')
|
||||||
|
->buildSql(true);
|
||||||
|
$nodeList = Db::name($this->config['system_node'])
|
||||||
|
->distinct(true)
|
||||||
|
->where("id IN {$buildAuthNodeSql}")
|
||||||
|
->column('node');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nodeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有节点信息.
|
||||||
|
* @time 2021-01-07
|
||||||
|
* @return array
|
||||||
|
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
||||||
|
*/
|
||||||
|
public function getNodeList()
|
||||||
|
{
|
||||||
|
return Db::name($this->config['system_node'])
|
||||||
|
->autoCache(null, null, 'table')
|
||||||
|
->column('id,node,title,type,is_auth', 'node');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取管理员信息.
|
||||||
|
* @time 2021-01-07
|
||||||
|
* @return array|\think\Model|null
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
* @author zhongshaofa <shaofa.zhong@happy-seed.com>
|
||||||
|
*/
|
||||||
|
public function getAdminInfo()
|
||||||
|
{
|
||||||
|
return Db::name($this->config['system_admin'])
|
||||||
|
->where('id', $this->adminId)
|
||||||
|
->autoCache('info', $this->adminId)
|
||||||
|
->find();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驼峰转下划线规则.
|
||||||
|
* @param string $node
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function parseNodeStr($node)
|
||||||
|
{
|
||||||
|
$array = explode('/', $node);
|
||||||
|
foreach ($array as $key => $val) {
|
||||||
|
if ($key == 0) {
|
||||||
|
$val = explode('.', $val);
|
||||||
|
foreach ($val as &$vo) {
|
||||||
|
$vo = \think\helper\Str::snake(lcfirst($vo));
|
||||||
|
}
|
||||||
|
$val = implode('.', $val);
|
||||||
|
$array[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$node = implode('/', $array);
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
extend/base/common/service/MenuServiceBase.php
Normal file
111
extend/base/common/service/MenuServiceBase.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\service;
|
||||||
|
|
||||||
|
use app\common\constants\MenuConstant;
|
||||||
|
use app\common\service\AuthService;
|
||||||
|
use think\facade\Db;
|
||||||
|
|
||||||
|
class MenuServiceBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 管理员ID.
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $adminId;
|
||||||
|
|
||||||
|
public function __construct($adminId)
|
||||||
|
{
|
||||||
|
$this->adminId = $adminId;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取首页信息.
|
||||||
|
* @return array|\think\Model|null
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function getHomeInfo()
|
||||||
|
{
|
||||||
|
$data = Db::name('system_menu')
|
||||||
|
->field('title,icon,href')
|
||||||
|
->where('delete_time', 0)
|
||||||
|
->where('pid', MenuConstant::HOME_PID)
|
||||||
|
->find();
|
||||||
|
!empty($data) && $data['href'] = __url($data['href']);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取后台菜单树信息.
|
||||||
|
* @return mixed
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
public function getMenuTree()
|
||||||
|
{
|
||||||
|
/** @var AuthService $authService */
|
||||||
|
$authServer = app(AuthService::class, ['adminId' => $this->adminId]);
|
||||||
|
|
||||||
|
return $this->buildMenuChild(0, $this->getMenuData(), $authServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildMenuChild($pid, $menuList, AuthService $authServer)
|
||||||
|
{
|
||||||
|
$treeList = [];
|
||||||
|
foreach ($menuList as &$v) {
|
||||||
|
$check = false;
|
||||||
|
if (!empty($v['auth_node'])) {
|
||||||
|
$check = $authServer->checkNode($v['auth_node']);
|
||||||
|
} elseif (!empty($v['href'])) {
|
||||||
|
$check = $authServer->checkNode($v['href']);
|
||||||
|
} else {
|
||||||
|
$check = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
!empty($v['href']) && $v['href'] = __url($v['href']);
|
||||||
|
if ($pid == $v['pid'] && $check) {
|
||||||
|
$node = $v;
|
||||||
|
$child = $this->buildMenuChild($v['id'], $menuList, $authServer);
|
||||||
|
if (!empty($child)) {
|
||||||
|
$node['child'] = $child;
|
||||||
|
}
|
||||||
|
if (!empty($v['href']) || !empty($child)) {
|
||||||
|
$treeList[] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $treeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有菜单数据.
|
||||||
|
* @return \think\Collection
|
||||||
|
* @throws \think\db\exception\DataNotFoundException
|
||||||
|
* @throws \think\db\exception\DbException
|
||||||
|
* @throws \think\db\exception\ModelNotFoundException
|
||||||
|
*/
|
||||||
|
protected function getMenuData()
|
||||||
|
{
|
||||||
|
$menuData = Db::name('system_menu')
|
||||||
|
->field('id,pid,title,icon,href,target')
|
||||||
|
->where('delete_time', 0)
|
||||||
|
->where([
|
||||||
|
['status', '=', '1'],
|
||||||
|
['pid', '<>', MenuConstant::HOME_PID],
|
||||||
|
])
|
||||||
|
->order([
|
||||||
|
'sort' => 'desc',
|
||||||
|
'id' => 'asc',
|
||||||
|
])
|
||||||
|
->select();
|
||||||
|
|
||||||
|
return $menuData;
|
||||||
|
}
|
||||||
|
}
|
||||||
168
extend/base/common/service/UploadServiceBase.php
Normal file
168
extend/base/common/service/UploadServiceBase.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\service;
|
||||||
|
|
||||||
|
use app\admin\model\SystemUploadfile;
|
||||||
|
use app\common\tools\PathTools;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use think\facade\App;
|
||||||
|
use think\facade\Filesystem;
|
||||||
|
use think\facade\Validate;
|
||||||
|
use think\File;
|
||||||
|
use think\file\UploadedFile;
|
||||||
|
|
||||||
|
class UploadServiceBase
|
||||||
|
{
|
||||||
|
protected $uploadType = 'local_public';
|
||||||
|
|
||||||
|
public function __construct($upload_type = null)
|
||||||
|
{
|
||||||
|
$uploadConfig = sysconfig('upload');
|
||||||
|
|
||||||
|
empty($upload_type) && $upload_type = $uploadConfig['upload_type'];
|
||||||
|
|
||||||
|
$this->uploadType = $upload_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(File $file, $allow_ext = null, $allow_size = null, $fail_exception = false)
|
||||||
|
{
|
||||||
|
$uploadConfig = sysconfig('upload');
|
||||||
|
|
||||||
|
if (!is_null($allow_ext)) {
|
||||||
|
$uploadConfig['upload_allow_ext'] = $allow_ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($allow_size)) {
|
||||||
|
$uploadConfig['upload_allow_size'] = $allow_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule = [
|
||||||
|
'upload_type|指定上传类型有误' => "in:{$uploadConfig['upload_allow_type']}",
|
||||||
|
'file|文件' => "require|file|fileExt:{$uploadConfig['upload_allow_ext']}|fileSize:{$uploadConfig['upload_allow_size']}",
|
||||||
|
];
|
||||||
|
|
||||||
|
$validat_result = Validate::failException($fail_exception)->check([
|
||||||
|
'upload_type' => $this->uploadType,
|
||||||
|
'file' => $file,
|
||||||
|
], $rule);
|
||||||
|
|
||||||
|
if (!$validat_result) {
|
||||||
|
return $validat_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 出于性能原因,您可以注释掉下面的代码
|
||||||
|
$file_path = $file->getRealPath();
|
||||||
|
|
||||||
|
if (strpos(file_get_contents($file_path), '<?php') !== false) {
|
||||||
|
if ($fail_exception) {
|
||||||
|
throw new ValidateException('文件含有PHP注入代码');
|
||||||
|
} else {
|
||||||
|
return '文件含有PHP注入代码';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateException($file, $allow_ext = null, $allow_size = null)
|
||||||
|
{
|
||||||
|
return $this->validate($file, $allow_ext, $allow_size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url($save_name)
|
||||||
|
{
|
||||||
|
$url = Filesystem::disk($this->uploadType)->url($save_name);
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储文件.
|
||||||
|
*
|
||||||
|
* @param File $file
|
||||||
|
* @param string|null $save_name
|
||||||
|
* @param bool $force_save 指定$save_name才可以用,设为true强制写入(不报错,如果存在则删除),否则是驱动的默认行为(覆盖、失败、异常)
|
||||||
|
* @param string $upload_dir
|
||||||
|
* @param bool $disable_model
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function save(File $file, string $save_name = null, $force_save = false, $upload_dir = 'upload', $disable_model = false)
|
||||||
|
{
|
||||||
|
$model_file = null;
|
||||||
|
|
||||||
|
if ($force_save && !is_null($save_name)) {
|
||||||
|
$file_path = $upload_dir . '/' . $save_name;
|
||||||
|
if (Filesystem::disk($this->uploadType)->has($file_path)) {
|
||||||
|
$model_file = SystemUploadfile::where('save_name', $save_name)->where('upload_type', $this->uploadType)->find();
|
||||||
|
Filesystem::disk($this->uploadType)->delete($file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($model_file)) {
|
||||||
|
$model_file = new SystemUploadfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
$model_file->upload_type = $this->uploadType;
|
||||||
|
$model_file->file_ext = strtolower($file->extension());
|
||||||
|
|
||||||
|
if ($file instanceof UploadedFile) {
|
||||||
|
$model_file->original_name = $file->getOriginalName();
|
||||||
|
$model_file->mime_type = $file->getOriginalMime();
|
||||||
|
} else {
|
||||||
|
$model_file->original_name = $file->getFilename();
|
||||||
|
$model_file->mime_type = $file->getMime();
|
||||||
|
}
|
||||||
|
|
||||||
|
$save_name = Filesystem::disk($this->uploadType)->putFile($upload_dir, $file, function () use ($save_name, $file) {
|
||||||
|
if (!is_null($save_name)) {
|
||||||
|
$ext_name = $file->extension();
|
||||||
|
|
||||||
|
if (empty($ext_name)) {
|
||||||
|
return $save_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$list_name = explode('.', $save_name);
|
||||||
|
|
||||||
|
array_pop($list_name);
|
||||||
|
|
||||||
|
return implode('.', $list_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return date('Ymd') . '/' . uniqid();
|
||||||
|
});
|
||||||
|
|
||||||
|
$url = $this->url($save_name);
|
||||||
|
|
||||||
|
$model_file->url = $url;
|
||||||
|
$model_file->save_name = $save_name;
|
||||||
|
|
||||||
|
$model_file->sha1 = $file->sha1();
|
||||||
|
|
||||||
|
$model_file->file_size = $file->getSize();
|
||||||
|
|
||||||
|
if (!$disable_model) {
|
||||||
|
$model_file->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveUrlFile($url, string $save_name = null, $force_save = false, $upload_dir = 'upload', $disable_model = false)
|
||||||
|
{
|
||||||
|
$runtime_path = App::getRuntimePath() . 'upload/temp/' . basename($url);
|
||||||
|
|
||||||
|
PathTools::intiDir($runtime_path);
|
||||||
|
|
||||||
|
$file_content = file_get_contents($url);
|
||||||
|
|
||||||
|
file_put_contents($runtime_path, $file_content);
|
||||||
|
|
||||||
|
$file = new File($runtime_path);
|
||||||
|
|
||||||
|
$response = $this->save($file, $save_name, $force_save, $upload_dir, $disable_model);
|
||||||
|
|
||||||
|
unlink($runtime_path);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
extend/base/common/tools/ExportToolsBase.php
Normal file
111
extend/base/common/tools/ExportToolsBase.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\tools;
|
||||||
|
|
||||||
|
use app\common\tools\PathTools;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||||
|
|
||||||
|
class ExportToolsBase
|
||||||
|
{
|
||||||
|
public static function excel($model, $where = [], $fields = [], $image_fields = [], $select_fields = [], $date_fields = [])
|
||||||
|
{
|
||||||
|
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
$write_col = 1;
|
||||||
|
$write_line = 1;
|
||||||
|
foreach ($fields as $field_key => $field_name) {
|
||||||
|
$col_key = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($write_col);
|
||||||
|
$sheet->setCellValue($col_key . $write_line, $field_name);
|
||||||
|
$sheet->getColumnDimension($col_key)->setWidth(mb_strlen($field_name) * 3);
|
||||||
|
|
||||||
|
$write_col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$runtime_file_list = [];
|
||||||
|
|
||||||
|
$limit = 1000;
|
||||||
|
|
||||||
|
$page = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$page++;
|
||||||
|
$list = $model
|
||||||
|
->where($where)->page($page, $limit)->select();
|
||||||
|
foreach ($list as $list_index => $item) {
|
||||||
|
$write_line++;
|
||||||
|
$write_col = 1;
|
||||||
|
|
||||||
|
foreach ($fields as $field_key => $field_name) {
|
||||||
|
$col_key = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($write_col);
|
||||||
|
|
||||||
|
$cel = null;
|
||||||
|
|
||||||
|
$value = \think\helper\Arr::get($item, $field_key);
|
||||||
|
|
||||||
|
if (in_array($field_key, $image_fields)) {
|
||||||
|
// 是图片
|
||||||
|
$cel = $value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (filter_var($value, FILTER_VALIDATE_URL)) {
|
||||||
|
$runtime_file = PathTools::tempBuildPath(uniqid());
|
||||||
|
|
||||||
|
$runtime_file_list[] = $runtime_file;
|
||||||
|
|
||||||
|
file_put_contents($runtime_file, file_get_contents($value));
|
||||||
|
|
||||||
|
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
||||||
|
$drawing->setName($field_name);
|
||||||
|
$drawing->setDescription($field_key);
|
||||||
|
$drawing->setPath($runtime_file);
|
||||||
|
$drawing->setHeight(36);
|
||||||
|
|
||||||
|
$drawing->setCoordinates($col_key . $write_line);
|
||||||
|
$drawing->setWorksheet($sheet);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
$message = $th->getMessage();
|
||||||
|
|
||||||
|
$cel .= "\n" . $message;
|
||||||
|
}
|
||||||
|
} elseif (array_key_exists($field_key, $select_fields)) {
|
||||||
|
// 需要设置选项
|
||||||
|
|
||||||
|
$cel = $select_fields[$field_key][$value] ?? $value;
|
||||||
|
} elseif (in_array($field_key, $date_fields)) {
|
||||||
|
if (empty($value)) {
|
||||||
|
$cel = '';
|
||||||
|
} else {
|
||||||
|
$cel = date('Y-m-d H:i:s', $value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$cel = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sheet->setCellValueExplicit($col_key . $write_line, $cel, DataType::TYPE_STRING);
|
||||||
|
$write_col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($list) < $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$writer->save('php://output');
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_clean();
|
||||||
|
|
||||||
|
foreach ($runtime_file_list as $runtime_file) {
|
||||||
|
if (file_exists($runtime_file)) {
|
||||||
|
unlink($runtime_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
162
extend/base/common/tools/PathToolsBase.php
Normal file
162
extend/base/common/tools/PathToolsBase.php
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\tools;
|
||||||
|
|
||||||
|
use Localheinz\Diff\Differ;
|
||||||
|
use think\facade\App;
|
||||||
|
|
||||||
|
class PathToolsBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 系统生成的文件,这些文件应当是可以任意删除的.
|
||||||
|
*
|
||||||
|
* @param string $file_name
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function publicBuildPath($file_name)
|
||||||
|
{
|
||||||
|
$file_path = App::getRootPath() . 'public/build/' . $file_name;
|
||||||
|
|
||||||
|
return self::intiDir($file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function publicBuildSaveName($file_name)
|
||||||
|
{
|
||||||
|
return '/build/' . $file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function safeBuildPath($save_name)
|
||||||
|
{
|
||||||
|
$file_path = App::getRootPath() . 'storage/' . $save_name;
|
||||||
|
|
||||||
|
return self::intiDir($file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tempBuildPath($file_name)
|
||||||
|
{
|
||||||
|
$runtime_path = App::getRuntimePath() . 'temp/' . $file_name;
|
||||||
|
|
||||||
|
return self::intiDir($runtime_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function intiDir($file_path, $is_dirname = false)
|
||||||
|
{
|
||||||
|
if (!$is_dirname) {
|
||||||
|
$dir_name = dirname($file_path);
|
||||||
|
} else {
|
||||||
|
$dir_name = $file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir($dir_name)) {
|
||||||
|
mkdir($dir_name, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function removeDir($dir_name)
|
||||||
|
{
|
||||||
|
if (!is_dir($dir_name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos(strtolower(PHP_OS), 'win') === 0) {
|
||||||
|
$dir_name = static::formatWinPath($dir_name);
|
||||||
|
exec("rd /s /q {$dir_name}", $output);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = opendir($dir_name);
|
||||||
|
while (false !== ($file = readdir($handle))) {
|
||||||
|
if ($file != '.' && $file != '..') {
|
||||||
|
is_dir("$dir_name/$file") ? self::removeDir("$dir_name/$file") : unlink("$dir_name/$file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($handle);
|
||||||
|
|
||||||
|
return rmdir($dir_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mapDir($dir, $callback = null)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$cdir = scandir($dir);
|
||||||
|
foreach ($cdir as $key => $value) {
|
||||||
|
if (!in_array($value, ['.', '..'])) {
|
||||||
|
$current_path = $dir . DS . $value;
|
||||||
|
if (is_dir($current_path)) {
|
||||||
|
$result[$value] = self::mapDir($current_path, $callback);
|
||||||
|
} else {
|
||||||
|
if (is_callable($callback)) {
|
||||||
|
$result[$value] = $callback($current_path, $value, $dir);
|
||||||
|
} else {
|
||||||
|
$result[$value] = $current_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatWinPath($content)
|
||||||
|
{
|
||||||
|
return str_replace('/', '\\', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function compareFiles($a, $b, $return_diff = false):bool|string
|
||||||
|
{
|
||||||
|
$result = true;
|
||||||
|
|
||||||
|
// Check if filesize is different
|
||||||
|
if (filesize($a) !== filesize($b)) {
|
||||||
|
$result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
// Check if content is different
|
||||||
|
$ah = fopen($a, 'rb');
|
||||||
|
$bh = fopen($b, 'rb');
|
||||||
|
|
||||||
|
while (!feof($ah)) {
|
||||||
|
if (fread($ah, 8192) != fread($bh, 8192)) {
|
||||||
|
$result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($ah);
|
||||||
|
fclose($bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
// 如果前面的方法认为文件变化,那么以文本变化的方式识别是否变化,并给出变化的内容
|
||||||
|
$text_mime_type = [];
|
||||||
|
$text_mime_type[] = 'text/html';
|
||||||
|
$text_mime_type[] = 'text/plain';
|
||||||
|
$text_mime_type[] = 'text/css';
|
||||||
|
$text_mime_type[] = 'image/svg+xml';
|
||||||
|
$text_mime_type[] = 'text/x-php';
|
||||||
|
$text_mime_type[] = 'application/json';
|
||||||
|
$text_mime_type[] = 'application/x-wine-extension-ini';
|
||||||
|
|
||||||
|
if (in_array(mime_content_type($a), $text_mime_type) && in_array(mime_content_type($b), $text_mime_type)) {
|
||||||
|
$a_content = file_get_contents($a);
|
||||||
|
$b_content = file_get_contents($b);
|
||||||
|
|
||||||
|
$diff = new Differ();
|
||||||
|
|
||||||
|
$diff_content = $diff->diff($a_content, $b_content);
|
||||||
|
|
||||||
|
if (!empty($diff_content)) {
|
||||||
|
$result = $return_diff ? $diff_content : false;
|
||||||
|
} else {
|
||||||
|
$result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
141
extend/base/common/traits/JumpTraitBase.php
Normal file
141
extend/base/common/traits/JumpTraitBase.php
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace base\common\traits;
|
||||||
|
|
||||||
|
use think\exception\HttpResponseException;
|
||||||
|
use think\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait JumpTrait.
|
||||||
|
*/
|
||||||
|
trait JumpTraitBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 操作成功跳转的快捷方法.
|
||||||
|
* @param mixed $msg 提示信息
|
||||||
|
* @param mixed $data 返回的数据
|
||||||
|
* @param string $url 跳转的 URL 地址
|
||||||
|
* @param int $wait 跳转等待时间
|
||||||
|
* @param array $header 发送的 Header 信息
|
||||||
|
* @return void
|
||||||
|
* @throws HttpResponseException
|
||||||
|
*/
|
||||||
|
protected function success($msg = '', $data = '', $url = null, $wait = 3, array $header = [])
|
||||||
|
{
|
||||||
|
if (is_null($url) && isset($_SERVER['HTTP_REFERER'])) {
|
||||||
|
$url = $_SERVER['HTTP_REFERER'];
|
||||||
|
} elseif ($url) {
|
||||||
|
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : app('route')->buildUrl($url)->__toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'code' => 1,
|
||||||
|
'msg' => $msg,
|
||||||
|
'data' => $data,
|
||||||
|
'url' => $url,
|
||||||
|
'wait' => $wait,
|
||||||
|
];
|
||||||
|
|
||||||
|
$type = $this->getResponseType();
|
||||||
|
if ($type == 'html') {
|
||||||
|
if (is_null($result['url'])) {
|
||||||
|
$result['url'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = view(app('config')->get('app.dispatch_success_tmpl'), $result);
|
||||||
|
} elseif ($type == 'json') {
|
||||||
|
$response = json($result);
|
||||||
|
}
|
||||||
|
throw new HttpResponseException($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作错误跳转的快捷方法.
|
||||||
|
* @param mixed $msg 提示信息
|
||||||
|
* @param mixed $data 返回的数据
|
||||||
|
* @param string $url 跳转的 URL 地址
|
||||||
|
* @param int $wait 跳转等待时间
|
||||||
|
* @param array $header 发送的 Header 信息
|
||||||
|
* @return void
|
||||||
|
* @throws HttpResponseException
|
||||||
|
*/
|
||||||
|
protected function error($msg = '', $data = '', $url = null, $wait = 3, array $header = [])
|
||||||
|
{
|
||||||
|
if (is_null($url)) {
|
||||||
|
$url = request()->isAjax() ? '' : 'javascript:history.back(-1);';
|
||||||
|
} elseif ($url) {
|
||||||
|
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : app('route')->buildUrl($url)->__toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $this->getResponseType();
|
||||||
|
$result = [
|
||||||
|
'code' => 0,
|
||||||
|
'msg' => $msg,
|
||||||
|
'data' => $data,
|
||||||
|
'url' => $url,
|
||||||
|
'wait' => $wait,
|
||||||
|
];
|
||||||
|
if ($type == 'html') {
|
||||||
|
if (is_null($result['url'])) {
|
||||||
|
$result['url'] = '';
|
||||||
|
}
|
||||||
|
$response = view(app('config')->get('app.dispatch_error_tmpl'), $result);
|
||||||
|
} elseif ($type == 'json') {
|
||||||
|
$response = json($result);
|
||||||
|
}
|
||||||
|
throw new HttpResponseException($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回封装后的 API 数据到客户端.
|
||||||
|
* @param mixed $data 要返回的数据
|
||||||
|
* @param int $code 返回的 code
|
||||||
|
* @param mixed $msg 提示信息
|
||||||
|
* @param string $type 返回数据格式
|
||||||
|
* @param array $header 发送的 Header 信息
|
||||||
|
* @return void
|
||||||
|
* @throws HttpResponseException
|
||||||
|
*/
|
||||||
|
protected function result($data, $code = 0, $msg = '', $type = '', array $header = [])
|
||||||
|
{
|
||||||
|
$result = [
|
||||||
|
'code' => $code,
|
||||||
|
'msg' => $msg,
|
||||||
|
'time' => time(),
|
||||||
|
'data' => $data,
|
||||||
|
];
|
||||||
|
$type = $type ?: $this->getResponseType();
|
||||||
|
$response = Response::create($result, $type)->header($header);
|
||||||
|
|
||||||
|
throw new HttpResponseException($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL 重定向.
|
||||||
|
* @param string $url 跳转的 URL 表达式
|
||||||
|
* @param array|int $params 其它 URL 参数
|
||||||
|
* @param int $code http code
|
||||||
|
* @param array $with 隐式传参
|
||||||
|
* @return void
|
||||||
|
* @throws HttpResponseException
|
||||||
|
*/
|
||||||
|
protected function redirect($url = [], $params = [], $code = 302)
|
||||||
|
{
|
||||||
|
if (is_int($params)) {
|
||||||
|
$code = $params;
|
||||||
|
$params = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Response::create($url, 'redirect', $code);
|
||||||
|
throw new HttpResponseException($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的 response 输出类型.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getResponseType()
|
||||||
|
{
|
||||||
|
return (request()->isJson() || request()->isAjax() || request()->isPost()) ? 'json' : 'html';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user