系统架构与核心技术选型
本系统采用前后端分离架构,前端负责用户界面交互,后端提供数据接口,数据库存储所有核心信息。技术栈选择成熟、文档齐全的开源方案,确保零门槛部署。
技术栈清单
- 前端框架: Vue.js 3.x + Element Plus UI 库
- 后端框架: Node.js + Express.js
- 数据库: MySQL 8.0
- 地图服务: 高德地图JavaScript API
- 部署工具: PM2 (进程管理)
环境准备与项目初始化
在开始编码前,需要完成开发环境的搭建。
开发环境安装
打开命令行终端,依次执行以下命令安装Node.js和MySQL。
1. 安装Node.js与npm
访问Node.js官网下载页面:https://nodejs.org/,下载并安装LTS版本。安装完成后,在终端验证:
```
node -v
npm -v
```
正确安装会显示版本号,如 v18.x.x 和 9.x.x。
2. 安装并配置MySQL数据库
访问MySQL社区版下载页面:https://dev.mysql.com/downloads/mysql/,根据操作系统下载安装。安装过程中会提示设置root用户密码,请务必记住。
安装后启动MySQL服务,并登录创建本系统所需数据库:
```
mysql -u root -p
```
输入密码后,执行以下SQL语句:
```
CREATE DATABASE archive_training_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE archive_training_system;
```
数据库设计与建表
系统核心是存储考试地点信息与预约记录。执行以下SQL语句创建数据表。
1. 创建考试地点表 (exam_location)
```
CREATE TABLE exam_location (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL COMMENT '地点名称,如:XX市档案局培训中心',
address VARCHAR(255) NOT NULL COMMENT '详细地址',
longitude DECIMAL(10, 7) NOT NULL COMMENT '经度,用于地图定位',
latitude DECIMAL(10, 7) NOT NULL COMMENT '纬度,用于地图定位',
contact_phone VARCHAR(20) COMMENT '联系电话',
capacity INT DEFAULT 0 COMMENT '该考点可容纳的最大考试人数',
description TEXT COMMENT '考点描述、注意事项',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
2. 创建培训考试场次表 (exam_session)
```
CREATE TABLE exam_session (
id INT PRIMARY KEY AUTO_INCREMENT,
location_id INT NOT NULL,
exam_date DATE NOT NULL COMMENT '考试日期',
start_time TIME NOT NULL COMMENT '考试开始时间',
end_time TIME NOT NULL COMMENT '考试结束时间',
total_seats INT NOT NULL COMMENT '本场次总座位数',
booked_seats INT DEFAULT 0 COMMENT '已被预约的座位数',
status ENUM('open', 'full', 'cancelled') DEFAULT 'open' COMMENT '场次状态',
FOREIGN KEY (location_id) REFERENCES exam_location(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
3. 创建预约记录表 (booking_record)
```
CREATE TABLE booking_record (
id INT PRIMARY KEY AUTO_INCREMENT,
session_id INT NOT NULL,
user_id_card VARCHAR(18) NOT NULL COMMENT '考生身份证号',
user_name VARCHAR(50) NOT NULL COMMENT '考生姓名',
user_phone VARCHAR(20) NOT NULL COMMENT '考生手机号',
booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '预约时间',
status ENUM('confirmed', 'cancelled') DEFAULT 'confirmed',
FOREIGN KEY (session_id) REFERENCES exam_session(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
后端API服务搭建
在项目根目录下,创建 server 文件夹,并初始化后端项目。
1. 初始化后端项目
在终端中进入 server 目录,执行:
```
npm init -y
npm install express mysql2 cors dotenv
```
2. 创建数据库连接配置文件
在 server 目录下创建 .env 文件,填入你的数据库信息:
```
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_mysql_root_password
DB_DATABASE=archive_training_system
PORT=3000
```
将 your_mysql_root_password 替换为你安装MySQL时设置的root密码。
3. 创建主应用文件 app.js

在 server 目录下创建 app.js,写入以下完整代码:
```
const express = require('express');
const mysql = require('mysql2/promise');
const cors = require('cors');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.json());
// 创建数据库连接池
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// API 1: 获取所有考试地点
app.get('/api/locations', async (req, res) => {
try {
const [rows] = await pool.query('SELECT FROM exam_location');
res.json({ success: true, data: rows });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// API 2: 根据地点ID获取可预约的考试场次
app.get('/api/sessions/:locationId', async (req, res) => {
const { locationId } = req.params;
const { date } = req.query; // 可选,查询指定日期的场次
let sql = `SELECT FROM exam_session WHERE location_id = ? AND status = 'open' AND total_seats > booked_seats`;
let params = [locationId];
if (date) {
sql += ` AND exam_date = ?`;
params.push(date);
}
sql += ` ORDER BY exam_date, start_time`;
try {
const [rows] = await pool.query(sql, params);
res.json({ success: true, data: rows });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
});
// API 3: 提交预约
app.post('/api/booking', async (req, res) => {
const { session_id, user_id_card, user_name, user_phone } = req.body;
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// 检查场次是否可预约
const [session] = await connection.query('SELECT FROM exam_session WHERE id = ? AND status = "open" FOR UPDATE', [session_id]);
if (session.length === 0) {
throw new Error('考试场次不存在或已关闭');
}
if (session[0].booked_seats >= session[0].total_seats) {
throw new Error('该场次已满员');
}
// 插入预约记录
await connection.query(
'INSERT INTO booking_record (session_id, user_id_card, user_name, user_phone) VALUES (?, ?, ?, ?)',
[session_id, user_id_card, user_name, user_phone]
);
// 更新场次已预约人数
await connection.query('UPDATE exam_session SET booked_seats = booked_seats + 1 WHERE id = ?', [session_id]);
await connection.commit();
res.json({ success: true, message: '预约成功' });
} catch (error) {
await connection.rollback();
res.status(400).json({ success: false, message: error.message });
} finally {
connection.release();
}
});
app.listen(process.env.PORT, () => {
console.log(`后端服务运行在 http://localhost:${process.env.PORT}`);
});
```
4. 启动后端服务
在 server 目录下执行:
```
node app.js
```
终端显示“后端服务运行在 http://localhost:3000”表示启动成功。
前端界面开发
在项目根目录下,创建 client 文件夹,用于存放前端代码。
1. 使用Vite快速创建Vue项目
在终端中进入项目根目录,执行:
```
npm create vue@latest client
```
创建过程中,根据提示选择以下配置:
- Vue Router: No (本项目单页,暂不需要)
- Pinia: No
- ESLint: No (为简化流程)
创建完成后,进入 client 目录,安装Element Plus和Axios:
```
cd client
npm install element-plus axios
```
2. 修改主入口文件
打开 client/src/main.js,修改为以下内容:
```
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(ElementPlus).mount('app')
```
3. 开发核心页面组件
替换 client/src/App.vue 的原有内容为以下完整代码:
```
档案培训考试地点查询与预约系统
{{ loc.name }}
{{ loc.address }}
联系电话:{{ loc.contact_phone }}
容量:{{ loc.capacity }}人
可预约场次
加载中...
该日期暂无可用场次
时间:{{ session.exam_date }} {{ session.start_time }} - {{ session.end_time }}
剩余座位:{{ session.total_seats - session.booked_seats }}
立即预约
请从左侧选择一个考试地点