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