1
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Admin;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
use Webman\Config;
|
||||
|
||||
/**
|
||||
* 管理员账户
|
||||
*/
|
||||
class AccountController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['login', 'logout', 'captcha', 'register', 'registerPage'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['info'];
|
||||
|
||||
/**
|
||||
* @var Admin
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户设置
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return raw_view('account/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$this->checkDatabaseAvailable();
|
||||
$captcha = $request->post('captcha', '');
|
||||
// if (strtolower($captcha) !== session('captcha-login')) {
|
||||
// return $this->json(1, '验证码错误');
|
||||
// }
|
||||
$request->session()->forget('captcha-login');
|
||||
$username = $request->post('username', '');
|
||||
$password = $request->post('password', '');
|
||||
if (!$username) {
|
||||
return $this->json(1, '用户名不能为空');
|
||||
}
|
||||
$this->checkLoginLimit($username);
|
||||
$admin = Admin::where('username', $username)->first();
|
||||
if (!$admin || !Util::passwordVerify($password, $admin->password)) {
|
||||
return $this->json(1, '账户不存在或密码错误');
|
||||
}
|
||||
if ($admin->status != 0) {
|
||||
return $this->json(1, '当前账户暂时无法登录');
|
||||
}
|
||||
$admin->login_at = date('Y-m-d H:i:s');
|
||||
$admin->save();
|
||||
$this->removeLoginLimit($username);
|
||||
$admin = $admin->toArray();
|
||||
$session = $request->session();
|
||||
$admin['password'] = md5($admin['password']);
|
||||
$session->set('admin', $admin);
|
||||
return $this->json(0, '登录成功', [
|
||||
'nickname' => $admin['nickname'],
|
||||
'token' => $request->sessionId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function logout(Request $request): Response
|
||||
{
|
||||
$request->session()->delete('admin');
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function info(Request $request): Response
|
||||
{
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
return $this->json(1);
|
||||
}
|
||||
$info = [
|
||||
'id' => $admin['id'],
|
||||
'username' => $admin['username'],
|
||||
'nickname' => $admin['nickname'],
|
||||
'avatar' => $admin['avatar'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile'],
|
||||
'isSuperAdmin' => Auth::isSuperAdmin(),
|
||||
'token' => $request->sessionId(),
|
||||
];
|
||||
return $this->json(0, 'ok', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$allow_column = [
|
||||
'nickname' => 'nickname',
|
||||
'avatar' => 'avatar',
|
||||
'email' => 'email',
|
||||
'mobile' => 'mobile',
|
||||
];
|
||||
|
||||
$data = $request->post();
|
||||
$update_data = [];
|
||||
foreach ($allow_column as $key => $column) {
|
||||
if (isset($data[$key])) {
|
||||
$update_data[$column] = $data[$key];
|
||||
}
|
||||
}
|
||||
if (isset($update_data['password'])) {
|
||||
$update_data['password'] = Util::passwordHash($update_data['password']);
|
||||
}
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
$admin = admin();
|
||||
unset($update_data['password']);
|
||||
foreach ($update_data as $key => $value) {
|
||||
$admin[$key] = $value;
|
||||
}
|
||||
$request->session()->set('admin', $admin);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function password(Request $request): Response
|
||||
{
|
||||
$hash = Admin::find(admin_id())['password'];
|
||||
$password = $request->post('password');
|
||||
if (!$password) {
|
||||
return $this->json(2, '密码不能为空');
|
||||
}
|
||||
if ($request->post('password_confirm') !== $password) {
|
||||
return $this->json(3, '两次密码输入不一致');
|
||||
}
|
||||
if (!Util::passwordVerify($request->post('old_password'), $hash)) {
|
||||
return $this->json(1, '原始密码不正确');
|
||||
}
|
||||
$update_data = [
|
||||
'password' => Util::passwordHash($password)
|
||||
];
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
* @param Request $request
|
||||
* @param string $type
|
||||
* @return Response
|
||||
*/
|
||||
public function captcha(Request $request, string $type = 'login'): Response
|
||||
{
|
||||
$builder = new PhraseBuilder(4, 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ');
|
||||
$captcha = new CaptchaBuilder(null, $builder);
|
||||
$captcha->build(120);
|
||||
$request->session()->set("captcha-$type", strtolower($captcha->getPhrase()));
|
||||
$img_content = $captcha->get();
|
||||
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
if (!is_dir($limit_log_path)) {
|
||||
mkdir($limit_log_path, 0777, true);
|
||||
}
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
$time = date('YmdH') . ceil(date('i')/5);
|
||||
$limit_info = [];
|
||||
if (is_file($limit_file)) {
|
||||
$json_str = file_get_contents($limit_file);
|
||||
$limit_info = json_decode($json_str, true);
|
||||
}
|
||||
|
||||
if (!$limit_info || $limit_info['time'] != $time) {
|
||||
$limit_info = [
|
||||
'username' => $username,
|
||||
'count' => 0,
|
||||
'time' => $time
|
||||
];
|
||||
}
|
||||
$limit_info['count']++;
|
||||
file_put_contents($limit_file, json_encode($limit_info));
|
||||
if ($limit_info['count'] >= 5) {
|
||||
throw new BusinessException('登录失败次数过多,请5分钟后再试');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
*/
|
||||
protected function removeLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
if (is_file($limit_file)) {
|
||||
unlink($limit_file);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkDatabaseAvailable()
|
||||
{
|
||||
if (!config('plugin.admin.database')) {
|
||||
throw new BusinessException('请重启webman');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册页面
|
||||
* @return Response
|
||||
*/
|
||||
public function registerPage(Request $request): Response
|
||||
{
|
||||
$config = Config::get('plugin.admin.app', []);
|
||||
$data = [
|
||||
'logo' => $config['logo'] ?? '/app/admin/admin/images/logo.png',
|
||||
'title' => $config['name'] ?? '管理系统',
|
||||
];
|
||||
return view('account/register', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function register(Request $request): Response
|
||||
{
|
||||
$username = $request->post('username');
|
||||
$nickname = $request->post('nickname');
|
||||
$password = $request->post('password');
|
||||
|
||||
if (empty($username) || empty($password) || empty($nickname)) {
|
||||
return json(['code' => 1, 'msg' => '用户名、昵称和密码不能为空']);
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
if (Admin::where('username', $username)->first()) {
|
||||
return json(['code' => 1, 'msg' => '用户名已存在']);
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
$user = new Admin();
|
||||
$user->username = $username;
|
||||
$user->nickname = $nickname;
|
||||
$user->password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$user->created_at = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
$adminRole = new AdminRole();
|
||||
$adminRole->admin_id = $user->id;
|
||||
$adminRole->role_id = 3;
|
||||
$adminRole->save();
|
||||
|
||||
return json(['code' => 0, 'msg' => '注册成功']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Admin;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
|
||||
/**
|
||||
* 管理员账户
|
||||
*/
|
||||
class AccountController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['login', 'logout', 'captcha', 'register', 'registerPage'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['info'];
|
||||
|
||||
/**
|
||||
* @var Admin
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户设置
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return raw_view('account/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$this->checkDatabaseAvailable();
|
||||
$captcha = $request->post('captcha', '');
|
||||
if (strtolower($captcha) !== session('captcha-login')) {
|
||||
return $this->json(1, '验证码错误');
|
||||
}
|
||||
$request->session()->forget('captcha-login');
|
||||
$username = $request->post('username', '');
|
||||
$password = $request->post('password', '');
|
||||
if (!$username) {
|
||||
return $this->json(1, '用户名不能为空');
|
||||
}
|
||||
$this->checkLoginLimit($username);
|
||||
$admin = Admin::where('username', $username)->first();
|
||||
if (!$admin || !Util::passwordVerify($password, $admin->password)) {
|
||||
return $this->json(1, '账户不存在或密码错误');
|
||||
}
|
||||
if ($admin->status != 0) {
|
||||
return $this->json(1, '当前账户暂时无法登录');
|
||||
}
|
||||
$admin->login_at = date('Y-m-d H:i:s');
|
||||
$admin->save();
|
||||
$this->removeLoginLimit($username);
|
||||
$admin = $admin->toArray();
|
||||
$session = $request->session();
|
||||
$admin['password'] = md5($admin['password']);
|
||||
$session->set('admin', $admin);
|
||||
return $this->json(0, '登录成功', [
|
||||
'nickname' => $admin['nickname'],
|
||||
'token' => $request->sessionId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function logout(Request $request): Response
|
||||
{
|
||||
$request->session()->delete('admin');
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function info(Request $request): Response
|
||||
{
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
return $this->json(1);
|
||||
}
|
||||
$info = [
|
||||
'id' => $admin['id'],
|
||||
'username' => $admin['username'],
|
||||
'nickname' => $admin['nickname'],
|
||||
'avatar' => $admin['avatar'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile'],
|
||||
'isSuperAdmin' => Auth::isSuperAdmin(),
|
||||
'token' => $request->sessionId(),
|
||||
];
|
||||
return $this->json(0, 'ok', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$allow_column = [
|
||||
'nickname' => 'nickname',
|
||||
'avatar' => 'avatar',
|
||||
'email' => 'email',
|
||||
'mobile' => 'mobile',
|
||||
];
|
||||
|
||||
$data = $request->post();
|
||||
$update_data = [];
|
||||
foreach ($allow_column as $key => $column) {
|
||||
if (isset($data[$key])) {
|
||||
$update_data[$column] = $data[$key];
|
||||
}
|
||||
}
|
||||
if (isset($update_data['password'])) {
|
||||
$update_data['password'] = Util::passwordHash($update_data['password']);
|
||||
}
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
$admin = admin();
|
||||
unset($update_data['password']);
|
||||
foreach ($update_data as $key => $value) {
|
||||
$admin[$key] = $value;
|
||||
}
|
||||
$request->session()->set('admin', $admin);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function password(Request $request): Response
|
||||
{
|
||||
$hash = Admin::find(admin_id())['password'];
|
||||
$password = $request->post('password');
|
||||
if (!$password) {
|
||||
return $this->json(2, '密码不能为空');
|
||||
}
|
||||
if ($request->post('password_confirm') !== $password) {
|
||||
return $this->json(3, '两次密码输入不一致');
|
||||
}
|
||||
if (!Util::passwordVerify($request->post('old_password'), $hash)) {
|
||||
return $this->json(1, '原始密码不正确');
|
||||
}
|
||||
$update_data = [
|
||||
'password' => Util::passwordHash($password)
|
||||
];
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
* @param Request $request
|
||||
* @param string $type
|
||||
* @return Response
|
||||
*/
|
||||
public function captcha(Request $request, string $type = 'login'): Response
|
||||
{
|
||||
$builder = new PhraseBuilder(4, 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ');
|
||||
$captcha = new CaptchaBuilder(null, $builder);
|
||||
$captcha->build(120);
|
||||
$request->session()->set("captcha-$type", strtolower($captcha->getPhrase()));
|
||||
$img_content = $captcha->get();
|
||||
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
if (!is_dir($limit_log_path)) {
|
||||
mkdir($limit_log_path, 0777, true);
|
||||
}
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
$time = date('YmdH') . ceil(date('i')/5);
|
||||
$limit_info = [];
|
||||
if (is_file($limit_file)) {
|
||||
$json_str = file_get_contents($limit_file);
|
||||
$limit_info = json_decode($json_str, true);
|
||||
}
|
||||
|
||||
if (!$limit_info || $limit_info['time'] != $time) {
|
||||
$limit_info = [
|
||||
'username' => $username,
|
||||
'count' => 0,
|
||||
'time' => $time
|
||||
];
|
||||
}
|
||||
$limit_info['count']++;
|
||||
file_put_contents($limit_file, json_encode($limit_info));
|
||||
if ($limit_info['count'] >= 5) {
|
||||
throw new BusinessException('登录失败次数过多,请5分钟后再试');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
*/
|
||||
protected function removeLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
if (is_file($limit_file)) {
|
||||
unlink($limit_file);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkDatabaseAvailable()
|
||||
{
|
||||
if (!config('plugin.admin.database')) {
|
||||
throw new BusinessException('请重启webman');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\model\Admin;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 管理员列表
|
||||
*/
|
||||
class AdminController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = ['select'];
|
||||
|
||||
/**
|
||||
* @var Admin
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 开启auth数据限制
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimit = 'auth';
|
||||
|
||||
/**
|
||||
* 以id为数据限制字段
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimitField = 'id';
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('admin/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
if ($format === 'select') {
|
||||
return $this->formatSelect($query->get());
|
||||
}
|
||||
$paginator = $query->paginate($limit);
|
||||
$items = $paginator->items();
|
||||
$admin_ids = array_column($items, 'id');
|
||||
$roles = AdminRole::whereIn('admin_id', $admin_ids)->get();
|
||||
$roles_map = [];
|
||||
foreach ($roles as $role) {
|
||||
$roles_map[$role['admin_id']][] = $role['role_id'];
|
||||
}
|
||||
$login_admin_id = admin_id();
|
||||
foreach ($items as $index => $item) {
|
||||
$admin_id = $item['id'];
|
||||
$items[$index]['roles'] = isset($roles_map[$admin_id]) ? implode(',', $roles_map[$admin_id]) : '';
|
||||
$items[$index]['show_toolbar'] = $admin_id != $login_admin_id;
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $paginator->total(), 'data' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $this->insertInput($request);
|
||||
unset($data['id']);
|
||||
$admin_id = $this->doInsert($data);
|
||||
$role_ids = $request->post('roles');
|
||||
$role_ids = $role_ids ? explode(',', $role_ids) : [];
|
||||
if (!$role_ids) {
|
||||
return $this->json(1, '至少选择一个角色组');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($role_ids, Auth::getScopeRoleIds())) {
|
||||
return $this->json(1, '角色超出权限范围');
|
||||
}
|
||||
AdminRole::where('admin_id', $admin_id)->delete();
|
||||
foreach ($role_ids as $id) {
|
||||
$admin_role = new AdminRole;
|
||||
$admin_role->admin_id = $admin_id;
|
||||
$admin_role->role_id = $id;
|
||||
$admin_role->save();
|
||||
}
|
||||
return $this->json(0, 'ok', ['id' => $admin_id]);
|
||||
}
|
||||
return raw_view('admin/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$admin_id = $request->post('id');
|
||||
if (!$admin_id) {
|
||||
return $this->json(1, '缺少参数');
|
||||
}
|
||||
|
||||
// 不能禁用自己
|
||||
if (isset($data['status']) && $data['status'] == 1 && $id == admin_id()) {
|
||||
return $this->json(1, '不能禁用自己');
|
||||
}
|
||||
|
||||
// 需要更新角色
|
||||
$role_ids = $request->post('roles');
|
||||
if ($role_ids !== null) {
|
||||
if (!$role_ids) {
|
||||
return $this->json(1, '至少选择一个角色组');
|
||||
}
|
||||
$role_ids = explode(',', $role_ids);
|
||||
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
$exist_role_ids = AdminRole::where('admin_id', $admin_id)->pluck('role_id')->toArray();
|
||||
$scope_role_ids = Auth::getScopeRoleIds();
|
||||
if (!$is_supper_admin && !array_intersect($exist_role_ids, $scope_role_ids)) {
|
||||
return $this->json(1, '无权限更改该记录');
|
||||
}
|
||||
if (!$is_supper_admin && array_diff($role_ids, $scope_role_ids)) {
|
||||
return $this->json(1, '角色超出权限范围');
|
||||
}
|
||||
|
||||
// 删除账户角色
|
||||
$delete_ids = array_diff($exist_role_ids, $role_ids);
|
||||
AdminRole::whereIn('role_id', $delete_ids)->where('admin_id', $admin_id)->delete();
|
||||
// 添加账户角色
|
||||
$add_ids = array_diff($role_ids, $exist_role_ids);
|
||||
foreach ($add_ids as $role_id) {
|
||||
$admin_role = new AdminRole;
|
||||
$admin_role->admin_id = $admin_id;
|
||||
$admin_role->role_id = $role_id;
|
||||
$admin_role->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
return raw_view('admin/update');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$primary_key = $this->model->getKeyName();
|
||||
$ids = $request->post($primary_key);
|
||||
if (!$ids) {
|
||||
return $this->json(0);
|
||||
}
|
||||
$ids = (array)$ids;
|
||||
if (in_array(admin_id(), $ids)) {
|
||||
return $this->json(1, '不能删除自己');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($ids, Auth::getScopeAdminIds())) {
|
||||
return $this->json(1, '无数据权限');
|
||||
}
|
||||
$this->model->whereIn($primary_key, $ids)->each(function (Admin $admin) {
|
||||
$admin->delete();
|
||||
});
|
||||
AdminRole::whereIn('admin_id', $ids)->each(function (AdminRole $admin_role) {
|
||||
$admin_role->delete();
|
||||
});
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Model;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 基础控制器
|
||||
*/
|
||||
class Base
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 无需登录及鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
/**
|
||||
* 需要登录无需鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = [];
|
||||
|
||||
/**
|
||||
* 数据限制
|
||||
* null 不做限制,任何管理员都可以查看该表的所有数据
|
||||
* auth 管理员能看到自己以及自己的子管理员插入的数据
|
||||
* personal 管理员只能看到自己插入的数据
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimit = null;
|
||||
|
||||
/**
|
||||
* 数据限制字段
|
||||
*/
|
||||
protected $dataLimitField = 'admin_id';
|
||||
|
||||
/**
|
||||
* 返回格式化json数据
|
||||
*
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @return Response
|
||||
*/
|
||||
protected function json(int $code, string $msg = 'ok', array $data = []): Response
|
||||
{
|
||||
return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
protected function success(string $msg = '成功', array $data = []): Response
|
||||
{
|
||||
return $this->json(0, $msg, $data);
|
||||
}
|
||||
|
||||
protected function fail(string $msg = '失败', array $data = []): Response
|
||||
{
|
||||
return $this->json(1, $msg, $data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Option;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
class ConfigController extends Base
|
||||
{
|
||||
/**
|
||||
* 不需要验证权限的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['get'];
|
||||
|
||||
/**
|
||||
* 账户设置
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('config/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return Response
|
||||
*/
|
||||
public function get(): Response
|
||||
{
|
||||
return json($this->getByDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于配置文件获取默认权限
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getByDefault()
|
||||
{
|
||||
$name = 'system_config';
|
||||
$config = Option::where('name', $name)->value('value');
|
||||
if (empty($config)) {
|
||||
$config = file_get_contents(base_path('plugin/admin/public/config/pear.config.json'));
|
||||
if ($config) {
|
||||
$option = new Option();
|
||||
$option->name = $name;
|
||||
$option->value = $config;
|
||||
$option->save();
|
||||
}
|
||||
}
|
||||
return json_decode($config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$post = $request->post();
|
||||
$config = $this->getByDefault();
|
||||
$data = [];
|
||||
foreach ($post as $section => $items) {
|
||||
if (!isset($config[$section])) {
|
||||
continue;
|
||||
}
|
||||
switch ($section) {
|
||||
case 'logo':
|
||||
$data[$section]['title'] = htmlspecialchars($items['title'] ?? '');
|
||||
$data[$section]['image'] = Util::filterUrlPath($items['image'] ?? '');
|
||||
$data[$section]['icp'] = htmlspecialchars($items['icp'] ?? '');
|
||||
$data[$section]['beian'] = htmlspecialchars($items['beian'] ?? '');
|
||||
$data[$section]['footer_txt'] = htmlspecialchars($items['footer_txt'] ?? '');
|
||||
break;
|
||||
case 'menu':
|
||||
$data[$section]['data'] = Util::filterUrlPath($items['data'] ?? '');
|
||||
$data[$section]['accordion'] = !empty($items['accordion']);
|
||||
$data[$section]['collapse'] = !empty($items['collapse']);
|
||||
$data[$section]['control'] = !empty($items['control']);
|
||||
$data[$section]['controlWidth'] = (int)($items['controlWidth'] ?? 2000);
|
||||
$data[$section]['select'] = (int)$items['select'] ?? 0;
|
||||
$data[$section]['async'] = true;
|
||||
break;
|
||||
case 'tab':
|
||||
$data[$section]['enable'] = true;
|
||||
$data[$section]['keepState'] = !empty($items['keepState']);
|
||||
$data[$section]['preload'] = !empty($items['preload']);
|
||||
$data[$section]['session'] = !empty($items['session']);
|
||||
$data[$section]['max'] = Util::filterNum($items['max'] ?? '30');
|
||||
$data[$section]['index']['id'] = Util::filterNum($items['index']['id'] ?? '0');
|
||||
$data[$section]['index']['href'] = Util::filterUrlPath($items['index']['href'] ?? '');
|
||||
$data[$section]['index']['title'] = htmlspecialchars($items['index']['title'] ?? '首页');
|
||||
break;
|
||||
case 'theme':
|
||||
$data[$section]['defaultColor'] = Util::filterNum($items['defaultColor'] ?? '2');
|
||||
$data[$section]['defaultMenu'] = $items['defaultMenu'] ?? '' == 'dark-theme' ? 'dark-theme' : 'light-theme';
|
||||
$data[$section]['defaultHeader'] = $items['defaultHeader'] ?? '' == 'dark-theme' ? 'dark-theme' : 'light-theme';
|
||||
$data[$section]['allowCustom'] = !empty($items['allowCustom']);
|
||||
$data[$section]['banner'] = !empty($items['banner']);
|
||||
break;
|
||||
case 'colors':
|
||||
foreach ($config['colors'] as $index => $item) {
|
||||
if (!isset($items[$index])) {
|
||||
$config['colors'][$index] = $item;
|
||||
continue;
|
||||
}
|
||||
$data_item = $items[$index];
|
||||
$data[$section][$index]['id'] = $index + 1;
|
||||
$data[$section][$index]['color'] = $this->filterColor($data_item['color'] ?? '');
|
||||
$data[$section][$index]['second'] = $this->filterColor($data_item['second'] ?? '');
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
$config = array_merge($config, $data);
|
||||
$name = 'system_config';
|
||||
Option::where('name', $name)->update([
|
||||
'value' => json_encode($config)
|
||||
]);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色检查
|
||||
* @param string $color
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function filterColor(string $color): string
|
||||
{
|
||||
if (!preg_match('/\#[a-zA-Z]6/', $color)) {
|
||||
throw new BusinessException('参数错误');
|
||||
}
|
||||
return $color;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\common\Util;
|
||||
use support\exception\BusinessException;
|
||||
use support\Model;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
class Crud extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
$data = $this->insertInput($request);
|
||||
$id = $this->doInsert($data);
|
||||
return $this->json(0, 'ok', ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
$this->doDelete($ids);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询前置
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function selectInput(Request $request): array
|
||||
{
|
||||
$field = $request->get('field');
|
||||
$order = $request->get('order', 'asc');
|
||||
$format = $request->get('format', 'normal');
|
||||
$limit = (int)$request->get('limit', $format === 'tree' ? 1000 : 10);
|
||||
$limit = $limit <= 0 ? 10 : $limit;
|
||||
$order = $order === 'asc' ? 'asc' : 'desc';
|
||||
$where = $request->get();
|
||||
$page = (int)$request->get('page');
|
||||
$page = $page > 0 ? $page : 1;
|
||||
$table = config('plugin.admin.database.connections.mysql.prefix') . $this->model->getTable();
|
||||
|
||||
$allow_column = Util::db()->select("desc `$table`");
|
||||
if (!$allow_column) {
|
||||
throw new BusinessException('表不存在');
|
||||
}
|
||||
$allow_column = array_column($allow_column, 'Field', 'Field');
|
||||
if (!in_array($field, $allow_column)) {
|
||||
$field = null;
|
||||
}
|
||||
foreach ($where as $column => $value) {
|
||||
if (
|
||||
$value === '' || !isset($allow_column[$column]) ||
|
||||
is_array($value) && (empty($value) || !in_array($value[0], ['null', 'not null']) && !isset($value[1]))
|
||||
) {
|
||||
unset($where[$column]);
|
||||
}
|
||||
}
|
||||
// 按照数据限制字段返回数据
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit === 'personal') {
|
||||
$where[$this->dataLimitField] = admin_id();
|
||||
} elseif ($this->dataLimit === 'auth') {
|
||||
$primary_key = $this->model->getKeyName();
|
||||
if (!Auth::isSuperAdmin() && (!isset($where[$primary_key]) || $this->dataLimitField != $primary_key)) {
|
||||
$where[$this->dataLimitField] = ['in', Auth::getScopeAdminIds(true)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$where, $format, $limit, $field, $order, $page];
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定查询where条件,并没有真正的查询数据库操作
|
||||
* @param array $where
|
||||
* @param string|null $field
|
||||
* @param string $order
|
||||
* @return EloquentBuilder|QueryBuilder|Model
|
||||
*/
|
||||
protected function doSelect(array $where, ?string $field = null, string $order= 'desc')
|
||||
{
|
||||
$model = $this->model;
|
||||
foreach ($where as $column => $value) {
|
||||
if (is_array($value)) {
|
||||
if ($value[0] === 'like' || $value[0] === 'not like') {
|
||||
$model = $model->where($column, $value[0], "%$value[1]%");
|
||||
} elseif (in_array($value[0], ['>', '=', '<', '<>'])) {
|
||||
$model = $model->where($column, $value[0], $value[1]);
|
||||
} elseif ($value[0] == 'in' && !empty($value[1])) {
|
||||
$valArr = $value[1];
|
||||
if (is_string($value[1])) {
|
||||
$valArr = explode(",", trim($value[1]));
|
||||
}
|
||||
$model = $model->whereIn($column, $valArr);
|
||||
} elseif ($value[0] == 'not in' && !empty($value[1])) {
|
||||
$valArr = $value[1];
|
||||
if (is_string($value[1])) {
|
||||
$valArr = explode(",", trim($value[1]));
|
||||
}
|
||||
$model = $model->whereNotIn($column, $valArr);
|
||||
}elseif ($value[0] == 'null') {
|
||||
$model = $model->whereNull($column);
|
||||
} elseif ($value[0] == 'not null') {
|
||||
$model = $model->whereNotNull($column);
|
||||
} elseif ($value[0] !== '' || $value[1] !== '') {
|
||||
$model = $model->whereBetween($column, $value);
|
||||
}
|
||||
} else {
|
||||
$model = $model->where($column, $value);
|
||||
}
|
||||
}
|
||||
if ($field) {
|
||||
$model = $model->orderBy($field, $order);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行真正查询,并返回格式化数据
|
||||
* @param $query
|
||||
* @param $format
|
||||
* @param $limit
|
||||
* @return Response
|
||||
*/
|
||||
protected function doFormat($query, $format, $limit): Response
|
||||
{
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$paginator = $query->paginate($limit);
|
||||
$total = $paginator->total();
|
||||
$items = $paginator->items();
|
||||
if (method_exists($this, "afterQuery")) {
|
||||
$items = call_user_func([$this, "afterQuery"], $items);
|
||||
}
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
return call_user_func([$this, $format_function], $items, $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function insertInput(Request $request): array
|
||||
{
|
||||
$data = $this->inputFilter($request->post());
|
||||
$password_filed = 'password';
|
||||
if (isset($data[$password_filed])) {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit === 'personal') {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
} elseif ($this->dataLimit === 'auth') {
|
||||
if (!empty($data[$this->dataLimitField])) {
|
||||
$admin_id = $data[$this->dataLimitField];
|
||||
if (!in_array($admin_id, Auth::getScopeAdminIds(true))) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} else {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
}
|
||||
}
|
||||
} elseif ($this->dataLimit && empty($data[$this->dataLimitField])) {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行插入
|
||||
* @param array $data
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function doInsert(array $data)
|
||||
{
|
||||
$primary_key = $this->model->getKeyName();
|
||||
$model_class = get_class($this->model);
|
||||
$model = new $model_class;
|
||||
foreach ($data as $key => $val) {
|
||||
$model->{$key} = $val;
|
||||
}
|
||||
$model->save();
|
||||
return $primary_key ? $model->$primary_key : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function updateInput(Request $request): array
|
||||
{
|
||||
$primary_key = $this->model->getKeyName();
|
||||
$id = $request->post($primary_key);
|
||||
$data = $this->inputFilter($request->post());
|
||||
$model = $this->model->find($id);
|
||||
if (!$model) {
|
||||
throw new BusinessException('记录不存在', 2);
|
||||
}
|
||||
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit == 'personal') {
|
||||
if ($model->{$this->dataLimitField} != admin_id()) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} elseif ($this->dataLimit == 'auth') {
|
||||
$scopeAdminIds = Auth::getScopeAdminIds(true);
|
||||
$admin_ids = [
|
||||
$data[$this->dataLimitField] ?? false, // 检查要更新的数据admin_id是否是有权限的值
|
||||
$model->{$this->dataLimitField} ?? false // 检查要更新的记录的admin_id是否有权限
|
||||
];
|
||||
foreach ($admin_ids as $admin_id) {
|
||||
if ($admin_id && !in_array($admin_id, $scopeAdminIds)) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$password_filed = 'password';
|
||||
if (isset($data[$password_filed])) {
|
||||
// 密码为空,则不更新密码
|
||||
if ($data[$password_filed] === '') {
|
||||
unset($data[$password_filed]);
|
||||
} else {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
}
|
||||
unset($data[$primary_key]);
|
||||
return [$id, $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行更新
|
||||
* @param $id
|
||||
* @param $data
|
||||
* @return void
|
||||
*/
|
||||
protected function doUpdate($id, $data)
|
||||
{
|
||||
$model = $this->model->find($id);
|
||||
foreach ($data as $key => $val) {
|
||||
$model->{$key} = $val;
|
||||
}
|
||||
$model->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对用户输入表单过滤
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function inputFilter(array $data): array
|
||||
{
|
||||
$table = config('plugin.admin.database.connections.mysql.prefix') . $this->model->getTable();
|
||||
$allow_column = $this->model->getConnection()->select("desc `$table`");
|
||||
if (!$allow_column) {
|
||||
throw new BusinessException('表不存在', 2);
|
||||
}
|
||||
$columns = array_column($allow_column, 'Type', 'Field');
|
||||
foreach ($data as $col => $item) {
|
||||
if (!isset($columns[$col])) {
|
||||
unset($data[$col]);
|
||||
continue;
|
||||
}
|
||||
// 非字符串类型传空则为null
|
||||
if ($item === '' && strpos(strtolower($columns[$col]), 'varchar') === false && strpos(strtolower($columns[$col]), 'text') === false) {
|
||||
$data[$col] = null;
|
||||
}
|
||||
if (is_array($item)) {
|
||||
$data[$col] = implode(',', $item);
|
||||
}
|
||||
}
|
||||
if (empty($data['created_at'])) {
|
||||
unset($data['created_at']);
|
||||
}
|
||||
if (empty($data['updated_at'])) {
|
||||
unset($data['updated_at']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function deleteInput(Request $request): array
|
||||
{
|
||||
$primary_key = $this->model->getKeyName();
|
||||
if (!$primary_key) {
|
||||
throw new BusinessException('该表无主键,不支持删除');
|
||||
}
|
||||
$ids = (array)$request->post($primary_key, []);
|
||||
if (!Auth::isSuperAdmin()){
|
||||
$admin_ids = [];
|
||||
if ($this->dataLimit) {
|
||||
$admin_ids = $this->model->where($primary_key, $ids)->pluck($this->dataLimitField)->toArray();
|
||||
}
|
||||
if ($this->dataLimit == 'personal') {
|
||||
if (!in_array(admin_id(), $admin_ids)) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} elseif ($this->dataLimit == 'auth') {
|
||||
if (array_diff($admin_ids, Auth::getScopeAdminIds(true))) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param array $ids
|
||||
* @return void
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
if (!$ids) {
|
||||
return;
|
||||
}
|
||||
$primary_key = $this->model->getKeyName();
|
||||
$this->model->whereIn($primary_key, $ids)->each(function ($model) {
|
||||
$model->delete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化树
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatTree($items): Response
|
||||
{
|
||||
$format_items = [];
|
||||
$primary_key = $this->model->getKeyName();
|
||||
foreach ($items as $item) {
|
||||
$format_items[] = [
|
||||
'name' => $this->guessName($item) ?: $item->$primary_key,
|
||||
'value' => (string)$item->$primary_key,
|
||||
'id' => $item->$primary_key,
|
||||
'pid' => $item->pid,
|
||||
];
|
||||
}
|
||||
$tree = new Tree($format_items);
|
||||
return $this->json(0, 'ok', $tree->getTree());
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化表格树
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatTableTree($items): Response
|
||||
{
|
||||
$tree = new Tree($items);
|
||||
return $this->json(0, 'ok', $tree->getTree());
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化下拉列表
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatSelect($items): Response
|
||||
{
|
||||
$formatted_items = [];
|
||||
$primary_key = $this->model->getKeyName();
|
||||
foreach ($items as $item) {
|
||||
$formatted_items[] = [
|
||||
'name' => $this->guessName($item) ?: $item->$primary_key,
|
||||
'value' => $item->$primary_key
|
||||
];
|
||||
}
|
||||
return $this->json(0, 'ok', $formatted_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用格式化
|
||||
* @param $items
|
||||
* @param $total
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatNormal($items, $total): Response
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $total, 'data' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据库后置方法,可用于修改数据
|
||||
* @param mixed $items 原数据
|
||||
* @return mixed 修改后数据
|
||||
*/
|
||||
protected function afterQuery($items)
|
||||
{
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 猜测记录名称
|
||||
* @param $item
|
||||
* @return mixed
|
||||
*/
|
||||
protected function guessName($item)
|
||||
{
|
||||
return $item->title ?? $item->name ?? $item->nickname ?? $item->username ?? $item->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 开发辅助相关
|
||||
*/
|
||||
class DevController
|
||||
{
|
||||
/**
|
||||
* 表单构建
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function formBuild()
|
||||
{
|
||||
return raw_view('dev/form-build');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\model\Dict;
|
||||
use plugin\admin\app\model\Option;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 字典管理
|
||||
*/
|
||||
class DictController extends Base
|
||||
{
|
||||
/**
|
||||
* 不需要授权的方法
|
||||
*/
|
||||
protected $noNeedAuth = ['get'];
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('dict/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$name = $request->get('name', '');
|
||||
$page = $request->get('page', 1);
|
||||
$limit = $request->get('limit', 10);
|
||||
$offset = ($page-1)*$limit;
|
||||
if ($name && is_string($name)) {
|
||||
$items = Option::where('name', 'like', "dict_$name%")->limit($limit)->offset($offset)->get()->toArray();
|
||||
} else {
|
||||
$items = Option::where('name', 'like', 'dict_%')->limit($limit)->offset($offset)->get()->toArray();
|
||||
}
|
||||
$get_items = Option::where('name', 'like', "dict_$name%")->get()->toArray();
|
||||
$count = count($get_items);
|
||||
foreach ($items as &$item) {
|
||||
$item['name'] = Dict::optionNameTodictName($item['name']);
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $count, 'data' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$name = $request->post('name');
|
||||
if (Dict::get($name)) {
|
||||
return $this->json(1, '字典已经存在');
|
||||
}
|
||||
if (!preg_match('/^[a-zA-Z0-9]+$/', $name)) {
|
||||
return $this->json(2, '字典名称只能是字母数字的组合');
|
||||
}
|
||||
$values = (array)$request->post('value', []);
|
||||
Dict::save($name, $values);
|
||||
}
|
||||
return raw_view('dict/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$name = $request->post('name');
|
||||
if (!Dict::get($name)) {
|
||||
return $this->json(1, '字典不存在');
|
||||
}
|
||||
if (!preg_match('/^[a-zA-Z0-9]+$/', $name)) {
|
||||
return $this->json(2, '字典名称只能是字母数字的组合');
|
||||
}
|
||||
Dict::save($name, $request->post('value'));
|
||||
}
|
||||
return raw_view('dict/update');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$names = (array)$request->post('name');
|
||||
Dict::delete($names);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param Request $request
|
||||
* @param $name
|
||||
* @return Response
|
||||
*/
|
||||
public function get(Request $request, $name): Response
|
||||
{
|
||||
return $this->json(0, 'ok', (array)Dict::get($name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Option;
|
||||
use plugin\admin\app\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use think\db\Where;
|
||||
use Throwable;
|
||||
use Workerman\Worker;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
|
||||
/**
|
||||
* 无需登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['index'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['dashboard'];
|
||||
|
||||
/**
|
||||
* 后台主页
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
clearstatcache();
|
||||
if (!is_file(base_path('plugin/admin/config/database.php'))) {
|
||||
return raw_view('index/install');
|
||||
}
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
$name = 'system_config';
|
||||
$config = Option::where('name', $name)->value('value');
|
||||
$config = json_decode($config, true);
|
||||
$title = $config['logo']['title'] ?? 'webman admin';
|
||||
$logo = $config['logo']['image'] ?? '/app/admin/admin/images/logo.png';
|
||||
return raw_view('account/login',['logo'=>$logo,'title'=>$title]);
|
||||
}
|
||||
return raw_view('index/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 仪表板
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function dashboard(Request $request): Response
|
||||
{
|
||||
// 今日新增用户数
|
||||
$today_user_count = User::where('created_at', '>', date('Y-m-d') . ' 00:00:00')->count();
|
||||
// 7天内新增用户数
|
||||
$day7_user_count = User::where('created_at', '>', date('Y-m-d H:i:s', time() - 7 * 24 * 60 * 60))->count();
|
||||
// 30天内新增用户数
|
||||
$day30_user_count = User::where('created_at', '>', date('Y-m-d H:i:s', time() - 30 * 24 * 60 * 60))->count();
|
||||
// 总用户数
|
||||
$user_count = User::count();
|
||||
// mysql版本
|
||||
$version = Util::db()->select('select VERSION() as version');
|
||||
$mysql_version = $version[0]->version ?? 'unknown';
|
||||
|
||||
$day7_detail = [];
|
||||
$now = time();
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$date = date('Y-m-d', $now - 24 * 60 * 60 * $i);
|
||||
$day7_detail[substr($date, 5)] = User::where('created_at', '>', "$date 00:00:00")
|
||||
->where('created_at', '<', "$date 23:59:59")->count();
|
||||
}
|
||||
|
||||
return raw_view('index/dashboard', [
|
||||
'today_user_count' => $today_user_count,
|
||||
'day7_user_count' => $day7_user_count,
|
||||
'day30_user_count' => $day30_user_count,
|
||||
'user_count' => $user_count,
|
||||
'php_version' => PHP_VERSION,
|
||||
'workerman_version' => Worker::VERSION,
|
||||
'webman_version' => Util::getPackageVersion('workerman/webman-framework'),
|
||||
'admin_version' => Util::getPackageVersion('webman/admin'),
|
||||
'mysql_version' => $mysql_version,
|
||||
'os' => PHP_OS,
|
||||
'day7_detail' => array_reverse($day7_detail),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Illuminate\Database\Capsule\Manager;
|
||||
use plugin\admin\app\common\Util;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
|
||||
/**
|
||||
* 安装
|
||||
*/
|
||||
class InstallController extends Base
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['step1', 'step2'];
|
||||
|
||||
/**
|
||||
* 设置数据库
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|\Throwable
|
||||
*/
|
||||
public function step1(Request $request): Response
|
||||
{
|
||||
$database_config_file = base_path() . '/plugin/admin/config/database.php';
|
||||
clearstatcache();
|
||||
if (is_file($database_config_file)) {
|
||||
return $this->json(1, '管理后台已经安装!如需重新安装,请删除该插件数据库配置文件并重启');
|
||||
}
|
||||
|
||||
if (!class_exists(CaptchaBuilder::class) || !class_exists(Manager::class)) {
|
||||
return $this->json(1, '请运行 composer require -W illuminate/database 安装illuminate/database组件并重启');
|
||||
}
|
||||
|
||||
$user = $request->post('user');
|
||||
$password = $request->post('password');
|
||||
$database = $request->post('database');
|
||||
$host = $request->post('host');
|
||||
$port = (int)$request->post('port') ?: 3306;
|
||||
$overwrite = $request->post('overwrite');
|
||||
|
||||
try {
|
||||
$db = $this->getPdo($host, $user, $password, $port);
|
||||
$smt = $db->query("show databases like '$database'");
|
||||
if (empty($smt->fetchAll())) {
|
||||
$db->exec("create database $database");
|
||||
}
|
||||
$db->exec("use $database");
|
||||
$smt = $db->query("show tables");
|
||||
$tables = $smt->fetchAll();
|
||||
} catch (\Throwable $e) {
|
||||
if (stripos($e, 'Access denied for user')) {
|
||||
return $this->json(1, '数据库用户名或密码错误');
|
||||
}
|
||||
if (stripos($e, 'Connection refused')) {
|
||||
return $this->json(1, 'Connection refused. 请确认数据库IP端口是否正确,数据库已经启动');
|
||||
}
|
||||
if (stripos($e, 'timed out')) {
|
||||
return $this->json(1, '数据库连接超时,请确认数据库IP端口是否正确,安全组及防火墙已经放行端口');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$tables_to_install = [
|
||||
'wa_admins',
|
||||
'wa_admin_roles',
|
||||
'wa_roles',
|
||||
'wa_rules',
|
||||
'wa_options',
|
||||
'wa_users',
|
||||
'wa_uploads',
|
||||
];
|
||||
|
||||
$tables_exist = [];
|
||||
foreach ($tables as $table) {
|
||||
$tables_exist[] = current($table);
|
||||
}
|
||||
$tables_conflict = array_intersect($tables_to_install, $tables_exist);
|
||||
if (!$overwrite) {
|
||||
if ($tables_conflict) {
|
||||
return $this->json(1, '以下表' . implode(',', $tables_conflict) . '已经存在,如需覆盖请选择强制覆盖');
|
||||
}
|
||||
} else {
|
||||
foreach ($tables_conflict as $table) {
|
||||
$db->exec("DROP TABLE `$table`");
|
||||
}
|
||||
}
|
||||
|
||||
$sql_file = base_path() . '/plugin/admin/install.sql';
|
||||
if (!is_file($sql_file)) {
|
||||
return $this->json(1, '数据库SQL文件不存在');
|
||||
}
|
||||
|
||||
$sql_query = file_get_contents($sql_file);
|
||||
$sql_query = $this->removeComments($sql_query);
|
||||
$sql_query = $this->splitSqlFile($sql_query, ';');
|
||||
foreach ($sql_query as $sql) {
|
||||
$db->exec($sql);
|
||||
}
|
||||
|
||||
// 导入菜单
|
||||
$menus = include base_path() . '/plugin/admin/config/menu.php';
|
||||
// 安装过程中没有数据库配置,无法使用api\Menu::import()方法
|
||||
$this->importMenu($menus, $db);
|
||||
|
||||
$config_content = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'default' => 'mysql',
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => '$host',
|
||||
'port' => '$port',
|
||||
'database' => '$database',
|
||||
'username' => '$user',
|
||||
'password' => '$password',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
'prefix' => '',
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
],
|
||||
],
|
||||
];
|
||||
EOF;
|
||||
|
||||
file_put_contents($database_config_file, $config_content);
|
||||
|
||||
$think_orm_config = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'default' => 'mysql',
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
// 数据库类型
|
||||
'type' => 'mysql',
|
||||
// 服务器地址
|
||||
'hostname' => '$host',
|
||||
// 数据库名
|
||||
'database' => '$database',
|
||||
// 数据库用户名
|
||||
'username' => '$user',
|
||||
// 数据库密码
|
||||
'password' => '$password',
|
||||
// 数据库连接端口
|
||||
'hostport' => $port,
|
||||
// 数据库连接参数
|
||||
'params' => [
|
||||
// 连接超时3秒
|
||||
\PDO::ATTR_TIMEOUT => 3,
|
||||
],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => 'utf8mb4',
|
||||
// 数据库表前缀
|
||||
'prefix' => '',
|
||||
// 断线重连
|
||||
'break_reconnect' => true,
|
||||
// 关闭SQL监听日志
|
||||
'trigger_sql' => true,
|
||||
// 自定义分页类
|
||||
'bootstrap' => ''
|
||||
],
|
||||
],
|
||||
];
|
||||
EOF;
|
||||
file_put_contents(base_path() . '/plugin/admin/config/thinkorm.php', $think_orm_config);
|
||||
|
||||
|
||||
// 尝试reload
|
||||
if (function_exists('posix_kill')) {
|
||||
set_error_handler(function () {});
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置管理员
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function step2(Request $request): Response
|
||||
{
|
||||
$username = $request->post('username');
|
||||
$password = $request->post('password');
|
||||
$password_confirm = $request->post('password_confirm');
|
||||
if ($password != $password_confirm) {
|
||||
return $this->json(1, '两次密码不一致');
|
||||
}
|
||||
if (!is_file($config_file = base_path() . '/plugin/admin/config/database.php')) {
|
||||
return $this->json(1, '请先完成第一步数据库配置');
|
||||
}
|
||||
$config = include $config_file;
|
||||
$connection = $config['connections']['mysql'];
|
||||
$pdo = $this->getPdo($connection['host'], $connection['username'], $connection['password'], $connection['port'], $connection['database']);
|
||||
|
||||
if ($pdo->query('select * from `wa_admins`')->fetchAll()) {
|
||||
return $this->json(1, '后台已经安装完毕,无法通过此页面创建管理员');
|
||||
}
|
||||
|
||||
$smt = $pdo->prepare("insert into `wa_admins` (`username`, `password`, `nickname`, `created_at`, `updated_at`) values (:username, :password, :nickname, :created_at, :updated_at)");
|
||||
$time = date('Y-m-d H:i:s');
|
||||
$data = [
|
||||
'username' => $username,
|
||||
'password' => Util::passwordHash($password),
|
||||
'nickname' => '超级管理员',
|
||||
'created_at' => $time,
|
||||
'updated_at' => $time
|
||||
];
|
||||
foreach ($data as $key => $value) {
|
||||
$smt->bindValue($key, $value);
|
||||
}
|
||||
$smt->execute();
|
||||
$admin_id = $pdo->lastInsertId();
|
||||
|
||||
$smt = $pdo->prepare("insert into `wa_admin_roles` (`role_id`, `admin_id`) values (:role_id, :admin_id)");
|
||||
$smt->bindValue('role_id', 1);
|
||||
$smt->bindValue('admin_id', $admin_id);
|
||||
$smt->execute();
|
||||
|
||||
$request->session()->flush();
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜单
|
||||
* @param array $menu
|
||||
* @param \PDO $pdo
|
||||
* @return int
|
||||
*/
|
||||
protected function addMenu(array $menu, \PDO $pdo): int
|
||||
{
|
||||
$allow_columns = ['title', 'key', 'icon', 'href', 'pid', 'weight', 'type'];
|
||||
$data = [];
|
||||
foreach ($allow_columns as $column) {
|
||||
if (isset($menu[$column])) {
|
||||
$data[$column] = $menu[$column];
|
||||
}
|
||||
}
|
||||
$time = date('Y-m-d H:i:s');
|
||||
$data['created_at'] = $data['updated_at'] = $time;
|
||||
$values = [];
|
||||
foreach ($data as $k => $v) {
|
||||
$values[] = ":$k";
|
||||
}
|
||||
$columns = array_keys($data);
|
||||
foreach ($columns as $k => $column) {
|
||||
$columns[$k] = "`$column`";
|
||||
}
|
||||
$sql = "insert into wa_rules (" .implode(',', $columns). ") values (" . implode(',', $values) . ")";
|
||||
$smt = $pdo->prepare($sql);
|
||||
foreach ($data as $key => $value) {
|
||||
$smt->bindValue($key, $value);
|
||||
}
|
||||
$smt->execute();
|
||||
return $pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入菜单
|
||||
* @param array $menu_tree
|
||||
* @param \PDO $pdo
|
||||
* @return void
|
||||
*/
|
||||
protected function importMenu(array $menu_tree, \PDO $pdo)
|
||||
{
|
||||
if (is_numeric(key($menu_tree)) && !isset($menu_tree['key'])) {
|
||||
foreach ($menu_tree as $item) {
|
||||
$this->importMenu($item, $pdo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$children = $menu_tree['children'] ?? [];
|
||||
unset($menu_tree['children']);
|
||||
$smt = $pdo->prepare("select * from wa_rules where `key`=:key limit 1");
|
||||
$smt->execute(['key' => $menu_tree['key']]);
|
||||
$old_menu = $smt->fetch();
|
||||
if ($old_menu) {
|
||||
$pid = $old_menu['id'];
|
||||
$params = [
|
||||
'title' => $menu_tree['title'],
|
||||
'icon' => $menu_tree['icon'] ?? '',
|
||||
'key' => $menu_tree['key'],
|
||||
];
|
||||
$sql = "update wa_rules set title=:title, icon=:icon where `key`=:key";
|
||||
$smt = $pdo->prepare($sql);
|
||||
$smt->execute($params);
|
||||
} else {
|
||||
$pid = $this->addMenu($menu_tree, $pdo);
|
||||
}
|
||||
foreach ($children as $menu) {
|
||||
$menu['pid'] = $pid;
|
||||
$this->importMenu($menu, $pdo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除sql文件中的注释
|
||||
* @param $sql
|
||||
* @return string
|
||||
*/
|
||||
protected function removeComments($sql): string
|
||||
{
|
||||
return preg_replace("/(\n--[^\n]*)/","", $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割sql文件
|
||||
* @param $sql
|
||||
* @param $delimiter
|
||||
* @return array
|
||||
*/
|
||||
function splitSqlFile($sql, $delimiter): array
|
||||
{
|
||||
$tokens = explode($delimiter, $sql);
|
||||
$output = array();
|
||||
$matches = array();
|
||||
$token_count = count($tokens);
|
||||
for ($i = 0; $i < $token_count; $i++) {
|
||||
if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) {
|
||||
$total_quotes = preg_match_all("/'/", $tokens[$i], $matches);
|
||||
$escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$i], $matches);
|
||||
$unescaped_quotes = $total_quotes - $escaped_quotes;
|
||||
|
||||
if (($unescaped_quotes % 2) == 0) {
|
||||
$output[] = $tokens[$i];
|
||||
$tokens[$i] = "";
|
||||
} else {
|
||||
$temp = $tokens[$i] . $delimiter;
|
||||
$tokens[$i] = "";
|
||||
|
||||
$complete_stmt = false;
|
||||
for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++) {
|
||||
$total_quotes = preg_match_all("/'/", $tokens[$j], $matches);
|
||||
$escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$j], $matches);
|
||||
$unescaped_quotes = $total_quotes - $escaped_quotes;
|
||||
if (($unescaped_quotes % 2) == 1) {
|
||||
$output[] = $temp . $tokens[$j];
|
||||
$tokens[$j] = "";
|
||||
$temp = "";
|
||||
$complete_stmt = true;
|
||||
$i = $j;
|
||||
} else {
|
||||
$temp .= $tokens[$j] . $delimiter;
|
||||
$tokens[$j] = "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\admin\app\model\Location;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
use util\Util;
|
||||
|
||||
/**
|
||||
* 仓库/港口
|
||||
*/
|
||||
class LocationController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Location
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Location;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return view('location/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('location/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::update($request);
|
||||
}
|
||||
return view('location/update');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉选项接口
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
|
||||
$locations = Location::get();
|
||||
return Util::success($locations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use plugin\admin\app\common\Util;
|
||||
use process\Monitor;
|
||||
use support\exception\BusinessException;
|
||||
use support\Log;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use ZIPARCHIVE;
|
||||
use function array_diff;
|
||||
use function ini_get;
|
||||
use function scandir;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PATH_SEPARATOR;
|
||||
|
||||
class PluginController extends Base
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['schema', 'captcha'];
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$client = $this->httpClient();
|
||||
$response = $client->get("/webman-admin/apps");
|
||||
return (string)$response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function list(Request $request): Response
|
||||
{
|
||||
$installed = $this->getLocalPlugins();
|
||||
|
||||
$client = $this->httpClient();
|
||||
$query = $request->get();
|
||||
$query['version'] = $this->getAdminVersion();
|
||||
$response = $client->get('/api/app/list', ['query' => $query]);
|
||||
$content = $response->getBody()->getContents();
|
||||
$data = json_decode($content, true);
|
||||
if (!$data) {
|
||||
$msg = "/api/app/list return $content";
|
||||
echo "msg\r\n";
|
||||
Log::error($msg);
|
||||
return $this->json(1, '获取数据出错');
|
||||
}
|
||||
$disabled = is_phar();
|
||||
foreach ($data['data']['items'] as $key => $item) {
|
||||
$name = $item['name'];
|
||||
$data['data']['items'][$key]['installed'] = $installed[$name] ?? 0;
|
||||
$data['data']['items'][$key]['disabled'] = $disabled;
|
||||
}
|
||||
$items = $data['data']['items'];
|
||||
$count = $data['data']['total'];
|
||||
return json(['code' => 0, 'msg' => 'ok', 'data' => $items, 'count' => $count]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws GuzzleException|BusinessException
|
||||
*/
|
||||
public function install(Request $request): Response
|
||||
{
|
||||
$name = $request->post('name');
|
||||
$version = $request->post('version');
|
||||
$installed_version = $this->getPluginVersion($name);
|
||||
if (!$name || !$version) {
|
||||
return $this->json(1, '缺少参数');
|
||||
}
|
||||
|
||||
$user = session('app-plugin-user');
|
||||
if (!$user) {
|
||||
return $this->json(-1, '请登录');
|
||||
}
|
||||
|
||||
// 获取下载zip文件url
|
||||
$data = $this->getDownloadUrl($name, $version);
|
||||
if ($data['code'] != 0) {
|
||||
return $this->json($data['code'], $data['msg'], $data['data'] ?? []);
|
||||
}
|
||||
|
||||
// 下载zip文件
|
||||
$base_path = base_path() . "/plugin/$name";
|
||||
$zip_file = "$base_path.zip";
|
||||
$extract_to = base_path() . '/plugin/';
|
||||
$this->downloadZipFile($data['data']['url'], $zip_file);
|
||||
|
||||
$has_zip_archive = class_exists(ZipArchive::class, false);
|
||||
if (!$has_zip_archive) {
|
||||
$cmd = $this->getUnzipCmd($zip_file, $extract_to);
|
||||
if (!$cmd) {
|
||||
throw new BusinessException('请给php安装zip模块或者给系统安装unzip命令');
|
||||
}
|
||||
if (!function_exists('proc_open')) {
|
||||
throw new BusinessException('请解除proc_open函数的禁用或者给php安装zip模块');
|
||||
}
|
||||
}
|
||||
|
||||
Util::pauseFileMonitor();
|
||||
try {
|
||||
// 解压zip到plugin目录
|
||||
if ($has_zip_archive) {
|
||||
$zip = new ZipArchive;
|
||||
$zip->open($zip_file);
|
||||
}
|
||||
|
||||
$context = null;
|
||||
$install_class = "\\plugin\\$name\\api\\Install";
|
||||
if ($installed_version) {
|
||||
// 执行beforeUpdate
|
||||
if (class_exists($install_class) && method_exists($install_class, 'beforeUpdate')) {
|
||||
$context = call_user_func([$install_class, 'beforeUpdate'], $installed_version, $version);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($zip)) {
|
||||
$zip->extractTo(base_path() . '/plugin/');
|
||||
unset($zip);
|
||||
} else {
|
||||
$this->unzipWithCmd($cmd);
|
||||
}
|
||||
|
||||
unlink($zip_file);
|
||||
|
||||
if ($installed_version) {
|
||||
// 执行update更新
|
||||
if (class_exists($install_class) && method_exists($install_class, 'update')) {
|
||||
call_user_func([$install_class, 'update'], $installed_version, $version, $context);
|
||||
}
|
||||
} else {
|
||||
// 执行install安装
|
||||
if (class_exists($install_class) && method_exists($install_class, 'install')) {
|
||||
call_user_func([$install_class, 'install'], $version);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Util::resumeFileMonitor();
|
||||
}
|
||||
|
||||
Util::reloadWebman();
|
||||
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function uninstall(Request $request): Response
|
||||
{
|
||||
$name = $request->post('name');
|
||||
$version = $request->post('version');
|
||||
if (!$name || !preg_match('/^[a-zA-Z0-9_]+$/', $name)) {
|
||||
return $this->json(1, '参数错误');
|
||||
}
|
||||
|
||||
// 获得插件路径
|
||||
clearstatcache();
|
||||
$path = get_realpath(base_path() . "/plugin/$name");
|
||||
if (!$path || !is_dir($path)) {
|
||||
return $this->json(1, '已经删除');
|
||||
}
|
||||
|
||||
// 执行uninstall卸载
|
||||
$install_class = "\\plugin\\$name\\api\\Install";
|
||||
if (class_exists($install_class) && method_exists($install_class, 'uninstall')) {
|
||||
call_user_func([$install_class, 'uninstall'], $version);
|
||||
}
|
||||
|
||||
// 删除目录
|
||||
clearstatcache();
|
||||
if (is_dir($path)) {
|
||||
$monitor_support_pause = method_exists(Monitor::class, 'pause');
|
||||
if ($monitor_support_pause) {
|
||||
Monitor::pause();
|
||||
}
|
||||
try {
|
||||
$this->rmDir($path);
|
||||
} finally {
|
||||
if ($monitor_support_pause) {
|
||||
Monitor::resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
Util::reloadWebman();
|
||||
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付
|
||||
* @param Request $request
|
||||
* @return string|Response
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function pay(Request $request)
|
||||
{
|
||||
$app = $request->get('app');
|
||||
if (!$app) {
|
||||
return response('app not found');
|
||||
}
|
||||
$token = session('app-plugin-token');
|
||||
if (!$token) {
|
||||
return 'Please login workerman.net';
|
||||
}
|
||||
$client = $this->httpClient();
|
||||
$response = $client->get("/payment/app/$app/$token");
|
||||
return (string)$response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录验证码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function captcha(Request $request): Response
|
||||
{
|
||||
$client = $this->httpClient();
|
||||
$response = $client->get('/user/captcha?type=login');
|
||||
$sid_str = $response->getHeaderLine('Set-Cookie');
|
||||
if (preg_match('/PHPSID=([a-zA-Z_0-9]+?);/', $sid_str, $match)) {
|
||||
$sid = $match[1];
|
||||
session()->set('app-plugin-token', $sid);
|
||||
}
|
||||
return response($response->getBody()->getContents())->withHeader('Content-Type', 'image/jpeg');
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录官网
|
||||
* @param Request $request
|
||||
* @return Response|string
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$client = $this->httpClient();
|
||||
if ($request->method() === 'GET') {
|
||||
$response = $client->get("/webman-admin/login");
|
||||
return (string)$response->getBody();
|
||||
}
|
||||
|
||||
$response = $client->post('/api/user/login', [
|
||||
'form_params' => [
|
||||
'email' => $request->post('username'),
|
||||
'password' => $request->post('password'),
|
||||
'captcha' => $request->post('captcha')
|
||||
]
|
||||
]);
|
||||
$content = $response->getBody()->getContents();
|
||||
$data = json_decode($content, true);
|
||||
if (!$data) {
|
||||
$msg = "/api/user/login return $content";
|
||||
echo "msg\r\n";
|
||||
Log::error($msg);
|
||||
return $this->json(1, '发生错误');
|
||||
}
|
||||
if ($data['code'] != 0) {
|
||||
return $this->json($data['code'], $data['msg']);
|
||||
}
|
||||
session()->set('app-plugin-user', [
|
||||
'uid' => $data['data']['uid']
|
||||
]);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取zip下载url
|
||||
* @param $name
|
||||
* @param $version
|
||||
* @return mixed
|
||||
* @throws BusinessException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
protected function getDownloadUrl($name, $version)
|
||||
{
|
||||
$client = $this->httpClient();
|
||||
$response = $client->get("/app/download/$name?version=$version");
|
||||
|
||||
$content = $response->getBody()->getContents();
|
||||
$data = json_decode($content, true);
|
||||
if (!$data) {
|
||||
$msg = "/api/app/download return $content";
|
||||
Log::error($msg);
|
||||
throw new BusinessException('访问官方接口失败 ' . $response->getStatusCode() . ' ' . $response->getReasonPhrase());
|
||||
}
|
||||
if ($data['code'] && $data['code'] != -1 && $data['code'] != -2) {
|
||||
throw new BusinessException($data['msg']);
|
||||
}
|
||||
if ($data['code'] == 0 && !isset($data['data']['url'])) {
|
||||
throw new BusinessException('官方接口返回数据错误');
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载zip
|
||||
* @param $url
|
||||
* @param $file
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
protected function downloadZipFile($url, $file)
|
||||
{
|
||||
$client = $this->downloadClient();
|
||||
$response = $client->get($url);
|
||||
$body = $response->getBody();
|
||||
$status = $response->getStatusCode();
|
||||
if ($status == 404) {
|
||||
throw new BusinessException('安装包不存在');
|
||||
}
|
||||
$zip_content = $body->getContents();
|
||||
if (empty($zip_content)) {
|
||||
throw new BusinessException('安装包不存在');
|
||||
}
|
||||
file_put_contents($file, $zip_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统支持的解压命令
|
||||
* @param $zip_file
|
||||
* @param $extract_to
|
||||
* @return mixed|string|null
|
||||
*/
|
||||
protected function getUnzipCmd($zip_file, $extract_to)
|
||||
{
|
||||
if ($cmd = $this->findCmd('unzip')) {
|
||||
$cmd = "$cmd -o -qq $zip_file -d $extract_to";
|
||||
} else if ($cmd = $this->findCmd('7z')) {
|
||||
$cmd = "$cmd x -bb0 -y $zip_file -o$extract_to";
|
||||
} else if ($cmd = $this->findCmd('7zz')) {
|
||||
$cmd = "$cmd x -bb0 -y $zip_file -o$extract_to";
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用解压命令解压
|
||||
* @param $cmd
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function unzipWithCmd($cmd)
|
||||
{
|
||||
$desc = [
|
||||
0 => ["pipe", "r"],
|
||||
1 => ["pipe", "w"],
|
||||
2 => ["pipe", "w"],
|
||||
];
|
||||
$handler = proc_open($cmd, $desc, $pipes);
|
||||
if (!is_resource($handler)) {
|
||||
throw new BusinessException("解压zip时出错:proc_open调用失败");
|
||||
}
|
||||
$err = fread($pipes[2], 1024);
|
||||
fclose($pipes[2]);
|
||||
proc_close($handler);
|
||||
if ($err) {
|
||||
throw new BusinessException("解压zip时出错:$err");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已安装的插件列表
|
||||
* @return array
|
||||
*/
|
||||
protected function getLocalPlugins(): array
|
||||
{
|
||||
clearstatcache();
|
||||
$installed = [];
|
||||
$plugin_names = array_diff(scandir(base_path() . '/plugin/'), array('.', '..')) ?: [];
|
||||
foreach ($plugin_names as $plugin_name) {
|
||||
if (is_dir(base_path() . "/plugin/$plugin_name") && $version = $this->getPluginVersion($plugin_name)) {
|
||||
$installed[$plugin_name] = $version;
|
||||
}
|
||||
}
|
||||
return $installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已安装的插件列表
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function getInstalledPlugins(Request $request): Response
|
||||
{
|
||||
return $this->json(0, 'ok', $this->getLocalPlugins());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取本地插件版本
|
||||
* @param $name
|
||||
* @return array|mixed|null
|
||||
*/
|
||||
protected function getPluginVersion($name)
|
||||
{
|
||||
if (!is_file($file = base_path() . "/plugin/$name/config/app.php")) {
|
||||
return null;
|
||||
}
|
||||
$config = include $file;
|
||||
return $config['version'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取webman/admin版本
|
||||
* @return string
|
||||
*/
|
||||
protected function getAdminVersion(): string
|
||||
{
|
||||
return config('plugin.admin.app.version', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录
|
||||
* @param $src
|
||||
* @return void
|
||||
*/
|
||||
protected function rmDir($src)
|
||||
{
|
||||
$dir = opendir($src);
|
||||
while (false !== ($file = readdir($dir))) {
|
||||
if (($file != '.') && ($file != '..')) {
|
||||
$full = $src . '/' . $file;
|
||||
if (is_dir($full)) {
|
||||
$this->rmDir($full);
|
||||
} else {
|
||||
unlink($full);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
rmdir($src);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取httpclient
|
||||
* @return Client
|
||||
*/
|
||||
protected function httpClient(): Client
|
||||
{
|
||||
// 下载zip
|
||||
$options = [
|
||||
'base_uri' => config('plugin.admin.app.plugin_market_host'),
|
||||
'timeout' => 60,
|
||||
'connect_timeout' => 5,
|
||||
'verify' => false,
|
||||
'http_errors' => false,
|
||||
'headers' => [
|
||||
'Referer' => \request()->fullUrl(),
|
||||
'User-Agent' => 'webman-app-plugin',
|
||||
'Accept' => 'application/json;charset=UTF-8',
|
||||
]
|
||||
];
|
||||
if ($token = session('app-plugin-token')) {
|
||||
$options['headers']['Cookie'] = "PHPSID=$token;";
|
||||
}
|
||||
return new Client($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载httpclient
|
||||
* @return Client
|
||||
*/
|
||||
protected function downloadClient(): Client
|
||||
{
|
||||
// 下载zip
|
||||
$options = [
|
||||
'timeout' => 59,
|
||||
'connect_timeout' => 5,
|
||||
'verify' => false,
|
||||
'http_errors' => false,
|
||||
'headers' => [
|
||||
'Referer' => \request()->fullUrl(),
|
||||
'User-Agent' => 'webman-app-plugin',
|
||||
]
|
||||
];
|
||||
if ($token = session('app-plugin-token')) {
|
||||
$options['headers']['Cookie'] = "PHPSID=$token;";
|
||||
}
|
||||
return new Client($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找系统命令
|
||||
* @param string $name
|
||||
* @param string|null $default
|
||||
* @param array $extraDirs
|
||||
* @return mixed|string|null
|
||||
*/
|
||||
protected function findCmd(string $name, ?string $default = null, array $extraDirs = [])
|
||||
{
|
||||
if (ini_get('open_basedir')) {
|
||||
$searchPath = array_merge(explode(PATH_SEPARATOR, ini_get('open_basedir')), $extraDirs);
|
||||
$dirs = [];
|
||||
foreach ($searchPath as $path) {
|
||||
if (@is_dir($path)) {
|
||||
$dirs[] = $path;
|
||||
} else {
|
||||
if (basename($path) == $name && @is_executable($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$dirs = array_merge(
|
||||
explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')),
|
||||
$extraDirs
|
||||
);
|
||||
}
|
||||
|
||||
$suffixes = [''];
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
$pathExt = getenv('PATHEXT');
|
||||
$suffixes = array_merge($pathExt ? explode(PATH_SEPARATOR, $pathExt) : ['.exe', '.bat', '.cmd', '.com'], $suffixes);
|
||||
}
|
||||
foreach ($suffixes as $suffix) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (@is_file($file = $dir . DIRECTORY_SEPARATOR . $name . $suffix) && ('\\' === DIRECTORY_SEPARATOR || @is_executable($file))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\model\Role;
|
||||
use plugin\admin\app\model\Rule;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 角色管理
|
||||
*/
|
||||
class RoleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = ['select'];
|
||||
|
||||
/**
|
||||
* @var Role
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Role;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('role/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$id = $request->get('id');
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$limit = 100000;
|
||||
$role_ids = Auth::getScopeRoleIds(true);
|
||||
if (!$id) {
|
||||
$where['id'] = ['in', $role_ids];
|
||||
} elseif (!in_array($id, $role_ids)) {
|
||||
throw new BusinessException('无权限');
|
||||
}
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $this->insertInput($request);
|
||||
$pid = $data['pid'] ?? null;
|
||||
if (!$pid) {
|
||||
return $this->json(1, '请选择父级角色组');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->json(1, '父级角色组超出权限范围');
|
||||
}
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
|
||||
$id = $this->doInsert($data);
|
||||
return $this->json(0, 'ok', ['id' => $id]);
|
||||
}
|
||||
return raw_view('role/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return raw_view('role/update');
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
$descendant_role_ids = Auth::getScopeRoleIds();
|
||||
if (!$is_supper_admin && !in_array($id, $descendant_role_ids)) {
|
||||
return $this->json(1, '无数据权限');
|
||||
}
|
||||
|
||||
$role = Role::find($id);
|
||||
if (!$role) {
|
||||
return $this->json(1, '数据不存在');
|
||||
}
|
||||
$is_supper_role = $role->rules === '*';
|
||||
|
||||
// 超级角色组不允许更改rules pid 字段
|
||||
if ($is_supper_role) {
|
||||
unset($data['rules'], $data['pid']);
|
||||
}
|
||||
|
||||
if (key_exists('pid', $data)) {
|
||||
$pid = $data['pid'];
|
||||
if (!$pid) {
|
||||
return $this->json(1, '请选择父级角色组');
|
||||
}
|
||||
if ($pid == $id) {
|
||||
return $this->json(1, '父级不能是自己');
|
||||
}
|
||||
if (!$is_supper_admin && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->json(1, '父级超出权限范围');
|
||||
}
|
||||
} else {
|
||||
$pid = $role->pid;
|
||||
}
|
||||
|
||||
if (!$is_supper_role) {
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
}
|
||||
|
||||
$this->doUpdate($id, $data);
|
||||
|
||||
// 删除所有子角色组中已经不存在的权限
|
||||
if (!$is_supper_role) {
|
||||
$tree = new Tree(Role::select(['id', 'pid'])->get());
|
||||
$descendant_roles = $tree->getDescendant([$id]);
|
||||
$descendant_role_ids = array_column($descendant_roles, 'id');
|
||||
$rule_ids = $data['rules'] ? explode(',', $data['rules']) : [];
|
||||
foreach ($descendant_role_ids as $role_id) {
|
||||
$tmp_role = Role::find($role_id);
|
||||
$tmp_rule_ids = $role->getRuleIds();
|
||||
$tmp_rule_ids = array_intersect(explode(',',$tmp_role->rules), $tmp_rule_ids);
|
||||
$tmp_role->rules = implode(',', $tmp_rule_ids);
|
||||
$tmp_role->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
if (in_array(1, $ids)) {
|
||||
return $this->json(1, '无法删除超级管理员角色');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($ids, Auth::getScopeRoleIds())) {
|
||||
return $this->json(1, '无删除权限');
|
||||
}
|
||||
$tree = new Tree(Role::get());
|
||||
$descendants = $tree->getDescendant($ids);
|
||||
if ($descendants) {
|
||||
$ids = array_merge($ids, array_column($descendants, 'id'));
|
||||
}
|
||||
$this->doDelete($ids);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function rules(Request $request): Response
|
||||
{
|
||||
$role_id = $request->get('id');
|
||||
if (empty($role_id)) {
|
||||
return $this->json(0, 'ok', []);
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && !in_array($role_id, Auth::getScopeRoleIds(true))) {
|
||||
return $this->json(1, '角色组超出权限范围');
|
||||
}
|
||||
$rule_id_string = Role::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
return $this->json(0, 'ok', []);
|
||||
}
|
||||
$rules = Rule::get();
|
||||
$include = [];
|
||||
if ($rule_id_string !== '*') {
|
||||
$include = explode(',', $rule_id_string);
|
||||
}
|
||||
$items = [];
|
||||
foreach ($rules as $item) {
|
||||
$items[] = [
|
||||
'name' => $item->title ?? $item->name ?? $item->id,
|
||||
'value' => (string)$item->id,
|
||||
'id' => $item->id,
|
||||
'pid' => $item->pid,
|
||||
];
|
||||
}
|
||||
$tree = new Tree($items);
|
||||
return $this->json(0, 'ok', $tree->getTree($include));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限字典是否合法
|
||||
* @param int $role_id
|
||||
* @param $rule_ids
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkRules(int $role_id, $rule_ids)
|
||||
{
|
||||
if ($rule_ids) {
|
||||
$rule_ids = explode(',', $rule_ids);
|
||||
if (in_array('*', $rule_ids)) {
|
||||
throw new BusinessException('非法数据');
|
||||
}
|
||||
$rule_exists = Rule::whereIn('id', $rule_ids)->pluck('id');
|
||||
if (count($rule_exists) != count($rule_ids)) {
|
||||
throw new BusinessException('权限不存在');
|
||||
}
|
||||
$rule_id_string = Role::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
if ($rule_id_string === '*') {
|
||||
return;
|
||||
}
|
||||
$legal_rule_ids = explode(',', $rule_id_string);
|
||||
if (array_diff($rule_ids, $legal_rule_ids)) {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Exception;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Role;
|
||||
use plugin\admin\app\model\Rule;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 权限菜单
|
||||
*/
|
||||
class RuleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要权限的方法
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['get', 'permission'];
|
||||
|
||||
/**
|
||||
* @var Rule
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('rule/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$this->syncRules();
|
||||
return parent::select($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
function get(Request $request): Response
|
||||
{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
$types = $request->get('type', '0,1');
|
||||
$types = is_string($types) ? explode(',', $types) : [0, 1];
|
||||
$items = Rule::orderBy('weight', 'desc')->get()->toArray();
|
||||
|
||||
$formatted_items = [];
|
||||
foreach ($items as $item) {
|
||||
$item['pid'] = (int)$item['pid'];
|
||||
$item['name'] = $item['title'];
|
||||
$item['value'] = $item['id'];
|
||||
$item['icon'] = $item['icon'] ? "layui-icon {$item['icon']}" : '';
|
||||
$formatted_items[] = $item;
|
||||
}
|
||||
|
||||
$tree = new Tree($formatted_items);
|
||||
$tree_items = $tree->getTree();
|
||||
// 超级管理员权限为 *
|
||||
if (!in_array('*', $rules)) {
|
||||
$this->removeNotContain($tree_items, 'id', $rules);
|
||||
}
|
||||
$this->removeNotContain($tree_items, 'type', $types);
|
||||
$menus = $this->empty_filter(Tree::arrayValues($tree_items));
|
||||
return $this->json(0, 'ok', $menus);
|
||||
}
|
||||
|
||||
private function empty_filter($menus)
|
||||
{
|
||||
return array_map(
|
||||
function ($menu) {
|
||||
if (isset($menu['children'])) {
|
||||
$menu['children'] = $this->empty_filter($menu['children']);
|
||||
}
|
||||
return $menu;
|
||||
},
|
||||
array_values(array_filter(
|
||||
$menus,
|
||||
function ($menu) {
|
||||
return $menu['type'] != 0 || isset($menu['children']) && count($this->empty_filter($menu['children'])) > 0;
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function permission(Request $request): Response
|
||||
{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
// 超级管理员
|
||||
if (in_array('*', $rules)) {
|
||||
return $this->json(0, 'ok', ['*']);
|
||||
}
|
||||
$keys = Rule::whereIn('id', $rules)->pluck('key');
|
||||
$permissions = [];
|
||||
foreach ($keys as $key) {
|
||||
if (!$key = Util::controllerToUrlPath($key)) {
|
||||
continue;
|
||||
}
|
||||
$code = str_replace('/', '.', trim($key, '/'));
|
||||
$permissions[] = $code;
|
||||
}
|
||||
$permissions = ["app.admin.vehicle.select","app.admin.vehicle.detail"];
|
||||
return $this->json(0, 'ok', $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类同步规则到数据库
|
||||
* @return void
|
||||
*/
|
||||
protected function syncRules()
|
||||
{
|
||||
$items = $this->model->where('key', 'like', '%\\\\%')->get()->keyBy('key');
|
||||
$methods_in_db = [];
|
||||
$methods_in_files = [];
|
||||
foreach ($items as $item) {
|
||||
$class = $item->key;
|
||||
if (strpos($class, '@')) {
|
||||
$methods_in_db[$class] = $class;
|
||||
continue;
|
||||
}
|
||||
if (class_exists($class)) {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$properties = $reflection->getDefaultProperties();
|
||||
$no_need_auth = array_merge($properties['noNeedLogin'] ?? [], $properties['noNeedAuth'] ?? []);
|
||||
$class = $reflection->getName();
|
||||
$pid = $item->id;
|
||||
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $method) {
|
||||
$method_name = $method->getName();
|
||||
if (strtolower($method_name) === 'index' || strpos($method_name, '__') === 0 || in_array($method_name, $no_need_auth)) {
|
||||
continue;
|
||||
}
|
||||
$name = "$class@$method_name";
|
||||
|
||||
$methods_in_files[$name] = $name;
|
||||
$title = Util::getCommentFirstLine($method->getDocComment()) ?: $method_name;
|
||||
$menu = $items[$name] ?? [];
|
||||
if ($menu) {
|
||||
if ($menu->title != $title) {
|
||||
Rule::where('key', $name)->update(['title' => $title]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$menu = new Rule;
|
||||
$menu->pid = $pid;
|
||||
$menu->key = $name;
|
||||
$menu->title = $title;
|
||||
$menu->type = 2;
|
||||
$menu->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从数据库中删除已经不存在的方法
|
||||
$menu_names_to_del = array_diff($methods_in_db, $methods_in_files);
|
||||
if ($menu_names_to_del) {
|
||||
//Rule::whereIn('key', $menu_names_to_del)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function selectInput(Request $request): array
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = parent::selectInput($request);
|
||||
// 允许通过type=0,1格式传递菜单类型
|
||||
$types = $request->get('type');
|
||||
if ($types && is_string($types)) {
|
||||
$where['type'] = ['in', explode(',', $types)];
|
||||
}
|
||||
// 默认weight排序
|
||||
if (!$field) {
|
||||
$field = 'weight';
|
||||
$order = 'desc';
|
||||
}
|
||||
return [$where, $format, $limit, $field, $order];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return raw_view('rule/insert');
|
||||
}
|
||||
$data = $this->insertInput($request);
|
||||
if (empty($data['type'])) {
|
||||
$data['type'] = strpos($data['key'], '\\') ? 1 : 0;
|
||||
}
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
$key = $data['key'] ?? '';
|
||||
if ($this->model->where('key', $key)->first()) {
|
||||
return $this->json(1, "菜单标识 $key 已经存在");
|
||||
}
|
||||
$data['pid'] = empty($data['pid']) ? 0 : $data['pid'];
|
||||
$this->doInsert($data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return raw_view('rule/update');
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
if (!$row = $this->model->find($id)) {
|
||||
return $this->json(2, '记录不存在');
|
||||
}
|
||||
if (isset($data['pid'])) {
|
||||
$data['pid'] = $data['pid'] ?: 0;
|
||||
if ($data['pid'] == $row['id']) {
|
||||
return $this->json(2, '不能将自己设置为上级菜单');
|
||||
}
|
||||
}
|
||||
if (isset($data['key'])) {
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
}
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
// 子规则一起删除
|
||||
$delete_ids = $children_ids = $ids;
|
||||
while($children_ids) {
|
||||
$children_ids = $this->model->whereIn('pid', $children_ids)->pluck('id')->toArray();
|
||||
$delete_ids = array_merge($delete_ids, $children_ids);
|
||||
}
|
||||
$this->doDelete($delete_ids);
|
||||
return $this->json(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除不包含某些数据的数组
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return void
|
||||
*/
|
||||
protected function removeNotContain(&$array, $key, $values)
|
||||
{
|
||||
foreach ($array as $k => &$item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->arrayContain($item, $key, $values)) {
|
||||
unset($array[$k]);
|
||||
} else {
|
||||
if (!isset($item['children'])) {
|
||||
continue;
|
||||
}
|
||||
$this->removeNotContain($item['children'], $key, $values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数组是否包含某些数据
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return bool
|
||||
*/
|
||||
protected function arrayContain(&$array, $key, $values): bool
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return false;
|
||||
}
|
||||
if (isset($array[$key]) && in_array($array[$key], $values)) {
|
||||
return true;
|
||||
}
|
||||
if (!isset($array['children'])) {
|
||||
return false;
|
||||
}
|
||||
foreach ($array['children'] as $item) {
|
||||
if ($this->arrayContain($item, $key, $values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限规则
|
||||
* @param $roles
|
||||
* @return array
|
||||
*/
|
||||
protected function getRules($roles): array
|
||||
{
|
||||
$rules_strings = $roles ? Role::whereIn('id', $roles)->pluck('rules') : [];
|
||||
$rules = [];
|
||||
foreach ($rules_strings as $rule_string) {
|
||||
if (!$rule_string) {
|
||||
continue;
|
||||
}
|
||||
$rules = array_merge($rules, explode(',', $rule_string));
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use app\service\OssService;
|
||||
use Exception;
|
||||
use Intervention\Image\ImageManagerStatic as Image;
|
||||
use plugin\admin\app\model\Upload;
|
||||
use Shopwwi\WebmanFilesystem\Facade\Storage;
|
||||
use support\exception\BusinessException;
|
||||
use support\Log;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
use util\Util;
|
||||
|
||||
/**
|
||||
* 附件管理
|
||||
*/
|
||||
class UploadController extends Crud
|
||||
{
|
||||
/**
|
||||
* @var Upload
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 只返回当前管理员数据
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimit = 'personal';
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Upload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('upload/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览附件
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function attachment(): Response
|
||||
{
|
||||
return raw_view('upload/attachment');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询附件
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
if (!empty($where['ext']) && is_string($where['ext'])) {
|
||||
$where['ext'] = ['in', explode(',', $where['ext'])];
|
||||
}
|
||||
if (!empty($where['name']) && is_string($where['name'])) {
|
||||
$where['name'] = ['like', "%{$where['name']}%"];
|
||||
}
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新附件
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return raw_view('upload/update');
|
||||
}
|
||||
return parent::update($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加附件
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return raw_view('upload/insert');
|
||||
}
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->json(1, '未找到文件');
|
||||
}
|
||||
$data = $this->base($request, '/upload/files/' . date('Ymd'));
|
||||
$upload = new Upload;
|
||||
$upload->admin_id = admin_id();
|
||||
$upload->name = $data['name'];
|
||||
[
|
||||
$upload->url,
|
||||
$upload->name,
|
||||
$_,
|
||||
$upload->file_size,
|
||||
$upload->mime_type,
|
||||
$upload->image_width,
|
||||
$upload->image_height,
|
||||
$upload->ext
|
||||
] = array_values($data);
|
||||
$upload->category = $request->post('category');
|
||||
$upload->save();
|
||||
return $this->json(0, '上传成功', [
|
||||
'url' => $data['url'],
|
||||
'name' => $data['name'],
|
||||
'size' => $data['size'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function file(Request $request): Response
|
||||
{
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->json(1, '未找到文件');
|
||||
}
|
||||
$img_exts = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif'
|
||||
];
|
||||
if (in_array($file->getUploadExtension(), $img_exts)) {
|
||||
return $this->image($request);
|
||||
}
|
||||
$data = $this->base($request, '/upload/files/' . date('Ymd'));
|
||||
return $this->json(0, '上传成功', [
|
||||
'url' => $data['url'],
|
||||
'name' => $data['name'],
|
||||
'size' => $data['size'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function image(Request $request): Response
|
||||
{
|
||||
$data = $this->base($request, '/upload/img/' . date('Ymd'));
|
||||
$realpath = $data['realpath'];
|
||||
try {
|
||||
$img = Image::make($realpath);
|
||||
$max_height = 1170;
|
||||
$max_width = 1170;
|
||||
$width = $img->width();
|
||||
$height = $img->height();
|
||||
$ratio = 1;
|
||||
if ($height > $max_height || $width > $max_width) {
|
||||
$ratio = $width > $height ? $max_width / $width : $max_height / $height;
|
||||
}
|
||||
$img->resize($width * $ratio, $height * $ratio)->save($realpath);
|
||||
} catch (Exception $e) {
|
||||
unlink($realpath);
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '处理图片发生错误'
|
||||
]);
|
||||
}
|
||||
return json([
|
||||
'code' => 0,
|
||||
'msg' => '上传成功',
|
||||
'data' => [
|
||||
'url' => $data['url'],
|
||||
'name' => $data['name'],
|
||||
'size' => $data['size'],
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传头像
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function avatar(Request $request): Response
|
||||
{
|
||||
$file = current($request->file());
|
||||
if ($file && $file->isValid()) {
|
||||
$ext = strtolower($file->getUploadExtension());
|
||||
if (!in_array($ext, ['jpg', 'jpeg', 'gif', 'png'])) {
|
||||
return json(['code' => 2, 'msg' => '仅支持 jpg jpeg gif png格式']);
|
||||
}
|
||||
$image = Image::make($file);
|
||||
$width = $image->width();
|
||||
$height = $image->height();
|
||||
$size = min($width, $height);
|
||||
$relative_path = 'upload/avatar/' . date('Ym');
|
||||
$real_path = base_path() . "/plugin/admin/public/$relative_path";
|
||||
if (!is_dir($real_path)) {
|
||||
mkdir($real_path, 0777, true);
|
||||
}
|
||||
$name = bin2hex(pack('Nn', time(), random_int(1, 65535)));
|
||||
$ext = $file->getUploadExtension();
|
||||
|
||||
$image->crop($size, $size)->resize(300, 300);
|
||||
$path = base_path() . "/plugin/admin/public/$relative_path/$name.lg.$ext";
|
||||
$image->save($path);
|
||||
|
||||
$image->resize(120, 120);
|
||||
$path = base_path() . "/plugin/admin/public/$relative_path/$name.md.$ext";
|
||||
$image->save($path);
|
||||
|
||||
$image->resize(60, 60);
|
||||
$path = base_path() . "/plugin/admin/public/$relative_path/$name.$ext";
|
||||
$image->save($path);
|
||||
|
||||
$image->resize(30, 30);
|
||||
$path = base_path() . "/plugin/admin/public/$relative_path/$name.sm.$ext";
|
||||
$image->save($path);
|
||||
|
||||
return json([
|
||||
'code' => 0,
|
||||
'msg' => '上传成功',
|
||||
'data' => [
|
||||
'url' => "/app/admin/$relative_path/$name.md.$ext"
|
||||
]
|
||||
]);
|
||||
}
|
||||
return json(['code' => 1, 'msg' => 'file not found']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除附件
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
$primary_key = $this->model->getKeyName();
|
||||
$files = $this->model->whereIn($primary_key, $ids)->get()->toArray();
|
||||
$file_list = array_map(function ($item) {
|
||||
$path = $item['url'];
|
||||
if (preg_match("#^/app/admin#", $path)) {
|
||||
$admin_public_path = config('plugin.admin.app.public_path') ?: base_path() . "/plugin/admin/public";
|
||||
return $admin_public_path . str_replace("/app/admin", "", $item['url']);
|
||||
}
|
||||
return null;
|
||||
}, $files);
|
||||
$file_list = array_filter($file_list, function ($item) {
|
||||
return !empty($item);
|
||||
});
|
||||
$result = parent::delete($request);
|
||||
if (($res = json_decode($result->rawBody())) && $res->code === 0) {
|
||||
foreach ($file_list as $file) {
|
||||
@unlink($file);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传数据
|
||||
* @param Request $request
|
||||
* @param $relative_dir
|
||||
* @return array
|
||||
* @throws BusinessException|\Random\RandomException
|
||||
*/
|
||||
protected function base(Request $request, $relative_dir): array
|
||||
{
|
||||
$relative_dir = ltrim($relative_dir, '\\/');
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
throw new BusinessException('未找到上传文件', 400);
|
||||
}
|
||||
|
||||
$admin_public_path = rtrim(config('plugin.admin.app.public_path', ''), '\\/');
|
||||
$base_dir = $admin_public_path ? $admin_public_path . DIRECTORY_SEPARATOR : base_path() . '/plugin/admin/public/';
|
||||
$full_dir = $base_dir . $relative_dir;
|
||||
if (!is_dir($full_dir)) {
|
||||
mkdir($full_dir, 0777, true);
|
||||
}
|
||||
|
||||
$ext = $file->getUploadExtension() ?: null;
|
||||
$mime_type = $file->getUploadMimeType();
|
||||
$file_name = $file->getUploadName();
|
||||
$file_size = $file->getSize();
|
||||
|
||||
if (!$ext && $file_name === 'blob') {
|
||||
[$___image, $ext] = explode('/', $mime_type);
|
||||
unset($___image);
|
||||
}
|
||||
|
||||
$ext = strtolower($ext);
|
||||
$ext_forbidden_map = ['php', 'php3', 'php5', 'css', 'js', 'html', 'htm', 'asp', 'jsp'];
|
||||
if (in_array($ext, $ext_forbidden_map)) {
|
||||
throw new BusinessException('不支持该格式的文件上传', 400);
|
||||
}
|
||||
|
||||
$relative_path = $relative_dir . '/' . bin2hex(pack('Nn', time(), random_int(1, 65535))) . ".$ext";
|
||||
$full_path = $base_dir . $relative_path;
|
||||
$file->move($full_path);
|
||||
$image_with = $image_height = 0;
|
||||
if ($img_info = getimagesize($full_path)) {
|
||||
[$image_with, $image_height] = $img_info;
|
||||
$mime_type = $img_info['mime'];
|
||||
}
|
||||
return [
|
||||
'url' => "/app/admin/$relative_path",
|
||||
'name' => $file_name,
|
||||
'realpath' => $full_path,
|
||||
'size' => $file_size,
|
||||
'mime_type' => $mime_type,
|
||||
'image_with' => $image_with,
|
||||
'image_height' => $image_height,
|
||||
'ext' => $ext,
|
||||
];
|
||||
}
|
||||
|
||||
// 上传单文件图片
|
||||
public function uploadImage(Request $request)
|
||||
{
|
||||
try {
|
||||
// $file = $request->file('file');
|
||||
$file = current($request->file());
|
||||
|
||||
if (!$file || !$file->isValid()) {
|
||||
return Util::fail($file,'上传失败2');
|
||||
}
|
||||
|
||||
$result = Storage::adapter('oss')
|
||||
->path('lingke_crm/'.date('Ymd'))
|
||||
->size(1024*1024*5)
|
||||
->extYes(['image/jpeg','image/png'])
|
||||
->extNo(['image/gif'])
|
||||
->upload($file);
|
||||
|
||||
$ossservice = new OssService();
|
||||
|
||||
$data = [
|
||||
'path' => $result->file_name,
|
||||
'original_name' => $result->origin_name,
|
||||
'image_url' => $ossservice->generatePrivateUrl($result->file_name)
|
||||
];
|
||||
|
||||
|
||||
return Util::success($data,'上传成功');
|
||||
}catch (\Throwable $e){
|
||||
Log::error('文件上传失败: ' . $e->getMessage());
|
||||
|
||||
return Util::fail([],'file:'.$e->getFile().'line:'.$e->getLine().'message:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
class UserController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new User;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return raw_view('user/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return raw_view('user/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::update($request);
|
||||
}
|
||||
return raw_view('user/update');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\model\Location;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use plugin\admin\app\model\Vehicle;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
|
||||
/**
|
||||
* 车辆信息
|
||||
*/
|
||||
class VehicleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['detail'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['detail'];
|
||||
|
||||
protected $status = ['未知','待发车','在途(发往fbo港口)','抵达发货港口','在途(发往目的地仓库)','抵达仓库','售出'];
|
||||
|
||||
/**
|
||||
* @var Vehicle
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->model = new Vehicle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
return view('vehicle/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('vehicle/insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::update($request);
|
||||
}
|
||||
return view('vehicle/update');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 详情
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function detail(Request $request): Response
|
||||
{
|
||||
return view('vehicle/detail');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
/* public function list(Request $request)
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
return json($where);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
$data = $this->doFormat($query, $format, $limit);
|
||||
// foreach ($data['data'] as &$value) {
|
||||
// $value['locations'] = Location::find($value['locations_id'])->name;
|
||||
// }
|
||||
// $data = json_decode(json_encode($data), true);
|
||||
return $data;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 查询后置处理,添加位置信息
|
||||
* @param mixed $items
|
||||
* @return mixed
|
||||
*/
|
||||
protected function afterQuery($items)
|
||||
{
|
||||
foreach ($items as &$item) {
|
||||
$location = Location::find($item->locations_id);
|
||||
$item->status = $this->status[$item->status]??$this->status[0];
|
||||
$item->location_name = $location ? $location->name : '';
|
||||
$final_location = Location::find($item->final_locations_id);
|
||||
$item->final_location_name = $final_location ? $final_location->name : '';
|
||||
$fbo_location = Location::find($item->fbo_locations_id);
|
||||
$item->fbo_location_name = $fbo_location ? $fbo_location->name : '';
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user