完善保险端前后端和养殖端小程序

This commit is contained in:
xuqiuyun
2025-09-22 19:09:45 +08:00
parent 02a25515a9
commit 325c114c38
256 changed files with 48348 additions and 4444 deletions

View 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')
};

View 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"
}
}

View File

@@ -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,

View 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();

View 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;

View 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
};

View File

@@ -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');
}
};

View File

@@ -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');
}
};

View File

@@ -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');
}
};

View 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;

View 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;

View File

@@ -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
};

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View 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;

View 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;

View 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);
});

View File

@@ -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连接已移除

View 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);
});

View 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);
});

View 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();

View 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();

View 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测试完成');
});

View 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();

View 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();

View 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;