1. 体育馆预约系统架构解析
这个体育馆预约平台采用了当前主流的"前后端分离+微服务"架构模式。后端基于SpringBoot2框架搭建RESTful API服务,前端使用Vue3构建响应式用户界面,两者通过HTTP协议进行数据交互。这种架构的最大优势在于前后端可以并行开发,且前端可以灵活适配多种终端设备。
在技术选型上,我们特别考虑了以下几个关键因素:
- 开发效率:SpringBoot2的自动配置和起步依赖大大减少了样板代码
- 性能需求:MyBatis-Plus的ActiveRecord模式简化了数据库操作
- 用户体验:Vue3的Composition API提供了更好的代码组织和复用
- 数据安全:MySQL8.0新增的窗口函数和JSON支持增强了数据处理能力
提示:在实际部署时,建议将前端项目打包后放在Nginx服务器上,后端服务使用Docker容器化部署,这样既能保证性能又便于扩展。
2. 核心功能模块实现
2.1 用户认证与权限控制
系统采用RBAC(基于角色的访问控制)模型,通过JWT实现无状态认证。核心实现逻辑如下:
java复制// JWT生成示例
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + JWT_EXPIRATION))
.signWith(SignatureAlgorithm.HS512, JWT_SECRET)
.compact();
}
权限控制方面,我们设计了三种角色:
- 普通用户:可查看场馆、预约时段
- 场馆管理员:可管理场馆信息、审核预约
- 系统管理员:拥有全部权限
2.2 预约业务逻辑实现
预约功能的核心难点在于处理并发预约和时段冲突。我们采用了乐观锁机制来解决这个问题:
java复制@Transactional
public BookingResult bookVenue(BookingRequest request) {
// 1. 检查时段是否可用
VenueTimeSlot slot = timeSlotMapper.selectAvailableSlot(
request.getVenueId(),
request.getBookingDate(),
request.getStartTime(),
request.getEndTime());
if (slot == null) {
return BookingResult.fail("该时段已被预约");
}
// 2. 创建订单(乐观锁)
int affected = timeSlotMapper.lockTimeSlot(slot.getId(), slot.getVersion());
if (affected == 0) {
return BookingResult.fail("预约冲突,请重试");
}
// 3. 生成订单
Order order = new Order();
order.setUserId(request.getUserId());
order.setVenueId(request.getVenueId());
// ...其他字段设置
orderMapper.insert(order);
return BookingResult.success(order.getId());
}
3. 数据库设计与优化
3.1 核心表结构解析
除了提供的用户表、场馆表和订单表外,系统还包含以下几个关键表:
时段表(venue_time_slot)
sql复制CREATE TABLE `venue_time_slot` (
`id` bigint NOT NULL AUTO_INCREMENT,
`venue_id` bigint NOT NULL,
`date` date NOT NULL,
`start_time` time NOT NULL,
`end_time` time NOT NULL,
`status` tinyint NOT NULL DEFAULT '1' COMMENT '1-可预约 0-已预约',
`version` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_venue_time` (`venue_id`,`date`,`start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
支付记录表(payment_record)
sql复制CREATE TABLE `payment_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_id` bigint NOT NULL,
`amount` decimal(10,2) NOT NULL,
`payment_method` varchar(20) NOT NULL,
`transaction_id` varchar(64) DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0-未支付 1-支付成功 2-支付失败',
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 查询性能优化
对于高频查询操作,我们采取了以下优化措施:
- 索引优化:为所有外键和查询条件字段建立合适索引
- 读写分离:使用Spring AbstractRoutingDataSource实现
- 缓存策略:Redis缓存热点数据,如场馆信息、热门时段等
java复制// 缓存示例
public Venue getVenueById(Long id) {
String cacheKey = "venue:" + id;
Venue venue = redisTemplate.opsForValue().get(cacheKey);
if (venue == null) {
venue = venueMapper.selectById(id);
if (venue != null) {
redisTemplate.opsForValue().set(cacheKey, venue, 30, TimeUnit.MINUTES);
}
}
return venue;
}
4. 前端关键实现
4.1 响应式布局设计
使用Vue3 + Element Plus构建适配多终端的界面:
vue复制<template>
<el-container>
<el-header>
<app-header :user="user" @logout="handleLogout" />
</el-header>
<el-main>
<router-view />
</el-main>
<el-footer>
<app-footer />
</el-footer>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import AppHeader from './components/Header.vue'
import AppFooter from './components/Footer.vue'
const router = useRouter()
const user = ref(null)
const handleLogout = () => {
localStorage.removeItem('token')
router.push('/login')
}
onMounted(async () => {
try {
const { data } = await getUserInfo()
user.value = data
} catch (error) {
console.error('获取用户信息失败', error)
}
})
</script>
4.2 预约日历组件
核心预约功能通过自定义日历组件实现:
vue复制<template>
<div class="booking-calendar">
<div class="calendar-header">
<button @click="prevMonth">上个月</button>
<h2>{{ currentYear }}年{{ currentMonth }}月</h2>
<button @click="nextMonth">下个月</button>
</div>
<div class="calendar-grid">
<div v-for="day in days" :key="day.date"
:class="['day-cell', { 'disabled': !day.available }]"
@click="selectDay(day)">
{{ day.date.getDate() }}
<div v-if="day.slots.length" class="slot-indicator">
{{ day.slots.length }}个时段
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
const props = defineProps({
venueId: {
type: Number,
required: true
}
})
const currentDate = ref(new Date())
// ...其他逻辑
</script>
5. 系统部署方案
5.1 开发环境配置
推荐使用以下开发工具栈:
- 后端:JDK17 + IntelliJ IDEA + Lombok插件
- 前端:Node.js 16+ + VSCode + Volar插件
- 数据库:MySQL8.0 + Redis6.2
- 构建工具:Maven3.8 + npm8.x
5.2 生产环境部署
我们采用Docker Compose进行容器化部署:
yaml复制version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_URL=jdbc:mysql://mysql:3306/booking
- REDIS_HOST=redis
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=booking
volumes:
- mysql-data:/var/lib/mysql
redis:
image: redis:6.2-alpine
volumes:
mysql-data:
6. 常见问题与解决方案
6.1 并发预约冲突
问题现象:多个用户同时预约同一时段时出现超卖
解决方案:
- 数据库层面使用乐观锁
- 业务层使用分布式锁(Redisson)
- 前端限制频繁点击
java复制// Redisson分布式锁示例
public BookingResult bookWithDistributedLock(BookingRequest request) {
RLock lock = redissonClient.getLock("venue:" + request.getVenueId());
try {
boolean acquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (acquired) {
return bookVenue(request);
}
return BookingResult.fail("系统繁忙,请稍后再试");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return BookingResult.fail("预约失败");
} finally {
lock.unlock();
}
}
6.2 支付状态同步
问题现象:第三方支付回调延迟导致订单状态不同步
解决方案:
- 实现支付状态轮询机制
- 设置订单过期时间(30分钟未支付自动取消)
- 使用消息队列处理支付结果
java复制// 支付状态检查任务
@Scheduled(fixedRate = 60000)
public void checkPaymentStatus() {
List<Order> unpaidOrders = orderMapper.selectUnpaidOrders();
unpaidOrders.forEach(order -> {
PaymentStatus status = paymentService.queryPaymentStatus(order.getId());
if (status == PaymentStatus.SUCCESS) {
orderMapper.updatePaymentStatus(order.getId(), 1);
// 释放锁定时段
timeSlotMapper.releaseSlot(order.getVenueId(), order.getBookingDate());
} else if (order.getCreateTime().before(thirtyMinutesAgo())) {
orderMapper.cancelOrder(order.getId());
timeSlotMapper.releaseSlot(order.getVenueId(), order.getBookingDate());
}
});
}
7. 项目扩展方向
在实际开发中,我们可以考虑以下几个扩展点来增强系统功能:
- 智能推荐:基于用户历史预约数据推荐相似场馆
- 社交功能:添加约球、比赛组织等社交元素
- 数据分析:生成场馆使用率报表和用户行为分析
- 物联网集成:对接智能门禁系统实现扫码入场
java复制// 智能推荐示例
public List<Venue> recommendVenues(Long userId) {
// 1. 获取用户历史预约
List<BookingHistory> history = bookingMapper.selectUserHistory(userId);
// 2. 提取特征(场馆类型、时段偏好等)
Set<String> features = extractFeatures(history);
// 3. 基于特征查询相似场馆
return venueMapper.selectByFeatures(new ArrayList<>(features));
}
这个体育馆预约系统从技术选型到架构设计都采用了当前主流的技术栈,既保证了开发效率又兼顾了系统性能。在实际部署时,建议根据具体场馆规模调整数据库和缓存配置,大型场馆可以考虑引入分库分表策略。