webman后台

This commit is contained in:
2026-03-07 19:42:22 +08:00
parent 9ed4c1bc58
commit 83725aef88
181 changed files with 19115 additions and 1 deletions

View File

@@ -0,0 +1,206 @@
<?php
declare(strict_types=1);
namespace app\admin\controller;
use ba\Terminal;
use ba\TableManager;
use support\think\Db;
use app\admin\model\AdminLog;
use app\common\library\Upload;
use app\common\library\upload\WebmanUploadedFile;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class Ajax extends Backend
{
protected array $noNeedPermission = ['*'];
protected array $noNeedLogin = ['terminal'];
public function upload(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('upload'));
$file = $request->file('file');
if (!$file) {
return $this->error(__('No files were uploaded'));
}
$file = new WebmanUploadedFile($file);
$driver = $request->get('driver', $request->post('driver', 'local'));
$topic = $request->get('topic', $request->post('topic', 'default'));
try {
$upload = new Upload();
$attachment = $upload
->setFile($file)
->setDriver($driver)
->setTopic($topic)
->upload(null, $this->auth->id);
unset($attachment['create_time'], $attachment['quote']);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
return $this->success(__('File uploaded successfully'), [
'file' => $attachment ?? []
]);
}
public function area(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->success('', get_area($request));
}
public function buildSuffixSvg(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$suffix = $request->get('suffix', $request->post('suffix', 'file'));
$background = $request->get('background', $request->post('background'));
$content = build_suffix_svg((string) $suffix, (string) $background);
return response($content, 200, [
'Content-Length' => strlen($content),
'Content-Type' => 'image/svg+xml'
]);
}
public function getDatabaseConnectionList(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$quickSearch = $request->get('quickSearch', '');
$connections = config('thinkorm.connections', config('database.connections', []));
$desensitization = [];
foreach ($connections as $key => $connection) {
$connConfig = TableManager::getConnectionConfig($key);
$desensitization[] = [
'type' => $connConfig['type'] ?? 'mysql',
'database' => substr_replace($connConfig['database'] ?? '', '****', 1, strlen($connConfig['database'] ?? '') > 4 ? 2 : 1),
'key' => $key,
];
}
if ($quickSearch) {
$desensitization = array_values(array_filter($desensitization, function ($item) use ($quickSearch) {
return preg_match("/$quickSearch/i", $item['key']);
}));
}
return $this->success('', ['list' => $desensitization]);
}
public function getTablePk(Request $request, ?string $table = null, ?string $connection = null): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$table = $table ?? $request->get('table', $request->post('table'));
$connection = $connection ?? $request->get('connection', $request->post('connection'));
if (!$table) {
return $this->error(__('Parameter error'));
}
$table = TableManager::tableName($table, true, $connection);
if (!TableManager::phinxAdapter(false, $connection)->hasTable($table)) {
return $this->error(__('Data table does not exist'));
}
$conn = TableManager::getConnection($connection);
$tablePk = Db::connect($conn)->table($table)->getPk();
return $this->success('', ['pk' => $tablePk]);
}
public function getTableList(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$quickSearch = $request->get('quickSearch', $request->post('quickSearch', ''));
$connection = $request->get('connection', $request->post('connection'));
$samePrefix = filter_var($request->get('samePrefix', $request->post('samePrefix', true)), FILTER_VALIDATE_BOOLEAN);
$excludeTable = $request->get('excludeTable', $request->post('excludeTable', []));
$excludeTable = is_array($excludeTable) ? $excludeTable : [];
$dbConfig = TableManager::getConnectionConfig($connection);
$tables = TableManager::getTableList($connection);
if ($quickSearch) {
$tables = array_filter($tables, function ($comment) use ($quickSearch) {
return preg_match("/$quickSearch/i", $comment);
});
}
$pattern = '/^' . preg_quote($dbConfig['prefix'] ?? '', '/') . '/i';
$outTables = [];
foreach ($tables as $table => $comment) {
if ($samePrefix && !preg_match($pattern, $table)) continue;
$tableNoPrefix = preg_replace($pattern, '', $table);
if (!in_array($tableNoPrefix, $excludeTable)) {
$outTables[] = [
'table' => $tableNoPrefix,
'comment' => $comment,
'connection' => $connection,
'prefix' => $dbConfig['prefix'] ?? '',
];
}
}
return $this->success('', ['list' => $outTables]);
}
public function getTableFieldList(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$table = $request->get('table', $request->post('table'));
$clean = filter_var($request->get('clean', $request->post('clean', true)), FILTER_VALIDATE_BOOLEAN);
$connection = $request->get('connection', $request->post('connection'));
if (!$table) {
return $this->error(__('Parameter error'));
}
$conn = TableManager::getConnection($connection);
$tablePk = Db::connect($conn)->name($table)->getPk();
return $this->success('', [
'pk' => $tablePk,
'fieldList' => TableManager::getTableColumns($table, $clean, $conn),
]);
}
public function changeTerminalConfig(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Change terminal config'));
if (Terminal::changeTerminalConfig()) {
return $this->success();
}
return $this->error(__('Failed to modify the terminal configuration. Please modify the configuration file manually:%s', ['/config/terminal.php']));
}
public function clearCache(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Clear cache'));
$type = $request->post('type');
if ($type === 'tp' || $type === 'all') {
clear_config_cache();
} else {
return $this->error(__('Parameter error'));
}
event_trigger('cacheClearAfter');
return $this->success(__('Cache cleaned~'));
}
public function terminal(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
(new Terminal())->exec();
return $this->success();
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace app\admin\controller;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class Dashboard extends Backend
{
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->success('', [
'remark' => get_route_remark()
]);
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace app\admin\controller;
use ba\ClickCaptcha;
use app\common\facade\Token;
use app\admin\model\AdminLog;
use app\common\controller\Backend;
use support\validation\Validator;
use support\validation\ValidationException;
use Webman\Http\Request;
use support\Response;
class Index extends Backend
{
protected array $noNeedLogin = ['logout', 'login'];
protected array $noNeedPermission = ['index'];
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$adminInfo = $this->auth->getInfo();
$adminInfo['super'] = $this->auth->isSuperAdmin();
unset($adminInfo['token'], $adminInfo['refresh_token']);
$menus = $this->auth->getMenus();
if (!$menus) {
return $this->error(__('No background menu, please contact super administrator!'));
}
return $this->success('', [
'adminInfo' => $adminInfo,
'menus' => $menus,
'siteConfig' => [
'siteName' => get_sys_config('site_name'),
'version' => get_sys_config('version'),
'apiUrl' => config('buildadmin.api_url'),
'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
'cdnUrl' => full_url(),
'cdnUrlParams' => config('buildadmin.cdn_url_params'),
],
'terminal' => [
'phpDevelopmentServer' => str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Development Server'),
'npmPackageManager' => config('terminal.npm_package_manager'),
]
]);
}
public function login(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($this->auth->isLogin()) {
return $this->success(__('You have already logged in. There is no need to log in again~'), [
'type' => $this->auth::LOGGED_IN
], $this->auth::LOGIN_RESPONSE_CODE);
}
$captchaSwitch = config('buildadmin.admin_login_captcha');
if ($request->method() === 'POST') {
$username = $request->post('username');
$password = $request->post('password');
$keep = $request->post('keep');
$rules = [
'username' => 'required|string|min:3|max:30',
'password' => 'required|string|regex:/^(?!.*[&<>"\'\n\r]).{6,32}$/',
];
$data = ['username' => $username, 'password' => $password];
if ($captchaSwitch) {
$rules['captchaId'] = 'required|string';
$rules['captchaInfo'] = 'required|string';
$data['captchaId'] = $request->post('captchaId');
$data['captchaInfo'] = $request->post('captchaInfo');
}
try {
Validator::make($data, $rules, [
'username.required' => __('Username'),
'password.required' => __('Password'),
'password.regex' => __('Please input correct password'),
])->validate();
} catch (ValidationException $e) {
return $this->error($e->getMessage());
}
if ($captchaSwitch) {
$captchaObj = new ClickCaptcha();
if (!$captchaObj->check($data['captchaId'], $data['captchaInfo'])) {
return $this->error(__('Captcha error'));
}
}
AdminLog::instance($request)->setTitle(__('Login'));
$res = $this->auth->login($username, $password, (bool) $keep);
if ($res === true) {
return $this->success(__('Login succeeded!'), [
'userInfo' => $this->auth->getInfo()
]);
}
$msg = $this->auth->getError();
return $this->error($msg ?: __('Incorrect user name or password!'));
}
return $this->success('', [
'captcha' => $captchaSwitch
]);
}
public function logout(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
$refreshToken = $request->post('refreshToken', '');
if ($refreshToken) {
Token::delete((string) $refreshToken);
}
$this->auth->logout();
return $this->success();
}
return $this->error(__('Method not allowed'), [], 0, ['statusCode' => 405]);
}
}

View File

@@ -0,0 +1,147 @@
<?php
declare(strict_types=1);
namespace app\admin\controller;
use ba\Exception as BaException;
use app\admin\model\AdminLog;
use app\admin\library\module\Server;
use app\admin\library\module\Manage;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class Module extends Backend
{
protected array $noNeedPermission = ['state', 'dependentInstallComplete'];
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->success('', [
'installed' => Server::installedList(root_path() . 'modules' . DIRECTORY_SEPARATOR),
'sysVersion' => config('buildadmin.version'),
'nuxtVersion' => Server::getNuxtVersion(),
]);
}
public function state(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$uid = $request->get('uid', '');
if (!$uid) {
return $this->error(__('Parameter error'));
}
return $this->success('', [
'state' => Manage::instance($uid)->getInstallState()
]);
}
public function install(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Install module'));
$uid = $request->get('uid', $request->post('uid', ''));
$update = filter_var($request->get('update', $request->post('update', false)), FILTER_VALIDATE_BOOLEAN);
if (!$uid) {
return $this->error(__('Parameter error'));
}
$res = [];
try {
$res = Manage::instance($uid)->install($update);
} catch (BaException $e) {
return $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
} catch (\Throwable $e) {
return $this->error(__($e->getMessage()));
}
return $this->success('', ['data' => $res]);
}
public function dependentInstallComplete(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$uid = $request->get('uid', '');
if (!$uid) {
return $this->error(__('Parameter error'));
}
try {
Manage::instance($uid)->dependentInstallComplete('all');
} catch (BaException $e) {
return $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
} catch (\Throwable $e) {
return $this->error(__($e->getMessage()));
}
return $this->success();
}
public function changeState(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Change module state'));
$uid = $request->post('uid', '');
$state = filter_var($request->post('state', false), FILTER_VALIDATE_BOOLEAN);
if (!$uid) {
return $this->error(__('Parameter error'));
}
$info = [];
try {
$info = Manage::instance($uid)->changeState($state);
} catch (BaException $e) {
return $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
} catch (\Throwable $e) {
return $this->error(__($e->getMessage()));
}
return $this->success('', ['info' => $info]);
}
public function uninstall(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Uninstall module'));
$uid = $request->post('uid', '');
if (!$uid) {
return $this->error(__('Parameter error'));
}
try {
Manage::instance($uid)->uninstall();
} catch (BaException $e) {
return $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
} catch (\Throwable $e) {
return $this->error(__($e->getMessage()));
}
return $this->success();
}
public function upload(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Upload module'));
$file = $request->file('file');
if (!$file) {
return $this->error(__('Parameter error'));
}
try {
$res = Manage::uploadFromRequest($request);
} catch (BaException $e) {
return $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
} catch (\Throwable $e) {
return $this->error(__($e->getMessage()));
}
return $this->success('', $res);
}
}

View File

@@ -0,0 +1,285 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\auth;
use Throwable;
use support\think\Db;
use support\validation\Validator;
use support\validation\ValidationException;
use app\common\controller\Backend;
use app\admin\model\Admin as AdminModel;
use support\Response;
use Webman\Http\Request;
class Admin extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['create_time', 'update_time', 'password', 'salt', 'login_failure', 'last_login_time', 'last_login_ip'];
protected array|string $quickSearchField = ['username', 'nickname'];
protected string|int|bool $dataLimit = 'allAuthAndOthers';
protected string $dataLimitField = 'id';
protected function initController(Request $request): void
{
$this->model = new AdminModel();
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') ?? $request->post('select')) {
$selectRes = $this->select($request);
if ($selectRes !== null) return $selectRes;
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withoutField('login_failure,password,salt')
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
if ($this->modelValidate) {
try {
$rules = [
'username' => 'required|string|regex:/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/|unique:admin,username',
'nickname' => 'required|string',
'password' => 'required|string|regex:/^(?!.*[&<>"\'\n\r]).{6,32}$/',
'email' => 'email|unique:admin,email',
'mobile' => 'regex:/^1[3-9]\d{9}$/|unique:admin,mobile',
'group_arr' => 'required|array',
];
$messages = [
'username.regex' => __('Please input correct username'),
'password.regex' => __('Please input correct password'),
];
Validator::make($data, $rules, $messages)->validate();
} catch (ValidationException $e) {
return $this->error($e->getMessage());
}
}
$passwd = $data['password'] ?? '';
$data = $this->excludeFields($data);
$result = false;
if (!empty($data['group_arr'])) {
$authRes = $this->checkGroupAuth($data['group_arr']);
if ($authRes !== null) return $authRes;
}
$this->model->startTrans();
try {
$result = $this->model->save($data);
if (!empty($data['group_arr'])) {
$groupAccess = [];
foreach ($data['group_arr'] as $datum) {
$groupAccess[] = [
'uid' => $this->model->id,
'group_id' => $datum,
];
}
Db::name('admin_group_access')->insertAll($groupAccess);
}
$this->model->commit();
if (!empty($passwd)) {
$this->model->resetPassword($this->model->id, $passwd);
}
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Added successfully'));
}
return $this->error(__('No rows were added'));
}
return $this->error(__('Parameter error'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->get($pk) ?? $request->post($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
return $this->error(__('You have no permission'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
if ($this->modelValidate) {
try {
$rules = [
'username' => 'required|string|regex:/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/|unique:admin,username,' . $id,
'nickname' => 'required|string',
'password' => 'nullable|string|regex:/^(?!.*[&<>"\'\n\r]).{6,32}$/',
'email' => 'email|unique:admin,email,' . $id,
'mobile' => 'regex:/^1[3-9]\d{9}$/|unique:admin,mobile,' . $id,
'group_arr' => 'required|array',
];
$messages = [
'username.regex' => __('Please input correct username'),
'password.regex' => __('Please input correct password'),
];
Validator::make($data, $rules, $messages)->validate();
} catch (ValidationException $e) {
return $this->error($e->getMessage());
}
}
if ($this->auth->id == $data['id'] && ($data['status'] ?? '') == 'disable') {
return $this->error(__('Please use another administrator account to disable the current account!'));
}
if (!empty($data['password'])) {
$this->model->resetPassword($row->id, $data['password']);
}
$groupAccess = [];
if (!empty($data['group_arr'])) {
$checkGroups = [];
$rowGroupArr = $row->group_arr ?? [];
foreach ($data['group_arr'] as $datum) {
if (!in_array($datum, $rowGroupArr)) {
$checkGroups[] = $datum;
}
$groupAccess[] = [
'uid' => $id,
'group_id' => $datum,
];
}
$authRes = $this->checkGroupAuth($checkGroups);
if ($authRes !== null) return $authRes;
}
Db::name('admin_group_access')
->where('uid', $id)
->delete();
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
$result = $row->save($data);
if ($groupAccess) {
Db::name('admin_group_access')->insertAll($groupAccess);
}
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Update successful'));
}
return $this->error(__('No rows updated'));
}
unset($row['salt'], $row['login_failure']);
$row['password'] = '';
return $this->success('', [
'row' => $row
]);
}
public function del(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$where = [];
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds) {
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
}
$ids = $request->get('ids') ?? $request->post('ids') ?? [];
$ids = is_array($ids) ? $ids : [];
$where[] = [$this->model->getPk(), 'in', $ids];
$data = $this->model->where($where)->select();
$count = 0;
$this->model->startTrans();
try {
foreach ($data as $v) {
if ($v->id != $this->auth->id) {
$count += $v->delete();
Db::name('admin_group_access')
->where('uid', $v['id'])
->delete();
}
}
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($count) {
return $this->success(__('Deleted successfully'));
}
return $this->error(__('No rows were deleted'));
}
/**
* 远程下拉Admin 无自定义,返回 null 走默认列表)
*/
protected function select(Request $request): ?Response
{
return null;
}
private function checkGroupAuth(array $groups): ?Response
{
if ($this->auth->isSuperAdmin()) {
return null;
}
$authGroups = $this->auth->getAllAuthGroups('allAuthAndOthers');
foreach ($groups as $group) {
if (!in_array($group, $authGroups)) {
return $this->error(__('You have no permission to add an administrator to this group!'));
}
}
return null;
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\auth;
use Throwable;
use app\common\controller\Backend;
use app\admin\model\AdminLog as AdminLogModel;
use support\Response;
use Webman\Http\Request;
class AdminLog extends Backend
{
protected ?object $model = null;
protected string|array $preExcludeFields = ['create_time', 'admin_id', 'username'];
protected string|array $quickSearchField = ['title'];
protected function initController(Request $request): void
{
$this->model = new AdminLogModel();
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') ?? $request->post('select')) {
$selectRes = $this->select($request);
if ($selectRes !== null) return $selectRes;
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
if (!$this->auth->isSuperAdmin()) {
$where[] = ['admin_id', '=', $this->auth->id];
}
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
/**
* 远程下拉AdminLog 无自定义,返回 null 走默认列表)
*/
protected function select(Request $request): ?Response
{
return null;
}
}

View File

@@ -0,0 +1,344 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\auth;
use Throwable;
use ba\Tree;
use support\think\Db;
use support\validation\Validator;
use support\validation\ValidationException;
use app\admin\model\AdminRule;
use app\admin\model\AdminGroup;
use app\common\controller\Backend;
use support\Response;
use Webman\Http\Request;
class Group extends Backend
{
protected string $authMethod = 'allAuthAndOthers';
protected ?object $model = null;
protected string|array $preExcludeFields = ['create_time', 'update_time'];
protected string|array $quickSearchField = 'name';
protected Tree $tree;
protected array $initValue = [];
protected string $keyword = '';
protected bool $assembleTree = true;
protected array $adminGroups = [];
protected function initController(Request $request): void
{
$this->model = new AdminGroup();
$this->tree = Tree::instance();
$isTree = $request->get('isTree') ?? $request->post('isTree') ?? true;
$this->initValue = $request->get('initValue') ?? [];
$this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
$this->keyword = $request->get('quickSearch') ?? $request->post('quickSearch') ?? '';
$this->assembleTree = $isTree && !$this->initValue;
$this->adminGroups = Db::name('admin_group_access')->where('uid', $this->auth->id)->column('group_id');
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') ?? $request->post('select')) {
return $this->select($request);
}
return $this->success('', [
'list' => $this->getGroups($request),
'group' => $this->adminGroups,
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$rulesRes = $this->handleRules($data);
if ($rulesRes instanceof Response) return $rulesRes;
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
try {
$rules = [
'name' => 'required|string',
'rules' => 'required',
];
$messages = [
'rules.required' => __('Please select rules'),
];
Validator::make($data, $rules, $messages)->validate();
} catch (ValidationException $e) {
throw $e;
}
}
$result = $this->model->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Added successfully'));
}
return $this->error(__('No rows were added'));
}
return $this->error(__('Parameter error'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->get($pk) ?? $request->post($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
$authRes = $this->checkAuth($id);
if ($authRes !== null) return $authRes;
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$adminGroup = Db::name('admin_group_access')->where('uid', $this->auth->id)->column('group_id');
if (in_array($data['id'], $adminGroup)) {
return $this->error(__('You cannot modify your own management group!'));
}
$data = $this->excludeFields($data);
$rulesRes = $this->handleRules($data);
if ($rulesRes instanceof Response) return $rulesRes;
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
try {
$rules = [
'name' => 'required|string',
'rules' => 'required',
];
$messages = [
'rules.required' => __('Please select rules'),
];
Validator::make($data, $rules, $messages)->validate();
} catch (ValidationException $e) {
throw $e;
}
}
$result = $row->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Update successful'));
}
return $this->error(__('No rows updated'));
}
$pidArr = AdminRule::field('pid')
->distinct()
->where('id', 'in', $row->rules)
->select()
->toArray();
$rules = $row->rules ? explode(',', $row->rules) : [];
foreach ($pidArr as $item) {
$ruKey = array_search($item['pid'], $rules);
if ($ruKey !== false) {
unset($rules[$ruKey]);
}
}
$row->rules = array_values($rules);
return $this->success('', [
'row' => $row
]);
}
public function del(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$ids = $request->get('ids') ?? $request->post('ids') ?? [];
$ids = is_array($ids) ? $ids : [];
$data = $this->model->where($this->model->getPk(), 'in', $ids)->select();
foreach ($data as $v) {
$authRes = $this->checkAuth($v->id);
if ($authRes !== null) return $authRes;
}
$subData = $this->model->where('pid', 'in', $ids)->column('pid', 'id');
foreach ($subData as $key => $subDatum) {
if (!in_array($key, $ids)) {
return $this->error(__('Please delete the child element first, or use batch deletion'));
}
}
$adminGroup = Db::name('admin_group_access')->where('uid', $this->auth->id)->column('group_id');
$count = 0;
$this->model->startTrans();
try {
foreach ($data as $v) {
if (!in_array($v['id'], $adminGroup)) {
$count += $v->delete();
}
}
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($count) {
return $this->success(__('Deleted successfully'));
}
return $this->error(__('No rows were deleted'));
}
public function select(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$data = $this->getGroups($request, [['status', '=', 1]]);
if ($this->assembleTree) {
$data = $this->tree->assembleTree($this->tree->getTreeArray($data));
}
return $this->success('', [
'options' => $data
]);
}
/**
* @return array|Response
*/
private function handleRules(array &$data)
{
if (!empty($data['rules']) && is_array($data['rules'])) {
$superAdmin = true;
$checkedRules = [];
$allRuleIds = AdminRule::column('id');
foreach ($data['rules'] as $postRuleId) {
if (in_array($postRuleId, $allRuleIds)) {
$checkedRules[] = $postRuleId;
}
}
foreach ($allRuleIds as $ruleId) {
if (!in_array($ruleId, $checkedRules)) {
$superAdmin = false;
}
}
if ($superAdmin && $this->auth->isSuperAdmin()) {
$data['rules'] = '*';
} else {
$ownedRuleIds = $this->auth->getRuleIds();
if (!array_diff($ownedRuleIds, $checkedRules)) {
return $this->error(__('Role group has all your rights, please contact the upper administrator to add or do not need to add!'));
}
if (array_diff($checkedRules, $ownedRuleIds) && !$this->auth->isSuperAdmin()) {
return $this->error(__('The group permission node exceeds the range that can be allocated'));
}
$data['rules'] = implode(',', $checkedRules);
}
} else {
unset($data['rules']);
}
return $data;
}
private function getGroups(Request $request, array $where = []): array
{
$pk = $this->model->getPk();
$initKey = $request->get('initKey') ?? $pk;
$absoluteAuth = $request->get('absoluteAuth') ?? false;
if ($this->keyword) {
$keyword = explode(' ', $this->keyword);
foreach ($keyword as $item) {
$where[] = [$this->quickSearchField, 'like', '%' . $item . '%'];
}
}
if ($this->initValue) {
$where[] = [$initKey, 'in', $this->initValue];
}
if (!$this->auth->isSuperAdmin()) {
$authGroups = $this->auth->getAllAuthGroups($this->authMethod, $where);
if (!$absoluteAuth) {
$authGroups = array_merge($this->adminGroups, $authGroups);
}
$where[] = ['id', 'in', $authGroups];
}
$data = $this->model->where($where)->select()->toArray();
foreach ($data as &$datum) {
if ($datum['rules']) {
if ($datum['rules'] == '*') {
$datum['rules'] = __('Super administrator');
} else {
$rules = explode(',', $datum['rules']);
if ($rules) {
$rulesFirstTitle = AdminRule::where('id', $rules[0])->value('title');
$datum['rules'] = count($rules) == 1 ? $rulesFirstTitle : $rulesFirstTitle . '等 ' . count($rules) . ' 项';
}
}
} else {
$datum['rules'] = __('No permission');
}
}
return $this->assembleTree ? $this->tree->assembleChild($data) : $data;
}
private function checkAuth($groupId): ?Response
{
$authGroups = $this->auth->getAllAuthGroups($this->authMethod, []);
if (!$this->auth->isSuperAdmin() && !in_array($groupId, $authGroups)) {
return $this->error(__($this->authMethod == 'allAuth' ? 'You need to have all permissions of this group to operate this group~' : 'You need to have all the permissions of the group and have additional permissions before you can operate the group~'));
}
return null;
}
}

View File

@@ -0,0 +1,302 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\auth;
use Throwable;
use ba\Tree;
use app\common\library\Menu;
use app\admin\model\AdminRule;
use app\admin\model\AdminGroup;
use app\admin\library\crud\Helper;
use app\common\controller\Backend;
use support\Response;
use Webman\Http\Request;
class Rule extends Backend
{
protected string|array $preExcludeFields = ['create_time', 'update_time'];
protected string|array $defaultSortField = ['weigh' => 'desc'];
protected string|array $quickSearchField = 'title';
protected ?object $model = null;
protected Tree $tree;
protected array $initValue = [];
protected string $keyword = '';
protected bool $assembleTree = true;
protected bool $modelValidate = false;
protected function initController(Request $request): void
{
$this->model = new AdminRule();
$this->tree = Tree::instance();
$isTree = $request->get('isTree') ?? $request->post('isTree') ?? true;
$this->initValue = $request->get('initValue') ?? [];
$this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
$this->keyword = $request->get('quickSearch') ?? $request->post('quickSearch') ?? '';
$this->assembleTree = $isTree && !$this->initValue;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') ?? $request->post('select')) {
return $this->select($request);
}
return $this->success('', [
'list' => $this->getMenus($request),
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
$data[$this->dataLimitField] = $this->auth->id;
}
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validate)) {
$validate = new $validate();
if ($this->modelSceneValidate) {
$validate->scene('add');
}
$validate->check($data);
}
}
$result = $this->model->save($data);
if (!empty($data['pid'])) {
$this->autoAssignPermission($this->model->id, (int) $data['pid']);
}
if (($data['type'] ?? '') == 'menu' && !empty($data['buttons'])) {
$newButtons = [];
foreach ($data['buttons'] as $button) {
foreach (Helper::$menuChildren as $menuChild) {
if ($menuChild['name'] == '/' . $button) {
$menuChild['name'] = $data['name'] . $menuChild['name'];
$newButtons[] = $menuChild;
}
}
}
if (!empty($newButtons)) {
Menu::create($newButtons, $this->model->id, 'ignore');
$children = AdminRule::where('pid', $this->model->id)->select();
foreach ($children as $child) {
$this->autoAssignPermission($child['id'], $this->model->id);
}
}
}
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Added successfully'));
}
return $this->error(__('No rows were added'));
}
return $this->error(__('Parameter error'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$id = $request->get($this->model->getPk()) ?? $request->post($this->model->getPk());
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
return $this->error(__('You have no permission'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validate)) {
$validate = new $validate();
if ($this->modelSceneValidate) {
$validate->scene('edit');
}
$validate->check($data);
}
}
if (isset($data['pid']) && $data['pid'] > 0) {
$parent = $this->model->where('id', $data['pid'])->find();
if ($parent && $parent['pid'] == $row['id']) {
$parent->pid = 0;
$parent->save();
}
}
$result = $row->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($result !== false) {
return $this->success(__('Update successful'));
}
return $this->error(__('No rows updated'));
}
return $this->success('', [
'row' => $row
]);
}
public function del(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$ids = $request->get('ids') ?? $request->post('ids') ?? [];
$ids = is_array($ids) ? $ids : [];
$subData = $this->model->where('pid', 'in', $ids)->column('pid', 'id');
foreach ($subData as $key => $subDatum) {
if (!in_array($key, $ids)) {
return $this->error(__('Please delete the child element first, or use batch deletion'));
}
}
return $this->delFromTrait($request);
}
public function select(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$data = $this->getMenus($request, [['type', 'in', ['menu_dir', 'menu']], ['status', '=', 1]]);
if ($this->assembleTree) {
$data = $this->tree->assembleTree($this->tree->getTreeArray($data, 'title'));
}
return $this->success('', [
'options' => $data
]);
}
protected function getMenus(Request $request, array $where = []): array
{
$pk = $this->model->getPk();
$initKey = $request->get('initKey') ?? $pk;
$ids = $this->auth->getRuleIds();
if (!in_array('*', $ids)) {
$where[] = ['id', 'in', $ids];
}
if ($this->keyword) {
$keyword = explode(' ', $this->keyword);
foreach ($keyword as $item) {
$where[] = [$this->quickSearchField, 'like', '%' . $item . '%'];
}
}
if ($this->initValue) {
$where[] = [$initKey, 'in', $this->initValue];
}
$rules = $this->model
->where($where)
->order($this->queryOrderBuilder())
->select()
->toArray();
return $this->assembleTree ? $this->tree->assembleChild($rules) : $rules;
}
private function autoAssignPermission(int $id, int $pid): void
{
$groups = AdminGroup::where('rules', '<>', '*')->select();
foreach ($groups as $group) {
/** @var AdminGroup $group */
$rules = explode(',', (string) $group->rules);
if (in_array($pid, $rules) && !in_array($id, $rules)) {
$rules[] = $id;
$group->rules = implode(',', $rules);
$group->save();
}
}
}
/**
* 调用 trait 的 del 逻辑(因 Rule 重写了 del需手动调用 trait
*/
private function delFromTrait(Request $request): Response
{
$where = [];
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds) {
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
}
$ids = $request->get('ids') ?? $request->post('ids') ?? [];
$ids = is_array($ids) ? $ids : [];
$where[] = [$this->model->getPk(), 'in', $ids];
$data = $this->model->where($where)->select();
$count = 0;
$this->model->startTrans();
try {
foreach ($data as $v) {
$count += $v->delete();
}
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
if ($count) {
return $this->success(__('Deleted successfully'));
}
return $this->error(__('No rows were deleted'));
}
}

View File

@@ -0,0 +1,762 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\crud;
use Throwable;
use ba\Exception as BaException;
use ba\Filesystem;
use ba\TableManager;
use app\admin\model\CrudLog;
use app\admin\model\AdminLog;
use app\admin\model\AdminRule;
use app\common\controller\Backend;
use app\common\library\Menu;
use app\admin\library\crud\Helper;
use Webman\Http\Request;
use support\Response;
use support\think\Db;
/**
* CRUD 代码生成器Webman 迁移版)
*/
class Crud extends Backend
{
protected array $modelData = [];
protected array $controllerData = [];
protected array $indexVueData = [];
protected array $formVueData = [];
protected string $webTranslate = '';
protected array $langTsData = [];
protected array $dtStringToArray = ['checkbox', 'selects', 'remoteSelects', 'city', 'images', 'files'];
protected array $noNeedPermission = ['logStart', 'getFileData', 'parseFieldData', 'generateCheck', 'uploadCompleted'];
protected function initController(Request $request): ?Response
{
return null;
}
public function generate(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$type = $request->post('type', '');
$table = $request->post('table', []);
$fields = $request->post('fields', []);
if (!$table || !$fields || !isset($table['name']) || !$table['name']) {
return $this->error(__('Parameter error'));
}
$crudLogId = 0;
try {
$crudLogId = Helper::recordCrudStatus([
'table' => $table,
'fields' => $fields,
'status' => 'start',
]);
$tableName = TableManager::tableName($table['name'], false, $table['databaseConnection'] ?? null);
if ($type == 'create' || ($table['rebuild'] ?? '') == 'Yes') {
TableManager::phinxTable($tableName, [], true, $table['databaseConnection'] ?? null)->drop()->save();
}
[$tablePk] = Helper::handleTableDesign($table, $fields);
$tableComment = mb_substr($table['comment'] ?? '', -1) == '表'
? mb_substr($table['comment'], 0, -1) . '管理'
: ($table['comment'] ?? '');
$modelFile = Helper::parseNameData($table['isCommonModel'] ?? false ? 'common' : 'admin', $tableName, 'model', $table['modelFile'] ?? '');
$validateFile = Helper::parseNameData($table['isCommonModel'] ?? false ? 'common' : 'admin', $tableName, 'validate', $table['validateFile'] ?? '');
$controllerFile = Helper::parseNameData('admin', $tableName, 'controller', $table['controllerFile'] ?? '');
$webViewsDir = Helper::parseWebDirNameData($tableName, 'views', $table['webViewsDir'] ?? '');
$webLangDir = Helper::parseWebDirNameData($tableName, 'lang', $table['webViewsDir'] ?? '');
$this->webTranslate = implode('.', $webLangDir['lang']) . '.';
$quickSearchField = $table['quickSearchField'] ?? [$tablePk];
if (!in_array($tablePk, $quickSearchField)) {
$quickSearchField[] = $tablePk;
}
$quickSearchFieldZhCnTitle = [];
$this->modelData = [
'append' => [], 'methods' => [], 'fieldType' => [], 'createTime' => '', 'updateTime' => '',
'beforeInsertMixins' => [], 'beforeInsert' => '', 'afterInsert' => '',
'connection' => $table['databaseConnection'] ?? '', 'name' => $tableName,
'className' => $modelFile['lastName'], 'namespace' => $modelFile['namespace'],
'relationMethodList' => [],
];
$this->controllerData = [
'use' => [], 'attr' => [], 'methods' => [], 'filterRule' => '',
'className' => $controllerFile['lastName'], 'namespace' => $controllerFile['namespace'],
'tableComment' => $tableComment, 'modelName' => $modelFile['lastName'],
'modelNamespace' => $modelFile['namespace'],
];
$this->indexVueData = [
'enableDragSort' => false, 'defaultItems' => [],
'tableColumn' => [['type' => 'selection', 'align' => 'center', 'operator' => 'false']],
'dblClickNotEditColumn' => ['undefined'], 'optButtons' => ['edit', 'delete'],
'defaultOrder' => '',
];
$this->formVueData = ['bigDialog' => false, 'formFields' => [], 'formValidatorRules' => [], 'imports' => []];
$this->langTsData = ['en' => [], 'zh-cn' => []];
$fieldsMap = [];
foreach ($fields as $field) {
$fieldsMap[$field['name']] = $field['designType'];
Helper::analyseField($field);
Helper::getDictData($this->langTsData['en'], $field, 'en');
Helper::getDictData($this->langTsData['zh-cn'], $field, 'zh-cn');
if (in_array($field['name'], $quickSearchField)) {
$quickSearchFieldZhCnTitle[] = $this->langTsData['zh-cn'][$field['name']] ?? $field['name'];
}
if (($field['designType'] ?? '') == 'switch') {
$this->indexVueData['dblClickNotEditColumn'][] = $field['name'];
}
$columnDict = $this->getColumnDict($field);
if (in_array($field['name'], $table['formFields'] ?? [])) {
$this->formVueData['formFields'][] = $this->getFormField($field, $columnDict, $table['databaseConnection'] ?? null);
}
if (in_array($field['name'], $table['columnFields'] ?? [])) {
$this->indexVueData['tableColumn'][] = $this->getTableColumn($field, $columnDict);
}
if (in_array($field['designType'] ?? '', ['remoteSelect', 'remoteSelects'])) {
$this->parseJoinData($field, $table);
}
$this->parseModelMethods($field, $this->modelData);
$this->parseSundryData($field, $table);
if (!in_array($field['name'], $table['formFields'] ?? [])) {
$this->controllerData['attr']['preExcludeFields'][] = $field['name'];
}
}
$this->langTsData['en']['quick Search Fields'] = implode(',', $quickSearchField);
$this->langTsData['zh-cn']['quick Search Fields'] = implode('、', $quickSearchFieldZhCnTitle);
$this->controllerData['attr']['quickSearchField'] = $quickSearchField;
$weighKey = array_search('weigh', $fieldsMap);
if ($weighKey !== false) {
$this->indexVueData['enableDragSort'] = true;
$this->modelData['afterInsert'] = Helper::assembleStub('mixins/model/afterInsert', ['field' => $weighKey]);
}
$this->indexVueData['tableColumn'][] = [
'label' => "t('Operate')", 'align' => 'center',
'width' => $this->indexVueData['enableDragSort'] ? 140 : 100,
'render' => 'buttons', 'buttons' => 'optButtons', 'operator' => 'false',
];
if ($this->indexVueData['enableDragSort']) {
array_unshift($this->indexVueData['optButtons'], 'weigh-sort');
}
Helper::writeWebLangFile($this->langTsData, $webLangDir);
Helper::writeModelFile($tablePk, $fieldsMap, $this->modelData, $modelFile);
Helper::writeControllerFile($this->controllerData, $controllerFile);
$validateContent = Helper::assembleStub('mixins/validate/validate', [
'namespace' => $validateFile['namespace'],
'className' => $validateFile['lastName'],
]);
Helper::writeFile($validateFile['parseFile'], $validateContent);
$this->indexVueData['tablePk'] = $tablePk;
$this->indexVueData['webTranslate'] = $this->webTranslate;
Helper::writeIndexFile($this->indexVueData, $webViewsDir, $controllerFile);
Helper::writeFormFile($this->formVueData, $webViewsDir, $fields, $this->webTranslate);
Helper::createMenu($webViewsDir, $tableComment);
Helper::recordCrudStatus(['id' => $crudLogId, 'status' => 'success']);
} catch (BaException $e) {
Helper::recordCrudStatus(['id' => $crudLogId ?: 0, 'status' => 'error']);
return $this->error($e->getMessage());
} catch (Throwable $e) {
Helper::recordCrudStatus(['id' => $crudLogId ?: 0, 'status' => 'error']);
if (env('app_debug', false)) throw $e;
return $this->error($e->getMessage());
}
return $this->success('', ['crudLog' => CrudLog::find($crudLogId)]);
}
public function logStart(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$id = $request->post('id');
$type = $request->post('type', '');
if ($type == 'Cloud history') {
try {
$client = get_ba_client();
$response = $client->request('GET', '/api/v6.Crud/info', [
'query' => [
'id' => $id,
'server' => 1,
'ba-user-token' => $request->post('token', ''),
]
]);
$content = $response->getBody()->getContents();
$statusCode = $response->getStatusCode();
if ($content == '' || stripos($content, '<title>系统发生错误</title>') !== false || $statusCode != 200) {
return $this->error(__('Failed to load cloud data'));
}
$json = json_decode($content, true);
if (json_last_error() != JSON_ERROR_NONE || !is_array($json)) {
return $this->error(__('Failed to load cloud data'));
}
if (($json['code'] ?? 0) != 1) {
return $this->error($json['msg'] ?? __('Failed to load cloud data'));
}
$info = $json['data']['info'] ?? null;
} catch (Throwable $e) {
return $this->error(__('Failed to load cloud data'));
}
} else {
$row = CrudLog::find($id);
$info = $row ? $row->toArray() : null;
}
if (!$info) {
return $this->error(__('Record not found'));
}
$connection = TableManager::getConnection($info['table']['databaseConnection'] ?? '');
$tableName = TableManager::tableName($info['table']['name'], false, $connection);
$adapter = TableManager::phinxAdapter(true, $connection);
if ($adapter->hasTable($tableName)) {
$info['table']['empty'] = Db::connect($connection)->name($tableName)->limit(1)->select()->isEmpty();
} else {
$info['table']['empty'] = true;
}
AdminLog::instance($request)->setTitle(__('Log start'));
return $this->success('', [
'table' => $info['table'],
'fields' => $info['fields'],
'sync' => $info['sync'] ?? 0,
]);
}
public function delete(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$id = $request->post('id');
$row = CrudLog::find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
$info = $row->toArray();
$webLangDir = Helper::parseWebDirNameData($info['table']['name'], 'lang', $info['table']['webViewsDir'] ?? '');
$files = [
$webLangDir['en'] . '.ts',
$webLangDir['zh-cn'] . '.ts',
($info['table']['webViewsDir'] ?? '') . '/index.vue',
($info['table']['webViewsDir'] ?? '') . '/popupForm.vue',
$info['table']['controllerFile'] ?? '',
$info['table']['modelFile'] ?? '',
$info['table']['validateFile'] ?? '',
];
try {
foreach ($files as $file) {
$file = Filesystem::fsFit(root_path() . $file);
if (file_exists($file)) {
unlink($file);
}
Filesystem::delEmptyDir(dirname($file));
}
Menu::delete(Helper::getMenuName($webLangDir), true);
Helper::recordCrudStatus(['id' => $id, 'status' => 'delete']);
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
return $this->success(__('Deleted successfully'));
}
public function getFileData(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$table = $request->get('table');
$commonModel = $request->get('commonModel', false);
if (!$table) {
return $this->error(__('Parameter error'));
}
try {
$modelFile = Helper::parseNameData($commonModel ? 'common' : 'admin', $table, 'model');
$validateFile = Helper::parseNameData($commonModel ? 'common' : 'admin', $table, 'validate');
$controllerFile = Helper::parseNameData('admin', $table, 'controller');
$webViewsDir = Helper::parseWebDirNameData($table, 'views');
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
$adminModelFiles = Filesystem::getDirFiles(root_path() . 'app' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
$commonModelFiles = Filesystem::getDirFiles(root_path() . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR);
$adminControllerFiles = get_controller_list();
$modelFileList = [];
$controllerFiles = [];
foreach ($adminModelFiles as $item) {
$item = Filesystem::fsFit('app/admin/model/' . $item);
$modelFileList[$item] = $item;
}
foreach ($commonModelFiles as $item) {
$item = Filesystem::fsFit('app/common/model/' . $item);
$modelFileList[$item] = $item;
}
$outExcludeController = ['Addon.php', 'Ajax.php', 'Dashboard.php', 'Index.php', 'Module.php', 'Terminal.php', 'routine/AdminInfo.php', 'routine/Config.php'];
foreach ($adminControllerFiles as $item) {
if (!in_array($item, $outExcludeController)) {
$item = Filesystem::fsFit('app/admin/controller/' . $item);
$controllerFiles[$item] = $item;
}
}
return $this->success('', [
'modelFile' => $modelFile['rootFileName'],
'controllerFile' => $controllerFile['rootFileName'],
'validateFile' => $validateFile['rootFileName'],
'controllerFileList' => $controllerFiles,
'modelFileList' => $modelFileList,
'webViewsDir' => $webViewsDir['views'],
]);
}
public function checkCrudLog(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$table = $request->get('table');
$connection = $request->get('connection', config('thinkorm.default', config('database.default', 'mysql')));
if (!$table) {
return $this->error(__('Parameter error'));
}
$crudLog = Db::name('crud_log')
->where('table_name', $table)
->where('connection', $connection)
->order('create_time desc')
->find();
$id = ($crudLog && isset($crudLog['status']) && $crudLog['status'] == 'success') ? $crudLog['id'] : 0;
return $this->success('', ['id' => $id]);
}
public function parseFieldData(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Parse field data'));
$type = $request->post('type');
$table = $request->post('table');
$connection = TableManager::getConnection($request->post('connection', ''));
$table = TableManager::tableName($table, true, $connection);
$connectionConfig = TableManager::getConnectionConfig($connection);
if ($type == 'db') {
$sql = 'SELECT * FROM `information_schema`.`tables` WHERE TABLE_SCHEMA = ? AND table_name = ?';
$tableInfo = Db::connect($connection)->query($sql, [$connectionConfig['database'], $table]);
if (!$tableInfo) {
return $this->error(__('Record not found'));
}
$adapter = TableManager::phinxAdapter(false, $connection);
$empty = $adapter->hasTable($table)
? Db::connect($connection)->table($table)->limit(1)->select()->isEmpty()
: true;
return $this->success('', [
'columns' => Helper::parseTableColumns($table, false, $connection),
'comment' => $tableInfo[0]['TABLE_COMMENT'] ?? '',
'empty' => $empty,
]);
}
return $this->error(__('Parameter error'));
}
public function generateCheck(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
AdminLog::instance($request)->setTitle(__('Generate check'));
$table = $request->post('table');
$connection = $request->post('connection', '');
$webViewsDir = $request->post('webViewsDir', '');
$controllerFile = $request->post('controllerFile', '');
if (!$table) {
return $this->error(__('Parameter error'));
}
try {
$webViewsDir = Helper::parseWebDirNameData($table, 'views', $webViewsDir);
$controllerFile = Helper::parseNameData('admin', $table, 'controller', $controllerFile)['rootFileName'];
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
$tableList = TableManager::getTableList($connection);
$tableExist = array_key_exists(TableManager::tableName($table, true, $connection), $tableList);
$controllerExist = file_exists(root_path() . $controllerFile);
$menuName = Helper::getMenuName($webViewsDir);
$menuExist = AdminRule::where('name', $menuName)->value('id');
if ($controllerExist || $tableExist || $menuExist) {
return $this->error('', [
'menu' => $menuExist,
'table' => $tableExist,
'controller' => $controllerExist,
], -1);
}
return $this->success();
}
public function uploadCompleted(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$syncIds = $request->post('syncIds', []);
$syncIds = is_array($syncIds) ? $syncIds : [];
$cancelSync = $request->post('cancelSync', false);
$crudLogModel = new CrudLog();
if ($cancelSync) {
$logData = $crudLogModel->where('id', 'in', array_keys($syncIds))->select();
foreach ($logData as $logDatum) {
if (isset($syncIds[$logDatum->id]) && $logDatum->sync == $syncIds[$logDatum->id]) {
$logDatum->sync = 0;
$logDatum->save();
}
}
return $this->success();
}
foreach ($syncIds as $key => $syncId) {
$row = $crudLogModel->find($key);
if ($row) {
$row->sync = $syncId;
$row->save();
}
}
return $this->success();
}
private function parseJoinData($field, $table): void
{
$dictEn = [];
$dictZhCn = [];
if (empty($field['form']['relation-fields']) || empty($field['form']['remote-table'])) {
return;
}
$columns = Helper::parseTableColumns($field['form']['remote-table'], true, $table['databaseConnection'] ?? null);
$relationFields = explode(',', $field['form']['relation-fields']);
$tableName = TableManager::tableName($field['form']['remote-table'], false, $table['databaseConnection'] ?? null);
$rnPattern = '/(.*)(_ids|_id)$/';
$relationName = preg_match($rnPattern, $field['name'])
? parse_name(preg_replace($rnPattern, '$1', $field['name']), 1, false)
: parse_name($field['name'] . '_table', 1, false);
if (empty($field['form']['remote-model']) || !file_exists(root_path() . $field['form']['remote-model'])) {
$joinModelFile = Helper::parseNameData('admin', $tableName, 'model', $field['form']['remote-model'] ?? '');
if (!file_exists(root_path() . $joinModelFile['rootFileName'])) {
$joinModelData = [
'append' => [], 'methods' => [], 'fieldType' => [], 'createTime' => '', 'updateTime' => '',
'beforeInsertMixins' => [], 'beforeInsert' => '', 'afterInsert' => '',
'connection' => $table['databaseConnection'] ?? '', 'name' => $tableName,
'className' => $joinModelFile['lastName'], 'namespace' => $joinModelFile['namespace'],
];
$joinTablePk = 'id';
$joinFieldsMap = [];
foreach ($columns as $column) {
$joinFieldsMap[$column['name']] = $column['designType'];
$this->parseModelMethods($column, $joinModelData);
if ($column['primaryKey']) $joinTablePk = $column['name'];
}
$weighKey = array_search('weigh', $joinFieldsMap);
if ($weighKey !== false) {
$joinModelData['afterInsert'] = Helper::assembleStub('mixins/model/afterInsert', ['field' => $weighKey]);
}
Helper::writeModelFile($joinTablePk, $joinFieldsMap, $joinModelData, $joinModelFile);
}
$field['form']['remote-model'] = $joinModelFile['rootFileName'];
}
if ($field['designType'] == 'remoteSelect') {
$this->controllerData['attr']['withJoinTable'][$relationName] = $relationName;
$relationData = [
'relationMethod' => $relationName, 'relationMode' => 'belongsTo',
'relationPrimaryKey' => $field['form']['remote-pk'] ?? 'id',
'relationForeignKey' => $field['name'],
'relationClassName' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']) . "::class",
];
$this->modelData['relationMethodList'][$relationName] = Helper::assembleStub('mixins/model/belongsTo', $relationData);
if ($relationFields) {
$this->controllerData['relationVisibleFieldList'][$relationData['relationMethod']] = $relationFields;
}
} elseif ($field['designType'] == 'remoteSelects') {
$this->modelData['append'][] = $relationName;
$this->modelData['methods'][] = Helper::assembleStub('mixins/model/getters/remoteSelectLabels', [
'field' => parse_name($relationName, 1),
'className' => str_replace(['.php', '/'], ['', '\\'], '\\' . $field['form']['remote-model']),
'primaryKey' => $field['form']['remote-pk'] ?? 'id',
'foreignKey' => $field['name'],
'labelFieldName' => $field['form']['remote-field'] ?? 'name',
]);
}
foreach ($relationFields as $relationField) {
if (!array_key_exists($relationField, $columns)) continue;
$relationFieldPrefix = $relationName . '.';
$relationFieldLangPrefix = strtolower($relationName) . '__';
Helper::getDictData($dictEn, $columns[$relationField], 'en', $relationFieldLangPrefix);
Helper::getDictData($dictZhCn, $columns[$relationField], 'zh-cn', $relationFieldLangPrefix);
if (($columns[$relationField]['designType'] ?? '') == 'switch') {
$this->indexVueData['dblClickNotEditColumn'][] = $field['name'];
}
$columnDict = $this->getColumnDict($columns[$relationField], $relationFieldLangPrefix);
$columns[$relationField]['designType'] = $field['designType'];
$columns[$relationField]['form'] = ($field['form'] ?? []) + ($columns[$relationField]['form'] ?? []);
$columns[$relationField]['table'] = ($field['table'] ?? []) + ($columns[$relationField]['table'] ?? []);
$remoteAttr = [
'pk' => $this->getRemoteSelectPk($field),
'field' => $field['form']['remote-field'] ?? 'name',
'remoteUrl' => $this->getRemoteSelectUrl($field),
];
if (($columns[$relationField]['table']['comSearchRender'] ?? '') == 'remoteSelect') {
$renderColumn = $columns[$relationField];
$renderColumn['table']['operator'] = 'false';
unset($renderColumn['table']['comSearchRender']);
$this->indexVueData['tableColumn'][] = $this->getTableColumn($renderColumn, $columnDict, $relationFieldPrefix, $relationFieldLangPrefix);
$columns[$relationField]['table']['show'] = 'false';
$columns[$relationField]['table']['label'] = "t('" . $this->webTranslate . $relationFieldLangPrefix . $columns[$relationField]['name'] . "')";
$columns[$relationField]['name'] = $field['name'];
if ($field['designType'] == 'remoteSelects') {
$remoteAttr['multiple'] = 'true';
}
$columnData = $this->getTableColumn($columns[$relationField], $columnDict, '', $relationFieldLangPrefix);
$columnData['comSearchInputAttr'] = array_merge($remoteAttr, $columnData['comSearchInputAttr'] ?? []);
} else {
$columnData = $this->getTableColumn($columns[$relationField], $columnDict, $relationFieldPrefix, $relationFieldLangPrefix);
}
$this->indexVueData['tableColumn'][] = $columnData;
}
$this->langTsData['en'] = array_merge($this->langTsData['en'], $dictEn);
$this->langTsData['zh-cn'] = array_merge($this->langTsData['zh-cn'], $dictZhCn);
}
private function parseModelMethods($field, &$modelData): void
{
if (($field['designType'] ?? '') == 'array') {
$modelData['fieldType'][$field['name']] = 'json';
} elseif (!in_array($field['name'], ['create_time', 'update_time', 'updatetime', 'createtime'])
&& ($field['designType'] ?? '') == 'datetime'
&& in_array($field['type'] ?? '', ['int', 'bigint'])) {
$modelData['fieldType'][$field['name']] = 'timestamp:Y-m-d H:i:s';
}
if (($field['designType'] ?? '') == 'spk') {
$modelData['beforeInsertMixins']['snowflake'] = Helper::assembleStub('mixins/model/mixins/beforeInsertWithSnowflake', []);
}
$fieldName = parse_name($field['name'], 1);
if (in_array($field['designType'] ?? '', $this->dtStringToArray)) {
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/stringToArray', ['field' => $fieldName]);
$modelData['methods'][] = Helper::assembleStub('mixins/model/setters/arrayToString', ['field' => $fieldName]);
} elseif (($field['designType'] ?? '') == 'array') {
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/jsonDecode', ['field' => $fieldName]);
} elseif (($field['designType'] ?? '') == 'time') {
$modelData['methods'][] = Helper::assembleStub('mixins/model/setters/time', ['field' => $fieldName]);
} elseif (($field['designType'] ?? '') == 'editor') {
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/htmlDecode', ['field' => $fieldName]);
} elseif (($field['designType'] ?? '') == 'spk') {
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/string', ['field' => $fieldName]);
} elseif (in_array($field['type'] ?? '', ['float', 'decimal', 'double'])) {
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/float', ['field' => $fieldName]);
}
if (($field['designType'] ?? '') == 'city') {
$modelData['append'][] = $field['name'] . '_text';
$modelData['methods'][] = Helper::assembleStub('mixins/model/getters/cityNames', [
'field' => $fieldName . 'Text',
'originalFieldName' => $field['name'],
]);
}
}
private function parseSundryData($field, $table): void
{
if (($field['designType'] ?? '') == 'editor') {
$this->formVueData['bigDialog'] = true;
$this->controllerData['filterRule'] = "\n" . Helper::tab(2) . '$this->request->filter(\'clean_xss\');';
}
if (!empty($table['defaultSortField']) && !empty($table['defaultSortType'])) {
$defaultSortField = "{$table['defaultSortField']},{$table['defaultSortType']}";
if ($defaultSortField == 'id,desc') {
$this->controllerData['attr']['defaultSortField'] = '';
} else {
$this->controllerData['attr']['defaultSortField'] = $defaultSortField;
$this->indexVueData['defaultOrder'] = Helper::buildDefaultOrder($table['defaultSortField'], $table['defaultSortType']);
}
}
if (($field['originalDesignType'] ?? '') == 'weigh' && $field['name'] != 'weigh') {
$this->controllerData['attr']['weighField'] = $field['name'];
}
}
private function getFormField($field, $columnDict, ?string $dbConnection = null): array
{
$formField = [
':label' => 't(\'' . $this->webTranslate . $field['name'] . '\')',
'type' => $field['designType'],
'v-model' => 'baTable.form.items!.' . $field['name'],
'prop' => $field['name'],
];
if ($columnDict || in_array($field['designType'], ['radio', 'checkbox', 'select', 'selects'])) {
$formField[':input-attr']['content'] = $columnDict;
} elseif ($field['designType'] == 'textarea') {
$formField[':input-attr']['rows'] = (int)($field['form']['rows'] ?? 3);
$formField['@keyup.enter.stop'] = '';
$formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
} elseif (in_array($field['designType'], ['remoteSelect', 'remoteSelects'])) {
$formField[':input-attr']['pk'] = $this->getRemoteSelectPk($field);
$formField[':input-attr']['field'] = $field['form']['remote-field'] ?? 'name';
$formField[':input-attr']['remoteUrl'] = $this->getRemoteSelectUrl($field);
} elseif ($field['designType'] == 'number') {
$formField[':input-attr']['step'] = (int)($field['form']['step'] ?? 1);
} elseif ($field['designType'] == 'icon') {
$formField[':input-attr']['placement'] = 'top';
} elseif ($field['designType'] == 'editor') {
$formField['@keyup.enter.stop'] = '';
$formField['@keyup.ctrl.enter'] = 'baTable.onSubmit(formRef)';
}
if (!in_array($field['designType'], ['image', 'images', 'file', 'files', 'switch'])) {
if (in_array($field['designType'], ['radio', 'checkbox', 'datetime', 'year', 'date', 'time', 'select', 'selects', 'remoteSelect', 'remoteSelects', 'city', 'icon'])) {
$formField[':placeholder'] = "t('Please select field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
} else {
$formField[':placeholder'] = "t('Please input field', { field: t('" . $this->webTranslate . $field['name'] . "') })";
}
}
if (($field['defaultType'] ?? '') == 'INPUT') {
$this->indexVueData['defaultItems'][$field['name']] = $field['default'];
}
if ($field['designType'] == 'editor') {
$this->indexVueData['defaultItems'][$field['name']] = (($field['defaultType'] ?? '') == 'INPUT' && ($field['default'] ?? '')) ? $field['default'] : '';
} elseif ($field['designType'] == 'array') {
$this->indexVueData['defaultItems'][$field['name']] = "[]";
} elseif (($field['defaultType'] ?? '') == 'INPUT' && in_array($field['designType'], $this->dtStringToArray) && str_contains($field['default'] ?? '', ',')) {
$this->indexVueData['defaultItems'][$field['name']] = Helper::buildSimpleArray(explode(',', $field['default']));
} elseif (($field['defaultType'] ?? '') == 'INPUT' && in_array($field['designType'], ['number', 'float'])) {
$this->indexVueData['defaultItems'][$field['name']] = (float)($field['default'] ?? 0);
}
if (isset($field['default']) && in_array($field['designType'], ['switch', 'number', 'float', 'remoteSelect']) && $field['default'] == 0) {
unset($this->indexVueData['defaultItems'][$field['name']]);
}
return $formField;
}
private function getRemoteSelectPk($field): string
{
$pk = $field['form']['remote-pk'] ?? 'id';
$alias = '';
if (!str_contains($pk, '.')) {
if (($field['form']['remote-source-config-type'] ?? '') == 'crud' && !empty($field['form']['remote-model'])) {
$alias = parse_name(basename(str_replace('\\', '/', $field['form']['remote-model']), '.php'));
} else {
$alias = $field['form']['remote-primary-table-alias'] ?? '';
}
}
return !empty($alias) ? "$alias.$pk" : $pk;
}
private function getRemoteSelectUrl($field): string
{
if (($field['form']['remote-source-config-type'] ?? '') == 'crud' && !empty($field['form']['remote-controller'])) {
$pathArr = [];
$controller = explode(DIRECTORY_SEPARATOR, $field['form']['remote-controller']);
$controller = str_replace('.php', '', $controller);
$redundantDir = ['app' => 0, 'admin' => 1, 'controller' => 2];
foreach ($controller as $key => $item) {
if (!array_key_exists($item, $redundantDir) || $key !== $redundantDir[$item]) {
$pathArr[] = $item;
}
}
$url = count($pathArr) > 1 ? implode('.', $pathArr) : ($pathArr[0] ?? '');
return '/admin/' . $url . '/index';
}
return $field['form']['remote-url'] ?? '';
}
private function getTableColumn($field, $columnDict, string $fieldNamePrefix = '', string $translationPrefix = ''): array
{
$column = [
'label' => "t('" . $this->webTranslate . $translationPrefix . $field['name'] . "')",
'prop' => $fieldNamePrefix . $field['name'] . (($field['designType'] ?? '') == 'city' ? '_text' : ''),
'align' => 'center',
];
if (isset($field['table']['operator']) && $field['table']['operator'] == 'LIKE') {
$column['operatorPlaceholder'] = "t('Fuzzy query')";
}
if (!empty($field['table'])) {
$column = array_merge($column, $field['table']);
$column['comSearchInputAttr'] = str_attr_to_array($column['comSearchInputAttr'] ?? '');
}
$columnReplaceValue = ['tag', 'tags', 'switch'];
if (!in_array($field['designType'] ?? '', ['remoteSelect', 'remoteSelects'])
&& ($columnDict || (isset($field['table']['render']) && in_array($field['table']['render'], $columnReplaceValue)))) {
$column['replaceValue'] = $columnDict;
}
if (isset($column['render']) && $column['render'] == 'none') {
unset($column['render']);
}
return $column;
}
private function getColumnDict($column, string $translationPrefix = ''): array
{
$dict = [];
if (in_array($column['type'] ?? '', ['enum', 'set'])) {
$dataType = str_replace(' ', '', $column['dataType'] ?? '');
$columnData = substr($dataType, stripos($dataType, '(') + 1, -1);
$columnData = explode(',', str_replace(["'", '"'], '', $columnData));
foreach ($columnData as $columnDatum) {
$dict[$columnDatum] = $column['name'] . ' ' . $columnDatum;
}
}
$dictData = [];
Helper::getDictData($dictData, $column, 'zh-cn', $translationPrefix);
if ($dictData) {
unset($dictData[$translationPrefix . $column['name']]);
foreach ($dictData as $key => $item) {
$keyName = str_replace($translationPrefix . $column['name'] . ' ', '', $key);
$dict[$keyName] = "t('" . $this->webTranslate . $key . "')";
}
}
return $dict;
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\crud;
use app\admin\model\CrudLog;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class Log extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['id', 'create_time'];
protected array|string $quickSearchField = ['id', 'table_name', 'comment'];
protected array $noNeedPermission = ['index'];
protected function initController(Request $request): ?Response
{
$this->model = new CrudLog();
if (!$this->auth->check('crud/crud/index')) {
return $this->error(__('You have no permission'), [], 401);
}
return null;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withJoin($this->withJoinTable ?? [], $this->withJoinType ?? 'LEFT')
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\routine;
use app\admin\model\Admin;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class AdminInfo extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['username', 'last_login_time', 'password', 'salt', 'status'];
protected array $authAllowFields = ['id', 'username', 'nickname', 'avatar', 'email', 'mobile', 'motto', 'last_login_time'];
protected function initController(Request $request): void
{
$this->auth->setAllowFields($this->authAllowFields);
$this->model = $this->auth->getAdmin();
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$info = $this->auth->getInfo();
return $this->success('', ['info' => $info]);
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->post($pk) ?? $request->get($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
if (!empty($data['avatar'])) {
$row->avatar = $data['avatar'];
if ($row->save()) {
return $this->success(__('Avatar modified successfully!'));
}
}
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
try {
$validate = new $validateClass();
$validate->scene('info')->check($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
if (!empty($data['password'])) {
$this->model->resetPassword($this->auth->id, $data['password']);
}
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
$result = $row->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Update successful')) : $this->error(__('No rows updated'));
}
return $this->success('', ['row' => $row]);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\routine;
use app\common\controller\Backend;
use app\common\model\Attachment as AttachmentModel;
use Webman\Http\Request;
use support\Response;
class Attachment extends Backend
{
protected ?object $model = null;
protected array|string $quickSearchField = 'name';
protected array $withJoinTable = ['admin', 'user'];
protected array|string $defaultSortField = ['last_upload_time' => 'desc'];
protected function initController(Request $request): void
{
$this->model = new AttachmentModel();
}
public function del(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$where = [];
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds) {
$where[] = [$this->dataLimitField, 'in', $dataLimitAdminIds];
}
$ids = $request->post('ids', $request->get('ids', []));
$ids = is_array($ids) ? $ids : [];
$where[] = [$this->model->getPk(), 'in', $ids];
$data = $this->model->where($where)->select();
$count = 0;
try {
foreach ($data as $v) {
$count += $v->delete();
}
} catch (\Throwable $e) {
return $this->error(__('%d records and files have been deleted', [$count]) . $e->getMessage());
}
return $count ? $this->success(__('%d records and files have been deleted', [$count])) : $this->error(__('No rows were deleted'));
}
}

View File

@@ -0,0 +1,168 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\routine;
use ba\Filesystem;
use app\common\library\Email;
use PHPMailer\PHPMailer\PHPMailer;
use app\common\controller\Backend;
use app\admin\model\Config as ConfigModel;
use PHPMailer\PHPMailer\Exception as PHPMailerException;
use Webman\Http\Request;
use support\Response;
class Config extends Backend
{
protected ?object $model = null;
protected array $filePath = [
'appConfig' => 'config/app.php',
'webAdminBase' => 'web/src/router/static/adminBase.ts',
'backendEntranceStub' => 'app/admin/library/stubs/backendEntrance.stub',
];
protected function initController(Request $request): void
{
$this->model = new ConfigModel();
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$configGroup = get_sys_config('config_group');
$config = $this->model->order('weigh desc')->select()->toArray();
$list = [];
$newConfigGroup = [];
if (is_array($configGroup)) {
foreach ($configGroup as $item) {
$key = $item['key'] ?? $item;
$val = $item['value'] ?? $item;
$list[$key] = ['name' => $key, 'title' => __($val)];
$newConfigGroup[$key] = $list[$key]['title'];
}
}
foreach ($config as $item) {
$group = $item['group'] ?? '';
if (isset($newConfigGroup[$group])) {
$item['title'] = __($item['title'] ?? '');
$list[$group]['list'][] = $item;
}
}
return $this->success('', [
'list' => $list,
'remark' => get_route_remark(),
'configGroup' => $newConfigGroup,
'quickEntrance' => get_sys_config('config_quick_entrance'),
]);
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$all = $this->model->select();
if ($request->method() === 'POST') {
$this->modelValidate = false;
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$configValue = [];
foreach ($all as $item) {
if (array_key_exists($item->name, $data)) {
$configValue[] = [
'id' => $item->id,
'type' => $item->getData('type'),
'value' => $data[$item->name]
];
}
}
$result = false;
$this->model->startTrans();
try {
foreach ($configValue as $cv) {
$this->model->where('id', $cv['id'])->update(['value' => $cv['value']]);
}
$this->model->commit();
$result = true;
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result ? $this->success(__('The current page configuration item was updated successfully')) : $this->error(__('No rows updated'));
}
return $this->error(__('Parameter error'));
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('add')->check($data);
}
}
$result = $this->model->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Added successfully')) : $this->error(__('No rows were added'));
}
public function sendTestMail(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$data = $request->post();
$mail = new Email();
try {
$mail->Host = $data['smtp_server'] ?? '';
$mail->SMTPAuth = true;
$mail->Username = $data['smtp_user'] ?? '';
$mail->Password = $data['smtp_pass'] ?? '';
$mail->SMTPSecure = ($data['smtp_verification'] ?? '') == 'SSL' ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = $data['smtp_port'] ?? 465;
$mail->setFrom($data['smtp_sender_mail'] ?? '', $data['smtp_user'] ?? '');
$mail->isSMTP();
$mail->addAddress($data['testMail'] ?? '');
$mail->isHTML();
$mail->setSubject(__('This is a test email') . '-' . get_sys_config('site_name'));
$mail->Body = __('Congratulations, receiving this email means that your email service has been configured correctly');
$mail->send();
} catch (PHPMailerException) {
return $this->error($mail->ErrorInfo ?? '');
}
return $this->success(__('Test mail sent successfully~'));
}
}

View File

@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\security;
use app\common\controller\Backend;
use app\admin\model\DataRecycle as DataRecycleModel;
use Webman\Http\Request;
use support\Response;
class DataRecycle extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['update_time', 'create_time'];
protected array|string $quickSearchField = 'name';
protected function initController(Request $request): ?Response
{
$this->model = new DataRecycleModel();
return null;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withJoin($this->withJoinTable ?? [], $this->withJoinType ?? 'LEFT')
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data['controller_as'] = str_ireplace('.php', '', $data['controller'] ?? '');
$data['controller_as'] = strtolower(str_ireplace(['\\', '.'], '/', $data['controller_as']));
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('add')->check($data);
}
}
$result = $this->model->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Added successfully')) : $this->error(__('No rows were added'));
}
return $this->success('', [
'controllers' => $this->getControllerList(),
]);
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->post($pk) ?? $request->get($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data['controller_as'] = str_ireplace('.php', '', $data['controller'] ?? '');
$data['controller_as'] = strtolower(str_ireplace(['\\', '.'], '/', $data['controller_as']));
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('edit')->check(array_merge($data, [$pk => $row[$pk]]));
}
}
$result = $row->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Update successful')) : $this->error(__('No rows updated'));
}
return $this->success('', [
'row' => $row,
'controllers' => $this->getControllerList(),
]);
}
private function getControllerList(): array
{
$outExcludeController = [
'Addon.php',
'Ajax.php',
'Module.php',
'Terminal.php',
'Dashboard.php',
'Index.php',
'routine/AdminInfo.php',
'user/MoneyLog.php',
'user/ScoreLog.php',
];
$outControllers = [];
$controllers = get_controller_list();
foreach ($controllers as $key => $controller) {
if (!in_array($controller, $outExcludeController)) {
$outControllers[$key] = $controller;
}
}
return $outControllers;
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\security;
use ba\TableManager;
use support\think\Db;
use app\common\controller\Backend;
use app\admin\model\DataRecycleLog as DataRecycleLogModel;
use Webman\Http\Request;
use support\Response;
class DataRecycleLog extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = [];
protected array|string $quickSearchField = 'recycle.name';
protected array $withJoinTable = ['recycle', 'admin'];
protected function initController(Request $request): ?Response
{
$this->model = new DataRecycleLogModel();
return null;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function restore(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$ids = $request->post('ids', $request->get('ids', []));
$ids = is_array($ids) ? $ids : [];
$data = $this->model->where('id', 'in', $ids)->select();
if (!$data) {
return $this->error(__('Record not found'));
}
$count = 0;
$this->model->startTrans();
try {
foreach ($data as $row) {
$recycleData = json_decode($row['data'], true);
if (is_array($recycleData) && Db::connect(TableManager::getConnection($row->connection))->name($row->data_table)->insert($recycleData)) {
$row->delete();
$count++;
}
}
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $count ? $this->success(__('Restore successful')) : $this->error(__('No rows were restore'));
}
public function info(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->get($pk) ?? $request->post($pk);
$row = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->where('data_recycle_log.id', $id)
->find();
if (!$row) {
return $this->error(__('Record not found'));
}
$data = $this->jsonToArray($row['data']);
if (is_array($data)) {
foreach ($data as $key => $item) {
$data[$key] = $this->jsonToArray($item);
}
}
$row['data'] = $data;
return $this->success('', ['row' => $row]);
}
protected function jsonToArray(mixed $value = ''): mixed
{
if (!is_string($value)) {
return $value;
}
$data = json_decode($value, true);
if (($data && is_object($data)) || (is_array($data) && !empty($data))) {
return $data;
}
return $value;
}
}

View File

@@ -0,0 +1,182 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\security;
use app\common\controller\Backend;
use app\admin\model\SensitiveData as SensitiveDataModel;
use Webman\Http\Request;
use support\Response;
class SensitiveData extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['update_time', 'create_time'];
protected array|string $quickSearchField = 'controller';
protected function initController(Request $request): ?Response
{
$this->model = new SensitiveDataModel();
return null;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
$items = $res->items();
foreach ($items as $item) {
if ($item->data_fields) {
$fields = [];
foreach ($item->data_fields as $key => $field) {
$fields[] = $field ?: $key;
}
$item->data_fields = $fields;
}
}
return $this->success('', [
'list' => $items,
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() !== 'POST') {
return $this->success('', ['controllers' => $this->getControllerList()]);
}
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data['controller_as'] = str_ireplace('.php', '', $data['controller'] ?? '');
$data['controller_as'] = strtolower(str_ireplace(['\\', '.'], '/', $data['controller_as']));
if (is_array($data['fields'] ?? null)) {
$data['data_fields'] = [];
foreach ($data['fields'] as $field) {
$data['data_fields'][$field['name']] = $field['value'];
}
}
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('add')->check($data);
}
}
$result = $this->model->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Added successfully')) : $this->error(__('No rows were added'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->get($pk) ?? $request->post($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
if ($request->method() !== 'POST') {
return $this->success('', [
'row' => $row,
'controllers' => $this->getControllerList(),
]);
}
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data['controller_as'] = str_ireplace('.php', '', $data['controller'] ?? '');
$data['controller_as'] = strtolower(str_ireplace(['\\', '.'], '/', $data['controller_as']));
if (is_array($data['fields'] ?? null)) {
$data['data_fields'] = [];
foreach ($data['fields'] as $field) {
$data['data_fields'][$field['name']] = $field['value'];
}
}
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('edit')->check(array_merge($data, [$pk => $row[$pk]]));
}
}
$result = $row->save($data);
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Update successful')) : $this->error(__('No rows updated'));
}
private function getControllerList(): array
{
$outExcludeController = [
'Addon.php',
'Ajax.php',
'Dashboard.php',
'Index.php',
'Module.php',
'Terminal.php',
'auth/AdminLog.php',
'routine/AdminInfo.php',
'routine/Config.php',
'user/MoneyLog.php',
'user/ScoreLog.php',
];
$outControllers = [];
$controllers = get_controller_list();
foreach ($controllers as $key => $controller) {
if (!in_array($controller, $outExcludeController)) {
$outControllers[$key] = $controller;
}
}
return $outControllers;
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\security;
use ba\TableManager;
use support\think\Db;
use app\common\controller\Backend;
use app\admin\model\SensitiveDataLog as SensitiveDataLogModel;
use Webman\Http\Request;
use support\Response;
class SensitiveDataLog extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = [];
protected array|string $quickSearchField = 'sensitive.name';
protected array $withJoinTable = ['sensitive', 'admin'];
protected function initController(Request $request): ?Response
{
$this->model = new SensitiveDataLogModel();
return null;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
$items = $res->items();
foreach ($items as $item) {
$item->id_value = $item['primary_key'] . '=' . $item->id_value;
}
return $this->success('', [
'list' => $items,
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function info(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->get($pk) ?? $request->post($pk);
$row = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->where('security_sensitive_data_log.id', $id)
->find();
if (!$row) {
return $this->error(__('Record not found'));
}
return $this->success('', ['row' => $row]);
}
public function rollback(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$ids = $request->post('ids', $request->get('ids', []));
$ids = is_array($ids) ? $ids : [];
$data = $this->model->where('id', 'in', $ids)->select();
if (!$data) {
return $this->error(__('Record not found'));
}
$count = 0;
$this->model->startTrans();
try {
foreach ($data as $row) {
$conn = Db::connect(TableManager::getConnection($row->connection));
if ($conn->name($row->data_table)->where($row->primary_key, $row->id_value)->update([$row->data_field => $row->before])) {
$count++;
}
}
$this->model->commit();
} catch (\Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $count ? $this->success(__('Rollback successful')) : $this->error(__('No rows were rolled back'));
}
}

View File

@@ -0,0 +1,129 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\user;
use Throwable;
use app\admin\model\UserRule;
use app\admin\model\UserGroup;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class Group extends Backend
{
protected ?object $model = null;
protected array|string $preExcludeFields = ['update_time', 'create_time'];
protected array|string $quickSearchField = 'name';
protected function initController(Request $request): void
{
$this->model = new UserGroup();
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data = $this->handleRules($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('add')->check($data);
}
}
$result = $this->model->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Added successfully')) : $this->error(__('No rows were added'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->post($pk) ?? $request->get($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$data = $this->excludeFields($data);
$data = $this->handleRules($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('edit')->check(array_merge($data, [$pk => $row[$pk]]));
}
}
$result = $row->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Update successful')) : $this->error(__('No rows updated'));
}
$pidArr = UserRule::field('pid')->distinct(true)->where('id', 'in', $row->rules)->select()->toArray();
$rules = $row->rules ? explode(',', $row->rules) : [];
foreach ($pidArr as $item) {
$ruKey = array_search($item['pid'], $rules);
if ($ruKey !== false) {
unset($rules[$ruKey]);
}
}
$row->rules = array_values($rules);
return $this->success('', ['row' => $row]);
}
private function handleRules(array $data): array
{
if (isset($data['rules']) && is_array($data['rules']) && $data['rules']) {
$rules = UserRule::select();
$super = true;
foreach ($rules as $rule) {
if (!in_array($rule['id'], $data['rules'])) {
$super = false;
break;
}
}
$data['rules'] = $super ? '*' : implode(',', $data['rules']);
} else {
unset($data['rules']);
}
return $data;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\user;
use app\admin\model\User;
use app\admin\model\UserMoneyLog;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class MoneyLog extends Backend
{
protected ?object $model = null;
protected array $withJoinTable = ['user'];
protected array|string $preExcludeFields = ['create_time'];
protected array|string $quickSearchField = ['user.username', 'user.nickname'];
protected function initController(Request $request): void
{
$this->model = new UserMoneyLog();
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
return $this->_add();
}
$userId = $request->get('userId', $request->post('userId', 0));
$user = User::where('id', $userId)->find();
if (!$user) {
return $this->error(__("The user can't find it"));
}
return $this->success('', ['user' => $user]);
}
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\user;
use ba\Tree;
use app\common\controller\Backend;
use app\admin\model\UserRule;
use Webman\Http\Request;
use support\Response;
class Rule extends Backend
{
protected ?object $model = null;
protected Tree $tree;
protected array $initValue = [];
protected bool $assembleTree = true;
protected string $keyword = '';
protected array|string $preExcludeFields = ['create_time', 'update_time'];
protected array|string $defaultSortField = ['weigh' => 'desc'];
protected array|string $quickSearchField = 'title';
protected function initController(Request $request): void
{
$this->model = new UserRule();
$this->tree = Tree::instance();
$isTree = filter_var($request->get('isTree', $request->post('isTree', true)), FILTER_VALIDATE_BOOLEAN);
$this->initValue = $request->get('initValue', $request->post('initValue', []));
$this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : [];
$this->keyword = $request->get('quickSearch', $request->post('quickSearch', ''));
$this->assembleTree = $isTree && !$this->initValue;
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, , , ) = $this->queryBuilder();
$list = $this->model->where($where)->order($this->defaultSortField)->select();
if ($this->assembleTree) {
$list = $this->tree->assembleChild($list->toArray());
} else {
$list = $list->toArray();
}
return $this->success('', [
'list' => $list,
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->_add();
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->_edit();
}
public function del(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
return $this->_del();
}
public function select(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
list($where, , , ) = $this->queryBuilder();
$list = $this->model->where($where)->order($this->defaultSortField)->select();
if ($this->assembleTree) {
$list = $this->tree->assembleChild($list->toArray());
} else {
$list = $list->toArray();
}
return $this->success('', ['list' => $list]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\user;
use app\admin\model\User;
use app\admin\model\UserScoreLog;
use app\common\controller\Backend;
use Webman\Http\Request;
use support\Response;
class ScoreLog extends Backend
{
protected ?object $model = null;
protected array $withJoinTable = ['user'];
protected array|string $preExcludeFields = ['create_time'];
protected array|string $quickSearchField = ['user.username', 'user.nickname'];
protected function initController(Request $request): void
{
$this->model = new UserScoreLog();
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() === 'POST') {
return $this->_add();
}
$userId = $request->get('userId', $request->post('userId', 0));
$user = User::where('id', $userId)->find();
if (!$user) {
return $this->error(__("The user can't find it"));
}
return $this->success('', ['user' => $user]);
}
}

View File

@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\user;
use Throwable;
use app\common\controller\Backend;
use app\admin\model\User as UserModel;
use Webman\Http\Request;
use support\Response;
class User extends Backend
{
protected ?object $model = null;
protected array $withJoinTable = ['userGroup'];
protected array|string $preExcludeFields = ['last_login_time', 'login_failure', 'password', 'salt'];
protected array|string $quickSearchField = ['username', 'nickname', 'id'];
protected function initController(Request $request): void
{
$this->model = new UserModel();
}
public function index(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->get('select') || $request->post('select')) {
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withoutField('password,salt')
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
public function add(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
$passwd = $data['password'] ?? '';
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
if ($this->modelSceneValidate) $validate->scene('add');
$validate->check($data);
}
}
$result = $this->model->save($data);
$this->model->commit();
if ($passwd) {
$this->model->resetPassword($this->model->id, $passwd);
}
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Added successfully')) : $this->error(__('No rows were added'));
}
public function edit(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
$pk = $this->model->getPk();
$id = $request->post($pk) ?? $request->get($pk);
$row = $this->model->find($id);
if (!$row) {
return $this->error(__('Record not found'));
}
$dataLimitAdminIds = $this->getDataLimitAdminIds();
if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
return $this->error(__('You have no permission'));
}
if ($request->method() === 'POST') {
$data = $request->post();
if (!$data) {
return $this->error(__('Parameter %s can not be empty', ['']));
}
if (!empty($data['password'])) {
$this->model->resetPassword($row->id, $data['password']);
}
$data = $this->excludeFields($data);
$result = false;
$this->model->startTrans();
try {
if ($this->modelValidate) {
$validateClass = str_replace("\\model\\", "\\validate\\", get_class($this->model));
if (class_exists($validateClass)) {
$validate = new $validateClass();
$validate->scene('edit')->check(array_merge($data, [$pk => $row[$pk]]));
}
}
$result = $row->save($data);
$this->model->commit();
} catch (Throwable $e) {
$this->model->rollback();
return $this->error($e->getMessage());
}
return $result !== false ? $this->success(__('Update successful')) : $this->error(__('No rows updated'));
}
unset($row['password'], $row['salt']);
$row['password'] = '';
return $this->success('', ['row' => $row]);
}
public function select(Request $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) return $response;
list($where, $alias, $limit, $order) = $this->queryBuilder();
$res = $this->model
->withoutField('password,salt')
->withJoin($this->withJoinTable, $this->withJoinType)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
]);
}
}