完善保险前后端、养殖端小程序
This commit is contained in:
27
insurance_backend/check_admin_token.js
Normal file
27
insurance_backend/check_admin_token.js
Normal 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();
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
295
insurance_backend/controllers/dashboardController.js
Normal file
295
insurance_backend/controllers/dashboardController.js
Normal 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
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
415
insurance_backend/controllers/permissionController.js
Normal file
415
insurance_backend/controllers/permissionController.js
Normal 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();
|
||||
420
insurance_backend/controllers/rolePermissionController.js
Normal file
420
insurance_backend/controllers/rolePermissionController.js
Normal 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();
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
73
insurance_backend/debug_api_response.js
Normal file
73
insurance_backend/debug_api_response.js
Normal 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);
|
||||
222
insurance_backend/docs/API认证文档.md
Normal file
222
insurance_backend/docs/API认证文档.md
Normal 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
|
||||
```
|
||||
@@ -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('权限不足'));
|
||||
}
|
||||
|
||||
119
insurance_backend/middleware/fixedTokenAuth.js
Normal file
119
insurance_backend/middleware/fixedTokenAuth.js
Normal 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;
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
42
insurance_backend/models/MenuPermission.js
Normal file
42
insurance_backend/models/MenuPermission.js
Normal 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;
|
||||
93
insurance_backend/models/Permission.js
Normal file
93
insurance_backend/models/Permission.js
Normal 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;
|
||||
42
insurance_backend/models/RolePermission.js
Normal file
42
insurance_backend/models/RolePermission.js
Normal 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;
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
222
insurance_backend/routes/dashboard.js
Normal file
222
insurance_backend/routes/dashboard.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
542
insurance_backend/routes/permissions.js
Normal file
542
insurance_backend/routes/permissions.js
Normal 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;
|
||||
384
insurance_backend/routes/rolePermissions.js
Normal file
384
insurance_backend/routes/rolePermissions.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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({
|
||||
|
||||
124
insurance_backend/scripts/fix_admin_permissions.js
Normal file
124
insurance_backend/scripts/fix_admin_permissions.js
Normal 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);
|
||||
});
|
||||
431
insurance_backend/seeders/20250925000001-init-permissions.js
Normal file
431
insurance_backend/seeders/20250925000001-init-permissions.js
Normal 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, {});
|
||||
}
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
191
insurance_backend/src/middleware/permissionMiddleware.js
Normal file
191
insurance_backend/src/middleware/permissionMiddleware.js
Normal 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
|
||||
};
|
||||
254
insurance_backend/src/services/permissionService.js
Normal file
254
insurance_backend/src/services/permissionService.js
Normal 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;
|
||||
@@ -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(() => {
|
||||
|
||||
109
insurance_backend/test_fixed_token.js
Normal file
109
insurance_backend/test_fixed_token.js
Normal 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();
|
||||
75
insurance_backend/test_role_permissions_api.js
Normal file
75
insurance_backend/test_role_permissions_api.js
Normal 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);
|
||||
Reference in New Issue
Block a user