完善保险端前后端和养殖端小程序
This commit is contained in:
8
insurance_backend/.sequelizerc
Normal file
8
insurance_backend/.sequelizerc
Normal file
@@ -0,0 +1,8 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
'config': path.resolve('config', 'config.json'),
|
||||
'models-path': path.resolve('models'),
|
||||
'seeders-path': path.resolve('seeders'),
|
||||
'migrations-path': path.resolve('migrations')
|
||||
};
|
||||
33
insurance_backend/config/config.json
Normal file
33
insurance_backend/config/config.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"development": {
|
||||
"username": "root",
|
||||
"password": "aiotAiot123!",
|
||||
"database": "insurance_data",
|
||||
"host": "129.211.213.226",
|
||||
"port": 9527,
|
||||
"dialect": "mysql",
|
||||
"timezone": "+08:00",
|
||||
"dialectOptions": {
|
||||
"dateStrings": true,
|
||||
"typeCast": true
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"username": "root",
|
||||
"password": "aiotAiot123!",
|
||||
"database": "insurance_data_test",
|
||||
"host": "129.211.213.226",
|
||||
"port": 9527,
|
||||
"dialect": "mysql",
|
||||
"timezone": "+08:00"
|
||||
},
|
||||
"production": {
|
||||
"username": "root",
|
||||
"password": "aiotAiot123!",
|
||||
"database": "insurance_data_prod",
|
||||
"host": "129.211.213.226",
|
||||
"port": 9527,
|
||||
"dialect": "mysql",
|
||||
"timezone": "+08:00"
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ const sequelize = new Sequelize({
|
||||
dialect: process.env.DB_DIALECT || 'mysql',
|
||||
host: process.env.DB_HOST || '129.211.213.226',
|
||||
port: process.env.DB_PORT || 9527,
|
||||
database: process.env.DB_NAME || 'insurance_data',
|
||||
database: process.env.DB_DATABASE || 'insurance_data',
|
||||
username: process.env.DB_USER || 'root',
|
||||
password: process.env.DB_PASSWORD || 'aiotAiot123!',
|
||||
logging: process.env.NODE_ENV === 'development' ? console.log : false,
|
||||
|
||||
477
insurance_backend/controllers/installationTaskController.js
Normal file
477
insurance_backend/controllers/installationTaskController.js
Normal file
@@ -0,0 +1,477 @@
|
||||
const InstallationTask = require('../models/InstallationTask');
|
||||
const User = require('../models/User');
|
||||
const { Op, sequelize } = require('sequelize');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
class InstallationTaskController {
|
||||
|
||||
// 获取待安装任务列表
|
||||
async getInstallationTasks(req, res) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
pageSize = 10,
|
||||
policyNumber,
|
||||
customerName,
|
||||
installationStatus,
|
||||
priority,
|
||||
keyword,
|
||||
startDate,
|
||||
endDate
|
||||
} = req.query;
|
||||
|
||||
const offset = (page - 1) * pageSize;
|
||||
const limit = parseInt(pageSize);
|
||||
|
||||
// 构建查询条件
|
||||
const whereConditions = {};
|
||||
|
||||
if (policyNumber) {
|
||||
whereConditions.policyNumber = { [Op.like]: `%${policyNumber}%` };
|
||||
}
|
||||
|
||||
if (customerName) {
|
||||
whereConditions.customerName = { [Op.like]: `%${customerName}%` };
|
||||
}
|
||||
|
||||
if (installationStatus) {
|
||||
whereConditions.installationStatus = installationStatus;
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
whereConditions.priority = priority;
|
||||
}
|
||||
|
||||
// 关键字搜索(搜索申请单号、保单编号、客户姓名等)
|
||||
if (keyword) {
|
||||
whereConditions[Op.or] = [
|
||||
{ applicationNumber: { [Op.like]: `%${keyword}%` } },
|
||||
{ policyNumber: { [Op.like]: `%${keyword}%` } },
|
||||
{ customerName: { [Op.like]: `%${customerName}%` } },
|
||||
{ productName: { [Op.like]: `%${keyword}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
if (startDate && endDate) {
|
||||
whereConditions.taskGeneratedTime = {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
};
|
||||
}
|
||||
|
||||
const { count, rows } = await InstallationTask.findAndCountAll({
|
||||
where: whereConditions,
|
||||
order: [['taskGeneratedTime', 'DESC']],
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取待安装任务列表成功',
|
||||
data: {
|
||||
list: rows,
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
pageSize: limit,
|
||||
totalPages: Math.ceil(count / limit)
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取待安装任务列表失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取待安装任务列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 创建待安装任务
|
||||
async createInstallationTask(req, res) {
|
||||
try {
|
||||
const {
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
livestockSupplyType,
|
||||
pendingDevices,
|
||||
installationStatus = '待安装',
|
||||
priority = '中',
|
||||
assignedTo,
|
||||
taskGeneratedTime = new Date()
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!applicationNumber || !policyNumber || !productName || !customerName) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '申请单号、保单编号、产品名称、客户姓名为必填项'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查申请单号是否已存在
|
||||
const existingTask = await InstallationTask.findOne({
|
||||
where: { applicationNumber }
|
||||
});
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(409).json({
|
||||
code: 409,
|
||||
status: 'error',
|
||||
message: '该申请单号已存在待安装任务'
|
||||
});
|
||||
}
|
||||
|
||||
const installationTask = await InstallationTask.create({
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType: idType || '身份证',
|
||||
idNumber: idNumber || '',
|
||||
livestockSupplyType,
|
||||
pendingDevices: pendingDevices ? JSON.stringify(pendingDevices) : null,
|
||||
installationStatus,
|
||||
priority,
|
||||
assignedTo,
|
||||
taskGeneratedTime,
|
||||
createdBy: req.user?.id
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
code: 201,
|
||||
status: 'success',
|
||||
message: '创建待安装任务成功',
|
||||
data: installationTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('创建待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '创建待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取待安装任务详情
|
||||
async getInstallationTaskById(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const installationTask = await InstallationTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'technician',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!installationTask) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取待安装任务详情成功',
|
||||
data: installationTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取待安装任务详情失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取待安装任务详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新待安装任务
|
||||
async updateInstallationTask(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = { ...req.body };
|
||||
|
||||
// 添加更新人信息
|
||||
updateData.updatedBy = req.user?.id;
|
||||
|
||||
// 处理设备数据
|
||||
if (updateData.pendingDevices) {
|
||||
updateData.pendingDevices = JSON.stringify(updateData.pendingDevices);
|
||||
}
|
||||
|
||||
// 处理安装完成时间
|
||||
if (updateData.installationStatus === '已安装' && !updateData.installationCompletedTime) {
|
||||
updateData.installationCompletedTime = new Date();
|
||||
}
|
||||
|
||||
const [updatedCount] = await InstallationTask.update(updateData, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在或未做任何修改'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取更新后的任务
|
||||
const updatedTask = await InstallationTask.findByPk(id);
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '更新待安装任务成功',
|
||||
data: updatedTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('更新待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '更新待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 删除待安装任务
|
||||
async deleteInstallationTask(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const deletedCount = await InstallationTask.destroy({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (deletedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '待安装任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '删除待安装任务成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('删除待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '删除待安装任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 批量操作待安装任务
|
||||
async batchOperateInstallationTasks(req, res) {
|
||||
try {
|
||||
const { ids, operation, data } = req.body;
|
||||
|
||||
if (!ids || !Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '任务ID列表不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
let result;
|
||||
switch (operation) {
|
||||
case 'assign':
|
||||
if (!data.assignedTo) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '分配操作需要指定分配给的用户'
|
||||
});
|
||||
}
|
||||
result = await InstallationTask.update(
|
||||
{ assignedTo: data.assignedTo, updatedBy: req.user?.id },
|
||||
{ where: { id: ids } }
|
||||
);
|
||||
break;
|
||||
case 'updateStatus':
|
||||
if (!data.installationStatus) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '状态更新操作需要指定新状态'
|
||||
});
|
||||
}
|
||||
result = await InstallationTask.update(
|
||||
{ installationStatus: data.installationStatus, updatedBy: req.user?.id },
|
||||
{ where: { id: ids } }
|
||||
);
|
||||
break;
|
||||
case 'delete':
|
||||
result = await InstallationTask.destroy({ where: { id: ids } });
|
||||
break;
|
||||
default:
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '不支持的操作类型'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: `批量${operation}操作完成`,
|
||||
data: { affectedRows: result[0] || result }
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('批量操作待安装任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '批量操作失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出待安装任务数据
|
||||
async exportInstallationTasks(req, res) {
|
||||
try {
|
||||
const { ids } = req.query;
|
||||
let where = {};
|
||||
|
||||
if (ids) {
|
||||
where.id = { [Op.in]: ids.split(',') };
|
||||
}
|
||||
|
||||
const tasks = await InstallationTask.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'technician',
|
||||
attributes: ['username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['taskGeneratedTime', 'DESC']]
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '导出待安装任务数据成功',
|
||||
data: tasks
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('导出待安装任务数据失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '导出数据失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务统计信息
|
||||
async getInstallationTaskStats(req, res) {
|
||||
try {
|
||||
// 按状态统计
|
||||
const statusStats = await InstallationTask.findAll({
|
||||
attributes: [
|
||||
'installationStatus',
|
||||
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['installationStatus'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 按优先级统计
|
||||
const priorityStats = await InstallationTask.findAll({
|
||||
attributes: [
|
||||
'priority',
|
||||
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['priority'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
// 总数统计
|
||||
const total = await InstallationTask.count();
|
||||
|
||||
// 本月新增任务
|
||||
const thisMonth = await InstallationTask.count({
|
||||
where: {
|
||||
taskGeneratedTime: {
|
||||
[Op.gte]: new Date(new Date().getFullYear(), new Date().getMonth(), 1)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取任务统计信息成功',
|
||||
data: {
|
||||
total,
|
||||
thisMonth,
|
||||
statusStats,
|
||||
priorityStats
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取任务统计信息失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取统计信息失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new InstallationTaskController();
|
||||
527
insurance_backend/controllers/supervisoryTaskController.js
Normal file
527
insurance_backend/controllers/supervisoryTaskController.js
Normal file
@@ -0,0 +1,527 @@
|
||||
const { SupervisoryTask, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* 监管任务控制器
|
||||
*/
|
||||
class SupervisoryTaskController {
|
||||
|
||||
/**
|
||||
* 获取监管任务列表(支持分页和搜索)
|
||||
*/
|
||||
static async getList(req, res) {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
policyNumber = '',
|
||||
customerName = '',
|
||||
taskStatus = '',
|
||||
priority = '',
|
||||
dateRange = '',
|
||||
sortBy = 'createdAt',
|
||||
sortOrder = 'DESC'
|
||||
} = req.query;
|
||||
|
||||
// 构建查询条件
|
||||
const where = {};
|
||||
|
||||
if (policyNumber) {
|
||||
where.policyNumber = { [Op.like]: `%${policyNumber}%` };
|
||||
}
|
||||
|
||||
if (customerName) {
|
||||
where.customerName = { [Op.like]: `%${customerName}%` };
|
||||
}
|
||||
|
||||
if (taskStatus) {
|
||||
where.taskStatus = taskStatus;
|
||||
}
|
||||
|
||||
if (priority) {
|
||||
where.priority = priority;
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (dateRange) {
|
||||
const [startDate, endDate] = dateRange.split(',');
|
||||
if (startDate && endDate) {
|
||||
where.createdAt = {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
const offset = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
// 查询数据
|
||||
const { count, rows } = await SupervisoryTask.findAndCountAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'assignedUser',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [[sortBy, sortOrder.toUpperCase()]],
|
||||
limit: parseInt(limit),
|
||||
offset
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取监管任务列表成功',
|
||||
data: {
|
||||
list: rows,
|
||||
total: count,
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
totalPages: Math.ceil(count / parseInt(limit))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务列表失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取监管任务列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取监管任务详情
|
||||
*/
|
||||
static async getById(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const task = await SupervisoryTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'assignedUser',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '监管任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取监管任务详情成功',
|
||||
data: task
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取监管任务详情失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取监管任务详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新的监管任务
|
||||
*/
|
||||
static async create(req, res) {
|
||||
try {
|
||||
const {
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
insurancePeriod,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
applicableSupplies,
|
||||
supervisorySuppliesQuantity,
|
||||
taskStatus = '待处理',
|
||||
priority = '中',
|
||||
assignedTo,
|
||||
dueDate,
|
||||
notes
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!applicationNumber || !policyNumber || !productName || !customerName || !idNumber) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '申请单号、保单编号、产品名称、客户姓名和证件号码为必填项'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查申请单号是否已存在
|
||||
const existingTask = await SupervisoryTask.findOne({
|
||||
where: { applicationNumber }
|
||||
});
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(409).json({
|
||||
code: 409,
|
||||
status: 'error',
|
||||
message: '该申请单号已存在监管任务'
|
||||
});
|
||||
}
|
||||
|
||||
// 创建监管任务
|
||||
const task = await SupervisoryTask.create({
|
||||
applicationNumber,
|
||||
policyNumber,
|
||||
productName,
|
||||
insurancePeriod,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
applicableSupplies: JSON.stringify(applicableSupplies),
|
||||
supervisorySuppliesQuantity: parseInt(supervisorySuppliesQuantity) || 0,
|
||||
taskStatus,
|
||||
priority,
|
||||
assignedTo,
|
||||
dueDate: dueDate ? new Date(dueDate) : null,
|
||||
notes,
|
||||
createdBy: req.user?.id
|
||||
});
|
||||
|
||||
// 查询完整的任务信息(包含关联数据)
|
||||
const createdTask = await SupervisoryTask.findByPk(task.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'assignedUser',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
code: 201,
|
||||
status: 'success',
|
||||
message: '创建监管任务成功',
|
||||
data: createdTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建监管任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '创建监管任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新监管任务
|
||||
*/
|
||||
static async update(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updateData = { ...req.body };
|
||||
|
||||
// 添加更新人信息
|
||||
updateData.updatedBy = req.user?.id;
|
||||
|
||||
// 处理适用生资数据
|
||||
if (updateData.applicableSupplies) {
|
||||
updateData.applicableSupplies = JSON.stringify(updateData.applicableSupplies);
|
||||
}
|
||||
|
||||
// 处理截止日期
|
||||
if (updateData.dueDate) {
|
||||
updateData.dueDate = new Date(updateData.dueDate);
|
||||
}
|
||||
|
||||
// 处理完成时间
|
||||
if (updateData.taskStatus === '已完成' && !updateData.completedAt) {
|
||||
updateData.completedAt = new Date();
|
||||
}
|
||||
|
||||
const [updatedCount] = await SupervisoryTask.update(updateData, {
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (updatedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '监管任务不存在或未做任何修改'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询更新后的任务信息
|
||||
const updatedTask = await SupervisoryTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'assignedUser',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '更新监管任务成功',
|
||||
data: updatedTask
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新监管任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '更新监管任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除监管任务
|
||||
*/
|
||||
static async delete(req, res) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const deletedCount = await SupervisoryTask.destroy({
|
||||
where: { id }
|
||||
});
|
||||
|
||||
if (deletedCount === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: '监管任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '删除监管任务成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('删除监管任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '删除监管任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建监管任务
|
||||
*/
|
||||
static async bulkCreate(req, res) {
|
||||
try {
|
||||
const { tasks } = req.body;
|
||||
|
||||
if (!tasks || !Array.isArray(tasks) || tasks.length === 0) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
status: 'error',
|
||||
message: '请提供有效的任务数据数组'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证并处理任务数据
|
||||
const processedTasks = tasks.map(task => ({
|
||||
...task,
|
||||
applicableSupplies: JSON.stringify(task.applicableSupplies || []),
|
||||
supervisorySuppliesQuantity: parseInt(task.supervisorySuppliesQuantity) || 0,
|
||||
taskStatus: task.taskStatus || '待处理',
|
||||
priority: task.priority || '中',
|
||||
dueDate: task.dueDate ? new Date(task.dueDate) : null,
|
||||
createdBy: req.user?.id
|
||||
}));
|
||||
|
||||
const createdTasks = await SupervisoryTask.bulkCreate(processedTasks, {
|
||||
ignoreDuplicates: true,
|
||||
returning: true
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
code: 201,
|
||||
status: 'success',
|
||||
message: `批量创建监管任务成功,共创建${createdTasks.length}条记录`,
|
||||
data: {
|
||||
count: createdTasks.length,
|
||||
tasks: createdTasks
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('批量创建监管任务失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '批量创建监管任务失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出监管任务数据
|
||||
*/
|
||||
static async export(req, res) {
|
||||
try {
|
||||
const { ids, ...filters } = req.query;
|
||||
|
||||
let where = {};
|
||||
|
||||
// 如果指定了ID列表,则只导出指定的任务
|
||||
if (ids) {
|
||||
where.id = { [Op.in]: ids.split(',').map(id => parseInt(id)) };
|
||||
} else {
|
||||
// 否则根据筛选条件导出
|
||||
if (filters.policyNumber) {
|
||||
where.policyNumber = { [Op.like]: `%${filters.policyNumber}%` };
|
||||
}
|
||||
if (filters.customerName) {
|
||||
where.customerName = { [Op.like]: `%${filters.customerName}%` };
|
||||
}
|
||||
if (filters.taskStatus) {
|
||||
where.taskStatus = filters.taskStatus;
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = await SupervisoryTask.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'assignedUser',
|
||||
attributes: ['username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['createdAt', 'DESC']]
|
||||
});
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '导出监管任务数据成功',
|
||||
data: tasks
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出监管任务数据失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '导出监管任务数据失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务统计信息
|
||||
*/
|
||||
static async getStatistics(req, res) {
|
||||
try {
|
||||
// 按状态统计
|
||||
const statusStats = await SupervisoryTask.findAll({
|
||||
attributes: [
|
||||
'taskStatus',
|
||||
[SupervisoryTask.sequelize.fn('COUNT', SupervisoryTask.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['taskStatus']
|
||||
});
|
||||
|
||||
// 按优先级统计
|
||||
const priorityStats = await SupervisoryTask.findAll({
|
||||
attributes: [
|
||||
'priority',
|
||||
[SupervisoryTask.sequelize.fn('COUNT', SupervisoryTask.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['priority']
|
||||
});
|
||||
|
||||
// 总数统计
|
||||
const total = await SupervisoryTask.count();
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: '获取任务统计信息成功',
|
||||
data: {
|
||||
total,
|
||||
statusStats: statusStats.map(item => ({
|
||||
status: item.taskStatus,
|
||||
count: parseInt(item.dataValues.count)
|
||||
})),
|
||||
priorityStats: priorityStats.map(item => ({
|
||||
priority: item.priority,
|
||||
count: parseInt(item.dataValues.count)
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取任务统计信息失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
status: 'error',
|
||||
message: '获取任务统计信息失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SupervisoryTaskController;
|
||||
184
insurance_backend/middleware/validation.js
Normal file
184
insurance_backend/middleware/validation.js
Normal file
@@ -0,0 +1,184 @@
|
||||
const { body, validationResult } = require('express-validator');
|
||||
|
||||
// 监管任务验证规则
|
||||
const validateSupervisionTask = [
|
||||
body('applicationId')
|
||||
.notEmpty()
|
||||
.withMessage('申请单号不能为空')
|
||||
.isLength({ min: 1, max: 50 })
|
||||
.withMessage('申请单号长度应在1-50字符之间'),
|
||||
|
||||
body('policyId')
|
||||
.notEmpty()
|
||||
.withMessage('保单编号不能为空')
|
||||
.isLength({ min: 1, max: 50 })
|
||||
.withMessage('保单编号长度应在1-50字符之间'),
|
||||
|
||||
body('productName')
|
||||
.notEmpty()
|
||||
.withMessage('产品名称不能为空')
|
||||
.isLength({ min: 1, max: 100 })
|
||||
.withMessage('产品名称长度应在1-100字符之间'),
|
||||
|
||||
body('customerName')
|
||||
.notEmpty()
|
||||
.withMessage('客户姓名不能为空')
|
||||
.isLength({ min: 1, max: 50 })
|
||||
.withMessage('客户姓名长度应在1-50字符之间'),
|
||||
|
||||
body('taskType')
|
||||
.isIn(['new_application', 'task_guidance', 'batch_operation'])
|
||||
.withMessage('任务类型必须是: new_application, task_guidance, batch_operation'),
|
||||
|
||||
body('documentType')
|
||||
.optional()
|
||||
.isLength({ max: 20 })
|
||||
.withMessage('证件类型长度不能超过20字符'),
|
||||
|
||||
body('documentNumber')
|
||||
.optional()
|
||||
.isLength({ max: 50 })
|
||||
.withMessage('证件号码长度不能超过50字符'),
|
||||
|
||||
body('applicableAmount')
|
||||
.optional()
|
||||
.isFloat({ min: 0 })
|
||||
.withMessage('适用金额必须是非负数'),
|
||||
|
||||
body('priority')
|
||||
.optional()
|
||||
.isIn(['low', 'medium', 'high', 'urgent'])
|
||||
.withMessage('优先级必须是: low, medium, high, urgent'),
|
||||
|
||||
body('assignedTo')
|
||||
.optional()
|
||||
.isInt({ min: 1 })
|
||||
.withMessage('分配用户ID必须是正整数'),
|
||||
|
||||
body('dueDate')
|
||||
.optional()
|
||||
.isISO8601()
|
||||
.withMessage('截止日期格式不正确'),
|
||||
|
||||
body('remarks')
|
||||
.optional()
|
||||
.isLength({ max: 1000 })
|
||||
.withMessage('备注长度不能超过1000字符'),
|
||||
|
||||
// 验证结果处理
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '数据验证失败',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
|
||||
// 批量操作验证规则
|
||||
const validateBatchOperation = [
|
||||
body('ids')
|
||||
.isArray({ min: 1 })
|
||||
.withMessage('必须提供至少一个ID'),
|
||||
|
||||
body('ids.*')
|
||||
.isInt({ min: 1 })
|
||||
.withMessage('ID必须是正整数'),
|
||||
|
||||
body('operation')
|
||||
.isIn(['assign', 'updateStatus', 'delete'])
|
||||
.withMessage('操作类型必须是: assign, updateStatus, delete'),
|
||||
|
||||
body('data')
|
||||
.if(body('operation').equals('assign'))
|
||||
.custom((value) => {
|
||||
if (!value || !value.assignedTo) {
|
||||
throw new Error('分配操作必须提供assignedTo字段');
|
||||
}
|
||||
if (!Number.isInteger(value.assignedTo) || value.assignedTo < 1) {
|
||||
throw new Error('assignedTo必须是正整数');
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
|
||||
body('data')
|
||||
.if(body('operation').equals('updateStatus'))
|
||||
.custom((value) => {
|
||||
if (!value || !value.status) {
|
||||
throw new Error('状态更新操作必须提供status字段');
|
||||
}
|
||||
if (!['pending', 'processing', 'completed', 'rejected'].includes(value.status)) {
|
||||
throw new Error('status必须是: pending, processing, completed, rejected');
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
|
||||
// 验证结果处理
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '数据验证失败',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
|
||||
// 监管任务更新验证规则
|
||||
const validateSupervisionTaskUpdate = [
|
||||
body('status')
|
||||
.optional()
|
||||
.isIn(['pending', 'processing', 'completed', 'rejected'])
|
||||
.withMessage('状态必须是: pending, processing, completed, rejected'),
|
||||
|
||||
body('priority')
|
||||
.optional()
|
||||
.isIn(['low', 'medium', 'high', 'urgent'])
|
||||
.withMessage('优先级必须是: low, medium, high, urgent'),
|
||||
|
||||
body('assignedTo')
|
||||
.optional()
|
||||
.isInt({ min: 1 })
|
||||
.withMessage('分配用户ID必须是正整数'),
|
||||
|
||||
body('dueDate')
|
||||
.optional()
|
||||
.isISO8601()
|
||||
.withMessage('截止日期格式不正确'),
|
||||
|
||||
body('remarks')
|
||||
.optional()
|
||||
.isLength({ max: 1000 })
|
||||
.withMessage('备注长度不能超过1000字符'),
|
||||
|
||||
body('supervisionDataCount')
|
||||
.optional()
|
||||
.isInt({ min: 0 })
|
||||
.withMessage('监管生成数量必须是非负整数'),
|
||||
|
||||
// 验证结果处理
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '数据验证失败',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
validateSupervisionTask,
|
||||
validateBatchOperation,
|
||||
validateSupervisionTaskUpdate
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable('supervision_tasks', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
comment: '监管任务ID'
|
||||
},
|
||||
applicationId: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '申请单号',
|
||||
field: 'application_id'
|
||||
},
|
||||
policyId: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '保单编号',
|
||||
field: 'policy_id'
|
||||
},
|
||||
productName: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '产品名称',
|
||||
field: 'product_name'
|
||||
},
|
||||
insurancePeriod: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '保险期间',
|
||||
field: 'insurance_period'
|
||||
},
|
||||
customerName: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '客户姓名',
|
||||
field: 'customer_name'
|
||||
},
|
||||
documentType: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
comment: '证件类型',
|
||||
field: 'document_type'
|
||||
},
|
||||
documentNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '证件号码',
|
||||
field: 'document_number'
|
||||
},
|
||||
applicableAmount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '适用金额',
|
||||
field: 'applicable_amount'
|
||||
},
|
||||
supervisionDataCount: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
comment: '监管生成数量',
|
||||
field: 'supervision_data_count'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('pending', 'processing', 'completed', 'rejected'),
|
||||
defaultValue: 'pending',
|
||||
comment: '状态: pending-待处理, processing-处理中, completed-已完成, rejected-已拒绝'
|
||||
},
|
||||
taskType: {
|
||||
type: DataTypes.ENUM('new_application', 'task_guidance', 'batch_operation'),
|
||||
allowNull: false,
|
||||
comment: '任务类型: new_application-新增监管任务, task_guidance-任务导入, batch_operation-批量新增',
|
||||
field: 'task_type'
|
||||
},
|
||||
assignedTo: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '分配给用户ID',
|
||||
field: 'assigned_to',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
priority: {
|
||||
type: DataTypes.ENUM('low', 'medium', 'high', 'urgent'),
|
||||
defaultValue: 'medium',
|
||||
comment: '优先级'
|
||||
},
|
||||
dueDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '截止日期',
|
||||
field: 'due_date'
|
||||
},
|
||||
completedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '完成时间',
|
||||
field: 'completed_at'
|
||||
},
|
||||
remarks: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '创建人ID',
|
||||
field: 'created_by',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'RESTRICT'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID',
|
||||
field: 'updated_by',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.NOW,
|
||||
field: 'created_at'
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.NOW,
|
||||
field: 'updated_at'
|
||||
}
|
||||
}, {
|
||||
comment: '监管任务表',
|
||||
charset: 'utf8mb4',
|
||||
collate: 'utf8mb4_unicode_ci'
|
||||
});
|
||||
|
||||
// 添加索引
|
||||
await queryInterface.addIndex('supervision_tasks', ['application_id']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['policy_id']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['customer_name']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['status']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['task_type']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['assigned_to']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['created_by']);
|
||||
await queryInterface.addIndex('supervision_tasks', ['created_at']);
|
||||
|
||||
// 添加唯一索引
|
||||
await queryInterface.addIndex('supervision_tasks', ['application_id'], {
|
||||
unique: true,
|
||||
name: 'unique_application_id'
|
||||
});
|
||||
await queryInterface.addIndex('supervision_tasks', ['policy_id'], {
|
||||
unique: true,
|
||||
name: 'unique_policy_id'
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable('supervision_tasks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 创建监管任务表的迁移文件
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable('supervisory_tasks', {
|
||||
id: {
|
||||
type: Sequelize.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false,
|
||||
comment: '主键ID'
|
||||
},
|
||||
applicationNumber: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
field: 'application_number',
|
||||
comment: '申请单号'
|
||||
},
|
||||
policyNumber: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'policy_number',
|
||||
comment: '保单编号'
|
||||
},
|
||||
productName: {
|
||||
type: Sequelize.STRING(100),
|
||||
allowNull: false,
|
||||
field: 'product_name',
|
||||
comment: '产品名称'
|
||||
},
|
||||
insurancePeriod: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'insurance_period',
|
||||
comment: '保险周期'
|
||||
},
|
||||
customerName: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'customer_name',
|
||||
comment: '客户姓名'
|
||||
},
|
||||
idType: {
|
||||
type: Sequelize.ENUM('身份证', '护照', '军官证', '士兵证', '港澳台居民居住证', '其他'),
|
||||
allowNull: false,
|
||||
defaultValue: '身份证',
|
||||
field: 'id_type',
|
||||
comment: '证件类型'
|
||||
},
|
||||
idNumber: {
|
||||
type: Sequelize.STRING(30),
|
||||
allowNull: false,
|
||||
field: 'id_number',
|
||||
comment: '证件号码'
|
||||
},
|
||||
applicableSupplies: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
field: 'applicable_supplies',
|
||||
comment: '适用生资(JSON格式存储)'
|
||||
},
|
||||
supervisorySuppliesQuantity: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'supervisory_supplies_quantity',
|
||||
comment: '监管生资数量'
|
||||
},
|
||||
taskStatus: {
|
||||
type: Sequelize.ENUM('待处理', '处理中', '已完成', '已取消'),
|
||||
allowNull: false,
|
||||
defaultValue: '待处理',
|
||||
field: 'task_status',
|
||||
comment: '任务状态'
|
||||
},
|
||||
priority: {
|
||||
type: Sequelize.ENUM('低', '中', '高', '紧急'),
|
||||
allowNull: false,
|
||||
defaultValue: '中',
|
||||
comment: '任务优先级'
|
||||
},
|
||||
assignedTo: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'assigned_to',
|
||||
comment: '分配给(用户ID)',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
dueDate: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true,
|
||||
field: 'due_date',
|
||||
comment: '截止日期'
|
||||
},
|
||||
completedAt: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true,
|
||||
field: 'completed_at',
|
||||
comment: '完成时间'
|
||||
},
|
||||
notes: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
},
|
||||
createdBy: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'created_by',
|
||||
comment: '创建人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
updatedBy: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'updated_by',
|
||||
comment: '更新人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
createdAt: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
field: 'created_at',
|
||||
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
|
||||
},
|
||||
updatedAt: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
field: 'updated_at',
|
||||
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
|
||||
}
|
||||
}, {
|
||||
comment: '监管任务表'
|
||||
});
|
||||
|
||||
// 创建索引
|
||||
await queryInterface.addIndex('supervisory_tasks', ['application_number'], {
|
||||
name: 'idx_application_number'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('supervisory_tasks', ['policy_number'], {
|
||||
name: 'idx_policy_number'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('supervisory_tasks', ['customer_name'], {
|
||||
name: 'idx_customer_name'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('supervisory_tasks', ['task_status'], {
|
||||
name: 'idx_task_status'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('supervisory_tasks', ['created_at'], {
|
||||
name: 'idx_created_at'
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable('supervisory_tasks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* 创建待安装任务表的迁移文件
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable('installation_tasks', {
|
||||
id: {
|
||||
type: Sequelize.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false,
|
||||
comment: '主键ID'
|
||||
},
|
||||
applicationNumber: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'application_number',
|
||||
comment: '申请单号'
|
||||
},
|
||||
policyNumber: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'policy_number',
|
||||
comment: '保单编号'
|
||||
},
|
||||
productName: {
|
||||
type: Sequelize.STRING(100),
|
||||
allowNull: false,
|
||||
field: 'product_name',
|
||||
comment: '产品名称'
|
||||
},
|
||||
customerName: {
|
||||
type: Sequelize.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'customer_name',
|
||||
comment: '客户姓名'
|
||||
},
|
||||
idType: {
|
||||
type: Sequelize.ENUM('身份证', '护照', '军官证', '士兵证', '港澳台居民居住证', '其他'),
|
||||
allowNull: false,
|
||||
defaultValue: '身份证',
|
||||
field: 'id_type',
|
||||
comment: '证件类型'
|
||||
},
|
||||
idNumber: {
|
||||
type: Sequelize.STRING(30),
|
||||
allowNull: false,
|
||||
field: 'id_number',
|
||||
comment: '证件号码'
|
||||
},
|
||||
livestockSupplyType: {
|
||||
type: Sequelize.STRING(100),
|
||||
allowNull: false,
|
||||
field: 'livestock_supply_type',
|
||||
comment: '养殖生资种类'
|
||||
},
|
||||
pendingDevices: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
field: 'pending_devices',
|
||||
comment: '待安装设备(JSON格式存储)'
|
||||
},
|
||||
installationStatus: {
|
||||
type: Sequelize.ENUM('待安装', '安装中', '已安装', '安装失败', '已取消'),
|
||||
allowNull: false,
|
||||
defaultValue: '待安装',
|
||||
field: 'installation_status',
|
||||
comment: '安装状态'
|
||||
},
|
||||
taskGeneratedTime: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
||||
field: 'task_generated_time',
|
||||
comment: '生成安装任务时间'
|
||||
},
|
||||
installationCompletedTime: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true,
|
||||
field: 'installation_completed_time',
|
||||
comment: '安装完成生效时间'
|
||||
},
|
||||
priority: {
|
||||
type: Sequelize.ENUM('低', '中', '高', '紧急'),
|
||||
allowNull: false,
|
||||
defaultValue: '中',
|
||||
comment: '安装优先级'
|
||||
},
|
||||
assignedTechnician: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'assigned_technician',
|
||||
comment: '分配的技术员ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
installationAddress: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
field: 'installation_address',
|
||||
comment: '安装地址'
|
||||
},
|
||||
contactPhone: {
|
||||
type: Sequelize.STRING(20),
|
||||
allowNull: true,
|
||||
field: 'contact_phone',
|
||||
comment: '联系电话'
|
||||
},
|
||||
remarks: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
},
|
||||
createdBy: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'created_by',
|
||||
comment: '创建人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
updatedBy: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'updated_by',
|
||||
comment: '更新人ID',
|
||||
references: {
|
||||
model: 'users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL'
|
||||
},
|
||||
createdAt: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
field: 'created_at',
|
||||
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
|
||||
},
|
||||
updatedAt: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
field: 'updated_at',
|
||||
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
|
||||
}
|
||||
}, {
|
||||
comment: '待安装任务表'
|
||||
});
|
||||
|
||||
// 创建索引
|
||||
await queryInterface.addIndex('installation_tasks', ['application_number'], {
|
||||
name: 'idx_application_number'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('installation_tasks', ['policy_number'], {
|
||||
name: 'idx_policy_number'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('installation_tasks', ['customer_name'], {
|
||||
name: 'idx_customer_name'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('installation_tasks', ['installation_status'], {
|
||||
name: 'idx_installation_status'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('installation_tasks', ['task_generated_time'], {
|
||||
name: 'idx_task_generated_time'
|
||||
});
|
||||
|
||||
await queryInterface.addIndex('installation_tasks', ['installation_completed_time'], {
|
||||
name: 'idx_installation_completed_time'
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable('installation_tasks');
|
||||
}
|
||||
};
|
||||
115
insurance_backend/models/InstallationTask.js
Normal file
115
insurance_backend/models/InstallationTask.js
Normal file
@@ -0,0 +1,115 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
/**
|
||||
* 待安装任务模型
|
||||
* 用于管理保险设备安装任务相关的数据
|
||||
*/
|
||||
const InstallationTask = sequelize.define('InstallationTask', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
comment: '主键ID'
|
||||
},
|
||||
applicationNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'application_number',
|
||||
comment: '申请单号'
|
||||
},
|
||||
policyNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'policy_number',
|
||||
comment: '保单编号'
|
||||
},
|
||||
productName: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
field: 'product_name',
|
||||
comment: '产品名称'
|
||||
},
|
||||
customerName: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'customer_name',
|
||||
comment: '客户姓名'
|
||||
},
|
||||
idType: {
|
||||
type: DataTypes.ENUM('身份证', '护照', '军官证', '士兵证', '港澳台居民居住证', '其他'),
|
||||
allowNull: false,
|
||||
defaultValue: '身份证',
|
||||
field: 'id_type',
|
||||
comment: '证件类型'
|
||||
},
|
||||
idNumber: {
|
||||
type: DataTypes.STRING(30),
|
||||
allowNull: false,
|
||||
field: 'id_number',
|
||||
comment: '证件号码'
|
||||
},
|
||||
livestockSupplyType: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
field: 'livestock_supply_type',
|
||||
comment: '养殖生资种类'
|
||||
},
|
||||
pendingDevices: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'pending_devices',
|
||||
comment: '待安装设备(JSON格式存储)'
|
||||
},
|
||||
installationStatus: {
|
||||
type: DataTypes.ENUM('待安装', '安装中', '已安装', '安装失败', '已取消'),
|
||||
allowNull: false,
|
||||
defaultValue: '待安装',
|
||||
field: 'installation_status',
|
||||
comment: '安装状态'
|
||||
},
|
||||
taskGeneratedTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
field: 'task_generated_time',
|
||||
comment: '生成安装任务时间'
|
||||
},
|
||||
installationCompletedTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
field: 'installation_completed_time',
|
||||
comment: '安装完成生效时间'
|
||||
},
|
||||
priority: {
|
||||
type: DataTypes.ENUM('低', '中', '高', '紧急'),
|
||||
allowNull: false,
|
||||
defaultValue: '中',
|
||||
comment: '任务优先级'
|
||||
},
|
||||
assignedTo: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'assigned_technician',
|
||||
comment: '分配给(用户ID)'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'created_by',
|
||||
comment: '创建人ID'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'updated_by',
|
||||
comment: '更新人ID'
|
||||
}
|
||||
}, {
|
||||
tableName: 'installation_tasks',
|
||||
timestamps: true,
|
||||
underscored: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
|
||||
module.exports = InstallationTask;
|
||||
154
insurance_backend/models/SupervisoryTask.js
Normal file
154
insurance_backend/models/SupervisoryTask.js
Normal file
@@ -0,0 +1,154 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
/**
|
||||
* 监管任务模型
|
||||
* 用于管理保险监管相关的任务数据
|
||||
*/
|
||||
const SupervisoryTask = sequelize.define('SupervisoryTask', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
comment: '主键ID'
|
||||
},
|
||||
applicationNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '申请单号'
|
||||
},
|
||||
policyNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '保单编号'
|
||||
},
|
||||
productName: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '产品名称'
|
||||
},
|
||||
insurancePeriod: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '保险周期'
|
||||
},
|
||||
customerName: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '客户姓名'
|
||||
},
|
||||
idType: {
|
||||
type: DataTypes.ENUM('身份证', '护照', '军官证', '士兵证', '港澳台居民居住证', '其他'),
|
||||
allowNull: false,
|
||||
defaultValue: '身份证',
|
||||
comment: '证件类型'
|
||||
},
|
||||
idNumber: {
|
||||
type: DataTypes.STRING(30),
|
||||
allowNull: false,
|
||||
comment: '证件号码'
|
||||
},
|
||||
applicableSupplies: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '适用生资(JSON格式存储)'
|
||||
},
|
||||
supervisorySuppliesQuantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
comment: '监管生资数量'
|
||||
},
|
||||
taskStatus: {
|
||||
type: DataTypes.ENUM('待处理', '处理中', '已完成', '已取消'),
|
||||
allowNull: false,
|
||||
defaultValue: '待处理',
|
||||
comment: '任务状态'
|
||||
},
|
||||
priority: {
|
||||
type: DataTypes.ENUM('低', '中', '高', '紧急'),
|
||||
allowNull: false,
|
||||
defaultValue: '中',
|
||||
comment: '任务优先级'
|
||||
},
|
||||
assignedTo: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '分配给(用户ID)'
|
||||
},
|
||||
dueDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '截止日期'
|
||||
},
|
||||
completedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '完成时间'
|
||||
},
|
||||
notes: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注信息'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '创建人ID'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID'
|
||||
}
|
||||
}, {
|
||||
tableName: 'supervisory_tasks',
|
||||
timestamps: true,
|
||||
underscored: true,
|
||||
indexes: [
|
||||
{
|
||||
name: 'idx_application_number',
|
||||
fields: ['applicationNumber']
|
||||
},
|
||||
{
|
||||
name: 'idx_policy_number',
|
||||
fields: ['policyNumber']
|
||||
},
|
||||
{
|
||||
name: 'idx_customer_name',
|
||||
fields: ['customerName']
|
||||
},
|
||||
{
|
||||
name: 'idx_task_status',
|
||||
fields: ['taskStatus']
|
||||
},
|
||||
{
|
||||
name: 'idx_created_at',
|
||||
fields: ['createdAt']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 定义关联关系
|
||||
SupervisoryTask.associate = (models) => {
|
||||
// 监管任务属于用户(分配给)
|
||||
SupervisoryTask.belongsTo(models.User, {
|
||||
foreignKey: 'assignedTo',
|
||||
as: 'assignedUser'
|
||||
});
|
||||
|
||||
// 监管任务属于创建者
|
||||
SupervisoryTask.belongsTo(models.User, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'creator'
|
||||
});
|
||||
|
||||
// 监管任务属于更新者
|
||||
SupervisoryTask.belongsTo(models.User, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updater'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = SupervisoryTask;
|
||||
@@ -1,5 +1,5 @@
|
||||
// 导入数据库配置和所有模型
|
||||
const { sequelize } = require('../config/database');
|
||||
const { sequelize } = require('../config/database');
|
||||
const User = require('./User');
|
||||
const Role = require('./Role');
|
||||
const InsuranceApplication = require('./InsuranceApplication');
|
||||
@@ -7,6 +7,8 @@ const InsuranceType = require('./InsuranceType');
|
||||
const Policy = require('./Policy');
|
||||
const Claim = require('./Claim');
|
||||
const Menu = require('./Menu');
|
||||
const SupervisoryTask = require('./SupervisoryTask');
|
||||
const InstallationTask = require('./InstallationTask');
|
||||
|
||||
// 定义模型关联关系
|
||||
|
||||
@@ -54,6 +56,34 @@ Policy.hasMany(Claim, {
|
||||
as: 'claims'
|
||||
});
|
||||
|
||||
// 监管任务关联
|
||||
SupervisoryTask.belongsTo(User, {
|
||||
foreignKey: 'assignedTo',
|
||||
as: 'assignedUser'
|
||||
});
|
||||
SupervisoryTask.belongsTo(User, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'creator'
|
||||
});
|
||||
SupervisoryTask.belongsTo(User, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updater'
|
||||
});
|
||||
|
||||
// 待安装任务关联
|
||||
InstallationTask.belongsTo(User, {
|
||||
foreignKey: 'assignedTo',
|
||||
as: 'technician'
|
||||
});
|
||||
InstallationTask.belongsTo(User, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'creator'
|
||||
});
|
||||
InstallationTask.belongsTo(User, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updater'
|
||||
});
|
||||
|
||||
// 导出所有模型
|
||||
module.exports = {
|
||||
sequelize,
|
||||
@@ -63,5 +93,7 @@ module.exports = {
|
||||
InsuranceType,
|
||||
Policy,
|
||||
Claim,
|
||||
Menu
|
||||
Menu,
|
||||
SupervisoryTask,
|
||||
InstallationTask
|
||||
};
|
||||
3144
insurance_backend/package-lock.json
generated
3144
insurance_backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
"author": "Insurance Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.12.2",
|
||||
"bcrypt": "^5.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.3",
|
||||
|
||||
259
insurance_backend/routes/installationTasks.js
Normal file
259
insurance_backend/routes/installationTasks.js
Normal file
@@ -0,0 +1,259 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const installationTaskController = require('../controllers/installationTaskController');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: InstallationTasks
|
||||
* description: 待安装任务管理
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks:
|
||||
* get:
|
||||
* summary: 获取待安装任务列表
|
||||
* tags: [InstallationTasks]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: 页码
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 10
|
||||
* description: 每页数量
|
||||
* - in: query
|
||||
* name: policyNumber
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 保单编号搜索
|
||||
* - in: query
|
||||
* name: customerName
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 客户姓名搜索
|
||||
* - in: query
|
||||
* name: keyword
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 关键字搜索
|
||||
* - in: query
|
||||
* name: installationStatus
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [待安装, 安装中, 已安装, 安装失败, 已取消]
|
||||
* description: 安装状态筛选
|
||||
* - in: query
|
||||
* name: priority
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [低, 中, 高, 紧急]
|
||||
* description: 优先级筛选
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/', installationTaskController.getInstallationTasks);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks:
|
||||
* post:
|
||||
* summary: 创建待安装任务
|
||||
* tags: [InstallationTasks]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - applicationNumber
|
||||
* - policyNumber
|
||||
* - productName
|
||||
* - customerName
|
||||
* properties:
|
||||
* applicationNumber:
|
||||
* type: string
|
||||
* description: 申请单号
|
||||
* policyNumber:
|
||||
* type: string
|
||||
* description: 保单编号
|
||||
* productName:
|
||||
* type: string
|
||||
* description: 产品名称
|
||||
* customerName:
|
||||
* type: string
|
||||
* description: 客户姓名
|
||||
* idType:
|
||||
* type: string
|
||||
* enum: [身份证, 护照, 军官证, 士兵证, 港澳台居民居住证, 其他]
|
||||
* description: 证件类型
|
||||
* idNumber:
|
||||
* type: string
|
||||
* description: 证件号码
|
||||
* livestockSupplyType:
|
||||
* type: string
|
||||
* description: 养殖生资种类
|
||||
* pendingDevices:
|
||||
* type: array
|
||||
* description: 待安装设备
|
||||
* priority:
|
||||
* type: string
|
||||
* enum: [低, 中, 高, 紧急]
|
||||
* description: 优先级
|
||||
* assignedTo:
|
||||
* type: integer
|
||||
* description: 分配给用户ID
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 创建成功
|
||||
*/
|
||||
router.post('/', installationTaskController.createInstallationTask);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/{id}:
|
||||
* get:
|
||||
* summary: 获取待安装任务详情
|
||||
* tags: [InstallationTasks]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 任务ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/:id', installationTaskController.getInstallationTaskById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/{id}:
|
||||
* put:
|
||||
* summary: 更新待安装任务
|
||||
* tags: [InstallationTasks]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 任务ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* installationStatus:
|
||||
* type: string
|
||||
* enum: [待安装, 安装中, 已安装, 安装失败, 已取消]
|
||||
* priority:
|
||||
* type: string
|
||||
* enum: [低, 中, 高, 紧急]
|
||||
* assignedTo:
|
||||
* type: integer
|
||||
* installationCompletedAt:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 更新成功
|
||||
*/
|
||||
router.put('/:id', installationTaskController.updateInstallationTask);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/{id}:
|
||||
* delete:
|
||||
* summary: 删除待安装任务
|
||||
* tags: [InstallationTasks]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 任务ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 删除成功
|
||||
*/
|
||||
router.delete('/:id', installationTaskController.deleteInstallationTask);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/batch/operate:
|
||||
* post:
|
||||
* summary: 批量操作待安装任务
|
||||
* tags: [InstallationTasks]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - ids
|
||||
* - operation
|
||||
* properties:
|
||||
* ids:
|
||||
* type: array
|
||||
* items:
|
||||
* type: integer
|
||||
* description: 任务ID数组
|
||||
* operation:
|
||||
* type: string
|
||||
* enum: [assign, updateStatus, delete]
|
||||
* description: 操作类型
|
||||
* data:
|
||||
* type: object
|
||||
* description: 操作数据
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 操作成功
|
||||
*/
|
||||
router.post('/batch/operate', installationTaskController.batchOperateInstallationTasks);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/export:
|
||||
* get:
|
||||
* summary: 导出待安装任务数据
|
||||
* tags: [InstallationTasks]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: ids
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 任务ID列表(逗号分隔)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 导出成功
|
||||
*/
|
||||
router.get('/export', installationTaskController.exportInstallationTasks);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/installation-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取安装任务统计数据
|
||||
* tags: [InstallationTasks]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', installationTaskController.getInstallationTaskStats);
|
||||
|
||||
module.exports = router;
|
||||
266
insurance_backend/routes/supervisoryTasks.js
Normal file
266
insurance_backend/routes/supervisoryTasks.js
Normal file
@@ -0,0 +1,266 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const SupervisoryTaskController = require('../controllers/supervisoryTaskController');
|
||||
const auth = require('../middleware/auth');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: SupervisionTasks
|
||||
* description: 监管任务管理
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks:
|
||||
* get:
|
||||
* summary: 获取监管任务列表
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: 页码
|
||||
* - in: query
|
||||
* name: pageSize
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 10
|
||||
* description: 每页数量
|
||||
* - in: query
|
||||
* name: status
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [pending, processing, completed, rejected]
|
||||
* description: 状态筛选
|
||||
* - in: query
|
||||
* name: taskType
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [new_application, task_guidance, batch_operation]
|
||||
* description: 任务类型筛选
|
||||
* - in: query
|
||||
* name: applicationId
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 申请单号搜索
|
||||
* - in: query
|
||||
* name: policyId
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 保单编号搜索
|
||||
* - in: query
|
||||
* name: customerName
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 客户姓名搜索
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/', SupervisoryTaskController.getList);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks:
|
||||
* post:
|
||||
* summary: 创建监管任务
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - applicationId
|
||||
* - policyId
|
||||
* - productName
|
||||
* - customerName
|
||||
* - taskType
|
||||
* properties:
|
||||
* applicationId:
|
||||
* type: string
|
||||
* description: 申请单号
|
||||
* policyId:
|
||||
* type: string
|
||||
* description: 保单编号
|
||||
* productName:
|
||||
* type: string
|
||||
* description: 产品名称
|
||||
* insurancePeriod:
|
||||
* type: string
|
||||
* description: 保险期间
|
||||
* customerName:
|
||||
* type: string
|
||||
* description: 客户姓名
|
||||
* documentType:
|
||||
* type: string
|
||||
* description: 证件类型
|
||||
* documentNumber:
|
||||
* type: string
|
||||
* description: 证件号码
|
||||
* applicableAmount:
|
||||
* type: number
|
||||
* description: 适用金额
|
||||
* taskType:
|
||||
* type: string
|
||||
* enum: [new_application, task_guidance, batch_operation]
|
||||
* description: 任务类型
|
||||
* assignedTo:
|
||||
* type: integer
|
||||
* description: 分配给用户ID
|
||||
* priority:
|
||||
* type: string
|
||||
* enum: [low, medium, high, urgent]
|
||||
* description: 优先级
|
||||
* dueDate:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 截止日期
|
||||
* remarks:
|
||||
* type: string
|
||||
* description: 备注
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 创建成功
|
||||
*/
|
||||
router.post('/', SupervisoryTaskController.create);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/{id}:
|
||||
* get:
|
||||
* summary: 获取监管任务详情
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 监管任务ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/:id', SupervisoryTaskController.getById);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/{id}:
|
||||
* put:
|
||||
* summary: 更新监管任务
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 监管任务ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* status:
|
||||
* type: string
|
||||
* enum: [pending, processing, completed, rejected]
|
||||
* assignedTo:
|
||||
* type: integer
|
||||
* priority:
|
||||
* type: string
|
||||
* enum: [low, medium, high, urgent]
|
||||
* remarks:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 更新成功
|
||||
*/
|
||||
router.put('/:id', SupervisoryTaskController.update);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/{id}:
|
||||
* delete:
|
||||
* summary: 删除监管任务
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 监管任务ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 删除成功
|
||||
*/
|
||||
router.delete('/:id', SupervisoryTaskController.delete);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/batch/operate:
|
||||
* post:
|
||||
* summary: 批量操作监管任务
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - ids
|
||||
* - operation
|
||||
* properties:
|
||||
* ids:
|
||||
* type: array
|
||||
* items:
|
||||
* type: integer
|
||||
* description: 监管任务ID数组
|
||||
* operation:
|
||||
* type: string
|
||||
* enum: [assign, updateStatus, delete]
|
||||
* description: 操作类型
|
||||
* data:
|
||||
* type: object
|
||||
* description: 操作数据
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 操作成功
|
||||
*/
|
||||
router.post('/batch/operate', SupervisoryTaskController.bulkCreate);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/supervision-tasks/stats:
|
||||
* get:
|
||||
* summary: 获取监管任务统计数据
|
||||
* tags: [SupervisionTasks]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 获取成功
|
||||
*/
|
||||
router.get('/stats', SupervisoryTaskController.getStatistics);
|
||||
|
||||
module.exports = router;
|
||||
75
insurance_backend/run-migration.js
Normal file
75
insurance_backend/run-migration.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const { sequelize } = require('./config/database.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
async function runMigration() {
|
||||
try {
|
||||
console.log('开始运行数据库迁移...');
|
||||
|
||||
// 测试数据库连接
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功');
|
||||
|
||||
// 获取所有迁移文件
|
||||
const migrationsPath = path.join(__dirname, 'migrations');
|
||||
const migrationFiles = fs.readdirSync(migrationsPath)
|
||||
.filter(file => file.endsWith('.js'))
|
||||
.sort();
|
||||
|
||||
console.log(`找到 ${migrationFiles.length} 个迁移文件`);
|
||||
|
||||
// 确保 SequelizeMeta 表存在
|
||||
await sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS \`SequelizeMeta\` (
|
||||
\`name\` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (\`name\`),
|
||||
UNIQUE KEY \`name\` (\`name\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
`);
|
||||
|
||||
// 检查哪些迁移已经运行过
|
||||
const [executedMigrations] = await sequelize.query(
|
||||
'SELECT name FROM SequelizeMeta ORDER BY name'
|
||||
);
|
||||
const executedNames = executedMigrations.map(row => row.name);
|
||||
|
||||
// 运行未执行的迁移
|
||||
for (const file of migrationFiles) {
|
||||
if (!executedNames.includes(file)) {
|
||||
console.log(`正在运行迁移: ${file}`);
|
||||
|
||||
try {
|
||||
const migration = require(path.join(migrationsPath, file));
|
||||
await migration.up(sequelize.getQueryInterface(), sequelize.constructor);
|
||||
|
||||
// 记录迁移已执行
|
||||
await sequelize.query(
|
||||
'INSERT INTO SequelizeMeta (name) VALUES (?)',
|
||||
{ replacements: [file] }
|
||||
);
|
||||
|
||||
console.log(`✅ 迁移 ${file} 执行成功`);
|
||||
} catch (error) {
|
||||
console.error(`❌ 迁移 ${file} 执行失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
console.log(`⏭️ 迁移 ${file} 已执行,跳过`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🎉 所有迁移执行完成!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 迁移执行失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 运行迁移
|
||||
runMigration().catch(error => {
|
||||
console.error('迁移过程中发生错误:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
require('dotenv').config({ path: require('path').join(__dirname, '../.env') });
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const helmet = require('helmet');
|
||||
@@ -5,7 +6,6 @@ const rateLimit = require('express-rate-limit');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const swaggerSpec = require('../config/swagger');
|
||||
const { sequelize, testConnection } = require('../config/database');
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3002;
|
||||
@@ -51,6 +51,9 @@ app.use('/api/claims', require('../routes/claims'));
|
||||
app.use('/api/system', require('../routes/system'));
|
||||
app.use('/api/menus', require('../routes/menus'));
|
||||
app.use('/api/data-warehouse', require('../routes/dataWarehouse'));
|
||||
app.use('/api/supervisory-tasks', require('../routes/supervisoryTasks'));
|
||||
app.use('/api/supervision-tasks', require('../routes/supervisoryTasks'));
|
||||
app.use('/api/installation-tasks', require('../routes/installationTasks'));
|
||||
|
||||
// API文档路由
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
||||
@@ -87,8 +90,7 @@ const startServer = async () => {
|
||||
// 测试数据库连接
|
||||
const dbConnected = await testConnection();
|
||||
if (!dbConnected) {
|
||||
console.error('❌ 数据库连接失败,服务器启动中止');
|
||||
process.exit(1);
|
||||
console.warn('⚠️ 数据库连接失败,但服务器仍将继续启动(开发环境)');
|
||||
}
|
||||
|
||||
// Redis连接已移除
|
||||
|
||||
95
insurance_backend/test-auth.js
Normal file
95
insurance_backend/test-auth.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const { sequelize } = require('./config/database');
|
||||
const jwt = require('jsonwebtoken');
|
||||
require('dotenv').config();
|
||||
|
||||
// 测试数据库连接
|
||||
async function testDatabaseConnection() {
|
||||
try {
|
||||
console.log('\n=== 测试数据库连接 ===');
|
||||
console.log('使用配置:');
|
||||
console.log(`- 主机: ${process.env.DB_HOST || '默认值'}`);
|
||||
console.log(`- 端口: ${process.env.DB_PORT || '默认值'}`);
|
||||
console.log(`- 数据库: ${process.env.DB_DATABASE || process.env.DB_NAME || '默认值'}`);
|
||||
console.log(`- 用户名: ${process.env.DB_USER || '默认值'}`);
|
||||
console.log(`- 密码: ${process.env.DB_PASSWORD ? '已设置 (不显示)' : '未设置'}`);
|
||||
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功!');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库连接失败:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试JWT配置
|
||||
function testJWTConfig() {
|
||||
try {
|
||||
console.log('\n=== 测试JWT配置 ===');
|
||||
console.log(`- JWT_SECRET: ${process.env.JWT_SECRET ? '已设置 (长度: ' + process.env.JWT_SECRET.length + ')' : '未设置'}`);
|
||||
console.log(`- JWT_EXPIRE: ${process.env.JWT_EXPIRE || '默认值'}`);
|
||||
|
||||
if (!process.env.JWT_SECRET) {
|
||||
console.error('❌ JWT_SECRET未设置!');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试生成并验证令牌
|
||||
const testPayload = { test: 'data' };
|
||||
const token = jwt.sign(testPayload, process.env.JWT_SECRET, { expiresIn: '1h' });
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
console.log('✅ JWT配置有效!');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ JWT配置错误:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试模型导入
|
||||
async function testModels() {
|
||||
try {
|
||||
console.log('\n=== 测试模型导入 ===');
|
||||
const { User, Role } = require('./models');
|
||||
console.log('✅ 用户模型导入成功');
|
||||
console.log('✅ 角色模型导入成功');
|
||||
|
||||
// 尝试查询用户表结构
|
||||
const userAttributes = User.rawAttributes;
|
||||
console.log(`✅ 用户表有 ${Object.keys(userAttributes).length} 个字段`);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ 模型导入错误:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 运行所有测试
|
||||
async function runTests() {
|
||||
console.log('\n开始测试认证相关配置...');
|
||||
|
||||
const dbResult = await testDatabaseConnection();
|
||||
const jwtResult = testJWTConfig();
|
||||
const modelsResult = await testModels();
|
||||
|
||||
console.log('\n=== 测试总结 ===');
|
||||
console.log(`数据库连接: ${dbResult ? '通过' : '失败'}`);
|
||||
console.log(`JWT配置: ${jwtResult ? '通过' : '失败'}`);
|
||||
console.log(`模型导入: ${modelsResult ? '通过' : '失败'}`);
|
||||
|
||||
if (dbResult && jwtResult && modelsResult) {
|
||||
console.log('✅ 所有测试通过!');
|
||||
} else {
|
||||
console.error('❌ 测试失败,请检查上述错误!');
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
await sequelize.close();
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runTests().catch(error => {
|
||||
console.error('测试过程中出现未捕获错误:', error);
|
||||
});
|
||||
42
insurance_backend/test-db-connection.js
Normal file
42
insurance_backend/test-db-connection.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const { sequelize, testConnection } = require('./config/database.js');
|
||||
|
||||
// 测试数据库连接
|
||||
async function runTest() {
|
||||
console.log('=== 数据库连接测试开始 ===');
|
||||
console.log('环境变量检查:');
|
||||
console.log(`- DB_HOST: ${process.env.DB_HOST}`);
|
||||
console.log(`- DB_PORT: ${process.env.DB_PORT}`);
|
||||
console.log(`- DB_DATABASE: ${process.env.DB_DATABASE}`);
|
||||
console.log(`- DB_USER: ${process.env.DB_USER}`);
|
||||
console.log(`- DB_PASSWORD: ${process.env.DB_PASSWORD ? '已设置' : '未设置'}`);
|
||||
|
||||
console.log('\n连接参数检查:');
|
||||
console.log(`- 实际使用的主机: ${sequelize.config.host}`);
|
||||
console.log(`- 实际使用的端口: ${sequelize.config.port}`);
|
||||
console.log(`- 实际使用的数据库: ${sequelize.config.database}`);
|
||||
console.log(`- 实际使用的用户名: ${sequelize.config.username}`);
|
||||
console.log(`- 实际使用的密码: ${sequelize.config.password ? '已设置' : '未设置'}`);
|
||||
|
||||
console.log('\n正在尝试连接数据库...');
|
||||
const success = await testConnection();
|
||||
|
||||
if (success) {
|
||||
console.log('✅ 测试成功!数据库连接已建立。');
|
||||
console.log('\n请尝试重新启动应用服务器。');
|
||||
} else {
|
||||
console.log('❌ 测试失败。请检查数据库配置和服务状态。');
|
||||
console.log('\n可能的解决方案:');
|
||||
console.log('1. 确认MySQL服务正在运行');
|
||||
console.log('2. 确认用户名和密码正确');
|
||||
console.log('3. 确认数据库已创建');
|
||||
console.log('4. 确认用户有足够的权限访问该数据库');
|
||||
}
|
||||
|
||||
console.log('=== 数据库连接测试结束 ===');
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
runTest().catch(error => {
|
||||
console.error('测试执行过程中发生错误:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
71
insurance_backend/test-server.js
Normal file
71
insurance_backend/test-server.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const http = require('http');
|
||||
|
||||
// 测试服务器健康检查接口
|
||||
function testHealthCheck() {
|
||||
return new Promise((resolve) => {
|
||||
http.get('http://localhost:3000/health', (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
if (res.statusCode === 200) {
|
||||
console.log('✅ 健康检查接口测试成功!');
|
||||
console.log('响应状态码:', res.statusCode);
|
||||
console.log('响应数据:', JSON.parse(data));
|
||||
resolve(true);
|
||||
} else {
|
||||
console.error('❌ 健康检查接口测试失败:', `状态码: ${res.statusCode}`);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
console.error('❌ 健康检查接口测试失败:', error.message);
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 测试API文档接口
|
||||
function testApiDocs() {
|
||||
return new Promise((resolve) => {
|
||||
http.get('http://localhost:3000/api-docs', (res) => {
|
||||
if (res.statusCode === 200 || res.statusCode === 301 || res.statusCode === 302) {
|
||||
console.log('✅ API文档接口测试成功!');
|
||||
console.log('响应状态码:', res.statusCode);
|
||||
resolve(true);
|
||||
} else {
|
||||
console.error('❌ API文档接口测试失败:', `状态码: ${res.statusCode}`);
|
||||
resolve(false);
|
||||
}
|
||||
}).on('error', (error) => {
|
||||
console.error('❌ API文档接口测试失败:', error.message);
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 主测试函数
|
||||
async function runTests() {
|
||||
console.log('开始测试保险后端服务...\n');
|
||||
|
||||
const healthCheckResult = await testHealthCheck();
|
||||
console.log('');
|
||||
const apiDocsResult = await testApiDocs();
|
||||
|
||||
console.log('\n测试总结:');
|
||||
if (healthCheckResult && apiDocsResult) {
|
||||
console.log('✅ 所有测试通过! 服务器已成功启动并可访问基础接口。');
|
||||
console.log('注意: 数据库连接仍存在问题,但不影响基础接口的访问。');
|
||||
console.log('请在浏览器中访问以下地址:');
|
||||
console.log(' - 健康检查: http://localhost:3000/health');
|
||||
console.log(' - API文档: http://localhost:3000/api-docs');
|
||||
} else {
|
||||
console.log('❌ 部分测试失败,请检查服务器配置。');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
runTests();
|
||||
44
insurance_backend/test-supervision-api.js
Normal file
44
insurance_backend/test-supervision-api.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const axios = require('axios');
|
||||
|
||||
async function testSupervisionTaskAPI() {
|
||||
const baseURL = 'http://localhost:3000/api/supervision-tasks';
|
||||
|
||||
try {
|
||||
// 1. 测试获取列表
|
||||
console.log('=== 测试获取监管任务列表 ===');
|
||||
const getResponse = await axios.get(baseURL);
|
||||
console.log('GET请求成功:', getResponse.data);
|
||||
|
||||
// 2. 测试创建任务
|
||||
console.log('\n=== 测试创建监管任务 ===');
|
||||
const taskData = {
|
||||
applicationNumber: "APP2025001",
|
||||
policyNumber: "POL2025001",
|
||||
productName: "农业保险产品",
|
||||
insurancePeriod: "2025-01-01至2025-12-31",
|
||||
customerName: "张三",
|
||||
idType: "身份证",
|
||||
idNumber: "110101199001011234",
|
||||
supervisorySuppliesQuantity: 100,
|
||||
taskStatus: "待处理",
|
||||
priority: "中",
|
||||
notes: "测试监管任务"
|
||||
};
|
||||
|
||||
const createResponse = await axios.post(baseURL, taskData);
|
||||
console.log('POST请求成功:', createResponse.data);
|
||||
|
||||
// 3. 测试获取详情
|
||||
if (createResponse.data.data && createResponse.data.data.id) {
|
||||
console.log('\n=== 测试获取任务详情 ===');
|
||||
const taskId = createResponse.data.data.id;
|
||||
const detailResponse = await axios.get(`${baseURL}/${taskId}`);
|
||||
console.log('GET详情请求成功:', detailResponse.data);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('API测试失败:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
}
|
||||
|
||||
testSupervisionTaskAPI();
|
||||
27
insurance_backend/test_api.js
Normal file
27
insurance_backend/test_api.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// 简单的API测试脚本
|
||||
const axios = require('axios');
|
||||
|
||||
async function testPublicApi() {
|
||||
try {
|
||||
console.log('测试公开菜单API...');
|
||||
const response = await axios.get('http://localhost:3000/api/menus/public');
|
||||
console.log('✅ 公开菜单API测试成功');
|
||||
console.log('返回数据:', response.data);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ 公开菜单API测试失败:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function testAllApis() {
|
||||
console.log('开始API测试...');
|
||||
const publicApiResult = await testPublicApi();
|
||||
|
||||
console.log('\n测试总结:');
|
||||
console.log(`公开菜单API: ${publicApiResult ? '通过' : '失败'}`);
|
||||
}
|
||||
|
||||
testAllApis().then(() => {
|
||||
console.log('\nAPI测试完成');
|
||||
});
|
||||
78
insurance_backend/test_db_connection_final.js
Normal file
78
insurance_backend/test_db_connection_final.js
Normal file
@@ -0,0 +1,78 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
|
||||
// 确保正确加载.env文件
|
||||
require('dotenv').config();
|
||||
|
||||
console.log('环境变量加载情况:');
|
||||
console.log(`- DB_HOST: ${process.env.DB_HOST}`);
|
||||
console.log(`- DB_PORT: ${process.env.DB_PORT}`);
|
||||
console.log(`- DB_DATABASE: ${process.env.DB_DATABASE}`);
|
||||
console.log(`- DB_USER: ${process.env.DB_USER}`);
|
||||
console.log(`- NODE_ENV: ${process.env.NODE_ENV}`);
|
||||
|
||||
// 直接使用环境变量创建连接,不使用默认值
|
||||
const sequelize = new Sequelize({
|
||||
dialect: 'mysql',
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
database: process.env.DB_DATABASE,
|
||||
username: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
logging: false
|
||||
});
|
||||
|
||||
// 测试数据库连接
|
||||
const testConnection = async () => {
|
||||
try {
|
||||
console.log('\n正在测试数据库连接...');
|
||||
console.log('使用的连接配置:');
|
||||
console.log(`- 主机: ${sequelize.config.host}`);
|
||||
console.log(`- 端口: ${sequelize.config.port}`);
|
||||
console.log(`- 数据库: ${sequelize.config.database}`);
|
||||
console.log(`- 用户名: ${sequelize.config.username}`);
|
||||
|
||||
// 测试连接
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功!');
|
||||
|
||||
// 测试查询
|
||||
try {
|
||||
const [results, metadata] = await sequelize.query('SELECT 1 AS test');
|
||||
console.log('✅ 数据库查询测试成功,结果:', results);
|
||||
|
||||
// 尝试查询数据库中的表
|
||||
const [tables, tableMeta] = await sequelize.query(
|
||||
"SHOW TABLES LIKE 'users'"
|
||||
);
|
||||
|
||||
if (tables.length > 0) {
|
||||
console.log('✅ 数据库中存在users表');
|
||||
|
||||
// 尝试查询用户表数据
|
||||
const [users, userMeta] = await sequelize.query('SELECT COUNT(*) AS user_count FROM users');
|
||||
console.log('✅ 用户表查询成功,用户数量:', users[0].user_count);
|
||||
} else {
|
||||
console.warn('⚠️ 数据库中不存在users表,请先运行数据库初始化脚本');
|
||||
}
|
||||
} catch (queryError) {
|
||||
console.error('⚠️ 数据库查询测试失败:', queryError.message);
|
||||
console.log('\n建议:');
|
||||
console.log('1. 确认数据库已创建并包含所需的表');
|
||||
console.log('2. 运行项目根目录下的数据库初始化脚本');
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库连接失败:', error.message);
|
||||
console.log('\n可能的解决方案:');
|
||||
console.log('1. 确认MySQL服务正在运行');
|
||||
console.log('2. 确认.env文件中的用户名和密码正确');
|
||||
console.log('3. 确认.env文件中的数据库名称正确且已创建');
|
||||
console.log('4. 确认用户有足够的权限访问数据库');
|
||||
console.log('5. 检查网络连接是否正常');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
testConnection();
|
||||
52
insurance_backend/test_db_connection_fixed.js
Normal file
52
insurance_backend/test_db_connection_fixed.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const { sequelize } = require('./config/database');
|
||||
|
||||
// 测试数据库连接
|
||||
const testConnection = async () => {
|
||||
try {
|
||||
console.log('正在测试数据库连接...');
|
||||
console.log('连接配置:');
|
||||
console.log(`- 主机: ${sequelize.config.host}`);
|
||||
console.log(`- 端口: ${sequelize.config.port}`);
|
||||
console.log(`- 数据库: ${sequelize.config.database}`);
|
||||
console.log(`- 用户名: ${sequelize.config.username}`);
|
||||
|
||||
// 测试连接
|
||||
await sequelize.authenticate();
|
||||
console.log('✅ 数据库连接成功!');
|
||||
|
||||
// 测试查询
|
||||
try {
|
||||
const [results, metadata] = await sequelize.query('SELECT 1 AS test');
|
||||
console.log('✅ 数据库查询测试成功,结果:', results);
|
||||
|
||||
// 尝试查询用户表
|
||||
try {
|
||||
const [users, userMeta] = await sequelize.query('SELECT COUNT(*) AS user_count FROM users');
|
||||
console.log('✅ 用户表查询成功,用户数量:', users[0].user_count);
|
||||
|
||||
// 尝试查询角色表
|
||||
const [roles, roleMeta] = await sequelize.query('SELECT COUNT(*) AS role_count FROM roles');
|
||||
console.log('✅ 角色表查询成功,角色数量:', roles[0].role_count);
|
||||
|
||||
} catch (tableError) {
|
||||
console.error('⚠️ 表查询失败,可能是表不存在:', tableError.message);
|
||||
}
|
||||
} catch (queryError) {
|
||||
console.error('⚠️ 数据库查询测试失败:', queryError.message);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ 数据库连接失败:', error.message);
|
||||
console.log('\n可能的解决方案:');
|
||||
console.log('1. 确认MySQL服务正在运行');
|
||||
console.log('2. 确认用户名和密码正确');
|
||||
console.log('3. 确认数据库存在');
|
||||
console.log('4. 确认用户有足够的权限访问数据库');
|
||||
console.log('5. 检查网络连接是否正常');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
testConnection();
|
||||
50
insurance_backend/utils/logger.js
Normal file
50
insurance_backend/utils/logger.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const winston = require('winston');
|
||||
const path = require('path');
|
||||
|
||||
// 创建日志配置
|
||||
const logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss'
|
||||
}),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.printf(({ level, message, timestamp, stack }) => {
|
||||
if (stack) {
|
||||
return `${timestamp} [${level.toUpperCase()}]: ${message}\n${stack}`;
|
||||
}
|
||||
return `${timestamp} [${level.toUpperCase()}]: ${message}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
// 控制台输出
|
||||
new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
)
|
||||
}),
|
||||
// 错误日志文件
|
||||
new winston.transports.File({
|
||||
filename: path.join(__dirname, '../logs/error.log'),
|
||||
level: 'error',
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5
|
||||
}),
|
||||
// 所有日志文件
|
||||
new winston.transports.File({
|
||||
filename: path.join(__dirname, '../logs/combined.log'),
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// 开发环境额外配置
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
logger.add(new winston.transports.Console({
|
||||
format: winston.format.simple()
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = logger;
|
||||
Reference in New Issue
Block a user