/** * 绑定信息控制器 * @file bindingController.js * @description 处理耳标与牛只档案的绑定信息查询 */ const { IotJbqClient, IotCattle, Farm, CattlePen, CattleBatch } = require('../models'); const { Op } = require('sequelize'); /** * 获取耳标绑定信息 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ const getBindingInfo = async (req, res) => { try { const { cid } = req.params; if (!cid) { return res.status(400).json({ success: false, message: '耳标编号不能为空', data: null }); } // 查询耳标信息 const jbqDevice = await IotJbqClient.findOne({ where: { cid: cid }, attributes: [ 'id', 'cid', 'aaid', 'org_id', 'uid', 'time', 'uptime', 'sid', 'walk', 'y_steps', 'r_walk', 'lat', 'lon', 'gps_state', 'voltage', 'temperature', 'temperature_two', 'state', 'type', 'sort', 'ver', 'weight', 'start_time', 'run_days', 'zenowalk', 'zenotime', 'is_read', 'read_end_time', 'bank_userid', 'bank_item_id', 'bank_house', 'bank_lanwei', 'bank_place', 'is_home', 'distribute_time', 'bandge_status', 'is_wear', 'is_temperature', 'source_id', 'expire_time' ] }); if (!jbqDevice) { return res.status(404).json({ success: false, message: '未找到指定的耳标设备', data: null }); } // 查询绑定的牛只档案信息 const cattleInfo = await IotCattle.findOne({ where: { ear_number: cid }, include: [ { model: Farm, as: 'farm', attributes: ['id', 'name', 'address', 'contact', 'phone'] }, { model: CattlePen, as: 'pen', attributes: ['id', 'name', 'description'] }, { model: CattleBatch, as: 'batch', attributes: ['id', 'name', 'description', 'start_date', 'end_date'] } ], attributes: [ 'id', 'orgId', 'earNumber', 'sex', 'strain', 'varieties', 'cate', 'birthWeight', 'birthday', 'penId', 'intoTime', 'parity', 'source', 'sourceDay', 'sourceWeight', 'weight', 'event', 'eventTime', 'lactationDay', 'semenNum', 'isWear', 'batchId', 'imgs', 'isEleAuth', 'isQuaAuth', 'isDelete', 'isOut', 'createUid', 'createTime', 'algebra', 'colour', 'infoWeight', 'descent', 'isVaccin', 'isInsemination', 'isInsure', 'isMortgage', 'updateTime', 'breedBullTime', 'level', 'sixWeight', 'eighteenWeight', 'twelveDayWeight', 'eighteenDayWeight', 'xxivDayWeight', 'semenBreedImgs', 'sellStatus', 'weightCalculateTime', 'dayOfBirthday' ] }); // 构建响应数据 const bindingInfo = { device: { id: jbqDevice.id, cid: jbqDevice.cid, aaid: jbqDevice.aaid, orgId: jbqDevice.org_id, uid: jbqDevice.uid, time: jbqDevice.time, uptime: jbqDevice.uptime, sid: jbqDevice.sid, walk: jbqDevice.walk, ySteps: jbqDevice.y_steps, rWalk: jbqDevice.r_walk, lat: jbqDevice.lat, lon: jbqDevice.lon, gpsState: jbqDevice.gps_state, voltage: jbqDevice.voltage, temperature: jbqDevice.temperature, temperatureTwo: jbqDevice.temperature_two, state: jbqDevice.state, type: jbqDevice.type, sort: jbqDevice.sort, ver: jbqDevice.ver, weight: jbqDevice.weight, startTime: jbqDevice.start_time, runDays: jbqDevice.run_days, zenowalk: jbqDevice.zenowalk, zenotime: jbqDevice.zenotime, isRead: jbqDevice.is_read, readEndTime: jbqDevice.read_end_time, bankUserid: jbqDevice.bank_userid, bankItemId: jbqDevice.bank_item_id, bankHouse: jbqDevice.bank_house, bankLanwei: jbqDevice.bank_lanwei, bankPlace: jbqDevice.bank_place, isHome: jbqDevice.is_home, distributeTime: jbqDevice.distribute_time, bandgeStatus: jbqDevice.bandge_status, isWear: jbqDevice.is_wear, isTemperature: jbqDevice.is_temperature, sourceId: jbqDevice.source_id, expireTime: jbqDevice.expire_time }, cattle: cattleInfo ? { id: cattleInfo.id, orgId: cattleInfo.orgId, earNumber: cattleInfo.earNumber, sex: cattleInfo.sex, strain: cattleInfo.strain, varieties: cattleInfo.varieties, cate: cattleInfo.cate, birthWeight: cattleInfo.birthWeight, birthday: cattleInfo.birthday, penId: cattleInfo.penId, intoTime: cattleInfo.intoTime, parity: cattleInfo.parity, source: cattleInfo.source, sourceDay: cattleInfo.sourceDay, sourceWeight: cattleInfo.sourceWeight, weight: cattleInfo.weight, event: cattleInfo.event, eventTime: cattleInfo.eventTime, lactationDay: cattleInfo.lactationDay, semenNum: cattleInfo.semenNum, isWear: cattleInfo.isWear, batchId: cattleInfo.batchId, imgs: cattleInfo.imgs, isEleAuth: cattleInfo.isEleAuth, isQuaAuth: cattleInfo.isQuaAuth, isDelete: cattleInfo.isDelete, isOut: cattleInfo.isOut, createUid: cattleInfo.createUid, createTime: cattleInfo.createTime, algebra: cattleInfo.algebra, colour: cattleInfo.colour, infoWeight: cattleInfo.infoWeight, descent: cattleInfo.descent, isVaccin: cattleInfo.isVaccin, isInsemination: cattleInfo.isInsemination, isInsure: cattleInfo.isInsure, isMortgage: cattleInfo.isMortgage, updateTime: cattleInfo.updateTime, breedBullTime: cattleInfo.breedBullTime, level: cattleInfo.level, sixWeight: cattleInfo.sixWeight, eighteenWeight: cattleInfo.eighteenWeight, twelveDayWeight: cattleInfo.twelveDayWeight, eighteenDayWeight: cattleInfo.eighteenDayWeight, xxivDayWeight: cattleInfo.xxivDayWeight, semenBreedImgs: cattleInfo.semenBreedImgs, sellStatus: cattleInfo.sellStatus, weightCalculateTime: cattleInfo.weightCalculateTime, dayOfBirthday: cattleInfo.dayOfBirthday, farm: cattleInfo.farm, pen: cattleInfo.pen, batch: cattleInfo.batch } : null, isBound: !!cattleInfo, bindingStatus: cattleInfo ? '已绑定' : '未绑定' }; res.json({ success: true, message: '获取绑定信息成功', data: bindingInfo, timestamp: new Date().toISOString() }); } catch (error) { console.error('获取绑定信息失败:', error); res.status(500).json({ success: false, message: '获取绑定信息失败: ' + error.message, data: null, timestamp: new Date().toISOString() }); } }; /** * 获取所有绑定状态统计 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ const getBindingStats = async (req, res) => { try { // 统计绑定状态 const stats = await IotJbqClient.findAll({ attributes: [ 'bandge_status', [IotJbqClient.sequelize.fn('COUNT', IotJbqClient.sequelize.col('id')), 'count'] ], group: ['bandge_status'], raw: true }); // 统计匹配情况 const matchStats = await IotJbqClient.findAll({ attributes: [ [IotJbqClient.sequelize.fn('COUNT', IotJbqClient.sequelize.col('IotJbqClient.id')), 'total_jbq'], [IotJbqClient.sequelize.fn('COUNT', IotJbqClient.sequelize.col('cattle.id')), 'matched_count'] ], include: [ { model: IotCattle, as: 'cattle', attributes: [], required: false, where: { earNumber: IotJbqClient.sequelize.col('IotJbqClient.cid') } } ], raw: true }); const result = { bindingStats: stats.map(stat => ({ status: stat.bandge_status === 1 ? '已绑定' : '未绑定', count: parseInt(stat.count) })), matchStats: { totalJbq: parseInt(matchStats[0]?.total_jbq || 0), matchedCount: parseInt(matchStats[0]?.matched_count || 0), matchRate: matchStats[0]?.total_jbq > 0 ? ((matchStats[0]?.matched_count / matchStats[0]?.total_jbq) * 100).toFixed(2) + '%' : '0%' } }; res.json({ success: true, message: '获取绑定统计成功', data: result, timestamp: new Date().toISOString() }); } catch (error) { console.error('获取绑定统计失败:', error); res.status(500).json({ success: false, message: '获取绑定统计失败: ' + error.message, data: null, timestamp: new Date().toISOString() }); } }; /** * 手动绑定耳标与牛只档案 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ const bindCattle = async (req, res) => { try { const { cid, cattleId } = req.body; if (!cid || !cattleId) { return res.status(400).json({ success: false, message: '耳标编号和牛只ID不能为空', data: null }); } // 检查耳标是否存在 const jbqDevice = await IotJbqClient.findOne({ where: { cid: cid } }); if (!jbqDevice) { return res.status(404).json({ success: false, message: '未找到指定的耳标设备', data: null }); } // 检查牛只档案是否存在 const cattle = await IotCattle.findByPk(cattleId); if (!cattle) { return res.status(404).json({ success: false, message: '未找到指定的牛只档案', data: null }); } // 更新牛只档案的耳标号 await cattle.update({ earNumber: cid, updateTime: Math.floor(Date.now() / 1000) }); // 更新耳标的绑定状态 await jbqDevice.update({ bandge_status: 1 }); res.json({ success: true, message: '绑定成功', data: { cid: cid, cattleId: cattleId, bindingStatus: '已绑定' }, timestamp: new Date().toISOString() }); } catch (error) { console.error('绑定失败:', error); res.status(500).json({ success: false, message: '绑定失败: ' + error.message, data: null, timestamp: new Date().toISOString() }); } }; /** * 解绑耳标与牛只档案 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 */ const unbindCattle = async (req, res) => { try { const { cid } = req.params; if (!cid) { return res.status(400).json({ success: false, message: '耳标编号不能为空', data: null }); } // 查找绑定的牛只档案 const cattle = await IotCattle.findOne({ where: { earNumber: cid } }); if (cattle) { // 清除牛只档案的耳标号 await cattle.update({ earNumber: null, updateTime: Math.floor(Date.now() / 1000) }); } // 更新耳标的绑定状态 const jbqDevice = await IotJbqClient.findOne({ where: { cid: cid } }); if (jbqDevice) { await jbqDevice.update({ bandge_status: 0 }); } res.json({ success: true, message: '解绑成功', data: { cid: cid, bindingStatus: '未绑定' }, timestamp: new Date().toISOString() }); } catch (error) { console.error('解绑失败:', error); res.status(500).json({ success: false, message: '解绑失败: ' + error.message, data: null, timestamp: new Date().toISOString() }); } }; module.exports = { getBindingInfo, getBindingStats, bindCattle, unbindCattle };