初始化-安装依赖

This commit is contained in:
2026-03-03 10:06:12 +08:00
parent 3f349a35a4
commit ec8cac4221
187 changed files with 26292 additions and 0 deletions

View File

@@ -0,0 +1,209 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use support\think\Db;
/**
* 数据表维护逻辑层
*/
class DatabaseLogic extends BaseLogic
{
/**
* 获取数据源
* @return array
*/
public function getDbSource(): array
{
$data = config('think-orm.connections');
$list = [];
foreach ($data as $k => $v) {
$list[] = $k;
}
return $list;
}
/**
* 数据列表
* @param $query
* @return mixed
*/
public function getList($query): mixed
{
$request = request();
$page = $request ? ($request->input('page') ?: 1) : 1;
$limit = $request ? ($request->input('limit') ?: 10) : 10;
return self::getTableList($query, $page, $limit);
}
/**
* 获取数据库表数据
*/
public function getTableList($query, $current_page = 1, $per_page = 10): array
{
if (!empty($query['source'])) {
if (!empty($query['name'])) {
$sql = 'show table status where name=:name ';
$list = Db::connect($query['source'])->query($sql, ['name' => $query['name']]);
} else {
$list = Db::connect($query['source'])->query('show table status');
}
} else {
if (!empty($query['name'])) {
$sql = 'show table status where name=:name ';
$list = Db::query($sql, ['name' => $query['name']]);
} else {
$list = Db::query('show table status');
}
}
$data = [];
foreach ($list as $item) {
$data[] = [
'name' => $item['Name'],
'engine' => $item['Engine'],
'rows' => $item['Rows'],
'data_free' => $item['Data_free'],
'data_length' => $item['Data_length'],
'index_length' => $item['Index_length'],
'collation' => $item['Collation'],
'create_time' => $item['Create_time'],
'update_time' => $item['Update_time'],
'comment' => $item['Comment'],
];
}
$total = count($data);
$last_page = ceil($total / $per_page);
$startIndex = ($current_page - 1) * $per_page;
$pageData = array_slice($data, $startIndex, $per_page);
return [
'data' => $pageData,
'total' => $total,
'current_page' => $current_page,
'per_page' => $per_page,
'last_page' => $last_page,
];
}
/**
* 获取列信息
*/
public function getColumnList($table, $source): array
{
$columnList = [];
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
if (!empty($source)) {
$list = Db::connect($source)->query('SHOW FULL COLUMNS FROM `' . $table . '`');
} else {
$list = Db::query('SHOW FULL COLUMNS FROM `' . $table . '`');
}
foreach ($list as $column) {
preg_match('/^\w+/', $column['Type'], $matches);
$columnList[] = [
'column_key' => $column['Key'],
'column_name' => $column['Field'],
'column_type' => $matches[0],
'column_comment' => trim(preg_replace("/\([^()]*\)/", "", $column['Comment'])),
'extra' => $column['Extra'],
'default_value' => $column['Default'],
'is_nullable' => $column['Null'],
];
}
}
return $columnList;
}
/**
* 优化表
*/
public function optimizeTable($tables)
{
foreach ($tables as $table) {
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
Db::execute('OPTIMIZE TABLE `' . $table . '`');
}
}
}
/**
* 清理表碎片
*/
public function fragmentTable($tables)
{
foreach ($tables as $table) {
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
Db::execute('ANALYZE TABLE `' . $table . '`');
}
}
}
/**
* 获取回收站数据
*/
public function recycleData($table)
{
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
// 查询表字段
$sql = 'SHOW COLUMNS FROM `' . $table . '` where Field = "delete_time"';
$columns = Db::query($sql);
$isDeleteTime = false;
if (count($columns) > 0) {
$isDeleteTime = true;
}
if (!$isDeleteTime) {
throw new ApiException('当前表不支持回收站功能');
}
// 查询软删除数据
$request = request();
$limit = $request ? ($request->input('limit') ?: 10) : 10;
return Db::table($table)->whereNotNull('delete_time')
->order('delete_time', 'desc')
->paginate($limit)
->toArray();
} else {
return [];
}
}
/**
* 删除数据
* @param $table
* @param $ids
* @return bool
*/
public function delete($table, $ids)
{
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
$count = Db::table($table)->whereIn('id', $ids)->delete($ids);
return $count > 0;
} else {
return false;
}
}
/**
* 恢复数据
* @param $table
* @param $ids
* @return bool
*/
public function recovery($table, $ids)
{
if (preg_match("/^[a-zA-Z0-9_]+$/", $table)) {
$count = Db::table($table)
->where('id', 'in', $ids)
->update(['delete_time' => null]);
return $count > 0;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,199 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use Exception;
use plugin\saiadmin\app\model\system\SystemAttachment;
use plugin\saiadmin\app\model\system\SystemCategory;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\service\storage\ChunkUploadService;
use plugin\saiadmin\service\storage\UploadService;
use plugin\saiadmin\utils\Arr;
use plugin\saiadmin\utils\Helper;
/**
* 附件逻辑层
*/
class SystemAttachmentLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemAttachment();
}
/**
* @param $category_id
* @param $ids
* @return mixed
*/
public function move($category_id, $ids): mixed
{
$category = SystemCategory::where('id', $category_id)->findOrEmpty();
if ($category->isEmpty()) {
throw new ApiException('目标分类不存在');
}
return $this->model->whereIn('id', $ids)->update(['category_id' => $category_id]);
}
/**
* 保存网络图片
* @param $url
* @param $config
* @return array
* @throws ApiException|Exception
*/
public function saveNetworkImage($url, $config): array
{
$image_data = file_get_contents($url);
if ($image_data === false) {
throw new ApiException('获取文件资源失败');
}
$image_resource = imagecreatefromstring($image_data);
if (!$image_resource) {
throw new ApiException('创建图片资源失败');
}
$filename = basename($url);
$file_extension = pathinfo($filename, PATHINFO_EXTENSION);
$full_dir = runtime_path() . '/resource/';
if (!is_dir($full_dir)) {
mkdir($full_dir, 0777, true);
}
$save_path = $full_dir . $filename;
$mime_type = 'image/';
switch ($file_extension) {
case 'jpg':
case 'jpeg':
$mime_type = 'image/jpeg';
$result = imagejpeg($image_resource, $save_path);
break;
case 'png':
$mime_type = 'image/png';
$result = imagepng($image_resource, $save_path);
break;
case 'gif':
$mime_type = 'image/gif';
$result = imagegif($image_resource, $save_path);
break;
default:
imagedestroy($image_resource);
throw new ApiException('文件格式错误');
}
imagedestroy($image_resource);
if (!$result) {
throw new ApiException('文件保存失败');
}
$hash = md5_file($save_path);
$size = filesize($save_path);
$model = $this->model->where('hash', $hash)->find();
if ($model) {
unlink($save_path);
return $model->toArray();
} else {
$logic = new SystemConfigLogic();
$uploadConfig = $logic->getGroup('upload_config');
$root = Arr::getConfigValue($uploadConfig, 'local_root');
$folder = date('Ymd');
$full_dir = base_path() . DIRECTORY_SEPARATOR . $root . $folder . DIRECTORY_SEPARATOR;
if (!is_dir($full_dir)) {
mkdir($full_dir, 0777, true);
}
$object_name = bin2hex(pack('Nn', time(), random_int(1, 65535))) . ".$file_extension";
$newPath = $full_dir . $object_name;
copy($save_path, $newPath);
unlink($save_path);
$domain = Arr::getConfigValue($uploadConfig, 'local_domain');
$uri = Arr::getConfigValue($uploadConfig, 'local_uri');
$baseUrl = $domain . $uri . $folder . '/';
$info['storage_mode'] = 1;
$info['category_id'] = request()->input('category_id', 1);
$info['origin_name'] = $filename;
$info['object_name'] = $object_name;
$info['hash'] = $hash;
$info['mime_type'] = $mime_type;
$info['storage_path'] = $root . $folder . '/' . $object_name;
$info['suffix'] = $file_extension;
$info['size_byte'] = $size;
$info['size_info'] = formatBytes($size);
$info['url'] = $baseUrl . $object_name;
$this->model->save($info);
return $info;
}
}
/**
* 文件上传
* @param string $upload
* @param bool $local
* @return array
*/
public function uploadBase(string $upload = 'image', bool $local = false): array
{
$logic = new SystemConfigLogic();
$uploadConfig = $logic->getGroup('upload_config');
$type = Arr::getConfigValue($uploadConfig, 'upload_mode');
if ($local === true) {
$type = 1;
}
$result = UploadService::disk($type, $upload)->uploadFile();
$data = $result[0];
$hash = $data['unique_id'];
$hash_check = config('plugin.saiadmin.saithink.file_hash', false);
if ($hash_check) {
$model = $this->model->where('hash', $hash)->findOrEmpty();
if (!$model->isEmpty()) {
return $model->toArray();
}
}
$url = str_replace('\\', '/', $data['url']);
$savePath = str_replace('\\', '/', $data['save_path']);
$info['storage_mode'] = $type;
$info['category_id'] = request()->input('category_id', 1);
$info['origin_name'] = $data['origin_name'];
$info['object_name'] = $data['save_name'];
$info['hash'] = $data['unique_id'];
$info['mime_type'] = $data['mime_type'];
$info['storage_path'] = $savePath;
$info['suffix'] = $data['extension'];
$info['size_byte'] = $data['size'];
$info['size_info'] = formatBytes($data['size']);
$info['url'] = $url;
$this->model->save($info);
return $info;
}
/**
* 切片上传
* @param $data
* @return array
*/
public function chunkUpload($data): array
{
$chunkService = new ChunkUploadService();
if ($data['index'] == 0) {
$model = $this->model->where('hash', $data['hash'])->findOrEmpty();
if (!$model->isEmpty()) {
return $model->toArray();
} else {
return $chunkService->checkChunk($data);
}
} else {
return $chunkService->uploadChunk($data);
}
}
}

View File

@@ -0,0 +1,101 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\app\model\system\SystemCategory;
use plugin\saiadmin\utils\Helper;
use plugin\saiadmin\utils\Arr;
/**
* 附件分类逻辑层
*/
class SystemCategoryLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemCategory();
}
/**
* 添加数据
*/
public function add($data): bool
{
$data = $this->handleData($data);
return $this->model->save($data);
}
/**
* 修改数据
*/
public function edit($id, $data): bool
{
$data = $this->handleData($data);
if ($data['parent_id'] == $id) {
throw new ApiException('上级分类和当前分类不能相同');
}
if (in_array($id, explode(',', $data['level']))) {
throw new ApiException('不能将上级分类设置为当前分类的子分类');
}
$model = $this->model->findOrEmpty($id);
if ($model->isEmpty()) {
throw new ApiException('数据不存在');
}
return $model->save($data);
}
/**
* 数据删除
*/
public function destroy($ids): bool
{
$num = $this->model->where('parent_id', 'in', $ids)->count();
if ($num > 0) {
throw new ApiException('该部门下存在子分类,请先删除子分类');
} else {
return $this->model->destroy($ids);
}
}
/**
* 数据处理
*/
protected function handleData($data)
{
if (empty($data['parent_id']) || $data['parent_id'] == 0) {
$data['level'] = '0';
$data['parent_id'] = 0;
} else {
$parentMenu = SystemCategory::findOrEmpty($data['parent_id']);
$data['level'] = $parentMenu['level'] . $parentMenu['id'] . ',';
}
return $data;
}
/**
* 数据树形化
* @param array $where
* @return array
*/
public function tree(array $where = []): array
{
$query = $this->search($where);
$request = request();
if ($request && $request->input('tree', 'false') === 'true') {
$query->field('id, id as value, category_name as label, parent_id, category_name, sort');
}
$query->order('sort', 'desc');
$data = $this->getAll($query);
return Helper::makeTree($data);
}
}

View File

@@ -0,0 +1,57 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\cache\ConfigCache;
use plugin\saiadmin\app\model\system\SystemConfigGroup;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\app\model\system\SystemConfig;
use support\think\Db;
/**
* 参数配置分组逻辑层
*/
class SystemConfigGroupLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemConfigGroup();
}
/**
* 删除配置信息
*/
public function destroy($ids): bool
{
$id = $ids[0];
$model = $this->model->where('id', $id)->findOrEmpty();
if ($model->isEmpty()) {
throw new ApiException('配置数据未找到');
}
if (in_array(intval($id), [1, 2, 3])) {
throw new ApiException('系统默认分组,无法删除');
}
Db::startTrans();
try {
// 删除配置组
$model->delete();
// 删除配置组数据
$typeIds = SystemConfig::where('group_id', $id)->column('id');
SystemConfig::destroy($typeIds);
ConfigCache::clearConfig($model->code);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
throw new ApiException('删除数据异常,请检查');
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\cache\ConfigCache;
use plugin\saiadmin\app\model\system\SystemConfig;
use plugin\saiadmin\app\model\system\SystemConfigGroup;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\utils\Helper;
/**
* 参数配置逻辑层
*/
class SystemConfigLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemConfig();
}
/**
* 添加数据
* @param mixed $data
* @return mixed
*/
public function add($data): mixed
{
$result = $this->model->create($data);
$group = SystemConfigGroup::find($data['group_id']);
ConfigCache::clearConfig($group->code);
return $result;
}
/**
* 编辑数据
* @param mixed $id
* @param mixed $data
* @return bool
*/
public function edit($id, $data): bool
{
$result = parent::edit($id, $data);
$group = SystemConfigGroup::find($data['group_id']);
ConfigCache::clearConfig($group->code);
return $result;
}
/**
* 批量更新
* @param mixed $group_id
* @param mixed $config
* @return bool
*/
public function batchUpdate($group_id, $config): bool
{
$group = SystemConfigGroup::find($group_id);
if (!$group) {
throw new ApiException('配置组未找到');
}
$saveData = [];
foreach ($config as $key => $value) {
$saveData[] = [
'id' => $value['id'],
'group_id' => $group_id,
'name' => $value['name'],
'key' => $value['key'],
'value' => $value['value']
];
}
// upsert: 根据 id 更新,如果不存在则插入
$this->model->saveAll($saveData);
ConfigCache::clearConfig($group->code);
return true;
}
/**
* 获取配置数据
* @param mixed $code
* @return array
*/
public function getData($code): array
{
$group = SystemConfigGroup::where('code', $code)->findOrEmpty();
if (empty($group)) {
return [];
}
$config = SystemConfig::where('group_id', $group['id'])->select()->toArray();
return $config;
}
/**
* 获取配置组
*/
public function getGroup($config): array
{
return ConfigCache::getConfig($config);
}
}

View File

@@ -0,0 +1,127 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\app\model\system\SystemDept;
use plugin\saiadmin\app\model\system\SystemUser;
use plugin\saiadmin\utils\Helper;
use plugin\saiadmin\utils\Arr;
/**
* 部门逻辑层
*/
class SystemDeptLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemDept();
}
/**
* 添加数据
*/
public function add($data): mixed
{
$data = $this->handleData($data);
$this->model->save($data);
return $this->model->getKey();
}
/**
* 修改数据
*/
public function edit($id, $data): mixed
{
$oldLevel = $data['level'] . $id . ',';
$data = $this->handleData($data);
if ($data['parent_id'] == $id) {
throw new ApiException('上级部门和当前部门不能相同');
}
if (in_array($id, explode(',', $data['level']))) {
throw new ApiException('不能将上级部门设置为当前部门的子部门');
}
$newLevel = $data['level'] . $id . ',';
$deptIds = $this->model->where('level', 'like', $oldLevel . '%')->column('id');
return $this->transaction(function () use ($deptIds, $oldLevel, $newLevel, $data, $id) {
$this->model->whereIn('id', $deptIds)->exp('level', "REPLACE(level, '$oldLevel', '$newLevel')")->update([]);
return $this->model->update($data, ['id' => $id]);
});
}
/**
* 数据删除
*/
public function destroy($ids): bool
{
$num = $this->model->where('parent_id', 'in', $ids)->count();
if ($num > 0) {
throw new ApiException('该部门下存在子部门,请先删除子部门');
} else {
$count = SystemUser::where('dept_id', 'in', $ids)->count();
if ($count > 0) {
throw new ApiException('该部门下存在用户,请先删除或者转移用户');
}
return $this->model->destroy($ids);
}
}
/**
* 数据处理
*/
protected function handleData($data)
{
// 处理上级部门
if (empty($data['parent_id']) || $data['parent_id'] == 0) {
$data['level'] = '0';
$data['parent_id'] = 0;
} else {
$parentMenu = SystemDept::findOrEmpty($data['parent_id']);
$data['level'] = $parentMenu['level'] . $parentMenu['id'] . ',';
}
return $data;
}
/**
* 数据树形化
* @param array $where
* @return array
*/
public function tree(array $where = []): array
{
$query = $this->search($where);
$request = request();
if ($request && $request->input('tree', 'false') === 'true') {
$query->field('id, id as value, name as label, parent_id');
}
$query->order('sort', 'desc');
$query->with(['leader']);
$data = $this->getAll($query);
return Helper::makeTree($data);
}
/**
* 可操作部门
* @param array $where
* @return array
*/
public function accessDept(array $where = []): array
{
$query = $this->search($where);
$query->auth($this->adminInfo['deptList']);
$query->field('id, id as value, name as label, parent_id');
$query->order('sort', 'desc');
$data = $this->getAll($query);
return Helper::makeTree($data);
}
}

View File

@@ -0,0 +1,46 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\app\model\system\SystemDictData;
use plugin\saiadmin\app\model\system\SystemDictType;
use plugin\saiadmin\app\cache\DictCache;
use plugin\saiadmin\utils\Helper;
/**
* 字典类型逻辑层
*/
class SystemDictDataLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemDictData();
}
/**
* 添加数据
* @param $data
* @return mixed
*/
public function add($data): mixed
{
$type = SystemDictType::where('id', $data['type_id'])->findOrEmpty();
if ($type->isEmpty()) {
throw new ApiException('字典类型不存在');
}
$data['code'] = $type->code;
$model = $this->model->create($data);
DictCache::clear();
return $model->getKey();
}
}

View File

@@ -0,0 +1,116 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\app\model\system\SystemDictType;
use plugin\saiadmin\app\model\system\SystemDictData;
use support\think\Db;
/**
* 字典类型逻辑层
*/
class SystemDictTypeLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemDictType();
}
/**
* 添加数据
*/
public function add($data): mixed
{
$model = $this->model->where('code', $data['code'])->findOrEmpty();
if (!$model->isEmpty()) {
throw new ApiException('该字典标识已存在');
}
return $this->model->save($data);
}
/**
* 数据更新
*/
public function edit($id, $data): mixed
{
Db::startTrans();
try {
// 修改数据字典类型
$result = $this->model->update($data, ['id' => $id]);
// 更新数据字典数据
SystemDictData::update(['code' => $data['code']], ['type_id' => $id]);
Db::commit();
return $result;
} catch (\Exception $e) {
Db::rollback();
throw new ApiException('修改数据异常,请检查');
}
}
/**
* 数据删除
*/
public function destroy($ids): bool
{
Db::startTrans();
try {
// 删除数据字典类型
$result = $this->model->destroy($ids);
// 删除数据字典数据
$typeIds = SystemDictData::where('type_id', 'in', $ids)->column('id');
SystemDictData::destroy($typeIds);
Db::commit();
return $result;
} catch (\Exception $e) {
Db::rollback();
throw new ApiException('删除数据异常,请检查');
}
}
/**
* 获取全部字典
* @return array
*/
public function getDictAll(): array
{
$data = $this->model->where('status', 1)->field('id, name, code, remark')
->with([
'dicts' => function ($query) {
$query->where('status', 1)->field('id, type_id, label, value, color, code, sort')->order('sort', 'desc');
}
])->select()->toArray();
return $this->packageDict($data, 'code');
}
/**
* 组合数据
* @param $array
* @param $field
* @return array
*/
private function packageDict($array, $field): array
{
$result = [];
foreach ($array as $item) {
if (isset($item[$field])) {
if (isset($result[$item[$field]])) {
$result[$item[$field]] = [($result[$item[$field]])];
$result[$item[$field]][] = $item['dicts'];
} else {
$result[$item[$field]] = $item['dicts'];
}
}
}
return $result;
}
}

View File

@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\model\system\SystemLoginLog;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\utils\Helper;
use support\think\Db;
/**
* 登录日志逻辑层
*/
class SystemLoginLogLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemLoginLog();
}
/**
* 登录统计图表
* @return array
*/
public function loginChart(): array
{
$sql = "
SELECT
d.date AS login_date,
COUNT(l.login_time) AS login_count
FROM
(SELECT CURDATE() - INTERVAL (a.N) DAY AS date
FROM (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
) d
LEFT JOIN sa_system_login_log l
ON DATE(l.login_time) = d.date
GROUP BY d.date
ORDER BY d.date ASC;
";
$data = Db::query($sql);
return [
'login_count' => array_column($data, 'login_count'),
'login_date' => array_column($data, 'login_date'),
];
}
/**
* 登录统计图表
* @return array
*/
public function loginBarChart(): array
{
$sql = "
SELECT
-- 拼接成 YYYY-MM 格式,例如 2023-01
CONCAT(LPAD(m.month_num, 2, '0'), '月') AS login_month,
COUNT(l.login_time) AS login_count
FROM
-- 生成 1 到 12 的月份数字
(SELECT 1 AS month_num UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) m
LEFT JOIN sa_system_login_log l
-- 关联条件:年份等于今年 且 月份等于生成的数字
ON YEAR(l.login_time) = YEAR(CURDATE())
AND MONTH(l.login_time) = m.month_num
GROUP BY
m.month_num
ORDER BY
m.month_num ASC;
";
$data = Db::query($sql);
return [
'login_count' => array_column($data, 'login_count'),
'login_month' => array_column($data, 'login_month'),
];
}
}

View File

@@ -0,0 +1,26 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\model\system\SystemMail;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\utils\Helper;
/**
* 邮件模型逻辑层
*/
class SystemMailLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemMail();
}
}

View File

@@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\model\system\SystemMenu;
use plugin\saiadmin\app\model\system\SystemRoleMenu;
use plugin\saiadmin\app\model\system\SystemUserRole;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\utils\Arr;
use plugin\saiadmin\utils\Helper;
/**
* 菜单逻辑层
*/
class SystemMenuLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemMenu();
}
/**
* 数据添加
*/
public function add($data): mixed
{
$data = $this->handleData($data);
return $this->model->save($data);
}
/**
* 数据修改
*/
public function edit($id, $data): mixed
{
$data = $this->handleData($data);
if ($data['parent_id'] == $id) {
throw new ApiException('不能设置父级为自身');
}
return $this->model->update($data, ['id' => $id]);
}
/**
* 数据删除
*/
public function destroy($ids): bool
{
$num = $this->model->where('parent_id', 'in', $ids)->count();
if ($num > 0) {
throw new ApiException('该菜单下存在子菜单,请先删除子菜单');
} else {
return $this->model->destroy($ids);
}
}
/**
* 数据处理
*/
protected function handleData($data)
{
// 处理上级菜单
if (empty($data['parent_id']) || $data['parent_id'] == 0) {
$data['level'] = '0';
$data['parent_id'] = 0;
} else {
$parentMenu = $this->model->findOrEmpty($data['parent_id']);
$data['level'] = $parentMenu['level'] . $parentMenu['id'] . ',';
}
return $data;
}
/**
* 数据树形化
* @param $where
* @return array
*/
public function tree($where = []): array
{
$query = $this->search($where);
$request = request();
if ($request && $request->input('tree', 'false') === 'true') {
$query->field('id, id as value, name as label, parent_id, type');
}
$query->order('sort', 'desc');
$data = $this->getAll($query);
return Helper::makeTree($data);
}
/**
* 权限菜单
* @return array
*/
public function auth(): array
{
$roleLogic = new SystemRoleLogic();
$role_ids = Arr::getArrayColumn($this->adminInfo['roleList'], 'id');
$roles = $roleLogic->getMenuIdsByRoleIds($role_ids);
$ids = $this->filterMenuIds($roles);
$query = $this->model
->field('id, id as value, name as label, parent_id, type')
->where('status', 1)
->where('id', 'in', $ids)
->order('sort', 'desc');
$data = $this->getAll($query);
return Helper::makeTree($data);
}
/**
* 获取全部菜单
*/
public function getAllMenus(): array
{
$query = $this->search(['status' => 1, 'type' => [1, 2, 4]])->order('sort', 'desc');
$data = $this->getAll($query);
return Helper::makeArtdMenus($data);
}
/**
* 获取全部权限
* @return array
*/
public function getAllAuth(): array
{
return SystemMenu::where('type', 3)
->where('status', 1)
->column('slug');
}
/**
* 根据角色获取权限
* @param $roleIds
* @return array
*/
public function getAuthByRole($roleIds): array
{
$menuId = SystemRoleMenu::whereIn('role_id', $roleIds)->column('menu_id');
return SystemMenu::distinct(true)
->where('type', 3)
->where('status', 1)
->where('id', 'in', array_unique($menuId))
->column('slug');
}
/**
* 根据角色获取菜单
* @param $roleIds
* @return array
*/
public function getMenuByRole($roleIds): array
{
$menuId = SystemRoleMenu::whereIn('role_id', $roleIds)->column('menu_id');
$data = SystemMenu::distinct(true)
->where('status', 1)
->where('type', 'in', [1, 2, 4])
->where('id', 'in', array_unique($menuId))
->order('sort', 'desc')
->select()
->toArray();
return Helper::makeArtdMenus($data);
}
/**
* 过滤通过角色查询出来的菜单id列表并去重
* @param array $roleData
* @return array
*/
public function filterMenuIds(array &$roleData): array
{
$ids = [];
foreach ($roleData as $val) {
foreach ($val['menus'] as $menu) {
$ids[] = $menu['id'];
}
}
unset($roleData);
return array_unique($ids);
}
}

View File

@@ -0,0 +1,38 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\model\system\SystemOperLog;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\utils\Helper;
/**
* 操作日志逻辑层
*/
class SystemOperLogLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemOperLog();
}
/**
* 获取自己的操作日志
* @param mixed $where
* @return array
*/
public function getOwnOperLogList($where): array
{
$query = $this->search($where);
$query->field('id, username, method, router, service_name, ip, ip_location, create_time');
return $this->getList($query);
}
}

View File

@@ -0,0 +1,95 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\model\system\SystemPost;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\service\OpenSpoutWriter;
use OpenSpout\Reader\XLSX\Reader;
/**
* 岗位管理逻辑层
*/
class SystemPostLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemPost();
}
/**
* 可操作岗位
* @param array $where
* @return array
*/
public function accessPost(array $where = []): array
{
$query = $this->search($where);
$query->field('id, id as value, name as label, name, code');
return $this->getAll($query);
}
/**
* 导入数据
*/
public function import($file)
{
$path = $this->getImport($file);
$reader = new Reader();
try {
$reader->open($path);
$data = [];
foreach ($reader->getSheetIterator() as $sheet) {
$isHeader = true;
foreach ($sheet->getRowIterator() as $row) {
if ($isHeader) {
$isHeader = false;
continue;
}
$cells = $row->getCells();
$data[] = [
'name' => $cells[0]->getValue(),
'code' => $cells[1]->getValue(),
'sort' => $cells[2]->getValue(),
'status' => $cells[3]->getValue(),
];
}
}
$this->saveAll($data);
} catch (\Exception $e) {
throw new ApiException('导入文件错误请上传正确的文件格式xlsx');
}
}
/**
* 导出数据
*/
public function export($where = [])
{
$query = $this->search($where)->field('id,name,code,sort,status,create_time');
$data = $this->getAll($query);
$file_name = '岗位数据.xlsx';
$header = ['编号', '岗位名称', '岗位标识', '排序', '状态', '创建时间'];
$filter = [
'status' => [
['value' => 1, 'label' => '正常'],
['value' => 2, 'label' => '禁用']
]
];
$writer = new OpenSpoutWriter($file_name);
$writer->setWidth([15, 15, 20, 15, 15, 25]);
$writer->setHeader($header);
$writer->setData($data, null, $filter);
$file_path = $writer->returnFile();
return response()->download($file_path, urlencode($file_name));
}
}

View File

@@ -0,0 +1,156 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\cache\UserMenuCache;
use plugin\saiadmin\app\model\system\SystemRole;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\utils\Helper;
use support\think\Cache;
use support\think\Db;
/**
* 角色逻辑层
*/
class SystemRoleLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemRole();
}
/**
* 添加数据
*/
public function add($data): bool
{
$data = $this->handleData($data);
return $this->model->save($data);
}
/**
* 修改数据
*/
public function edit($id, $data): bool
{
$model = $this->model->findOrEmpty($id);
if ($model->isEmpty()) {
throw new ApiException('数据不存在');
}
$data = $this->handleData($data);
return $model->save($data);
}
/**
* 删除数据
*/
public function destroy($ids): bool
{
// 越权保护
$levelArr = array_column($this->adminInfo['roleList'], 'level');
$maxLevel = max($levelArr);
$num = SystemRole::where('level', '>=', $maxLevel)->whereIn('id', $ids)->count();
if ($num > 0) {
throw new ApiException('不能操作比当前账户职级高的角色');
} else {
return $this->model->destroy($ids);
}
}
/**
* 数据处理
*/
protected function handleData($data)
{
// 越权保护
$levelArr = array_column($this->adminInfo['roleList'], 'level');
$maxLevel = max($levelArr);
if ($data['level'] >= $maxLevel) {
throw new ApiException('不能操作比当前账户职级高的角色');
}
return $data;
}
/**
* 可操作角色
* @param array $where
* @return array
*/
public function accessRole(array $where = []): array
{
$query = $this->search($where);
// 越权保护
$levelArr = array_column($this->adminInfo['roleList'], 'level');
$maxLevel = max($levelArr);
$query->where('level', '<', $maxLevel);
$query->order('sort', 'desc');
return $this->getAll($query);
}
/**
* 根据角色数组获取菜单
* @param $ids
* @return array
*/
public function getMenuIdsByRoleIds($ids): array
{
if (empty($ids))
return [];
return $this->model->where('id', 'in', $ids)->with([
'menus' => function ($query) {
$query->where('status', 1)->order('sort', 'desc');
}
])->select()->toArray();
}
/**
* 根据角色获取菜单
* @param $id
* @return array
*/
public function getMenuByRole($id): array
{
$role = $this->model->findOrEmpty($id);
$menus = $role->menus ?: [];
return [
'id' => $id,
'menus' => $menus
];
}
/**
* 保存菜单权限
* @param $id
* @param $menu_ids
* @return mixed
*/
public function saveMenuPermission($id, $menu_ids): mixed
{
return $this->transaction(function () use ($id, $menu_ids) {
$role = $this->model->findOrEmpty($id);
if ($role) {
$role->menus()->detach();
$data = array_map(function ($menu_id) use ($id) {
return ['menu_id' => $menu_id, 'role_id' => $id];
}, $menu_ids);
Db::name('sa_system_role_menu')->limit(100)->insertAll($data);
}
$cache = config('plugin.saiadmin.saithink.button_cache');
$tag = $cache['role'] . $id;
Cache::tag($tag)->clear(); // 清理权限缓存-角色TAG
UserMenuCache::clearMenuCache(); // 清理菜单缓存
return true;
});
}
}

View File

@@ -0,0 +1,336 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\system;
use plugin\saiadmin\app\cache\UserAuthCache;
use plugin\saiadmin\app\cache\UserInfoCache;
use plugin\saiadmin\app\cache\UserMenuCache;
use plugin\saiadmin\app\model\system\SystemDept;
use plugin\saiadmin\app\model\system\SystemRole;
use plugin\saiadmin\app\model\system\SystemUser;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\basic\think\BaseLogic;
use Webman\Event\Event;
use Tinywan\Jwt\JwtToken;
/**
* 用户信息逻辑层
*/
class SystemUserLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new SystemUser();
}
/**
* 分页数据列表
* @param mixed $where
* @return array
*/
public function indexList($where): array
{
$query = $this->search($where);
$query->with(['depts']);
$query->auth($this->adminInfo['deptList']);
return $this->getList($query);
}
/**
* 用户列表数据
* @param mixed $where
* @return array
*/
public function openUserList($where): array
{
$query = $this->search($where);
$query->field('id, username, realname, avatar, phone, email');
return $this->getList($query);
}
/**
* 读取用户信息
* @param mixed $id
* @return array
*/
public function getUser($id): array
{
$admin = $this->model->findOrEmpty($id);
$data = $admin->hidden(['password'])->toArray();
$data['roleList'] = $admin->roles->toArray() ?: [];
$data['postList'] = $admin->posts->toArray() ?: [];
$data['deptList'] = $admin->depts ? $admin->depts->toArray() : [];
return $data;
}
/**
* 读取数据
* @param $id
* @return array
*/
public function read($id): array
{
$data = $this->getUser($id);
if ($this->adminInfo['id'] > 1) {
// 部门保护
if (!$this->deptProtect($this->adminInfo['deptList'], $data['dept_id'])) {
throw new ApiException('没有权限操作该部门数据');
}
}
return $data;
}
/**
* 添加数据
* @param $data
* @return mixed
*/
public function add($data): mixed
{
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
return $this->transaction(function () use ($data) {
$role_ids = $data['role_ids'] ?? [];
$post_ids = $data['post_ids'] ?? [];
if ($this->adminInfo['id'] > 1) {
// 部门保护
if (!$this->deptProtect($this->adminInfo['deptList'], $data['dept_id'])) {
throw new ApiException('没有权限操作该部门数据');
}
// 越权保护
if (!$this->roleProtect($this->adminInfo['roleList'], $role_ids)) {
throw new ApiException('没有权限操作该角色数据');
}
}
$user = SystemUser::create($data);
$user->roles()->detach();
$user->posts()->detach();
$user->roles()->saveAll($role_ids);
if (!empty($post_ids)) {
$user->posts()->save($post_ids);
}
return $user;
});
}
/**
* 修改数据
* @param $id
* @param $data
* @return mixed
*/
public function edit($id, $data): mixed
{
unset($data['password']);
return $this->transaction(function () use ($data, $id) {
$role_ids = $data['role_ids'] ?? [];
$post_ids = $data['post_ids'] ?? [];
// 仅可修改当前部门和子部门的用户
$query = $this->model->where('id', $id);
$query->auth($this->adminInfo['deptList']);
$user = $query->findOrEmpty();
if ($user->isEmpty()) {
throw new ApiException('没有权限操作该数据');
}
if ($this->adminInfo['id'] > 1) {
// 部门保护
if (!$this->deptProtect($this->adminInfo['deptList'], $data['dept_id'])) {
throw new ApiException('没有权限操作该部门数据');
}
// 越权保护
if (!$this->roleProtect($this->adminInfo['roleList'], $role_ids)) {
throw new ApiException('没有权限操作该角色数据');
}
}
$result = parent::edit($id, $data);
if ($result) {
$user->roles()->detach();
$user->posts()->detach();
$user->roles()->saveAll($role_ids);
if (!empty($post_ids)) {
$user->posts()->save($post_ids);
}
UserInfoCache::clearUserInfo($id);
UserAuthCache::clearUserAuth($id);
UserMenuCache::clearUserMenu($id);
}
return $result;
});
}
/**
* 删除数据
* @param $ids
* @return bool
*/
public function destroy($ids): bool
{
if (is_array($ids)) {
if (count($ids) > 1) {
throw new ApiException('禁止批量删除操作');
}
$ids = $ids[0];
}
if ($ids == 1) {
throw new ApiException('超级管理员禁止删除');
}
$query = $this->model->where('id', $ids);
$query->auth($this->adminInfo['deptList']);
$user = $query->findOrEmpty();
if ($user->isEmpty()) {
throw new ApiException('没有权限操作该数据');
}
if ($this->adminInfo['id'] > 1) {
$role_ids = $user->roles->toArray() ?: [];
if (!empty($role_ids)) {
// 越权保护
if (!$this->roleProtect($this->adminInfo['roleList'], array_column($role_ids, 'id'))) {
throw new ApiException('没有权限操作该角色数据');
}
}
}
UserInfoCache::clearUserInfo($ids);
UserAuthCache::clearUserAuth($ids);
UserMenuCache::clearUserMenu($ids);
return parent::destroy($ids);
}
/**
* 用户登录
* @param string $username
* @param string $password
* @param string $type
* @return array
*/
public function login(string $username, string $password, string $type): array
{
$adminInfo = $this->model->where('username', $username)->findOrEmpty();
$status = 1;
$message = '登录成功';
if ($adminInfo->isEmpty()) {
$message = '账号或密码错误,请重新输入!';
throw new ApiException($message);
}
if ($adminInfo->status === 2) {
$status = 0;
$message = '您已被禁止登录!';
}
if (!password_verify($password, $adminInfo->password)) {
$status = 0;
$message = '账号或密码错误,请重新输入!';
}
if ($status === 0) {
// 登录事件
Event::emit('user.login', compact('username', 'status', 'message'));
throw new ApiException($message);
}
$adminInfo->login_time = date('Y-m-d H:i:s');
$adminInfo->login_ip = request()->getRealIp();
$adminInfo->save();
$access_exp = config('plugin.saiadmin.saithink.access_exp', 3 * 3600);
$token = JwtToken::generateToken([
'access_exp' => $access_exp,
'id' => $adminInfo->id,
'username' => $adminInfo->username,
'type' => $type,
'plat' => 'saiadmin',
]);
// 登录事件
$admin_id = $adminInfo->id;
Event::emit('user.login', compact('username', 'status', 'message', 'admin_id'));
return $token;
}
/**
* 更新资料
* @param mixed $id
* @param mixed $data
* @return bool
*/
public function updateInfo($id, $data): bool
{
$this->model->update($data, ['id' => $id], ['realname', 'gender', 'phone', 'email', 'avatar', 'signed']);
return true;
}
/**
* 密码修改
* @param $adminId
* @param $oldPassword
* @param $newPassword
* @return bool
*/
public function modifyPassword($adminId, $oldPassword, $newPassword): bool
{
$model = $this->model->findOrEmpty($adminId);
if (password_verify($oldPassword, $model->password)) {
$model->password = password_hash($newPassword, PASSWORD_DEFAULT);
return $model->save();
} else {
throw new ApiException('原密码错误');
}
}
/**
* 修改数据
*/
public function authEdit($id, $data)
{
if ($this->adminInfo['id'] > 1) {
// 判断用户是否可以操作
$query = SystemUser::where('id', $id);
$query->auth($this->adminInfo['deptList']);
$user = $query->findOrEmpty();
if ($user->isEmpty()) {
throw new ApiException('没有权限操作该数据');
}
}
parent::edit($id, $data);
}
/**
* 部门保护
* @param $dept
* @param $dept_id
* @return bool
*/
public function deptProtect($dept, $dept_id): bool
{
// 部门保护
$deptIds = [$dept['id']];
$deptLevel = $dept['level'] . $dept['id'] . ',';
$dept_ids = SystemDept::whereLike('level', $deptLevel . '%')->column('id');
$deptIds = array_merge($deptIds, $dept_ids);
if (!in_array($dept_id, $deptIds)) {
return false;
}
return true;
}
/**
* 越权保护
* @param $roleList
* @param $role_ids
* @return bool
*/
public function roleProtect($roleList, $role_ids): bool
{
// 越权保护
$levelArr = array_column($roleList, 'level');
$maxLevel = max($levelArr);
$currentLevel = SystemRole::whereIn('id', $role_ids)->max('level');
if ($currentLevel >= $maxLevel) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\tool;
use plugin\saiadmin\app\model\tool\CrontabLog;
use plugin\saiadmin\basic\think\BaseLogic;
/**
* 定时任务日志逻辑层
*/
class CrontabLogLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new CrontabLog();
}
}

View File

@@ -0,0 +1,247 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\tool;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Webman\Channel\Client as ChannelClient;
use plugin\saiadmin\app\model\tool\Crontab;
use plugin\saiadmin\app\model\tool\CrontabLog;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\exception\ApiException;
/**
* 定时任务逻辑层
*/
class CrontabLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new Crontab();
}
/**
* 添加任务
*/
public function add($data): bool
{
$second = $data['second'];
$minute = $data['minute'];
$hour = $data['hour'];
$week = $data['week'];
$day = $data['day'];
$month = $data['month'];
// 规则处理
$rule = match ($data['task_style']) {
1 => "0 {$minute} {$hour} * * *",
2 => "0 {$minute} * * * *",
3 => "0 {$minute} */{$hour} * * *",
4 => "0 */{$minute} * * * *",
5 => "*/{$second} * * * * *",
6 => "0 {$minute} {$hour} * * {$week}",
7 => "0 {$minute} {$hour} {$day} * *",
8 => "0 {$minute} {$hour} {$day} {$month} *",
default => throw new ApiException("任务类型异常"),
};
// 定时任务模型新增
$model = Crontab::create([
'name' => $data['name'],
'type' => $data['type'],
'task_style' => $data['task_style'],
'rule' => $rule,
'target' => $data['target'],
'parameter' => $data['parameter'],
'status' => $data['status'],
'remark' => $data['remark'],
]);
$id = $model->getKey();
// 连接到Channel服务
ChannelClient::connect();
ChannelClient::publish('crontab', ['args' => $id]);
return true;
}
/**
* 修改任务
*/
public function edit($id, $data): bool
{
$second = $data['second'];
$minute = $data['minute'];
$hour = $data['hour'];
$week = $data['week'];
$day = $data['day'];
$month = $data['month'];
// 规则处理
$rule = match ($data['task_style']) {
1 => "0 {$minute} {$hour} * * *",
2 => "0 {$minute} * * * *",
3 => "0 {$minute} */{$hour} * * *",
4 => "0 */{$minute} * * * *",
5 => "*/{$second} * * * * *",
6 => "0 {$minute} {$hour} * * {$week}",
7 => "0 {$minute} {$hour} {$day} * *",
8 => "0 {$minute} {$hour} {$day} {$month} *",
default => throw new ApiException("任务类型异常"),
};
// 查询任务数据
$model = $this->model->findOrEmpty($id);
if ($model->isEmpty()) {
throw new ApiException('数据不存在');
}
$result = $model->save([
'name' => $data['name'],
'type' => $data['type'],
'task_style' => $data['task_style'],
'rule' => $rule,
'target' => $data['target'],
'parameter' => $data['parameter'],
'status' => $data['status'],
'remark' => $data['remark'],
]);
if ($result) {
// 连接到Channel服务
ChannelClient::connect();
ChannelClient::publish('crontab', ['args' => $id]);
}
// 修改任务数据
return $result;
}
/**
* 删除定时任务
* @param $ids
* @return bool
* @throws Exception
*/
public function destroy($ids): bool
{
if (is_array($ids)) {
if (count($ids) > 1) {
throw new ApiException('禁止批量删除操作');
}
$ids = $ids[0];
}
$result = parent::destroy($ids);
if ($result) {
// 连接到Channel服务
ChannelClient::connect();
ChannelClient::publish('crontab', ['args' => $ids]);
}
return $result;
}
/**
* 修改状态
* @param $id
* @param $status
* @return bool
*/
public function changeStatus($id, $status): bool
{
$model = $this->model->findOrEmpty($id);
if ($model->isEmpty()) {
throw new ApiException('数据不存在');
}
$result = $model->save(['status' => $status]);
if ($result) {
// 连接到Channel服务
ChannelClient::connect();
ChannelClient::publish('crontab', ['args' => $id]);
}
return $result;
}
/**
* 执行定时任务
* @param $id
* @return bool
*/
public function run($id): bool
{
$info = $this->model->where('status', 1)->findOrEmpty($id);
if ($info->isEmpty()) {
return false;
}
$data['crontab_id'] = $info->id;
$data['name'] = $info->name;
$data['target'] = $info->target;
$data['parameter'] = $info->parameter;
switch ($info->type) {
case 1:
// URL任务GET
$httpClient = new Client([
'timeout' => 5,
'verify' => false,
]);
try {
$httpClient->request('GET', $info->target);
$data['status'] = 1;
CrontabLog::create($data);
return true;
} catch (GuzzleException $e) {
$data['status'] = 2;
$data['exception_info'] = $e->getMessage();
CrontabLog::create($data);
return false;
}
case 2:
// URL任务POST
$httpClient = new Client([
'timeout' => 5,
'verify' => false,
]);
try {
$res = $httpClient->request('POST', $info->target, [
'form_params' => json_decode($info->parameter ?? '', true)
]);
$data['status'] = 1;
$data['exception_info'] = $res->getBody();
CrontabLog::create($data);
return true;
} catch (GuzzleException $e) {
$data['status'] = 2;
$data['exception_info'] = $e->getMessage();
CrontabLog::create($data);
return false;
}
case 3:
// 类任务
$class_name = $info->target;
$method_name = 'run';
$class = new $class_name;
if (method_exists($class, $method_name)) {
$return = $class->$method_name($info->parameter);
$data['status'] = 1;
$data['exception_info'] = $return;
CrontabLog::create($data);
return true;
} else {
$data['status'] = 2;
$data['exception_info'] = '类:' . $class_name . ',方法:run,未找到';
CrontabLog::create($data);
return false;
}
default:
return false;
}
}
}

View File

@@ -0,0 +1,213 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\tool;
use plugin\saiadmin\app\model\tool\GenerateColumns;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\utils\Helper;
/**
* 代码生成业务字段逻辑层
*/
class GenerateColumnsLogic extends BaseLogic
{
/**
* 构造函数
*/
public function __construct()
{
$this->model = new GenerateColumns();
}
public function saveExtra($data)
{
$default_column = ['create_time', 'update_time', 'created_by', 'updated_by', 'delete_time', 'remark'];
// 组装数据
foreach ($data as $k => $item) {
if ($item['column_name'] == 'delete_time') {
continue;
}
$column = [
'table_id' => $item['table_id'],
'column_name' => $item['column_name'],
'column_comment' => $item['column_comment'],
'column_type' => $item['column_type'],
'default_value' => $item['default_value'],
'is_pk' => ($item['column_key'] == 'PRI') ? 2 : 1,
'is_required' => $item['is_nullable'] == 'NO' ? 2 : 1,
'query_type' => 'eq',
'view_type' => 'input',
'sort' => count($data) - $k,
'options' => $item['options'] ?? null
];
// 设置默认选项
if (!in_array($item['column_name'], $default_column) && empty($item['column_key'])) {
$column = array_merge(
$column,
[
'is_insert' => 2,
'is_edit' => 2,
'is_list' => 2,
'is_query' => 1,
'is_sort' => 1,
]
);
}
$keyList = [
'column_comment',
'column_type',
'default_value',
'is_pk',
'is_required',
'is_insert',
'is_edit',
'is_list',
'is_query',
'is_sort',
'query_type',
'view_type',
'dict_type',
'options',
'sort',
'is_cover'
];
foreach ($keyList as $key) {
if (isset($item[$key]))
$column[$key] = $item[$key];
}
GenerateColumns::create($this->fieldDispose($column));
}
}
public function update($data, $where)
{
$data['is_insert'] = $data['is_insert'] ? 2 : 1;
$data['is_edit'] = $data['is_edit'] ? 2 : 1;
$data['is_list'] = $data['is_list'] ? 2 : 1;
$data['is_query'] = $data['is_query'] ? 2 : 1;
$data['is_sort'] = $data['is_sort'] ? 2 : 1;
$data['is_required'] = $data['is_required'] ? 2 : 1;
$this->model->update($data, $where);
}
private function fieldDispose(array $column): array
{
$object = new class {
public function viewTypeDispose(&$column): void
{
switch ($column['column_type']) {
case 'varchar':
$column['view_type'] = 'input';
break;
// 富文本
case 'text':
case 'longtext':
$column['is_list'] = 1;
$column['is_query'] = 1;
$column['view_type'] = 'editor';
$options = [
'height' => 400,
];
$column['options'] = $options;
break;
// 日期字段
case 'datetime':
$column['view_type'] = 'date';
$options = [
'mode' => 'datetime'
];
$column['options'] = $options;
$column['query_type'] = 'between';
break;
case 'date':
$column['view_type'] = 'date';
$options = [
'mode' => 'date'
];
$column['options'] = $options;
$column['query_type'] = 'between';
break;
}
}
public function columnName(&$column): void
{
if (stristr($column['column_name'], 'name')) {
$column['is_query'] = 2;
$column['is_required'] = 2;
$column['query_type'] = 'like';
}
if (stristr($column['column_name'], 'title')) {
$column['is_query'] = 2;
$column['is_required'] = 2;
$column['query_type'] = 'like';
}
if (stristr($column['column_name'], 'type')) {
$column['is_query'] = 2;
$column['is_required'] = 2;
$column['query_type'] = 'eq';
}
if (stristr($column['column_name'], 'image')) {
$column['is_query'] = 1;
$column['view_type'] = 'uploadImage';
$options = [
'multiple' => false,
'limit' => 1,
];
$column['options'] = $options;
}
if (stristr($column['column_name'], 'file')) {
$column['is_query'] = 1;
$column['view_type'] = 'uploadFile';
$options = [
'multiple' => false,
'limit' => 1,
];
$column['options'] = $options;
}
if (stristr($column['column_name'], 'attach')) {
$column['is_query'] = 1;
$column['view_type'] = 'uploadFile';
$options = [
'multiple' => false,
'limit' => 1,
];
$column['options'] = $options;
}
if ($column['column_name'] === 'sort') {
$column['view_type'] = 'inputNumber';
}
if ($column['column_name'] === 'status') {
$column['view_type'] = 'radio';
$column['dict_type'] = 'data_status';
}
if (stristr($column['column_name'], 'is_')) {
$column['view_type'] = 'radio';
$column['dict_type'] = 'yes_or_no';
}
}
};
if (!$column['is_cover']) {
$object->viewTypeDispose($column);
$object->columnName($column);
}
$column['options'] = json_encode($column['options'], JSON_UNESCAPED_UNICODE);
return $column;
}
}

View File

@@ -0,0 +1,478 @@
<?php
// +----------------------------------------------------------------------
// | saiadmin [ saiadmin快速开发框架 ]
// +----------------------------------------------------------------------
// | Author: sai <1430792918@qq.com>
// +----------------------------------------------------------------------
namespace plugin\saiadmin\app\logic\tool;
use plugin\saiadmin\app\cache\UserMenuCache;
use plugin\saiadmin\app\logic\system\DatabaseLogic;
use plugin\saiadmin\app\model\system\SystemMenu;
use plugin\saiadmin\app\model\tool\GenerateTables;
use plugin\saiadmin\app\model\tool\GenerateColumns;
use plugin\saiadmin\exception\ApiException;
use plugin\saiadmin\basic\think\BaseLogic;
use plugin\saiadmin\utils\Helper;
use plugin\saiadmin\utils\code\CodeZip;
use plugin\saiadmin\utils\code\CodeEngine;
/**
* 代码生成业务逻辑层
*/
class GenerateTablesLogic extends BaseLogic
{
protected $columnLogic = null;
protected $dataLogic = null;
/**
* 构造函数
*/
public function __construct()
{
$this->model = new GenerateTables();
$this->columnLogic = new GenerateColumnsLogic();
$this->dataLogic = new DatabaseLogic();
}
/**
* 删除表和字段信息
* @param $ids
* @return bool
*/
public function destroy($ids): bool
{
return $this->transaction(function () use ($ids) {
parent::destroy($ids);
GenerateColumns::destroy(function ($query) use ($ids) {
$query->where('table_id', 'in', $ids);
});
return true;
});
}
/**
* 装载表信息
* @param $names
* @param $source
* @return void
*/
public function loadTable($names, $source): void
{
$data = config('think-orm.connections');
$config = $data[$source];
if (!$config) {
throw new ApiException('数据库配置读取失败');
}
$prefix = $config['prefix'] ?? '';
foreach ($names as $item) {
$class_name = $item['name'];
if (!empty($prefix)) {
$class_name = Helper::str_replace_once($prefix, '', $class_name);
}
$class_name = Helper::camel($class_name);
$tableInfo = [
'table_name' => $item['name'],
'table_comment' => $item['comment'],
'class_name' => $class_name,
'business_name' => Helper::get_business($item['name']),
'belong_menu_id' => 80,
'menu_name' => $item['comment'],
'tpl_category' => 'single',
'template' => 'app',
'stub' => 'think',
'namespace' => '',
'package_name' => '',
'source' => $source,
'generate_menus' => 'index,save,update,read,destroy',
];
$model = GenerateTables::create($tableInfo);
$columns = $this->dataLogic->getColumnList($item['name'], $source);
foreach ($columns as &$column) {
$column['table_id'] = $model->id;
$column['is_cover'] = false;
}
$this->columnLogic->saveExtra($columns);
}
}
/**
* 同步表字段信息
* @param $id
* @return void
*/
public function sync($id)
{
$model = $this->model->findOrEmpty($id);
// 拉取已有数据表信息
$queryModel = $this->columnLogic->model->where('table_id', $id);
$columnLogicData = $this->columnLogic->getAll($queryModel);
$columnLogicList = [];
foreach ($columnLogicData as $item) {
$columnLogicList[$item['column_name']] = $item;
}
GenerateColumns::destroy(function ($query) use ($id) {
$query->where('table_id', $id);
});
$columns = $this->dataLogic->getColumnList($model->table_name, $model->source ?? '');
foreach ($columns as &$column) {
$column['table_id'] = $model->id;
$column['is_cover'] = false;
if (isset($columnLogicList[$column['column_name']])) {
// 存在历史信息的情况
$getcolumnLogicItem = $columnLogicList[$column['column_name']];
if ($getcolumnLogicItem['column_type'] == $column['column_type']) {
$column['is_cover'] = true;
foreach ($getcolumnLogicItem as $key => $item) {
$array = [
'column_comment',
'column_type',
'default_value',
'is_pk',
'is_required',
'is_insert',
'is_edit',
'is_list',
'is_query',
'is_sort',
'query_type',
'view_type',
'dict_type',
'options',
'sort',
'is_cover'
];
if (in_array($key, $array)) {
$column[$key] = $item;
}
}
}
}
}
$this->columnLogic->saveExtra($columns);
}
/**
* 代码预览
* @param $id
* @return array
*/
public function preview($id): array
{
$data = $this->renderData($id);
$codeEngine = new CodeEngine($data);
$controllerContent = $codeEngine->renderContent('php', 'controller.stub');
$logicContent = $codeEngine->renderContent('php', 'logic.stub');
$modelContent = $codeEngine->renderContent('php', 'model.stub');
$validateContent = $codeEngine->renderContent('php', 'validate.stub');
$sqlContent = $codeEngine->renderContent('sql', 'sql.stub');
$indexContent = $codeEngine->renderContent('vue', 'index.stub');
$editContent = $codeEngine->renderContent('vue', 'edit-dialog.stub');
$searchContent = $codeEngine->renderContent('vue', 'table-search.stub');
$apiContent = $codeEngine->renderContent('ts', 'api.stub');
// 返回生成内容
return [
[
'tab_name' => 'controller.php',
'name' => 'controller',
'lang' => 'php',
'code' => $controllerContent
],
[
'tab_name' => 'logic.php',
'name' => 'logic',
'lang' => 'php',
'code' => $logicContent
],
[
'tab_name' => 'model.php',
'name' => 'model',
'lang' => 'php',
'code' => $modelContent
],
[
'tab_name' => 'validate.php',
'name' => 'validate',
'lang' => 'php',
'code' => $validateContent
],
[
'tab_name' => 'sql.sql',
'name' => 'sql',
'lang' => 'sql',
'code' => $sqlContent
],
[
'tab_name' => 'index.vue',
'name' => 'index',
'lang' => 'html',
'code' => $indexContent
],
[
'tab_name' => 'edit-dialog.vue',
'name' => 'edit-dialog',
'lang' => 'html',
'code' => $editContent
],
[
'tab_name' => 'table-search.vue',
'name' => 'table-search',
'lang' => 'html',
'code' => $searchContent
],
[
'tab_name' => 'api.ts',
'name' => 'api',
'lang' => 'javascript',
'code' => $apiContent
]
];
}
/**
* 生成到模块
* @param $id
*/
public function genModule($id)
{
$data = $this->renderData($id);
// 生成文件到模块
$codeEngine = new CodeEngine($data);
$codeEngine->generateBackend('controller', $codeEngine->renderContent('php', 'controller.stub'));
$codeEngine->generateBackend('logic', $codeEngine->renderContent('php', 'logic.stub'));
$codeEngine->generateBackend('model', $codeEngine->renderContent('php', 'model.stub'));
$codeEngine->generateBackend('validate', $codeEngine->renderContent('php', 'validate.stub'));
$codeEngine->generateFrontend('index', $codeEngine->renderContent('vue', 'index.stub'));
$codeEngine->generateFrontend('edit-dialog', $codeEngine->renderContent('vue', 'edit-dialog.stub'));
$codeEngine->generateFrontend('table-search', $codeEngine->renderContent('vue', 'table-search.stub'));
$codeEngine->generateFrontend('api', $codeEngine->renderContent('ts', 'api.stub'));
}
/**
* 处理数据
* @param $id
* @return array
*/
protected function renderData($id): array
{
$table = $this->model->findOrEmpty($id);
if (!in_array($table['template'], ["plugin", "app"])) {
throw new ApiException('应用类型必须为plugin或者app');
}
if (empty($table['namespace'])) {
throw new ApiException('请先设置应用名称');
}
$columns = $this->columnLogic->where('table_id', $id)
->order('sort', 'desc')
->select()
->toArray();
$pk = 'id';
foreach ($columns as &$column) {
if ($column['is_pk'] == 2) {
$pk = $column['column_name'];
}
if ($column['column_name'] == 'delete_time') {
unset($column['column_name']);
}
}
// 处理特殊变量
if ($table['template'] == 'plugin') {
$namespace_start = "plugin\\" . $table['namespace'] . "\\app\\admin\\";
$namespace_start_model = "plugin\\" . $table['namespace'] . "\\app\\";
$namespace_end = "\\" . $table['package_name'];
$url_path = 'app/' . $table['namespace'] . '/admin/' . $table['package_name'] . '/' . $table['class_name'];
$route = 'app/';
} else {
$namespace_start = "app\\" . $table['namespace'] . "\\";
$namespace_start_model = "app\\" . $table['namespace'] . "\\";
$namespace_end = "\\" . $table['package_name'];
$url_path = $table['namespace'] . '/' . $table['package_name'] . '/' . $table['class_name'];
$route = '';
}
$config = config('think-orm');
$data = $table->toArray();
$data['pk'] = $pk;
$data['namespace_start'] = $namespace_start;
$data['namespace_start_model'] = $namespace_start_model;
$data['namespace_end'] = $namespace_end;
$data['url_path'] = $url_path;
$data['route'] = $route;
$data['tables'] = [$data];
$data['columns'] = $columns;
$data['db_source'] = $config['default'] ?? 'mysql';
return $data;
}
/**
* 生成到模块
*/
public function generateFile($id)
{
$table = $this->model->where('id', $id)->findOrEmpty();
if ($table->isEmpty()) {
throw new ApiException('请选择要生成的表');
}
$debug = config('app.debug', true);
if (!$debug) {
throw new ApiException('非调试模式下,不允许生成文件');
}
$this->updateMenu($table);
$this->genModule($id);
UserMenuCache::clearMenuCache();
}
/**
* 代码生成下载
*/
public function generate($idsArr): array
{
$zip = new CodeZip();
$tables = $this->model->where('id', 'in', $idsArr)->select()->toArray();
foreach ($idsArr as $table_id) {
$data = $this->renderData($table_id);
$data['tables'] = $tables;
$codeEngine = new CodeEngine($data);
$codeEngine->generateTemp();
}
$filename = 'saiadmin.zip';
$download = $zip->compress();
return compact('filename', 'download');
}
/**
* 处理菜单列表
* @param $tables
*/
public function updateMenu($tables)
{
/*不存在的情况下进行新建操作*/
$url_path = $tables['namespace'] . ":" . $tables['package_name'] . ':' . $tables['business_name'];
$code = $tables['namespace'] . "/" . $tables['package_name'] . '/' . $tables['business_name'];
$path = $tables['package_name'] . '/' . $tables['business_name'];
$component = $tables['namespace'] . "/" . $tables['package_name'] . '/' . $tables['business_name'];
/*先获取一下已有的路由中是否包含当前ID的路由的核心信息*/
$model = new SystemMenu();
$tableMenu = $model->where('generate_id', $tables['id'])->findOrEmpty();
$fistMenu = [
'parent_id' => $tables['belong_menu_id'],
'name' => $tables['menu_name'],
'code' => $code,
'path' => $path,
'icon' => 'ri:home-2-line',
'component' => "/plugin/$component/index",
'type' => 2,
'sort' => 100,
'is_iframe' => 2,
'is_keep_alive' => 2,
'is_hidden' => 2,
'is_fixed_tab' => 2,
'is_full_page' => 2,
'generate_id' => $tables['id']
];
if ($tableMenu->isEmpty()) {
$temp = SystemMenu::create($fistMenu);
$fistMenuId = $temp->id;
} else {
$fistMenu['id'] = $tableMenu['id'];
$tableMenu->save($fistMenu);
$fistMenuId = $tableMenu['id'];
}
/*开始进行子权限的判定操作*/
$childNodes = [
['name' => '列表', 'key' => 'index'],
['name' => '保存', 'key' => 'save'],
['name' => '更新', 'key' => 'update'],
['name' => '读取', 'key' => 'read'],
['name' => '删除', 'key' => 'destroy'],
];
foreach ($childNodes as $node) {
$nodeData = $model->where('parent_id', $fistMenuId)->where('generate_key', $node['key'])->findOrEmpty();
$childNodeData = [
'parent_id' => $fistMenuId,
'name' => $node['name'],
'slug' => "$url_path:{$node['key']}",
'type' => 3,
'sort' => 100,
'is_iframe' => 2,
'is_keep_alive' => 2,
'is_hidden' => 2,
'is_fixed_tab' => 2,
'is_full_page' => 2,
'generate_key' => $node['key']
];
if (!empty($nodeData)) {
$childNodeData['id'] = $nodeData['id'];
$nodeData->save($childNodeData);
} else {
$menuModel = new SystemMenu();
$menuModel->save($childNodeData);
}
}
}
/**
* 获取数据表字段信息
* @param $table_id
* @return mixed
*/
public function getTableColumns($table_id): mixed
{
$query = $this->columnLogic->where('table_id', $table_id);
return $this->columnLogic->getAll($query);
}
/**
* 编辑数据
* @param $id
* @param $data
* @return mixed
*/
public function edit($id, $data): mixed
{
$columns = $data['columns'];
unset($data['columns']);
if (!empty($data['belong_menu_id'])) {
$data['belong_menu_id'] = is_array($data['belong_menu_id']) ? array_pop($data['belong_menu_id']) : $data['belong_menu_id'];
} else {
$data['belong_menu_id'] = 0;
}
$data['generate_menus'] = implode(',', $data['generate_menus']);
if (empty($data['options'])) {
unset($data['options']);
}
$data['options'] = json_encode($data['options'], JSON_UNESCAPED_UNICODE);
// 更新业务表
$this->update($data, ['id' => $id]);
// 更新业务字段表
foreach ($columns as $column) {
if ($column['options']) {
$column['options'] = json_encode($column['options'], JSON_NUMERIC_CHECK);
}
$this->columnLogic->update($column, ['id' => $column['id']]);
}
return true;
}
}