commit aaee84759316647539b3be7c6974102068162a39 Author: huanglinhuan Date: Wed Dec 3 22:20:43 2025 +0800 init commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..2cae279 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +.env +uploads/* +!uploads/.gitkeep +*.log +.DS_Store + diff --git a/README.md b/README.md new file mode 100755 index 0000000..d07db62 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# 机型信息管理系统 - 后端API + +## 技术栈 +- Node.js +- Express +- JWT认证 +- bcryptjs密码加密 + +## 安装依赖 +```bash +npm install +``` + +## 运行项目 +```bash +# 开发模式(需要安装nodemon) +npm run dev + +# 生产模式 +npm start +``` + +## 默认登录信息 +- 用户名: `admin` +- 密码: `admin123` + +## API接口 + +### 认证接口 +- `POST /api/auth/login` - 用户登录 +- `GET /api/auth/verify` - 验证token + +### 机型接口(需要认证) +- `GET /api/aircraft/list` - 获取机型列表 +- `GET /api/aircraft/:id` - 获取机型详情 + +### PDF资料接口(需要认证) +- `GET /api/pdf/aircraft/:aircraftId` - 获取指定机型的PDF资料 +- `GET /api/pdf/list` - 获取所有PDF资料 +- `GET /api/pdf/file/:id` - 获取PDF文件信息 + +## 环境变量 +创建 `.env` 文件并设置: +``` +PORT=3001 +JWT_SECRET=your-secret-key-change-in-production +``` + diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100755 index 0000000..e7ce47f --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,27 @@ +const jwt = require('jsonwebtoken'); + +const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; + +// 验证JWT token的中间件 +const authenticateToken = (req, res, next) => { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ error: '未提供访问令牌' }); + } + + jwt.verify(token, JWT_SECRET, (err, user) => { + if (err) { + return res.status(403).json({ error: '无效的访问令牌' }); + } + req.user = user; + next(); + }); +}; + +module.exports = { + authenticateToken, + JWT_SECRET +}; + diff --git a/package.json b/package.json new file mode 100755 index 0000000..d970039 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "department-api", + "version": "1.0.0", + "description": "机型信息管理系统后端API", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" + }, + "keywords": ["api", "express", "nodejs"], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "cors": "^2.8.5", + "jsonwebtoken": "^9.0.2", + "bcryptjs": "^2.4.3", + "dotenv": "^16.3.1", + "multer": "^1.4.5-lts.1" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } +} + diff --git a/routes/aircraft.js b/routes/aircraft.js new file mode 100755 index 0000000..653a605 --- /dev/null +++ b/routes/aircraft.js @@ -0,0 +1,120 @@ +const express = require('express'); +const router = express.Router(); + +// 模拟机型数据库(实际项目中应使用真实数据库) +const aircrafts = [ + { + id: 1, + name: '波音737', + code: 'B737', + manufacturer: '波音公司', + type: '窄体客机', + description: '波音737是波音公司生产的中短程双发窄体客机,是全球最畅销的商用喷气式客机。', + specifications: { + maxPassengers: 215, + maxRange: '5700km', + cruiseSpeed: '840km/h', + length: '39.5m', + wingspan: '35.8m' + } + }, + { + id: 2, + name: '空客A320', + code: 'A320', + manufacturer: '空中客车公司', + type: '窄体客机', + description: '空客A320是空中客车公司生产的单通道窄体客机,是空客最成功的机型之一。', + specifications: { + maxPassengers: 180, + maxRange: '6150km', + cruiseSpeed: '840km/h', + length: '37.57m', + wingspan: '35.8m' + } + }, + { + id: 3, + name: '波音787', + code: 'B787', + manufacturer: '波音公司', + type: '宽体客机', + description: '波音787梦想客机是波音公司生产的双发宽体中远程客机,采用大量复合材料。', + specifications: { + maxPassengers: 330, + maxRange: '15700km', + cruiseSpeed: '913km/h', + length: '57m', + wingspan: '60m' + } + }, + { + id: 4, + name: '空客A350', + code: 'A350', + manufacturer: '空中客车公司', + type: '宽体客机', + description: '空客A350是空中客车公司生产的双发宽体远程客机,采用先进材料和设计。', + specifications: { + maxPassengers: 440, + maxRange: '15000km', + cruiseSpeed: '903km/h', + length: '66.8m', + wingspan: '64.75m' + } + } +]; + +// 获取所有机型列表 +router.get('/list', (req, res) => { + try { + const { search } = req.query; + + let filteredAircrafts = aircrafts; + if (search) { + const searchLower = search.toLowerCase(); + filteredAircrafts = aircrafts.filter(aircraft => + aircraft.name.toLowerCase().includes(searchLower) || + aircraft.code.toLowerCase().includes(searchLower) || + aircraft.manufacturer.toLowerCase().includes(searchLower) + ); + } + + res.json({ + success: true, + data: filteredAircrafts.map(aircraft => ({ + id: aircraft.id, + name: aircraft.name, + code: aircraft.code, + manufacturer: aircraft.manufacturer, + type: aircraft.type + })) + }); + } catch (error) { + console.error('获取机型列表错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +// 获取单个机型详细信息 +router.get('/:id', (req, res) => { + try { + const id = parseInt(req.params.id); + const aircraft = aircrafts.find(a => a.id === id); + + if (!aircraft) { + return res.status(404).json({ error: '机型不存在' }); + } + + res.json({ + success: true, + data: aircraft + }); + } catch (error) { + console.error('获取机型详情错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +module.exports = router; + diff --git a/routes/auth.js b/routes/auth.js new file mode 100755 index 0000000..8bcd2d6 --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,90 @@ +const express = require('express'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const { JWT_SECRET } = require('../middleware/auth'); + +const router = express.Router(); + +// 模拟用户数据库(实际项目中应使用真实数据库) +const users = [ + { + id: 1, + username: 'admin', + password: '$2a$10$rOzJqJqJqJqJqJqJqJqJqOqJqJqJqJqJqJqJqJqJqJqJqJqJqJq', // password: admin123 + name: '管理员' + } +]; + +// 初始化默认用户(密码:admin123) +async function initDefaultUser() { + const hashedPassword = await bcrypt.hash('admin123', 10); + users[0].password = hashedPassword; +} +initDefaultUser(); + +// 登录接口 +router.post('/login', async (req, res) => { + try { + const { username, password } = req.body; + + if (!username || !password) { + return res.status(400).json({ error: '用户名和密码不能为空' }); + } + + // 查找用户 + const user = users.find(u => u.username === username); + if (!user) { + return res.status(401).json({ error: '用户名或密码错误' }); + } + + // 验证密码 + const isValidPassword = await bcrypt.compare(password, user.password); + if (!isValidPassword) { + return res.status(401).json({ error: '用户名或密码错误' }); + } + + // 生成JWT token + const token = jwt.sign( + { id: user.id, username: user.username, name: user.name }, + JWT_SECRET, + { expiresIn: '24h' } + ); + + res.json({ + success: true, + token, + user: { + id: user.id, + username: user.username, + name: user.name + } + }); + } catch (error) { + console.error('登录错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +// 验证token接口 +router.get('/verify', async (req, res) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ error: '未提供访问令牌' }); + } + + jwt.verify(token, JWT_SECRET, (err, user) => { + if (err) { + return res.status(403).json({ error: '无效的访问令牌' }); + } + res.json({ success: true, user }); + }); + } catch (error) { + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +module.exports = router; + diff --git a/routes/pdf.js b/routes/pdf.js new file mode 100755 index 0000000..3d91b60 --- /dev/null +++ b/routes/pdf.js @@ -0,0 +1,145 @@ +const express = require('express'); +const router = express.Router(); +const path = require('path'); +const fs = require('fs'); + +// 模拟PDF资料数据库(实际项目中应使用真实数据库) +const pdfMaterials = [ + { + id: 1, + aircraftId: 1, + title: '波音737操作手册', + fileName: 'B737_manual.pdf', + description: '波音737详细操作手册', + uploadDate: '2024-01-15', + fileSize: '2.5MB' + }, + { + id: 2, + aircraftId: 1, + title: '波音737维护手册', + fileName: 'B737_maintenance.pdf', + description: '波音737维护保养手册', + uploadDate: '2024-01-20', + fileSize: '3.2MB' + }, + { + id: 3, + aircraftId: 2, + title: '空客A320操作手册', + fileName: 'A320_manual.pdf', + description: '空客A320详细操作手册', + uploadDate: '2024-02-01', + fileSize: '2.8MB' + }, + { + id: 4, + aircraftId: 2, + title: '空客A320技术规格', + fileName: 'A320_specs.pdf', + description: '空客A320技术规格说明', + uploadDate: '2024-02-05', + fileSize: '1.5MB' + }, + { + id: 5, + aircraftId: 3, + title: '波音787操作手册', + fileName: 'B787_manual.pdf', + description: '波音787详细操作手册', + uploadDate: '2024-02-10', + fileSize: '4.1MB' + }, + { + id: 6, + aircraftId: 4, + title: '空客A350操作手册', + fileName: 'A350_manual.pdf', + description: '空客A350详细操作手册', + uploadDate: '2024-02-15', + fileSize: '3.9MB' + } +]; + +// 获取机型的PDF资料列表 +router.get('/aircraft/:aircraftId', (req, res) => { + try { + const aircraftId = parseInt(req.params.aircraftId); + const { search } = req.query; + + let materials = pdfMaterials.filter(m => m.aircraftId === aircraftId); + + if (search) { + const searchLower = search.toLowerCase(); + materials = materials.filter(m => + m.title.toLowerCase().includes(searchLower) || + m.description.toLowerCase().includes(searchLower) + ); + } + + res.json({ + success: true, + data: materials + }); + } catch (error) { + console.error('获取PDF资料错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +// 获取所有PDF资料 +router.get('/list', (req, res) => { + try { + const { search } = req.query; + + let materials = pdfMaterials; + + if (search) { + const searchLower = search.toLowerCase(); + materials = materials.filter(m => + m.title.toLowerCase().includes(searchLower) || + m.description.toLowerCase().includes(searchLower) + ); + } + + res.json({ + success: true, + data: materials + }); + } catch (error) { + console.error('获取PDF资料列表错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +// 获取PDF文件URL(实际项目中应该检查文件是否存在) +router.get('/file/:id', (req, res) => { + try { + const id = parseInt(req.params.id); + const material = pdfMaterials.find(m => m.id === id); + + if (!material) { + return res.status(404).json({ error: '资料不存在' }); + } + + // 返回PDF文件的URL + // 实际项目中,这里应该检查文件是否真实存在 + const fileUrl = `/uploads/${material.fileName}`; + + res.json({ + success: true, + data: { + id: material.id, + title: material.title, + fileUrl: fileUrl, + fileName: material.fileName + } + }); + } catch (error) { + console.error('获取PDF文件错误:', error); + res.status(500).json({ error: '服务器内部错误' }); + } +}); + +module.exports = router; + diff --git a/server.js b/server.js new file mode 100755 index 0000000..21f6a6c --- /dev/null +++ b/server.js @@ -0,0 +1,33 @@ +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const authRoutes = require('./routes/auth'); +const aircraftRoutes = require('./routes/aircraft'); +const pdfRoutes = require('./routes/pdf'); +const { authenticateToken } = require('./middleware/auth'); + +const app = express(); +const PORT = process.env.PORT || 3001; + +// 中间件 +app.use(cors()); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// 静态文件服务 - 用于提供PDF文件 +app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); + +// 路由 +app.use('/api/auth', authRoutes); +app.use('/api/aircraft', authenticateToken, aircraftRoutes); +app.use('/api/pdf', authenticateToken, pdfRoutes); + +// 健康检查 +app.get('/api/health', (req, res) => { + res.json({ status: 'ok', message: 'API服务运行正常' }); +}); + +app.listen(PORT, () => { + console.log(`服务器运行在端口 ${PORT}`); +}); + diff --git a/uploads/.gitkeep b/uploads/.gitkeep new file mode 100755 index 0000000..e69de29