1. 项目概述
这个智能餐厅管理系统是我去年带队完成的一个商业项目,当时客户是连锁餐饮品牌"鲜味坊",他们需要一套能整合线上线下点餐、库存管理、会员营销的解决方案。经过3个月开发,我们基于SpringBoot+Vue技术栈实现了这套系统,上线后帮助客户提升了37%的翻台率和23%的客单价。
系统最核心的价值在于通过算法实现了三个智能化:
- 动态菜单推荐(根据时段、库存、用户画像)
- 后厨自动排单(基于菜品制作时长和订单紧急度)
- 实时库存预警(联动供应商系统)
2. 技术架构设计
2.1 整体架构
采用经典的三层架构,但做了针对性优化:
code复制表现层:Vue3 + Element Plus
↓ HTTP/WebSocket
业务层:SpringBoot 2.7 + Spring Security
↓ JPA/Hibernate
数据层:MySQL 8.0(主)+ Redis 7.0(缓存)
特别说明几个关键设计点:
- 使用WebSocket实现后厨大屏的实时订单推送
- 采用Redisson分布式锁处理高并发下单
- 菜单推荐服务单独部署为微服务
2.2 技术选型对比
我们评估过几个备选方案:
- 数据库:MySQL vs PostgreSQL
- 最终选择MySQL因为客户运维团队更熟悉
- 缓存:Redis vs Memcached
- 选用Redis因其支持更丰富的数据结构
- 前端:Vue vs React
- 选择Vue3因其更易上手且生态完善
3. 核心模块实现
3.1 智能点餐模块
java复制// 核心推荐算法伪代码
public List<Dish> recommendDishes(User user) {
// 1. 基于用户历史订单的协同过滤
List<Dish> cfRecommend = cfService.recommend(user.getId());
// 2. 基于当前库存的实时过滤
List<Dish> availableDishes = inventoryService.filterAvailable(cfRecommend);
// 3. 基于时段的热销加权
return rankingService.timeWeighting(availableDishes);
}
遇到的坑:
- 初期没考虑库存实时性,导致推荐了已售罄菜品
- 解决方案:引入Redis缓存库存状态,每5秒更新
3.2 后厨调度系统
关键数据结构:
sql复制CREATE TABLE kitchen_task (
id BIGINT PRIMARY KEY,
order_id BIGINT,
dish_id INT,
status ENUM('PENDING','COOKING','COMPLETED'),
priority TINYINT COMMENT '1-5级优先级',
start_time DATETIME,
estimated_duration INT COMMENT '预计制作时长(秒)'
);
调度算法要点:
- 优先处理加急订单(priority=5)
- 相同优先级按FIFO
- 自动计算预计出餐时间
3.3 库存预警模块
配置示例(application.yml):
yaml复制inventory:
warning:
thresholds:
meat: 10% # 剩余10%时预警
vegetable: 15%
seafood: 8% # 海鲜类需要更高安全库存
notify:
email: purchase@example.com
sms: 13800138000
4. 性能优化实践
4.1 数据库优化
-
索引优化:
- 为order表的create_time、status字段添加联合索引
- 使用覆盖索引优化菜品查询
-
查询优化:
java复制// 错误示例 - N+1查询问题 List<Order> orders = orderRepository.findAll(); orders.forEach(order -> { order.getItems(); // 触发延迟加载 }); // 正确做法 - 使用JOIN FETCH @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.createTime > :date") List<Order> findRecentOrders(@Param("date") LocalDateTime date);
4.2 缓存策略
采用多级缓存架构:
- 本地缓存(Caffeine):存储静态数据如菜品分类
- Redis缓存:
- 订单状态(过期时间30分钟)
- 库存余量(过期时间5秒)
- 缓存击穿防护:
java复制public Dish getDishById(Long id) { String key = "dish:" + id; Dish dish = redisTemplate.opsForValue().get(key); if (dish == null) { synchronized (this) { dish = redisTemplate.opsForValue().get(key); if (dish == null) { dish = dishRepository.findById(id).orElseThrow(); redisTemplate.opsForValue().set(key, dish, 1, TimeUnit.HOURS); } } } return dish; }
5. 安全防护措施
5.1 防XSS攻击
前端:
javascript复制// 使用DOMPurify过滤输入
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
后端:
java复制@PostMapping("/comment")
public ResponseEntity<?> addComment(@RequestBody @Validated CommentDTO dto) {
String safeContent = HtmlUtils.htmlEscape(dto.getContent());
// 后续处理...
}
5.2 接口安全
-
采用JWT认证
-
敏感接口增加频控:
java复制@RateLimiter(value = 10, key = "#userId") @PostMapping("/coupon/apply") public ResponseEntity<?> applyCoupon(@RequestParam Long userId) { // ... } -
SQL注入防护:
- 严格使用预编译语句
- 禁止拼接SQL
6. 部署方案
6.1 容器化部署
Dockerfile示例:
dockerfile复制FROM openjdk:17-jdk-slim
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
docker-compose.yml关键配置:
yaml复制services:
app:
image: restaurant-system:1.0
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
redis:
image: redis:7.0-alpine
ports:
- "6379:6379"
6.2 高可用方案
- 前端:Nginx负载均衡 + CDN加速
- 后端:Kubernetes集群部署
- 3个Pod实例
- HPA自动扩缩容
- 数据库:主从复制+读写分离
7. 踩坑实录
-
SpringBoot多模块项目打包问题:
- 现象:子模块依赖找不到
- 解决:在父pom中添加:
xml复制<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>exec</classifier> </configuration> </plugin> -
Redis缓存穿透:
- 现象:频繁查询不存在的菜品ID
- 解决:布隆过滤器+空值缓存
-
分布式事务问题:
- 现象:下单减库存不一致
- 解决:引入Seata AT模式
8. 扩展建议
-
可增加的功能:
- 人脸识别支付
- 智能排班系统
- 供应商协同平台
-
性能监控方案:
java复制@Bean public MeterRegistryCustomizer<PrometheusMeterRegistry> metrics() { return registry -> registry.config().commonTags("application", "restaurant-system"); } -
灰度发布策略:
- 基于Header的路由
- 使用SpringCloud Gateway实现
这套系统目前已在12家门店稳定运行,最大的收获是认识到架构设计必须考虑实际业务场景。比如我们最初设计的复杂推荐算法,在实际运行中发现简单规则+实时库存的效果反而更好。
