初始化-安装依赖

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;
}
}