2025-08-31 00:45:46 +08:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const cors = require('cors');
|
|
|
|
|
|
const helmet = require('helmet');
|
|
|
|
|
|
const morgan = require('morgan');
|
|
|
|
|
|
const rateLimit = require('express-rate-limit');
|
|
|
|
|
|
const xss = require('xss-clean');
|
|
|
|
|
|
const hpp = require('hpp');
|
|
|
|
|
|
const swaggerUi = require('swagger-ui-express');
|
|
|
|
|
|
const swaggerSpec = require('./config/swagger');
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
console.log('🔧 初始化Express应用...');
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
const { globalErrorHandler, notFound } = require('./utils/errors');
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-09-11 15:05:23 +08:00
|
|
|
|
// 检查是否为无数据库模式
|
|
|
|
|
|
const NO_DB_MODE = process.env.NO_DB_MODE === 'true';
|
|
|
|
|
|
|
2025-09-22 15:28:18 +08:00
|
|
|
|
let authRoutes, userRoutes, travelRoutes, animalRoutes, orderRoutes, adminRoutes, travelRegistrationRoutes, promotionRoutes, merchantRoutes, travelsRoutes;
|
2025-09-11 15:05:23 +08:00
|
|
|
|
|
2025-09-22 02:04:07 +08:00
|
|
|
|
// 路由导入
|
2025-09-11 15:05:23 +08:00
|
|
|
|
if (NO_DB_MODE) {
|
|
|
|
|
|
console.log('⚠️ 无数据库模式:将使用模拟路由');
|
|
|
|
|
|
} else {
|
2025-09-22 02:04:07 +08:00
|
|
|
|
console.log('✅ 数据库模式:加载实际路由');
|
2025-09-11 15:05:23 +08:00
|
|
|
|
authRoutes = require('./routes/auth');
|
|
|
|
|
|
userRoutes = require('./routes/user');
|
|
|
|
|
|
travelRoutes = require('./routes/travel');
|
2025-09-22 15:28:18 +08:00
|
|
|
|
travelsRoutes = require('./routes/travels'); // 新增travels路由
|
2025-09-11 15:05:23 +08:00
|
|
|
|
animalRoutes = require('./routes/animal');
|
|
|
|
|
|
orderRoutes = require('./routes/order');
|
|
|
|
|
|
adminRoutes = require('./routes/admin'); // 新增管理员路由
|
2025-09-20 16:15:59 +08:00
|
|
|
|
travelRegistrationRoutes = require('./routes/travelRegistration'); // 旅行报名路由
|
2025-09-21 23:18:08 +08:00
|
|
|
|
paymentRoutes = require('./routes/payment-simple');
|
|
|
|
|
|
animalClaimRoutes = require('./routes/animalClaim-simple'); // 动物认领路由(简化版)
|
2025-09-22 02:04:07 +08:00
|
|
|
|
promotionRoutes = require('./routes/promotion'); // 促销活动路由
|
|
|
|
|
|
merchantRoutes = require('./routes/merchant'); // 商户路由
|
2025-09-11 15:05:23 +08:00
|
|
|
|
}
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
const app = express();
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
console.log('✅ Express应用初始化完成');
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 安全中间件
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(helmet());
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// CORS配置
|
|
|
|
|
|
app.use(cors({
|
|
|
|
|
|
origin: process.env.NODE_ENV === 'production'
|
|
|
|
|
|
? ['https://your-domain.com']
|
2025-09-21 23:18:08 +08:00
|
|
|
|
: [
|
|
|
|
|
|
'https://www.jiebanke.com',
|
|
|
|
|
|
'https://admin.jiebanke.com',
|
|
|
|
|
|
'https://webapi.jiebanke.com',
|
|
|
|
|
|
'http://localhost:3150', // 管理后台本地开发地址
|
|
|
|
|
|
'http://localhost:3000', // 备用端口
|
2025-09-22 02:04:07 +08:00
|
|
|
|
'http://localhost:3200', // 备用端口
|
2025-09-21 23:18:08 +08:00
|
|
|
|
'http://127.0.0.1:3150', // 备用地址
|
2025-09-22 02:04:07 +08:00
|
|
|
|
'http://127.0.0.1:3000', // 备用地址
|
|
|
|
|
|
'http://127.0.0.1:3200' // 备用地址
|
2025-09-21 23:18:08 +08:00
|
|
|
|
],
|
|
|
|
|
|
credentials: true,
|
|
|
|
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
|
|
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
2025-08-31 00:45:46 +08:00
|
|
|
|
}));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 请求日志
|
|
|
|
|
|
if (process.env.NODE_ENV === 'development') {
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(morgan('dev'));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
} else {
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(morgan('combined'));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 请求频率限制
|
|
|
|
|
|
const limiter = rateLimit({
|
|
|
|
|
|
windowMs: 15 * 60 * 1000, // 15分钟
|
|
|
|
|
|
max: process.env.NODE_ENV === 'production' ? 100 : 1000, // 生产环境100次,开发环境1000次
|
|
|
|
|
|
message: {
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
code: 429,
|
|
|
|
|
|
message: '请求过于频繁,请稍后再试',
|
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
|
}
|
2025-08-31 00:45:46 +08:00
|
|
|
|
});
|
|
|
|
|
|
app.use('/api', limiter);
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 请求体解析
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(express.json({ limit: '10kb' }));
|
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 数据清洗
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(xss()); // 防止XSS攻击
|
2025-08-30 14:33:49 +08:00
|
|
|
|
app.use(hpp({ // 防止参数污染
|
|
|
|
|
|
whitelist: [
|
|
|
|
|
|
'page',
|
|
|
|
|
|
'pageSize',
|
|
|
|
|
|
'sort',
|
|
|
|
|
|
'fields',
|
|
|
|
|
|
'price',
|
|
|
|
|
|
'rating',
|
|
|
|
|
|
'distance'
|
|
|
|
|
|
]
|
2025-08-31 00:45:46 +08:00
|
|
|
|
}));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 静态文件服务
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use('/uploads', express.static('uploads'));
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-30 15:29:51 +08:00
|
|
|
|
// Swagger文档路由
|
|
|
|
|
|
if (process.env.NODE_ENV === 'development' || process.env.ENABLE_SWAGGER === 'true') {
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
2025-09-03 13:25:08 +08:00
|
|
|
|
console.log('📚 Swagger文档已启用: https://webapi.jiebanke.com/api-docs');
|
2025-08-30 15:29:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-30 14:33:49 +08:00
|
|
|
|
// 健康检查路由
|
|
|
|
|
|
app.get('/health', (req, res) => {
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
status: 'OK',
|
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
|
uptime: process.uptime(),
|
2025-09-11 15:05:23 +08:00
|
|
|
|
environment: process.env.NODE_ENV || 'development',
|
|
|
|
|
|
noDbMode: NO_DB_MODE
|
2025-08-31 00:45:46 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-09-21 23:18:08 +08:00
|
|
|
|
// API根路由
|
|
|
|
|
|
app.get('/api/v1', (req, res) => {
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '杰伴客API服务运行正常',
|
|
|
|
|
|
version: '1.0.0',
|
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
|
endpoints: {
|
|
|
|
|
|
auth: '/api/v1/auth',
|
|
|
|
|
|
users: '/api/v1/users',
|
|
|
|
|
|
travel: '/api/v1/travel',
|
|
|
|
|
|
animals: '/api/v1/animals',
|
|
|
|
|
|
orders: '/api/v1/orders',
|
|
|
|
|
|
payments: '/api/v1/payments',
|
|
|
|
|
|
animalClaims: '/api/v1/animal-claims',
|
|
|
|
|
|
admin: '/api/v1/admin',
|
2025-09-22 02:04:07 +08:00
|
|
|
|
travelRegistration: '/api/v1/travel-registration',
|
|
|
|
|
|
promotion: '/api/v1/promotion'
|
2025-09-21 23:18:08 +08:00
|
|
|
|
},
|
|
|
|
|
|
documentation: 'https://webapi.jiebanke.com/api-docs'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-08-31 23:26:25 +08:00
|
|
|
|
// 系统统计路由
|
|
|
|
|
|
app.get('/system-stats', (req, res) => {
|
|
|
|
|
|
const stats = {
|
|
|
|
|
|
status: 'OK',
|
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
|
environment: process.env.NODE_ENV || 'development',
|
|
|
|
|
|
nodeVersion: process.version,
|
|
|
|
|
|
memoryUsage: process.memoryUsage(),
|
|
|
|
|
|
uptime: process.uptime(),
|
|
|
|
|
|
cpuCount: require('os').cpus().length,
|
|
|
|
|
|
platform: process.platform,
|
2025-09-11 15:05:23 +08:00
|
|
|
|
architecture: process.arch,
|
|
|
|
|
|
noDbMode: NO_DB_MODE
|
2025-08-31 23:26:25 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
res.status(200).json(stats);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-11 15:05:23 +08:00
|
|
|
|
// 无数据库模式下的模拟路由
|
|
|
|
|
|
if (NO_DB_MODE) {
|
|
|
|
|
|
// 认证路由
|
|
|
|
|
|
app.use('/api/v1/auth', (req, res) => {
|
|
|
|
|
|
if (req.method === 'POST' && req.path === '/login') {
|
|
|
|
|
|
// 模拟登录响应
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
message: '模拟登录成功',
|
|
|
|
|
|
data: {
|
|
|
|
|
|
token: 'mock-jwt-token',
|
|
|
|
|
|
user: {
|
|
|
|
|
|
id: 1,
|
|
|
|
|
|
username: 'mockuser',
|
|
|
|
|
|
email: 'mock@example.com',
|
|
|
|
|
|
role: 'user'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,该功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 其他路由的通用响应
|
|
|
|
|
|
app.use('/api/v1/users', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,用户管理功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/travel', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,旅行相关功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/animals', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,动物相关功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/orders', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,订单相关功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-20 16:15:59 +08:00
|
|
|
|
app.use('/api/v1/travel-registration', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,旅行报名功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/payments', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,支付功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/animal-claims', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,动物认领功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-11 15:05:23 +08:00
|
|
|
|
app.use('/api/v1/admin', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,管理员功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-09-22 02:04:07 +08:00
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/promotion', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,促销活动功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
app.use('/api/v1/merchants', (req, res) => {
|
|
|
|
|
|
res.status(503).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '当前为无数据库模式,商户功能不可用'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-09-11 15:05:23 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// API路由
|
|
|
|
|
|
app.use('/api/v1/auth', authRoutes);
|
|
|
|
|
|
app.use('/api/v1/users', userRoutes);
|
|
|
|
|
|
app.use('/api/v1/travel', travelRoutes);
|
2025-09-22 15:28:18 +08:00
|
|
|
|
app.use('/api/v1/travels', travelsRoutes); // 新增travels路由
|
2025-09-11 15:05:23 +08:00
|
|
|
|
app.use('/api/v1/animals', animalRoutes);
|
|
|
|
|
|
app.use('/api/v1/orders', orderRoutes);
|
2025-09-20 16:15:59 +08:00
|
|
|
|
app.use('/api/v1/payments', paymentRoutes);
|
|
|
|
|
|
// 动物认领路由
|
|
|
|
|
|
app.use('/api/v1/animal-claims', animalClaimRoutes);
|
2025-09-11 15:05:23 +08:00
|
|
|
|
// 管理员路由
|
|
|
|
|
|
app.use('/api/v1/admin', adminRoutes);
|
2025-09-20 16:15:59 +08:00
|
|
|
|
// 旅行报名路由
|
|
|
|
|
|
app.use('/api/v1/travel-registration', travelRegistrationRoutes);
|
2025-09-22 02:04:07 +08:00
|
|
|
|
// 促销活动路由
|
|
|
|
|
|
app.use('/api/v1/promotion', promotionRoutes);
|
|
|
|
|
|
// 商户路由
|
|
|
|
|
|
app.use('/api/v1/merchants', merchantRoutes);
|
2025-09-11 15:05:23 +08:00
|
|
|
|
}
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 404处理
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use('*', notFound);
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 全局错误处理
|
2025-08-31 00:45:46 +08:00
|
|
|
|
app.use(globalErrorHandler);
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
console.log('✅ 应用配置完成');
|
2025-08-30 14:33:49 +08:00
|
|
|
|
|
2025-08-31 00:45:46 +08:00
|
|
|
|
module.exports = app;
|