Browse Source
feat(piadmin): 新增用户菜单管理功能
feat(piadmin): 新增用户菜单管理功能
- 添加用户菜单控制器、模型、服务及数据访问对象 - 实现菜单的增删改查及权限获取接口 - 在路由中注册用户菜单相关接口地址 - 支持菜单树形结构展示与层级路径管理 - 增加菜单编码唯一性校验和父子关系处理逻辑 - 提供批量删除及其子菜单的功能实现 - 完善菜单数据软删除和事务回滚机制master
5 changed files with 369 additions and 0 deletions
-
96plugin/piadmin/app/controller/v1/UserMenuController.php
-
17plugin/piadmin/app/dao/UserMenuDao.php
-
16plugin/piadmin/app/model/UserMenu.php
-
17plugin/piadmin/app/route/v1/route.php
-
223plugin/piadmin/app/service/UserMenuService.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()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
@ -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)); // 重新索引并去重
|
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue