修改管理后台

This commit is contained in:
shenquanyi
2025-09-12 20:08:42 +08:00
parent 39d61c6f9b
commit 80a24c2d60
286 changed files with 75316 additions and 9452 deletions

View File

@@ -0,0 +1,251 @@
/**
* 系统管理功能数据库迁移
* @file 20250118000000_system_management.js
* @description 创建系统配置和菜单权限管理表
*/
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// 创建系统配置表
await queryInterface.createTable('system_configs', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '配置ID'
},
config_key: {
type: Sequelize.STRING(100),
allowNull: false,
unique: true,
comment: '配置键名'
},
config_value: {
type: Sequelize.TEXT,
allowNull: true,
comment: '配置值'
},
config_type: {
type: Sequelize.ENUM('string', 'number', 'boolean', 'json', 'array'),
allowNull: false,
defaultValue: 'string',
comment: '配置类型'
},
category: {
type: Sequelize.STRING(50),
allowNull: false,
defaultValue: 'general',
comment: '配置分类'
},
description: {
type: Sequelize.STRING(255),
allowNull: true,
comment: '配置描述'
},
is_public: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
comment: '是否公开(前端可访问)'
},
is_editable: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否可编辑'
},
sort_order: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序顺序'
},
updated_by: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '最后更新人ID'
},
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')
}
}, {
transaction,
comment: '系统配置表'
});
// 创建菜单权限表
await queryInterface.createTable('menu_permissions', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '权限ID'
},
menu_key: {
type: Sequelize.STRING(100),
allowNull: false,
unique: true,
comment: '菜单标识'
},
menu_name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '菜单名称'
},
menu_path: {
type: Sequelize.STRING(200),
allowNull: true,
comment: '菜单路径'
},
parent_id: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '父菜单ID'
},
menu_type: {
type: Sequelize.ENUM('page', 'button', 'api'),
allowNull: false,
defaultValue: 'page',
comment: '菜单类型'
},
required_roles: {
type: Sequelize.TEXT,
allowNull: true,
comment: '所需角色JSON数组'
},
required_permissions: {
type: Sequelize.TEXT,
allowNull: true,
comment: '所需权限JSON数组'
},
icon: {
type: Sequelize.STRING(50),
allowNull: true,
comment: '菜单图标'
},
sort_order: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '排序顺序'
},
is_visible: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否可见'
},
is_enabled: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否启用'
},
description: {
type: Sequelize.STRING(255),
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')
}
}, {
transaction,
comment: '菜单权限表'
});
// 添加索引
await queryInterface.addIndex('system_configs', ['config_key'], {
unique: true,
transaction
});
await queryInterface.addIndex('system_configs', ['category'], {
transaction
});
await queryInterface.addIndex('system_configs', ['is_public'], {
transaction
});
await queryInterface.addIndex('menu_permissions', ['menu_key'], {
unique: true,
transaction
});
await queryInterface.addIndex('menu_permissions', ['parent_id'], {
transaction
});
await queryInterface.addIndex('menu_permissions', ['menu_type'], {
transaction
});
await queryInterface.addIndex('menu_permissions', ['sort_order'], {
transaction
});
// 添加外键约束
await queryInterface.addConstraint('menu_permissions', {
fields: ['parent_id'],
type: 'foreign key',
name: 'fk_menu_permissions_parent_id',
references: {
table: 'menu_permissions',
field: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
transaction
});
await transaction.commit();
console.log('系统管理表创建成功');
} catch (error) {
await transaction.rollback();
console.error('系统管理表创建失败:', error);
throw error;
}
},
down: async (queryInterface, Sequelize) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// 删除外键约束
await queryInterface.removeConstraint('menu_permissions', 'fk_menu_permissions_parent_id', {
transaction
});
// 删除表
await queryInterface.dropTable('menu_permissions', { transaction });
await queryInterface.dropTable('system_configs', { transaction });
await transaction.commit();
console.log('系统管理表删除成功');
} catch (error) {
await transaction.rollback();
console.error('系统管理表删除失败:', error);
throw error;
}
}
};

View File

@@ -0,0 +1,157 @@
/**
* 电子围栏表迁移
* 创建电子围栏相关表结构
*/
module.exports = {
up: async (queryInterface, Sequelize) => {
// 创建电子围栏表
await queryInterface.createTable('electronic_fences', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '围栏ID'
},
name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '围栏名称'
},
type: {
type: Sequelize.ENUM('collector', 'grazing', 'safety'),
allowNull: false,
defaultValue: 'collector',
comment: '围栏类型: collector-采集器电子围栏, grazing-放牧围栏, safety-安全围栏'
},
description: {
type: Sequelize.TEXT,
allowNull: true,
comment: '围栏描述'
},
coordinates: {
type: Sequelize.JSON,
allowNull: false,
comment: '围栏坐标点数组'
},
center_lng: {
type: Sequelize.DECIMAL(10, 7),
allowNull: false,
comment: '围栏中心经度'
},
center_lat: {
type: Sequelize.DECIMAL(10, 7),
allowNull: false,
comment: '围栏中心纬度'
},
area: {
type: Sequelize.DECIMAL(10, 4),
allowNull: true,
comment: '围栏面积(平方米)'
},
grazing_status: {
type: Sequelize.ENUM('grazing', 'not_grazing'),
allowNull: false,
defaultValue: 'not_grazing',
comment: '放牧状态: grazing-放牧中, not_grazing-未放牧'
},
inside_count: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '安全区域内动物数量'
},
outside_count: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '安全区域外动物数量'
},
is_active: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否启用'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '关联农场ID',
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
created_by: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '创建人ID',
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
updated_by: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '更新人ID',
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '更新时间'
}
}, {
comment: '电子围栏表',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
});
// 创建索引
await queryInterface.addIndex('electronic_fences', ['name'], {
name: 'idx_fence_name'
});
await queryInterface.addIndex('electronic_fences', ['type'], {
name: 'idx_fence_type'
});
await queryInterface.addIndex('electronic_fences', ['center_lng', 'center_lat'], {
name: 'idx_fence_center'
});
await queryInterface.addIndex('electronic_fences', ['is_active'], {
name: 'idx_fence_active'
});
await queryInterface.addIndex('electronic_fences', ['farm_id'], {
name: 'idx_fence_farm_id'
});
await queryInterface.addIndex('electronic_fences', ['grazing_status'], {
name: 'idx_fence_grazing_status'
});
},
down: async (queryInterface, Sequelize) => {
// 删除表
await queryInterface.dropTable('electronic_fences');
}
};

View File

@@ -0,0 +1,124 @@
/**
* 电子围栏坐标点数据表迁移
* 用于存储围栏绘制过程中用户选定的经纬度坐标点
*/
module.exports = {
up: async (queryInterface, Sequelize) => {
// 创建电子围栏坐标点表
await queryInterface.createTable('electronic_fence_points', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '主键ID'
},
fence_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '关联的围栏ID',
references: {
model: 'electronic_fences',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
point_order: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '坐标点在围栏中的顺序从0开始'
},
longitude: {
type: Sequelize.DECIMAL(10, 7),
allowNull: false,
comment: '经度'
},
latitude: {
type: Sequelize.DECIMAL(10, 7),
allowNull: false,
comment: '纬度'
},
point_type: {
type: Sequelize.ENUM('corner', 'control', 'marker'),
allowNull: false,
defaultValue: 'corner',
comment: '坐标点类型corner-拐角点control-控制点marker-标记点'
},
description: {
type: Sequelize.TEXT,
allowNull: true,
comment: '坐标点描述信息'
},
is_active: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '是否激活'
},
created_by: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '创建人ID',
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
updated_by: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '更新人ID',
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '更新时间'
}
});
// 添加索引
await queryInterface.addIndex('electronic_fence_points', ['fence_id'], {
name: 'idx_fence_points_fence_id'
});
await queryInterface.addIndex('electronic_fence_points', ['fence_id', 'point_order'], {
name: 'idx_fence_points_fence_order'
});
await queryInterface.addIndex('electronic_fence_points', ['longitude', 'latitude'], {
name: 'idx_fence_points_coordinates'
});
await queryInterface.addIndex('electronic_fence_points', ['point_type'], {
name: 'idx_fence_points_type'
});
await queryInterface.addIndex('electronic_fence_points', ['is_active'], {
name: 'idx_fence_points_active'
});
console.log('✅ 电子围栏坐标点表创建成功');
},
down: async (queryInterface, Sequelize) => {
// 删除表
await queryInterface.dropTable('electronic_fence_points');
console.log('✅ 电子围栏坐标点表删除成功');
}
};

View File

@@ -0,0 +1,115 @@
/**
* 创建栏舍表迁移
* @file 20250118000003_create_pens_table.js
* @description 创建栏舍管理表
*/
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('pens', {
id: {
type: Sequelize.BIGINT,
primaryKey: true,
autoIncrement: true,
comment: '栏舍ID'
},
name: {
type: Sequelize.STRING(50),
allowNull: false,
comment: '栏舍名称'
},
animal_type: {
type: Sequelize.ENUM('马', '牛', '羊', '家禽', '猪'),
allowNull: false,
comment: '动物类型'
},
pen_type: {
type: Sequelize.STRING(50),
allowNull: true,
comment: '栏舍类型'
},
responsible: {
type: Sequelize.STRING(20),
allowNull: false,
comment: '负责人'
},
capacity: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1,
comment: '容量'
},
status: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
comment: '状态true-开启false-关闭'
},
description: {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注信息'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '所属农场ID'
},
creator: {
type: Sequelize.STRING(50),
allowNull: false,
defaultValue: 'admin',
comment: '创建人'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '更新时间'
}
}, {
comment: '栏舍管理表',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
});
// 添加索引
await queryInterface.addIndex('pens', ['name'], {
name: 'idx_pens_name'
});
await queryInterface.addIndex('pens', ['animal_type'], {
name: 'idx_pens_animal_type'
});
await queryInterface.addIndex('pens', ['farm_id'], {
name: 'idx_pens_farm_id'
});
await queryInterface.addIndex('pens', ['status'], {
name: 'idx_pens_status'
});
await queryInterface.addIndex('pens', ['created_at'], {
name: 'idx_pens_created_at'
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('pens');
}
};

View File

@@ -0,0 +1,89 @@
/**
* 更新栏舍动物类型枚举值
* 将中文枚举值改为英文枚举值
*/
module.exports = {
up: async (queryInterface, Sequelize) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// 首先更新现有数据
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = 'horse' WHERE animal_type = '马';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = 'cattle' WHERE animal_type = '牛';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = 'sheep' WHERE animal_type = '羊';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = 'poultry' WHERE animal_type = '家禽';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = 'pig' WHERE animal_type = '猪';
`, { transaction });
// 修改枚举类型
await queryInterface.changeColumn('pens', 'animal_type', {
type: Sequelize.ENUM('horse', 'cattle', 'sheep', 'poultry', 'pig'),
allowNull: false,
comment: '动物类型'
}, { transaction });
await transaction.commit();
console.log('栏舍动物类型枚举值更新成功');
} catch (error) {
await transaction.rollback();
console.error('栏舍动物类型枚举值更新失败:', error);
throw error;
}
},
down: async (queryInterface, Sequelize) => {
const transaction = await queryInterface.sequelize.transaction();
try {
// 恢复中文枚举值
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = '马' WHERE animal_type = 'horse';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = '牛' WHERE animal_type = 'cattle';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = '羊' WHERE animal_type = 'sheep';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = '家禽' WHERE animal_type = 'poultry';
`, { transaction });
await queryInterface.sequelize.query(`
UPDATE pens SET animal_type = '猪' WHERE animal_type = 'pig';
`, { transaction });
// 恢复原始枚举类型
await queryInterface.changeColumn('pens', 'animal_type', {
type: Sequelize.ENUM('马', '牛', '羊', '家禽', '猪'),
allowNull: false,
comment: '动物类型'
}, { transaction });
await transaction.commit();
console.log('栏舍动物类型枚举值回滚成功');
} catch (error) {
await transaction.rollback();
console.error('栏舍动物类型枚举值回滚失败:', error);
throw error;
}
}
};

View File

@@ -0,0 +1,106 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('cattle_pens', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '栏舍名称'
},
code: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true,
comment: '栏舍编号'
},
type: {
type: Sequelize.ENUM('育成栏', '产房', '配种栏', '隔离栏', '治疗栏'),
allowNull: false,
comment: '栏舍类型'
},
capacity: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '栏舍容量'
},
current_count: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '当前牛只数量'
},
area: {
type: Sequelize.DECIMAL(10, 2),
allowNull: false,
comment: '面积(平方米)'
},
location: {
type: Sequelize.TEXT,
allowNull: true,
comment: '位置描述'
},
status: {
type: Sequelize.ENUM('启用', '停用'),
allowNull: false,
defaultValue: '启用',
comment: '状态'
},
remark: {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '所属农场ID'
},
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')
}
}, {
engine: 'InnoDB',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '栏舍设置表'
});
// 添加索引
await queryInterface.addIndex('cattle_pens', ['code'], {
name: 'idx_cattle_pens_code',
unique: true
});
await queryInterface.addIndex('cattle_pens', ['farm_id'], {
name: 'idx_cattle_pens_farm_id'
});
await queryInterface.addIndex('cattle_pens', ['status'], {
name: 'idx_cattle_pens_status'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('cattle_pens');
}
};

View File

@@ -0,0 +1,120 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('cattle_batches', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
name: {
type: Sequelize.STRING(200),
allowNull: false,
comment: '批次名称'
},
code: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true,
comment: '批次编号'
},
type: {
type: Sequelize.ENUM('育成批次', '繁殖批次', '育肥批次', '隔离批次', '治疗批次'),
allowNull: false,
comment: '批次类型'
},
start_date: {
type: Sequelize.DATE,
allowNull: false,
comment: '开始日期'
},
expected_end_date: {
type: Sequelize.DATE,
allowNull: true,
comment: '预计结束日期'
},
actual_end_date: {
type: Sequelize.DATE,
allowNull: true,
comment: '实际结束日期'
},
target_count: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '目标牛只数量'
},
current_count: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
comment: '当前牛只数量'
},
manager: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '负责人'
},
status: {
type: Sequelize.ENUM('进行中', '已完成', '已暂停'),
allowNull: false,
defaultValue: '进行中',
comment: '状态'
},
remark: {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '所属农场ID'
},
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')
}
}, {
engine: 'InnoDB',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '批次设置表'
});
// 添加索引
await queryInterface.addIndex('cattle_batches', ['code'], {
name: 'idx_cattle_batches_code',
unique: true
});
await queryInterface.addIndex('cattle_batches', ['farm_id'], {
name: 'idx_cattle_batches_farm_id'
});
await queryInterface.addIndex('cattle_batches', ['status'], {
name: 'idx_cattle_batches_status'
});
await queryInterface.addIndex('cattle_batches', ['type'], {
name: 'idx_cattle_batches_type'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('cattle_batches');
}
};

View File

@@ -0,0 +1,87 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('cattle_batch_animals', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
batch_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'cattle_batches',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '批次ID'
},
animal_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'animals',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '动物ID'
},
added_date: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: '添加日期'
},
added_by: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '添加人ID'
},
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')
}
}, {
engine: 'InnoDB',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '批次牛只关联表'
});
// 添加索引
await queryInterface.addIndex('cattle_batch_animals', ['batch_id'], {
name: 'idx_cattle_batch_animals_batch_id'
});
await queryInterface.addIndex('cattle_batch_animals', ['animal_id'], {
name: 'idx_cattle_batch_animals_animal_id'
});
// 添加唯一约束,防止重复添加
await queryInterface.addIndex('cattle_batch_animals', ['batch_id', 'animal_id'], {
name: 'idx_cattle_batch_animals_unique',
unique: true
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('cattle_batch_animals');
}
};

View File

@@ -0,0 +1,135 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('cattle_transfer_records', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
record_id: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true,
comment: '记录编号'
},
animal_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'animals',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '动物ID'
},
from_pen_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'cattle_pens',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '转出栏舍ID'
},
to_pen_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'cattle_pens',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '转入栏舍ID'
},
transfer_date: {
type: Sequelize.DATE,
allowNull: false,
comment: '转栏日期'
},
reason: {
type: Sequelize.ENUM('正常调栏', '疾病治疗', '配种需要', '产房准备', '隔离观察', '其他'),
allowNull: false,
comment: '转栏原因'
},
operator: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '操作人员'
},
status: {
type: Sequelize.ENUM('已完成', '进行中'),
allowNull: false,
defaultValue: '已完成',
comment: '状态'
},
remark: {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '所属农场ID'
},
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')
}
}, {
engine: 'InnoDB',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '转栏记录表'
});
// 添加索引
await queryInterface.addIndex('cattle_transfer_records', ['record_id'], {
name: 'idx_cattle_transfer_records_record_id',
unique: true
});
await queryInterface.addIndex('cattle_transfer_records', ['animal_id'], {
name: 'idx_cattle_transfer_records_animal_id'
});
await queryInterface.addIndex('cattle_transfer_records', ['from_pen_id'], {
name: 'idx_cattle_transfer_records_from_pen_id'
});
await queryInterface.addIndex('cattle_transfer_records', ['to_pen_id'], {
name: 'idx_cattle_transfer_records_to_pen_id'
});
await queryInterface.addIndex('cattle_transfer_records', ['transfer_date'], {
name: 'idx_cattle_transfer_records_transfer_date'
});
await queryInterface.addIndex('cattle_transfer_records', ['status'], {
name: 'idx_cattle_transfer_records_status'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('cattle_transfer_records');
}
};

View File

@@ -0,0 +1,134 @@
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('cattle_exit_records', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
record_id: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true,
comment: '记录编号'
},
animal_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'animals',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '动物ID'
},
exit_date: {
type: Sequelize.DATE,
allowNull: false,
comment: '离栏日期'
},
exit_reason: {
type: Sequelize.ENUM('出售', '死亡', '淘汰', '转场', '其他'),
allowNull: false,
comment: '离栏原因'
},
original_pen_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'cattle_pens',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
comment: '原栏舍ID'
},
destination: {
type: Sequelize.STRING(200),
allowNull: false,
comment: '去向'
},
disposal_method: {
type: Sequelize.ENUM('屠宰', '转售', '掩埋', '焚烧', '其他'),
allowNull: false,
comment: '处理方式'
},
handler: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '处理人员'
},
status: {
type: Sequelize.ENUM('已确认', '待确认', '已取消'),
allowNull: false,
defaultValue: '待确认',
comment: '状态'
},
remark: {
type: Sequelize.TEXT,
allowNull: true,
comment: '备注'
},
farm_id: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'farms',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: '所属农场ID'
},
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')
}
}, {
engine: 'InnoDB',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: '离栏记录表'
});
// 添加索引
await queryInterface.addIndex('cattle_exit_records', ['record_id'], {
name: 'idx_cattle_exit_records_record_id',
unique: true
});
await queryInterface.addIndex('cattle_exit_records', ['animal_id'], {
name: 'idx_cattle_exit_records_animal_id'
});
await queryInterface.addIndex('cattle_exit_records', ['original_pen_id'], {
name: 'idx_cattle_exit_records_original_pen_id'
});
await queryInterface.addIndex('cattle_exit_records', ['exit_date'], {
name: 'idx_cattle_exit_records_exit_date'
});
await queryInterface.addIndex('cattle_exit_records', ['exit_reason'], {
name: 'idx_cattle_exit_records_exit_reason'
});
await queryInterface.addIndex('cattle_exit_records', ['status'], {
name: 'idx_cattle_exit_records_status'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('cattle_exit_records');
}
};

View File

@@ -0,0 +1,153 @@
/**
* 创建操作日志表迁移
* @file 20250118000010_create_operation_logs_table.js
* @description 创建操作日志表,用于记录系统用户的操作记录
*/
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('operation_logs', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: '主键ID'
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
comment: '操作用户ID'
},
username: {
type: Sequelize.STRING(50),
allowNull: false,
comment: '操作用户名'
},
user_role: {
type: Sequelize.STRING(50),
allowNull: false,
comment: '操作用户角色'
},
operation_type: {
type: Sequelize.ENUM('CREATE', 'UPDATE', 'DELETE'),
allowNull: false,
comment: '操作类型CREATE-新增UPDATE-编辑DELETE-删除'
},
module_name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '操作模块名称'
},
table_name: {
type: Sequelize.STRING(100),
allowNull: false,
comment: '操作的数据表名'
},
record_id: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '操作的记录ID'
},
operation_desc: {
type: Sequelize.TEXT,
allowNull: false,
comment: '操作描述'
},
old_data: {
type: Sequelize.JSON,
allowNull: true,
comment: '操作前的数据(编辑和删除时记录)'
},
new_data: {
type: Sequelize.JSON,
allowNull: true,
comment: '操作后的数据(新增和编辑时记录)'
},
ip_address: {
type: Sequelize.STRING(45),
allowNull: true,
comment: '操作IP地址'
},
user_agent: {
type: Sequelize.TEXT,
allowNull: true,
comment: '用户代理信息'
},
request_url: {
type: Sequelize.STRING(500),
allowNull: true,
comment: '请求URL'
},
request_method: {
type: Sequelize.STRING(10),
allowNull: true,
comment: '请求方法'
},
response_status: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '响应状态码'
},
execution_time: {
type: Sequelize.INTEGER,
allowNull: true,
comment: '执行时间(毫秒)'
},
error_message: {
type: Sequelize.TEXT,
allowNull: true,
comment: '错误信息(如果有)'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '创建时间'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
comment: '更新时间'
}
}, {
comment: '操作日志表',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
});
// 创建索引
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', ['module_name'], {
name: 'idx_operation_logs_module_name'
});
await queryInterface.addIndex('operation_logs', ['table_name'], {
name: 'idx_operation_logs_table_name'
});
await queryInterface.addIndex('operation_logs', ['created_at'], {
name: 'idx_operation_logs_created_at'
});
await queryInterface.addIndex('operation_logs', ['user_id', 'operation_type'], {
name: 'idx_operation_logs_user_operation'
});
await queryInterface.addIndex('operation_logs', ['module_name', 'operation_type'], {
name: 'idx_operation_logs_module_operation'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('operation_logs');
}
};

View File

@@ -0,0 +1,55 @@
/**
* 更新操作日志表操作类型枚举
* @file 20250118000011_update_operation_logs_enum.js
* @description 添加更多操作类型到操作日志表
*/
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
try {
console.log('开始更新操作日志表操作类型枚举...');
// 修改操作类型枚举
await queryInterface.changeColumn('operation_logs', 'operation_type', {
type: Sequelize.ENUM(
'CREATE',
'UPDATE',
'DELETE',
'READ',
'LOGIN',
'LOGOUT',
'EXPORT',
'IMPORT',
'BATCH_DELETE',
'BATCH_UPDATE'
),
allowNull: false,
comment: '操作类型CREATE-新增UPDATE-编辑DELETE-删除READ-查看LOGIN-登录LOGOUT-登出EXPORT-导出IMPORT-导入BATCH_DELETE-批量删除BATCH_UPDATE-批量更新'
});
console.log('✅ 操作日志表操作类型枚举更新成功');
} catch (error) {
console.error('❌ 更新操作日志表操作类型枚举失败:', error);
throw error;
}
},
async down(queryInterface, Sequelize) {
try {
console.log('开始回滚操作日志表操作类型枚举...');
// 回滚到原始枚举
await queryInterface.changeColumn('operation_logs', 'operation_type', {
type: Sequelize.ENUM('CREATE', 'UPDATE', 'DELETE'),
allowNull: false,
comment: '操作类型CREATE-新增UPDATE-编辑DELETE-删除'
});
console.log('✅ 操作日志表操作类型枚举回滚成功');
} catch (error) {
console.error('❌ 回滚操作日志表操作类型枚举失败:', error);
throw error;
}
}
};