继续完善保险项目和养殖端小程序

This commit is contained in:
xuqiuyun
2025-09-29 18:35:03 +08:00
parent 4af8368097
commit 4e8d4dc92d
192 changed files with 4886 additions and 35384 deletions

View File

@@ -0,0 +1,142 @@
const puppeteer = require('puppeteer');
async function checkInsuranceNavigation() {
console.log('🔍 检查险种管理页面导航...');
const browser = await puppeteer.launch({
headless: false,
slowMo: 1000
});
const page = await browser.newPage();
try {
// 访问前端页面并登录
await page.goto('http://localhost:3002', { waitUntil: 'networkidle2' });
// 登录
await page.type('input[type="text"], input[placeholder*="用户名"], input[placeholder*="账号"]', 'admin');
await page.type('input[type="password"], input[placeholder*="密码"]', '123456');
const loginSelectors = ['button[type="submit"]', '.login-btn', 'button'];
for (const selector of loginSelectors) {
try {
const button = await page.$(selector);
if (button) {
await button.click();
break;
}
} catch (e) {
// 继续尝试下一个选择器
}
}
// 等待登录完成
await new Promise(resolve => setTimeout(resolve, 3000));
console.log('✅ 登录完成当前URL:', page.url());
// 方法1直接访问险种管理页面URL
console.log('🔍 方法1直接访问险种管理页面URL...');
await page.goto('http://localhost:3002/#/insurance-types', { waitUntil: 'networkidle2' });
await new Promise(resolve => setTimeout(resolve, 3000));
let currentUrl = page.url();
console.log('直接访问后的URL:', currentUrl);
if (currentUrl.includes('insurance-types')) {
console.log('✅ 直接访问险种管理页面成功');
// 检查页面内容
const pageContent = await page.content();
const hasInsuranceContent = pageContent.includes('险种管理') ||
pageContent.includes('保险类型') ||
pageContent.includes('新增险种');
console.log('页面包含险种管理内容:', hasInsuranceContent);
// 查找表格元素
const tableElements = await page.evaluate(() => {
const selectors = [
'table',
'.ant-table',
'.ant-table-tbody',
'.el-table',
'.data-table',
'[class*="table"]',
'tbody tr',
'.ant-table-row'
];
const found = [];
selectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
found.push({
selector,
count: elements.length,
visible: elements[0] ? !elements[0].hidden && elements[0].offsetParent !== null : false
});
}
});
return found;
});
console.log('找到的表格元素:', JSON.stringify(tableElements, null, 2));
// 检查是否有数据加载
const hasData = await page.evaluate(() => {
const rows = document.querySelectorAll('.ant-table-tbody tr, tbody tr, .table-row');
return rows.length > 0;
});
console.log('表格是否有数据:', hasData);
// 检查加载状态
const isLoading = await page.evaluate(() => {
const loadingElements = document.querySelectorAll('.ant-spin, .loading, [class*="loading"]');
return loadingElements.length > 0;
});
console.log('页面是否在加载中:', isLoading);
// 等待一段时间看数据是否会加载
console.log('等待数据加载...');
await new Promise(resolve => setTimeout(resolve, 5000));
const hasDataAfterWait = await page.evaluate(() => {
const rows = document.querySelectorAll('.ant-table-tbody tr, tbody tr, .table-row');
return rows.length > 0;
});
console.log('等待后表格是否有数据:', hasDataAfterWait);
// 检查网络请求
const networkLogs = [];
page.on('response', response => {
if (response.url().includes('insurance-types') || response.url().includes('api')) {
networkLogs.push({
url: response.url(),
status: response.status(),
statusText: response.statusText()
});
}
});
// 刷新页面看网络请求
await page.reload({ waitUntil: 'networkidle2' });
await new Promise(resolve => setTimeout(resolve, 3000));
console.log('网络请求日志:', JSON.stringify(networkLogs, null, 2));
} else {
console.log('❌ 直接访问险种管理页面失败,仍在:', currentUrl);
}
} catch (error) {
console.error('检查导航失败:', error.message);
} finally {
await browser.close();
}
}
checkInsuranceNavigation().catch(console.error);

View File

@@ -0,0 +1,116 @@
const puppeteer = require('puppeteer');
async function checkLoginDOM() {
console.log('🔍 检查登录后的DOM结构...');
const browser = await puppeteer.launch({
headless: false,
slowMo: 500
});
const page = await browser.newPage();
try {
// 访问前端页面
await page.goto('http://localhost:3002', { waitUntil: 'networkidle2' });
// 填写登录信息
await page.type('input[type="text"], input[placeholder*="用户名"], input[placeholder*="账号"]', 'admin');
await page.type('input[type="password"], input[placeholder*="密码"]', '123456');
// 点击登录按钮
const loginSelectors = ['button[type="submit"]', '.login-btn', 'button'];
let loginClicked = false;
for (const selector of loginSelectors) {
try {
const button = await page.$(selector);
if (button) {
await button.click();
loginClicked = true;
break;
}
} catch (e) {
// 继续尝试下一个选择器
}
}
if (!loginClicked) {
await page.keyboard.press('Enter');
}
// 等待页面加载
await new Promise(resolve => setTimeout(resolve, 5000));
// 获取当前URL
const currentUrl = page.url();
console.log('当前URL:', currentUrl);
// 检查页面标题
const title = await page.title();
console.log('页面标题:', title);
// 查找可能的用户信息元素
const userElements = await page.evaluate(() => {
const selectors = [
'.user-info',
'.header-user',
'.logout-btn',
'.user-name',
'.username',
'.user-avatar',
'.header-right',
'.navbar-user',
'.el-dropdown',
'[class*="user"]',
'[class*="logout"]',
'[class*="header"]'
];
const found = [];
selectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
if (elements.length > 0) {
found.push({
selector,
count: elements.length,
text: Array.from(elements).map(el => el.textContent?.trim()).filter(t => t)
});
}
});
return found;
});
console.log('找到的用户相关元素:', JSON.stringify(userElements, null, 2));
// 获取页面的主要结构
const pageStructure = await page.evaluate(() => {
const body = document.body;
const mainElements = [];
// 查找主要的容器元素
const containers = body.querySelectorAll('div[class], nav[class], header[class], main[class]');
containers.forEach(el => {
if (el.className) {
mainElements.push({
tag: el.tagName.toLowerCase(),
className: el.className,
text: el.textContent?.substring(0, 100) + '...'
});
}
});
return mainElements.slice(0, 20); // 只返回前20个
});
console.log('页面主要结构:', JSON.stringify(pageStructure, null, 2));
} catch (error) {
console.error('检查DOM失败:', error.message);
} finally {
await browser.close();
}
}
checkLoginDOM().catch(console.error);

View File

@@ -0,0 +1,136 @@
const puppeteer = require('puppeteer');
async function checkTokenStatus() {
console.log('🔍 检查Token状态...');
const browser = await puppeteer.launch({
headless: false,
slowMo: 500
});
const page = await browser.newPage();
try {
// 访问前端页面并登录
await page.goto('http://localhost:3002', { waitUntil: 'networkidle2' });
// 登录
await page.type('input[type="text"], input[placeholder*="用户名"], input[placeholder*="账号"]', 'admin');
await page.type('input[type="password"], input[placeholder*="密码"]', '123456');
const loginSelectors = ['button[type="submit"]', '.login-btn', 'button'];
for (const selector of loginSelectors) {
try {
const button = await page.$(selector);
if (button) {
await button.click();
break;
}
} catch (e) {
// 继续尝试下一个选择器
}
}
// 等待登录完成
await new Promise(resolve => setTimeout(resolve, 3000));
console.log('✅ 登录完成当前URL:', page.url());
// 检查localStorage中的Token信息
const tokenInfo = await page.evaluate(() => {
return {
accessToken: localStorage.getItem('accessToken'),
refreshToken: localStorage.getItem('refreshToken'),
tokenExpiresAt: localStorage.getItem('tokenExpiresAt'),
userInfo: localStorage.getItem('userInfo'),
currentTime: Date.now()
};
});
console.log('Token信息:');
console.log('- accessToken:', tokenInfo.accessToken ? `${tokenInfo.accessToken.substring(0, 20)}...` : 'null');
console.log('- refreshToken:', tokenInfo.refreshToken ? `${tokenInfo.refreshToken.substring(0, 20)}...` : 'null');
console.log('- tokenExpiresAt:', tokenInfo.tokenExpiresAt);
console.log('- userInfo:', tokenInfo.userInfo);
console.log('- currentTime:', tokenInfo.currentTime);
if (tokenInfo.tokenExpiresAt) {
const expiresAt = parseInt(tokenInfo.tokenExpiresAt);
const isExpired = tokenInfo.currentTime >= expiresAt;
const timeUntilExpiry = expiresAt - tokenInfo.currentTime;
console.log('- Token是否过期:', isExpired);
console.log('- 距离过期时间:', Math.round(timeUntilExpiry / 1000), '秒');
}
// 测试API调用
console.log('🔍 测试API调用...');
const apiResponse = await page.evaluate(async () => {
try {
const token = localStorage.getItem('accessToken');
const response = await fetch('http://localhost:3000/api/insurance-types', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return {
status: response.status,
statusText: response.statusText,
ok: response.ok,
data: response.ok ? await response.json() : await response.text()
};
} catch (error) {
return {
error: error.message
};
}
});
console.log('API响应:', JSON.stringify(apiResponse, null, 2));
// 现在尝试访问险种管理页面
console.log('🔍 尝试访问险种管理页面...');
await page.goto('http://localhost:3002/#/insurance-types', { waitUntil: 'networkidle2' });
await new Promise(resolve => setTimeout(resolve, 3000));
const finalUrl = page.url();
console.log('最终URL:', finalUrl);
if (finalUrl.includes('insurance-types')) {
console.log('✅ 成功访问险种管理页面');
// 检查页面是否有表格
const hasTable = await page.evaluate(() => {
const tables = document.querySelectorAll('table, .ant-table, .ant-table-tbody');
return tables.length > 0;
});
console.log('页面是否有表格:', hasTable);
} else if (finalUrl.includes('login')) {
console.log('❌ 被重定向到登录页面');
// 检查控制台错误
const consoleLogs = [];
page.on('console', msg => {
if (msg.type() === 'error') {
consoleLogs.push(msg.text());
}
});
console.log('控制台错误:', consoleLogs);
} else {
console.log('❌ 访问了其他页面:', finalUrl);
}
} catch (error) {
console.error('检查Token状态失败:', error.message);
} finally {
await browser.close();
}
}
checkTokenStatus().catch(console.error);

View File

@@ -1,33 +0,0 @@
{
"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

@@ -1,49 +0,0 @@
const { Sequelize } = require('sequelize');
require('dotenv').config();
// 创建Sequelize实例
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_DATABASE || 'insurance_data',
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'aiotAiot123!',
logging: process.env.NODE_ENV === 'development' ? console.log : false,
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
define: {
timestamps: true,
underscored: true,
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
},
dialectOptions: {
// 解决MySQL严格模式问题
dateStrings: true,
typeCast: true,
// 允许0000-00-00日期值
connectAttributes: {
sql_mode: 'TRADITIONAL'
}
},
timezone: '+08:00' // 设置时区为东八区
});
// 测试数据库连接
const testConnection = async () => {
try {
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
return true;
} catch (error) {
console.error('❌ 数据库连接失败:', error.message);
return false;
}
};
module.exports = { sequelize, testConnection };

View File

@@ -1,42 +0,0 @@
const redis = require('redis');
require('dotenv').config();
// 创建Redis客户端
const createRedisClient = () => {
const client = redis.createClient({
socket: {
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379
},
password: process.env.REDIS_PASSWORD || '',
legacyMode: false
});
// 错误处理
client.on('error', (err) => {
console.error('❌ Redis连接错误:', err);
});
// 连接成功
client.on('connect', () => {
console.log('✅ Redis连接成功');
});
return client;
};
// 创建并连接Redis客户端
const redisClient = createRedisClient();
// 连接Redis
const connectRedis = async () => {
try {
await redisClient.connect();
return true;
} catch (error) {
console.error('❌ Redis连接失败:', error.message);
return false;
}
};
module.exports = { redisClient, connectRedis };

View File

@@ -1,244 +0,0 @@
const { Claim, Policy, User, InsuranceApplication, InsuranceType } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取理赔列表
const getClaims = async (req, res) => {
try {
const {
claim_no,
customer_name,
claim_status,
dateRange,
page = 1,
limit = 10
} = req.query;
const whereClause = {};
// 理赔编号筛选
if (claim_no) {
whereClause.claim_no = { [Op.like]: `%${claim_no}%` };
}
// 理赔状态筛选
if (claim_status) {
whereClause.claim_status = claim_status;
}
// 日期范围筛选
if (dateRange && dateRange.start && dateRange.end) {
whereClause.claim_date = {
[Op.between]: [new Date(dateRange.start), new Date(dateRange.end)]
};
}
const offset = (page - 1) * limit;
const { count, rows } = await Claim.findAndCountAll({
where: whereClause,
include: [
{
model: Policy,
as: 'policy',
attributes: ['id', 'policy_no', 'coverage_amount'],
include: [
{
model: InsuranceApplication,
as: 'application',
attributes: ['id', 'customer_name'],
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name']
}
]
}
]
},
{
model: User,
as: 'customer',
attributes: ['id', 'real_name', 'username']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
],
order: [['created_at', 'DESC']],
offset,
limit: parseInt(limit)
});
res.json(responseFormat.pagination(rows, {
page: parseInt(page),
limit: parseInt(limit),
total: count
}, '获取理赔列表成功'));
} catch (error) {
console.error('获取理赔列表错误:', error);
res.status(500).json(responseFormat.error('获取理赔列表失败'));
}
};
// 创建理赔申请
const createClaim = async (req, res) => {
try {
const claimData = req.body;
// 生成理赔编号
const claimNo = `CLM${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
const claim = await Claim.create({
...claimData,
claim_no: claimNo
});
res.status(201).json(responseFormat.created(claim, '理赔申请创建成功'));
} catch (error) {
console.error('创建理赔申请错误:', error);
res.status(500).json(responseFormat.error('创建理赔申请失败'));
}
};
// 获取单个理赔详情
const getClaimById = async (req, res) => {
try {
const { id } = req.params;
const claim = await Claim.findByPk(id, {
include: [
{
model: Policy,
as: 'policy',
include: [
{
model: InsuranceApplication,
as: 'application',
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description']
}
]
}
]
},
{
model: User,
as: 'customer',
attributes: ['id', 'real_name', 'username', 'phone', 'email']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
]
});
if (!claim) {
return res.status(404).json(responseFormat.error('理赔申请不存在'));
}
res.json(responseFormat.success(claim, '获取理赔详情成功'));
} catch (error) {
console.error('获取理赔详情错误:', error);
res.status(500).json(responseFormat.error('获取理赔详情失败'));
}
};
// 审核理赔申请
const reviewClaim = async (req, res) => {
try {
const { id } = req.params;
const { claim_status, review_notes } = req.body;
const reviewerId = req.user.id;
const claim = await Claim.findByPk(id);
if (!claim) {
return res.status(404).json(responseFormat.error('理赔申请不存在'));
}
if (!['approved', 'rejected', 'processing', 'paid'].includes(claim_status)) {
return res.status(400).json(responseFormat.error('无效的理赔状态'));
}
await claim.update({
claim_status,
review_notes,
reviewer_id: reviewerId,
review_date: new Date()
});
res.json(responseFormat.success(claim, '理赔申请审核成功'));
} catch (error) {
console.error('审核理赔申请错误:', error);
res.status(500).json(responseFormat.error('审核理赔申请失败'));
}
};
// 更新理赔支付状态
const updateClaimPayment = async (req, res) => {
try {
const { id } = req.params;
const claim = await Claim.findByPk(id);
if (!claim) {
return res.status(404).json(responseFormat.error('理赔申请不存在'));
}
if (claim.claim_status !== 'approved') {
return res.status(400).json(responseFormat.error('只有已批准的理赔才能进行支付'));
}
await claim.update({
claim_status: 'paid',
payment_date: new Date()
});
res.json(responseFormat.success(claim, '理赔支付状态更新成功'));
} catch (error) {
console.error('更新理赔支付状态错误:', error);
res.status(500).json(responseFormat.error('更新理赔支付状态失败'));
}
};
// 获取理赔统计
const getClaimStats = async (req, res) => {
try {
const stats = await Claim.findAll({
attributes: [
'claim_status',
[Claim.sequelize.fn('COUNT', Claim.sequelize.col('id')), 'count'],
[Claim.sequelize.fn('SUM', Claim.sequelize.col('claim_amount')), 'total_amount']
],
group: ['claim_status']
});
const total = await Claim.count();
const totalAmount = await Claim.sum('claim_amount');
res.json(responseFormat.success({
stats,
total,
total_amount: totalAmount || 0
}, '获取理赔统计成功'));
} catch (error) {
console.error('获取理赔统计错误:', error);
res.status(500).json(responseFormat.error('获取理赔统计失败'));
}
};
module.exports = {
getClaims,
createClaim,
getClaimById,
reviewClaim,
updateClaimPayment,
getClaimStats
};

View File

@@ -1,435 +0,0 @@
const { Device, DeviceAlert, User } = require('../models');
const { Op } = require('sequelize');
const logger = require('../utils/logger');
/**
* 设备控制器
*/
class DeviceController {
/**
* 获取设备列表
*/
static async getDeviceList(req, res) {
try {
const {
page = 1,
limit = 20,
device_type,
status,
farm_id,
pen_id,
keyword
} = req.query;
// 构建查询条件
const whereCondition = {};
if (device_type) {
whereCondition.device_type = device_type;
}
if (status) {
whereCondition.status = status;
}
if (farm_id) {
whereCondition.farm_id = farm_id;
}
if (pen_id) {
whereCondition.pen_id = pen_id;
}
if (keyword) {
whereCondition[Op.or] = [
{ device_code: { [Op.like]: `%${keyword}%` } },
{ device_name: { [Op.like]: `%${keyword}%` } },
{ device_model: { [Op.like]: `%${keyword}%` } },
{ manufacturer: { [Op.like]: `%${keyword}%` } }
];
}
const offset = (page - 1) * limit;
const { count, rows } = await Device.findAndCountAll({
where: whereCondition,
include: [
{
model: User,
as: 'creator',
attributes: ['id', 'username', 'real_name'],
required: false
}
],
order: [['created_at', 'DESC']],
limit: parseInt(limit),
offset: offset
});
res.json({
success: true,
data: {
devices: rows,
pagination: {
current_page: parseInt(page),
per_page: parseInt(limit),
total: count,
total_pages: Math.ceil(count / limit)
}
}
});
} catch (error) {
logger.error('获取设备列表失败:', error);
res.status(500).json({
success: false,
message: '获取设备列表失败',
error: error.message
});
}
}
/**
* 获取设备详情
*/
static async getDeviceDetail(req, res) {
try {
const { id } = req.params;
const device = await Device.findByPk(id, {
include: [
{
model: User,
as: 'creator',
attributes: ['id', 'username', 'real_name'],
required: false
},
{
model: User,
as: 'updater',
attributes: ['id', 'username', 'real_name'],
required: false
}
]
});
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在'
});
}
// 获取设备相关的预警统计
const alertStats = await DeviceAlert.findAll({
attributes: [
'alert_level',
[DeviceAlert.sequelize.fn('COUNT', DeviceAlert.sequelize.col('id')), 'count']
],
where: {
device_id: id
},
group: ['alert_level'],
raw: true
});
// 获取最近的预警记录
const recentAlerts = await DeviceAlert.findAll({
where: {
device_id: id
},
order: [['alert_time', 'DESC']],
limit: 5,
attributes: ['id', 'alert_type', 'alert_level', 'alert_title', 'alert_time', 'status']
});
res.json({
success: true,
data: {
device,
alert_stats: alertStats,
recent_alerts: recentAlerts
}
});
} catch (error) {
logger.error('获取设备详情失败:', error);
res.status(500).json({
success: false,
message: '获取设备详情失败',
error: error.message
});
}
}
/**
* 创建设备
*/
static async createDevice(req, res) {
try {
const {
device_code,
device_name,
device_type,
device_model,
manufacturer,
installation_location,
installation_date,
farm_id,
pen_id,
status = 'normal'
} = req.body;
const userId = req.user.id;
// 检查设备编号是否已存在
const existingDevice = await Device.findOne({
where: { device_code }
});
if (existingDevice) {
return res.status(400).json({
success: false,
message: '设备编号已存在'
});
}
const device = await Device.create({
device_code,
device_name,
device_type,
device_model,
manufacturer,
installation_location,
installation_date,
farm_id,
pen_id,
status,
created_by: userId,
updated_by: userId
});
res.json({
success: true,
message: '设备创建成功',
data: device
});
} catch (error) {
logger.error('创建设备失败:', error);
res.status(500).json({
success: false,
message: '创建设备失败',
error: error.message
});
}
}
/**
* 更新设备
*/
static async updateDevice(req, res) {
try {
const { id } = req.params;
const {
device_code,
device_name,
device_type,
device_model,
manufacturer,
installation_location,
installation_date,
farm_id,
pen_id,
status
} = req.body;
const userId = req.user.id;
const device = await Device.findByPk(id);
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在'
});
}
// 如果修改了设备编号,检查是否与其他设备重复
if (device_code && device_code !== device.device_code) {
const existingDevice = await Device.findOne({
where: {
device_code,
id: { [Op.ne]: id }
}
});
if (existingDevice) {
return res.status(400).json({
success: false,
message: '设备编号已存在'
});
}
}
await device.update({
device_code,
device_name,
device_type,
device_model,
manufacturer,
installation_location,
installation_date,
farm_id,
pen_id,
status,
updated_by: userId
});
res.json({
success: true,
message: '设备更新成功',
data: device
});
} catch (error) {
logger.error('更新设备失败:', error);
res.status(500).json({
success: false,
message: '更新设备失败',
error: error.message
});
}
}
/**
* 删除设备
*/
static async deleteDevice(req, res) {
try {
const { id } = req.params;
const device = await Device.findByPk(id);
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在'
});
}
// 检查是否有关联的预警记录
const alertCount = await DeviceAlert.count({
where: { device_id: id }
});
if (alertCount > 0) {
return res.status(400).json({
success: false,
message: '该设备存在预警记录,无法删除'
});
}
await device.destroy();
res.json({
success: true,
message: '设备删除成功'
});
} catch (error) {
logger.error('删除设备失败:', error);
res.status(500).json({
success: false,
message: '删除设备失败',
error: error.message
});
}
}
/**
* 获取设备类型列表
*/
static async getDeviceTypes(req, res) {
try {
const deviceTypes = await Device.findAll({
attributes: [
[Device.sequelize.fn('DISTINCT', Device.sequelize.col('device_type')), 'device_type']
],
where: {
device_type: {
[Op.ne]: null
}
},
raw: true
});
res.json({
success: true,
data: deviceTypes.map(item => item.device_type)
});
} catch (error) {
logger.error('获取设备类型失败:', error);
res.status(500).json({
success: false,
message: '获取设备类型失败',
error: error.message
});
}
}
/**
* 获取设备状态统计
*/
static async getDeviceStats(req, res) {
try {
const { farm_id } = req.query;
const whereCondition = {};
if (farm_id) {
whereCondition.farm_id = farm_id;
}
// 按状态统计
const devicesByStatus = await Device.findAll({
attributes: [
'status',
[Device.sequelize.fn('COUNT', Device.sequelize.col('id')), 'count']
],
where: whereCondition,
group: ['status'],
raw: true
});
// 按类型统计
const devicesByType = await Device.findAll({
attributes: [
'device_type',
[Device.sequelize.fn('COUNT', Device.sequelize.col('id')), 'count']
],
where: whereCondition,
group: ['device_type'],
raw: true
});
// 总设备数
const totalDevices = await Device.count({
where: whereCondition
});
res.json({
success: true,
data: {
total_devices: totalDevices,
devices_by_status: devicesByStatus,
devices_by_type: devicesByType
}
});
} catch (error) {
logger.error('获取设备统计失败:', error);
res.status(500).json({
success: false,
message: '获取设备统计失败',
error: error.message
});
}
}
}
module.exports = DeviceController;

View File

@@ -1,477 +0,0 @@
const InstallationTask = require('../models/InstallationTask');
const User = require('../models/User');
const { Op, sequelize } = require('sequelize');
const logger = require('../utils/logger');
class InstallationTaskController {
// 获取待安装任务列表
async getInstallationTasks(req, res) {
try {
const {
page = 1,
pageSize = 10,
policyNumber,
customerName,
installationStatus,
priority,
keyword,
startDate,
endDate
} = req.query;
const offset = (page - 1) * pageSize;
const limit = parseInt(pageSize);
// 构建查询条件
const whereConditions = {};
if (policyNumber) {
whereConditions.policyNumber = { [Op.like]: `%${policyNumber}%` };
}
if (customerName) {
whereConditions.customerName = { [Op.like]: `%${customerName}%` };
}
if (installationStatus) {
whereConditions.installationStatus = installationStatus;
}
if (priority) {
whereConditions.priority = priority;
}
// 关键字搜索(搜索申请单号、保单编号、客户姓名等)
if (keyword) {
whereConditions[Op.or] = [
{ applicationNumber: { [Op.like]: `%${keyword}%` } },
{ policyNumber: { [Op.like]: `%${keyword}%` } },
{ customerName: { [Op.like]: `%${customerName}%` } },
{ productName: { [Op.like]: `%${keyword}%` } }
];
}
if (startDate && endDate) {
whereConditions.taskGeneratedTime = {
[Op.between]: [new Date(startDate), new Date(endDate)]
};
}
const { count, rows } = await InstallationTask.findAndCountAll({
where: whereConditions,
order: [['taskGeneratedTime', 'DESC']],
offset,
limit
});
res.json({
code: 200,
status: 'success',
message: '获取待安装任务列表成功',
data: {
list: rows,
total: count,
page: parseInt(page),
pageSize: limit,
totalPages: Math.ceil(count / limit)
}
});
} catch (error) {
logger.error('获取待安装任务列表失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '获取待安装任务列表失败',
error: error.message
});
}
}
// 创建待安装任务
async createInstallationTask(req, res) {
try {
const {
applicationNumber,
policyNumber,
productName,
customerName,
idType,
idNumber,
livestockSupplyType,
pendingDevices,
installationStatus = '待安装',
priority = '中',
assignedTo,
taskGeneratedTime = new Date()
} = req.body;
// 验证必填字段
if (!applicationNumber || !policyNumber || !productName || !customerName) {
return res.status(400).json({
code: 400,
status: 'error',
message: '申请单号、保单编号、产品名称、客户姓名为必填项'
});
}
// 检查申请单号是否已存在
const existingTask = await InstallationTask.findOne({
where: { applicationNumber }
});
if (existingTask) {
return res.status(409).json({
code: 409,
status: 'error',
message: '该申请单号已存在待安装任务'
});
}
const installationTask = await InstallationTask.create({
applicationNumber,
policyNumber,
productName,
customerName,
idType: idType || '身份证',
idNumber: idNumber || '',
livestockSupplyType,
pendingDevices: pendingDevices ? JSON.stringify(pendingDevices) : null,
installationStatus,
priority,
assignedTo,
taskGeneratedTime,
createdBy: req.user?.id
});
res.status(201).json({
code: 201,
status: 'success',
message: '创建待安装任务成功',
data: installationTask
});
} catch (error) {
logger.error('创建待安装任务失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '创建待安装任务失败',
error: error.message
});
}
}
// 获取待安装任务详情
async getInstallationTaskById(req, res) {
try {
const { id } = req.params;
const installationTask = await InstallationTask.findByPk(id, {
include: [
{
model: User,
as: 'technician',
attributes: ['id', 'username', 'real_name']
},
{
model: User,
as: 'creator',
attributes: ['id', 'username', 'real_name']
},
{
model: User,
as: 'updater',
attributes: ['id', 'username', 'real_name']
}
]
});
if (!installationTask) {
return res.status(404).json({
code: 404,
status: 'error',
message: '待安装任务不存在'
});
}
res.json({
code: 200,
status: 'success',
message: '获取待安装任务详情成功',
data: installationTask
});
} catch (error) {
logger.error('获取待安装任务详情失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '获取待安装任务详情失败',
error: error.message
});
}
}
// 更新待安装任务
async updateInstallationTask(req, res) {
try {
const { id } = req.params;
const updateData = { ...req.body };
// 添加更新人信息
updateData.updatedBy = req.user?.id;
// 处理设备数据
if (updateData.pendingDevices) {
updateData.pendingDevices = JSON.stringify(updateData.pendingDevices);
}
// 处理安装完成时间
if (updateData.installationStatus === '已安装' && !updateData.installationCompletedTime) {
updateData.installationCompletedTime = new Date();
}
const [updatedCount] = await InstallationTask.update(updateData, {
where: { id }
});
if (updatedCount === 0) {
return res.status(404).json({
code: 404,
status: 'error',
message: '待安装任务不存在或未做任何修改'
});
}
// 获取更新后的任务
const updatedTask = await InstallationTask.findByPk(id);
res.json({
code: 200,
status: 'success',
message: '更新待安装任务成功',
data: updatedTask
});
} catch (error) {
logger.error('更新待安装任务失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '更新待安装任务失败',
error: error.message
});
}
}
// 删除待安装任务
async deleteInstallationTask(req, res) {
try {
const { id } = req.params;
const deletedCount = await InstallationTask.destroy({
where: { id }
});
if (deletedCount === 0) {
return res.status(404).json({
code: 404,
status: 'error',
message: '待安装任务不存在'
});
}
res.json({
code: 200,
status: 'success',
message: '删除待安装任务成功'
});
} catch (error) {
logger.error('删除待安装任务失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '删除待安装任务失败',
error: error.message
});
}
}
// 批量操作待安装任务
async batchOperateInstallationTasks(req, res) {
try {
const { ids, operation, data } = req.body;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
return res.status(400).json({
code: 400,
status: 'error',
message: '任务ID列表不能为空'
});
}
let result;
switch (operation) {
case 'assign':
if (!data.assignedTo) {
return res.status(400).json({
code: 400,
status: 'error',
message: '分配操作需要指定分配给的用户'
});
}
result = await InstallationTask.update(
{ assignedTo: data.assignedTo, updatedBy: req.user?.id },
{ where: { id: ids } }
);
break;
case 'updateStatus':
if (!data.installationStatus) {
return res.status(400).json({
code: 400,
status: 'error',
message: '状态更新操作需要指定新状态'
});
}
result = await InstallationTask.update(
{ installationStatus: data.installationStatus, updatedBy: req.user?.id },
{ where: { id: ids } }
);
break;
case 'delete':
result = await InstallationTask.destroy({ where: { id: ids } });
break;
default:
return res.status(400).json({
code: 400,
status: 'error',
message: '不支持的操作类型'
});
}
res.json({
code: 200,
status: 'success',
message: `批量${operation}操作完成`,
data: { affectedRows: result[0] || result }
});
} catch (error) {
logger.error('批量操作待安装任务失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '批量操作失败',
error: error.message
});
}
}
// 导出待安装任务数据
async exportInstallationTasks(req, res) {
try {
const { ids } = req.query;
let where = {};
if (ids) {
where.id = { [Op.in]: ids.split(',') };
}
const tasks = await InstallationTask.findAll({
where,
include: [
{
model: User,
as: 'technician',
attributes: ['username', 'real_name']
},
{
model: User,
as: 'creator',
attributes: ['username', 'real_name']
}
],
order: [['taskGeneratedTime', 'DESC']]
});
res.json({
code: 200,
status: 'success',
message: '导出待安装任务数据成功',
data: tasks
});
} catch (error) {
logger.error('导出待安装任务数据失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '导出数据失败',
error: error.message
});
}
}
// 获取任务统计信息
async getInstallationTaskStats(req, res) {
try {
// 按状态统计
const statusStats = await InstallationTask.findAll({
attributes: [
'installationStatus',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['installationStatus'],
raw: true
});
// 按优先级统计
const priorityStats = await InstallationTask.findAll({
attributes: [
'priority',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['priority'],
raw: true
});
// 总数统计
const total = await InstallationTask.count();
// 本月新增任务
const thisMonth = await InstallationTask.count({
where: {
taskGeneratedTime: {
[Op.gte]: new Date(new Date().getFullYear(), new Date().getMonth(), 1)
}
}
});
res.json({
code: 200,
status: 'success',
message: '获取任务统计信息成功',
data: {
total,
thisMonth,
statusStats,
priorityStats
}
});
} catch (error) {
logger.error('获取任务统计信息失败:', error);
res.status(500).json({
code: 500,
status: 'error',
message: '获取统计信息失败',
error: error.message
});
}
}
}
module.exports = new InstallationTaskController();

View File

@@ -1,400 +0,0 @@
const { InsuranceApplication, InsuranceType, User } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取保险申请列表
const getApplications = async (req, res) => {
try {
console.log('获取保险申请列表 - 请求参数:', req.query);
const {
applicantName,
status,
insuranceType,
insuranceCategory,
applicationNumber,
dateRange,
page = 1,
limit = 10
} = req.query;
const whereClause = {};
const includeClause = [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description'],
where: {}
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
];
// 申请单号筛选
if (applicationNumber) {
whereClause.application_no = { [Op.like]: `%${applicationNumber}%` };
}
// 投保人姓名筛选
if (applicantName) {
whereClause.customer_name = { [Op.like]: `%${applicantName}%` };
}
// 状态筛选
if (status) {
whereClause.status = status;
}
// 参保险种筛选
if (insuranceType) {
includeClause[0].where.name = { [Op.like]: `%${insuranceType}%` };
}
// 参保类型筛选
if (insuranceCategory) {
whereClause.insurance_category = { [Op.like]: `%${insuranceCategory}%` };
}
// 日期范围筛选
if (dateRange && dateRange.start && dateRange.end) {
whereClause.application_date = {
[Op.between]: [new Date(dateRange.start), new Date(dateRange.end)]
};
}
const offset = (page - 1) * limit;
// 如果没有保险类型筛选条件清空where条件
if (!insuranceType) {
delete includeClause[0].where;
}
console.log('查询条件:', { whereClause, includeClause, offset, limit: parseInt(limit) });
const { count, rows } = await InsuranceApplication.findAndCountAll({
where: whereClause,
include: includeClause,
order: [['created_at', 'DESC']],
offset,
limit: parseInt(limit)
});
console.log('查询结果:', { count, rowsLength: rows.length });
res.json(responseFormat.pagination(rows, {
page: parseInt(page),
limit: parseInt(limit),
total: count
}, '获取保险申请列表成功'));
} catch (error) {
console.error('获取保险申请列表错误:', error);
res.status(500).json(responseFormat.error('获取保险申请列表失败'));
}
};
// 创建保险申请
const createApplication = async (req, res) => {
try {
const {
customer_name,
customer_id_card,
customer_phone,
customer_address,
insurance_type_id,
insurance_category,
application_quantity,
application_amount,
remarks
} = req.body;
// 验证必填字段
if (!customer_name || !customer_id_card || !customer_phone || !customer_address || !insurance_type_id) {
return res.status(400).json(responseFormat.error('请填写所有必填字段'));
}
// 生成申请编号
const applicationNo = `${new Date().getFullYear()}${(new Date().getMonth() + 1).toString().padStart(2, '0')}${new Date().getDate().toString().padStart(2, '0')}${Date.now().toString().slice(-6)}`;
const application = await InsuranceApplication.create({
application_no: applicationNo,
customer_name,
customer_id_card,
customer_phone,
customer_address,
insurance_type_id,
insurance_category,
application_quantity: application_quantity || 1,
application_amount: application_amount || 0,
remarks,
status: 'pending'
});
// 返回创建的申请信息,包含关联数据
const createdApplication = await InsuranceApplication.findByPk(application.id, {
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description']
}
]
});
res.status(201).json(responseFormat.created(createdApplication, '保险申请创建成功'));
} catch (error) {
console.error('创建保险申请错误:', error);
if (error.name === 'SequelizeValidationError') {
const errorMessages = error.errors.map(err => err.message).join(', ');
return res.status(400).json(responseFormat.error(`数据验证失败: ${errorMessages}`));
}
res.status(500).json(responseFormat.error('创建保险申请失败'));
}
};
// 获取单个保险申请详情
const getApplicationById = async (req, res) => {
try {
const { id } = req.params;
const application = await InsuranceApplication.findByPk(id, {
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description']
},
{
model: User,
as: 'reviewer',
attributes: ['id', 'real_name', 'username']
}
]
});
if (!application) {
return res.status(404).json(responseFormat.error('保险申请不存在'));
}
res.json(responseFormat.success(application, '获取保险申请详情成功'));
} catch (error) {
console.error('获取保险申请详情错误:', error);
res.status(500).json(responseFormat.error('获取保险申请详情失败'));
}
};
// 更新保险申请
const updateApplication = async (req, res) => {
try {
const { id } = req.params;
const updateData = req.body;
const application = await InsuranceApplication.findByPk(id);
if (!application) {
return res.status(404).json(responseFormat.error('保险申请不存在'));
}
await application.update(updateData);
res.json(responseFormat.success(application, '保险申请更新成功'));
} catch (error) {
console.error('更新保险申请错误:', error);
res.status(500).json(responseFormat.error('更新保险申请失败'));
}
};
// 审核保险申请
const reviewApplication = async (req, res) => {
try {
const { id } = req.params;
const { status, review_notes } = req.body;
const reviewerId = req.user.id;
const application = await InsuranceApplication.findByPk(id);
if (!application) {
return res.status(404).json(responseFormat.error('保险申请不存在'));
}
if (!['approved', 'rejected', 'under_review'].includes(status)) {
return res.status(400).json(responseFormat.error('无效的审核状态'));
}
await application.update({
status,
review_notes,
reviewer_id: reviewerId,
review_date: new Date()
});
res.json(responseFormat.success(application, '保险申请审核成功'));
} catch (error) {
console.error('审核保险申请错误:', error);
res.status(500).json(responseFormat.error('审核保险申请失败'));
}
};
// 删除保险申请
const deleteApplication = async (req, res) => {
try {
const { id } = req.params;
const application = await InsuranceApplication.findByPk(id);
if (!application) {
return res.status(404).json(responseFormat.error('保险申请不存在'));
}
await application.destroy();
res.json(responseFormat.success(null, '保险申请删除成功'));
} catch (error) {
console.error('删除保险申请错误:', error);
res.status(500).json(responseFormat.error('删除保险申请失败'));
}
};
// 获取保险申请统计
const getApplicationStats = async (req, res) => {
try {
const stats = await InsuranceApplication.findAll({
attributes: [
'status',
[InsuranceApplication.sequelize.fn('COUNT', InsuranceApplication.sequelize.col('id')), 'count']
],
group: ['status']
});
const total = await InsuranceApplication.count();
res.json(responseFormat.success({
stats,
total
}, '获取保险申请统计成功'));
} catch (error) {
console.error('获取保险申请统计错误:', error);
res.status(500).json(responseFormat.error('获取保险申请统计失败'));
}
};
// 获取参保类型选项
const getInsuranceCategories = async (req, res) => {
try {
const categories = await InsuranceApplication.findAll({
attributes: ['insurance_category'],
where: {
insurance_category: {
[Op.ne]: null
}
},
group: ['insurance_category'],
raw: true
});
const categoryList = categories.map(item => item.insurance_category).filter(Boolean);
// 添加一些常用的参保类型
const defaultCategories = ['牛', '羊', '猪', '鸡', '鸭', '鹅'];
const allCategories = [...new Set([...defaultCategories, ...categoryList])];
res.json(responseFormat.success(allCategories, '获取参保类型选项成功'));
} catch (error) {
console.error('获取参保类型选项错误:', error);
res.status(500).json(responseFormat.error('获取参保类型选项失败'));
}
};
// 导出保险申请数据
const exportApplications = async (req, res) => {
try {
const {
page = 1,
limit = 1000,
applicantName,
insuranceType,
insuranceCategory,
status
} = req.query;
const where = {};
if (applicantName) {
where.applicant_name = { [Op.like]: `%${applicantName}%` };
}
if (insuranceType) {
where.insurance_type = insuranceType;
}
if (insuranceCategory) {
where.insurance_category = insuranceCategory;
}
if (status) {
where.status = status;
}
const applications = await InsuranceApplication.findAll({
where,
include: [
{
model: InsuranceType,
as: 'insuranceTypeInfo',
attributes: ['name', 'description']
},
{
model: User,
as: 'createdByUser',
attributes: ['username', 'real_name']
}
],
order: [['created_at', 'DESC']],
limit: parseInt(limit),
offset: (parseInt(page) - 1) * parseInt(limit)
});
// 简单的CSV格式导出
const csvHeader = '申请编号,申请人姓名,身份证号,联系电话,参保类型,保险类型,保险金额,保险期限,地址,状态,申请时间,备注\n';
const csvData = applications.map(app => {
const statusMap = {
'pending': '待审核',
'initial_approved': '初审通过',
'under_review': '复审中',
'approved': '已通过',
'rejected': '已拒绝'
};
return [
app.application_number || '',
app.applicant_name || '',
app.id_card || '',
app.phone || '',
app.insurance_category || '',
app.insurance_type || '',
app.insurance_amount || '',
app.insurance_period || '',
app.address || '',
statusMap[app.status] || app.status,
app.created_at ? new Date(app.created_at).toLocaleString('zh-CN') : '',
app.remarks || ''
].map(field => `"${String(field).replace(/"/g, '""')}"`).join(',');
}).join('\n');
const csvContent = csvHeader + csvData;
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="insurance_applications_${new Date().toISOString().slice(0, 10)}.csv"`);
res.send('\uFEFF' + csvContent); // 添加BOM以支持中文
} catch (error) {
console.error('导出保险申请数据错误:', error);
res.status(500).json(responseFormat.error('导出保险申请数据失败'));
}
};
module.exports = {
getApplications,
createApplication,
getApplicationById,
updateApplication,
reviewApplication,
deleteApplication,
getApplicationStats,
getInsuranceCategories,
exportApplications
};

View File

@@ -4,7 +4,21 @@ const responseFormat = require('../utils/response');
// 获取保险类型列表
const getInsuranceTypes = async (req, res) => {
const requestStartTime = new Date();
try {
// ========== 后端接收数据日志 ==========
console.log('🔵 [后端] 接收到获取险种列表请求');
console.log('📥 [后端] 请求时间:', requestStartTime.toISOString());
console.log('📥 [后端] 请求方法:', req.method);
console.log('📥 [后端] 请求路径:', req.path);
console.log('📥 [后端] 查询参数:', JSON.stringify(req.query, null, 2));
console.log('📥 [后端] 用户信息:', req.user ? {
id: req.user.id,
username: req.user.username,
role: req.user.role
} : '未认证用户');
const {
page = 1,
pageSize = 10,
@@ -14,40 +28,122 @@ const getInsuranceTypes = async (req, res) => {
service_area,
on_sale_status
} = req.query;
const offset = (page - 1) * pageSize;
const whereClause = {};
if (name) {
whereClause.name = { [Op.like]: `%${name}%` };
}
if (status) {
whereClause.status = status;
}
if (applicable_livestock) {
whereClause.applicable_livestock = { [Op.like]: `%${applicable_livestock}%` };
}
if (service_area) {
whereClause.service_area = { [Op.like]: `%${service_area}%` };
}
if (on_sale_status !== undefined && on_sale_status !== '') {
whereClause.on_sale_status = on_sale_status === 'true';
}
const { count, rows } = await InsuranceType.findAndCountAll({
where: whereClause,
limit: parseInt(pageSize),
offset: offset,
order: [['add_time', 'DESC'], ['created_at', 'DESC']]
// ========== 后端数据处理日志 ==========
console.log('⚙️ [后端] 开始处理查询参数');
console.log('⚙️ [后端] 原始查询参数:', {
page, pageSize, name, status, applicable_livestock, service_area, on_sale_status
});
res.json(responseFormat.pagination(rows, {
page: parseInt(page),
limit: parseInt(pageSize),
const processedParams = {
page: parseInt(page) || 1,
pageSize: parseInt(pageSize) || 10,
name: name ? String(name).trim() : null,
status: status ? String(status).trim() : null,
applicable_livestock: applicable_livestock ? String(applicable_livestock).trim() : null,
service_area: service_area ? String(service_area).trim() : null,
on_sale_status: on_sale_status !== undefined && on_sale_status !== '' ? on_sale_status === 'true' : null
};
console.log('⚙️ [后端] 处理后的查询参数:', processedParams);
const offset = (processedParams.page - 1) * processedParams.pageSize;
console.log('⚙️ [后端] 分页计算 - offset:', offset, 'limit:', processedParams.pageSize);
const whereClause = {};
if (processedParams.name) {
whereClause.name = processedParams.name; // 精确查询
}
if (processedParams.status) {
whereClause.status = processedParams.status;
}
if (processedParams.applicable_livestock) {
whereClause.applicable_livestock = { [Op.like]: `%${processedParams.applicable_livestock}%` };
}
if (processedParams.service_area) {
whereClause.service_area = { [Op.like]: `%${processedParams.service_area}%` };
}
if (processedParams.on_sale_status !== null) {
whereClause.on_sale_status = processedParams.on_sale_status;
}
console.log('⚙️ [后端] 构建的WHERE条件:', whereClause);
const queryOptions = {
where: whereClause,
limit: processedParams.pageSize,
offset: offset,
order: [['add_time', 'DESC'], ['created_at', 'DESC']]
};
console.log('⚙️ [后端] 数据库查询选项:', queryOptions);
console.log('⚙️ [后端] 开始执行数据库查询...');
const { count, rows } = await InsuranceType.findAndCountAll(queryOptions);
console.log('✅ [后端] 数据库查询完成');
console.log('✅ [后端] 查询结果统计:', {
totalCount: count,
returnedRows: rows.length,
currentPage: processedParams.page,
pageSize: processedParams.pageSize,
totalPages: Math.ceil(count / processedParams.pageSize)
});
// ========== 后端响应数据日志 ==========
const responseEndTime = new Date();
const processingTime = responseEndTime - requestStartTime;
const paginationData = {
page: processedParams.page,
limit: processedParams.pageSize,
total: count
}, '获取险种列表成功'));
};
const successResponse = responseFormat.pagination(rows, paginationData, '获取险种列表成功');
console.log('📤 [后端] 准备发送成功响应');
console.log('📤 [后端] 响应时间:', responseEndTime.toISOString());
console.log('📤 [后端] 处理耗时:', processingTime + 'ms');
console.log('📤 [后端] 响应状态码:', 200);
console.log('📤 [后端] 响应数据结构:', {
success: successResponse.success,
message: successResponse.message,
dataType: typeof successResponse.data,
dataLength: Array.isArray(successResponse.data) ? successResponse.data.length : 'not array',
paginationType: typeof successResponse.pagination,
paginationKeys: successResponse.pagination ? Object.keys(successResponse.pagination) : 'no pagination'
});
console.log('📤 [后端] 分页信息:', successResponse.pagination);
console.log('📤 [后端] 返回的险种数据概览:', rows.map(item => ({
id: item.id,
name: item.name,
status: item.status,
on_sale_status: item.on_sale_status
})));
res.json(successResponse);
} catch (error) {
console.error('获取险种列表错误:', error);
res.status(500).json(responseFormat.error('获取险种列表失败'));
// ========== 后端错误处理日志 ==========
const errorEndTime = new Date();
const processingTime = errorEndTime - requestStartTime;
console.error('❌ [后端] 获取险种列表发生错误');
console.error('❌ [后端] 错误时间:', errorEndTime.toISOString());
console.error('❌ [后端] 处理耗时:', processingTime + 'ms');
console.error('❌ [后端] 错误类型:', error.name);
console.error('❌ [后端] 错误消息:', error.message);
console.error('❌ [后端] 错误堆栈:', error.stack);
const errorResponse = responseFormat.error('获取险种列表失败');
console.log('📤 [后端] 发送错误响应:', {
statusCode: 500,
response: errorResponse
});
res.status(500).json(errorResponse);
}
};
@@ -70,7 +166,26 @@ const getInsuranceTypeById = async (req, res) => {
// 创建险种
const createInsuranceType = async (req, res) => {
const requestStartTime = new Date();
try {
// ========== 后端接收数据日志 ==========
console.log('🔵 [后端] 接收到创建险种请求');
console.log('📥 [后端] 请求时间:', requestStartTime.toISOString());
console.log('📥 [后端] 请求方法:', req.method);
console.log('📥 [后端] 请求路径:', req.path);
console.log('📥 [后端] 请求头信息:', {
'content-type': req.headers['content-type'],
'authorization': req.headers.authorization ? '已提供' : '未提供',
'user-agent': req.headers['user-agent']
});
console.log('📥 [后端] 原始请求体数据:', JSON.stringify(req.body, null, 2));
console.log('📥 [后端] 用户信息:', req.user ? {
id: req.user.id,
username: req.user.username,
role: req.user.role
} : '未认证用户');
const {
name,
description,
@@ -88,13 +203,9 @@ const createInsuranceType = async (req, res) => {
status = 'active'
} = req.body;
// 检查名称是否已存在
const existingType = await InsuranceType.findOne({ where: { name } });
if (existingType) {
return res.status(400).json(responseFormat.error('险种名称已存在'));
}
const insuranceType = await InsuranceType.create({
// ========== 后端数据处理日志 ==========
console.log('⚙️ [后端] 开始数据处理和验证');
console.log('⚙️ [后端] 解构后的数据:', {
name,
description,
applicable_livestock,
@@ -104,22 +215,116 @@ const createInsuranceType = async (req, res) => {
coverage_amount_max,
premium_rate,
service_area,
add_time: add_time || new Date(),
add_time,
on_sale_status,
sort_order,
remarks,
status,
created_by: req.user?.id
status
});
res.status(201).json(responseFormat.success(insuranceType, '创建险种成功'));
// 数据类型验证和转换
const processedData = {
name: name ? String(name).trim() : null,
description: description ? String(description).trim() : null,
applicable_livestock: Array.isArray(applicable_livestock) ?
applicable_livestock.join(',') :
(applicable_livestock ? String(applicable_livestock).trim() : null),
insurance_term: insurance_term ? Number(insurance_term) : null,
policy_form: policy_form ? String(policy_form).trim() : null,
coverage_amount_min: coverage_amount_min ? Number(coverage_amount_min) : null,
coverage_amount_max: coverage_amount_max ? Number(coverage_amount_max) : null,
premium_rate: premium_rate ? Number(premium_rate) : null,
service_area: service_area ? String(service_area).trim() : null,
add_time: add_time ? new Date(add_time) : new Date(),
on_sale_status: Boolean(on_sale_status),
sort_order: sort_order ? Number(sort_order) : 0,
remarks: remarks ? String(remarks).trim() : null,
status: status || 'active'
};
console.log('⚙️ [后端] 处理后的数据:', processedData);
// 检查名称是否已存在
console.log('⚙️ [后端] 检查险种名称是否已存在:', processedData.name);
const existingType = await InsuranceType.findOne({ where: { name: processedData.name } });
if (existingType) {
console.log('❌ [后端] 险种名称已存在:', existingType.name);
const errorResponse = responseFormat.error('险种名称已存在');
console.log('📤 [后端] 发送错误响应:', errorResponse);
return res.status(400).json(errorResponse);
}
console.log('✅ [后端] 险种名称验证通过,开始创建数据');
// 准备数据库插入数据
const dbInsertData = {
...processedData,
created_by: req.user?.id || null
};
console.log('⚙️ [后端] 准备插入数据库的数据:', dbInsertData);
const insuranceType = await InsuranceType.create(dbInsertData);
console.log('✅ [后端] 险种创建成功,数据库返回:', {
id: insuranceType.id,
name: insuranceType.name,
created_at: insuranceType.created_at
});
// ========== 后端响应数据日志 ==========
const responseEndTime = new Date();
const processingTime = responseEndTime - requestStartTime;
const successResponse = responseFormat.success(insuranceType, '创建险种成功');
console.log('📤 [后端] 准备发送成功响应');
console.log('📤 [后端] 响应时间:', responseEndTime.toISOString());
console.log('📤 [后端] 处理耗时:', processingTime + 'ms');
console.log('📤 [后端] 响应状态码:', 201);
console.log('📤 [后端] 响应数据结构:', {
success: successResponse.success,
message: successResponse.message,
dataType: typeof successResponse.data,
dataId: successResponse.data?.id,
dataKeys: Object.keys(successResponse.data || {})
});
console.log('📤 [后端] 完整响应数据:', JSON.stringify(successResponse, null, 2));
res.status(201).json(successResponse);
} catch (error) {
console.error('创建险种错误:', error);
// ========== 后端错误处理日志 ==========
const errorEndTime = new Date();
const processingTime = errorEndTime - requestStartTime;
console.error('❌ [后端] 创建险种发生错误');
console.error('❌ [后端] 错误时间:', errorEndTime.toISOString());
console.error('❌ [后端] 处理耗时:', processingTime + 'ms');
console.error('❌ [后端] 错误类型:', error.name);
console.error('❌ [后端] 错误消息:', error.message);
console.error('❌ [后端] 错误堆栈:', error.stack);
let errorResponse;
let statusCode = 500;
if (error.name === 'SequelizeValidationError') {
const messages = error.errors.map(err => err.message);
return res.status(400).json(responseFormat.error(messages.join(', ')));
errorResponse = responseFormat.error(messages.join(', '));
statusCode = 400;
console.error('❌ [后端] 数据验证错误:', messages);
} else {
errorResponse = responseFormat.error('创建险种失败');
console.error('❌ [后端] 服务器内部错误');
}
res.status(500).json(responseFormat.error('创建险种失败'));
console.log('📤 [后端] 发送错误响应:', {
statusCode,
response: errorResponse
});
res.status(statusCode).json(errorResponse);
}
};
@@ -162,10 +367,18 @@ const updateInsuranceType = async (req, res) => {
}
}
// 处理applicable_livestock数据类型转换
let processedApplicableLivestock = insuranceType.applicable_livestock;
if (applicable_livestock !== undefined) {
processedApplicableLivestock = Array.isArray(applicable_livestock) ?
applicable_livestock.join(',') :
(applicable_livestock ? String(applicable_livestock).trim() : null);
}
await insuranceType.update({
name: name || insuranceType.name,
description: description !== undefined ? description : insuranceType.description,
applicable_livestock: applicable_livestock !== undefined ? applicable_livestock : insuranceType.applicable_livestock,
applicable_livestock: processedApplicableLivestock,
insurance_term: insurance_term !== undefined ? insurance_term : insuranceType.insurance_term,
policy_form: policy_form !== undefined ? policy_form : insuranceType.policy_form,
coverage_amount_min: coverage_amount_min !== undefined ? coverage_amount_min : insuranceType.coverage_amount_min,

View File

@@ -1,221 +0,0 @@
const LivestockType = require('../models/LivestockType');
const User = require('../models/User');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取牲畜类型列表
const getLivestockTypes = async (req, res) => {
try {
const {
name,
is_active,
page = 1,
limit = 10
} = req.query;
const whereClause = {};
// 名称筛选
if (name) {
whereClause.name = { [Op.like]: `%${name}%` };
}
// 状态筛选
if (is_active !== undefined) {
whereClause.is_active = is_active === 'true';
}
const offset = (page - 1) * limit;
const { count, rows } = await LivestockType.findAndCountAll({
where: whereClause,
order: [['created_at', 'DESC']],
offset,
limit: parseInt(limit)
});
res.json(responseFormat.pagination(rows, {
page: parseInt(page),
limit: parseInt(limit),
total: count
}, '获取牲畜类型列表成功'));
} catch (error) {
console.error('获取牲畜类型列表错误:', error);
res.status(500).json(responseFormat.error('获取牲畜类型列表失败'));
}
};
// 获取所有启用的牲畜类型(用于下拉选择)
const getActiveLivestockTypes = async (req, res) => {
try {
const types = await LivestockType.findAll({
where: { is_active: true },
attributes: ['id', 'name', 'description', 'base_value', 'premium_rate'],
order: [['name', 'ASC']]
});
res.json(responseFormat.success(types, '获取启用牲畜类型成功'));
} catch (error) {
console.error('获取启用牲畜类型错误:', error);
res.status(500).json(responseFormat.error('获取启用牲畜类型失败'));
}
};
// 创建牲畜类型
const createLivestockType = async (req, res) => {
try {
const typeData = req.body;
// 检查名称是否已存在
const existingType = await LivestockType.findOne({
where: { name: typeData.name }
});
if (existingType) {
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
}
const type = await LivestockType.create({
...typeData,
created_by: req.user?.id
});
res.status(201).json(responseFormat.created(type, '牲畜类型创建成功'));
} catch (error) {
console.error('创建牲畜类型错误:', error);
if (error.name === 'SequelizeValidationError') {
const messages = error.errors.map(err => err.message);
return res.status(400).json(responseFormat.error(messages.join(', ')));
}
res.status(500).json(responseFormat.error('创建牲畜类型失败'));
}
};
// 获取单个牲畜类型详情
const getLivestockTypeById = async (req, res) => {
try {
const { id } = req.params;
const type = await LivestockType.findByPk(id);
if (!type) {
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
}
res.json(responseFormat.success(type, '获取牲畜类型详情成功'));
} catch (error) {
console.error('获取牲畜类型详情错误:', error);
res.status(500).json(responseFormat.error('获取牲畜类型详情失败'));
}
};
// 更新牲畜类型
const updateLivestockType = async (req, res) => {
try {
const { id } = req.params;
const updateData = req.body;
const type = await LivestockType.findByPk(id);
if (!type) {
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
}
// 如果更新名称,检查是否与其他记录重复
if (updateData.name && updateData.name !== type.name) {
const existingType = await LivestockType.findOne({
where: {
name: updateData.name,
id: { [Op.ne]: id }
}
});
if (existingType) {
return res.status(400).json(responseFormat.error('牲畜类型名称已存在'));
}
}
updateData.updated_by = req.user?.id;
await type.update(updateData);
res.json(responseFormat.success(type, '牲畜类型更新成功'));
} catch (error) {
console.error('更新牲畜类型错误:', error);
if (error.name === 'SequelizeValidationError') {
const messages = error.errors.map(err => err.message);
return res.status(400).json(responseFormat.error(messages.join(', ')));
}
res.status(500).json(responseFormat.error('更新牲畜类型失败'));
}
};
// 删除牲畜类型(软删除 - 设置为不启用)
const deleteLivestockType = async (req, res) => {
try {
const { id } = req.params;
const type = await LivestockType.findByPk(id);
if (!type) {
return res.status(404).json(responseFormat.error('牲畜类型不存在'));
}
// 检查是否有关联的保单
const LivestockPolicy = require('../models/LivestockPolicy');
const relatedPolicies = await LivestockPolicy.count({
where: { livestock_type_id: id }
});
if (relatedPolicies > 0) {
// 如果有关联保单,只能设置为不启用
await type.update({
is_active: false,
updated_by: req.user?.id
});
return res.json(responseFormat.success(null, '牲畜类型已设置为不启用(存在关联保单)'));
}
// 如果没有关联保单,可以物理删除
await type.destroy();
res.json(responseFormat.success(null, '牲畜类型删除成功'));
} catch (error) {
console.error('删除牲畜类型错误:', error);
res.status(500).json(responseFormat.error('删除牲畜类型失败'));
}
};
// 批量更新牲畜类型状态
const batchUpdateLivestockTypeStatus = async (req, res) => {
try {
const { ids, is_active } = req.body;
if (!Array.isArray(ids) || ids.length === 0) {
return res.status(400).json(responseFormat.error('请提供有效的ID列表'));
}
await LivestockType.update(
{
is_active,
updated_by: req.user?.id
},
{
where: { id: { [Op.in]: ids } }
}
);
res.json(responseFormat.success(null, '批量更新牲畜类型状态成功'));
} catch (error) {
console.error('批量更新牲畜类型状态错误:', error);
res.status(500).json(responseFormat.error('批量更新牲畜类型状态失败'));
}
};
module.exports = {
getLivestockTypes,
getActiveLivestockTypes,
createLivestockType,
getLivestockTypeById,
updateLivestockType,
deleteLivestockType,
batchUpdateLivestockTypeStatus
};

View File

@@ -1,178 +0,0 @@
const { User, Role, Menu } = require('../models');
/**
* 获取菜单列表
* @param {Object} req - Express请求对象
* @param {Object} res - Express响应对象
*/
exports.getMenus = async (req, res) => {
try {
console.log('开始获取菜单,用户信息:', req.user);
// 获取用户ID从JWT中解析或通过其他方式获取
const userId = req.user?.id; // 假设通过认证中间件解析后存在
console.log('用户ID:', userId);
// 如果没有用户ID返回基础菜单
if (!userId) {
console.log('没有用户ID返回基础菜单');
const menus = await Menu.findAll({
where: {
parent_id: null,
show: true,
status: 'active'
},
include: [
{
model: Menu,
as: 'children',
where: {
show: true,
status: 'active'
},
required: false,
order: [['order', 'ASC']]
}
],
order: [['order', 'ASC']]
});
console.log('基础菜单查询结果:', menus.length);
return res.json({
code: 200,
status: 'success',
data: menus,
message: '获取菜单成功'
});
}
console.log('查询用户信息...');
// 获取用户信息和角色
const user = await User.findByPk(userId, {
include: [
{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}
]
});
console.log('用户查询结果:', user ? '找到用户' : '用户不存在');
if (!user) {
return res.status(404).json({
code: 404,
status: 'error',
message: '用户不存在'
});
}
// 获取角色的权限列表
const userPermissions = user.role?.permissions || [];
console.log('用户权限:', userPermissions);
console.log('查询菜单数据...');
// 查询菜单,这里简化处理,实际应用中可能需要根据权限过滤
const menus = await Menu.findAll({
where: {
parent_id: null,
show: true,
status: 'active'
},
include: [
{
model: Menu,
as: 'children',
where: {
show: true,
status: 'active'
},
required: false,
order: [['order', 'ASC']]
}
],
order: [['order', 'ASC']]
});
console.log('菜单查询结果:', menus.length);
// 这里可以添加根据权限过滤菜单的逻辑
// 简化示例,假设所有用户都能看到所有激活的菜单
return res.json({
code: 200,
status: 'success',
data: menus,
message: '获取菜单成功'
});
} catch (error) {
console.error('获取菜单失败:', error);
console.error('错误堆栈:', error.stack);
return res.status(500).json({
code: 500,
status: 'error',
message: '服务器内部错误',
error: error.message
});
}
};
/**
* 获取所有菜单(包括非激活状态,仅管理员可用)
* @param {Object} req - Express请求对象
* @param {Object} res - Express响应对象
*/
exports.getAllMenus = async (req, res) => {
try {
// 检查用户是否为管理员(简化示例)
const user = await User.findByPk(req.user?.id, {
include: [
{
model: Role,
attributes: ['id', 'name', 'permissions']
}
]
});
if (!user || !user.Role || !user.Role.permissions.includes('*:*')) {
return res.status(403).json({
code: 403,
status: 'error',
message: '没有权限查看所有菜单'
});
}
// 查询所有菜单
const menus = await Menu.findAll({
where: {
parent_id: null
},
include: [
{
model: Menu,
as: 'children',
required: false,
order: [['order', 'ASC']]
}
],
order: [['order', 'ASC']]
});
return res.json({
code: 200,
status: 'success',
data: menus,
message: '获取所有菜单成功'
});
} catch (error) {
console.error('获取所有菜单失败:', error);
return res.status(500).json({
code: 500,
status: 'error',
message: '服务器内部错误'
});
}
};

View File

@@ -1,344 +0,0 @@
const { OperationLog, User } = require('../models');
const { Op } = require('sequelize');
const ExcelJS = require('exceljs');
/**
* 操作日志控制器
*/
class OperationLogController {
/**
* 获取操作日志列表
*/
async getOperationLogs(req, res) {
try {
const {
page = 1,
limit = 20,
user_id,
operation_type,
operation_module,
status,
start_date,
end_date,
keyword
} = req.query;
// 构建查询条件
const whereConditions = {};
if (user_id) {
whereConditions.user_id = user_id;
}
if (operation_type) {
whereConditions.operation_type = operation_type;
}
if (operation_module) {
whereConditions.operation_module = operation_module;
}
if (status) {
whereConditions.status = status;
}
// 时间范围查询
if (start_date || end_date) {
whereConditions.created_at = {};
if (start_date) {
whereConditions.created_at[Op.gte] = new Date(start_date);
}
if (end_date) {
const endDateTime = new Date(end_date);
endDateTime.setHours(23, 59, 59, 999);
whereConditions.created_at[Op.lte] = endDateTime;
}
}
// 关键词搜索
if (keyword) {
whereConditions[Op.or] = [
{ operation_content: { [Op.like]: `%${keyword}%` } },
{ operation_target: { [Op.like]: `%${keyword}%` } },
{ request_url: { [Op.like]: `%${keyword}%` } },
{ ip_address: { [Op.like]: `%${keyword}%` } }
];
}
// 分页参数
const offset = (parseInt(page) - 1) * parseInt(limit);
// 查询操作日志
const { count, rows } = await OperationLog.findAndCountAll({
where: whereConditions,
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'real_name', 'email']
}
],
order: [['created_at', 'DESC']],
limit: parseInt(limit),
offset: offset
});
const totalPages = Math.ceil(count / parseInt(limit));
res.json({
status: 'success',
message: '获取操作日志列表成功',
data: {
logs: rows,
total: count,
page: parseInt(page),
limit: parseInt(limit),
totalPages: totalPages
}
});
} catch (error) {
console.error('获取操作日志列表失败:', error);
res.status(500).json({
status: 'error',
message: '获取操作日志列表失败',
error: error.message
});
}
}
/**
* 获取操作日志统计信息
*/
async getOperationStats(req, res) {
try {
const {
user_id,
start_date,
end_date
} = req.query;
// 构建查询条件
const whereConditions = {};
if (user_id) {
whereConditions.user_id = user_id;
}
// 时间范围查询
if (start_date || end_date) {
whereConditions.created_at = {};
if (start_date) {
whereConditions.created_at[Op.gte] = new Date(start_date);
}
if (end_date) {
const endDateTime = new Date(end_date);
endDateTime.setHours(23, 59, 59, 999);
whereConditions.created_at[Op.lte] = endDateTime;
}
}
// 获取统计数据
const stats = await OperationLog.getOperationStats(whereConditions);
res.json({
status: 'success',
message: '获取操作日志统计成功',
data: stats
});
} catch (error) {
console.error('获取操作日志统计失败:', error);
res.status(500).json({
status: 'error',
message: '获取操作日志统计失败',
error: error.message
});
}
}
/**
* 获取操作日志详情
*/
async getOperationLogById(req, res) {
try {
const { id } = req.params;
const log = await OperationLog.findByPk(id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'real_name', 'email']
}
]
});
if (!log) {
return res.status(404).json({
status: 'error',
message: '操作日志不存在'
});
}
res.json({
status: 'success',
message: '获取操作日志详情成功',
data: log
});
} catch (error) {
console.error('获取操作日志详情失败:', error);
res.status(500).json({
status: 'error',
message: '获取操作日志详情失败',
error: error.message
});
}
}
/**
* 导出操作日志
*/
async exportOperationLogs(req, res) {
try {
const {
user_id,
operation_type,
operation_module,
status,
start_date,
end_date,
keyword
} = req.body;
// 构建查询条件
const whereConditions = {};
if (user_id) {
whereConditions.user_id = user_id;
}
if (operation_type) {
whereConditions.operation_type = operation_type;
}
if (operation_module) {
whereConditions.operation_module = operation_module;
}
if (status) {
whereConditions.status = status;
}
// 时间范围查询
if (start_date || end_date) {
whereConditions.created_at = {};
if (start_date) {
whereConditions.created_at[Op.gte] = new Date(start_date);
}
if (end_date) {
const endDateTime = new Date(end_date);
endDateTime.setHours(23, 59, 59, 999);
whereConditions.created_at[Op.lte] = endDateTime;
}
}
// 关键词搜索
if (keyword) {
whereConditions[Op.or] = [
{ operation_content: { [Op.like]: `%${keyword}%` } },
{ operation_target: { [Op.like]: `%${keyword}%` } },
{ request_url: { [Op.like]: `%${keyword}%` } },
{ ip_address: { [Op.like]: `%${keyword}%` } }
];
}
// 查询所有符合条件的日志
const logs = await OperationLog.findAll({
where: whereConditions,
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'real_name', 'email']
}
],
order: [['created_at', 'DESC']]
});
// 创建Excel工作簿
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('操作日志');
// 设置表头
worksheet.columns = [
{ header: 'ID', key: 'id', width: 10 },
{ header: '操作用户', key: 'username', width: 15 },
{ header: '真实姓名', key: 'real_name', width: 15 },
{ header: '操作类型', key: 'operation_type', width: 15 },
{ header: '操作模块', key: 'operation_module', width: 15 },
{ header: '操作内容', key: 'operation_content', width: 30 },
{ header: '操作目标', key: 'operation_target', width: 20 },
{ header: '请求方法', key: 'request_method', width: 10 },
{ header: '请求URL', key: 'request_url', width: 30 },
{ header: 'IP地址', key: 'ip_address', width: 15 },
{ header: '执行时间(ms)', key: 'execution_time', width: 12 },
{ header: '状态', key: 'status', width: 10 },
{ header: '错误信息', key: 'error_message', width: 30 },
{ header: '创建时间', key: 'created_at', width: 20 }
];
// 添加数据
logs.forEach(log => {
worksheet.addRow({
id: log.id,
username: log.user ? log.user.username : '',
real_name: log.user ? log.user.real_name : '',
operation_type: log.operation_type,
operation_module: log.operation_module,
operation_content: log.operation_content,
operation_target: log.operation_target,
request_method: log.request_method,
request_url: log.request_url,
ip_address: log.ip_address,
execution_time: log.execution_time,
status: log.status,
error_message: log.error_message,
created_at: log.created_at
});
});
// 设置响应头
const filename = `操作日志_${new Date().toISOString().slice(0, 10)}.xlsx`;
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`);
// 写入响应
await workbook.xlsx.write(res);
res.end();
// 记录导出操作日志
await OperationLog.logOperation({
user_id: req.user.id,
operation_type: 'export',
operation_module: 'operation_logs',
operation_content: '导出操作日志',
operation_target: `导出${logs.length}条记录`,
request_method: 'POST',
request_url: '/api/operation-logs/export',
request_params: req.body,
ip_address: req.ip,
user_agent: req.get('User-Agent'),
status: 'success'
});
} catch (error) {
console.error('导出操作日志失败:', error);
res.status(500).json({
status: 'error',
message: '导出操作日志失败',
error: error.message
});
}
}
}
module.exports = new OperationLogController();

View File

@@ -1,415 +0,0 @@
const { Permission, Role, Menu, RolePermission, MenuPermission } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
/**
* 构建权限树形结构
*/
function buildPermissionTree(permissions, parentId = null) {
const tree = [];
for (const permission of permissions) {
if (permission.parent_id === parentId) {
const children = buildPermissionTree(permissions, permission.id);
const node = {
...permission.toJSON(),
children: children.length > 0 ? children : undefined
};
tree.push(node);
}
}
return tree;
}
/**
* 权限管理控制器
*/
class PermissionController {
/**
* 获取权限列表
*/
async getPermissions(req, res) {
try {
const {
page = 1,
limit = 10,
module,
type,
status = 'active',
keyword
} = req.query;
const offset = (page - 1) * limit;
const where = { status };
// 模块筛选
if (module) {
where.module = module;
}
// 类型筛选
if (type) {
where.type = type;
}
// 关键词搜索
if (keyword) {
where[Op.or] = [
{ name: { [Op.like]: `%${keyword}%` } },
{ code: { [Op.like]: `%${keyword}%` } },
{ description: { [Op.like]: `%${keyword}%` } }
];
}
const { count, rows } = await Permission.findAndCountAll({
where,
include: [
{
model: Permission,
as: 'parent',
attributes: ['id', 'name', 'code']
},
{
model: Permission,
as: 'children',
attributes: ['id', 'name', 'code', 'type']
}
],
order: [['sort_order', 'ASC'], ['id', 'ASC']],
limit: parseInt(limit),
offset: parseInt(offset)
});
res.json(responseFormat.success({
permissions: rows,
pagination: {
total: count,
page: parseInt(page),
limit: parseInt(limit),
pages: Math.ceil(count / limit)
}
}, '获取权限列表成功'));
} catch (error) {
console.error('获取权限列表失败:', error);
res.status(500).json(responseFormat.error('获取权限列表失败'));
}
}
/**
* 获取权限树形结构
*/
async getPermissionTree(req, res) {
try {
const { module, type } = req.query;
const where = { status: 'active' };
if (module) {
where.module = module;
}
if (type) {
where.type = type;
}
const permissions = await Permission.findAll({
where,
order: [['sort_order', 'ASC'], ['id', 'ASC']]
});
// 构建树形结构
const tree = buildPermissionTree(permissions);
res.json(responseFormat.success(tree, '获取权限树成功'));
} catch (error) {
console.error('获取权限树失败:', error);
res.status(500).json(responseFormat.error('获取权限树失败'));
}
}
/**
* 获取单个权限详情
*/
async getPermissionById(req, res) {
try {
const { id } = req.params;
const permission = await Permission.findByPk(id, {
include: [
{
model: Permission,
as: 'parent',
attributes: ['id', 'name', 'code']
},
{
model: Permission,
as: 'children',
attributes: ['id', 'name', 'code', 'type']
}
]
});
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
res.json(responseFormat.success(permission, '获取权限详情成功'));
} catch (error) {
console.error('获取权限详情失败:', error);
res.status(500).json(responseFormat.error('获取权限详情失败'));
}
}
/**
* 创建权限
*/
async createPermission(req, res) {
try {
const {
name,
code,
description,
module,
type = 'operation',
parent_id,
sort_order = 0
} = req.body;
// 验证必填字段
if (!name || !code || !module) {
return res.status(400).json(responseFormat.error('权限名称、权限代码和所属模块为必填项'));
}
// 检查权限代码是否已存在
const existingPermission = await Permission.findOne({ where: { code } });
if (existingPermission) {
return res.status(400).json(responseFormat.error('权限代码已存在'));
}
// 如果有父权限,验证父权限是否存在
if (parent_id) {
const parentPermission = await Permission.findByPk(parent_id);
if (!parentPermission) {
return res.status(400).json(responseFormat.error('父权限不存在'));
}
}
const permission = await Permission.create({
name,
code,
description,
module,
type,
parent_id,
sort_order,
status: 'active'
});
res.status(201).json(responseFormat.created(permission, '创建权限成功'));
} catch (error) {
console.error('创建权限失败:', error);
res.status(500).json(responseFormat.error('创建权限失败'));
}
}
/**
* 更新权限
*/
async updatePermission(req, res) {
try {
const { id } = req.params;
const {
name,
code,
description,
module,
type,
parent_id,
sort_order,
status
} = req.body;
const permission = await Permission.findByPk(id);
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
// 如果修改了权限代码,检查是否与其他权限冲突
if (code && code !== permission.code) {
const existingPermission = await Permission.findOne({
where: {
code,
id: { [Op.ne]: id }
}
});
if (existingPermission) {
return res.status(400).json(responseFormat.error('权限代码已存在'));
}
}
// 如果有父权限,验证父权限是否存在且不是自己
if (parent_id) {
if (parent_id == id) {
return res.status(400).json(responseFormat.error('不能将自己设为父权限'));
}
const parentPermission = await Permission.findByPk(parent_id);
if (!parentPermission) {
return res.status(400).json(responseFormat.error('父权限不存在'));
}
}
await permission.update({
name: name || permission.name,
code: code || permission.code,
description: description !== undefined ? description : permission.description,
module: module || permission.module,
type: type || permission.type,
parent_id: parent_id !== undefined ? parent_id : permission.parent_id,
sort_order: sort_order !== undefined ? sort_order : permission.sort_order,
status: status || permission.status
});
res.json(responseFormat.success(permission, '更新权限成功'));
} catch (error) {
console.error('更新权限失败:', error);
res.status(500).json(responseFormat.error('更新权限失败'));
}
}
/**
* 删除权限
*/
async deletePermission(req, res) {
try {
const { id } = req.params;
const permission = await Permission.findByPk(id);
if (!permission) {
return res.status(404).json(responseFormat.error('权限不存在'));
}
// 检查是否有子权限
const childrenCount = await Permission.count({ where: { parent_id: id } });
if (childrenCount > 0) {
return res.status(400).json(responseFormat.error('该权限下还有子权限,无法删除'));
}
// 检查是否有角色在使用该权限
const rolePermissionCount = await RolePermission.count({ where: { permission_id: id } });
if (rolePermissionCount > 0) {
return res.status(400).json(responseFormat.error('该权限正在被角色使用,无法删除'));
}
// 检查是否有菜单在使用该权限
const menuPermissionCount = await MenuPermission.count({ where: { permission_id: id } });
if (menuPermissionCount > 0) {
return res.status(400).json(responseFormat.error('该权限正在被菜单使用,无法删除'));
}
await permission.destroy();
res.json(responseFormat.success(null, '删除权限成功'));
} catch (error) {
console.error('删除权限失败:', error);
res.status(500).json(responseFormat.error('删除权限失败'));
}
}
/**
* 获取角色权限
*/
async getRolePermissions(req, res) {
try {
const { roleId } = req.params;
const role = await Role.findByPk(roleId, {
include: [
{
model: Permission,
as: 'rolePermissions',
through: {
attributes: ['granted']
}
}
]
});
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
res.json(responseFormat.success({
role: {
id: role.id,
name: role.name,
description: role.description
},
permissions: role.rolePermissions
}, '获取角色权限成功'));
} catch (error) {
console.error('获取角色权限失败:', error);
res.status(500).json(responseFormat.error('获取角色权限失败'));
}
}
/**
* 分配角色权限
*/
async assignRolePermissions(req, res) {
try {
const { roleId } = req.params;
const { permissionIds } = req.body;
if (!Array.isArray(permissionIds)) {
return res.status(400).json(responseFormat.error('权限ID列表格式错误'));
}
const role = await Role.findByPk(roleId);
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
// 删除现有的角色权限关联
await RolePermission.destroy({ where: { role_id: roleId } });
// 创建新的角色权限关联
if (permissionIds.length > 0) {
const rolePermissions = permissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
await RolePermission.bulkCreate(rolePermissions);
}
res.json(responseFormat.success(null, '分配角色权限成功'));
} catch (error) {
console.error('分配角色权限失败:', error);
res.status(500).json(responseFormat.error('分配角色权限失败'));
}
}
/**
* 获取模块列表
*/
async getModules(req, res) {
try {
const modules = await Permission.findAll({
attributes: ['module'],
group: ['module'],
order: [['module', 'ASC']]
});
const moduleList = modules.map(item => item.module);
res.json(responseFormat.success(moduleList, '获取模块列表成功'));
} catch (error) {
console.error('获取模块列表失败:', error);
res.status(500).json(responseFormat.error('获取模块列表失败'));
}
}
}
module.exports = new PermissionController();

View File

@@ -1,239 +0,0 @@
const { Policy, InsuranceApplication, InsuranceType, User } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取保单列表
const getPolicies = async (req, res) => {
try {
const {
policy_number, // 前端发送的参数名
policyholder_name, // 前端发送的参数名
insurance_type_id, // 前端发送的参数名
status, // 前端发送的参数名
page = 1,
pageSize = 10 // 前端发送的参数名
} = req.query;
const whereClause = {};
// 保单编号筛选
if (policy_number) {
whereClause.policy_no = { [Op.like]: `%${policy_number}%` };
}
// 保单状态筛选
if (status) {
whereClause.policy_status = status;
}
// 保险类型筛选
if (insurance_type_id) {
whereClause.insurance_type_id = insurance_type_id;
}
const offset = (page - 1) * pageSize;
const { count, rows } = await Policy.findAndCountAll({
where: whereClause,
include: [
{
model: InsuranceApplication,
as: 'application',
attributes: ['id', 'customer_name', 'customer_phone'],
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name']
}
]
},
{
model: User,
as: 'customer',
attributes: ['id', 'real_name', 'username']
},
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name']
}
],
order: [['created_at', 'DESC']],
offset,
limit: parseInt(pageSize)
});
// 处理返回数据,确保前端能够正确解析
const processedRows = rows.map(row => {
const policy = row.toJSON();
return {
id: policy.id,
policy_number: policy.policy_no, // 映射字段名
policyholder_name: policy.application?.customer_name || policy.customer?.real_name || '',
insured_name: policy.application?.customer_name || policy.customer?.real_name || '',
insurance_type_id: policy.insurance_type_id,
insurance_type_name: policy.insurance_type?.name || '',
premium_amount: parseFloat(policy.premium_amount) || 0,
coverage_amount: parseFloat(policy.coverage_amount) || 0,
start_date: policy.start_date,
end_date: policy.end_date,
status: policy.policy_status, // 映射字段名
phone: policy.application?.customer_phone || '',
email: policy.customer?.email || '',
address: policy.application?.address || '',
remarks: policy.terms_and_conditions || '',
created_at: policy.created_at,
updated_at: policy.updated_at
};
});
res.json(responseFormat.pagination(processedRows, {
page: parseInt(page),
pageSize: parseInt(pageSize),
total: count
}, '获取保单列表成功'));
} catch (error) {
console.error('获取保单列表错误:', error);
res.status(500).json(responseFormat.error('获取保单列表失败'));
}
};
// 创建保单
const createPolicy = async (req, res) => {
try {
const policyData = req.body;
// 生成保单编号
const policyNo = `POL${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
const policy = await Policy.create({
...policyData,
policy_no: policyNo
});
res.status(201).json(responseFormat.created(policy, '保单创建成功'));
} catch (error) {
console.error('创建保单错误:', error);
res.status(500).json(responseFormat.error('创建保单失败'));
}
};
// 获取单个保单详情
const getPolicyById = async (req, res) => {
try {
const { id } = req.params;
const policy = await Policy.findByPk(id, {
include: [
{
model: InsuranceApplication,
as: 'application',
include: [
{
model: InsuranceType,
as: 'insurance_type',
attributes: ['id', 'name', 'description', 'premium_rate']
}
]
},
{
model: User,
as: 'customer',
attributes: ['id', 'real_name', 'username', 'phone', 'email']
}
]
});
if (!policy) {
return res.status(404).json(responseFormat.error('保单不存在'));
}
res.json(responseFormat.success(policy, '获取保单详情成功'));
} catch (error) {
console.error('获取保单详情错误:', error);
res.status(500).json(responseFormat.error('获取保单详情失败'));
}
};
// 更新保单
const updatePolicy = async (req, res) => {
try {
const { id } = req.params;
const updateData = req.body;
const policy = await Policy.findByPk(id);
if (!policy) {
return res.status(404).json(responseFormat.error('保单不存在'));
}
await policy.update(updateData);
res.json(responseFormat.success(policy, '保单更新成功'));
} catch (error) {
console.error('更新保单错误:', error);
res.status(500).json(responseFormat.error('更新保单失败'));
}
};
// 更新保单状态
const updatePolicyStatus = async (req, res) => {
try {
const { id } = req.params;
const { policy_status } = req.body;
const policy = await Policy.findByPk(id);
if (!policy) {
return res.status(404).json(responseFormat.error('保单不存在'));
}
if (!['active', 'expired', 'cancelled', 'suspended'].includes(policy_status)) {
return res.status(400).json(responseFormat.error('无效的保单状态'));
}
await policy.update({ policy_status });
res.json(responseFormat.success(policy, '保单状态更新成功'));
} catch (error) {
console.error('更新保单状态错误:', error);
res.status(500).json(responseFormat.error('更新保单状态失败'));
}
};
// 获取保单统计
const getPolicyStats = async (req, res) => {
try {
const stats = await Policy.findAll({
attributes: [
'policy_status',
[Policy.sequelize.fn('COUNT', Policy.sequelize.col('id')), 'count'],
[Policy.sequelize.fn('SUM', Policy.sequelize.col('coverage_amount')), 'total_coverage'],
[Policy.sequelize.fn('SUM', Policy.sequelize.col('premium_amount')), 'total_premium']
],
group: ['policy_status']
});
const total = await Policy.count();
const totalCoverage = await Policy.sum('coverage_amount');
const totalPremium = await Policy.sum('premium_amount');
res.json(responseFormat.success({
stats,
total,
total_coverage: totalCoverage || 0,
total_premium: totalPremium || 0
}, '获取保单统计成功'));
} catch (error) {
console.error('获取保单统计错误:', error);
res.status(500).json(responseFormat.error('获取保单统计失败'));
}
};
module.exports = {
getPolicies,
createPolicy,
getPolicyById,
updatePolicy,
updatePolicyStatus,
getPolicyStats
};

View File

@@ -1,603 +0,0 @@
const { sequelize } = require('../config/database');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
/**
* 监管任务结项控制器
* 处理监管任务结项相关的业务逻辑
*/
// 获取监管任务结项列表
const getTaskCompletions = async (req, res) => {
try {
const {
application_no, // 申请编号
policy_no, // 保单号
product_name, // 产品名称
customer_name, // 客户名称
completion_date, // 结项日期
status, // 状态
reviewer_name, // 审核人员
page = 1,
limit = 10
} = req.query;
// 构建查询条件
let whereClause = '';
const params = [];
if (application_no) {
whereClause += ' AND rtc.application_no LIKE ?';
params.push(`%${application_no}%`);
}
if (policy_no) {
whereClause += ' AND rtc.policy_no LIKE ?';
params.push(`%${policy_no}%`);
}
if (product_name) {
whereClause += ' AND rtc.product_name LIKE ?';
params.push(`%${product_name}%`);
}
if (customer_name) {
whereClause += ' AND rtc.customer_name LIKE ?';
params.push(`%${customer_name}%`);
}
if (completion_date) {
whereClause += ' AND DATE(rtc.completion_date) = ?';
params.push(completion_date);
}
if (status) {
whereClause += ' AND rtc.status = ?';
params.push(status);
}
if (reviewer_name) {
whereClause += ' AND rtc.reviewer_name LIKE ?';
params.push(`%${reviewer_name}%`);
}
// 计算偏移量
const offset = (page - 1) * limit;
// 查询总数
const countQuery = `
SELECT COUNT(*) as total
FROM regulatory_task_completions rtc
WHERE 1=1 ${whereClause}
`;
const [countResult] = await sequelize.query(countQuery, {
replacements: params,
type: sequelize.QueryTypes.SELECT
});
// 查询数据
const dataQuery = `
SELECT
rtc.id,
rtc.application_no,
rtc.policy_no,
rtc.product_name,
rtc.customer_name,
rtc.customer_phone,
rtc.insurance_amount,
rtc.premium_amount,
rtc.start_date,
rtc.end_date,
rtc.completion_date,
rtc.status,
rtc.reviewer_name,
rtc.review_comments,
rtc.created_at,
rtc.updated_at
FROM regulatory_task_completions rtc
WHERE 1=1 ${whereClause}
ORDER BY rtc.created_at DESC
LIMIT ? OFFSET ?
`;
const results = await sequelize.query(dataQuery, {
replacements: [...params, parseInt(limit), offset],
type: sequelize.QueryTypes.SELECT
});
res.json(responseFormat.success({
list: results,
pagination: {
current: parseInt(page),
pageSize: parseInt(limit),
total: countResult.total,
totalPages: Math.ceil(countResult.total / limit)
}
}));
} catch (error) {
console.error('获取监管任务结项列表失败:', error);
res.status(500).json(responseFormat.error('获取监管任务结项列表失败'));
}
};
// 获取单个监管任务结项详情
const getTaskCompletionById = async (req, res) => {
try {
const { id } = req.params;
const query = `
SELECT
rtc.*,
(
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'id', rtca.id,
'file_name', rtca.file_name,
'file_path', rtca.file_path,
'file_size', rtca.file_size,
'file_type', rtca.file_type,
'upload_time', rtca.upload_time
)
)
FROM regulatory_task_completion_attachments rtca
WHERE rtca.completion_id = rtc.id
) as attachments,
(
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
'id', rtcl.id,
'operation_type', rtcl.operation_type,
'operation_description', rtcl.operation_description,
'operator_name', rtcl.operator_name,
'operation_time', rtcl.operation_time,
'ip_address', rtcl.ip_address
)
)
FROM regulatory_task_completion_logs rtcl
WHERE rtcl.completion_id = rtc.id
ORDER BY rtcl.operation_time DESC
) as operation_logs
FROM regulatory_task_completions rtc
WHERE rtc.id = ?
`;
const [result] = await sequelize.query(query, {
replacements: [id],
type: sequelize.QueryTypes.SELECT
});
if (!result) {
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
}
// 解析JSON字段
if (result.attachments) {
result.attachments = JSON.parse(result.attachments) || [];
} else {
result.attachments = [];
}
if (result.operation_logs) {
result.operation_logs = JSON.parse(result.operation_logs) || [];
} else {
result.operation_logs = [];
}
res.json(responseFormat.success(result));
} catch (error) {
console.error('获取监管任务结项详情失败:', error);
res.status(500).json(responseFormat.error('获取监管任务结项详情失败'));
}
};
// 创建监管任务结项记录
const createTaskCompletion = async (req, res) => {
const transaction = await sequelize.transaction();
try {
const {
application_no,
policy_no,
product_name,
customer_name,
customer_phone,
insurance_amount,
premium_amount,
start_date,
end_date,
completion_date,
status = 'pending',
review_comments,
attachments = []
} = req.body;
// 验证必填字段
if (!application_no || !policy_no || !product_name || !customer_name) {
return res.status(400).json(responseFormat.error('申请编号、保单号、产品名称、客户名称为必填项'));
}
// 检查申请编号是否已存在
const existingQuery = `
SELECT id FROM regulatory_task_completions
WHERE application_no = ? OR policy_no = ?
`;
const [existing] = await sequelize.query(existingQuery, {
replacements: [application_no, policy_no],
type: sequelize.QueryTypes.SELECT,
transaction
});
if (existing) {
await transaction.rollback();
return res.status(400).json(responseFormat.error('申请编号或保单号已存在'));
}
// 创建监管任务结项记录
const insertQuery = `
INSERT INTO regulatory_task_completions (
application_no, policy_no, product_name, customer_name, customer_phone,
insurance_amount, premium_amount, start_date, end_date, completion_date,
status, review_comments, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
`;
const [insertResult] = await sequelize.query(insertQuery, {
replacements: [
application_no, policy_no, product_name, customer_name, customer_phone,
insurance_amount, premium_amount, start_date, end_date, completion_date,
status, review_comments
],
type: sequelize.QueryTypes.INSERT,
transaction
});
const completionId = insertResult;
// 创建附件记录
if (attachments && attachments.length > 0) {
for (const attachment of attachments) {
const attachmentQuery = `
INSERT INTO regulatory_task_completion_attachments (
completion_id, file_name, file_path, file_size, file_type, upload_time
) VALUES (?, ?, ?, ?, ?, NOW())
`;
await sequelize.query(attachmentQuery, {
replacements: [
completionId,
attachment.file_name,
attachment.file_path,
attachment.file_size,
attachment.file_type
],
type: sequelize.QueryTypes.INSERT,
transaction
});
}
}
// 记录操作日志
const logQuery = `
INSERT INTO regulatory_task_completion_logs (
completion_id, operation_type, operation_description,
operator_name, operation_time, ip_address
) VALUES (?, ?, ?, ?, NOW(), ?)
`;
await sequelize.query(logQuery, {
replacements: [
completionId,
'create',
'创建监管任务结项记录',
req.user?.real_name || '系统',
req.ip
],
type: sequelize.QueryTypes.INSERT,
transaction
});
await transaction.commit();
res.status(201).json(responseFormat.success({
id: completionId,
message: '监管任务结项记录创建成功'
}));
} catch (error) {
await transaction.rollback();
console.error('创建监管任务结项记录失败:', error);
res.status(500).json(responseFormat.error('创建监管任务结项记录失败'));
}
};
// 更新监管任务结项记录
const updateTaskCompletion = async (req, res) => {
const transaction = await sequelize.transaction();
try {
const { id } = req.params;
const {
application_no,
policy_no,
product_name,
customer_name,
customer_phone,
insurance_amount,
premium_amount,
start_date,
end_date,
completion_date,
status,
review_comments
} = req.body;
// 检查记录是否存在
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
const [existing] = await sequelize.query(checkQuery, {
replacements: [id],
type: sequelize.QueryTypes.SELECT,
transaction
});
if (!existing) {
await transaction.rollback();
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
}
// 更新记录
const updateQuery = `
UPDATE regulatory_task_completions SET
application_no = ?, policy_no = ?, product_name = ?, customer_name = ?,
customer_phone = ?, insurance_amount = ?, premium_amount = ?,
start_date = ?, end_date = ?, completion_date = ?, status = ?,
review_comments = ?, updated_at = NOW()
WHERE id = ?
`;
await sequelize.query(updateQuery, {
replacements: [
application_no, policy_no, product_name, customer_name, customer_phone,
insurance_amount, premium_amount, start_date, end_date, completion_date,
status, review_comments, id
],
type: sequelize.QueryTypes.UPDATE,
transaction
});
// 记录操作日志
const logQuery = `
INSERT INTO regulatory_task_completion_logs (
completion_id, operation_type, operation_description,
operator_name, operation_time, ip_address
) VALUES (?, ?, ?, ?, NOW(), ?)
`;
await sequelize.query(logQuery, {
replacements: [
id,
'update',
'更新监管任务结项记录',
req.user?.real_name || '系统',
req.ip
],
type: sequelize.QueryTypes.INSERT,
transaction
});
await transaction.commit();
res.json(responseFormat.success({ message: '监管任务结项记录更新成功' }));
} catch (error) {
await transaction.rollback();
console.error('更新监管任务结项记录失败:', error);
res.status(500).json(responseFormat.error('更新监管任务结项记录失败'));
}
};
// 删除监管任务结项记录
const deleteTaskCompletion = async (req, res) => {
const transaction = await sequelize.transaction();
try {
const { id } = req.params;
// 检查记录是否存在
const checkQuery = `SELECT id FROM regulatory_task_completions WHERE id = ?`;
const [existing] = await sequelize.query(checkQuery, {
replacements: [id],
type: sequelize.QueryTypes.SELECT,
transaction
});
if (!existing) {
await transaction.rollback();
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
}
// 删除相关附件记录
await sequelize.query(
'DELETE FROM regulatory_task_completion_attachments WHERE completion_id = ?',
{
replacements: [id],
type: sequelize.QueryTypes.DELETE,
transaction
}
);
// 删除相关日志记录
await sequelize.query(
'DELETE FROM regulatory_task_completion_logs WHERE completion_id = ?',
{
replacements: [id],
type: sequelize.QueryTypes.DELETE,
transaction
}
);
// 删除主记录
await sequelize.query(
'DELETE FROM regulatory_task_completions WHERE id = ?',
{
replacements: [id],
type: sequelize.QueryTypes.DELETE,
transaction
}
);
await transaction.commit();
res.json(responseFormat.success({ message: '监管任务结项记录删除成功' }));
} catch (error) {
await transaction.rollback();
console.error('删除监管任务结项记录失败:', error);
res.status(500).json(responseFormat.error('删除监管任务结项记录失败'));
}
};
// 审核监管任务结项记录
const reviewTaskCompletion = async (req, res) => {
const transaction = await sequelize.transaction();
try {
const { id } = req.params;
const { status, review_comments, reviewer_name } = req.body;
// 验证状态值
const validStatuses = ['pending', 'approved', 'rejected', 'completed'];
if (!validStatuses.includes(status)) {
return res.status(400).json(responseFormat.error('无效的状态值'));
}
// 检查记录是否存在
const checkQuery = `SELECT id, status FROM regulatory_task_completions WHERE id = ?`;
const [existing] = await sequelize.query(checkQuery, {
replacements: [id],
type: sequelize.QueryTypes.SELECT,
transaction
});
if (!existing) {
await transaction.rollback();
return res.status(404).json(responseFormat.error('监管任务结项记录不存在'));
}
// 更新审核状态
const updateQuery = `
UPDATE regulatory_task_completions SET
status = ?, review_comments = ?, reviewer_name = ?, updated_at = NOW()
WHERE id = ?
`;
await sequelize.query(updateQuery, {
replacements: [status, review_comments, reviewer_name, id],
type: sequelize.QueryTypes.UPDATE,
transaction
});
// 记录操作日志
const logQuery = `
INSERT INTO regulatory_task_completion_logs (
completion_id, operation_type, operation_description,
operator_name, operation_time, ip_address
) VALUES (?, ?, ?, ?, NOW(), ?)
`;
const operationDesc = `审核监管任务结项记录,状态变更为:${status}`;
await sequelize.query(logQuery, {
replacements: [
id,
'review',
operationDesc,
reviewer_name || req.user?.real_name || '系统',
req.ip
],
type: sequelize.QueryTypes.INSERT,
transaction
});
await transaction.commit();
res.json(responseFormat.success({ message: '监管任务结项记录审核成功' }));
} catch (error) {
await transaction.rollback();
console.error('审核监管任务结项记录失败:', error);
res.status(500).json(responseFormat.error('审核监管任务结项记录失败'));
}
};
// 获取监管任务结项统计数据
const getTaskCompletionStats = async (req, res) => {
try {
// 状态统计
const statusStatsQuery = `
SELECT
status,
COUNT(*) as count
FROM regulatory_task_completions
GROUP BY status
`;
const statusStats = await sequelize.query(statusStatsQuery, {
type: sequelize.QueryTypes.SELECT
});
// 月度统计
const monthlyStatsQuery = `
SELECT
DATE_FORMAT(completion_date, '%Y-%m') as month,
COUNT(*) as count,
SUM(insurance_amount) as total_amount
FROM regulatory_task_completions
WHERE completion_date >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(completion_date, '%Y-%m')
ORDER BY month
`;
const monthlyStats = await sequelize.query(monthlyStatsQuery, {
type: sequelize.QueryTypes.SELECT
});
// 产品统计
const productStatsQuery = `
SELECT
product_name,
COUNT(*) as count,
SUM(insurance_amount) as total_amount
FROM regulatory_task_completions
GROUP BY product_name
ORDER BY count DESC
LIMIT 10
`;
const productStats = await sequelize.query(productStatsQuery, {
type: sequelize.QueryTypes.SELECT
});
res.json(responseFormat.success({
statusStats,
monthlyStats,
productStats
}));
} catch (error) {
console.error('获取监管任务结项统计数据失败:', error);
res.status(500).json(responseFormat.error('获取监管任务结项统计数据失败'));
}
};
module.exports = {
getTaskCompletions,
getTaskCompletionById,
createTaskCompletion,
updateTaskCompletion,
deleteTaskCompletion,
reviewTaskCompletion,
getTaskCompletionStats
};

View File

@@ -1,459 +0,0 @@
const { Role, Permission, RolePermission, User } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
/**
* 角色权限管理控制器
* 专门处理角色权限的分配、管理和动态调用
*/
class RolePermissionController {
/**
* 获取所有角色及其权限
*/
async getAllRolesWithPermissions(req, res) {
try {
const roles = await Role.findAll({
order: [['id', 'ASC']]
});
const rolesData = await Promise.all(roles.map(async (role) => {
// 从RolePermission表获取权限
const rolePermissions = await RolePermission.findAll({
where: {
role_id: role.id,
granted: true
},
include: [{
model: Permission,
as: 'permission',
attributes: ['id', 'name', 'code', 'description', 'module', 'type']
}]
});
const permissions = rolePermissions.map(rp => rp.permission.code);
return {
id: role.id,
name: role.name,
description: role.description,
status: role.status,
permissions: permissions,
permissionCount: permissions.length
};
}));
res.json(responseFormat.success({
roles: rolesData,
total: rolesData.length
}, '获取角色权限列表成功'));
} catch (error) {
console.error('获取角色权限列表失败:', error);
res.status(500).json(responseFormat.error('获取角色权限列表失败'));
}
}
/**
* 获取所有权限
*/
async getAllPermissions(req, res) {
try {
const permissions = await Permission.findAll({
order: [['id', 'ASC']]
});
res.json(responseFormat.success(permissions, '获取权限列表成功'));
} catch (error) {
console.error('获取权限列表失败:', error);
res.status(500).json(responseFormat.error('获取权限列表失败'));
}
}
/**
* 获取指定角色的权限详情
*/
async getRolePermissionDetail(req, res) {
try {
const { roleId } = req.params;
console.log('获取角色权限详情角色ID:', roleId);
const role = await Role.findByPk(roleId);
console.log('角色查询结果:', role ? role.name : '未找到');
if (!role) {
return res.status(404).json(responseFormat.error('角色不存在'));
}
// 获取所有权限用于对比
const allPermissions = await Permission.findAll({
attributes: ['id', 'name', 'code', 'description', 'module', 'type', 'parent_id'],
order: [['module', 'ASC'], ['id', 'ASC']]
});
console.log('权限查询结果:', allPermissions.length, '个权限');
// 从RolePermission表获取角色已分配的权限
const rolePermissions = await RolePermission.findAll({
where: {
role_id: roleId,
granted: true
},
include: [{
model: Permission,
as: 'permission',
attributes: ['id', 'name', 'code', 'description', 'module', 'type']
}]
});
const assignedPermissionCodes = rolePermissions.map(rp => rp.permission.code);
console.log('已分配权限代码:', assignedPermissionCodes.length, '个');
// 暂时返回简化数据,不构建权限树
res.json(responseFormat.success({
role: {
id: role.id,
name: role.name,
description: role.description,
status: role.status
},
assignedPermissions: assignedPermissionCodes,
allPermissions: allPermissions.map(p => ({
id: p.id,
name: p.name,
code: p.code,
description: p.description,
module: p.module,
type: p.type,
parent_id: p.parent_id,
assigned: assignedPermissionCodes.includes(p.code)
})),
assignedCount: assignedPermissionCodes.length,
totalCount: allPermissions.length
}, '获取角色权限详情成功'));
} catch (error) {
console.error('获取角色权限详情失败:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json(responseFormat.error('获取角色权限详情失败'));
}
}
/**
* 批量分配角色权限
*/
async batchAssignPermissions(req, res) {
try {
const { roleId } = req.params;
const { permissionIds, operation = 'replace' } = req.body;
console.log('=== 批量分配权限开始 ===');
console.log('角色ID:', roleId);
console.log('权限ID列表:', permissionIds);
console.log('操作类型:', operation);
console.log('请求体:', JSON.stringify(req.body, null, 2));
if (!Array.isArray(permissionIds)) {
console.log('❌ 权限ID列表格式错误');
return res.status(400).json(responseFormat.error('权限ID列表格式错误'));
}
const role = await Role.findByPk(roleId);
if (!role) {
console.log('❌ 角色不存在');
return res.status(404).json(responseFormat.error('角色不存在'));
}
console.log('找到角色:', role.name);
// 验证权限ID是否存在
const validPermissions = await Permission.findAll({
where: { id: { [Op.in]: permissionIds } },
attributes: ['id']
});
const validPermissionIds = validPermissions.map(p => p.id);
const invalidIds = permissionIds.filter(id => !validPermissionIds.includes(id));
console.log('有效权限ID:', validPermissionIds);
console.log('无效权限ID:', invalidIds);
if (invalidIds.length > 0) {
console.log('❌ 存在无效的权限ID');
return res.status(400).json(responseFormat.error(`无效的权限ID: ${invalidIds.join(', ')}`));
}
// 根据操作类型处理权限分配
if (operation === 'replace') {
console.log('执行替换模式权限分配');
// 替换模式:删除现有权限,添加新权限
const deletedCount = await RolePermission.destroy({ where: { role_id: roleId } });
console.log('删除现有权限数量:', deletedCount);
if (permissionIds.length > 0) {
const rolePermissions = permissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
console.log('准备创建的权限记录:', rolePermissions);
const createdPermissions = await RolePermission.bulkCreate(rolePermissions);
console.log('成功创建的权限记录数量:', createdPermissions.length);
}
} else if (operation === 'add') {
// 添加模式:只添加新权限
const existingPermissions = await RolePermission.findAll({
where: { role_id: roleId },
attributes: ['permission_id']
});
const existingIds = existingPermissions.map(p => p.permission_id);
const newPermissionIds = permissionIds.filter(id => !existingIds.includes(id));
if (newPermissionIds.length > 0) {
const rolePermissions = newPermissionIds.map(permissionId => ({
role_id: roleId,
permission_id: permissionId,
granted: true
}));
await RolePermission.bulkCreate(rolePermissions);
}
} else if (operation === 'remove') {
// 移除模式:删除指定权限
await RolePermission.destroy({
where: {
role_id: roleId,
permission_id: { [Op.in]: permissionIds }
}
});
}
console.log('✅ 权限分配完成');
res.json(responseFormat.success(null, `${operation === 'replace' ? '替换' : operation === 'add' ? '添加' : '移除'}角色权限成功`));
} catch (error) {
console.error('❌ 批量分配权限失败:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json(responseFormat.error('批量分配角色权限失败'));
}
console.log('=== 批量分配权限结束 ===');
}
/**
* 复制角色权限
*/
async copyRolePermissions(req, res) {
try {
const { sourceRoleId, targetRoleId } = req.body;
if (!sourceRoleId || !targetRoleId) {
return res.status(400).json(responseFormat.error('源角色ID和目标角色ID不能为空'));
}
if (sourceRoleId === targetRoleId) {
return res.status(400).json(responseFormat.error('源角色和目标角色不能相同'));
}
// 验证角色存在
const [sourceRole, targetRole] = await Promise.all([
Role.findByPk(sourceRoleId),
Role.findByPk(targetRoleId)
]);
if (!sourceRole) {
return res.status(404).json(responseFormat.error('源角色不存在'));
}
if (!targetRole) {
return res.status(404).json(responseFormat.error('目标角色不存在'));
}
// 获取源角色的权限
const sourcePermissions = await RolePermission.findAll({
where: { role_id: sourceRoleId },
attributes: ['permission_id']
});
// 删除目标角色现有权限
await RolePermission.destroy({ where: { role_id: targetRoleId } });
// 复制权限到目标角色
if (sourcePermissions.length > 0) {
const targetPermissions = sourcePermissions.map(p => ({
role_id: targetRoleId,
permission_id: p.permission_id,
granted: true
}));
await RolePermission.bulkCreate(targetPermissions);
}
res.json(responseFormat.success(null, `成功将 ${sourceRole.name} 的权限复制到 ${targetRole.name}`));
} catch (error) {
console.error('复制角色权限失败:', error);
res.status(500).json(responseFormat.error('复制角色权限失败'));
}
}
/**
* 检查用户权限
*/
async checkUserPermission(req, res) {
try {
const { userId, permissionCode } = req.params;
const user = await User.findByPk(userId, {
include: [
{
model: Role,
as: 'role',
include: [
{
model: Permission,
as: 'rolePermissions',
where: { code: permissionCode },
required: false,
through: {
attributes: ['granted']
}
}
]
}
]
});
if (!user) {
return res.status(404).json(responseFormat.error('用户不存在'));
}
const hasPermission = user.role &&
user.role.rolePermissions &&
user.role.rolePermissions.length > 0 &&
user.role.rolePermissions[0].RolePermission.granted;
res.json(responseFormat.success({
userId: user.id,
username: user.username,
roleName: user.role ? user.role.name : null,
permissionCode,
hasPermission,
checkTime: new Date()
}, '权限检查完成'));
} catch (error) {
console.error('检查用户权限失败:', error);
res.status(500).json(responseFormat.error('检查用户权限失败'));
}
}
/**
* 获取权限统计信息
*/
async getPermissionStats(req, res) {
try {
// 统计各种数据
const [
totalRoles,
totalPermissions,
moduleStats,
roles
] = await Promise.all([
Role.count(),
Permission.count(),
Permission.findAll({
attributes: [
'module',
[Permission.sequelize.fn('COUNT', Permission.sequelize.col('id')), 'count']
],
group: ['module'],
order: [['module', 'ASC']]
}),
Role.findAll({
attributes: ['id', 'name', 'permissions'],
order: [['name', 'ASC']]
})
]);
// 计算总分配数和角色权限分布
let totalAssignments = 0;
const roleDistribution = roles.map(role => {
let permissions = [];
if (Array.isArray(role.permissions)) {
permissions = role.permissions;
} else if (typeof role.permissions === 'string') {
try {
permissions = JSON.parse(role.permissions);
} catch (e) {
permissions = [];
}
}
const permissionCount = permissions.length;
totalAssignments += permissionCount;
return {
roleId: role.id,
roleName: role.name,
permissionCount
};
});
res.json(responseFormat.success({
overview: {
totalRoles,
totalPermissions,
totalAssignments,
averagePermissionsPerRole: totalRoles > 0 ? Math.round(totalAssignments / totalRoles) : 0
},
moduleDistribution: moduleStats.map(stat => ({
module: stat.module,
count: parseInt(stat.dataValues.count)
})),
roleDistribution
}, '获取权限统计成功'));
} catch (error) {
console.error('获取权限统计失败:', error);
res.status(500).json(responseFormat.error('获取权限统计失败'));
}
}
/**
* 构建权限树
*/
buildPermissionTree(permissions, parentId = null) {
const tree = [];
for (const permission of permissions) {
if (permission.parent_id === parentId) {
const children = this.buildPermissionTree(permissions, permission.id);
const node = {
...(permission.toJSON ? permission.toJSON() : permission),
children: children.length > 0 ? children : undefined
};
tree.push(node);
}
}
return tree;
}
/**
* 标记已分配的权限
*/
markAssignedPermissions(permissions, assignedIds) {
return permissions.map(permission => ({
...permission,
assigned: assignedIds.includes(permission.id),
children: permission.children ?
this.markAssignedPermissions(permission.children, assignedIds) :
undefined
}));
}
/**
* 根据权限代码标记已分配的权限
*/
markAssignedPermissionsByCode(permissions, assignedCodes) {
return permissions.map(permission => ({
...permission,
assigned: assignedCodes.includes(permission.code),
children: permission.children ?
this.markAssignedPermissionsByCode(permission.children, assignedCodes) :
undefined
}));
}
}
module.exports = new RolePermissionController();

View File

@@ -1,316 +0,0 @@
const { User, Role, InsuranceApplication, Policy, Claim } = require('../models');
const responseFormat = require('../utils/response');
const { Op } = require('sequelize');
// 获取系统统计信息
const getSystemStats = async (req, res) => {
try {
const [
totalUsers,
totalRoles,
totalApplications,
totalPolicies,
totalClaims,
activeUsers,
pendingApplications,
approvedApplications,
activePolicies,
pendingClaims,
approvedClaims
] = await Promise.all([
User.count(),
Role.count(),
InsuranceApplication.count(),
Policy.count(),
Claim.count(),
User.count({ where: { status: 'active' } }),
InsuranceApplication.count({ where: { status: 'pending' } }),
InsuranceApplication.count({ where: { status: 'approved' } }),
Policy.count({ where: { policy_status: 'active' } }),
Claim.count({ where: { claim_status: 'pending' } }),
Claim.count({ where: { claim_status: 'approved' } })
]);
// 获取最近7天的数据趋势
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const recentStats = await Promise.all([
User.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
InsuranceApplication.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
Policy.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } }),
Claim.count({ where: { created_at: { [Op.gte]: sevenDaysAgo } } })
]);
res.json(responseFormat.success({
overview: {
users: totalUsers,
roles: totalRoles,
applications: totalApplications,
policies: totalPolicies,
claims: totalClaims
},
status: {
active_users: activeUsers,
pending_applications: pendingApplications,
approved_applications: approvedApplications,
active_policies: activePolicies,
pending_claims: pendingClaims,
approved_claims: approvedClaims
},
recent: {
new_users: recentStats[0],
new_applications: recentStats[1],
new_policies: recentStats[2],
new_claims: recentStats[3]
}
}, '获取系统统计信息成功'));
} catch (error) {
console.error('获取系统统计信息错误:', error);
res.status(500).json(responseFormat.error('获取系统统计信息失败'));
}
};
// 获取系统日志(模拟)
const getSystemLogs = async (req, res) => {
try {
const { page = 1, limit = 50, level, start_date, end_date } = req.query;
const offset = (page - 1) * limit;
// 模拟日志数据 - 扩展更多有意义的日志记录
const mockLogs = [
{
id: 1,
level: 'info',
message: '系统启动成功',
timestamp: new Date().toISOString(),
user: 'system'
},
{
id: 2,
level: 'info',
message: '数据库连接成功',
timestamp: new Date(Date.now() - 1000 * 60).toISOString(),
user: 'system'
},
{
id: 3,
level: 'warning',
message: 'Redis连接失败使用内存缓存',
timestamp: new Date(Date.now() - 1000 * 120).toISOString(),
user: 'system'
},
{
id: 4,
level: 'info',
message: '用户 admin 登录成功',
timestamp: new Date(Date.now() - 1000 * 180).toISOString(),
user: 'admin'
},
{
id: 5,
level: 'info',
message: '新增保险申请:车险申请 - 申请人:张三',
timestamp: new Date(Date.now() - 1000 * 240).toISOString(),
user: 'zhangsan'
},
{
id: 6,
level: 'info',
message: '保单生效:保单号 POL-2024-001 - 投保人:李四',
timestamp: new Date(Date.now() - 1000 * 300).toISOString(),
user: 'lisi'
},
{
id: 7,
level: 'warning',
message: '理赔申请待审核:理赔号 CLM-2024-001 - 申请人:王五',
timestamp: new Date(Date.now() - 1000 * 360).toISOString(),
user: 'wangwu'
},
{
id: 8,
level: 'info',
message: '新用户注册:用户名 zhaoliu',
timestamp: new Date(Date.now() - 1000 * 420).toISOString(),
user: 'system'
},
{
id: 9,
level: 'error',
message: '支付接口调用失败:订单号 ORD-2024-001',
timestamp: new Date(Date.now() - 1000 * 480).toISOString(),
user: 'system'
},
{
id: 10,
level: 'info',
message: '保险类型更新:新增意外险产品',
timestamp: new Date(Date.now() - 1000 * 540).toISOString(),
user: 'admin'
},
{
id: 11,
level: 'info',
message: '系统备份完成:数据库备份成功',
timestamp: new Date(Date.now() - 1000 * 600).toISOString(),
user: 'system'
},
{
id: 12,
level: 'warning',
message: '磁盘空间不足警告:剩余空间 15%',
timestamp: new Date(Date.now() - 1000 * 660).toISOString(),
user: 'system'
},
{
id: 13,
level: 'info',
message: '理赔审核通过:理赔号 CLM-2024-002 - 赔付金额 ¥5000',
timestamp: new Date(Date.now() - 1000 * 720).toISOString(),
user: 'admin'
},
{
id: 14,
level: 'info',
message: '保单续费成功:保单号 POL-2024-002 - 续费期限 1年',
timestamp: new Date(Date.now() - 1000 * 780).toISOString(),
user: 'system'
},
{
id: 15,
level: 'error',
message: '短信发送失败:手机号 138****8888',
timestamp: new Date(Date.now() - 1000 * 840).toISOString(),
user: 'system'
}
];
// 简单的过滤逻辑
let filteredLogs = mockLogs;
if (level) {
filteredLogs = filteredLogs.filter(log => log.level === level);
}
if (start_date) {
const startDate = new Date(start_date);
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) >= startDate);
}
if (end_date) {
const endDate = new Date(end_date);
filteredLogs = filteredLogs.filter(log => new Date(log.timestamp) <= endDate);
}
// 分页
const paginatedLogs = filteredLogs.slice(offset, offset + parseInt(limit));
res.json(responseFormat.success({
logs: paginatedLogs,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: filteredLogs.length,
pages: Math.ceil(filteredLogs.length / parseInt(limit))
}
}, '获取系统日志成功'));
} catch (error) {
console.error('获取系统日志错误:', error);
res.status(500).json(responseFormat.error('获取系统日志失败'));
}
};
// 获取系统配置(模拟)
const getSystemConfig = async (req, res) => {
try {
const config = {
site_name: '保险端口管理系统',
version: '1.0.0',
environment: process.env.NODE_ENV || 'development',
max_file_size: '10MB',
allowed_file_types: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'],
session_timeout: 3600,
backup_enabled: true,
backup_schedule: '0 2 * * *', // 每天凌晨2点
email_notifications: true,
sms_notifications: false,
maintenance_mode: false
};
res.json(responseFormat.success(config, '获取系统配置成功'));
} catch (error) {
console.error('获取系统配置错误:', error);
res.status(500).json(responseFormat.error('获取系统配置失败'));
}
};
// 更新系统配置(模拟)
const updateSystemConfig = async (req, res) => {
try {
const { maintenance_mode, email_notifications, sms_notifications } = req.body;
// 模拟更新配置
const updatedConfig = {
maintenance_mode: maintenance_mode !== undefined ? maintenance_mode : false,
email_notifications: email_notifications !== undefined ? email_notifications : true,
sms_notifications: sms_notifications !== undefined ? sms_notifications : false,
updated_at: new Date().toISOString()
};
res.json(responseFormat.success(updatedConfig, '系统配置更新成功'));
} catch (error) {
console.error('更新系统配置错误:', error);
res.status(500).json(responseFormat.error('更新系统配置失败'));
}
};
// 备份数据库(模拟)
const backupDatabase = async (req, res) => {
try {
// 模拟备份过程
const backupInfo = {
id: `backup_${Date.now()}`,
filename: `backup_${new Date().toISOString().replace(/:/g, '-')}.sql`,
size: '2.5MB',
status: 'completed',
created_at: new Date().toISOString(),
download_url: '/api/system/backup/download/backup.sql'
};
res.json(responseFormat.success(backupInfo, '数据库备份成功'));
} catch (error) {
console.error('数据库备份错误:', error);
res.status(500).json(responseFormat.error('数据库备份失败'));
}
};
// 恢复数据库(模拟)
const restoreDatabase = async (req, res) => {
try {
const { backup_id } = req.body;
if (!backup_id) {
return res.status(400).json(responseFormat.error('备份ID不能为空'));
}
// 模拟恢复过程
const restoreInfo = {
backup_id,
status: 'completed',
restored_at: new Date().toISOString(),
message: '数据库恢复成功'
};
res.json(responseFormat.success(restoreInfo, '数据库恢复成功'));
} catch (error) {
console.error('数据库恢复错误:', error);
res.status(500).json(responseFormat.error('数据库恢复失败'));
}
};
module.exports = {
getSystemStats,
getSystemLogs,
getSystemConfig,
updateSystemConfig,
backupDatabase,
restoreDatabase
};

View File

@@ -453,8 +453,8 @@ components:
example: "针对牛羊等大型牲畜的综合保险产品"
applicable_livestock:
type: string
description: 适用牲畜类型
example: "牛、羊"
description: 适用牲畜类型,多个类型用逗号分隔。前端可发送数组,后端会自动转换为字符串存储
example: "牛,羊,猪"
maxLength: 100
insurance_term:
type: integer
@@ -530,8 +530,8 @@ components:
example: "针对牛羊等大型牲畜的综合保险产品"
applicable_livestock:
type: string
description: 适用牲畜类型
example: "牛、羊"
description: 适用牲畜类型,多个类型用逗号分隔。前端可发送数组,后端会自动转换为字符串存储
example: "牛,羊,猪"
maxLength: 100
insurance_term:
type: integer

View File

@@ -1,212 +0,0 @@
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const Role = require('../models/Role');
const responseFormat = require('../utils/response');
// JWT认证中间件
const jwtAuth = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
console.log('Authorization header:', authHeader);
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json(responseFormat.error('未提供有效的认证token'));
}
const token = authHeader.substring(7);
console.log('提取的token:', token);
console.log('token类型:', typeof token);
console.log('token长度:', token.length);
// 首先尝试固定token验证
const user = await User.findOne({
where: {
fixed_token: token,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (user) {
// 固定token验证成功
req.user = {
id: user.id,
userId: user.id,
username: user.username,
role_id: user.role_id,
role: user.role,
permissions: user.role ? user.role.permissions : [],
type: 'fixed_token'
};
return next();
}
// 如果固定token验证失败尝试JWT验证
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (decoded.type !== 'access') {
return res.status(401).json(responseFormat.error('Token类型错误'));
}
// 验证用户是否存在且状态正常
const jwtUser = await User.findOne({
where: {
id: decoded.id,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (!jwtUser) {
return res.status(401).json(responseFormat.error('用户不存在或已被禁用'));
}
req.user = {
id: decoded.id,
userId: decoded.id,
username: decoded.username,
role_id: decoded.role_id,
role: jwtUser.role,
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : []),
type: 'jwt'
};
return next();
} catch (jwtError) {
if (jwtError.name === 'TokenExpiredError') {
return res.status(401).json(responseFormat.error('Token已过期', 'TOKEN_EXPIRED'));
} else if (jwtError.name === 'JsonWebTokenError') {
return res.status(401).json(responseFormat.error('Token无效'));
}
throw jwtError;
}
} catch (error) {
console.error('Token验证错误:', error);
return res.status(500).json(responseFormat.error('服务器内部错误'));
}
};
// 权限检查中间件
const checkPermission = (resource, action) => {
return async (req, res, next) => {
try {
console.log(`权限检查 - 资源: ${resource}, 操作: ${action}, 用户:`, req.user);
const user = req.user;
if (!user || !user.role_id) {
console.log('权限检查失败 - 用户角色信息缺失');
return res.status(403).json(responseFormat.error('用户角色信息缺失'));
}
let permissions = [];
// 优先使用JWT中的权限信息
if (user.permissions) {
if (typeof user.permissions === 'string') {
try {
permissions = JSON.parse(user.permissions);
} catch (e) {
console.log('JWT权限解析失败:', e.message);
permissions = [];
}
} else if (Array.isArray(user.permissions)) {
permissions = user.permissions;
}
}
const requiredPermission = `${resource}:${action}`;
// 首先检查JWT中的权限
let hasPermission = permissions.includes(requiredPermission) ||
permissions.includes('*:*') ||
permissions.includes('*');
// 如果JWT中没有权限信息或者JWT权限不足从数据库查询最新权限
if (permissions.length === 0 || !hasPermission) {
console.log('JWT权限不足或为空从数据库获取最新权限...');
const { Role, RolePermission, Permission } = require('../models');
const userRole = await Role.findByPk(user.role_id);
if (!userRole) {
console.log('权限检查失败 - 用户角色不存在, role_id:', user.role_id);
return res.status(403).json(responseFormat.error('用户角色不存在'));
}
// 从RolePermission表获取权限
const rolePermissions = await RolePermission.findAll({
where: {
role_id: user.role_id,
granted: true
},
include: [{
model: Permission,
as: 'permission',
attributes: ['code']
}]
});
permissions = rolePermissions.map(rp => rp.permission.code);
console.log('从RolePermission表获取的最新权限:', permissions);
// 重新检查权限
hasPermission = permissions.includes(requiredPermission) ||
permissions.includes('*:*') ||
permissions.includes('*');
}
console.log('权限检查 - 用户权限:', permissions, '需要权限:', requiredPermission, '是否有权限:', hasPermission);
// 检查权限或超级管理员权限
if (!hasPermission) {
console.log('权限检查失败 - 权限不足');
return res.status(403).json(responseFormat.error('权限不足'));
}
console.log('权限检查通过');
next();
} catch (error) {
return res.status(500).json(responseFormat.error('权限验证失败'));
}
};
};
// 可选认证中间件(不强制要求认证)
const optionalAuth = (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (token) {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
} catch (error) {
// 令牌无效,但不阻止请求
console.warn('可选认证令牌无效:', error.message);
}
}
next();
};
// 别名导出以匹配路由中的使用
const authenticateToken = jwtAuth;
const requirePermission = (permission) => {
const [resource, action] = permission.split(':');
return checkPermission(resource, action);
};
module.exports = {
jwtAuth,
checkPermission,
optionalAuth,
authenticateToken,
requirePermission
};

View File

@@ -1,119 +0,0 @@
const User = require('../models/User');
const Role = require('../models/Role');
/**
* 固定Token认证中间件
* 支持JWT token和固定token两种认证方式
*/
const fixedTokenAuth = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({
status: 'error',
message: '未提供认证token'
});
}
// 检查是否为Bearer token格式
if (authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7);
// 首先尝试固定token验证
const user = await User.findOne({
where: {
fixed_token: token,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (user) {
// 固定token验证成功
req.user = {
id: user.id,
username: user.username,
role_id: user.role_id,
role: user.role,
permissions: user.role ? user.role.permissions : []
};
return next();
}
// 如果固定token验证失败尝试JWT验证
const jwt = require('jsonwebtoken');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (decoded.type !== 'access') {
return res.status(401).json({
status: 'error',
message: 'Token类型错误'
});
}
// 验证用户是否存在且状态正常
const jwtUser = await User.findOne({
where: {
id: decoded.userId,
status: 'active'
},
include: [{
model: Role,
as: 'role',
attributes: ['id', 'name', 'permissions']
}]
});
if (!jwtUser) {
return res.status(401).json({
status: 'error',
message: '用户不存在或已被禁用'
});
}
req.user = {
id: jwtUser.id,
username: jwtUser.username,
role_id: jwtUser.role_id,
role: jwtUser.role,
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : [])
};
return next();
} catch (jwtError) {
if (jwtError.name === 'TokenExpiredError') {
return res.status(401).json({
status: 'error',
code: 'TOKEN_EXPIRED',
message: 'Token已过期'
});
} else if (jwtError.name === 'JsonWebTokenError') {
return res.status(401).json({
status: 'error',
message: 'Token无效'
});
}
throw jwtError;
}
} else {
return res.status(401).json({
status: 'error',
message: 'Token格式错误请使用Bearer格式'
});
}
} catch (error) {
console.error('Token验证错误:', error);
return res.status(500).json({
status: 'error',
message: '服务器内部错误'
});
}
};
module.exports = fixedTokenAuth;

View File

@@ -1,302 +0,0 @@
const { OperationLog } = require('../models');
/**
* 操作日志记录中间件
* 自动记录用户的操作行为
*/
class OperationLogger {
/**
* 记录操作日志的中间件
*/
static logOperation(options = {}) {
return async (req, res, next) => {
const startTime = Date.now();
// 保存原始的res.json方法
const originalJson = res.json;
// 重写res.json方法以捕获响应数据
res.json = function(data) {
const endTime = Date.now();
const executionTime = endTime - startTime;
// 异步记录操作日志,不阻塞响应
setImmediate(async () => {
try {
await OperationLogger.recordLog(req, res, data, executionTime, options);
} catch (error) {
console.error('记录操作日志失败:', error);
}
});
// 调用原始的json方法
return originalJson.call(this, data);
};
next();
};
}
/**
* 记录操作日志
*/
static async recordLog(req, res, responseData, executionTime, options) {
try {
// 如果用户未登录,不记录日志
if (!req.user || !req.user.id) {
return;
}
// 获取操作类型
const operationType = OperationLogger.getOperationType(req.method, req.url, options);
// 获取操作模块
const operationModule = OperationLogger.getOperationModule(req.url, options);
// 获取操作内容
const operationContent = OperationLogger.getOperationContent(req, operationType, operationModule, options);
// 获取操作目标
const operationTarget = OperationLogger.getOperationTarget(req, responseData, options);
// 获取操作状态
const status = OperationLogger.getOperationStatus(res.statusCode, responseData);
// 获取错误信息
const errorMessage = OperationLogger.getErrorMessage(responseData, status);
// 记录操作日志
await OperationLog.logOperation({
user_id: req.user.id,
operation_type: operationType,
operation_module: operationModule,
operation_content: operationContent,
operation_target: operationTarget,
request_method: req.method,
request_url: req.originalUrl || req.url,
request_params: {
query: req.query,
body: OperationLogger.sanitizeRequestBody(req.body),
params: req.params
},
response_status: res.statusCode,
response_data: OperationLogger.sanitizeResponseData(responseData),
ip_address: OperationLogger.getClientIP(req),
user_agent: req.get('User-Agent') || '',
execution_time: executionTime,
status: status,
error_message: errorMessage
});
} catch (error) {
console.error('记录操作日志时发生错误:', error);
}
}
/**
* 获取操作类型
*/
static getOperationType(method, url, options) {
if (options.operation_type) {
return options.operation_type;
}
// 根据URL和HTTP方法推断操作类型
if (url.includes('/login')) return 'login';
if (url.includes('/logout')) return 'logout';
if (url.includes('/export')) return 'export';
if (url.includes('/import')) return 'import';
if (url.includes('/approve')) return 'approve';
if (url.includes('/reject')) return 'reject';
switch (method.toUpperCase()) {
case 'GET':
return 'view';
case 'POST':
return 'create';
case 'PUT':
case 'PATCH':
return 'update';
case 'DELETE':
return 'delete';
default:
return 'other';
}
}
/**
* 获取操作模块
*/
static getOperationModule(url, options) {
if (options.operation_module) {
return options.operation_module;
}
// 从URL中提取模块名
const pathSegments = url.split('/').filter(segment => segment && segment !== 'api');
if (pathSegments.length > 0) {
return pathSegments[0].replace(/-/g, '_');
}
return 'unknown';
}
/**
* 获取操作内容
*/
static getOperationContent(req, operationType, operationModule, options) {
if (options.operation_content) {
return options.operation_content;
}
const actionMap = {
'login': '用户登录',
'logout': '用户退出',
'view': '查看',
'create': '创建',
'update': '更新',
'delete': '删除',
'export': '导出',
'import': '导入',
'approve': '审批通过',
'reject': '审批拒绝'
};
const moduleMap = {
'users': '用户',
'roles': '角色',
'insurance': '保险',
'policies': '保单',
'claims': '理赔',
'system': '系统',
'operation_logs': '操作日志',
'devices': '设备',
'device_alerts': '设备告警'
};
const action = actionMap[operationType] || operationType;
const module = moduleMap[operationModule] || operationModule;
return `${action}${module}`;
}
/**
* 获取操作目标
*/
static getOperationTarget(req, responseData, options) {
if (options.operation_target) {
return options.operation_target;
}
// 尝试从请求参数中获取ID
if (req.params.id) {
return `ID: ${req.params.id}`;
}
// 尝试从响应数据中获取信息
if (responseData && responseData.data) {
if (responseData.data.id) {
return `ID: ${responseData.data.id}`;
}
if (responseData.data.name) {
return `名称: ${responseData.data.name}`;
}
if (responseData.data.username) {
return `用户: ${responseData.data.username}`;
}
}
return '';
}
/**
* 获取操作状态
*/
static getOperationStatus(statusCode, responseData) {
if (statusCode >= 200 && statusCode < 300) {
return 'success';
} else if (statusCode >= 400 && statusCode < 500) {
return 'failed';
} else {
return 'error';
}
}
/**
* 获取错误信息
*/
static getErrorMessage(responseData, status) {
if (status === 'success') {
return null;
}
if (responseData && responseData.message) {
return responseData.message;
}
if (responseData && responseData.error) {
return responseData.error;
}
return null;
}
/**
* 清理请求体数据(移除敏感信息)
*/
static sanitizeRequestBody(body) {
if (!body || typeof body !== 'object') {
return body;
}
const sanitized = { ...body };
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth'];
sensitiveFields.forEach(field => {
if (sanitized[field]) {
sanitized[field] = '***';
}
});
return sanitized;
}
/**
* 清理响应数据(移除敏感信息)
*/
static sanitizeResponseData(data) {
if (!data || typeof data !== 'object') {
return data;
}
// 只保留基本的响应信息,不保存完整的响应数据
return {
status: data.status,
message: data.message,
code: data.code
};
}
/**
* 获取客户端IP地址
*/
static getClientIP(req) {
return req.ip ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
(req.connection.socket ? req.connection.socket.remoteAddress : null) ||
'127.0.0.1';
}
/**
* 创建特定操作的日志记录器
*/
static createLogger(operationType, operationModule, operationContent) {
return OperationLogger.logOperation({
operation_type: operationType,
operation_module: operationModule,
operation_content: operationContent
});
}
}
module.exports = OperationLogger;

View File

@@ -1,177 +0,0 @@
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

@@ -1,16 +0,0 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'fixed_token', {
type: Sequelize.STRING(255),
allowNull: true,
unique: true,
comment: '用户固定token用于API访问验证'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('users', 'fixed_token');
}
};

View File

@@ -1,146 +0,0 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('operation_logs', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
operation_type: {
type: Sequelize.ENUM(
'login', // 登录
'logout', // 登出
'create', // 创建
'update', // 更新
'delete', // 删除
'view', // 查看
'export', // 导出
'import', // 导入
'approve', // 审批
'reject', // 拒绝
'system_config', // 系统配置
'user_manage', // 用户管理
'role_manage', // 角色管理
'other' // 其他
),
allowNull: false,
comment: '操作类型'
},
operation_module: {
type: Sequelize.STRING(50),
allowNull: false,
comment: '操作模块(如:用户管理、设备管理、预警管理等)'
},
operation_content: {
type: Sequelize.TEXT,
allowNull: false,
comment: '操作内容描述'
},
operation_target: {
type: Sequelize.STRING(100),
allowNull: true,
comment: '操作目标用户ID、设备ID等'
},
request_method: {
type: Sequelize.ENUM('GET', 'POST', 'PUT', 'DELETE', 'PATCH'),
allowNull: true,
comment: 'HTTP请求方法'
},
request_url: {
type: Sequelize.STRING(500),
allowNull: true,
comment: '请求URL'
},
request_params: {
type: Sequelize.TEXT,
allowNull: true,
comment: '请求参数JSON格式'
},
response_status: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '响应状态码'
},
ip_address: {
type: Sequelize.STRING(45),
allowNull: true,
comment: 'IP地址支持IPv6'
},
user_agent: {
type: Sequelize.TEXT,
allowNull: true,
comment: '用户代理信息'
},
execution_time: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '执行时间(毫秒)'
},
status: {
type: Sequelize.ENUM('success', 'failed', 'error'),
defaultValue: 'success',
comment: '操作状态'
},
error_message: {
type: Sequelize.TEXT,
allowNull: true,
comment: '错误信息'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
}
}, {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '系统操作日志表'
});
// 创建索引
await queryInterface.addIndex('operation_logs', ['user_id'], {
name: 'idx_operation_logs_user_id'
});
await queryInterface.addIndex('operation_logs', ['operation_type'], {
name: 'idx_operation_logs_operation_type'
});
await queryInterface.addIndex('operation_logs', ['operation_module'], {
name: 'idx_operation_logs_operation_module'
});
await queryInterface.addIndex('operation_logs', ['created_at'], {
name: 'idx_operation_logs_created_at'
});
await queryInterface.addIndex('operation_logs', ['status'], {
name: 'idx_operation_logs_status'
});
await queryInterface.addIndex('operation_logs', ['ip_address'], {
name: 'idx_operation_logs_ip_address'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('operation_logs');
}
};

View File

@@ -1,181 +0,0 @@
/**
* 创建监管任务表的迁移文件
*/
'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

@@ -1,190 +0,0 @@
/**
* 创建待安装任务表的迁移文件
*/
'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

@@ -1,142 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Claim = sequelize.define('Claim', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '理赔ID'
},
claim_no: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '理赔编号',
validate: {
notEmpty: {
msg: '理赔编号不能为空'
}
}
},
policy_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '关联的保单ID',
references: {
model: 'policies',
key: 'id'
}
},
customer_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '客户ID',
references: {
model: 'users',
key: 'id'
}
},
claim_amount: {
type: DataTypes.DECIMAL(15, 2),
allowNull: false,
comment: '理赔金额',
validate: {
min: {
args: [0],
msg: '理赔金额不能小于0'
}
}
},
claim_date: {
type: DataTypes.DATE,
allowNull: false,
comment: '理赔发生日期'
},
incident_description: {
type: DataTypes.TEXT,
allowNull: false,
comment: '事故描述',
validate: {
notEmpty: {
msg: '事故描述不能为空'
}
}
},
claim_status: {
type: DataTypes.ENUM('pending', 'approved', 'rejected', 'processing', 'paid'),
allowNull: false,
defaultValue: 'pending',
comment: '理赔状态pending-待审核approved-已批准rejected-已拒绝processing-处理中paid-已支付'
},
review_notes: {
type: DataTypes.TEXT,
allowNull: true,
comment: '审核备注'
},
reviewer_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '审核人ID',
references: {
model: 'users',
key: 'id'
}
},
review_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '审核日期'
},
payment_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '支付日期'
},
supporting_documents: {
type: DataTypes.JSON,
allowNull: true,
comment: '支持文件JSON数组包含文件URL和描述'
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '创建人ID'
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '更新人ID'
}
}, {
tableName: 'claims',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
name: 'idx_claim_no',
fields: ['claim_no'],
unique: true
},
{
name: 'idx_claim_policy',
fields: ['policy_id']
},
{
name: 'idx_claim_customer',
fields: ['customer_id']
},
{
name: 'idx_claim_status',
fields: ['claim_status']
},
{
name: 'idx_claim_date',
fields: ['claim_date']
}
],
comment: '理赔表'
});
module.exports = Claim;

View File

@@ -1,86 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
/**
* 设备模型
* 用于管理保险相关的设备信息
*/
const Device = sequelize.define('Device', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '设备ID'
},
device_number: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '设备编号'
},
device_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '设备名称'
},
device_type: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '设备类型'
},
device_model: {
type: DataTypes.STRING(100),
comment: '设备型号'
},
manufacturer: {
type: DataTypes.STRING(100),
comment: '制造商'
},
installation_location: {
type: DataTypes.STRING(200),
comment: '安装位置'
},
installation_date: {
type: DataTypes.DATE,
comment: '安装日期'
},
status: {
type: DataTypes.ENUM('normal', 'warning', 'error', 'offline'),
defaultValue: 'normal',
comment: '设备状态normal-正常warning-警告error-故障offline-离线'
},
farm_id: {
type: DataTypes.INTEGER,
comment: '所属养殖场ID'
},
barn_id: {
type: DataTypes.INTEGER,
comment: '所属栏舍ID'
},
created_by: {
type: DataTypes.INTEGER,
comment: '创建人ID'
},
updated_by: {
type: DataTypes.INTEGER,
comment: '更新人ID'
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
comment: '创建时间'
},
updated_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
comment: '更新时间'
}
}, {
tableName: 'devices',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
comment: '设备信息表'
});
module.exports = Device;

View File

@@ -1,217 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const LivestockPolicy = sequelize.define('LivestockPolicy', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '生资保单ID'
},
policy_no: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '保单编号',
validate: {
notEmpty: {
msg: '保单编号不能为空'
}
}
},
livestock_type_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '牲畜类型ID',
references: {
model: 'livestock_types',
key: 'id'
}
},
policyholder_name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '投保人姓名',
validate: {
notEmpty: {
msg: '投保人姓名不能为空'
}
}
},
policyholder_id_card: {
type: DataTypes.STRING(18),
allowNull: false,
comment: '投保人身份证号',
validate: {
notEmpty: {
msg: '投保人身份证号不能为空'
}
}
},
policyholder_phone: {
type: DataTypes.STRING(20),
allowNull: false,
comment: '投保人手机号',
validate: {
notEmpty: {
msg: '投保人手机号不能为空'
}
}
},
policyholder_address: {
type: DataTypes.STRING(500),
allowNull: false,
comment: '投保人地址',
validate: {
notEmpty: {
msg: '投保人地址不能为空'
}
}
},
farm_name: {
type: DataTypes.STRING(200),
allowNull: true,
comment: '养殖场名称'
},
farm_address: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '养殖场地址'
},
farm_license: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '养殖场许可证号'
},
livestock_count: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '投保牲畜数量',
validate: {
min: {
args: [1],
msg: '投保牲畜数量不能小于1'
}
}
},
unit_value: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
comment: '单头价值',
validate: {
min: {
args: [0],
msg: '单头价值不能小于0'
}
}
},
total_value: {
type: DataTypes.DECIMAL(15, 2),
allowNull: false,
comment: '总保额',
validate: {
min: {
args: [0],
msg: '总保额不能小于0'
}
}
},
premium_amount: {
type: DataTypes.DECIMAL(15, 2),
allowNull: false,
comment: '保费金额',
validate: {
min: {
args: [0],
msg: '保费金额不能小于0'
}
}
},
start_date: {
type: DataTypes.DATE,
allowNull: false,
comment: '保险开始日期'
},
end_date: {
type: DataTypes.DATE,
allowNull: false,
comment: '保险结束日期'
},
policy_status: {
type: DataTypes.ENUM('active', 'expired', 'cancelled', 'suspended'),
allowNull: false,
defaultValue: 'active',
comment: '保单状态active-有效expired-已过期cancelled-已取消suspended-已暂停'
},
payment_status: {
type: DataTypes.ENUM('paid', 'unpaid', 'partial'),
allowNull: false,
defaultValue: 'unpaid',
comment: '支付状态paid-已支付unpaid-未支付partial-部分支付'
},
payment_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '支付日期'
},
policy_document_url: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '保单文档URL'
},
notes: {
type: DataTypes.TEXT,
allowNull: true,
comment: '备注信息'
},
created_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '创建人ID',
references: {
model: 'users',
key: 'id'
}
},
updated_by: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '更新人ID',
references: {
model: 'users',
key: 'id'
}
}
}, {
tableName: 'livestock_policies',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
name: 'idx_livestock_policy_no',
fields: ['policy_no'],
unique: true
},
{
name: 'idx_livestock_policy_policyholder',
fields: ['policyholder_name']
},
{
name: 'idx_livestock_policy_status',
fields: ['policy_status']
},
{
name: 'idx_livestock_policy_payment_status',
fields: ['payment_status']
},
{
name: 'idx_livestock_policy_dates',
fields: ['start_date', 'end_date']
}
],
comment: '生资保单表'
});
module.exports = LivestockPolicy;

View File

@@ -1,101 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const LivestockType = sequelize.define('LivestockType', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '牲畜类型ID'
},
name: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
comment: '牲畜类型名称',
validate: {
notEmpty: {
msg: '牲畜类型名称不能为空'
}
}
},
code: {
type: DataTypes.STRING(20),
allowNull: false,
unique: true,
comment: '牲畜类型代码'
},
description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '牲畜类型描述'
},
unit_price_min: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 0.00,
comment: '单头最低价值',
validate: {
min: {
args: [0],
msg: '单头最低价值不能小于0'
}
}
},
unit_price_max: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
defaultValue: 50000.00,
comment: '单头最高价值',
validate: {
min: {
args: [0],
msg: '单头最高价值不能小于0'
}
}
},
premium_rate: {
type: DataTypes.DECIMAL(5, 4),
allowNull: false,
defaultValue: 0.0050,
comment: '保险费率',
validate: {
min: {
args: [0],
msg: '保费费率不能小于0'
},
max: {
args: [1],
msg: '保费费率不能大于1'
}
}
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
allowNull: false,
defaultValue: 'active',
comment: '状态'
}
}, {
tableName: 'livestock_types',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
name: 'idx_livestock_type_name',
fields: ['name']
},
{
name: 'idx_livestock_type_code',
fields: ['code']
},
{
name: 'idx_livestock_type_status',
fields: ['status']
}
],
comment: '牲畜类型表'
});
module.exports = LivestockType;

View File

@@ -1,85 +0,0 @@
const { sequelize } = require('../config/database');
const { DataTypes } = require('sequelize');
const Menu = sequelize.define('Menu', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING(50),
allowNull: false,
validate: {
len: [2, 50]
}
},
key: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
validate: {
len: [2, 50]
}
},
path: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
len: [1, 100]
}
},
icon: {
type: DataTypes.STRING(50),
allowNull: true
},
parent_id: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'menus',
key: 'id'
},
defaultValue: null
},
component: {
type: DataTypes.STRING(100),
allowNull: true
},
order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active'
},
show: {
type: DataTypes.BOOLEAN,
defaultValue: true
}
}, {
tableName: 'menus',
timestamps: true,
underscored: true,
indexes: [
{ fields: ['key'] },
{ fields: ['parent_id'] },
{ fields: ['status'] },
{ fields: ['order'] }
]
});
// 设置自关联
Menu.hasMany(Menu, {
as: 'children',
foreignKey: 'parent_id'
});
Menu.belongsTo(Menu, {
as: 'parent',
foreignKey: 'parent_id'
});
module.exports = Menu;

View File

@@ -1,42 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const MenuPermission = sequelize.define('MenuPermission', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '关联ID'
},
menu_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '菜单ID'
},
permission_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '权限ID'
},
required: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否必需权限'
}
}, {
tableName: 'menu_permissions',
timestamps: true,
underscored: true,
indexes: [
{
fields: ['menu_id', 'permission_id'],
unique: true,
name: 'uk_menu_permission'
},
{ fields: ['menu_id'] },
{ fields: ['permission_id'] }
]
});
module.exports = MenuPermission;

View File

@@ -1,270 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const OperationLog = sequelize.define('OperationLog', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
},
comment: '操作用户ID'
},
operation_type: {
type: DataTypes.ENUM(
'login', // 登录
'logout', // 登出
'create', // 创建
'update', // 更新
'delete', // 删除
'view', // 查看
'export', // 导出
'import', // 导入
'approve', // 审批
'reject', // 拒绝
'system_config', // 系统配置
'user_manage', // 用户管理
'role_manage', // 角色管理
'other' // 其他
),
allowNull: false,
comment: '操作类型'
},
operation_module: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '操作模块(如:用户管理、设备管理、预警管理等)'
},
operation_content: {
type: DataTypes.TEXT,
allowNull: false,
comment: '操作内容描述'
},
operation_target: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '操作目标用户ID、设备ID等'
},
request_method: {
type: DataTypes.ENUM('GET', 'POST', 'PUT', 'DELETE', 'PATCH'),
allowNull: true,
comment: 'HTTP请求方法'
},
request_url: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '请求URL'
},
request_params: {
type: DataTypes.TEXT,
allowNull: true,
comment: '请求参数JSON格式',
get() {
const value = this.getDataValue('request_params');
return value ? JSON.parse(value) : null;
},
set(value) {
this.setDataValue('request_params', value ? JSON.stringify(value) : null);
}
},
response_status: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '响应状态码'
},
ip_address: {
type: DataTypes.STRING(45),
allowNull: true,
comment: 'IP地址支持IPv6'
},
user_agent: {
type: DataTypes.TEXT,
allowNull: true,
comment: '用户代理信息'
},
execution_time: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '执行时间(毫秒)'
},
status: {
type: DataTypes.ENUM('success', 'failed', 'error'),
defaultValue: 'success',
comment: '操作状态'
},
error_message: {
type: DataTypes.TEXT,
allowNull: true,
comment: '错误信息'
}
}, {
tableName: 'operation_logs',
timestamps: true,
underscored: true,
indexes: [
{ fields: ['user_id'] },
{ fields: ['operation_type'] },
{ fields: ['operation_module'] },
{ fields: ['created_at'] },
{ fields: ['status'] },
{ fields: ['ip_address'] }
]
});
// 定义关联关系
OperationLog.associate = function(models) {
// 操作日志属于用户
OperationLog.belongsTo(models.User, {
foreignKey: 'user_id',
as: 'user',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
});
};
// 静态方法:记录操作日志
OperationLog.logOperation = async function(logData) {
try {
const log = await this.create({
user_id: logData.userId,
operation_type: logData.operationType,
operation_module: logData.operationModule,
operation_content: logData.operationContent,
operation_target: logData.operationTarget,
request_method: logData.requestMethod,
request_url: logData.requestUrl,
request_params: logData.requestParams,
response_status: logData.responseStatus,
ip_address: logData.ipAddress,
user_agent: logData.userAgent,
execution_time: logData.executionTime,
status: logData.status || 'success',
error_message: logData.errorMessage
});
return log;
} catch (error) {
console.error('记录操作日志失败:', error);
throw error;
}
};
// 静态方法:获取操作日志列表
OperationLog.getLogsList = async function(options = {}) {
const {
page = 1,
limit = 20,
userId,
operationType,
operationModule,
status,
startDate,
endDate,
keyword
} = options;
const where = {};
// 构建查询条件
if (userId) where.user_id = userId;
if (operationType) where.operation_type = operationType;
if (operationModule) where.operation_module = operationModule;
if (status) where.status = status;
// 时间范围查询
if (startDate || endDate) {
where.created_at = {};
if (startDate) where.created_at[sequelize.Op.gte] = new Date(startDate);
if (endDate) where.created_at[sequelize.Op.lte] = new Date(endDate);
}
// 关键词搜索
if (keyword) {
where[sequelize.Op.or] = [
{ operation_content: { [sequelize.Op.like]: `%${keyword}%` } },
{ operation_target: { [sequelize.Op.like]: `%${keyword}%` } }
];
}
const offset = (page - 1) * limit;
const result = await this.findAndCountAll({
where,
include: [{
model: sequelize.models.User,
as: 'user',
attributes: ['id', 'username', 'real_name']
}],
order: [['created_at', 'DESC']],
limit: parseInt(limit),
offset: parseInt(offset)
});
return {
logs: result.rows,
total: result.count,
page: parseInt(page),
limit: parseInt(limit),
totalPages: Math.ceil(result.count / limit)
};
};
// 静态方法:获取操作统计
OperationLog.getOperationStats = async function(options = {}) {
const { startDate, endDate, userId } = options;
const where = {};
if (userId) where.user_id = userId;
if (startDate || endDate) {
where.created_at = {};
if (startDate) where.created_at[sequelize.Op.gte] = new Date(startDate);
if (endDate) where.created_at[sequelize.Op.lte] = new Date(endDate);
}
// 按操作类型统计
const typeStats = await this.findAll({
where,
attributes: [
'operation_type',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['operation_type'],
raw: true
});
// 按操作模块统计
const moduleStats = await this.findAll({
where,
attributes: [
'operation_module',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['operation_module'],
raw: true
});
// 按状态统计
const statusStats = await this.findAll({
where,
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status'],
raw: true
});
return {
typeStats,
moduleStats,
statusStats
};
};
module.exports = OperationLog;

View File

@@ -1,93 +0,0 @@
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const Permission = sequelize.define('Permission', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
comment: '权限ID'
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
comment: '权限名称',
validate: {
len: [2, 100]
}
},
code: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
comment: '权限代码',
validate: {
len: [2, 100],
is: /^[a-zA-Z0-9_:]+$/ // 只允许字母、数字、下划线和冒号
}
},
description: {
type: DataTypes.TEXT,
allowNull: true,
comment: '权限描述'
},
module: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '所属模块',
validate: {
len: [2, 50]
}
},
type: {
type: DataTypes.ENUM('menu', 'operation'),
allowNull: false,
defaultValue: 'operation',
comment: '权限类型menu-菜单权限operation-操作权限'
},
parent_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '父权限ID'
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
allowNull: false,
defaultValue: 'active',
comment: '状态'
},
sort_order: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序'
}
}, {
tableName: 'permissions',
timestamps: true,
underscored: true,
indexes: [
{ fields: ['code'], unique: true },
{ fields: ['module'] },
{ fields: ['type'] },
{ fields: ['parent_id'] },
{ fields: ['status'] }
]
});
// 定义自关联关系
Permission.hasMany(Permission, {
as: 'children',
foreignKey: 'parent_id',
onDelete: 'SET NULL',
onUpdate: 'CASCADE'
});
Permission.belongsTo(Permission, {
as: 'parent',
foreignKey: 'parent_id',
onDelete: 'SET NULL',
onUpdate: 'CASCADE'
});
module.exports = Permission;

View File

@@ -78,6 +78,31 @@ const Policy = sequelize.define('Policy', {
allowNull: false,
comment: '保险结束日期'
},
policyholder_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '投保人姓名'
},
insured_name: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '被保险人姓名'
},
phone: {
type: DataTypes.STRING(20),
allowNull: true,
comment: '联系电话'
},
email: {
type: DataTypes.STRING(100),
allowNull: true,
comment: '电子邮箱'
},
address: {
type: DataTypes.STRING(500),
allowNull: true,
comment: '联系地址'
},
policy_status: {
type: DataTypes.ENUM('active', 'expired', 'cancelled', 'suspended'),
allowNull: false,

File diff suppressed because it is too large Load Diff

View File

@@ -1,60 +0,0 @@
{
"name": "insurance_backend",
"version": "1.0.0",
"description": "保险端口后端服务",
"main": "src/app.js",
"scripts": {
"start": "node src/app.js",
"dev": "nodemon src/app.js",
"test": "jest",
"migrate": "npx sequelize-cli db:migrate",
"seed": "npx sequelize-cli db:seed:all",
"migrate:undo": "npx sequelize-cli db:migrate:undo",
"seed:undo": "npx sequelize-cli db:seed:undo:all"
},
"keywords": [
"insurance",
"backend",
"nodejs",
"express"
],
"author": "Insurance Team",
"license": "MIT",
"dependencies": {
"axios": "^1.12.2",
"bcrypt": "^5.1.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"exceljs": "^4.4.0",
"express": "^4.18.2",
"express-rate-limit": "^8.1.0",
"helmet": "^8.1.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"multer": "^1.4.5-lts.1",
"mysql2": "^2.3.3",
"redis": "^4.5.0",
"sanitize-html": "^2.8.1",
"sequelize": "^6.29.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"validator": "^13.9.0",
"winston": "^3.8.2"
},
"devDependencies": {
"eslint": "^8.32.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.5.0",
"nodemon": "^2.0.20",
"sequelize-cli": "^6.6.0",
"supertest": "^6.3.3"
},
"engines": {
"node": "16.20.2",
"npm": ">=8.0.0"
}
}

View File

@@ -33,4 +33,9 @@ router.patch('/:id/status', jwtAuth, checkPermission('insurance:policy', 'edit')
policyController.updatePolicyStatus
);
// 删除保单
router.delete('/:id', jwtAuth, checkPermission('insurance:policy', 'delete'),
policyController.deletePolicy
);
module.exports = router;

View File

@@ -1,75 +0,0 @@
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

@@ -19,7 +19,10 @@ const PORT = process.env.PORT || 3000;
// 安全中间件
app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3001',
origin: [
process.env.FRONTEND_URL || 'http://localhost:3001',
'http://localhost:3002'
],
credentials: true
}));

View File

@@ -1,95 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,29 +0,0 @@
const express = require('express');
// 测试路由加载
console.log('开始测试路由加载...');
try {
// 测试设备路由
const deviceRoutes = require('./routes/devices');
console.log('✅ 设备路由加载成功');
// 测试设备控制器
const deviceController = require('./controllers/deviceController');
console.log('✅ 设备控制器加载成功');
// 测试模型
const { Device, DeviceAlert } = require('./models');
console.log('✅ 设备模型加载成功');
// 创建简单的Express应用测试
const app = express();
app.use('/api/devices', deviceRoutes);
console.log('✅ 路由注册成功');
console.log('所有组件加载正常!');
} catch (error) {
console.error('❌ 路由加载失败:', error.message);
console.error('错误详情:', error);
}

View File

@@ -1,71 +0,0 @@
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

@@ -1,44 +0,0 @@
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

@@ -1,34 +0,0 @@
// 简单的API测试脚本
const axios = require('axios');
async function testAuthenticatedApi() {
try {
console.log('测试认证菜单API...');
// 注意这个测试需要有效的JWT token
const response = await axios.get('http://localhost:3000/api/menus', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN_HERE'
}
});
console.log('✅ 认证菜单API测试成功');
console.log('返回数据:', response.data);
return true;
} catch (error) {
console.error('❌ 认证菜单API测试失败:', error.message);
console.log('提示请确保服务器运行并提供有效的JWT token');
return false;
}
}
async function testAllApis() {
console.log('开始API测试...');
console.log('注意所有API现在都需要认证请确保提供有效的JWT token');
const authApiResult = await testAuthenticatedApi();
console.log('\n测试总结:');
console.log(`认证菜单API: ${authApiResult ? '通过' : '失败'}`);
}
testAllApis().then(() => {
console.log('\nAPI测试完成');
});

View File

@@ -1,99 +0,0 @@
const axios = require('axios');
// 创建一个模拟浏览器的axios实例
const browserAPI = axios.create({
baseURL: 'http://localhost:3001',
timeout: 10000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
});
async function testBrowserBehavior() {
console.log('=== 模拟浏览器行为测试 ===\n');
try {
// 1. 模拟前端登录
console.log('1. 模拟浏览器登录...');
const loginResponse = await browserAPI.post('/api/auth/login', {
username: 'admin',
password: '123456'
});
console.log('登录响应状态:', loginResponse.status);
console.log('登录响应数据:', JSON.stringify(loginResponse.data, null, 2));
if (!loginResponse.data || loginResponse.data.code !== 200) {
console.log('❌ 登录失败');
return;
}
const token = loginResponse.data.data.token;
console.log('✅ 获取到Token:', token.substring(0, 50) + '...');
// 2. 设置Authorization header
browserAPI.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// 3. 模拟前端API调用
console.log('\n2. 模拟浏览器API调用...');
try {
const apiResponse = await browserAPI.get('/api/data-warehouse/overview');
console.log('✅ API调用成功!');
console.log('状态码:', apiResponse.status);
console.log('响应数据:', JSON.stringify(apiResponse.data, null, 2));
} catch (apiError) {
console.log('❌ API调用失败:', apiError.response?.status, apiError.response?.statusText);
console.log('错误详情:', apiError.response?.data);
console.log('请求头:', apiError.config?.headers);
// 检查是否是权限问题
if (apiError.response?.status === 403) {
console.log('\n🔍 403错误分析:');
console.log('- Token是否正确传递:', !!apiError.config?.headers?.Authorization);
console.log('- Authorization头:', apiError.config?.headers?.Authorization?.substring(0, 50) + '...');
// 尝试验证token
console.log('\n3. 验证Token有效性...');
try {
const profileResponse = await browserAPI.get('/api/auth/profile');
console.log('✅ Token验证成功用户信息:', profileResponse.data);
} catch (profileError) {
console.log('❌ Token验证失败:', profileError.response?.status, profileError.response?.data);
}
}
}
// 4. 测试其他需要权限的接口
console.log('\n4. 测试其他权限接口...');
const testAPIs = [
'/api/insurance/applications',
'/api/device-alerts/stats',
'/api/system/stats'
];
for (const apiPath of testAPIs) {
try {
const response = await browserAPI.get(apiPath);
console.log(`${apiPath}: 成功 (${response.status})`);
} catch (error) {
console.log(`${apiPath}: 失败 (${error.response?.status}) - ${error.response?.data?.message || error.message}`);
}
}
} catch (error) {
console.log('❌ 测试失败:', error.response?.data || error.message);
if (error.response) {
console.log('错误状态:', error.response.status);
console.log('错误数据:', error.response.data);
}
}
}
testBrowserBehavior();

View File

@@ -1,78 +0,0 @@
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

@@ -1,52 +0,0 @@
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

@@ -1,37 +0,0 @@
const axios = require('axios');
async function testServer() {
console.log('开始测试服务器...');
try {
// 测试健康检查
console.log('\n1. 测试健康检查接口...');
const healthResponse = await axios.get('http://localhost:3004/health');
console.log('健康检查成功:', healthResponse.data);
} catch (error) {
console.log('健康检查失败:', error.response?.status, error.response?.data || error.message);
}
try {
// 测试登录接口
console.log('\n2. 测试登录接口...');
const loginResponse = await axios.post('http://localhost:3004/api/auth/login', {
username: 'admin',
password: '123456'
});
console.log('登录成功:', loginResponse.data);
} catch (error) {
console.log('登录失败:', error.response?.status, error.response?.data || error.message);
}
try {
// 测试不存在的接口
console.log('\n3. 测试不存在的接口...');
const notFoundResponse = await axios.get('http://localhost:3004/nonexistent');
console.log('不存在接口响应:', notFoundResponse.data);
} catch (error) {
console.log('不存在接口错误:', error.response?.status, error.response?.data || error.message);
}
}
testServer();

View File

@@ -1,109 +0,0 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000';
// 测试固定令牌功能
async function testFixedToken() {
try {
console.log('🧪 开始测试固定令牌功能...\n');
// 1. 登录获取JWT令牌
console.log('1. 登录获取JWT令牌...');
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, {
username: 'admin',
password: '123456'
});
if (loginResponse.data.code !== 200) {
console.error('❌ 登录失败:', loginResponse.data.message);
return;
}
const jwtToken = loginResponse.data.data.accessToken;
const userId = loginResponse.data.data.user.id;
console.log('✅ 登录成功用户ID:', userId);
// 2. 检查用户是否已有固定令牌
console.log('\n2. 检查用户固定令牌状态...');
const tokenInfoResponse = await axios.get(`${BASE_URL}/api/users/${userId}/fixed-token`, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
console.log('✅ 令牌状态:', tokenInfoResponse.data.data);
// 3. 如果没有固定令牌,则生成一个
let fixedToken;
if (!tokenInfoResponse.data.data.hasToken) {
console.log('\n3. 生成固定令牌...');
const generateResponse = await axios.post(`${BASE_URL}/api/users/${userId}/fixed-token`, {}, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
if (generateResponse.data.code === 200) {
fixedToken = generateResponse.data.data.fixed_token;
console.log('✅ 固定令牌生成成功');
console.log('🔑 固定令牌:', fixedToken);
} else {
console.error('❌ 固定令牌生成失败:', generateResponse.data.message);
return;
}
} else {
console.log('\n3. 用户已有固定令牌,重新生成...');
const regenerateResponse = await axios.put(`${BASE_URL}/api/users/${userId}/fixed-token`, {}, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
if (regenerateResponse.data.code === 200) {
fixedToken = regenerateResponse.data.data.fixed_token;
console.log('✅ 固定令牌重新生成成功');
console.log('🔑 新固定令牌:', fixedToken);
} else {
console.error('❌ 固定令牌重新生成失败:', regenerateResponse.data.message);
return;
}
}
// 4. 使用固定令牌访问API
console.log('\n4. 使用固定令牌访问用户列表API...');
const usersResponse = await axios.get(`${BASE_URL}/api/users`, {
headers: { Authorization: `Bearer ${fixedToken}` }
});
if (usersResponse.data.code === 200) {
console.log('✅ 使用固定令牌访问API成功');
console.log('📊 用户数量:', usersResponse.data.data.users.length);
} else {
console.error('❌ 使用固定令牌访问API失败:', usersResponse.data.message);
}
// 5. 测试无效令牌
console.log('\n5. 测试无效固定令牌...');
try {
await axios.get(`${BASE_URL}/api/users`, {
headers: { Authorization: 'Bearer ft_invalid_token' }
});
console.error('❌ 无效令牌测试失败应该返回401错误');
} catch (error) {
if (error.response && error.response.status === 401) {
console.log('✅ 无效令牌正确返回401错误');
} else {
console.error('❌ 无效令牌测试异常:', error.message);
}
}
// 6. 再次检查令牌信息
console.log('\n6. 再次检查令牌信息...');
const finalTokenInfoResponse = await axios.get(`${BASE_URL}/api/users/${userId}/fixed-token`, {
headers: { Authorization: `Bearer ${jwtToken}` }
});
console.log('✅ 最终令牌状态:', finalTokenInfoResponse.data.data);
console.log('\n🎉 固定令牌功能测试完成!');
} catch (error) {
console.error('❌ 测试过程中发生错误:', error.response?.data || error.message);
}
}
// 运行测试
testFixedToken();

View File

@@ -1,35 +0,0 @@
const axios = require('axios');
async function testAPI() {
try {
// 先登录获取token
const loginResponse = await axios.post('http://localhost:3000/api/auth/login', {
username: 'admin',
password: '123456'
});
const token = loginResponse.data.data.token;
console.log('✅ 登录成功');
// 测试获取牲畜类型列表
const response = await axios.get('http://localhost:3000/api/livestock-types', {
headers: {
'Authorization': `Bearer ${token}`
}
});
console.log('✅ 获取牲畜类型列表成功');
console.log('响应数据:', JSON.stringify(response.data, null, 2));
} catch (error) {
console.error('❌ 错误:', error.response?.data || error.message);
if (error.response?.data) {
console.error('详细错误:', JSON.stringify(error.response.data, null, 2));
}
if (error.stack) {
console.error('错误堆栈:', error.stack);
}
}
}
testAPI();

View File

@@ -1,21 +0,0 @@
const axios = require('axios');
(async () => {
try {
console.log('开始测试登录...');
const response = await axios.post('http://localhost:3000/api/auth/login', {
username: 'admin',
password: '123456'
});
console.log('登录成功!');
console.log('状态码:', response.status);
console.log('响应数据:', JSON.stringify(response.data, null, 2));
} catch (error) {
console.log('登录失败!');
console.log('状态码:', error.response?.status);
console.log('错误信息:', error.response?.data || error.message);
}
})();

View File

@@ -1,32 +0,0 @@
const axios = require('axios');
async function testMenuAPI() {
try {
// 1. 先登录获取token
console.log('1. 登录获取token...');
const loginResponse = await axios.post('http://localhost:3000/api/auth/login', {
username: 'admin',
password: '123456'
});
const token = loginResponse.data.data.accessToken;
console.log('登录成功token:', token.substring(0, 50) + '...');
// 2. 测试菜单接口
console.log('\n2. 测试菜单接口...');
const menuResponse = await axios.get('http://localhost:3000/api/menus', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log('菜单接口响应状态:', menuResponse.status);
console.log('菜单接口响应数据:', JSON.stringify(menuResponse.data, null, 2));
} catch (error) {
console.error('测试失败:', error.response?.data || error.message);
}
}
testMenuAPI();

View File

@@ -1,23 +0,0 @@
const axios = require('axios');
async function testParams() {
try {
console.log('🧪 测试查询参数传递...');
const response = await axios.get('http://localhost:3000/api/livestock-claims/test-params', {
params: {
claim_no: 'LC2024001',
page: 1,
pageSize: 10
}
});
console.log('✅ 测试端点响应:');
console.log(JSON.stringify(response.data, null, 2));
} catch (error) {
console.error('❌ 测试失败:', error.response?.data || error.message);
}
}
testParams();

View File

@@ -1,19 +0,0 @@
const bcrypt = require('bcrypt');
const hash = '$2b$12$2gMSr66wlftS./7f7U9JJeSZrpOPTQUFXLUANJ3a0IfWoiKPCuSDO';
(async () => {
try {
console.log('测试密码验证...');
const passwords = ['admin', 'admin123', '123456', 'password', 'Admin123'];
for (const pwd of passwords) {
const result = await bcrypt.compare(pwd, hash);
console.log(`密码 '${pwd}': ${result ? '正确' : '错误'}`);
}
} catch (error) {
console.error('错误:', error);
}
})();

View File

@@ -1,75 +0,0 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000/api';
const ADMIN_TOKEN = '5659725423f665a8bf5053b37e624ea86387f9113ae77ac75fc102012a349180';
// 创建axios实例
const api = axios.create({
baseURL: BASE_URL,
headers: {
'Authorization': `Bearer ${ADMIN_TOKEN}`,
'Content-Type': 'application/json'
}
});
async function testRolePermissionsAPI() {
console.log('开始测试角色权限管理API...\n');
// 测试1: 获取所有权限
try {
console.log('1. 测试获取所有权限...');
const response = await api.get('/role-permissions/permissions');
console.log('✅ 获取所有权限成功');
console.log('权限数量:', response.data.data?.length || 0);
} catch (error) {
console.log('❌ 获取所有权限失败:', error.response?.data?.message || error.message);
}
// 测试2: 获取所有角色及其权限
try {
console.log('\n2. 测试获取所有角色及其权限...');
const response = await api.get('/role-permissions/roles');
console.log('✅ 获取所有角色及其权限成功');
const roles = response.data.data?.roles || [];
console.log('角色数量:', roles.length);
if (roles && roles.length > 0) {
roles.forEach(role => {
console.log(` - ${role.name}: ${role.permissionCount || 0} 个权限`);
});
}
} catch (error) {
console.log('❌ 获取所有角色及其权限失败:', error.response?.data?.message || error.message);
}
// 测试3: 获取特定角色的详细权限
try {
console.log('\n3. 测试获取特定角色的详细权限...');
const response = await api.get('/role-permissions/roles/1/permissions');
console.log('✅ 获取特定角色的详细权限成功');
console.log('权限树结构:', response.data.data ? '已构建' : '未构建');
} catch (error) {
console.log('❌ 获取特定角色的详细权限失败:', error.response?.data?.message || error.message);
}
// 测试4: 权限统计
try {
console.log('\n4. 测试权限统计...');
const response = await api.get('/role-permissions/stats');
console.log('✅ 权限统计成功');
if (response.data.data) {
const stats = response.data.data;
console.log('统计信息:');
console.log(` - 总角色数: ${stats.overview?.totalRoles || 0}`);
console.log(` - 总权限数: ${stats.overview?.totalPermissions || 0}`);
console.log(` - 总分配数: ${stats.overview?.totalAssignments || 0}`);
console.log(` - 平均每角色权限数: ${stats.overview?.averagePermissionsPerRole || 0}`);
}
} catch (error) {
console.log('❌ 权限统计失败:', error.response?.data?.message || error.message);
}
console.log('\n测试完成!');
}
// 运行测试
testRolePermissionsAPI().catch(console.error);

View File

@@ -1,31 +0,0 @@
const http = require('http');
// 测试健康检查接口
const options = {
hostname: 'localhost',
port: 3000,
path: '/health',
method: 'GET'
};
console.log('正在测试健康检查接口...');
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
console.log(`响应头: ${JSON.stringify(res.headers)}`);
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('响应内容:', data);
});
});
req.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
req.end();

View File

@@ -1,42 +0,0 @@
const { sequelize } = require('./config/database');
const bcrypt = require('bcrypt');
async function updateAdminPassword() {
try {
// 连接数据库
await sequelize.authenticate();
console.log('✅ 数据库连接成功');
// 密码为123456使用bcrypt进行哈希处理
const plainPassword = '123456';
const hashedPassword = await bcrypt.hash(plainPassword, 12);
console.log('🔑 密码哈希生成成功');
// 更新admin用户的密码
const [updatedRows] = await sequelize.query(
'UPDATE users SET password = :password WHERE username = :username',
{
replacements: {
password: hashedPassword,
username: 'admin'
}
}
);
if (updatedRows > 0) {
console.log('✅ admin用户密码已更新为: 123456');
} else {
console.log('⚠️ 未找到admin用户请检查数据库');
}
} catch (error) {
console.error('❌ 更新密码失败:', error.message);
} finally {
// 关闭数据库连接
await sequelize.close();
console.log('🔒 数据库连接已关闭');
}
}
// 执行脚本
updateAdminPassword();