高校自习室作为学生自主学习的重要场所,其管理效率直接影响着学生的学习体验。传统的人工管理模式存在诸多痛点:预约流程繁琐(需要现场排队或电话预约)、座位信息不透明(无法实时查看空位情况)、资源分配不合理(高峰时段座位紧张而其他时段闲置)。这些问题导致学生经常需要花费大量时间寻找座位,而管理人员也疲于应对各种协调工作。
我在实际调研中发现,某高校图书馆自习室在工作日晚间的座位使用率高达120%(存在占座现象),而周末白天却只有30%的利用率。这种资源分配不均的情况促使我们开发这套智能化管理系统。
关键需求指标:
- 支持至少500人同时在线预约
- 响应时间控制在1秒以内
- 实现95%以上的座位利用率
- 提供移动端和Web端双平台访问
系统采用前后端分离的微服务架构,主要技术栈如下:
code复制前端:Vue.js + Element UI + Axios
后端:Spring Boot 2.7 + Spring Security + MyBatis Plus
数据库:MySQL 8.0 + Redis缓存
部署:Docker + Nginx负载均衡
这种架构选择基于以下考虑:
核心表关系图如下:
sql复制-- 自习室表
CREATE TABLE `study_room` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`capacity` int NOT NULL,
`status` tinyint DEFAULT '1' COMMENT '0-关闭 1-开放',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 座位表
CREATE TABLE `seat` (
`id` int NOT NULL AUTO_INCREMENT,
`room_id` int NOT NULL,
`number` varchar(10) NOT NULL,
`type` tinyint DEFAULT '0' COMMENT '0-普通 1-静音区',
PRIMARY KEY (`id`),
KEY `idx_room` (`room_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 预约记录表
CREATE TABLE `reservation` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`seat_id` int NOT NULL,
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
`status` tinyint DEFAULT '0' COMMENT '0-待使用 1-使用中 2-已完成',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_time` (`start_time`,`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
特别注意的索引设计:
预约模块采用时间片分割算法,将每天划分为48个30分钟的时间段。核心预约逻辑如下:
java复制public Result makeReservation(ReservationDTO dto) {
// 校验时间有效性
if(!TimeUtil.isValidTimeSlot(dto.getStartTime(), dto.getEndTime())){
return Result.error("无效的时间段");
}
// 检查冲突预约
List<Reservation> conflicts = reservationMapper.checkConflict(
dto.getSeatId(), dto.getStartTime(), dto.getEndTime());
if(!conflicts.isEmpty()){
return Result.error("该时段已被预约");
}
// 创建预约记录
Reservation reservation = new Reservation();
BeanUtils.copyProperties(dto, reservation);
reservation.setStatus(0);
reservationMapper.insert(reservation);
// 设置Redis缓存
String key = "seat:" + dto.getSeatId() + ":"
+ TimeUtil.toSlot(dto.getStartTime());
redisTemplate.opsForValue().set(key, "reserved", 30, TimeUnit.MINUTES);
return Result.success(reservation.getId());
}
使用WebSocket实现座位状态实时更新:
javascript复制// 前端WebSocket连接
const socket = new WebSocket('wss://yourdomain.com/ws');
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if(data.type === 'seatUpdate') {
// 更新UI中的座位状态
this.updateSeatStatus(data.seatId, data.status);
}
};
// 后端推送逻辑
@GetMapping("/pushSeatStatus")
public void pushSeatStatus(@RequestParam int seatId,
@RequestParam int status) {
simpMessagingTemplate.convertAndSend(
"/topic/seatUpdate",
new SeatMessage(seatId, status));
}
针对选课高峰期的并发问题,我们采用以下方案:
java复制public boolean tryLock(String key, long expire) {
return redisTemplate.opsForValue()
.setIfAbsent(key, "1", expire, TimeUnit.SECONDS);
}
java复制@RabbitListener(queues = "reservation.queue")
public void processReservation(ReservationDTO dto) {
// 处理预约逻辑
}
java复制@Scheduled(fixedRate = 60000) // 每分钟检查一次
public void checkTimeoutReservations() {
List<Reservation> timeoutList = reservationMapper
.selectTimeoutReservations(LocalDateTime.now());
timeoutList.forEach(res -> {
res.setStatus(3); // 超时状态
reservationMapper.updateById(res);
releaseSeat(res.getSeatId());
});
}
使用JMeter模拟500并发用户:
| 测试场景 | 平均响应时间 | 错误率 | TPS |
|---|---|---|---|
| 查询座位 | 238ms | 0% | 1250 |
| 提交预约 | 512ms | 1.2% | 680 |
| 并发预约 | 1.2s | 3.5% | 420 |
优化措施:
采用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
部署注意事项:
在实际开发过程中,有几个关键经验值得分享:
java复制public void updateSeat(Seat seat) {
// 第一次删除缓存
redis.delete("seat:" + seat.getId());
// 更新数据库
seatMapper.updateById(seat);
// 延迟1秒后再次删除
executor.schedule(() -> {
redis.delete("seat:" + seat.getId());
}, 1, TimeUnit.SECONDS);
}
vue复制<virtual-scroller :items="seats" item-height="50">
<template v-slot="{ item }">
<seat-item :data="item" />
</template>
</virtual-scroller>
这个项目从技术角度实现了完整的全栈开发流程,特别在解决高并发场景下的系统设计方面积累了宝贵经验。对于想要学习Spring Boot+Vue技术栈的同学,建议从简单的CRUD功能开始,逐步添加复杂功能模块。