rentease-backend-new/controllers/menuController.js

456 lines
12 KiB
JavaScript

const { Op } = require('sequelize');
const Menu = require('../models/Menu');
const RoleMenu = require('../models/RoleMenu');
const { logOperation } = require('../utils/logger');
const response = require('../utils/response');
// 获取菜单树
exports.getMenuTree = async (req, res) => {
try {
const { type, status } = req.query;
const userType = req.user.userType;
const where = { isDeleted: false };
if (type) {
where.type = type;
}
if (status) {
where.status = status;
}
// 普通管理员只能获取基础菜单
if (userType === 'tenant_admin') {
where.isBasic = 1;
}
const menus = await Menu.findAll({
where,
order: [['sort', 'ASC'], ['createTime', 'ASC']]
});
const menuTree = buildTree(menus);
response.success(res, '获取成功', menuTree);
} catch (error) {
console.error('获取菜单树失败:', error);
response.serverError(res, '获取菜单树失败', error);
}
};
// 获取菜单列表(平铺)
exports.getMenuList = async (req, res) => {
try {
const { page = 1, pageSize = 10, name, type, status } = req.query;
const where = { isDeleted: false };
if (name) {
where.name = { [Op.like]: `%${name}%` };
}
if (type) {
where.type = type;
}
if (status) {
where.status = status;
}
const { count, rows } = await Menu.findAndCountAll({
where,
order: [['sort', 'ASC'], ['createTime', 'ASC']],
offset: (page - 1) * pageSize,
limit: parseInt(pageSize)
});
response.success(res, '获取成功', {
list: rows,
total: count,
page: parseInt(page),
pageSize: parseInt(pageSize)
});
} catch (error) {
console.error('获取菜单列表失败:', error);
response.serverError(res, '获取菜单列表失败', error);
}
};
// 获取菜单详情
exports.getMenuById = async (req, res) => {
try {
const { id } = req.params;
const menu = await Menu.findByPk(id, {
where: { isDeleted: false }
});
if (!menu) {
return response.notFound(res, '菜单不存在');
}
response.success(res, '获取成功', menu);
} catch (error) {
console.error('获取菜单详情失败:', error);
response.serverError(res, '获取菜单详情失败', error);
}
};
// 创建菜单
exports.createMenu = async (req, res) => {
try {
const { parentId, name, code, type, path, component, icon, sort, visible, status } = req.body;
if (!name || !code || !type) {
return response.badRequest(res, '菜单名称、编码和类型不能为空');
}
if (type === 'menu' && !path) {
return response.badRequest(res, '菜单类型必须填写路由路径');
}
const existingMenu = await Menu.findOne({
where: { code, isDeleted: false }
});
if (existingMenu) {
return response.badRequest(res, '菜单编码已存在');
}
if (parentId) {
const parentMenu = await Menu.findByPk(parentId, {
where: { isDeleted: false }
});
if (!parentMenu) {
return response.badRequest(res, '父菜单不存在');
}
}
const menu = await Menu.create({
parentId: parentId || null,
name,
code,
type,
path: type === 'menu' ? path : null,
component,
icon,
sort: sort || 0,
visible: visible || 'show',
status: status || 'active',
createBy: req.user.id,
updateBy: req.user.id
});
await logOperation({
userId: req.user.id,
username: req.user.username,
module: '菜单管理',
action: '创建',
description: `创建菜单: ${name}`,
method: req.method,
path: req.path,
ip: req.ip,
status: 'success'
});
response.success(res, '创建成功', menu);
} catch (error) {
console.error('创建菜单失败:', error);
response.serverError(res, '创建菜单失败', error);
}
};
// 更新菜单
exports.updateMenu = async (req, res) => {
try {
const { id } = req.params;
const { parentId, name, code, type, path, component, icon, sort, visible, status } = req.body;
const menu = await Menu.findByPk(id, {
where: { isDeleted: false }
});
if (!menu) {
return response.notFound(res, '菜单不存在');
}
if (code && code !== menu.code) {
const existingMenu = await Menu.findOne({
where: { code, isDeleted: false, id: { [Op.ne]: id } }
});
if (existingMenu) {
return response.badRequest(res, '菜单编码已存在');
}
}
if (parentId) {
if (parseInt(parentId) === parseInt(id)) {
return response.badRequest(res, '不能将菜单设置为自己的子菜单');
}
const parentMenu = await Menu.findByPk(parentId, {
where: { isDeleted: false }
});
if (!parentMenu) {
return response.badRequest(res, '父菜单不存在');
}
}
const updateData = {
updateBy: req.user.id
};
if (parentId !== undefined) updateData.parentId = parentId || null;
if (name !== undefined) updateData.name = name;
if (code !== undefined) updateData.code = code;
if (type !== undefined) updateData.type = type;
if (path !== undefined) updateData.path = path;
if (component !== undefined) updateData.component = component;
if (icon !== undefined) updateData.icon = icon;
if (sort !== undefined) updateData.sort = sort;
if (visible !== undefined) updateData.visible = visible;
if (status !== undefined) updateData.status = status;
await menu.update(updateData);
await logOperation({
userId: req.user.id,
username: req.user.username,
module: '菜单管理',
action: '更新',
description: `更新菜单: ${menu.name}`,
method: req.method,
path: req.path,
ip: req.ip,
status: 'success'
});
response.success(res, '更新成功', menu);
} catch (error) {
console.error('更新菜单失败:', error);
response.serverError(res, '更新菜单失败', error);
}
};
// 删除菜单
exports.deleteMenu = async (req, res) => {
try {
const { id } = req.params;
const menu = await Menu.findByPk(id, {
where: { isDeleted: false }
});
if (!menu) {
return response.notFound(res, '菜单不存在');
}
const children = await Menu.findAll({
where: { parentId: id, isDeleted: false }
});
if (children.length > 0) {
return response.badRequest(res, '该菜单下有子菜单,无法删除');
}
await menu.update({
isDeleted: true,
updateBy: req.user.id
});
await RoleMenu.destroy({
where: { menuId: id }
});
await logOperation({
userId: req.user.id,
username: req.user.username,
module: '菜单管理',
action: '删除',
description: `删除菜单: ${menu.name}`,
method: req.method,
path: req.path,
ip: req.ip,
status: 'success'
});
response.success(res, '删除成功');
} catch (error) {
console.error('删除菜单失败:', error);
response.serverError(res, '删除菜单失败', error);
}
};
// 获取角色的菜单权限
exports.getRoleMenus = async (req, res) => {
try {
const { roleId } = req.params;
const Role = require('../models/Role');
// 先检查角色是否存在
const role = await Role.findOne({
where: { id: roleId, isDeleted: 0 }
});
if (!role) {
return response.notFound(res, '角色不存在');
}
// 获取角色的菜单权限
const roleData = await Role.findByPk(roleId, {
include: [{
model: Menu,
as: 'menus',
where: { isDeleted: 0 },
required: false,
through: { attributes: [] }
}]
});
const menus = roleData ? roleData.menus : [];
response.success(res, '获取成功', menus);
} catch (error) {
console.error('获取角色菜单失败:', error);
response.serverError(res, '获取角色菜单失败', error);
}
};
// 分配菜单权限给角色
exports.assignMenusToRole = async (req, res) => {
try {
const { roleId } = req.params;
const { menuIds } = req.body;
if (!Array.isArray(menuIds)) {
return response.badRequest(res, '菜单ID必须是数组');
}
const Role = require('../models/Role');
const role = await Role.findByPk(roleId);
if (!role) {
return response.notFound(res, '角色不存在');
}
await RoleMenu.destroy({
where: { roleId }
});
if (menuIds.length > 0) {
const roleMenus = menuIds.map(menuId => ({
roleId,
menuId
}));
await RoleMenu.bulkCreate(roleMenus);
}
await logOperation({
userId: req.user.id,
username: req.user.username,
module: '菜单管理',
action: '分配权限',
description: `为角色 ${role.name} 分配菜单权限`,
method: req.method,
path: req.path,
ip: req.ip,
status: 'success'
});
response.success(res, '分配成功');
} catch (error) {
console.error('分配菜单权限失败:', error);
response.serverError(res, '分配菜单权限失败', error);
}
};
// 获取当前用户的菜单权限
exports.getUserMenus = async (req, res) => {
try {
const userId = req.user.id;
const roleId = req.user.roleId;
// 获取用户角色
const User = require('../models/User');
const user = await User.findByPk(userId, {
include: [{
model: require('../models/Role'),
as: 'role'
}]
});
// 系统管理员或租户管理员返回所有菜单
if (user.userType === 'super_admin' || user.userType === 'tenant_admin') {
// 先获取所有可见的菜单
const menuList = await Menu.findAll({
where: {
isDeleted: false,
status: 'active',
visible: 'show'
},
order: [['sort', 'ASC']]
});
// 再获取所有按钮
const buttonList = await Menu.findAll({
where: {
isDeleted: false,
status: 'active',
type: 'button'
},
order: [['sort', 'ASC']]
});
// 合并菜单和按钮
const allMenus = [...menuList, ...buttonList];
const menuTree = buildTree(allMenus);
return response.success(res, '获取成功', menuTree);
}
if (!user.role) {
return response.forbidden(res, '用户没有分配角色');
}
// 普通用户返回角色分配的菜单
const Role = require('../models/Role');
const roleData = await Role.findByPk(roleId, {
include: [{
model: Menu,
as: 'menus',
where: {
isDeleted: false,
status: 'active'
},
through: { attributes: [] }
}]
});
if (!roleData || !roleData.menus) {
return response.success(res, '获取成功', []);
}
// 按sort排序
const menus = roleData.menus.sort((a, b) => a.sort - b.sort);
// 构建菜单树
const menuTree = buildTree(menus);
response.success(res, '获取成功', menuTree);
} catch (error) {
console.error('获取用户菜单失败:', error);
response.serverError(res, '获取用户菜单失败', error);
}
};
// 构建菜单树
function buildTree(menus, parentId = null) {
return menus
.filter(menu => menu.parentId === parentId)
.map(menu => {
// 获取菜单的原始数据
const menuData = menu.toJSON ? menu.toJSON() : menu;
return {
...menuData,
children: buildTree(menus, menu.id)
};
});
}