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