Files
nxxmdata/backend/models/MenuPermission.js
2025-09-12 20:08:42 +08:00

384 lines
9.7 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.

/**
* 菜单权限模型
* @file MenuPermission.js
* @description 菜单权限配置数据模型
*/
const { DataTypes } = require('sequelize');
const BaseModel = require('./BaseModel');
const { sequelize } = require('../config/database-simple');
class MenuPermission extends BaseModel {
/**
* 获取用户可访问的菜单
* @param {Array} userRoles 用户角色数组
* @param {Array} userPermissions 用户权限数组
* @returns {Array} 菜单树结构
*/
static async getUserMenus(userRoles = [], userPermissions = []) {
try {
const allMenus = await this.findAll({
where: {
is_visible: true,
is_enabled: true
},
order: [['sort_order', 'ASC']]
});
// 过滤用户有权限的菜单
const accessibleMenus = allMenus.filter(menu => {
return this.checkMenuAccess(menu, userRoles, userPermissions);
});
// 构建菜单树
return this.buildMenuTree(accessibleMenus);
} catch (error) {
console.error('获取用户菜单失败:', error);
return [];
}
}
/**
* 检查菜单访问权限
* @param {Object} menu 菜单对象
* @param {Array} userRoles 用户角色
* @param {Array} userPermissions 用户权限
* @returns {boolean} 是否有权限
*/
static checkMenuAccess(menu, userRoles, userPermissions) {
// 解析所需角色
let requiredRoles = [];
if (menu.required_roles) {
try {
requiredRoles = JSON.parse(menu.required_roles);
} catch (error) {
console.error('解析菜单所需角色失败:', error);
}
}
// 解析所需权限
let requiredPermissions = [];
if (menu.required_permissions) {
try {
requiredPermissions = JSON.parse(menu.required_permissions);
} catch (error) {
console.error('解析菜单所需权限失败:', error);
}
}
// 如果没有配置权限要求,则默认允许访问
if (requiredRoles.length === 0 && requiredPermissions.length === 0) {
return true;
}
// 检查角色权限
if (requiredRoles.length > 0) {
const hasRole = requiredRoles.some(role => userRoles.includes(role));
if (!hasRole) {
return false;
}
}
// 检查具体权限
if (requiredPermissions.length > 0) {
const hasPermission = requiredPermissions.some(permission =>
userPermissions.includes(permission)
);
if (!hasPermission) {
return false;
}
}
return true;
}
/**
* 构建菜单树结构
* @param {Array} menus 菜单数组
* @returns {Array} 菜单树
*/
static buildMenuTree(menus) {
const menuMap = new Map();
const roots = [];
// 创建菜单映射
menus.forEach(menu => {
menuMap.set(menu.id, {
...menu.dataValues,
children: []
});
});
// 构建树结构
menus.forEach(menu => {
const menuNode = menuMap.get(menu.id);
if (menu.parent_id && menuMap.has(menu.parent_id)) {
const parent = menuMap.get(menu.parent_id);
parent.children.push(menuNode);
} else {
roots.push(menuNode);
}
});
return roots;
}
/**
* 初始化默认菜单权限
*/
static async initDefaultMenus() {
try {
const defaultMenus = [
// 主要功能模块
{
menu_key: 'home',
menu_name: '首页',
menu_path: '/',
menu_type: 'page',
icon: 'home-outlined',
sort_order: 1,
required_roles: '["user", "admin", "manager"]'
},
{
menu_key: 'dashboard',
menu_name: '系统概览',
menu_path: '/dashboard',
menu_type: 'page',
icon: 'dashboard-outlined',
sort_order: 2,
required_roles: '["user", "admin", "manager"]'
},
{
menu_key: 'monitor',
menu_name: '实时监控',
menu_path: '/monitor',
menu_type: 'page',
icon: 'line-chart-outlined',
sort_order: 3,
required_roles: '["user", "admin", "manager"]'
},
{
menu_key: 'analytics',
menu_name: '数据分析',
menu_path: '/analytics',
menu_type: 'page',
icon: 'bar-chart-outlined',
sort_order: 4,
required_roles: '["user", "admin", "manager"]'
},
// 管理功能模块
{
menu_key: 'farms',
menu_name: '养殖场管理',
menu_path: '/farms',
menu_type: 'page',
icon: 'home-outlined',
sort_order: 10,
required_roles: '["admin", "manager"]'
},
{
menu_key: 'animals',
menu_name: '动物管理',
menu_path: '/animals',
menu_type: 'page',
icon: 'bug-outlined',
sort_order: 11,
required_roles: '["admin", "manager"]'
},
{
menu_key: 'devices',
menu_name: '设备管理',
menu_path: '/devices',
menu_type: 'page',
icon: 'desktop-outlined',
sort_order: 12,
required_roles: '["admin", "manager"]'
},
{
menu_key: 'alerts',
menu_name: '预警管理',
menu_path: '/alerts',
menu_type: 'page',
icon: 'alert-outlined',
sort_order: 13,
required_roles: '["admin", "manager"]'
},
// 业务功能模块
{
menu_key: 'products',
menu_name: '产品管理',
menu_path: '/products',
menu_type: 'page',
icon: 'shopping-outlined',
sort_order: 20,
required_roles: '["admin", "manager"]'
},
{
menu_key: 'orders',
menu_name: '订单管理',
menu_path: '/orders',
menu_type: 'page',
icon: 'shopping-cart-outlined',
sort_order: 21,
required_roles: '["admin", "manager"]'
},
{
menu_key: 'reports',
menu_name: '报表管理',
menu_path: '/reports',
menu_type: 'page',
icon: 'file-text-outlined',
sort_order: 22,
required_roles: '["admin", "manager"]'
},
// 系统管理模块
{
menu_key: 'users',
menu_name: '用户管理',
menu_path: '/users',
menu_type: 'page',
icon: 'user-outlined',
sort_order: 30,
required_roles: '["admin"]'
},
{
menu_key: 'system',
menu_name: '系统管理',
menu_path: '/system',
menu_type: 'page',
icon: 'setting-outlined',
sort_order: 31,
required_roles: '["admin"]'
}
];
for (const menuData of defaultMenus) {
const existing = await this.findOne({
where: { menu_key: menuData.menu_key }
});
if (!existing) {
await this.create(menuData);
}
}
console.log('默认菜单权限初始化完成');
} catch (error) {
console.error('初始化默认菜单权限失败:', error);
throw error;
}
}
}
// 初始化MenuPermission模型
MenuPermission.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '权限ID'
},
menu_key: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '菜单标识'
},
menu_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '菜单名称'
},
menu_path: {
type: DataTypes.STRING(200),
allowNull: true,
comment: '菜单路径'
},
parent_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '父菜单ID'
},
menu_type: {
type: DataTypes.ENUM('page', 'button', 'api'),
allowNull: false,
defaultValue: 'page',
comment: '菜单类型'
},
required_roles: {
type: DataTypes.TEXT,
allowNull: true,
comment: '所需角色JSON数组'
},
required_permissions: {
type: DataTypes.TEXT,
allowNull: true,
comment: '所需权限JSON数组'
},
icon: {
type: DataTypes.STRING(50),
allowNull: true,
comment: '菜单图标'
},
sort_order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序顺序'
},
is_visible: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否可见'
},
is_enabled: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否启用'
},
description: {
type: DataTypes.STRING(255),
allowNull: true,
comment: '菜单描述'
}
}, {
sequelize,
tableName: 'menu_permissions',
modelName: 'MenuPermission',
comment: '菜单权限表',
indexes: [
{
fields: ['menu_key'],
unique: true
},
{
fields: ['parent_id']
},
{
fields: ['menu_type']
},
{
fields: ['sort_order']
}
]
});
// 定义自关联关系
MenuPermission.associate = function(models) {
// 自关联:父子菜单关系
MenuPermission.hasMany(MenuPermission, {
as: 'children',
foreignKey: 'parent_id'
});
MenuPermission.belongsTo(MenuPermission, {
as: 'parent',
foreignKey: 'parent_id'
});
};
module.exports = MenuPermission;