mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-09-02 14:11:38 +08:00
增加扩展机制定位文件;将common模块实现扩展模式;发布新版本;
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
use app\commno\exception\EventException;
|
||||
use app\common\service\AuthService;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\facade\App;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Env;
|
||||
use think\facade\Event;
|
||||
@@ -306,3 +307,19 @@ function event_response($name, $params = [])
|
||||
|
||||
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
|
||||
|
||||
|
||||
namespace app\common\command;
|
||||
|
||||
use base\common\command\CurdBase;
|
||||
|
||||
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 Curd extends Command
|
||||
class Curd extends CurdBase
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
namespace app\common\command;
|
||||
|
||||
use base\common\command\NodeBase;
|
||||
|
||||
use app\admin\model\SystemNode;
|
||||
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
|
||||
class Node extends NodeBase
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
namespace app\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;
|
||||
use base\common\command\OssStaticBase;
|
||||
|
||||
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;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
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;
|
||||
use base\common\command\TimerBase;
|
||||
|
||||
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;
|
||||
|
||||
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\App;
|
||||
use base\common\command\admin\ClearBase;
|
||||
|
||||
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;
|
||||
|
||||
use app\admin\model\SystemAdmin;
|
||||
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;
|
||||
use base\common\command\admin\ResetPasswordBase;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
use base\common\command\admin\UpdateBase;
|
||||
|
||||
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;
|
||||
|
||||
use think\App as ThinkApp;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use base\common\command\admin\VersionBase;
|
||||
|
||||
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;
|
||||
|
||||
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\App;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use base\common\command\curd\MigrateBase;
|
||||
|
||||
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
|
||||
|
||||
|
||||
namespace app\common\constants;
|
||||
|
||||
use base\common\constants\AdminConstantBase;
|
||||
|
||||
/**
|
||||
* 管理员常量
|
||||
* Class AdminConstant
|
||||
* @package app\common\constants
|
||||
* Class AdminConstant.
|
||||
*/
|
||||
class AdminConstant
|
||||
class AdminConstant extends AdminConstantBase
|
||||
{
|
||||
|
||||
/**
|
||||
* 超级管理员,不受权限控制
|
||||
*/
|
||||
const SUPER_ADMIN_ID = 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace app\common\constants;
|
||||
|
||||
use base\common\constants\MenuConstantBase;
|
||||
|
||||
/**
|
||||
* 菜单常量
|
||||
* Class MenuConstant
|
||||
* @package app\common\constants
|
||||
* Class MenuConstant.
|
||||
*/
|
||||
class MenuConstant
|
||||
class MenuConstant extends MenuConstantBase
|
||||
{
|
||||
|
||||
/**
|
||||
* 首页的PID
|
||||
*/
|
||||
const HOME_PID = 99999999;
|
||||
|
||||
/**
|
||||
* 模块名前缀
|
||||
*/
|
||||
const MODULE_PREFIX = 'ulthon_admin_';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,442 +2,11 @@
|
||||
|
||||
namespace app\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;
|
||||
use base\common\controller\AdminControllerBase;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
use app\BaseController;
|
||||
use app\common\traits\JumpTrait;
|
||||
use base\common\controller\ToolsControllerBase;
|
||||
|
||||
class ToolsController extends BaseController
|
||||
class ToolsController extends ToolsControllerBase
|
||||
{
|
||||
use JumpTrait;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
|
||||
namespace app\common\event\AdminLoginSuccess;
|
||||
|
||||
use app\admin\model\SystemAdmin;
|
||||
use think\facade\Log;
|
||||
use base\common\event\AdminLoginSuccess\LogEventBase;
|
||||
|
||||
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;
|
||||
|
||||
use think\facade\Env;
|
||||
use think\facade\View;
|
||||
use base\common\event\AdminLoginType\DemoEventBase;
|
||||
|
||||
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;
|
||||
|
||||
use Exception;
|
||||
use extend\base\commno\exception\EventExceptionBase;
|
||||
|
||||
class EventException extends Exception
|
||||
class EventException extends EventExceptionBase
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,106 +2,8 @@
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\facade\Cache;
|
||||
use think\facade\Env;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
use think\helper\Str;
|
||||
use base\common\model\BaseModelBase;
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
|
||||
use think\model\concern\SoftDelete;
|
||||
use base\common\model\TimeModelBase;
|
||||
|
||||
/**
|
||||
* 有关时间的模型
|
||||
* Class TimeModel
|
||||
* @package app\common\model
|
||||
* Class TimeModel.
|
||||
*/
|
||||
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
|
||||
|
||||
namespace app\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;
|
||||
use base\common\provider\ExceptionHandleBase;
|
||||
|
||||
/**
|
||||
* 应用异常处理类
|
||||
* 应用异常处理类.
|
||||
*/
|
||||
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;
|
||||
|
||||
use base\common\provider\RequestBase;
|
||||
|
||||
// 应用请求对象类
|
||||
class Request extends \think\Request
|
||||
class Request extends RequestBase
|
||||
{
|
||||
protected $filter = [];
|
||||
}
|
||||
|
||||
@@ -2,82 +2,8 @@
|
||||
|
||||
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;
|
||||
|
||||
use think\db\Query as DbQuery;
|
||||
use think\facade\Env;
|
||||
use think\facade\Log;
|
||||
use think\helper\Str;
|
||||
use base\common\provider\db\QueryBase;
|
||||
|
||||
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
|
||||
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\constants\AdminConstant;
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use base\common\service\AuthServiceBase;
|
||||
|
||||
/**
|
||||
* 权限验证服务
|
||||
* Class AuthService
|
||||
* @package app\common\service
|
||||
* Class AuthService.
|
||||
*/
|
||||
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
|
||||
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\constants\MenuConstant;
|
||||
use think\facade\Db;
|
||||
use base\common\service\MenuServiceBase;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
use base\common\service\UploadServiceBase;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
use Localheinz\Diff\Differ;
|
||||
use think\facade\App;
|
||||
use base\common\tools\PathToolsBase;
|
||||
|
||||
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;
|
||||
|
||||
use think\exception\HttpResponseException;
|
||||
use think\Response;
|
||||
use base\common\traits\JumpTraitBase;
|
||||
|
||||
/**
|
||||
* Trait JumpTrait
|
||||
* @package app\common\traits
|
||||
* Trait JumpTrait.
|
||||
*/
|
||||
trait JumpTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* 操作成功跳转的快捷方法
|
||||
* @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';
|
||||
}
|
||||
use JumpTraitBase;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用设置
|
||||
// +----------------------------------------------------------------------
|
||||
@@ -7,39 +8,39 @@ use think\facade\Env;
|
||||
|
||||
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',
|
||||
// 应用映射(自动多应用模式有效)
|
||||
'app_map' => [
|
||||
'app_map' => [
|
||||
Env::get('adminsystem.admin', 'admin') => 'admin',
|
||||
],
|
||||
// 后台别名
|
||||
'admin_alias_name' => Env::get('adminsystem.admin', 'admin'),
|
||||
// 域名绑定(自动多应用模式有效)
|
||||
'domain_bind' => [],
|
||||
'domain_bind' => [],
|
||||
// 禁止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_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[] = 'composer.json';
|
||||
|
||||
$skip_files[] = 'app/common/event/AdminLoginSuccess/LogEvent.php';
|
||||
$skip_files[] = 'app/common/event/AdminLoginType/DemoEvent.php';
|
||||
|
||||
$config['skip_files'] = $skip_files;
|
||||
|
||||
$skip_dir = [];
|
||||
|
||||
$skip_dir[] = 'runtime';
|
||||
$skip_dir[] = 'vendor';
|
||||
|
||||
$skip_dir[] = 'app/admin/controller';
|
||||
$skip_dir[] = 'app/admin/middleware';
|
||||
$skip_dir[] = 'app/admin/model';
|
||||
@@ -26,20 +31,37 @@ $skip_dir[] = 'app/admin/service';
|
||||
$skip_dir[] = 'app/admin/traits';
|
||||
$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;
|
||||
|
||||
// append 如果当前版本不存在,则追加,如果存在,则不应当覆盖
|
||||
// append 的文件应当在skip内部
|
||||
|
||||
$append_files = [];
|
||||
|
||||
$append_files[] = 'app/common/app/functions.php';
|
||||
$append_files[] = 'app/common/app/listen.php';
|
||||
$append_files[] = 'app/common/app/middleware.php';
|
||||
$append_files[] = 'app/common/app/service.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;
|
||||
|
||||
$append_dir = [];
|
||||
|
||||
$append_dir[] = 'app/admin/controller';
|
||||
$append_dir[] = 'app/admin/middleware';
|
||||
$append_dir[] = 'app/admin/model';
|
||||
@@ -47,6 +69,17 @@ $append_dir[] = 'app/admin/service';
|
||||
$append_dir[] = 'app/admin/traits';
|
||||
$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;
|
||||
|
||||
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