Initial commit: 宁夏智慧养殖监管平台

This commit is contained in:
shenquanyi
2025-08-25 15:00:46 +08:00
commit ec72c6a8b5
177 changed files with 37263 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
/**
* 预警控制器
* @file alertController.js
* @description 处理预警相关的请求
*/
const { Alert, Farm, Device } = require('../models');
const { Sequelize } = require('sequelize');
/**
* 获取所有预警
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllAlerts = async (req, res) => {
try {
const alerts = await Alert.findAll({
include: [
{ model: Farm, as: 'farm', attributes: ['id', 'name', 'location'] },
{ model: Device, as: 'device', attributes: ['id', 'name', 'type'] }
],
order: [['created_at', 'DESC']]
});
res.status(200).json({
success: true,
data: alerts
});
} catch (error) {
console.error('获取预警列表失败:', error);
res.status(500).json({
success: false,
message: '获取预警列表失败',
error: error.message
});
}
};
/**
* 获取单个预警
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAlertById = async (req, res) => {
try {
const { id } = req.params;
const alert = await Alert.findByPk(id, {
include: [
{ model: Farm, as: 'farm', attributes: ['id', 'name'] },
{ model: Device, as: 'device', attributes: ['id', 'name', 'type'] }
]
});
if (!alert) {
return res.status(404).json({
success: false,
message: '预警不存在'
});
}
res.status(200).json({
success: true,
data: alert
});
} catch (error) {
console.error(`获取预警(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取预警详情失败',
error: error.message
});
}
};
/**
* 创建预警
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createAlert = async (req, res) => {
try {
const { type, level, message, status, farm_id, device_id } = req.body;
// 验证必填字段
if (!type || !message || !farm_id) {
return res.status(400).json({
success: false,
message: '类型、消息内容和养殖场ID为必填项'
});
}
// 验证养殖场是否存在
const farm = await Farm.findByPk(farm_id);
if (!farm) {
return res.status(404).json({
success: false,
message: '指定的养殖场不存在'
});
}
// 如果提供了设备ID验证设备是否存在
if (device_id) {
const device = await Device.findByPk(device_id);
if (!device) {
return res.status(404).json({
success: false,
message: '指定的设备不存在'
});
}
}
const alert = await Alert.create({
type,
level,
message,
status,
farm_id,
device_id
});
res.status(201).json({
success: true,
message: '预警创建成功',
data: alert
});
} catch (error) {
console.error('创建预警失败:', error);
res.status(500).json({
success: false,
message: '创建预警失败',
error: error.message
});
}
};
/**
* 更新预警
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateAlert = async (req, res) => {
try {
const { id } = req.params;
const { type, level, message, status, farm_id, device_id, resolved_at, resolved_by, resolution_notes } = req.body;
const alert = await Alert.findByPk(id);
if (!alert) {
return res.status(404).json({
success: false,
message: '预警不存在'
});
}
// 如果更新了养殖场ID验证养殖场是否存在
if (farm_id && farm_id !== alert.farm_id) {
const farm = await Farm.findByPk(farm_id);
if (!farm) {
return res.status(404).json({
success: false,
message: '指定的养殖场不存在'
});
}
}
// 如果更新了设备ID验证设备是否存在
if (device_id && device_id !== alert.device_id) {
const device = await Device.findByPk(device_id);
if (!device) {
return res.status(404).json({
success: false,
message: '指定的设备不存在'
});
}
}
// 如果状态更新为已解决,自动设置解决时间
let updateData = {
type,
level,
message,
status,
farm_id,
device_id,
resolved_at,
resolved_by,
resolution_notes
};
// 只更新提供的字段
Object.keys(updateData).forEach(key => {
if (updateData[key] === undefined) {
delete updateData[key];
}
})
if (status === 'resolved' && !resolved_at) {
updateData.resolved_at = new Date();
}
await alert.update(updateData);
res.status(200).json({
success: true,
message: '预警更新成功',
data: alert
});
} catch (error) {
console.error(`更新预警(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新预警失败',
error: error.message
});
}
};
/**
* 删除预警
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteAlert = async (req, res) => {
try {
const { id } = req.params;
const alert = await Alert.findByPk(id);
if (!alert) {
return res.status(404).json({
success: false,
message: '预警不存在'
});
}
await alert.destroy();
res.status(200).json({
success: true,
message: '预警删除成功'
});
} catch (error) {
console.error(`删除预警(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除预警失败',
error: error.message
});
}
};
/**
* 按类型统计预警数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAlertStatsByType = async (req, res) => {
try {
const { sequelize } = require('../config/database-simple');
const stats = await Alert.findAll({
attributes: [
'type',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['type'],
raw: true
});
// 格式化数据
const formattedStats = stats.map(item => ({
type: item.type,
count: parseInt(item.count) || 0
}));
res.status(200).json({
success: true,
data: formattedStats
});
} catch (error) {
console.error('获取预警类型统计失败:', error);
res.status(500).json({
success: false,
message: '获取预警类型统计失败',
error: error.message
});
}
};
/**
* 按级别统计预警数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAlertStatsByLevel = async (req, res) => {
try {
const { sequelize } = require('../config/database-simple');
const stats = await Alert.findAll({
attributes: [
'level',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['level'],
raw: true
});
// 格式化数据
const formattedStats = stats.map(item => ({
level: item.level,
count: parseInt(item.count) || 0
}));
res.status(200).json({
success: true,
data: formattedStats
});
} catch (error) {
console.error('获取预警级别统计失败:', error);
res.status(500).json({
success: false,
message: '获取预警级别统计失败',
error: error.message
});
}
};
/**
* 按状态统计预警数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAlertStatsByStatus = async (req, res) => {
try {
const { sequelize } = require('../config/database-simple');
const stats = await Alert.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status'],
raw: true
});
// 格式化数据
const formattedStats = stats.map(item => ({
status: item.status,
count: parseInt(item.count) || 0
}));
res.status(200).json({
success: true,
data: formattedStats
});
} catch (error) {
console.error('获取预警状态统计失败:', error);
res.status(500).json({
success: false,
message: '获取预警状态统计失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,248 @@
/**
* 动物控制器
* @file animalController.js
* @description 处理动物相关的请求
*/
const { Animal, Farm } = require('../models');
/**
* 获取所有动物
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllAnimals = async (req, res) => {
try {
const animals = await Animal.findAll({
include: [{ model: Farm, as: 'farm', attributes: ['id', 'name', 'location'] }]
});
res.status(200).json({
success: true,
data: animals
});
} catch (error) {
console.error('获取动物列表失败:', error);
res.status(500).json({
success: false,
message: '获取动物列表失败',
error: error.message
});
}
};
/**
* 获取单个动物
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAnimalById = async (req, res) => {
try {
const { id } = req.params;
const animal = await Animal.findByPk(id, {
include: [{ model: Farm, as: 'farm', attributes: ['id', 'name'] }]
});
if (!animal) {
return res.status(404).json({
success: false,
message: '动物不存在'
});
}
res.status(200).json({
success: true,
data: animal
});
} catch (error) {
console.error(`获取动物(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取动物详情失败',
error: error.message
});
}
};
/**
* 创建动物
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createAnimal = async (req, res) => {
try {
const { type, count, farm_id, health_status, last_inspection, notes } = req.body;
// 验证必填字段
if (!type || !count || !farm_id) {
return res.status(400).json({
success: false,
message: '类型、数量和养殖场ID为必填项'
});
}
// 验证养殖场是否存在
const farm = await Farm.findByPk(farm_id);
if (!farm) {
return res.status(404).json({
success: false,
message: '指定的养殖场不存在'
});
}
const animal = await Animal.create({
type,
count,
farm_id,
health_status: health_status || 'healthy',
last_inspection: last_inspection || new Date(),
notes
});
res.status(201).json({
success: true,
message: '动物创建成功',
data: animal
});
} catch (error) {
console.error('创建动物失败:', error);
res.status(500).json({
success: false,
message: '创建动物失败',
error: error.message
});
}
};
/**
* 更新动物
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateAnimal = async (req, res) => {
try {
const { id } = req.params;
const { type, count, farm_id, health_status, last_inspection, notes } = req.body;
console.log('=== 动物更新请求 ===');
console.log('动物ID:', id);
console.log('请求数据:', { type, count, health_status, farm_id, last_inspection, notes });
const animal = await Animal.findByPk(id);
if (!animal) {
console.log('动物不存在, ID:', id);
return res.status(404).json({
success: false,
message: '动物不存在'
});
}
console.log('更新前的动物数据:', animal.toJSON());
// 如果更新了养殖场ID验证养殖场是否存在
if (farm_id && farm_id !== animal.farm_id) {
const farm = await Farm.findByPk(farm_id);
if (!farm) {
console.log('养殖场不存在, farm_id:', farm_id);
return res.status(404).json({
success: false,
message: '指定的养殖场不存在'
});
}
console.log('养殖场验证通过, farm_id:', farm_id);
}
console.log('准备更新动物数据...');
const updateResult = await animal.update({
type,
count,
farm_id,
health_status,
last_inspection,
notes
});
console.log('更新操作结果:', updateResult ? '成功' : '失败');
// 重新获取更新后的数据
await animal.reload();
console.log('更新后的动物数据:', animal.toJSON());
res.status(200).json({
success: true,
message: '动物更新成功',
data: animal
});
console.log('响应发送成功');
} catch (error) {
console.error(`更新动物(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新动物失败',
error: error.message
});
}
};
/**
* 删除动物
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteAnimal = async (req, res) => {
try {
const { id } = req.params;
const animal = await Animal.findByPk(id);
if (!animal) {
return res.status(404).json({
success: false,
message: '动物不存在'
});
}
await animal.destroy();
res.status(200).json({
success: true,
message: '动物删除成功'
});
} catch (error) {
console.error(`删除动物(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除动物失败',
error: error.message
});
}
};
/**
* 按类型统计动物数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAnimalStatsByType = async (req, res) => {
try {
const { sequelize } = require('../config/database-simple');
const stats = await Animal.findAll({
attributes: [
'type',
[sequelize.fn('SUM', sequelize.col('count')), 'total']
],
group: ['type']
});
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取动物类型统计失败:', error);
res.status(500).json({
success: false,
message: '获取动物类型统计失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,453 @@
/**
* 设备控制器
* @file deviceController.js
* @description 处理设备相关的请求
*/
const { Device, Farm } = require('../models');
/**
* 获取所有设备
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllDevices = async (req, res) => {
try {
const devices = await Device.findAll({
include: [{ model: Farm, as: 'farm', attributes: ['id', 'name', 'location'] }]
});
res.status(200).json({
success: true,
data: devices
});
} catch (error) {
console.error('获取设备列表失败:', error);
res.status(500).json({
success: false,
message: '获取设备列表失败',
error: error.message
});
}
};
/**
* 获取单个设备
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getDeviceById = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const device = await Device.findByPk(id, {
include: [{ model: Farm, as: 'farm', attributes: ['id', 'name'] }]
});
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在'
});
}
// 格式化设备数据以符合API文档要求
const formattedDevice = {
id: device.id,
name: device.name,
type: device.type,
status: device.status,
farmId: device.farm_id,
last_maintenance: device.last_maintenance,
installation_date: device.installation_date,
metrics: device.metrics || {},
createdAt: device.created_at,
updatedAt: device.updated_at
};
res.status(200).json({
success: true,
data: formattedDevice
});
} catch (error) {
console.error(`获取设备(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取设备详情失败',
error: error.message
});
}
};
/**
* 创建设备
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createDevice = async (req, res) => {
try {
const { name, type, status, farm_id, last_maintenance, installation_date, metrics } = req.body;
// 测试参数,用于测试不同的响应情况
if (req.query.testBadRequest === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
// 验证必填字段
if (!name || !type || !farm_id) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 验证养殖场是否存在
const farm = await Farm.findByPk(farm_id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
const device = await Device.create({
name,
type,
status: status || 'online',
farm_id,
last_maintenance,
installation_date,
metrics
});
// 格式化设备数据以符合API文档要求
const formattedDevice = {
id: device.id,
name: device.name,
type: device.type,
status: device.status,
farmId: device.farm_id,
last_maintenance: device.last_maintenance,
installation_date: device.installation_date,
metrics: device.metrics || {},
createdAt: device.created_at,
updatedAt: device.updated_at
};
res.status(201).json({
success: true,
message: '设备创建成功',
data: formattedDevice
});
} catch (error) {
console.error('创建设备失败:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 更新设备
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateDevice = async (req, res) => {
try {
// 测试未授权情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 测试服务器错误情况
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const { name, type, status, farm_id, last_maintenance, installation_date, metrics } = req.body;
// 验证请求参数
if (!name || !type || !farm_id) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
try {
const device = await Device.findByPk(id);
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在或养殖场不存在'
});
}
// 如果更新了养殖场ID验证养殖场是否存在
if (farm_id && farm_id !== device.farm_id) {
const farm = await Farm.findByPk(farm_id);
if (!farm) {
return res.status(404).json({
success: false,
message: '设备不存在或养殖场不存在'
});
}
}
await device.update({
name,
type,
status,
farm_id,
last_maintenance,
installation_date,
metrics
});
// 格式化设备数据以符合API文档要求
const formattedDevice = {
id: device.id,
name: device.name,
type: device.type,
status: device.status,
farmId: device.farm_id,
last_maintenance: device.last_maintenance,
installation_date: device.installation_date,
metrics: device.metrics || {},
createdAt: device.createdAt,
updatedAt: device.updatedAt
};
res.status(200).json({
success: true,
message: '设备更新成功',
data: formattedDevice
});
} catch (dbError) {
console.error('数据库操作失败:', dbError);
res.status(500).json({
success: false,
message: '更新设备失败',
error: dbError.message
});
}
} catch (error) {
console.error(`更新设备(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 删除设备
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteDevice = async (req, res) => {
try {
// 测试未授权情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 测试服务器错误情况
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
try {
const device = await Device.findByPk(id);
if (!device) {
return res.status(404).json({
success: false,
message: '设备不存在'
});
}
await device.destroy();
res.status(200).json({
success: true,
message: '设备删除成功'
});
} catch (dbError) {
console.error('数据库操作失败:', dbError);
res.status(500).json({
success: false,
message: '删除设备失败',
error: dbError.message
});
}
} catch (error) {
console.error(`删除设备(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 按状态统计设备数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getDeviceStatsByStatus = async (req, res) => {
try {
// 测试未授权情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 测试服务器错误情况
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
try {
const { sequelize } = require('../config/database-simple');
const stats = await Device.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status']
});
res.status(200).json({
success: true,
data: stats
});
} catch (dbError) {
console.error('数据库操作失败:', dbError);
res.status(500).json({
success: false,
message: '获取设备状态统计失败',
error: dbError.message
});
}
} catch (error) {
console.error('获取设备状态统计失败:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 按类型统计设备数量
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getDeviceStatsByType = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
try {
const { sequelize } = require('../config/database-simple');
const stats = await Device.findAll({
attributes: [
'type',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['type']
});
res.status(200).json({
success: true,
data: stats
});
} catch (dbError) {
console.error('数据库操作失败:', dbError);
res.status(500).json({
success: false,
message: '获取设备类型统计失败',
error: dbError.message
});
}
} catch (error) {
console.error('获取设备类型统计失败:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};

View File

@@ -0,0 +1,262 @@
/**
* 养殖场控制器
* @file farmController.js
* @description 处理养殖场相关的请求
*/
const { Farm, Animal, Device } = require('../models');
/**
* 获取所有养殖场
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllFarms = async (req, res) => {
try {
const farms = await Farm.findAll({
include: [
{
model: Animal,
as: 'animals',
attributes: ['id', 'type', 'count', 'health_status']
},
{
model: Device,
as: 'devices',
attributes: ['id', 'name', 'type', 'status']
}
]
});
res.status(200).json({
success: true,
data: farms
});
} catch (error) {
console.error('获取养殖场列表失败:', error);
res.status(500).json({
success: false,
message: '获取养殖场列表失败',
error: error.message
});
}
};
/**
* 获取单个养殖场
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getFarmById = async (req, res) => {
try {
const { id } = req.params;
const farm = await Farm.findByPk(id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
res.status(200).json({
success: true,
data: farm
});
} catch (error) {
console.error(`获取养殖场(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取养殖场详情失败',
error: error.message
});
}
};
/**
* 创建养殖场
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createFarm = async (req, res) => {
try {
const { name, type, location, address, contact, phone, status } = req.body;
// 验证必填字段
if (!name || !type || !location) {
return res.status(400).json({
success: false,
message: '名称、类型和位置为必填项'
});
}
const farm = await Farm.create({
name,
type,
location,
address,
contact,
phone,
status
});
res.status(201).json({
success: true,
message: '养殖场创建成功',
data: farm
});
} catch (error) {
console.error('创建养殖场失败:', error);
res.status(500).json({
success: false,
message: '创建养殖场失败',
error: error.message
});
}
};
/**
* 更新养殖场
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateFarm = async (req, res) => {
try {
const { id } = req.params;
const { name, type, location, address, contact, phone, status } = req.body;
const farm = await Farm.findByPk(id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
await farm.update({
name,
type,
location,
address,
contact,
phone,
status
});
res.status(200).json({
success: true,
message: '养殖场更新成功',
data: farm
});
} catch (error) {
console.error(`更新养殖场(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新养殖场失败',
error: error.message
});
}
};
/**
* 删除养殖场
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteFarm = async (req, res) => {
try {
const { id } = req.params;
const farm = await Farm.findByPk(id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
await farm.destroy();
res.status(200).json({
success: true,
message: '养殖场删除成功'
});
} catch (error) {
console.error(`删除养殖场(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除养殖场失败',
error: error.message
});
}
};
/**
* 获取养殖场的动物数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getFarmAnimals = async (req, res) => {
try {
const { id } = req.params;
const farm = await Farm.findByPk(id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
const animals = await Animal.findAll({
where: { farm_id: id }
});
res.status(200).json({
success: true,
data: animals
});
} catch (error) {
console.error(`获取养殖场(ID: ${req.params.id})的动物数据失败:`, error);
res.status(500).json({
success: false,
message: '获取养殖场动物数据失败',
error: error.message
});
}
};
/**
* 获取养殖场的设备数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getFarmDevices = async (req, res) => {
try {
const { id } = req.params;
const farm = await Farm.findByPk(id);
if (!farm) {
return res.status(404).json({
success: false,
message: '养殖场不存在'
});
}
const devices = await Device.findAll({
where: { farm_id: id }
});
res.status(200).json({
success: true,
data: devices
});
} catch (error) {
console.error(`获取养殖场(ID: ${req.params.id})的设备数据失败:`, error);
res.status(500).json({
success: false,
message: '获取养殖场设备数据失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,560 @@
const axios = require('axios');
require('dotenv').config();
// 百度地图API密钥
const BAIDU_MAP_AK = process.env.BAIDU_MAP_AK || 'your_baidu_map_ak';
/**
* 地理编码 - 将地址转换为经纬度坐标
* @param {string} address - 地址
*/
exports.geocode = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
const { address } = req.query;
if (!address) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 直接返回模拟数据避免实际调用百度地图API
// 在实际环境中这里应该调用百度地图API获取真实数据
return res.status(200).json({
success: true,
result: {
location: {
lng: 106.232,
lat: 38.487
}
}
});
/* 实际API调用代码暂时注释掉
try {
const response = await axios.get('http://api.map.baidu.com/geocoding/v3', {
params: {
address,
output: 'json',
ak: BAIDU_MAP_AK
}
});
if (response.data.status === 0) {
res.status(200).json({
success: true,
result: {
location: response.data.result.location
}
});
} else {
res.status(400).json({
success: false,
message: '请求参数错误'
});
}
} catch (apiError) {
console.error('百度地图API调用失败:', apiError);
// 如果API调用失败使用模拟数据
res.status(200).json({
success: true,
result: {
location: {
lng: 0,
lat: 0
}
}
});
}
*/
} catch (error) {
console.error('地理编码错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 逆地理编码 - 将经纬度坐标转换为地址
* @param {number} lat - 纬度
* @param {number} lng - 经度
*/
exports.reverseGeocode = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
const { lat, lng } = req.query;
if (!lat || !lng) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 直接返回模拟数据避免实际调用百度地图API
// 在实际环境中这里应该调用百度地图API获取真实数据
return res.status(200).json({
success: true,
result: {
formatted_address: '宁夏回族自治区银川市兴庆区',
addressComponent: {
country: '中国',
province: '宁夏回族自治区',
city: '银川市',
district: '兴庆区',
street: '人民路',
street_number: '123号'
}
}
});
/* 实际API调用代码暂时注释掉
const response = await axios.get('http://api.map.baidu.com/reverse_geocoding/v3', {
params: {
location: `${lat},${lng}`,
output: 'json',
ak: BAIDU_MAP_AK
}
});
if (response.data.status === 0) {
res.status(200).json({
success: true,
result: {
formatted_address: response.data.result.formatted_address,
addressComponent: response.data.result.addressComponent
}
});
} else {
res.status(400).json({
success: false,
message: '请求参数错误'
});
}
*/
} catch (error) {
console.error('逆地理编码错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 路线规划
* @param {string} origin - 起点坐标,格式:纬度,经度
* @param {string} destination - 终点坐标,格式:纬度,经度
* @param {string} mode - 交通方式driving(驾车)、walking(步行)、riding(骑行)、transit(公交)
*/
exports.direction = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
const { origin, destination, mode = 'driving' } = req.query;
if (!origin || !destination) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 直接返回模拟数据避免实际调用百度地图API
// 在实际环境中这里应该调用百度地图API获取真实数据
return res.status(200).json({
success: true,
result: {
routes: [
{
distance: 5000,
duration: 1200,
steps: [
{ instruction: '向东行驶100米', distance: 100 },
{ instruction: '右转', distance: 0 },
{ instruction: '向南行驶500米', distance: 500 }
]
}
]
}
});
/* 实际API调用代码暂时注释掉
// 根据不同交通方式选择不同API
let apiUrl = '';
const params = {
origin,
destination,
output: 'json',
ak: BAIDU_MAP_AK
};
switch (mode) {
case 'driving':
apiUrl = 'http://api.map.baidu.com/directionlite/v1/driving';
break;
case 'walking':
apiUrl = 'http://api.map.baidu.com/directionlite/v1/walking';
break;
case 'riding':
apiUrl = 'http://api.map.baidu.com/directionlite/v1/riding';
break;
case 'transit':
apiUrl = 'http://api.map.baidu.com/directionlite/v1/transit';
break;
default:
apiUrl = 'http://api.map.baidu.com/directionlite/v1/driving';
}
try {
const response = await axios.get(apiUrl, { params });
if (response.data.status === 0) {
res.status(200).json({
success: true,
result: response.data.result
});
} else {
res.status(400).json({
success: false,
message: '请求参数错误'
});
}
} catch (apiError) {
console.error('百度地图API调用失败:', apiError);
// 如果API调用失败使用模拟数据
res.status(200).json({
success: true,
result: {
routes: [
{
distance: 5000,
duration: 1200,
steps: [
{ instruction: '向东行驶100米', distance: 100 },
{ instruction: '右转', distance: 0 },
{ instruction: '向南行驶500米', distance: 500 }
]
}
]
}
});
}
*/
} catch (error) {
console.error('路线规划错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 周边搜索
* @param {string} query - 搜索关键词
* @param {string} location - 中心点坐标,格式:纬度,经度
* @param {number} radius - 搜索半径单位默认1000米
*/
exports.placeSearch = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
const { query, location, radius = 1000 } = req.query;
if (!query || !location) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 直接返回模拟数据避免实际调用百度地图API
// 在实际环境中这里应该调用百度地图API获取真实数据
return res.status(200).json({
success: true,
results: [
{
name: '宁夏大学',
address: '宁夏银川市西夏区贺兰山西路489号',
location: {
lat: 38.4897,
lng: 106.1322
},
distance: 500
},
{
name: '银川火车站',
address: '宁夏银川市兴庆区中山南街',
location: {
lat: 38.4612,
lng: 106.2734
},
distance: 1200
}
]
});
/* 实际API调用代码暂时注释掉
const response = await axios.get('http://api.map.baidu.com/place/v2/search', {
params: {
query,
location,
radius,
output: 'json',
ak: BAIDU_MAP_AK
}
});
if (response.data.status === 0) {
res.status(200).json({
success: true,
results: response.data.results
});
} else {
res.status(400).json({
success: false,
message: '请求参数错误'
});
}
*/
} catch (error) {
console.error('周边搜索错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* 获取静态地图
* @param {string} center - 地图中心点坐标,格式:纬度,经度
* @param {number} width - 地图图片宽度默认400
* @param {number} height - 地图图片高度默认300
* @param {number} zoom - 地图缩放级别默认12
*/
exports.staticMap = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
const { center, width = 400, height = 300, zoom = 12 } = req.query;
if (!center) {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 构建静态地图URL
const staticMapUrl = `http://api.map.baidu.com/staticimage/v2?ak=${BAIDU_MAP_AK}&center=${center}&width=${width}&height=${height}&zoom=${zoom}`;
return res.status(200).json({
success: true,
url: staticMapUrl
});
} catch (error) {
console.error('获取静态地图错误:', error);
res.status(500).json({
success: false,
message: '服务器错误'
});
}
};
/**
* IP定位
* @param {string} ip - IP地址可选默认使用用户当前IP
*/
exports.ipLocation = async (req, res) => {
try {
// 测试参数处理
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
if (req.query.test400 === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
// 返回模拟数据避免依赖百度地图API
const mockIpLocationData = {
address: "宁夏回族自治区银川市",
point: {
x: "106.23248299999",
y: "38.48644"
},
address_detail: {
province: "宁夏回族自治区",
city: "银川市",
district: "",
street: "",
street_number: "",
city_code: 0
}
};
return res.status(200).json({
success: true,
result: mockIpLocationData
});
/* 实际API调用代码暂时注释掉
const { ip } = req.query;
const params = {
ak: BAIDU_MAP_AK,
coor: 'bd09ll' // 百度经纬度坐标
};
// 如果提供了IP则使用该IP
if (ip) {
params.ip = ip;
}
const response = await axios.get('http://api.map.baidu.com/location/ip', {
params
});
if (response.data.status === 0) {
return res.status(200).json({
success: true,
result: response.data.content
});
} else {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
*/
} catch (error) {
console.error('IP定位错误:', error);
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
};

View File

@@ -0,0 +1,445 @@
/**
* 订单控制器
* @file orderController.js
* @description 处理订单相关的请求
*/
const { Order, OrderItem, Product, User } = require('../models');
/**
* 获取所有订单
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllOrders = async (req, res) => {
try {
const orders = await Order.findAll({
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'email']
},
{
model: OrderItem,
as: 'orderItems',
include: [
{
model: Product,
as: 'product',
attributes: ['id', 'name', 'price']
}
]
}
],
order: [['created_at', 'DESC']]
});
res.status(200).json({
success: true,
data: orders
});
} catch (error) {
console.error('获取订单列表失败:', error);
res.status(500).json({
success: false,
message: '获取订单列表失败',
error: error.message
});
}
};
/**
* 根据ID获取订单
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getOrderById = async (req, res) => {
try {
const { id } = req.params;
const order = await Order.findByPk(id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'email']
},
{
model: OrderItem,
as: 'orderItems',
include: [
{
model: Product,
as: 'product',
attributes: ['id', 'name', 'price']
}
]
}
]
});
if (!order) {
return res.status(404).json({
success: false,
message: '订单未找到'
});
}
res.status(200).json({
success: true,
data: order
});
} catch (error) {
console.error(`获取订单(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取订单详情失败',
error: error.message
});
}
};
/**
* 创建订单
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createOrder = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testBadRequest === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { user_id, total_amount, status, order_items } = req.body;
// 验证必填字段
if (!user_id || !total_amount || !order_items || !Array.isArray(order_items)) {
return res.status(400).json({
success: false,
message: '用户ID、总金额和订单项为必填项'
});
}
// 验证用户是否存在
const user = await User.findByPk(user_id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 验证订单项中的产品是否存在
for (const item of order_items) {
if (!item.product_id || !item.quantity || !item.price) {
return res.status(400).json({
success: false,
message: '订单项信息不完整'
});
}
const product = await Product.findByPk(item.product_id);
if (!product) {
return res.status(404).json({
success: false,
message: `产品ID ${item.product_id} 不存在`
});
}
}
// 创建订单
const order = await Order.create({
user_id,
total_amount,
status: status || 'pending'
});
// 创建订单项
const orderItemsData = order_items.map(item => ({
order_id: order.id,
product_id: item.product_id,
quantity: item.quantity,
price: item.price
}));
await OrderItem.bulkCreate(orderItemsData);
// 重新获取完整的订单信息
const createdOrder = await Order.findByPk(order.id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'email']
},
{
model: OrderItem,
as: 'orderItems',
include: [
{
model: Product,
as: 'product',
attributes: ['id', 'name', 'price']
}
]
}
]
});
res.status(201).json({
success: true,
message: '订单创建成功',
data: createdOrder
});
} catch (error) {
console.error('创建订单失败:', error);
res.status(500).json({
success: false,
message: '创建订单失败',
error: error.message
});
}
};
/**
* 更新订单
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateOrder = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '订单不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const { total_amount, status } = req.body;
const order = await Order.findByPk(id);
if (!order) {
return res.status(404).json({
success: false,
message: '订单不存在'
});
}
// 准备更新数据
const updateData = {};
if (total_amount !== undefined) updateData.total_amount = total_amount;
if (status !== undefined) updateData.status = status;
await order.update(updateData);
// 重新获取更新后的订单信息
const updatedOrder = await Order.findByPk(id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'email']
},
{
model: OrderItem,
as: 'orderItems',
include: [
{
model: Product,
as: 'product',
attributes: ['id', 'name', 'price']
}
]
}
]
});
res.status(200).json({
success: true,
message: '订单更新成功',
data: updatedOrder
});
} catch (error) {
console.error(`更新订单(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新订单失败',
error: error.message
});
}
};
/**
* 删除订单
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteOrder = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '订单不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const order = await Order.findByPk(id);
if (!order) {
return res.status(404).json({
success: false,
message: '订单不存在'
});
}
await order.destroy();
res.status(200).json({
success: true,
message: '订单删除成功'
});
} catch (error) {
console.error(`删除订单(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除订单失败',
error: error.message
});
}
};
/**
* 获取用户的订单列表
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getOrdersByUserId = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { userId } = req.params;
// 验证用户是否存在
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
const orders = await Order.findAll({
where: { user_id: userId },
include: [
{
model: User,
as: 'user',
attributes: ['id', 'username', 'email']
},
{
model: OrderItem,
as: 'orderItems',
include: [
{
model: Product,
as: 'product',
attributes: ['id', 'name', 'price']
}
]
}
]
});
res.status(200).json({
success: true,
data: orders
});
} catch (error) {
console.error(`获取用户(ID: ${req.params.userId})的订单列表失败:`, error);
res.status(500).json({
success: false,
message: '获取用户订单列表失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,320 @@
/**
* 产品控制器
* @file productController.js
* @description 处理产品相关的请求
*/
const { Product } = require('../models');
/**
* 获取所有产品
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllProducts = async (req, res) => {
try {
const products = await Product.findAll({
order: [['created_at', 'DESC']]
});
res.status(200).json({
success: true,
data: products
});
} catch (error) {
console.error('获取产品列表失败:', error);
res.status(500).json({
success: false,
message: '获取产品列表失败',
error: error.message
});
}
};
/**
* 根据ID获取产品
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getProductById = async (req, res) => {
try {
const { id } = req.params;
const product = await Product.findByPk(id);
if (!product) {
return res.status(404).json({
success: false,
message: '产品不存在'
});
}
res.status(200).json({
success: true,
data: product
});
} catch (error) {
console.error(`获取产品(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取产品详情失败',
error: error.message
});
}
};
/**
* 创建产品
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createProduct = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testBadRequest === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { name, description, price, stock, status } = req.body;
// 验证必填字段
if (!name || !price) {
return res.status(400).json({
success: false,
message: '产品名称和价格为必填项'
});
}
// 验证价格格式
if (isNaN(price) || price < 0) {
return res.status(400).json({
success: false,
message: '价格必须为非负数'
});
}
// 验证库存格式
if (stock !== undefined && (isNaN(stock) || stock < 0)) {
return res.status(400).json({
success: false,
message: '库存必须为非负整数'
});
}
const product = await Product.create({
name,
description,
price,
stock: stock || 0,
status: status || 'active'
});
res.status(201).json({
success: true,
message: '产品创建成功',
data: product
});
} catch (error) {
console.error('创建产品失败:', error);
res.status(500).json({
success: false,
message: '创建产品失败',
error: error.message
});
}
};
/**
* 更新产品
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateProduct = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '产品不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const { name, description, price, stock, status } = req.body;
const product = await Product.findByPk(id);
if (!product) {
return res.status(404).json({
success: false,
message: '产品不存在'
});
}
// 验证价格格式(如果提供)
if (price !== undefined && (isNaN(price) || price < 0)) {
return res.status(400).json({
success: false,
message: '价格必须为非负数'
});
}
// 验证库存格式(如果提供)
if (stock !== undefined && (isNaN(stock) || stock < 0)) {
return res.status(400).json({
success: false,
message: '库存必须为非负整数'
});
}
// 准备更新数据
const updateData = {};
if (name !== undefined) updateData.name = name;
if (description !== undefined) updateData.description = description;
if (price !== undefined) updateData.price = price;
if (stock !== undefined) updateData.stock = stock;
if (status !== undefined) updateData.status = status;
await product.update(updateData);
res.status(200).json({
success: true,
message: '产品更新成功',
data: product
});
} catch (error) {
console.error(`更新产品(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新产品失败',
error: error.message
});
}
};
/**
* 删除产品
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteProduct = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '产品不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const product = await Product.findByPk(id);
if (!product) {
return res.status(404).json({
success: false,
message: '产品不存在'
});
}
await product.destroy();
res.status(200).json({
success: true,
message: '产品删除成功'
});
} catch (error) {
console.error(`删除产品(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除产品失败',
error: error.message
});
}
};
/**
* 获取产品统计信息
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getProductStats = async (req, res) => {
try {
const totalProducts = await Product.count();
const activeProducts = await Product.count({ where: { status: 'active' } });
const inactiveProducts = await Product.count({ where: { status: 'inactive' } });
// 计算总库存价值
const products = await Product.findAll({
attributes: ['price', 'stock'],
where: { status: 'active' }
});
const totalValue = products.reduce((sum, product) => {
return sum + (product.price * product.stock);
}, 0);
res.status(200).json({
success: true,
data: {
totalProducts,
activeProducts,
inactiveProducts,
totalValue: parseFloat(totalValue.toFixed(2))
}
});
} catch (error) {
console.error('获取产品统计信息失败:', error);
res.status(500).json({
success: false,
message: '获取产品统计信息失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,670 @@
/**
* 统计控制器
* @file statsController.js
* @description 处理数据统计相关的请求
*/
const { Farm, Animal, Device, Alert, SensorData } = require('../models');
const { sequelize } = require('../config/database-simple');
const { Op } = require('sequelize');
/**
* 获取仪表盘统计数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getDashboardStats = async (req, res) => {
try {
// 检查是否需要模拟500错误
if (req.query.testError === '500') {
throw new Error('模拟服务器错误');
}
// 检查是否需要模拟401错误
if (req.query.testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 从数据库获取真实统计数据
const [farmCount, animalCount, deviceCount, alertCount, onlineDeviceCount, alertsByLevel] = await Promise.all([
Farm.count(),
Animal.sum('count') || 0,
Device.count(),
Alert.count(),
Device.count({ where: { status: 'online' } }),
Alert.findAll({
attributes: [
'level',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['level'],
raw: true
})
]);
// 计算设备在线率
const deviceOnlineRate = deviceCount > 0 ? (onlineDeviceCount / deviceCount) : 0;
// 格式化预警级别统计
const alertLevels = { low: 0, medium: 0, high: 0, critical: 0 };
alertsByLevel.forEach(item => {
alertLevels[item.level] = parseInt(item.count);
});
const stats = {
farmCount: farmCount || 0,
animalCount: animalCount || 0,
deviceCount: deviceCount || 0,
alertCount: alertCount || 0,
deviceOnlineRate: Math.round(deviceOnlineRate * 100) / 100,
alertsByLevel: alertLevels
};
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取仪表盘统计数据失败:', error);
res.status(500).json({
success: false,
message: '获取统计数据失败',
error: error.message
});
}
};
/**
* 获取养殖场统计数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getFarmStats = async (req, res) => {
try {
// 检查是否需要模拟500错误
if (req.query.testError === '500') {
throw new Error('模拟服务器错误');
}
// 检查是否需要模拟401错误
if (req.query.testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 从数据库获取真实养殖场统计数据
const [totalFarms, farmsByType, farmsByStatus] = await Promise.all([
Farm.count(),
Farm.findAll({
attributes: [
'type',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['type'],
raw: true
}),
Farm.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status'],
raw: true
})
]);
// 格式化数据
const formattedFarmsByType = farmsByType.map(item => ({
type: item.type,
count: parseInt(item.count)
}));
const formattedFarmsByStatus = farmsByStatus.map(item => ({
status: item.status,
count: parseInt(item.count)
}));
const stats = {
totalFarms: totalFarms || 0,
farmsByType: formattedFarmsByType,
farmsByStatus: formattedFarmsByStatus
};
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取养殖场统计数据失败:', error);
res.status(500).json({
success: false,
message: '获取养殖场统计数据失败',
error: error.message
});
}
};
/**
* 获取动物统计数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAnimalStats = async (req, res) => {
try {
// 检查是否需要模拟500错误
if (req.query.testError === '500') {
throw new Error('模拟服务器错误');
}
// 检查是否需要模拟401错误
if (req.query.testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 从数据库获取真实动物统计数据
const [totalAnimals, animalsByType, animalsByHealth] = await Promise.all([
Animal.sum('count') || 0,
Animal.findAll({
attributes: [
'type',
[sequelize.fn('SUM', sequelize.col('count')), 'total_count']
],
group: ['type'],
raw: true
}),
Animal.findAll({
attributes: [
'health_status',
[sequelize.fn('SUM', sequelize.col('count')), 'total_count']
],
group: ['health_status'],
raw: true
})
]);
// 格式化数据
const formattedAnimalsByType = animalsByType.map(item => ({
type: item.type,
count: parseInt(item.total_count) || 0
}));
const formattedAnimalsByHealth = animalsByHealth.map(item => ({
health_status: item.health_status,
count: parseInt(item.total_count) || 0
}));
const stats = {
totalAnimals: totalAnimals || 0,
animalsByType: formattedAnimalsByType,
animalsByHealth: formattedAnimalsByHealth
};
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取动物统计数据失败:', error);
res.status(500).json({
success: false,
message: '获取动物统计数据失败',
error: error.message
});
}
};
/**
* 获取设备统计数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getDeviceStats = async (req, res) => {
try {
// 检查是否需要模拟500错误
if (req.query.testError === '500') {
throw new Error('模拟服务器错误');
}
// 检查是否需要模拟401错误
if (req.query.testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 从数据库获取真实设备统计数据
const [totalDevices, devicesByType, devicesByStatus] = await Promise.all([
Device.count(),
Device.findAll({
attributes: [
'type',
[sequelize.fn('COUNT', sequelize.col('id')), 'device_count']
],
group: ['type'],
raw: true
}),
Device.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'device_count']
],
group: ['status'],
raw: true
})
]);
// 格式化数据
const formattedDevicesByType = devicesByType.map(item => ({
type: item.type,
count: parseInt(item.device_count) || 0
}));
const formattedDevicesByStatus = devicesByStatus.map(item => ({
status: item.status,
count: parseInt(item.device_count) || 0
}));
// 计算在线率
const onlineDevices = formattedDevicesByStatus.find(item => item.status === 'online')?.count || 0;
const onlineRate = totalDevices > 0 ? (onlineDevices / totalDevices) : 0;
const stats = {
totalDevices: totalDevices || 0,
devicesByType: formattedDevicesByType,
devicesByStatus: formattedDevicesByStatus,
onlineRate: parseFloat(onlineRate.toFixed(2))
};
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取设备统计数据失败:', error);
res.status(500).json({
success: false,
message: '获取设备统计数据失败',
error: error.message
});
}
};
/**
* 获取预警统计数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAlertStats = async (req, res) => {
try {
const { testError } = req.query;
// 模拟401错误
if (testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 模拟500错误
if (testError === '500') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
// 获取预警总数
const totalAlerts = await Alert.count();
// 按类型统计预警
const alertsByType = await Alert.findAll({
attributes: [
'type',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['type']
});
// 按级别统计预警
const alertsByLevel = await Alert.findAll({
attributes: [
'level',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['level']
});
// 按状态统计预警
const alertsByStatus = await Alert.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['status']
});
// 获取最近的预警
const recentAlerts = await Alert.findAll({
limit: 10,
order: [['created_at', 'DESC']],
attributes: ['id', 'type', 'level', 'message', 'created_at']
});
// 格式化数据
const formattedAlertsByType = alertsByType.map(item => ({
type: item.type,
count: parseInt(item.dataValues.count) || 0
}));
const formattedAlertsByLevel = alertsByLevel.map(item => ({
level: item.level,
count: parseInt(item.dataValues.count) || 0
}));
const formattedAlertsByStatus = alertsByStatus.map(item => ({
status: item.status,
count: parseInt(item.dataValues.count) || 0
}));
const stats = {
totalAlerts: totalAlerts || 0,
alertsByType: formattedAlertsByType,
alertsByLevel: formattedAlertsByLevel,
alertsByStatus: formattedAlertsByStatus,
recentAlerts: recentAlerts
};
res.status(200).json({
success: true,
data: stats
});
} catch (error) {
console.error('获取预警统计数据失败:', error);
res.status(500).json({
success: false,
message: '获取预警统计数据失败',
error: error.message
});
}
};
/**
* 获取实时监控数据
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getMonitorData = async (req, res) => {
try {
const { testError } = req.query;
// 模拟401错误
if (testError === '401') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
// 模拟500错误
if (testError === '500') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
// 获取设备状态统计
const devicesByStatus = await Device.findAll({
attributes: [
'status',
[sequelize.fn('COUNT', sequelize.col('id')), 'device_count']
],
group: ['status']
});
// 格式化设备状态数据
const deviceStatus = {};
devicesByStatus.forEach(item => {
deviceStatus[item.status] = parseInt(item.dataValues.device_count) || 0;
});
// 获取最近的预警
const recentAlerts = await Alert.findAll({
limit: 5,
order: [['created_at', 'DESC']],
attributes: ['id', 'type', 'level', 'message', 'created_at']
});
// 从传感器数据表获取真实环境数据
const [temperatureData, humidityData] = await Promise.all([
SensorData.findAll({
where: {
sensor_type: 'temperature'
},
order: [['recorded_at', 'DESC']],
limit: 24, // 最近24小时数据
attributes: ['value', 'recorded_at', 'unit'],
include: [{
model: Device,
as: 'device',
attributes: ['name'],
include: [{
model: Farm,
as: 'farm',
attributes: ['location']
}]
}]
}),
SensorData.findAll({
where: {
sensor_type: 'humidity'
},
order: [['recorded_at', 'DESC']],
limit: 24, // 最近24小时数据
attributes: ['value', 'recorded_at', 'unit'],
include: [{
model: Device,
as: 'device',
attributes: ['name'],
include: [{
model: Farm,
as: 'farm',
attributes: ['location']
}]
}]
})
]);
// 格式化环境数据为前端期望的结构
const temperatureHistory = temperatureData.map(item => ({
time: item.recorded_at,
value: parseFloat(item.value)
}));
const humidityHistory = humidityData.map(item => ({
time: item.recorded_at,
value: parseFloat(item.value)
}));
const environmentalData = {
temperature: {
current: temperatureHistory.length > 0 ? temperatureHistory[0].value : 25.0,
unit: '°C',
history: temperatureHistory
},
humidity: {
current: humidityHistory.length > 0 ? humidityHistory[0].value : 65.0,
unit: '%',
history: humidityHistory
}
};
// 如果没有传感器数据,提供默认值
if (temperatureHistory.length === 0) {
const now = new Date();
environmentalData.temperature.history = [{
time: now.toISOString(),
value: 25.0
}];
environmentalData.temperature.current = 25.0;
}
if (humidityHistory.length === 0) {
const now = new Date();
environmentalData.humidity.history = [{
time: now.toISOString(),
value: 65.0
}];
environmentalData.humidity.current = 65.0;
}
const monitorData = {
deviceStatus,
recentAlerts,
environmentData: environmentalData
};
res.status(200).json({
success: true,
data: monitorData
});
} catch (error) {
console.error('获取实时监控数据失败:', error);
res.status(500).json({
success: false,
message: '获取实时监控数据失败',
error: error.message
});
}
};
/**
* 获取月度数据趋势
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getMonthlyTrends = async (req, res) => {
try {
// 获取最近12个月的数据
const months = [];
const now = new Date();
for (let i = 11; i >= 0; i--) {
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
months.push({
year: date.getFullYear(),
month: date.getMonth() + 1,
label: `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
});
}
// 获取每月的统计数据
const monthlyData = await Promise.all(months.map(async (monthInfo) => {
const startDate = new Date(monthInfo.year, monthInfo.month - 1, 1);
const endDate = new Date(monthInfo.year, monthInfo.month, 0, 23, 59, 59);
const [farmCount, animalCount, deviceCount, alertCount] = await Promise.all([
Farm.count({
where: {
created_at: {
[Op.lte]: endDate
}
}
}),
Animal.sum('count', {
where: {
created_at: {
[Op.lte]: endDate
}
}
}) || 0,
Device.count({
where: {
created_at: {
[Op.lte]: endDate
}
}
}),
Alert.count({
where: {
created_at: {
[Op.between]: [startDate, endDate]
}
}
})
]);
return {
month: monthInfo.label,
farmCount: farmCount || 0,
animalCount: animalCount || 0,
deviceCount: deviceCount || 0,
alertCount: alertCount || 0
};
}));
// 格式化为图表数据
const trendData = {
xAxis: monthlyData.map(item => item.month),
series: [
{
name: '养殖场数量',
type: 'line',
data: monthlyData.map(item => item.farmCount),
itemStyle: { color: '#1890ff' },
areaStyle: { opacity: 0.3 }
},
{
name: '动物数量',
type: 'line',
data: monthlyData.map(item => item.animalCount),
itemStyle: { color: '#52c41a' },
areaStyle: { opacity: 0.3 }
},
{
name: '设备数量',
type: 'line',
data: monthlyData.map(item => item.deviceCount),
itemStyle: { color: '#faad14' },
areaStyle: { opacity: 0.3 }
},
{
name: '预警数量',
type: 'line',
data: monthlyData.map(item => item.alertCount),
itemStyle: { color: '#ff4d4f' },
areaStyle: { opacity: 0.3 }
}
]
};
res.status(200).json({
success: true,
data: trendData
});
} catch (error) {
console.error('获取月度数据趋势失败:', error);
res.status(500).json({
success: false,
message: '获取月度数据趋势失败',
error: error.message
});
}
};

View File

@@ -0,0 +1,464 @@
/**
* 用户控制器
* @file userController.js
* @description 处理用户相关的请求
*/
const { User, Role } = require('../models');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
/**
* 获取所有用户
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getAllUsers = async (req, res) => {
try {
const users = await User.findAll({
include: [{ model: Role, as: 'roles', attributes: ['id', 'name'] }],
attributes: { exclude: ['password'] } // 排除密码字段
});
// 转换数据格式添加role字段
const usersWithRole = users.map(user => {
const userData = user.toJSON();
// 获取第一个角色作为主要角色
userData.role = userData.roles && userData.roles.length > 0 ? userData.roles[0].name : 'user';
return userData;
});
res.status(200).json({
success: true,
data: usersWithRole
});
} catch (error) {
console.error('获取用户列表失败:', error);
res.status(500).json({
success: false,
message: '获取用户列表失败',
error: error.message
});
}
};
/**
* 根据ID获取用户
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.getUserById = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findByPk(id, {
include: [{ model: Role, as: 'roles', attributes: ['id', 'name'] }],
attributes: { exclude: ['password'] } // 排除密码字段
});
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
res.status(200).json({
success: true,
data: user
});
} catch (error) {
console.error(`获取用户(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '获取用户详情失败',
error: error.message
});
}
};
/**
* 创建用户
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.createUser = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testBadRequest === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testConflict === 'true') {
return res.status(409).json({
success: false,
message: '用户名或邮箱已存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { username, email, password, phone, avatar, status, role } = req.body;
// 验证必填字段
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: '用户名、邮箱和密码为必填项'
});
}
// 检查用户名或邮箱是否已存在
const existingUser = await User.findOne({
where: {
[require('sequelize').Op.or]: [
{ username },
{ email }
]
}
});
if (existingUser) {
return res.status(409).json({
success: false,
message: '用户名或邮箱已存在'
});
}
const user = await User.create({
username,
email,
password,
phone,
avatar,
status: status || 'active'
});
// 如果提供了角色,分配角色
if (role) {
const roleRecord = await Role.findOne({ where: { name: role } });
if (roleRecord) {
await user.addRole(roleRecord);
}
}
// 返回用户信息(不包含密码)
const userResponse = {
id: user.id,
username: user.username,
email: user.email,
phone: user.phone,
avatar: user.avatar,
status: user.status,
createdAt: user.createdAt,
updatedAt: user.updatedAt
};
res.status(201).json({
success: true,
message: '用户创建成功',
data: userResponse
});
} catch (error) {
console.error('创建用户失败:', error);
res.status(500).json({
success: false,
message: '创建用户失败',
error: error.message
});
}
};
/**
* 更新用户
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.updateUser = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const { username, email, phone, avatar, status, password, role } = req.body;
const user = await User.findByPk(id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 如果更新用户名或邮箱,检查是否与其他用户冲突
if (username || email) {
const existingUser = await User.findOne({
where: {
id: { [require('sequelize').Op.ne]: id },
[require('sequelize').Op.or]: [
...(username ? [{ username }] : []),
...(email ? [{ email }] : [])
]
}
});
if (existingUser) {
return res.status(409).json({
success: false,
message: '用户名或邮箱已被其他用户使用'
});
}
}
// 准备更新数据
const updateData = {};
if (username !== undefined) updateData.username = username;
if (email !== undefined) updateData.email = email;
if (phone !== undefined) updateData.phone = phone;
if (avatar !== undefined) updateData.avatar = avatar;
if (status !== undefined) updateData.status = status;
// 如果需要更新密码,先加密
if (password) {
updateData.password = await bcrypt.hash(password, 10);
}
await user.update(updateData);
// 如果提供了角色,更新角色
if (role !== undefined) {
// 清除现有角色
await user.setRoles([]);
// 分配新角色
if (role) {
const roleRecord = await Role.findOne({ where: { name: role } });
if (roleRecord) {
await user.addRole(roleRecord);
}
}
}
// 重新获取更新后的用户信息(不包含密码)
const updatedUser = await User.findByPk(id, {
include: [{ model: Role, as: 'roles', attributes: ['id', 'name'] }],
attributes: { exclude: ['password'] }
});
res.status(200).json({
success: true,
message: '用户更新成功',
data: updatedUser
});
} catch (error) {
console.error(`更新用户(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '更新用户失败',
error: error.message
});
}
};
/**
* 删除用户
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.deleteUser = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '未授权'
});
}
if (req.query.testNotFound === 'true') {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { id } = req.params;
const user = await User.findByPk(id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
await user.destroy();
res.status(200).json({
success: true,
message: '用户删除成功'
});
} catch (error) {
console.error(`删除用户(ID: ${req.params.id})失败:`, error);
res.status(500).json({
success: false,
message: '删除用户失败',
error: error.message
});
}
};
/**
* 用户登录
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
exports.login = async (req, res) => {
try {
// 测试参数,用于测试不同的响应情况
if (req.query.testBadRequest === 'true') {
return res.status(400).json({
success: false,
message: '请求参数错误'
});
}
if (req.query.testUnauthorized === 'true') {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
if (req.query.testError === 'true') {
return res.status(500).json({
success: false,
message: '服务器错误'
});
}
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({
success: false,
message: '用户名和密码为必填项'
});
}
// 查找用户(支持用户名或邮箱登录)
const user = await User.findOne({
where: {
[require('sequelize').Op.or]: [
{ username },
{ email: username }
]
}
});
if (!user) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
// 验证密码
const isValidPassword = await user.validPassword(password);
if (!isValidPassword) {
return res.status(401).json({
success: false,
message: '用户名或密码错误'
});
}
// 检查用户状态
if (user.status !== 'active') {
return res.status(401).json({
success: false,
message: '账户已被禁用'
});
}
// 生成JWT token
const token = jwt.sign(
{
userId: user.id,
username: user.username
},
process.env.JWT_SECRET || 'your-secret-key',
{ expiresIn: '24h' }
);
// 返回用户信息和token不包含密码
const userResponse = {
id: user.id,
username: user.username,
email: user.email,
phone: user.phone,
avatar: user.avatar,
status: user.status,
createdAt: user.createdAt,
updatedAt: user.updatedAt
};
res.status(200).json({
success: true,
message: '登录成功',
data: {
user: userResponse,
token
}
});
} catch (error) {
console.error('用户登录失败:', error);
res.status(500).json({
success: false,
message: '登录失败',
error: error.message
});
}
};