文件还在测试中

This commit is contained in:
2026-08-08 15:53:53 +08:00
parent 5f1e7adb64
commit 8101177cb5
241 changed files with 18074 additions and 22 deletions
+256
View File
@@ -0,0 +1,256 @@
<?php
namespace App\Controllers\Subsys;
use App\Controllers\Controller;
use App\Models\AdminUser;
use Core\Db;
/**
* 分系统用户管理(CRM/PSI 共用)。
* 仅该系统管理员可访问:管理「属于本系统的用户」及其页面权限。
* 普通用户仅能看到/访问自己被授权的页面(由 subsys_page_can 控制)。
*/
abstract class UsersController extends Controller
{
/** 子类返回 'crm' | 'psi' */
abstract protected function sys(): string;
private function sysName(): string
{
return $this->sys() === 'psi' ? 'PSI 进销存' : 'CRM 客户管理';
}
private function nav(): array
{
return \subsys_nav($this->sys());
}
public function index()
{
$sys = $this->sys();
$other = $sys === 'crm' ? 'psi' : 'crm';
$col = $sys . '_role';
$rows = Db::query("SELECT * FROM admin_users WHERE `{$col}` != 'none' ORDER BY id ASC")->fetchAll();
// 子系统管理员仅管理「本系统的用户」,严格执行边界隔离:
// - 不可越权操作后台超管 / 全局管理员;
// - 不可跨界看到 / 管理另一系统(CRM 看不到 PSI,PSI 看不到 CRM)。
if (admin_role() !== 'super_admin') {
$rows = array_filter($rows, static function ($r) use ($other) {
// 排除后台超管 / 全局管理员
if (in_array($r['role'] ?? 'none', ['super_admin', 'admin'], true)) return false;
// 排除另一系统的管理员(防跨界)
if (($r[$other . '_role'] ?? 'none') === 'admin') return false;
return true;
});
}
return $this->renderSubsys($sys, 'subsys/users', [
'users' => $rows,
'sysName' => $this->sysName(),
'pages' => \subsys_pages($sys),
'sys' => $sys,
], $this->nav(), 'users');
}
public function create()
{
$sys = $this->sys();
$user = [
'id' => 0, 'username' => '', 'name' => '', 'status' => 1,
$sys . '_role' => 'user',
$sys . '_perms' => '',
];
return $this->renderSubsys($sys, 'subsys/user_form', [
'user' => $user,
'sysName'=> $this->sysName(),
'pages' => \subsys_pages($sys),
'sys' => $sys,
'edit' => false,
], $this->nav(), 'users');
}
public function store()
{
$sys = $this->sys();
$upper = strtoupper($sys);
if (!csrf_check()) {
$this->flash('表单已过期,请重试', 'err');
return $this->redirect($upper . '/users/create');
}
$username = trim($this->post('username'));
$name = trim($this->post('name'));
$pwd = $this->post('password');
$role = $this->post($sys . '_role') === 'admin' ? 'admin' : 'user';
$perms = $this->collectPerms($sys);
$status = $this->post('status') === '0' ? 0 : 1;
if ($username === '' || $pwd === '') {
$this->flash('用户名和密码不能为空', 'err');
return $this->redirect($upper . '/users/create');
}
if (!preg_match('/^[a-zA-Z0-9_]{3,30}$/', $username)) {
$this->flash('账号须为 3-30 位字母/数字/下划线', 'err');
return $this->redirect($upper . '/users/create');
}
if (strlen($pwd) < 6) {
$this->flash('密码至少 6 位', 'err');
return $this->redirect($upper . '/users/create');
}
if ((new AdminUser())->byUsername($username)) {
$this->flash('用户名已存在', 'err');
return $this->redirect($upper . '/users/create');
}
(new AdminUser())->insert([
'username' => $username,
'password' => password_hash($pwd, PASSWORD_DEFAULT),
'name' => $name,
'role' => 'none', // 子系统账号:全局后台角色为 none
$sys . '_role' => $role,
$sys . '_perms' => json_encode($perms, JSON_UNESCAPED_UNICODE),
'status' => $status,
'created_at' => date('Y-m-d H:i:s'),
]);
$this->flash('用户已创建', 'ok');
return $this->redirect($upper . '/users');
}
public function edit($id)
{
$sys = $this->sys();
$user = (new AdminUser())->find($id);
if (!$user || !$this->manageable($user)) {
return $this->redirect(strtoupper($sys) . '/users');
}
return $this->renderSubsys($sys, 'subsys/user_form', [
'user' => $user,
'sysName'=> $this->sysName(),
'pages' => \subsys_pages($sys),
'sys' => $sys,
'edit' => true,
], $this->nav(), 'users');
}
public function update($id)
{
$sys = $this->sys();
$upper = strtoupper($sys);
if (!csrf_check()) {
$this->flash('表单已过期,请重试', 'err');
return $this->redirect($upper . '/users');
}
$user = (new AdminUser())->find($id);
if (!$user || !$this->manageable($user)) {
$this->flash('无权操作该用户', 'err');
return $this->redirect($upper . '/users');
}
$username = trim($this->post('username'));
$name = trim($this->post('name'));
$pwd = $this->post('password');
$role = $this->post($sys . '_role') === 'admin' ? 'admin' : 'user';
$perms = $this->collectPerms($sys);
$status = $this->post('status') === '0' ? 0 : 1;
if ($username === '' || !preg_match('/^[a-zA-Z0-9_]{3,30}$/', $username)) {
$this->flash('账号不合法', 'err');
return $this->redirect($upper . '/users/edit/' . $id);
}
$existing = (new AdminUser())->byUsername($username);
if ($existing && (int)$existing['id'] !== (int)$id) {
$this->flash('用户名已存在', 'err');
return $this->redirect($upper . '/users/edit/' . $id);
}
if ($pwd !== '' && strlen($pwd) < 6) {
$this->flash('密码至少 6 位', 'err');
return $this->redirect($upper . '/users/edit/' . $id);
}
$data = [
'username' => $username,
'name' => $name,
$sys . '_role' => $role,
$sys . '_perms' => json_encode($perms, JSON_UNESCAPED_UNICODE),
'status' => $status,
];
if ($pwd !== '') {
$data['password'] = password_hash($pwd, PASSWORD_DEFAULT);
}
(new AdminUser())->update($id, $data);
$this->flash('用户已更新', 'ok');
return $this->redirect($upper . '/users');
}
public function destroy($id)
{
$sys = $this->sys();
$upper = strtoupper($sys);
if (!csrf_check()) {
$this->flash('操作已失效,请重试', 'err');
return $this->redirect($upper . '/users');
}
$user = (new AdminUser())->find($id);
if (!$user || !$this->manageable($user) || (int)$user['id'] === (int)admin_uid()) {
$this->flash('无法删除该用户', 'err');
return $this->redirect($upper . '/users');
}
(new AdminUser())->delete($id);
$this->flash('用户已删除', 'ok');
return $this->redirect($upper . '/users');
}
/** 管理员为该用户重置密码 */
public function reset($id)
{
$sys = $this->sys();
$upper = strtoupper($sys);
$user = (new AdminUser())->find($id);
if (!$user || !$this->manageable($user)) {
$this->flash('无权操作该用户', 'err');
return $this->redirect($upper . '/users');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!csrf_check()) {
$this->flash('表单已过期,请重试', 'err');
return $this->redirect($upper . '/users/reset/' . $id);
}
$pwd = $this->post('password');
if ($pwd === '' || strlen($pwd) < 6) {
$this->flash('密码至少 6 位', 'err');
return $this->redirect($upper . '/users/reset/' . $id);
}
(new AdminUser())->update($id, ['password' => password_hash($pwd, PASSWORD_DEFAULT)]);
$this->flash('密码已重置', 'ok');
return $this->redirect($upper . '/users');
}
return $this->renderSubsys($sys, 'subsys/user_reset', [
'user' => $user,
'sysName'=> $this->sysName(),
'sys' => $sys,
], $this->nav(), 'users');
}
/** 收集页面权限:返回 {page: bool} 形式(与后台 UserController 一致,登录时按 JSON 解码) */
private function collectPerms(string $sys): array
{
$out = [];
foreach (\subsys_pages($sys) as $p) {
if ($p === 'dashboard') continue;
$out[$p] = $this->post('perm_' . $p) ? true : false;
}
return $out;
}
/** 当前登录管理员是否可管理该目标用户(严格边界隔离:只管本系统用户,禁止跨界) */
private function manageable(array $target): bool
{
if (admin_role() === 'super_admin') return true;
$sys = $this->sys();
$other = $sys === 'crm' ? 'psi' : 'crm';
// 不能管理后台超管 / 全局管理员
if (in_array($target['role'] ?? 'none', ['super_admin', 'admin'], true)) return false;
// 只能管理本系统用户
if (($target[$sys . '_role'] ?? 'none') === 'none') return false;
// 不能跨界管理另一系统的管理员(CRM 管不了 PSI,PSI 管不了 CRM
if (($target[$other . '_role'] ?? 'none') === 'admin') return false;
return true;
}
}