330 lines
9.0 KiB
JavaScript
330 lines
9.0 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const helmet = require('helmet');
|
|
const dotenv = require('dotenv');
|
|
const rateLimit = require('express-rate-limit');
|
|
const mysql = require('mysql2/promise');
|
|
const path = require('path');
|
|
|
|
// 导入路由模块
|
|
const authModule = require('./routes/auth');
|
|
const usersModule = require('./routes/users');
|
|
const cattleModule = require('./routes/cattle');
|
|
const financeModule = require('./routes/finance');
|
|
const tradingModule = require('./routes/trading');
|
|
const governmentModule = require('./routes/government');
|
|
const mallModule = require('./routes/mall');
|
|
|
|
// 加载环境变量
|
|
dotenv.config();
|
|
|
|
// 数据库连接
|
|
let pool, testDatabaseConnection;
|
|
|
|
if (process.env.DB_TYPE === 'sqlite') {
|
|
// 使用SQLite
|
|
const sqliteDb = require('./database-sqlite');
|
|
pool = sqliteDb.pool;
|
|
testDatabaseConnection = sqliteDb.testDatabaseConnection;
|
|
} else {
|
|
// 使用MySQL
|
|
const mysql = require('mysql2/promise');
|
|
|
|
// 数据库连接配置
|
|
const dbConfig = {
|
|
host: process.env.DB_HOST,
|
|
port: process.env.DB_PORT,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME,
|
|
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
connectionLimit: 10,
|
|
acquireTimeout: 60000,
|
|
timeout: 60000
|
|
};
|
|
|
|
// 创建数据库连接池
|
|
pool = mysql.createPool(dbConfig);
|
|
|
|
// 测试数据库连接
|
|
testDatabaseConnection = async function() {
|
|
try {
|
|
const connection = await pool.getConnection();
|
|
console.log('✅ MySQL数据库连接成功');
|
|
console.log(`📍 连接到: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
|
|
connection.release();
|
|
return true;
|
|
} catch (error) {
|
|
console.error('❌ 数据库连接失败:', error.message);
|
|
console.log('⚠️ 服务将继续运行,但数据库功能不可用');
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
|
|
// 创建Express应用
|
|
const app = express();
|
|
const PORT = process.env.PORT || 8888;
|
|
|
|
// 初始化数据库和中间件的异步函数
|
|
async function initializeApp() {
|
|
// 启动时测试数据库连接
|
|
await testDatabaseConnection();
|
|
|
|
// 设置路由模块的数据库连接
|
|
authModule.setPool(pool);
|
|
usersModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
cattleModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
financeModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
tradingModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
governmentModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
mallModule.setMiddleware(authModule.authenticateToken, authModule.checkPermission, pool);
|
|
|
|
console.log('✅ 中间件初始化完成');
|
|
}
|
|
|
|
// 初始化应用(不阻塞服务启动)
|
|
initializeApp().catch(console.error);
|
|
|
|
// 导入错误处理中间件
|
|
const { errorHandler, notFoundHandler, requestLogger, performanceMonitor } = require('./middleware/errorHandler');
|
|
|
|
// 中间件
|
|
app.use(requestLogger); // 请求日志
|
|
app.use(performanceMonitor); // 性能监控
|
|
app.use(helmet()); // 安全头部
|
|
app.use(cors({
|
|
origin: process.env.CORS_ORIGIN || '*',
|
|
credentials: true
|
|
})); // 跨域支持
|
|
app.use(express.json({ limit: '10mb' })); // JSON解析
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' })); // URL编码解析
|
|
|
|
// 速率限制
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15分钟
|
|
max: 100, // 限制每个IP 15分钟内最多100个请求
|
|
message: '请求过于频繁,请稍后再试'
|
|
});
|
|
app.use(limiter);
|
|
|
|
// 基础路由
|
|
app.get('/', (req, res) => {
|
|
res.json({
|
|
message: '欢迎使用锡林郭勒盟地区智慧养殖产业平台API服务',
|
|
version: '1.0.0',
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
});
|
|
|
|
app.get('/health', async (req, res) => {
|
|
try {
|
|
// 测试数据库连接
|
|
const connection = await pool.getConnection();
|
|
const [rows] = await connection.execute('SELECT 1 as test');
|
|
connection.release();
|
|
|
|
res.json({
|
|
status: 'OK',
|
|
timestamp: new Date().toISOString(),
|
|
database: 'Connected',
|
|
environment: process.env.NODE_ENV,
|
|
version: '1.0.0'
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
status: 'ERROR',
|
|
timestamp: new Date().toISOString(),
|
|
database: 'Disconnected',
|
|
error: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// API路由
|
|
app.use('/api/v1/auth', authModule.router);
|
|
app.use('/api/v1/users', usersModule.router);
|
|
app.use('/api/v1/cattle', cattleModule.router);
|
|
app.use('/api/v1/finance', financeModule.router);
|
|
app.use('/api/v1/trading', tradingModule.router);
|
|
app.use('/api/v1/government', governmentModule.router);
|
|
app.use('/api/v1/mall', mallModule.router);
|
|
|
|
// 数据库查询接口
|
|
app.get('/api/v1/database/tables', async (req, res) => {
|
|
try {
|
|
const [tables] = await pool.execute(
|
|
'SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = ?',
|
|
[process.env.DB_NAME]
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
database: process.env.DB_NAME,
|
|
tables: tables,
|
|
count: tables.length
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
});
|
|
|
|
// 数据库连接状态接口
|
|
app.get('/api/v1/database/status', async (req, res) => {
|
|
try {
|
|
const [statusResult] = await pool.execute('SHOW STATUS LIKE "Threads_connected"');
|
|
const [versionResult] = await pool.execute('SELECT VERSION() as version');
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
connected: true,
|
|
version: versionResult[0].version,
|
|
threads_connected: statusResult[0].Value,
|
|
database: process.env.DB_NAME,
|
|
host: process.env.DB_HOST,
|
|
port: process.env.DB_PORT
|
|
},
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
});
|
|
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: '区域未找到' });
|
|
}
|
|
});
|
|
|
|
// 错误处理中间件(必须放在所有路由之后)
|
|
app.use(notFoundHandler); // 404处理
|
|
app.use(errorHandler); // 统一错误处理
|
|
|
|
// 启动服务器
|
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
|
|
app.listen(PORT, HOST, () => {
|
|
console.log(`API服务器正在监听: http://${HOST}:${PORT}`);
|
|
console.log(`服务器绑定到: ${HOST}, 端口: ${PORT}`);
|
|
console.log('尝试使用以下URL访问:');
|
|
console.log(` http://localhost:${PORT}`);
|
|
console.log(` http://127.0.0.1:${PORT}`);
|
|
});
|
|
|
|
module.exports = app; |