1. 饭店预约系统技术栈解析
这套饭店预约系统采用了当前企业级开发中最流行的前后端分离架构。后端基于Java生态中的SpringBoot框架,前端则使用Vue.js渐进式框架,这种组合在2023年StackOverflow开发者调查中被评为最受欢迎的全栈组合之一。
SpringBoot的自动配置特性让我们能快速搭建起包含Spring MVC、Spring Data JPA等组件的RESTful API服务。实测中,仅需基础的application.yml配置就能完成:
yaml复制server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/restaurant
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
前端Vue 3的组合式API写法让表格组件的开发效率提升明显。特别是配合Element Plus的el-table组件,预约列表页的核心代码可以精简到:
vue复制<template>
<el-table :data="reservations">
<el-table-column prop="customerName" label="客户姓名" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="tableSize" label="餐桌大小" />
</el-table>
</template>
2. 数据库设计与核心业务逻辑
餐饮行业的预约业务有着独特的领域模型。经过对三家不同规模餐厅的调研,我们设计了包含以下关键实体的ER图:

其中最复杂的当属餐桌状态管理逻辑。系统需要实时跟踪每个时间段的餐桌占用情况,这涉及到:
java复制@Entity
public class DiningTable {
@Id
@GeneratedValue
private Long id;
@Enumerated(EnumType.STRING)
private TableStatus status; // AVAILABLE, RESERVED, OCCUPIED
@OneToMany(mappedBy = "table")
private List<Reservation> reservations;
}
高峰期并发处理是餐饮系统的痛点。我们在Service层采用了乐观锁机制:
java复制@Transactional
public ReservationResult makeReservation(ReservationRequest request) {
DiningTable table = tableRepo.findById(request.getTableId())
.orElseThrow(() -> new BizException("餐桌不存在"));
if(table.getStatus() != TableStatus.AVAILABLE) {
throw new BizException("该餐桌不可预约");
}
// 使用版本号控制并发
int updated = tableRepo.updateStatus(table.getId(),
TableStatus.RESERVED, table.getVersion());
if(updated == 0) {
throw new ConcurrentModificationException("预约冲突请重试");
}
// 后续预约逻辑...
}
3. 前后端交互关键实现
跨域问题是前后端分离项目的常见坑点。我们在SpringBoot中配置了全局CORS过滤器:
java复制@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(false)
.maxAge(3600);
}
};
}
对于时间参数传递,前后端统一使用ISO8601格式。前端使用dayjs处理:
javascript复制import dayjs from 'dayjs'
const formatReservationTime = (time) => {
return dayjs(time).format('YYYY-MM-DDTHH:mm:ssZ')
}
后端则通过Jackson配置全局日期格式:
java复制@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
builder.serializers(new LocalDateTimeSerializer(
DateTimeFormatter.ISO_DATE_TIME));
};
}
}
4. 系统安全与权限控制
餐饮系统需要区分管理员、服务员和普通顾客三种角色。我们采用Spring Security + JWT的方案:
安全配置核心代码:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/staff/**").hasAnyRole("STAFF", "ADMIN")
.anyRequest().permitAll()
.and()
.addFilter(new JwtAuthFilter(authenticationManager()));
return http.build();
}
}
前端路由守卫实现权限过滤:
javascript复制router.beforeEach((to, from, next) => {
const roles = store.getters.roles
if (to.meta.roles && !to.meta.roles.some(role => roles.includes(role))) {
next('/403')
} else {
next()
}
})
特别注意密码存储必须使用BCrypt加密:
java复制@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
5. 性能优化实战经验
菜单图片等静态资源我们采用CDN加速,Nginx配置示例:
nginx复制location ~* \.(jpg|png|gif)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
proxy_pass http://cdn.example.com;
}
数据库查询优化方面,针对高频访问的今日预约列表,我们添加了复合索引:
sql复制CREATE INDEX idx_reservation_date_status
ON reservation(reservation_date, status);
对于热门餐厅的预约抢购场景,我们使用Redis实现分布式锁:
java复制public boolean tryLock(String key, long expireSeconds) {
String value = UUID.randomUUID().toString();
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(key, value, expireSeconds, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
6. 部署与监控方案
实际生产环境我们推荐使用Docker Compose部署:
dockerfile复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
backend:
build: ./backend
ports:
- "8080:8080"
frontend:
build: ./frontend
ports:
- "80:80"
SpringBoot Actuator提供了健康检查端点:
yaml复制management:
endpoints:
web:
exposure:
include: health,metrics,info
endpoint:
health:
show-details: always
前端错误监控我们接入Sentry:
javascript复制import * as Sentry from "@sentry/vue";
Sentry.init({
dsn: "your_dsn",
integrations: [new BrowserTracing()],
tracesSampleRate: 0.2
});
7. 典型业务场景解决方案
节假日预约高峰期的系统扩容方案:
- 预先进行压力测试,确定单节点QPS上限
- 配置Kubernetes HPA自动扩缩容
- 数据库读写分离,查询走从库
退订处理流程的注意事项:
java复制public void cancelReservation(Long id) {
Reservation reservation = reservationRepo.findById(id)
.orElseThrow(() -> new BizException("预约不存在"));
if (reservation.getStatus() == ReservationStatus.CANCELLED) {
throw new BizException("请勿重复取消");
}
// 释放餐桌
DiningTable table = reservation.getTable();
table.setStatus(TableStatus.AVAILABLE);
// 记录取消原因
reservation.setStatus(ReservationStatus.CANCELLED);
reservation.setCancelTime(LocalDateTime.now());
}
客户迟到处理策略:
- 超过15分钟自动发送短信提醒
- 超过30分钟释放餐桌但保留预约记录
- 频繁迟到客户加入信用观察名单
8. 扩展功能与二次开发建议
与第三方外卖平台对接时,建议:
- 使用Spring Integration实现消息通道
- 订单状态变更采用Webhook回调
- 接口调用增加重试机制
会员积分系统的设计要点:
java复制public class PointsService {
@Transactional
public void addPoints(Long customerId, int points) {
// 防止积分超发
int updated = customerRepo.addPoints(customerId, points);
if (updated == 0) {
throw new OptimisticLockingFailureException("积分更新冲突");
}
// 记录积分流水
PointsLog log = new PointsLog();
log.setCustomerId(customerId);
log.setPoints(points);
log.setType(PointsType.ORDER);
logRepo.save(log);
}
}
数据分析模块可集成:
- 使用Elasticsearch实现预约记录全文检索
- 通过Spring Batch生成每日营业报表
- 利用ECharts可视化客户就餐偏好
