完善保险前后端、养殖端小程序

This commit is contained in:
xuqiuyun
2025-09-25 19:09:51 +08:00
parent 76b5393182
commit 852adbcfff
199 changed files with 8642 additions and 52333 deletions

View File

@@ -0,0 +1,27 @@
const { User } = require('./models');
async function checkAdminToken() {
try {
const admin = await User.findOne({
where: { username: 'admin' },
attributes: ['id', 'username', 'fixed_token', 'status']
});
if (admin) {
console.log('Admin用户信息:');
console.log('ID:', admin.id);
console.log('用户名:', admin.username);
console.log('Token:', admin.fixed_token);
console.log('状态:', admin.status);
} else {
console.log('未找到admin用户');
}
process.exit(0);
} catch (error) {
console.error('错误:', error.message);
process.exit(1);
}
}
checkAdminToken();

View File

@@ -30,7 +30,14 @@ const swaggerDefinition = {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
bearerFormat: 'JWT',
description: 'JWT令牌认证'
},
fixedTokenAuth: {
type: 'apiKey',
in: 'header',
name: 'Authorization',
description: '固定令牌认证格式Bearer <fixed_token>'
}
},
schemas: {
@@ -42,6 +49,7 @@ const swaggerDefinition = {
email: { type: 'string', description: '邮箱' },
phone: { type: 'string', description: '手机号' },
status: { type: 'string', enum: ['active', 'inactive'], description: '用户状态' },
fixed_token: { type: 'string', description: '固定令牌用于API访问验证', nullable: true },
createdAt: { type: 'string', format: 'date-time', description: '创建时间' },
updatedAt: { type: 'string', format: 'date-time', description: '更新时间' }
}
@@ -214,6 +222,21 @@ const swaggerDefinition = {
message: { type: 'string', description: '错误信息' },
timestamp: { type: 'string', format: 'date-time', description: '时间戳' }
}
},
FixedTokenInfo: {
type: 'object',
properties: {
hasToken: { type: 'boolean', description: '是否已生成固定令牌' },
tokenPreview: { type: 'string', description: '令牌预览仅显示前8位', nullable: true },
createdAt: { type: 'string', format: 'date-time', description: '令牌创建时间', nullable: true }
}
},
FixedTokenGenerated: {
type: 'object',
properties: {
token: { type: 'string', description: '生成的固定令牌(仅在生成时返回完整令牌)' },
message: { type: 'string', description: '操作结果信息' }
}
}
},
responses: {
@@ -248,6 +271,22 @@ const swaggerDefinition = {
}
}
}
},
ForbiddenError: {
description: '权限不足',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error'
},
example: {
code: 403,
status: 'error',
message: '权限不足',
timestamp: '2024-01-01T00:00:00.000Z'
}
}
}
}
}
},

View File

@@ -0,0 +1,295 @@
const { Op, sequelize } = require('sequelize');
const { sequelize: dbSequelize } = require('../config/database');
const User = require('../models/User');
const InsuranceApplication = require('../models/InsuranceApplication');
const Policy = require('../models/Policy');
const Claim = require('../models/Claim');
const OperationLog = require('../models/OperationLog');
/**
* 获取仪表板统计数据
*/
const getStats = async (req, res) => {
try {
console.log('获取仪表板统计数据...');
// 获取今日日期范围
const today = new Date();
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
// 获取本月日期范围
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
// 并行查询各种统计数据
const [
totalApplications,
todayApplications,
monthApplications,
totalPolicies,
activePolicies,
totalClaims,
pendingClaims,
totalUsers,
recentActivities
] = await Promise.all([
// 总申请数
InsuranceApplication.count(),
// 今日申请数
InsuranceApplication.count({
where: {
created_at: {
[Op.gte]: startOfDay,
[Op.lt]: endOfDay
}
}
}),
// 本月申请数
InsuranceApplication.count({
where: {
created_at: {
[Op.gte]: startOfMonth,
[Op.lt]: endOfMonth
}
}
}),
// 总保单数
Policy.count(),
// 有效保单数
Policy.count({
where: {
policy_status: 'active'
}
}),
// 总理赔数
Claim.count(),
// 待处理理赔数
Claim.count({
where: {
claim_status: 'pending'
}
}),
// 总用户数
User.count(),
// 最近活动(从系统日志获取)
OperationLog.findAll({
limit: 10,
order: [['created_at', 'DESC']],
attributes: ['id', 'operation_type', 'operation_content', 'created_at', 'user_id']
})
]);
// 计算增长率(简化计算,实际应该与上一期间对比)
const applicationGrowthRate = todayApplications > 0 ?
((todayApplications / Math.max(monthApplications - todayApplications, 1)) * 100).toFixed(1) : 0;
const policyGrowthRate = activePolicies > 0 ?
((activePolicies / Math.max(totalPolicies - activePolicies, 1)) * 100).toFixed(1) : 0;
const statsData = {
// 核心指标
totalApplications,
todayApplications,
monthApplications,
applicationGrowthRate: parseFloat(applicationGrowthRate),
totalPolicies,
activePolicies,
policyGrowthRate: parseFloat(policyGrowthRate),
totalClaims,
pendingClaims,
claimProcessingRate: totalClaims > 0 ?
(((totalClaims - pendingClaims) / totalClaims) * 100).toFixed(1) : 0,
totalUsers,
// 快速统计
quickStats: {
newApplicationsToday: todayApplications,
pendingReviews: pendingClaims,
activeUsers: totalUsers, // 简化处理,实际应该查询活跃用户
systemAlerts: 0 // 简化处理,实际应该查询系统告警
}
};
console.log('统计数据获取成功:', statsData);
res.json({
code: 200,
status: 'success',
data: statsData,
message: '获取仪表板统计数据成功',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('获取仪表板统计数据失败:', error);
res.status(500).json({
code: 500,
status: 'error',
data: null,
message: '获取仪表板统计数据失败',
timestamp: new Date().toISOString()
});
}
};
/**
* 获取最近活动
*/
const getRecentActivities = async (req, res) => {
try {
console.log('获取最近活动数据...');
const { limit = 20 } = req.query;
// 从系统日志获取最近活动
const activities = await OperationLog.findAll({
limit: parseInt(limit),
order: [['created_at', 'DESC']],
attributes: ['id', 'operation_type', 'operation_content', 'created_at', 'user_id']
});
// 格式化活动数据
const formattedActivities = activities.map(activity => ({
id: activity.id,
type: activity.operation_type,
title: activity.operation_content,
description: `操作用户: ${activity.user_id || '系统'}`,
timestamp: activity.created_at,
user: activity.user_id,
level: activity.operation_type
}));
console.log(`获取到 ${formattedActivities.length} 条最近活动`);
res.json({
code: 200,
status: 'success',
data: formattedActivities,
message: '获取最近活动成功',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('获取最近活动失败:', error);
res.status(500).json({
code: 500,
status: 'error',
data: null,
message: '获取最近活动失败',
timestamp: new Date().toISOString()
});
}
};
/**
* 获取图表数据
*/
const getChartData = async (req, res) => {
try {
console.log('获取图表数据...');
const { type = 'applications', period = '7d' } = req.query;
let startDate;
const endDate = new Date();
// 根据时间周期设置开始日期
switch (period) {
case '7d':
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
break;
case '30d':
startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000);
break;
case '90d':
startDate = new Date(endDate.getTime() - 90 * 24 * 60 * 60 * 1000);
break;
default:
startDate = new Date(endDate.getTime() - 7 * 24 * 60 * 60 * 1000);
}
let chartData = [];
if (type === 'applications') {
// 获取申请数据趋势
const applications = await InsuranceApplication.findAll({
where: {
created_at: {
[Op.gte]: startDate,
[Op.lte]: endDate
}
},
attributes: [
[dbSequelize.fn('DATE', dbSequelize.col('created_at')), 'date'],
[dbSequelize.fn('COUNT', dbSequelize.col('id')), 'count']
],
group: [dbSequelize.fn('DATE', dbSequelize.col('created_at'))],
order: [[dbSequelize.fn('DATE', dbSequelize.col('created_at')), 'ASC']]
});
chartData = applications.map(item => ({
date: item.dataValues.date,
value: parseInt(item.dataValues.count)
}));
} else if (type === 'policies') {
// 获取保单数据趋势
const policies = await Policy.findAll({
where: {
created_at: {
[Op.gte]: startDate,
[Op.lte]: endDate
}
},
attributes: [
[dbSequelize.fn('DATE', dbSequelize.col('created_at')), 'date'],
[dbSequelize.fn('COUNT', dbSequelize.col('id')), 'count']
],
group: [dbSequelize.fn('DATE', dbSequelize.col('created_at'))],
order: [[dbSequelize.fn('DATE', dbSequelize.col('created_at')), 'ASC']]
});
chartData = policies.map(item => ({
date: item.dataValues.date,
value: parseInt(item.dataValues.count)
}));
}
console.log(`获取到 ${chartData.length} 条图表数据`);
res.json({
code: 200,
status: 'success',
data: chartData,
message: '获取图表数据成功',
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('获取图表数据失败:', error);
res.status(500).json({
code: 500,
status: 'error',
data: null,
message: '获取图表数据失败',
timestamp: new Date().toISOString()
});
}
};
module.exports = {
getStats,
getRecentActivities,
getChartData
};

View File

@@ -4,6 +4,9 @@ const { Op, Sequelize } = require('sequelize');
// 获取数据仓库概览
const getOverview = async (req, res) => {
try {
// 获取总用户数
const totalUsers = await User.count();
// 获取总申请数
const totalApplications = await InsuranceApplication.count();
@@ -43,6 +46,7 @@ const getOverview = async (req, res) => {
res.json({
success: true,
data: {
totalUsers,
totalApplications,
totalPolicies,
totalClaims,

View File

@@ -23,12 +23,10 @@ const getInsuranceTypes = async (req, res) => {
order: [['created_at', 'DESC']]
});
res.json(responseFormat.success({
list: rows,
total: count,
res.json(responseFormat.pagination(rows, {
page: parseInt(page),
pageSize: parseInt(pageSize),
pages: Math.ceil(count / pageSize)
limit: parseInt(pageSize),
total: count
}, '获取险种列表成功'));
} catch (error) {
console.error('获取险种列表错误:', error);

View File

@@ -0,0 +1,415 @@
const { Permission, Role, Menu, RolePermission, MenuPermission } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
/**
* 构建权限树形结构
*/
function buildPermissionTree(permissions, parentId = null) {
const tree = [];
for (const permission of permissions) {
if (permission.parent_id === parentId) {
const children = buildPermissionTree(permissions, permission.id);
const node = {
...permission.toJSON(),
children: children.length > 0 ? children : undefined
};
tree.push(node);
}
}
return tree;
}
/**
* 权限管理控制器
*/
class PermissionController {
/**
* 获取权限列表
*/
async getPermissions(req, res) {
try {
const {
page = 1,
limit = 10,
module,
type,
status = 'active',
keyword
} = req.query;
const offset = (page - 1) * limit;
const where = { status };
// 模块筛选
if (module) {
where.module = module;
}
// 类型筛选
if (type) {
where.type = type;
}
// 关键词搜索
if (keyword) {
where[Op.or] = [
{ name: { [Op.like]: `%${keyword}%` } },
{ code: { [Op.like]: `%${keyword}%` } },
{ description: { [Op.like]: `%${keyword}%` } }
];
}
const { count, rows } = await Permission.findAndCountAll({
where,
include: [
{
model: Permission,
as: 'parent',
attributes: ['id', 'name', 'code']
},
{
model: Permission,
as: 'children',
attributes: ['id', 'name', 'code', 'type']
}
],
order: [['sort_order', 'ASC'], ['id', 'ASC']],
limit: parseInt(limit),
offset: parseInt(offset)
});
res.json(responseFormat.success({
permissions: rows,
pagination: {
total: count,
page: parseInt(page),
limit: parseInt(limit),
pages: Math.ceil(count / limit)
}
}, '获取权限列表成功'));
} catch (error) {
console.error('获取权限列表失败:', error);
res.status(500).json(responseFormat.error('获取权限列表失败'));
}
}
/**
* 获取权限树形结构
*/
async getPermissionTree(req, res) {
try {
const { module, type } = req.query;
const where = { status: 'active' };
if (module) {
where.module = module;
}
if (type) {
where.type = type;
}
const permissions = await Permission.findAll({
where,
order: [['sort_order', 'ASC'], ['id', 'ASC']]
});
// 构建树形结构
const tree = buildPermissionTree(permissions);
res.json(responseFormat.success(tree, '获取权限树成功'));
} catch (error) {
console.error('获取权限树失败:', error);
res.status(500).json(responseFormat.error('获取权限树失败'));
}
}
/**
* 获取单个权限详情
*/
async getPermissionById(req, res) {
try {
const { id } = req.params;
const permission = await Permission.findByPk(id, {
include: [
{
model: Permission,
as: 'parent',
attributes: ['id', 'name', 'code']
},
{
model: Permission,
as: 'children',
attributes: ['id', 'name', 'code', 'type']
}
]
});
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
res.json(responseFormat.success(permission, '获取权限详情成功'));
} catch (error) {
console.error('获取权限详情失败:', error);
res.status(500).json(responseFormat.error('获取权限详情失败'));
}
}
/**
* 创建权限
*/
async createPermission(req, res) {
try {
const {
name,
code,
description,
module,
type = 'operation',
parent_id,
sort_order = 0
} = req.body;
// 验证必填字段
if (!name || !code || !module) {
return res.status(400).json(responseFormat.error('权限名称、权限代码和所属模块为必填项'));
}
// 检查权限代码是否已存在
const existingPermission = await Permission.findOne({ where: { code } });
if (existingPermission) {
return res.status(400).json(responseFormat.error('权限代码已存在'));
}
// 如果有父权限,验证父权限是否存在
if (parent_id) {
const parentPermission = await Permission.findByPk(parent_id);
if (!parentPermission) {
return res.status(400).json(responseFormat.error('父权限不存在'));
}
}
const permission = await Permission.create({
name,
code,
description,
module,
type,
parent_id,
sort_order,
status: 'active'
});
res.status(201).json(responseFormat.created(permission, '创建权限成功'));
} catch (error) {
console.error('创建权限失败:', error);
res.status(500).json(responseFormat.error('创建权限失败'));
}
}
/**
* 更新权限
*/
async updatePermission(req, res) {
try {
const { id } = req.params;
const {
name,
code,
description,
module,
type,
parent_id,
sort_order,
status
} = req.body;
const permission = await Permission.findByPk(id);
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
// 如果修改了权限代码,检查是否与其他权限冲突
if (code && code !== permission.code) {
const existingPermission = await Permission.findOne({
where: {
code,
id: { [Op.ne]: id }
}
});
if (existingPermission) {
return res.status(400).json(responseFormat.error('权限代码已存在'));
}
}
// 如果有父权限,验证父权限是否存在且不是自己
if (parent_id) {
if (parent_id == id) {
return res.status(400).json(responseFormat.error('不能将自己设为父权限'));
}
const parentPermission = await Permission.findByPk(parent_id);
if (!parentPermission) {
return res.status(400).json(responseFormat.error('父权限不存在'));
}
}
await permission.update({
name: name || permission.name,
code: code || permission.code,
description: description !== undefined ? description : permission.description,
module: module || permission.module,
type: type || permission.type,
parent_id: parent_id !== undefined ? parent_id : permission.parent_id,
sort_order: sort_order !== undefined ? sort_order : permission.sort_order,
status: status || permission.status
});
res.json(responseFormat.success(permission, '更新权限成功'));
} catch (error) {
console.error('更新权限失败:', error);
res.status(500).json(responseFormat.error('更新权限失败'));
}
}
/**
* 删除权限
*/
async deletePermission(req, res) {
try {
const { id } = req.params;
const permission = await Permission.findByPk(id);
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
// 检查是否有子权限
const childrenCount = await Permission.count({ where: { parent_id: id } });
if (childrenCount > 0) {
return res.status(400).json(responseFormat.error('该权限下还有子权限,无法删除'));
}
// 检查是否有角色在使用该权限
const rolePermissionCount = await RolePermission.count({ where: { permission_id: id } });
if (rolePermissionCount > 0) {
return res.status(400).json(responseFormat.error('该权限正在被角色使用,无法删除'));
}
// 检查是否有菜单在使用该权限
const menuPermissionCount = await MenuPermission.count({ where: { permission_id: id } });
if (menuPermissionCount > 0) {
return res.status(400).json(responseFormat.error('该权限正在被菜单使用,无法删除'));
}
await permission.destroy();
res.json(responseFormat.success(null, '删除权限成功'));
} catch (error) {
console.error('删除权限失败:', error);
res.status(500).json(responseFormat.error('删除权限失败'));
}
}
/**
* 获取角色权限
*/
async getRolePermissions(req, res) {
try {
const { roleId } = req.params;
const role = await Role.findByPk(roleId, {
include: [
{
model: Permission,
as: 'rolePermissions',
through: {
attributes: ['granted']
}
}
]
});
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
res.json(responseFormat.success({
role: {
id: role.id,
name: role.name,
description: role.description
},
permissions: role.rolePermissions
}, '获取角色权限成功'));
} catch (error) {
console.error('获取角色权限失败:', error);
res.status(500).json(responseFormat.error('获取角色权限失败'));
}
}
/**
* 分配角色权限
*/
async assignRolePermissions(req, res) {
try {
const { roleId } = req.params;
const { permissionIds } = req.body;
if (!Array.isArray(permissionIds)) {
return res.status(400).json(responseFormat.error('权限ID列表格式错误'));
}
const role = await Role.findByPk(roleId);
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
// 删除现有的角色权限关联
await RolePermission.destroy({ where: { role_id: roleId } });
// 创建新的角色权限关联
if (permissionIds.length > 0) {
const rolePermissions = permissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
await RolePermission.bulkCreate(rolePermissions);
}
res.json(responseFormat.success(null, '分配角色权限成功'));
} catch (error) {
console.error('分配角色权限失败:', error);
res.status(500).json(responseFormat.error('分配角色权限失败'));
}
}
/**
* 获取模块列表
*/
async getModules(req, res) {
try {
const modules = await Permission.findAll({
attributes: ['module'],
group: ['module'],
order: [['module', 'ASC']]
});
const moduleList = modules.map(item => item.module);
res.json(responseFormat.success(moduleList, '获取模块列表成功'));
} catch (error) {
console.error('获取模块列表失败:', error);
res.status(500).json(responseFormat.error('获取模块列表失败'));
}
}
}
module.exports = new PermissionController();

View File

@@ -0,0 +1,420 @@
const { Role, Permission, RolePermission, User } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
/**
* 角色权限管理控制器
* 专门处理角色权限的分配、管理和动态调用
*/
class RolePermissionController {
/**
* 获取所有角色及其权限
*/
async getAllRolesWithPermissions(req, res) {
try {
const roles = await Role.findAll({
order: [['id', 'ASC']]
});
const rolesData = roles.map(role => {
let permissions = [];
if (Array.isArray(role.permissions)) {
permissions = role.permissions;
} else if (typeof role.permissions === 'string') {
try {
permissions = JSON.parse(role.permissions);
} catch (e) {
permissions = [];
}
}
return {
id: role.id,
name: role.name,
description: role.description,
status: role.status,
permissions: permissions,
permissionCount: permissions.length
};
});
res.json(responseFormat.success({
roles: rolesData,
total: rolesData.length
}, '获取角色权限列表成功'));
} catch (error) {
console.error('获取角色权限列表失败:', error);
res.status(500).json(responseFormat.error('获取角色权限列表失败'));
}
}
/**
* 获取所有权限
*/
async getAllPermissions(req, res) {
try {
const permissions = await Permission.findAll({
order: [['id', 'ASC']]
});
res.json(responseFormat.success(permissions, '获取权限列表成功'));
} catch (error) {
console.error('获取权限列表失败:', error);
res.status(500).json(responseFormat.error('获取权限列表失败'));
}
}
/**
* 获取指定角色的权限详情
*/
async getRolePermissionDetail(req, res) {
try {
const { roleId } = req.params;
const role = await Role.findByPk(roleId);
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
// 获取所有权限用于对比
const allPermissions = await Permission.findAll({
attributes: ['id', 'name', 'code', 'description', 'module', 'type', 'parent_id'],
order: [['module', 'ASC'], ['id', 'ASC']]
});
// 构建权限树结构
const controller = this;
const permissionTree = controller.buildPermissionTree(allPermissions);
// 获取角色已分配的权限代码
let assignedPermissionCodes = [];
if (Array.isArray(role.permissions)) {
assignedPermissionCodes = role.permissions;
} else if (typeof role.permissions === 'string') {
try {
assignedPermissionCodes = JSON.parse(role.permissions);
} catch (e) {
assignedPermissionCodes = [];
}
}
// 标记已分配的权限
const markedPermissions = controller.markAssignedPermissionsByCode(permissionTree, assignedPermissionCodes);
res.json(responseFormat.success({
role: {
id: role.id,
name: role.name,
description: role.description,
status: role.status
},
assignedPermissions: assignedPermissionCodes,
allPermissions: markedPermissions,
assignedCount: assignedPermissionCodes.length,
totalCount: allPermissions.length
}, '获取角色权限详情成功'));
} catch (error) {
console.error('获取角色权限详情失败:', error);
res.status(500).json(responseFormat.error('获取角色权限详情失败'));
}
}
/**
* 批量分配角色权限
*/
async batchAssignPermissions(req, res) {
try {
const { roleId } = req.params;
const { permissionIds, operation = 'replace' } = req.body;
if (!Array.isArray(permissionIds)) {
return res.status(400).json(responseFormat.error('权限ID列表格式错误'));
}
const role = await Role.findByPk(roleId);
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
// 验证权限ID是否存在
const validPermissions = await Permission.findAll({
where: { id: { [Op.in]: permissionIds } },
attributes: ['id']
});
const validPermissionIds = validPermissions.map(p => p.id);
const invalidIds = permissionIds.filter(id => !validPermissionIds.includes(id));
if (invalidIds.length > 0) {
return res.status(400).json(responseFormat.error(`无效的权限ID: ${invalidIds.join(', ')}`));
}
// 根据操作类型处理权限分配
if (operation === 'replace') {
// 替换模式:删除现有权限,添加新权限
await RolePermission.destroy({ where: { role_id: roleId } });
if (permissionIds.length > 0) {
const rolePermissions = permissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
await RolePermission.bulkCreate(rolePermissions);
}
} else if (operation === 'add') {
// 添加模式:只添加新权限
const existingPermissions = await RolePermission.findAll({
where: { role_id: roleId },
attributes: ['permission_id']
});
const existingIds = existingPermissions.map(p => p.permission_id);
const newPermissionIds = permissionIds.filter(id => !existingIds.includes(id));
if (newPermissionIds.length > 0) {
const rolePermissions = newPermissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
await RolePermission.bulkCreate(rolePermissions);
}
} else if (operation === 'remove') {
// 移除模式:删除指定权限
await RolePermission.destroy({
where: {
role_id: roleId,
permission_id: { [Op.in]: permissionIds }
}
});
}
res.json(responseFormat.success(null, `${operation === 'replace' ? '替换' : operation === 'add' ? '添加' : '移除'}角色权限成功`));
} catch (error) {
console.error('批量分配角色权限失败:', error);
res.status(500).json(responseFormat.error('批量分配角色权限失败'));
}
}
/**
* 复制角色权限
*/
async copyRolePermissions(req, res) {
try {
const { sourceRoleId, targetRoleId } = req.body;
if (!sourceRoleId || !targetRoleId) {
return res.status(400).json(responseFormat.error('源角色ID和目标角色ID不能为空'));
}
if (sourceRoleId === targetRoleId) {
return res.status(400).json(responseFormat.error('源角色和目标角色不能相同'));
}
// 验证角色存在
const [sourceRole, targetRole] = await Promise.all([
Role.findByPk(sourceRoleId),
Role.findByPk(targetRoleId)
]);
if (!sourceRole) {
return res.status(404).json(responseFormat.error('源角色不存在'));
}
if (!targetRole) {
return res.status(404).json(responseFormat.error('目标角色不存在'));
}
// 获取源角色的权限
const sourcePermissions = await RolePermission.findAll({
where: { role_id: sourceRoleId },
attributes: ['permission_id']
});
// 删除目标角色现有权限
await RolePermission.destroy({ where: { role_id: targetRoleId } });
// 复制权限到目标角色
if (sourcePermissions.length > 0) {
const targetPermissions = sourcePermissions.map(p => ({
role_id: targetRoleId,
permission_id: p.permission_id,
granted: true
}));
await RolePermission.bulkCreate(targetPermissions);
}
res.json(responseFormat.success(null, `成功将 ${sourceRole.name} 的权限复制到 ${targetRole.name}`));
} catch (error) {
console.error('复制角色权限失败:', error);
res.status(500).json(responseFormat.error('复制角色权限失败'));
}
}
/**
* 检查用户权限
*/
async checkUserPermission(req, res) {
try {
const { userId, permissionCode } = req.params;
const user = await User.findByPk(userId, {
include: [
{
model: Role,
as: 'role',
include: [
{
model: Permission,
as: 'rolePermissions',
where: { code: permissionCode },
required: false,
through: {
attributes: ['granted']
}
}
]
}
]
});
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
const hasPermission = user.role &&
user.role.rolePermissions &&
user.role.rolePermissions.length > 0 &&
user.role.rolePermissions[0].RolePermission.granted;
res.json(responseFormat.success({
userId: user.id,
username: user.username,
roleName: user.role ? user.role.name : null,
permissionCode,
hasPermission,
checkTime: new Date()
}, '权限检查完成'));
} catch (error) {
console.error('检查用户权限失败:', error);
res.status(500).json(responseFormat.error('检查用户权限失败'));
}
}
/**
* 获取权限统计信息
*/
async getPermissionStats(req, res) {
try {
// 统计各种数据
const [
totalRoles,
totalPermissions,
moduleStats,
roles
] = await Promise.all([
Role.count(),
Permission.count(),
Permission.findAll({
attributes: [
'module',
[Permission.sequelize.fn('COUNT', Permission.sequelize.col('id')), 'count']
],
group: ['module'],
order: [['module', 'ASC']]
}),
Role.findAll({
attributes: ['id', 'name', 'permissions'],
order: [['name', 'ASC']]
})
]);
// 计算总分配数和角色权限分布
let totalAssignments = 0;
const roleDistribution = roles.map(role => {
let permissions = [];
if (Array.isArray(role.permissions)) {
permissions = role.permissions;
} else if (typeof role.permissions === 'string') {
try {
permissions = JSON.parse(role.permissions);
} catch (e) {
permissions = [];
}
}
const permissionCount = permissions.length;
totalAssignments += permissionCount;
return {
roleId: role.id,
roleName: role.name,
permissionCount
};
});
res.json(responseFormat.success({
overview: {
totalRoles,
totalPermissions,
totalAssignments,
averagePermissionsPerRole: totalRoles > 0 ? Math.round(totalAssignments / totalRoles) : 0
},
moduleDistribution: moduleStats.map(stat => ({
module: stat.module,
count: parseInt(stat.dataValues.count)
})),
roleDistribution
}, '获取权限统计成功'));
} catch (error) {
console.error('获取权限统计失败:', error);
res.status(500).json(responseFormat.error('获取权限统计失败'));
}
}
/**
* 构建权限树
*/
buildPermissionTree(permissions, parentId = null) {
const tree = [];
for (const permission of permissions) {
if (permission.parent_id === parentId) {
const children = this.buildPermissionTree(permissions, permission.id);
const node = {
...(permission.toJSON ? permission.toJSON() : permission),
children: children.length > 0 ? children : undefined
};
tree.push(node);
}
}
return tree;
}
/**
* 标记已分配的权限
*/
markAssignedPermissions(permissions, assignedIds) {
return permissions.map(permission => ({
...permission,
assigned: assignedIds.includes(permission.id),
children: permission.children ?
this.markAssignedPermissions(permission.children, assignedIds) :
undefined
}));
}
/**
* 根据权限代码标记已分配的权限
*/
markAssignedPermissionsByCode(permissions, assignedCodes) {
return permissions.map(permission => ({
...permission,
assigned: assignedCodes.includes(permission.code),
children: permission.children ?
this.markAssignedPermissionsByCode(permission.children, assignedCodes) :
undefined
}));
}
}
module.exports = new RolePermissionController();

View File

@@ -77,7 +77,7 @@ const getSystemLogs = async (req, res) => {
const { page = 1, limit = 50, level, start_date, end_date } = req.query;
const offset = (page - 1) * limit;
// 模拟日志数据
// 模拟日志数据 - 扩展更多有意义的日志记录
const mockLogs = [
{
id: 1,
@@ -96,9 +96,93 @@ const getSystemLogs = async (req, res) => {
{
id: 3,
level: 'warning',
message: 'Redis连接失败',
message: 'Redis连接失败,使用内存缓存',
timestamp: new Date(Date.now() - 1000 * 120).toISOString(),
user: 'system'
},
{
id: 4,
level: 'info',
message: '用户 admin 登录成功',
timestamp: new Date(Date.now() - 1000 * 180).toISOString(),
user: 'admin'
},
{
id: 5,
level: 'info',
message: '新增保险申请:车险申请 - 申请人:张三',
timestamp: new Date(Date.now() - 1000 * 240).toISOString(),
user: 'zhangsan'
},
{
id: 6,
level: 'info',
message: '保单生效:保单号 POL-2024-001 - 投保人:李四',
timestamp: new Date(Date.now() - 1000 * 300).toISOString(),
user: 'lisi'
},
{
id: 7,
level: 'warning',
message: '理赔申请待审核:理赔号 CLM-2024-001 - 申请人:王五',
timestamp: new Date(Date.now() - 1000 * 360).toISOString(),
user: 'wangwu'
},
{
id: 8,
level: 'info',
message: '新用户注册:用户名 zhaoliu',
timestamp: new Date(Date.now() - 1000 * 420).toISOString(),
user: 'system'
},
{
id: 9,
level: 'error',
message: '支付接口调用失败:订单号 ORD-2024-001',
timestamp: new Date(Date.now() - 1000 * 480).toISOString(),
user: 'system'
},
{
id: 10,
level: 'info',
message: '保险类型更新:新增意外险产品',
timestamp: new Date(Date.now() - 1000 * 540).toISOString(),
user: 'admin'
},
{
id: 11,
level: 'info',
message: '系统备份完成:数据库备份成功',
timestamp: new Date(Date.now() - 1000 * 600).toISOString(),
user: 'system'
},
{
id: 12,
level: 'warning',
message: '磁盘空间不足警告:剩余空间 15%',
timestamp: new Date(Date.now() - 1000 * 660).toISOString(),
user: 'system'
},
{
id: 13,
level: 'info',
message: '理赔审核通过:理赔号 CLM-2024-002 - 赔付金额 ¥5000',
timestamp: new Date(Date.now() - 1000 * 720).toISOString(),
user: 'admin'
},
{
id: 14,
level: 'info',
message: '保单续费成功:保单号 POL-2024-002 - 续费期限 1年',
timestamp: new Date(Date.now() - 1000 * 780).toISOString(),
user: 'system'
},
{
id: 15,
level: 'error',
message: '短信发送失败:手机号 138****8888',
timestamp: new Date(Date.now() - 1000 * 840).toISOString(),
user: 'system'
}
];
@@ -125,7 +209,7 @@ const getSystemLogs = async (req, res) => {
page: parseInt(page),
limit: parseInt(limit),
total: filteredLogs.length,
pages: Math.ceil(filteredLogs.length / limit)
pages: Math.ceil(filteredLogs.length / parseInt(limit))
}
}, '获取系统日志成功'));
} catch (error) {

View File

@@ -1,6 +1,7 @@
const { User, Role } = require('../models');
const { Op } = require('sequelize');
const responseFormat = require('../utils/response');
const crypto = require('crypto');
// 获取用户列表
const getUsers = async (req, res) => {
@@ -379,6 +380,112 @@ const uploadAvatar = async (req, res) => {
}
};
// 生成固定token
const generateFixedToken = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
// 生成32位随机token
const fixedToken = crypto.randomBytes(32).toString('hex');
// 更新用户的固定token
await user.update({ fixed_token: fixedToken });
res.json(responseFormat.success({
fixed_token: fixedToken,
user_id: user.id,
username: user.username
}, '固定Token生成成功'));
} catch (error) {
console.error('生成固定Token错误:', error);
if (error.name === 'SequelizeUniqueConstraintError') {
return res.status(400).json(responseFormat.error('Token生成失败请重试'));
}
res.status(500).json(responseFormat.error('生成固定Token失败'));
}
};
// 重新生成固定token
const regenerateFixedToken = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
// 生成新的32位随机token
const newFixedToken = crypto.randomBytes(32).toString('hex');
// 更新用户的固定token
await user.update({ fixed_token: newFixedToken });
res.json(responseFormat.success({
fixed_token: newFixedToken,
user_id: user.id,
username: user.username
}, '固定Token重新生成成功'));
} catch (error) {
console.error('重新生成固定Token错误:', error);
if (error.name === 'SequelizeUniqueConstraintError') {
return res.status(400).json(responseFormat.error('Token生成失败请重试'));
}
res.status(500).json(responseFormat.error('重新生成固定Token失败'));
}
};
// 删除固定token
const deleteFixedToken = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
// 清除用户的固定token
await user.update({ fixed_token: null });
res.json(responseFormat.success(null, '固定Token删除成功'));
} catch (error) {
console.error('删除固定Token错误:', error);
res.status(500).json(responseFormat.error('删除固定Token失败'));
}
};
// 获取用户的固定token信息
const getFixedTokenInfo = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByPk(id, {
attributes: ['id', 'username', 'fixed_token']
});
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
res.json(responseFormat.success({
userId: user.id,
username: user.username,
hasToken: !!user.fixed_token,
tokenPreview: user.fixed_token ? `${user.fixed_token.substring(0, 8)}...` : null,
createdAt: user.fixed_token ? new Date().toISOString() : null
}, '获取固定Token信息成功'));
} catch (error) {
console.error('获取固定Token信息错误:', error);
res.status(500).json(responseFormat.error('获取固定Token信息失败'));
}
};
module.exports = {
getUsers,
getUser,
@@ -389,5 +496,9 @@ module.exports = {
getProfile,
updateProfile,
changePassword,
uploadAvatar
uploadAvatar,
generateFixedToken,
regenerateFixedToken,
deleteFixedToken,
getFixedTokenInfo
};

View File

@@ -0,0 +1,73 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000/api';
const ADMIN_TOKEN = '5659725423f665a8bf5053b37e624ea86387f9113ae77ac75fc102012a349180';
// 创建axios实例
const api = axios.create({
baseURL: BASE_URL,
headers: {
'Authorization': `Bearer ${ADMIN_TOKEN}`,
'Content-Type': 'application/json'
}
});
async function debugAPIResponse() {
console.log('调试API响应格式...\n');
try {
console.log('测试获取所有角色及其权限...');
const response = await api.get('/role-permissions/roles');
console.log('完整响应:', JSON.stringify(response.data, null, 2));
console.log('数据路径检查:');
console.log('- response.data:', typeof response.data);
console.log('- response.data.data:', typeof response.data.data);
// 尝试解析data字段
let parsedData = response.data.data;
if (typeof parsedData === 'string') {
try {
parsedData = JSON.parse(parsedData);
console.log('- 解析后的data:', typeof parsedData);
console.log('- 解析后的data.roles:', typeof parsedData?.roles);
console.log('- 解析后的data.roles 是数组吗:', Array.isArray(parsedData?.roles));
if (parsedData?.roles) {
console.log('- 角色数量:', parsedData.roles.length);
}
} catch (e) {
console.log('- 无法解析data字段:', e.message);
}
} else {
console.log('- response.data.data.roles:', typeof response.data.data?.roles);
console.log('- response.data.data.roles 是数组吗:', Array.isArray(response.data.data?.roles));
if (response.data.data?.roles) {
console.log('- 角色数量:', response.data.data.roles.length);
}
}
} catch (error) {
console.log('错误:', {
code: error.response?.status,
status: error.response?.data?.status,
data: error.response?.data?.data,
message: error.response?.data?.message,
});
}
try {
console.log('\n测试获取特定角色的详细权限...');
const response = await api.get('/role-permissions/roles/1/permissions');
console.log('完整响应:', JSON.stringify(response.data, null, 2));
} catch (error) {
console.log('错误:', error.response?.data || error.message);
}
try {
console.log('\n测试权限统计...');
const response = await api.get('/role-permissions/stats');
console.log('完整响应:', JSON.stringify(response.data, null, 2));
} catch (error) {
console.log('错误:', error.response?.data || error.message);
}
}
debugAPIResponse().catch(console.error);

View File

@@ -0,0 +1,222 @@
# API认证文档
## 概述
保险端口系统API支持两种认证方式
1. **JWT令牌认证**用于Web应用的用户会话认证
2. **固定令牌认证**用于API访问的长期认证
## 认证方式
### 1. JWT令牌认证
#### 获取JWT令牌
```http
POST /auth/login
Content-Type: application/json
{
"username": "admin",
"password": "123456"
}
```
#### 响应示例
```json
{
"code": 200,
"status": "success",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com"
}
}
}
```
#### 使用JWT令牌
在请求头中添加Authorization字段
```http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
### 2. 固定令牌认证
#### 生成固定令牌
管理员可以为用户生成固定令牌:
```http
POST /users/{id}/fixed-token
Authorization: Bearer <admin_jwt_token>
```
#### 响应示例
```json
{
"code": 200,
"status": "success",
"data": {
"token": "ft_1234567890abcdef1234567890abcdef12345678",
"message": "固定令牌生成成功"
}
}
```
#### 使用固定令牌
在请求头中添加Authorization字段
```http
Authorization: Bearer ft_1234567890abcdef1234567890abcdef12345678
```
## 固定令牌管理
### 获取令牌信息
```http
GET /users/{id}/fixed-token
Authorization: Bearer <admin_jwt_token>
```
### 重新生成令牌
```http
PUT /users/{id}/fixed-token
Authorization: Bearer <admin_jwt_token>
```
### 删除令牌
```http
DELETE /users/{id}/fixed-token
Authorization: Bearer <admin_jwt_token>
```
## 权限控制
### 权限模型
系统采用基于角色的权限控制RBAC每个用户都有一个角色角色包含多个权限。
### 权限格式
权限格式为:`资源:操作`
- `user:read` - 读取用户信息
- `user:create` - 创建用户
- `user:update` - 更新用户信息
- `user:delete` - 删除用户
### 常用权限列表
| 权限 | 描述 |
|------|------|
| `user:read` | 查看用户信息 |
| `user:create` | 创建用户 |
| `user:update` | 更新用户信息 |
| `user:delete` | 删除用户 |
| `insurance_applications:read` | 查看保险申请 |
| `insurance_applications:create` | 创建保险申请 |
| `insurance_applications:update` | 更新保险申请 |
| `insurance_applications:delete` | 删除保险申请 |
| `policies:read` | 查看保单 |
| `policies:create` | 创建保单 |
| `policies:update` | 更新保单 |
| `policies:delete` | 删除保单 |
| `claims:read` | 查看理赔 |
| `claims:create` | 创建理赔 |
| `claims:update` | 更新理赔 |
| `claims:delete` | 删除理赔 |
## 错误响应
### 401 未授权
```json
{
"code": 401,
"status": "error",
"message": "未授权访问",
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
### 403 权限不足
```json
{
"code": 403,
"status": "error",
"message": "权限不足",
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
### 令牌过期
```json
{
"code": 401,
"status": "error",
"message": "令牌已过期",
"timestamp": "2024-01-01T00:00:00.000Z"
}
```
## 安全注意事项
### JWT令牌
1. JWT令牌有效期为24小时
2. 令牌包含用户信息和权限
3. 令牌在服务器端无法撤销,只能等待过期
### 固定令牌
1. 固定令牌永不过期,除非手动删除
2. 固定令牌具有与用户相同的权限
3. 固定令牌应妥善保管,避免泄露
4. 建议定期更换固定令牌
### 最佳实践
1. 在生产环境中使用HTTPS
2. 定期轮换固定令牌
3. 监控API访问日志
4. 及时撤销不再使用的令牌
5. 使用最小权限原则
## 示例代码
### JavaScript (Axios)
```javascript
// 使用JWT令牌
const jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
const response = await axios.get('/users', {
headers: {
'Authorization': `Bearer ${jwtToken}`
}
});
// 使用固定令牌
const fixedToken = 'ft_1234567890abcdef1234567890abcdef12345678';
const response = await axios.get('/users', {
headers: {
'Authorization': `Bearer ${fixedToken}`
}
});
```
### Python (requests)
```python
import requests
# 使用JWT令牌
jwt_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
headers = {'Authorization': f'Bearer {jwt_token}'}
response = requests.get('http://localhost:3000/users', headers=headers)
# 使用固定令牌
fixed_token = 'ft_1234567890abcdef1234567890abcdef12345678'
headers = {'Authorization': f'Bearer {fixed_token}'}
response = requests.get('http://localhost:3000/users', headers=headers)
```
### cURL
```bash
# 使用JWT令牌
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
http://localhost:3000/users
# 使用固定令牌
curl -H "Authorization: Bearer ft_1234567890abcdef1234567890abcdef12345678" \
http://localhost:3000/users
```

View File

@@ -1,32 +1,97 @@
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const Role = require('../models/Role');
const responseFormat = require('../utils/response');
// JWT认证中间件
const jwtAuth = (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json(responseFormat.error('未提供认证令牌'));
}
const jwtAuth = async (req, res, next) => {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const authHeader = req.headers.authorization;
console.log('Authorization header:', authHeader);
// 检查Token类型只接受访问令牌
if (decoded.type && decoded.type !== 'access') {
return res.status(401).json(responseFormat.error('无效的令牌类型'));
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json(responseFormat.error('未提供有效的认证token'));
}
const token = authHeader.substring(7);
console.log('提取的token:', token);
console.log('token类型:', typeof token);
console.log('token长度:', token.length);
// 首先尝试固定token验证
const user = await User.findOne({
where: {
fixed_token: token,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (user) {
// 固定token验证成功
req.user = {
id: user.id,
userId: user.id,
username: user.username,
role_id: user.role_id,
role: user.role,
permissions: user.role ? user.role.permissions : [],
type: 'fixed_token'
};
return next();
}
// 如果固定token验证失败尝试JWT验证
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (decoded.type !== 'access') {
return res.status(401).json(responseFormat.error('Token类型错误'));
}
// 验证用户是否存在且状态正常
const jwtUser = await User.findOne({
where: {
id: decoded.id,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (!jwtUser) {
return res.status(401).json(responseFormat.error('用户不存在或已被禁用'));
}
req.user = {
id: decoded.id,
userId: decoded.id,
username: decoded.username,
role_id: decoded.role_id,
role: jwtUser.role,
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : []),
type: 'jwt'
};
return next();
} catch (jwtError) {
if (jwtError.name === 'TokenExpiredError') {
return res.status(401).json(responseFormat.error('Token已过期', 'TOKEN_EXPIRED'));
} else if (jwtError.name === 'JsonWebTokenError') {
return res.status(401).json(responseFormat.error('Token无效'));
}
throw jwtError;
}
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json(responseFormat.error('认证令牌已过期', 'TOKEN_EXPIRED'));
} else if (error.name === 'JsonWebTokenError') {
return res.status(401).json(responseFormat.error('认证令牌无效', 'TOKEN_INVALID'));
} else {
return res.status(401).json(responseFormat.error('认证失败', 'AUTH_FAILED'));
}
console.error('Token验证错误:', error);
return res.status(500).json(responseFormat.error('服务器内部错误'));
}
};
@@ -58,8 +123,16 @@ const checkPermission = (resource, action) => {
}
}
// 如果JWT中没有权限信息从数据库查询
if (permissions.length === 0) {
const requiredPermission = `${resource}:${action}`;
// 首先检查JWT中的权限
let hasPermission = permissions.includes(requiredPermission) ||
permissions.includes('*:*') ||
permissions.includes('*');
// 如果JWT中没有权限信息或者JWT权限不足从数据库查询最新权限
if (permissions.length === 0 || !hasPermission) {
console.log('JWT权限不足或为空从数据库获取最新权限...');
const { Role } = require('../models');
const userRole = await Role.findByPk(user.role_id);
@@ -81,14 +154,19 @@ const checkPermission = (resource, action) => {
} else if (Array.isArray(rolePermissions)) {
permissions = rolePermissions;
}
console.log('从数据库获取的最新权限:', permissions);
// 重新检查权限
hasPermission = permissions.includes(requiredPermission) ||
permissions.includes('*:*') ||
permissions.includes('*');
}
const requiredPermission = `${resource}:${action}`;
console.log('权限检查 - 用户权限:', permissions, '需要权限:', requiredPermission);
console.log('权限检查 - 用户权限:', permissions, '需要权限:', requiredPermission, '是否有权限:', hasPermission);
// 检查权限或超级管理员权限
if (!permissions.includes(requiredPermission) && !permissions.includes('*:*') && !permissions.includes('*')) {
if (!hasPermission) {
console.log('权限检查失败 - 权限不足');
return res.status(403).json(responseFormat.error('权限不足'));
}

View File

@@ -0,0 +1,119 @@
const User = require('../models/User');
const Role = require('../models/Role');
/**
* 固定Token认证中间件
* 支持JWT token和固定token两种认证方式
*/
const fixedTokenAuth = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({
status: 'error',
message: '未提供认证token'
});
}
// 检查是否为Bearer token格式
if (authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7);
// 首先尝试固定token验证
const user = await User.findOne({
where: {
fixed_token: token,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (user) {
// 固定token验证成功
req.user = {
id: user.id,
username: user.username,
role_id: user.role_id,
role: user.role,
permissions: user.role ? user.role.permissions : []
};
return next();
}
// 如果固定token验证失败尝试JWT验证
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (decoded.type !== 'access') {
return res.status(401).json({
status: 'error',
message: 'Token类型错误'
});
}
// 验证用户是否存在且状态正常
const jwtUser = await User.findOne({
where: {
id: decoded.userId,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (!jwtUser) {
return res.status(401).json({
status: 'error',
message: '用户不存在或已被禁用'
});
}
req.user = {
id: jwtUser.id,
username: jwtUser.username,
role_id: jwtUser.role_id,
role: jwtUser.role,
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : [])
};
return next();
} catch (jwtError) {
if (jwtError.name === 'TokenExpiredError') {
return res.status(401).json({
status: 'error',
code: 'TOKEN_EXPIRED',
message: 'Token已过期'
});
} else if (jwtError.name === 'JsonWebTokenError') {
return res.status(401).json({
status: 'error',
message: 'Token无效'
});
}
throw jwtError;
}
} else {
return res.status(401).json({
status: 'error',
message: 'Token格式错误请使用Bearer格式'
});
}
} catch (error) {
console.error('Token验证错误:', error);
return res.status(500).json({
status: 'error',
message: '服务器内部错误'
});
}
};
module.exports = fixedTokenAuth;

View File

@@ -0,0 +1,16 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'fixed_token', {
type: Sequelize.STRING(255),
allowNull: true,
unique: true,
comment: '用户固定token用于API访问验证'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('users', 'fixed_token');
}
};

View File

@@ -1,75 +0,0 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
// 添加参保类型字段
await queryInterface.addColumn('insurance_applications', 'insurance_category', {
type: Sequelize.STRING(50),
allowNull: true,
comment: '参保类型(如:牛、羊、猪等)',
after: 'insurance_type_id'
});
// 添加申请数量字段
await queryInterface.addColumn('insurance_applications', 'application_quantity', {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: 1,
comment: '申请数量',
validate: {
min: 1
},
after: 'insurance_category'
});
// 添加备注字段
await queryInterface.addColumn('insurance_applications', 'remarks', {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注信息',
after: 'review_notes'
});
// 更新状态枚举值以匹配UI显示
await queryInterface.changeColumn('insurance_applications', 'status', {
type: Sequelize.ENUM(
'pending', // 待初审
'initial_approved', // 初审通过待复核
'under_review', // 已支付待复核
'approved', // 已支付
'rejected', // 已拒绝
'paid' // 已支付
),
allowNull: false,
defaultValue: 'pending',
comment: '申请状态'
});
// 添加索引
await queryInterface.addIndex('insurance_applications', ['insurance_category'], {
name: 'idx_insurance_applications_category'
});
await queryInterface.addIndex('insurance_applications', ['application_quantity'], {
name: 'idx_insurance_applications_quantity'
});
},
down: async (queryInterface, Sequelize) => {
// 移除索引
await queryInterface.removeIndex('insurance_applications', 'idx_insurance_applications_category');
await queryInterface.removeIndex('insurance_applications', 'idx_insurance_applications_quantity');
// 移除添加的字段
await queryInterface.removeColumn('insurance_applications', 'insurance_category');
await queryInterface.removeColumn('insurance_applications', 'application_quantity');
await queryInterface.removeColumn('insurance_applications', 'remarks');
// 恢复原始状态枚举
await queryInterface.changeColumn('insurance_applications', 'status', {
type: Sequelize.ENUM('pending', 'approved', 'rejected', 'under_review'),
allowNull: false,
defaultValue: 'pending'
});
}
};

View File

@@ -0,0 +1,109 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
// 创建权限表
await queryInterface.createTable('permissions', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '权限ID'
},
name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '权限名称'
},
code: {
type: Sequelize.STRING(100),
allowNull: false,
unique: true,
comment: '权限代码'
},
description: {
type: Sequelize.TEXT,
allowNull: true,
comment: '权限描述'
},
module: {
type: Sequelize.STRING(50),
allowNull: false,
comment: '所属模块'
},
type: {
type: Sequelize.ENUM('menu', 'operation'),
allowNull: false,
defaultValue: 'operation',
comment: '权限类型menu-菜单权限operation-操作权限'
},
parent_id: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '父权限ID',
references: {
model: 'permissions',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
status: {
type: Sequelize.ENUM('active', 'inactive'),
allowNull: false,
defaultValue: 'active',
comment: '状态'
},
sort_order: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
comment: '更新时间'
}
}, {
comment: '权限表'
});
// 创建索引
await queryInterface.addIndex('permissions', ['code'], {
name: 'idx_permissions_code'
});
await queryInterface.addIndex('permissions', ['module'], {
name: 'idx_permissions_module'
});
await queryInterface.addIndex('permissions', ['type'], {
name: 'idx_permissions_type'
});
await queryInterface.addIndex('permissions', ['parent_id'], {
name: 'idx_permissions_parent_id'
});
await queryInterface.addIndex('permissions', ['status'], {
name: 'idx_permissions_status'
});
},
async down(queryInterface, Sequelize) {
// 删除索引
await queryInterface.removeIndex('permissions', 'idx_permissions_code');
await queryInterface.removeIndex('permissions', 'idx_permissions_module');
await queryInterface.removeIndex('permissions', 'idx_permissions_type');
await queryInterface.removeIndex('permissions', 'idx_permissions_parent_id');
await queryInterface.removeIndex('permissions', 'idx_permissions_status');
// 删除表
await queryInterface.dropTable('permissions');
}
};

View File

@@ -0,0 +1,82 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
// 创建角色权限关联表
await queryInterface.createTable('role_permissions', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '关联ID'
},
role_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '角色ID',
references: {
model: 'roles',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
permission_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '权限ID',
references: {
model: 'permissions',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
granted: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否授权'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
comment: '更新时间'
}
}, {
comment: '角色权限关联表'
});
// 创建唯一索引
await queryInterface.addIndex('role_permissions', ['role_id', 'permission_id'], {
name: 'uk_role_permission',
unique: true
});
// 创建普通索引
await queryInterface.addIndex('role_permissions', ['role_id'], {
name: 'idx_role_permissions_role_id'
});
await queryInterface.addIndex('role_permissions', ['permission_id'], {
name: 'idx_role_permissions_permission_id'
});
},
async down(queryInterface, Sequelize) {
// 删除索引
await queryInterface.removeIndex('role_permissions', 'uk_role_permission');
await queryInterface.removeIndex('role_permissions', 'idx_role_permissions_role_id');
await queryInterface.removeIndex('role_permissions', 'idx_role_permissions_permission_id');
// 删除表
await queryInterface.dropTable('role_permissions');
}
};

View File

@@ -0,0 +1,82 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
// 创建菜单权限关联表
await queryInterface.createTable('menu_permissions', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '关联ID'
},
menu_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '菜单ID',
references: {
model: 'menus',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
permission_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '权限ID',
references: {
model: 'permissions',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
required: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否必需权限'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
comment: '更新时间'
}
}, {
comment: '菜单权限关联表'
});
// 创建唯一索引
await queryInterface.addIndex('menu_permissions', ['menu_id', 'permission_id'], {
name: 'uk_menu_permission',
unique: true
});
// 创建普通索引
await queryInterface.addIndex('menu_permissions', ['menu_id'], {
name: 'idx_menu_permissions_menu_id'
});
await queryInterface.addIndex('menu_permissions', ['permission_id'], {
name: 'idx_menu_permissions_permission_id'
});
},
async down(queryInterface, Sequelize) {
// 删除索引
await queryInterface.removeIndex('menu_permissions', 'uk_menu_permission');
await queryInterface.removeIndex('menu_permissions', 'idx_menu_permissions_menu_id');
await queryInterface.removeIndex('menu_permissions', 'idx_menu_permissions_permission_id');
// 删除表
await queryInterface.dropTable('menu_permissions');
}
};

View File

@@ -0,0 +1,42 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const MenuPermission = sequelize.define('MenuPermission', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '关联ID'
},
menu_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '菜单ID'
},
permission_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '权限ID'
},
required: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否必需权限'
}
}, {
tableName: 'menu_permissions',
timestamps: true,
underscored: true,
indexes: [
{
fields: ['menu_id', 'permission_id'],
unique: true,
name: 'uk_menu_permission'
},
{ fields: ['menu_id'] },
{ fields: ['permission_id'] }
]
});
module.exports = MenuPermission;

View File

@@ -0,0 +1,93 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Permission = sequelize.define('Permission', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '权限ID'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '权限名称',
validate: {
len: [2, 100]
}
},
code: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
comment: '权限代码',
validate: {
len: [2, 100],
is: /^[a-zA-Z0-9_:]+$/ // 只允许字母、数字、下划线和冒号
}
},
description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '权限描述'
},
module: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '所属模块',
validate: {
len: [2, 50]
}
},
type: {
type: DataTypes.ENUM('menu', 'operation'),
allowNull: false,
defaultValue: 'operation',
comment: '权限类型menu-菜单权限operation-操作权限'
},
parent_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '父权限ID'
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
allowNull: false,
defaultValue: 'active',
comment: '状态'
},
sort_order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序'
}
}, {
tableName: 'permissions',
timestamps: true,
underscored: true,
indexes: [
{ fields: ['code'], unique: true },
{ fields: ['module'] },
{ fields: ['type'] },
{ fields: ['parent_id'] },
{ fields: ['status'] }
]
});
// 定义自关联关系
Permission.hasMany(Permission, {
as: 'children',
foreignKey: 'parent_id',
onDelete: 'SET NULL',
onUpdate: 'CASCADE'
});
Permission.belongsTo(Permission, {
as: 'parent',
foreignKey: 'parent_id',
onDelete: 'SET NULL',
onUpdate: 'CASCADE'
});
module.exports = Permission;

View File

@@ -0,0 +1,42 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const RolePermission = sequelize.define('RolePermission', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '关联ID'
},
role_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '角色ID'
},
permission_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '权限ID'
},
granted: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否授权'
}
}, {
tableName: 'role_permissions',
timestamps: true,
underscored: true,
indexes: [
{
fields: ['role_id', 'permission_id'],
unique: true,
name: 'uk_role_permission'
},
{ fields: ['role_id'] },
{ fields: ['permission_id'] }
]
});
module.exports = RolePermission;

View File

@@ -65,6 +65,12 @@ const User = sequelize.define('User', {
avatar: {
type: DataTypes.STRING(255),
allowNull: true
},
fixed_token: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
comment: '用户固定token用于API访问验证'
}
}, {
tableName: 'users',

View File

@@ -2,6 +2,9 @@
const { sequelize } = require('../config/database');
const User = require('./User');
const Role = require('./Role');
const Permission = require('./Permission');
const RolePermission = require('./RolePermission');
const MenuPermission = require('./MenuPermission');
const InsuranceApplication = require('./InsuranceApplication');
const InsuranceType = require('./InsuranceType');
const Policy = require('./Policy');
@@ -22,6 +25,46 @@ const OperationLog = require('./OperationLog');
User.belongsTo(Role, { foreignKey: 'role_id', as: 'role' });
Role.hasMany(User, { foreignKey: 'role_id', as: 'users' });
// 角色和权限多对多关联(通过中间表)
Role.belongsToMany(Permission, {
through: RolePermission,
foreignKey: 'role_id',
otherKey: 'permission_id',
as: 'rolePermissions'
});
Permission.belongsToMany(Role, {
through: RolePermission,
foreignKey: 'permission_id',
otherKey: 'role_id',
as: 'permissionRoles'
});
// 角色权限关联表的直接关联
RolePermission.belongsTo(Role, { foreignKey: 'role_id', as: 'role' });
RolePermission.belongsTo(Permission, { foreignKey: 'permission_id', as: 'permission' });
Role.hasMany(RolePermission, { foreignKey: 'role_id', as: 'rolePermissionRecords' });
Permission.hasMany(RolePermission, { foreignKey: 'permission_id', as: 'permissionRoleRecords' });
// 菜单和权限多对多关联(通过中间表)
Menu.belongsToMany(Permission, {
through: MenuPermission,
foreignKey: 'menu_id',
otherKey: 'permission_id',
as: 'permissions'
});
Permission.belongsToMany(Menu, {
through: MenuPermission,
foreignKey: 'permission_id',
otherKey: 'menu_id',
as: 'menus'
});
// 菜单权限关联表的直接关联
MenuPermission.belongsTo(Menu, { foreignKey: 'menu_id', as: 'menu' });
MenuPermission.belongsTo(Permission, { foreignKey: 'permission_id', as: 'permission' });
Menu.hasMany(MenuPermission, { foreignKey: 'menu_id', as: 'menuPermissions' });
Permission.hasMany(MenuPermission, { foreignKey: 'permission_id', as: 'menuPermissions' });
// 保险申请和保险类型关联
InsuranceApplication.belongsTo(InsuranceType, {
foreignKey: 'insurance_type_id',
@@ -82,6 +125,26 @@ Policy.hasMany(Claim, {
as: 'claims'
});
// 理赔和客户关联
Claim.belongsTo(User, {
foreignKey: 'customer_id',
as: 'customer'
});
User.hasMany(Claim, {
foreignKey: 'customer_id',
as: 'customer_claims'
});
// 理赔和审核人关联
Claim.belongsTo(User, {
foreignKey: 'reviewer_id',
as: 'reviewer'
});
User.hasMany(Claim, {
foreignKey: 'reviewer_id',
as: 'reviewed_claims'
});
// 监管任务关联
SupervisoryTask.belongsTo(User, {
foreignKey: 'assignedTo',
@@ -198,6 +261,9 @@ module.exports = {
sequelize,
User,
Role,
Permission,
RolePermission,
MenuPermission,
InsuranceApplication,
InsuranceType,
Policy,

View File

@@ -0,0 +1,222 @@
const express = require('express');
const router = express.Router();
const dashboardController = require('../controllers/dashboardController');
const { jwtAuth, checkPermission } = require('../middleware/auth');
/**
* @swagger
* tags:
* name: Dashboard
* description: 仪表板相关接口
*/
/**
* @swagger
* /api/dashboard/stats:
* get:
* summary: 获取仪表板统计数据
* tags: [Dashboard]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* type: object
* properties:
* totalApplications:
* type: integer
* description: 总申请数
* todayApplications:
* type: integer
* description: 今日申请数
* monthApplications:
* type: integer
* description: 本月申请数
* applicationGrowthRate:
* type: number
* description: 申请增长率
* totalPolicies:
* type: integer
* description: 总保单数
* activePolicies:
* type: integer
* description: 有效保单数
* policyGrowthRate:
* type: number
* description: 保单增长率
* totalClaims:
* type: integer
* description: 总理赔数
* pendingClaims:
* type: integer
* description: 待处理理赔数
* claimProcessingRate:
* type: string
* description: 理赔处理率
* totalUsers:
* type: integer
* description: 总用户数
* quickStats:
* type: object
* properties:
* newApplicationsToday:
* type: integer
* pendingReviews:
* type: integer
* activeUsers:
* type: integer
* systemAlerts:
* type: integer
* message:
* type: string
* example: 获取仪表板统计数据成功
* timestamp:
* type: string
* format: date-time
* 401:
* description: 未授权
* 500:
* description: 服务器内部错误
*/
router.get('/stats', jwtAuth, checkPermission('dashboard', 'read'), dashboardController.getStats);
/**
* @swagger
* /api/dashboard/recent-activities:
* get:
* summary: 获取最近活动
* tags: [Dashboard]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: limit
* schema:
* type: integer
* default: 20
* description: 返回记录数量限制
* responses:
* 200:
* description: 成功获取最近活动
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* type:
* type: string
* description: 活动类型
* title:
* type: string
* description: 活动标题
* description:
* type: string
* description: 活动描述
* timestamp:
* type: string
* format: date-time
* user:
* type: string
* description: 操作用户
* level:
* type: string
* description: 日志级别
* message:
* type: string
* example: 获取最近活动成功
* timestamp:
* type: string
* format: date-time
* 401:
* description: 未授权
* 500:
* description: 服务器内部错误
*/
router.get('/recent-activities', jwtAuth, checkPermission('dashboard', 'read'), dashboardController.getRecentActivities);
/**
* @swagger
* /api/dashboard/chart-data:
* get:
* summary: 获取图表数据
* tags: [Dashboard]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: type
* schema:
* type: string
* enum: [applications, policies, claims]
* default: applications
* description: 图表数据类型
* - in: query
* name: period
* schema:
* type: string
* enum: [7d, 30d, 90d]
* default: 7d
* description: 时间周期
* responses:
* 200:
* description: 成功获取图表数据
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* type: array
* items:
* type: object
* properties:
* date:
* type: string
* format: date
* value:
* type: integer
* message:
* type: string
* example: 获取图表数据成功
* timestamp:
* type: string
* format: date-time
* 401:
* description: 未授权
* 500:
* description: 服务器内部错误
*/
router.get('/chart-data', jwtAuth, checkPermission('dashboard', 'read'), dashboardController.getChartData);
module.exports = router;

View File

@@ -1,6 +1,7 @@
const express = require('express');
const router = express.Router();
const installationTaskController = require('../controllers/installationTaskController');
const { jwtAuth, requirePermission } = require('../middleware/auth');
/**
* @swagger
@@ -59,7 +60,7 @@ const installationTaskController = require('../controllers/installationTaskContr
* 200:
* description: 获取成功
*/
router.get('/', installationTaskController.getInstallationTasks);
router.get('/', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.getInstallationTasks);
/**
* @swagger
@@ -115,13 +116,13 @@ router.get('/', installationTaskController.getInstallationTasks);
* 201:
* description: 创建成功
*/
router.post('/', installationTaskController.createInstallationTask);
router.post('/', jwtAuth, requirePermission('installation_tasks:create'), installationTaskController.createInstallationTask);
/**
* @swagger
* /api/installation-tasks/{id}:
* get:
* summary: 获取待安装任务详情
* summary: 根据ID获取待安装任务详情
* tags: [InstallationTasks]
* parameters:
* - in: path
@@ -133,8 +134,10 @@ router.post('/', installationTaskController.createInstallationTask);
* responses:
* 200:
* description: 获取成功
* 404:
* description: 任务不存在
*/
router.get('/:id', installationTaskController.getInstallationTaskById);
router.get('/:id', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.getInstallationTaskById);
/**
* @swagger
@@ -142,12 +145,14 @@ router.get('/:id', installationTaskController.getInstallationTaskById);
* put:
* summary: 更新待安装任务
* tags: [InstallationTasks]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* type: string
* description: 任务ID
* requestBody:
* required: true
@@ -162,16 +167,13 @@ router.get('/:id', installationTaskController.getInstallationTaskById);
* priority:
* type: string
* enum: [低, 中, 高, 紧急]
* assignedTo:
* type: integer
* installationCompletedAt:
* notes:
* type: string
* format: date-time
* responses:
* 200:
* description: 更新成功
*/
router.put('/:id', installationTaskController.updateInstallationTask);
router.put('/:id', jwtAuth, requirePermission('installation_tasks:update'), installationTaskController.updateInstallationTask);
/**
* @swagger
@@ -189,8 +191,10 @@ router.put('/:id', installationTaskController.updateInstallationTask);
* responses:
* 200:
* description: 删除成功
* 404:
* description: 任务不存在
*/
router.delete('/:id', installationTaskController.deleteInstallationTask);
router.delete('/:id', jwtAuth, requirePermission('installation_tasks:delete'), installationTaskController.deleteInstallationTask);
/**
* @swagger
@@ -224,7 +228,7 @@ router.delete('/:id', installationTaskController.deleteInstallationTask);
* 200:
* description: 操作成功
*/
router.post('/batch/operate', installationTaskController.batchOperateInstallationTasks);
router.post('/batch/operate', jwtAuth, requirePermission('installation_tasks:update'), installationTaskController.batchOperateInstallationTasks);
/**
* @swagger
@@ -232,6 +236,8 @@ router.post('/batch/operate', installationTaskController.batchOperateInstallatio
* get:
* summary: 导出待安装任务数据
* tags: [InstallationTasks]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: ids
@@ -242,7 +248,7 @@ router.post('/batch/operate', installationTaskController.batchOperateInstallatio
* 200:
* description: 导出成功
*/
router.get('/export', installationTaskController.exportInstallationTasks);
router.get('/export', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.exportInstallationTasks);
/**
* @swagger
@@ -250,10 +256,12 @@ router.get('/export', installationTaskController.exportInstallationTasks);
* get:
* summary: 获取安装任务统计数据
* tags: [InstallationTasks]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
*/
router.get('/stats', installationTaskController.getInstallationTaskStats);
router.get('/stats', jwtAuth, requirePermission('installation_tasks:read'), installationTaskController.getInstallationTaskStats);
module.exports = router;

View File

@@ -10,37 +10,7 @@ const { jwtAuth } = require('../middleware/auth');
* description: 菜单管理相关接口
*/
/**
* @swagger
* /api/menus/public:
* get:
* summary: 获取公开菜单列表(无需认证)
* tags: [Menus]
* responses:
* 200:
* description: 成功获取菜单列表
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* type: array
* items:
* $ref: '#/components/schemas/Menu'
* message:
* type: string
* example: 获取菜单成功
* 500:
* description: 服务器内部错误
*/
router.get('/public', menuController.getMenus);
// 移除了公共API路径所有菜单API都需要认证
/**
* @swagger

View File

@@ -0,0 +1,542 @@
const express = require('express');
const router = express.Router();
const permissionController = require('../controllers/permissionController');
const { jwtAuth } = require('../middleware/auth');
// 所有权限管理路由都需要认证
router.use(jwtAuth);
/**
* @swagger
* components:
* schemas:
* Permission:
* type: object
* required:
* - name
* - code
* - module
* properties:
* id:
* type: integer
* description: 权限ID
* name:
* type: string
* description: 权限名称
* code:
* type: string
* description: 权限代码
* description:
* type: string
* description: 权限描述
* module:
* type: string
* description: 所属模块
* type:
* type: string
* enum: [menu, operation]
* description: 权限类型
* parent_id:
* type: integer
* description: 父权限ID
* status:
* type: string
* enum: [active, inactive]
* description: 状态
* sort_order:
* type: integer
* description: 排序
* created_at:
* type: string
* format: date-time
* description: 创建时间
* updated_at:
* type: string
* format: date-time
* description: 更新时间
*/
/**
* @swagger
* /api/permissions:
* get:
* summary: 获取权限列表
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: 页码
* - in: query
* name: limit
* schema:
* type: integer
* default: 10
* description: 每页数量
* - in: query
* name: module
* schema:
* type: string
* description: 模块筛选
* - in: query
* name: type
* schema:
* type: string
* enum: [menu, operation]
* description: 类型筛选
* - in: query
* name: status
* schema:
* type: string
* enum: [active, inactive]
* default: active
* description: 状态筛选
* - in: query
* name: keyword
* schema:
* type: string
* description: 关键词搜索
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取权限列表成功
* data:
* type: object
* properties:
* permissions:
* type: array
* items:
* $ref: '#/components/schemas/Permission'
* pagination:
* type: object
* properties:
* total:
* type: integer
* page:
* type: integer
* limit:
* type: integer
* pages:
* type: integer
*/
router.get('/', permissionController.getPermissions);
/**
* @swagger
* /api/permissions/tree:
* get:
* summary: 获取权限树形结构
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: module
* schema:
* type: string
* description: 模块筛选
* - in: query
* name: type
* schema:
* type: string
* enum: [menu, operation]
* description: 类型筛选
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取权限树成功
* data:
* type: array
* items:
* $ref: '#/components/schemas/Permission'
*/
router.get('/tree', permissionController.getPermissionTree);
/**
* @swagger
* /api/permissions/modules:
* get:
* summary: 获取模块列表
* tags: [权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取模块列表成功
* data:
* type: array
* items:
* type: string
*/
router.get('/modules', permissionController.getModules);
/**
* @swagger
* /api/permissions/{id}:
* get:
* summary: 获取权限详情
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 权限ID
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取权限详情成功
* data:
* $ref: '#/components/schemas/Permission'
* 404:
* description: 权限不存在
*/
router.get('/:id', permissionController.getPermissionById);
/**
* @swagger
* /api/permissions:
* post:
* summary: 创建权限
* tags: [权限管理]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - code
* - module
* properties:
* name:
* type: string
* description: 权限名称
* code:
* type: string
* description: 权限代码
* description:
* type: string
* description: 权限描述
* module:
* type: string
* description: 所属模块
* type:
* type: string
* enum: [menu, operation]
* default: operation
* description: 权限类型
* parent_id:
* type: integer
* description: 父权限ID
* sort_order:
* type: integer
* default: 0
* description: 排序
* responses:
* 201:
* description: 创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 创建权限成功
* data:
* $ref: '#/components/schemas/Permission'
* 400:
* description: 请求参数错误
*/
router.post('/', permissionController.createPermission);
/**
* @swagger
* /api/permissions/{id}:
* put:
* summary: 更新权限
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 权限ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description: 权限名称
* code:
* type: string
* description: 权限代码
* description:
* type: string
* description: 权限描述
* module:
* type: string
* description: 所属模块
* type:
* type: string
* enum: [menu, operation]
* description: 权限类型
* parent_id:
* type: integer
* description: 父权限ID
* sort_order:
* type: integer
* description: 排序
* status:
* type: string
* enum: [active, inactive]
* description: 状态
* responses:
* 200:
* description: 更新成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 更新权限成功
* data:
* $ref: '#/components/schemas/Permission'
* 404:
* description: 权限不存在
*/
router.put('/:id', permissionController.updatePermission);
/**
* @swagger
* /api/permissions/{id}:
* delete:
* summary: 删除权限
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 权限ID
* responses:
* 200:
* description: 删除成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 删除权限成功
* 404:
* description: 权限不存在
* 400:
* description: 权限正在使用中,无法删除
*/
router.delete('/:id', permissionController.deletePermission);
/**
* @swagger
* /api/permissions/roles/{roleId}:
* get:
* summary: 获取角色权限
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: roleId
* required: true
* schema:
* type: integer
* description: 角色ID
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取角色权限成功
* data:
* type: object
* properties:
* role:
* type: object
* properties:
* id:
* type: integer
* name:
* type: string
* description:
* type: string
* permissions:
* type: array
* items:
* $ref: '#/components/schemas/Permission'
* 404:
* description: 角色不存在
*/
router.get('/roles/:roleId', permissionController.getRolePermissions);
/**
* @swagger
* /api/permissions/roles/{roleId}/assign:
* post:
* summary: 分配角色权限
* tags: [权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: roleId
* required: true
* schema:
* type: integer
* description: 角色ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - permissionIds
* properties:
* permissionIds:
* type: array
* items:
* type: integer
* description: 权限ID列表
* example:
* permissionIds: [1, 2, 3, 4]
* responses:
* 200:
* description: 分配成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 分配角色权限成功
* 404:
* description: 角色不存在
* 400:
* description: 请求参数错误
*/
router.post('/roles/:roleId/assign', permissionController.assignRolePermissions);
module.exports = router;

View File

@@ -0,0 +1,384 @@
const express = require('express');
const router = express.Router();
const rolePermissionController = require('../controllers/rolePermissionController');
const { jwtAuth } = require('../middleware/auth');
// 应用认证中间件
router.use(jwtAuth);
/**
* @swagger
* components:
* schemas:
* RolePermissionAssignment:
* type: object
* properties:
* roleId:
* type: integer
* description: 角色ID
* permissionIds:
* type: array
* items:
* type: integer
* description: 权限ID列表
* operation:
* type: string
* enum: [replace, add, remove]
* default: replace
* description: 操作类型
*/
/**
* @swagger
* /api/role-permissions:
* get:
* summary: 获取所有角色及其权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取角色权限列表成功
* data:
* type: object
* properties:
* roles:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* name:
* type: string
* description:
* type: string
* permissions:
* type: array
* permissionCount:
* type: integer
* total:
* type: integer
*/
router.get('/', rolePermissionController.getAllRolesWithPermissions);
/**
* @swagger
* /api/role-permissions/permissions:
* get:
* summary: 获取所有权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
*/
router.get('/permissions', rolePermissionController.getAllPermissions);
/**
* @swagger
* /api/role-permissions/roles:
* get:
* summary: 获取所有角色及其权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
*/
router.get('/roles', rolePermissionController.getAllRolesWithPermissions);
/**
* @swagger
* /api/role-permissions/roles/{roleId}/permissions:
* get:
* summary: 获取指定角色的权限详情
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: roleId
* required: true
* schema:
* type: integer
* description: 角色ID
* responses:
* 200:
* description: 获取成功
* 404:
* description: 角色不存在
*/
router.get('/roles/:roleId/permissions', rolePermissionController.getRolePermissionDetail);
/**
* @swagger
* /api/role-permissions/stats:
* get:
* summary: 获取权限统计信息
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
*/
router.get('/stats', rolePermissionController.getPermissionStats);
/**
* @swagger
* /api/role-permissions/{roleId}:
* get:
* summary: 获取指定角色的权限详情
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: roleId
* required: true
* schema:
* type: integer
* description: 角色ID
* responses:
* 200:
* description: 获取成功
* 404:
* description: 角色不存在
*/
router.get('/:roleId', rolePermissionController.getRolePermissionDetail);
/**
* @swagger
* /api/role-permissions/{roleId}/assign:
* post:
* summary: 批量分配角色权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: roleId
* required: true
* schema:
* type: integer
* description: 角色ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - permissionIds
* properties:
* permissionIds:
* type: array
* items:
* type: integer
* description: 权限ID列表
* operation:
* type: string
* enum: [replace, add, remove]
* default: replace
* description: 操作类型
* examples:
* replace:
* summary: 替换权限
* value:
* permissionIds: [1, 2, 3, 4]
* operation: "replace"
* add:
* summary: 添加权限
* value:
* permissionIds: [5, 6]
* operation: "add"
* remove:
* summary: 移除权限
* value:
* permissionIds: [1, 2]
* operation: "remove"
* responses:
* 200:
* description: 分配成功
* 400:
* description: 请求参数错误
* 404:
* description: 角色不存在
*/
router.post('/:roleId/assign', rolePermissionController.batchAssignPermissions);
/**
* @swagger
* /api/role-permissions/copy:
* post:
* summary: 复制角色权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - sourceRoleId
* - targetRoleId
* properties:
* sourceRoleId:
* type: integer
* description: 源角色ID
* targetRoleId:
* type: integer
* description: 目标角色ID
* example:
* sourceRoleId: 1
* targetRoleId: 2
* responses:
* 200:
* description: 复制成功
* 400:
* description: 请求参数错误
* 404:
* description: 角色不存在
*/
router.post('/copy', rolePermissionController.copyRolePermissions);
/**
* @swagger
* /api/role-permissions/check/{userId}/{permissionCode}:
* get:
* summary: 检查用户权限
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: userId
* required: true
* schema:
* type: integer
* description: 用户ID
* - in: path
* name: permissionCode
* required: true
* schema:
* type: string
* description: 权限代码
* responses:
* 200:
* description: 检查完成
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 权限检查完成
* data:
* type: object
* properties:
* userId:
* type: integer
* username:
* type: string
* roleName:
* type: string
* permissionCode:
* type: string
* hasPermission:
* type: boolean
* checkTime:
* type: string
* format: date-time
* 404:
* description: 用户不存在
*/
router.get('/check/:userId/:permissionCode', rolePermissionController.checkUserPermission);
/**
* @swagger
* /api/role-permissions/stats:
* get:
* summary: 获取权限统计信息
* tags: [角色权限管理]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 获取权限统计成功
* data:
* type: object
* properties:
* overview:
* type: object
* properties:
* totalRoles:
* type: integer
* totalPermissions:
* type: integer
* totalAssignments:
* type: integer
* averagePermissionsPerRole:
* type: integer
* moduleDistribution:
* type: array
* items:
* type: object
* properties:
* module:
* type: string
* count:
* type: integer
* roleDistribution:
* type: array
* items:
* type: object
* properties:
* roleId:
* type: integer
* roleName:
* type: string
* permissionCount:
* type: integer
*/
module.exports = router;

View File

@@ -1,7 +1,7 @@
const express = require('express');
const router = express.Router();
const SupervisoryTaskController = require('../controllers/supervisoryTaskController');
const auth = require('../middleware/auth');
const { jwtAuth, requirePermission } = require('../middleware/auth');
/**
* @swagger
@@ -62,7 +62,7 @@ const auth = require('../middleware/auth');
* 200:
* description: 获取成功
*/
router.get('/', SupervisoryTaskController.getList);
router.get('/', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.getList);
/**
* @swagger
@@ -131,7 +131,7 @@ router.get('/', SupervisoryTaskController.getList);
* 201:
* description: 创建成功
*/
router.post('/', SupervisoryTaskController.create);
router.post('/', jwtAuth, requirePermission('supervision_tasks:create'), SupervisoryTaskController.create);
/**
* @swagger
@@ -152,7 +152,7 @@ router.post('/', SupervisoryTaskController.create);
* 200:
* description: 获取成功
*/
router.get('/:id', SupervisoryTaskController.getById);
router.get('/:id', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.getById);
/**
* @swagger
@@ -167,8 +167,8 @@ router.get('/:id', SupervisoryTaskController.getById);
* name: id
* required: true
* schema:
* type: integer
* description: 监管任务ID
* type: string
* description: 任务ID
* requestBody:
* required: true
* content:
@@ -179,18 +179,22 @@ router.get('/:id', SupervisoryTaskController.getById);
* status:
* type: string
* enum: [pending, processing, completed, rejected]
* description: 状态
* assignedTo:
* type: integer
* type: string
* description: 分配给
* priority:
* type: string
* enum: [low, medium, high, urgent]
* remarks:
* description: 优先级
* notes:
* type: string
* description: 备注
* responses:
* 200:
* description: 更新成功
*/
router.put('/:id', SupervisoryTaskController.update);
router.put('/:id', jwtAuth, requirePermission('supervision_tasks:update'), SupervisoryTaskController.update);
/**
* @swagger
@@ -205,13 +209,13 @@ router.put('/:id', SupervisoryTaskController.update);
* name: id
* required: true
* schema:
* type: integer
* description: 监管任务ID
* type: string
* description: 任务ID
* responses:
* 200:
* description: 删除成功
*/
router.delete('/:id', SupervisoryTaskController.delete);
router.delete('/:id', jwtAuth, requirePermission('supervision_tasks:delete'), SupervisoryTaskController.delete);
/**
* @swagger
@@ -247,13 +251,13 @@ router.delete('/:id', SupervisoryTaskController.delete);
* 200:
* description: 操作成功
*/
router.post('/batch/operate', SupervisoryTaskController.bulkCreate);
router.post('/batch/operate', jwtAuth, requirePermission('supervision_tasks:create'), SupervisoryTaskController.bulkCreate);
/**
* @swagger
* /api/supervision-tasks/stats:
* get:
* summary: 获取监管任务统计数据
* summary: 获取监管任务统计信息
* tags: [SupervisionTasks]
* security:
* - bearerAuth: []
@@ -261,6 +265,6 @@ router.post('/batch/operate', SupervisoryTaskController.bulkCreate);
* 200:
* description: 获取成功
*/
router.get('/stats', SupervisoryTaskController.getStatistics);
router.get('/stats', jwtAuth, requirePermission('supervision_tasks:read'), SupervisoryTaskController.getStatistics);
module.exports = router;

View File

@@ -34,4 +34,172 @@ router.put('/change-password', jwtAuth, userController.changePassword);
// 上传头像(不需要特殊权限,用户可以上传自己的头像)
router.post('/avatar', jwtAuth, userController.uploadAvatar);
/**
* @swagger
* /users/{id}/fixed-token:
* get:
* summary: 获取用户固定令牌信息
* description: 获取指定用户的固定令牌信息,包括是否已生成令牌、令牌预览等
* tags: [用户管理]
* security:
* - bearerAuth: []
* - fixedTokenAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 用户ID
* responses:
* 200:
* description: 获取成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* $ref: '#/components/schemas/FixedTokenInfo'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* $ref: '#/components/responses/ForbiddenError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* post:
* summary: 生成用户固定令牌
* description: 为指定用户生成新的固定令牌用于API访问验证
* tags: [用户管理]
* security:
* - bearerAuth: []
* - fixedTokenAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 用户ID
* responses:
* 200:
* description: 生成成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* $ref: '#/components/schemas/FixedTokenGenerated'
* 400:
* description: 用户已有固定令牌
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* $ref: '#/components/responses/ForbiddenError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* put:
* summary: 重新生成用户固定令牌
* description: 重新生成指定用户的固定令牌,原令牌将失效
* tags: [用户管理]
* security:
* - bearerAuth: []
* - fixedTokenAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 用户ID
* responses:
* 200:
* description: 重新生成成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* data:
* $ref: '#/components/schemas/FixedTokenGenerated'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* $ref: '#/components/responses/ForbiddenError'
* 404:
* $ref: '#/components/responses/NotFoundError'
* delete:
* summary: 删除用户固定令牌
* description: 删除指定用户的固定令牌,令牌将立即失效
* tags: [用户管理]
* security:
* - bearerAuth: []
* - fixedTokenAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 用户ID
* responses:
* 200:
* description: 删除成功
* content:
* application/json:
* schema:
* type: object
* properties:
* code:
* type: integer
* example: 200
* status:
* type: string
* example: success
* message:
* type: string
* example: 固定令牌删除成功
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* $ref: '#/components/responses/ForbiddenError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
// 固定Token管理路由需要管理员权限
// 获取用户固定Token信息
router.get('/:id/fixed-token', jwtAuth, checkPermission('user', 'read'), userController.getFixedTokenInfo);
// 生成用户固定Token
router.post('/:id/fixed-token', jwtAuth, checkPermission('user', 'update'), userController.generateFixedToken);
// 重新生成用户固定Token
router.put('/:id/fixed-token', jwtAuth, checkPermission('user', 'update'), userController.regenerateFixedToken);
// 删除用户固定Token
router.delete('/:id/fixed-token', jwtAuth, checkPermission('user', 'delete'), userController.deleteFixedToken);
module.exports = router;

View File

@@ -26,7 +26,8 @@ async function fixAdminPermissions() {
'insurance_type:read', 'insurance_type:create', 'insurance_type:update', 'insurance_type:delete', 'insurance_type:review',
'policy:read', 'policy:create', 'policy:update', 'policy:delete',
'claim:read', 'claim:create', 'claim:update', 'claim:review',
'system:read', 'system:update', 'system:admin'
'system:read', 'system:update', 'system:admin',
'dashboard:read', 'dashboard:update'
];
await adminRole.update({

View File

@@ -0,0 +1,124 @@
const mysql = require('mysql2/promise');
require('dotenv').config();
/**
* 修复admin用户权限脚本
* 添加缺失的supervision_tasks:read和installation_tasks:read权限
*/
async function fixAdminPermissions() {
let connection;
try {
// 创建数据库连接
connection = await mysql.createConnection({
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'aiotAiot123!',
database: process.env.DB_NAME || 'insurance_data'
});
console.log('✅ 数据库连接成功');
// 查询当前admin角色的权限
const [adminRole] = await connection.query(
'SELECT id, name, permissions FROM roles WHERE name = ?',
['admin']
);
if (adminRole.length === 0) {
console.log('❌ 未找到admin角色');
return;
}
console.log('📋 当前admin角色权限:', adminRole[0].permissions);
// 解析当前权限
let currentPermissions;
try {
currentPermissions = JSON.parse(adminRole[0].permissions);
} catch (error) {
console.log('⚠️ 权限格式错误,重新设置为完整权限列表');
currentPermissions = [];
}
// 定义完整的权限列表
const fullPermissions = [
"user:read", "user:create", "user:update", "user:delete",
"insurance:read", "insurance:create", "insurance:update", "insurance:delete", "insurance:review",
"policy:read", "policy:create", "policy:update", "policy:delete",
"livestock_policy:read", "livestock_policy:create", "livestock_policy:update", "livestock_policy:delete",
"claim:read", "claim:create", "claim:update", "claim:review",
"supervision_tasks:read", "supervision_tasks:create", "supervision_tasks:update", "supervision_tasks:delete",
"installation_tasks:read", "installation_tasks:create", "installation_tasks:update", "installation_tasks:delete",
"system:read", "system:update", "system:admin",
"data:read", "data:create", "data:update", "data:delete"
];
// 更新admin角色权限
await connection.query(
'UPDATE roles SET permissions = ? WHERE name = ?',
[JSON.stringify(fullPermissions), 'admin']
);
console.log('✅ admin角色权限已更新');
console.log('📋 新的权限列表:', JSON.stringify(fullPermissions, null, 2));
// 查询admin用户并更新其权限缓存
const [adminUsers] = await connection.query(
'SELECT id, username FROM users WHERE role_id = ?',
[adminRole[0].id]
);
console.log(`✅ 找到 ${adminUsers.length} 个admin用户权限已生效`);
// 验证权限更新
const [updatedRole] = await connection.query(
'SELECT permissions FROM roles WHERE name = ?',
['admin']
);
let updatedPermissions;
try {
updatedPermissions = JSON.parse(updatedRole[0].permissions);
} catch (error) {
console.log('⚠️ 验证时权限格式错误:', updatedRole[0].permissions);
updatedPermissions = fullPermissions; // 使用我们刚设置的权限
}
const hasSupervisionRead = updatedPermissions.includes('supervision_tasks:read');
const hasInstallationRead = updatedPermissions.includes('installation_tasks:read');
console.log('🔍 权限验证:');
console.log(` - supervision_tasks:read: ${hasSupervisionRead ? '✅' : '❌'}`);
console.log(` - installation_tasks:read: ${hasInstallationRead ? '✅' : '❌'}`);
if (hasSupervisionRead && hasInstallationRead) {
console.log('🎉 权限修复成功!');
} else {
console.log('❌ 权限修复失败,请检查数据库');
}
} catch (error) {
console.error('❌ 权限修复失败:', error.message);
throw error;
} finally {
// 关闭数据库连接
if (connection) {
await connection.end();
console.log('🔌 数据库连接已关闭');
}
}
}
// 执行脚本
fixAdminPermissions()
.then(() => {
console.log('✨ 权限修复任务已完成');
process.exit(0);
})
.catch(() => {
console.error('❌ 权限修复任务失败');
process.exit(1);
});

View File

@@ -0,0 +1,431 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
// 插入基础权限数据
await queryInterface.bulkInsert('permissions', [
// 系统管理模块权限
{
name: '系统管理',
code: 'system:manage',
description: '系统管理模块访问权限',
module: 'system',
type: 'menu',
parent_id: null,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '用户管理',
code: 'system:user:manage',
description: '用户管理页面访问权限',
module: 'system',
type: 'menu',
parent_id: 1,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '用户查看',
code: 'system:user:view',
description: '查看用户列表权限',
module: 'system',
type: 'operation',
parent_id: 2,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '用户新增',
code: 'system:user:create',
description: '新增用户权限',
module: 'system',
type: 'operation',
parent_id: 2,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '用户编辑',
code: 'system:user:edit',
description: '编辑用户权限',
module: 'system',
type: 'operation',
parent_id: 2,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '用户删除',
code: 'system:user:delete',
description: '删除用户权限',
module: 'system',
type: 'operation',
parent_id: 2,
status: 'active',
sort_order: 4,
created_at: new Date(),
updated_at: new Date()
},
{
name: '角色管理',
code: 'system:role:manage',
description: '角色管理页面访问权限',
module: 'system',
type: 'menu',
parent_id: 1,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '角色查看',
code: 'system:role:view',
description: '查看角色列表权限',
module: 'system',
type: 'operation',
parent_id: 7,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '角色新增',
code: 'system:role:create',
description: '新增角色权限',
module: 'system',
type: 'operation',
parent_id: 7,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '角色编辑',
code: 'system:role:edit',
description: '编辑角色权限',
module: 'system',
type: 'operation',
parent_id: 7,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '角色删除',
code: 'system:role:delete',
description: '删除角色权限',
module: 'system',
type: 'operation',
parent_id: 7,
status: 'active',
sort_order: 4,
created_at: new Date(),
updated_at: new Date()
},
{
name: '权限管理',
code: 'system:permission:manage',
description: '权限管理页面访问权限',
module: 'system',
type: 'menu',
parent_id: 1,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '权限查看',
code: 'system:permission:view',
description: '查看权限列表权限',
module: 'system',
type: 'operation',
parent_id: 12,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '权限分配',
code: 'system:permission:assign',
description: '分配权限给角色权限',
module: 'system',
type: 'operation',
parent_id: 12,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
// 保险管理模块权限
{
name: '保险管理',
code: 'insurance:manage',
description: '保险管理模块访问权限',
module: 'insurance',
type: 'menu',
parent_id: null,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '保险申请管理',
code: 'insurance:application:manage',
description: '保险申请管理页面访问权限',
module: 'insurance',
type: 'menu',
parent_id: 15,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '保险申请查看',
code: 'insurance:application:view',
description: '查看保险申请权限',
module: 'insurance',
type: 'operation',
parent_id: 16,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '保险申请审核',
code: 'insurance:application:review',
description: '审核保险申请权限',
module: 'insurance',
type: 'operation',
parent_id: 16,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '保单管理',
code: 'insurance:policy:manage',
description: '保单管理页面访问权限',
module: 'insurance',
type: 'menu',
parent_id: 15,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '保单查看',
code: 'insurance:policy:view',
description: '查看保单权限',
module: 'insurance',
type: 'operation',
parent_id: 19,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '理赔管理',
code: 'insurance:claim:manage',
description: '理赔管理页面访问权限',
module: 'insurance',
type: 'menu',
parent_id: 15,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '理赔查看',
code: 'insurance:claim:view',
description: '查看理赔权限',
module: 'insurance',
type: 'operation',
parent_id: 21,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '理赔审核',
code: 'insurance:claim:review',
description: '审核理赔权限',
module: 'insurance',
type: 'operation',
parent_id: 21,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
// 监管任务模块权限
{
name: '监管任务',
code: 'supervision:manage',
description: '监管任务模块访问权限',
module: 'supervision',
type: 'menu',
parent_id: null,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '监管任务查看',
code: 'supervision:task:view',
description: '查看监管任务权限',
module: 'supervision',
type: 'operation',
parent_id: 24,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '监管任务创建',
code: 'supervision:task:create',
description: '创建监管任务权限',
module: 'supervision',
type: 'operation',
parent_id: 24,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '监管任务分配',
code: 'supervision:task:assign',
description: '分配监管任务权限',
module: 'supervision',
type: 'operation',
parent_id: 24,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
// 设备管理模块权限
{
name: '设备管理',
code: 'device:manage',
description: '设备管理模块访问权限',
module: 'device',
type: 'menu',
parent_id: null,
status: 'active',
sort_order: 4,
created_at: new Date(),
updated_at: new Date()
},
{
name: '设备查看',
code: 'device:view',
description: '查看设备权限',
module: 'device',
type: 'operation',
parent_id: 28,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
},
{
name: '设备新增',
code: 'device:create',
description: '新增设备权限',
module: 'device',
type: 'operation',
parent_id: 28,
status: 'active',
sort_order: 2,
created_at: new Date(),
updated_at: new Date()
},
{
name: '设备编辑',
code: 'device:edit',
description: '编辑设备权限',
module: 'device',
type: 'operation',
parent_id: 28,
status: 'active',
sort_order: 3,
created_at: new Date(),
updated_at: new Date()
},
{
name: '设备删除',
code: 'device:delete',
description: '删除设备权限',
module: 'device',
type: 'operation',
parent_id: 28,
status: 'active',
sort_order: 4,
created_at: new Date(),
updated_at: new Date()
},
// 日志管理模块权限
{
name: '日志管理',
code: 'log:manage',
description: '日志管理模块访问权限',
module: 'log',
type: 'menu',
parent_id: null,
status: 'active',
sort_order: 5,
created_at: new Date(),
updated_at: new Date()
},
{
name: '操作日志查看',
code: 'log:operation:view',
description: '查看操作日志权限',
module: 'log',
type: 'operation',
parent_id: 33,
status: 'active',
sort_order: 1,
created_at: new Date(),
updated_at: new Date()
}
], {});
},
async down(queryInterface, Sequelize) {
await queryInterface.bulkDelete('permissions', null, {});
}
};

View File

@@ -17,11 +17,15 @@ app.use(cors({
credentials: true
}));
// 速率限制
// 速率限制 - 开发环境下放宽限制
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP每15分钟最多100个请求
message: '请求过于频繁,请稍后再试'
windowMs: 1 * 60 * 1000, // 1分钟
max: 1000, // 限制每个IP每1分钟最多1000个请求
message: '请求过于频繁,请稍后再试',
skip: (req) => {
// 在开发环境下跳过限流
return process.env.NODE_ENV === 'development';
}
});
app.use(limiter);
@@ -56,6 +60,7 @@ app.use('/api/insurance-types', require('../routes/insuranceTypes'));
app.use('/api/policies', require('../routes/policies'));
app.use('/api/claims', require('../routes/claims'));
app.use('/api/system', require('../routes/system'));
app.use('/api/dashboard', require('../routes/dashboard'));
app.use('/api/operation-logs', require('../routes/operationLogs'));
app.use('/api/menus', require('../routes/menus'));
app.use('/api/data-warehouse', require('../routes/dataWarehouse'));
@@ -73,6 +78,10 @@ app.use('/api/livestock-claims', require('../routes/livestockClaims'));
app.use('/api/devices', require('../routes/devices'));
app.use('/api/device-alerts', require('../routes/deviceAlerts'));
// 权限管理相关路由
app.use('/api/permissions', require('../routes/permissions'));
app.use('/api/role-permissions', require('../routes/rolePermissions'));
// API文档路由
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
explorer: true,

View File

@@ -0,0 +1,191 @@
const PermissionService = require('../services/permissionService');
const jwt = require('jsonwebtoken');
/**
* 权限检查中间件
* @param {string|string[]} requiredPermissions - 必需的权限代码(单个或数组)
* @param {string} checkType - 检查类型:'any'(任意一个)或 'all'(全部)
* @returns {Function} Express中间件函数
*/
const requirePermission = (requiredPermissions, checkType = 'any') => {
return async (req, res, next) => {
try {
// 从请求头获取token
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
success: false,
message: '未提供认证令牌'
});
}
// 验证token并获取用户信息
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const userId = decoded.userId;
if (!userId) {
return res.status(401).json({
success: false,
message: '无效的认证令牌'
});
}
// 确保权限是数组格式
const permissions = Array.isArray(requiredPermissions)
? requiredPermissions
: [requiredPermissions];
let hasPermission = false;
// 根据检查类型进行权限验证
if (checkType === 'all') {
hasPermission = await PermissionService.checkUserAllPermissions(userId, permissions);
} else {
hasPermission = await PermissionService.checkUserAnyPermission(userId, permissions);
}
if (!hasPermission) {
return res.status(403).json({
success: false,
message: '权限不足,无法访问此资源',
requiredPermissions: permissions
});
}
// 将用户ID添加到请求对象中供后续使用
req.userId = userId;
next();
} catch (error) {
console.error('权限检查中间件错误:', error);
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
message: '无效的认证令牌'
});
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '认证令牌已过期'
});
}
return res.status(500).json({
success: false,
message: '权限检查失败'
});
}
};
};
/**
* 检查单个权限的中间件
* @param {string} permission - 权限代码
* @returns {Function} Express中间件函数
*/
const requireSinglePermission = (permission) => {
return requirePermission(permission, 'any');
};
/**
* 检查多个权限中任意一个的中间件
* @param {string[]} permissions - 权限代码数组
* @returns {Function} Express中间件函数
*/
const requireAnyPermission = (permissions) => {
return requirePermission(permissions, 'any');
};
/**
* 检查多个权限全部具备的中间件
* @param {string[]} permissions - 权限代码数组
* @returns {Function} Express中间件函数
*/
const requireAllPermissions = (permissions) => {
return requirePermission(permissions, 'all');
};
/**
* 管理员权限检查中间件
* @returns {Function} Express中间件函数
*/
const requireAdmin = () => {
return requireSinglePermission('system:admin');
};
/**
* 超级管理员权限检查中间件
* @returns {Function} Express中间件函数
*/
const requireSuperAdmin = () => {
return requireSinglePermission('system:super_admin');
};
/**
* 权限检查装饰器(用于控制器方法)
* @param {string|string[]} permissions - 权限代码
* @param {string} checkType - 检查类型
* @returns {Function} 装饰器函数
*/
const withPermission = (permissions, checkType = 'any') => {
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = async function(req, res, next) {
try {
const middleware = requirePermission(permissions, checkType);
await new Promise((resolve, reject) => {
middleware(req, res, (err) => {
if (err) reject(err);
else resolve();
});
});
return originalMethod.call(this, req, res, next);
} catch (error) {
return res.status(403).json({
success: false,
message: '权限不足'
});
}
};
return descriptor;
};
};
/**
* 动态权限检查中间件
* 根据请求参数动态确定所需权限
* @param {Function} permissionResolver - 权限解析函数
* @returns {Function} Express中间件函数
*/
const requireDynamicPermission = (permissionResolver) => {
return async (req, res, next) => {
try {
const requiredPermissions = await permissionResolver(req);
const middleware = requirePermission(requiredPermissions);
return middleware(req, res, next);
} catch (error) {
console.error('动态权限检查失败:', error);
return res.status(500).json({
success: false,
message: '权限检查失败'
});
}
};
};
module.exports = {
requirePermission,
requireSinglePermission,
requireAnyPermission,
requireAllPermissions,
requireAdmin,
requireSuperAdmin,
withPermission,
requireDynamicPermission
};

View File

@@ -0,0 +1,254 @@
const { User, Role, Permission, RolePermission } = require('../models');
/**
* 权限服务类 - 提供动态权限检查和管理功能
*/
class PermissionService {
/**
* 检查用户是否具有指定权限
* @param {number} userId - 用户ID
* @param {string} permissionCode - 权限代码
* @returns {Promise<boolean>} 是否具有权限
*/
static async checkUserPermission(userId, permissionCode) {
try {
const user = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role',
include: [{
model: Permission,
as: 'permissions',
where: { code: permissionCode },
required: false
}]
}]
});
if (!user || !user.role) {
return false;
}
return user.role.permissions && user.role.permissions.length > 0;
} catch (error) {
console.error('检查用户权限失败:', error);
return false;
}
}
/**
* 检查用户是否具有多个权限中的任意一个
* @param {number} userId - 用户ID
* @param {string[]} permissionCodes - 权限代码数组
* @returns {Promise<boolean>} 是否具有任意一个权限
*/
static async checkUserAnyPermission(userId, permissionCodes) {
try {
const user = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role',
include: [{
model: Permission,
as: 'permissions',
where: { code: permissionCodes },
required: false
}]
}]
});
if (!user || !user.role) {
return false;
}
return user.role.permissions && user.role.permissions.length > 0;
} catch (error) {
console.error('检查用户权限失败:', error);
return false;
}
}
/**
* 检查用户是否具有所有指定权限
* @param {number} userId - 用户ID
* @param {string[]} permissionCodes - 权限代码数组
* @returns {Promise<boolean>} 是否具有所有权限
*/
static async checkUserAllPermissions(userId, permissionCodes) {
try {
const user = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role',
include: [{
model: Permission,
as: 'permissions',
where: { code: permissionCodes },
required: false
}]
}]
});
if (!user || !user.role) {
return false;
}
const userPermissions = user.role.permissions || [];
return permissionCodes.every(code =>
userPermissions.some(permission => permission.code === code)
);
} catch (error) {
console.error('检查用户权限失败:', error);
return false;
}
}
/**
* 获取用户的所有权限
* @param {number} userId - 用户ID
* @returns {Promise<string[]>} 用户权限代码数组
*/
static async getUserPermissions(userId) {
try {
const user = await User.findByPk(userId, {
include: [{
model: Role,
as: 'role',
include: [{
model: Permission,
as: 'permissions'
}]
}]
});
if (!user || !user.role || !user.role.permissions) {
return [];
}
return user.role.permissions.map(permission => permission.code);
} catch (error) {
console.error('获取用户权限失败:', error);
return [];
}
}
/**
* 获取角色的所有权限
* @param {number} roleId - 角色ID
* @returns {Promise<Permission[]>} 权限对象数组
*/
static async getRolePermissions(roleId) {
try {
const role = await Role.findByPk(roleId, {
include: [{
model: Permission,
as: 'permissions'
}]
});
return role ? role.permissions || [] : [];
} catch (error) {
console.error('获取角色权限失败:', error);
return [];
}
}
/**
* 检查权限是否存在
* @param {string} permissionCode - 权限代码
* @returns {Promise<boolean>} 权限是否存在
*/
static async permissionExists(permissionCode) {
try {
const permission = await Permission.findOne({
where: { code: permissionCode }
});
return !!permission;
} catch (error) {
console.error('检查权限存在性失败:', error);
return false;
}
}
/**
* 获取权限树结构
* @returns {Promise<Object[]>} 权限树
*/
static async getPermissionTree() {
try {
const permissions = await Permission.findAll({
order: [['module', 'ASC'], ['type', 'ASC'], ['code', 'ASC']]
});
// 按模块分组
const modules = {};
permissions.forEach(permission => {
if (!modules[permission.module]) {
modules[permission.module] = {
module: permission.module,
permissions: []
};
}
modules[permission.module].permissions.push(permission);
});
return Object.values(modules);
} catch (error) {
console.error('获取权限树失败:', error);
return [];
}
}
/**
* 批量检查用户权限
* @param {number} userId - 用户ID
* @param {string[]} permissionCodes - 权限代码数组
* @returns {Promise<Object>} 权限检查结果对象
*/
static async batchCheckUserPermissions(userId, permissionCodes) {
try {
const userPermissions = await this.getUserPermissions(userId);
const result = {};
permissionCodes.forEach(code => {
result[code] = userPermissions.includes(code);
});
return result;
} catch (error) {
console.error('批量检查用户权限失败:', error);
return {};
}
}
/**
* 获取用户可访问的菜单
* @param {number} userId - 用户ID
* @returns {Promise<Object[]>} 可访问的菜单数组
*/
static async getUserAccessibleMenus(userId) {
try {
const userPermissions = await this.getUserPermissions(userId);
// 这里可以根据权限过滤菜单
// 示例:假设菜单与权限有对应关系
const allMenus = [
{ id: 1, name: '用户管理', permission: 'user:view', path: '/users' },
{ id: 2, name: '角色管理', permission: 'role:view', path: '/roles' },
{ id: 3, name: '权限管理', permission: 'permission:view', path: '/permissions' },
{ id: 4, name: '保单管理', permission: 'policy:view', path: '/policies' },
{ id: 5, name: '理赔管理', permission: 'claim:view', path: '/claims' },
{ id: 6, name: '系统设置', permission: 'system:view', path: '/system' }
];
return allMenus.filter(menu =>
userPermissions.includes(menu.permission)
);
} catch (error) {
console.error('获取用户可访问菜单失败:', error);
return [];
}
}
}
module.exports = PermissionService;

View File

@@ -1,25 +1,32 @@
// 简单的API测试脚本
const axios = require('axios');
async function testPublicApi() {
async function testAuthenticatedApi() {
try {
console.log('测试公开菜单API...');
const response = await axios.get('http://localhost:3000/api/menus/public');
console.log('✅ 公开菜单API测试成功');
console.log('测试认证菜单API...');
// 注意这个测试需要有效的JWT token
const response = await axios.get('http://localhost:3000/api/menus', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
}
});
console.log('✅ 认证菜单API测试成功');
console.log('返回数据:', response.data);
return true;
} catch (error) {
console.error('❌ 公开菜单API测试失败:', error.message);
console.error('❌ 认证菜单API测试失败:', error.message);
console.log('提示请确保服务器运行并提供有效的JWT token');
return false;
}
}
async function testAllApis() {
console.log('开始API测试...');
const publicApiResult = await testPublicApi();
console.log('注意所有API现在都需要认证请确保提供有效的JWT token');
const authApiResult = await testAuthenticatedApi();
console.log('\n测试总结:');
console.log(`公开菜单API: ${publicApiResult ? '通过' : '失败'}`);
console.log(`认证菜单API: ${authApiResult ? '通过' : '失败'}`);
}
testAllApis().then(() => {

View File

@@ -0,0 +1,109 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000';
// 测试固定令牌功能
async function testFixedToken() {
try {
console.log('🧪 开始测试固定令牌功能...\n');
// 1. 登录获取JWT令牌
console.log('1. 登录获取JWT令牌...');
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, {
username: 'admin',
password: '123456'
});
if (loginResponse.data.code !== 200) {
console.error('❌ 登录失败:', loginResponse.data.message);
return;
}
const jwtToken = loginResponse.data.data.accessToken;
const userId = loginResponse.data.data.user.id;
console.log('✅ 登录成功用户ID:', userId);
// 2. 检查用户是否已有固定令牌
console.log('\n2. 检查用户固定令牌状态...');
const tokenInfoResponse = await axios.get(`${BASE_URL}/api/users/${userId}/fixed-token`, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
console.log('✅ 令牌状态:', tokenInfoResponse.data.data);
// 3. 如果没有固定令牌,则生成一个
let fixedToken;
if (!tokenInfoResponse.data.data.hasToken) {
console.log('\n3. 生成固定令牌...');
const generateResponse = await axios.post(`${BASE_URL}/api/users/${userId}/fixed-token`, {}, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
if (generateResponse.data.code === 200) {
fixedToken = generateResponse.data.data.fixed_token;
console.log('✅ 固定令牌生成成功');
console.log('🔑 固定令牌:', fixedToken);
} else {
console.error('❌ 固定令牌生成失败:', generateResponse.data.message);
return;
}
} else {
console.log('\n3. 用户已有固定令牌,重新生成...');
const regenerateResponse = await axios.put(`${BASE_URL}/api/users/${userId}/fixed-token`, {}, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
if (regenerateResponse.data.code === 200) {
fixedToken = regenerateResponse.data.data.fixed_token;
console.log('✅ 固定令牌重新生成成功');
console.log('🔑 新固定令牌:', fixedToken);
} else {
console.error('❌ 固定令牌重新生成失败:', regenerateResponse.data.message);
return;
}
}
// 4. 使用固定令牌访问API
console.log('\n4. 使用固定令牌访问用户列表API...');
const usersResponse = await axios.get(`${BASE_URL}/api/users`, {
headers: { Authorization: `Bearer ${fixedToken}` }
});
if (usersResponse.data.code === 200) {
console.log('✅ 使用固定令牌访问API成功');
console.log('📊 用户数量:', usersResponse.data.data.users.length);
} else {
console.error('❌ 使用固定令牌访问API失败:', usersResponse.data.message);
}
// 5. 测试无效令牌
console.log('\n5. 测试无效固定令牌...');
try {
await axios.get(`${BASE_URL}/api/users`, {
headers: { Authorization: 'Bearer ft_invalid_token' }
});
console.error('❌ 无效令牌测试失败应该返回401错误');
} catch (error) {
if (error.response && error.response.status === 401) {
console.log('✅ 无效令牌正确返回401错误');
} else {
console.error('❌ 无效令牌测试异常:', error.message);
}
}
// 6. 再次检查令牌信息
console.log('\n6. 再次检查令牌信息...');
const finalTokenInfoResponse = await axios.get(`${BASE_URL}/api/users/${userId}/fixed-token`, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
console.log('✅ 最终令牌状态:', finalTokenInfoResponse.data.data);
console.log('\n🎉 固定令牌功能测试完成!');
} catch (error) {
console.error('❌ 测试过程中发生错误:', error.response?.data || error.message);
}
}
// 运行测试
testFixedToken();

View File

@@ -0,0 +1,75 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000/api';
const ADMIN_TOKEN = '5659725423f665a8bf5053b37e624ea86387f9113ae77ac75fc102012a349180';
// 创建axios实例
const api = axios.create({
baseURL: BASE_URL,
headers: {
'Authorization': `Bearer ${ADMIN_TOKEN}`,
'Content-Type': 'application/json'
}
});
async function testRolePermissionsAPI() {
console.log('开始测试角色权限管理API...\n');
// 测试1: 获取所有权限
try {
console.log('1. 测试获取所有权限...');
const response = await api.get('/role-permissions/permissions');
console.log('✅ 获取所有权限成功');
console.log('权限数量:', response.data.data?.length || 0);
} catch (error) {
console.log('❌ 获取所有权限失败:', error.response?.data?.message || error.message);
}
// 测试2: 获取所有角色及其权限
try {
console.log('\n2. 测试获取所有角色及其权限...');
const response = await api.get('/role-permissions/roles');
console.log('✅ 获取所有角色及其权限成功');
const roles = response.data.data?.roles || [];
console.log('角色数量:', roles.length);
if (roles && roles.length > 0) {
roles.forEach(role => {
console.log(` - ${role.name}: ${role.permissionCount || 0} 个权限`);
});
}
} catch (error) {
console.log('❌ 获取所有角色及其权限失败:', error.response?.data?.message || error.message);
}
// 测试3: 获取特定角色的详细权限
try {
console.log('\n3. 测试获取特定角色的详细权限...');
const response = await api.get('/role-permissions/roles/1/permissions');
console.log('✅ 获取特定角色的详细权限成功');
console.log('权限树结构:', response.data.data ? '已构建' : '未构建');
} catch (error) {
console.log('❌ 获取特定角色的详细权限失败:', error.response?.data?.message || error.message);
}
// 测试4: 权限统计
try {
console.log('\n4. 测试权限统计...');
const response = await api.get('/role-permissions/stats');
console.log('✅ 权限统计成功');
if (response.data.data) {
const stats = response.data.data;
console.log('统计信息:');
console.log(` - 总角色数: ${stats.overview?.totalRoles || 0}`);
console.log(` - 总权限数: ${stats.overview?.totalPermissions || 0}`);
console.log(` - 总分配数: ${stats.overview?.totalAssignments || 0}`);
console.log(` - 平均每角色权限数: ${stats.overview?.averagePermissionsPerRole || 0}`);
}
} catch (error) {
console.log('❌ 权限统计失败:', error.response?.data?.message || error.message);
}
console.log('\n测试完成!');
}
// 运行测试
testRolePermissionsAPI().catch(console.error);