完善保险端的前后端
This commit is contained in:
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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('更新险种状态失败'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
365
insurance_backend/controllers/livestockClaimController.js
Normal file
365
insurance_backend/controllers/livestockClaimController.js
Normal 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
|
||||
};
|
||||
348
insurance_backend/controllers/livestockPolicyController.js
Normal file
348
insurance_backend/controllers/livestockPolicyController.js
Normal 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
|
||||
};
|
||||
221
insurance_backend/controllers/livestockTypeController.js
Normal file
221
insurance_backend/controllers/livestockTypeController.js
Normal 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
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
Reference in New Issue
Block a user