rentease-backend/controllers/menuController.js

564 lines
13 KiB
JavaScript
Raw Normal View History

2026-03-08 16:28:33 +00:00
const { Op } = require('sequelize');
const Menu = require('../models/Menu');
const RoleMenu = require('../models/RoleMenu');
const { logOperation } = require('../utils/logger');
// 获取菜单树
exports.getMenuTree = async (req, res) => {
try {
const { type, status } = req.query;
const where = { isDeleted: false };
if (type) {
where.type = type;
}
if (status) {
where.status = status;
}
const menus = await Menu.findAll({
where,
order: [['sort', 'ASC'], ['createdAt', 'ASC']]
});
const menuTree = buildTree(menus);
res.json({
code: 200,
message: '获取成功',
data: menuTree
});
} catch (error) {
console.error('获取菜单树失败:', error);
res.status(500).json({
code: 500,
message: '获取菜单树失败',
error: error.message
});
}
};
// 获取菜单列表(平铺)
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'], ['createdAt', 'ASC']],
offset: (page - 1) * pageSize,
limit: parseInt(pageSize)
});
res.json({
code: 200,
message: '获取成功',
data: {
list: rows,
total: count,
page: parseInt(page),
pageSize: parseInt(pageSize)
}
});
} catch (error) {
console.error('获取菜单列表失败:', error);
res.status(500).json({
code: 500,
message: '获取菜单列表失败',
error: error.message
});
}
};
// 获取菜单详情
exports.getMenuById = async (req, res) => {
try {
const { id } = req.params;
const menu = await Menu.findByPk(id, {
where: { isDeleted: false }
});
if (!menu) {
return res.status(404).json({
code: 404,
message: '菜单不存在'
});
}
res.json({
code: 200,
message: '获取成功',
data: menu
});
} catch (error) {
console.error('获取菜单详情失败:', error);
res.status(500).json({
code: 500,
message: '获取菜单详情失败',
error: error.message
});
}
};
// 创建菜单
exports.createMenu = async (req, res) => {
try {
const { parentId, name, code, type, path, component, icon, sort, visible, status } = req.body;
if (!name || !code || !type) {
return res.status(400).json({
code: 400,
message: '菜单名称、编码和类型不能为空'
});
}
if (type === 'menu' && !path) {
return res.status(400).json({
code: 400,
message: '菜单类型必须填写路由路径'
});
}
const existingMenu = await Menu.findOne({
where: { code, isDeleted: false }
});
if (existingMenu) {
return res.status(400).json({
code: 400,
message: '菜单编码已存在'
});
}
if (parentId) {
const parentMenu = await Menu.findByPk(parentId, {
where: { isDeleted: false }
});
if (!parentMenu) {
return res.status(400).json({
code: 400,
message: '父菜单不存在'
});
}
}
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'
});
res.json({
code: 200,
message: '创建成功',
data: menu
});
} catch (error) {
console.error('创建菜单失败:', error);
res.status(500).json({
code: 500,
message: '创建菜单失败',
error: error.message
});
}
};
// 更新菜单
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 res.status(404).json({
code: 404,
message: '菜单不存在'
});
}
if (code && code !== menu.code) {
const existingMenu = await Menu.findOne({
where: { code, isDeleted: false, id: { [Op.ne]: id } }
});
if (existingMenu) {
return res.status(400).json({
code: 400,
message: '菜单编码已存在'
});
}
}
if (parentId) {
if (parseInt(parentId) === parseInt(id)) {
return res.status(400).json({
code: 400,
message: '不能将菜单设置为自己的子菜单'
});
}
const parentMenu = await Menu.findByPk(parentId, {
where: { isDeleted: false }
});
if (!parentMenu) {
return res.status(400).json({
code: 400,
message: '父菜单不存在'
});
}
}
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'
});
res.json({
code: 200,
message: '更新成功',
data: menu
});
} catch (error) {
console.error('更新菜单失败:', error);
res.status(500).json({
code: 500,
message: '更新菜单失败',
error: error.message
});
}
};
// 删除菜单
exports.deleteMenu = async (req, res) => {
try {
const { id } = req.params;
const menu = await Menu.findByPk(id, {
where: { isDeleted: false }
});
if (!menu) {
return res.status(404).json({
code: 404,
message: '菜单不存在'
});
}
const children = await Menu.findAll({
where: { parentId: id, isDeleted: false }
});
if (children.length > 0) {
return res.status(400).json({
code: 400,
message: '该菜单下有子菜单,无法删除'
});
}
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'
});
res.json({
code: 200,
message: '删除成功'
});
} catch (error) {
console.error('删除菜单失败:', error);
res.status(500).json({
code: 500,
message: '删除菜单失败',
error: error.message
});
}
};
// 获取角色的菜单权限
exports.getRoleMenus = async (req, res) => {
try {
const { roleId } = req.params;
const role = await Menu.findOne({
where: { id: roleId, isDeleted: false }
});
if (!role) {
return res.status(404).json({
code: 404,
message: '角色不存在'
});
}
const Role = require('../models/Role');
const roleData = await Role.findByPk(roleId, {
include: [{
model: Menu,
as: 'menus',
where: { isDeleted: false },
through: { attributes: [] }
}]
});
const menus = roleData ? roleData.menus : [];
res.json({
code: 200,
message: '获取成功',
data: menus
});
} catch (error) {
console.error('获取角色菜单失败:', error);
res.status(500).json({
code: 500,
message: '获取角色菜单失败',
error: error.message
});
}
};
// 分配菜单权限给角色
exports.assignMenusToRole = async (req, res) => {
try {
const { roleId } = req.params;
const { menuIds } = req.body;
if (!Array.isArray(menuIds)) {
return res.status(400).json({
code: 400,
message: '菜单ID必须是数组'
});
}
const Role = require('../models/Role');
const role = await Role.findByPk(roleId);
if (!role) {
return res.status(404).json({
code: 404,
message: '角色不存在'
});
}
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'
});
res.json({
code: 200,
message: '分配成功'
});
} catch (error) {
console.error('分配菜单权限失败:', error);
res.status(500).json({
code: 500,
message: '分配菜单权限失败',
error: error.message
});
}
};
// 获取当前用户的菜单权限
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'
}]
});
// 超级管理员isSuperAdmin为1或角色code为admin返回所有菜单
if (user.isSuperAdmin === 1 || (user.role && user.role.code === '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 res.json({
code: 200,
message: '获取成功',
data: menuTree
});
}
if (!user.role) {
return res.status(403).json({
code: 403,
message: '用户没有分配角色'
});
}
// 普通用户返回角色分配的菜单
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 res.json({
code: 200,
message: '获取成功',
data: []
});
}
// 按sort排序
const menus = roleData.menus.sort((a, b) => a.sort - b.sort);
// 构建菜单树
const menuTree = buildTree(menus);
res.json({
code: 200,
message: '获取成功',
data: menuTree
});
} catch (error) {
console.error('获取用户菜单失败:', error);
res.status(500).json({
code: 500,
message: '获取用户菜单失败',
error: error.message
});
}
};
// 构建菜单树
function buildTree(menus, parentId = null) {
return menus
.filter(menu => menu.parentId === parentId)
.map(menu => ({
...menu.dataValues,
children: buildTree(menus, menu.id)
}));
}