const express = require('express'); const cors = require('cors'); const path = require('path'); const dotenv = require('dotenv'); dotenv.config(); const app = express(); const PORT = process.env.PORT || 3002; // 中间件 - CORS配置 // 当前暂无域名,员工通过公网IP访问,暂时允许所有来源 // TODO: 申请域名后,在 .env 的 CORS_ORIGIN 中填写域名,并切换为严格模式 const corsWhitelist = process.env.CORS_ORIGIN ? process.env.CORS_ORIGIN.split(',') : []; const corsOptions = { origin: (origin, callback) => { // 暂无域名阶段:允许所有来源访问(公网IP访问需要) if (!origin || corsWhitelist.length === 0 || corsWhitelist.includes(origin)) { callback(null, true); } else { // 有域名后可改为 callback(new Error('Not allowed'), false) 限制来源 callback(null, true); } }, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] }; app.use(cors(corsOptions)); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ extended: true, limit: '50mb' })); // 静态文件服务 - 前端应用 app.use(express.static(path.join(__dirname, '../frontend/dist'))); // 加载路由模块 app.use('/api/auth', require('./routes/auth')); app.use('/api/users', require('./routes/users')); app.use('/api/products', require('./routes/products')); app.use('/api/customers', require('./routes/customers')); app.use('/api/suppliers', require('./routes/suppliers')); app.use('/api/subcontractors', require('./routes/subcontractors')); app.use('/api/projects', require('./routes/projects')); app.use('/api/upload', require('./routes/upload')); app.use('/api/construction', require('./routes/construction')); app.use('/api/categories', require('./routes/categories')); app.use('/api/payment-nodes', require('./routes/paymentNodes')); app.use('/api/payment-records', require('./routes/paymentRecords')); app.use('/api/exchange-rates', require('./routes/exchange')); app.use('/api/advances', require('./routes/advances')); app.use('/api/payment-requests', require('./routes/payments')); app.use('/api/verifications', require('./routes/verifications')); app.use('/api/executions', require('./routes/executions')); app.use('/api/reimbursements', require('./routes/reimbursements')); app.use('/api/purchase-requests', require('./routes/purchase')); app.use('/api/purchase-orders', require('./routes/purchase-orders')); app.use('/api/payment-plans', require('./routes/payment-plans')); app.use('/api/inventory', require('./routes/inventory')); app.use('/api/finance-stats', require('./routes/finance-stats')); app.use('/api/budget-projects', require('./routes/budget')); app.use('/api/health', require('./routes/health')); app.use('/api/logistics-companies', require('./routes/logistics-companies')); app.use('/api/logistics', require('./routes/logistics')); app.use('/api/payment-execution', require('./routes/payment-execution')); app.use('/api/verifications-new', require('./routes/verifications-new')); app.use('/api/returns', require('./routes/returns')); app.use('/api/project-materials', require('./routes/project-materials')); app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../frontend/dist/index.html')); }); // 错误处理中间件 - 生产环境隐藏内部错误详情 app.use((err, req, res, next) => { console.error('[ERROR]', err.message || err); const isDev = process.env.NODE_ENV === 'development'; res.status(err.status || 500).json({ success: false, message: isDev ? (err.message || '服务器内部错误') : '服务器内部错误', ...(isDev && { stack: err.stack }) }); }); // 启动服务器 app.listen(PORT, () => { console.log(` 🚀 公司财务管理系统 - 模块化后端 =========================================== 📍 服务器地址: http://0.0.0.0:${PORT} 🌐 外部访问: http://${process.env.EXTERNAL_IP || 'localhost'}:${PORT} 🔗 核心API端点: - 健康检查: /api/health - 客户管理: /api/customers - 供应商管理: /api/suppliers - 项目管理: /api/projects - 商品管理: /api/products - 采购申请: /api/purchase-requests - 库存管理: /api/inventory - 付款节点: /api/payment-nodes - 付款记录: /api/payment-records - 汇率管理: /api/exchange-rates - 预支款管理: /api/advances - 报销管理: /api/reimbursements - 财务统计: /api/finance-stats ✅ 所有API已就绪 ✅ 前端应用已集成 ✅ 数据库已连接 ✅ 等待用户访问 ⏰ 启动时间: ${new Date().toISOString()} =========================================== `); }).on('error', (err) => { console.error('服务器启动失败:', err); process.exit(1); });