2025-12-25 10:48:35 +08:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const Role = require('../models/Role');
|
|
|
|
|
|
const Permission = require('../models/Permission');
|
|
|
|
|
|
const UserRole = require('../models/UserRole');
|
|
|
|
|
|
const User = require('../models/User');
|
|
|
|
|
|
const { authMiddleware } = require('../middleware/auth');
|
2026-03-20 17:15:14 +08:00
|
|
|
|
const { logRoleOperation } = require('../utils/operationLogger');
|
2025-12-25 10:48:35 +08:00
|
|
|
|
|
|
|
|
|
|
const router = express.Router();
|
|
|
|
|
|
|
|
|
|
|
|
const generateId = () => {
|
|
|
|
|
|
return 'role_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 9);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const { Op } = require('sequelize');
|
|
|
|
|
|
|
|
|
|
|
|
router.get('/', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { page = 1, pageSize = 10, roleName, status } = req.query;
|
|
|
|
|
|
const offset = (parseInt(page) - 1) * parseInt(pageSize);
|
|
|
|
|
|
const limit = parseInt(pageSize);
|
|
|
|
|
|
|
|
|
|
|
|
const where = {};
|
|
|
|
|
|
if (roleName) {
|
|
|
|
|
|
where.roleName = { [Op.like]: `%${roleName}%` };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const { count, rows: roles } = await Role.findAndCountAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
limit,
|
|
|
|
|
|
offset,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
order: [
|
|
|
|
|
|
['sort', 'ASC'],
|
|
|
|
|
|
['createdAt', 'DESC'],
|
|
|
|
|
|
],
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
total: count,
|
|
|
|
|
|
page: parseInt(page),
|
|
|
|
|
|
pageSize: parseInt(pageSize),
|
2026-03-27 19:12:16 +08:00
|
|
|
|
roles,
|
|
|
|
|
|
},
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取角色列表错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '获取角色列表失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.get('/all', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const roles = await Role.findAll({
|
|
|
|
|
|
where: { status: 'active' },
|
2026-03-27 19:12:16 +08:00
|
|
|
|
order: [['sort', 'ASC']],
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
data: roles,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取所有角色错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '获取角色列表失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.get('/:roleId', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const role = await Role.findByPk(req.params.roleId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!role) {
|
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '角色不存在',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const permissions = await Permission.findAll({
|
|
|
|
|
|
where: { status: 'active' },
|
2026-03-27 19:12:16 +08:00
|
|
|
|
order: [['sort', 'ASC']],
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
role,
|
|
|
|
|
|
permissions,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
rolePermissions: role.permissions || [],
|
|
|
|
|
|
},
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取角色详情错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '获取角色详情失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.post('/', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { roleName, roleCode, description, permissions, status, sort } = req.body;
|
|
|
|
|
|
|
|
|
|
|
|
if (!roleName || !roleCode) {
|
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '角色名称和角色编码不能为空',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const existingRole = await Role.findOne({ where: { roleCode } });
|
|
|
|
|
|
if (existingRole) {
|
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '角色编码已存在',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const role = await Role.create({
|
|
|
|
|
|
roleId: generateId(),
|
|
|
|
|
|
roleName,
|
|
|
|
|
|
roleCode,
|
|
|
|
|
|
description,
|
|
|
|
|
|
permissions: permissions || [],
|
|
|
|
|
|
status: status || 'active',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
sort: sort || 0,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
const permissionNames = permissions && permissions.length > 0 ? permissions.join('、') : '无';
|
2026-03-20 17:15:14 +08:00
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
await logRoleOperation(
|
|
|
|
|
|
'create',
|
|
|
|
|
|
`创建角色【${roleName}】(编码:${roleCode},权限:${permissionNames})`,
|
|
|
|
|
|
{
|
|
|
|
|
|
targetId: role.roleId,
|
|
|
|
|
|
targetName: roleName,
|
|
|
|
|
|
afterState: role.toJSON(),
|
|
|
|
|
|
req,
|
|
|
|
|
|
metadata: { roleCode, permissions, permissionNames },
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2026-03-20 17:15:14 +08:00
|
|
|
|
|
2025-12-25 10:48:35 +08:00
|
|
|
|
res.status(201).json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '创建成功',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
data: role,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('创建角色错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '创建角色失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.put('/:roleId', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { roleName, description, permissions, status, sort } = req.body;
|
|
|
|
|
|
const role = await Role.findByPk(req.params.roleId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!role) {
|
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '角色不存在',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 17:15:14 +08:00
|
|
|
|
const beforeState = role.toJSON();
|
|
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
if (roleName !== undefined) {
|
|
|
|
|
|
role.roleName = roleName;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (description !== undefined) {
|
|
|
|
|
|
role.description = description;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (permissions !== undefined) {
|
|
|
|
|
|
role.permissions = permissions;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status !== undefined) {
|
|
|
|
|
|
role.status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sort !== undefined) {
|
|
|
|
|
|
role.sort = sort;
|
|
|
|
|
|
}
|
2025-12-25 10:48:35 +08:00
|
|
|
|
|
|
|
|
|
|
await role.save();
|
|
|
|
|
|
|
2026-03-20 17:15:14 +08:00
|
|
|
|
const afterState = role.toJSON();
|
|
|
|
|
|
const changedFields = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (roleName !== undefined && beforeState.roleName !== roleName) {
|
|
|
|
|
|
changedFields.roleName = { from: beforeState.roleName, to: roleName };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (description !== undefined && beforeState.description !== description) {
|
|
|
|
|
|
changedFields.description = { from: beforeState.description, to: description };
|
|
|
|
|
|
}
|
|
|
|
|
|
if (permissions !== undefined) {
|
|
|
|
|
|
const oldPerms = (beforeState.permissions || []).sort().join(',');
|
|
|
|
|
|
const newPerms = (permissions || []).sort().join(',');
|
|
|
|
|
|
if (oldPerms !== newPerms) {
|
|
|
|
|
|
changedFields.permissions = { from: beforeState.permissions, to: permissions };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (status !== undefined && beforeState.status !== status) {
|
|
|
|
|
|
const statusText = { active: '启用', inactive: '禁用' };
|
2026-03-27 19:12:16 +08:00
|
|
|
|
changedFields.status = {
|
|
|
|
|
|
from: beforeState.status,
|
|
|
|
|
|
to: status,
|
|
|
|
|
|
fromText: statusText[beforeState.status],
|
|
|
|
|
|
toText: statusText[status],
|
|
|
|
|
|
};
|
2026-03-20 17:15:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
const changeDetails = Object.entries(changedFields)
|
|
|
|
|
|
.map(([field, values]) => {
|
|
|
|
|
|
const fieldNames = {
|
|
|
|
|
|
roleName: '角色名称',
|
|
|
|
|
|
description: '描述',
|
|
|
|
|
|
permissions: '权限',
|
|
|
|
|
|
status: '状态',
|
|
|
|
|
|
};
|
|
|
|
|
|
const displayName = fieldNames[field] || field;
|
2026-03-20 17:15:14 +08:00
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
if (field === 'permissions') {
|
|
|
|
|
|
return `权限: ${(values.from || []).join('、') || '无'} → ${(values.to || []).join('、') || '无'}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (field === 'status') {
|
|
|
|
|
|
return `状态: ${values.fromText} → ${values.toText}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${displayName}: ${values.from ?? '空'} → ${values.to ?? '空'}`;
|
|
|
|
|
|
})
|
|
|
|
|
|
.join(';');
|
2026-03-20 17:15:14 +08:00
|
|
|
|
|
|
|
|
|
|
const updateDesc = changeDetails
|
|
|
|
|
|
? `更新角色【${role.roleName}】:${changeDetails}`
|
|
|
|
|
|
: `更新角色【${role.roleName}】`;
|
|
|
|
|
|
|
|
|
|
|
|
await logRoleOperation('update', updateDesc, {
|
|
|
|
|
|
targetId: role.roleId,
|
|
|
|
|
|
targetName: role.roleName,
|
|
|
|
|
|
beforeState,
|
|
|
|
|
|
afterState,
|
|
|
|
|
|
req,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
metadata: {
|
|
|
|
|
|
changedFields,
|
|
|
|
|
|
oldRoleName: beforeState.roleName,
|
|
|
|
|
|
oldPermissions: beforeState.permissions,
|
|
|
|
|
|
},
|
2026-03-20 17:15:14 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-12-25 10:48:35 +08:00
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '更新成功',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
data: role,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('更新角色错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '更新角色失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.delete('/:roleId', authMiddleware, async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const role = await Role.findByPk(req.params.roleId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!role) {
|
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '角色不存在',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (role.roleCode === 'admin') {
|
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '不能删除管理员角色',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const userCount = await UserRole.count({ where: { RoleId: role.roleId } });
|
|
|
|
|
|
if (userCount > 0) {
|
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '该角色下有用户,不能删除',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-20 17:15:14 +08:00
|
|
|
|
const roleName = role.roleName;
|
|
|
|
|
|
const roleCode = role.roleCode;
|
|
|
|
|
|
const beforeState = role.toJSON();
|
|
|
|
|
|
|
2025-12-25 10:48:35 +08:00
|
|
|
|
await role.destroy();
|
|
|
|
|
|
|
2026-03-27 19:12:16 +08:00
|
|
|
|
await logRoleOperation(
|
|
|
|
|
|
'delete',
|
|
|
|
|
|
`删除角色【${roleName}】(编码:${roleCode},权限:${(role.permissions || []).join('、') || '无'})`,
|
|
|
|
|
|
{
|
|
|
|
|
|
targetId: req.params.roleId,
|
|
|
|
|
|
targetName: roleName,
|
|
|
|
|
|
beforeState,
|
|
|
|
|
|
req,
|
|
|
|
|
|
metadata: { roleCode, userCount, permissions: role.permissions },
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2026-03-20 17:15:14 +08:00
|
|
|
|
|
2025-12-25 10:48:35 +08:00
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '删除成功',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('删除角色错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '删除角色失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
router.post('/init-roles', async (req, res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const defaultRoles = [
|
|
|
|
|
|
{
|
|
|
|
|
|
roleId: 'role_admin',
|
|
|
|
|
|
roleName: '管理员',
|
|
|
|
|
|
roleCode: 'admin',
|
|
|
|
|
|
description: '系统管理员,拥有所有权限',
|
|
|
|
|
|
permissions: ['*'],
|
|
|
|
|
|
status: 'active',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
sort: 1,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
roleId: 'role_operator',
|
|
|
|
|
|
roleName: '运维人员',
|
|
|
|
|
|
roleCode: 'operator',
|
|
|
|
|
|
description: '负责日常运维操作',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
permissions: [
|
|
|
|
|
|
'devices:read',
|
|
|
|
|
|
'devices:write',
|
|
|
|
|
|
'racks:read',
|
|
|
|
|
|
'rooms:read',
|
|
|
|
|
|
'consumables:read',
|
|
|
|
|
|
'consumables:write',
|
|
|
|
|
|
],
|
2025-12-25 10:48:35 +08:00
|
|
|
|
status: 'active',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
sort: 2,
|
2025-12-25 10:48:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
roleId: 'role_viewer',
|
|
|
|
|
|
roleName: '只读用户',
|
|
|
|
|
|
roleCode: 'viewer',
|
|
|
|
|
|
description: '仅能查看数据',
|
|
|
|
|
|
permissions: ['devices:read', 'racks:read', 'rooms:read', 'consumables:read'],
|
|
|
|
|
|
status: 'active',
|
2026-03-27 19:12:16 +08:00
|
|
|
|
sort: 3,
|
|
|
|
|
|
},
|
2025-12-25 10:48:35 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
for (const roleData of defaultRoles) {
|
|
|
|
|
|
await Role.upsert(roleData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '初始化角色成功',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('初始化角色错误:', error);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2026-03-27 19:12:16 +08:00
|
|
|
|
message: '初始化角色失败',
|
2025-12-25 10:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = router;
|