Files
nxxmdata/backend/controllers/cattleBatchController.js
2025-09-16 16:07:32 +08:00

664 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { CattleBatch, IotCattle, Farm, CattleBatchAnimal, User, CattleType, CattleUser, CattlePen } = require('../models');
const { Op } = require('sequelize');
/**
* 批次设置控制器
*/
class CattleBatchController {
/**
* 获取批次列表
*/
async getBatches(req, res) {
try {
const { page = 1, pageSize = 10, search, type, status } = req.query;
const offset = (page - 1) * pageSize;
console.log('🔍 [后端-批次设置] 搜索请求参数:', { page, pageSize, search, type, status });
// 构建查询条件
const where = {};
if (search) {
where[Op.or] = [
{ name: { [Op.like]: `%${search}%` } },
{ code: { [Op.like]: `%${search}%` } }
];
console.log('🔍 [后端-批次设置] 搜索条件:', where[Op.or]);
}
if (type) {
where.type = type;
}
if (status) {
where.status = status;
}
const { count, rows } = await CattleBatch.findAndCountAll({
where,
include: [
{
model: Farm,
as: 'farm',
attributes: ['id', 'name']
}
],
limit: parseInt(pageSize),
offset: offset,
order: [['created_at', 'DESC']]
});
console.log('🔍 [后端-批次设置] 查询结果:', {
总数: count,
当前页数据量: rows.length,
搜索关键词: search,
查询条件: where
});
res.json({
success: true,
data: {
list: rows,
total: count,
page: parseInt(page),
pageSize: parseInt(pageSize)
},
message: '获取批次列表成功'
});
} catch (error) {
console.error('获取批次列表失败:', error);
res.status(500).json({
success: false,
message: '获取批次列表失败',
error: error.message
});
}
}
/**
* 获取批次详情
*/
async getBatchById(req, res) {
try {
const { id } = req.params;
const batch = await CattleBatch.findByPk(id, {
include: [
{
model: Farm,
as: 'farm',
attributes: ['id', 'name']
}
]
});
if (!batch) {
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
res.json({
success: true,
data: batch,
message: '获取批次详情成功'
});
} catch (error) {
console.error('获取批次详情失败:', error);
res.status(500).json({
success: false,
message: '获取批次详情失败',
error: error.message
});
}
}
/**
* 创建批次
*/
async createBatch(req, res) {
try {
console.log('🆕 [后端-批次设置] 开始创建操作');
console.log('📋 [后端-批次设置] 请求数据:', req.body);
const {
name,
code,
type,
startDate,
expectedEndDate,
actualEndDate,
targetCount,
currentCount,
manager,
status,
remark,
farmId
} = req.body;
// 验证必填字段
if (!name || !code || !type || !startDate || !targetCount || !manager) {
console.log('❌ [后端-批次设置] 必填字段验证失败:', {
name: !!name,
code: !!code,
type: !!type,
startDate: !!startDate,
targetCount: !!targetCount,
manager: !!manager
});
return res.status(400).json({
success: false,
message: '请填写所有必填字段(批次名称、编号、类型、开始日期、目标数量、负责人)'
});
}
// 检查批次编号是否已存在
const existingBatch = await CattleBatch.findOne({
where: { code }
});
if (existingBatch) {
console.log('❌ [后端-批次设置] 批次编号已存在:', code);
return res.status(400).json({
success: false,
message: '批次编号已存在'
});
}
// 检查农场是否存在
const farm = await Farm.findByPk(farmId);
if (!farm) {
console.log('❌ [后端-批次设置] 农场不存在:', farmId);
return res.status(400).json({
success: false,
message: '农场不存在'
});
}
// 准备创建数据
const createData = {
name,
code,
type,
startDate: new Date(startDate),
expectedEndDate: expectedEndDate ? new Date(expectedEndDate) : null,
actualEndDate: actualEndDate ? new Date(actualEndDate) : null,
targetCount: parseInt(targetCount),
currentCount: currentCount ? parseInt(currentCount) : 0,
manager,
status: status || '进行中',
remark: remark || '',
farmId: farmId || 1
};
console.log('📝 [后端-批次设置] 准备创建的数据:', createData);
const batch = await CattleBatch.create(createData);
console.log('✅ [后端-批次设置] 批次创建成功:', batch.id);
res.status(201).json({
success: true,
data: batch,
message: '创建批次成功'
});
} catch (error) {
console.error('❌ [后端-批次设置] 创建失败:', error);
res.status(500).json({
success: false,
message: '创建批次失败',
error: error.message
});
}
}
/**
* 更新批次
*/
async updateBatch(req, res) {
try {
const { id } = req.params;
const updateData = req.body;
console.log('🔄 [后端-批次设置] 开始更新操作');
console.log('📋 [后端-批次设置] 请求参数:', {
batchId: id,
updateData: updateData
});
const batch = await CattleBatch.findByPk(id);
if (!batch) {
console.log('❌ [后端-批次设置] 批次不存在ID:', id);
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
console.log('📝 [后端-批次设置] 原始批次数据:', {
id: batch.id,
name: batch.name,
code: batch.code,
type: batch.type,
description: batch.description,
status: batch.status,
startDate: batch.startDate,
expectedEndDate: batch.expectedEndDate,
actualEndDate: batch.actualEndDate,
targetCount: batch.targetCount,
currentCount: batch.currentCount,
manager: batch.manager,
remark: batch.remark,
farmId: batch.farmId
});
// 如果更新编号,检查是否已存在
if (updateData.code && updateData.code !== batch.code) {
console.log('🔄 [后端-批次设置] 检测到编号变更,检查是否已存在');
console.log('📝 [后端-批次设置] 编号变更详情:', {
oldCode: batch.code,
newCode: updateData.code
});
const existingBatch = await CattleBatch.findOne({
where: { code: updateData.code, id: { [Op.ne]: id } }
});
if (existingBatch) {
console.log('❌ [后端-批次设置] 批次编号已存在');
return res.status(400).json({
success: false,
message: '批次编号已存在'
});
}
console.log('✅ [后端-批次设置] 批次编号可用');
}
await batch.update(updateData);
console.log('✅ [后端-批次设置] 批次更新成功');
res.json({
success: true,
data: batch,
message: '更新批次成功'
});
} catch (error) {
console.error('❌ [后端-批次设置] 更新失败:', error);
res.status(500).json({
success: false,
message: '更新批次失败',
error: error.message
});
}
}
/**
* 删除批次
*/
async deleteBatch(req, res) {
try {
const { id } = req.params;
const batch = await CattleBatch.findByPk(id);
if (!batch) {
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
// 检查是否有牛只在批次中
const animalCount = await CattleBatchAnimal.count({
where: { batchId: id }
});
if (animalCount > 0) {
return res.status(400).json({
success: false,
message: '批次中还有牛只,无法删除'
});
}
await batch.destroy();
res.json({
success: true,
message: '删除批次成功'
});
} catch (error) {
console.error('删除批次失败:', error);
res.status(500).json({
success: false,
message: '删除批次失败',
error: error.message
});
}
}
/**
* 批量删除批次
*/
async batchDeleteBatches(req, res) {
try {
const { ids } = req.body;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
return res.status(400).json({
success: false,
message: '请选择要删除的批次'
});
}
// 检查是否有批次包含牛只
const animalCount = await CattleBatchAnimal.count({
where: { batchId: { [Op.in]: ids } }
});
if (animalCount > 0) {
return res.status(400).json({
success: false,
message: '部分批次中还有牛只,无法删除'
});
}
await CattleBatch.destroy({
where: { id: { [Op.in]: ids } }
});
res.json({
success: true,
message: `成功删除 ${ids.length} 个批次`
});
} catch (error) {
console.error('批量删除批次失败:', error);
res.status(500).json({
success: false,
message: '批量删除批次失败',
error: error.message
});
}
}
/**
* 获取批次中的牛只
*/
async getBatchAnimals(req, res) {
try {
const { id } = req.params;
const { page = 1, pageSize = 10 } = req.query;
const offset = (page - 1) * pageSize;
console.log('🔍 开始获取批次牛只数据');
console.log('📋 批次信息:', { id, page, pageSize, offset });
// 检查批次是否存在
const batch = await CattleBatch.findByPk(id);
if (!batch) {
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
console.log('✅ 批次存在:', batch.name);
// 获取批次中的牛只直接通过batchId字段查询
const { count, rows } = await IotCattle.findAndCountAll({
where: { batchId: id },
attributes: [
'id',
'earNumber',
'sex',
'strain',
'varieties',
'birthday',
'parity',
'orgId',
'penId'
],
include: [
{
model: Farm,
as: 'farm',
attributes: ['id', 'name']
}
],
limit: parseInt(pageSize),
offset: offset,
order: [['earNumber', 'ASC']]
});
console.log(`📊 查询结果: 总记录数=${count}, 返回记录数=${rows.length}`);
// 获取品种和品系映射数据
const typeIds = [...new Set(rows.map(cattle => cattle.varieties).filter(id => id))];
const strainIds = [...new Set(rows.map(cattle => cattle.strain).filter(id => id))];
const penIds = [...new Set(rows.map(cattle => cattle.penId).filter(id => id))];
const typeNames = {};
if (typeIds.length > 0) {
const types = await CattleType.findAll({
where: { id: typeIds },
attributes: ['id', 'name']
});
types.forEach(type => {
typeNames[type.id] = type.name;
});
}
const userNames = {};
if (strainIds.length > 0) {
const users = await CattleUser.findAll({
where: { id: strainIds },
attributes: ['id', 'name']
});
users.forEach(user => {
userNames[user.id] = user.name;
});
}
const penNames = {};
if (penIds.length > 0) {
const pens = await CattlePen.findAll({
where: { id: penIds },
attributes: ['id', 'name']
});
pens.forEach(pen => {
penNames[pen.id] = pen.name;
});
}
// 转换数据格式,添加计算字段
const transformedRows = rows.map(cattle => {
// 计算月龄(基于出生日期)
let ageInMonths = 0;
if (cattle.birthday) {
const birthDate = new Date(cattle.birthday * 1000);
const now = new Date();
ageInMonths = Math.floor((now - birthDate) / (1000 * 60 * 60 * 24 * 30));
}
// 性别转换
const genderMap = { 1: '公', 2: '母', 0: '未知' };
const gender = genderMap[cattle.sex] || '未知';
// 品种转换(动态查询)
const breed = typeNames[cattle.varieties] || `品种ID:${cattle.varieties}`;
// 生理阶段判断
let physiologicalStage = '未知';
if (ageInMonths < 6) {
physiologicalStage = '犊牛';
} else if (ageInMonths < 12) {
physiologicalStage = '育成牛';
} else if (ageInMonths < 24) {
physiologicalStage = '青年牛';
} else if (cattle.sex === 2) {
if (cattle.parity > 0) {
physiologicalStage = '泌乳牛';
} else {
physiologicalStage = '后备母牛';
}
} else {
physiologicalStage = '种公牛';
}
return {
id: cattle.id,
earTag: cattle.earNumber,
breed: breed,
gender: gender,
ageInMonths: ageInMonths,
physiologicalStage: physiologicalStage,
pen: cattle.penId ? (penNames[cattle.penId] || `栏舍ID:${cattle.penId}`) : '未分配栏舍',
farm: cattle.farm
};
});
console.log('🔄 转换后的数据示例:', transformedRows.slice(0, 2));
res.json({
success: true,
data: {
list: transformedRows,
total: count,
page: parseInt(page),
pageSize: parseInt(pageSize)
},
message: '获取批次牛只成功'
});
} catch (error) {
console.error('获取批次牛只失败:', error);
res.status(500).json({
success: false,
message: '获取批次牛只失败',
error: error.message
});
}
}
/**
* 添加牛只到批次
*/
async addAnimalsToBatch(req, res) {
try {
const { id } = req.params;
const { animalIds } = req.body;
if (!animalIds || !Array.isArray(animalIds) || animalIds.length === 0) {
return res.status(400).json({
success: false,
message: '请选择要添加的牛只'
});
}
// 检查批次是否存在
const batch = await CattleBatch.findByPk(id);
if (!batch) {
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
// 检查牛只是否存在
const animals = await IotCattle.findAll({
where: { id: { [Op.in]: animalIds } }
});
if (animals.length !== animalIds.length) {
return res.status(400).json({
success: false,
message: '部分牛只不存在'
});
}
// 检查哪些牛只已经在批次中
const existingAssociations = await CattleBatchAnimal.findAll({
where: {
batchId: id,
animalId: { [Op.in]: animalIds }
}
});
const existingAnimalIds = existingAssociations.map(assoc => assoc.animalId);
const newAnimalIds = animalIds.filter(id => !existingAnimalIds.includes(id));
if (newAnimalIds.length === 0) {
return res.status(400).json({
success: false,
message: '所有牛只都已在该批次中'
});
}
// 添加新的关联
const associations = newAnimalIds.map(animalId => ({
batchId: id,
animalId: animalId,
joinDate: new Date()
}));
await CattleBatchAnimal.bulkCreate(associations);
res.json({
success: true,
message: `成功添加 ${newAnimalIds.length} 头牛只到批次`,
data: {
addedCount: newAnimalIds.length,
skippedCount: existingAnimalIds.length
}
});
} catch (error) {
console.error('添加牛只到批次失败:', error);
res.status(500).json({
success: false,
message: '添加牛只到批次失败',
error: error.message
});
}
}
/**
* 从批次中移除牛只
*/
async removeAnimalFromBatch(req, res) {
try {
const { id, animalId } = req.params;
// 检查批次是否存在
const batch = await CattleBatch.findByPk(id);
if (!batch) {
return res.status(404).json({
success: false,
message: '批次不存在'
});
}
// 检查关联是否存在
const association = await CattleBatchAnimal.findOne({
where: { batchId: id, animalId }
});
if (!association) {
return res.status(404).json({
success: false,
message: '牛只不在该批次中'
});
}
await association.destroy();
res.json({
success: true,
message: '从批次中移除牛只成功'
});
} catch (error) {
console.error('从批次中移除牛只失败:', error);
res.status(500).json({
success: false,
message: '从批次中移除牛只失败',
error: error.message
});
}
}
}
module.exports = new CattleBatchController();