Files
orico-official-website/app/admin/controller/v1/Menu.php

484 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\exception\InvalidOperateException;
use app\admin\model\v1\SysMenuAbilityPermissionModel;
use app\admin\model\v1\SysMenuModel;
use app\admin\validate\v1\SysMenuValidate;
use think\facade\Db;
/**
* 菜单控制器
*/
class Menu
{
// 菜单列表
public function list()
{
$params = request()->param(['title' => '']);
$menus = SysMenuModel::field([
'id',
'pid',
'title'
])
->with(['menu_ability_permission'])
->withSearch(['title'], [
'title' => $params['title'] ?? null
])
->enabled()
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->hidden(['menu_ability_permission.menu_id']);
return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
}
// 分页数据
public function index()
{
$params = request()->param(['title' => '']);
$menus = SysMenuModel::field([
'id',
'pid',
'icon',
'title',
'sort',
'hidden',
'status'
])
->withSearch(['title'], [
'title' => $params['title'] ?? null
])
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
}
// 详情数据
public function read()
{
$id = request()->param('id');
$menu = SysMenuModel::withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->with(['menu_ability_permission'])
->bypk($id)
->find()
->hidden(['menu_ability_permission.menu_id']);
if (empty($menu)) {
return error('菜单不存在');
}
return success('获取成功', $menu);
}
// 新增数据
public function save()
{
$post = request()->post([
'pid' => 0,
'title',
'name',
'path',
'icon',
'redirect',
'component',
'hidden' => 0,
'actived' => 0,
'keep_alive' => 0,
'sort' => 0,
'status' => 1,
// $[*].ability_name 能力名称
// $[*].permission 权限标志
// $[*].sort 排序
'menu_ability_permission' => '[]'
]);
$menu_ability_permission = [];
if (!empty($post['menu_ability_permission'])) {
$menu_ability_permission = json_decode($post['menu_ability_permission'], true);
unset($post['menu_ability_permission']);
}
$validate = new SysMenuValidate;
$check_data = array_merge($post, ['menu_ability_permission' => $menu_ability_permission]);
if (!$validate->scene('create')->check($check_data)) {
return error($validate->getError());
}
SysMenuModel::startTrans();
try {
// 新增菜单
$menu = SysMenuModel::create($post);
if ($menu->isEmpty()) {
throw new InvalidOperateException('新增菜单失败');
}
// 新增菜单能力权限
if (!empty($menu_ability_permission)) {
foreach ($menu_ability_permission as &$item) {
$item['menu_id'] = $menu->id;
}
unset($item);
$permission = $menu->menuAbilityPermission()->saveAll($menu_ability_permission);
if (empty($permission)) {
throw new InvalidOperateException('新增菜单能力权限失败');
}
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
return error('操作失败');
}
return success('操作成功');
}
// 更新数据
public function update()
{
$id = request()->param('id');
$put = request()->put([
'pid' => 0,
'title',
'name',
'path',
'icon',
'redirect',
'component',
'hidden' => 0,
'actived' => 0,
'keep_alive' => 0,
'sort' => 0,
'status' => 1,
// $[*].ability_name 能力名称
// $[*].permission 权限标志
// $[*].sort 排序
'menu_ability_permission' => '[]'
]);
$menu_ability_permission = [];
if (!empty($put['menu_ability_permission'])) {
$menu_ability_permission = json_decode($put['menu_ability_permission'], true);
unset($put['menu_ability_permission']);
}
$validate = new SysMenuValidate;
$check_data = array_merge($put, ['id' => $id, 'menu_ability_permission' => $menu_ability_permission]);
if (!$validate->check($check_data)) {
return error($validate->getError());
}
SysMenuModel::startTrans();
try {
$menu = SysMenuModel::bypk($id)->find();
if ($menu->isEmpty()) {
throw new InvalidOperateException('请确认操作对象是否存在');
}
// 更新菜单
if (!$menu->save($put)) {
throw new InvalidOperateException('更新菜单失败');
}
// 更新菜单能力权限
$original_ability = SysMenuAbilityPermissionModel::menuId($menu->id)->select();
if (!$original_ability->isEmpty()) {
if (!$original_ability->delete()) {
throw new InvalidOperateException('删除旧菜单能力权限失败');
}
}
if (!empty($menu_ability_permission)) {
foreach ($menu_ability_permission as &$item) {
$item['menu_id'] = $menu->id;
}
unset($item);
$permission = (new SysMenuAbilityPermissionModel)->saveAll($menu_ability_permission);
if (empty($permission)) {
throw new InvalidOperateException('更新菜单能力权限失败');
}
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
return error('操作失败');
}
return success('操作成功');
}
// 导入数据
public function import()
{
// 获取上传文件
$file = request()->file('file');
if (empty($file)) {
return error('请上传文件');
}
// 读取文件
$keys_map = [
'A' => 'id',
'B' => 'pid',
'C' => 'title',
'D' => 'name',
'E' => 'path',
'F' => 'icon',
'G' => 'redirect',
'H' => 'component',
'I' => 'hidden',
'J' => 'actived',
'K' => 'keep_alive',
'L' => 'sort',
'M' => 'status',
'N' => 'menu_ability_permission'
];
$xlsx_data = xlsx_reader($file->getRealPath(), 2, $keys_map);
if (empty($xlsx_data)) {
return success('操作成功');
}
// 处理行序号,防止后续被打乱
foreach ($xlsx_data as $row => &$item) {
$item['seq_no'] = $row;
}
unset($item);
// 数据组装成树形结构,方便处理上下级关系
$xlsx_data_tree = array_to_tree($xlsx_data, 0, 'pid');
// 处理导入菜单数据
$handle_errors = $this->handleImportData($xlsx_data_tree);
if (!empty($handle_errors)) {
return error(implode(PHP_EOL, $handle_errors));
}
return success('操作成功');
}
// 处理导入数据
private function handleImportData($menus)
{
$errors = [];
$menu_model = new SysMenuModel;
$chunks = array_chunk($menus, 100, true);
foreach ($chunks as $chunk) {
// 执行保存
if (!$this->executeReplaceMenu($chunk)) {
$errors[] = sprintf('第【%s】行保存失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
// 获取该批次菜单以获取新增情况的菜单的id
$menus_map = $menu_model->whereIn('name', array_column($chunk, 'name'))->column('id', 'name');
foreach ($chunk as &$it) {
// 更改菜单的子菜单pid值为新值
if (!empty($it['children'])) {
foreach ($it['children'] as &$child) {
if (isset($menus_map[$child['name']])) {
$child['pid'] = $menus_map[$child['name']];
}
}
unset($child);
}
// 更改菜单能力权限的menu_id
if (!empty($it['menu_ability_permission'])) {
$menu_ability_permission = json_decode($it['menu_ability_permission'], true);
foreach ($menu_ability_permission as &$permission) {
if (isset($menus_map[$it['name']])) {
$permission['menu_id'] = $menus_map[$it['name']];
}
}
unset($permission);
$it['menu_ability_permission'] = $menu_ability_permission;
}
}
unset($it);
// 处理子菜单
$childrens = array_reduce(array_column($chunk, 'children'), 'array_merge', []);
$handle_errors = $this->handleImportData($childrens);
if (!empty($handler_ret)) {
$errors = array_merge($errors, $handle_errors);
}
// 更新菜单能力权限
$menu_ability_permissions = array_reduce(array_column($chunk, 'menu_ability_permission'), 'array_merge', []);
if (!empty($menu_ability_permissions)) {
// 菜单能力权限模型实例
$ability_permission_model = new SysMenuAbilityPermissionModel;
// 删除旧的菜单能力权限
$menus_id = array_column($menu_ability_permissions, 'menu_id');
$exists = $ability_permission_model->whereIn('menu_id', $menus_id)->count();
if ($exists) {
$deleted = $ability_permission_model->whereIn('menu_id', $menus_id)->delete();
if (!$deleted) {
$errors[] = sprintf('第【%s】行删除旧菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
}
// 保存新的菜单能力权限
$ability_updated = $ability_permission_model->saveAll($menu_ability_permissions);
if ($ability_updated->isEmpty()) {
$errors[] = sprintf('第【%s】行保存新菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
}
}
return $errors;
}
// 组装repalce语句的values
private function buildReplaceMenuValues($menu)
{
return sprintf(
'(%d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, %d, %d, %d, %d)',
$menu['id'],
$menu['pid'],
$menu['title'],
$menu['name'],
$menu['path'],
$menu['icon'],
$menu['redirect'],
$menu['component'],
$menu['hidden'],
$menu['actived'],
$menu['keep_alive'],
$menu['sort'],
$menu['status']
);
}
// 执行repalce语句
private function executeReplaceMenu($menus)
{
// 获取已存在的菜单
$menus_map = SysMenuModel::whereIn('name', array_column($menus, 'name'))->column('id', 'name');
// 组装保存sql语句
$values = [];
foreach ($menus as &$it) {
if (isset($menus_map[$it['name']])) {
$it['id'] = $menus_map[$it['name']];
}
if ($it['level'] == 1) {
$it['pid'] = 0;
}
$values[] = $this->buildReplaceMenuValues($it);
}
unset($it);
return Db::execute(
sprintf(
'REPLACE INTO %s (
`id`,
`pid`,
`title`,
`name`,
`path`,
`icon`,
`redirect`,
`component`,
`hidden`,
`actived`,
`keep_alive`,
`sort`,
`status`
) VALUES %s;',
(new SysMenuModel)->getTable(),
implode(',', $values)
)
);
}
// 导出数据
public function export()
{
$schema = [
'id' => '菜单ID',
'pid' => '父级ID',
'title' => '菜单标题',
'name' => '菜单名称',
'path' => '菜单路径',
'icon' => '菜单图标',
'redirect' => '菜单重定向路径',
'component' => '菜单组件路径',
'hidden' => '是否隐藏',
'actived' => '是否激活',
'keep_alive' => '是否缓存',
'sort' => '排序',
'status' => '状态',
'menu_ability_permission' => '菜单能力权限'
];
// 获取导出数据
$data = $this->getExportMenuData();
// 导出
xlsx_writer($data, $schema)->save('php://output');
}
private function getExportMenuData()
{
$param = request()->param(['title' => '']);
$menus = SysMenuModel::withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->with(['menu_ability_permission'])
->withSearch(['title'], [
'title' => $param['title'] ?? null
])
->order(['id' => 'asc'])
->select()
->hidden(['menu_ability_permission.menu_id']);
if (!$menus->isEmpty()) {
$menus->each(function ($item) {
$item->menu_ability_permission = json_encode($item->menu_ability_permission);
});
}
return $menus;
}
// 删除数据
public function delete()
{
$id = request()->param('id');
SysMenuModel::startTrans();
try {
$menu = SysMenuModel::bypk($id)->find();
if ($menu->isEmpty()) {
throw new InvalidOperateException('请确认操作对象是否存在');
}
if (!$menu->delete()) {
throw new InvalidOperateException('删除菜单失败');
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
return error('操作失败');
}
return success('操作成功');
}
}