初始化-安装依赖

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

View File

@@ -0,0 +1,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();
}
}

View 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']);
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

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

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

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

View File

@@ -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('清理成功');
}
}

View File

@@ -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('删除失败');
}
}
}

View File

@@ -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('删除失败');
}
}
}

View File

@@ -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('操作成功');
}
}

View File

@@ -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());
}
}
}

View File

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

View File

@@ -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('删除失败');
}
}
}

View File

@@ -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('删除失败');
}
}
}

View File

@@ -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('参数错误,请检查');
}
}
}

View File

@@ -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('删除失败');
}
}
}

View File

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

View File

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

View File

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

View File

@@ -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('删除成功');
}
}

View File

@@ -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('修改成功');
}
}

View 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('参数错误,请检查');
}
}
}

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View 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 . '/%');
}
}

View 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 . '%');
}
}

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

View File

@@ -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 . '%');
}
}

View 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 父级ID0为根节点
* @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');
}
}

View 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%");
}
}

View 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');
}
}

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

View 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 . '%');
}
}

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

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

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

View 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');
}
}

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

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

View 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');
}
}

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

View 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');
}
}

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

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

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

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

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View File

@@ -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',
],
];
}

View 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',
],
];
}

View File

@@ -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',
]
];
}

View 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>

View 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>