1618 lines
44 KiB
JavaScript
1618 lines
44 KiB
JavaScript
/**
|
||
* 智能设备路由
|
||
* @file smart-devices.js
|
||
* @description 智能设备相关的API路由
|
||
*/
|
||
const express = require('express');
|
||
const router = express.Router();
|
||
const { verifyToken, checkRole } = require('../middleware/auth');
|
||
const { requirePermission } = require('../middleware/permission');
|
||
const { IotXqClient, IotJbqServer, IotJbqClient } = require('../models');
|
||
const { Op } = require('sequelize');
|
||
|
||
// 公开API路由,不需要验证token
|
||
const publicRoutes = express.Router();
|
||
router.use('/public', publicRoutes);
|
||
|
||
// 公开导出智能耳标数据(无分页限制)
|
||
publicRoutes.get('/eartags/export', async (req, res) => {
|
||
try {
|
||
const { search } = req.query;
|
||
|
||
// 构建查询条件
|
||
const whereConditions = {};
|
||
|
||
// 搜索条件
|
||
if (search) {
|
||
whereConditions[Op.or] = [
|
||
{ aaid: { [Op.like]: `%${search}%` } },
|
||
{ cid: { [Op.like]: `%${search}%` } },
|
||
{ sid: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
// 查询所有数据,不分页
|
||
const rows = await IotJbqClient.findAll({
|
||
where: whereConditions,
|
||
order: [
|
||
['uptime', 'DESC'], // 按更新时间倒序
|
||
['id', 'DESC'] // 次要排序按ID倒序
|
||
]
|
||
});
|
||
|
||
// 格式化数据以匹配导出需求
|
||
const formattedData = rows.map(item => {
|
||
// 计算当日运动量
|
||
const totalSteps = parseInt(item.walk) || 0;
|
||
const yesterdaySteps = parseInt(item.y_steps) || 0;
|
||
const dailySteps = totalSteps - yesterdaySteps;
|
||
|
||
// 格式化时间
|
||
let lastUpdate = '';
|
||
if (item.uptime) {
|
||
const date = new Date(item.uptime * 1000);
|
||
lastUpdate = date.toLocaleString('zh-CN');
|
||
}
|
||
|
||
// 设备状态映射
|
||
const getDeviceStatus = (state) => {
|
||
switch (state) {
|
||
case 1: return '在线';
|
||
case 2: return '离线';
|
||
default: return '未知';
|
||
}
|
||
};
|
||
|
||
// 定位信息判断
|
||
const hasLocation = item.lat && item.lon && item.lat !== '0' && item.lon !== '0';
|
||
|
||
return {
|
||
id: item.id,
|
||
eartagNumber: item.aaid || `DEV${String(item.id).padStart(6, '0')}`,
|
||
deviceStatus: getDeviceStatus(item.state),
|
||
battery: item.voltage !== null && item.voltage !== undefined ? item.voltage : '0',
|
||
temperature: item.getTemperatureValue().toFixed(2),
|
||
collectedHost: item.sid || '-',
|
||
totalMovement: totalSteps,
|
||
dailyMovement: dailySteps,
|
||
location: hasLocation ? '有定位' : '无定位',
|
||
lastUpdate: lastUpdate,
|
||
bindingStatus: item.bandge_status === 1 ? '已绑定' : '未绑定',
|
||
gpsSignal: item.getGpsSignalLevel(),
|
||
longitude: item.lon || '',
|
||
latitude: item.lat || ''
|
||
};
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
data: formattedData,
|
||
total: formattedData.length,
|
||
message: '导出数据获取成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('导出智能耳标数据失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '导出数据获取失败: ' + error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
// 公开获取智能耳标列表
|
||
publicRoutes.get('/eartags', async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 10, status, search } = req.query;
|
||
const offset = (page - 1) * limit;
|
||
|
||
// 构建查询条件
|
||
const whereConditions = {};
|
||
|
||
// 状态筛选
|
||
if (status) {
|
||
switch (status) {
|
||
case 'online':
|
||
whereConditions.state = 1;
|
||
break;
|
||
case 'offline':
|
||
whereConditions.state = 0;
|
||
break;
|
||
case 'alarm':
|
||
whereConditions.state = 2;
|
||
break;
|
||
case 'maintenance':
|
||
whereConditions.state = 3;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 搜索条件
|
||
if (search) {
|
||
whereConditions[Op.or] = [
|
||
{ aaid: { [Op.like]: `%${search}%` } },
|
||
{ cid: { [Op.like]: `%${search}%` } },
|
||
{ sid: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
// 查询数据库
|
||
const { count, rows } = await IotJbqClient.findAndCountAll({
|
||
where: whereConditions,
|
||
limit: parseInt(limit),
|
||
offset: parseInt(offset),
|
||
order: [
|
||
['uptime', 'DESC'], // 按更新时间倒序
|
||
['id', 'DESC'] // 次要排序按ID倒序
|
||
]
|
||
});
|
||
|
||
// 格式化数据以匹配前端UI需求
|
||
const formattedData = rows.map(item => ({
|
||
id: item.id,
|
||
sn: item.sn || item.deviceId || `DEV${String(item.id).padStart(6, '0')}`,
|
||
battery: item.battery !== null ? item.battery : '0',
|
||
rsrp: item.rsrp || '-',
|
||
bandge_status: item.bandge_status || 0,
|
||
deviceInfo: item.ver || '未知',
|
||
temperature: item.getTemperatureValue().toFixed(2),
|
||
status: item.getStatusText(),
|
||
steps: item.steps || 0,
|
||
location: item.longitude && item.latitude ? '有定位' : '无定位',
|
||
updateInterval: Math.floor((Date.now() - (item.uptime * 1000)) / 60000) || 0,
|
||
lastUpdate: item.getLastUpdateTime(),
|
||
undefinedInfo: item.is_wear ? '已佩戴' : '未佩戴',
|
||
deviceStatus: item.is_connect ? '使用中' : '离线',
|
||
longitude: item.longitude,
|
||
latitude: item.latitude,
|
||
altitude: item.altitude,
|
||
is_wear: item.is_wear,
|
||
is_connect: item.is_connect,
|
||
fence_id: item.fence_id,
|
||
gpsSignal: item.getGpsSignalLevel(),
|
||
batteryPercent: item.getBatteryPercent()
|
||
}));
|
||
|
||
// 计算统计数据
|
||
const stats = {
|
||
total: count,
|
||
online: rows.filter(item => item.state === 1).length,
|
||
offline: rows.filter(item => item.state === 0).length,
|
||
alarm: rows.filter(item => item.state === 2).length,
|
||
maintenance: rows.filter(item => item.state === 3).length
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
list: formattedData,
|
||
pagination: {
|
||
current: parseInt(page),
|
||
pageSize: parseInt(limit),
|
||
total: count,
|
||
pages: Math.ceil(count / parseInt(limit))
|
||
},
|
||
stats
|
||
},
|
||
message: '获取智能耳标列表成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('获取智能耳标列表失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能耳标列表失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
// 公开获取智能耳标详情
|
||
publicRoutes.get('/eartags/search/:deviceNumber', async (req, res) => {
|
||
try {
|
||
const { deviceNumber } = req.params;
|
||
|
||
const device = await IotJbqClient.findOne({
|
||
where: {
|
||
[Op.or]: [
|
||
{ sn: deviceNumber },
|
||
{ deviceId: deviceNumber }
|
||
]
|
||
}
|
||
});
|
||
|
||
if (!device) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '未找到该设备'
|
||
});
|
||
}
|
||
|
||
// 格式化设备数据
|
||
const deviceData = {
|
||
id: device.id,
|
||
sn: device.sn || device.deviceId || `DEV${String(device.id).padStart(6, '0')}`,
|
||
battery: device.battery !== null ? device.battery : '0',
|
||
rsrp: device.rsrp || '-',
|
||
bandge_status: device.bandge_status || 0,
|
||
deviceInfo: device.ver || '未知',
|
||
temperature: device.getTemperatureValue().toFixed(2),
|
||
status: device.getStatusText(),
|
||
steps: device.steps || 0,
|
||
location: device.longitude && device.latitude ? '有定位' : '无定位',
|
||
updateInterval: Math.floor((Date.now() - (device.uptime * 1000)) / 60000) || 0,
|
||
lastUpdate: device.getLastUpdateTime(),
|
||
undefinedInfo: device.is_wear ? '已佩戴' : '未佩戴',
|
||
deviceStatus: device.is_connect ? '使用中' : '离线',
|
||
longitude: device.longitude,
|
||
latitude: device.latitude,
|
||
altitude: device.altitude,
|
||
is_wear: device.is_wear,
|
||
is_connect: device.is_connect,
|
||
fence_id: device.fence_id,
|
||
gpsSignal: device.getGpsSignalLevel(),
|
||
batteryPercent: device.getBatteryPercent()
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: deviceData,
|
||
message: '获取设备详情成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('搜索设备失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '搜索设备失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* components:
|
||
* schemas:
|
||
* SmartDevice:
|
||
* type: object
|
||
* properties:
|
||
* id:
|
||
* type: integer
|
||
* deviceId:
|
||
* type: string
|
||
* type:
|
||
* type: string
|
||
* enum: [eartag, anklet, collar]
|
||
* animalId:
|
||
* type: integer
|
||
* model:
|
||
* type: string
|
||
* status:
|
||
* type: string
|
||
* battery:
|
||
* type: integer
|
||
* lastUpdate:
|
||
* type: string
|
||
* format: date-time
|
||
*/
|
||
|
||
// ==================== 智能耳标 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/eartags/search/{deviceNumber}:
|
||
* get:
|
||
* summary: 搜索智能耳标
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: deviceNumber
|
||
* required: true
|
||
* schema:
|
||
* type: string
|
||
* description: 耳标设备编号
|
||
* responses:
|
||
* 200:
|
||
* description: 搜索成功
|
||
* 404:
|
||
* description: 未找到耳标
|
||
*/
|
||
router.get('/eartags/search/:deviceNumber', verifyToken, requirePermission('smart_eartag:view'), async (req, res) => {
|
||
try {
|
||
const { deviceNumber } = req.params;
|
||
|
||
if (!deviceNumber) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '请输入耳标设备编号'
|
||
});
|
||
}
|
||
|
||
// 搜索耳标设备
|
||
const eartag = await IotJbqClient.findOne({
|
||
where: {
|
||
[Op.or]: [
|
||
{ sid: deviceNumber },
|
||
{ cid: deviceNumber }
|
||
]
|
||
}
|
||
});
|
||
|
||
if (!eartag) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '未找到指定的耳标设备',
|
||
data: null
|
||
});
|
||
}
|
||
|
||
// 检查绑定状态
|
||
let bindingInfo = null;
|
||
try {
|
||
const Animal = require('../models/Animal');
|
||
const animal = await Animal.findOne({
|
||
where: { collar_number: deviceNumber }
|
||
});
|
||
|
||
if (animal) {
|
||
bindingInfo = {
|
||
isBound: true,
|
||
animalId: animal.id,
|
||
earTag: animal.ear_tag,
|
||
animalType: animal.getAnimalTypeText(),
|
||
breed: animal.breed,
|
||
category: animal.getCategoryText(),
|
||
boundDate: animal.created_at
|
||
};
|
||
} else {
|
||
bindingInfo = {
|
||
isBound: false,
|
||
message: '该耳标未绑定动物'
|
||
};
|
||
}
|
||
} catch (bindingError) {
|
||
console.log('绑定检查失败:', bindingError.message);
|
||
bindingInfo = {
|
||
isBound: false,
|
||
message: '无法检查绑定状态'
|
||
};
|
||
}
|
||
|
||
// 格式化设备数据 - 严格按照图片中的字段映射
|
||
const formattedData = {
|
||
id: eartag.id,
|
||
eartagNumber: eartag.getEartagNumber(), // 耳标编号 (aaid)
|
||
battery: eartag.getBatteryPercent(), // 设备电量/%
|
||
temperature: eartag.getTemperatureValue().toFixed(2), // 设备温度/°C
|
||
collectedHost: eartag.getHostId(), // 被采集主机 (sid)
|
||
totalMovement: eartag.getTotalMovement(), // 总运动量 (walk)
|
||
dailyMovement: eartag.getDailyMovement(), // 当日运动量 (y_steps)
|
||
location: eartag.hasLocation() ? '有定位' : '无定位', // 定位信息
|
||
lastUpdate: eartag.getLastUpdateTime(), // 数据最后更新时间
|
||
bindingStatus: eartag.getBandgeStatusText(), // 绑定牲畜 (bandge_status)
|
||
deviceStatus: eartag.getStatusText(), // 设备状态 (state)
|
||
wearStatus: eartag.getWearStatusText(), // 佩戴状态 (is_wear)
|
||
bindingInfo: bindingInfo,
|
||
raw: {
|
||
state: eartag.state,
|
||
voltage: eartag.voltage,
|
||
temperature_raw: eartag.temperature,
|
||
lat: eartag.lat,
|
||
lon: eartag.lon,
|
||
gps_state: eartag.gps_state,
|
||
uptime: eartag.uptime,
|
||
time: eartag.time,
|
||
uid: eartag.uid,
|
||
sid: eartag.sid,
|
||
cid: eartag.cid,
|
||
bandge_status: eartag.bandge_status,
|
||
is_wear: eartag.is_wear
|
||
}
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '搜索成功',
|
||
data: formattedData
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('搜索耳标失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '搜索失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/eartags:
|
||
* get:
|
||
* summary: 获取智能耳标列表
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: query
|
||
* name: page
|
||
* schema:
|
||
* type: integer
|
||
* description: 页码
|
||
* - in: query
|
||
* name: limit
|
||
* schema:
|
||
* type: integer
|
||
* description: 每页数量
|
||
* - in: query
|
||
* name: status
|
||
* schema:
|
||
* type: string
|
||
* description: 设备状态筛选
|
||
* - in: query
|
||
* name: search
|
||
* schema:
|
||
* type: string
|
||
* description: 搜索关键词
|
||
* responses:
|
||
* 200:
|
||
* description: 成功获取智能耳标列表
|
||
*/
|
||
router.get('/eartags', verifyToken, requirePermission('smart_eartag:view'), async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 10, status, search } = req.query;
|
||
const offset = (page - 1) * limit;
|
||
|
||
// 构建查询条件
|
||
const whereConditions = {};
|
||
|
||
// 状态筛选
|
||
if (status) {
|
||
switch (status) {
|
||
case 'online':
|
||
whereConditions.state = 1;
|
||
break;
|
||
case 'offline':
|
||
whereConditions.state = 0;
|
||
break;
|
||
case 'alarm':
|
||
whereConditions.state = 2;
|
||
break;
|
||
case 'maintenance':
|
||
whereConditions.state = 3;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 搜索条件
|
||
if (search) {
|
||
whereConditions[Op.or] = [
|
||
{ sid: { [Op.like]: `%${search}%` } },
|
||
{ cid: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
// 查询数据库
|
||
const { count, rows } = await IotJbqClient.findAndCountAll({
|
||
where: whereConditions,
|
||
limit: parseInt(limit),
|
||
offset: parseInt(offset),
|
||
order: [
|
||
['uptime', 'DESC'], // 按更新时间倒序
|
||
['id', 'DESC'] // 次要排序按ID倒序
|
||
]
|
||
});
|
||
|
||
// 格式化数据以匹配前端UI需求 - 严格按照图片中的字段映射
|
||
const formattedData = rows.map(item => ({
|
||
id: item.id,
|
||
// 耳标编号字段 - 使用aaid字段
|
||
eartagNumber: item.getEartagNumber(),
|
||
// 设备电量%字段 - 使用电压计算电量百分比
|
||
battery: item.getBatteryPercent(),
|
||
// 设备温度字段 - 使用temperature字段
|
||
temperature: item.getTemperatureValue().toFixed(2),
|
||
// 被采集主机字段 - 使用sid字段
|
||
collectedHost: item.getHostId(),
|
||
// 总运动量字段 - 使用walk字段
|
||
totalMovement: item.getTotalMovement(),
|
||
// 当日运动量字段 - 使用y_steps字段
|
||
dailyMovement: item.getDailyMovement(),
|
||
// 定位信息 - 检查是否有经纬度
|
||
location: item.hasLocation() ? '有定位' : '无定位',
|
||
// 数据最后更新时间
|
||
lastUpdate: item.getLastUpdateTime(),
|
||
// 绑定牲畜状态 - 使用bandge_status字段
|
||
bindingStatus: item.getBandgeStatusText(),
|
||
// 设备状态
|
||
deviceStatus: item.getStatusText(),
|
||
// 佩戴状态
|
||
wearStatus: item.getWearStatusText(),
|
||
|
||
// 保留原始数据供其他功能使用
|
||
lat: item.lat,
|
||
lon: item.lon,
|
||
gps_state: item.gps_state,
|
||
voltage: item.voltage,
|
||
state: item.state,
|
||
bandge_status: item.bandge_status,
|
||
is_wear: item.is_wear,
|
||
uptime: item.uptime,
|
||
time: item.time,
|
||
uid: item.uid,
|
||
sid: item.sid,
|
||
cid: item.cid,
|
||
source_id: item.source_id,
|
||
raw: {
|
||
state: item.state,
|
||
voltage_raw: item.voltage,
|
||
temperature_raw: item.temperature,
|
||
lat: item.lat,
|
||
lon: item.lon,
|
||
gps_state: item.gps_state,
|
||
uptime: item.uptime,
|
||
time: item.time,
|
||
uid: item.uid,
|
||
sid: item.sid,
|
||
cid: item.cid,
|
||
bandge_status: item.bandge_status,
|
||
is_wear: item.is_wear,
|
||
walk: item.walk,
|
||
y_steps: item.y_steps
|
||
}
|
||
}));
|
||
|
||
// 计算统计数据
|
||
const stats = {
|
||
total: count,
|
||
online: formattedData.filter(item => item.deviceStatus === '在线').length,
|
||
offline: formattedData.filter(item => item.deviceStatus === '离线').length,
|
||
alarm: formattedData.filter(item => item.deviceStatus === '报警').length,
|
||
maintenance: formattedData.filter(item => item.deviceStatus === '维护').length,
|
||
bound: formattedData.filter(item => item.bindingStatus === '已绑定').length,
|
||
unbound: formattedData.filter(item => item.bindingStatus === '未绑定').length
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: formattedData,
|
||
total: count,
|
||
stats,
|
||
pagination: {
|
||
page: parseInt(page),
|
||
limit: parseInt(limit),
|
||
total: count,
|
||
pages: Math.ceil(count / limit)
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取智能耳标列表失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能耳标列表失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/eartags:
|
||
* post:
|
||
* summary: 创建智能耳标
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* requestBody:
|
||
* required: true
|
||
* content:
|
||
* application/json:
|
||
* schema:
|
||
* type: object
|
||
* properties:
|
||
* deviceId:
|
||
* type: string
|
||
* animalId:
|
||
* type: integer
|
||
* model:
|
||
* type: string
|
||
* responses:
|
||
* 201:
|
||
* description: 智能耳标创建成功
|
||
*/
|
||
router.post('/eartags', verifyToken, requirePermission('smart_eartag:create'), async (req, res) => {
|
||
try {
|
||
const { deviceId, animalId, model, notes } = req.body;
|
||
|
||
// 这里应该调用数据库创建操作
|
||
const newEartag = {
|
||
id: Date.now(), // 模拟ID
|
||
deviceId,
|
||
animalId,
|
||
model,
|
||
status: 'online',
|
||
battery: 100,
|
||
notes,
|
||
created_at: new Date().toISOString()
|
||
};
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
data: newEartag,
|
||
message: '智能耳标创建成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('创建智能耳标失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '创建智能耳标失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
// ==================== 智能脚环 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/anklets:
|
||
* get:
|
||
* summary: 获取智能脚环列表
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.get('/anklets', verifyToken, requirePermission('smart_anklet:view'), async (req, res) => {
|
||
try {
|
||
// 模拟数据
|
||
const anklets = [
|
||
{
|
||
id: 1,
|
||
deviceId: 'AN001',
|
||
animalName: '牛001',
|
||
model: 'SmartAnklet-V1',
|
||
status: 'active',
|
||
stepCount: 2456,
|
||
heartRate: 75,
|
||
temperature: 38.5,
|
||
lastUpdate: '2025-01-18 10:30:00'
|
||
},
|
||
{
|
||
id: 2,
|
||
deviceId: 'AN002',
|
||
animalName: '牛002',
|
||
model: 'SmartAnklet-V1',
|
||
status: 'standby',
|
||
stepCount: 1823,
|
||
heartRate: 68,
|
||
temperature: 38.2,
|
||
lastUpdate: '2025-01-18 09:15:00'
|
||
},
|
||
{
|
||
id: 3,
|
||
deviceId: 'AN003',
|
||
animalName: '羊001',
|
||
model: 'SmartAnklet-V2',
|
||
status: 'active',
|
||
stepCount: 3124,
|
||
heartRate: 82,
|
||
temperature: 39.1,
|
||
lastUpdate: '2025-01-18 10:25:00'
|
||
}
|
||
];
|
||
|
||
res.json({
|
||
success: true,
|
||
data: anklets,
|
||
total: anklets.length
|
||
});
|
||
} catch (error) {
|
||
console.error('获取智能脚环列表失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能脚环列表失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/anklets:
|
||
* post:
|
||
* summary: 创建智能脚环
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.post('/anklets', verifyToken, requirePermission('smart_anklet:create'), async (req, res) => {
|
||
try {
|
||
const { deviceId, animalId, model, frequency, notes } = req.body;
|
||
|
||
const newAnklet = {
|
||
id: Date.now(),
|
||
deviceId,
|
||
animalId,
|
||
model,
|
||
frequency,
|
||
status: 'active',
|
||
stepCount: 0,
|
||
heartRate: 0,
|
||
temperature: 0,
|
||
notes,
|
||
created_at: new Date().toISOString()
|
||
};
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
data: newAnklet,
|
||
message: '智能脚环创建成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('创建智能脚环失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '创建智能脚环失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
// ==================== 智能项圈 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/collars/search/{collarNumber}:
|
||
* get:
|
||
* summary: 搜索智能项圈
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: collarNumber
|
||
* required: true
|
||
* schema:
|
||
* type: string
|
||
* description: 项圈编号
|
||
* responses:
|
||
* 200:
|
||
* description: 搜索成功
|
||
* 404:
|
||
* description: 未找到项圈
|
||
*/
|
||
router.get('/collars/search/:collarNumber', verifyToken, requirePermission('smart_collar:view'), async (req, res) => {
|
||
try {
|
||
const { collarNumber } = req.params;
|
||
|
||
if (!collarNumber) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: '请输入项圈编号'
|
||
});
|
||
}
|
||
|
||
// 搜索项圈设备
|
||
const collar = await IotXqClient.findOne({
|
||
where: {
|
||
[Op.or]: [
|
||
{ sn: collarNumber },
|
||
{ deviceId: collarNumber }
|
||
]
|
||
}
|
||
});
|
||
|
||
if (!collar) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '未找到指定的项圈设备',
|
||
data: null
|
||
});
|
||
}
|
||
|
||
// 检查绑定状态
|
||
let bindingInfo = null;
|
||
try {
|
||
const Animal = require('../models/Animal');
|
||
const animal = await Animal.findOne({
|
||
where: { collar_number: collarNumber }
|
||
});
|
||
|
||
if (animal) {
|
||
bindingInfo = {
|
||
isBound: true,
|
||
animalId: animal.id,
|
||
earTag: animal.ear_tag,
|
||
animalType: animal.getAnimalTypeText(),
|
||
breed: animal.breed,
|
||
category: animal.getCategoryText(),
|
||
boundDate: animal.created_at
|
||
};
|
||
} else {
|
||
bindingInfo = {
|
||
isBound: false,
|
||
message: '该项圈未绑定动物'
|
||
};
|
||
}
|
||
} catch (bindingError) {
|
||
console.log('绑定检查失败:', bindingError.message);
|
||
bindingInfo = {
|
||
isBound: false,
|
||
message: '无法检查绑定状态'
|
||
};
|
||
}
|
||
|
||
// 格式化设备数据
|
||
const formattedData = {
|
||
id: collar.id,
|
||
sn: collar.sn || collar.deviceId || `DEV${String(collar.id).padStart(6, '0')}`,
|
||
battery: collar.battery !== null ? collar.battery : '0',
|
||
rsrp: collar.rsrp || '-',
|
||
bandge_status: collar.bandge_status || 0,
|
||
deviceInfo: collar.ver || '未知',
|
||
temperature: collar.getTemperatureValue().toFixed(2),
|
||
status: collar.getStatusText(),
|
||
steps: collar.steps || 0,
|
||
location: collar.longitude && collar.latitude ? '有定位' : '无定位',
|
||
updateInterval: Math.floor((Date.now() - (collar.uptime * 1000)) / 60000) || 0,
|
||
lastUpdate: collar.getLastUpdateTime(),
|
||
undefinedInfo: collar.is_wear ? '已佩戴' : '未佩戴',
|
||
deviceStatus: collar.is_connect ? '使用中' : '离线',
|
||
longitude: collar.longitude,
|
||
latitude: collar.latitude,
|
||
altitude: collar.altitude,
|
||
is_wear: collar.is_wear,
|
||
is_connect: collar.is_connect,
|
||
fence_id: collar.fence_id,
|
||
gpsSignal: collar.getGpsSignalLevel(),
|
||
batteryPercent: collar.getBatteryPercent(),
|
||
bindingInfo: bindingInfo,
|
||
raw: {
|
||
state: collar.state,
|
||
battery_raw: collar.battery,
|
||
temperature_raw: collar.temperature,
|
||
nsat: collar.nsat,
|
||
uptime: collar.uptime,
|
||
time: collar.time,
|
||
uid: collar.uid,
|
||
sn: collar.sn,
|
||
rsrp: collar.rsrp,
|
||
bandge_status: collar.bandge_status
|
||
}
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '搜索成功',
|
||
data: formattedData
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('搜索项圈失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '搜索失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/collars:
|
||
* get:
|
||
* summary: 获取智能项圈列表
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.get('/collars', verifyToken, requirePermission('smart_collar:view'), async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 10, status, search } = req.query;
|
||
const offset = (page - 1) * limit;
|
||
|
||
// 构建查询条件
|
||
const whereConditions = {};
|
||
|
||
// 状态筛选
|
||
if (status) {
|
||
switch (status) {
|
||
case 'online':
|
||
whereConditions.state = 1;
|
||
break;
|
||
case 'offline':
|
||
whereConditions.state = 0;
|
||
break;
|
||
case 'alarm':
|
||
whereConditions.state = 2;
|
||
break;
|
||
case 'maintenance':
|
||
whereConditions.state = 3;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 搜索条件
|
||
if (search) {
|
||
whereConditions[Op.or] = [
|
||
{ deviceId: { [Op.like]: `%${search}%` } },
|
||
{ sn: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
// 查询数据库
|
||
const { count, rows } = await IotXqClient.findAndCountAll({
|
||
where: whereConditions,
|
||
limit: parseInt(limit),
|
||
offset: parseInt(offset),
|
||
order: [
|
||
['uptime', 'DESC'], // 按更新时间倒序
|
||
['id', 'DESC'] // 次要排序按ID倒序
|
||
]
|
||
});
|
||
|
||
// 格式化数据以匹配前端UI需求 - 使用正确的字段映射
|
||
const formattedData = rows.map(item => ({
|
||
id: item.id,
|
||
// 项目编号字段 - 优先使用sn字段,为空时使用deviceId,最后使用id
|
||
sn: item.sn || item.deviceId || `DEV${String(item.id).padStart(6, '0')}`,
|
||
// 设备电量%字段 - 直接使用battery字段的真实值
|
||
battery: item.battery !== null ? item.battery : '0',
|
||
// 设备信号字段 - 使用rsrp字段
|
||
rsrp: item.rsrp || '-',
|
||
// 绑带状态字段 - 使用bandge_status字段
|
||
bandge_status: item.bandge_status || 0,
|
||
|
||
// 其他字段 - 全部使用数据库真实数据
|
||
deviceInfo: item.ver || '未知', // 使用固件版本作为设备信息
|
||
temperature: item.getTemperatureValue().toFixed(2),
|
||
status: item.getStatusText(),
|
||
steps: item.steps || 0,
|
||
location: item.longitude && item.latitude ? '有定位' : '无定位',
|
||
updateInterval: Math.floor((Date.now() - (item.uptime * 1000)) / 60000) || 0, // 计算实际更新间隔(分钟)
|
||
lastUpdate: item.getLastUpdateTime(),
|
||
undefinedInfo: item.is_wear ? '已佩戴' : '未佩戴', // 使用佩戴状态
|
||
deviceStatus: item.is_connect ? '使用中' : '离线',
|
||
|
||
// 保留原始数据供其他功能使用
|
||
longitude: item.longitude,
|
||
latitude: item.latitude,
|
||
altitude: item.altitude,
|
||
is_wear: item.is_wear,
|
||
is_connect: item.is_connect,
|
||
fence_id: item.fence_id,
|
||
gpsSignal: item.getGpsSignalLevel(),
|
||
batteryPercent: item.getBatteryPercent(), // 保留计算后的电量百分比
|
||
raw: {
|
||
state: item.state,
|
||
battery_raw: item.battery,
|
||
temperature_raw: item.temperature,
|
||
nsat: item.nsat,
|
||
uptime: item.uptime,
|
||
time: item.time,
|
||
uid: item.uid,
|
||
sn: item.sn,
|
||
rsrp: item.rsrp,
|
||
bandge_status: item.bandge_status
|
||
}
|
||
}));
|
||
|
||
// 计算统计数据
|
||
const stats = {
|
||
total: count,
|
||
normal: formattedData.filter(item => item.status === '在线').length,
|
||
lowBattery: formattedData.filter(item => item.battery < 20).length,
|
||
maintenance: formattedData.filter(item => item.status === '维护' || !item.raw.is_connect).length,
|
||
offline: formattedData.filter(item => item.status === '离线').length
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: formattedData,
|
||
total: count,
|
||
stats,
|
||
pagination: {
|
||
page: parseInt(page),
|
||
limit: parseInt(limit),
|
||
total: count,
|
||
pages: Math.ceil(count / limit)
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('获取智能项圈列表失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能项圈列表失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/collars:
|
||
* post:
|
||
* summary: 创建智能项圈
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.post('/collars', verifyToken, requirePermission('smart_collar:create'), async (req, res) => {
|
||
try {
|
||
const { deviceId, animalId, model, features, uploadFreq, notes } = req.body;
|
||
|
||
const newCollar = {
|
||
id: Date.now(),
|
||
deviceId,
|
||
animalId,
|
||
model,
|
||
features,
|
||
uploadFreq,
|
||
status: 'normal',
|
||
battery: 100,
|
||
gpsSignal: 5,
|
||
temperature: 0,
|
||
notes,
|
||
created_at: new Date().toISOString()
|
||
};
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
data: newCollar,
|
||
message: '智能项圈创建成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('创建智能项圈失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '创建智能项圈失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
// ==================== 通用设备操作 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/{type}/{id}:
|
||
* put:
|
||
* summary: 更新智能设备
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: type
|
||
* required: true
|
||
* schema:
|
||
* type: string
|
||
* enum: [eartags, anklets, collars]
|
||
* - in: path
|
||
* name: id
|
||
* required: true
|
||
* schema:
|
||
* type: integer
|
||
*/
|
||
router.put('/:type/:id', verifyToken, async (req, res) => {
|
||
try {
|
||
const { type, id } = req.params;
|
||
const updateData = req.body;
|
||
|
||
// 检查权限
|
||
const permissionMap = {
|
||
'eartags': 'smart_eartag:update',
|
||
'anklets': 'smart_anklet:update',
|
||
'collars': 'smart_collar:update'
|
||
};
|
||
|
||
const requiredPermission = permissionMap[type];
|
||
if (!requiredPermission || !req.user.permissions.includes(requiredPermission)) {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: '权限不足'
|
||
});
|
||
}
|
||
|
||
// 模拟更新操作
|
||
const updatedDevice = {
|
||
id: parseInt(id),
|
||
...updateData,
|
||
updated_at: new Date().toISOString()
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: updatedDevice,
|
||
message: '设备更新成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('更新设备失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '更新设备失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/{type}/{id}:
|
||
* delete:
|
||
* summary: 删除智能设备
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.delete('/:type/:id', verifyToken, async (req, res) => {
|
||
try {
|
||
const { type, id } = req.params;
|
||
|
||
// 检查权限
|
||
const permissionMap = {
|
||
'eartags': 'smart_eartag:delete',
|
||
'anklets': 'smart_anklet:delete',
|
||
'collars': 'smart_collar:delete'
|
||
};
|
||
|
||
const requiredPermission = permissionMap[type];
|
||
if (!requiredPermission || !req.user.permissions.includes(requiredPermission)) {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: '权限不足'
|
||
});
|
||
}
|
||
|
||
// 模拟删除操作
|
||
res.json({
|
||
success: true,
|
||
message: '设备删除成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('删除设备失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '删除设备失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
// ==================== 统计数据 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/stats:
|
||
* get:
|
||
* summary: 获取智能设备统计数据
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
*/
|
||
router.get('/stats', verifyToken, requirePermission('smart_device:view'), async (req, res) => {
|
||
try {
|
||
// 模拟统计数据
|
||
const stats = {
|
||
eartags: {
|
||
total: 15,
|
||
online: 12,
|
||
offline: 2,
|
||
error: 1
|
||
},
|
||
anklets: {
|
||
total: 18,
|
||
active: 14,
|
||
standby: 3,
|
||
fault: 1
|
||
},
|
||
collars: {
|
||
total: 22,
|
||
normal: 18,
|
||
lowBattery: 3,
|
||
maintenance: 1
|
||
}
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: stats
|
||
});
|
||
} catch (error) {
|
||
console.error('获取统计数据失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取统计数据失败'
|
||
});
|
||
}
|
||
});
|
||
|
||
// ==================== 智能主机 API ====================
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/hosts:
|
||
* get:
|
||
* summary: 获取智能主机列表
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: query
|
||
* name: page
|
||
* schema:
|
||
* type: integer
|
||
* description: 页码
|
||
* - in: query
|
||
* name: limit
|
||
* schema:
|
||
* type: integer
|
||
* description: 每页数量
|
||
* - in: query
|
||
* name: status
|
||
* schema:
|
||
* type: string
|
||
* description: 设备状态筛选
|
||
* - in: query
|
||
* name: search
|
||
* schema:
|
||
* type: string
|
||
* description: 搜索关键词
|
||
* responses:
|
||
* 200:
|
||
* description: 成功获取智能主机列表
|
||
*/
|
||
router.get('/hosts', verifyToken, requirePermission('smart_host:view'), async (req, res) => {
|
||
try {
|
||
const { page = 1, limit = 10, status, search } = req.query;
|
||
const offset = (page - 1) * limit;
|
||
|
||
// 构建查询条件
|
||
const whereConditions = {};
|
||
|
||
// 状态筛选
|
||
if (status) {
|
||
switch (status) {
|
||
case 'online':
|
||
whereConditions.state = 1;
|
||
break;
|
||
case 'offline':
|
||
whereConditions.state = 0;
|
||
break;
|
||
case 'alarm':
|
||
whereConditions.state = 2;
|
||
break;
|
||
case 'maintenance':
|
||
whereConditions.state = 3;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 搜索条件
|
||
if (search) {
|
||
whereConditions[Op.or] = [
|
||
{ sid: { [Op.like]: `%${search}%` } },
|
||
{ title: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
// 查询数据库
|
||
const { count, rows } = await IotJbqServer.findAndCountAll({
|
||
where: whereConditions,
|
||
limit: parseInt(limit),
|
||
offset: parseInt(offset),
|
||
order: [
|
||
['uptime', 'DESC'], // 按更新时间倒序
|
||
['id', 'DESC'] // 次要排序按ID倒序
|
||
]
|
||
});
|
||
|
||
// 格式化数据以匹配前端UI需求
|
||
const hosts = rows.map(host => ({
|
||
id: host.id,
|
||
deviceNumber: host.sid, // 设备编号
|
||
battery: host.getBatteryPercent(), // 设备电量%
|
||
signalValue: host.getSignalText(), // 设备信号值
|
||
temperature: host.getTemperatureValue(), // 设备温度/°C
|
||
updateTime: host.getLastUpdateTime(), // 更新时间
|
||
networkStatus: host.getNetworkStatusText(), // 联网状态(基于simId)
|
||
gpsStatus: host.getGpsStatusText(), // GPS状态
|
||
latitude: host.lat, // 纬度
|
||
longitude: host.lon, // 经度
|
||
voltage: host.voltage, // 电压
|
||
signal: host.signa, // 信号强度数值
|
||
state: host.state, // 设备状态
|
||
title: host.title, // 设备标题
|
||
org_id: host.org_id, // 组织ID
|
||
uid: host.uid, // 用户ID
|
||
fence_id: host.fence_id, // 围栏ID
|
||
source_id: host.source_id, // 数据源ID
|
||
simId: host.simId // SIM卡ID,用于判断联网状态
|
||
}));
|
||
|
||
// 计算统计数据
|
||
const stats = {
|
||
total: count,
|
||
online: rows.filter(host => host.state === 1).length,
|
||
offline: rows.filter(host => host.state === 0).length,
|
||
alarm: rows.filter(host => host.state === 2).length,
|
||
maintenance: rows.filter(host => host.state === 3).length
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: hosts,
|
||
total: count,
|
||
stats: stats,
|
||
message: '获取智能主机列表成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('获取智能主机列表失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能主机列表失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/hosts/{id}:
|
||
* get:
|
||
* summary: 获取智能主机详情
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: id
|
||
* required: true
|
||
* schema:
|
||
* type: integer
|
||
* description: 主机ID
|
||
* responses:
|
||
* 200:
|
||
* description: 成功获取智能主机详情
|
||
*/
|
||
router.get('/hosts/:id', verifyToken, requirePermission('smart_host:view'), async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const host = await IotJbqServer.findByPk(id);
|
||
|
||
if (!host) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '智能主机不存在'
|
||
});
|
||
}
|
||
|
||
// 格式化数据
|
||
const hostData = {
|
||
id: host.id,
|
||
deviceNumber: host.sid,
|
||
battery: host.getBatteryPercent(),
|
||
signalValue: host.getSignalText(),
|
||
temperature: host.getTemperatureValue(),
|
||
updateTime: host.getLastUpdateTime(),
|
||
networkStatus: host.getNetworkStatusText(), // 联网状态(基于simId)
|
||
gpsStatus: host.getGpsStatusText(),
|
||
latitude: host.lat,
|
||
longitude: host.lon,
|
||
voltage: host.voltage,
|
||
signal: host.signa,
|
||
state: host.state,
|
||
title: host.title,
|
||
org_id: host.org_id,
|
||
uid: host.uid,
|
||
fence_id: host.fence_id,
|
||
source_id: host.source_id,
|
||
simId: host.simId, // SIM卡ID,用于判断联网状态
|
||
gps_state: host.gps_state,
|
||
ver: host.ver,
|
||
macsid: host.macsid,
|
||
ctwing: host.ctwing,
|
||
bank_lanwei: host.bank_lanwei,
|
||
bank_house: host.bank_house,
|
||
bank_item_id: host.bank_item_id
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: hostData,
|
||
message: '获取智能主机详情成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('获取智能主机详情失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '获取智能主机详情失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/hosts:
|
||
* post:
|
||
* summary: 创建智能主机
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* requestBody:
|
||
* required: true
|
||
* content:
|
||
* application/json:
|
||
* schema:
|
||
* type: object
|
||
* properties:
|
||
* sid:
|
||
* type: string
|
||
* title:
|
||
* type: string
|
||
* org_id:
|
||
* type: integer
|
||
* uid:
|
||
* type: integer
|
||
* responses:
|
||
* 201:
|
||
* description: 智能主机创建成功
|
||
*/
|
||
router.post('/hosts', verifyToken, requirePermission('smart_host:create'), async (req, res) => {
|
||
try {
|
||
const { sid, title, org_id, uid } = req.body;
|
||
|
||
const newHost = await IotJbqServer.create({
|
||
sid,
|
||
title: title || '',
|
||
org_id: org_id || 0,
|
||
uid: uid || 0,
|
||
time: Math.floor(Date.now() / 1000),
|
||
uptime: Math.floor(Date.now() / 1000),
|
||
state: 0,
|
||
gps_state: 'V',
|
||
lat: '90',
|
||
lon: '0',
|
||
signa: '0',
|
||
voltage: '100',
|
||
temperature: '25',
|
||
ver: '0',
|
||
fence_id: 0
|
||
});
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
data: newHost,
|
||
message: '智能主机创建成功'
|
||
});
|
||
} catch (error) {
|
||
console.error('创建智能主机失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '创建智能主机失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/hosts/{id}:
|
||
* put:
|
||
* summary: 更新智能主机
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: id
|
||
* required: true
|
||
* schema:
|
||
* type: integer
|
||
* description: 主机ID
|
||
* requestBody:
|
||
* required: true
|
||
* content:
|
||
* application/json:
|
||
* schema:
|
||
* type: object
|
||
* properties:
|
||
* title:
|
||
* type: string
|
||
* state:
|
||
* type: integer
|
||
* responses:
|
||
* 200:
|
||
* description: 智能主机更新成功
|
||
*/
|
||
router.put('/hosts/:id', verifyToken, requirePermission('smart_host:update'), async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
const { title, state } = req.body;
|
||
|
||
const host = await IotJbqServer.findByPk(id);
|
||
|
||
if (!host) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '智能主机不存在'
|
||
});
|
||
}
|
||
|
||
// 更新字段
|
||
if (title !== undefined) host.title = title;
|
||
if (state !== undefined) host.state = state;
|
||
|
||
// 更新上传时间
|
||
host.uptime = Math.floor(Date.now() / 1000);
|
||
|
||
await host.save();
|
||
|
||
res.json({
|
||
success: true,
|
||
data: host,
|
||
message: '智能主机更新成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('更新智能主机失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '更新智能主机失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @swagger
|
||
* /api/smart-devices/hosts/{id}:
|
||
* delete:
|
||
* summary: 删除智能主机
|
||
* tags: [Smart Devices]
|
||
* security:
|
||
* - bearerAuth: []
|
||
* parameters:
|
||
* - in: path
|
||
* name: id
|
||
* required: true
|
||
* schema:
|
||
* type: integer
|
||
* description: 主机ID
|
||
* responses:
|
||
* 200:
|
||
* description: 智能主机删除成功
|
||
*/
|
||
router.delete('/hosts/:id', verifyToken, requirePermission('smart_host:delete'), async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const host = await IotJbqServer.findByPk(id);
|
||
|
||
if (!host) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '智能主机不存在'
|
||
});
|
||
}
|
||
|
||
await host.destroy();
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '智能主机删除成功'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('删除智能主机失败:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: '删除智能主机失败',
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|