初始化-安装依赖
This commit is contained in:
94
server/plugin/saiadmin/app/cache/ConfigCache.php
vendored
Normal file
94
server/plugin/saiadmin/app/cache/ConfigCache.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigLogic;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 配置缓存
|
||||
*/
|
||||
class ConfigCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.config_cache', [
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'prefix' => 'saiadmin:config_cache:config_',
|
||||
'tag' => 'saiadmin:config_cache'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息
|
||||
*/
|
||||
public static function getConfig(string $code = ''): array
|
||||
{
|
||||
if (empty($code)) {
|
||||
return [];
|
||||
}
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$config = Cache::get($cache['prefix'] . md5($code));
|
||||
if ($config) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
// 设置配置并获取
|
||||
$config = static::setConfig($code);
|
||||
if ($config) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置数据
|
||||
*/
|
||||
public static function setConfig(string $code): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
|
||||
$data = (new SystemConfigLogic())->getData($code);
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tag = [];
|
||||
$tag[] = $cache['tag'];
|
||||
|
||||
// 保存到缓存
|
||||
Cache::tag($tag)->set($cache['prefix'] . md5($code), $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理单个配置缓存
|
||||
*/
|
||||
public static function clearConfig(string $code): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::delete($cache['prefix'] . md5($code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理全部配置缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear(): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::tag($cache['tag'])->clear();
|
||||
}
|
||||
}
|
||||
86
server/plugin/saiadmin/app/cache/DictCache.php
vendored
Normal file
86
server/plugin/saiadmin/app/cache/DictCache.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemDictTypeLogic;
|
||||
use plugin\saiadmin\app\model\system\SystemDictType;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 字典信息缓存
|
||||
*/
|
||||
class DictCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.dict_cache', [
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'tag' => 'saiadmin:dict_cache',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部字典
|
||||
*/
|
||||
public static function getDictAll(): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$data = Cache::get($cache['tag']);
|
||||
if ($data) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// 获取信息并返回
|
||||
$data = static::setDictAll();
|
||||
if ($data) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个字典
|
||||
*/
|
||||
public static function getDict($code): array
|
||||
{
|
||||
$data = static::getDictAll();
|
||||
if (isset($data[$code])) {
|
||||
return $data[$code];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全部字典
|
||||
*/
|
||||
public static function setDictAll(): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
$data = (new SystemDictTypeLogic)->getDictAll();
|
||||
|
||||
Cache::set($cache['tag'], $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除全部字典信息
|
||||
*/
|
||||
public static function clear(): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::delete($cache['tag']);
|
||||
}
|
||||
}
|
||||
104
server/plugin/saiadmin/app/cache/ReflectionCache.php
vendored
Normal file
104
server/plugin/saiadmin/app/cache/ReflectionCache.php
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 反射文件缓存
|
||||
*/
|
||||
class ReflectionCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.reflection_cache', [
|
||||
'tag' => 'saiadmin:reflection',
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'no_need' => 'saiadmin:reflection_cache:no_need_',
|
||||
'attr' => 'saiadmin:reflection_cache:attr_',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取控制器中无需登录的方法列表
|
||||
*/
|
||||
public static function getNoNeedLogin(string $controller): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
$tag = [];
|
||||
$tag[] = $cache['tag'];
|
||||
$key = $cache['no_need'] . md5($controller);
|
||||
|
||||
$data = Cache::get($key);
|
||||
if ($data !== null) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// 反射逻辑
|
||||
if (class_exists($controller)) {
|
||||
$ref = new ReflectionClass($controller);
|
||||
$data = $ref->getDefaultProperties()['noNeedLogin'] ?? [];
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
Cache::tag($tag)->set($key, $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法上的权限属性
|
||||
*/
|
||||
public static function getPermissionAttributes(string $controller, string $action): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
$tag = [];
|
||||
$tag[] = $cache['tag'];
|
||||
$key = $cache['attr'] . md5($controller . '::' . $action);
|
||||
|
||||
$data = Cache::get($key);
|
||||
if ($data) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
if (method_exists($controller, $action)) {
|
||||
$refMethod = new ReflectionMethod($controller, $action);
|
||||
$attributes = $refMethod->getAttributes(Permission::class);
|
||||
if (!empty($attributes)) {
|
||||
$attr = $attributes[0]->newInstance();
|
||||
$data = [
|
||||
'title' => $attr->getTitle(),
|
||||
'slug' => $attr->getSlug(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Cache::tag($tag)->set($key, $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有反射缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear(): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::tag($cache['tag'])->clear();
|
||||
}
|
||||
|
||||
}
|
||||
143
server/plugin/saiadmin/app/cache/UserAuthCache.php
vendored
Normal file
143
server/plugin/saiadmin/app/cache/UserAuthCache.php
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemMenuLogic;
|
||||
use plugin\saiadmin\app\model\system\SystemUserRole;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 用户权限缓存
|
||||
*/
|
||||
class UserAuthCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.button_cache', [
|
||||
'prefix' => 'saiadmin:button_cache:user_',
|
||||
'expire' => 60 * 60 * 2,
|
||||
'all' => 'saiadmin:button_cache:all',
|
||||
'role' => 'saiadmin:button_cache:role_',
|
||||
'tag' => 'saiadmin:button_cache',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的权限
|
||||
*/
|
||||
public static function getUserAuth($uid): array
|
||||
{
|
||||
if (empty($uid)) {
|
||||
return [];
|
||||
}
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$auth = Cache::get($cache['prefix'] . $uid);
|
||||
if ($auth) {
|
||||
return $auth;
|
||||
}
|
||||
|
||||
// 设置权限并返回
|
||||
$auth = static::setUserAuth($uid);
|
||||
if ($auth) {
|
||||
return $auth;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户的权限
|
||||
*/
|
||||
public static function setUserAuth($uid): array
|
||||
{
|
||||
// 从缓存获取,直接返回
|
||||
$roleIds = SystemUserRole::getRoleIds($uid);
|
||||
|
||||
// 获取角色关联的菜单权限
|
||||
$data = (new SystemMenuLogic())->getAuthByRole($roleIds);
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cache = static::cacheConfig();
|
||||
|
||||
$tag = [];
|
||||
$tag[] = $cache['tag'];
|
||||
if (!empty($roleIds)) {
|
||||
foreach ($roleIds as $role) {
|
||||
$tag[] = $cache['role'] . $role;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到缓存
|
||||
Cache::tag($tag)->set($cache['prefix'] . $uid, $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部权限
|
||||
*/
|
||||
public static function getAllAuth(): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$auth = Cache::get($cache['all']);
|
||||
if ($auth) {
|
||||
return $auth;
|
||||
}
|
||||
|
||||
$all = (new SystemMenuLogic())->getAllAuth();
|
||||
|
||||
// 设置权限并返回
|
||||
Cache::tag($cache['tag'])->set($cache['all'], $all, $cache['expire']);
|
||||
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
*/
|
||||
public static function clearUserAuth($uid): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::delete($cache['prefix'] . $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理角色缓存
|
||||
*/
|
||||
public static function clearUserAuthByRoleId($role_id): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
if (is_array($role_id)) {
|
||||
$tags = [];
|
||||
foreach ($role_id as $id) {
|
||||
$tags[] = $cache['role'] . $id;
|
||||
}
|
||||
} else {
|
||||
$tags = $cache['role'] . $role_id;
|
||||
}
|
||||
return Cache::tag($tags)->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有用户缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear(): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::tag($cache['tag'])->clear();
|
||||
}
|
||||
}
|
||||
145
server/plugin/saiadmin/app/cache/UserInfoCache.php
vendored
Normal file
145
server/plugin/saiadmin/app/cache/UserInfoCache.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemUserLogic;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 用户信息缓存
|
||||
*/
|
||||
class UserInfoCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.user_cache', [
|
||||
'prefix' => 'saiadmin:user_cache:info_',
|
||||
'expire' => 60 * 60 * 4,
|
||||
'dept' => 'saiadmin:user_cache:dept_',
|
||||
'role' => 'saiadmin:user_cache:role_',
|
||||
'post' => 'saiadmin:user_cache:post_',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id获取缓存管理员信息
|
||||
*/
|
||||
public static function getUserInfo($uid): array
|
||||
{
|
||||
if (empty($uid)) {
|
||||
return [];
|
||||
}
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$adminInfo = Cache::get($cache['prefix'] . $uid);
|
||||
|
||||
if ($adminInfo) {
|
||||
return $adminInfo;
|
||||
}
|
||||
|
||||
// 获取缓存信息并返回
|
||||
$adminInfo = static::setUserInfo($uid);
|
||||
if ($adminInfo) {
|
||||
return $adminInfo;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置管理员信息
|
||||
*/
|
||||
public static function setUserInfo($uid): array
|
||||
{
|
||||
$data = (new SystemUserLogic())->getUser($uid);
|
||||
$cache = static::cacheConfig();
|
||||
|
||||
$tags = [];
|
||||
if (!empty($data['deptList'])) {
|
||||
$tags[] = $cache['dept'] . $data['deptList']['id'];
|
||||
}
|
||||
if (!empty($data['roleList'])) {
|
||||
foreach ($data['roleList'] as $role) {
|
||||
$tags[] = $cache['role'] . $role['id'];
|
||||
}
|
||||
}
|
||||
if (!empty($data['postList'])) {
|
||||
foreach ($data['postList'] as $post) {
|
||||
$tags[] = $cache['post'] . $post['id'];
|
||||
}
|
||||
}
|
||||
Cache::tag($tags)->set($cache['prefix'] . $uid, $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理管理员信息缓存
|
||||
*/
|
||||
public static function clearUserInfo($uid): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::delete($cache['prefix'] . $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理部门下所有用户缓存
|
||||
*/
|
||||
public static function clearUserInfoByDeptId($dept_id): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
if (is_array($dept_id)) {
|
||||
$tags = [];
|
||||
foreach ($dept_id as $id) {
|
||||
$tags[] = $cache['dept'] . $id;
|
||||
}
|
||||
} else {
|
||||
$tags = $cache['dept'] . $dept_id;
|
||||
}
|
||||
return Cache::tag($tags)->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理角色下所有用户缓存
|
||||
*/
|
||||
public static function clearUserInfoByRoleId($role_id): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
if (is_array($role_id)) {
|
||||
$tags = [];
|
||||
foreach ($role_id as $id) {
|
||||
$tags[] = $cache['role'] . $id;
|
||||
}
|
||||
} else {
|
||||
$tags = $cache['role'] . $role_id;
|
||||
}
|
||||
return Cache::tag($tags)->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理岗位下所有用户缓存
|
||||
*/
|
||||
public static function clearUserInfoByPostId($post_id): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
if (is_array($post_id)) {
|
||||
$tags = [];
|
||||
foreach ($post_id as $id) {
|
||||
$tags[] = $cache['post'] . $id;
|
||||
}
|
||||
} else {
|
||||
$tags = $cache['post'] . $post_id;
|
||||
}
|
||||
return Cache::tag($tags)->clear();
|
||||
}
|
||||
|
||||
}
|
||||
100
server/plugin/saiadmin/app/cache/UserMenuCache.php
vendored
Normal file
100
server/plugin/saiadmin/app/cache/UserMenuCache.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace plugin\saiadmin\app\cache;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemMenuLogic;
|
||||
use plugin\saiadmin\app\model\system\SystemUserRole;
|
||||
use support\think\Cache;
|
||||
|
||||
/**
|
||||
* 用户菜单缓存
|
||||
*/
|
||||
class UserMenuCache
|
||||
{
|
||||
/**
|
||||
* 读取缓存配置
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheConfig(): array
|
||||
{
|
||||
return config('plugin.saiadmin.saithink.menu_cache', [
|
||||
'prefix' => 'saiadmin:menu_cache:user_',
|
||||
'expire' => 60 * 60 * 24 * 7,
|
||||
'tag' => 'saiadmin:menu_cache',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户的菜单
|
||||
*/
|
||||
public static function getUserMenu($uid): array
|
||||
{
|
||||
if (empty($uid)) {
|
||||
return [];
|
||||
}
|
||||
$cache = static::cacheConfig();
|
||||
// 直接从缓存获取
|
||||
$menu = Cache::get($cache['prefix'] . $uid);
|
||||
if ($menu) {
|
||||
return $menu;
|
||||
}
|
||||
|
||||
// 设置用户菜单并获取
|
||||
$menu = static::setUserMenu($uid);
|
||||
if ($menu) {
|
||||
return $menu;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户菜单
|
||||
*/
|
||||
public static function setUserMenu($uid): array
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
$tag = [];
|
||||
$tag[] = $cache['tag'];
|
||||
$logic = new SystemMenuLogic();
|
||||
if ($uid == 1) {
|
||||
$data = $logic->getAllMenus();
|
||||
} else {
|
||||
$roleIds = SystemUserRole::getRoleIds($uid);
|
||||
$data = $logic->getMenuByRole($roleIds);
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到缓存
|
||||
Cache::tag($tag)->set($cache['prefix'] . $uid, $data, $cache['expire']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户缓存
|
||||
*/
|
||||
public static function clearUserMenu($uid): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::delete($cache['prefix'] . $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有菜单缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clearMenuCache(): bool
|
||||
{
|
||||
$cache = static::cacheConfig();
|
||||
return Cache::tag($cache['tag'])->clear();
|
||||
}
|
||||
|
||||
}
|
||||
363
server/plugin/saiadmin/app/controller/InstallController.php
Normal file
363
server/plugin/saiadmin/app/controller/InstallController.php
Normal file
@@ -0,0 +1,363 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller;
|
||||
|
||||
use Throwable;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\basic\OpenController;
|
||||
|
||||
/**
|
||||
* 安装控制器
|
||||
*/
|
||||
class InstallController extends OpenController
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
*/
|
||||
protected array $noNeedLogin = ['index', 'install'];
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* @var string
|
||||
*/
|
||||
protected string $app = 'saiadmin';
|
||||
|
||||
protected string $version = '6.0.0';
|
||||
|
||||
/**
|
||||
* 安装首页
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data['app'] = $this->app;
|
||||
$data['version'] = config('plugin.saiadmin.app.version', $this->version);
|
||||
|
||||
$env = base_path() . DIRECTORY_SEPARATOR . '.env';
|
||||
|
||||
clearstatcache();
|
||||
if (is_file($env)) {
|
||||
$data['error'] = '程序已经安装';
|
||||
return view('install/error', $data);
|
||||
}
|
||||
|
||||
if (!is_writable(base_path() . DIRECTORY_SEPARATOR . 'config')) {
|
||||
$data['error'] = '权限认证失败';
|
||||
return view('install/error', $data);
|
||||
}
|
||||
|
||||
return view('install/index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行安装
|
||||
*/
|
||||
public function install(Request $request)
|
||||
{
|
||||
$env = base_path() . DIRECTORY_SEPARATOR . '.env';
|
||||
|
||||
clearstatcache();
|
||||
if (is_file($env)) {
|
||||
return $this->fail('管理后台已经安装!如需重新安装,请删除根目录env配置文件并重启');
|
||||
}
|
||||
|
||||
$user = $request->post('username');
|
||||
$password = $request->post('password');
|
||||
$database = $request->post('database');
|
||||
$host = $request->post('host');
|
||||
$port = (int) $request->post('port') ?: 3306;
|
||||
$dataType = $request->post('dataType', 'demo');
|
||||
|
||||
try {
|
||||
$db = $this->getPdo($host, $user, $password, $port);
|
||||
$smt = $db->query("show databases like '$database'");
|
||||
if (empty($smt->fetchAll())) {
|
||||
$db->exec("create database `$database` CHARSET utf8mb4 COLLATE utf8mb4_general_ci");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$message = $e->getMessage();
|
||||
if (stripos($message, 'Access denied for user')) {
|
||||
return $this->fail('数据库用户名或密码错误');
|
||||
}
|
||||
if (stripos($message, 'Connection refused')) {
|
||||
return $this->fail('Connection refused. 请确认数据库IP端口是否正确,数据库已经启动');
|
||||
}
|
||||
if (stripos($message, 'timed out')) {
|
||||
return $this->fail('数据库连接超时,请确认数据库IP端口是否正确,安全组及防火墙已经放行端口');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$db->exec("use `$database`");
|
||||
|
||||
$smt = $db->query("show tables like 'sa_system_menu';");
|
||||
$tables = $smt->fetchAll();
|
||||
if (count($tables) > 0) {
|
||||
return $this->fail('数据库已经安装,请勿重复安装');
|
||||
}
|
||||
|
||||
if ($dataType == 'demo') {
|
||||
$sql_file = base_path() . '/plugin/saiadmin/db/saiadmin-6.0.sql';
|
||||
} else {
|
||||
$sql_file = base_path() . '/plugin/saiadmin/db/saiadmin-pure.sql';
|
||||
}
|
||||
|
||||
if (!is_file($sql_file)) {
|
||||
return $this->fail('数据库SQL文件不存在');
|
||||
}
|
||||
|
||||
$sql_query = file_get_contents($sql_file);
|
||||
|
||||
$db->exec($sql_query);
|
||||
|
||||
$this->generateConfig();
|
||||
|
||||
$env_config = <<<EOF
|
||||
# 数据库配置
|
||||
DB_TYPE = mysql
|
||||
DB_HOST = $host
|
||||
DB_PORT = $port
|
||||
DB_NAME = $database
|
||||
DB_USER = $user
|
||||
DB_PASSWORD = $password
|
||||
DB_PREFIX =
|
||||
|
||||
# 缓存方式
|
||||
CACHE_MODE = file
|
||||
|
||||
# Redis配置
|
||||
REDIS_HOST = 127.0.0.1
|
||||
REDIS_PORT = 6379
|
||||
REDIS_PASSWORD = ''
|
||||
REDIS_DB = 0
|
||||
|
||||
# 验证码配置
|
||||
CAPTCHA_MODE = cache
|
||||
|
||||
#前端目录
|
||||
FRONTEND_DIR = saiadmin-artd
|
||||
EOF;
|
||||
file_put_contents(base_path() . DIRECTORY_SEPARATOR . '.env', $env_config);
|
||||
|
||||
// 尝试reload
|
||||
if (function_exists('posix_kill')) {
|
||||
set_error_handler(function () {});
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
return $this->success('安装成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成配置文件
|
||||
*/
|
||||
protected function generateConfig()
|
||||
{
|
||||
// 1、think-orm配置文件
|
||||
$think_orm_config = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => 'mysql',
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
// 数据库类型
|
||||
'type' => env('DB_TYPE', 'mysql'),
|
||||
// 服务器地址
|
||||
'hostname' => env('DB_HOST', '127.0.0.1'),
|
||||
// 数据库名
|
||||
'database' => env('DB_NAME', 'saiadmin'),
|
||||
// 数据库用户名
|
||||
'username' => env('DB_USER', 'root'),
|
||||
// 数据库密码
|
||||
'password' => env('DB_PASSWORD', '123456'),
|
||||
// 数据库连接端口
|
||||
'hostport' => env('DB_PORT', 3306),
|
||||
// 数据库连接参数
|
||||
'params' => [
|
||||
// 连接超时3秒
|
||||
\PDO::ATTR_TIMEOUT => 3,
|
||||
],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => 'utf8',
|
||||
// 数据库表前缀
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
// 断线重连
|
||||
'break_reconnect' => true,
|
||||
// 自定义分页类
|
||||
'bootstrap' => '',
|
||||
// 连接池配置
|
||||
'pool' => [
|
||||
'max_connections' => 5, // 最大连接数
|
||||
'min_connections' => 1, // 最小连接数
|
||||
'wait_timeout' => 3, // 从连接池获取连接等待超时时间
|
||||
'idle_timeout' => 60, // 连接最大空闲时间,超过该时间会被回收
|
||||
'heartbeat_interval' => 50, // 心跳检测间隔,需要小于60秒
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/config/think-orm.php', $think_orm_config);
|
||||
|
||||
// 2、chache配置文件
|
||||
$cache_config = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => env('CACHE_MODE', 'file'),
|
||||
'stores' => [
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => runtime_path('cache')
|
||||
],
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default'
|
||||
],
|
||||
'array' => [
|
||||
'driver' => 'array'
|
||||
]
|
||||
]
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/config/cache.php', $cache_config);
|
||||
|
||||
// 3、redis配置文件
|
||||
$redis_config = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'password' => env('REDIS_PASSWORD', ''),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DB', 0),
|
||||
'pool' => [
|
||||
'max_connections' => 5,
|
||||
'min_connections' => 1,
|
||||
'wait_timeout' => 3,
|
||||
'idle_timeout' => 60,
|
||||
'heartbeat_interval' => 50,
|
||||
],
|
||||
]
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/config/redis.php', $redis_config);
|
||||
|
||||
// 4、think-cache配置文件
|
||||
$think_cache_config = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
// 默认缓存驱动
|
||||
'default' => env('CACHE_MODE', 'file'),
|
||||
// 缓存连接方式配置
|
||||
'stores' => [
|
||||
// redis缓存
|
||||
'redis' => [
|
||||
// 驱动方式
|
||||
'type' => 'redis',
|
||||
// 服务器地址
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
// 服务器端口
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
// 服务器密码
|
||||
'password' => env('REDIS_PASSWORD', ''),
|
||||
// 数据库
|
||||
'select' => env('REDIS_DB', 0),
|
||||
// 缓存前缀
|
||||
'prefix' => 'cache:',
|
||||
// 默认缓存有效期 0表示永久缓存
|
||||
'expire' => 0,
|
||||
// Thinkphp官方没有这个参数,由于生成的tag键默认不过期,如果tag键数量很大,避免长时间占用内存,可以设置一个超过其他缓存的过期时间,0为不设置
|
||||
'tag_expire' => 86400 * 30,
|
||||
// 缓存标签前缀
|
||||
'tag_prefix' => 'tag:',
|
||||
// 连接池配置
|
||||
'pool' => [
|
||||
'max_connections' => 5, // 最大连接数
|
||||
'min_connections' => 1, // 最小连接数
|
||||
'wait_timeout' => 3, // 从连接池获取连接等待超时时间
|
||||
'idle_timeout' => 60, // 连接最大空闲时间,超过该时间会被回收
|
||||
'heartbeat_interval' => 50, // 心跳检测间隔,需要小于60秒
|
||||
],
|
||||
],
|
||||
// 文件缓存
|
||||
'file' => [
|
||||
// 驱动方式
|
||||
'type' => 'file',
|
||||
// 设置不同的缓存保存目录
|
||||
'path' => runtime_path() . '/file/',
|
||||
],
|
||||
],
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/config/think-cache.php', $think_cache_config);
|
||||
|
||||
// 5、database配置文件
|
||||
$database = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'default' => 'mysql',
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
'driver' => env('DB_TYPE', 'mysql'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', 3306),
|
||||
'database' => env('DB_NAME', 'saiadmin'),
|
||||
'username' => env('DB_USER', 'root'),
|
||||
'password' => env('DB_PASSWORD', '123456'),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_general_ci'),
|
||||
'prefix' => env('DB_PREFIX', ''),
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => [
|
||||
PDO::ATTR_EMULATE_PREPARES => false, // Must be false for Swoole and Swow drivers.
|
||||
],
|
||||
'pool' => [
|
||||
'max_connections' => 5,
|
||||
'min_connections' => 1,
|
||||
'wait_timeout' => 3,
|
||||
'idle_timeout' => 60,
|
||||
'heartbeat_interval' => 50,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/config/database.php', $database);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取pdo连接
|
||||
* @param $host
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param $port
|
||||
* @param $database
|
||||
* @return \PDO
|
||||
*/
|
||||
protected function getPdo($host, $username, $password, $port, $database = null): \PDO
|
||||
{
|
||||
$dsn = "mysql:host=$host;port=$port;";
|
||||
if ($database) {
|
||||
$dsn .= "dbname=$database";
|
||||
}
|
||||
$params = [
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4",
|
||||
\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
|
||||
\PDO::ATTR_EMULATE_PREPARES => false,
|
||||
\PDO::ATTR_TIMEOUT => 5,
|
||||
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
|
||||
];
|
||||
return new \PDO($dsn, $username, $password, $params);
|
||||
}
|
||||
}
|
||||
61
server/plugin/saiadmin/app/controller/LoginController.php
Normal file
61
server/plugin/saiadmin/app/controller/LoginController.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\saiadmin\utils\Captcha;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemUserLogic;
|
||||
|
||||
/**
|
||||
* 登录控制器
|
||||
*/
|
||||
class LoginController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
*/
|
||||
protected array $noNeedLogin = ['captcha', 'login'];
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
public function captcha() : Response
|
||||
{
|
||||
$captcha = new Captcha();
|
||||
$result = $captcha->imageCaptcha();
|
||||
if ($result['result'] !== 1) {
|
||||
return $this->fail($result['message']);
|
||||
}
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$username = $request->post('username', '');
|
||||
$password = $request->post('password', '');
|
||||
$type = $request->post('type', 'pc');
|
||||
|
||||
$code = $request->post('code', '');
|
||||
$uuid = $request->post('uuid', '');
|
||||
$captcha = new Captcha();
|
||||
if (!$captcha->checkCaptcha($uuid, $code)) {
|
||||
return $this->fail('验证码错误');
|
||||
}
|
||||
$logic = new SystemUserLogic();
|
||||
$data = $logic->login($username, $password, $type);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
263
server/plugin/saiadmin/app/controller/SystemController.php
Normal file
263
server/plugin/saiadmin/app/controller/SystemController.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller;
|
||||
|
||||
use plugin\saiadmin\app\cache\DictCache;
|
||||
use plugin\saiadmin\app\cache\UserAuthCache;
|
||||
use plugin\saiadmin\app\cache\UserInfoCache;
|
||||
use plugin\saiadmin\app\cache\UserMenuCache;
|
||||
use plugin\saiadmin\app\logic\system\SystemCategoryLogic;
|
||||
use plugin\saiadmin\app\logic\system\SystemLoginLogLogic;
|
||||
use plugin\saiadmin\app\logic\system\SystemOperLogLogic;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemUserLogic;
|
||||
use plugin\saiadmin\app\logic\system\SystemAttachmentLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\saiadmin\utils\Arr;
|
||||
use Tinywan\Storage\Storage;
|
||||
|
||||
/**
|
||||
* 系统控制器
|
||||
*/
|
||||
class SystemController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
public function userInfo(): Response
|
||||
{
|
||||
$info['user'] = $this->adminInfo;
|
||||
$info = [];
|
||||
$info['id'] = $this->adminInfo['id'];
|
||||
$info['username'] = $this->adminInfo['username'];
|
||||
$info['dashboard'] = $this->adminInfo['dashboard'];
|
||||
$info['avatar'] = $this->adminInfo['avatar'];
|
||||
$info['email'] = $this->adminInfo['email'];
|
||||
$info['phone'] = $this->adminInfo['phone'];
|
||||
$info['gender'] = $this->adminInfo['gender'];
|
||||
$info['signed'] = $this->adminInfo['signed'];
|
||||
$info['realname'] = $this->adminInfo['realname'];
|
||||
$info['department'] = $this->adminInfo['deptList'];
|
||||
if ($this->adminInfo['id'] === 1) {
|
||||
$info['buttons'] = ['*'];
|
||||
$info['roles'] = ['super_admin'];
|
||||
} else {
|
||||
$info['buttons'] = UserAuthCache::getUserAuth($this->adminInfo['id']);
|
||||
$info['roles'] = Arr::getArrayColumn($this->adminInfo['roleList'], 'code');
|
||||
}
|
||||
return $this->success($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部字典数据
|
||||
*/
|
||||
public function dictAll(): Response
|
||||
{
|
||||
$dict = DictCache::getDictAll();
|
||||
return $this->success($dict);
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单数据
|
||||
* @return Response
|
||||
*/
|
||||
public function menu(): Response
|
||||
{
|
||||
$data = UserMenuCache::getUserMenu($this->adminInfo['id']);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件列表读取', 'core:system:resource')]
|
||||
public function getResourceCategory(Request $request): Response
|
||||
{
|
||||
$logic = new SystemCategoryLogic();
|
||||
$data = $logic->tree([]);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件列表读取', 'core:system:resource')]
|
||||
public function getResourceList(Request $request): Response
|
||||
{
|
||||
$logic = new SystemAttachmentLogic();
|
||||
$where = $request->more([
|
||||
['origin_name', ''],
|
||||
['category_id', ''],
|
||||
]);
|
||||
$query = $logic->search($where);
|
||||
$query->whereIn('mime_type', ['image/jpeg', 'image/png', 'image/gif', 'image/webp']);
|
||||
$data = $logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户列表读取', 'core:system:user')]
|
||||
public function getUserList(Request $request): Response
|
||||
{
|
||||
$logic = new SystemUserLogic();
|
||||
$where = $request->more([
|
||||
['keyword', ''],
|
||||
['dept_id', ''],
|
||||
]);
|
||||
$data = $logic->openUserList($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载网络图片
|
||||
*/
|
||||
#[Permission('上传网络图片', 'core:system:uploadImage')]
|
||||
public function saveNetworkImage(Request $request): Response
|
||||
{
|
||||
$url = $request->input('url', '');
|
||||
$config = Storage::getConfig('local');
|
||||
$logic = new SystemAttachmentLogic();
|
||||
$data = $logic->saveNetworkImage($url, $config);
|
||||
return $this->success($data, '操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*/
|
||||
#[Permission('上传图片', 'core:system:uploadImage')]
|
||||
public function uploadImage(Request $request): Response
|
||||
{
|
||||
$logic = new SystemAttachmentLogic();
|
||||
$type = $request->input('mode', 'system');
|
||||
if ($type == 'local') {
|
||||
return $this->success($logic->uploadBase('image', true));
|
||||
}
|
||||
return $this->success($logic->uploadBase('image'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
#[Permission('上传文件', 'core:system:uploadFile')]
|
||||
public function uploadFile(Request $request): Response
|
||||
{
|
||||
$logic = new SystemAttachmentLogic();
|
||||
$type = $request->input('mode', 'system');
|
||||
if ($type == 'local') {
|
||||
return $this->success($logic->uploadBase('file', true));
|
||||
}
|
||||
return $this->success($logic->uploadBase('file'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切片上传
|
||||
*/
|
||||
#[Permission('上传文件', 'core:system:chunkUpload')]
|
||||
public function chunkUpload(Request $request): Response
|
||||
{
|
||||
$logic = new SystemAttachmentLogic();
|
||||
$data = $request->post();
|
||||
$result = $logic->chunkUpload($data);
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录日志
|
||||
* @return Response
|
||||
*/
|
||||
public function getLoginLogList(): Response
|
||||
{
|
||||
$logic = new SystemLoginLogLogic();
|
||||
$logic->init($this->adminInfo);
|
||||
$query = $logic->search(['username' => $this->adminName]);
|
||||
$data = $logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志
|
||||
* @return Response
|
||||
*/
|
||||
public function getOperationLogList(): Response
|
||||
{
|
||||
$logic = new SystemOperLogLogic();
|
||||
$logic->init($this->adminInfo);
|
||||
$data = $logic->getOwnOperLogList(['username' => $this->adminName]);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @return Response
|
||||
*/
|
||||
public function clearAllCache(): Response
|
||||
{
|
||||
UserInfoCache::clearUserInfo($this->adminId);
|
||||
UserAuthCache::clearUserAuth($this->adminId);
|
||||
UserMenuCache::clearUserMenu($this->adminId);
|
||||
return $this->success([], '清除缓存成功!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 基本统计
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('工作台数据统计', 'core:console:list')]
|
||||
public function statistics(): Response
|
||||
{
|
||||
$userLogic = new SystemUserLogic();
|
||||
$userCount = $userLogic->count('id');
|
||||
$uploadLogic = new SystemAttachmentLogic();
|
||||
$attachCount = $uploadLogic->count('id');
|
||||
$loginLogic = new SystemLoginLogLogic();
|
||||
$loginCount = $loginLogic->count('id');
|
||||
$operLogic = new SystemOperLogLogic();
|
||||
$operCount = $operLogic->count('id');
|
||||
return $this->success([
|
||||
'user' => $userCount,
|
||||
'attach' => $attachCount,
|
||||
'login' => $loginCount,
|
||||
'operate' => $operCount,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录统计曲线图
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('工作台数据统计', 'core:console:list')]
|
||||
public function loginChart(): Response
|
||||
{
|
||||
$logic = new SystemLoginLogLogic();
|
||||
$data = $logic->loginChart();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录统计柱状图
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('工作台数据统计', 'core:console:list')]
|
||||
public function loginBarChart(): Response
|
||||
{
|
||||
$logic = new SystemLoginLogLogic();
|
||||
$data = $logic->loginBarChart();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\DatabaseLogic;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 数据表维护控制器
|
||||
*/
|
||||
class DataBaseController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new DatabaseLogic();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据源列表
|
||||
* @return Response
|
||||
*/
|
||||
public function source(): Response
|
||||
{
|
||||
$data = $this->logic->getDbSource();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据表列表', 'core:database:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['source', ''],
|
||||
]);
|
||||
$data = $this->logic->getList($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收站数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('回收站数据', 'core:recycle:index')]
|
||||
public function recycle(Request $request): Response
|
||||
{
|
||||
$table = $request->input('table', '');
|
||||
$data = $this->logic->recycleData($table);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('回收站销毁', 'core:recycle:edit')]
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$table = $request->input('table', '');
|
||||
$ids = $request->input('ids', '');
|
||||
if (!empty($ids)) {
|
||||
$result = $this->logic->delete($table, $ids);
|
||||
if (!$result) {
|
||||
return $this->fail('操作失败');
|
||||
}
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('回收站恢复', 'core:recycle:edit')]
|
||||
public function recovery(Request $request): Response
|
||||
{
|
||||
$table = $request->input('table', '');
|
||||
$ids = $request->input('ids', '');
|
||||
if (!empty($ids)) {
|
||||
$result = $this->logic->recovery($table, $ids);
|
||||
if (!$result) {
|
||||
return $this->fail('操作失败');
|
||||
}
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表字段信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据表字段', 'core:database:index')]
|
||||
public function detailed(Request $request): Response
|
||||
{
|
||||
$table = $request->input('table', '');
|
||||
$data = $this->logic->getColumnList($table, '');
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据表优化表', 'core:database:edit')]
|
||||
public function optimize(Request $request): Response
|
||||
{
|
||||
$tables = $request->input('tables', []);
|
||||
$this->logic->optimizeTable($tables);
|
||||
return $this->success('优化成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理表碎片
|
||||
*/
|
||||
#[Permission('数据表清理碎片', 'core:database:edit')]
|
||||
public function fragment(Request $request): Response
|
||||
{
|
||||
$tables = $request->input('tables', []);
|
||||
$this->logic->fragmentTable($tables);
|
||||
return $this->success('清理成功');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemAttachmentLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 附件管理控制器
|
||||
*/
|
||||
class SystemAttachmentController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemAttachmentLogic();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件数据列表', 'core:attachment:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['origin_name', ''],
|
||||
['category_id', ''],
|
||||
['storage_mode', ''],
|
||||
['mime_type', ''],
|
||||
['create_time', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件数据修改', 'core:attachment:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$result = $this->logic->edit($data['id'], ['origin_name' => $data['origin_name']]);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件数据删除', 'core:attachment:edit')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动分类
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件移动分类', 'core:attachment:edit')]
|
||||
public function move(Request $request) : Response
|
||||
{
|
||||
$category_id = $request->post('category_id', '');
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids) || empty($category_id)) {
|
||||
return $this->fail('参数错误,请检查参数');
|
||||
}
|
||||
$result = $this->logic->move($category_id, $ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\validate\system\SystemCategoryValidate;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemCategoryLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 附件分类控制器
|
||||
*/
|
||||
class SystemCategoryController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemCategoryLogic();
|
||||
$this->validate = new SystemCategoryValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件分类列表', 'core:attachment:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['category_name', ''],
|
||||
]);
|
||||
$data = $this->logic->tree($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件分类读取', 'core:attachment:index')]
|
||||
public function read(Request $request) : Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件分类添加', 'core:attachment:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件分类修改', 'core:attachment:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('附件分类删除', 'core:attachment:edit')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\ConfigCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigLogic;
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigGroupLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemConfigValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 配置项数据控制器
|
||||
*/
|
||||
class SystemConfigController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemConfigLogic();
|
||||
$this->validate = new SystemConfigValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置列表', 'core:config:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['group_id', ''],
|
||||
['name', ''],
|
||||
['key', ''],
|
||||
]);
|
||||
$this->logic->setOrderField('sort');
|
||||
$this->logic->setOrderType('desc');
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改配置内容
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置修改', 'core:config:update')]
|
||||
public function batchUpdate(Request $request): Response
|
||||
{
|
||||
$group_id = $request->post('group_id');
|
||||
$config = $request->post('config');
|
||||
if (empty($group_id) || empty($config)) {
|
||||
return $this->fail('参数错误');
|
||||
}
|
||||
$this->logic->batchUpdate($group_id, $config);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\ConfigCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigGroupLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemConfigGroupValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use plugin\saiadmin\utils\Arr;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\saiadmin\service\EmailService;
|
||||
use plugin\saiadmin\app\model\system\SystemMail;
|
||||
|
||||
/**
|
||||
* 配置控制器
|
||||
*/
|
||||
class SystemConfigGroupController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemConfigGroupLogic();
|
||||
$this->validate = new SystemConfigGroupValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置列表', 'core:config:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getAll($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
ConfigCache::clearConfig($data['code']);
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置管理', 'core:config:edit')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮件测试
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('系统设置修改', 'core:config:update')]
|
||||
public function email(Request $request) : Response
|
||||
{
|
||||
$email = $request->input('email', '');
|
||||
if (empty($email)) {
|
||||
return $this->fail('请输入邮箱');
|
||||
}
|
||||
$subject = "测试邮件";
|
||||
$code = "9527";
|
||||
$content = "<h1>验证码:{code}</h1><p>这是一封测试邮件,请忽略</p>";
|
||||
$template = [
|
||||
'code' => $code
|
||||
];
|
||||
$config = EmailService::getConfig();
|
||||
$model = SystemMail::create([
|
||||
'gateway' => Arr::getConfigValue($config,'Host'),
|
||||
'from' => Arr::getConfigValue($config,'From'),
|
||||
'email' => $email,
|
||||
'code' => $code,
|
||||
]);
|
||||
try {
|
||||
$result = EmailService::sendByTemplate($email, $subject, $content, $template);
|
||||
if (!empty($result)) {
|
||||
$model->status = 'failure';
|
||||
$model->response = $result;
|
||||
$model->save();
|
||||
return $this->fail('发送失败,请查看日志');
|
||||
} else {
|
||||
$model->status = 'success';
|
||||
$model->save();
|
||||
return $this->success([], '发送成功');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$model->status = 'failure';
|
||||
$model->response = $e->getMessage();
|
||||
$model->save();
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\validate\system\SystemDeptValidate;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemDeptLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 部门控制器
|
||||
*/
|
||||
class SystemDeptController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemDeptLogic();
|
||||
$this->validate = new SystemDeptValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('部门数据列表', 'core:dept:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$data = $this->logic->tree($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('部门数据读取', 'core:dept:read')]
|
||||
public function read(Request $request) : Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('部门数据添加', 'core:dept:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('部门数据修改','core:dept:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('部门数据删除','core:dept:destroy')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可操作部门
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function accessDept(Request $request) : Response
|
||||
{
|
||||
$where = ['status' => 1];
|
||||
$data = $this->logic->accessDept($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\DictCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemDictDataLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemDictDataValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 字典数据控制器
|
||||
*/
|
||||
class SystemDictDataController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemDictDataLogic();
|
||||
$this->validate = new SystemDictDataValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典列表', 'core:dict:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['label', ''],
|
||||
['value', ''],
|
||||
['type_id', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$this->logic->setOrderField('sort');
|
||||
$this->logic->setOrderType('desc');
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\DictCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemDictTypeLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemDictTypeValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Cache;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 字典类型控制器
|
||||
*/
|
||||
class SystemDictTypeController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemDictTypeLogic();
|
||||
$this->validate = new SystemDictTypeValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典列表', 'core:dict:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('数据字典管理', 'core:dict:edit')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
DictCache::clear();
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemLoginLogLogic;
|
||||
use plugin\saiadmin\app\logic\system\SystemOperLogLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 日志控制器
|
||||
*/
|
||||
class SystemLogController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* 登录日志列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('登录日志列表', 'core:logs:login')]
|
||||
public function getLoginLogPageList(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['login_time', ''],
|
||||
['username', ''],
|
||||
['status', ''],
|
||||
['ip', ''],
|
||||
]);
|
||||
$logic = new SystemLoginLogLogic();
|
||||
$query = $logic->search($where);
|
||||
$data = $logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除登录日志
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('登录日志删除', 'core:logs:deleteLogin')]
|
||||
public function deleteLoginLog(Request $request) : Response
|
||||
{
|
||||
$ids = $request->input('ids', '');
|
||||
$logic = new SystemLoginLogLogic();
|
||||
if (!empty($ids)) {
|
||||
$logic->destroy($ids);
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('操作日志列表', 'core:logs:Oper')]
|
||||
public function getOperLogPageList(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['create_time', ''],
|
||||
['username', ''],
|
||||
['service_name', ''],
|
||||
['router', ''],
|
||||
['ip', ''],
|
||||
]);
|
||||
$logic = new SystemOperLogLogic();
|
||||
$logic->init($this->adminInfo);
|
||||
$query = $logic->search($where);
|
||||
$data = $logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('操作日志删除', 'core:logs:deleteOper')]
|
||||
public function deleteOperLog(Request $request) : Response
|
||||
{
|
||||
$ids = $request->input('ids', '');
|
||||
$logic = new SystemOperLogLogic();
|
||||
if (!empty($ids)) {
|
||||
$logic->destroy($ids);
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemMailLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemMailValidate;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 邮件记录控制器
|
||||
*/
|
||||
class SystemMailController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemMailLogic();
|
||||
$this->validate = new SystemMailValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('邮件日志列表', 'core:email:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['gateway', ''],
|
||||
['from', ''],
|
||||
['code', ''],
|
||||
['email', ''],
|
||||
['status', ''],
|
||||
['create_time', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('邮件日志删除', 'core:email:destroy')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\UserMenuCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemMenuLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemMenuValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 菜单控制器
|
||||
*/
|
||||
class SystemMenuController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemMenuLogic();
|
||||
$this->validate = new SystemMenuValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('菜单数据列表', 'core:menu:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['path', ''],
|
||||
['menu', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$data = $this->logic->tree($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('菜单数据读取', 'core:menu:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('菜单数据添加', 'core:menu:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
UserMenuCache::clearMenuCache();
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('菜单数据修改', 'core:menu:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
UserMenuCache::clearMenuCache();
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('菜单数据删除', 'core:menu:destroy')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
UserMenuCache::clearMenuCache();
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 可操作菜单
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function accessMenu(Request $request): Response
|
||||
{
|
||||
$where = [];
|
||||
if ($this->adminId > 1) {
|
||||
$data = $this->logic->auth();
|
||||
} else {
|
||||
$data = $this->logic->tree($where);
|
||||
}
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\system\SystemPostLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemPostValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 岗位信息控制器
|
||||
*/
|
||||
class SystemPostController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemPostLogic();
|
||||
$this->validate = new SystemPostValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据列表', 'core:post:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据读取', 'core:post:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据添加', 'core:post:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据修改', 'core:post:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据删除', 'core:post:destroy')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据导入', 'core:post:import')]
|
||||
public function import(Request $request): Response
|
||||
{
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->fail('未找到上传文件');
|
||||
}
|
||||
$this->logic->import($file);
|
||||
return $this->success('导入成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('岗位数据导出', 'core:post:export')]
|
||||
public function export(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
return $this->logic->export($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载导入模板
|
||||
* @return Response
|
||||
*/
|
||||
public function downloadTemplate(): Response
|
||||
{
|
||||
$file_name = "template.xlsx";
|
||||
return downloadFile($file_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可操作岗位
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function accessPost(Request $request): Response
|
||||
{
|
||||
$where = ['status' => 1];
|
||||
$data = $this->logic->accessPost($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\model\system\SystemUserRole;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\cache\UserInfoCache;
|
||||
use plugin\saiadmin\app\model\system\SystemUser;
|
||||
use plugin\saiadmin\app\validate\system\SystemRoleValidate;
|
||||
use plugin\saiadmin\app\logic\system\SystemRoleLogic;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 角色控制器
|
||||
*/
|
||||
class SystemRoleController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemRoleLogic();
|
||||
$this->validate = new SystemRoleValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据列表', 'core:role:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['code', ''],
|
||||
['status', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$levelArr = array_column($this->adminInfo['roleList'], 'level');
|
||||
$maxLevel = max($levelArr);
|
||||
$query->where('level', '<', $maxLevel);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据读取', 'core:role:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据添加', 'core:role:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据修改', 'core:role:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据删除', 'core:role:destroy')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色获取菜单
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色数据列表', 'core:role:index')]
|
||||
public function getMenuByRole(Request $request): Response
|
||||
{
|
||||
$id = $request->get('id');
|
||||
$data = $this->logic->getMenuByRole($id);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('角色菜单权限', 'core:role:menu')]
|
||||
public function menuPermission(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id');
|
||||
$menu_ids = $request->post('menu_ids');
|
||||
$this->logic->saveMenuPermission($id, $menu_ids);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 可操作角色
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function accessRole(Request $request): Response
|
||||
{
|
||||
$where = ['status' => 1];
|
||||
$data = $this->logic->accessRole($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\utils\ServerMonitor;
|
||||
use support\think\Cache;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 邮件记录控制器
|
||||
*/
|
||||
class SystemServerController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('服务监控', 'core:server:monitor')]
|
||||
public function monitor(Request $request): Response
|
||||
{
|
||||
$service = new ServerMonitor();
|
||||
return $this->success([
|
||||
'memory' => $service->getMemoryInfo(),
|
||||
'disk' => $service->getDiskInfo(),
|
||||
'phpEnv' => $service->getPhpAndEnvInfo(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('缓存信息', 'core:server:cache')]
|
||||
public function cache(Request $request): Response
|
||||
{
|
||||
$menu_cache = config('plugin.saiadmin.saithink.menu_cache', []);
|
||||
$button_cache = config('plugin.saiadmin.saithink.button_cache', []);
|
||||
$config_cache = config('plugin.saiadmin.saithink.config_cache', []);
|
||||
$dict_cache = config('plugin.saiadmin.saithink.dict_cache', []);
|
||||
$reflection_cache = config('plugin.saiadmin.saithink.reflection_cache', []);
|
||||
|
||||
return $this->success([
|
||||
'menu_cache' => $menu_cache,
|
||||
'button_cache' => $button_cache,
|
||||
'config_cache' => $config_cache,
|
||||
'dict_cache' => $dict_cache,
|
||||
'reflection_cache' => $reflection_cache
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('缓存数据清理', 'core:server:clear')]
|
||||
public function clear(Request $request) : Response
|
||||
{
|
||||
$tag = $request->input('tag', '');
|
||||
if (empty($tag)) {
|
||||
return $this->fail('请选择要删除的缓存');
|
||||
}
|
||||
Cache::tag($tag)->clear();
|
||||
Cache::delete($tag);
|
||||
return $this->success('删除成功');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\cache\UserAuthCache;
|
||||
use plugin\saiadmin\app\cache\UserMenuCache;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\cache\UserInfoCache;
|
||||
use plugin\saiadmin\app\logic\system\SystemUserLogic;
|
||||
use plugin\saiadmin\app\validate\system\SystemUserValidate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 用户信息控制器
|
||||
*/
|
||||
class SystemUserController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new SystemUserLogic();
|
||||
$this->validate = new SystemUserValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户数据列表', 'core:user:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['username', ''],
|
||||
['phone', ''],
|
||||
['email', ''],
|
||||
['status', ''],
|
||||
['dept_id', ''],
|
||||
['create_time', ''],
|
||||
]);
|
||||
$data = $this->logic->indexList($where);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户数据读取', 'core:user:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户数据保存', 'core:user:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户数据更新', 'core:user:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户数据删除', 'core:user:destroy')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->input('ids', '');
|
||||
if (!empty($ids)) {
|
||||
$this->logic->destroy($ids);
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户缓存
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('清理用户缓存', 'core:user:cache')]
|
||||
public function clearCache(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id', '');
|
||||
UserInfoCache::clearUserInfo($id);
|
||||
UserAuthCache::clearUserAuth($id);
|
||||
UserMenuCache::clearUserMenu($id);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户密码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('修改用户密码', 'core:user:password')]
|
||||
public function initUserPassword(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id', '');
|
||||
$password = $request->post('password', '');
|
||||
if ($id == 1) {
|
||||
return $this->fail('超级管理员不允许重置密码');
|
||||
}
|
||||
$data = ['password' => password_hash($password, PASSWORD_DEFAULT)];
|
||||
$this->logic->authEdit($id, $data);
|
||||
UserInfoCache::clearUserInfo($id);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户首页
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('设置用户首页', 'core:user:home')]
|
||||
public function setHomePage(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id', '');
|
||||
$dashboard = $request->post('dashboard', '');
|
||||
$data = ['dashboard' => $dashboard];
|
||||
$this->logic->authEdit($id, $data);
|
||||
UserInfoCache::clearUserInfo($id);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新资料
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户修改资料')]
|
||||
public function updateInfo(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
unset($data['deptList']);
|
||||
unset($data['postList']);
|
||||
unset($data['roleList']);
|
||||
$result = $this->logic->updateInfo($this->adminId, $data);
|
||||
if ($result) {
|
||||
UserInfoCache::clearUserInfo($this->adminId);
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('用户修改密码')]
|
||||
public function modifyPassword(Request $request): Response
|
||||
{
|
||||
$oldPassword = $request->input('oldPassword');
|
||||
$newPassword = $request->input('newPassword');
|
||||
$this->logic->modifyPassword($this->adminId, $oldPassword, $newPassword);
|
||||
UserInfoCache::clearUserInfo($this->adminId);
|
||||
return $this->success('修改成功');
|
||||
}
|
||||
}
|
||||
181
server/plugin/saiadmin/app/controller/tool/CrontabController.php
Normal file
181
server/plugin/saiadmin/app/controller/tool/CrontabController.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\tool;
|
||||
|
||||
use plugin\saiadmin\app\logic\tool\CrontabLogic;
|
||||
use plugin\saiadmin\app\logic\tool\CrontabLogLogic;
|
||||
use plugin\saiadmin\app\validate\tool\CrontabValidate;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use Webman\Channel\Client;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 定时任务控制器
|
||||
*/
|
||||
class CrontabController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new CrontabLogic();
|
||||
$this->validate = new CrontabValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务列表', 'tool:crontab:index')]
|
||||
public function index(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['name', ''],
|
||||
['type', ''],
|
||||
['status', ''],
|
||||
['create_time', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务添加', 'tool:crontab:edit')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务修改', 'tool:crontab:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务删除', 'tool:crontab:edit')]
|
||||
public function destroy(Request $request) : Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务状态修改', 'tool:crontab:edit')]
|
||||
public function changeStatus(Request $request) : Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$status = $request->input('status', 1);
|
||||
if (empty($id)) {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
$result = $this->logic->changeStatus($id, $status);
|
||||
if ($result) {
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行定时任务
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务执行', 'tool:crontab:run')]
|
||||
public function run(Request $request) : Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$result = $this->logic->run($id);
|
||||
if ($result) {
|
||||
return $this->success('执行成功');
|
||||
} else {
|
||||
return $this->fail('执行失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务日志
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务日志', 'tool:crontab:index')]
|
||||
public function logPageList(Request $request) : Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['crontab_id', ''],
|
||||
['create_time', []]
|
||||
]);
|
||||
$logic = new CrontabLogLogic();
|
||||
$query = $logic->search($where);
|
||||
$data = $logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务日志删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('定时任务日志删除', 'tool:crontab:edit')]
|
||||
public function deleteCrontabLog(Request $request) : Response
|
||||
{
|
||||
$ids = $request->input('ids', '');
|
||||
if (!empty($ids)) {
|
||||
$logic = new CrontabLogLogic();
|
||||
$logic->destroy($ids);
|
||||
return $this->success('操作成功');
|
||||
} else {
|
||||
return $this->fail('参数错误,请检查');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\tool;
|
||||
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\app\logic\tool\GenerateTablesLogic;
|
||||
use plugin\saiadmin\app\validate\tool\GenerateTablesValidate;
|
||||
use plugin\saiadmin\app\cache\UserMenuCache;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 代码生成控制器
|
||||
*/
|
||||
class GenerateTablesController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new GenerateTablesLogic();
|
||||
$this->validate = new GenerateTablesValidate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成列表', 'tool:code:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
['table_name', ''],
|
||||
]);
|
||||
$query = $this->logic->search($where);
|
||||
$data = $this->logic->getList($query);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成列表', 'tool:code:index')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成修改', 'tool:code:edit')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成删除', 'tool:code:edit')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载数据表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成装载', 'tool:code:edit')]
|
||||
public function loadTable(Request $request): Response
|
||||
{
|
||||
$names = $request->input('names', []);
|
||||
$source = $request->input('source', '');
|
||||
$this->logic->loadTable($names, $source);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步数据表字段信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成同步表结构', 'tool:code:edit')]
|
||||
public function sync(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$this->logic->sync($id);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 代码预览
|
||||
*/
|
||||
#[Permission('代码生成预览', 'tool:code:edit')]
|
||||
public function preview(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$data = $this->logic->preview($id);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 代码生成
|
||||
*/
|
||||
#[Permission('代码生成文件', 'tool:code:edit')]
|
||||
public function generate(Request $request): Response
|
||||
{
|
||||
$ids = $request->input('ids', '');
|
||||
$data = $this->logic->generate($ids);
|
||||
return response()->download($data['download'], $data['filename']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成到模块
|
||||
*/
|
||||
#[Permission('代码生成到模块', 'tool:code:edit')]
|
||||
public function generateFile(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$this->logic->generateFile($id);
|
||||
UserMenuCache::clearMenuCache();
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据表字段信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('代码生成读取表字段', 'tool:code:index')]
|
||||
public function getTableColumns(Request $request): Response
|
||||
{
|
||||
$table_id = $request->input('table_id', '');
|
||||
$data = $this->logic->getTableColumns($table_id);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
}
|
||||
162
server/plugin/saiadmin/app/event/SystemUser.php
Normal file
162
server/plugin/saiadmin/app/event/SystemUser.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\event;
|
||||
|
||||
use plugin\saiadmin\app\cache\ReflectionCache;
|
||||
use plugin\saiadmin\app\model\system\SystemLoginLog;
|
||||
use plugin\saiadmin\app\model\system\SystemOperLog;
|
||||
|
||||
class SystemUser
|
||||
{
|
||||
/**
|
||||
* 登录日志
|
||||
* @param $item
|
||||
*/
|
||||
public function login($item)
|
||||
{
|
||||
$request = request();
|
||||
$ip = $request ? $request->getRealIp() : '127.0.0.1';
|
||||
$http_user_agent = $request ? $request->header('user-agent') : '';
|
||||
$data['username'] = $item['username'];
|
||||
$data['ip'] = $ip;
|
||||
$data['ip_location'] = self::getIpLocation($ip);
|
||||
$data['os'] = self::getOs($http_user_agent);
|
||||
$data['browser'] = self::getBrowser($http_user_agent);
|
||||
$data['status'] = $item['status'];
|
||||
$data['message'] = $item['message'];
|
||||
$data['login_time'] = date('Y-m-d H:i:s');
|
||||
if (isset($item['admin_id'])) {
|
||||
$data['created_by'] = $item['admin_id'];
|
||||
$data['updated_by'] = $item['admin_id'];
|
||||
}
|
||||
SystemLoginLog::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录操作日志
|
||||
*/
|
||||
public function operateLog(): bool
|
||||
{
|
||||
$request = request();
|
||||
if (!$request) {
|
||||
return false;
|
||||
}
|
||||
if ($request->method() === 'GET') {
|
||||
return false;
|
||||
}
|
||||
$info = getCurrentInfo();
|
||||
$ip = $request->getRealIp();
|
||||
$module = $request->plugin;
|
||||
$rule = trim($request->uri());
|
||||
$data['username'] = $info['username'];
|
||||
$data['method'] = $request->method();
|
||||
$data['router'] = $rule;
|
||||
$data['service_name'] = self::getServiceName();
|
||||
$data['app'] = $module;
|
||||
$data['ip'] = $ip;
|
||||
$data['ip_location'] = self::getIpLocation($ip);
|
||||
$data['request_data'] = $this->filterParams($request->all());
|
||||
SystemOperLog::create($data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务名称
|
||||
*/
|
||||
protected function getServiceName(): string
|
||||
{
|
||||
$request = request();
|
||||
if (!$request) {
|
||||
return '未命名业务';
|
||||
}
|
||||
$permissions = ReflectionCache::getPermissionAttributes($request->controller, $request->action);
|
||||
if (!empty($permissions)) {
|
||||
return $permissions['title'] ?? '未命名业务';
|
||||
} else {
|
||||
return '未命名业务';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤字段
|
||||
*/
|
||||
protected function filterParams($params): string
|
||||
{
|
||||
$blackList = ['password', 'oldPassword', 'newPassword'];
|
||||
foreach ($params as $key => $value) {
|
||||
if (in_array($key, $blackList)) {
|
||||
$params[$key] = '******';
|
||||
}
|
||||
}
|
||||
return json_encode($params, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地理位置
|
||||
*/
|
||||
protected function getIpLocation($ip): string
|
||||
{
|
||||
$ip2region = new \Ip2Region();
|
||||
try {
|
||||
$region = $ip2region->memorySearch($ip);
|
||||
} catch (\Exception $e) {
|
||||
return '未知';
|
||||
}
|
||||
list($country, $province, $city, $network) = explode('|', $region['region']);
|
||||
if ($network === '内网IP') {
|
||||
return $network;
|
||||
}
|
||||
if ($country == '中国') {
|
||||
return $province . '-' . $city . ':' . $network;
|
||||
} else if ($country == '0') {
|
||||
return '未知';
|
||||
} else {
|
||||
return $country;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器信息
|
||||
*/
|
||||
protected function getBrowser($user_agent): string
|
||||
{
|
||||
$br = 'Unknown';
|
||||
if (preg_match('/MSIE/i', $user_agent)) {
|
||||
$br = 'MSIE';
|
||||
} elseif (preg_match('/Firefox/i', $user_agent)) {
|
||||
$br = 'Firefox';
|
||||
} elseif (preg_match('/Chrome/i', $user_agent)) {
|
||||
$br = 'Chrome';
|
||||
} elseif (preg_match('/Safari/i', $user_agent)) {
|
||||
$br = 'Safari';
|
||||
} elseif (preg_match('/Opera/i', $user_agent)) {
|
||||
$br = 'Opera';
|
||||
} else {
|
||||
$br = 'Other';
|
||||
}
|
||||
return $br;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作系统信息
|
||||
*/
|
||||
protected function getOs($user_agent): string
|
||||
{
|
||||
$os = 'Unknown';
|
||||
if (preg_match('/win/i', $user_agent)) {
|
||||
$os = 'Win';
|
||||
} elseif (preg_match('/mac/i', $user_agent)) {
|
||||
$os = 'Mac';
|
||||
} elseif (preg_match('/linux/i', $user_agent)) {
|
||||
$os = 'Linux';
|
||||
} else {
|
||||
$os = 'Other';
|
||||
}
|
||||
return $os;
|
||||
}
|
||||
|
||||
}
|
||||
68
server/plugin/saiadmin/app/exception/Handler.php
Normal file
68
server/plugin/saiadmin/app/exception/Handler.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\exception;
|
||||
|
||||
use Throwable;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Exception\ExceptionHandler;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 异常处理类
|
||||
*/
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
public $dontReport = [
|
||||
ApiException::class,
|
||||
];
|
||||
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
if ($this->shouldntReport($exception)) {
|
||||
return;
|
||||
}
|
||||
$logs = '';
|
||||
if ($request = \request()) {
|
||||
$user = getCurrentInfo();
|
||||
$logs .= $request->method() . ' ' . $request->uri();
|
||||
$logs .= PHP_EOL . '[request_param]: ' . json_encode($request->all());
|
||||
$logs .= PHP_EOL . '[timestamp]: ' . date('Y-m-d H:i:s');
|
||||
$logs .= PHP_EOL . '[client_ip]: ' . $request->getRealIp();
|
||||
$logs .= PHP_EOL . '[action_user]: ' . var_export($user, true);
|
||||
$logs .= PHP_EOL . '[exception_handle]: ' . get_class($exception);
|
||||
$logs .= PHP_EOL . '[exception_info]: ' . PHP_EOL . $exception;
|
||||
}
|
||||
$this->logger->error($logs);
|
||||
}
|
||||
|
||||
public function render(Request $request, Throwable $exception): Response
|
||||
{
|
||||
$debug = config('app.debug', true);
|
||||
$code = $exception->getCode();
|
||||
$json = [
|
||||
'code' => $code ? $code : 500,
|
||||
'message' => $code !== 500 ? $exception->getMessage() : 'Server internal error',
|
||||
'type' => 'failed'
|
||||
];
|
||||
if ($debug) {
|
||||
$json['request_url'] = $request->method() . ' ' . $request->uri();
|
||||
$json['timestamp'] = date('Y-m-d H:i:s');
|
||||
$json['client_ip'] = $request->getRealIp();
|
||||
$json['request_param'] = $request->all();
|
||||
$json['exception_handle'] = get_class($exception);
|
||||
$json['exception_info'] = [
|
||||
'code' => $exception->getCode(),
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => explode("\n", $exception->getTraceAsString())
|
||||
];
|
||||
}
|
||||
return new Response(200, ['Content-Type' => 'application/json;charset=utf-8'], json_encode($json));
|
||||
}
|
||||
}
|
||||
114
server/plugin/saiadmin/app/functions.php
Normal file
114
server/plugin/saiadmin/app/functions.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
use Webman\Route;
|
||||
use support\Response;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\app\cache\ConfigCache;
|
||||
use plugin\saiadmin\app\cache\DictCache;
|
||||
|
||||
if (!function_exists('getCurrentInfo')) {
|
||||
/**
|
||||
* 获取当前登录用户
|
||||
*/
|
||||
function getCurrentInfo(): bool|array
|
||||
{
|
||||
if (!request()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$token = JwtToken::getExtend();
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('fastRoute')) {
|
||||
/**
|
||||
* 快速注册路由[index|save|update|read|destroy|import|export]
|
||||
* @param string $name
|
||||
* @param string $controller
|
||||
* @return void
|
||||
*/
|
||||
function fastRoute(string $name, string $controller): void
|
||||
{
|
||||
$name = trim($name, '/');
|
||||
if (method_exists($controller, 'index'))
|
||||
Route::get("/$name/index", [$controller, 'index']);
|
||||
if (method_exists($controller, 'save'))
|
||||
Route::post("/$name/save", [$controller, 'save']);
|
||||
if (method_exists($controller, 'update'))
|
||||
Route::put("/$name/update", [$controller, 'update']);
|
||||
if (method_exists($controller, 'read'))
|
||||
Route::get("/$name/read", [$controller, 'read']);
|
||||
if (method_exists($controller, 'destroy'))
|
||||
Route::delete("/$name/destroy", [$controller, 'destroy']);
|
||||
if (method_exists($controller, 'import'))
|
||||
Route::post("/$name/import", [$controller, 'import']);
|
||||
if (method_exists($controller, 'export'))
|
||||
Route::post("/$name/export", [$controller, 'export']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('downloadFile')) {
|
||||
/**
|
||||
* 下载模板
|
||||
* @param $file_name
|
||||
* @return Response
|
||||
*/
|
||||
function downloadFile($file_name): Response
|
||||
{
|
||||
$base_dir = config('plugin.saiadmin.saithink.template', base_path() . '/public/template');
|
||||
if (file_exists($base_dir . DIRECTORY_SEPARATOR . $file_name)) {
|
||||
return response()->download($base_dir . DIRECTORY_SEPARATOR . $file_name, urlencode($file_name));
|
||||
} else {
|
||||
throw new ApiException('模板不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('formatBytes')) {
|
||||
/**
|
||||
* 根据字节计算大小
|
||||
* @param $bytes
|
||||
* @return string
|
||||
*/
|
||||
function formatBytes($bytes): string
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
for ($i = 0; $bytes > 1024; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
return round($bytes, 2) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getConfigGroup')) {
|
||||
/**
|
||||
* 读取配置组
|
||||
* @param $group
|
||||
* @return array
|
||||
*/
|
||||
function getConfigGroup($group): array
|
||||
{
|
||||
return ConfigCache::getConfig($group);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('dictDataList')) {
|
||||
/**
|
||||
* 根据字典编码获取字典列表
|
||||
* @param string $code 字典编码
|
||||
* @return array
|
||||
*/
|
||||
function dictDataList(string $code): array
|
||||
{
|
||||
return DictCache::getDict($code);
|
||||
}
|
||||
}
|
||||
209
server/plugin/saiadmin/app/logic/system/DatabaseLogic.php
Normal file
209
server/plugin/saiadmin/app/logic/system/DatabaseLogic.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
101
server/plugin/saiadmin/app/logic/system/SystemCategoryLogic.php
Normal file
101
server/plugin/saiadmin/app/logic/system/SystemCategoryLogic.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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('删除数据异常,请检查');
|
||||
}
|
||||
}
|
||||
}
|
||||
107
server/plugin/saiadmin/app/logic/system/SystemConfigLogic.php
Normal file
107
server/plugin/saiadmin/app/logic/system/SystemConfigLogic.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
127
server/plugin/saiadmin/app/logic/system/SystemDeptLogic.php
Normal file
127
server/plugin/saiadmin/app/logic/system/SystemDeptLogic.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
116
server/plugin/saiadmin/app/logic/system/SystemDictTypeLogic.php
Normal file
116
server/plugin/saiadmin/app/logic/system/SystemDictTypeLogic.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
26
server/plugin/saiadmin/app/logic/system/SystemMailLogic.php
Normal file
26
server/plugin/saiadmin/app/logic/system/SystemMailLogic.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
189
server/plugin/saiadmin/app/logic/system/SystemMenuLogic.php
Normal file
189
server/plugin/saiadmin/app/logic/system/SystemMenuLogic.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
95
server/plugin/saiadmin/app/logic/system/SystemPostLogic.php
Normal file
95
server/plugin/saiadmin/app/logic/system/SystemPostLogic.php
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
156
server/plugin/saiadmin/app/logic/system/SystemRoleLogic.php
Normal file
156
server/plugin/saiadmin/app/logic/system/SystemRoleLogic.php
Normal 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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
336
server/plugin/saiadmin/app/logic/system/SystemUserLogic.php
Normal file
336
server/plugin/saiadmin/app/logic/system/SystemUserLogic.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
25
server/plugin/saiadmin/app/logic/tool/CrontabLogLogic.php
Normal file
25
server/plugin/saiadmin/app/logic/tool/CrontabLogLogic.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
247
server/plugin/saiadmin/app/logic/tool/CrontabLogic.php
Normal file
247
server/plugin/saiadmin/app/logic/tool/CrontabLogic.php
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
213
server/plugin/saiadmin/app/logic/tool/GenerateColumnsLogic.php
Normal file
213
server/plugin/saiadmin/app/logic/tool/GenerateColumnsLogic.php
Normal 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;
|
||||
}
|
||||
}
|
||||
478
server/plugin/saiadmin/app/logic/tool/GenerateTablesLogic.php
Normal file
478
server/plugin/saiadmin/app/logic/tool/GenerateTablesLogic.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
70
server/plugin/saiadmin/app/middleware/CheckAuth.php
Normal file
70
server/plugin/saiadmin/app/middleware/CheckAuth.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\middleware;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use plugin\saiadmin\app\cache\UserAuthCache;
|
||||
use plugin\saiadmin\app\cache\ReflectionCache;
|
||||
use plugin\saiadmin\exception\SystemException;
|
||||
|
||||
/**
|
||||
* 权限检查中间件
|
||||
*/
|
||||
class CheckAuth implements MiddlewareInterface
|
||||
{
|
||||
|
||||
public function process(Request $request, callable $handler) : Response
|
||||
{
|
||||
$controller = $request->controller;
|
||||
$action = $request->action;
|
||||
|
||||
// 通过反射获取控制器哪些方法不需要登录
|
||||
$noNeedLogin = ReflectionCache::getNoNeedLogin($controller);
|
||||
|
||||
// 不登录访问,无需权限验证
|
||||
if (in_array($action, $noNeedLogin)) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
// 登录信息
|
||||
$token = getCurrentInfo();
|
||||
if ($token === false) {
|
||||
throw new SystemException('用户信息读取失败,无法访问或操作');
|
||||
}
|
||||
|
||||
// 系统默认超级管理员,无需权限验证
|
||||
if ($token['id'] === 1) {
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
// 2. 获取接口权限属性 (使用缓存类)
|
||||
$permissions = ReflectionCache::getPermissionAttributes($controller, $action);
|
||||
|
||||
if (!empty($permissions) && !empty($permissions['slug'])) {
|
||||
// 用户权限缓存
|
||||
$auth = UserAuthCache::getUserAuth($token['id']);
|
||||
|
||||
if (!$this->checkPermissions($permissions, $auth)) {
|
||||
throw new SystemException('权限不足,无法访问或操作');
|
||||
}
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限
|
||||
*/
|
||||
private function checkPermissions(array $attr, array $userPermissions): bool
|
||||
{
|
||||
// 直接对比 slug
|
||||
return in_array($attr['slug'], $userPermissions);
|
||||
}
|
||||
|
||||
}
|
||||
40
server/plugin/saiadmin/app/middleware/CheckLogin.php
Normal file
40
server/plugin/saiadmin/app/middleware/CheckLogin.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\middleware;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use Tinywan\Jwt\JwtToken;
|
||||
use plugin\saiadmin\app\cache\ReflectionCache;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 登录检查中间件
|
||||
*/
|
||||
class CheckLogin implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
// 通过反射获取控制器哪些方法不需要登录
|
||||
$noNeedLogin = ReflectionCache::getNoNeedLogin($request->controller);
|
||||
// 访问的方法需要登录
|
||||
if (!in_array($request->action, $noNeedLogin)) {
|
||||
try {
|
||||
$token = JwtToken::getExtend();
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException('您的登录凭证错误或者已过期,请重新登录', 401);
|
||||
}
|
||||
if ($token['plat'] !== 'saiadmin') {
|
||||
throw new ApiException('登录凭证校验失败');
|
||||
}
|
||||
$request->setHeader('check_login', true);
|
||||
$request->setHeader('check_admin', $token);
|
||||
}
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
||||
33
server/plugin/saiadmin/app/middleware/CrossDomain.php
Normal file
33
server/plugin/saiadmin/app/middleware/CrossDomain.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\middleware;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
|
||||
/**
|
||||
* 跨域中间件
|
||||
*/
|
||||
class CrossDomain implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $handler) : Response
|
||||
{
|
||||
// 如果是options请求则返回一个空响应,否则继续向洋葱芯穿越,并得到一个响应
|
||||
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
|
||||
|
||||
// 给响应添加跨域相关的http头
|
||||
$response->withHeaders([
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
|
||||
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
|
||||
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
38
server/plugin/saiadmin/app/middleware/SystemLog.php
Normal file
38
server/plugin/saiadmin/app/middleware/SystemLog.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\middleware;
|
||||
|
||||
use Webman\Event\Event;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\app\cache\ReflectionCache;
|
||||
|
||||
class SystemLog implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param callable $handler
|
||||
* @return Response
|
||||
*/
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
// 通过反射获取控制器哪些方法不需要登录
|
||||
$noNeedLogin = ReflectionCache::getNoNeedLogin($request->controller);
|
||||
// 访问的方法需要登录
|
||||
if (!in_array($request->action, $noNeedLogin)) {
|
||||
try {
|
||||
// 记录日志
|
||||
Event::emit('user.operateLog', true);
|
||||
} catch (\Throwable $e) {
|
||||
throw new ApiException('登录凭获取失败,请检查');
|
||||
}
|
||||
}
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
||||
60
server/plugin/saiadmin/app/model/system/SystemAttachment.php
Normal file
60
server/plugin/saiadmin/app/model/system/SystemAttachment.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 附件模型
|
||||
*
|
||||
* sa_system_attachment 附件信息表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $category_id 文件分类
|
||||
* @property $storage_mode 存储模式
|
||||
* @property $origin_name 原文件名
|
||||
* @property $object_name 新文件名
|
||||
* @property $hash 文件hash
|
||||
* @property $mime_type 资源类型
|
||||
* @property $storage_path 存储目录
|
||||
* @property $suffix 文件后缀
|
||||
* @property $size_byte 字节数
|
||||
* @property $size_info 文件大小
|
||||
* @property $url url地址
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemAttachment extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_attachment';
|
||||
|
||||
/**
|
||||
* 原文件名搜索
|
||||
*/
|
||||
public function searchOriginNameAttr($query, $value)
|
||||
{
|
||||
$query->where('origin_name', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件类型搜索
|
||||
*/
|
||||
public function searchMimeTypeAttr($query, $value)
|
||||
{
|
||||
$query->where('mime_type', 'like', $value . '/%');
|
||||
}
|
||||
|
||||
}
|
||||
46
server/plugin/saiadmin/app/model/system/SystemCategory.php
Normal file
46
server/plugin/saiadmin/app/model/system/SystemCategory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 附件分类模型
|
||||
*
|
||||
* sa_system_category 附件分类表
|
||||
*
|
||||
* @property $id 分类ID
|
||||
* @property $parent_id 父id
|
||||
* @property $level 组集关系
|
||||
* @property $category_name 分类名称
|
||||
* @property $sort 排序
|
||||
* @property $status 状态
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemCategory extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_category';
|
||||
|
||||
/**
|
||||
* 分类名称搜索
|
||||
*/
|
||||
public function searchCategoryNameAttr($query, $value)
|
||||
{
|
||||
$query->where('category_name', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
}
|
||||
45
server/plugin/saiadmin/app/model/system/SystemConfig.php
Normal file
45
server/plugin/saiadmin/app/model/system/SystemConfig.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 参数配置模型
|
||||
*
|
||||
* sa_system_config 参数配置信息表
|
||||
*
|
||||
* @property $id 编号
|
||||
* @property $group_id 组id
|
||||
* @property $key 配置键名
|
||||
* @property $value 配置值
|
||||
* @property $name 配置名称
|
||||
* @property $input_type 数据输入类型
|
||||
* @property $config_select_data 配置选项数据
|
||||
* @property $sort 排序
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建人
|
||||
* @property $updated_by 更新人
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemConfig extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_config';
|
||||
|
||||
public function getConfigSelectDataAttr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 参数配置分组模型
|
||||
*
|
||||
* sa_system_config_group 参数配置分组表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $name 字典名称
|
||||
* @property $code 字典标示
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建人
|
||||
* @property $updated_by 更新人
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemConfigGroup extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_config_group';
|
||||
|
||||
/**
|
||||
* 关联配置列表
|
||||
*/
|
||||
public function configs()
|
||||
{
|
||||
return $this->hasMany(SystemConfig::class, 'group_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 名称搜索
|
||||
*/
|
||||
public function searchNameAttr($query, $value)
|
||||
{
|
||||
return $query->where('name', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码搜索
|
||||
*/
|
||||
public function searchCodeAttr($query, $value)
|
||||
{
|
||||
return $query->where('code', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
}
|
||||
62
server/plugin/saiadmin/app/model/system/SystemDept.php
Normal file
62
server/plugin/saiadmin/app/model/system/SystemDept.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 部门模型
|
||||
*
|
||||
* sa_system_dept 部门表
|
||||
*
|
||||
* @property $id 编号
|
||||
* @property $parent_id 父级ID,0为根节点
|
||||
* @property $name 部门名称
|
||||
* @property $code 部门编码
|
||||
* @property $leader_id 部门负责人ID
|
||||
* @property $level 祖级列表,格式: 0,1,5,
|
||||
* @property $sort 排序,数字越小越靠前
|
||||
* @property $status 状态: 1启用, 0禁用
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemDept extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_dept';
|
||||
|
||||
/**
|
||||
* 权限范围
|
||||
*/
|
||||
public function scopeAuth($query, $value)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$deptIds = [$value['id']];
|
||||
$deptLevel = $value['level'] . $value['id'] . ',';
|
||||
$ids = static::whereLike('level', $deptLevel . '%')->column('id');
|
||||
$deptIds = array_merge($deptIds, $ids);
|
||||
$query->whereIn('id', $deptIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门领导
|
||||
*/
|
||||
public function leader()
|
||||
{
|
||||
return $this->hasOne(SystemUser::class, 'id', 'leader_id');
|
||||
}
|
||||
|
||||
}
|
||||
47
server/plugin/saiadmin/app/model/system/SystemDictData.php
Normal file
47
server/plugin/saiadmin/app/model/system/SystemDictData.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 字典数据模型
|
||||
*
|
||||
* sa_system_dict_data 字典数据表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $type_id 字典类型ID
|
||||
* @property $label 字典标签
|
||||
* @property $value 字典值
|
||||
* @property $color 字典颜色
|
||||
* @property $code 字典标示
|
||||
* @property $sort 排序
|
||||
* @property $status 状态
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemDictData extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_dict_data';
|
||||
|
||||
/**
|
||||
* 关键字搜索
|
||||
*/
|
||||
public function searchKeywordsAttr($query, $value)
|
||||
{
|
||||
$query->where('label|code', 'LIKE', "%$value%");
|
||||
}
|
||||
}
|
||||
44
server/plugin/saiadmin/app/model/system/SystemDictType.php
Normal file
44
server/plugin/saiadmin/app/model/system/SystemDictType.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 字典类型模型
|
||||
*
|
||||
* sa_system_dict_type 字典类型表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $name 字典名称
|
||||
* @property $code 字典标示
|
||||
* @property $status 状态
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemDictType extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_dict_type';
|
||||
|
||||
/**
|
||||
* 关联字典数据
|
||||
*/
|
||||
public function dicts()
|
||||
{
|
||||
return $this->hasMany(SystemDictData::class, 'type_id', 'id');
|
||||
}
|
||||
|
||||
}
|
||||
50
server/plugin/saiadmin/app/model/system/SystemLoginLog.php
Normal file
50
server/plugin/saiadmin/app/model/system/SystemLoginLog.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 登录日志模型
|
||||
*
|
||||
* sa_system_login_log 登录日志表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $username 用户名
|
||||
* @property $ip 登录IP地址
|
||||
* @property $ip_location IP所属地
|
||||
* @property $os 操作系统
|
||||
* @property $browser 浏览器
|
||||
* @property $status 登录状态
|
||||
* @property $message 提示消息
|
||||
* @property $login_time 登录时间
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 更新时间
|
||||
*/
|
||||
class SystemLoginLog extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_login_log';
|
||||
|
||||
/**
|
||||
* 时间范围搜索
|
||||
*/
|
||||
public function searchLoginTimeAttr($query, $value)
|
||||
{
|
||||
$query->whereTime('login_time', 'between', $value);
|
||||
}
|
||||
|
||||
}
|
||||
52
server/plugin/saiadmin/app/model/system/SystemMail.php
Normal file
52
server/plugin/saiadmin/app/model/system/SystemMail.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 邮件记录模型
|
||||
*
|
||||
* sa_system_mail 邮件记录
|
||||
*
|
||||
* @property $id 编号
|
||||
* @property $gateway 网关
|
||||
* @property $from 发送人
|
||||
* @property $email 接收人
|
||||
* @property $code 验证码
|
||||
* @property $content 邮箱内容
|
||||
* @property $status 发送状态
|
||||
* @property $response 返回结果
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemMail extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_mail';
|
||||
|
||||
/**
|
||||
* 发送人搜索
|
||||
*/
|
||||
public function searchFromAttr($query, $value)
|
||||
{
|
||||
$query->where('from', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收人搜索
|
||||
*/
|
||||
public function searchEmailAttr($query, $value)
|
||||
{
|
||||
$query->where('email', 'like', '%' . $value . '%');
|
||||
}
|
||||
}
|
||||
86
server/plugin/saiadmin/app/model/system/SystemMenu.php
Normal file
86
server/plugin/saiadmin/app/model/system/SystemMenu.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 菜单模型
|
||||
*
|
||||
* sa_system_menu 菜单权限表
|
||||
*
|
||||
* @property $id
|
||||
* @property $parent_id 父级ID
|
||||
* @property $name 菜单名称
|
||||
* @property $code 组件名称
|
||||
* @property $slug 权限标识,如 user:list, user:add
|
||||
* @property $type 类型: 1目录, 2菜单, 3按钮/API
|
||||
* @property $path 路由地址或API路径
|
||||
* @property $component 前端组件路径,如 layout/User
|
||||
* @property $method 请求方式
|
||||
* @property $icon 图标
|
||||
* @property $sort 排序
|
||||
* @property $link_url 外部链接
|
||||
* @property $is_iframe 是否iframe
|
||||
* @property $is_keep_alive 是否缓存
|
||||
* @property $is_hidden 是否隐藏
|
||||
* @property $is_fixed_tab 是否固定标签页
|
||||
* @property $is_full_page 是否全屏
|
||||
* @property $generate_id 生成id
|
||||
* @property $generate_key 生成key
|
||||
* @property $status 状态
|
||||
* @property $remark
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemMenu extends BaseModel
|
||||
{
|
||||
// 完整数据库表名称
|
||||
protected $table = 'sa_system_menu';
|
||||
// 主键
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* Id搜索
|
||||
*/
|
||||
public function searchIdAttr($query, $value)
|
||||
{
|
||||
$query->whereIn('id', $value);
|
||||
}
|
||||
|
||||
public function searchNameAttr($query, $value)
|
||||
{
|
||||
$query->where('name', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
public function searchPathAttr($query, $value)
|
||||
{
|
||||
$query->where('path', 'like', '%' . $value . '%');
|
||||
}
|
||||
|
||||
public function searchMenuAttr($query, $value)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$query->whereIn('type', [1, 2]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type搜索
|
||||
*/
|
||||
public function searchTypeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$query->whereIn('type', $value);
|
||||
} else {
|
||||
$query->where('type', $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
42
server/plugin/saiadmin/app/model/system/SystemOperLog.php
Normal file
42
server/plugin/saiadmin/app/model/system/SystemOperLog.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 操作日志模型
|
||||
*
|
||||
* sa_system_oper_log 操作日志表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $username 用户名
|
||||
* @property $app 应用名称
|
||||
* @property $method 请求方式
|
||||
* @property $router 请求路由
|
||||
* @property $service_name 业务名称
|
||||
* @property $ip 请求IP地址
|
||||
* @property $ip_location IP所属地
|
||||
* @property $request_data 请求数据
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 更新时间
|
||||
*/
|
||||
class SystemOperLog extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_oper_log';
|
||||
|
||||
}
|
||||
37
server/plugin/saiadmin/app/model/system/SystemPost.php
Normal file
37
server/plugin/saiadmin/app/model/system/SystemPost.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 岗位模型
|
||||
*
|
||||
* sa_system_post 岗位信息表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $name 岗位名称
|
||||
* @property $code 岗位代码
|
||||
* @property $sort 排序
|
||||
* @property $status 状态
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemPost extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_post';
|
||||
|
||||
}
|
||||
78
server/plugin/saiadmin/app/model/system/SystemRole.php
Normal file
78
server/plugin/saiadmin/app/model/system/SystemRole.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 角色模型
|
||||
*
|
||||
* sa_system_role 角色表
|
||||
*
|
||||
* @property $id
|
||||
* @property $name 角色名称
|
||||
* @property $code 角色标识,如: hr_manager
|
||||
* @property $level 角色级别:用于行政控制,不可操作级别大于自己的角色
|
||||
* @property $data_scope 数据范围: 1全部, 2本部门及下属, 3本部门, 4仅本人, 5自定义
|
||||
* @property $remark 备注
|
||||
* @property $sort
|
||||
* @property $status 状态: 1启用, 0禁用
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemRole extends BaseModel
|
||||
{
|
||||
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 数据表完整名称
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'sa_system_role';
|
||||
|
||||
/**
|
||||
* 权限范围
|
||||
*/
|
||||
public function scopeAuth($query, $value)
|
||||
{
|
||||
$id = $value['id'];
|
||||
$roles = $value['roles'];
|
||||
if ($id > 1) {
|
||||
$ids = [];
|
||||
foreach ($roles as $item) {
|
||||
$ids[] = $item['id'];
|
||||
$temp = static::whereRaw('FIND_IN_SET("' . $item['id'] . '", level) > 0')->column('id');
|
||||
$ids = array_merge($ids, $temp);
|
||||
}
|
||||
$query->where('id', 'in', array_unique($ids));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过中间表获取菜单
|
||||
*/
|
||||
public function menus()
|
||||
{
|
||||
return $this->belongsToMany(SystemMenu::class, SystemRoleMenu::class, 'menu_id', 'role_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过中间表获取部门
|
||||
*/
|
||||
public function depts()
|
||||
{
|
||||
return $this->belongsToMany(SystemDept::class, SystemRoleDept::class, 'dept_id', 'role_id');
|
||||
}
|
||||
|
||||
}
|
||||
25
server/plugin/saiadmin/app/model/system/SystemRoleDept.php
Normal file
25
server/plugin/saiadmin/app/model/system/SystemRoleDept.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use think\model\Pivot;
|
||||
|
||||
/**
|
||||
* 角色部门关联模型
|
||||
*
|
||||
* sa_system_role_dept 角色-自定义数据权限关联
|
||||
*
|
||||
* @property $id
|
||||
* @property $role_id
|
||||
* @property $dept_id
|
||||
*/
|
||||
class SystemRoleDept extends Pivot
|
||||
{
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_role_dept';
|
||||
}
|
||||
25
server/plugin/saiadmin/app/model/system/SystemRoleMenu.php
Normal file
25
server/plugin/saiadmin/app/model/system/SystemRoleMenu.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use think\model\Pivot;
|
||||
|
||||
/**
|
||||
* 角色菜单关联模型
|
||||
*
|
||||
* sa_system_role_menu 角色权限关联
|
||||
*
|
||||
* @property $id
|
||||
* @property $role_id
|
||||
* @property $menu_id
|
||||
*/
|
||||
class SystemRoleMenu extends Pivot
|
||||
{
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_role_menu';
|
||||
}
|
||||
96
server/plugin/saiadmin/app/model/system/SystemUser.php
Normal file
96
server/plugin/saiadmin/app/model/system/SystemUser.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 用户信息模型
|
||||
*
|
||||
* sa_system_user 用户表
|
||||
*
|
||||
* @property $id
|
||||
* @property $username 登录账号
|
||||
* @property $password 加密密码
|
||||
* @property $realname 真实姓名
|
||||
* @property $gender 性别
|
||||
* @property $avatar 头像
|
||||
* @property $email 邮箱
|
||||
* @property $phone 手机号
|
||||
* @property $signed 个性签名
|
||||
* @property $dashboard 工作台
|
||||
* @property $dept_id 主归属部门
|
||||
* @property $is_super 是否超级管理员: 1是
|
||||
* @property $status 状态: 1启用, 2禁用
|
||||
* @property $remark 备注
|
||||
* @property $login_time 最后登录时间
|
||||
* @property $login_ip 最后登录IP
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class SystemUser extends BaseModel
|
||||
{
|
||||
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 数据表完整名称
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'sa_system_user';
|
||||
|
||||
public function searchKeywordAttr($query, $value)
|
||||
{
|
||||
if ($value) {
|
||||
$query->where('username|realname|phone', 'like', '%' . $value . '%');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限范围 - 过滤部门用户
|
||||
*/
|
||||
public function scopeAuth($query, $value)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$deptIds = [$value['id']];
|
||||
$deptLevel = $value['level'] . $value['id'] . ',';
|
||||
$dept_ids = SystemDept::whereLike('level', $deptLevel . '%')->column('id');
|
||||
$deptIds = array_merge($deptIds, $dept_ids);
|
||||
$query->whereIn('dept_id', $deptIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过中间表关联角色
|
||||
*/
|
||||
public function roles()
|
||||
{
|
||||
return $this->belongsToMany(SystemRole::class, SystemUserRole::class, 'role_id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过中间表关联岗位
|
||||
*/
|
||||
public function posts()
|
||||
{
|
||||
return $this->belongsToMany(SystemPost::class, SystemUserPost::class, 'post_id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过中间表关联部门
|
||||
*/
|
||||
public function depts()
|
||||
{
|
||||
return $this->belongsTo(SystemDept::class, 'dept_id', 'id');
|
||||
}
|
||||
}
|
||||
25
server/plugin/saiadmin/app/model/system/SystemUserPost.php
Normal file
25
server/plugin/saiadmin/app/model/system/SystemUserPost.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use think\model\Pivot;
|
||||
|
||||
/**
|
||||
* 用户岗位关联模型
|
||||
*
|
||||
* sa_system_user_post 用户与岗位关联表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $user_id 用户主键
|
||||
* @property $post_id 岗位主键
|
||||
*/
|
||||
class SystemUserPost extends Pivot
|
||||
{
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_user_post';
|
||||
}
|
||||
35
server/plugin/saiadmin/app/model/system/SystemUserRole.php
Normal file
35
server/plugin/saiadmin/app/model/system/SystemUserRole.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\system;
|
||||
|
||||
use think\model\Pivot;
|
||||
|
||||
/**
|
||||
* 用户角色关联模型
|
||||
*
|
||||
* sa_system_user_role 用户角色关联
|
||||
*
|
||||
* @property $id
|
||||
* @property $user_id
|
||||
* @property $role_id
|
||||
*/
|
||||
class SystemUserRole extends Pivot
|
||||
{
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_system_user_role';
|
||||
|
||||
/**
|
||||
* 获取角色id
|
||||
* @param mixed $user_id
|
||||
* @return array
|
||||
*/
|
||||
public static function getRoleIds($user_id): array
|
||||
{
|
||||
return static::where('user_id', $user_id)->column('role_id');
|
||||
}
|
||||
}
|
||||
40
server/plugin/saiadmin/app/model/tool/Crontab.php
Normal file
40
server/plugin/saiadmin/app/model/tool/Crontab.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\tool;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 定时任务模型
|
||||
*
|
||||
* sa_tool_crontab 定时任务信息表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $name 任务名称
|
||||
* @property $type 任务类型
|
||||
* @property $target 调用任务字符串
|
||||
* @property $parameter 调用任务参数
|
||||
* @property $task_style 执行类型
|
||||
* @property $rule 任务执行表达式
|
||||
* @property $status 状态
|
||||
* @property $remark 备注
|
||||
* @property $created_by 创建者
|
||||
* @property $updated_by 更新者
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class Crontab extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_tool_crontab';
|
||||
|
||||
}
|
||||
36
server/plugin/saiadmin/app/model/tool/CrontabLog.php
Normal file
36
server/plugin/saiadmin/app/model/tool/CrontabLog.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\tool;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 定时任务日志模型
|
||||
*
|
||||
* sa_tool_crontab_log 定时任务执行日志表
|
||||
*
|
||||
* @property $id 主键
|
||||
* @property $crontab_id 任务ID
|
||||
* @property $name 任务名称
|
||||
* @property $target 任务调用目标字符串
|
||||
* @property $parameter 任务调用参数
|
||||
* @property $exception_info 异常信息
|
||||
* @property $status 执行状态
|
||||
* @property $create_time 创建时间
|
||||
* @property $update_time 修改时间
|
||||
*/
|
||||
class CrontabLog extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_tool_crontab_log';
|
||||
|
||||
}
|
||||
29
server/plugin/saiadmin/app/model/tool/GenerateColumns.php
Normal file
29
server/plugin/saiadmin/app/model/tool/GenerateColumns.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\tool;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 代码生成业务字段模型
|
||||
*/
|
||||
class GenerateColumns extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_tool_generate_columns';
|
||||
|
||||
public function getOptionsAttr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
}
|
||||
29
server/plugin/saiadmin/app/model/tool/GenerateTables.php
Normal file
29
server/plugin/saiadmin/app/model/tool/GenerateTables.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\model\tool;
|
||||
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
|
||||
/**
|
||||
* 代码生成业务模型
|
||||
*/
|
||||
class GenerateTables extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
protected $table = 'sa_tool_generate_tables';
|
||||
|
||||
public function getOptionsAttr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 附件分类验证器
|
||||
*/
|
||||
class SystemCategoryValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'category_name' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'category_name' => '分类名称必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [
|
||||
'category_name',
|
||||
],
|
||||
'edit' => [
|
||||
'category_name',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
use plugin\saiadmin\app\model\system\SystemConfigGroup;
|
||||
|
||||
/**
|
||||
* 字典类型验证器
|
||||
*/
|
||||
class SystemConfigGroupValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require|max:16',
|
||||
'code' => 'require|alphaDash|unique:' . SystemConfigGroup::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name.require' => '组名称必须填写',
|
||||
'name.max' => '组名称最多不能超过16个字符',
|
||||
'name.chs' => '组名称必须是中文',
|
||||
'code.require' => '组标识必须填写',
|
||||
'code.alphaDash' => '组标识只能由英文字母组成',
|
||||
'code.unique' => '配置组标识不能重复',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'code',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'code',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 字典类型验证器
|
||||
*/
|
||||
class SystemConfigValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'key' => 'require',
|
||||
'group_id' => 'require',
|
||||
'input_type' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name' => '配置标题必须填写',
|
||||
'key' => '配置标识必须填写',
|
||||
'group_id' => '所属组必须填写',
|
||||
'input_type' => '输入组件必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'key',
|
||||
'group_id',
|
||||
'input_type',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'key',
|
||||
'group_id',
|
||||
'input_type',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saithink [ saithink快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 字典类型验证器
|
||||
*/
|
||||
class SystemCrontabValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'type' => 'require',
|
||||
'rule' => 'require',
|
||||
'target' => 'require',
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name' => '任务名称必须填写',
|
||||
'type' => '任务类型必须填写',
|
||||
'rule' => '任务规则必须填写',
|
||||
'target' => '调用目标必须填写',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'type',
|
||||
'rule',
|
||||
'target',
|
||||
'status',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'type',
|
||||
'rule',
|
||||
'target',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 部门验证器
|
||||
*/
|
||||
class SystemDeptValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name' => '部门名称必须填写',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [
|
||||
'name',
|
||||
'status',
|
||||
],
|
||||
'edit' => [
|
||||
'name',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 字典数据验证器
|
||||
*/
|
||||
class SystemDictDataValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'label' => 'require',
|
||||
'value' => 'require',
|
||||
'status' => 'require',
|
||||
'type_id' => 'require',
|
||||
'code' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'label' => '字典标签必须填写',
|
||||
'value' => '字典键值必须填写',
|
||||
'status' => '状态必须填写',
|
||||
'type_id' => '字典类型必须填写',
|
||||
'code' => '字典标识必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'label',
|
||||
'value',
|
||||
'status',
|
||||
'type_id',
|
||||
],
|
||||
'update' => [
|
||||
'label',
|
||||
'value',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
use plugin\saiadmin\app\model\system\SystemDictType;
|
||||
|
||||
/**
|
||||
* 字典类型验证器
|
||||
*/
|
||||
class SystemDictTypeValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require|max:16',
|
||||
'code' => 'require|alphaDash|unique:' . SystemDictType::class,
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name.require' => '字典名称必须填写',
|
||||
'name.max' => '字典名称最多不能超过16个字符',
|
||||
'code.require' => '字典标识必须填写',
|
||||
'code.alphaDash' => '字典标识只能由英文字母组成',
|
||||
'code.unique' => '字典标识已存在',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 邮件验证器
|
||||
*/
|
||||
class SystemMailValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'gateway' => 'require',
|
||||
'from' => 'require',
|
||||
'email' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'gateway' => '网关必须填写',
|
||||
'from' => '发件人必须填写',
|
||||
'email' => '接收人必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'gateway',
|
||||
'from',
|
||||
'email',
|
||||
],
|
||||
'update' => [
|
||||
'gateway',
|
||||
'from',
|
||||
'email',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 菜单验证器
|
||||
*/
|
||||
class SystemMenuValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require|max:16',
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name.require' => '菜单名称必须填写',
|
||||
'name.max' => '菜单名称最多不能超过16个字符',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'status',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 系统公告验证器
|
||||
*/
|
||||
class SystemNoticeValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'title' => 'require|min:4',
|
||||
'content' => 'require',
|
||||
'type' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'title.require' => '公告标题必须填写',
|
||||
'title.min' => '公告标题必须大于4个字符',
|
||||
'content' => '公告内容必须填写',
|
||||
'type' => '公告类型必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'title',
|
||||
'content',
|
||||
'type',
|
||||
],
|
||||
'update' => [
|
||||
'title',
|
||||
'content',
|
||||
'type',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 用户角色验证器
|
||||
*/
|
||||
class SystemPostValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'code' => 'require',
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name' => '岗位名称必须填写',
|
||||
'code' => '岗位标识必须填写',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
use plugin\saiadmin\app\model\system\SystemRole;
|
||||
|
||||
/**
|
||||
* 用户角色验证器
|
||||
*/
|
||||
class SystemRoleValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require|max:16',
|
||||
'code' => 'require|alphaDash|unique:' . SystemRole::class,
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name.require' => '角色名称必须填写',
|
||||
'name.max' => '角色名称最多不能超过16个字符',
|
||||
'code.require' => '角色标识必须填写',
|
||||
'code.alphaDash' => '角色标识只能由英文字母组成',
|
||||
'code.unique' => '角色标识不能重复',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
'edit' => [
|
||||
'name',
|
||||
'code',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\system;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
use plugin\saiadmin\app\model\system\SystemUser;
|
||||
|
||||
/**
|
||||
* 用户信息验证器
|
||||
*/
|
||||
class SystemUserValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'username' => 'require|max:16|unique:' . SystemUser::class,
|
||||
'password' => 'require|min:6|max:16',
|
||||
'role_ids' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'username.require' => '用户名必须填写',
|
||||
'username.max' => '用户名最多不能超过16个字符',
|
||||
'username.unique' => '用户名不能重复',
|
||||
'password.require' => '密码必须填写',
|
||||
'password.min' => '密码最少为6位',
|
||||
'password.max' => '密码长度不能超过16位',
|
||||
'role_ids' => '角色必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'username',
|
||||
'password',
|
||||
'role_ids',
|
||||
],
|
||||
'update' => [
|
||||
'username',
|
||||
'role_ids',
|
||||
],
|
||||
];
|
||||
}
|
||||
54
server/plugin/saiadmin/app/validate/tool/CrontabValidate.php
Normal file
54
server/plugin/saiadmin/app/validate/tool/CrontabValidate.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\tool;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 字典类型验证器
|
||||
*/
|
||||
class CrontabValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'name' => 'require',
|
||||
'type' => 'require',
|
||||
'target' => 'require',
|
||||
'status' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'name' => '任务名称必须填写',
|
||||
'type' => '任务类型必须填写',
|
||||
'target' => '调用目标必须填写',
|
||||
'status' => '状态必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'name',
|
||||
'type',
|
||||
'target',
|
||||
'status',
|
||||
],
|
||||
'update' => [
|
||||
'name',
|
||||
'type',
|
||||
'target',
|
||||
'status',
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\validate\tool;
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* 用户角色验证器
|
||||
*/
|
||||
class GenerateTablesValidate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
'table_name' => 'require',
|
||||
'table_comment' => 'require',
|
||||
'class_name' => 'require|alphaDash',
|
||||
'business_name' => 'require|alphaDash',
|
||||
'template' => 'require',
|
||||
'namespace' => 'require',
|
||||
'menu_name' => 'require',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
'table_name' => '表名称必须填写',
|
||||
'table_comment' => '表描述必须填写',
|
||||
'class_name.require' => '实体类必须填写',
|
||||
'class_name.alphaDash' => '实体类必须是英文',
|
||||
'business_name.require' => '实体别名必须填写',
|
||||
'business_name.alphaDash' => '实体别名必须是英文',
|
||||
'template' => '模板必须填写',
|
||||
'namespace' => '命名空间必须填写',
|
||||
'menu_name' => '菜单名称必须填写',
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
'table_name',
|
||||
'table_comment',
|
||||
'class_name',
|
||||
'business_name',
|
||||
'template',
|
||||
'namespace',
|
||||
'menu_name',
|
||||
],
|
||||
'update' => [
|
||||
'table_name',
|
||||
'table_comment',
|
||||
'class_name',
|
||||
'business_name',
|
||||
'template',
|
||||
'namespace',
|
||||
'menu_name',
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
253
server/plugin/saiadmin/app/view/install/error.html
Normal file
253
server/plugin/saiadmin/app/view/install/error.html
Normal file
@@ -0,0 +1,253 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>安装错误</title>
|
||||
<link href="/app/saiadmin/assets/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-gradient: linear-gradient(135deg, #7166F0 0%, #8F85F3 100%);
|
||||
--error-gradient: linear-gradient(135deg, #7166F0 0%, #8F85F3 100%);
|
||||
--bg-gradient: linear-gradient(135deg, #F8FAFC 0%, #F1F5F9 100%);
|
||||
--card-bg: rgba(255, 255, 255, 0.95);
|
||||
--text-primary: #1E293B;
|
||||
--text-secondary: #475569;
|
||||
--border-color: rgba(113, 102, 240, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-gradient);
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
text-align: left;
|
||||
padding: 1rem 2rem;
|
||||
background: var(--card-bg);
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
animation: pulse 2s infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.logo-icon img:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.logo-section h1 {
|
||||
font-size: 1.4rem;
|
||||
background: var(--primary-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
.install-wrapper {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
animation: fadeIn 0.8s ease-out;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
background: var(--card-bg);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-color);
|
||||
transform: translateY(0);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.error-container:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
animation: shake 0.5s ease-in-out;
|
||||
color: #7166F0;
|
||||
}
|
||||
|
||||
.error-icon svg {
|
||||
filter: drop-shadow(0 5px 15px rgba(113, 102, 240, 0.2));
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
background: var(--error-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 2.5rem;
|
||||
line-height: 1.6;
|
||||
background: rgba(113, 102, 240, 0.05);
|
||||
padding: 1rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(113, 102, 240, 0.1);
|
||||
}
|
||||
|
||||
.btn-custom {
|
||||
padding: 1rem 2.5rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 50px;
|
||||
background: var(--primary-gradient);
|
||||
border: none;
|
||||
color: white;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 5px 15px rgba(113, 102, 240, 0.3);
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
letter-spacing: 0.5px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-custom:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(113, 102, 240, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-custom:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-custom::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(45deg, rgba(255,255,255,0.1), rgba(255,255,255,0));
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.6s ease;
|
||||
}
|
||||
|
||||
.btn-custom:hover::after {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-5px); }
|
||||
75% { transform: translateX(5px); }
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.logo-section {
|
||||
padding: 0.8rem 1rem;
|
||||
}
|
||||
.logo-section h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.logo-icon img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.main-content {
|
||||
margin-top: 60px;
|
||||
padding: 1rem;
|
||||
}
|
||||
.error-container {
|
||||
padding: 2rem;
|
||||
}
|
||||
.error-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.btn-custom {
|
||||
padding: 0.8rem 2rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Logo Section -->
|
||||
<div class="logo-section">
|
||||
<div class="logo-icon">
|
||||
<img src="https://saithink.top/images/logo.png">
|
||||
</div>
|
||||
<h1>【{{app}}-{{version}}】安装配置向导</h1>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="install-wrapper">
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-exclamation-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="error-title">安装失败</h2>
|
||||
<p class="error-message">错误提示:{{error}}</p>
|
||||
<a href="/core/install" class="btn btn-custom">重新安装</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1023
server/plugin/saiadmin/app/view/install/index.html
Normal file
1023
server/plugin/saiadmin/app/view/install/index.html
Normal file
@@ -0,0 +1,1023 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>应用安装向导</title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="/app/saiadmin/assets/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<style>
|
||||
:root {
|
||||
--primary: #7166F0;
|
||||
--primary-light: #8B7FF7;
|
||||
--primary-dark: #5B4FD9;
|
||||
--success: #10B981;
|
||||
--warning: #F59E0B;
|
||||
--dark: #1F2937;
|
||||
--light: #F8FAFC;
|
||||
--border-radius: 10px;
|
||||
--box-shadow: 0 10px 30px rgba(113, 102, 240, 0.1);
|
||||
--transition: all 0.3s ease;
|
||||
--primary-gradient: linear-gradient(135deg, #7166F0 0%, #8B7FF7 100%);
|
||||
--card-bg: rgba(255, 255, 255, 0.95);
|
||||
--border-color: rgba(113, 102, 240, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #F5F3FF 0%, #EDE9FE 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 30px 15px;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
.install-wrapper {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
text-align: left;
|
||||
padding: 1rem 2rem;
|
||||
background: var(--card-bg);
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
animation: pulse 2s infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.logo-icon img:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.logo-section h1 {
|
||||
font-size: 1.4rem;
|
||||
background: var(--primary-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
.install-container {
|
||||
background: var(--card-bg);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
padding: 25px 30px 0;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 50px;
|
||||
background-color: #F5F3FF;
|
||||
margin-bottom: 25px;
|
||||
overflow: visible;
|
||||
box-shadow: inset 0 1px 3px rgba(113, 102, 240, 0.1);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
background: var(--primary-gradient);
|
||||
border-radius: 50px;
|
||||
position: relative;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.progress-bar::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
border: 3px solid var(--primary);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.steps-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
border: 2px solid #F5F3FF;
|
||||
color: #94A3B8;
|
||||
transition: var(--transition);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-icon i {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #94A3B8;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.step-item.active .step-icon,
|
||||
.step-item.completed .step-icon {
|
||||
border-color: var(--primary);
|
||||
color: white;
|
||||
background: var(--primary-gradient);
|
||||
}
|
||||
|
||||
.step-item.active .step-text,
|
||||
.step-item.completed .step-text {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.step-item.completed .step-icon::after {
|
||||
content: '\f00c';
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-weight: 900;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
padding: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
display: none;
|
||||
animation: fadeIn 0.5s ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-content.active {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 25px;
|
||||
color: var(--primary);
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--dark);
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.form-control {
|
||||
height: 50px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 2px solid #F5F3FF;
|
||||
padding: 10px 15px 10px 50px;
|
||||
font-size: 15px;
|
||||
transition: var(--transition);
|
||||
background-color: #F8FAFC;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary);
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 4px rgba(113, 102, 240, 0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control::placeholder {
|
||||
color: #94A3B8;
|
||||
}
|
||||
|
||||
/* Custom Input Group Styles */
|
||||
.custom-input-group {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.custom-input-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
color: #94A3B8;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.custom-input-group:hover .custom-input-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.custom-input-group .form-control:focus + .custom-input-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* 添加输入框动画效果 */
|
||||
.form-control:focus + .custom-input-icon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 添加输入框hover效果 */
|
||||
.form-control:hover {
|
||||
border-color: #ced4da;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* 添加密码输入框特殊样式 */
|
||||
input[type="password"].form-control {
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* 添加数字输入框特殊样式 */
|
||||
input[type="number"].form-control {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
input[type="number"].form-control::-webkit-outer-spin-button,
|
||||
input[type="number"].form-control::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 添加输入框聚焦时的标签动画 */
|
||||
.form-group:focus-within .form-label {
|
||||
color: var(--primary);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 添加输入框验证状态样式 */
|
||||
.form-control.is-valid {
|
||||
border-color: var(--success);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.form-control.is-invalid {
|
||||
border-color: var(--warning);
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* 添加输入框加载状态样式 */
|
||||
.form-control.is-loading {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%234361ee' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-6.219-8.56'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 10px center;
|
||||
background-size: 20px;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 50px;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 0 25px;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
transition: var(--transition);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-gradient);
|
||||
border: none;
|
||||
color: white;
|
||||
transition: var(--transition);
|
||||
box-shadow: 0 5px 15px rgba(113, 102, 240, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(113, 102, 240, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.log-container {
|
||||
background: #F8FAFC;
|
||||
border-radius: var(--border-radius);
|
||||
border: 2px solid #F5F3FF;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.log-item {
|
||||
padding: 15px;
|
||||
border-radius: var(--border-radius);
|
||||
background: white;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 2px 5px rgba(113, 102, 240, 0.05);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
animation: slideIn 0.3s ease;
|
||||
border: 1px solid #F5F3FF;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.log-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(113, 102, 240, 0.1);
|
||||
}
|
||||
|
||||
.log-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: rgba(113, 102, 240, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.log-item:hover .log-icon {
|
||||
transform: scale(1.1);
|
||||
background: rgba(113, 102, 240, 0.2);
|
||||
}
|
||||
|
||||
.log-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.log-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--dark);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
font-size: 12px;
|
||||
color: #94A3B8;
|
||||
}
|
||||
|
||||
.completed-message {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
animation: fadeIn 0.5s ease;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.completed-icon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: rgba(113, 102, 240, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 30px;
|
||||
color: var(--primary);
|
||||
font-size: 40px;
|
||||
animation: scaleIn 0.5s ease;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.completed-icon:hover {
|
||||
transform: scale(1.1);
|
||||
background: rgba(113, 102, 240, 0.2);
|
||||
}
|
||||
|
||||
.completed-title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
color: var(--primary);
|
||||
background: var(--primary-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.completed-subtitle {
|
||||
font-size: 16px;
|
||||
color: #6c757d;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
max-width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
padding: 12px 30px;
|
||||
font-size: 16px;
|
||||
border-radius: 50px;
|
||||
background: var(--primary-gradient);
|
||||
border: none;
|
||||
color: white;
|
||||
transition: var(--transition);
|
||||
box-shadow: 0 5px 15px rgba(113, 102, 240, 0.3);
|
||||
}
|
||||
|
||||
.action-buttons .btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(113, 102, 240, 0.4);
|
||||
}
|
||||
|
||||
.action-buttons .btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from { transform: scale(0); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { transform: translateY(10px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.content-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 45px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.custom-input-icon {
|
||||
height: 45px;
|
||||
width: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.steps-container {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.log-container {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.completed-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.completed-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 2: Installation Process Styles */
|
||||
#step2 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#step2 .progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#step2 .log-container {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Step 3: Installation Complete Styles */
|
||||
#step3 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#step3 .completed-message {
|
||||
width: 100%;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
#step3 .completed-subtitle {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
#step3 .action-buttons {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 确保所有步骤内容区域宽度一致 */
|
||||
.row {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.col-md-6 {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.col-md-6 {
|
||||
width: 50%;
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="install-wrapper">
|
||||
<!-- Logo Section -->
|
||||
<div class="logo-section">
|
||||
<div class="logo-icon">
|
||||
<img src="https://saithink.top/images/logo.png" alt="Logo">
|
||||
</div>
|
||||
<h1>【{{app}}-{{version}}】安装配置向导</h1>
|
||||
</div>
|
||||
|
||||
<!-- Main Install Container -->
|
||||
<div class="main-content">
|
||||
<div class="install-container">
|
||||
<!-- Progress Section -->
|
||||
<div class="progress-container">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="steps-container">
|
||||
<div class="step-item active" data-step="1">
|
||||
<div class="step-icon">
|
||||
<i class="fas fa-database"></i>
|
||||
</div>
|
||||
<div class="step-text">数据库配置</div>
|
||||
</div>
|
||||
<div class="step-item" data-step="2">
|
||||
<div class="step-icon">
|
||||
<i class="fas fa-cogs"></i>
|
||||
</div>
|
||||
<div class="step-text">执行安装</div>
|
||||
</div>
|
||||
<div class="step-item" data-step="3">
|
||||
<div class="step-icon">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
<div class="step-text">安装完成</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Section -->
|
||||
<div class="content-section">
|
||||
<!-- Step 1: Database Config -->
|
||||
<div class="step-content active" id="step1">
|
||||
<h2 class="step-title">数据库配置</h2>
|
||||
<form id="dbConfigForm">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">数据库数据</label>
|
||||
<div class="radio-group" style="display: flex; gap: 20px;">
|
||||
<label class="radio-label" style="display: flex; align-items: center; cursor: pointer; padding: 10px 15px; border: 2px solid #F5F3FF; border-radius: 10px; background: #F8FAFC; transition: all 0.3s ease;">
|
||||
<input type="radio" name="dataType" id="dataTypeDemo" value="demo" checked style="width: 18px; height: 18px; margin-right: 10px; accent-color: #7166f0;">
|
||||
<span style="display: flex; flex-direction: column;">
|
||||
<strong style="color: #1e293b; font-size: 14px;">附带演示数据</strong>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio-label" style="display: flex; align-items: center; cursor: pointer; padding: 10px 15px; border: 2px solid #F5F3FF; border-radius: 10px; background: #F8FAFC; transition: all 0.3s ease;">
|
||||
<input type="radio" name="dataType" id="dataTypePure" value="pure" style="width: 18px; height: 18px; margin-right: 10px; accent-color: #7166f0;">
|
||||
<span style="display: flex; flex-direction: column;">
|
||||
<strong style="color: #1e293b; font-size: 14px;">纯净数据</strong>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dbHost" class="form-label">数据库主机</label>
|
||||
<div class="custom-input-group">
|
||||
<input type="text" class="form-control" id="dbHost" value="127.0.0.1" required>
|
||||
<div class="custom-input-icon">
|
||||
<i class="fas fa-server"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dbPort" class="form-label">端口</label>
|
||||
<div class="custom-input-group">
|
||||
<input type="number" class="form-control" id="dbPort" value="3306" required>
|
||||
<div class="custom-input-icon">
|
||||
<i class="fas fa-plug"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dbName" class="form-label">数据库名</label>
|
||||
<div class="custom-input-group">
|
||||
<input type="text" class="form-control" id="dbName" required>
|
||||
<div class="custom-input-icon">
|
||||
<i class="fas fa-database"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dbUser" class="form-label">用户名</label>
|
||||
<div class="custom-input-group">
|
||||
<input type="text" class="form-control" id="dbUser" required>
|
||||
<div class="custom-input-icon">
|
||||
<i class="fas fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="dbPassword" class="form-label">密码</label>
|
||||
<div class="custom-input-group">
|
||||
<input type="password" class="form-control" id="dbPassword" required>
|
||||
<div class="custom-input-icon">
|
||||
<i class="fas fa-key"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end mt-4">
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(1)">
|
||||
<span>下一步</span>
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Installation Process -->
|
||||
<div class="step-content" id="step2">
|
||||
<h2 class="step-title">执行安装</h2>
|
||||
<div class="progress mb-4">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="log-container" id="installLog">
|
||||
<div class="log-item">
|
||||
<div class="log-icon">
|
||||
<i class="fas fa-info"></i>
|
||||
</div>
|
||||
<div class="log-content">
|
||||
<div class="log-text">准备开始安装...</div>
|
||||
<div class="log-time">刚刚</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Installation Complete -->
|
||||
<div class="step-content" id="step3">
|
||||
<div class="completed-message">
|
||||
<div class="completed-icon">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
<h2 class="completed-title">安装成功</h2>
|
||||
<p class="completed-subtitle">恭喜!已成功安装系统,请重启webman,启动前端项目进行访问。</p>
|
||||
<div class="action-buttons">
|
||||
<a href="http://localhost:3006" class="btn btn-primary">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>进入管理后台</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="/app/saiadmin/assets/jquery.min.js"></script>
|
||||
<script>
|
||||
|
||||
// 显示通知
|
||||
function showNotification(type, message) {
|
||||
const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
|
||||
const color = type === 'success' ? '#4cc9f0' : '#f72585';
|
||||
|
||||
const notification = $(`
|
||||
<div class="notification" style="
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
z-index: 1000;
|
||||
transform: translateX(120%);
|
||||
transition: transform 0.3s ease;
|
||||
">
|
||||
<div style="
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: ${color};
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
">
|
||||
<i class="fas fa-${icon}"></i>
|
||||
</div>
|
||||
<div>${message}</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
$('body').append(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.css('transform', 'translateX(0)');
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.css('transform', 'translateX(120%)');
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 切换步骤
|
||||
function nextStep(currentStep) {
|
||||
if (currentStep === 1) {
|
||||
// 验证数据库配置
|
||||
if (!validateDbConfig()) {
|
||||
return;
|
||||
}
|
||||
// 开始安装过程
|
||||
startInstallation();
|
||||
} else {
|
||||
// 隐藏当前步骤,显示下一步
|
||||
$('.step-content').removeClass('active');
|
||||
$(`#step${currentStep + 1}`).addClass('active');
|
||||
|
||||
// 更新步骤指示器
|
||||
$('.step-item').removeClass('active');
|
||||
$(`.step-item[data-step="${currentStep + 1}"]`).addClass('active');
|
||||
|
||||
// 将之前的步骤标记为已完成
|
||||
for (let i = 1; i <= currentStep; i++) {
|
||||
$(`.step-item[data-step="${i}"]`).addClass('completed');
|
||||
}
|
||||
|
||||
// 更新进度条
|
||||
const progress = ((currentStep + 1) / 3) * 100;
|
||||
$('.progress-bar').css('width', progress + '%');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 验证数据库配置
|
||||
function validateDbConfig() {
|
||||
const required = ['dbHost', 'dbName', 'dbUser', 'dbPassword'];
|
||||
for (const field of required) {
|
||||
if (!$(`#${field}`).val()) {
|
||||
showNotification('error', '请填写所有必填字段');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 开始安装过程
|
||||
function startInstallation() {
|
||||
const dbConfig = {
|
||||
host: $('#dbHost').val(),
|
||||
port: $('#dbPort').val(),
|
||||
database: $('#dbName').val(),
|
||||
username: $('#dbUser').val(),
|
||||
password: $('#dbPassword').val(),
|
||||
prefix: $('#dbPrefix').val(),
|
||||
dataType: $('input[name="dataType"]:checked').val()
|
||||
};
|
||||
|
||||
// 模拟安装过程
|
||||
const steps = [
|
||||
{ icon: 'database', text: '正在创建数据库表...' },
|
||||
{ icon: 'file-import', text: '正在导入基础数据...' },
|
||||
{ icon: 'cogs', text: '正在配置系统参数...' },
|
||||
{ icon: 'tachometer-alt', text: '正在优化系统性能...' }
|
||||
];
|
||||
|
||||
let currentStep = 0;
|
||||
const installLog = $('#installLog');
|
||||
const progressBar = $('.progress-bar');
|
||||
|
||||
function runStep() {
|
||||
if (currentStep < steps.length) {
|
||||
// 获取当前时间
|
||||
const now = new Date();
|
||||
const timeString = now.getHours().toString().padStart(2, '0') + ':' +
|
||||
now.getMinutes().toString().padStart(2, '0') + ':' +
|
||||
now.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
// 添加日志
|
||||
const step = steps[currentStep];
|
||||
installLog.append(`
|
||||
<div class="log-item">
|
||||
<div class="log-icon">
|
||||
<i class="fas fa-${step.icon}"></i>
|
||||
</div>
|
||||
<div class="log-content">
|
||||
<div class="log-text">${step.text}</div>
|
||||
<div class="log-time">${timeString}</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
installLog.scrollTop(installLog[0].scrollHeight);
|
||||
|
||||
// 更新进度
|
||||
const progress = ((currentStep + 1) / steps.length) * 100;
|
||||
progressBar.css('width', progress + '%');
|
||||
|
||||
// 模拟异步操作
|
||||
setTimeout(() => {
|
||||
currentStep++;
|
||||
runStep();
|
||||
}, 1000);
|
||||
} else {
|
||||
// 安装完成
|
||||
setTimeout(() => {
|
||||
nextStep(2);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/core/install/install',
|
||||
method: 'POST',
|
||||
data: dbConfig,
|
||||
success: function(response) {
|
||||
if (response.code == 200) {
|
||||
|
||||
// 隐藏当前步骤,显示下一步
|
||||
$('.step-content').removeClass('active');
|
||||
$(`#step2`).addClass('active');
|
||||
|
||||
// 更新步骤指示器
|
||||
$('.step-item').removeClass('active');
|
||||
$(`.step-item[data-step=2]`).addClass('active');
|
||||
|
||||
runStep()
|
||||
} else {
|
||||
showNotification('error', '数据库连接失败:' + response.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotification('error', '请求失败,请检查网络连接');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
109
server/plugin/saiadmin/basic/AbstractLogic.php
Normal file
109
server/plugin/saiadmin/basic/AbstractLogic.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic;
|
||||
|
||||
use plugin\saiadmin\basic\contracts\LogicInterface;
|
||||
|
||||
/**
|
||||
* 抽象逻辑层基类
|
||||
* 定义通用属性和方法签名,具体实现由各 ORM 驱动完成
|
||||
*/
|
||||
abstract class AbstractLogic implements LogicInterface
|
||||
{
|
||||
/**
|
||||
* 模型注入
|
||||
* @var object
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* 管理员信息
|
||||
* @var array
|
||||
*/
|
||||
protected array $adminInfo;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
* @var string
|
||||
*/
|
||||
protected string $orderField = '';
|
||||
|
||||
/**
|
||||
* 排序方式
|
||||
* @var string
|
||||
*/
|
||||
protected string $orderType = 'ASC';
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param $user
|
||||
* @return void
|
||||
*/
|
||||
public function init($user): void
|
||||
{
|
||||
$this->adminInfo = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置排序字段
|
||||
* @param string $field
|
||||
* @return static
|
||||
*/
|
||||
public function setOrderField(string $field): static
|
||||
{
|
||||
$this->orderField = $field;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置排序方式
|
||||
* @param string $type
|
||||
* @return static
|
||||
*/
|
||||
public function setOrderType(string $type): static
|
||||
{
|
||||
$this->orderType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型实例
|
||||
* @return object
|
||||
*/
|
||||
public function getModel(): object
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传的导入文件
|
||||
* @param $file
|
||||
* @return string
|
||||
*/
|
||||
public function getImport($file): string
|
||||
{
|
||||
$full_dir = runtime_path() . '/resource/';
|
||||
if (!is_dir($full_dir)) {
|
||||
mkdir($full_dir, 0777, true);
|
||||
}
|
||||
$ext = $file->getUploadExtension() ?: null;
|
||||
$full_path = $full_dir . md5(time()) . '.' . $ext;
|
||||
$file->move($full_path);
|
||||
return $full_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法调用代理到模型
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $name, array $arguments): mixed
|
||||
{
|
||||
return call_user_func_array([$this->model, $name], $arguments);
|
||||
}
|
||||
}
|
||||
74
server/plugin/saiadmin/basic/BaseController.php
Normal file
74
server/plugin/saiadmin/basic/BaseController.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic;
|
||||
|
||||
use plugin\saiadmin\app\cache\UserInfoCache;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 基类 控制器继承此类
|
||||
*/
|
||||
class BaseController extends OpenController
|
||||
{
|
||||
|
||||
/**
|
||||
* 当前登陆管理员信息
|
||||
*/
|
||||
protected $adminInfo;
|
||||
|
||||
/**
|
||||
* 当前登陆管理员ID
|
||||
*/
|
||||
protected int $adminId;
|
||||
|
||||
/**
|
||||
* 当前登陆管理员账号
|
||||
*/
|
||||
protected string $adminName;
|
||||
|
||||
/**
|
||||
* 逻辑层注入
|
||||
*/
|
||||
protected $logic;
|
||||
|
||||
/**
|
||||
* 验证器注入
|
||||
*/
|
||||
protected $validate;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
protected function init(): void
|
||||
{
|
||||
// 登录模式赋值
|
||||
$isLogin = request()->header('check_login', false);
|
||||
if ($isLogin) {
|
||||
$result = request()->header('check_admin');
|
||||
$this->adminId = $result['id'];
|
||||
$this->adminName = $result['username'];
|
||||
$this->adminInfo = UserInfoCache::getUserInfo($result['id']);
|
||||
|
||||
// 用户数据传递给逻辑层
|
||||
$this->logic && $this->logic->init($this->adminInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证器调用
|
||||
*/
|
||||
protected function validate(string $scene, $data): bool
|
||||
{
|
||||
if ($this->validate) {
|
||||
if (!$this->validate->scene($scene)->check($data)) {
|
||||
throw new ApiException($this->validate->getError());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
77
server/plugin/saiadmin/basic/BaseValidate.php
Normal file
77
server/plugin/saiadmin/basic/BaseValidate.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 验证器基类
|
||||
*/
|
||||
class BaseValidate extends Validate
|
||||
{
|
||||
|
||||
/**
|
||||
* 验证是否唯一
|
||||
* @access public
|
||||
* @param mixed $value 字段值
|
||||
* @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名
|
||||
* @param array $data 数据
|
||||
* @param string $field 验证字段名
|
||||
* @return bool
|
||||
*/
|
||||
public function unique($value, $rule, array $data = [], string $field = ''): bool
|
||||
{
|
||||
if (is_string($rule)) {
|
||||
$rule = explode(',', $rule);
|
||||
}
|
||||
|
||||
if (str_contains($rule[0], '\\')) {
|
||||
// 指定模型类
|
||||
$db = new $rule[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = $rule[1] ?? $field;
|
||||
$map = [];
|
||||
|
||||
if (str_contains($key, '^')) {
|
||||
// 支持多个字段验证
|
||||
$fields = explode('^', $key);
|
||||
foreach ($fields as $key) {
|
||||
if (isset($data[$key])) {
|
||||
$map[] = [$key, '=', $data[$key]];
|
||||
}
|
||||
}
|
||||
} elseif (strpos($key, '=')) {
|
||||
// 支持复杂验证
|
||||
parse_str($key, $array);
|
||||
foreach ($array as $k => $val) {
|
||||
$map[] = [$k, '=', $data[$k] ?? $val];
|
||||
}
|
||||
} elseif (isset($data[$field])) {
|
||||
$map[] = [$key, '=', $data[$field]];
|
||||
}
|
||||
|
||||
$pk = !empty($rule[3]) ? $rule[3] : $db->getPrimaryKeyName();
|
||||
|
||||
if (is_string($pk)) {
|
||||
if (isset($rule[2])) {
|
||||
$map[] = [$pk, '<>', $rule[2]];
|
||||
} elseif (isset($data[$pk])) {
|
||||
$map[] = [$pk, '<>', $data[$pk]];
|
||||
}
|
||||
}
|
||||
|
||||
if ($db->where($map)->count() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
60
server/plugin/saiadmin/basic/OpenController.php
Normal file
60
server/plugin/saiadmin/basic/OpenController.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 基类 控制器继承此类
|
||||
*/
|
||||
class OpenController
|
||||
{
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// 控制器初始化
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回json内容
|
||||
* @param array|string $data
|
||||
* @param string $msg
|
||||
* @param int $option
|
||||
* @return Response
|
||||
*/
|
||||
public function success(array | string $data = [], string $msg = 'success', int $option = JSON_UNESCAPED_UNICODE): Response
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$msg = $data;
|
||||
}
|
||||
return json(['code' => 200, 'message' => $msg, 'data' => $data], $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败返回json内容
|
||||
* @param string $msg
|
||||
* @return Response
|
||||
*/
|
||||
public function fail(string $msg = 'fail'): Response
|
||||
{
|
||||
return json(['code' => 400, 'message' => $msg]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
protected function init(): void
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
||||
79
server/plugin/saiadmin/basic/contracts/LogicInterface.php
Normal file
79
server/plugin/saiadmin/basic/contracts/LogicInterface.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\contracts;
|
||||
|
||||
/**
|
||||
* Logic 接口定义
|
||||
* 所有 Logic 基类必须实现此接口
|
||||
*/
|
||||
interface LogicInterface
|
||||
{
|
||||
/**
|
||||
* 初始化
|
||||
* @param mixed $user 用户信息
|
||||
* @return void
|
||||
*/
|
||||
public function init($user): void;
|
||||
|
||||
/**
|
||||
* 添加数据
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function add(array $data): mixed;
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param mixed $id
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id, array $data): mixed;
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function read($id): mixed;
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param mixed $ids
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy($ids): bool;
|
||||
|
||||
/**
|
||||
* 搜索器搜索
|
||||
* @param array $searchWhere
|
||||
* @return mixed
|
||||
*/
|
||||
public function search(array $searchWhere = []): mixed;
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getList($query): mixed;
|
||||
|
||||
/**
|
||||
* 获取全部数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAll($query): mixed;
|
||||
|
||||
/**
|
||||
* 数据库事务操作
|
||||
* @param callable $closure
|
||||
* @param bool $isTran
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(callable $closure, bool $isTran = true): mixed;
|
||||
}
|
||||
27
server/plugin/saiadmin/basic/contracts/ModelInterface.php
Normal file
27
server/plugin/saiadmin/basic/contracts/ModelInterface.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\contracts;
|
||||
|
||||
/**
|
||||
* Model 接口定义
|
||||
* 所有 Model 基类必须实现此接口
|
||||
*/
|
||||
interface ModelInterface
|
||||
{
|
||||
/**
|
||||
* 获取表名
|
||||
* @return string
|
||||
*/
|
||||
public function getTableName(): string;
|
||||
|
||||
/**
|
||||
* 获取主键名
|
||||
* @return string
|
||||
*/
|
||||
public function getPrimaryKeyName(): string;
|
||||
|
||||
}
|
||||
152
server/plugin/saiadmin/basic/eloquent/BaseLogic.php
Normal file
152
server/plugin/saiadmin/basic/eloquent/BaseLogic.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\eloquent;
|
||||
|
||||
use support\Db;
|
||||
use plugin\saiadmin\basic\AbstractLogic;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* Laravel Eloquent 逻辑层基类
|
||||
*/
|
||||
class BaseLogic extends AbstractLogic
|
||||
{
|
||||
/**
|
||||
* 数据库事务操作
|
||||
* @param callable $closure
|
||||
* @param bool $isTran
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(callable $closure, bool $isTran = true): mixed
|
||||
{
|
||||
return $isTran ? Db::transaction($closure) : $closure();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function add(array $data): mixed
|
||||
{
|
||||
$model = $this->model->create($data);
|
||||
return $model->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param mixed $id
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id, array $data): mixed
|
||||
{
|
||||
$model = $this->model->find($id);
|
||||
if (!$model) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return $model->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function read($id): mixed
|
||||
{
|
||||
$model = $this->model->find($id);
|
||||
if (!$model) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param mixed $ids
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy($ids): bool
|
||||
{
|
||||
return $this->model->destroy($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索器搜索
|
||||
* @param array $searchWhere
|
||||
* @return mixed
|
||||
*/
|
||||
public function search(array $searchWhere = []): mixed
|
||||
{
|
||||
$withSearch = array_keys($searchWhere);
|
||||
$data = [];
|
||||
foreach ($searchWhere as $key => $value) {
|
||||
if ($value !== '' && $value !== null && $value !== []) {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
$withSearch = array_keys($data);
|
||||
return $this->model->withSearch($withSearch, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getList($query): mixed
|
||||
{
|
||||
$request = request();
|
||||
$saiType = $request ? $request->input('saiType', 'list') : 'list';
|
||||
$page = $request ? $request->input('page', 1) : 1;
|
||||
$limit = $request ? $request->input('limit', 10) : 10;
|
||||
$orderField = $request ? $request->input('orderField', '') : '';
|
||||
$orderType = $request ? $request->input('orderType', $this->orderType) : $this->orderType;
|
||||
|
||||
if (empty($orderField)) {
|
||||
$orderField = $this->orderField !== '' ? $this->orderField : $this->model->getKeyName();
|
||||
}
|
||||
|
||||
$query->orderBy($orderField, $orderType);
|
||||
|
||||
if ($saiType === 'all') {
|
||||
return $query->get()->toArray();
|
||||
}
|
||||
|
||||
$list = $query->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
return [
|
||||
'current_page' => $list->currentPage(),
|
||||
'per_page' => $list->perPage(),
|
||||
'last_page' => $list->lastPage(),
|
||||
'has_more' => $list->hasMorePages(),
|
||||
'total' => $list->total(),
|
||||
'data' => $list->items(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAll($query): mixed
|
||||
{
|
||||
$request = request();
|
||||
$orderField = $request ? $request->input('orderField', '') : '';
|
||||
$orderType = $request ? $request->input('orderType', $this->orderType) : $this->orderType;
|
||||
|
||||
if (empty($orderField)) {
|
||||
$orderField = $this->orderField !== '' ? $this->orderField : $this->model->getKeyName();
|
||||
}
|
||||
|
||||
$query->orderBy($orderField, $orderType);
|
||||
return $query->get()->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
171
server/plugin/saiadmin/basic/eloquent/BaseModel.php
Normal file
171
server/plugin/saiadmin/basic/eloquent/BaseModel.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\eloquent;
|
||||
|
||||
use support\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use plugin\saiadmin\basic\contracts\ModelInterface;
|
||||
|
||||
/**
|
||||
* Laravel Eloquent 模型基类
|
||||
*/
|
||||
class BaseModel extends Model implements ModelInterface
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* 创建时间字段
|
||||
*/
|
||||
const CREATED_AT = 'create_time';
|
||||
|
||||
/**
|
||||
* 更新时间字段
|
||||
*/
|
||||
const UPDATED_AT = 'update_time';
|
||||
|
||||
/**
|
||||
* 删除时间字段
|
||||
*/
|
||||
const DELETED_AT = 'delete_time';
|
||||
|
||||
/**
|
||||
* 隐藏字段
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['delete_time'];
|
||||
|
||||
/**
|
||||
* 不可批量赋值的属性 (为空表示全部可赋值)
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* 类型转换
|
||||
* @return array
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'create_time' => 'datetime:Y-m-d H:i:s',
|
||||
'update_time' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理时区问题
|
||||
* @param \DateTimeInterface $date
|
||||
* @return string
|
||||
*/
|
||||
protected function serializeDate(\DateTimeInterface $date): string
|
||||
{
|
||||
return $date->format($this->dateFormat ?: 'Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表名
|
||||
* @return string
|
||||
*/
|
||||
public function getTableName(): string
|
||||
{
|
||||
return $this->getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键名
|
||||
* @return string
|
||||
*/
|
||||
public function getPrimaryKeyName(): string
|
||||
{
|
||||
return $this->getKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索器搜索
|
||||
* @param array $fields
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function withSearch(array $fields, array $data): mixed
|
||||
{
|
||||
$query = $this->newQuery();
|
||||
foreach ($fields as $field) {
|
||||
$method = 'search' . ucfirst($this->toCamelCase($field)) . 'Attr';
|
||||
if (method_exists($this, $method) && isset($data[$field]) && $data[$field] !== '') {
|
||||
$this->$method($query, $data[$field]);
|
||||
} else {
|
||||
$query->where($field, $data[$field]);
|
||||
}
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线命名转换为驼峰命名
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
protected function toCamelCase(string $str): string
|
||||
{
|
||||
return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $str))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加时间范围搜索
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchCreateTimeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$query->whereBetween('create_time', $value);
|
||||
} else {
|
||||
$query->where('create_time', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间范围搜索
|
||||
* @param mixed $query
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function searchUpdateTimeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$query->whereBetween('update_time', $value);
|
||||
} else {
|
||||
$query->where('update_time', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型启动事件
|
||||
* @return void
|
||||
*/
|
||||
protected static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// 创建前事件
|
||||
static::creating(function ($model) {
|
||||
$info = getCurrentInfo();
|
||||
$schema = $model->getConnection()->getSchemaBuilder();
|
||||
if ($info && $schema->hasColumn($model->getTable(), 'created_by')) {
|
||||
$model->created_by = $info['id'];
|
||||
}
|
||||
});
|
||||
|
||||
// 保存前事件
|
||||
static::saving(function ($model) {
|
||||
$info = getCurrentInfo();
|
||||
$schema = $model->getConnection()->getSchemaBuilder();
|
||||
if ($info && $schema->hasColumn($model->getTable(), 'updated_by')) {
|
||||
$model->updated_by = $info['id'];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
142
server/plugin/saiadmin/basic/think/BaseLogic.php
Normal file
142
server/plugin/saiadmin/basic/think/BaseLogic.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\think;
|
||||
|
||||
use support\think\Db;
|
||||
use plugin\saiadmin\basic\AbstractLogic;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* ThinkORM 逻辑层基类
|
||||
*/
|
||||
class BaseLogic extends AbstractLogic
|
||||
{
|
||||
/**
|
||||
* 数据库事务操作
|
||||
* @param callable $closure
|
||||
* @param bool $isTran
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(callable $closure, bool $isTran = true): mixed
|
||||
{
|
||||
return $isTran ? Db::transaction($closure) : $closure();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function add(array $data): mixed
|
||||
{
|
||||
$model = $this->model->create($data);
|
||||
return $model->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param mixed $id
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id, array $data): mixed
|
||||
{
|
||||
$model = $this->model->findOrEmpty($id);
|
||||
if ($model->isEmpty()) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return $model->save($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param mixed $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function read($id): mixed
|
||||
{
|
||||
$model = $this->model->findOrEmpty($id);
|
||||
if ($model->isEmpty()) {
|
||||
throw new ApiException('数据不存在');
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param mixed $ids
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy($ids): bool
|
||||
{
|
||||
return $this->model->destroy($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索器搜索
|
||||
* @param array $searchWhere
|
||||
* @return mixed
|
||||
*/
|
||||
public function search(array $searchWhere = []): mixed
|
||||
{
|
||||
$withSearch = array_keys($searchWhere);
|
||||
$data = [];
|
||||
foreach ($searchWhere as $key => $value) {
|
||||
if ($value !== '' && $value !== null && $value !== []) {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
$withSearch = array_keys($data);
|
||||
return $this->model->withSearch($withSearch, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getList($query): mixed
|
||||
{
|
||||
$request = request();
|
||||
$saiType = $request ? $request->input('saiType', 'list') : 'list';
|
||||
$page = $request ? $request->input('page', 1) : 1;
|
||||
$limit = $request ? $request->input('limit', 10) : 10;
|
||||
$orderField = $request ? $request->input('orderField', '') : '';
|
||||
$orderType = $request ? $request->input('orderType', $this->orderType) : $this->orderType;
|
||||
|
||||
if (empty($orderField)) {
|
||||
$orderField = $this->orderField !== '' ? $this->orderField : $this->model->getPk();
|
||||
}
|
||||
|
||||
$query->order($orderField, $orderType);
|
||||
|
||||
if ($saiType === 'all') {
|
||||
return $query->select()->toArray();
|
||||
}
|
||||
|
||||
return $query->paginate($limit, false, ['page' => $page])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部数据
|
||||
* @param mixed $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAll($query): mixed
|
||||
{
|
||||
$request = request();
|
||||
$orderField = $request ? $request->input('orderField', '') : '';
|
||||
$orderType = $request ? $request->input('orderType', $this->orderType) : $this->orderType;
|
||||
|
||||
if (empty($orderField)) {
|
||||
$orderField = $this->orderField !== '' ? $this->orderField : $this->model->getPk();
|
||||
}
|
||||
|
||||
$query->order($orderField, $orderType);
|
||||
return $query->select()->toArray();
|
||||
}
|
||||
}
|
||||
117
server/plugin/saiadmin/basic/think/BaseModel.php
Normal file
117
server/plugin/saiadmin/basic/think/BaseModel.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\basic\think;
|
||||
|
||||
use support\think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
use plugin\saiadmin\basic\contracts\ModelInterface;
|
||||
|
||||
/**
|
||||
* ThinkORM 模型基类
|
||||
*/
|
||||
class BaseModel extends Model implements ModelInterface
|
||||
{
|
||||
use SoftDelete;
|
||||
|
||||
/**
|
||||
* 删除时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
/**
|
||||
* 创建时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $createTime = 'create_time';
|
||||
|
||||
/**
|
||||
* 更新时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $updateTime = 'update_time';
|
||||
|
||||
/**
|
||||
* 隐藏字段
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['delete_time'];
|
||||
|
||||
/**
|
||||
* 只读字段
|
||||
* @var array
|
||||
*/
|
||||
protected $readonly = ['created_by', 'create_time'];
|
||||
|
||||
/**
|
||||
* 获取表名
|
||||
* @return string
|
||||
*/
|
||||
public function getTableName(): string
|
||||
{
|
||||
return $this->getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键名
|
||||
* @return string
|
||||
*/
|
||||
public function getPrimaryKeyName(): string
|
||||
{
|
||||
return $this->getPk();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加时间范围搜索
|
||||
* @param $query
|
||||
* @param $value
|
||||
*/
|
||||
public function searchCreateTimeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$query->whereBetween('create_time', $value);
|
||||
} else {
|
||||
$query->where('create_time', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新时间范围搜索
|
||||
* @param mixed $query
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function searchUpdateTimeAttr($query, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$query->whereBetween('update_time', $value);
|
||||
} else {
|
||||
$query->where('update_time', '=', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增前事件
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
public static function onBeforeInsert($model): void
|
||||
{
|
||||
$info = getCurrentInfo();
|
||||
$info && $model->setAttr('created_by', $info['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入前事件
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
public static function onBeforeWrite($model): void
|
||||
{
|
||||
$info = getCurrentInfo();
|
||||
$info && $model->setAttr('updated_by', $info['id']);
|
||||
}
|
||||
}
|
||||
182
server/plugin/saiadmin/command/SaiOrm.php
Normal file
182
server/plugin/saiadmin/command/SaiOrm.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\saiadmin\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||
|
||||
/**
|
||||
* SaiAdmin ORM 切换命令
|
||||
* 用于切换 saiadmin 插件的 ORM 实现 (think / eloquent)
|
||||
*/
|
||||
class SaiOrm extends Command
|
||||
{
|
||||
protected static $defaultName = 'sai:orm';
|
||||
protected static $defaultDescription = '切换 SaiAdmin 使用的 ORM';
|
||||
|
||||
/**
|
||||
* ORM 源文件目录
|
||||
*/
|
||||
protected string $ormSourcePath;
|
||||
|
||||
/**
|
||||
* 目标插件目录
|
||||
*/
|
||||
protected string $pluginAppPath;
|
||||
|
||||
/**
|
||||
* ORM 选项配置
|
||||
*/
|
||||
protected array $ormOptions = [
|
||||
'think' => '1. ThinkORM (TopThink)',
|
||||
'eloquent' => '2. Eloquent ORM (Laravel)',
|
||||
'exit' => '3. 退出,什么也不做',
|
||||
];
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('sai:orm')
|
||||
->setDescription('切换 SaiAdmin 使用的 ORM');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('SaiAdmin ORM 切换工具');
|
||||
$io->text('此命令只切换 saiadmin 框架核心使用的 ORM, 不影响其他模块功能, 多种 ORM 可以同时使用!');
|
||||
$io->newLine();
|
||||
|
||||
// 创建选择问题(编号从1开始)
|
||||
$helper = $this->getHelper('question');
|
||||
$choices = [
|
||||
1 => '1. ThinkORM (TopThink)',
|
||||
2 => '2. Eloquent ORM (Laravel)',
|
||||
3 => '3. 退出,什么也不做',
|
||||
];
|
||||
$question = new ChoiceQuestion(
|
||||
'请选择要使用的 ORM 框架:',
|
||||
$choices,
|
||||
1 // 默认选中第一个
|
||||
);
|
||||
$question->setErrorMessage('选项 %s 无效');
|
||||
|
||||
// 获取用户选择
|
||||
$selected = $helper->ask($input, $output, $question);
|
||||
|
||||
// 根据选择的文本反查 key
|
||||
$selectedKey = array_search($selected, $choices);
|
||||
|
||||
// 如果选择退出
|
||||
if ($selectedKey == 3) {
|
||||
$io->newLine();
|
||||
$io->info('已退出,什么也没做。');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// 映射选项到 ORM 类型
|
||||
$ormMap = [1 => 'think', 2 => 'eloquent'];
|
||||
$orm = $ormMap[$selectedKey];
|
||||
|
||||
$io->newLine();
|
||||
$io->section("您选择了: {$selected}");
|
||||
|
||||
// 确认操作
|
||||
if (!$io->confirm('确定要切换吗?这将覆盖 saiadmin 核心代码文件', true)) {
|
||||
$io->warning('操作已取消');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// 设置路径
|
||||
$this->ormSourcePath = BASE_PATH . '/vendor/saithink/saiadmin/src/orm/' . $orm . '/app';
|
||||
$this->pluginAppPath = BASE_PATH . '/plugin/saiadmin/app';
|
||||
|
||||
// 检查源目录是否存在
|
||||
if (!is_dir($this->ormSourcePath)) {
|
||||
$io->error("ORM 源目录不存在: {$this->ormSourcePath}");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$io->section('开始复制文件...');
|
||||
|
||||
try {
|
||||
$copiedFiles = $this->copyDirectory($this->ormSourcePath, $this->pluginAppPath, $io);
|
||||
|
||||
$io->newLine();
|
||||
$io->success([
|
||||
"ORM 切换成功!",
|
||||
"已切换到: {$selected}",
|
||||
"复制文件数: {$copiedFiles}"
|
||||
]);
|
||||
|
||||
$io->note([
|
||||
'请重启 webman 服务使更改生效',
|
||||
'命令: php webman restart 或 php windows.php'
|
||||
]);
|
||||
|
||||
return Command::SUCCESS;
|
||||
} catch (\Exception $e) {
|
||||
$io->error("切换失败: " . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归复制目录
|
||||
* @param string $source 源目录
|
||||
* @param string $dest 目标目录
|
||||
* @param SymfonyStyle $io 输出接口
|
||||
* @return int 复制的文件数量
|
||||
*/
|
||||
protected function copyDirectory(string $source, string $dest, SymfonyStyle $io): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest, 0755, true);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
// 先计算文件总数用于进度条
|
||||
$files = [];
|
||||
foreach ($iterator as $item) {
|
||||
if (!$item->isDir()) {
|
||||
$files[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建进度条
|
||||
$io->progressStart(count($files));
|
||||
|
||||
// 重新遍历并复制
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $item) {
|
||||
$destPath = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
|
||||
|
||||
if ($item->isDir()) {
|
||||
if (!is_dir($destPath)) {
|
||||
mkdir($destPath, 0755, true);
|
||||
}
|
||||
} else {
|
||||
copy($item->getPathname(), $destPath);
|
||||
$count++;
|
||||
$io->progressAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
$io->progressFinish();
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
297
server/plugin/saiadmin/command/SaiPlugin.php
Normal file
297
server/plugin/saiadmin/command/SaiPlugin.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\saiadmin\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* SaiAdmin 插件创建命令
|
||||
* 用于创建 saiadmin 插件
|
||||
*/
|
||||
class SaiPlugin extends Command
|
||||
{
|
||||
protected static $defaultName = 'sai:plugin';
|
||||
protected static $defaultDescription = '创建 SaiAdmin 插件';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title('SaiAdmin 插件创建工具');
|
||||
$io->text('此命令用于创建基于webman的 saiadmin 插件, 用于扩展 saiadmin 框架功能!');
|
||||
$io->newLine();
|
||||
|
||||
$name = $input->getArgument('name');
|
||||
$io->text("创建 SaiAdmin 插件 $name");
|
||||
|
||||
if (strpos($name, '/') !== false) {
|
||||
$io->error('名称错误,名称必须不包含字符 \'/\'');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
// Create dir config/plugin/$name
|
||||
if (is_dir($plugin_config_path = base_path() . "/plugin/$name")) {
|
||||
$io->error("目录 $plugin_config_path 已存在");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->createAll($name);
|
||||
|
||||
$io->newLine();
|
||||
$io->success("SaiAdmin 插件创建成功!");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createAll($name)
|
||||
{
|
||||
$base_path = base_path();
|
||||
$this->mkdir("$base_path/plugin/$name/app", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/admin/controller", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/admin/logic", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/api/controller", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/api/logic", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/cache", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/event", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/model", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/middleware", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/config", 0777, true);
|
||||
$this->createControllerFile("$base_path/plugin/$name/app/api/controller/IndexController.php", $name);
|
||||
$this->createFunctionsFile("$base_path/plugin/$name/app/functions.php");
|
||||
$this->createConfigFiles("$base_path/plugin/$name/config", $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function mkdir($path, $mode = 0777, $recursive = false)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
return;
|
||||
}
|
||||
echo "Create $path\r\n";
|
||||
mkdir($path, $mode, $recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createControllerFile($path, $name)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace plugin\\$name\\app\\api\\controller;
|
||||
|
||||
use plugin\\saiadmin\\basic\\OpenController;
|
||||
|
||||
class IndexController extends OpenController
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
return \$this->success([
|
||||
'app' => '$name',
|
||||
'version' => '1.0.0',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents($path, $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
* @return void
|
||||
*/
|
||||
protected function createFunctionsFile($file)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $base
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createConfigFiles($base, $name)
|
||||
{
|
||||
// app.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use support\\Request;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => false,
|
||||
'version' => '1.0.0'
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/app.php", $content);
|
||||
|
||||
// autoload.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'files' => [
|
||||
base_path() . '/plugin/$name/app/functions.php',
|
||||
]
|
||||
];
|
||||
EOF;
|
||||
file_put_contents("$base/autoload.php", $content);
|
||||
|
||||
// container.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return new Webman\\Container;
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/container.php", $content);
|
||||
|
||||
|
||||
// exception.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'' => \\plugin\\saiadmin\\app\\exception\\Handler::class,
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/exception.php", $content);
|
||||
|
||||
// log.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handlers' => [
|
||||
[
|
||||
'class' => Monolog\\Handler\\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
runtime_path() . '/logs/$name.log',
|
||||
7,
|
||||
Monolog\\Logger::DEBUG,
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\\Formatter\\LineFormatter::class,
|
||||
'constructor' => [null, 'Y-m-d H:i:s', true],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/log.php", $content);
|
||||
|
||||
// middleware.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use plugin\saiadmin\app\middleware\SystemLog;
|
||||
use plugin\saiadmin\app\middleware\CheckLogin;
|
||||
use plugin\saiadmin\app\middleware\CheckAuth;
|
||||
|
||||
return [
|
||||
'admin' => [
|
||||
CheckLogin::class,
|
||||
CheckAuth::class,
|
||||
SystemLog::class,
|
||||
],
|
||||
'api' => [
|
||||
]
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/middleware.php", $content);
|
||||
|
||||
// process.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/process.php", $content);
|
||||
|
||||
// route.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use Webman\\Route;
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/route.php", $content);
|
||||
|
||||
// static.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enable' => true,
|
||||
'middleware' => [], // Static file Middleware
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/static.php", $content);
|
||||
|
||||
// translation.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Default language
|
||||
'locale' => 'zh_CN',
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => base_path() . "/plugin/$name/resource/translations",
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/translation.php", $content);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
198
server/plugin/saiadmin/command/SaiUpgrade.php
Normal file
198
server/plugin/saiadmin/command/SaiUpgrade.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\saiadmin\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* SaiAdmin 升级命令
|
||||
* 用于从 vendor 目录升级 saiadmin 插件到最新版本
|
||||
*/
|
||||
class SaiUpgrade extends Command
|
||||
{
|
||||
protected static $defaultName = 'sai:upgrade';
|
||||
protected static $defaultDescription = '升级 SaiAdmin 插件到最新版本';
|
||||
|
||||
/**
|
||||
* 升级源目录
|
||||
*/
|
||||
protected string $sourcePath;
|
||||
|
||||
/**
|
||||
* 目标插件目录
|
||||
*/
|
||||
protected string $targetPath;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName('sai:upgrade')
|
||||
->setDescription('升级 SaiAdmin 插件到最新版本');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('SaiAdmin 升级工具');
|
||||
$io->text([
|
||||
'此命令将从 vendor 目录复制最新版本的 saiadmin 插件文件到 plugin 目录',
|
||||
'源目录: vendor/saithink/saiadmin/src/plugin/saiadmin',
|
||||
'目标目录: plugin/saiadmin',
|
||||
]);
|
||||
$io->newLine();
|
||||
|
||||
// 设置路径
|
||||
$this->sourcePath = BASE_PATH . '/vendor/saithink/saiadmin/src/plugin/saiadmin';
|
||||
$this->targetPath = BASE_PATH . '/plugin/saiadmin';
|
||||
|
||||
// 检查源目录是否存在
|
||||
if (!is_dir($this->sourcePath)) {
|
||||
$io->error([
|
||||
"升级源目录不存在: {$this->sourcePath}",
|
||||
"请确保已通过 composer 安装了 saithink/saiadmin 包",
|
||||
]);
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// 获取版本信息
|
||||
$currentVersion = $this->getVersion($this->targetPath);
|
||||
$latestVersion = $this->getVersion($this->sourcePath);
|
||||
|
||||
// 显示版本信息
|
||||
$io->section('版本信息');
|
||||
$io->table(
|
||||
['项目', '版本'],
|
||||
[
|
||||
['当前版本', $currentVersion ?: '未知'],
|
||||
['最新版本', $latestVersion ?: '未知'],
|
||||
]
|
||||
);
|
||||
|
||||
// 版本对比提示
|
||||
if ($currentVersion && $latestVersion) {
|
||||
if (version_compare($currentVersion, $latestVersion, '>=')) {
|
||||
$io->success('当前已是最新版本!');
|
||||
if (!$io->confirm('是否仍要继续覆盖安装?', false)) {
|
||||
$io->info('操作已取消');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
} else {
|
||||
$io->info("发现新版本: {$currentVersion} → {$latestVersion}");
|
||||
}
|
||||
}
|
||||
|
||||
// 警告信息
|
||||
$io->warning([
|
||||
"注意:此操作将覆盖 {$this->targetPath} 目录的现有文件!",
|
||||
"建议在执行前备份您的自定义修改。",
|
||||
]);
|
||||
|
||||
// 确认操作
|
||||
if (!$io->confirm('确定要执行升级操作吗?', false)) {
|
||||
$io->info('操作已取消');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$io->section('开始升级...');
|
||||
|
||||
try {
|
||||
$copiedFiles = $this->copyDirectory($this->sourcePath, $this->targetPath, $io);
|
||||
|
||||
$io->newLine();
|
||||
$io->success([
|
||||
"SaiAdmin 升级成功!",
|
||||
"复制文件数: {$copiedFiles}",
|
||||
]);
|
||||
|
||||
$io->note([
|
||||
'请重启 webman 服务使更改生效',
|
||||
'命令: php webman restart 或 php windows.php',
|
||||
]);
|
||||
|
||||
return Command::SUCCESS;
|
||||
} catch (\Exception $e) {
|
||||
$io->error("升级失败: " . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归复制目录
|
||||
* @param string $source 源目录
|
||||
* @param string $dest 目标目录
|
||||
* @param SymfonyStyle $io 输出接口
|
||||
* @return int 复制的文件数量
|
||||
*/
|
||||
protected function copyDirectory(string $source, string $dest, SymfonyStyle $io): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest, 0755, true);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
// 先计算文件总数用于进度条
|
||||
$files = [];
|
||||
foreach ($iterator as $item) {
|
||||
if (!$item->isDir()) {
|
||||
$files[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建进度条
|
||||
$io->progressStart(count($files));
|
||||
|
||||
// 重新遍历并复制
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $item) {
|
||||
$destPath = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
|
||||
|
||||
if ($item->isDir()) {
|
||||
if (!is_dir($destPath)) {
|
||||
mkdir($destPath, 0755, true);
|
||||
}
|
||||
} else {
|
||||
copy($item->getPathname(), $destPath);
|
||||
$count++;
|
||||
$io->progressAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
$io->progressFinish();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从目录中获取版本号
|
||||
* @param string $path 插件目录路径
|
||||
* @return string|null 版本号
|
||||
*/
|
||||
protected function getVersion(string $path): ?string
|
||||
{
|
||||
$configFile = $path . '/config/app.php';
|
||||
|
||||
if (!file_exists($configFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$config = include $configFile;
|
||||
return $config['version'] ?? null;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
server/plugin/saiadmin/config/app.php
Normal file
10
server/plugin/saiadmin/config/app.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
use support\Request;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => false,
|
||||
'version' => '6.0.7'
|
||||
];
|
||||
6
server/plugin/saiadmin/config/autoload.php
Normal file
6
server/plugin/saiadmin/config/autoload.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'files' => [
|
||||
base_path() . '/plugin/saiadmin/app/functions.php',
|
||||
]
|
||||
];
|
||||
2
server/plugin/saiadmin/config/container.php
Normal file
2
server/plugin/saiadmin/config/container.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
return new Webman\Container;
|
||||
2
server/plugin/saiadmin/config/database.php
Normal file
2
server/plugin/saiadmin/config/database.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
return [];
|
||||
9
server/plugin/saiadmin/config/event.php
Normal file
9
server/plugin/saiadmin/config/event.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
return [
|
||||
'user.login' => [
|
||||
[plugin\saiadmin\app\event\SystemUser::class, 'login'],
|
||||
],
|
||||
'user.operateLog' => [
|
||||
[plugin\saiadmin\app\event\SystemUser::class, 'operateLog'],
|
||||
]
|
||||
];
|
||||
5
server/plugin/saiadmin/config/exception.php
Normal file
5
server/plugin/saiadmin/config/exception.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'' => \plugin\saiadmin\app\exception\Handler::class,
|
||||
];
|
||||
20
server/plugin/saiadmin/config/log.php
Normal file
20
server/plugin/saiadmin/config/log.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handlers' => [
|
||||
[
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
runtime_path() . '/logs/saiadmin.log',
|
||||
7,
|
||||
Monolog\Logger::DEBUG,
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\Formatter\LineFormatter::class,
|
||||
'constructor' => [null, 'Y-m-d H:i:s', true],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
13
server/plugin/saiadmin/config/middleware.php
Normal file
13
server/plugin/saiadmin/config/middleware.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
use plugin\saiadmin\app\middleware\SystemLog;
|
||||
use plugin\saiadmin\app\middleware\CheckLogin;
|
||||
use plugin\saiadmin\app\middleware\CheckAuth;
|
||||
|
||||
return [
|
||||
'' => [
|
||||
CheckLogin::class,
|
||||
CheckAuth::class,
|
||||
SystemLog::class,
|
||||
]
|
||||
];
|
||||
6
server/plugin/saiadmin/config/process.php
Normal file
6
server/plugin/saiadmin/config/process.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'task' => [
|
||||
'handler' => plugin\saiadmin\process\Task::class
|
||||
]
|
||||
];
|
||||
115
server/plugin/saiadmin/config/route.php
Normal file
115
server/plugin/saiadmin/config/route.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
use Webman\Route;
|
||||
|
||||
Route::group('/core', function () {
|
||||
|
||||
Route::get('/install', [plugin\saiadmin\app\controller\InstallController::class, 'index']);
|
||||
Route::post('/install/install', [plugin\saiadmin\app\controller\InstallController::class, 'install']);
|
||||
|
||||
Route::get('/captcha', [plugin\saiadmin\app\controller\LoginController::class, 'captcha']);
|
||||
Route::post('/login', [plugin\saiadmin\app\controller\LoginController::class, 'login']);
|
||||
|
||||
Route::get('/system/user', [plugin\saiadmin\app\controller\SystemController::class, 'userInfo']);
|
||||
Route::get("/system/dictAll", [plugin\saiadmin\app\controller\SystemController::class, 'dictAll']);
|
||||
Route::get('/system/menu', [plugin\saiadmin\app\controller\SystemController::class, 'menu']);
|
||||
|
||||
Route::get('/system/statistics', [plugin\saiadmin\app\controller\SystemController::class, 'statistics']);
|
||||
Route::get('/system/loginChart', [plugin\saiadmin\app\controller\SystemController::class, 'loginChart']);
|
||||
Route::get('/system/loginBarChart', [plugin\saiadmin\app\controller\SystemController::class, 'loginBarChart']);
|
||||
Route::get('/system/clearAllCache', [plugin\saiadmin\app\controller\SystemController::class, 'clearAllCache']);
|
||||
|
||||
Route::get("/system/getResourceCategory", [plugin\saiadmin\app\controller\SystemController::class, 'getResourceCategory']);
|
||||
Route::get("/system/getResourceList", [plugin\saiadmin\app\controller\SystemController::class, 'getResourceList']);
|
||||
Route::post("/system/saveNetworkImage", [plugin\saiadmin\app\controller\SystemController::class, 'saveNetworkImage']);
|
||||
Route::post("/system/uploadImage", [plugin\saiadmin\app\controller\SystemController::class, 'uploadImage']);
|
||||
Route::post("/system/uploadFile", [plugin\saiadmin\app\controller\SystemController::class, 'uploadFile']);
|
||||
Route::post("/system/chunkUpload", [plugin\saiadmin\app\controller\SystemController::class, 'chunkUpload']);
|
||||
Route::get("/system/getUserList", [plugin\saiadmin\app\controller\SystemController::class, 'getUserList']);
|
||||
Route::get("/system/getLoginLogList", [plugin\saiadmin\app\controller\SystemController::class, 'getLoginLogList']);
|
||||
Route::get("/system/getOperationLogList", [plugin\saiadmin\app\controller\SystemController::class, 'getOperationLogList']);
|
||||
|
||||
// 用户管理
|
||||
fastRoute("user", \plugin\saiadmin\app\controller\system\SystemUserController::class);
|
||||
Route::post("/user/updateInfo", [\plugin\saiadmin\app\controller\system\SystemUserController::class, 'updateInfo']);
|
||||
Route::post("/user/modifyPassword", [\plugin\saiadmin\app\controller\system\SystemUserController::class, 'modifyPassword']);
|
||||
Route::post("/user/clearCache", [\plugin\saiadmin\app\controller\system\SystemUserController::class, 'clearCache']);
|
||||
Route::post("/user/initUserPassword", [\plugin\saiadmin\app\controller\system\SystemUserController::class, 'initUserPassword']);
|
||||
Route::post("/user/setHomePage", [\plugin\saiadmin\app\controller\system\SystemUserController::class, 'setHomePage']);
|
||||
|
||||
// 角色管理
|
||||
fastRoute('role', \plugin\saiadmin\app\controller\system\SystemRoleController::class);
|
||||
Route::get("/role/accessRole", [\plugin\saiadmin\app\controller\system\SystemRoleController::class, 'accessRole']);
|
||||
Route::get("/role/getMenuByRole", [\plugin\saiadmin\app\controller\system\SystemRoleController::class, 'getMenuByRole']);
|
||||
Route::post("/role/menuPermission", [\plugin\saiadmin\app\controller\system\SystemRoleController::class, 'menuPermission']);
|
||||
|
||||
// 部门管理
|
||||
fastRoute("dept", \plugin\saiadmin\app\controller\system\SystemDeptController::class);
|
||||
Route::get("/dept/accessDept", [\plugin\saiadmin\app\controller\system\SystemDeptController::class, 'accessDept']);
|
||||
|
||||
// 岗位管理
|
||||
fastRoute('post', \plugin\saiadmin\app\controller\system\SystemPostController::class);
|
||||
Route::get("/post/accessPost", [\plugin\saiadmin\app\controller\system\SystemPostController::class, 'accessPost']);
|
||||
Route::post("/post/downloadTemplate", [plugin\saiadmin\app\controller\system\SystemPostController::class, 'downloadTemplate']);
|
||||
|
||||
// 菜单管理
|
||||
fastRoute('menu', \plugin\saiadmin\app\controller\system\SystemMenuController::class);
|
||||
Route::get("/menu/accessMenu", [\plugin\saiadmin\app\controller\system\SystemMenuController::class, 'accessMenu']);
|
||||
// 字典类型管理
|
||||
fastRoute('dictType', \plugin\saiadmin\app\controller\system\SystemDictTypeController::class);
|
||||
// 字典数据管理
|
||||
fastRoute('dictData', \plugin\saiadmin\app\controller\system\SystemDictDataController::class);
|
||||
// 附件管理
|
||||
fastRoute('attachment', \plugin\saiadmin\app\controller\system\SystemAttachmentController::class);
|
||||
Route::post("/attachment/move", [\plugin\saiadmin\app\controller\system\SystemAttachmentController::class, 'move']);
|
||||
// 附件分类
|
||||
fastRoute('category', \plugin\saiadmin\app\controller\system\SystemCategoryController::class);
|
||||
// 系统设置
|
||||
fastRoute('configGroup', \plugin\saiadmin\app\controller\system\SystemConfigGroupController::class);
|
||||
Route::post("/configGroup/email", [\plugin\saiadmin\app\controller\system\SystemConfigGroupController::class, 'email']);
|
||||
fastRoute('config', \plugin\saiadmin\app\controller\system\SystemConfigController::class);
|
||||
Route::post("/config/batchUpdate", [\plugin\saiadmin\app\controller\system\SystemConfigController::class, 'batchUpdate']);
|
||||
|
||||
// 日志管理
|
||||
Route::get("/logs/getLoginLogPageList", [\plugin\saiadmin\app\controller\system\SystemLogController::class, 'getLoginLogPageList']);
|
||||
Route::delete("/logs/deleteLoginLog", [\plugin\saiadmin\app\controller\system\SystemLogController::class, 'deleteLoginLog']);
|
||||
Route::get("/logs/getOperLogPageList", [\plugin\saiadmin\app\controller\system\SystemLogController::class, 'getOperLogPageList']);
|
||||
Route::delete("/logs/deleteOperLog", [\plugin\saiadmin\app\controller\system\SystemLogController::class, 'deleteOperLog']);
|
||||
fastRoute("email", \plugin\saiadmin\app\controller\system\SystemMailController::class);
|
||||
|
||||
// 服务管理
|
||||
Route::get("/server/monitor", [\plugin\saiadmin\app\controller\system\SystemServerController::class, 'monitor']);
|
||||
Route::get("/server/cache", [\plugin\saiadmin\app\controller\system\SystemServerController::class, 'cache']);
|
||||
Route::post("/server/clear", [\plugin\saiadmin\app\controller\system\SystemServerController::class, 'clear']);
|
||||
|
||||
// 数据表维护
|
||||
Route::get("/database/index", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'index']);
|
||||
Route::get("/database/recycle", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'recycle']);
|
||||
Route::delete("/database/delete", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'delete']);
|
||||
Route::post("/database/recovery", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'recovery']);
|
||||
Route::get("/database/dataSource", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'source']);
|
||||
Route::get("/database/detailed", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'detailed']);
|
||||
Route::post("/database/optimize", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'optimize']);
|
||||
Route::post("/database/fragment", [\plugin\saiadmin\app\controller\system\DataBaseController::class, 'fragment']);
|
||||
|
||||
});
|
||||
|
||||
Route::group('/tool', function () {
|
||||
|
||||
// 定时任务
|
||||
fastRoute('crontab', \plugin\saiadmin\app\controller\tool\CrontabController::class);
|
||||
Route::post("/crontab/run", [\plugin\saiadmin\app\controller\tool\CrontabController::class, 'run']);
|
||||
Route::get("/crontab/logPageList", [\plugin\saiadmin\app\controller\tool\CrontabController::class, 'logPageList']);
|
||||
Route::delete('/crontab/deleteCrontabLog', [\plugin\saiadmin\app\controller\tool\CrontabController::class, 'deleteCrontabLog']);
|
||||
|
||||
// 代码生成
|
||||
fastRoute('code', \plugin\saiadmin\app\controller\tool\GenerateTablesController::class);
|
||||
Route::get("/code/getTableColumns", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'getTableColumns']);
|
||||
Route::get("/code/preview", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'preview']);
|
||||
Route::post("/code/loadTable", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'loadTable']);
|
||||
Route::post("/code/generate", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'generate']);
|
||||
Route::post("/code/generateFile", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'generateFile']);
|
||||
Route::post("/code/sync", [\plugin\saiadmin\app\controller\tool\GenerateTablesController::class, 'sync']);
|
||||
});
|
||||
|
||||
Route::disableDefaultRoute('saiadmin');
|
||||
74
server/plugin/saiadmin/config/saithink.php
Normal file
74
server/plugin/saiadmin/config/saithink.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
|
||||
'access_exp' => 8 * 60 * 60, // 登录token有效期,默认8小时
|
||||
|
||||
// 验证码存储模式
|
||||
'captcha' => [
|
||||
// 验证码存储模式 session或者cache
|
||||
'mode' => getenv('CAPTCHA_MODE'),
|
||||
// 验证码过期时间 (秒)
|
||||
'expire' => 300,
|
||||
],
|
||||
|
||||
// excel模板下载路径
|
||||
'template' => base_path(). '/plugin/saiadmin/public/template',
|
||||
|
||||
// excel导出文件路径
|
||||
'export_path' => base_path() . '/plugin/saiadmin/public/export/',
|
||||
|
||||
// 文件开启hash验证,开启后上传文件将会判断数据库中是否存在,如果存在直接获取
|
||||
'file_hash' => false,
|
||||
|
||||
// 用户信息缓存
|
||||
'user_cache' => [
|
||||
'prefix' => 'saiadmin:user_cache:info_',
|
||||
'expire' => 60 * 60 * 4,
|
||||
'dept' => 'saiadmin:user_cache:dept_',
|
||||
'role' => 'saiadmin:user_cache:role_',
|
||||
'post' => 'saiadmin:user_cache:post_',
|
||||
],
|
||||
|
||||
// 用户权限缓存
|
||||
'button_cache' => [
|
||||
'prefix' => 'saiadmin:button_cache:user_',
|
||||
'expire' => 60 * 60 * 2,
|
||||
'all' => 'saiadmin:button_cache:all',
|
||||
'role' => 'saiadmin:button_cache:role_',
|
||||
'tag' => 'saiadmin:button_cache',
|
||||
],
|
||||
|
||||
// 用户菜单缓存
|
||||
'menu_cache' => [
|
||||
'prefix' => 'saiadmin:menu_cache:user_',
|
||||
'expire' => 60 * 60 * 24 * 7,
|
||||
'tag' => 'saiadmin:menu_cache',
|
||||
],
|
||||
|
||||
// 字典缓存
|
||||
'dict_cache' => [
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'tag' => 'saiadmin:dict_cache',
|
||||
],
|
||||
|
||||
// 配置数据缓存
|
||||
'config_cache' => [
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'prefix' => 'saiadmin:config_cache:config_',
|
||||
'tag' => 'saiadmin:config_cache'
|
||||
],
|
||||
|
||||
// 反射缓存
|
||||
'reflection_cache' => [
|
||||
'tag' => 'saiadmin:reflection',
|
||||
'expire' => 60 * 60 * 24 * 365,
|
||||
'no_need' => 'saiadmin:reflection_cache:no_need_',
|
||||
'attr' => 'saiadmin:reflection_cache:attr_',
|
||||
],
|
||||
|
||||
];
|
||||
6
server/plugin/saiadmin/config/static.php
Normal file
6
server/plugin/saiadmin/config/static.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enable' => true,
|
||||
'middleware' => [], // Static file Middleware
|
||||
];
|
||||
10
server/plugin/saiadmin/config/translation.php
Normal file
10
server/plugin/saiadmin/config/translation.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Default language
|
||||
'locale' => 'zh_CN',
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => base_path() . "/plugin/saiadmin/resource/translations",
|
||||
];
|
||||
10
server/plugin/saiadmin/config/view.php
Normal file
10
server/plugin/saiadmin/config/view.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
use support\view\Raw;
|
||||
use support\view\Twig;
|
||||
use support\view\Blade;
|
||||
use support\view\ThinkPHP;
|
||||
|
||||
return [
|
||||
'handler' => Twig::class
|
||||
];
|
||||
4
server/plugin/saiadmin/db/plugin.sql
Normal file
4
server/plugin/saiadmin/db/plugin.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_menu
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_menu` SELECT NULL, 0, '插件市场', 'Plugin', '', 2, '/plugin', '/plugin/saipackage/install/index', NULL, 'ri:apps-2-ai-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sa_system_menu` WHERE `code` = 'Plugin' AND `create_time` = '2026-01-01 00:00:00' AND ISNULL(`delete_time`));
|
||||
898
server/plugin/saiadmin/db/saiadmin-6.0.sql
Normal file
898
server/plugin/saiadmin/db/saiadmin-6.0.sql
Normal file
File diff suppressed because one or more lines are too long
766
server/plugin/saiadmin/db/saiadmin-pure.sql
Normal file
766
server/plugin/saiadmin/db/saiadmin-pure.sql
Normal file
@@ -0,0 +1,766 @@
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_attachment
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_attachment`;
|
||||
CREATE TABLE `sa_system_attachment` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`category_id` int(11) NULL DEFAULT 0 COMMENT '文件分类',
|
||||
`storage_mode` smallint(6) NULL DEFAULT 1 COMMENT '存储模式 (1 本地 2 阿里云 3 七牛云 4 腾讯云)',
|
||||
`origin_name` varchar(255) NULL DEFAULT NULL COMMENT '原文件名',
|
||||
`object_name` varchar(50) NULL DEFAULT NULL COMMENT '新文件名',
|
||||
`hash` varchar(64) NULL DEFAULT NULL COMMENT '文件hash',
|
||||
`mime_type` varchar(255) NULL DEFAULT NULL COMMENT '资源类型',
|
||||
`storage_path` varchar(100) NULL DEFAULT NULL COMMENT '存储目录',
|
||||
`suffix` varchar(10) NULL DEFAULT NULL COMMENT '文件后缀',
|
||||
`size_byte` bigint(20) NULL DEFAULT NULL COMMENT '字节数',
|
||||
`size_info` varchar(50) NULL DEFAULT NULL COMMENT '文件大小',
|
||||
`url` varchar(255) NULL DEFAULT NULL COMMENT 'url地址',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `hash`(`hash`) USING BTREE,
|
||||
INDEX `idx_url`(`url`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time`) USING BTREE,
|
||||
INDEX `idx_category_id`(`category_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '附件信息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_attachment
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_category
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_category`;
|
||||
CREATE TABLE `sa_system_category` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
|
||||
`parent_id` int(11) NOT NULL DEFAULT 0 COMMENT '父id',
|
||||
`level` varchar(255) NULL DEFAULT NULL COMMENT '组集关系',
|
||||
`category_name` varchar(100) NOT NULL DEFAULT '' COMMENT '分类名称',
|
||||
`sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序',
|
||||
`status` tinyint(1) NULL DEFAULT 1 COMMENT '状态',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `pid`(`parent_id`) USING BTREE,
|
||||
INDEX `sort`(`sort`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 6 COMMENT = '附件分类表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_category
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_category` VALUES (1, 0, '0,', '全部分类', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_category` VALUES (2, 1, '0,1,', '图片分类', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_category` VALUES (3, 1, '0,1,', '文件分类', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_category` VALUES (4, 1, '0,1,', '系统图片', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_category` VALUES (5, 1, '0,1,', '其他分类', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_config
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_config`;
|
||||
CREATE TABLE `sa_system_config` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`group_id` int(11) NULL DEFAULT NULL COMMENT '组id',
|
||||
`key` varchar(32) NOT NULL COMMENT '配置键名',
|
||||
`value` text NULL COMMENT '配置值',
|
||||
`name` varchar(255) NULL DEFAULT NULL COMMENT '配置名称',
|
||||
`input_type` varchar(32) NULL DEFAULT NULL COMMENT '数据输入类型',
|
||||
`config_select_data` varchar(500) NULL DEFAULT NULL COMMENT '配置选项数据',
|
||||
`sort` smallint(5) UNSIGNED NULL DEFAULT 0 COMMENT '排序',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建人',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新人',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`, `key`) USING BTREE,
|
||||
INDEX `group_id`(`group_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 302 COMMENT = '参数配置信息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_config
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_config` VALUES (1, 1, 'site_copyright', 'Copyright © 2024 saithink', '版权信息', 'textarea', NULL, 96, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (2, 1, 'site_desc', '基于vue3 + webman 的极速开发框架', '网站描述', 'textarea', NULL, 97, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (3, 1, 'site_keywords', '后台管理系统', '网站关键字', 'input', NULL, 98, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (4, 1, 'site_name', 'SaiAdmin', '网站名称', 'input', NULL, 99, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (5, 1, 'site_record_number', '9527', '网站备案号', 'input', NULL, 95, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (6, 2, 'upload_allow_file', 'txt,doc,docx,xls,xlsx,ppt,pptx,rar,zip,7z,gz,pdf,wps,md,jpg,png,jpeg,mp4,pem,crt', '文件类型', 'input', NULL, 0, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (7, 2, 'upload_allow_image', 'jpg,jpeg,png,gif,svg,bmp', '图片类型', 'input', NULL, 0, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (8, 2, 'upload_mode', '1', '上传模式', 'select', '[{\"label\":\"本地上传\",\"value\":\"1\"},{\"label\":\"阿里云OSS\",\"value\":\"2\"},{\"label\":\"七牛云\",\"value\":\"3\"},{\"label\":\"腾讯云COS\",\"value\":\"4\"},{\"label\":\"亚马逊S3\",\"value\":\"5\"}]', 99, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (10, 2, 'upload_size', '52428800', '上传大小', 'input', NULL, 88, '单位Byte,1MB=1024*1024Byte', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (11, 2, 'local_root', 'public/storage/', '本地存储路径', 'input', NULL, 0, '本地存储文件路径', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (12, 2, 'local_domain', 'http://127.0.0.1:8787', '本地存储域名', 'input', NULL, 0, 'http://127.0.0.1:8787', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (13, 2, 'local_uri', '/storage/', '本地访问路径', 'input', NULL, 0, '访问是通过domain + uri', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (14, 2, 'qiniu_accessKey', '', '七牛key', 'input', NULL, 0, '七牛云存储secretId', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (15, 2, 'qiniu_secretKey', '', '七牛secret', 'input', NULL, 0, '七牛云存储secretKey', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (16, 2, 'qiniu_bucket', '', '七牛bucket', 'input', NULL, 0, '七牛云存储bucket', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (17, 2, 'qiniu_dirname', '', '七牛dirname', 'input', NULL, 0, '七牛云存储dirname', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (18, 2, 'qiniu_domain', '', '七牛domain', 'input', NULL, 0, '七牛云存储domain', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (19, 2, 'cos_secretId', '', '腾讯Id', 'input', NULL, 0, '腾讯云存储secretId', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (20, 2, 'cos_secretKey', '', '腾讯key', 'input', NULL, 0, '腾讯云secretKey', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (21, 2, 'cos_bucket', '', '腾讯bucket', 'input', NULL, 0, '腾讯云存储bucket', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (22, 2, 'cos_dirname', '', '腾讯dirname', 'input', NULL, 0, '腾讯云存储dirname', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (23, 2, 'cos_domain', '', '腾讯domain', 'input', NULL, 0, '腾讯云存储domain', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (24, 2, 'cos_region', '', '腾讯region', 'input', NULL, 0, '腾讯云存储region', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (25, 2, 'oss_accessKeyId', '', '阿里Id', 'input', NULL, 0, '阿里云存储accessKeyId', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (26, 2, 'oss_accessKeySecret', '', '阿里Secret', 'input', NULL, 0, '阿里云存储accessKeySecret', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (27, 2, 'oss_bucket', '', '阿里bucket', 'input', NULL, 0, '阿里云存储bucket', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (28, 2, 'oss_dirname', '', '阿里dirname', 'input', NULL, 0, '阿里云存储dirname', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (29, 2, 'oss_domain', '', '阿里domain', 'input', NULL, 0, '阿里云存储domain', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (30, 2, 'oss_endpoint', '', '阿里endpoint', 'input', NULL, 0, '阿里云存储endpoint', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (31, 3, 'Host', 'smtp.qq.com', 'SMTP服务器', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (32, 3, 'Port', '465', 'SMTP端口', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (33, 3, 'Username', '', 'SMTP用户名', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (34, 3, 'Password', '', 'SMTP密码', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (35, 3, 'SMTPSecure', 'ssl', 'SMTP验证方式', 'radio', '[\r\n {\"label\":\"ssl\",\"value\":\"ssl\"},\r\n {\"label\":\"tsl\",\"value\":\"tsl\"}\r\n]', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (36, 3, 'From', '', '默认发件人', 'input', '', 100, '默认发件的邮箱地址', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (37, 3, 'FromName', '账户注册', '默认发件名称', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (38, 3, 'CharSet', 'UTF-8', '编码', 'input', '', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (39, 3, 'SMTPDebug', '0', '调试模式', 'radio', '[\r\n {\"label\":\"关闭\",\"value\":\"0\"},\r\n {\"label\":\"client\",\"value\":\"1\"},\r\n {\"label\":\"server\",\"value\":\"2\"}\r\n]', 100, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (40, 2, 's3_key', '', 'key', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (41, 2, 's3_secret', '', 'secret', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (42, 2, 's3_bucket', '', 'bucket', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (43, 2, 's3_dirname', '', 'dirname', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (44, 2, 's3_domain', '', 'domain', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (45, 2, 's3_region', '', 'region', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (46, 2, 's3_version', '', 'version', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (47, 2, 's3_use_path_style_endpoint', '', 'path_style_endpoint', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (48, 2, 's3_endpoint', '', 'endpoint', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config` VALUES (49, 2, 's3_acl', '', 'acl', 'input', '', 0, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_config_group
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_config_group`;
|
||||
CREATE TABLE `sa_system_config_group` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(50) NULL DEFAULT NULL COMMENT '字典名称',
|
||||
`code` varchar(100) NULL DEFAULT NULL COMMENT '字典标示',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建人',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新人',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 4 COMMENT = '参数配置分组表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_config_group
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_config_group` VALUES (1, '站点配置', 'site_config', '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config_group` VALUES (2, '上传配置', 'upload_config', NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_config_group` VALUES (3, '邮件服务', 'email_config', NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_dept`;
|
||||
CREATE TABLE `sa_system_dept` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`parent_id` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '父级ID,0为根节点',
|
||||
`name` varchar(64) NOT NULL COMMENT '部门名称',
|
||||
`code` varchar(64) NULL DEFAULT NULL COMMENT '部门编码',
|
||||
`leader_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '部门负责人ID',
|
||||
`level` varchar(255) NULL DEFAULT '' COMMENT '祖级列表,格式: 0,1,5, (便于查询子孙节点)',
|
||||
`sort` int(11) NULL DEFAULT 0 COMMENT '排序,数字越小越靠前',
|
||||
`status` tinyint(1) NULL DEFAULT 1 COMMENT '状态: 1启用, 0禁用',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_parent_id`(`parent_id`) USING BTREE,
|
||||
INDEX `idx_path`(`level`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1114 COMMENT = '部门表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_dept
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_dept` VALUES (1, 0, '腾讯集团', 'GROUP', 1, '0,', 100, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_dict_data
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_dict_data`;
|
||||
CREATE TABLE `sa_system_dict_data` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`type_id` int(11) UNSIGNED NULL DEFAULT NULL COMMENT '字典类型ID',
|
||||
`label` varchar(50) NULL DEFAULT NULL COMMENT '字典标签',
|
||||
`value` varchar(100) NULL DEFAULT NULL COMMENT '字典值',
|
||||
`color` varchar(50) NULL DEFAULT NULL COMMENT '字典颜色',
|
||||
`code` varchar(100) NULL DEFAULT NULL COMMENT '字典标示',
|
||||
`sort` smallint(5) UNSIGNED NULL DEFAULT 0 COMMENT '排序',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `type_id`(`type_id`) USING BTREE,
|
||||
INDEX `idx_code`(`code`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 50 COMMENT = '字典数据表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_dict_data
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_dict_data` VALUES (2, 2, '本地存储', '1', '#5d87ff', 'upload_mode', 99, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (3, 2, '阿里云OSS', '2', '#f9901f', 'upload_mode', 98, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (4, 2, '七牛云', '3', '#00ced1', 'upload_mode', 97, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (5, 2, '腾讯云COS', '4', '#1d84ff', 'upload_mode', 96, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (6, 2, '亚马逊S3', '5', '#ff80c8', 'upload_mode', 95, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (7, 3, '正常', '1', '#13deb9', 'data_status', 0, 1, '1为正常', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (8, 3, '停用', '2', '#ff4d4f', 'data_status', 0, 1, '2为停用', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (9, 4, '统计页面', 'statistics', '#00ced1', 'dashboard', 100, 1, '管理员用', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (10, 4, '工作台', 'work', '#ff8c00', 'dashboard', 50, 1, '员工使用', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (11, 5, '男', '1', '#5d87ff', 'gender', 0, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (12, 5, '女', '2', '#ff4500', 'gender', 0, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (13, 5, '未知', '3', '#b48df3', 'gender', 0, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (16, 12, '图片', 'image', '#60c041', 'attachment_type', 10, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (17, 12, '文档', 'text', '#1d84ff', 'attachment_type', 9, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (18, 12, '音频', 'audio', '#00ced1', 'attachment_type', 8, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (19, 12, '视频', 'video', '#ff4500', 'attachment_type', 7, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (20, 12, '应用程序', 'application', '#ff8c00', 'attachment_type', 6, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (21, 13, '目录', '1', '#909399', 'menu_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (22, 13, '菜单', '2', '#1e90ff', 'menu_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (23, 13, '按钮', '3', '#ff4500', 'menu_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (24, 13, '外链', '4', '#00ced1', 'menu_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (25, 14, '是', '1', '#60c041', 'yes_or_no', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (26, 14, '否', '2', '#ff4500', 'yes_or_no', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (47, 20, 'URL任务GET', '1', '#5d87ff', 'crontab_task_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (48, 20, 'URL任务POST', '2', '#00ced1', 'crontab_task_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_data` VALUES (49, 20, '类任务', '3', '#ff8c00', 'crontab_task_type', 100, 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_dict_type
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_dict_type`;
|
||||
CREATE TABLE `sa_system_dict_type` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(50) NULL DEFAULT NULL COMMENT '字典名称',
|
||||
`code` varchar(100) NULL DEFAULT NULL COMMENT '字典标示',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_code`(`code`) USING BTREE,
|
||||
INDEX `idx_name`(`name`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 24 COMMENT = '字典类型表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_dict_type
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_dict_type` VALUES (2, '存储模式', 'upload_mode', 1, '上传文件存储模式', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (3, '数据状态', 'data_status', 1, '通用数据状态', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (4, '后台首页', 'dashboard', 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (5, '性别', 'gender', 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (12, '附件类型', 'attachment_type', 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (13, '菜单类型', 'menu_type', 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (14, '是否', 'yes_or_no', 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_dict_type` VALUES (20, '定时任务类型', 'crontab_task_type', 1, '', 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_login_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_login_log`;
|
||||
CREATE TABLE `sa_system_login_log` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`username` varchar(20) NULL DEFAULT NULL COMMENT '用户名',
|
||||
`ip` varchar(45) NULL DEFAULT NULL COMMENT '登录IP地址',
|
||||
`ip_location` varchar(255) NULL DEFAULT NULL COMMENT 'IP所属地',
|
||||
`os` varchar(50) NULL DEFAULT NULL COMMENT '操作系统',
|
||||
`browser` varchar(50) NULL DEFAULT NULL COMMENT '浏览器',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '登录状态 (1成功 2失败)',
|
||||
`message` varchar(50) NULL DEFAULT NULL COMMENT '提示消息',
|
||||
`login_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '登录时间',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `username`(`username`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time`) USING BTREE,
|
||||
INDEX `idx_login_time`(`login_time`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '登录日志表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_login_log
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_mail
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_mail`;
|
||||
CREATE TABLE `sa_system_mail` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '编号',
|
||||
`gateway` varchar(50) NULL DEFAULT NULL COMMENT '网关',
|
||||
`from` varchar(50) NULL DEFAULT NULL COMMENT '发送人',
|
||||
`email` varchar(50) NULL DEFAULT NULL COMMENT '接收人',
|
||||
`code` varchar(20) NULL DEFAULT NULL COMMENT '验证码',
|
||||
`content` varchar(500) NULL DEFAULT NULL COMMENT '邮箱内容',
|
||||
`status` varchar(20) NULL DEFAULT NULL COMMENT '发送状态',
|
||||
`response` varchar(500) NULL DEFAULT NULL COMMENT '返回结果',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '邮件记录' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_mail
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_menu`;
|
||||
CREATE TABLE `sa_system_menu` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`parent_id` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '父级ID',
|
||||
`name` varchar(64) NOT NULL COMMENT '菜单名称',
|
||||
`code` varchar(64) NULL DEFAULT NULL COMMENT '组件名称',
|
||||
`slug` varchar(100) NULL DEFAULT NULL COMMENT '权限标识,如 user:list, user:add',
|
||||
`type` tinyint(1) NOT NULL DEFAULT 1 COMMENT '类型: 1目录, 2菜单, 3按钮/API',
|
||||
`path` varchar(255) NULL DEFAULT NULL COMMENT '路由地址(前端)或API路径(后端)',
|
||||
`component` varchar(255) NULL DEFAULT NULL COMMENT '前端组件路径,如 layout/User',
|
||||
`method` varchar(10) NULL DEFAULT NULL COMMENT '请求方式',
|
||||
`icon` varchar(64) NULL DEFAULT NULL COMMENT '图标',
|
||||
`sort` int(11) NULL DEFAULT 100 COMMENT '排序',
|
||||
`link_url` varchar(255) NULL DEFAULT NULL COMMENT '外部链接',
|
||||
`is_iframe` tinyint(1) NULL DEFAULT 2 COMMENT '是否iframe',
|
||||
`is_keep_alive` tinyint(1) NULL DEFAULT 2 COMMENT '是否缓存',
|
||||
`is_hidden` tinyint(1) NULL DEFAULT 2 COMMENT '是否隐藏',
|
||||
`is_fixed_tab` tinyint(1) NULL DEFAULT 2 COMMENT '是否固定标签页',
|
||||
`is_full_page` tinyint(1) NULL DEFAULT 2 COMMENT '是否全屏',
|
||||
`generate_id` int(11) NULL DEFAULT 0 COMMENT '生成id',
|
||||
`generate_key` varchar(255) NULL DEFAULT NULL COMMENT '生成key',
|
||||
`status` tinyint(1) NULL DEFAULT 1 COMMENT '状态',
|
||||
`remark` varchar(255) NULL DEFAULT NULL,
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_parent_id`(`parent_id`) USING BTREE,
|
||||
INDEX `idx_slug`(`slug`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1000 COMMENT = '菜单权限表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_menu
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_menu` VALUES (1, 0, '仪表盘', 'Dashboard', NULL, 1, '/dashboard', NULL, NULL, 'ri:pie-chart-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (2, 1, '工作台', 'Console', NULL, 2, 'console', '/dashboard/console', NULL, 'ri:home-smile-2-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (3, 0, '系统管理', 'System', NULL, 1, '/system', NULL, NULL, 'ri:user-3-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (4, 3, '用户管理', 'User', NULL, 2, 'user', '/system/user', NULL, 'ri:user-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (5, 3, '部门管理', 'Dept', NULL, 2, 'dept', '/system/dept', NULL, 'ri:node-tree', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (6, 3, '角色管理', 'Role', NULL, 2, 'role', '/system/role', NULL, 'ri:admin-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (7, 3, '岗位管理', 'Post', '', 2, 'post', '/system/post', NULL, 'ri:signpost-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (8, 3, '菜单管理', 'Menu', NULL, 2, 'menu', '/system/menu', NULL, 'ri:menu-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (10, 0, '运维管理', 'Safeguard', NULL, 1, '/safeguard', '', NULL, 'ri:shield-check-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (11, 10, '缓存管理', 'Cache', '', 2, 'cache', '/safeguard/cache', NULL, 'ri:keyboard-box-line', 80, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (12, 10, '数据字典', 'Dict', NULL, 2, 'dict', '/safeguard/dict', NULL, 'ri:database-2-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (13, 10, '附件管理', 'Attachment', '', 2, 'attachment', '/safeguard/attachment', NULL, 'ri:file-cloud-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (14, 10, '数据表维护', 'Database', '', 2, 'database', '/safeguard/database', NULL, 'ri:database-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (15, 10, '登录日志', 'LoginLog', '', 2, 'login-log', '/safeguard/login-log', NULL, 'ri:login-circle-line', 50, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (16, 10, '操作日志', 'OperLog', '', 2, 'oper-log', '/safeguard/oper-log', NULL, 'ri:shield-keyhole-line', 50, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (17, 10, '邮件日志', 'EmailLog', '', 2, 'email-log', '/safeguard/email-log', NULL, 'ri:mail-line', 50, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (18, 3, '系统设置', 'Config', NULL, 2, 'config', '/system/config', NULL, 'ri:settings-4-line', 100, NULL, 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (19, 0, '官方文档', 'Document', '', 4, '', '', NULL, 'ri:file-copy-2-fill', 90, 'https://saithink.top', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (20, 4, '数据列表', '', 'core:user:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (21, 1, '个人中心', 'UserCenter', '', 2, 'user-center', '/dashboard/user-center/index', NULL, 'ri:user-2-line', 100, '', 2, 2, 1, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (22, 4, '添加', '', 'core:user:save', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (23, 4, '修改', '', 'core:user:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (24, 4, '读取', '', 'core:user:read', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (25, 4, '删除', '', 'core:user:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (26, 4, '重置密码', '', 'core:user:password', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (27, 4, '清理缓存', '', 'core:user:cache', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (28, 4, '设置工作台', '', 'core:user:home', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (29, 5, '数据列表', '', 'core:dept:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (30, 5, '添加', '', 'core:dept:save', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (31, 5, '修改', '', 'core:dept:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (32, 5, '读取', '', 'core:dept:read', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (33, 5, '删除', '', 'core:dept:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (34, 6, '添加', '', 'core:role:save', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (35, 6, '数据列表', '', 'core:role:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (36, 6, '修改', '', 'core:role:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (37, 6, '读取', '', 'core:role:read', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (38, 6, '删除', '', 'core:role:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (39, 6, '菜单权限', '', 'core:role:menu', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (41, 7, '数据列表', '', 'core:post:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (42, 7, '添加', '', 'core:post:save', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (43, 7, '修改', '', 'core:post:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (44, 7, '读取', '', 'core:post:read', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (45, 7, '删除', '', 'core:post:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (46, 7, '导入', '', 'core:post:import', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (47, 7, '导出', '', 'core:post:export', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (48, 8, '数据列表', '', 'core:menu:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (49, 8, '读取', '', 'core:menu:read', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (50, 8, '添加', '', 'core:menu:save', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (51, 8, '修改', '', 'core:menu:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (52, 8, '删除', '', 'core:menu:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (53, 18, '数据列表', '', 'core:config:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (54, 18, '管理', '', 'core:config:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (55, 18, '修改', '', 'core:config:update', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (56, 12, '数据列表', '', 'core:dict:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (57, 12, '管理', '', 'core:dict:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (58, 13, '数据列表', '', 'core:attachment:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (59, 13, '管理', '', 'core:attachment:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (60, 14, '数据表列表', '', 'core:database:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (61, 14, '数据表维护', '', 'core:database:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (62, 14, '回收站数据', '', 'core:recycle:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (63, 14, '回收站管理', '', 'core:recycle:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (64, 15, '数据列表', '', 'core:logs:login', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (65, 15, '删除', '', 'core:logs:deleteLogin', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (66, 16, '数据列表', '', 'core:logs:Oper', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (67, 16, '删除', '', 'core:logs:deleteOper', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (68, 17, '数据列表', '', 'core:email:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (69, 17, '删除', '', 'core:email:destroy', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (70, 10, '服务监控', 'Server', '', 2, 'server', '/safeguard/server', NULL, 'ri:server-line', 90, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (71, 70, '数据列表', '', 'core:server:monitor', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (72, 11, '数据列表', '', 'core:server:cache', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (73, 11, '缓存清理', '', 'core:server:clear', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (74, 2, '登录数据统计', '', 'core:console:list', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (75, 0, '附加权限', 'Permission', '', 1, 'permission', '', NULL, 'ri:apps-2-ai-line', 100, '', 2, 2, 1, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (76, 75, '上传图片', '', 'core:system:uploadImage', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (77, 75, '上传文件', '', 'core:system:uploadFile', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (78, 75, '附件列表', '', 'core:system:resource', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (79, 75, '用户列表', '', 'core:system:user', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (80, 0, '工具', 'Tool', '', 1, '/tool', '', NULL, 'ri:tools-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (81, 80, '代码生成', 'Code', '', 2, 'code', '/tool/code', NULL, 'ri:code-s-slash-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (82, 80, '定时任务', 'Crontab', '', 2, 'crontab', '/tool/crontab', NULL, 'ri:time-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (83, 82, '数据列表', '', 'tool:crontab:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (84, 82, '管理', '', 'tool:crontab:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (85, 82, '运行任务', '', 'tool:crontab:run', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (86, 81, '数据列表', '', 'tool:code:index', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (87, 81, '管理', '', 'tool:code:edit', 3, '', '', NULL, '', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
INSERT INTO `sa_system_menu` VALUES (88, 0, '插件市场', 'Plugin', '', 2, '/plugin', '/plugin/saipackage/install/index', NULL, 'ri:apps-2-ai-line', 100, '', 2, 2, 2, 2, 2, 0, NULL, 1, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_oper_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_oper_log`;
|
||||
CREATE TABLE `sa_system_oper_log` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`username` varchar(20) NULL DEFAULT NULL COMMENT '用户名',
|
||||
`app` varchar(50) NULL DEFAULT NULL COMMENT '应用名称',
|
||||
`method` varchar(20) NULL DEFAULT NULL COMMENT '请求方式',
|
||||
`router` varchar(500) NULL DEFAULT NULL COMMENT '请求路由',
|
||||
`service_name` varchar(30) NULL DEFAULT NULL COMMENT '业务名称',
|
||||
`ip` varchar(45) NULL DEFAULT NULL COMMENT '请求IP地址',
|
||||
`ip_location` varchar(255) NULL DEFAULT NULL COMMENT 'IP所属地',
|
||||
`request_data` text NULL COMMENT '请求数据',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `username`(`username`) USING BTREE,
|
||||
INDEX `idx_create_time`(`create_time`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '操作日志表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_oper_log
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_post
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_post`;
|
||||
CREATE TABLE `sa_system_post` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(50) NULL DEFAULT NULL COMMENT '岗位名称',
|
||||
`code` varchar(100) NULL DEFAULT NULL COMMENT '岗位代码',
|
||||
`sort` smallint(5) UNSIGNED NULL DEFAULT 0 COMMENT '排序',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 87 COMMENT = '岗位信息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_role`;
|
||||
CREATE TABLE `sa_system_role` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(64) NOT NULL COMMENT '角色名称',
|
||||
`code` varchar(64) NOT NULL COMMENT '角色标识(英文唯一),如: hr_manager',
|
||||
`level` int(11) NULL DEFAULT 1 COMMENT '角色级别(1-100):用于行政控制,不可操作级别>=自己的角色',
|
||||
`data_scope` tinyint(4) NULL DEFAULT 1 COMMENT '数据范围: 1全部, 2本部门及下属, 3本部门, 4仅本人, 5自定义',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`sort` int(11) NULL DEFAULT 100,
|
||||
`status` tinyint(1) NULL DEFAULT 1 COMMENT '状态: 1启用, 0禁用',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_slug`(`code`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 17 COMMENT = '角色表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_role` VALUES (1, '超级管理员', 'super_admin', 100, 1, '系统维护者,拥有所有权限', 100, 1, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_role_dept
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_role_dept`;
|
||||
CREATE TABLE `sa_system_role_dept` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`role_id` bigint(20) UNSIGNED NOT NULL,
|
||||
`dept_id` bigint(20) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_role_id`(`role_id`) USING BTREE,
|
||||
INDEX `idx_dept_id`(`dept_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '角色-自定义数据权限关联' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_role_dept
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_role_menu
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_role_menu`;
|
||||
CREATE TABLE `sa_system_role_menu` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`role_id` bigint(20) UNSIGNED NOT NULL,
|
||||
`menu_id` bigint(20) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_menu_id`(`menu_id`) USING BTREE,
|
||||
INDEX `idx_role_id`(`role_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '角色权限关联' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_role_menu
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_user
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_user`;
|
||||
CREATE TABLE `sa_system_user` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(64) NOT NULL COMMENT '登录账号',
|
||||
`password` varchar(255) NOT NULL COMMENT '加密密码',
|
||||
`realname` varchar(64) NULL DEFAULT NULL COMMENT '真实姓名',
|
||||
`gender` varchar(10) NULL DEFAULT NULL COMMENT '性别',
|
||||
`avatar` varchar(255) NULL DEFAULT NULL COMMENT '头像',
|
||||
`email` varchar(128) NULL DEFAULT NULL COMMENT '邮箱',
|
||||
`phone` varchar(20) NULL DEFAULT NULL COMMENT '手机号',
|
||||
`signed` varchar(255) NULL DEFAULT NULL COMMENT '个性签名',
|
||||
`dashboard` varchar(255) NULL DEFAULT 'work' COMMENT '工作台',
|
||||
`dept_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '主归属部门',
|
||||
`is_super` tinyint(1) NULL DEFAULT 0 COMMENT '是否超级管理员: 1是(跳过权限检查), 0否',
|
||||
`status` tinyint(1) NULL DEFAULT 1 COMMENT '状态: 1启用, 0禁用',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`login_time` timestamp(0) NULL DEFAULT NULL COMMENT '最后登录时间',
|
||||
`login_ip` varchar(45) NULL DEFAULT NULL COMMENT '最后登录IP',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `uk_username`(`username`) USING BTREE,
|
||||
INDEX `idx_dept_id`(`dept_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 110 COMMENT = '用户表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_user
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_user` VALUES (1, 'admin', '$2y$10$wnixh48uDnaW/6D9EygDd.OHJK0vQY/4nHaTjMKBCVDBP2NiTatqS', '祭道之上', '2', 'https://image.saithink.top/saiadmin/avatar.jpg', 'saiadmin@admin.com', '15888888888', 'SaiAdmin是兼具设计美学与高效开发的后台系统!', 'statistics', 1, 1, 1, NULL, NULL, NULL, 1, 1, '2026-01-01 00:00:00', '2026-01-01 00:00:00', NULL);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_user_post
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_user_post`;
|
||||
CREATE TABLE `sa_system_user_post` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户主键',
|
||||
`post_id` bigint(20) UNSIGNED NOT NULL COMMENT '岗位主键',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_user_id`(`user_id`) USING BTREE,
|
||||
INDEX `idx_post_id`(`post_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '用户与岗位关联表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_user_post
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_system_user_role
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_system_user_role`;
|
||||
CREATE TABLE `sa_system_user_role` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint(20) UNSIGNED NOT NULL,
|
||||
`role_id` bigint(20) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_role_id`(`role_id`) USING BTREE,
|
||||
INDEX `idx_user_id`(`user_id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 55 COMMENT = '用户角色关联' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_system_user_role
|
||||
-- ----------------------------
|
||||
INSERT INTO `sa_system_user_role` VALUES (1, 1, 1);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_tool_crontab
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_tool_crontab`;
|
||||
CREATE TABLE `sa_tool_crontab` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`name` varchar(100) NULL DEFAULT NULL COMMENT '任务名称',
|
||||
`type` smallint(6) NULL DEFAULT 4 COMMENT '任务类型',
|
||||
`target` varchar(500) NULL DEFAULT NULL COMMENT '调用任务字符串',
|
||||
`parameter` varchar(1000) NULL DEFAULT NULL COMMENT '调用任务参数',
|
||||
`task_style` tinyint(1) NULL DEFAULT NULL COMMENT '执行类型',
|
||||
`rule` varchar(32) NULL DEFAULT NULL COMMENT '任务执行表达式',
|
||||
`singleton` smallint(6) NULL DEFAULT 1 COMMENT '是否单次执行 (1 是 2 不是)',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '状态 (1正常 2停用)',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '定时任务信息表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_tool_crontab_log
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_tool_crontab_log`;
|
||||
CREATE TABLE `sa_tool_crontab_log` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`crontab_id` int(11) UNSIGNED NULL DEFAULT NULL COMMENT '任务ID',
|
||||
`name` varchar(255) NULL DEFAULT NULL COMMENT '任务名称',
|
||||
`target` varchar(500) NULL DEFAULT NULL COMMENT '任务调用目标字符串',
|
||||
`parameter` varchar(1000) NULL DEFAULT NULL COMMENT '任务调用参数',
|
||||
`exception_info` varchar(2000) NULL DEFAULT NULL COMMENT '异常信息',
|
||||
`status` smallint(6) NULL DEFAULT 1 COMMENT '执行状态 (1成功 2失败)',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '定时任务执行日志表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_tool_crontab_log
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_tool_generate_columns
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_tool_generate_columns`;
|
||||
CREATE TABLE `sa_tool_generate_columns` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`table_id` int(11) UNSIGNED NULL DEFAULT NULL COMMENT '所属表ID',
|
||||
`column_name` varchar(200) NULL DEFAULT NULL COMMENT '字段名称',
|
||||
`column_comment` varchar(255) NULL DEFAULT NULL COMMENT '字段注释',
|
||||
`column_type` varchar(50) NULL DEFAULT NULL COMMENT '字段类型',
|
||||
`default_value` varchar(50) NULL DEFAULT NULL COMMENT '默认值',
|
||||
`is_pk` smallint(6) NULL DEFAULT 1 COMMENT '1 非主键 2 主键',
|
||||
`is_required` smallint(6) NULL DEFAULT 1 COMMENT '1 非必填 2 必填',
|
||||
`is_insert` smallint(6) NULL DEFAULT 1 COMMENT '1 非插入字段 2 插入字段',
|
||||
`is_edit` smallint(6) NULL DEFAULT 1 COMMENT '1 非编辑字段 2 编辑字段',
|
||||
`is_list` smallint(6) NULL DEFAULT 1 COMMENT '1 非列表显示字段 2 列表显示字段',
|
||||
`is_query` smallint(6) NULL DEFAULT 1 COMMENT '1 非查询字段 2 查询字段',
|
||||
`is_sort` smallint(6) NULL DEFAULT 1 COMMENT '1 非排序 2 排序',
|
||||
`query_type` varchar(100) NULL DEFAULT 'eq' COMMENT '查询方式 eq 等于, neq 不等于, gt 大于, lt 小于, like 范围',
|
||||
`view_type` varchar(100) NULL DEFAULT 'text' COMMENT '页面控件,text, textarea, password, select, checkbox, radio, date, upload, ma-upload(封装的上传控件)',
|
||||
`dict_type` varchar(200) NULL DEFAULT NULL COMMENT '字典类型',
|
||||
`allow_roles` varchar(255) NULL DEFAULT NULL COMMENT '允许查看该字段的角色',
|
||||
`options` varchar(1000) NULL DEFAULT NULL COMMENT '字段其他设置',
|
||||
`sort` tinyint(3) UNSIGNED NULL DEFAULT 0 COMMENT '排序',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '代码生成业务字段表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_tool_generate_columns
|
||||
-- ----------------------------
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sa_tool_generate_tables
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `sa_tool_generate_tables`;
|
||||
CREATE TABLE `sa_tool_generate_tables` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`table_name` varchar(200) NULL DEFAULT NULL COMMENT '表名称',
|
||||
`table_comment` varchar(500) NULL DEFAULT NULL COMMENT '表注释',
|
||||
`stub` varchar(50) NULL DEFAULT NULL COMMENT 'stub类型',
|
||||
`template` varchar(50) NULL DEFAULT NULL COMMENT '模板名称',
|
||||
`namespace` varchar(255) NULL DEFAULT NULL COMMENT '命名空间',
|
||||
`package_name` varchar(100) NULL DEFAULT NULL COMMENT '控制器包名',
|
||||
`business_name` varchar(50) NULL DEFAULT NULL COMMENT '业务名称',
|
||||
`class_name` varchar(50) NULL DEFAULT NULL COMMENT '类名称',
|
||||
`menu_name` varchar(100) NULL DEFAULT NULL COMMENT '生成菜单名',
|
||||
`belong_menu_id` int(11) NULL DEFAULT NULL COMMENT '所属菜单',
|
||||
`tpl_category` varchar(100) NULL DEFAULT NULL COMMENT '生成类型,single 单表CRUD,tree 树表CRUD,parent_sub父子表CRUD',
|
||||
`generate_type` smallint(6) NULL DEFAULT 1 COMMENT '1 压缩包下载 2 生成到模块',
|
||||
`generate_path` varchar(100) NULL DEFAULT 'saiadmin-artd' COMMENT '前端根目录',
|
||||
`generate_model` smallint(6) NULL DEFAULT 1 COMMENT '1 软删除 2 非软删除',
|
||||
`generate_menus` varchar(255) NULL DEFAULT NULL COMMENT '生成菜单列表',
|
||||
`build_menu` smallint(6) NULL DEFAULT 1 COMMENT '是否构建菜单',
|
||||
`component_type` smallint(6) NULL DEFAULT 1 COMMENT '组件显示方式',
|
||||
`options` varchar(1500) NULL DEFAULT NULL COMMENT '其他业务选项',
|
||||
`form_width` int(11) NULL DEFAULT 800 COMMENT '表单宽度',
|
||||
`is_full` tinyint(1) NULL DEFAULT 1 COMMENT '是否全屏',
|
||||
`remark` varchar(255) NULL DEFAULT NULL COMMENT '备注',
|
||||
`source` varchar(255) NULL DEFAULT NULL COMMENT '数据源',
|
||||
`created_by` int(11) NULL DEFAULT NULL COMMENT '创建者',
|
||||
`updated_by` int(11) NULL DEFAULT NULL COMMENT '更新者',
|
||||
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
|
||||
`delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '代码生成业务表' ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of sa_tool_generate_tables
|
||||
-- ----------------------------
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
22
server/plugin/saiadmin/exception/ApiException.php
Normal file
22
server/plugin/saiadmin/exception/ApiException.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\exception;
|
||||
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use support\exception\BusinessException;
|
||||
|
||||
/**
|
||||
* 常规操作异常-只返回json数据,不记录异常日志
|
||||
*/
|
||||
class ApiException extends BusinessException
|
||||
{
|
||||
public function render(Request $request): ?Response
|
||||
{
|
||||
return json(['code' => $this->getCode() ?: 500, 'message' => $this->getMessage()]);
|
||||
}
|
||||
}
|
||||
20
server/plugin/saiadmin/exception/SystemException.php
Normal file
20
server/plugin/saiadmin/exception/SystemException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 系统接口错误-返回json数据,并且记录异常日志
|
||||
*/
|
||||
class SystemException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message, $code = 400, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
69
server/plugin/saiadmin/process/Task.php
Normal file
69
server/plugin/saiadmin/process/Task.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\process;
|
||||
|
||||
use plugin\saiadmin\app\logic\tool\CrontabLogic;
|
||||
use Webman\Channel\Client;
|
||||
use Workerman\Crontab\Crontab;
|
||||
|
||||
class Task
|
||||
{
|
||||
protected $logic; //login对象
|
||||
public $crontabIds = []; //定时任务表主键id => Crontab对象id
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$dbName = env('DB_NAME');
|
||||
if (!empty($dbName)) {
|
||||
$this->logic = new CrontabLogic();
|
||||
// 连接webman channel服务
|
||||
Client::connect();
|
||||
// 订阅某个自定义事件并注册回调,收到事件后会自动触发此回调
|
||||
Client::on('crontab', function ($data) {
|
||||
$this->reload($data);
|
||||
});
|
||||
}
|
||||
}
|
||||
public function onWorkerStart()
|
||||
{
|
||||
$dbName = env('DB_NAME');
|
||||
if (!empty($dbName)) {
|
||||
$this->initStart();
|
||||
}
|
||||
}
|
||||
|
||||
public function initStart()
|
||||
{
|
||||
$logic = new CrontabLogic();
|
||||
$taskList = $logic->getAll($logic->search(['status' => 1]));
|
||||
foreach ($taskList as $item) {
|
||||
$crontab = new Crontab($item['rule'], function () use ($item) {
|
||||
$this->logic->run($item['id']);
|
||||
});
|
||||
$this->crontabIds[intval($item['id'])] = $crontab->getId(); //存储定时任务表主键id => Crontab对象id
|
||||
echo PHP_EOL . date('Y-m-d H:i:s') . " => 定时任务[" . $item['id'] . "][" . $item['name'] . "]:启动成功" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
public function reload($data)
|
||||
{
|
||||
$id = intval($data['args'] ?? 0); //定时任务表主键id
|
||||
if (isset($this->crontabIds[$id])) {
|
||||
Crontab::remove($this->crontabIds[$id]);
|
||||
unset($this->crontabIds[$id]); //删除定时任务表主键id => Crontab对象id
|
||||
echo PHP_EOL . date('Y-m-d H:i:s') . " => 定时任务[" . $id . "]:移除成功" . PHP_EOL;
|
||||
}
|
||||
$item = $this->logic->read($id);// 查询定时任务表数据
|
||||
if ($item && $item['status'] == 1) {
|
||||
$crontab = new Crontab($item['rule'], function () use ($item) {
|
||||
$this->logic->run($item['id']);
|
||||
});
|
||||
$this->crontabIds[$id] = $crontab->getId(); //存储定时任务表主键id => Crontab对象id
|
||||
echo PHP_EOL . date('Y-m-d H:i:s') . " => 定时任务[" . $item['id'] . "][" . $item['name'] . "]:启动成功" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
server/plugin/saiadmin/process/Test.php
Normal file
15
server/plugin/saiadmin/process/Test.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\process;
|
||||
|
||||
class Test
|
||||
{
|
||||
public function run($args): void
|
||||
{
|
||||
echo '任务[Test]调用:' . date('Y-m-d H:i:s') . "\n";
|
||||
}
|
||||
}
|
||||
7
server/plugin/saiadmin/public/assets/bootstrap.min.css
vendored
Normal file
7
server/plugin/saiadmin/public/assets/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
server/plugin/saiadmin/public/assets/jquery.min.js
vendored
Normal file
2
server/plugin/saiadmin/public/assets/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
server/plugin/saiadmin/public/template/template.xlsx
Normal file
BIN
server/plugin/saiadmin/public/template/template.xlsx
Normal file
Binary file not shown.
100
server/plugin/saiadmin/service/EmailService.php
Normal file
100
server/plugin/saiadmin/service/EmailService.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\service;
|
||||
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigLogic;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\utils\Arr;
|
||||
|
||||
/**
|
||||
* 邮件服务类
|
||||
*/
|
||||
class EmailService
|
||||
{
|
||||
/**
|
||||
* 读取配置
|
||||
* @return array
|
||||
*/
|
||||
public static function getConfig(): array
|
||||
{
|
||||
$logic = new SystemConfigLogic();
|
||||
$config = $logic->getGroup('email_config');
|
||||
if (!$config) {
|
||||
throw new ApiException('未设置邮件配置');
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Mailer
|
||||
* @return PHPMailer
|
||||
*/
|
||||
public static function getMailer(): PHPMailer
|
||||
{
|
||||
if (!class_exists(PHPMailer::class)) {
|
||||
throw new ApiException('请执行 composer require phpmailer/phpmailer 并重启');
|
||||
}
|
||||
$config = static::getConfig();
|
||||
$mailer = new PHPMailer();
|
||||
$mailer->SMTPDebug = intval(Arr::getConfigValue($config,'SMTPDebug'));
|
||||
$mailer->isSMTP();
|
||||
$mailer->Host = Arr::getConfigValue($config,'Host');
|
||||
$mailer->SMTPAuth = true;
|
||||
$mailer->CharSet = Arr::getConfigValue($config,'CharSet');
|
||||
$mailer->Username = Arr::getConfigValue($config,'Username');
|
||||
$mailer->Password = Arr::getConfigValue($config,'Password');
|
||||
$mailer->SMTPSecure = Arr::getConfigValue($config,'SMTPSecure');
|
||||
$mailer->Port = Arr::getConfigValue($config,'Port');
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
* @param $from
|
||||
* @param $to
|
||||
* @param $subject
|
||||
* @param $content
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function send($from, $to, $subject, $content): string
|
||||
{
|
||||
$mailer = static::getMailer();
|
||||
call_user_func_array([$mailer, 'setFrom'], (array)$from);
|
||||
call_user_func_array([$mailer, 'addAddress'], (array)$to);
|
||||
$mailer->Subject = $subject;
|
||||
$mailer->isHTML(true);
|
||||
$mailer->Body = $content;
|
||||
$mailer->send();
|
||||
return $mailer->ErrorInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照模版发送
|
||||
* @param string|array $to
|
||||
* @param $subject
|
||||
* @param $content
|
||||
* @param array $templateData
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function sendByTemplate($to, $subject, $content, array $templateData = []): string
|
||||
{
|
||||
if ($templateData) {
|
||||
$search = [];
|
||||
foreach ($templateData as $key => $value) {
|
||||
$search[] = '{' . $key . '}';
|
||||
}
|
||||
$content = str_replace($search, array_values($templateData), $content);
|
||||
}
|
||||
$config = static::getConfig();
|
||||
return static::send([Arr::getConfigValue($config,'From'), Arr::getConfigValue($config,'FromName')], $to, $subject, $content);
|
||||
}
|
||||
|
||||
}
|
||||
136
server/plugin/saiadmin/service/OpenSpoutWriter.php
Normal file
136
server/plugin/saiadmin/service/OpenSpoutWriter.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace plugin\saiadmin\service;
|
||||
|
||||
use OpenSpout\Common\Entity\Style\Border;
|
||||
use OpenSpout\Common\Entity\Style\BorderPart;
|
||||
use OpenSpout\Writer\XLSX\Writer;
|
||||
use OpenSpout\Common\Entity\Row;
|
||||
use OpenSpout\Common\Entity\Style\Style;
|
||||
|
||||
/**
|
||||
* Excel写入类
|
||||
* OpenSpout
|
||||
*/
|
||||
class OpenSpoutWriter
|
||||
{
|
||||
/**
|
||||
* 操作实例
|
||||
* @var Writer
|
||||
*/
|
||||
protected $instance;
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
* @var string
|
||||
*/
|
||||
protected $filepath;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param string $fileName 文件名称
|
||||
*/
|
||||
public function __construct(string $fileName)
|
||||
{
|
||||
$this->filepath = $this->getFileName($fileName);
|
||||
$this->instance = new Writer();
|
||||
$this->instance->openToFile($this->filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的文件路径
|
||||
* @param string $fileName
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName(string $fileName): string
|
||||
{
|
||||
$path = config('plugin.saiadmin.saithink.export_path',base_path() . '/plugin/saiadmin/public/export/');
|
||||
@mkdir($path, 0777, true);
|
||||
return $path . $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表格宽度
|
||||
* @param array $width 宽度数组
|
||||
* @return void
|
||||
*/
|
||||
public function setWidth(array $width = [])
|
||||
{
|
||||
if (empty($width)) {
|
||||
return;
|
||||
}
|
||||
$sheet = $this->instance->getCurrentSheet();
|
||||
foreach ($width as $key => $value) {
|
||||
$sheet->setColumnWidth($value, $key + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表头
|
||||
* @param array $header 表头数组
|
||||
* @param $style
|
||||
* @return void
|
||||
*/
|
||||
public function setHeader(array $header = [], $style = null): void
|
||||
{
|
||||
if (empty($style)) {
|
||||
$border = new Border(
|
||||
new BorderPart("top", "black", "thin"),
|
||||
new BorderPart("right", "black", "thin"),
|
||||
new BorderPart("bottom", "black", "thin"),
|
||||
new BorderPart("left", "black", "thin"),
|
||||
);
|
||||
$style = new Style();
|
||||
$style->setFontBold();
|
||||
$style->setCellAlignment("center");
|
||||
$style->setBorder($border);
|
||||
}
|
||||
$rowFromValues = Row::fromValues($header, $style);
|
||||
$this->instance->addRow($rowFromValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据
|
||||
* @param array $data 数据数组
|
||||
* @param $style
|
||||
* @return void
|
||||
*/
|
||||
public function setData(array $data = [], $style = null, array $filter = []): void
|
||||
{
|
||||
if (empty($style)) {
|
||||
$border = new Border(
|
||||
new BorderPart("top", "black", "thin"),
|
||||
new BorderPart("right", "black", "thin"),
|
||||
new BorderPart("bottom", "black", "thin"),
|
||||
new BorderPart("left", "black", "thin"),
|
||||
);
|
||||
$style = new Style();
|
||||
$style->setCellAlignment("center");
|
||||
$style->setBorder($border);
|
||||
}
|
||||
foreach($data as $row) {
|
||||
if (!empty($filter)) {
|
||||
foreach ($filter as $key => $value) {
|
||||
foreach ($value as $item) {
|
||||
if ($item['value'] == $row[$key]) {
|
||||
$row[$key] = $item['label'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$rowFromValues = Row::fromValues($row, $style);
|
||||
$this->instance->addRow($rowFromValues);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件
|
||||
* @return string
|
||||
*/
|
||||
public function returnFile(): string
|
||||
{
|
||||
$this->instance->close();
|
||||
return $this->filepath;
|
||||
}
|
||||
|
||||
}
|
||||
51
server/plugin/saiadmin/service/Permission.php
Normal file
51
server/plugin/saiadmin/service/Permission.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\saiadmin\service;
|
||||
|
||||
/**
|
||||
* 权限注解
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
|
||||
class Permission
|
||||
{
|
||||
/**
|
||||
* 权限标题/名称
|
||||
*/
|
||||
public string $title;
|
||||
|
||||
/**
|
||||
* 权限标识(唯一,格式如:module:controller:action)
|
||||
*/
|
||||
public ?string $slug = null;
|
||||
|
||||
/**
|
||||
* 构造函数 #[Permission(title:'标题', slug:'标识')]
|
||||
* @param string|null $title
|
||||
* @param string|null $slug
|
||||
*/
|
||||
public function __construct(
|
||||
?string $title = null,
|
||||
?string $slug = null,
|
||||
)
|
||||
{
|
||||
$this->title = $title ?? '';
|
||||
$this->slug = $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限标题
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限标识
|
||||
*/
|
||||
public function getSlug(): ?string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
}
|
||||
137
server/plugin/saiadmin/service/storage/ChunkUploadService.php
Normal file
137
server/plugin/saiadmin/service/storage/ChunkUploadService.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
namespace plugin\saiadmin\service\storage;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigLogic;
|
||||
use plugin\saiadmin\app\model\system\SystemAttachment;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\utils\Arr;
|
||||
|
||||
/**
|
||||
* 切片上传服务
|
||||
*/
|
||||
class ChunkUploadService
|
||||
{
|
||||
/**
|
||||
* 基础配置
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
protected $path;
|
||||
|
||||
protected $folder = "chunk";
|
||||
|
||||
/**
|
||||
* 初始化切片上传服务
|
||||
*/
|
||||
public function __construct($folder = "chunk")
|
||||
{
|
||||
$logic = new SystemConfigLogic();
|
||||
$this->folder = $folder;
|
||||
$this->config = $logic->getGroup('upload_config');
|
||||
$this->path = $this->checkPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并创建上传路径
|
||||
* @return string
|
||||
*/
|
||||
public function checkPath(): string
|
||||
{
|
||||
$root = Arr::getConfigValue($this->config, 'local_root');
|
||||
$path = base_path() . DIRECTORY_SEPARATOR . $root . $this->folder . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查切片文件上传状态
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function checkChunk($data): array
|
||||
{
|
||||
$allow_file = Arr::getConfigValue($this->config, 'upload_allow_file');
|
||||
if (!in_array($data['ext'], explode(',', $allow_file))) {
|
||||
throw new ApiException('不支持该格式的文件上传');
|
||||
}
|
||||
// 检查已经上传的分片文件
|
||||
for ($i = 0; $i < $data['total']; ++$i) {
|
||||
$chunkFile = $this->path . "{$data['hash']}_{$data['total']}_{$i}.chunk";
|
||||
if (!file_exists($chunkFile)) {
|
||||
if ($i == 0) {
|
||||
return $this->uploadChunk($data);
|
||||
} else {
|
||||
return ['chunk' => $i, 'status' => 'resume'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 分片文件已经全部上传
|
||||
return ['chunk' => $i, 'status' => 'success'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传切片
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function uploadChunk($data): array
|
||||
{
|
||||
$allow_file = Arr::getConfigValue($this->config, 'upload_allow_file');
|
||||
if (!in_array($data['ext'], explode(',', $allow_file))) {
|
||||
throw new ApiException('不支持该格式的文件上传');
|
||||
}
|
||||
$request = request();
|
||||
if (!$request) {
|
||||
throw new ApiException('切片上传服务必须在 HTTP 请求环境下调用');
|
||||
}
|
||||
$uploadFile = current($request->file());
|
||||
$chunkName = $this->path . "{$data['hash']}_{$data['total']}_{$data['index']}.chunk";
|
||||
$uploadFile->move($chunkName);
|
||||
if (($data['index'] + 1) == $data['total']) {
|
||||
return $this->mergeChunk($data);
|
||||
}
|
||||
return ['chunk' => $data['index'], 'status' => 'success'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并切片文件
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function mergeChunk($data): array
|
||||
{
|
||||
$filePath = $this->path . $data['hash'] . '.' . $data['ext'];
|
||||
$fileHandle = fopen($filePath, 'w');
|
||||
for ($i = 0; $i < $data['total']; ++$i) {
|
||||
$chunkFile = $this->path . "{$data['hash']}_{$data['total']}_{$i}.chunk";
|
||||
if (!file_exists($chunkFile)) {
|
||||
throw new ApiException('切片文件查找失败,请重新上传');
|
||||
}
|
||||
fwrite($fileHandle, file_get_contents($chunkFile));
|
||||
unlink($chunkFile);
|
||||
}
|
||||
|
||||
$domain = Arr::getConfigValue($this->config, 'local_domain');
|
||||
$uri = Arr::getConfigValue($this->config, 'local_uri');
|
||||
$baseUrl = $domain . $uri . $this->folder . '/';
|
||||
|
||||
$save_path = Arr::getConfigValue($this->config, 'local_root') . $this->folder . '/';
|
||||
$object_name = $data['hash'] . '.' . $data['ext'];
|
||||
|
||||
$info['storage_mode'] = 1;
|
||||
$info['category_id'] = 1;
|
||||
$info['origin_name'] = $data['name'];
|
||||
$info['object_name'] = $object_name;
|
||||
$info['hash'] = $data['hash'];
|
||||
$info['mime_type'] = $data['type'];
|
||||
$info['storage_path'] = $save_path . $object_name;
|
||||
$info['suffix'] = $data['ext'];
|
||||
$info['size_byte'] = $data['size'];
|
||||
$info['size_info'] = formatBytes($data['size']);
|
||||
$info['url'] = $baseUrl . $object_name;
|
||||
SystemAttachment::create($info);
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
136
server/plugin/saiadmin/service/storage/UploadService.php
Normal file
136
server/plugin/saiadmin/service/storage/UploadService.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\service\storage;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemConfigLogic;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\utils\Arr;
|
||||
|
||||
/**
|
||||
* 文件上传服务
|
||||
* @method static array uploadFile(array $config = []) 上传文件
|
||||
* @method static array uploadBase64(string $base64, string $extension = 'png') 上传Base64文件
|
||||
* @method static array uploadServerFile(string $file_path) 上传服务端文件
|
||||
*/
|
||||
class UploadService
|
||||
{
|
||||
/**
|
||||
* @desc 存储磁盘
|
||||
* @param int $type
|
||||
* @param string $upload
|
||||
* @param bool $_is_file_upload
|
||||
* @return mixed
|
||||
*/
|
||||
public static function disk(int $type = 1, string $upload = 'image', bool $_is_file_upload = true)
|
||||
{
|
||||
$logic = new SystemConfigLogic();
|
||||
$uploadConfig = $logic->getGroup('upload_config');
|
||||
|
||||
$file = current(request()->file());
|
||||
$ext = $file->getUploadExtension() ?: null;
|
||||
$file_size = $file->getSize();
|
||||
if ($file_size > Arr::getConfigValue($uploadConfig, 'upload_size')) {
|
||||
throw new ApiException('文件大小超过限制');
|
||||
}
|
||||
$allow_file = Arr::getConfigValue($uploadConfig, 'upload_allow_file');
|
||||
$allow_image = Arr::getConfigValue($uploadConfig, 'upload_allow_image');
|
||||
if ($upload == 'image') {
|
||||
if (!in_array($ext, explode(',', $allow_image))) {
|
||||
throw new ApiException('不支持该格式的文件上传');
|
||||
}
|
||||
} else {
|
||||
if (!in_array($ext, explode(',', $allow_file))) {
|
||||
throw new ApiException('不支持该格式的文件上传');
|
||||
}
|
||||
}
|
||||
switch ($type) {
|
||||
case 1:
|
||||
// 本地
|
||||
$config = [
|
||||
'adapter' => \Tinywan\Storage\Adapter\LocalAdapter::class,
|
||||
'root' => Arr::getConfigValue($uploadConfig, 'local_root'),
|
||||
'dirname' => function () {
|
||||
return date('Ymd');
|
||||
},
|
||||
'domain' => Arr::getConfigValue($uploadConfig, 'local_domain'),
|
||||
'uri' => Arr::getConfigValue($uploadConfig, 'local_uri'),
|
||||
'algo' => 'sha1',
|
||||
];
|
||||
break;
|
||||
case 2:
|
||||
// 阿里云
|
||||
$config = [
|
||||
'adapter' => \Tinywan\Storage\Adapter\OssAdapter::class,
|
||||
'accessKeyId' => Arr::getConfigValue($uploadConfig, 'oss_accessKeyId'),
|
||||
'accessKeySecret' => Arr::getConfigValue($uploadConfig, 'oss_accessKeySecret'),
|
||||
'bucket' => Arr::getConfigValue($uploadConfig, 'oss_bucket'),
|
||||
'dirname' => Arr::getConfigValue($uploadConfig, 'oss_dirname'),
|
||||
'domain' => Arr::getConfigValue($uploadConfig, 'oss_domain'),
|
||||
'endpoint' => Arr::getConfigValue($uploadConfig, 'oss_endpoint'),
|
||||
'algo' => 'sha1',
|
||||
];
|
||||
break;
|
||||
case 3:
|
||||
// 七牛
|
||||
$config = [
|
||||
'adapter' => \Tinywan\Storage\Adapter\QiniuAdapter::class,
|
||||
'accessKey' => Arr::getConfigValue($uploadConfig, 'qiniu_accessKey'),
|
||||
'secretKey' => Arr::getConfigValue($uploadConfig, 'qiniu_secretKey'),
|
||||
'bucket' => Arr::getConfigValue($uploadConfig, 'qiniu_bucket'),
|
||||
'dirname' => Arr::getConfigValue($uploadConfig, 'qiniu_dirname'),
|
||||
'domain' => Arr::getConfigValue($uploadConfig, 'qiniu_domain'),
|
||||
];
|
||||
break;
|
||||
case 4:
|
||||
// 腾讯云
|
||||
$config = [
|
||||
'adapter' => \Tinywan\Storage\Adapter\CosAdapter::class,
|
||||
'secretId' => Arr::getConfigValue($uploadConfig, 'cos_secretId'),
|
||||
'secretKey' => Arr::getConfigValue($uploadConfig, 'cos_secretKey'),
|
||||
'bucket' => Arr::getConfigValue($uploadConfig, 'cos_bucket'),
|
||||
'dirname' => Arr::getConfigValue($uploadConfig, 'cos_dirname'),
|
||||
'domain' => Arr::getConfigValue($uploadConfig, 'cos_domain'),
|
||||
'region' => Arr::getConfigValue($uploadConfig, 'cos_region'),
|
||||
];
|
||||
break;
|
||||
case 5:
|
||||
// s3 亚马逊
|
||||
$config = [
|
||||
'adapter' => \Tinywan\Storage\Adapter\S3Adapter::class,
|
||||
'key' => Arr::getConfigValue($uploadConfig, 's3_key'),
|
||||
'secret' => Arr::getConfigValue($uploadConfig, 's3_secret'),
|
||||
'bucket' => Arr::getConfigValue($uploadConfig, 's3_bucket'),
|
||||
'dirname' => Arr::getConfigValue($uploadConfig, 's3_dirname'),
|
||||
'domain' => Arr::getConfigValue($uploadConfig, 's3_domain'),
|
||||
'region' => Arr::getConfigValue($uploadConfig, 's3_region'),
|
||||
'version' => Arr::getConfigValue($uploadConfig, 's3_version'),
|
||||
// 'use_path_style_endpoint' => Arr::getConfigValue($uploadConfig,'s3_use_path_style_endpoint'),
|
||||
'use_path_style_endpoint' => filter_var(Arr::getConfigValue($uploadConfig, 's3_use_path_style_endpoint'), FILTER_VALIDATE_BOOLEAN),
|
||||
'endpoint' => Arr::getConfigValue($uploadConfig, 's3_endpoint'),
|
||||
'acl' => Arr::getConfigValue($uploadConfig, 's3_acl'),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
throw new ApiException('该上传模式不存在');
|
||||
}
|
||||
return new $config['adapter'](array_merge(
|
||||
$config,
|
||||
['_is_file_upload' => $_is_file_upload]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return mixed
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
*/
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
return static::disk()->{$name}(...$arguments);
|
||||
}
|
||||
}
|
||||
204
server/plugin/saiadmin/utils/Arr.php
Normal file
204
server/plugin/saiadmin/utils/Arr.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils;
|
||||
|
||||
/**
|
||||
* Array操作类
|
||||
* Class Arr
|
||||
*/
|
||||
class Arr
|
||||
{
|
||||
/**
|
||||
* 获取数组中指定的列
|
||||
* @param array $source
|
||||
* @param string $column
|
||||
* @return array
|
||||
*/
|
||||
public static function getArrayColumn($source, $column): array
|
||||
{
|
||||
$columnArr = [];
|
||||
foreach ($source as $item) {
|
||||
$columnArr[] = $item[$column];
|
||||
}
|
||||
return $columnArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取数组中指定的列
|
||||
* @param array $source
|
||||
* @param array $column
|
||||
* @return array
|
||||
*/
|
||||
public static function getArrayColumns($source, $columns): array
|
||||
{
|
||||
$columnArr = [];
|
||||
foreach ($source as $item) {
|
||||
$tempArr = [];
|
||||
foreach ($columns as $key) {
|
||||
$temp = explode('.', $key);
|
||||
if (count($temp) > 1) {
|
||||
$tempArr[$key] = $item[$temp[0]][$temp[1]];
|
||||
} else {
|
||||
$tempArr[$key] = $item[$key];
|
||||
}
|
||||
}
|
||||
$columnArr[] = $tempArr;
|
||||
}
|
||||
return $columnArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把二维数组中某列设置为key返回
|
||||
* @param array $array 输入数组
|
||||
* @param string $field 要作为键的字段名
|
||||
* @param bool $unique 要做键的字段是否唯一(该字段与记录是否一一对应)
|
||||
* @return array
|
||||
*/
|
||||
public static function fieldAsKey($array, $field, $unique = false) {
|
||||
$result = [];
|
||||
foreach ($array as $item) {
|
||||
if (isset($item[$field])) {
|
||||
if (!$unique && isset($result[$item[$field]])) {
|
||||
$unique = true;
|
||||
$result[$item[$field]] = [($result[$item[$field]])];
|
||||
$result[$item[$field]][] = $item;
|
||||
} elseif ($unique) {
|
||||
$result[$item[$field]][] = $item;
|
||||
} else {
|
||||
$result[$item[$field]] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转字符串去重复
|
||||
* @param array $data
|
||||
* @return false|string[]
|
||||
*/
|
||||
public static function unique(array $data)
|
||||
{
|
||||
return array_unique(explode(',', implode(',', $data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组中去重复过后的指定key值
|
||||
* @param array $list
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public static function getUniqueKey(array $list, string $key)
|
||||
{
|
||||
return array_unique(array_column($list, $key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并二维数组,并且指定key去重, 第一个覆盖第二个
|
||||
* @param array $arr1
|
||||
* @param array $arr2
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public static function mergeArray(array $arr1, array $arr2, string $key)
|
||||
{
|
||||
$arr = array_merge($arr1,$arr2);
|
||||
$tmp_arr = [];
|
||||
foreach($arr as $k => $v) {
|
||||
if(in_array($v[$key], $tmp_arr)) {
|
||||
unset($arr[$k]);
|
||||
} else {
|
||||
$tmp_arr[] = $v[$key];
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 相同键值的合并作为键生成新数组
|
||||
* @param array $data
|
||||
* @param string $field
|
||||
* @return array
|
||||
*/
|
||||
public static function groupSameField(array $data, string $field)
|
||||
{
|
||||
$result= [];
|
||||
foreach ($data as $key => $info) {
|
||||
$result[$info[$field]][] = $info;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成无限级树算法
|
||||
* @param array $arr 输入数组
|
||||
* @param number $pid 根级的pid
|
||||
* @param string $column_name 列名,id|pid父id的名字|children子数组的键名
|
||||
* @return array $ret
|
||||
*/
|
||||
public static function makeTree($arr, $pid = 0, $column_name = 'id|pid|children') {
|
||||
list($idname, $pidname, $cldname) = explode('|', $column_name);
|
||||
$ret = array();
|
||||
foreach ($arr as $k => $v) {
|
||||
if ($v [$pidname] == $pid) {
|
||||
$tmp = $arr [$k];
|
||||
unset($arr [$k]);
|
||||
$tmp [$cldname] = self::makeTree($arr, $v [$idname], $column_name);
|
||||
$ret [] = $tmp;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二位数组按某个键值排序
|
||||
* @param array $arr
|
||||
* @param string $key
|
||||
* @param int $sort
|
||||
* @return array
|
||||
*/
|
||||
public static function sortArray($arr, $key, $sort = SORT_ASC)
|
||||
{
|
||||
array_multisort(array_column($arr,$key),$sort,$arr);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组中根据某一列中某个字段的值来查询这一列数据
|
||||
* @param $array
|
||||
* @param $column
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public static function getArrayByColumn($array, $column, $value): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($array as $key => $item) {
|
||||
if ($item[$column] == $value) {
|
||||
$result = $item;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组中根据key值获取value
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function getConfigValue($array, $key)
|
||||
{
|
||||
foreach ($array as $item) {
|
||||
if ($item['key'] === $key) {
|
||||
return $item['value'];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
121
server/plugin/saiadmin/utils/Captcha.php
Normal file
121
server/plugin/saiadmin/utils/Captcha.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils;
|
||||
|
||||
use support\think\Cache;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 验证码工具类
|
||||
*/
|
||||
class Captcha
|
||||
{
|
||||
/**
|
||||
* 图形验证码
|
||||
* @return array
|
||||
*/
|
||||
public static function imageCaptcha(): array
|
||||
{
|
||||
$builder = new PhraseBuilder(4, 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ');
|
||||
$captcha = new CaptchaBuilder(null, $builder);
|
||||
$captcha->setBackgroundColor(242, 243, 245);
|
||||
$captcha->build(120, 36);
|
||||
|
||||
$uuid = Uuid::uuid4();
|
||||
$key = $uuid->toString();
|
||||
$mode = config('plugin.saiadmin.saithink.captcha.mode', 'session');
|
||||
$expire = config('plugin.saiadmin.saithink.captcha.expire', 300);
|
||||
$code = strtolower($captcha->getPhrase());
|
||||
if ($mode === 'cache') {
|
||||
try {
|
||||
Cache::set($key, $code, $expire);
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
'result' => -1,
|
||||
'message' => '验证码获取失败,请检查缓存配置'
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$request = request();
|
||||
if ($request) {
|
||||
$request->session()->set($key, $code);
|
||||
}
|
||||
}
|
||||
$img_content = $captcha->get();
|
||||
return [
|
||||
'result' => 1,
|
||||
'uuid' => $key,
|
||||
'image' => 'data:image/png;base64,' . base64_encode($img_content)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字验证码
|
||||
* @param string $key
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
public static function numberCaptcha(string $key, int $length = 4): array
|
||||
{
|
||||
$code = str_pad(rand(0, 999999), $length, '0', STR_PAD_LEFT);
|
||||
$mode = config('plugin.saiadmin.saithink.captcha.mode', 'session');
|
||||
$expire = config('plugin.saiadmin.saithink.captcha.expire', 300);
|
||||
if ($mode === 'cache') {
|
||||
try {
|
||||
Cache::set($key, $code, $expire);
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
'result' => -1,
|
||||
'message' => '验证码获取失败,请检查缓存配置'
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$request = request();
|
||||
if ($request) {
|
||||
$request->session()->set($key, $code);
|
||||
}
|
||||
}
|
||||
return [
|
||||
'result' => 1,
|
||||
'uuid' => $key,
|
||||
'code' => $code,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码验证
|
||||
* @param string $uuid
|
||||
* @param string|int $captcha
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkCaptcha(string $uuid, string|int $captcha): bool
|
||||
{
|
||||
$mode = config('plugin.saiadmin.saithink.captcha.mode', 'session');
|
||||
if ($mode === 'cache') {
|
||||
try {
|
||||
$code = Cache::get($uuid);
|
||||
Cache::delete($uuid);
|
||||
} catch (\Exception $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$code = session($uuid);
|
||||
session()->forget($uuid);
|
||||
} catch (\Exception $e) {
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
}
|
||||
if (strtolower($captcha) !== $code) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
254
server/plugin/saiadmin/utils/Helper.php
Normal file
254
server/plugin/saiadmin/utils/Helper.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils;
|
||||
|
||||
/**
|
||||
* 帮助类
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* 数据树形化
|
||||
* @param array $data 数据
|
||||
* @param string $childrenname 子数据名
|
||||
* @param string $keyName 数据key名
|
||||
* @param string $pidName 数据上级key名
|
||||
* @return array
|
||||
*/
|
||||
public static function makeTree(array $data, string $childrenname = 'children', string $keyName = 'id', string $pidName = 'parent_id')
|
||||
{
|
||||
$list = [];
|
||||
foreach ($data as $value) {
|
||||
$list[$value[$keyName]] = $value;
|
||||
}
|
||||
$tree = []; //格式化好的树
|
||||
foreach ($list as $item) {
|
||||
if (isset($list[$item[$pidName]])) {
|
||||
$list[$item[$pidName]][$childrenname][] = &$list[$item[$keyName]];
|
||||
} else {
|
||||
$tree[] = &$list[$item[$keyName]];
|
||||
}
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成Arco菜单
|
||||
* @param array $data 数据
|
||||
* @param string $childrenname 子数据名
|
||||
* @param string $keyName 数据key名
|
||||
* @param string $pidName 数据上级key名
|
||||
* @return array
|
||||
*/
|
||||
public static function makeArcoMenus(array $data, string $childrenname = 'children', string $keyName = 'id', string $pidName = 'parent_id')
|
||||
{
|
||||
$list = [];
|
||||
foreach ($data as $value) {
|
||||
if ($value['type'] === 'M'){
|
||||
$path = '/'.$value['route'];
|
||||
$layout = isset($value['is_layout']) ? $value['is_layout'] : 1;
|
||||
$temp = [
|
||||
$keyName => $value[$keyName],
|
||||
$pidName => $value[$pidName],
|
||||
'name' => $value['route'],
|
||||
'path' => $path,
|
||||
'component' => $value['component'],
|
||||
'redirect' => $value['redirect'],
|
||||
'meta' => [
|
||||
'title' => $value['name'],
|
||||
'type' => $value['type'],
|
||||
'hidden' => $value['is_hidden'] === 1,
|
||||
'layout' => $layout === 1,
|
||||
'hiddenBreadcrumb' => false,
|
||||
'icon' => $value['icon'],
|
||||
],
|
||||
];
|
||||
$list[$value[$keyName]] = $temp;
|
||||
}
|
||||
if ($value['type'] === 'I' || $value['type'] === 'L'){
|
||||
$temp = [
|
||||
$keyName => $value[$keyName],
|
||||
$pidName => $value[$pidName],
|
||||
'name' => $value['code'],
|
||||
'path' => $value['route'],
|
||||
'meta' => [
|
||||
'title' => $value['name'],
|
||||
'type' => $value['type'],
|
||||
'hidden' => $value['is_hidden'] === 1,
|
||||
'hiddenBreadcrumb' => false,
|
||||
'icon' => $value['icon'],
|
||||
],
|
||||
];
|
||||
$list[$value[$keyName]] = $temp;
|
||||
}
|
||||
}
|
||||
$tree = []; //格式化好的树
|
||||
foreach ($list as $item) {
|
||||
if (isset($list[$item[$pidName]])) {
|
||||
$list[$item[$pidName]][$childrenname][] = &$list[$item[$keyName]];
|
||||
} else {
|
||||
$tree[] = &$list[$item[$keyName]];
|
||||
}
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成Artd菜单
|
||||
* @param array $data
|
||||
* @param string $childrenname
|
||||
* @param string $keyName
|
||||
* @param string $pidName
|
||||
* @return array
|
||||
*/
|
||||
public static function makeArtdMenus(array $data, string $childrenname = 'children', string $keyName = 'id', string $pidName = 'parent_id')
|
||||
{
|
||||
$list = [];
|
||||
foreach ($data as $value) {
|
||||
$component = '';
|
||||
if ($value['type'] === 1) {
|
||||
$component = '/index/index';
|
||||
}
|
||||
if ($value['type'] === 2) {
|
||||
$component = $value['component'];
|
||||
}
|
||||
$temp = [
|
||||
$keyName => $value[$keyName],
|
||||
$pidName => $value[$pidName],
|
||||
'name' => $value['code'],
|
||||
'path' => $value['path'],
|
||||
'component' => $component,
|
||||
'meta' => [
|
||||
'title' => $value['name'],
|
||||
'icon' => $value['icon'],
|
||||
'isIframe' => $value['is_iframe'] === 1,
|
||||
'keepAlive' => $value['is_keep_alive'] === 1,
|
||||
'isHide' => $value['is_hidden'] === 1,
|
||||
'fixedTab' => $value['is_fixed_tab'] === 1,
|
||||
'isFullPage' => $value['is_full_page'] === 1,
|
||||
],
|
||||
];
|
||||
if ($value['type'] === 4) {
|
||||
$temp['path'] = '/outside/Iframe';
|
||||
$temp['meta']['link'] = $value['link_url'];
|
||||
}
|
||||
$list[$value[$keyName]] = $temp;
|
||||
}
|
||||
$tree = [];
|
||||
foreach ($list as $item) {
|
||||
if (isset($list[$item[$pidName]])) {
|
||||
$list[$item[$pidName]][$childrenname][] = &$list[$item[$keyName]];
|
||||
} else {
|
||||
$tree[] = &$list[$item[$keyName]];
|
||||
}
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰
|
||||
*/
|
||||
public static function camelize($uncamelized_words,$separator='_')
|
||||
{
|
||||
$uncamelized_words = $separator. str_replace($separator, " ", strtolower($uncamelized_words));
|
||||
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰命名转下划线命名
|
||||
*/
|
||||
public static function uncamelize($camelCaps,$separator='_')
|
||||
{
|
||||
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为驼峰
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function camel(string $value): string
|
||||
{
|
||||
static $cache = [];
|
||||
$key = $value;
|
||||
|
||||
if (isset($cache[$key])) {
|
||||
return $cache[$key];
|
||||
}
|
||||
|
||||
$value = ucwords(str_replace(['-', '_'], ' ', $value));
|
||||
|
||||
return $cache[$key] = str_replace(' ', '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务名称
|
||||
* @param string $tableName
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_business(string $tableName)
|
||||
{
|
||||
$start = strrpos($tableName,'_');
|
||||
if ($start !== false) {
|
||||
$result = substr($tableName, $start + 1);
|
||||
} else {
|
||||
$result = $tableName;
|
||||
}
|
||||
return static::camelize($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务名称
|
||||
* @param string $tableName
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_big_business(string $tableName)
|
||||
{
|
||||
$start = strrpos($tableName,'_');
|
||||
$result = substr($tableName, $start + 1);
|
||||
return static::camel($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 只替换一次字符串
|
||||
* @param $needle
|
||||
* @param $replace
|
||||
* @param $haystack
|
||||
* @return array|mixed|string|string[]
|
||||
*/
|
||||
public static function str_replace_once($needle, $replace, $haystack)
|
||||
{
|
||||
$pos = strpos($haystack, $needle);
|
||||
if ($pos === false) {
|
||||
return $haystack;
|
||||
}
|
||||
return substr_replace($haystack, $replace, $pos, strlen($needle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历目录
|
||||
* @param $template_name
|
||||
* @return array
|
||||
*/
|
||||
public static function get_dir($template_name)
|
||||
{
|
||||
$dir = base_path($template_name);
|
||||
$fileDir = [];
|
||||
if (is_dir($dir)){
|
||||
if ($dh = opendir($dir)){
|
||||
while (($file = readdir($dh)) !== false){
|
||||
if($file != "." && $file != ".."){
|
||||
array_push($fileDir, $file);
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
return $fileDir;
|
||||
}
|
||||
}
|
||||
253
server/plugin/saiadmin/utils/ServerMonitor.php
Normal file
253
server/plugin/saiadmin/utils/ServerMonitor.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils;
|
||||
|
||||
/**
|
||||
* 服务器监控信息
|
||||
*/
|
||||
class ServerMonitor
|
||||
{
|
||||
/**
|
||||
* 获取内存信息
|
||||
* @return array
|
||||
*/
|
||||
public function getMemoryInfo(): array
|
||||
{
|
||||
$totalMem = 0; // 总内存 (Bytes)
|
||||
$freeMem = 0; // 可用/剩余内存 (Bytes)
|
||||
|
||||
if (stristr(PHP_OS, 'WIN')) {
|
||||
// Windows 系统
|
||||
// 一次性获取 总可见内存 和 空闲物理内存 (单位都是 KB)
|
||||
// TotalVisibleMemorySize: 操作系统可识别的内存总数 (比物理内存条总数略少,更准确反映可用上限)
|
||||
// FreePhysicalMemory: 当前可用物理内存
|
||||
$cmd = 'wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /format:csv';
|
||||
$output = shell_exec($cmd);
|
||||
$output = mb_convert_encoding($output ?? '', 'UTF-8', 'GBK, UTF-8, ASCII');
|
||||
$lines = explode("\n", trim($output));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// CSV 格式: Node,FreePhysicalMemory,TotalVisibleMemorySize
|
||||
$parts = str_getcsv($line);
|
||||
|
||||
// 确保解析正确且排除标题行 (通常索引1是Free, 2是Total)
|
||||
if (count($parts) >= 3 && is_numeric($parts[1])) {
|
||||
$freeMem = floatval($parts[1]) * 1024; // KB -> Bytes
|
||||
$totalMem = floatval($parts[2]) * 1024; // KB -> Bytes
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Linux 系统
|
||||
// 读取 /proc/meminfo,效率远高于 shell_exec('cat ...')
|
||||
$memInfo = @file_get_contents('/proc/meminfo');
|
||||
if ($memInfo) {
|
||||
// 使用正则提取 MemTotal 和 MemAvailable (单位 kB)
|
||||
// MemAvailable 是较新的内核指标,比单纯的 MemFree 更准确(包含可回收的缓存)
|
||||
if (preg_match('/^MemTotal:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$totalMem = floatval($matches[1]) * 1024;
|
||||
}
|
||||
|
||||
if (preg_match('/^MemAvailable:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$freeMem = floatval($matches[1]) * 1024;
|
||||
} else {
|
||||
// 如果内核太老没有 MemAvailable,退化使用 MemFree
|
||||
if (preg_match('/^MemFree:\s+(\d+)\s+kB/m', $memInfo, $matches)) {
|
||||
$freeMem = floatval($matches[1]) * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算已用内存
|
||||
$usedMem = $totalMem - $freeMem;
|
||||
|
||||
// 避免除以0
|
||||
$rate = ($totalMem > 0) ? ($usedMem / $totalMem) * 100 : 0;
|
||||
|
||||
// PHP 自身占用
|
||||
$phpMem = memory_get_usage(true);
|
||||
|
||||
return [
|
||||
// 人类可读格式 (String)
|
||||
'total' => $this->formatBytes($totalMem),
|
||||
'free' => $this->formatBytes($freeMem),
|
||||
'used' => $this->formatBytes($usedMem),
|
||||
'php' => $this->formatBytes($phpMem),
|
||||
'rate' => sprintf('%.2f', $rate) . '%',
|
||||
|
||||
// 原始数值 (Float/Int),方便前端图表使用或逻辑判断,统一单位 Bytes
|
||||
'raw' => [
|
||||
'total' => $totalMem,
|
||||
'free' => $freeMem,
|
||||
'used' => $usedMem,
|
||||
'php' => $phpMem,
|
||||
'rate' => round($rate, 2)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PHP及环境信息
|
||||
* @return array
|
||||
*/
|
||||
public function getPhpAndEnvInfo(): array
|
||||
{
|
||||
return [
|
||||
'php_version' => PHP_VERSION,
|
||||
'os' => PHP_OS,
|
||||
'project_path' => BASE_PATH,
|
||||
'memory_limit' => ini_get('memory_limit'),
|
||||
'max_execution_time' => ini_get('max_execution_time'),
|
||||
'error_reporting' => ini_get('error_reporting'),
|
||||
'display_errors' => ini_get('display_errors'),
|
||||
'upload_max_filesize' => ini_get('upload_max_filesize'),
|
||||
'post_max_size' => ini_get('post_max_size'),
|
||||
'extension_dir' => ini_get('extension_dir'),
|
||||
'loaded_extensions' => implode(', ', get_loaded_extensions()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取磁盘信息
|
||||
* @return array
|
||||
*/
|
||||
public function getDiskInfo(): array
|
||||
{
|
||||
$disk = [];
|
||||
|
||||
if (stristr(PHP_OS, 'WIN')) {
|
||||
// Windows 系统
|
||||
// 使用 CSV 格式输出,避免空格解析错误;SkipTop=1 跳过空行
|
||||
// LogicalDisk 包含: Caption(盘符), FreeSpace(剩余字节), Size(总字节)
|
||||
$cmd = 'wmic logicaldisk get Caption,FreeSpace,Size /format:csv';
|
||||
$output = shell_exec($cmd);
|
||||
|
||||
// 转换编码,防止中文乱码(视服务器环境而定,通常 Windows CMD 输出为 GBK)
|
||||
$output = mb_convert_encoding($output ?? '', 'UTF-8', 'GBK, UTF-8, ASCII');
|
||||
$lines = explode("\n", trim($output));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// CSV 格式: Node,Caption,FreeSpace,Size
|
||||
$parts = str_getcsv($line);
|
||||
|
||||
// 确保数据列足够且是一个盘符 (例如 "C:")
|
||||
// 索引通常是: 1=>Caption, 2=>FreeSpace, 3=>Size (索引0通常是计算机名)
|
||||
if (count($parts) >= 4 && preg_match('/^[A-Z]:$/', $parts[1])) {
|
||||
$caption = $parts[1];
|
||||
$freeSpace = floatval($parts[2]);
|
||||
$totalSize = floatval($parts[3]);
|
||||
|
||||
// 避免除以 0 错误(如光驱未放入光盘时 Size 可能为 0 或 null)
|
||||
if ($totalSize <= 0) continue;
|
||||
|
||||
$usedSpace = $totalSize - $freeSpace;
|
||||
|
||||
$disk[] = [
|
||||
'filesystem' => $caption,
|
||||
'mounted_on' => $caption,
|
||||
'size' => $this->formatBytes($totalSize),
|
||||
'available' => $this->formatBytes($freeSpace),
|
||||
'used' => $this->formatBytes($usedSpace),
|
||||
'use_percentage' => sprintf('%.2f', ($usedSpace / $totalSize) * 100) . '%',
|
||||
'raw' => [ // 保留原始数据以便前端或其他逻辑使用
|
||||
'size' => $totalSize,
|
||||
'available' => $freeSpace,
|
||||
'used' => $usedSpace
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Linux 系统
|
||||
// -P: POSIX 输出格式(强制在一行显示,防止长挂载点换行)
|
||||
// -T: 显示文件系统类型
|
||||
// 默认单位是 1K-blocks (1024字节)
|
||||
$output = shell_exec('df -TP 2>/dev/null');
|
||||
$lines = explode("\n", trim($output ?? ''));
|
||||
|
||||
// 过滤表头
|
||||
array_shift($lines);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) continue;
|
||||
|
||||
// 限制分割数量,防止挂载点名称中有空格导致解析错位(虽然 -P 很大程度避免了这个问题,但仍需谨慎)
|
||||
$parts = preg_split('/\s+/', $line);
|
||||
|
||||
// df -TP 输出列: Filesystem(0), Type(1), 1024-blocks(2), Used(3), Available(4), Capacity(5), Mounted on(6)
|
||||
if (count($parts) >= 7) {
|
||||
$filesystem = $parts[0];
|
||||
$type = $parts[1];
|
||||
$totalKB = floatval($parts[2]); // 单位是 KB
|
||||
$usedKB = floatval($parts[3]);
|
||||
$availKB = floatval($parts[4]);
|
||||
$mountedOn = $parts[6];
|
||||
|
||||
// 过滤逻辑:只显示物理硬盘或特定挂载点
|
||||
// 通常过滤掉 tmpfs, devtmpfs, overlay, squashfs(snap) 等
|
||||
// 如果你只想看 /dev/ 开头的物理盘,保留原来的正则即可
|
||||
if (!preg_match('/^\/dev\//', $filesystem)) {
|
||||
// continue; // 根据需求决定是否取消注释此行
|
||||
}
|
||||
// 过滤掉 Docker overlay 或 kubelet 等产生的繁杂挂载
|
||||
if (strpos($filesystem, 'overlay') !== false) continue;
|
||||
|
||||
// 转换为字节
|
||||
$totalSize = $totalKB * 1024;
|
||||
$usedSize = $usedKB * 1024;
|
||||
$freeSize = $availKB * 1024;
|
||||
|
||||
if ($totalSize <= 0) continue;
|
||||
|
||||
$disk[] = [
|
||||
'filesystem' => $filesystem,
|
||||
'type' => $type,
|
||||
'mounted_on' => $mountedOn,
|
||||
'size' => $this->formatBytes($totalSize),
|
||||
'available' => $this->formatBytes($freeSize),
|
||||
'used' => $this->formatBytes($usedSize),
|
||||
'use_percentage' => sprintf('%.2f', ($usedSize / $totalSize) * 100) . '%',
|
||||
'raw' => [
|
||||
'size' => $totalSize,
|
||||
'available' => $freeSize,
|
||||
'used' => $usedSize
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $disk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化字节为可读格式 (B, KB, MB, GB, TB...)
|
||||
* @param int|float $bytes 字节数
|
||||
* @param int $precision 小数点后保留位数
|
||||
* @return string
|
||||
*/
|
||||
private function formatBytes($bytes, int $precision = 2): string
|
||||
{
|
||||
if ($bytes <= 0) {
|
||||
return '0 B';
|
||||
}
|
||||
$base = log($bytes, 1024);
|
||||
$suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
|
||||
// 确保不会数组越界
|
||||
$class = min((int)floor($base), count($suffixes) - 1);
|
||||
return sprintf("%." . $precision . "f", $bytes / pow(1024, $class)) . ' ' . $suffixes[$class];
|
||||
}
|
||||
|
||||
}
|
||||
289
server/plugin/saiadmin/utils/code/CodeEngine.php
Normal file
289
server/plugin/saiadmin/utils/code/CodeEngine.php
Normal file
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils\code;
|
||||
|
||||
use Twig\TwigFilter;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
// 定义目录分隔符常量
|
||||
defined('DS') or define('DS', DIRECTORY_SEPARATOR);
|
||||
|
||||
/**
|
||||
* 代码生成引擎
|
||||
*/
|
||||
class CodeEngine
|
||||
{
|
||||
/**
|
||||
* @var array 值栈
|
||||
*/
|
||||
private array $value = [];
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
* @var string
|
||||
*/
|
||||
private string $stub = 'saiadmin';
|
||||
|
||||
/**
|
||||
* 获取配置文件
|
||||
* @return string[]
|
||||
*/
|
||||
private static function _getConfig(): array
|
||||
{
|
||||
return [
|
||||
'template_path' => base_path() . DS . 'plugin' . DS . 'saiadmin' . DS . 'utils' . DS . 'code' . DS . 'stub',
|
||||
'generate_path' => runtime_path() . DS . 'code_engine' . DS . 'saiadmin',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param array $data 数据
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
// 读取配置文件
|
||||
$config = self::_getConfig();
|
||||
|
||||
// 判断模板是否存在
|
||||
if (!is_dir($config['template_path'])) {
|
||||
throw new ApiException('模板目录不存在!');
|
||||
}
|
||||
// 判断文件生成目录是否存在
|
||||
if (!is_dir($config['generate_path'])) {
|
||||
mkdir($config['generate_path'], 0770, true);
|
||||
}
|
||||
// 赋值
|
||||
$this->value = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模板名称
|
||||
* @param $stub
|
||||
* @return void
|
||||
*/
|
||||
public function setStub($stub): void
|
||||
{
|
||||
$this->stub = $stub;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染文件内容
|
||||
*/
|
||||
public function renderContent($path, $filename): string
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
|
||||
$path = $config['template_path'] . DS . $this->stub . DS . $path;
|
||||
|
||||
$loader = new FilesystemLoader($path);
|
||||
$twig = new Environment($loader);
|
||||
$camelFilter = new TwigFilter('camel', function ($value) {
|
||||
static $cache = [];
|
||||
$key = $value;
|
||||
if (isset($cache[$key])) {
|
||||
return $cache[$key];
|
||||
}
|
||||
$value = ucwords(str_replace(['-', '_'], ' ', $value));
|
||||
return $cache[$key] = str_replace(' ', '', $value);
|
||||
});
|
||||
$boolFilter = new TwigFilter('bool', function ($value) {
|
||||
if ($value == 1) {
|
||||
return 'true';
|
||||
} else {
|
||||
return 'false';
|
||||
}
|
||||
});
|
||||
$formatFilter = new TwigFilter('formatNumber', function ($value) {
|
||||
if (ctype_digit((string) $value)) {
|
||||
return $value;
|
||||
} else {
|
||||
return '1';
|
||||
}
|
||||
});
|
||||
$defaultFilter = new TwigFilter('parseNumber', function ($value) {
|
||||
if ($value) {
|
||||
return $value;
|
||||
} else {
|
||||
return 'null';
|
||||
}
|
||||
});
|
||||
$containsFilter = new TwigFilter('str_contains', function ($haystack, $needle) {
|
||||
return str_contains($haystack ?? '', $needle ?? '');
|
||||
});
|
||||
|
||||
$twig->addFilter($camelFilter);
|
||||
$twig->addFilter($boolFilter);
|
||||
$twig->addFilter($containsFilter);
|
||||
$twig->addFilter($formatFilter);
|
||||
$twig->addFilter($defaultFilter);
|
||||
|
||||
return $twig->render($filename, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成后端文件
|
||||
*/
|
||||
public function generateBackend($action, $content): void
|
||||
{
|
||||
$outPath = '';
|
||||
if ($this->value['template'] == 'app') {
|
||||
$rootPath = base_path() . DS . 'app' . DS . $this->value['namespace'];
|
||||
$adminPath = '';
|
||||
|
||||
} else {
|
||||
$rootPath = base_path() . DS . 'plugin' . DS . $this->value['namespace'] . DS . 'app';
|
||||
$adminPath = DS . 'admin';
|
||||
}
|
||||
$subPath = DS . $this->value['package_name'];
|
||||
switch ($action) {
|
||||
case 'controller':
|
||||
$outPath = $rootPath . $adminPath . DS . 'controller' . $subPath . DS . $this->value['class_name'] . 'Controller.php';
|
||||
break;
|
||||
case 'logic':
|
||||
$outPath = $rootPath . $adminPath . DS . 'logic' . $subPath . DS . $this->value['class_name'] . 'Logic.php';
|
||||
break;
|
||||
case 'validate':
|
||||
$outPath = $rootPath . $adminPath . DS . 'validate' . $subPath . DS . $this->value['class_name'] . 'Validate.php';
|
||||
break;
|
||||
case 'model':
|
||||
$outPath = $rootPath . DS . 'model' . $subPath . DS . $this->value['class_name'] . '.php';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($outPath)) {
|
||||
throw new ApiException('文件类型异常,无法生成指定文件!');
|
||||
}
|
||||
if (!is_dir(dirname($outPath))) {
|
||||
mkdir(dirname($outPath), 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($outPath, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成前端文件
|
||||
*/
|
||||
public function generateFrontend($action, $content): void
|
||||
{
|
||||
$rootPath = dirname(base_path()) . DS . $this->value['generate_path'];
|
||||
if (!is_dir($rootPath)) {
|
||||
throw new ApiException('前端目录查找失败,必须与后端目录为同级目录!');
|
||||
}
|
||||
|
||||
$rootPath = $rootPath . DS . 'src' . DS . 'views' . DS . 'plugin' . DS . $this->value['namespace'];
|
||||
$subPath = DS . $this->value['package_name'];
|
||||
switch ($action) {
|
||||
case 'index':
|
||||
$outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'index.vue';
|
||||
break;
|
||||
case 'edit-dialog':
|
||||
$outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'edit-dialog.vue';
|
||||
break;
|
||||
case 'table-search':
|
||||
$outPath = $rootPath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'table-search.vue';
|
||||
break;
|
||||
case 'api':
|
||||
$outPath = $rootPath . DS . 'api' . $subPath . DS . $this->value['business_name'] . '.ts';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($outPath)) {
|
||||
throw new ApiException('文件类型异常,无法生成指定文件!');
|
||||
}
|
||||
if (!is_dir(dirname($outPath))) {
|
||||
mkdir(dirname($outPath), 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($outPath, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成临时文件
|
||||
*/
|
||||
public function generateTemp(): void
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
$rootPath = $config['generate_path'];
|
||||
|
||||
$vuePath = $rootPath . DS . 'vue' . DS . 'src' . DS . 'views' . DS . 'plugin' . DS . $this->value['namespace'];
|
||||
$phpPath = $rootPath . DS . 'php';
|
||||
$sqlPath = $rootPath . DS . 'sql';
|
||||
if ($this->value['template'] == 'app') {
|
||||
$phpPath = $phpPath . DS . 'app' . DS . $this->value['namespace'];
|
||||
$adminPath = '';
|
||||
} else {
|
||||
$phpPath = $phpPath . DS . 'plugin' . DS . $this->value['namespace'] . DS . 'app';
|
||||
$adminPath = DS . 'admin';
|
||||
}
|
||||
$subPath = DS . $this->value['package_name'];
|
||||
|
||||
$indexOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'index.vue';
|
||||
$this->checkPath($indexOutPath);
|
||||
$indexContent = $this->renderContent('vue', 'index.stub');
|
||||
file_put_contents($indexOutPath, $indexContent);
|
||||
|
||||
$editOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'edit-dialog.vue';
|
||||
$this->checkPath($editOutPath);
|
||||
$editContent = $this->renderContent('vue', 'edit-dialog.stub');
|
||||
file_put_contents($editOutPath, $editContent);
|
||||
|
||||
$searchOutPath = $vuePath . $subPath . DS . $this->value['business_name'] . DS . 'modules' . DS . 'table-search.vue';
|
||||
$this->checkPath($searchOutPath);
|
||||
$searchContent = $this->renderContent('vue', 'table-search.stub');
|
||||
file_put_contents($searchOutPath, $searchContent);
|
||||
|
||||
$viewOutPath = $vuePath . DS . 'api' . $subPath . DS . $this->value['business_name'] . '.ts';
|
||||
$this->checkPath($viewOutPath);
|
||||
$viewContent = $this->renderContent('ts', 'api.stub');
|
||||
file_put_contents($viewOutPath, $viewContent);
|
||||
|
||||
$controllerOutPath = $phpPath . $adminPath . DS . 'controller' . $subPath . DS . $this->value['class_name'] . 'Controller.php';
|
||||
$this->checkPath($controllerOutPath);
|
||||
$controllerContent = $this->renderContent('php', 'controller.stub');
|
||||
file_put_contents($controllerOutPath, $controllerContent);
|
||||
|
||||
$logicOutPath = $phpPath . $adminPath . DS . 'logic' . $subPath . DS . $this->value['class_name'] . 'Logic.php';
|
||||
$this->checkPath($logicOutPath);
|
||||
$logicContent = $this->renderContent('php', 'logic.stub');
|
||||
file_put_contents($logicOutPath, $logicContent);
|
||||
|
||||
$validateOutPath = $phpPath . $adminPath . DS . 'validate' . $subPath . DS . $this->value['class_name'] . 'Validate.php';
|
||||
$this->checkPath($validateOutPath);
|
||||
$validateContent = $this->renderContent('php', 'validate.stub');
|
||||
file_put_contents($validateOutPath, $validateContent);
|
||||
|
||||
$modelOutPath = $phpPath . DS . 'model' . $subPath . DS . $this->value['class_name'] . '.php';
|
||||
$this->checkPath($modelOutPath);
|
||||
$modelContent = $this->renderContent('php', 'model.stub');
|
||||
file_put_contents($modelOutPath, $modelContent);
|
||||
|
||||
$sqlOutPath = $sqlPath . DS . 'sql.sql';
|
||||
$this->checkPath($sqlOutPath);
|
||||
$sqlContent = $this->renderContent('sql', 'sql.stub');
|
||||
file_put_contents($sqlOutPath, $sqlContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并生成路径
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function checkPath($path): void
|
||||
{
|
||||
if (!is_dir(dirname($path))) {
|
||||
mkdir(dirname($path), 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
164
server/plugin/saiadmin/utils/code/CodeZip.php
Normal file
164
server/plugin/saiadmin/utils/code/CodeZip.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: sai <1430792918@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\utils\code;
|
||||
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 代码构建 压缩类
|
||||
*/
|
||||
class CodeZip
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取配置文件
|
||||
* @return string[]
|
||||
*/
|
||||
private static function _getConfig(): array
|
||||
{
|
||||
return [
|
||||
'template_path' => base_path().DIRECTORY_SEPARATOR.'plugin'.DIRECTORY_SEPARATOR.'saiadmin'.DIRECTORY_SEPARATOR.'utils'.DIRECTORY_SEPARATOR.'code'.DIRECTORY_SEPARATOR.'stub',
|
||||
'generate_path' => runtime_path().DIRECTORY_SEPARATOR.'code_engine'.DIRECTORY_SEPARATOR.'saiadmin',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// 读取配置文件
|
||||
$config = self::_getConfig();
|
||||
|
||||
// 清理源目录
|
||||
if (is_dir($config['generate_path'])) {
|
||||
$this->recursiveDelete($config['generate_path']);
|
||||
}
|
||||
|
||||
// 清理压缩文件
|
||||
$zipName = $config['generate_path'].'.zip';
|
||||
if (is_file($zipName)) {
|
||||
unlink($zipName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件压缩
|
||||
*/
|
||||
public function compress(bool $isDownload = false)
|
||||
{
|
||||
// 读取配置文件
|
||||
$config = self::_getConfig();
|
||||
$zipArc = new \ZipArchive;
|
||||
$zipName = $config['generate_path'].'.zip';
|
||||
$dirPath = $config['generate_path'];
|
||||
if ($zipArc->open($zipName, \ZipArchive::OVERWRITE | \ZipArchive::CREATE) !== true) {
|
||||
throw new ApiException('无法打开文件,或者文件创建失败');
|
||||
}
|
||||
$this->addFileToZip($dirPath, $zipArc);
|
||||
$zipArc->close();
|
||||
// 是否下载
|
||||
if ($isDownload) {
|
||||
$this->toBinary($zipName);
|
||||
} else {
|
||||
return $zipName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件解压
|
||||
*/
|
||||
public function deCompress(string $file, string $dirName)
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
// zip实例化对象
|
||||
$zipArc = new \ZipArchive();
|
||||
// 打开文件
|
||||
if (!$zipArc->open($file)) {
|
||||
return false;
|
||||
}
|
||||
// 解压文件
|
||||
if (!$zipArc->extractTo($dirName)) {
|
||||
// 关闭
|
||||
$zipArc->close();
|
||||
return false;
|
||||
}
|
||||
return $zipArc->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件加入到压缩包
|
||||
*/
|
||||
public function addFileToZip($rootPath, $zip)
|
||||
{
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($rootPath),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
foreach ($files as $name => $file)
|
||||
{
|
||||
// Skip directories (they would be added automatically)
|
||||
if (!$file->isDir())
|
||||
{
|
||||
// Get real and relative path for current file
|
||||
$filePath = $file->getRealPath();
|
||||
$relativePath = substr($filePath, strlen($rootPath) + 1);
|
||||
|
||||
// Add current file to archive
|
||||
$zip->addFile($filePath, $relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除目录下所有文件和文件夹
|
||||
*/
|
||||
public function recursiveDelete($dir)
|
||||
{
|
||||
// 打开指定目录
|
||||
if ($handle = @opendir($dir)) {
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
if (($file == ".") || ($file == "..")) {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($dir . '/' . $file)) {
|
||||
// 递归
|
||||
self::recursiveDelete($dir . '/' . $file);
|
||||
} else {
|
||||
unlink($dir . '/' . $file); // 删除文件
|
||||
}
|
||||
}
|
||||
@closedir($handle);
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载二进制流文件
|
||||
*/
|
||||
public function toBinary(string $fileName)
|
||||
{
|
||||
try {
|
||||
header("Cache-Control: public");
|
||||
header("Content-Description: File Transfer");
|
||||
header('Content-disposition: attachment; filename=' . basename($fileName)); //文件名
|
||||
header("Content-Type: application/zip"); //zip格式的
|
||||
header("Content-Transfer-Encoding: binary"); //告诉浏览器,这是二进制文件
|
||||
header('Content-Length: ' . filesize($fileName)); //告诉浏览器,文件大小
|
||||
if(ob_get_length() > 0) {
|
||||
ob_clean();
|
||||
}
|
||||
flush();
|
||||
@readfile($fileName);
|
||||
@unlink($fileName);
|
||||
} catch (\Throwable $th) {
|
||||
throw new ApiException('系统生成文件错误');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: your name
|
||||
// +----------------------------------------------------------------------
|
||||
namespace {{namespace_start}}controller{{namespace_end}};
|
||||
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use {{namespace_start}}logic{{namespace_end}}\{{class_name}}Logic;
|
||||
use {{namespace_start}}validate{{namespace_end}}\{{class_name}}Validate;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* {{menu_name}}控制器
|
||||
*/
|
||||
class {{class_name}}Controller extends BaseController
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->logic = new {{class_name}}Logic();
|
||||
$this->validate = new {{class_name}}Validate;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('{{menu_name}}列表', '{{namespace}}:{{package_name}}:{{business_name}}:index')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$where = $request->more([
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == '2' %}
|
||||
['{{column.column_name}}', ''],
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]);
|
||||
{% if tpl_category == 'single' %}
|
||||
$query = $this->logic->search($where);
|
||||
{% if options.relations != null %}
|
||||
$query->with([
|
||||
{% for item in options.relations %}
|
||||
'{{item.name}}',
|
||||
{% endfor %}
|
||||
]);
|
||||
{% endif %}
|
||||
$data = $this->logic->getList($query);
|
||||
{% endif %}
|
||||
{% if tpl_category == 'tree' %}
|
||||
$data = $this->logic->tree($where);
|
||||
{% endif %}
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('{{menu_name}}读取', '{{namespace}}:{{package_name}}:{{business_name}}:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$id = $request->input('id', '');
|
||||
$model = $this->logic->read($id);
|
||||
if ($model) {
|
||||
$data = is_array($model) ? $model : $model->toArray();
|
||||
return $this->success($data);
|
||||
} else {
|
||||
return $this->fail('未查找到信息');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('{{menu_name}}添加', '{{namespace}}:{{package_name}}:{{business_name}}:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('save', $data);
|
||||
$result = $this->logic->add($data);
|
||||
if ($result) {
|
||||
return $this->success('添加成功');
|
||||
} else {
|
||||
return $this->fail('添加失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('{{menu_name}}修改', '{{namespace}}:{{package_name}}:{{business_name}}:update')]
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$data = $request->post();
|
||||
$this->validate('update', $data);
|
||||
$result = $this->logic->edit($data['id'], $data);
|
||||
if ($result) {
|
||||
return $this->success('修改成功');
|
||||
} else {
|
||||
return $this->fail('修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
#[Permission('{{menu_name}}删除', '{{namespace}}:{{package_name}}:{{business_name}}:destroy')]
|
||||
public function destroy(Request $request): Response
|
||||
{
|
||||
$ids = $request->post('ids', '');
|
||||
if (empty($ids)) {
|
||||
return $this->fail('请选择要删除的数据');
|
||||
}
|
||||
$result = $this->logic->destroy($ids);
|
||||
if ($result) {
|
||||
return $this->success('删除成功');
|
||||
} else {
|
||||
return $this->fail('删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: your name
|
||||
// +----------------------------------------------------------------------
|
||||
namespace {{namespace_start}}logic{{namespace_end}};
|
||||
|
||||
{% if stub == 'eloquent' %}
|
||||
use plugin\saiadmin\basic\eloquent\BaseLogic;
|
||||
{% else %}
|
||||
use plugin\saiadmin\basic\think\BaseLogic;
|
||||
{% endif %}
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
use plugin\saiadmin\utils\Helper;
|
||||
use {{namespace_start_model}}model{{namespace_end}}\{{class_name}};
|
||||
|
||||
/**
|
||||
* {{menu_name}}逻辑层
|
||||
*/
|
||||
class {{class_name}}Logic extends BaseLogic
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new {{class_name}}();
|
||||
}
|
||||
|
||||
{% if tpl_category == 'tree' %}
|
||||
/**
|
||||
* 修改数据
|
||||
* @param $id
|
||||
* @param $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function edit($id, $data): mixed
|
||||
{
|
||||
if (!isset($data['{{options.tree_parent_id}}'])) {
|
||||
$data['{{options.tree_parent_id}}'] = 0;
|
||||
}
|
||||
if ($data['{{options.tree_parent_id}}'] == $data['{{options.tree_id}}']) {
|
||||
throw new ApiException('不能设置父级为自身');
|
||||
}
|
||||
return parent::edit($id, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param $ids
|
||||
*/
|
||||
public function destroy($ids): bool
|
||||
{
|
||||
$num = $this->model->whereIn('{{options.tree_parent_id}}', $ids)->count();
|
||||
if ($num > 0) {
|
||||
throw new ApiException('该分类下存在子分类,请先删除子分类');
|
||||
} else {
|
||||
return parent::destroy($ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 树形数据
|
||||
*/
|
||||
public function tree($where)
|
||||
{
|
||||
$query = $this->search($where);
|
||||
$request = request();
|
||||
if ($request && $request->input('tree', 'false') === 'true') {
|
||||
{% if stub == 'eloquent' %}
|
||||
$query->select('{{options.tree_id}}', '{{options.tree_id}} as value', '{{options.tree_name}} as label', '{{options.tree_parent_id}}');
|
||||
{% else %}
|
||||
$query->field('{{options.tree_id}}, {{options.tree_id}} as value, {{options.tree_name}} as label, {{options.tree_parent_id}}');
|
||||
{% endif %}
|
||||
}
|
||||
{% if options.relations != null %}
|
||||
$query->with([
|
||||
{% for item in options.relations %}
|
||||
'{{item.name}}',
|
||||
{% endfor %}
|
||||
]);
|
||||
{% endif %}
|
||||
$data = $this->getAll($query);
|
||||
return Helper::makeTree($data);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
}
|
||||
304
server/plugin/saiadmin/utils/code/stub/saiadmin/php/model.stub
Normal file
304
server/plugin/saiadmin/utils/code/stub/saiadmin/php/model.stub
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: your name
|
||||
// +----------------------------------------------------------------------
|
||||
namespace {{namespace_start_model}}model{{namespace_end}};
|
||||
|
||||
{% if stub == 'eloquent' %}
|
||||
use plugin\saiadmin\basic\eloquent\BaseModel;
|
||||
{% else %}
|
||||
use plugin\saiadmin\basic\think\BaseModel;
|
||||
{% endif %}
|
||||
|
||||
/**
|
||||
* {{menu_name}}模型
|
||||
*
|
||||
* {{table_name}} {{table_comment}}
|
||||
*
|
||||
{% for column in columns %}
|
||||
* @property {{column.php_type}} ${{column.column_name}} {{column.column_comment}}
|
||||
{% endfor %}
|
||||
*/
|
||||
class {{class_name}} extends BaseModel
|
||||
{
|
||||
/**
|
||||
* 数据表主键
|
||||
* @var string
|
||||
*/
|
||||
{% if stub == 'eloquent' %}
|
||||
protected $primaryKey = '{{pk}}';
|
||||
{% else %}
|
||||
protected $pk = '{{pk}}';
|
||||
{% endif %}
|
||||
|
||||
/**
|
||||
* 数据库表名称
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '{{table_name}}';
|
||||
|
||||
{% if source != db_source and source != '' %}
|
||||
/**
|
||||
* 数据库连接
|
||||
* @var string
|
||||
*/
|
||||
protected $connection = '{{source}}';
|
||||
|
||||
{% endif %}
|
||||
{% if stub == 'eloquent' %}
|
||||
/**
|
||||
* 属性转换
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return array_merge(parent::casts(), [
|
||||
{% for column in columns %}
|
||||
{% if column.view_type == 'inputTag' or column.view_type == 'checkbox' %}
|
||||
'{{column.column_name}}' => 'array',
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadImage' and column.options.limit > 1 %}
|
||||
'{{column.column_name}}' => 'array',
|
||||
{% endif %}
|
||||
{% if column.view_type == 'imagePicker' and column.options.limit > 1 %}
|
||||
'{{column.column_name}}' => 'array',
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadFile' and column.options.limit > 1 %}
|
||||
'{{column.column_name}}' => 'array',
|
||||
{% endif %}
|
||||
{% if column.view_type == 'chunkUpload' and column.options.limit > 1 %}
|
||||
'{{column.column_name}}' => 'array',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
]);
|
||||
}
|
||||
{% else %}
|
||||
{% for column in columns %}
|
||||
{% if column.view_type == 'inputTag' or column.view_type == 'checkbox' %}
|
||||
/**
|
||||
* {{column.column_comment}} 保存数组转换
|
||||
*/
|
||||
public function set{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {{column.column_comment}} 读取数组转换
|
||||
*/
|
||||
public function get{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadImage' and column.options.limit > 1 %}
|
||||
/**
|
||||
* {{column.column_comment}} 保存数组转换
|
||||
*/
|
||||
public function set{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {{column.column_comment}} 读取数组转换
|
||||
*/
|
||||
public function get{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.view_type == 'imagePicker' and column.options.limit > 1 %}
|
||||
/**
|
||||
* {{column.column_comment}} 保存数组转换
|
||||
*/
|
||||
public function set{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {{column.column_comment}} 读取数组转换
|
||||
*/
|
||||
public function get{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadFile' and column.options.limit > 1 %}
|
||||
/**
|
||||
* {{column.column_comment}} 保存数组转换
|
||||
*/
|
||||
public function set{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {{column.column_comment}} 读取数组转换
|
||||
*/
|
||||
public function get{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.view_type == 'chunkUpload' and column.options.limit > 1 %}
|
||||
/**
|
||||
* {{column.column_comment}} 保存数组转换
|
||||
*/
|
||||
public function set{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {{column.column_comment}} 读取数组转换
|
||||
*/
|
||||
public function get{{column.column_name | camel}}Attr($value)
|
||||
{
|
||||
return json_decode($value ?? '', true);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == 2 and column.query_type == 'neq' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', '<>', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'gt' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', '>', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'gte' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', '>=', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'lt' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', '<', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'lte' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', '<=', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'like' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->where('{{column.column_name}}', 'like', '%'.$value.'%');
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'in' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->whereIn('{{column.column_name}}', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'notin' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->whereNotIn('{{column.column_name}}', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'between' %}
|
||||
/**
|
||||
* {{column.column_comment}} 搜索
|
||||
*/
|
||||
public function search{{column.column_name | camel}}Attr($query, $value)
|
||||
{
|
||||
$query->whereBetween('{{column.column_name}}', $value);
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for item in options.relations %}
|
||||
{% if item.type == 'belongsTo' %}
|
||||
/**
|
||||
* 关联模型 {{item.name}}
|
||||
*/
|
||||
public function {{item.name}}()
|
||||
{
|
||||
return $this->{{item.type}}({{item.model}}::class, '{{item.localKey}}', '{{item.foreignKey}}');
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if item.type == 'hasOne' or item.type == 'hasMany' %}
|
||||
/**
|
||||
* 关联模型 {{item.name}}
|
||||
*/
|
||||
public function {{item.name}}()
|
||||
{
|
||||
return $this->{{item.type}}({{item.model}}::class, '{{item.localKey}}', '{{item.foreignKey}}');
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if item.type == 'belongsToMany' and stub == 'think' %}
|
||||
/**
|
||||
* 关联模型 {{item.name}}
|
||||
*/
|
||||
public function {{item.name}}()
|
||||
{
|
||||
return $this->{{item.type}}({{item.model}}::class, {{item.table}}::class, '{{item.localKey}}', '{{item.foreignKey}}');
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if item.type == 'belongsToMany' and stub == 'eloquent' %}
|
||||
/**
|
||||
* 关联模型 {{item.name}}
|
||||
*/
|
||||
public function {{item.name}}()
|
||||
{
|
||||
return $this->{{item.type}}({{item.model}}::class, {{item.table}}::class, '{{item.foreignKey}}', '{{item.localKey}}');
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: your name
|
||||
// +----------------------------------------------------------------------
|
||||
namespace {{namespace_start}}validate{{namespace_end}};
|
||||
|
||||
use plugin\saiadmin\basic\BaseValidate;
|
||||
|
||||
/**
|
||||
* {{menu_name}}验证器
|
||||
*/
|
||||
class {{class_name}}Validate extends BaseValidate
|
||||
{
|
||||
/**
|
||||
* 定义验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
{% for column in columns %}
|
||||
{% if column.is_required == 2 and column.is_pk != 2 %}
|
||||
'{{column.column_name}}' => 'require',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义错误信息
|
||||
*/
|
||||
protected $message = [
|
||||
{% for column in columns %}
|
||||
{% if column.is_required == 2 and column.is_pk != 2 %}
|
||||
'{{column.column_name}}' => '{{column.column_comment}}必须填写',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
/**
|
||||
* 定义场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'save' => [
|
||||
{% for column in columns %}
|
||||
{% if column.is_required == 2 and column.is_pk != 2 %}
|
||||
'{{column.column_name}}',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
],
|
||||
'update' => [
|
||||
{% for column in columns %}
|
||||
{% if column.is_required == 2 and column.is_pk != 2 %}
|
||||
'{{column.column_name}}',
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
14
server/plugin/saiadmin/utils/code/stub/saiadmin/sql/sql.stub
Normal file
14
server/plugin/saiadmin/utils/code/stub/saiadmin/sql/sql.stub
Normal file
@@ -0,0 +1,14 @@
|
||||
-- 数据库语句--
|
||||
|
||||
{% for column in tables %}
|
||||
-- 菜单[{{column.menu_name}}] SQL
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `code`, `slug`, `type`, `path`, `component`, `icon`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES ({{column.belong_menu_id}}, '{{column.menu_name}}', '{{column.namespace}}/{{column.package_name}}/{{column.business_name}}', '', 2, '{{column.package_name}}/{{column.business_name}}', '/plugin/{{column.namespace}}/{{column.package_name}}/{{column.business_name}}/index', 'ri:home-2-line', 100, 2, 2, 2, 2, 2, now(), now());
|
||||
|
||||
SET @id := LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `slug`, `type`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES (@id, '列表', '{{column.namespace}}:{{column.package_name}}:{{column.business_name}}:index', 3, 100, 2, 2, 2, 2, 2, now(), now());
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `slug`, `type`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES (@id, '保存', '{{column.namespace}}:{{column.package_name}}:{{column.business_name}}:save', 3, 100, 2, 2, 2, 2, 2, now(), now());
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `slug`, `type`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES (@id, '更新', '{{column.namespace}}:{{column.package_name}}:{{column.business_name}}:update', 3, 100, 2, 2, 2, 2, 2, now(), now());
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `slug`, `type`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES (@id, '读取', '{{column.namespace}}:{{column.package_name}}:{{column.business_name}}:read', 3, 100, 2, 2, 2, 2, 2, now(), now());
|
||||
INSERT INTO `sa_system_menu`(`parent_id`, `name`, `slug`, `type`, `sort`, `is_iframe`, `is_keep_alive`, `is_hidden`, `is_fixed_tab`, `is_full_page`, `create_time`, `update_time`) VALUES (@id, '删除', '{{column.namespace}}:{{column.package_name}}:{{column.business_name}}:destroy', 3, 100, 2, 2, 2, 2, 2, now(), now());
|
||||
{% endfor %}
|
||||
69
server/plugin/saiadmin/utils/code/stub/saiadmin/ts/api.stub
Normal file
69
server/plugin/saiadmin/utils/code/stub/saiadmin/ts/api.stub
Normal file
@@ -0,0 +1,69 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
/**
|
||||
* {{menu_name}} API接口
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* 获取数据列表
|
||||
* @param params 搜索参数
|
||||
* @returns 数据列表
|
||||
*/
|
||||
list(params: Record<string, any>) {
|
||||
{% if tpl_category == 'tree' %}
|
||||
return request.get<Api.Common.ApiData[]>({
|
||||
{% else %}
|
||||
return request.get<Api.Common.ApiPage>({
|
||||
{% endif %}
|
||||
url: '/{{url_path}}/index',
|
||||
params
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
* @param id 数据ID
|
||||
* @returns 数据详情
|
||||
*/
|
||||
read(id: number | string) {
|
||||
return request.get<Api.Common.ApiData>({
|
||||
url: '/{{url_path}}/read?id=' + id
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建数据
|
||||
* @param params 数据参数
|
||||
* @returns 执行结果
|
||||
*/
|
||||
save(params: Record<string, any>) {
|
||||
return request.post<any>({
|
||||
url: '/{{url_path}}/save',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param params 数据参数
|
||||
* @returns 执行结果
|
||||
*/
|
||||
update(params: Record<string, any>) {
|
||||
return request.put<any>({
|
||||
url: '/{{url_path}}/update',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
* @param id 数据ID
|
||||
* @returns 执行结果
|
||||
*/
|
||||
delete(params: Record<string, any>) {
|
||||
return request.del<any>({
|
||||
url: '/{{url_path}}/destroy',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
{% if component_type == 1 %}
|
||||
<el-dialog
|
||||
{% else %}
|
||||
<el-drawer
|
||||
{% endif %}
|
||||
v-model="visible"
|
||||
:title="dialogType === 'add' ? '新增{{menu_name}}' : '编辑{{menu_name}}'"
|
||||
{% if is_full == 2 %}
|
||||
{% if component_type == 1 %}
|
||||
:fullscreen="true"
|
||||
{% else %}
|
||||
size="100%"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if component_type == 1 %}
|
||||
width="{{form_width}}px"
|
||||
{% else %}
|
||||
:size="{{form_width}}"
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
align-center
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
|
||||
{% for column in columns %}
|
||||
{% if column.is_insert == 2 %}
|
||||
{% if column.view_type == 'input' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input v-model="formData.{{column.column_name}}" placeholder="请输入{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'password' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input v-model="formData.{{column.column_name}}" type="password" placeholder="请输入{{column.column_comment}}" show-password />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'textarea' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input v-model="formData.{{column.column_name}}" type="textarea" :rows="2" placeholder="请输入{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'inputNumber' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input-number v-model="formData.{{column.column_name}}" placeholder="请输入{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'inputTag' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input-tag v-model="formData.{{column.column_name}}" placeholder="请输入{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'switch' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-switch v-model="formData.{{column.column_name}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'slider' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-slider v-model="formData.{{column.column_name}}" placeholder="请输入{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'select' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-select v-model="formData.{{column.column_name}}" :options="[]" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'saSelect' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-select v-model="formData.{{column.column_name}}" dict="{{column.dict_type}}" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'treeSelect' and column.column_name == options.tree_parent_id %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-tree-select
|
||||
v-model="formData.{{column.column_name}}"
|
||||
:data="optionData.treeData"
|
||||
placeholder="请选择{{column.column_comment}}"
|
||||
check-strictly
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'treeSelect' and column.column_name != options.tree_parent_id %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-tree-select v-model="formData.{{column.column_name}}" :data="[]" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'radio' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-radio v-model="formData.{{column.column_name}}" dict="{{column.dict_type}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'checkbox' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-checkbox v-model="formData.{{column.column_name}}" dict="{{column.dict_type}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'date' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-date-picker v-model="formData.{{column.column_name}}" type="{{column.options.mode}}" placeholder="请选择{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'time' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-time-picker v-model="formData.{{column.column_name}}" placeholder="请选择{{column.column_comment}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'rate' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-rate v-model="formData.{{column.column_name}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'cascader' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-cascader v-model="formData.{{column.column_name}}" :options="[]" placeholder="请选择{{column.column_comment}}" allow-clear />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'userSelect' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-user v-model="formData.{{column.column_name}}" :multiple="{{column.options.multiple|bool}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadImage' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-image-upload v-model="formData.{{column.column_name}}" :limit="{{column.options.limit|formatNumber}}" :multiple="{{column.options.multiple|bool}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'imagePicker' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-image-picker v-model="formData.{{column.column_name}}" :limit="{{column.options.limit|formatNumber}}" :multiple="{{column.options.multiple|bool}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'uploadFile' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-file-upload v-model="formData.{{column.column_name}}" :limit="{{column.options.limit|formatNumber}}" :multiple="{{column.options.multiple|bool}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'chunkUpload' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-chunk-upload v-model="formData.{{column.column_name}}" :limit="{{column.options.limit|formatNumber}}" :multiple="{{column.options.multiple|bool}}" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'editor' %}
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-editor v-model="formData.{{column.column_name}}" height="{{column.options.height}}px" />
|
||||
</el-form-item>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||||
</template>
|
||||
{% if component_type == 1 %}
|
||||
</el-dialog>
|
||||
{% else %}
|
||||
</el-drawer>
|
||||
{% endif %}
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '../../../api/{{package_name}}/{{business_name}}'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
dialogType: string
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
dialogType: 'add',
|
||||
data: undefined
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
{% if tpl_category == 'tree' %}
|
||||
const optionData = reactive({
|
||||
treeData: <any[]>[]
|
||||
})
|
||||
{% endif %}
|
||||
|
||||
/**
|
||||
* 弹窗显示状态双向绑定
|
||||
*/
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单验证规则
|
||||
*/
|
||||
const rules = reactive<FormRules>({
|
||||
{% for column in columns %}
|
||||
{% if column.is_required == 2 and column.is_pk == 1 %}
|
||||
{{column.column_name}}: [{ required: true, message: '{{column.column_comment}}必需填写', trigger: 'blur' }],
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
{% for column in columns %}
|
||||
{% if column.is_pk == 2 %}
|
||||
{{column.column_name}}: null,
|
||||
{% elseif column.is_insert == 2 %}
|
||||
{% if column.column_type == 'int' or column.column_type == 'smallint' or column.column_type == 'tinyint' %}
|
||||
{{column.column_name}}: {{column.default_value | parseNumber}},
|
||||
{% elseif column.view_type == 'inputTag' or column.view_type == 'checkbox' or column.options.limit > 1 %}
|
||||
{{column.column_name}}: [],
|
||||
{% elseif column.view_type == 'userSelect' %}
|
||||
{{column.column_name}}: null,
|
||||
{% else %}
|
||||
{{column.column_name}}: '{{column.default_value}}',
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const formData = reactive({ ...initialFormData })
|
||||
|
||||
/**
|
||||
* 监听弹窗打开,初始化表单数据
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
initPage()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 初始化页面数据
|
||||
*/
|
||||
const initPage = async () => {
|
||||
// 先重置为初始值
|
||||
Object.assign(formData, initialFormData)
|
||||
{% if tpl_category == 'tree' %}
|
||||
// 初始化树形数据
|
||||
const data = await api.list({ tree: true })
|
||||
optionData.treeData = [
|
||||
{
|
||||
id: 0,
|
||||
value: 0,
|
||||
label: '无上级分类',
|
||||
children: data
|
||||
}
|
||||
]
|
||||
{% endif %}
|
||||
// 如果有数据,则填充数据
|
||||
if (props.data) {
|
||||
await nextTick()
|
||||
initForm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单数据
|
||||
*/
|
||||
const initForm = () => {
|
||||
if (props.data) {
|
||||
for (const key in formData) {
|
||||
if (props.data[key] != null && props.data[key] != undefined) {
|
||||
;(formData as any)[key] = props.data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
if (props.dialogType === 'add') {
|
||||
await api.save(formData)
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
await api.update(formData)
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
189
server/plugin/saiadmin/utils/code/stub/saiadmin/vue/index.stub
Normal file
189
server/plugin/saiadmin/utils/code/stub/saiadmin/vue/index.stub
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div class="art-full-height">
|
||||
<!-- 搜索面板 -->
|
||||
<TableSearch v-model="searchForm" @search="handleSearch" @reset="resetSearchParams" />
|
||||
|
||||
<ElCard class="art-table-card" shadow="never">
|
||||
<!-- 表格头部 -->
|
||||
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
|
||||
<template #left>
|
||||
<ElSpace wrap>
|
||||
<ElButton v-permission="'{{namespace}}:{{package_name}}:{{business_name}}:save'" @click="showDialog('add')" v-ripple>
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:add-fill" />
|
||||
</template>
|
||||
新增
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-permission="'{{namespace}}:{{package_name}}:{{business_name}}:destroy'"
|
||||
:disabled="selectedRows.length === 0"
|
||||
@click="deleteSelectedRows(api.delete, refreshData)"
|
||||
v-ripple
|
||||
>
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:delete-bin-5-line" />
|
||||
</template>
|
||||
删除
|
||||
</ElButton>
|
||||
{% if tpl_category == 'tree' %}
|
||||
<ElButton @click="toggleExpand" v-ripple>
|
||||
<template #icon>
|
||||
<ArtSvgIcon v-if="isExpanded" icon="ri:collapse-diagonal-line" />
|
||||
<ArtSvgIcon v-else icon="ri:expand-diagonal-line" />
|
||||
</template>
|
||||
{{ isExpanded ? '收起' : '展开' }}
|
||||
</ElButton>
|
||||
{% endif %}
|
||||
</ElSpace>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
rowKey="id"
|
||||
:loading="loading"
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
@sort-change="handleSortChange"
|
||||
@selection-change="handleSelectionChange"
|
||||
@pagination:size-change="handleSizeChange"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
>
|
||||
<!-- 操作列 -->
|
||||
<template #operation="{ row }">
|
||||
<div class="flex gap-2">
|
||||
<SaButton
|
||||
v-permission="'{{namespace}}:{{package_name}}:{{business_name}}:update'"
|
||||
type="secondary"
|
||||
@click="showDialog('edit', row)"
|
||||
/>
|
||||
<SaButton
|
||||
v-permission="'{{namespace}}:{{package_name}}:{{business_name}}:destroy'"
|
||||
type="error"
|
||||
@click="deleteRow(row, api.delete, refreshData)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<EditDialog
|
||||
v-model="dialogVisible"
|
||||
:dialog-type="dialogType"
|
||||
:data="dialogData"
|
||||
@success="refreshData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTable } from '@/hooks/core/useTable'
|
||||
import { useSaiAdmin } from '@/composables/useSaiAdmin'
|
||||
import api from '../../api/{{package_name}}/{{business_name}}'
|
||||
import TableSearch from './modules/table-search.vue'
|
||||
import EditDialog from './modules/edit-dialog.vue'
|
||||
|
||||
{% if tpl_category == 'tree' %}
|
||||
// 状态管理
|
||||
const isExpanded = ref(false)
|
||||
const tableRef = ref()
|
||||
{% endif %}
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = ref({
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == 2 and column.query_type != 'between' %}
|
||||
{{column.column_name}}: undefined,
|
||||
{% endif %}
|
||||
{% if column.is_query == 2 and column.query_type == 'between' %}
|
||||
{{column.column_name}}: [],
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
})
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = (params: Record<string, any>) => {
|
||||
Object.assign(searchParams, params)
|
||||
getData()
|
||||
}
|
||||
|
||||
// 表格配置
|
||||
const {
|
||||
columns,
|
||||
columnChecks,
|
||||
data,
|
||||
loading,
|
||||
getData,
|
||||
searchParams,
|
||||
pagination,
|
||||
resetSearchParams,
|
||||
handleSortChange,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
refreshData
|
||||
} = useTable({
|
||||
core: {
|
||||
apiFn: api.list,
|
||||
columnsFactory: () => [
|
||||
{ type: 'selection' },
|
||||
{% for column in columns %}
|
||||
{% if column.is_list == 2 %}
|
||||
{% if column.view_type == 'uploadImage' or column.view_type == 'imagePicker' %}
|
||||
{ prop: '{{column.column_name}}', label: '{{column.column_comment}}', saiType: 'image' },
|
||||
{% else %}
|
||||
{% if column.is_sort == 2 %}
|
||||
{% if column.view_type == 'saSelect' or column.view_type == 'radio' %}
|
||||
{ prop: '{{column.column_name}}', label: '{{column.column_comment}}', saiType: 'dict', saiDict: '{{column.dict_type}}', sortable: true },
|
||||
{% else %}
|
||||
{ prop: '{{column.column_name}}', label: '{{column.column_comment}}', sortable: true },
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if column.view_type == 'saSelect' or column.view_type == 'radio' %}
|
||||
{ prop: '{{column.column_name}}', label: '{{column.column_comment}}', saiType: 'dict', saiDict: '{{column.dict_type}}' },
|
||||
{% else %}
|
||||
{ prop: '{{column.column_name}}', label: '{{column.column_comment}}' },
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{ prop: 'operation', label: '操作', width: 100, fixed: 'right', useSlot: true }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 编辑配置
|
||||
const {
|
||||
dialogType,
|
||||
dialogVisible,
|
||||
dialogData,
|
||||
showDialog,
|
||||
deleteRow,
|
||||
deleteSelectedRows,
|
||||
handleSelectionChange,
|
||||
selectedRows
|
||||
} = useSaiAdmin()
|
||||
|
||||
{% if tpl_category == 'tree' %}
|
||||
// 切换展开/收起所有菜单
|
||||
const toggleExpand = (): void => {
|
||||
isExpanded.value = !isExpanded.value
|
||||
nextTick(() => {
|
||||
if (tableRef.value?.elTableRef && data.value) {
|
||||
const processRows = (rows: any[]) => {
|
||||
rows.forEach((row) => {
|
||||
if (row.children?.length) {
|
||||
tableRef.value.elTableRef.toggleRowExpansion(row, isExpanded.value)
|
||||
processRows(row.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
processRows(data.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
{% endif %}
|
||||
</script>
|
||||
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<sa-search-bar
|
||||
ref="searchBarRef"
|
||||
v-model="formData"
|
||||
label-width="100px"
|
||||
:showExpand="false"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
@expand="handleExpand"
|
||||
>
|
||||
{% for column in columns %}
|
||||
{% if column.is_query == 2 %}
|
||||
{% if column.view_type == 'select' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-select v-model="formData.{{column.column_name}}" :options="[]" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'saSelect' or column.view_type == 'radio' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<sa-select v-model="formData.{{column.column_name}}" dict="{{column.dict_type}}" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'cascader' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-cascader v-model="formData.{{column.column_name}}" :options="[]" placeholder="请选择{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'date' and column.options.mode == 'date' and column.query_type == 'between' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-date-picker v-model="formData.{{column.column_name}}" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'date' and column.options.mode == 'datetime' and column.query_type == 'between' %}
|
||||
<el-col v-bind="setSpan(12)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-date-picker v-model="formData.{{column.column_name}}" type="datetimerange" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type == 'date' and column.query_type != 'between' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-date-picker v-model="formData.{{column.column_name}}" type="{{column.options.mode}}" placeholder="请选择{{column.column_comment}}" value-format="YYYY-MM-DD HH:mm:ss" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% if column.view_type != 'select' and column.view_type != 'radio' and column.view_type != 'saSelect' and column.view_type != 'cascader' and column.view_type != 'date' %}
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="{{column.column_comment}}" prop="{{column.column_name}}">
|
||||
<el-input v-model="formData.{{column.column_name}}" placeholder="请输入{{column.column_comment}}" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</sa-search-bar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
modelValue: Record<string, any>
|
||||
}
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: Record<string, any>): void
|
||||
(e: 'search', params: Record<string, any>): void
|
||||
(e: 'reset'): void
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
// 展开/收起
|
||||
const isExpanded = ref<boolean>(false)
|
||||
|
||||
// 表单数据双向绑定
|
||||
const searchBarRef = ref()
|
||||
const formData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
// 重置
|
||||
function handleReset() {
|
||||
searchBarRef.value?.ref.resetFields()
|
||||
emit('reset')
|
||||
}
|
||||
|
||||
// 搜索
|
||||
async function handleSearch() {
|
||||
emit('search', formData.value)
|
||||
}
|
||||
|
||||
// 展开/收起
|
||||
function handleExpand(expanded: boolean) {
|
||||
isExpanded.value = expanded
|
||||
}
|
||||
|
||||
// 栅格占据的列数
|
||||
const setSpan = (span: number) => {
|
||||
return {
|
||||
span: span,
|
||||
xs: 24, // 手机:满宽显示
|
||||
sm: span >= 12 ? span : 12, // 平板:大于等于12保持,否则用半宽
|
||||
md: span >= 8 ? span : 8, // 中等屏幕:大于等于8保持,否则用三分之一宽
|
||||
lg: span,
|
||||
xl: span
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user