523 lines
14 KiB
JavaScript
523 lines
14 KiB
JavaScript
const { CattlePen, Farm, IotCattle, CattleType, CattleUser } = require('../models');
|
||
const { Op } = require('sequelize');
|
||
|
||
/**
|
||
* 栏舍设置控制器
|
||
*/
|
||
class CattlePenController {
|
||
/**
|
||
* 获取栏舍列表
|
||
*/
|
||
async getPens(req, res) {
|
||
try {
|
||
const { page = 1, pageSize = 10, search, name, status, type } = req.query;
|
||
const offset = (page - 1) * pageSize;
|
||
|
||
console.log('=== 获取栏舍列表 ===');
|
||
console.log('请求时间:', new Date().toISOString());
|
||
console.log('请求参数:', { page, pageSize, search, name, status, type });
|
||
console.log('请求来源:', req.ip);
|
||
|
||
// 构建查询条件
|
||
const where = {};
|
||
|
||
// 支持 search 和 name 参数(兼容性处理)
|
||
const searchKeyword = search || name;
|
||
if (searchKeyword) {
|
||
console.log('🔍 [后端-栏舍设置] 搜索关键词:', searchKeyword);
|
||
where[Op.or] = [
|
||
{ name: { [Op.like]: `%${searchKeyword}%` } },
|
||
{ code: { [Op.like]: `%${searchKeyword}%` } }
|
||
];
|
||
console.log('🔍 [后端-栏舍设置] 搜索条件构建完成');
|
||
}
|
||
|
||
if (status) {
|
||
where.status = status;
|
||
}
|
||
if (type) {
|
||
where.type = type;
|
||
}
|
||
|
||
console.log('🔍 [后端-栏舍设置] 构建的查询条件:', JSON.stringify(where, null, 2));
|
||
|
||
console.log('🔍 [后端-栏舍设置] 开始执行查询...');
|
||
const { count, rows } = await CattlePen.findAndCountAll({
|
||
where,
|
||
include: [
|
||
{
|
||
model: Farm,
|
||
as: 'farm',
|
||
attributes: ['id', 'name']
|
||
}
|
||
],
|
||
limit: parseInt(pageSize),
|
||
offset: parseInt(offset),
|
||
order: [['created_at', 'DESC']]
|
||
});
|
||
|
||
console.log('📊 [后端-栏舍设置] 查询结果:', {
|
||
总数: count,
|
||
当前页记录数: rows.length,
|
||
记录列表: rows.map(item => ({
|
||
id: item.id,
|
||
name: item.name,
|
||
code: item.code,
|
||
type: item.type,
|
||
status: item.status
|
||
}))
|
||
});
|
||
|
||
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 getPenById(req, res) {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const pen = await CattlePen.findByPk(id, {
|
||
include: [
|
||
{
|
||
model: Farm,
|
||
as: 'farm',
|
||
attributes: ['id', 'name']
|
||
}
|
||
]
|
||
});
|
||
|
||
if (!pen) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '栏舍不存在'
|
||
});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: pen,
|
||
message: '获取栏舍详情成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('获取栏舍详情失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取栏舍详情失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建栏舍
|
||
*/
|
||
async createPen(req, res) {
|
||
try {
|
||
console.log('🆕 [后端-栏舍设置] 开始创建操作');
|
||
console.log('📋 [后端-栏舍设置] 请求数据:', req.body);
|
||
|
||
const penData = req.body;
|
||
|
||
// 验证必填字段
|
||
if (!penData.name || !penData.code || !penData.type || !penData.capacity || !penData.area) {
|
||
console.log('❌ [后端-栏舍设置] 必填字段验证失败:', {
|
||
name: !!penData.name,
|
||
code: !!penData.code,
|
||
type: !!penData.type,
|
||
capacity: !!penData.capacity,
|
||
area: !!penData.area
|
||
});
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '请填写所有必填字段(栏舍名称、编号、类型、容量、面积)'
|
||
});
|
||
}
|
||
|
||
// 检查编号是否已存在
|
||
const existingPen = await CattlePen.findOne({
|
||
where: { code: penData.code }
|
||
});
|
||
|
||
if (existingPen) {
|
||
console.log('❌ [后端-栏舍设置] 栏舍编号已存在:', penData.code);
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '栏舍编号已存在'
|
||
});
|
||
}
|
||
|
||
// 准备创建数据
|
||
const createData = {
|
||
name: penData.name,
|
||
code: penData.code,
|
||
type: penData.type,
|
||
capacity: parseInt(penData.capacity),
|
||
currentCount: penData.currentCount ? parseInt(penData.currentCount) : 0,
|
||
area: parseFloat(penData.area),
|
||
location: penData.location || '',
|
||
status: penData.status || '启用',
|
||
remark: penData.remark || '',
|
||
farmId: penData.farmId || 1
|
||
};
|
||
|
||
console.log('📝 [后端-栏舍设置] 准备创建的数据:', createData);
|
||
|
||
const pen = await CattlePen.create(createData);
|
||
|
||
console.log('✅ [后端-栏舍设置] 栏舍创建成功:', pen.id);
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
data: pen,
|
||
message: '创建栏舍成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('❌ [后端-栏舍设置] 创建失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '创建栏舍失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新栏舍
|
||
*/
|
||
async updatePen(req, res) {
|
||
try {
|
||
const { id } = req.params;
|
||
const updateData = req.body;
|
||
|
||
const pen = await CattlePen.findByPk(id);
|
||
if (!pen) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '栏舍不存在'
|
||
});
|
||
}
|
||
|
||
// 如果更新编号,检查是否与其他栏舍冲突
|
||
if (updateData.code && updateData.code !== pen.code) {
|
||
const existingPen = await CattlePen.findOne({
|
||
where: {
|
||
code: updateData.code,
|
||
id: { [Op.ne]: id }
|
||
}
|
||
});
|
||
|
||
if (existingPen) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '栏舍编号已存在'
|
||
});
|
||
}
|
||
}
|
||
|
||
await pen.update(updateData);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: pen,
|
||
message: '更新栏舍成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('更新栏舍失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '更新栏舍失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除栏舍
|
||
*/
|
||
async deletePen(req, res) {
|
||
try {
|
||
console.log('🗑️ [后端-栏舍设置] 开始删除操作');
|
||
console.log('📋 [后端-栏舍设置] 请求参数:', req.params);
|
||
|
||
const { id } = req.params;
|
||
|
||
const pen = await CattlePen.findByPk(id);
|
||
if (!pen) {
|
||
console.log('❌ [后端-栏舍设置] 栏舍不存在,ID:', id);
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '栏舍不存在'
|
||
});
|
||
}
|
||
|
||
console.log('✅ [后端-栏舍设置] 找到栏舍:', {
|
||
id: pen.id,
|
||
name: pen.name,
|
||
code: pen.code
|
||
});
|
||
|
||
// 注意:由于IotCattle表中没有penId字段,暂时跳过牛只检查
|
||
// 在实际应用中,应该根据业务需求决定是否需要检查关联数据
|
||
|
||
await pen.destroy();
|
||
console.log('✅ [后端-栏舍设置] 栏舍删除成功');
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '删除栏舍成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('❌ [后端-栏舍设置] 删除失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '删除栏舍失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量删除栏舍
|
||
*/
|
||
async batchDeletePens(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 IotCattle.count({
|
||
where: { penId: { [Op.in]: ids } }
|
||
});
|
||
|
||
if (animalCount > 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '选中的栏舍中还有牛只,无法删除'
|
||
});
|
||
}
|
||
|
||
await CattlePen.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 getPenIotCattles(req, res) {
|
||
try {
|
||
const { id } = req.params;
|
||
const { page = 1, pageSize = 10 } = req.query;
|
||
const offset = (page - 1) * pageSize;
|
||
|
||
const pen = await CattlePen.findByPk(id);
|
||
if (!pen) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '栏舍不存在'
|
||
});
|
||
}
|
||
|
||
const { count, rows } = await IotCattle.findAndCountAll({
|
||
where: { penId: id },
|
||
limit: parseInt(pageSize),
|
||
offset: parseInt(offset),
|
||
order: [['created_at', 'DESC']]
|
||
});
|
||
|
||
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 getPenAnimals(req, res) {
|
||
try {
|
||
const { id } = req.params;
|
||
const { page = 1, pageSize = 10 } = req.query;
|
||
const offset = (page - 1) * pageSize;
|
||
|
||
// 检查栏舍是否存在
|
||
const pen = await CattlePen.findByPk(id);
|
||
if (!pen) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '栏舍不存在'
|
||
});
|
||
}
|
||
|
||
// 获取栏舍中的牛只
|
||
const { count, rows } = await IotCattle.findAndCountAll({
|
||
where: { penId: id },
|
||
attributes: [
|
||
'id',
|
||
'earNumber',
|
||
'sex',
|
||
'strain',
|
||
'varieties',
|
||
'birthday',
|
||
'parity',
|
||
'orgId'
|
||
],
|
||
include: [
|
||
{
|
||
model: Farm,
|
||
as: 'farm',
|
||
attributes: ['id', 'name']
|
||
}
|
||
],
|
||
limit: parseInt(pageSize),
|
||
offset: offset,
|
||
order: [['earNumber', 'ASC']]
|
||
});
|
||
|
||
// 获取品种和品系映射数据
|
||
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 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 transformedRows = rows.map(cattle => {
|
||
// 计算月龄(基于出生日期)
|
||
let ageInMonths = 0;
|
||
if (cattle.birthday) {
|
||
const birthDate = new Date(cattle.birthday * 1000); // 假设birthday是Unix时间戳
|
||
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,
|
||
farm: cattle.farm
|
||
};
|
||
});
|
||
|
||
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
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = new CattlePenController();
|