修改养殖端小程序,保险前后端和小程序

This commit is contained in:
xuqiuyun
2025-09-19 18:13:07 +08:00
parent eb3c4604d3
commit 35db747d4f
89 changed files with 16231 additions and 1500 deletions

View File

@@ -0,0 +1,59 @@
-- 初始化菜单数据
-- 注意请确保menus表已经创建
-- 首先清空现有菜单数据
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE menus;
SET FOREIGN_KEY_CHECKS = 1;
-- 插入父级菜单
INSERT INTO menus (name, key, path, icon, parent_id, component, `order`, status, show, created_at, updated_at)
VALUES
('仪表板', 'Dashboard', '/dashboard', 'DashboardOutlined', NULL, 'views/Dashboard.vue', 1, 'active', true, NOW(), NOW()),
('数据揽仓', 'DataWarehouse', '/data-warehouse', 'DatabaseOutlined', NULL, '', 2, 'active', true, NOW(), NOW()),
('监管任务', 'SupervisionTask', '/supervision-task', 'CheckCircleOutlined', NULL, '', 3, 'active', true, NOW(), NOW()),
('待安装任务', 'PendingInstallationTask', '/pending-installation', 'ExclamationCircleOutlined', NULL, '', 4, 'active', true, NOW(), NOW()),
('监管任务已结项', 'CompletedTask', '/completed-tasks', 'FileDoneOutlined', NULL, '', 5, 'active', true, NOW(), NOW()),
('投保客户单', 'InsuredCustomers', '/insured-customers', 'ShopOutlined', NULL, '', 6, 'active', true, NOW(), NOW()),
('生资保单', 'AgriculturalInsurance', '/agricultural-insurance', 'FileProtectOutlined', NULL, '', 7, 'active', true, NOW(), NOW()),
('险种管理', 'InsuranceTypeManagement', '/insurance-types', 'MedicineBoxOutlined', NULL, '', 8, 'active', true, NOW(), NOW()),
('客户理赔', 'CustomerClaims', '/customer-claims', 'AlertCircleOutlined', NULL, '', 9, 'active', true, NOW(), NOW()),
('消息通知', 'Notifications', '/notifications', 'BellOutlined', NULL, '', 10, 'active', true, NOW(), NOW()),
('子账号管理', 'UserManagement', '/users', 'UserAddOutlined', NULL, 'views/UserManagement.vue', 11, 'active', true, NOW(), NOW()),
('系统设置', 'SystemSettings', '/system-settings', 'SettingOutlined', NULL, '', 12, 'active', true, NOW(), NOW()),
('个人中心', 'UserProfile', '/profile', 'UserSwitchOutlined', NULL, '', 13, 'active', true, NOW(), NOW());
-- 插入子菜单
-- 投保客户单的子菜单
INSERT INTO menus (name, key, path, icon, parent_id, component, `order`, status, show, created_at, updated_at)
SELECT
'参保申请', 'ApplicationManagement', '/applications', 'FileTextOutlined', id, 'views/ApplicationManagement.vue', 1, 'active', true, NOW(), NOW()
FROM menus
WHERE `key` = 'InsuredCustomers';
-- 生资保单的子菜单
INSERT INTO menus (name, key, path, icon, parent_id, component, `order`, status, show, created_at, updated_at)
SELECT
'生资保单列表', 'PolicyManagement', '/policies', 'FileDoneOutlined', id, 'views/PolicyManagement.vue', 1, 'active', true, NOW(), NOW()
FROM menus
WHERE `key` = 'AgriculturalInsurance';
-- 险种管理的子菜单
INSERT INTO menus (name, key, path, icon, parent_id, component, `order`, status, show, created_at, updated_at)
SELECT
'险种管理', 'InsuranceTypeList', '/insurance-types', 'MedicineBoxOutlined', id, 'views/InsuranceTypeManagement.vue', 1, 'active', true, NOW(), NOW()
FROM menus
WHERE `key` = 'InsuranceTypeManagement';
-- 客户理赔的子菜单
INSERT INTO menus (name, key, path, icon, parent_id, component, `order`, status, show, created_at, updated_at)
SELECT
'客户理赔', 'ClaimManagement', '/claims', 'SafetyCertificateOutlined', id, 'views/ClaimManagement.vue', 1, 'active', true, NOW(), NOW()
FROM menus
WHERE `key` = 'CustomerClaims';
-- 查询插入结果
SELECT * FROM menus ORDER BY parent_id, `order`;
-- 输出插入的记录数
SELECT CONCAT('成功插入 ', COUNT(*), ' 条菜单记录') AS result FROM menus;

View File

@@ -0,0 +1,175 @@
// 初始化菜单表结构并插入数据
const { sequelize } = require('../config/database');
const { DataTypes } = require('sequelize');
// 定义Menu模型
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'
});
// 初始化函数
async function initializeMenus() {
try {
console.log('🔄 开始初始化菜单表结构...');
// 检查并创建表
const tableExists = await sequelize.query(
"SHOW TABLES LIKE 'menus'"
);
if (tableExists[0].length === 0) {
console.log('📝 menus表不存在开始创建...');
await Menu.sync({ force: true });
console.log('✅ menus表创建成功');
} else {
console.log(' menus表已存在开始清空数据...');
await sequelize.query('SET FOREIGN_KEY_CHECKS = 0');
await sequelize.query('TRUNCATE TABLE menus');
await sequelize.query('SET FOREIGN_KEY_CHECKS = 1');
console.log('✅ menus表数据已清空');
}
console.log('📊 开始插入菜单数据...');
// 插入父级菜单
const parentMenus = await Menu.bulkCreate([
{ name: '仪表板', key: 'Dashboard', path: '/dashboard', icon: 'DashboardOutlined', parent_id: null, component: 'views/Dashboard.vue', order: 1, status: 'active', show: true },
{ name: '数据览仓', key: 'DataWarehouse', path: '/data-warehouse', icon: 'DatabaseOutlined', parent_id: null, component: '', order: 2, status: 'active', show: true },
{ name: '监管任务', key: 'SupervisionTask', path: '/supervision-task', icon: 'CheckCircleOutlined', parent_id: null, component: '', order: 3, status: 'active', show: true },
{ name: '待安装任务', key: 'PendingInstallationTask', path: '/pending-installation', icon: 'ExclamationCircleOutlined', parent_id: null, component: '', order: 4, status: 'active', show: true },
{ name: '监管任务已结项', key: 'CompletedTask', path: '/completed-tasks', icon: 'FileDoneOutlined', parent_id: null, component: '', order: 5, status: 'active', show: true },
{ name: '投保客户单', key: 'InsuredCustomers', path: '/insured-customers', icon: 'ShopOutlined', parent_id: null, component: '', order: 6, status: 'active', show: true },
{ name: '生资保单', key: 'AgriculturalInsurance', path: '/agricultural-insurance', icon: 'FileProtectOutlined', parent_id: null, component: '', order: 7, status: 'active', show: true },
{ name: '险种管理', key: 'InsuranceTypeManagement', path: '/insurance-types', icon: 'MedicineBoxOutlined', parent_id: null, component: '', order: 8, status: 'active', show: true },
{ name: '客户理赔', key: 'CustomerClaims', path: '/customer-claims', icon: 'AlertCircleOutlined', parent_id: null, component: '', order: 9, status: 'active', show: true },
{ name: '消息通知', key: 'Notifications', path: '/notifications', icon: 'BellOutlined', parent_id: null, component: '', order: 10, status: 'active', show: true },
{ name: '子账号管理', key: 'UserManagement', path: '/users', icon: 'UserAddOutlined', parent_id: null, component: 'views/UserManagement.vue', order: 11, status: 'active', show: true },
{ name: '系统设置', key: 'SystemSettings', path: '/system-settings', icon: 'SettingOutlined', parent_id: null, component: '', order: 12, status: 'active', show: true },
{ name: '个人中心', key: 'UserProfile', path: '/profile', icon: 'UserSwitchOutlined', parent_id: null, component: '', order: 13, status: 'active', show: true }
]);
console.log(`✅ 已插入 ${parentMenus.length} 条父级菜单数据`);
// 查找父级菜单ID
const parentMenuMap = {};
for (const menu of parentMenus) {
parentMenuMap[menu.key] = menu.id;
}
// 插入子菜单
const subMenus = await Menu.bulkCreate([
// 投保客户单的子菜单
{ name: '参保申请', key: 'ApplicationManagement', path: '/applications', icon: 'FileTextOutlined', parent_id: parentMenuMap.InsuredCustomers, component: 'views/ApplicationManagement.vue', order: 1, status: 'active', show: true },
// 生资保单的子菜单
{ name: '生资保单列表', key: 'PolicyManagement', path: '/policies', icon: 'FileDoneOutlined', parent_id: parentMenuMap.AgriculturalInsurance, component: 'views/PolicyManagement.vue', order: 1, status: 'active', show: true },
// 险种管理的子菜单
{ name: '险种管理', key: 'InsuranceTypeList', path: '/insurance-types', icon: 'MedicineBoxOutlined', parent_id: parentMenuMap.InsuranceTypeManagement, component: 'views/InsuranceTypeManagement.vue', order: 1, status: 'active', show: true },
// 客户理赔的子菜单
{ name: '客户理赔', key: 'ClaimManagement', path: '/claims', icon: 'SafetyCertificateOutlined', parent_id: parentMenuMap.CustomerClaims, component: 'views/ClaimManagement.vue', order: 1, status: 'active', show: true }
]);
console.log(`✅ 已插入 ${subMenus.length} 条子菜单数据`);
// 查询所有菜单
const allMenus = await Menu.findAll({
order: [['parent_id', 'ASC'], ['order', 'ASC']],
include: [{ model: Menu, as: 'children' }]
});
console.log(`✅ 菜单数据初始化完成,共 ${allMenus.length} 条记录`);
} catch (error) {
console.error('❌ 菜单数据初始化失败:', error.message);
throw error;
} finally {
// 关闭数据库连接
await sequelize.close();
console.log('🔌 数据库连接已关闭');
}
}
// 执行初始化
initializeMenus().catch(err => {
console.error('❌ 菜单数据初始化任务失败');
process.exit(1);
});

View File

@@ -0,0 +1,225 @@
const { sequelize } = require('../config/database');
const { DataTypes } = require('sequelize');
const { User, Role, InsuranceType, InsuranceApplication, Policy, Claim } = require('../models');
// 创建随机数据的辅助函数
const randomString = (length = 10, chars = 'abcdefghijklmnopqrstuvwxyz0123456789') => {
let result = '';
const charsLength = chars.length;
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * charsLength));
}
return result;
};
const randomDate = (start, end) => {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
};
const randomNumber = (min, max) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
const randomDecimal = (min, max, decimals = 2) => {
return Number((Math.random() * (max - min) + min).toFixed(decimals));
};
const generateApplicationNo = () => {
return `APP${Date.now()}${randomString(6).toUpperCase()}`;
};
const generatePolicyNo = () => {
return `POL${Date.now()}${randomString(6).toUpperCase()}`;
};
const generateClaimNo = () => {
return `CLAIM${Date.now()}${randomString(6).toUpperCase()}`;
};
// 生成随机身份证号
const generateIdCard = () => {
// 简化版身份证号生成,真实场景需要更复杂的校验
const areaCode = randomNumber(110000, 659004);
const birthYear = randomNumber(1960, 2000);
const birthMonth = String(randomNumber(1, 12)).padStart(2, '0');
const birthDay = String(randomNumber(1, 28)).padStart(2, '0');
const seq = String(randomNumber(100, 999)).padStart(3, '0');
const checkDigit = Math.random() > 0.9 ? 'X' : randomNumber(0, 9);
return `${areaCode}${birthYear}${birthMonth}${birthDay}${seq}${checkDigit}`;
};
// 生成随机手机号
const generatePhone = () => {
return `1${randomNumber(3, 9)}${randomString(9, '0123456789')}`;
};
// 初始化测试数据
async function seedData() {
try {
// 确保数据库连接正常
await sequelize.authenticate();
console.log('数据库连接成功');
// 1. 创建角色(如果不存在)
let adminRole = await Role.findOne({ where: { name: 'admin' } });
if (!adminRole) {
adminRole = await Role.create({
name: 'admin',
description: '管理员角色'
});
}
// 2. 创建用户(如果不存在)
let adminUser = await User.findOne({ where: { username: 'admin' } });
if (!adminUser) {
adminUser = await User.create({
username: 'admin',
password: 'admin123', // 实际应用中应该使用bcrypt加密
email: 'admin@example.com',
phone: '13800138000',
role_id: adminRole.id,
status: 1
});
}
// 3. 创建保险类型
const insuranceTypes = [
{
name: '健康保险',
description: '提供医疗费用报销和健康保障',
coverage_amount_min: 10000.00,
coverage_amount_max: 500000.00,
premium_rate: 0.005,
terms_years: 1,
status: 1
},
{
name: '人寿保险',
description: '为家人提供经济保障',
coverage_amount_min: 50000.00,
coverage_amount_max: 1000000.00,
premium_rate: 0.01,
terms_years: 10,
status: 1
},
{
name: '财产保险',
description: '保障个人财产安全',
coverage_amount_min: 20000.00,
coverage_amount_max: 200000.00,
premium_rate: 0.003,
terms_years: 1,
status: 1
},
{
name: '意外保险',
description: '提供意外事故保障',
coverage_amount_min: 50000.00,
coverage_amount_max: 300000.00,
premium_rate: 0.002,
terms_years: 1,
status: 1
}
];
// 清除现有保险类型并插入新数据
await InsuranceType.destroy({ where: {} });
const createdTypes = await InsuranceType.bulkCreate(insuranceTypes);
console.log(`已创建 ${createdTypes.length} 种保险类型`);
// 4. 创建保险申请、保单和理赔数据
const applications = [];
const policies = [];
const claims = [];
// 为每种保险类型创建多个申请
for (let i = 0; i < 50; i++) {
const type = createdTypes[randomNumber(0, createdTypes.length - 1)];
const coverageAmount = randomDecimal(type.coverage_amount_min, type.coverage_amount_max);
const premium = Number((coverageAmount * type.premium_rate).toFixed(2));
const application = {
application_no: generateApplicationNo(),
customer_name: `客户${i + 1}`,
customer_id_card: generateIdCard(),
customer_phone: generatePhone(),
customer_address: `测试地址${i + 1}`,
insurance_type_id: type.id,
coverage_amount: coverageAmount,
premium: premium,
application_date: randomDate(new Date(2023, 0, 1), new Date()),
status: randomNumber(0, 3), // 0: 待审核, 1: 已批准, 2: 已拒绝, 3: 已撤销
reviewer_id: adminUser.id,
review_date: randomDate(new Date(2023, 0, 1), new Date()),
rejection_reason: randomNumber(0, 2) === 0 ? '资料不完整' : null
};
applications.push(application);
}
// 清除现有申请并插入新数据
await InsuranceApplication.destroy({ where: {} });
const createdApplications = await InsuranceApplication.bulkCreate(applications);
console.log(`已创建 ${createdApplications.length} 个保险申请`);
// 为已批准的申请创建保单
for (const app of createdApplications) {
if (app.status === 1) { // 只有已批准的申请才有保单
const policy = {
policy_no: generatePolicyNo(),
application_id: app.id,
insurance_type_id: app.insurance_type_id,
customer_id: adminUser.id, // 简化处理,实际应该关联真实客户
coverage_amount: app.coverage_amount,
premium: app.premium,
start_date: randomDate(app.review_date, new Date()),
end_date: new Date(app.review_date.getTime() + 365 * 24 * 60 * 60 * 1000),
status: 1, // 1: 有效
created_by: adminUser.id,
created_at: app.review_date
};
policies.push(policy);
}
}
// 清除现有保单并插入新数据
await Policy.destroy({ where: {} });
const createdPolicies = await Policy.bulkCreate(policies);
console.log(`已创建 ${createdPolicies.length} 个保单`);
// 为部分保单创建理赔
for (const policy of createdPolicies) {
if (randomNumber(0, 3) === 0) { // 约25%的保单会有理赔
const claimAmount = randomDecimal(policy.coverage_amount * 0.1, policy.coverage_amount * 0.8);
const claim = {
claim_no: generateClaimNo(),
policy_id: policy.id,
customer_id: policy.customer_id,
claim_amount: claimAmount,
claim_date: randomDate(policy.start_date, new Date()),
status: randomNumber(0, 2), // 0: 待审核, 1: 已批准, 2: 已拒绝
claim_reason: '意外事故',
created_at: randomDate(policy.start_date, new Date()),
updated_at: new Date()
};
claims.push(claim);
}
}
// 清除现有理赔并插入新数据
await Claim.destroy({ where: {} });
const createdClaims = await Claim.bulkCreate(claims);
console.log(`已创建 ${createdClaims.length} 个理赔记录`);
console.log('数据览仓测试数据插入完成');
} catch (error) {
console.error('数据插入过程中发生错误:', error);
} finally {
// 关闭数据库连接
await sequelize.close();
}
}
// 执行数据初始化
seedData();

View File

@@ -0,0 +1,62 @@
const mysql = require('mysql2/promise');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
/**
* 初始化菜单数据脚本
* 该脚本会读取并执行init_menus.sql文件为数据库添加初始菜单数据
*/
async function seedMenus() {
let connection;
try {
// 创建数据库连接
connection = await mysql.createConnection({
host: process.env.DB_HOST || '129.211.213.226',
port: process.env.DB_PORT || 9527,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'aiotAiot123!',
database: process.env.DB_NAME || 'insurance_data'
});
console.log('✅ 数据库连接成功');
// 读取SQL脚本文件
const sqlFilePath = path.join(__dirname, 'init_menus.sql');
const sql = fs.readFileSync(sqlFilePath, 'utf8');
console.log('📄 读取SQL脚本成功');
// 执行SQL脚本
const [results] = await connection.query(sql);
console.log('🚀 菜单数据初始化成功');
// 查询并显示已插入的菜单数量
const [menusCount] = await connection.query('SELECT COUNT(*) as count FROM menus');
console.log(`✅ 共插入 ${menusCount[0].count} 条菜单记录`);
} catch (error) {
console.error('❌ 菜单数据初始化失败:', error.message);
throw error;
} finally {
// 关闭数据库连接
if (connection) {
await connection.end();
console.log('🔌 数据库连接已关闭');
}
}
}
// 执行脚本
seedMenus()
.then(() => {
console.log('✨ 菜单数据初始化任务已完成');
process.exit(0);
})
.catch(() => {
console.error('❌ 菜单数据初始化任务失败');
process.exit(1);
});