继续完善保险项目和养殖端小程序
This commit is contained in:
@@ -1,244 +0,0 @@
|
||||
const { Claim, Policy, User, InsuranceApplication, InsuranceType } = require('../models');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取理赔列表
|
||||
const getClaims = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
claim_no,
|
||||
customer_name,
|
||||
claim_status,
|
||||
dateRange,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 理赔编号筛选
|
||||
if (claim_no) {
|
||||
whereClause.claim_no = { [Op.like]: `%${claim_no}%` };
|
||||
}
|
||||
|
||||
// 理赔状态筛选
|
||||
if (claim_status) {
|
||||
whereClause.claim_status = claim_status;
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (dateRange && dateRange.start && dateRange.end) {
|
||||
whereClause.claim_date = {
|
||||
[Op.between]: [new Date(dateRange.start), new Date(dateRange.end)]
|
||||
};
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await Claim.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: Policy,
|
||||
as: 'policy',
|
||||
attributes: ['id', 'policy_no', 'coverage_amount'],
|
||||
include: [
|
||||
{
|
||||
model: InsuranceApplication,
|
||||
as: 'application',
|
||||
attributes: ['id', 'customer_name'],
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'customer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取理赔列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取理赔列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取理赔列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建理赔申请
|
||||
const createClaim = async (req, res) => {
|
||||
try {
|
||||
const claimData = req.body;
|
||||
|
||||
// 生成理赔编号
|
||||
const claimNo = `CLM${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
|
||||
const claim = await Claim.create({
|
||||
...claimData,
|
||||
claim_no: claimNo
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(claim, '理赔申请创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建理赔申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('创建理赔申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个理赔详情
|
||||
const getClaimById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const claim = await Claim.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: Policy,
|
||||
as: 'policy',
|
||||
include: [
|
||||
{
|
||||
model: InsuranceApplication,
|
||||
as: 'application',
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'customer',
|
||||
attributes: ['id', 'real_name', 'username', 'phone', 'email']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('理赔申请不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(claim, '获取理赔详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取理赔详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取理赔详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 审核理赔申请
|
||||
const reviewClaim = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { claim_status, review_notes } = req.body;
|
||||
const reviewerId = req.user.id;
|
||||
|
||||
const claim = await Claim.findByPk(id);
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('理赔申请不存在'));
|
||||
}
|
||||
|
||||
if (!['approved', 'rejected', 'processing', 'paid'].includes(claim_status)) {
|
||||
return res.status(400).json(responseFormat.error('无效的理赔状态'));
|
||||
}
|
||||
|
||||
await claim.update({
|
||||
claim_status,
|
||||
review_notes,
|
||||
reviewer_id: reviewerId,
|
||||
review_date: new Date()
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(claim, '理赔申请审核成功'));
|
||||
} catch (error) {
|
||||
console.error('审核理赔申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('审核理赔申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新理赔支付状态
|
||||
const updateClaimPayment = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const claim = await Claim.findByPk(id);
|
||||
if (!claim) {
|
||||
return res.status(404).json(responseFormat.error('理赔申请不存在'));
|
||||
}
|
||||
|
||||
if (claim.claim_status !== 'approved') {
|
||||
return res.status(400).json(responseFormat.error('只有已批准的理赔才能进行支付'));
|
||||
}
|
||||
|
||||
await claim.update({
|
||||
claim_status: 'paid',
|
||||
payment_date: new Date()
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(claim, '理赔支付状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新理赔支付状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新理赔支付状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取理赔统计
|
||||
const getClaimStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await Claim.findAll({
|
||||
attributes: [
|
||||
'claim_status',
|
||||
[Claim.sequelize.fn('COUNT', Claim.sequelize.col('id')), 'count'],
|
||||
[Claim.sequelize.fn('SUM', Claim.sequelize.col('claim_amount')), 'total_amount']
|
||||
],
|
||||
group: ['claim_status']
|
||||
});
|
||||
|
||||
const total = await Claim.count();
|
||||
const totalAmount = await Claim.sum('claim_amount');
|
||||
|
||||
res.json(responseFormat.success({
|
||||
stats,
|
||||
total,
|
||||
total_amount: totalAmount || 0
|
||||
}, '获取理赔统计成功'));
|
||||
} catch (error) {
|
||||
console.error('获取理赔统计错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取理赔统计失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getClaims,
|
||||
createClaim,
|
||||
getClaimById,
|
||||
reviewClaim,
|
||||
updateClaimPayment,
|
||||
getClaimStats
|
||||
};
|
||||
@@ -1,435 +0,0 @@
|
||||
const { Device, DeviceAlert, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* 设备控制器
|
||||
*/
|
||||
class DeviceController {
|
||||
|
||||
/**
|
||||
* 获取设备列表
|
||||
*/
|
||||
static async getDeviceList(req, res) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 20,
|
||||
device_type,
|
||||
status,
|
||||
farm_id,
|
||||
pen_id,
|
||||
keyword
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereCondition = {};
|
||||
if (device_type) {
|
||||
whereCondition.device_type = device_type;
|
||||
}
|
||||
if (status) {
|
||||
whereCondition.status = status;
|
||||
}
|
||||
if (farm_id) {
|
||||
whereCondition.farm_id = farm_id;
|
||||
}
|
||||
if (pen_id) {
|
||||
whereCondition.pen_id = pen_id;
|
||||
}
|
||||
if (keyword) {
|
||||
whereCondition[Op.or] = [
|
||||
{ device_code: { [Op.like]: `%${keyword}%` } },
|
||||
{ device_name: { [Op.like]: `%${keyword}%` } },
|
||||
{ device_model: { [Op.like]: `%${keyword}%` } },
|
||||
{ manufacturer: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await Device.findAndCountAll({
|
||||
where: whereCondition,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name'],
|
||||
required: false
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: offset
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
devices: rows,
|
||||
pagination: {
|
||||
current_page: parseInt(page),
|
||||
per_page: parseInt(limit),
|
||||
total: count,
|
||||
total_pages: Math.ceil(count / limit)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取设备列表失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取设备列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备详情
|
||||
*/
|
||||
static async getDeviceDetail(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const device = await Device.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name'],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name'],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '设备不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取设备相关的预警统计
|
||||
const alertStats = await DeviceAlert.findAll({
|
||||
attributes: [
|
||||
'alert_level',
|
||||
[DeviceAlert.sequelize.fn('COUNT', DeviceAlert.sequelize.col('id')), 'count']
|
||||
],
|
||||
where: {
|
||||
device_id: id
|
||||
},
|
||||
group: ['alert_level'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 获取最近的预警记录
|
||||
const recentAlerts = await DeviceAlert.findAll({
|
||||
where: {
|
||||
device_id: id
|
||||
},
|
||||
order: [['alert_time', 'DESC']],
|
||||
limit: 5,
|
||||
attributes: ['id', 'alert_type', 'alert_level', 'alert_title', 'alert_time', 'status']
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
device,
|
||||
alert_stats: alertStats,
|
||||
recent_alerts: recentAlerts
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取设备详情失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取设备详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备
|
||||
*/
|
||||
static async createDevice(req, res) {
|
||||
try {
|
||||
const {
|
||||
device_code,
|
||||
device_name,
|
||||
device_type,
|
||||
device_model,
|
||||
manufacturer,
|
||||
installation_location,
|
||||
installation_date,
|
||||
farm_id,
|
||||
pen_id,
|
||||
status = 'normal'
|
||||
} = req.body;
|
||||
|
||||
const userId = req.user.id;
|
||||
|
||||
// 检查设备编号是否已存在
|
||||
const existingDevice = await Device.findOne({
|
||||
where: { device_code }
|
||||
});
|
||||
|
||||
if (existingDevice) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '设备编号已存在'
|
||||
});
|
||||
}
|
||||
|
||||
const device = await Device.create({
|
||||
device_code,
|
||||
device_name,
|
||||
device_type,
|
||||
device_model,
|
||||
manufacturer,
|
||||
installation_location,
|
||||
installation_date,
|
||||
farm_id,
|
||||
pen_id,
|
||||
status,
|
||||
created_by: userId,
|
||||
updated_by: userId
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '设备创建成功',
|
||||
data: device
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('创建设备失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '创建设备失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备
|
||||
*/
|
||||
static async updateDevice(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
device_code,
|
||||
device_name,
|
||||
device_type,
|
||||
device_model,
|
||||
manufacturer,
|
||||
installation_location,
|
||||
installation_date,
|
||||
farm_id,
|
||||
pen_id,
|
||||
status
|
||||
} = req.body;
|
||||
|
||||
const userId = req.user.id;
|
||||
|
||||
const device = await Device.findByPk(id);
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '设备不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 如果修改了设备编号,检查是否与其他设备重复
|
||||
if (device_code && device_code !== device.device_code) {
|
||||
const existingDevice = await Device.findOne({
|
||||
where: {
|
||||
device_code,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
});
|
||||
|
||||
if (existingDevice) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '设备编号已存在'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await device.update({
|
||||
device_code,
|
||||
device_name,
|
||||
device_type,
|
||||
device_model,
|
||||
manufacturer,
|
||||
installation_location,
|
||||
installation_date,
|
||||
farm_id,
|
||||
pen_id,
|
||||
status,
|
||||
updated_by: userId
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '设备更新成功',
|
||||
data: device
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('更新设备失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新设备失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备
|
||||
*/
|
||||
static async deleteDevice(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const device = await Device.findByPk(id);
|
||||
if (!device) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '设备不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否有关联的预警记录
|
||||
const alertCount = await DeviceAlert.count({
|
||||
where: { device_id: id }
|
||||
});
|
||||
|
||||
if (alertCount > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '该设备存在预警记录,无法删除'
|
||||
});
|
||||
}
|
||||
|
||||
await device.destroy();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '设备删除成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('删除设备失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '删除设备失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备类型列表
|
||||
*/
|
||||
static async getDeviceTypes(req, res) {
|
||||
try {
|
||||
const deviceTypes = await Device.findAll({
|
||||
attributes: [
|
||||
[Device.sequelize.fn('DISTINCT', Device.sequelize.col('device_type')), 'device_type']
|
||||
],
|
||||
where: {
|
||||
device_type: {
|
||||
[Op.ne]: null
|
||||
}
|
||||
},
|
||||
raw: true
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: deviceTypes.map(item => item.device_type)
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取设备类型失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取设备类型失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备状态统计
|
||||
*/
|
||||
static async getDeviceStats(req, res) {
|
||||
try {
|
||||
const { farm_id } = req.query;
|
||||
|
||||
const whereCondition = {};
|
||||
if (farm_id) {
|
||||
whereCondition.farm_id = farm_id;
|
||||
}
|
||||
|
||||
// 按状态统计
|
||||
const devicesByStatus = await Device.findAll({
|
||||
attributes: [
|
||||
'status',
|
||||
[Device.sequelize.fn('COUNT', Device.sequelize.col('id')), 'count']
|
||||
],
|
||||
where: whereCondition,
|
||||
group: ['status'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 按类型统计
|
||||
const devicesByType = await Device.findAll({
|
||||
attributes: [
|
||||
'device_type',
|
||||
[Device.sequelize.fn('COUNT', Device.sequelize.col('id')), 'count']
|
||||
],
|
||||
where: whereCondition,
|
||||
group: ['device_type'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 总设备数
|
||||
const totalDevices = await Device.count({
|
||||
where: whereCondition
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
total_devices: totalDevices,
|
||||
devices_by_status: devicesByStatus,
|
||||
devices_by_type: devicesByType
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取设备统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取设备统计失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DeviceController;
|
||||
@@ -1,477 +0,0 @@
|
||||
const InstallationTask = require('../models/InstallationTask');
|
||||
const User = require('../models/User');
|
||||
const { Op, sequelize } = require('sequelize');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
class InstallationTaskController {
|
||||
|
||||
// 获取待安装任务列表
|
||||
async getInstallationTasks(req, res) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
pageSize = 10,
|
||||
policyNumber,
|
||||
customerName,
|
||||
installationStatus,
|
||||
priority,
|
||||
keyword,
|
||||
startDate,
|
||||
endDate
|
||||
} = req.query;
|
||||
|
||||
const offset = (page - 1) * pageSize;
|
||||
const limit = parseInt(pageSize);
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
if (policyNumber) {
|
||||
whereConditions.policyNumber = { [Op.like]: `%${policyNumber}%` };
|
||||
}
|
||||
|
||||
if (customerName) {
|
||||
whereConditions.customerName = { [Op.like]: `%${customerName}%` };
|
||||
}
|
||||
|
||||
if (installationStatus) {
|
||||
whereConditions.installationStatus = installationStatus;
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
whereConditions.priority = priority;
|
||||
}
|
||||
|
||||
// 关键字搜索(搜索申请单号、保单编号、客户姓名等)
|
||||
if (keyword) {
|
||||
whereConditions[Op.or] = [
|
||||
{ applicationNumber: { [Op.like]: `%${keyword}%` } },
|
||||
{ policyNumber: { [Op.like]: `%${keyword}%` } },
|
||||
{ customerName: { [Op.like]: `%${customerName}%` } },
|
||||
{ productName: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
if (startDate && endDate) {
|
||||
whereConditions.taskGeneratedTime = {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
};
|
||||
}
|
||||
|
||||
const { count, rows } = await InstallationTask.findAndCountAll({
|
||||
where: whereConditions,
|
||||
order: [['taskGeneratedTime', 'DESC']],
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取待安装任务列表成功',
|
||||
data: {
|
||||
list: rows,
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
pageSize: limit,
|
||||
totalPages: Math.ceil(count / limit)
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取待安装任务列表失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取待安装任务列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 创建待安装任务
|
||||
async createInstallationTask(req, res) {
|
||||
try {
|
||||
const {
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
livestockSupplyType,
|
||||
pendingDevices,
|
||||
installationStatus = '待安装',
|
||||
priority = '中',
|
||||
assignedTo,
|
||||
taskGeneratedTime = new Date()
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!applicationNumber || !policyNumber || !productName || !customerName) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '申请单号、保单编号、产品名称、客户姓名为必填项'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查申请单号是否已存在
|
||||
const existingTask = await InstallationTask.findOne({
|
||||
where: { applicationNumber }
|
||||
});
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(409).json({
|
||||
code: 409,
|
||||
status: 'error',
|
||||
message: '该申请单号已存在待安装任务'
|
||||
});
|
||||
}
|
||||
|
||||
const installationTask = await InstallationTask.create({
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType: idType || '身份证',
|
||||
idNumber: idNumber || '',
|
||||
livestockSupplyType,
|
||||
pendingDevices: pendingDevices ? JSON.stringify(pendingDevices) : null,
|
||||
installationStatus,
|
||||
priority,
|
||||
assignedTo,
|
||||
taskGeneratedTime,
|
||||
createdBy: req.user?.id
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
code: 201,
|
||||
status: 'success',
|
||||
message: '创建待安装任务成功',
|
||||
data: installationTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('创建待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '创建待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取待安装任务详情
|
||||
async getInstallationTaskById(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const installationTask = await InstallationTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'technician',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!installationTask) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取待安装任务详情成功',
|
||||
data: installationTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取待安装任务详情失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取待安装任务详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新待安装任务
|
||||
async updateInstallationTask(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = { ...req.body };
|
||||
|
||||
// 添加更新人信息
|
||||
updateData.updatedBy = req.user?.id;
|
||||
|
||||
// 处理设备数据
|
||||
if (updateData.pendingDevices) {
|
||||
updateData.pendingDevices = JSON.stringify(updateData.pendingDevices);
|
||||
}
|
||||
|
||||
// 处理安装完成时间
|
||||
if (updateData.installationStatus === '已安装' && !updateData.installationCompletedTime) {
|
||||
updateData.installationCompletedTime = new Date();
|
||||
}
|
||||
|
||||
const [updatedCount] = await InstallationTask.update(updateData, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在或未做任何修改'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取更新后的任务
|
||||
const updatedTask = await InstallationTask.findByPk(id);
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '更新待安装任务成功',
|
||||
data: updatedTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('更新待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '更新待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 删除待安装任务
|
||||
async deleteInstallationTask(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const deletedCount = await InstallationTask.destroy({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (deletedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '删除待安装任务成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('删除待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '删除待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 批量操作待安装任务
|
||||
async batchOperateInstallationTasks(req, res) {
|
||||
try {
|
||||
const { ids, operation, data } = req.body;
|
||||
|
||||
if (!ids || !Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '任务ID列表不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
let result;
|
||||
switch (operation) {
|
||||
case 'assign':
|
||||
if (!data.assignedTo) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '分配操作需要指定分配给的用户'
|
||||
});
|
||||
}
|
||||
result = await InstallationTask.update(
|
||||
{ assignedTo: data.assignedTo, updatedBy: req.user?.id },
|
||||
{ where: { id: ids } }
|
||||
);
|
||||
break;
|
||||
case 'updateStatus':
|
||||
if (!data.installationStatus) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '状态更新操作需要指定新状态'
|
||||
});
|
||||
}
|
||||
result = await InstallationTask.update(
|
||||
{ installationStatus: data.installationStatus, updatedBy: req.user?.id },
|
||||
{ where: { id: ids } }
|
||||
);
|
||||
break;
|
||||
case 'delete':
|
||||
result = await InstallationTask.destroy({ where: { id: ids } });
|
||||
break;
|
||||
default:
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '不支持的操作类型'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: `批量${operation}操作完成`,
|
||||
data: { affectedRows: result[0] || result }
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('批量操作待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '批量操作失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出待安装任务数据
|
||||
async exportInstallationTasks(req, res) {
|
||||
try {
|
||||
const { ids } = req.query;
|
||||
let where = {};
|
||||
|
||||
if (ids) {
|
||||
where.id = { [Op.in]: ids.split(',') };
|
||||
}
|
||||
|
||||
const tasks = await InstallationTask.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'technician',
|
||||
attributes: ['username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['taskGeneratedTime', 'DESC']]
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '导出待安装任务数据成功',
|
||||
data: tasks
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('导出待安装任务数据失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '导出数据失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务统计信息
|
||||
async getInstallationTaskStats(req, res) {
|
||||
try {
|
||||
// 按状态统计
|
||||
const statusStats = await InstallationTask.findAll({
|
||||
attributes: [
|
||||
'installationStatus',
|
||||
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['installationStatus'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 按优先级统计
|
||||
const priorityStats = await InstallationTask.findAll({
|
||||
attributes: [
|
||||
'priority',
|
||||
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['priority'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 总数统计
|
||||
const total = await InstallationTask.count();
|
||||
|
||||
// 本月新增任务
|
||||
const thisMonth = await InstallationTask.count({
|
||||
where: {
|
||||
taskGeneratedTime: {
|
||||
[Op.gte]: new Date(new Date().getFullYear(), new Date().getMonth(), 1)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取任务统计信息成功',
|
||||
data: {
|
||||
total,
|
||||
thisMonth,
|
||||
statusStats,
|
||||
priorityStats
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取任务统计信息失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取统计信息失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new InstallationTaskController();
|
||||
@@ -1,400 +0,0 @@
|
||||
const { InsuranceApplication, InsuranceType, User } = require('../models');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取保险申请列表
|
||||
const getApplications = async (req, res) => {
|
||||
try {
|
||||
console.log('获取保险申请列表 - 请求参数:', req.query);
|
||||
const {
|
||||
applicantName,
|
||||
status,
|
||||
insuranceType,
|
||||
insuranceCategory,
|
||||
applicationNumber,
|
||||
dateRange,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
const includeClause = [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description'],
|
||||
where: {}
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
];
|
||||
|
||||
// 申请单号筛选
|
||||
if (applicationNumber) {
|
||||
whereClause.application_no = { [Op.like]: `%${applicationNumber}%` };
|
||||
}
|
||||
|
||||
// 投保人姓名筛选
|
||||
if (applicantName) {
|
||||
whereClause.customer_name = { [Op.like]: `%${applicantName}%` };
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status) {
|
||||
whereClause.status = status;
|
||||
}
|
||||
|
||||
// 参保险种筛选
|
||||
if (insuranceType) {
|
||||
includeClause[0].where.name = { [Op.like]: `%${insuranceType}%` };
|
||||
}
|
||||
|
||||
// 参保类型筛选
|
||||
if (insuranceCategory) {
|
||||
whereClause.insurance_category = { [Op.like]: `%${insuranceCategory}%` };
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (dateRange && dateRange.start && dateRange.end) {
|
||||
whereClause.application_date = {
|
||||
[Op.between]: [new Date(dateRange.start), new Date(dateRange.end)]
|
||||
};
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 如果没有保险类型筛选条件,清空where条件
|
||||
if (!insuranceType) {
|
||||
delete includeClause[0].where;
|
||||
}
|
||||
|
||||
console.log('查询条件:', { whereClause, includeClause, offset, limit: parseInt(limit) });
|
||||
|
||||
const { count, rows } = await InsuranceApplication.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: includeClause,
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
console.log('查询结果:', { count, rowsLength: rows.length });
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取保险申请列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保险申请列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保险申请列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建保险申请
|
||||
const createApplication = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
customer_name,
|
||||
customer_id_card,
|
||||
customer_phone,
|
||||
customer_address,
|
||||
insurance_type_id,
|
||||
insurance_category,
|
||||
application_quantity,
|
||||
application_amount,
|
||||
remarks
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!customer_name || !customer_id_card || !customer_phone || !customer_address || !insurance_type_id) {
|
||||
return res.status(400).json(responseFormat.error('请填写所有必填字段'));
|
||||
}
|
||||
|
||||
// 生成申请编号
|
||||
const applicationNo = `${new Date().getFullYear()}${(new Date().getMonth() + 1).toString().padStart(2, '0')}${new Date().getDate().toString().padStart(2, '0')}${Date.now().toString().slice(-6)}`;
|
||||
|
||||
const application = await InsuranceApplication.create({
|
||||
application_no: applicationNo,
|
||||
customer_name,
|
||||
customer_id_card,
|
||||
customer_phone,
|
||||
customer_address,
|
||||
insurance_type_id,
|
||||
insurance_category,
|
||||
application_quantity: application_quantity || 1,
|
||||
application_amount: application_amount || 0,
|
||||
remarks,
|
||||
status: 'pending'
|
||||
});
|
||||
|
||||
// 返回创建的申请信息,包含关联数据
|
||||
const createdApplication = await InsuranceApplication.findByPk(application.id, {
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(createdApplication, '保险申请创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建保险申请错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const errorMessages = error.errors.map(err => err.message).join(', ');
|
||||
return res.status(400).json(responseFormat.error(`数据验证失败: ${errorMessages}`));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建保险申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个保险申请详情
|
||||
const getApplicationById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const application = await InsuranceApplication.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'reviewer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!application) {
|
||||
return res.status(404).json(responseFormat.error('保险申请不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(application, '获取保险申请详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保险申请详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保险申请详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新保险申请
|
||||
const updateApplication = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
const application = await InsuranceApplication.findByPk(id);
|
||||
if (!application) {
|
||||
return res.status(404).json(responseFormat.error('保险申请不存在'));
|
||||
}
|
||||
|
||||
await application.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(application, '保险申请更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新保险申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新保险申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 审核保险申请
|
||||
const reviewApplication = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, review_notes } = req.body;
|
||||
const reviewerId = req.user.id;
|
||||
|
||||
const application = await InsuranceApplication.findByPk(id);
|
||||
if (!application) {
|
||||
return res.status(404).json(responseFormat.error('保险申请不存在'));
|
||||
}
|
||||
|
||||
if (!['approved', 'rejected', 'under_review'].includes(status)) {
|
||||
return res.status(400).json(responseFormat.error('无效的审核状态'));
|
||||
}
|
||||
|
||||
await application.update({
|
||||
status,
|
||||
review_notes,
|
||||
reviewer_id: reviewerId,
|
||||
review_date: new Date()
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(application, '保险申请审核成功'));
|
||||
} catch (error) {
|
||||
console.error('审核保险申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('审核保险申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除保险申请
|
||||
const deleteApplication = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const application = await InsuranceApplication.findByPk(id);
|
||||
if (!application) {
|
||||
return res.status(404).json(responseFormat.error('保险申请不存在'));
|
||||
}
|
||||
|
||||
await application.destroy();
|
||||
|
||||
res.json(responseFormat.success(null, '保险申请删除成功'));
|
||||
} catch (error) {
|
||||
console.error('删除保险申请错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除保险申请失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取保险申请统计
|
||||
const getApplicationStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await InsuranceApplication.findAll({
|
||||
attributes: [
|
||||
'status',
|
||||
[InsuranceApplication.sequelize.fn('COUNT', InsuranceApplication.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['status']
|
||||
});
|
||||
|
||||
const total = await InsuranceApplication.count();
|
||||
|
||||
res.json(responseFormat.success({
|
||||
stats,
|
||||
total
|
||||
}, '获取保险申请统计成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保险申请统计错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保险申请统计失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取参保类型选项
|
||||
const getInsuranceCategories = async (req, res) => {
|
||||
try {
|
||||
const categories = await InsuranceApplication.findAll({
|
||||
attributes: ['insurance_category'],
|
||||
where: {
|
||||
insurance_category: {
|
||||
[Op.ne]: null
|
||||
}
|
||||
},
|
||||
group: ['insurance_category'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
const categoryList = categories.map(item => item.insurance_category).filter(Boolean);
|
||||
|
||||
// 添加一些常用的参保类型
|
||||
const defaultCategories = ['牛', '羊', '猪', '鸡', '鸭', '鹅'];
|
||||
const allCategories = [...new Set([...defaultCategories, ...categoryList])];
|
||||
|
||||
res.json(responseFormat.success(allCategories, '获取参保类型选项成功'));
|
||||
} catch (error) {
|
||||
console.error('获取参保类型选项错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取参保类型选项失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 导出保险申请数据
|
||||
const exportApplications = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 1000,
|
||||
applicantName,
|
||||
insuranceType,
|
||||
insuranceCategory,
|
||||
status
|
||||
} = req.query;
|
||||
|
||||
const where = {};
|
||||
|
||||
if (applicantName) {
|
||||
where.applicant_name = { [Op.like]: `%${applicantName}%` };
|
||||
}
|
||||
if (insuranceType) {
|
||||
where.insurance_type = insuranceType;
|
||||
}
|
||||
if (insuranceCategory) {
|
||||
where.insurance_category = insuranceCategory;
|
||||
}
|
||||
if (status) {
|
||||
where.status = status;
|
||||
}
|
||||
|
||||
const applications = await InsuranceApplication.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insuranceTypeInfo',
|
||||
attributes: ['name', 'description']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'createdByUser',
|
||||
attributes: ['username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: (parseInt(page) - 1) * parseInt(limit)
|
||||
});
|
||||
|
||||
// 简单的CSV格式导出
|
||||
const csvHeader = '申请编号,申请人姓名,身份证号,联系电话,参保类型,保险类型,保险金额,保险期限,地址,状态,申请时间,备注\n';
|
||||
const csvData = applications.map(app => {
|
||||
const statusMap = {
|
||||
'pending': '待审核',
|
||||
'initial_approved': '初审通过',
|
||||
'under_review': '复审中',
|
||||
'approved': '已通过',
|
||||
'rejected': '已拒绝'
|
||||
};
|
||||
|
||||
return [
|
||||
app.application_number || '',
|
||||
app.applicant_name || '',
|
||||
app.id_card || '',
|
||||
app.phone || '',
|
||||
app.insurance_category || '',
|
||||
app.insurance_type || '',
|
||||
app.insurance_amount || '',
|
||||
app.insurance_period || '',
|
||||
app.address || '',
|
||||
statusMap[app.status] || app.status,
|
||||
app.created_at ? new Date(app.created_at).toLocaleString('zh-CN') : '',
|
||||
app.remarks || ''
|
||||
].map(field => `"${String(field).replace(/"/g, '""')}"`).join(',');
|
||||
}).join('\n');
|
||||
|
||||
const csvContent = csvHeader + csvData;
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="insurance_applications_${new Date().toISOString().slice(0, 10)}.csv"`);
|
||||
res.send('\uFEFF' + csvContent); // 添加BOM以支持中文
|
||||
} catch (error) {
|
||||
console.error('导出保险申请数据错误:', error);
|
||||
res.status(500).json(responseFormat.error('导出保险申请数据失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getApplications,
|
||||
createApplication,
|
||||
getApplicationById,
|
||||
updateApplication,
|
||||
reviewApplication,
|
||||
deleteApplication,
|
||||
getApplicationStats,
|
||||
getInsuranceCategories,
|
||||
exportApplications
|
||||
};
|
||||
@@ -4,7 +4,21 @@ const responseFormat = require('../utils/response');
|
||||
|
||||
// 获取保险类型列表
|
||||
const getInsuranceTypes = async (req, res) => {
|
||||
const requestStartTime = new Date();
|
||||
|
||||
try {
|
||||
// ========== 后端接收数据日志 ==========
|
||||
console.log('🔵 [后端] 接收到获取险种列表请求');
|
||||
console.log('📥 [后端] 请求时间:', requestStartTime.toISOString());
|
||||
console.log('📥 [后端] 请求方法:', req.method);
|
||||
console.log('📥 [后端] 请求路径:', req.path);
|
||||
console.log('📥 [后端] 查询参数:', JSON.stringify(req.query, null, 2));
|
||||
console.log('📥 [后端] 用户信息:', req.user ? {
|
||||
id: req.user.id,
|
||||
username: req.user.username,
|
||||
role: req.user.role
|
||||
} : '未认证用户');
|
||||
|
||||
const {
|
||||
page = 1,
|
||||
pageSize = 10,
|
||||
@@ -14,40 +28,122 @@ const getInsuranceTypes = async (req, res) => {
|
||||
service_area,
|
||||
on_sale_status
|
||||
} = req.query;
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const whereClause = {};
|
||||
if (name) {
|
||||
whereClause.name = { [Op.like]: `%${name}%` };
|
||||
}
|
||||
if (status) {
|
||||
whereClause.status = status;
|
||||
}
|
||||
if (applicable_livestock) {
|
||||
whereClause.applicable_livestock = { [Op.like]: `%${applicable_livestock}%` };
|
||||
}
|
||||
if (service_area) {
|
||||
whereClause.service_area = { [Op.like]: `%${service_area}%` };
|
||||
}
|
||||
if (on_sale_status !== undefined && on_sale_status !== '') {
|
||||
whereClause.on_sale_status = on_sale_status === 'true';
|
||||
}
|
||||
|
||||
const { count, rows } = await InsuranceType.findAndCountAll({
|
||||
where: whereClause,
|
||||
limit: parseInt(pageSize),
|
||||
offset: offset,
|
||||
order: [['add_time', 'DESC'], ['created_at', 'DESC']]
|
||||
// ========== 后端数据处理日志 ==========
|
||||
console.log('⚙️ [后端] 开始处理查询参数');
|
||||
console.log('⚙️ [后端] 原始查询参数:', {
|
||||
page, pageSize, name, status, applicable_livestock, service_area, on_sale_status
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(pageSize),
|
||||
const processedParams = {
|
||||
page: parseInt(page) || 1,
|
||||
pageSize: parseInt(pageSize) || 10,
|
||||
name: name ? String(name).trim() : null,
|
||||
status: status ? String(status).trim() : null,
|
||||
applicable_livestock: applicable_livestock ? String(applicable_livestock).trim() : null,
|
||||
service_area: service_area ? String(service_area).trim() : null,
|
||||
on_sale_status: on_sale_status !== undefined && on_sale_status !== '' ? on_sale_status === 'true' : null
|
||||
};
|
||||
|
||||
console.log('⚙️ [后端] 处理后的查询参数:', processedParams);
|
||||
|
||||
const offset = (processedParams.page - 1) * processedParams.pageSize;
|
||||
console.log('⚙️ [后端] 分页计算 - offset:', offset, 'limit:', processedParams.pageSize);
|
||||
|
||||
const whereClause = {};
|
||||
if (processedParams.name) {
|
||||
whereClause.name = processedParams.name; // 精确查询
|
||||
}
|
||||
if (processedParams.status) {
|
||||
whereClause.status = processedParams.status;
|
||||
}
|
||||
if (processedParams.applicable_livestock) {
|
||||
whereClause.applicable_livestock = { [Op.like]: `%${processedParams.applicable_livestock}%` };
|
||||
}
|
||||
if (processedParams.service_area) {
|
||||
whereClause.service_area = { [Op.like]: `%${processedParams.service_area}%` };
|
||||
}
|
||||
if (processedParams.on_sale_status !== null) {
|
||||
whereClause.on_sale_status = processedParams.on_sale_status;
|
||||
}
|
||||
|
||||
console.log('⚙️ [后端] 构建的WHERE条件:', whereClause);
|
||||
|
||||
const queryOptions = {
|
||||
where: whereClause,
|
||||
limit: processedParams.pageSize,
|
||||
offset: offset,
|
||||
order: [['add_time', 'DESC'], ['created_at', 'DESC']]
|
||||
};
|
||||
|
||||
console.log('⚙️ [后端] 数据库查询选项:', queryOptions);
|
||||
console.log('⚙️ [后端] 开始执行数据库查询...');
|
||||
|
||||
const { count, rows } = await InsuranceType.findAndCountAll(queryOptions);
|
||||
|
||||
console.log('✅ [后端] 数据库查询完成');
|
||||
console.log('✅ [后端] 查询结果统计:', {
|
||||
totalCount: count,
|
||||
returnedRows: rows.length,
|
||||
currentPage: processedParams.page,
|
||||
pageSize: processedParams.pageSize,
|
||||
totalPages: Math.ceil(count / processedParams.pageSize)
|
||||
});
|
||||
|
||||
// ========== 后端响应数据日志 ==========
|
||||
const responseEndTime = new Date();
|
||||
const processingTime = responseEndTime - requestStartTime;
|
||||
|
||||
const paginationData = {
|
||||
page: processedParams.page,
|
||||
limit: processedParams.pageSize,
|
||||
total: count
|
||||
}, '获取险种列表成功'));
|
||||
};
|
||||
|
||||
const successResponse = responseFormat.pagination(rows, paginationData, '获取险种列表成功');
|
||||
|
||||
console.log('📤 [后端] 准备发送成功响应');
|
||||
console.log('📤 [后端] 响应时间:', responseEndTime.toISOString());
|
||||
console.log('📤 [后端] 处理耗时:', processingTime + 'ms');
|
||||
console.log('📤 [后端] 响应状态码:', 200);
|
||||
console.log('📤 [后端] 响应数据结构:', {
|
||||
success: successResponse.success,
|
||||
message: successResponse.message,
|
||||
dataType: typeof successResponse.data,
|
||||
dataLength: Array.isArray(successResponse.data) ? successResponse.data.length : 'not array',
|
||||
paginationType: typeof successResponse.pagination,
|
||||
paginationKeys: successResponse.pagination ? Object.keys(successResponse.pagination) : 'no pagination'
|
||||
});
|
||||
console.log('📤 [后端] 分页信息:', successResponse.pagination);
|
||||
console.log('📤 [后端] 返回的险种数据概览:', rows.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
status: item.status,
|
||||
on_sale_status: item.on_sale_status
|
||||
})));
|
||||
|
||||
res.json(successResponse);
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取险种列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取险种列表失败'));
|
||||
// ========== 后端错误处理日志 ==========
|
||||
const errorEndTime = new Date();
|
||||
const processingTime = errorEndTime - requestStartTime;
|
||||
|
||||
console.error('❌ [后端] 获取险种列表发生错误');
|
||||
console.error('❌ [后端] 错误时间:', errorEndTime.toISOString());
|
||||
console.error('❌ [后端] 处理耗时:', processingTime + 'ms');
|
||||
console.error('❌ [后端] 错误类型:', error.name);
|
||||
console.error('❌ [后端] 错误消息:', error.message);
|
||||
console.error('❌ [后端] 错误堆栈:', error.stack);
|
||||
|
||||
const errorResponse = responseFormat.error('获取险种列表失败');
|
||||
|
||||
console.log('📤 [后端] 发送错误响应:', {
|
||||
statusCode: 500,
|
||||
response: errorResponse
|
||||
});
|
||||
|
||||
res.status(500).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,7 +166,26 @@ const getInsuranceTypeById = async (req, res) => {
|
||||
|
||||
// 创建险种
|
||||
const createInsuranceType = async (req, res) => {
|
||||
const requestStartTime = new Date();
|
||||
|
||||
try {
|
||||
// ========== 后端接收数据日志 ==========
|
||||
console.log('🔵 [后端] 接收到创建险种请求');
|
||||
console.log('📥 [后端] 请求时间:', requestStartTime.toISOString());
|
||||
console.log('📥 [后端] 请求方法:', req.method);
|
||||
console.log('📥 [后端] 请求路径:', req.path);
|
||||
console.log('📥 [后端] 请求头信息:', {
|
||||
'content-type': req.headers['content-type'],
|
||||
'authorization': req.headers.authorization ? '已提供' : '未提供',
|
||||
'user-agent': req.headers['user-agent']
|
||||
});
|
||||
console.log('📥 [后端] 原始请求体数据:', JSON.stringify(req.body, null, 2));
|
||||
console.log('📥 [后端] 用户信息:', req.user ? {
|
||||
id: req.user.id,
|
||||
username: req.user.username,
|
||||
role: req.user.role
|
||||
} : '未认证用户');
|
||||
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
@@ -88,13 +203,9 @@ const createInsuranceType = async (req, res) => {
|
||||
status = 'active'
|
||||
} = req.body;
|
||||
|
||||
// 检查名称是否已存在
|
||||
const existingType = await InsuranceType.findOne({ where: { name } });
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('险种名称已存在'));
|
||||
}
|
||||
|
||||
const insuranceType = await InsuranceType.create({
|
||||
// ========== 后端数据处理日志 ==========
|
||||
console.log('⚙️ [后端] 开始数据处理和验证');
|
||||
console.log('⚙️ [后端] 解构后的数据:', {
|
||||
name,
|
||||
description,
|
||||
applicable_livestock,
|
||||
@@ -104,22 +215,116 @@ const createInsuranceType = async (req, res) => {
|
||||
coverage_amount_max,
|
||||
premium_rate,
|
||||
service_area,
|
||||
add_time: add_time || new Date(),
|
||||
add_time,
|
||||
on_sale_status,
|
||||
sort_order,
|
||||
remarks,
|
||||
status,
|
||||
created_by: req.user?.id
|
||||
status
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.success(insuranceType, '创建险种成功'));
|
||||
// 数据类型验证和转换
|
||||
const processedData = {
|
||||
name: name ? String(name).trim() : null,
|
||||
description: description ? String(description).trim() : null,
|
||||
applicable_livestock: Array.isArray(applicable_livestock) ?
|
||||
applicable_livestock.join(',') :
|
||||
(applicable_livestock ? String(applicable_livestock).trim() : null),
|
||||
insurance_term: insurance_term ? Number(insurance_term) : null,
|
||||
policy_form: policy_form ? String(policy_form).trim() : null,
|
||||
coverage_amount_min: coverage_amount_min ? Number(coverage_amount_min) : null,
|
||||
coverage_amount_max: coverage_amount_max ? Number(coverage_amount_max) : null,
|
||||
premium_rate: premium_rate ? Number(premium_rate) : null,
|
||||
service_area: service_area ? String(service_area).trim() : null,
|
||||
add_time: add_time ? new Date(add_time) : new Date(),
|
||||
on_sale_status: Boolean(on_sale_status),
|
||||
sort_order: sort_order ? Number(sort_order) : 0,
|
||||
remarks: remarks ? String(remarks).trim() : null,
|
||||
status: status || 'active'
|
||||
};
|
||||
|
||||
console.log('⚙️ [后端] 处理后的数据:', processedData);
|
||||
|
||||
// 检查名称是否已存在
|
||||
console.log('⚙️ [后端] 检查险种名称是否已存在:', processedData.name);
|
||||
const existingType = await InsuranceType.findOne({ where: { name: processedData.name } });
|
||||
|
||||
if (existingType) {
|
||||
console.log('❌ [后端] 险种名称已存在:', existingType.name);
|
||||
const errorResponse = responseFormat.error('险种名称已存在');
|
||||
console.log('📤 [后端] 发送错误响应:', errorResponse);
|
||||
return res.status(400).json(errorResponse);
|
||||
}
|
||||
|
||||
console.log('✅ [后端] 险种名称验证通过,开始创建数据');
|
||||
|
||||
// 准备数据库插入数据
|
||||
const dbInsertData = {
|
||||
...processedData,
|
||||
created_by: req.user?.id || null
|
||||
};
|
||||
|
||||
console.log('⚙️ [后端] 准备插入数据库的数据:', dbInsertData);
|
||||
|
||||
const insuranceType = await InsuranceType.create(dbInsertData);
|
||||
|
||||
console.log('✅ [后端] 险种创建成功,数据库返回:', {
|
||||
id: insuranceType.id,
|
||||
name: insuranceType.name,
|
||||
created_at: insuranceType.created_at
|
||||
});
|
||||
|
||||
// ========== 后端响应数据日志 ==========
|
||||
const responseEndTime = new Date();
|
||||
const processingTime = responseEndTime - requestStartTime;
|
||||
|
||||
const successResponse = responseFormat.success(insuranceType, '创建险种成功');
|
||||
|
||||
console.log('📤 [后端] 准备发送成功响应');
|
||||
console.log('📤 [后端] 响应时间:', responseEndTime.toISOString());
|
||||
console.log('📤 [后端] 处理耗时:', processingTime + 'ms');
|
||||
console.log('📤 [后端] 响应状态码:', 201);
|
||||
console.log('📤 [后端] 响应数据结构:', {
|
||||
success: successResponse.success,
|
||||
message: successResponse.message,
|
||||
dataType: typeof successResponse.data,
|
||||
dataId: successResponse.data?.id,
|
||||
dataKeys: Object.keys(successResponse.data || {})
|
||||
});
|
||||
console.log('📤 [后端] 完整响应数据:', JSON.stringify(successResponse, null, 2));
|
||||
|
||||
res.status(201).json(successResponse);
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建险种错误:', error);
|
||||
// ========== 后端错误处理日志 ==========
|
||||
const errorEndTime = new Date();
|
||||
const processingTime = errorEndTime - requestStartTime;
|
||||
|
||||
console.error('❌ [后端] 创建险种发生错误');
|
||||
console.error('❌ [后端] 错误时间:', errorEndTime.toISOString());
|
||||
console.error('❌ [后端] 处理耗时:', processingTime + 'ms');
|
||||
console.error('❌ [后端] 错误类型:', error.name);
|
||||
console.error('❌ [后端] 错误消息:', error.message);
|
||||
console.error('❌ [后端] 错误堆栈:', error.stack);
|
||||
|
||||
let errorResponse;
|
||||
let statusCode = 500;
|
||||
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
errorResponse = responseFormat.error(messages.join(', '));
|
||||
statusCode = 400;
|
||||
console.error('❌ [后端] 数据验证错误:', messages);
|
||||
} else {
|
||||
errorResponse = responseFormat.error('创建险种失败');
|
||||
console.error('❌ [后端] 服务器内部错误');
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建险种失败'));
|
||||
|
||||
console.log('📤 [后端] 发送错误响应:', {
|
||||
statusCode,
|
||||
response: errorResponse
|
||||
});
|
||||
|
||||
res.status(statusCode).json(errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,10 +367,18 @@ const updateInsuranceType = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理applicable_livestock数据类型转换
|
||||
let processedApplicableLivestock = insuranceType.applicable_livestock;
|
||||
if (applicable_livestock !== undefined) {
|
||||
processedApplicableLivestock = Array.isArray(applicable_livestock) ?
|
||||
applicable_livestock.join(',') :
|
||||
(applicable_livestock ? String(applicable_livestock).trim() : null);
|
||||
}
|
||||
|
||||
await insuranceType.update({
|
||||
name: name || insuranceType.name,
|
||||
description: description !== undefined ? description : insuranceType.description,
|
||||
applicable_livestock: applicable_livestock !== undefined ? applicable_livestock : insuranceType.applicable_livestock,
|
||||
applicable_livestock: processedApplicableLivestock,
|
||||
insurance_term: insurance_term !== undefined ? insurance_term : insuranceType.insurance_term,
|
||||
policy_form: policy_form !== undefined ? policy_form : insuranceType.policy_form,
|
||||
coverage_amount_min: coverage_amount_min !== undefined ? coverage_amount_min : insuranceType.coverage_amount_min,
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
const LivestockType = require('../models/LivestockType');
|
||||
const User = require('../models/User');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取牲畜类型列表
|
||||
const getLivestockTypes = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
is_active,
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 名称筛选
|
||||
if (name) {
|
||||
whereClause.name = { [Op.like]: `%${name}%` };
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (is_active !== undefined) {
|
||||
whereClause.is_active = is_active === 'true';
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows } = await LivestockType.findAndCountAll({
|
||||
where: whereClause,
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(limit)
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(rows, {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: count
|
||||
}, '获取牲畜类型列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取牲畜类型列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取所有启用的牲畜类型(用于下拉选择)
|
||||
const getActiveLivestockTypes = async (req, res) => {
|
||||
try {
|
||||
const types = await LivestockType.findAll({
|
||||
where: { is_active: true },
|
||||
attributes: ['id', 'name', 'description', 'base_value', 'premium_rate'],
|
||||
order: [['name', 'ASC']]
|
||||
});
|
||||
|
||||
res.json(responseFormat.success(types, '获取启用牲畜类型成功'));
|
||||
} catch (error) {
|
||||
console.error('获取启用牲畜类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取启用牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建牲畜类型
|
||||
const createLivestockType = async (req, res) => {
|
||||
try {
|
||||
const typeData = req.body;
|
||||
|
||||
// 检查名称是否已存在
|
||||
const existingType = await LivestockType.findOne({
|
||||
where: { name: typeData.name }
|
||||
});
|
||||
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
|
||||
}
|
||||
|
||||
const type = await LivestockType.create({
|
||||
...typeData,
|
||||
created_by: req.user?.id
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(type, '牲畜类型创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建牲畜类型错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('创建牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个牲畜类型详情
|
||||
const getLivestockTypeById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(type, '获取牲畜类型详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取牲畜类型详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取牲畜类型详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新牲畜类型
|
||||
const updateLivestockType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
// 如果更新名称,检查是否与其他记录重复
|
||||
if (updateData.name && updateData.name !== type.name) {
|
||||
const existingType = await LivestockType.findOne({
|
||||
where: {
|
||||
name: updateData.name,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
});
|
||||
|
||||
if (existingType) {
|
||||
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
|
||||
}
|
||||
}
|
||||
|
||||
updateData.updated_by = req.user?.id;
|
||||
|
||||
await type.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(type, '牲畜类型更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新牲畜类型错误:', error);
|
||||
if (error.name === 'SequelizeValidationError') {
|
||||
const messages = error.errors.map(err => err.message);
|
||||
return res.status(400).json(responseFormat.error(messages.join(', ')));
|
||||
}
|
||||
res.status(500).json(responseFormat.error('更新牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除牲畜类型(软删除 - 设置为不启用)
|
||||
const deleteLivestockType = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const type = await LivestockType.findByPk(id);
|
||||
if (!type) {
|
||||
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
|
||||
}
|
||||
|
||||
// 检查是否有关联的保单
|
||||
const LivestockPolicy = require('../models/LivestockPolicy');
|
||||
const relatedPolicies = await LivestockPolicy.count({
|
||||
where: { livestock_type_id: id }
|
||||
});
|
||||
|
||||
if (relatedPolicies > 0) {
|
||||
// 如果有关联保单,只能设置为不启用
|
||||
await type.update({
|
||||
is_active: false,
|
||||
updated_by: req.user?.id
|
||||
});
|
||||
return res.json(responseFormat.success(null, '牲畜类型已设置为不启用(存在关联保单)'));
|
||||
}
|
||||
|
||||
// 如果没有关联保单,可以物理删除
|
||||
await type.destroy();
|
||||
|
||||
res.json(responseFormat.success(null, '牲畜类型删除成功'));
|
||||
} catch (error) {
|
||||
console.error('删除牲畜类型错误:', error);
|
||||
res.status(500).json(responseFormat.error('删除牲畜类型失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 批量更新牲畜类型状态
|
||||
const batchUpdateLivestockTypeStatus = async (req, res) => {
|
||||
try {
|
||||
const { ids, is_active } = req.body;
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json(responseFormat.error('请提供有效的ID列表'));
|
||||
}
|
||||
|
||||
await LivestockType.update(
|
||||
{
|
||||
is_active,
|
||||
updated_by: req.user?.id
|
||||
},
|
||||
{
|
||||
where: { id: { [Op.in]: ids } }
|
||||
}
|
||||
);
|
||||
|
||||
res.json(responseFormat.success(null, '批量更新牲畜类型状态成功'));
|
||||
} catch (error) {
|
||||
console.error('批量更新牲畜类型状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('批量更新牲畜类型状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLivestockTypes,
|
||||
getActiveLivestockTypes,
|
||||
createLivestockType,
|
||||
getLivestockTypeById,
|
||||
updateLivestockType,
|
||||
deleteLivestockType,
|
||||
batchUpdateLivestockTypeStatus
|
||||
};
|
||||
@@ -1,178 +0,0 @@
|
||||
const { User, Role, Menu } = require('../models');
|
||||
|
||||
/**
|
||||
* 获取菜单列表
|
||||
* @param {Object} req - Express请求对象
|
||||
* @param {Object} res - Express响应对象
|
||||
*/
|
||||
exports.getMenus = async (req, res) => {
|
||||
try {
|
||||
console.log('开始获取菜单,用户信息:', req.user);
|
||||
|
||||
// 获取用户ID(从JWT中解析或通过其他方式获取)
|
||||
const userId = req.user?.id; // 假设通过认证中间件解析后存在
|
||||
|
||||
console.log('用户ID:', userId);
|
||||
|
||||
// 如果没有用户ID,返回基础菜单
|
||||
if (!userId) {
|
||||
console.log('没有用户ID,返回基础菜单');
|
||||
const menus = await Menu.findAll({
|
||||
where: {
|
||||
parent_id: null,
|
||||
show: true,
|
||||
status: 'active'
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Menu,
|
||||
as: 'children',
|
||||
where: {
|
||||
show: true,
|
||||
status: 'active'
|
||||
},
|
||||
required: false,
|
||||
order: [['order', 'ASC']]
|
||||
}
|
||||
],
|
||||
order: [['order', 'ASC']]
|
||||
});
|
||||
|
||||
console.log('基础菜单查询结果:', menus.length);
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
data: menus,
|
||||
message: '获取菜单成功'
|
||||
});
|
||||
}
|
||||
|
||||
console.log('查询用户信息...');
|
||||
// 获取用户信息和角色
|
||||
const user = await User.findByPk(userId, {
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
console.log('用户查询结果:', user ? '找到用户' : '用户不存在');
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '用户不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取角色的权限列表
|
||||
const userPermissions = user.role?.permissions || [];
|
||||
console.log('用户权限:', userPermissions);
|
||||
|
||||
console.log('查询菜单数据...');
|
||||
// 查询菜单,这里简化处理,实际应用中可能需要根据权限过滤
|
||||
const menus = await Menu.findAll({
|
||||
where: {
|
||||
parent_id: null,
|
||||
show: true,
|
||||
status: 'active'
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Menu,
|
||||
as: 'children',
|
||||
where: {
|
||||
show: true,
|
||||
status: 'active'
|
||||
},
|
||||
required: false,
|
||||
order: [['order', 'ASC']]
|
||||
}
|
||||
],
|
||||
order: [['order', 'ASC']]
|
||||
});
|
||||
|
||||
console.log('菜单查询结果:', menus.length);
|
||||
|
||||
// 这里可以添加根据权限过滤菜单的逻辑
|
||||
// 简化示例,假设所有用户都能看到所有激活的菜单
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
data: menus,
|
||||
message: '获取菜单成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取菜单失败:', error);
|
||||
console.error('错误堆栈:', error.stack);
|
||||
return res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '服务器内部错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有菜单(包括非激活状态,仅管理员可用)
|
||||
* @param {Object} req - Express请求对象
|
||||
* @param {Object} res - Express响应对象
|
||||
*/
|
||||
exports.getAllMenus = async (req, res) => {
|
||||
try {
|
||||
// 检查用户是否为管理员(简化示例)
|
||||
const user = await User.findByPk(req.user?.id, {
|
||||
include: [
|
||||
{
|
||||
model: Role,
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!user || !user.Role || !user.Role.permissions.includes('*:*')) {
|
||||
return res.status(403).json({
|
||||
code: 403,
|
||||
status: 'error',
|
||||
message: '没有权限查看所有菜单'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询所有菜单
|
||||
const menus = await Menu.findAll({
|
||||
where: {
|
||||
parent_id: null
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Menu,
|
||||
as: 'children',
|
||||
required: false,
|
||||
order: [['order', 'ASC']]
|
||||
}
|
||||
],
|
||||
order: [['order', 'ASC']]
|
||||
});
|
||||
|
||||
return res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
data: menus,
|
||||
message: '获取所有菜单成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取所有菜单失败:', error);
|
||||
return res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,344 +0,0 @@
|
||||
const { OperationLog, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
const ExcelJS = require('exceljs');
|
||||
|
||||
/**
|
||||
* 操作日志控制器
|
||||
*/
|
||||
class OperationLogController {
|
||||
/**
|
||||
* 获取操作日志列表
|
||||
*/
|
||||
async getOperationLogs(req, res) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 20,
|
||||
user_id,
|
||||
operation_type,
|
||||
operation_module,
|
||||
status,
|
||||
start_date,
|
||||
end_date,
|
||||
keyword
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
if (user_id) {
|
||||
whereConditions.user_id = user_id;
|
||||
}
|
||||
|
||||
if (operation_type) {
|
||||
whereConditions.operation_type = operation_type;
|
||||
}
|
||||
|
||||
if (operation_module) {
|
||||
whereConditions.operation_module = operation_module;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
whereConditions.status = status;
|
||||
}
|
||||
|
||||
// 时间范围查询
|
||||
if (start_date || end_date) {
|
||||
whereConditions.created_at = {};
|
||||
if (start_date) {
|
||||
whereConditions.created_at[Op.gte] = new Date(start_date);
|
||||
}
|
||||
if (end_date) {
|
||||
const endDateTime = new Date(end_date);
|
||||
endDateTime.setHours(23, 59, 59, 999);
|
||||
whereConditions.created_at[Op.lte] = endDateTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (keyword) {
|
||||
whereConditions[Op.or] = [
|
||||
{ operation_content: { [Op.like]: `%${keyword}%` } },
|
||||
{ operation_target: { [Op.like]: `%${keyword}%` } },
|
||||
{ request_url: { [Op.like]: `%${keyword}%` } },
|
||||
{ ip_address: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
const offset = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
// 查询操作日志
|
||||
const { count, rows } = await OperationLog.findAndCountAll({
|
||||
where: whereConditions,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
attributes: ['id', 'username', 'real_name', 'email']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: offset
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(count / parseInt(limit));
|
||||
|
||||
res.json({
|
||||
status: 'success',
|
||||
message: '获取操作日志列表成功',
|
||||
data: {
|
||||
logs: rows,
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
totalPages: totalPages
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取操作日志列表失败:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message: '获取操作日志列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志统计信息
|
||||
*/
|
||||
async getOperationStats(req, res) {
|
||||
try {
|
||||
const {
|
||||
user_id,
|
||||
start_date,
|
||||
end_date
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
if (user_id) {
|
||||
whereConditions.user_id = user_id;
|
||||
}
|
||||
|
||||
// 时间范围查询
|
||||
if (start_date || end_date) {
|
||||
whereConditions.created_at = {};
|
||||
if (start_date) {
|
||||
whereConditions.created_at[Op.gte] = new Date(start_date);
|
||||
}
|
||||
if (end_date) {
|
||||
const endDateTime = new Date(end_date);
|
||||
endDateTime.setHours(23, 59, 59, 999);
|
||||
whereConditions.created_at[Op.lte] = endDateTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
const stats = await OperationLog.getOperationStats(whereConditions);
|
||||
|
||||
res.json({
|
||||
status: 'success',
|
||||
message: '获取操作日志统计成功',
|
||||
data: stats
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取操作日志统计失败:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message: '获取操作日志统计失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志详情
|
||||
*/
|
||||
async getOperationLogById(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const log = await OperationLog.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
attributes: ['id', 'username', 'real_name', 'email']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!log) {
|
||||
return res.status(404).json({
|
||||
status: 'error',
|
||||
message: '操作日志不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
status: 'success',
|
||||
message: '获取操作日志详情成功',
|
||||
data: log
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取操作日志详情失败:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message: '获取操作日志详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出操作日志
|
||||
*/
|
||||
async exportOperationLogs(req, res) {
|
||||
try {
|
||||
const {
|
||||
user_id,
|
||||
operation_type,
|
||||
operation_module,
|
||||
status,
|
||||
start_date,
|
||||
end_date,
|
||||
keyword
|
||||
} = req.body;
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
if (user_id) {
|
||||
whereConditions.user_id = user_id;
|
||||
}
|
||||
|
||||
if (operation_type) {
|
||||
whereConditions.operation_type = operation_type;
|
||||
}
|
||||
|
||||
if (operation_module) {
|
||||
whereConditions.operation_module = operation_module;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
whereConditions.status = status;
|
||||
}
|
||||
|
||||
// 时间范围查询
|
||||
if (start_date || end_date) {
|
||||
whereConditions.created_at = {};
|
||||
if (start_date) {
|
||||
whereConditions.created_at[Op.gte] = new Date(start_date);
|
||||
}
|
||||
if (end_date) {
|
||||
const endDateTime = new Date(end_date);
|
||||
endDateTime.setHours(23, 59, 59, 999);
|
||||
whereConditions.created_at[Op.lte] = endDateTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (keyword) {
|
||||
whereConditions[Op.or] = [
|
||||
{ operation_content: { [Op.like]: `%${keyword}%` } },
|
||||
{ operation_target: { [Op.like]: `%${keyword}%` } },
|
||||
{ request_url: { [Op.like]: `%${keyword}%` } },
|
||||
{ ip_address: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// 查询所有符合条件的日志
|
||||
const logs = await OperationLog.findAll({
|
||||
where: whereConditions,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'user',
|
||||
attributes: ['id', 'username', 'real_name', 'email']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']]
|
||||
});
|
||||
|
||||
// 创建Excel工作簿
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet('操作日志');
|
||||
|
||||
// 设置表头
|
||||
worksheet.columns = [
|
||||
{ header: 'ID', key: 'id', width: 10 },
|
||||
{ header: '操作用户', key: 'username', width: 15 },
|
||||
{ header: '真实姓名', key: 'real_name', width: 15 },
|
||||
{ header: '操作类型', key: 'operation_type', width: 15 },
|
||||
{ header: '操作模块', key: 'operation_module', width: 15 },
|
||||
{ header: '操作内容', key: 'operation_content', width: 30 },
|
||||
{ header: '操作目标', key: 'operation_target', width: 20 },
|
||||
{ header: '请求方法', key: 'request_method', width: 10 },
|
||||
{ header: '请求URL', key: 'request_url', width: 30 },
|
||||
{ header: 'IP地址', key: 'ip_address', width: 15 },
|
||||
{ header: '执行时间(ms)', key: 'execution_time', width: 12 },
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
{ header: '错误信息', key: 'error_message', width: 30 },
|
||||
{ header: '创建时间', key: 'created_at', width: 20 }
|
||||
];
|
||||
|
||||
// 添加数据
|
||||
logs.forEach(log => {
|
||||
worksheet.addRow({
|
||||
id: log.id,
|
||||
username: log.user ? log.user.username : '',
|
||||
real_name: log.user ? log.user.real_name : '',
|
||||
operation_type: log.operation_type,
|
||||
operation_module: log.operation_module,
|
||||
operation_content: log.operation_content,
|
||||
operation_target: log.operation_target,
|
||||
request_method: log.request_method,
|
||||
request_url: log.request_url,
|
||||
ip_address: log.ip_address,
|
||||
execution_time: log.execution_time,
|
||||
status: log.status,
|
||||
error_message: log.error_message,
|
||||
created_at: log.created_at
|
||||
});
|
||||
});
|
||||
|
||||
// 设置响应头
|
||||
const filename = `操作日志_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`);
|
||||
|
||||
// 写入响应
|
||||
await workbook.xlsx.write(res);
|
||||
res.end();
|
||||
|
||||
// 记录导出操作日志
|
||||
await OperationLog.logOperation({
|
||||
user_id: req.user.id,
|
||||
operation_type: 'export',
|
||||
operation_module: 'operation_logs',
|
||||
operation_content: '导出操作日志',
|
||||
operation_target: `导出${logs.length}条记录`,
|
||||
request_method: 'POST',
|
||||
request_url: '/api/operation-logs/export',
|
||||
request_params: req.body,
|
||||
ip_address: req.ip,
|
||||
user_agent: req.get('User-Agent'),
|
||||
status: 'success'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出操作日志失败:', error);
|
||||
res.status(500).json({
|
||||
status: 'error',
|
||||
message: '导出操作日志失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new OperationLogController();
|
||||
@@ -1,415 +0,0 @@
|
||||
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();
|
||||
@@ -1,239 +0,0 @@
|
||||
const { Policy, InsuranceApplication, InsuranceType, User } = require('../models');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取保单列表
|
||||
const getPolicies = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
policy_number, // 前端发送的参数名
|
||||
policyholder_name, // 前端发送的参数名
|
||||
insurance_type_id, // 前端发送的参数名
|
||||
status, // 前端发送的参数名
|
||||
page = 1,
|
||||
pageSize = 10 // 前端发送的参数名
|
||||
} = req.query;
|
||||
|
||||
const whereClause = {};
|
||||
|
||||
// 保单编号筛选
|
||||
if (policy_number) {
|
||||
whereClause.policy_no = { [Op.like]: `%${policy_number}%` };
|
||||
}
|
||||
|
||||
// 保单状态筛选
|
||||
if (status) {
|
||||
whereClause.policy_status = status;
|
||||
}
|
||||
|
||||
// 保险类型筛选
|
||||
if (insurance_type_id) {
|
||||
whereClause.insurance_type_id = insurance_type_id;
|
||||
}
|
||||
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const { count, rows } = await Policy.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: InsuranceApplication,
|
||||
as: 'application',
|
||||
attributes: ['id', 'customer_name', 'customer_phone'],
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'customer',
|
||||
attributes: ['id', 'real_name', 'username']
|
||||
},
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
],
|
||||
order: [['created_at', 'DESC']],
|
||||
offset,
|
||||
limit: parseInt(pageSize)
|
||||
});
|
||||
|
||||
// 处理返回数据,确保前端能够正确解析
|
||||
const processedRows = rows.map(row => {
|
||||
const policy = row.toJSON();
|
||||
return {
|
||||
id: policy.id,
|
||||
policy_number: policy.policy_no, // 映射字段名
|
||||
policyholder_name: policy.application?.customer_name || policy.customer?.real_name || '',
|
||||
insured_name: policy.application?.customer_name || policy.customer?.real_name || '',
|
||||
insurance_type_id: policy.insurance_type_id,
|
||||
insurance_type_name: policy.insurance_type?.name || '',
|
||||
premium_amount: parseFloat(policy.premium_amount) || 0,
|
||||
coverage_amount: parseFloat(policy.coverage_amount) || 0,
|
||||
start_date: policy.start_date,
|
||||
end_date: policy.end_date,
|
||||
status: policy.policy_status, // 映射字段名
|
||||
phone: policy.application?.customer_phone || '',
|
||||
email: policy.customer?.email || '',
|
||||
address: policy.application?.address || '',
|
||||
remarks: policy.terms_and_conditions || '',
|
||||
created_at: policy.created_at,
|
||||
updated_at: policy.updated_at
|
||||
};
|
||||
});
|
||||
|
||||
res.json(responseFormat.pagination(processedRows, {
|
||||
page: parseInt(page),
|
||||
pageSize: parseInt(pageSize),
|
||||
total: count
|
||||
}, '获取保单列表成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保单列表错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保单列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建保单
|
||||
const createPolicy = async (req, res) => {
|
||||
try {
|
||||
const policyData = req.body;
|
||||
|
||||
// 生成保单编号
|
||||
const policyNo = `POL${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
|
||||
const policy = await Policy.create({
|
||||
...policyData,
|
||||
policy_no: policyNo
|
||||
});
|
||||
|
||||
res.status(201).json(responseFormat.created(policy, '保单创建成功'));
|
||||
} catch (error) {
|
||||
console.error('创建保单错误:', error);
|
||||
res.status(500).json(responseFormat.error('创建保单失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个保单详情
|
||||
const getPolicyById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const policy = await Policy.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: InsuranceApplication,
|
||||
as: 'application',
|
||||
include: [
|
||||
{
|
||||
model: InsuranceType,
|
||||
as: 'insurance_type',
|
||||
attributes: ['id', 'name', 'description', 'premium_rate']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'customer',
|
||||
attributes: ['id', 'real_name', 'username', 'phone', 'email']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('保单不存在'));
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(policy, '获取保单详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保单详情错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保单详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新保单
|
||||
const updatePolicy = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
|
||||
const policy = await Policy.findByPk(id);
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('保单不存在'));
|
||||
}
|
||||
|
||||
await policy.update(updateData);
|
||||
|
||||
res.json(responseFormat.success(policy, '保单更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新保单错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新保单失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新保单状态
|
||||
const updatePolicyStatus = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { policy_status } = req.body;
|
||||
|
||||
const policy = await Policy.findByPk(id);
|
||||
if (!policy) {
|
||||
return res.status(404).json(responseFormat.error('保单不存在'));
|
||||
}
|
||||
|
||||
if (!['active', 'expired', 'cancelled', 'suspended'].includes(policy_status)) {
|
||||
return res.status(400).json(responseFormat.error('无效的保单状态'));
|
||||
}
|
||||
|
||||
await policy.update({ policy_status });
|
||||
|
||||
res.json(responseFormat.success(policy, '保单状态更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新保单状态错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新保单状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取保单统计
|
||||
const getPolicyStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await Policy.findAll({
|
||||
attributes: [
|
||||
'policy_status',
|
||||
[Policy.sequelize.fn('COUNT', Policy.sequelize.col('id')), 'count'],
|
||||
[Policy.sequelize.fn('SUM', Policy.sequelize.col('coverage_amount')), 'total_coverage'],
|
||||
[Policy.sequelize.fn('SUM', Policy.sequelize.col('premium_amount')), 'total_premium']
|
||||
],
|
||||
group: ['policy_status']
|
||||
});
|
||||
|
||||
const total = await Policy.count();
|
||||
const totalCoverage = await Policy.sum('coverage_amount');
|
||||
const totalPremium = await Policy.sum('premium_amount');
|
||||
|
||||
res.json(responseFormat.success({
|
||||
stats,
|
||||
total,
|
||||
total_coverage: totalCoverage || 0,
|
||||
total_premium: totalPremium || 0
|
||||
}, '获取保单统计成功'));
|
||||
} catch (error) {
|
||||
console.error('获取保单统计错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取保单统计失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getPolicies,
|
||||
createPolicy,
|
||||
getPolicyById,
|
||||
updatePolicy,
|
||||
updatePolicyStatus,
|
||||
getPolicyStats
|
||||
};
|
||||
@@ -1,603 +0,0 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* 监管任务结项控制器
|
||||
* 处理监管任务结项相关的业务逻辑
|
||||
*/
|
||||
|
||||
// 获取监管任务结项列表
|
||||
const getTaskCompletions = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
application_no, // 申请编号
|
||||
policy_no, // 保单号
|
||||
product_name, // 产品名称
|
||||
customer_name, // 客户名称
|
||||
completion_date, // 结项日期
|
||||
status, // 状态
|
||||
reviewer_name, // 审核人员
|
||||
page = 1,
|
||||
limit = 10
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
let whereClause = '';
|
||||
const params = [];
|
||||
|
||||
if (application_no) {
|
||||
whereClause += ' AND rtc.application_no LIKE ?';
|
||||
params.push(`%${application_no}%`);
|
||||
}
|
||||
|
||||
if (policy_no) {
|
||||
whereClause += ' AND rtc.policy_no LIKE ?';
|
||||
params.push(`%${policy_no}%`);
|
||||
}
|
||||
|
||||
if (product_name) {
|
||||
whereClause += ' AND rtc.product_name LIKE ?';
|
||||
params.push(`%${product_name}%`);
|
||||
}
|
||||
|
||||
if (customer_name) {
|
||||
whereClause += ' AND rtc.customer_name LIKE ?';
|
||||
params.push(`%${customer_name}%`);
|
||||
}
|
||||
|
||||
if (completion_date) {
|
||||
whereClause += ' AND DATE(rtc.completion_date) = ?';
|
||||
params.push(completion_date);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
whereClause += ' AND rtc.status = ?';
|
||||
params.push(status);
|
||||
}
|
||||
|
||||
if (reviewer_name) {
|
||||
whereClause += ' AND rtc.reviewer_name LIKE ?';
|
||||
params.push(`%${reviewer_name}%`);
|
||||
}
|
||||
|
||||
// 计算偏移量
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 查询总数
|
||||
const countQuery = `
|
||||
SELECT COUNT(*) as total
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE 1=1 ${whereClause}
|
||||
`;
|
||||
|
||||
const [countResult] = await sequelize.query(countQuery, {
|
||||
replacements: params,
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 查询数据
|
||||
const dataQuery = `
|
||||
SELECT
|
||||
rtc.id,
|
||||
rtc.application_no,
|
||||
rtc.policy_no,
|
||||
rtc.product_name,
|
||||
rtc.customer_name,
|
||||
rtc.customer_phone,
|
||||
rtc.insurance_amount,
|
||||
rtc.premium_amount,
|
||||
rtc.start_date,
|
||||
rtc.end_date,
|
||||
rtc.completion_date,
|
||||
rtc.status,
|
||||
rtc.reviewer_name,
|
||||
rtc.review_comments,
|
||||
rtc.created_at,
|
||||
rtc.updated_at
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE 1=1 ${whereClause}
|
||||
ORDER BY rtc.created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
|
||||
const results = await sequelize.query(dataQuery, {
|
||||
replacements: [...params, parseInt(limit), offset],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
res.json(responseFormat.success({
|
||||
list: results,
|
||||
pagination: {
|
||||
current: parseInt(page),
|
||||
pageSize: parseInt(limit),
|
||||
total: countResult.total,
|
||||
totalPages: Math.ceil(countResult.total / limit)
|
||||
}
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项列表失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项列表失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个监管任务结项详情
|
||||
const getTaskCompletionById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
rtc.*,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', rtca.id,
|
||||
'file_name', rtca.file_name,
|
||||
'file_path', rtca.file_path,
|
||||
'file_size', rtca.file_size,
|
||||
'file_type', rtca.file_type,
|
||||
'upload_time', rtca.upload_time
|
||||
)
|
||||
)
|
||||
FROM regulatory_task_completion_attachments rtca
|
||||
WHERE rtca.completion_id = rtc.id
|
||||
) as attachments,
|
||||
(
|
||||
SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', rtcl.id,
|
||||
'operation_type', rtcl.operation_type,
|
||||
'operation_description', rtcl.operation_description,
|
||||
'operator_name', rtcl.operator_name,
|
||||
'operation_time', rtcl.operation_time,
|
||||
'ip_address', rtcl.ip_address
|
||||
)
|
||||
)
|
||||
FROM regulatory_task_completion_logs rtcl
|
||||
WHERE rtcl.completion_id = rtc.id
|
||||
ORDER BY rtcl.operation_time DESC
|
||||
) as operation_logs
|
||||
FROM regulatory_task_completions rtc
|
||||
WHERE rtc.id = ?
|
||||
`;
|
||||
|
||||
const [result] = await sequelize.query(query, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 解析JSON字段
|
||||
if (result.attachments) {
|
||||
result.attachments = JSON.parse(result.attachments) || [];
|
||||
} else {
|
||||
result.attachments = [];
|
||||
}
|
||||
|
||||
if (result.operation_logs) {
|
||||
result.operation_logs = JSON.parse(result.operation_logs) || [];
|
||||
} else {
|
||||
result.operation_logs = [];
|
||||
}
|
||||
|
||||
res.json(responseFormat.success(result));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项详情失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项详情失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 创建监管任务结项记录
|
||||
const createTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const {
|
||||
application_no,
|
||||
policy_no,
|
||||
product_name,
|
||||
customer_name,
|
||||
customer_phone,
|
||||
insurance_amount,
|
||||
premium_amount,
|
||||
start_date,
|
||||
end_date,
|
||||
completion_date,
|
||||
status = 'pending',
|
||||
review_comments,
|
||||
attachments = []
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!application_no || !policy_no || !product_name || !customer_name) {
|
||||
return res.status(400).json(responseFormat.error('申请编号、保单号、产品名称、客户名称为必填项'));
|
||||
}
|
||||
|
||||
// 检查申请编号是否已存在
|
||||
const existingQuery = `
|
||||
SELECT id FROM regulatory_task_completions
|
||||
WHERE application_no = ? OR policy_no = ?
|
||||
`;
|
||||
|
||||
const [existing] = await sequelize.query(existingQuery, {
|
||||
replacements: [application_no, policy_no],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json(responseFormat.error('申请编号或保单号已存在'));
|
||||
}
|
||||
|
||||
// 创建监管任务结项记录
|
||||
const insertQuery = `
|
||||
INSERT INTO regulatory_task_completions (
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
`;
|
||||
|
||||
const [insertResult] = await sequelize.query(insertQuery, {
|
||||
replacements: [
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
const completionId = insertResult;
|
||||
|
||||
// 创建附件记录
|
||||
if (attachments && attachments.length > 0) {
|
||||
for (const attachment of attachments) {
|
||||
const attachmentQuery = `
|
||||
INSERT INTO regulatory_task_completion_attachments (
|
||||
completion_id, file_name, file_path, file_size, file_type, upload_time
|
||||
) VALUES (?, ?, ?, ?, ?, NOW())
|
||||
`;
|
||||
|
||||
await sequelize.query(attachmentQuery, {
|
||||
replacements: [
|
||||
completionId,
|
||||
attachment.file_name,
|
||||
attachment.file_path,
|
||||
attachment.file_size,
|
||||
attachment.file_type
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
completionId,
|
||||
'create',
|
||||
'创建监管任务结项记录',
|
||||
req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.status(201).json(responseFormat.success({
|
||||
id: completionId,
|
||||
message: '监管任务结项记录创建成功'
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('创建监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('创建监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新监管任务结项记录
|
||||
const updateTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
application_no,
|
||||
policy_no,
|
||||
product_name,
|
||||
customer_name,
|
||||
customer_phone,
|
||||
insurance_amount,
|
||||
premium_amount,
|
||||
start_date,
|
||||
end_date,
|
||||
completion_date,
|
||||
status,
|
||||
review_comments
|
||||
} = req.body;
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 更新记录
|
||||
const updateQuery = `
|
||||
UPDATE regulatory_task_completions SET
|
||||
application_no = ?, policy_no = ?, product_name = ?, customer_name = ?,
|
||||
customer_phone = ?, insurance_amount = ?, premium_amount = ?,
|
||||
start_date = ?, end_date = ?, completion_date = ?, status = ?,
|
||||
review_comments = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
`;
|
||||
|
||||
await sequelize.query(updateQuery, {
|
||||
replacements: [
|
||||
application_no, policy_no, product_name, customer_name, customer_phone,
|
||||
insurance_amount, premium_amount, start_date, end_date, completion_date,
|
||||
status, review_comments, id
|
||||
],
|
||||
type: sequelize.QueryTypes.UPDATE,
|
||||
transaction
|
||||
});
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
id,
|
||||
'update',
|
||||
'更新监管任务结项记录',
|
||||
req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录更新成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('更新监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('更新监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 删除监管任务结项记录
|
||||
const deleteTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 删除相关附件记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completion_attachments WHERE completion_id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 删除相关日志记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completion_logs WHERE completion_id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
// 删除主记录
|
||||
await sequelize.query(
|
||||
'DELETE FROM regulatory_task_completions WHERE id = ?',
|
||||
{
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.DELETE,
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录删除成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('删除监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('删除监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 审核监管任务结项记录
|
||||
const reviewTaskCompletion = async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { status, review_comments, reviewer_name } = req.body;
|
||||
|
||||
// 验证状态值
|
||||
const validStatuses = ['pending', 'approved', 'rejected', 'completed'];
|
||||
if (!validStatuses.includes(status)) {
|
||||
return res.status(400).json(responseFormat.error('无效的状态值'));
|
||||
}
|
||||
|
||||
// 检查记录是否存在
|
||||
const checkQuery = `SELECT id, status FROM regulatory_task_completions WHERE id = ?`;
|
||||
const [existing] = await sequelize.query(checkQuery, {
|
||||
replacements: [id],
|
||||
type: sequelize.QueryTypes.SELECT,
|
||||
transaction
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
|
||||
}
|
||||
|
||||
// 更新审核状态
|
||||
const updateQuery = `
|
||||
UPDATE regulatory_task_completions SET
|
||||
status = ?, review_comments = ?, reviewer_name = ?, updated_at = NOW()
|
||||
WHERE id = ?
|
||||
`;
|
||||
|
||||
await sequelize.query(updateQuery, {
|
||||
replacements: [status, review_comments, reviewer_name, id],
|
||||
type: sequelize.QueryTypes.UPDATE,
|
||||
transaction
|
||||
});
|
||||
|
||||
// 记录操作日志
|
||||
const logQuery = `
|
||||
INSERT INTO regulatory_task_completion_logs (
|
||||
completion_id, operation_type, operation_description,
|
||||
operator_name, operation_time, ip_address
|
||||
) VALUES (?, ?, ?, ?, NOW(), ?)
|
||||
`;
|
||||
|
||||
const operationDesc = `审核监管任务结项记录,状态变更为:${status}`;
|
||||
await sequelize.query(logQuery, {
|
||||
replacements: [
|
||||
id,
|
||||
'review',
|
||||
operationDesc,
|
||||
reviewer_name || req.user?.real_name || '系统',
|
||||
req.ip
|
||||
],
|
||||
type: sequelize.QueryTypes.INSERT,
|
||||
transaction
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json(responseFormat.success({ message: '监管任务结项记录审核成功' }));
|
||||
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
console.error('审核监管任务结项记录失败:', error);
|
||||
res.status(500).json(responseFormat.error('审核监管任务结项记录失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取监管任务结项统计数据
|
||||
const getTaskCompletionStats = async (req, res) => {
|
||||
try {
|
||||
// 状态统计
|
||||
const statusStatsQuery = `
|
||||
SELECT
|
||||
status,
|
||||
COUNT(*) as count
|
||||
FROM regulatory_task_completions
|
||||
GROUP BY status
|
||||
`;
|
||||
|
||||
const statusStats = await sequelize.query(statusStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 月度统计
|
||||
const monthlyStatsQuery = `
|
||||
SELECT
|
||||
DATE_FORMAT(completion_date, '%Y-%m') as month,
|
||||
COUNT(*) as count,
|
||||
SUM(insurance_amount) as total_amount
|
||||
FROM regulatory_task_completions
|
||||
WHERE completion_date >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
|
||||
GROUP BY DATE_FORMAT(completion_date, '%Y-%m')
|
||||
ORDER BY month
|
||||
`;
|
||||
|
||||
const monthlyStats = await sequelize.query(monthlyStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
// 产品统计
|
||||
const productStatsQuery = `
|
||||
SELECT
|
||||
product_name,
|
||||
COUNT(*) as count,
|
||||
SUM(insurance_amount) as total_amount
|
||||
FROM regulatory_task_completions
|
||||
GROUP BY product_name
|
||||
ORDER BY count DESC
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
const productStats = await sequelize.query(productStatsQuery, {
|
||||
type: sequelize.QueryTypes.SELECT
|
||||
});
|
||||
|
||||
res.json(responseFormat.success({
|
||||
statusStats,
|
||||
monthlyStats,
|
||||
productStats
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务结项统计数据失败:', error);
|
||||
res.status(500).json(responseFormat.error('获取监管任务结项统计数据失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getTaskCompletions,
|
||||
getTaskCompletionById,
|
||||
createTaskCompletion,
|
||||
updateTaskCompletion,
|
||||
deleteTaskCompletion,
|
||||
reviewTaskCompletion,
|
||||
getTaskCompletionStats
|
||||
};
|
||||
@@ -1,459 +0,0 @@
|
||||
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 = await Promise.all(roles.map(async (role) => {
|
||||
// 从RolePermission表获取权限
|
||||
const rolePermissions = await RolePermission.findAll({
|
||||
where: {
|
||||
role_id: role.id,
|
||||
granted: true
|
||||
},
|
||||
include: [{
|
||||
model: Permission,
|
||||
as: 'permission',
|
||||
attributes: ['id', 'name', 'code', 'description', 'module', 'type']
|
||||
}]
|
||||
});
|
||||
|
||||
const permissions = rolePermissions.map(rp => rp.permission.code);
|
||||
|
||||
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;
|
||||
console.log('获取角色权限详情,角色ID:', roleId);
|
||||
|
||||
const role = await Role.findByPk(roleId);
|
||||
console.log('角色查询结果:', role ? role.name : '未找到');
|
||||
|
||||
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']]
|
||||
});
|
||||
console.log('权限查询结果:', allPermissions.length, '个权限');
|
||||
|
||||
// 从RolePermission表获取角色已分配的权限
|
||||
const rolePermissions = await RolePermission.findAll({
|
||||
where: {
|
||||
role_id: roleId,
|
||||
granted: true
|
||||
},
|
||||
include: [{
|
||||
model: Permission,
|
||||
as: 'permission',
|
||||
attributes: ['id', 'name', 'code', 'description', 'module', 'type']
|
||||
}]
|
||||
});
|
||||
|
||||
const assignedPermissionCodes = rolePermissions.map(rp => rp.permission.code);
|
||||
console.log('已分配权限代码:', assignedPermissionCodes.length, '个');
|
||||
|
||||
// 暂时返回简化数据,不构建权限树
|
||||
res.json(responseFormat.success({
|
||||
role: {
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
description: role.description,
|
||||
status: role.status
|
||||
},
|
||||
assignedPermissions: assignedPermissionCodes,
|
||||
allPermissions: allPermissions.map(p => ({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
code: p.code,
|
||||
description: p.description,
|
||||
module: p.module,
|
||||
type: p.type,
|
||||
parent_id: p.parent_id,
|
||||
assigned: assignedPermissionCodes.includes(p.code)
|
||||
})),
|
||||
assignedCount: assignedPermissionCodes.length,
|
||||
totalCount: allPermissions.length
|
||||
}, '获取角色权限详情成功'));
|
||||
} catch (error) {
|
||||
console.error('获取角色权限详情失败:', error);
|
||||
console.error('错误堆栈:', error.stack);
|
||||
res.status(500).json(responseFormat.error('获取角色权限详情失败'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配角色权限
|
||||
*/
|
||||
async batchAssignPermissions(req, res) {
|
||||
try {
|
||||
const { roleId } = req.params;
|
||||
const { permissionIds, operation = 'replace' } = req.body;
|
||||
|
||||
console.log('=== 批量分配权限开始 ===');
|
||||
console.log('角色ID:', roleId);
|
||||
console.log('权限ID列表:', permissionIds);
|
||||
console.log('操作类型:', operation);
|
||||
console.log('请求体:', JSON.stringify(req.body, null, 2));
|
||||
|
||||
if (!Array.isArray(permissionIds)) {
|
||||
console.log('❌ 权限ID列表格式错误');
|
||||
return res.status(400).json(responseFormat.error('权限ID列表格式错误'));
|
||||
}
|
||||
|
||||
const role = await Role.findByPk(roleId);
|
||||
if (!role) {
|
||||
console.log('❌ 角色不存在');
|
||||
return res.status(404).json(responseFormat.error('角色不存在'));
|
||||
}
|
||||
|
||||
console.log('找到角色:', role.name);
|
||||
|
||||
// 验证权限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));
|
||||
|
||||
console.log('有效权限ID:', validPermissionIds);
|
||||
console.log('无效权限ID:', invalidIds);
|
||||
|
||||
if (invalidIds.length > 0) {
|
||||
console.log('❌ 存在无效的权限ID');
|
||||
return res.status(400).json(responseFormat.error(`无效的权限ID: ${invalidIds.join(', ')}`));
|
||||
}
|
||||
|
||||
// 根据操作类型处理权限分配
|
||||
if (operation === 'replace') {
|
||||
console.log('执行替换模式权限分配');
|
||||
|
||||
// 替换模式:删除现有权限,添加新权限
|
||||
const deletedCount = await RolePermission.destroy({ where: { role_id: roleId } });
|
||||
console.log('删除现有权限数量:', deletedCount);
|
||||
|
||||
if (permissionIds.length > 0) {
|
||||
const rolePermissions = permissionIds.map(permissionId => ({
|
||||
role_id: roleId,
|
||||
permission_id: permissionId,
|
||||
granted: true
|
||||
}));
|
||||
console.log('准备创建的权限记录:', rolePermissions);
|
||||
|
||||
const createdPermissions = await RolePermission.bulkCreate(rolePermissions);
|
||||
console.log('成功创建的权限记录数量:', createdPermissions.length);
|
||||
}
|
||||
} 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 }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ 权限分配完成');
|
||||
res.json(responseFormat.success(null, `${operation === 'replace' ? '替换' : operation === 'add' ? '添加' : '移除'}角色权限成功`));
|
||||
} catch (error) {
|
||||
console.error('❌ 批量分配权限失败:', error);
|
||||
console.error('错误堆栈:', error.stack);
|
||||
res.status(500).json(responseFormat.error('批量分配角色权限失败'));
|
||||
}
|
||||
|
||||
console.log('=== 批量分配权限结束 ===');
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制角色权限
|
||||
*/
|
||||
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();
|
||||
@@ -1,316 +0,0 @@
|
||||
const { User, Role, InsuranceApplication, Policy, Claim } = require('../models');
|
||||
const responseFormat = require('../utils/response');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// 获取系统统计信息
|
||||
const getSystemStats = async (req, res) => {
|
||||
try {
|
||||
const [
|
||||
totalUsers,
|
||||
totalRoles,
|
||||
totalApplications,
|
||||
totalPolicies,
|
||||
totalClaims,
|
||||
activeUsers,
|
||||
pendingApplications,
|
||||
approvedApplications,
|
||||
activePolicies,
|
||||
pendingClaims,
|
||||
approvedClaims
|
||||
] = await Promise.all([
|
||||
User.count(),
|
||||
Role.count(),
|
||||
InsuranceApplication.count(),
|
||||
Policy.count(),
|
||||
Claim.count(),
|
||||
User.count({ where: { status: 'active' } }),
|
||||
InsuranceApplication.count({ where: { status: 'pending' } }),
|
||||
InsuranceApplication.count({ where: { status: 'approved' } }),
|
||||
Policy.count({ where: { policy_status: 'active' } }),
|
||||
Claim.count({ where: { claim_status: 'pending' } }),
|
||||
Claim.count({ where: { claim_status: 'approved' } })
|
||||
]);
|
||||
|
||||
// 获取最近7天的数据趋势
|
||||
const sevenDaysAgo = new Date();
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
||||
|
||||
const recentStats = await Promise.all([
|
||||
User.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
|
||||
InsuranceApplication.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
|
||||
Policy.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
|
||||
Claim.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } })
|
||||
]);
|
||||
|
||||
res.json(responseFormat.success({
|
||||
overview: {
|
||||
users: totalUsers,
|
||||
roles: totalRoles,
|
||||
applications: totalApplications,
|
||||
policies: totalPolicies,
|
||||
claims: totalClaims
|
||||
},
|
||||
status: {
|
||||
active_users: activeUsers,
|
||||
pending_applications: pendingApplications,
|
||||
approved_applications: approvedApplications,
|
||||
active_policies: activePolicies,
|
||||
pending_claims: pendingClaims,
|
||||
approved_claims: approvedClaims
|
||||
},
|
||||
recent: {
|
||||
new_users: recentStats[0],
|
||||
new_applications: recentStats[1],
|
||||
new_policies: recentStats[2],
|
||||
new_claims: recentStats[3]
|
||||
}
|
||||
}, '获取系统统计信息成功'));
|
||||
} catch (error) {
|
||||
console.error('获取系统统计信息错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取系统统计信息失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取系统日志(模拟)
|
||||
const getSystemLogs = async (req, res) => {
|
||||
try {
|
||||
const { page = 1, limit = 50, level, start_date, end_date } = req.query;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 模拟日志数据 - 扩展更多有意义的日志记录
|
||||
const mockLogs = [
|
||||
{
|
||||
id: 1,
|
||||
level: 'info',
|
||||
message: '系统启动成功',
|
||||
timestamp: new Date().toISOString(),
|
||||
user: 'system'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
level: 'info',
|
||||
message: '数据库连接成功',
|
||||
timestamp: new Date(Date.now() - 1000 * 60).toISOString(),
|
||||
user: 'system'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
level: 'warning',
|
||||
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'
|
||||
}
|
||||
];
|
||||
|
||||
// 简单的过滤逻辑
|
||||
let filteredLogs = mockLogs;
|
||||
if (level) {
|
||||
filteredLogs = filteredLogs.filter(log => log.level === level);
|
||||
}
|
||||
if (start_date) {
|
||||
const startDate = new Date(start_date);
|
||||
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) >= startDate);
|
||||
}
|
||||
if (end_date) {
|
||||
const endDate = new Date(end_date);
|
||||
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) <= endDate);
|
||||
}
|
||||
|
||||
// 分页
|
||||
const paginatedLogs = filteredLogs.slice(offset, offset + parseInt(limit));
|
||||
|
||||
res.json(responseFormat.success({
|
||||
logs: paginatedLogs,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total: filteredLogs.length,
|
||||
pages: Math.ceil(filteredLogs.length / parseInt(limit))
|
||||
}
|
||||
}, '获取系统日志成功'));
|
||||
} catch (error) {
|
||||
console.error('获取系统日志错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取系统日志失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 获取系统配置(模拟)
|
||||
const getSystemConfig = async (req, res) => {
|
||||
try {
|
||||
const config = {
|
||||
site_name: '保险端口管理系统',
|
||||
version: '1.0.0',
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
max_file_size: '10MB',
|
||||
allowed_file_types: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'],
|
||||
session_timeout: 3600,
|
||||
backup_enabled: true,
|
||||
backup_schedule: '0 2 * * *', // 每天凌晨2点
|
||||
email_notifications: true,
|
||||
sms_notifications: false,
|
||||
maintenance_mode: false
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(config, '获取系统配置成功'));
|
||||
} catch (error) {
|
||||
console.error('获取系统配置错误:', error);
|
||||
res.status(500).json(responseFormat.error('获取系统配置失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新系统配置(模拟)
|
||||
const updateSystemConfig = async (req, res) => {
|
||||
try {
|
||||
const { maintenance_mode, email_notifications, sms_notifications } = req.body;
|
||||
|
||||
// 模拟更新配置
|
||||
const updatedConfig = {
|
||||
maintenance_mode: maintenance_mode !== undefined ? maintenance_mode : false,
|
||||
email_notifications: email_notifications !== undefined ? email_notifications : true,
|
||||
sms_notifications: sms_notifications !== undefined ? sms_notifications : false,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(updatedConfig, '系统配置更新成功'));
|
||||
} catch (error) {
|
||||
console.error('更新系统配置错误:', error);
|
||||
res.status(500).json(responseFormat.error('更新系统配置失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 备份数据库(模拟)
|
||||
const backupDatabase = async (req, res) => {
|
||||
try {
|
||||
// 模拟备份过程
|
||||
const backupInfo = {
|
||||
id: `backup_${Date.now()}`,
|
||||
filename: `backup_${new Date().toISOString().replace(/:/g, '-')}.sql`,
|
||||
size: '2.5MB',
|
||||
status: 'completed',
|
||||
created_at: new Date().toISOString(),
|
||||
download_url: '/api/system/backup/download/backup.sql'
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(backupInfo, '数据库备份成功'));
|
||||
} catch (error) {
|
||||
console.error('数据库备份错误:', error);
|
||||
res.status(500).json(responseFormat.error('数据库备份失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 恢复数据库(模拟)
|
||||
const restoreDatabase = async (req, res) => {
|
||||
try {
|
||||
const { backup_id } = req.body;
|
||||
|
||||
if (!backup_id) {
|
||||
return res.status(400).json(responseFormat.error('备份ID不能为空'));
|
||||
}
|
||||
|
||||
// 模拟恢复过程
|
||||
const restoreInfo = {
|
||||
backup_id,
|
||||
status: 'completed',
|
||||
restored_at: new Date().toISOString(),
|
||||
message: '数据库恢复成功'
|
||||
};
|
||||
|
||||
res.json(responseFormat.success(restoreInfo, '数据库恢复成功'));
|
||||
} catch (error) {
|
||||
console.error('数据库恢复错误:', error);
|
||||
res.status(500).json(responseFormat.error('数据库恢复失败'));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getSystemStats,
|
||||
getSystemLogs,
|
||||
getSystemConfig,
|
||||
updateSystemConfig,
|
||||
backupDatabase,
|
||||
restoreDatabase
|
||||
};
|
||||
Reference in New Issue
Block a user