部署保险端项目和大屏
This commit is contained in:
143
insurance_backend/models/Claim.js
Normal file
143
insurance_backend/models/Claim.js
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
|
||||
87
insurance_backend/models/Device.js
Normal file
87
insurance_backend/models/Device.js
Normal file
@@ -0,0 +1,87 @@
|
||||
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;
|
||||
|
||||
218
insurance_backend/models/LivestockPolicy.js
Normal file
218
insurance_backend/models/LivestockPolicy.js
Normal file
@@ -0,0 +1,218 @@
|
||||
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;
|
||||
|
||||
102
insurance_backend/models/LivestockType.js
Normal file
102
insurance_backend/models/LivestockType.js
Normal file
@@ -0,0 +1,102 @@
|
||||
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;
|
||||
|
||||
85
insurance_backend/models/Menu.js
Normal file
85
insurance_backend/models/Menu.js
Normal file
@@ -0,0 +1,85 @@
|
||||
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;
|
||||
43
insurance_backend/models/MenuPermission.js
Normal file
43
insurance_backend/models/MenuPermission.js
Normal file
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
|
||||
271
insurance_backend/models/OperationLog.js
Normal file
271
insurance_backend/models/OperationLog.js
Normal file
@@ -0,0 +1,271 @@
|
||||
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;
|
||||
|
||||
94
insurance_backend/models/Permission.js
Normal file
94
insurance_backend/models/Permission.js
Normal file
@@ -0,0 +1,94 @@
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
const bcrypt = require('bcrypt');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
const User = sequelize.define('User', {
|
||||
id: {
|
||||
|
||||
Reference in New Issue
Block a user