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

626
backend/routes/alerts.js Normal file
View File

@@ -0,0 +1,626 @@
/**
* 预警路由
* @file alerts.js
* @description 定义预警相关的API路由
*/
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const alertController = require('../controllers/alertController');
const { verifyToken } = require('../middleware/auth');
// 公开API路由不需要验证token
const publicRoutes = express.Router();
router.use('/public', publicRoutes);
// 公开获取所有预警数据
publicRoutes.get('/', alertController.getAllAlerts);
// 公开获取单个预警数据
publicRoutes.get('/:id', alertController.getAlertById);
// 公开获取预警统计信息
publicRoutes.get('/stats/type', alertController.getAlertStatsByType);
publicRoutes.get('/stats/level', alertController.getAlertStatsByLevel);
publicRoutes.get('/stats/status', alertController.getAlertStatsByStatus);
// 公开更新预警状态
publicRoutes.put('/:id/status', alertController.updateAlert);
/**
* @swagger
* tags:
* name: Alerts
* description: 预警管理API
*/
/**
* @swagger
* /api/alerts:
* get:
* summary: 获取所有预警
* tags: [Alerts]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取预警列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* $ref: '#/components/schemas/Alert'
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
alertController.getAllAlerts(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockAlerts = [
{
id: 0,
type: "string",
level: "low",
message: "string",
status: "active",
farmId: 0,
deviceId: 0,
resolved_at: "2025-08-20T01:09:30.453Z",
resolved_by: 0,
resolution_notes: "string",
createdAt: "2025-08-20T01:09:30.453Z",
updatedAt: "2025-08-20T01:09:30.453Z"
}
];
res.status(200).json({
success: true,
data: mockAlerts
});
}
});
/**
* @swagger
* /api/alerts/{id}:
* get:
* summary: 获取单个预警
* tags: [Alerts]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 预警ID
* responses:
* 200:
* description: 成功获取预警详情
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* $ref: '#/components/schemas/Alert'
* 401:
* description: 未授权
* 404:
* description: 预警不存在
* 500:
* description: 服务器错误
*/
router.get('/:id', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
alertController.getAlertById(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockAlert = {
id: parseInt(req.params.id),
type: "temperature",
level: "medium",
message: "温度异常警告",
status: "active",
farmId: 1,
deviceId: 1,
resolved_at: null,
resolved_by: null,
resolution_notes: null,
createdAt: "2025-08-20T01:09:30.453Z",
updatedAt: "2025-08-20T01:09:30.453Z"
};
res.status(200).json({
success: true,
data: mockAlert
});
}
});
/**
* @swagger
* /api/alerts:
* post:
* summary: 创建预警
* tags: [Alerts]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - type
* - message
* - farmId
* properties:
* type:
* type: string
* description: 预警类型
* level:
* type: string
* enum: [low, medium, high, critical]
* description: 预警级别
* message:
* type: string
* description: 预警消息
* status:
* type: string
* enum: [active, acknowledged, resolved]
* description: 预警状态
* farmId:
* type: integer
* description: 所属养殖场ID
* deviceId:
* type: integer
* description: 关联设备ID
* responses:
* 201:
* description: 预警创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 预警创建成功
* data:
* $ref: '#/components/schemas/Alert'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 养殖场或设备不存在
* 500:
* description: 服务器错误
*/
router.post('/', verifyToken, alertController.createAlert);
/**
* @swagger
* /api/alerts/{id}:
* put:
* summary: 更新预警
* tags: [Alerts]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 预警ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* type:
* type: string
* description: 预警类型
* level:
* type: string
* enum: [low, medium, high, critical]
* description: 预警级别
* message:
* type: string
* description: 预警消息
* status:
* type: string
* enum: [active, acknowledged, resolved]
* description: 预警状态
* farmId:
* type: integer
* description: 所属养殖场ID
* deviceId:
* type: integer
* description: 关联设备ID
* resolved_at:
* type: string
* format: date-time
* description: 解决时间
* resolved_by:
* type: integer
* description: 解决人ID
* resolution_notes:
* type: string
* description: 解决备注
* responses:
* 200:
* description: 预警更新成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 预警更新成功
* data:
* $ref: '#/components/schemas/Alert'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 预警不存在或养殖场/设备不存在
* 500:
* description: 服务器错误
*/
router.put('/:id', verifyToken, alertController.updateAlert);
/**
* @swagger
* /api/alerts/{id}:
* delete:
* summary: 删除预警
* tags: [Alerts]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 预警ID
* responses:
* 200:
* description: 预警删除成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 预警删除成功
* 401:
* description: 未授权
* 404:
* description: 预警不存在
* 500:
* description: 服务器错误
*/
router.delete('/:id', verifyToken, alertController.deleteAlert);
/**
* @swagger
* /api/alerts/stats/type:
* get:
* summary: 按类型统计预警数量
* tags: [Alerts]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取预警类型统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 温度异常
* count:
* type: integer
* example: 12
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/type', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
alertController.getAlertStatsByType(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockStats = [
{ type: 'temperature', count: 12 },
{ type: 'humidity', count: 8 },
{ type: 'system', count: 5 },
{ type: 'power', count: 3 }
];
res.status(200).json({
success: true,
data: mockStats
});
}
});
/**
* @swagger
* /api/alerts/stats/level:
* get:
* summary: 按级别统计预警数量
* tags: [Alerts]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取预警级别统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* level:
* type: string
* example: high
* count:
* type: integer
* example: 8
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/level', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
alertController.getAlertStatsByLevel(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockStats = [
{ level: 'high', count: 7 },
{ level: 'medium', count: 15 },
{ level: 'low', count: 6 }
];
res.status(200).json({
success: true,
data: mockStats
});
}
});
/**
* @swagger
* /api/alerts/stats/status:
* get:
* summary: 按状态统计预警数量
* tags: [Alerts]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取预警状态统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* status:
* type: string
* example: active
* count:
* type: integer
* example: 15
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/status', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
alertController.getAlertStatsByStatus(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockStats = [
{ status: 'active', count: 18 },
{ status: 'resolved', count: 10 }
];
res.status(200).json({
success: true,
data: mockStats
});
}
});
module.exports = router;

464
backend/routes/animals.js Normal file
View File

@@ -0,0 +1,464 @@
/**
* 动物路由
* @file animals.js
* @description 定义动物相关的API路由
*/
const express = require('express');
const router = express.Router();
const animalController = require('../controllers/animalController');
const { verifyToken } = require('../middleware/auth');
const jwt = require('jsonwebtoken');
// 公开API路由不需要验证token
const publicRoutes = express.Router();
router.use('/public', publicRoutes);
// 公开获取所有动物数据
publicRoutes.get('/', async (req, res) => {
try {
// 尝试从数据库获取数据
const { Animal, Farm } = require('../models');
const animals = await Animal.findAll({
include: [{ model: Farm, as: 'farm', attributes: ['id', 'name'] }]
});
res.status(200).json({
success: true,
data: animals,
source: 'database'
});
} catch (error) {
console.error('从数据库获取动物列表失败,使用模拟数据:', error.message);
// 数据库不可用时返回模拟数据
const mockAnimals = [
{ id: 1, name: '牛001', type: '肉牛', breed: '西门塔尔牛', age: 2, weight: 450, status: 'healthy', farmId: 1, farm: { id: 1, name: '宁夏农场1' } },
{ id: 2, name: '牛002', type: '肉牛', breed: '安格斯牛', age: 3, weight: 500, status: 'healthy', farmId: 1, farm: { id: 1, name: '宁夏农场1' } },
{ id: 3, name: '羊001', type: '肉羊', breed: '小尾寒羊', age: 1, weight: 70, status: 'sick', farmId: 2, farm: { id: 2, name: '宁夏农场2' } }
];
res.status(200).json({
success: true,
data: mockAnimals,
source: 'mock',
message: '数据库不可用,使用模拟数据'
});
}
});
/**
* @swagger
* tags:
* name: Animals
* description: 动物管理API
*/
/**
* @swagger
* /api/animals:
* get:
* summary: 获取所有动物
* tags: [Animals]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取动物列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* $ref: '#/components/schemas/Animal'
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
animalController.getAllAnimals(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockAnimals = [
{ id: 1, name: '牛001', type: '肉牛', breed: '西门塔尔牛', age: 2, weight: 450, status: 'healthy', farmId: 1, farm: { id: 1, name: '示例养殖场1' } },
{ id: 2, name: '牛002', type: '肉牛', breed: '安格斯牛', age: 3, weight: 500, status: 'healthy', farmId: 1, farm: { id: 1, name: '示例养殖场1' } },
{ id: 3, name: '羊001', type: '肉羊', breed: '小尾寒羊', age: 1, weight: 70, status: 'sick', farmId: 2, farm: { id: 2, name: '示例养殖场2' } }
];
res.status(200).json({
success: true,
data: mockAnimals
});
}
});
/**
* @swagger
* /api/animals/{id}:
* get:
* summary: 获取单个动物
* tags: [Animals]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 动物ID
* responses:
* 200:
* description: 成功获取动物详情
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* $ref: '#/components/schemas/Animal'
* 401:
* description: 未授权
* 404:
* description: 动物不存在
* 500:
* description: 服务器错误
*/
router.get('/:id', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
animalController.getAnimalById(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const animalId = parseInt(req.params.id);
const mockAnimal = {
id: animalId,
name: `动物${animalId}`,
type: animalId % 2 === 0 ? '肉牛' : '肉羊',
breed: animalId % 2 === 0 ? '西门塔尔牛' : '小尾寒羊',
age: Math.floor(Math.random() * 5) + 1,
weight: animalId % 2 === 0 ? Math.floor(Math.random() * 200) + 400 : Math.floor(Math.random() * 50) + 50,
status: Math.random() > 0.7 ? 'sick' : 'healthy',
farmId: Math.ceil(animalId / 3),
farm: { id: Math.ceil(animalId / 3), name: `示例养殖场${Math.ceil(animalId / 3)}` }
};
res.status(200).json({
success: true,
data: mockAnimal
});
}
});
/**
* @swagger
* /api/animals:
* post:
* summary: 创建动物
* tags: [Animals]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - type
* - count
* - farmId
* properties:
* type:
* type: string
* description: 动物类型
* count:
* type: integer
* description: 数量
* farmId:
* type: integer
* description: 所属养殖场ID
* health_status:
* type: string
* enum: [healthy, sick, quarantine]
* description: 健康状态
* last_inspection:
* type: string
* format: date-time
* description: 最近检查时间
* notes:
* type: string
* description: 备注
* responses:
* 201:
* description: 动物创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 动物创建成功
* data:
* $ref: '#/components/schemas/Animal'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 养殖场不存在
* 500:
* description: 服务器错误
*/
router.post('/', verifyToken, animalController.createAnimal);
/**
* @swagger
* /api/animals/{id}:
* put:
* summary: 更新动物
* tags: [Animals]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 动物ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* type:
* type: string
* description: 动物类型
* count:
* type: integer
* description: 数量
* farmId:
* type: integer
* description: 所属养殖场ID
* health_status:
* type: string
* enum: [healthy, sick, quarantine]
* description: 健康状态
* last_inspection:
* type: string
* format: date-time
* description: 最近检查时间
* notes:
* type: string
* description: 备注
* responses:
* 200:
* description: 动物更新成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 动物更新成功
* data:
* $ref: '#/components/schemas/Animal'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 动物不存在或养殖场不存在
* 500:
* description: 服务器错误
*/
router.put('/:id', verifyToken, animalController.updateAnimal);
/**
* @swagger
* /api/animals/{id}:
* delete:
* summary: 删除动物
* tags: [Animals]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 动物ID
* responses:
* 200:
* description: 动物删除成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 动物删除成功
* 401:
* description: 未授权
* 404:
* description: 动物不存在
* 500:
* description: 服务器错误
*/
router.delete('/:id', verifyToken, animalController.deleteAnimal);
/**
* @swagger
* /api/animals/stats/type:
* get:
* summary: 按类型统计动物数量
* tags: [Animals]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取动物类型统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 牛
* total:
* type: integer
* example: 5000
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/type', (req, res) => {
// 从请求头获取token
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
success: false,
message: '访问令牌缺失'
});
}
try {
// 验证token
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your_jwt_secret_key');
// 将用户信息添加到请求对象中
req.user = decoded;
// 调用控制器方法获取数据
animalController.getAnimalStatsByType(req, res);
} catch (error) {
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: '访问令牌无效'
});
}
// 返回模拟数据
const mockStats = [
{ type: '肉牛', total: 5280 },
{ type: '奶牛', total: 2150 },
{ type: '肉羊', total: 8760 },
{ type: '奶羊', total: 1430 },
{ type: '猪', total: 12500 }
];
res.status(200).json({
success: true,
data: mockStats
});
}
});
module.exports = router;

1174
backend/routes/auth.js Normal file

File diff suppressed because it is too large Load Diff

366
backend/routes/devices.js Normal file
View File

@@ -0,0 +1,366 @@
/**
* 设备路由
* @file devices.js
* @description 定义设备相关的API路由
*/
const express = require('express');
const router = express.Router();
const deviceController = require('../controllers/deviceController');
const { verifyToken } = require('../middleware/auth');
// 公开API路由不需要验证token
const publicRoutes = express.Router();
router.use('/public', publicRoutes);
// 公开创建设备接口
publicRoutes.post('/', deviceController.createDevice);
// 公开获取单个设备接口
publicRoutes.get('/:id', deviceController.getDeviceById);
// 公开更新设备接口
publicRoutes.put('/:id', deviceController.updateDevice);
// 公开删除设备接口
publicRoutes.delete('/:id', deviceController.deleteDevice);
// 公开获取设备状态统计接口
publicRoutes.get('/stats/status', deviceController.getDeviceStatsByStatus);
// 公开获取设备类型统计接口
publicRoutes.get('/stats/type', deviceController.getDeviceStatsByType);
// 公开获取所有设备数据
publicRoutes.get('/', deviceController.getAllDevices);
/**
* @swagger
* tags:
* name: Devices
* description: 设备管理API
*/
/**
* @swagger
* /api/devices:
* get:
* summary: 获取所有设备
* tags: [Devices]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取设备列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* $ref: '#/components/schemas/Device'
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/', verifyToken, deviceController.getAllDevices);
/**
* @swagger
* /api/devices/{id}:
* get:
* summary: 获取单个设备
* tags: [Devices]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 设备ID
* responses:
* 200:
* description: 成功获取设备详情
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* $ref: '#/components/schemas/Device'
* 401:
* description: 未授权
* 404:
* description: 设备不存在
* 500:
* description: 服务器错误
*/
router.get('/:id', verifyToken, deviceController.getDeviceById);
/**
* @swagger
* /api/devices:
* post:
* summary: 创建设备
* tags: [Devices]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - type
* - farmId
* properties:
* name:
* type: string
* description: 设备名称
* type:
* type: string
* description: 设备类型
* status:
* type: string
* enum: [online, offline, maintenance]
* description: 设备状态
* farmId:
* type: integer
* description: 所属养殖场ID
* last_maintenance:
* type: string
* format: date-time
* description: 最近维护时间
* installation_date:
* type: string
* format: date-time
* description: 安装日期
* metrics:
* type: object
* description: 设备指标数据
* responses:
* 201:
* description: 设备创建成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 设备创建成功
* data:
* $ref: '#/components/schemas/Device'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 养殖场不存在
* 500:
* description: 服务器错误
*/
router.post('/', verifyToken, deviceController.createDevice);
/**
* @swagger
* /api/devices/{id}:
* put:
* summary: 更新设备
* tags: [Devices]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 设备ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description: 设备名称
* type:
* type: string
* description: 设备类型
* status:
* type: string
* enum: [online, offline, maintenance]
* description: 设备状态
* farmId:
* type: integer
* description: 所属养殖场ID
* last_maintenance:
* type: string
* format: date-time
* description: 最近维护时间
* installation_date:
* type: string
* format: date-time
* description: 安装日期
* metrics:
* type: object
* description: 设备指标数据
* responses:
* 200:
* description: 设备更新成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 设备更新成功
* data:
* $ref: '#/components/schemas/Device'
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 设备不存在或养殖场不存在
* 500:
* description: 服务器错误
*/
router.put('/:id', verifyToken, deviceController.updateDevice);
/**
* @swagger
* /api/devices/{id}:
* delete:
* summary: 删除设备
* tags: [Devices]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 设备ID
* responses:
* 200:
* description: 设备删除成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: 设备删除成功
* 401:
* description: 未授权
* 404:
* description: 设备不存在
* 500:
* description: 服务器错误
*/
router.delete('/:id', verifyToken, deviceController.deleteDevice);
/**
* @swagger
* /api/devices/stats/status:
* get:
* summary: 按状态统计设备数量
* tags: [Devices]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取设备状态统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* status:
* type: string
* example: online
* count:
* type: integer
* example: 25
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/status', verifyToken, deviceController.getDeviceStatsByStatus);
/**
* @swagger
* /api/devices/stats/type:
* get:
* summary: 按类型统计设备数量
* tags: [Devices]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取设备类型统计
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 温度传感器
* count:
* type: integer
* example: 15
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/stats/type', verifyToken, deviceController.getDeviceStatsByType);
module.exports = router;

163
backend/routes/farms.js Normal file
View File

@@ -0,0 +1,163 @@
const express = require('express');
const router = express.Router();
const farmController = require('../controllers/farmController');
/**
* @swagger
* /api/farms:
* get:
* summary: 获取所有养殖场
* tags: [Farms]
* responses:
* 200:
* description: 成功获取养殖场列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: array
* items:
* $ref: '#/components/schemas/Farm'
*/
router.get('/', farmController.getAllFarms);
// 公共路由必须在参数路由之前定义
router.get('/public', farmController.getAllFarms);
/**
* @swagger
* /api/farms/{id}:
* get:
* summary: 根据ID获取养殖场
* tags: [Farms]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 养殖场ID
* responses:
* 200:
* description: 成功获取养殖场详情
* 404:
* description: 养殖场不存在
*/
router.get('/:id', farmController.getFarmById);
/**
* @swagger
* /api/farms:
* post:
* summary: 创建新养殖场
* tags: [Farms]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/FarmInput'
* responses:
* 201:
* description: 养殖场创建成功
* 400:
* description: 请求参数错误
*/
router.post('/', farmController.createFarm);
/**
* @swagger
* /api/farms/{id}:
* put:
* summary: 更新养殖场信息
* tags: [Farms]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 养殖场ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/FarmInput'
* responses:
* 200:
* description: 养殖场更新成功
* 404:
* description: 养殖场不存在
*/
router.put('/:id', farmController.updateFarm);
/**
* @swagger
* /api/farms/{id}:
* delete:
* summary: 删除养殖场
* tags: [Farms]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 养殖场ID
* responses:
* 200:
* description: 养殖场删除成功
* 404:
* description: 养殖场不存在
*/
router.delete('/:id', farmController.deleteFarm);
/**
* @swagger
* /api/farms/{id}/animals:
* get:
* summary: 获取养殖场的动物列表
* tags: [Farms]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 养殖场ID
* responses:
* 200:
* description: 成功获取动物列表
* 404:
* description: 养殖场不存在
*/
router.get('/:id/animals', farmController.getFarmAnimals);
/**
* @swagger
* /api/farms/{id}/devices:
* get:
* summary: 获取养殖场的设备列表
* tags: [Farms]
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* description: 养殖场ID
* responses:
* 200:
* description: 成功获取设备列表
* 404:
* description: 养殖场不存在
*/
router.get('/:id/devices', farmController.getFarmDevices);
// 公共农场数据接口(保留兼容性)
module.exports = router;

333
backend/routes/map.js Normal file
View File

@@ -0,0 +1,333 @@
const express = require('express');
const router = express.Router();
const mapController = require('../controllers/mapController');
const farmController = require('../controllers/farmController');
const { verifyToken } = require('../middleware/auth');
// 公开API路由不需要验证token
const publicRoutes = express.Router();
router.use('/public', publicRoutes);
// 公开地理编码接口
publicRoutes.get('/geocode', mapController.geocode);
// 公开反向地理编码接口
publicRoutes.get('/reverse-geocode', mapController.reverseGeocode);
// 公开路线规划接口
publicRoutes.get('/direction', mapController.direction);
// 公开周边搜索接口
publicRoutes.get('/place-search', mapController.placeSearch);
// 公开静态地图接口
publicRoutes.get('/static-map', mapController.staticMap);
// 公开IP定位接口
publicRoutes.get('/ip-location', mapController.ipLocation);
// 公开获取养殖场地理位置数据
publicRoutes.get('/farms', farmController.getAllFarms);
/**
* @swagger
* tags:
* name: Map
* description: 百度地图API服务
*/
/**
* @swagger
* /api/map/geocode:
* get:
* summary: 地理编码 - 将地址转换为经纬度坐标
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: address
* schema:
* type: string
* required: true
* description: 地址
* responses:
* 200:
* description: 地理编码成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* result:
* type: object
* properties:
* location:
* type: object
* properties:
* lng:
* type: number
* description: 经度
* lat:
* type: number
* description: 纬度
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/geocode', verifyToken, mapController.geocode);
/**
* @swagger
* /api/map/reverse-geocode:
* get:
* summary: 逆地理编码 - 将经纬度坐标转换为地址
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: lat
* schema:
* type: number
* required: true
* description: 纬度
* - in: query
* name: lng
* schema:
* type: number
* required: true
* description: 经度
* responses:
* 200:
* description: 逆地理编码成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* result:
* type: object
* properties:
* formatted_address:
* type: string
* description: 结构化地址
* addressComponent:
* type: object
* description: 地址组成部分
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/reverse-geocode', verifyToken, mapController.reverseGeocode);
/**
* @swagger
* /api/map/direction:
* get:
* summary: 路线规划
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: origin
* schema:
* type: string
* required: true
* description: 起点坐标,格式:纬度,经度
* - in: query
* name: destination
* schema:
* type: string
* required: true
* description: 终点坐标,格式:纬度,经度
* - in: query
* name: mode
* schema:
* type: string
* enum: [driving, walking, riding, transit]
* required: false
* description: 交通方式driving(驾车)、walking(步行)、riding(骑行)、transit(公交)
* responses:
* 200:
* description: 路线规划成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* result:
* type: object
* description: 路线规划结果
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/direction', verifyToken, mapController.direction);
/**
* @swagger
* /api/map/place-search:
* get:
* summary: 周边搜索
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: query
* schema:
* type: string
* required: true
* description: 搜索关键词
* - in: query
* name: location
* schema:
* type: string
* required: true
* description: 中心点坐标,格式:纬度,经度
* - in: query
* name: radius
* schema:
* type: number
* required: false
* description: 搜索半径单位默认1000米
* responses:
* 200:
* description: 周边搜索成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* results:
* type: array
* items:
* type: object
* description: 搜索结果
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/place-search', verifyToken, mapController.placeSearch);
/**
* @swagger
* /api/map/static-map:
* get:
* summary: 获取静态地图
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: center
* schema:
* type: string
* required: true
* description: 地图中心点坐标,格式:纬度,经度
* - in: query
* name: width
* schema:
* type: number
* required: false
* description: 地图图片宽度默认400
* - in: query
* name: height
* schema:
* type: number
* required: false
* description: 地图图片高度默认300
* - in: query
* name: zoom
* schema:
* type: number
* required: false
* description: 地图缩放级别默认12
* responses:
* 200:
* description: 获取静态地图成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* url:
* type: string
* description: 静态地图URL
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/static-map', verifyToken, mapController.staticMap);
/**
* @swagger
* /api/map/ip-location:
* get:
* summary: IP定位
* tags: [Map]
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: ip
* schema:
* type: string
* required: false
* description: IP地址可选默认使用用户当前IP
* responses:
* 200:
* description: IP定位成功
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* result:
* type: object
* description: IP定位结果
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/ip-location', verifyToken, mapController.ipLocation);
module.exports = router;

118
backend/routes/orders.js Normal file
View File

@@ -0,0 +1,118 @@
const express = require('express');
const router = express.Router();
const orderController = require('../controllers/orderController');
/**
* @swagger
* tags:
* name: Orders
* description: 订单管理
*/
/**
* @swagger
* components:
* schemas:
* Order:
* type: object
* required:
* - id
* - userId
* - totalAmount
* - status
* properties:
* id:
* type: integer
* description: 订单ID
* userId:
* type: integer
* description: 用户ID
* totalAmount:
* type: number
* format: float
* description: 订单总金额
* status:
* type: string
* description: 订单状态
* enum: [pending, paid, shipped, delivered, cancelled]
* example:
* id: 1
* userId: 2
* totalAmount: 199.98
* status: "paid"
*/
/**
* @swagger
* /api/orders:
* get:
* summary: 获取所有订单
* tags: [Orders]
* responses:
* 200:
* description: 订单列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: array
* items:
* $ref: '#/components/schemas/Order'
* 500:
* description: 服务器错误
*/
// 获取所有订单
router.get('/', orderController.getAllOrders);
/**
* @swagger
* /api/orders/{id}:
* get:
* summary: 根据ID获取订单
* tags: [Orders]
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 订单ID
* responses:
* 200:
* description: 订单信息
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* $ref: '#/components/schemas/Order'
* 404:
* description: 订单未找到
* 500:
* description: 服务器错误
*/
// 根据ID获取订单
router.get('/:id', orderController.getOrderById);
// 创建订单
router.post('/', orderController.createOrder);
// 更新订单
router.put('/:id', orderController.updateOrder);
// 删除订单
router.delete('/:id', orderController.deleteOrder);
// 获取用户的订单列表
router.get('/user/:userId', orderController.getOrdersByUserId);
module.exports = router;

View File

@@ -0,0 +1,192 @@
/**
* 性能监控路由
* @file performance-routes.js
* @description 提供性能监控数据的API路由
*/
const express = require('express');
const router = express.Router();
const { performanceMonitor } = require('../utils/performance-monitor');
const { apiPerformanceMonitor, apiErrorMonitor } = require('../middleware/performance-middleware');
const logger = require('../utils/logger');
// 应用性能监控中间件到所有路由
router.use(apiPerformanceMonitor);
/**
* @api {get} /api/performance/metrics 获取所有性能指标
* @apiName GetAllMetrics
* @apiGroup Performance
* @apiDescription 获取系统、数据库和API的所有性能指标
* @apiSuccess {Object} metrics 所有性能指标数据
*/
router.get('/metrics', async (req, res) => {
try {
const metrics = await performanceMonitor.getAllMetrics();
res.json(metrics);
} catch (error) {
logger.error('获取性能指标失败:', error);
res.status(500).json({ error: '获取性能指标失败', message: error.message });
}
});
/**
* @api {get} /api/performance/system 获取系统资源指标
* @apiName GetSystemMetrics
* @apiGroup Performance
* @apiDescription 获取CPU、内存和磁盘使用情况
* @apiSuccess {Object} metrics 系统资源指标数据
*/
router.get('/system', (req, res) => {
try {
const metrics = performanceMonitor.getSystemMetrics();
res.json(metrics);
} catch (error) {
logger.error('获取系统资源指标失败:', error);
res.status(500).json({ error: '获取系统资源指标失败', message: error.message });
}
});
/**
* @api {get} /api/performance/database 获取数据库性能指标
* @apiName GetDatabaseMetrics
* @apiGroup Performance
* @apiDescription 获取数据库连接池状态、慢查询和查询模式统计
* @apiSuccess {Object} metrics 数据库性能指标数据
*/
router.get('/database', async (req, res) => {
try {
const metrics = await performanceMonitor.getDatabaseMetrics();
res.json(metrics);
} catch (error) {
logger.error('获取数据库性能指标失败:', error);
res.status(500).json({ error: '获取数据库性能指标失败', message: error.message });
}
});
/**
* @api {get} /api/performance/api 获取API性能指标
* @apiName GetApiMetrics
* @apiGroup Performance
* @apiDescription 获取API请求统计、响应时间和错误率
* @apiSuccess {Object} metrics API性能指标数据
*/
router.get('/api', (req, res) => {
try {
const metrics = performanceMonitor.getApiStats();
res.json(metrics);
} catch (error) {
logger.error('获取API性能指标失败:', error);
res.status(500).json({ error: '获取API性能指标失败', message: error.message });
}
});
/**
* @api {post} /api/performance/start 启动性能监控
* @apiName StartMonitoring
* @apiGroup Performance
* @apiDescription 启动系统性能监控
* @apiParam {Number} [interval] 监控间隔(毫秒)
* @apiSuccess {Object} result 操作结果
*/
router.post('/start', (req, res) => {
try {
const interval = req.body.interval;
const result = performanceMonitor.startMonitoring(interval);
res.json(result);
} catch (error) {
logger.error('启动性能监控失败:', error);
res.status(500).json({ error: '启动性能监控失败', message: error.message });
}
});
/**
* @api {post} /api/performance/stop 停止性能监控
* @apiName StopMonitoring
* @apiGroup Performance
* @apiDescription 停止系统性能监控
* @apiSuccess {Object} result 操作结果
*/
router.post('/stop', (req, res) => {
try {
const result = performanceMonitor.stopMonitoring();
res.json(result);
} catch (error) {
logger.error('停止性能监控失败:', error);
res.status(500).json({ error: '停止性能监控失败', message: error.message });
}
});
/**
* @api {get} /api/performance/status 获取监控状态
* @apiName GetMonitoringStatus
* @apiGroup Performance
* @apiDescription 获取当前性能监控的状态
* @apiSuccess {Object} status 监控状态
*/
router.get('/status', (req, res) => {
try {
const status = performanceMonitor.getMonitoringStatus();
res.json(status);
} catch (error) {
logger.error('获取监控状态失败:', error);
res.status(500).json({ error: '获取监控状态失败', message: error.message });
}
});
/**
* @api {post} /api/performance/thresholds 设置警报阈值
* @apiName SetAlertThresholds
* @apiGroup Performance
* @apiDescription 设置性能监控的警报阈值
* @apiParam {Object} thresholds 警报阈值配置
* @apiSuccess {Object} result 操作结果
*/
router.post('/thresholds', (req, res) => {
try {
const thresholds = req.body;
const result = performanceMonitor.setAlertThresholds(thresholds);
res.json(result);
} catch (error) {
logger.error('设置警报阈值失败:', error);
res.status(500).json({ error: '设置警报阈值失败', message: error.message });
}
});
/**
* @api {get} /api/performance/thresholds 获取警报阈值
* @apiName GetAlertThresholds
* @apiGroup Performance
* @apiDescription 获取当前设置的警报阈值
* @apiSuccess {Object} thresholds 警报阈值配置
*/
router.get('/thresholds', (req, res) => {
try {
const thresholds = performanceMonitor.getAlertThresholds();
res.json(thresholds);
} catch (error) {
logger.error('获取警报阈值失败:', error);
res.status(500).json({ error: '获取警报阈值失败', message: error.message });
}
});
/**
* @api {post} /api/performance/api/reset 重置API统计
* @apiName ResetApiStats
* @apiGroup Performance
* @apiDescription 重置API性能统计数据
* @apiSuccess {Object} result 操作结果
*/
router.post('/api/reset', (req, res) => {
try {
const result = performanceMonitor.resetApiStats();
res.json(result);
} catch (error) {
logger.error('重置API统计失败:', error);
res.status(500).json({ error: '重置API统计失败', message: error.message });
}
});
// 应用错误处理中间件
router.use(apiErrorMonitor);
module.exports = router;

230
backend/routes/products.js Normal file
View File

@@ -0,0 +1,230 @@
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');
const { verifyToken } = require('../middleware/auth');
/**
* @swagger
* tags:
* name: Products
* description: 产品管理
*/
/**
* @swagger
* components:
* schemas:
* Product:
* type: object
* required:
* - id
* - name
* - price
* - stock
* properties:
* id:
* type: integer
* description: 产品ID
* name:
* type: string
* description: 产品名称
* description:
* type: string
* description: 产品描述
* price:
* type: number
* format: float
* description: 产品价格
* stock:
* type: integer
* description: 产品库存
* status:
* type: string
* enum: [active, inactive]
* description: 产品状态
* created_at:
* type: string
* format: date-time
* description: 创建时间
* updated_at:
* type: string
* format: date-time
* description: 更新时间
* example:
* id: 1
* name: "示例产品1"
* description: "这是一个示例产品"
* price: 99.99
* stock: 100
* status: "active"
/**
* @swagger
* /api/products:
* get:
* summary: 获取所有产品
* tags: [Products]
* responses:
* 200:
* description: 产品列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: array
* items:
* $ref: '#/components/schemas/Product'
* 500:
* description: 服务器错误
*/
router.get('/', productController.getAllProducts);
/**
* @swagger
* /api/products:
* post:
* summary: 创建新产品
* tags: [Products]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - price
* - stock
* properties:
* name:
* type: string
* description:
* type: string
* price:
* type: number
* stock:
* type: integer
* status:
* type: string
* enum: [active, inactive]
* responses:
* 201:
* description: 产品创建成功
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
*/
router.post('/', verifyToken, productController.createProduct);
/**
* @swagger
* /api/products/{id}:
* get:
* summary: 根据ID获取产品
* tags: [Products]
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 产品ID
* responses:
* 200:
* description: 产品信息
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* $ref: '#/components/schemas/Product'
* 404:
* description: 产品未找到
* 500:
* description: 服务器错误
*/
router.get('/:id', productController.getProductById);
/**
* @swagger
* /api/products/{id}:
* put:
* summary: 更新产品
* tags: [Products]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 产品ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description:
* type: string
* price:
* type: number
* stock:
* type: integer
* status:
* type: string
* enum: [active, inactive]
* responses:
* 200:
* description: 产品更新成功
* 400:
* description: 请求参数错误
* 401:
* description: 未授权
* 404:
* description: 产品未找到
*/
router.put('/:id', verifyToken, productController.updateProduct);
/**
* @swagger
* /api/products/{id}:
* delete:
* summary: 删除产品
* tags: [Products]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 产品ID
* responses:
* 200:
* description: 产品删除成功
* 401:
* description: 未授权
* 404:
* description: 产品未找到
*/
router.delete('/:id', verifyToken, productController.deleteProduct);
module.exports = router;

484
backend/routes/stats.js Normal file
View File

@@ -0,0 +1,484 @@
/**
* 统计数据路由
* @file stats.js
* @description 定义统计数据相关的API路由
*/
const express = require('express');
const router = express.Router();
const statsController = require('../controllers/statsController');
const { verifyToken } = require('../middleware/auth');
// 公开API路由不需要验证token
const publicRoutes = express.Router();
router.use('/public', publicRoutes);
// 公开获取仪表盘统计数据
publicRoutes.get('/dashboard', statsController.getDashboardStats);
// 公开获取监控数据
publicRoutes.get('/monitoring', statsController.getMonitorData);
// 公开获取月度数据趋势
publicRoutes.get('/monthly-trends', statsController.getMonthlyTrends);
/**
* @swagger
* tags:
* name: Statistics
* description: 统计数据API
*/
/**
* @swagger
* /api/stats/dashboard:
* get:
* summary: 获取仪表盘统计数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取仪表盘统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* farmCount:
* type: integer
* example: 12
* animalCount:
* type: integer
* example: 5000
* deviceCount:
* type: integer
* example: 150
* alertCount:
* type: integer
* example: 25
* deviceOnlineRate:
* type: number
* format: float
* example: 0.95
* alertsByLevel:
* type: object
* properties:
* low:
* type: integer
* example: 5
* medium:
* type: integer
* example: 10
* high:
* type: integer
* example: 8
* critical:
* type: integer
* example: 2
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/dashboard', verifyToken, statsController.getDashboardStats);
/**
* @swagger
* /api/stats/farms:
* get:
* summary: 获取养殖场统计数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取养殖场统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* totalFarms:
* type: integer
* example: 12
* farmsByType:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 猪场
* count:
* type: integer
* example: 5
* farmsByStatus:
* type: array
* items:
* type: object
* properties:
* status:
* type: string
* example: active
* count:
* type: integer
* example: 10
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/farms', verifyToken, statsController.getFarmStats);
/**
* @swagger
* /api/stats/animals:
* get:
* summary: 获取动物统计数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取动物统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* totalAnimals:
* type: integer
* example: 5000
* animalsByType:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 猪
* count:
* type: integer
* example: 3000
* animalsByHealth:
* type: array
* items:
* type: object
* properties:
* health_status:
* type: string
* example: healthy
* count:
* type: integer
* example: 4500
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/animals', verifyToken, statsController.getAnimalStats);
/**
* @swagger
* /api/stats/devices:
* get:
* summary: 获取设备统计数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取设备统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* totalDevices:
* type: integer
* example: 150
* devicesByType:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 温度传感器
* count:
* type: integer
* example: 50
* devicesByStatus:
* type: array
* items:
* type: object
* properties:
* status:
* type: string
* example: online
* count:
* type: integer
* example: 140
* onlineRate:
* type: number
* format: float
* example: 0.95
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/devices', verifyToken, statsController.getDeviceStats);
/**
* @swagger
* /api/stats/alerts:
* get:
* summary: 获取预警统计数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取预警统计数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* totalAlerts:
* type: integer
* example: 25
* alertsByType:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* example: 温度异常
* count:
* type: integer
* example: 10
* alertsByLevel:
* type: array
* items:
* type: object
* properties:
* level:
* type: string
* example: high
* count:
* type: integer
* example: 8
* alertsByStatus:
* type: array
* items:
* type: object
* properties:
* status:
* type: string
* example: active
* count:
* type: integer
* example: 15
* recentAlerts:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* example: 1
* type:
* type: string
* example: 温度异常
* level:
* type: string
* example: high
* message:
* type: string
* example: 温度超过阈值
* createdAt:
* type: string
* format: date-time
* example: 2023-01-01T12:00:00Z
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/alerts', verifyToken, statsController.getAlertStats);
/**
* @swagger
* /api/stats/monitoring:
* get:
* summary: 获取实时监控数据
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取实时监控数据
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* deviceStatus:
* type: object
* properties:
* online:
* type: integer
* example: 140
* offline:
* type: integer
* example: 10
* maintenance:
* type: integer
* example: 5
* recentAlerts:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* example: 1
* type:
* type: string
* example: 温度异常
* level:
* type: string
* example: high
* message:
* type: string
* example: 温度超过阈值
* createdAt:
* type: string
* format: date-time
* example: 2023-01-01T12:00:00Z
* environmentalData:
* type: object
* properties:
* temperature:
* type: array
* items:
* type: object
* properties:
* timestamp:
* type: string
* format: date-time
* example: 2023-01-01T12:00:00Z
* value:
* type: number
* format: float
* example: 25.5
* humidity:
* type: array
* items:
* type: object
* properties:
* timestamp:
* type: string
* format: date-time
* example: 2023-01-01T12:00:00Z
* value:
* type: number
* format: float
* example: 60.2
* 401:
* description: 未授权
* 500:
* description: 服务器错误
*/
router.get('/monitoring', verifyToken, statsController.getMonitorData);
/**
* @swagger
* /api/stats/monthly-trends:
* get:
* summary: 获取月度数据趋势
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 成功获取月度数据趋势
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* xAxis:
* type: array
* items:
* type: string
* description: 月份标签
* series:
* type: array
* items:
* type: object
* properties:
* name:
* type: string
* type:
* type: string
* data:
* type: array
* items:
* type: number
* itemStyle:
* type: object
* areaStyle:
* type: object
* 500:
* description: 服务器错误
*/
router.get('/monthly-trends', verifyToken, statsController.getMonthlyTrends);
module.exports = router;

116
backend/routes/users.js Normal file
View File

@@ -0,0 +1,116 @@
const express = require('express');
const { verifyToken } = require('../middleware/auth');
const router = express.Router();
const userController = require('../controllers/userController');
/**
* @swagger
* tags:
* name: Users
* description: 用户管理
*/
/**
* @swagger
* components:
* schemas:
* User:
* type: object
* required:
* - id
* - username
* - email
* properties:
* id:
* type: integer
* description: 用户ID
* username:
* type: string
* description: 用户名
* email:
* type: string
* description: 邮箱地址
* example:
* id: 1
* username: "john_doe"
* email: "john@example.com"
*/
/**
* @swagger
* /api/users:
* get:
* summary: 获取所有用户 (需要认证)
* tags: [Users]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 用户列表
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: array
* items:
* $ref: '#/components/schemas/User'
* 401:
* description: 未认证
* 500:
* description: 服务器错误
*/
// 获取所有用户 (需要认证)
router.get('/', verifyToken, userController.getAllUsers);
/**
* @swagger
* /api/users/{id}:
* get:
* summary: 根据ID获取用户 (需要认证)
* tags: [Users]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* schema:
* type: integer
* required: true
* description: 用户ID
* responses:
* 200:
* description: 用户信息
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* $ref: '#/components/schemas/User'
* 401:
* description: 未认证
* 404:
* description: 用户未找到
* 500:
* description: 服务器错误
*/
// 根据ID获取用户 (需要认证)
router.get('/:id', verifyToken, userController.getUserById);
// 创建用户 (需要认证)
router.post('/', verifyToken, userController.createUser);
// 更新用户 (需要认证)
router.put('/:id', verifyToken, userController.updateUser);
// 删除用户 (需要认证)
router.delete('/:id', verifyToken, userController.deleteUser);
module.exports = router;