1. 项目背景与核心需求
网上订餐系统作为典型的O2O应用场景,已经成为餐饮行业数字化转型的基础设施。这个SpringBoot+Vue的全栈项目需要解决三个核心问题:
- 业务并发处理:午晚高峰时段需要支撑每秒50+订单的并发创建能力
- 实时状态同步:从后厨制作到骑手配送的全流程状态更新延迟需控制在3秒内
- 多端一致性:Web、H5、小程序等多终端的数据展示与交互逻辑统一
我去年参与过一个日订单量2万+的餐饮系统重构,发现这类系统最关键的其实不是功能复杂度,而是订单状态的强一致性保证。很多团队在开发初期只关注CRUD实现,等真正上线后才会发现状态同步问题导致的客诉。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型分析
2.1 后端技术栈
选择SpringBoot作为后端框架主要基于以下考量:
- 自动装配机制:通过@SpringBootApplication注解快速整合MyBatis、Redis等组件
- 嵌入式Tomcat:省去外部容器部署复杂度,开发阶段直接运行main方法
- Starter生态:使用spring-boot-starter-websocket实现订单状态推送
- 健康检查:配合Actuator实现服务监控
关键配置示例(application.yml):
yaml复制spring:
websocket:
allowed-origins: "*"
redis:
host: 127.0.0.1
lettuce:
pool:
max-active: 8 # 根据压测结果调整
2.2 前端技术栈
Vue3组合式API相比选项式API更适合订餐系统的特点:
- 订单看板:使用Composition API封装状态管理逻辑
- 实时推送:WebSocket连接建议封装为自定义Hook
- 性能优化:
- 菜品列表采用虚拟滚动(vue-virtual-scroller)
- 图片懒加载(v-lazy指令)
典型组件结构:
javascript复制// 订单卡片组件
export default {
setup() {
const { orderStatus } = useWebSocket()
return { orderStatus }
}
}
3. 核心模块设计
3.1 订单状态机设计
这是系统最复杂的部分,需要明确定义状态流转规则:
mermaid复制stateDiagram-v2
[*] --> 待支付
待支付 --> 已取消: 超时30分钟
待支付 --> 已支付: 成功付款
已支付 --> 制作中: 厨房接单
制作中 --> 配送中: 出餐完成
配送中 --> 已完成: 用户签收
实际开发中建议采用状态模式(State Pattern)实现:
java复制public interface OrderState {
void handle(OrderContext context);
}
@Component
@Scope("prototype")
public class PaidState implements OrderState {
@Override
public void handle(OrderContext context) {
// 触发厨房打印小票逻辑
}
}
3.2 实时通信方案
对比三种方案后,我们最终选择方案三:
| 方案 | 延迟 | 开发成本 | 兼容性 |
|---|---|---|---|
| 短轮询 | 高 | 低 | 高 |
| SSE | 中 | 中 | 中 |
| WebSocket+STOMP | 低 | 高 | 高 |
STOMP配置关键代码:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
4. 性能优化实践
4.1 缓存策略设计
采用多级缓存架构:
- 本地缓存:Caffeine缓存热门菜品(TTL=5分钟)
- 分布式缓存:Redis缓存店铺信息(TTL=1小时)
- 缓存穿透:使用布隆过滤器拦截无效ID查询
缓存更新策略对比:
- 方案A:定时全量刷新 → 简单但资源浪费
- 方案B:消息队列触发更新 → 复杂但精准
- 方案C:结合TTL+懒加载 → 折中方案
我们最终选择方案C的实现:
java复制@Cacheable(value = "dishes", key = "#id", unless = "#result == null")
public Dish getDishById(Long id) {
// 数据库查询
}
4.2 数据库优化
针对订单表的高频查询操作:
- 读写分离:使用Sharding-JDBC配置
- 索引优化:组合索引(status, create_time)
- 冷热分离:3个月前的订单归档到历史表
分库分表示例配置:
yaml复制spring:
shardingsphere:
datasource:
names: ds0,ds1
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
5. 安全防护措施
5.1 支付安全
- 防重放攻击:每次支付请求必须携带唯一nonce
- 金额校验:前端传参与后台计算金额比对
- 签名验证:使用HMAC-SHA256签名机制
支付流程校验逻辑:
java复制public boolean verifyPayment(PaymentRequest request) {
String localSign = HmacUtil.sign(request.getOrderNo()+request.getAmount());
return localSign.equals(request.getSign());
}
5.2 接口防护
- Rate Limiter:Guava RateLimiter控制并发
- 参数过滤:使用Jackson的@JsonFilter
- XSS防御:自定义HttpMessageConverter处理HTML转义
防御配置示例:
java复制@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
return registration;
}
6. 部署架构方案
6.1 容器化部署
Docker Compose文件关键配置:
yaml复制services:
app:
image: openjdk:11-jre
environment:
- SPRING_PROFILES_ACTIVE=prod
ports:
- "8080:8080"
depends_on:
- redis
- mysql
mysql:
image: mysql:5.7
volumes:
- ./mysql/data:/var/lib/mysql
6.2 监控方案
Prometheus监控指标配置示例:
yaml复制metrics:
tags:
application: ${spring.application.name}
management:
endpoints:
web:
exposure:
include: "*"
在真实生产环境中,我们还需要考虑灰度发布方案。通过Nginx配置可以实现:
nginx复制upstream backend {
server 172.17.0.1:8080 weight=9;
server 172.17.0.1:8081 weight=1;
}
这个项目让我深刻体会到,订餐系统最难的不是技术实现,而是对餐饮业务的理解。比如最初我们设计的超时取消逻辑是固定30分钟,但实际运营中发现下午茶时段应该延长到45分钟。技术方案必须保持足够的灵活性来适应业务变化
