2025-08-18 23:48:54 +08:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const cors = require('cors');
|
|
|
|
|
|
const helmet = require('helmet');
|
|
|
|
|
|
const dotenv = require('dotenv');
|
|
|
|
|
|
const rateLimit = require('express-rate-limit');
|
|
|
|
|
|
|
|
|
|
|
|
// 加载环境变量
|
|
|
|
|
|
dotenv.config();
|
|
|
|
|
|
|
|
|
|
|
|
// 创建Express应用
|
|
|
|
|
|
const app = express();
|
2025-09-11 17:11:12 +08:00
|
|
|
|
const PORT = process.env.PORT || 3350; // 生产环境使用3350端口
|
2025-08-18 23:48:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 中间件
|
|
|
|
|
|
app.use(helmet()); // 安全头部
|
|
|
|
|
|
app.use(cors()); // 跨域支持
|
|
|
|
|
|
app.use(express.json({ limit: '10mb' })); // JSON解析
|
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' })); // URL编码解析
|
|
|
|
|
|
|
|
|
|
|
|
// 速率限制
|
|
|
|
|
|
const limiter = rateLimit({
|
|
|
|
|
|
windowMs: 15 * 60 * 1000, // 15分钟
|
2025-09-11 17:11:12 +08:00
|
|
|
|
max: 1000, // 生产环境提高限制
|
2025-08-18 23:48:54 +08:00
|
|
|
|
message: '请求过于频繁,请稍后再试'
|
|
|
|
|
|
});
|
|
|
|
|
|
app.use(limiter);
|
|
|
|
|
|
|
|
|
|
|
|
// 基础路由
|
|
|
|
|
|
app.get('/', (req, res) => {
|
2025-08-20 03:01:14 +08:00
|
|
|
|
res.json({
|
|
|
|
|
|
message: '欢迎使用锡林郭勒盟地区智慧养殖产业平台API服务',
|
|
|
|
|
|
version: '1.0.0',
|
2025-09-11 17:11:12 +08:00
|
|
|
|
environment: process.env.NODE_ENV || 'development',
|
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
|
docs: 'https://xlapi.jiebanke.com/docs'
|
2025-08-20 03:01:14 +08:00
|
|
|
|
});
|
2025-08-18 23:48:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.get('/health', (req, res) => {
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
status: 'OK',
|
2025-09-11 17:11:12 +08:00
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
|
uptime: process.uptime(),
|
|
|
|
|
|
memory: process.memoryUsage()
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// API信息端点
|
|
|
|
|
|
app.get('/api/info', (req, res) => {
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
name: 'xlxumu-api',
|
|
|
|
|
|
version: '1.0.0',
|
|
|
|
|
|
environment: process.env.NODE_ENV,
|
|
|
|
|
|
port: PORT,
|
|
|
|
|
|
node_version: process.version,
|
|
|
|
|
|
platform: process.platform
|
2025-08-18 23:48:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-08-20 20:34:52 +08:00
|
|
|
|
// 大屏可视化地图数据接口
|
|
|
|
|
|
app.get('/api/v1/dashboard/map/regions', (req, res) => {
|
|
|
|
|
|
// 模拟锡林郭勒盟各区域数据
|
|
|
|
|
|
const regions = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'xlg',
|
|
|
|
|
|
name: '锡林浩特市',
|
|
|
|
|
|
coordinates: [116.093, 43.946],
|
|
|
|
|
|
cattle_count: 25600,
|
|
|
|
|
|
farm_count: 120,
|
|
|
|
|
|
output_value: 650000000
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'dwq',
|
|
|
|
|
|
name: '东乌旗',
|
|
|
|
|
|
coordinates: [116.980, 45.514],
|
|
|
|
|
|
cattle_count: 18500,
|
|
|
|
|
|
farm_count: 95,
|
|
|
|
|
|
output_value: 480000000
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'xwq',
|
|
|
|
|
|
name: '西乌旗',
|
|
|
|
|
|
coordinates: [117.615, 44.587],
|
|
|
|
|
|
cattle_count: 21200,
|
|
|
|
|
|
farm_count: 108,
|
|
|
|
|
|
output_value: 520000000
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'abg',
|
|
|
|
|
|
name: '阿巴嘎旗',
|
|
|
|
|
|
coordinates: [114.971, 44.022],
|
|
|
|
|
|
cattle_count: 16800,
|
|
|
|
|
|
farm_count: 86,
|
|
|
|
|
|
output_value: 420000000
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'snz',
|
|
|
|
|
|
name: '苏尼特左旗',
|
|
|
|
|
|
coordinates: [113.653, 43.859],
|
|
|
|
|
|
cattle_count: 12400,
|
|
|
|
|
|
farm_count: 65,
|
|
|
|
|
|
output_value: 310000000
|
|
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
res.json({ regions });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.get('/api/v1/dashboard/map/region/:regionId', (req, res) => {
|
|
|
|
|
|
const { regionId } = req.params;
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟各区域详细数据
|
|
|
|
|
|
const regionDetails = {
|
|
|
|
|
|
'xlg': {
|
|
|
|
|
|
region: {
|
|
|
|
|
|
id: 'xlg',
|
|
|
|
|
|
name: '锡林浩特市',
|
|
|
|
|
|
coordinates: [116.093, 43.946],
|
|
|
|
|
|
cattle_count: 25600,
|
|
|
|
|
|
farm_count: 120,
|
|
|
|
|
|
output_value: 650000000,
|
|
|
|
|
|
trend: 'up'
|
|
|
|
|
|
},
|
|
|
|
|
|
farms: [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'FARM001',
|
|
|
|
|
|
name: '锡林浩特市第一牧场',
|
|
|
|
|
|
coordinates: [116.120, 43.950],
|
|
|
|
|
|
cattle_count: 2450,
|
|
|
|
|
|
output_value: 62000000
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'FARM002',
|
|
|
|
|
|
name: '锡林浩特市第二牧场',
|
|
|
|
|
|
coordinates: [116.080, 43.930],
|
|
|
|
|
|
cattle_count: 2100,
|
|
|
|
|
|
output_value: 53000000
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
'dwq': {
|
|
|
|
|
|
region: {
|
|
|
|
|
|
id: 'dwq',
|
|
|
|
|
|
name: '东乌旗',
|
|
|
|
|
|
coordinates: [116.980, 45.514],
|
|
|
|
|
|
cattle_count: 18500,
|
|
|
|
|
|
farm_count: 95,
|
|
|
|
|
|
output_value: 480000000,
|
|
|
|
|
|
trend: 'up'
|
|
|
|
|
|
},
|
|
|
|
|
|
farms: [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'FARM003',
|
|
|
|
|
|
name: '东乌旗牧场A',
|
|
|
|
|
|
coordinates: [116.990, 45.520],
|
|
|
|
|
|
cattle_count: 1950,
|
|
|
|
|
|
output_value: 49000000
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const detail = regionDetails[regionId];
|
|
|
|
|
|
if (detail) {
|
|
|
|
|
|
res.json(detail);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
res.status(404).json({ error: '区域未找到' });
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-11 17:11:12 +08:00
|
|
|
|
// 错误处理中间件
|
|
|
|
|
|
app.use((err, req, res, next) => {
|
|
|
|
|
|
console.error('服务器错误:', err.stack);
|
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
error: '内部服务器错误',
|
|
|
|
|
|
message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong!'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 404处理
|
|
|
|
|
|
app.use((req, res) => {
|
|
|
|
|
|
res.status(404).json({
|
|
|
|
|
|
error: '接口未找到',
|
|
|
|
|
|
path: req.path,
|
|
|
|
|
|
method: req.method
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 优雅关机处理
|
|
|
|
|
|
process.on('SIGINT', () => {
|
|
|
|
|
|
console.log('\n收到SIGINT信号,正在优雅关闭服务器...');
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
process.on('SIGTERM', () => {
|
|
|
|
|
|
console.log('\n收到SIGTERM信号,正在优雅关闭服务器...');
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-08-18 23:48:54 +08:00
|
|
|
|
// 启动服务器
|
2025-09-11 17:11:12 +08:00
|
|
|
|
const server = app.listen(PORT, '0.0.0.0', () => {
|
|
|
|
|
|
console.log(`🚀 API服务器正在运行:`);
|
|
|
|
|
|
console.log(` 📍 本地: http://localhost:${PORT}`);
|
|
|
|
|
|
console.log(` 🌐 网络: http://0.0.0.0:${PORT}`);
|
|
|
|
|
|
console.log(` 🏷️ 环境: ${process.env.NODE_ENV || 'development'}`);
|
|
|
|
|
|
console.log(` ⏰ 启动时间: ${new Date().toLocaleString()}`);
|
2025-08-18 23:48:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-11 17:11:12 +08:00
|
|
|
|
// 设置超时
|
|
|
|
|
|
server.timeout = 60000;
|
|
|
|
|
|
server.keepAliveTimeout = 5000;
|
|
|
|
|
|
|
2025-08-18 23:48:54 +08:00
|
|
|
|
module.exports = app;
|