完善保险端的前后端

This commit is contained in:
shenquanyi
2025-09-23 18:29:24 +08:00
parent e7a0cd4aa3
commit b58ed724b0
69 changed files with 9728 additions and 769 deletions

View File

@@ -109,8 +109,12 @@ const login = async (req, res) => {
expires_in: 7 * 24 * 60 * 60 // 7天
}, '登录成功'));
} catch (error) {
console.error('登录错误:', error);
res.status(500).json(responseFormat.error('登录失败'));
console.error('登录错误详情:', {
message: error.message,
stack: error.stack,
name: error.name
});
res.status(500).json(responseFormat.error('登录失败: ' + error.message));
}
};

View File

@@ -5,19 +5,41 @@ const { Op } = require('sequelize');
// 获取保险申请列表
const getApplications = async (req, res) => {
try {
console.log('获取保险申请列表 - 请求参数:', req.query);
const {
name,
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 (name) {
whereClause.customer_name = { [Op.like]: `%${name}%` };
// 申请单号筛选
if (applicationNumber) {
whereClause.application_no = { [Op.like]: `%${applicationNumber}%` };
}
// 投保人姓名筛选
if (applicantName) {
whereClause.customer_name = { [Op.like]: `%${applicantName}%` };
}
// 状态筛选
@@ -25,6 +47,16 @@ const getApplications = async (req, res) => {
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 = {
@@ -34,25 +66,23 @@ const getApplications = async (req, res) => {
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: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
],
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),
@@ -67,19 +97,58 @@ const getApplications = async (req, res) => {
// 创建保险申请
const createApplication = async (req, res) => {
try {
const applicationData = req.body;
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 = `INS${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
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({
...applicationData,
application_no: applicationNo
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'
});
res.status(201).json(responseFormat.created(application, '保险申请创建成功'));
// 返回创建的申请信息,包含关联数据
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('创建保险申请失败'));
}
};
@@ -207,6 +276,117 @@ const getApplicationStats = async (req, res) => {
}
};
// 获取参保类型选项
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,
@@ -214,5 +394,7 @@ module.exports = {
updateApplication,
reviewApplication,
deleteApplication,
getApplicationStats
getApplicationStats,
getInsuranceCategories,
exportApplications
};

View File

@@ -1,4 +1,5 @@
const { InsuranceType } = require('../models');
const { Op } = require('sequelize');
const responseFormat = require('../utils/response');
// 获取保险类型列表
@@ -28,10 +29,10 @@ const getInsuranceTypes = async (req, res) => {
page: parseInt(page),
pageSize: parseInt(pageSize),
pages: Math.ceil(count / pageSize)
}, '获取保险类型列表成功'));
}, '获取险种列表成功'));
} catch (error) {
console.error('获取保险类型列表错误:', error);
res.status(500).json(responseFormat.error('获取保险类型列表失败'));
console.error('获取险种列表错误:', error);
res.status(500).json(responseFormat.error('获取险种列表失败'));
}
};
@@ -52,12 +53,11 @@ const getInsuranceTypeById = async (req, res) => {
}
};
// 创建保险类型
// 创建险种
const createInsuranceType = async (req, res) => {
try {
const {
name,
code,
description,
coverage_amount_min,
coverage_amount_max,
@@ -68,39 +68,36 @@ const createInsuranceType = async (req, res) => {
// 检查名称是否已存在
const existingType = await InsuranceType.findOne({ where: { name } });
if (existingType) {
return res.status(400).json(responseFormat.error('保险类型名称已存在'));
}
// 检查代码是否已存在
const existingCode = await InsuranceType.findOne({ where: { code } });
if (existingCode) {
return res.status(400).json(responseFormat.error('保险类型代码已存在'));
return res.status(400).json(responseFormat.error('险种名称已存在'));
}
const insuranceType = await InsuranceType.create({
name,
code,
description,
coverage_amount_min,
coverage_amount_max,
premium_rate,
status
status,
created_by: req.user?.id
});
res.status(201).json(responseFormat.success(insuranceType, '创建保险类型成功'));
res.status(201).json(responseFormat.success(insuranceType, '创建险种成功'));
} catch (error) {
console.error('创建保险类型错误:', error);
res.status(500).json(responseFormat.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 updateInsuranceType = async (req, res) => {
try {
const { id } = req.params;
const {
name,
code,
description,
coverage_amount_min,
coverage_amount_max,
@@ -110,67 +107,68 @@ const updateInsuranceType = async (req, res) => {
const insuranceType = await InsuranceType.findByPk(id);
if (!insuranceType) {
return res.status(404).json(responseFormat.error('保险类型不存在'));
return res.status(404).json(responseFormat.error('险种不存在'));
}
// 检查名称是否已被其他类型使用
if (name && name !== insuranceType.name) {
const existingType = await InsuranceType.findOne({ where: { name } });
const existingType = await InsuranceType.findOne({
where: {
name,
id: { [Op.ne]: id }
}
});
if (existingType) {
return res.status(400).json(responseFormat.error('保险类型名称已存在'));
}
}
// 检查代码是否已被其他类型使用
if (code && code !== insuranceType.code) {
const existingCode = await InsuranceType.findOne({ where: { code } });
if (existingCode) {
return res.status(400).json(responseFormat.error('保险类型代码已存在'));
return res.status(400).json(responseFormat.error('险种名称已存在'));
}
}
await insuranceType.update({
name: name || insuranceType.name,
code: code || insuranceType.code,
description: description || insuranceType.description,
coverage_amount_min: coverage_amount_min || insuranceType.coverage_amount_min,
coverage_amount_max: coverage_amount_max || insuranceType.coverage_amount_max,
premium_rate: premium_rate || insuranceType.premium_rate,
status: status || insuranceType.status
status: status || insuranceType.status,
updated_by: req.user?.id
});
res.json(responseFormat.success(insuranceType, '更新保险类型成功'));
res.json(responseFormat.success(insuranceType, '更新险种成功'));
} catch (error) {
console.error('更新保险类型错误:', error);
res.status(500).json(responseFormat.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 deleteInsuranceType = async (req, res) => {
try {
const { id } = req.params;
const insuranceType = await InsuranceType.findByPk(id);
if (!insuranceType) {
return res.status(404).json(responseFormat.error('保险类型不存在'));
return res.status(404).json(responseFormat.error('险种不存在'));
}
// 检查是否有相关的保险申请或保单
const hasApplications = await insuranceType.countInsuranceApplications();
if (hasApplications > 0) {
return res.status(400).json(responseFormat.error('该保险类型下存在保险申请,无法删除'));
}
// const hasApplications = await insuranceType.countInsuranceApplications();
// if (hasApplications > 0) {
// return res.status(400).json(responseFormat.error('该险种下存在保险申请,无法删除'));
// }
await insuranceType.destroy();
res.json(responseFormat.success(null, '删除保险类型成功'));
res.json(responseFormat.success(null, '删除险种成功'));
} catch (error) {
console.error('删除保险类型错误:', error);
res.status(500).json(responseFormat.error('删除保险类型失败'));
console.error('删除险种错误:', error);
res.status(500).json(responseFormat.error('删除险种失败'));
}
};
// 更新保险类型状态
// 更新险种状态
const updateInsuranceTypeStatus = async (req, res) => {
try {
const { id } = req.params;
@@ -178,14 +176,14 @@ const updateInsuranceTypeStatus = async (req, res) => {
const insuranceType = await InsuranceType.findByPk(id);
if (!insuranceType) {
return res.status(404).json(responseFormat.error('保险类型不存在'));
return res.status(404).json(responseFormat.error('险种不存在'));
}
await insuranceType.update({ status });
res.json(responseFormat.success(insuranceType, '更新保险类型状态成功'));
await insuranceType.update({ status, updated_by: req.user?.id });
res.json(responseFormat.success(insuranceType, '更新险种状态成功'));
} catch (error) {
console.error('更新保险类型状态错误:', error);
res.status(500).json(responseFormat.error('更新保险类型状态失败'));
console.error('更新险种状态错误:', error);
res.status(500).json(responseFormat.error('更新险种状态失败'));
}
};

View File

@@ -0,0 +1,365 @@
const LivestockClaim = require('../models/LivestockClaim');
const LivestockPolicy = require('../models/LivestockPolicy');
const LivestockType = require('../models/LivestockType');
const User = require('../models/User');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取生资理赔列表
const getLivestockClaims = async (req, res) => {
try {
const {
claim_no,
policy_no,
farmer_name,
claim_status,
claim_type,
start_date,
end_date,
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 (claim_type) {
whereClause.claim_type = claim_type;
}
// 日期范围筛选
if (start_date && end_date) {
whereClause.incident_date = {
[Op.between]: [new Date(start_date), new Date(end_date)]
};
}
const offset = (page - 1) * limit;
const { count, rows } = await LivestockClaim.findAndCountAll({
where: whereClause,
include: [
{
model: LivestockPolicy,
as: 'policy',
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone', 'livestock_count'],
where: policy_no ? { policy_no: { [Op.like]: `%${policy_no}%` } } : {},
required: !!policy_no,
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name']
}
]
},
{
model: User,
as: 'creator',
attributes: ['id', 'real_name', 'username']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
],
order: [['created_at', 'DESC']],
offset,
limit: parseInt(limit)
});
// 如果有农户姓名筛选,需要在关联查询后再过滤
let filteredRows = rows;
if (farmer_name) {
filteredRows = rows.filter(claim =>
claim.policy && claim.policy.farmer_name.includes(farmer_name)
);
}
res.json(responseFormat.pagination(filteredRows, {
page: parseInt(page),
limit: parseInt(limit),
total: count
}, '获取生资理赔列表成功'));
} catch (error) {
console.error('获取生资理赔列表错误:', error);
res.status(500).json(responseFormat.error('获取生资理赔列表失败'));
}
};
// 创建生资理赔申请
const createLivestockClaim = async (req, res) => {
try {
const claimData = req.body;
// 验证保单是否存在且有效
const policy = await LivestockPolicy.findByPk(claimData.policy_id);
if (!policy) {
return res.status(400).json(responseFormat.error('保单不存在'));
}
if (policy.policy_status !== 'active') {
return res.status(400).json(responseFormat.error('保单状态无效,无法申请理赔'));
}
// 生成理赔编号
const claimNo = `LC${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
const claim = await LivestockClaim.create({
...claimData,
claim_no: claimNo,
claim_status: 'pending',
created_by: req.user?.id
});
// 获取完整的理赔信息(包含关联数据)
const fullClaim = await LivestockClaim.findByPk(claim.id, {
include: [
{
model: LivestockPolicy,
as: 'policy',
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone'],
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name']
}
]
}
]
});
res.status(201).json(responseFormat.created(fullClaim, '生资理赔申请创建成功'));
} catch (error) {
console.error('创建生资理赔申请错误:', error);
res.status(500).json(responseFormat.error('创建生资理赔申请失败'));
}
};
// 获取单个生资理赔详情
const getLivestockClaimById = async (req, res) => {
try {
const { id } = req.params;
const claim = await LivestockClaim.findByPk(id, {
include: [
{
model: LivestockPolicy,
as: 'policy',
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name', 'description', 'base_value']
}
]
},
{
model: User,
as: 'creator',
attributes: ['id', 'real_name', 'username']
},
{
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 reviewLivestockClaim = async (req, res) => {
try {
const { id } = req.params;
const { claim_status, review_notes, approved_amount } = req.body;
const claim = await LivestockClaim.findByPk(id);
if (!claim) {
return res.status(404).json(responseFormat.error('生资理赔不存在'));
}
if (claim.claim_status !== 'pending') {
return res.status(400).json(responseFormat.error('该理赔已经审核过了'));
}
const updateData = {
claim_status,
review_notes,
reviewed_by: req.user?.id,
reviewed_at: new Date()
};
if (claim_status === 'approved' && approved_amount) {
updateData.approved_amount = approved_amount;
}
await claim.update(updateData);
// 获取更新后的完整信息
const updatedClaim = await LivestockClaim.findByPk(id, {
include: [
{
model: LivestockPolicy,
as: 'policy',
attributes: ['id', 'policy_no', 'farmer_name', 'farmer_phone']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
]
});
res.json(responseFormat.success(updatedClaim, '生资理赔审核成功'));
} catch (error) {
console.error('审核生资理赔错误:', error);
res.status(500).json(responseFormat.error('审核生资理赔失败'));
}
};
// 更新理赔支付状态
const updateLivestockClaimPayment = async (req, res) => {
try {
const { id } = req.params;
const { payment_status, payment_date, payment_method, payment_reference } = req.body;
const claim = await LivestockClaim.findByPk(id);
if (!claim) {
return res.status(404).json(responseFormat.error('生资理赔不存在'));
}
if (claim.claim_status !== 'approved') {
return res.status(400).json(responseFormat.error('只有已批准的理赔才能更新支付状态'));
}
const updateData = {
payment_status,
payment_date,
payment_method,
payment_reference,
updated_by: req.user?.id
};
await claim.update(updateData);
res.json(responseFormat.success(claim, '理赔支付状态更新成功'));
} catch (error) {
console.error('更新理赔支付状态错误:', error);
res.status(500).json(responseFormat.error('更新理赔支付状态失败'));
}
};
// 获取生资理赔统计
const getLivestockClaimStats = async (req, res) => {
try {
const { sequelize } = require('../config/database');
// 总理赔数
const totalClaims = await LivestockClaim.count();
// 待审核理赔数
const pendingClaims = await LivestockClaim.count({
where: { claim_status: 'pending' }
});
// 已批准理赔数
const approvedClaims = await LivestockClaim.count({
where: { claim_status: 'approved' }
});
// 已拒绝理赔数
const rejectedClaims = await LivestockClaim.count({
where: { claim_status: 'rejected' }
});
// 总理赔金额
const totalClaimAmount = await LivestockClaim.sum('claim_amount') || 0;
// 已支付理赔金额
const paidClaimAmount = await LivestockClaim.sum('approved_amount', {
where: {
claim_status: 'approved',
payment_status: 'paid'
}
}) || 0;
// 按理赔类型统计
const typeStats = await sequelize.query(`
SELECT
claim_type,
COUNT(*) as claim_count,
SUM(claim_amount) as total_claim_amount,
SUM(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE 0 END) as total_approved_amount,
AVG(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE NULL END) as avg_approved_amount
FROM livestock_claims
GROUP BY claim_type
ORDER BY claim_count DESC
`, { type: sequelize.QueryTypes.SELECT });
// 按月份统计最近12个月
const monthlyStats = await sequelize.query(`
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
COUNT(*) as claim_count,
SUM(claim_amount) as total_claim_amount,
SUM(CASE WHEN claim_status = 'approved' THEN approved_amount ELSE 0 END) as total_approved_amount,
COUNT(CASE WHEN claim_status = 'approved' THEN 1 END) as approved_count
FROM livestock_claims
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month DESC
`, { type: sequelize.QueryTypes.SELECT });
const stats = {
overview: {
total_claims: totalClaims,
pending_claims: pendingClaims,
approved_claims: approvedClaims,
rejected_claims: rejectedClaims,
total_claim_amount: parseFloat(totalClaimAmount),
paid_claim_amount: parseFloat(paidClaimAmount),
approval_rate: totalClaims > 0 ? (approvedClaims / totalClaims * 100).toFixed(2) : 0
},
by_type: typeStats,
monthly: monthlyStats
};
res.json(responseFormat.success(stats, '获取生资理赔统计成功'));
} catch (error) {
console.error('获取生资理赔统计错误:', error);
res.status(500).json(responseFormat.error('获取生资理赔统计失败'));
}
};
module.exports = {
getLivestockClaims,
createLivestockClaim,
getLivestockClaimById,
reviewLivestockClaim,
updateLivestockClaimPayment,
getLivestockClaimStats
};

View File

@@ -0,0 +1,348 @@
const LivestockPolicy = require('../models/LivestockPolicy');
const LivestockType = require('../models/LivestockType');
const User = require('../models/User');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取生资保单列表
const getLivestockPolicies = async (req, res) => {
try {
const {
policy_no,
farmer_name,
farmer_phone,
policy_status,
payment_status,
livestock_type_id,
start_date,
end_date,
page = 1,
limit = 10
} = req.query;
const whereClause = {};
// 保单编号筛选
if (policy_no) {
whereClause.policy_no = { [Op.like]: `%${policy_no}%` };
}
// 农户姓名筛选
if (farmer_name) {
whereClause.farmer_name = { [Op.like]: `%${farmer_name}%` };
}
// 农户电话筛选
if (farmer_phone) {
whereClause.farmer_phone = { [Op.like]: `%${farmer_phone}%` };
}
// 保单状态筛选
if (policy_status) {
whereClause.policy_status = policy_status;
}
// 支付状态筛选
if (payment_status) {
whereClause.payment_status = payment_status;
}
// 牲畜类型筛选
if (livestock_type_id) {
whereClause.livestock_type_id = livestock_type_id;
}
// 日期范围筛选
if (start_date && end_date) {
whereClause.start_date = {
[Op.between]: [new Date(start_date), new Date(end_date)]
};
}
const offset = (page - 1) * limit;
const { count, rows } = await LivestockPolicy.findAndCountAll({
where: whereClause,
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
},
{
model: User,
as: 'creator',
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 createLivestockPolicy = async (req, res) => {
try {
const policyData = req.body;
// 生成保单编号
const policyNo = `LP${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
// 计算总保额和保费
const totalValue = policyData.livestock_count * policyData.unit_value;
const premiumAmount = totalValue * policyData.premium_rate;
const policy = await LivestockPolicy.create({
...policyData,
policy_no: policyNo,
total_value: totalValue,
premium_amount: premiumAmount,
created_by: req.user?.id
});
// 获取完整的保单信息(包含关联数据)
const fullPolicy = await LivestockPolicy.findByPk(policy.id, {
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
}
]
});
res.status(201).json(responseFormat.created(fullPolicy, '生资保单创建成功'));
} catch (error) {
console.error('创建生资保单错误:', error);
res.status(500).json(responseFormat.error('创建生资保单失败'));
}
};
// 获取单个生资保单详情
const getLivestockPolicyById = async (req, res) => {
try {
const { id } = req.params;
const policy = await LivestockPolicy.findByPk(id, {
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
},
{
model: User,
as: 'creator',
attributes: ['id', 'real_name', 'username']
},
{
model: User,
as: 'updater',
attributes: ['id', 'real_name', 'username']
}
]
});
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 updateLivestockPolicy = async (req, res) => {
try {
const { id } = req.params;
const updateData = req.body;
const policy = await LivestockPolicy.findByPk(id);
if (!policy) {
return res.status(404).json(responseFormat.error('生资保单不存在'));
}
// 如果更新了数量或单价,重新计算总保额和保费
if (updateData.livestock_count || updateData.unit_value || updateData.premium_rate) {
const livestockCount = updateData.livestock_count || policy.livestock_count;
const unitValue = updateData.unit_value || policy.unit_value;
const premiumRate = updateData.premium_rate || policy.premium_rate;
updateData.total_value = livestockCount * unitValue;
updateData.premium_amount = updateData.total_value * premiumRate;
}
updateData.updated_by = req.user?.id;
await policy.update(updateData);
// 获取更新后的完整信息
const updatedPolicy = await LivestockPolicy.findByPk(id, {
include: [
{
model: LivestockType,
as: 'livestock_type',
attributes: ['id', 'name', 'description', 'unit_price_min', 'unit_price_max', 'premium_rate']
}
]
});
res.json(responseFormat.success(updatedPolicy, '生资保单更新成功'));
} catch (error) {
console.error('更新生资保单错误:', error);
res.status(500).json(responseFormat.error('更新生资保单失败'));
}
};
// 更新生资保单状态
const updateLivestockPolicyStatus = async (req, res) => {
try {
const { id } = req.params;
const { policy_status, payment_status, payment_date } = req.body;
const policy = await LivestockPolicy.findByPk(id);
if (!policy) {
return res.status(404).json(responseFormat.error('生资保单不存在'));
}
const updateData = {
updated_by: req.user?.id
};
if (policy_status) updateData.policy_status = policy_status;
if (payment_status) updateData.payment_status = payment_status;
if (payment_date) updateData.payment_date = payment_date;
await policy.update(updateData);
res.json(responseFormat.success(policy, '生资保单状态更新成功'));
} catch (error) {
console.error('更新生资保单状态错误:', error);
res.status(500).json(responseFormat.error('更新生资保单状态失败'));
}
};
// 获取生资保单统计
const getLivestockPolicyStats = async (req, res) => {
try {
const { sequelize } = require('../config/database');
// 总保单数
const totalPolicies = await LivestockPolicy.count();
// 有效保单数
const activePolicies = await LivestockPolicy.count({
where: { policy_status: 'active' }
});
// 已支付保单数
const paidPolicies = await LivestockPolicy.count({
where: { payment_status: 'paid' }
});
// 总保费收入
const totalPremium = await LivestockPolicy.sum('premium_amount', {
where: { payment_status: 'paid' }
}) || 0;
// 总保额
const totalCoverage = await LivestockPolicy.sum('total_value', {
where: { policy_status: 'active' }
}) || 0;
// 按牲畜类型统计
const typeStats = await sequelize.query(`
SELECT
lt.name as livestock_type,
COUNT(lp.id) as policy_count,
SUM(lp.livestock_count) as total_livestock,
SUM(lp.total_value) as total_coverage,
SUM(lp.premium_amount) as total_premium
FROM livestock_policies lp
LEFT JOIN livestock_types lt ON lp.livestock_type_id = lt.id
WHERE lp.policy_status = 'active'
GROUP BY lt.id, lt.name
ORDER BY policy_count DESC
`, { type: sequelize.QueryTypes.SELECT });
// 按月份统计最近12个月
const monthlyStats = await sequelize.query(`
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
COUNT(*) as policy_count,
SUM(premium_amount) as premium_amount,
SUM(total_value) as total_coverage
FROM livestock_policies
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month DESC
`, { type: sequelize.QueryTypes.SELECT });
const stats = {
overview: {
total_policies: totalPolicies,
active_policies: activePolicies,
paid_policies: paidPolicies,
total_premium: parseFloat(totalPremium),
total_coverage: parseFloat(totalCoverage),
payment_rate: totalPolicies > 0 ? (paidPolicies / totalPolicies * 100).toFixed(2) : 0
},
by_type: typeStats,
monthly: monthlyStats
};
res.json(responseFormat.success(stats, '获取生资保单统计成功'));
} catch (error) {
console.error('获取生资保单统计错误:', error);
res.status(500).json(responseFormat.error('获取生资保单统计失败'));
}
};
// 删除生资保单
const deleteLivestockPolicy = async (req, res) => {
try {
const { id } = req.params;
const policy = await LivestockPolicy.findByPk(id);
if (!policy) {
return res.status(404).json(responseFormat.error('生资保单不存在'));
}
// 检查保单状态,只有草稿状态的保单可以删除
if (policy.policy_status === 'active') {
return res.status(400).json(responseFormat.error('生效中的保单不能删除'));
}
// 物理删除保单
await policy.destroy();
res.json(responseFormat.success(null, '生资保单删除成功'));
} catch (error) {
console.error('删除生资保单错误:', error);
res.status(500).json(responseFormat.error('删除生资保单失败'));
}
};
module.exports = {
getLivestockPolicies,
createLivestockPolicy,
getLivestockPolicyById,
updateLivestockPolicy,
updateLivestockPolicyStatus,
deleteLivestockPolicy,
getLivestockPolicyStats
};

View File

@@ -0,0 +1,221 @@
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
};

View File

@@ -0,0 +1,603 @@
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
};