rentease-backend/controllers/menuController.js

564 lines
13 KiB
JavaScript
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.

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)
}));
}