- 像素级复刻中国铁路 12306 网站的核心界面与订票流程,包含前端应用与后端 API。
- 面向试用与中期验收,提供一键本地运行、测试与可选 MySQL 兼容验证。
12306/
├── frontend/ # 前端 React + TypeScript + Vite
├── backend/ # 后端 Express + Sequelize (SQLite/MySQL)
├── docs/ # 文档(测试指南、项目计划等)
└── README.md # 使用与部署指南(当前文件)
- 前端:React 19 + TypeScript、Vite 7、React Router 7
- 后端:Node.js、Express 5、Sequelize 6、SQLite3、MySQL2
- 测试:Jest(后端)、Playwright(E2E)、Vitest(前端单测)
环境要求
- Node.js 20
- 可选:Docker(用于 MySQL 兼容测试)
安装依赖(两种方式任选其一,建议选择方法一)
方式一:进入子目录安装(开发者常用)
# 后端
cd backend
npm install
# 前端
cd ../frontend
npm install方式二:在仓库根目录使用 --prefix(一键跨平台)
npm install --prefix backend
npm install --prefix frontend启动服务(两种方式任选其一,建议使用方法一)
方式一:进入子目录运行(推荐开发模式,自动热重载)
- 终端 A(后端):
cd backendnpm run dev(使用 nodemon 监听源码)
- 终端 B(前端):
cd frontendnpm run dev
方式二:在仓库根目录使用 --prefix(适合一键启停)
-
启动后端(开发或演示二选一):
- 开发模式:
npm run dev --prefix backend - 演示模式:
npm start --prefix backend
- 开发模式:
-
启动前端:
npm run dev --prefix frontend -
就绪检查:访问
http://localhost:3000/health与http://localhost:5174
说明:前端已在
vite.config.ts配置server.port=5174与/api代理到后端http://localhost:3000。
方案 A:快速演示(SQLite)
- 后端:
PORT=3000 npm start --prefix backend - 前端构建:
npm run build --prefix frontend - 前端预览:
npm run preview --prefix frontend(默认http://localhost:4173) - 反向代理:将前端请求
/api指向后端http://localhost:3000(Nginx/网关均可)
方案 B:MySQL 部署
- 启动数据库(示例,Docker):
docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=trae_12306 -p 3306:3306 mysql:8
- 后端环境变量:
DB_DIALECT=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_USER=rootDB_PASS=rootDB_NAME=trae_12306- 可选:
API_PREFIX=/api/v1、CORS_ORIGIN=http://localhost:5174 - 订单回收与状态轮转:
ORDER_UNPAID_TTL_MS、ORDER_CLEANUP_INTERVAL_MS
- 启动后端:
npm start --prefix backend - 前端同方案 A
server {
listen 80;
server_name your-domain.com;
# 前端静态资源(构建产物)
root /var/www/12306-frontend/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 代理后端 API
location /api/v1/ {
proxy_pass http://127.0.0.1:3000/api/v1/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
}
}注意:前端当前使用绝对地址 http://127.0.0.1:3000/api/v1(frontend/src/services/api.ts:1)。若部署到远程域名,请确保后端可通过该地址访问,或将该常量调整为部署域名的 API 根路径并相应设置 CORS_ORIGIN。
version: '3.8'
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: trae_12306
ports:
- '3306:3306'
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-proot']
interval: 10s
timeout: 5s
retries: 10
backend:
build: ./backend
environment:
NODE_ENV: production
DB_DIALECT: mysql
DB_HOST: mysql
DB_PORT: 3306
DB_USER: root
DB_PASS: root
DB_NAME: trae_12306
API_PREFIX: /api/v1
CORS_ORIGIN: http://localhost:5174
depends_on:
mysql:
condition: service_healthy
ports:
- '3000:3000'该示例仅用于后端与数据库的容器化;前端可在宿主机上进行构建与静态服务。若需要前端容器与统一域名,请将 frontend 构建产物挂载到 Nginx 容器并确保前端的 API 地址与后端一致。
- 测试与运行指南:详见
docs/测试与运行指南.md - 后端 SQLite 测试:
npm test --prefix backend - 后端 MySQL 测试:
npm run test:mysql --prefix backend - 前端 E2E(需服务就绪):
- 安装浏览器:
npx playwright install - 启动服务后执行:
npm run test:e2e --prefix frontend
- 安装浏览器:
- 前端单测:
npm run test:unit --prefix frontend -- --coverage - 质量检查:
npm run lint --prefix frontend && npm run typecheck --prefix frontend
- 首页与购票入口、公告与导航
- 用户系统:注册、登录、身份验证、个人中心、常用乘车人管理
- 车票服务:车次查询、余票与时刻表、订票和订单管理
- 支付流程:订单支付、支付状态、退票/改签(模拟)
- 其他:帮助与站点查询(演示数据)
- 里程碑:组件库、核心页面、API、集成与验收(详见
docs/项目计划.md) - 质量保障:ESLint、TypeScript 类型检查、自动化测试(E2E/单测/后端集成)
- 无法访问接口:检查后端是否运行、
/health是否返回 OK、前端代理是否正确 - E2E 登录不稳定:使用统一登录工具
frontend/tests/e2e/utils/auth.ts的ensureLogin(page) - SQLite 并发写锁:已启用 WAL 与
busy_timeout;若仍异常,确认单机负载与并发测试设置 - MySQL 兼容:
users.passenger_type使用中文枚举;order_passengers.phone为VARCHAR(15),超长后端返回 400 - 生产部署注意:前端默认调用
http://127.0.0.1:3000/api/v1,跨域部署时请配置后端CORS_ORIGIN为前端域名,或调整前端API_BASE_URL与 Nginx 代理。
- 代码规范:ESLint、TypeScript 严格模式、组件命名 PascalCase、文件命名 kebab-case
- Git 提交规范:
feat/fix/docs/style/refactor/test/chore - PR 流程:Fork → 分支 → 提交 → 推送 → 发起 PR
- 许可证:MIT(见
LICENSE) - 声明:本项目仅用于学习与研究;UI 与流程参考 12306 网站,版权归相关方所有