Browse Source

feat(piadmin): 新增用户菜单管理功能

- 添加用户菜单控制器、模型、服务及数据访问对象
- 实现菜单的增删改查及权限获取接口
- 在路由中注册用户菜单相关接口地址
- 支持菜单树形结构展示与层级路径管理
- 增加菜单编码唯一性校验和父子关系处理逻辑
- 提供批量删除及其子菜单的功能实现
- 完善菜单数据软删除和事务回滚机制
master
zhangf@suq.cn 7 days ago
parent
commit
45ce3c92d1
  1. 96
      plugin/piadmin/app/controller/v1/UserMenuController.php
  2. 17
      plugin/piadmin/app/dao/UserMenuDao.php
  3. 16
      plugin/piadmin/app/model/UserMenu.php
  4. 17
      plugin/piadmin/app/route/v1/route.php
  5. 223
      plugin/piadmin/app/service/UserMenuService.php

96
plugin/piadmin/app/controller/v1/UserMenuController.php

@ -0,0 +1,96 @@
<?php
namespace plugin\piadmin\app\controller\v1;
use plugin\piadmin\app\base\BaseController;
use plugin\piadmin\app\service\UserMenuService;
use plugin\piadmin\app\validate\MenuValidate;
use support\Request;
class UserMenuController extends BaseController
{
// 无需登录方法
protected $noNeedLogin = [];
public function __construct()
{
parent::__construct();
$this->service = app()->make(UserMenuService::class);
}
public function index(Request $request)
{
$all = $request->all();
$list = $this->service->getMenuList($all);
return success($list);
}
public function save(Request $request)
{
$params = requestOnly([
'pid' => 0,
'name' => '',
'code' => '',
'icon' => '',
'route' => '',
'component' => '',
'redirect' => '',
'is_hidden' => 2,
'is_layout' => 2,
'type' => '',
'status' => 1,
'sort' => 1000,
'remark' => ''
]);
validate(MenuValidate::class)->scene('save')->check($params);
$res = $this->service->saveData($params);
return success($res);
}
public function update(Request $request)
{
$params = requestOnly([
'id' => '',
'pid' => 0,
'name' => '',
'code' => '',
'icon' => '',
'route' => '',
'component' => '',
'redirect' => '',
'is_hidden' => 2,
'is_layout' => 2,
'type' => '',
'status' => 1,
'sort' => 1000,
'remark' => ''
]);
validate(MenuValidate::class)->scene('update')->check($params);
$res = $this->service->updateData($params);
return success($res);
}
public function delete(Request $request)
{
$ids = $request->input('ids');
$res = $this->service->deleteData($ids);
return success($res);
}
public function read(Request $request)
{
$id = $request->input('id');
$res = $this->service->readData($id);
return success($res);
}
public function getUserMenuPermissions()
{
return success($this->service->getUserMenu());
}
}

17
plugin/piadmin/app/dao/UserMenuDao.php

@ -0,0 +1,17 @@
<?php
namespace plugin\piadmin\app\dao;
use plugin\piadmin\app\base\BaseDao;
use plugin\piadmin\app\model\UserMenu;
class UserMenuDao extends BaseDao
{
protected function setModel(): string
{
return UserMenu::class;
}
}

16
plugin/piadmin/app/model/UserMenu.php

@ -0,0 +1,16 @@
<?php
namespace plugin\piadmin\app\model;
use plugin\piadmin\app\base\BaseModel;
use think\model\concern\SoftDelete;
class UserMenu extends BaseModel
{
protected $table = 'pi_user_menu';
use SoftDelete;
protected string $deleteTime = 'delete_time';
protected $defaultSoftDelete = 0;
}

17
plugin/piadmin/app/route/v1/route.php

@ -8,6 +8,7 @@ use plugin\piadmin\app\controller\v1\SystemAdminController;
use plugin\piadmin\app\controller\v1\SystemDeptController;
use plugin\piadmin\app\controller\v1\SystemRoleController;
use plugin\piadmin\app\controller\v1\UserLoginController;
use plugin\piadmin\app\controller\v1\UserMenuController;
use plugin\piadmin\app\middleware\AdminAuthorizationMiddleware;
use plugin\piadmin\app\middleware\PermissionsMiddleware;
use Webman\Route;
@ -21,6 +22,13 @@ Route::group('/piadmin/v1', function () {
Route::get('/captcha', [LoginController::class, 'captcha'])->setParams(['perm' => ['captcha']]);
});
Route::group('/piadmin/v1', function () {
// 获取当前用户菜单权限
Route::get('/user/getUserMenuPermissions', [UserMenuController::class, 'getUserMenuPermissions'])->setParams(['perm' => ['getUserMenuPermissions']]);
})->middleware([
\plugin\piadmin\app\middleware\UserAuthorizationMiddleware::class,
]);;
// 需要登录的接口
Route::group('/piadmin/v1', function () {
// 获取当前用户菜单权限
@ -35,6 +43,15 @@ Route::group('/piadmin/v1', function () {
Route::post('/delete', [MenuController::class, 'delete'])->setParams(['perm' => ['menuDelete']]);
});
// 用户菜单
Route::group('/userMenu', function () {
Route::get('/index', [UserMenuController::class, 'index'])->setParams(['perm' => ['userMenuIndex']]);
Route::post('/save', [UserMenuController::class, 'save'])->setParams(['perm' => ['userMenuSave']]);
Route::post('/update', [UserMenuController::class, 'update'])->setParams(['perm' => ['userMenuUpdate']]);
Route::get('/read', [UserMenuController::class, 'read'])->setParams(['perm' => ['userMenuRead']]);
Route::post('/delete', [UserMenuController::class, 'delete'])->setParams(['perm' => ['userMenuDelete']]);
});
// 角色
Route::group('/role', function () {
Route::post('/save', [SystemRoleController::class, 'save'])->setParams(['perm' => ['roleSave']]);

223
plugin/piadmin/app/service/UserMenuService.php

@ -0,0 +1,223 @@
<?php
namespace plugin\piadmin\app\service;
use plugin\piadmin\app\base\BaseService;
use plugin\piadmin\app\dao\UserMenuDao;
use plugin\piadmin\app\exception\ApiException;
use plugin\piadmin\app\utils\AdminTokenUtils;
use plugin\piadmin\app\utils\IdPathUtils;
use plugin\piadmin\app\utils\IdUtils;
use plugin\piadmin\app\utils\TreeUtils;
use support\Log;
use think\facade\Db;
class UserMenuService extends BaseService
{
public function __construct(UserMenuDao $dao)
{
$this->dao = $dao;
}
public function getMenuList($params = [])
{
$where = [];
$list = $this->dao->getList($where, '*', 0, 0, 'sort asc');
return TreeUtils::toTree($list);
}
/**
* 保存数据
* @param $params
* @return \plugin\piadmin\app\base\BaseModel
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function saveData($params)
{
// 检查code
$codeExist = $this->dao->be([
'code'=>$params['code']
]);
if ($codeExist) {
throw new ApiException(trans(401003));
}
$parentMenu = $this->getMenu($params['pid']);
if (empty($parentMenu)) {
throw new ApiException(trans(401001));
}
$newUuid = IdUtils::uuid();
$newIdPath = IdPathUtils::addItem($parentMenu['id_path'], $newUuid);
$params['id_path'] = $newIdPath;
$params['uuid'] = $newUuid;
return $this->dao->save($params);
}
/**
* 更新数据
* @param $params
* @return \plugin\piadmin\app\base\BaseModel
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function updateData($params)
{
$menu = $this->dao->get($params['id']);
if (empty($menu)) {
throw new ApiException(trans(401005));
}
if($params['pid'] == $menu['id']){
throw new ApiException(trans(401008));
}
// code有没有变化
if ($params['code'] != $menu['code']) {
$codeExist = $this->dao->be([
'code'=>$params['code']
]);
if ($codeExist) {
throw new ApiException(trans(401003));
}
}
$newIdPath = '';
// 上级菜单有没有变化
if ($params['pid'] != $menu['pid']) {
$parentMenu = $this->getMenu($params['pid']);
if (empty($parentMenu)) {
throw new ApiException(trans(401001));
}
$newIdPath = IdPathUtils::addItem($parentMenu['id_path'], $menu['id']);
$params['id_path'] = $newIdPath;
}
unset($params['id']);
// 落库
Db::startTrans();
try {
$this->dao->update(['id' => $menu['id']], $params);
if (!empty($newIdPath)) {
$this->updateAllChildrenIdPath($menu['id_path'], $newIdPath);
}
Db::commit();
} catch (\Exception $exception) {
Db::rollback();
Log::error($exception->getTraceAsString());
throw new ApiException($exception->getMessage());
}
return $params;
}
/**
* 删除数据
* @param mixed $id
* @return array
*/
public function deleteData($ids)
{
//根据ids获取所有子菜单
$ids = $this->getChildrenMenuIdsBatch($ids);
// 落库
Db::startTrans();
try {
foreach ($ids as $id) {
$this->dao->softDel($id);
}
Db::commit();
} catch (\Exception $exception) {
Db::rollback();
Log::error($exception->getTraceAsString());
throw new ApiException($exception->getMessage());
}
return [];
}
/**
* 获取数据
* @param mixed $id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function readData(mixed $id)
{
$menu = $this->dao->get(['id' => $id]);
if (empty($menu)) {
return [];
}
return $menu->toArray();
}
public function getUserMenu()
{
$list = $this->dao->getList([], '*', 0, 0, 'sort asc');
return TreeUtils::toTree($list);
}
// ============================================================ 私有方法 ===============================================
/**
* 获取菜单
* @param $id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function getMenu($id): array
{
if ($id == 0) {
return [
'id' => 0,
'uuid' => 0,
'name' => '顶级菜单',
'code' => 'top',
'type' => 'M',
'status' => 1,
'sort' => 1000,
'pid' => 0,
'id_path' => '0',
];
}
$menu = $this->dao->get(['id' => $id]);
if (empty($menu)) {
return [];
}
return $menu->toArray();
}
private function updateAllChildrenIdPath($oldIdPath, $newIdPath)
{
// 获取所有子级
$childrenIds = $this->dao->getColumn([
['id_path', 'like', $oldIdPath . ',%']
], 'id');
$children = $this->dao->getList([
['id', 'in', $childrenIds]
], 'id, id_path');
foreach ($children as $child) {
$newIdPath = str_replace($oldIdPath, $newIdPath, $child['id_path']);
$this->dao->update(['id' => $child['id']], [
'id_path' => $newIdPath
]);
}
}
public function getChildrenMenuIdsBatch(array $ids): array
{
$allMenus = $this->dao->getList([], 'id, pid');
$allChildrenIds = [];
// 批量处理所有ID的子节点查找
foreach ($ids as $id) {
$childIds = TreeUtils::getChildrenIds($allMenus, $id);
$allChildrenIds = array_merge($allChildrenIds, $childIds);
}
return array_values(array_unique($allChildrenIds)); // 重新索引并去重
}
}
Loading…
Cancel
Save