1. 项目背景与核心价值
每到中午11:50,校园广播响起下课铃声的那一刻,教学楼到食堂的通道就会上演一场"百米冲刺"。作为经历过四年大学食堂洗礼的过来人,我深知传统食堂的三大痛点:窗口前永远排不完的长队、热门菜品总是早早售罄、备餐量永远在"不够卖"和"浪费"之间摇摆。这促使我开发了这套基于SpringBoot的智慧餐厅系统,用技术手段重构校园就餐流程。
这套系统的创新点在于将"档口菜单→学生点餐→后厨备餐→订单配送→评价反馈"全流程数字化。实测数据显示,在已落地的试点食堂中,学生平均就餐等待时间从25分钟降至8分钟,档口食材浪费率降低62%。其核心价值体现在三个维度:
对学生而言:通过手机端实时查看菜单详情(含食材、口味、图片)、提前下单支付、定时取餐,把排队时间转化为学习或休息时间。系统支持的收藏夹和菜品评价功能,还能帮助学生快速找到符合口味的餐品。
对商家而言:后台的销量预测看板和库存预警功能,让备餐量更精准。某麻辣香锅档口老板反馈,使用系统后每日食材采购成本降低30%,因为可以根据前3天的订单数据动态调整次日备货量。
对食堂管理方而言:系统自动生成的"菜品热度排行榜"和"窗口服务评分",为优化食堂运营提供数据支撑。例如通过分析发现,原以为畅销的红烧肉在周三午餐时段下单量骤减50%,调查后发现是因周三上午体育课后学生更倾向选择清淡菜品。
2. 技术架构设计解析
2.1 整体技术选型
系统采用经典的B/S架构,前端使用Vue.js+ElementUI实现响应式布局,后端基于SpringBoot 2.7.3构建,数据库选用MySQL 8.0。这个技术组合的决策依据是:
SpringBoot的优势:自动配置特性大幅减少XML配置,内嵌Tomcat简化部署,Starter依赖管理让整合MyBatis-Plus、Redis等组件变得简单。实测从零搭建基础框架仅需15分钟。
Vue.js的考量:双向数据绑定和组件化开发特别适合频繁交互的点餐场景。例如购物车模块采用Vuex管理状态,在商品增减时能实时计算总价,代码量比传统jQuery方案减少40%。
MySQL版本选择:8.0版对JSON字段的原生支持(如存储菜品口味选项)、窗口函数(用于销量排行统计)等特性,比5.7版更适合餐饮业务场景。通过EXPLAIN分析优化后,高峰期订单查询响应时间稳定在200ms以内。
2.2 核心架构设计
系统采用分层架构设计,各层职责分明:
┌───────────────────────────────────────┐ │ 表现层 │ │ Vue前端(用户端/商家端/管理端) │ └───────────────┬───────────────────────┘ │ HTTP/HTTPS ┌───────────────▼───────────────────────┐ │ 控制层 │ │ Spring MVC (Restful API接口) │ └───────────────┬───────────────────────┘ │ 方法调用 ┌───────────────▼───────────────────────┐ │ 业务层 │ │ Service (订单处理/库存管理逻辑) │ └───────────────┬───────────────────────┘ │ DAO调用 ┌───────────────▼───────────────────────┐ │ 持久层 │ │ MyBatis-Plus (ORM操作) │ └───────────────┬───────────────────────┘ │ JDBC ┌───────────────▼───────────────────────┐ │ 数据层 │ │ MySQL (主从复制+读写分离) │ └───────────────────────────────────────┘2.3 关键设计决策
- 多租户设计方案:采用schema隔离方式,每个食堂商户有独立的数据库schema。这样既保证数据隔离,又方便做跨商户的聚合统计。在商户管理Service中通过动态数据源切换实现:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DBContextHolder.get(); // 从ThreadLocal获取当前商户标识 } }- 高并发订单处理:使用Redis实现三级缓冲:
- 第一层:商品库存缓存(Redis String)
- 第二层:订单队列(Redis List)
- 第三层:分布式锁(Redisson)防止超卖
当用户提交订单时,系统先检查Redis库存,充足则生成预订单进入队列,最后由后台服务异步处理。这套方案在模拟测试中可支撑3000+ TPS。
3. 核心功能实现细节
3.1 智能菜单管理模块
3.1.1 动态菜品分类
采用字典表设计实现可配置的分类体系,核心表结构:
CREATE TABLE `food_category` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL COMMENT '分类名称', `sort` int DEFAULT '0' COMMENT '展示顺序', `merchant_id` int NOT NULL COMMENT '关联商户', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_merchant` (`merchant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;业务逻辑亮点:
- 分类支持拖拽排序,前端使用Sortable.js实现,排序结果通过AJAX实时保存
- 启用/禁用状态控制:当分类下无有效菜品时自动灰显
- 分类热度统计:基于菜品点击量的加权计算
3.1.2 菜品信息管理
菜品表设计充分考虑扩展性,关键字段包括:
CREATE TABLE `food_info` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '菜品名称', `category_id` int NOT NULL COMMENT '分类ID', `price` decimal(10,2) NOT NULL COMMENT '售价', `cost` decimal(10,2) DEFAULT NULL COMMENT '成本价', `stock` int NOT NULL DEFAULT '0' COMMENT '库存', `tags` json DEFAULT NULL COMMENT '口味标签', `images` json DEFAULT NULL COMMENT '图片JSON数组', `detail` text COMMENT '详情HTML', `sales` int DEFAULT '0' COMMENT '销量', `status` tinyint DEFAULT '1' COMMENT '1上架 0下架', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;特色实现:
- 图片处理:使用Thumbnailator生成三种尺寸缩略图(800x600详情图、300x200列表图、100x100缩略图)
- 口味标签:JSON格式存储如
["微辣","免葱","少盐"],前端通过v-for动态渲染 - 智能推荐:基于用户历史订单的协同过滤算法,在菜品详情页展示"喜欢该菜品的用户还买了..."
3.2 购物车与订单系统
3.2.1 购物车设计
采用Redis Hash+MySQL持久化方案:
- 临时购物车(未登录用户):存在Redis,key为sessionId
- 用户购物车(已登录):同步到MySQL,表结构如下:
CREATE TABLE `cart` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `food_id` int NOT NULL, `quantity` int NOT NULL DEFAULT '1', `selected` tinyint DEFAULT '1' COMMENT '是否选中', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_food` (`user_id`,`food_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;并发控制:使用Redis的WATCH+MULTI实现乐观锁,防止重复提交
3.2.2 订单状态机
订单生命周期管理采用状态模式:
public interface OrderState { void pay(Order order); void cancel(Order order); void deliver(Order order); void receive(Order order); } // 具体实现如: public class PendingPaymentState implements OrderState { @Override public void pay(Order order) { // 扣减库存 // 更改状态为待发货 order.setState(new PaidState()); } }状态流转示意图:
待支付 ──支付──> 待发货 ──发货──> 待收货 │ │ └──取消──> 已取消 └──取消──> 退款中3.3 智能调度看板
3.3.1 实时数据统计
使用Spring Scheduler+ECharts实现:
- 每5分钟统计一次各窗口订单量
- 热力图展示各时段订单密度
- 异常检测:当某时段订单量突降50%以上触发告警
核心统计SQL示例:
SELECT DATE_FORMAT(create_time,'%H:00') AS hour, COUNT(*) AS order_count, SUM(total_amount) AS revenue FROM orders WHERE merchant_id = #{merchantId} GROUP BY hour ORDER BY hour;3.3.2 备餐建议算法
基于历史数据的加权预测模型:
今日建议备餐量 = (上周同日销量 * 0.4) + (前三日平均销量 * 0.3) + (昨日销量 * 0.2) + (天气系数 * 0.1)天气系数通过对接气象API获取,雨天通常增加15%主食类备货。
4. 开发实战经验总结
4.1 性能优化要点
缓存策略:
- 菜单数据:Redis缓存,30分钟过期
- 用户信息:Caffeine本地缓存,LRU策略
- 注意缓存击穿问题:使用互斥锁重建缓存
SQL优化案例:
- 订单分页查询:避免
SELECT *,只取必要字段 - 建立组合索引:
(user_id, status)加速我的订单查询 - 统计查询:使用物化视图预计算
- 订单分页查询:避免
前端性能:
- 图片懒加载:Intersection Observer API
- 组件异步加载:Vue的defineAsyncComponent
- Webpack分包优化:vendor包分离
4.2 典型问题排查
问题1:高峰期出现库存超卖
- 现象:多人同时下单同一商品,库存减为负数
- 排查:日志显示多个请求同时通过库存检查
- 解决:引入Redisson分布式锁
RLock lock = redissonClient.getLock("stock_lock:"+skuId); try { lock.lock(); // 扣减库存操作 } finally { lock.unlock(); }问题2:订单列表加载缓慢
- 现象:用户历史订单超过1000条时,查询耗时3s+
- 排查:EXPLAIN显示全表扫描
- 解决:添加
(user_id, create_time)降序索引,查询速度提升至200ms
4.3 安全防护措施
输入验证:
- 使用Hibernate Validator校验DTO
- 防XSS:Jackson的@JsonSerialize配合HTMLEscape
- 价格计算:BigDecimal避免浮点误差
接口防护:
- 敏感操作(如删除)要求二次密码确认
- 支付接口实现签名验证
- 使用Spring Security的@PreAuthorize控制权限
数据安全:
- 密码存储:BCryptPasswordEncoder
- 敏感信息加密:AES算法加密手机号
- 日志脱敏:使用@Sensitive注解标记敏感字段
5. 部署与运维方案
5.1 环境准备清单
| 组件 | 版本 | 备注 |
|---|---|---|
| JDK | 1.8+ | 推荐Amazon Corretto 8 |
| MySQL | 8.0 | 需配置大小写敏感 |
| Redis | 6.2+ | 开启持久化 |
| Nginx | 1.20+ | 负载均衡+静态资源托管 |
| Linux | CentOS 7+ | 建议4核8G以上配置 |
5.2 高可用部署架构
┌───────────────┐ │ DNS轮询 │ └──────┬───────┘ ↓ ┌─────────────────────────────────────────┐ │ Nginx集群 │ │ ┌───────────┐ ┌───────────┐ │ │ │ Nginx 01 │───────│ Nginx 02 │ │ │ └───────────┘ └───────────┘ │ └───────┬────────────────────┬───────────┘ ↓ ↓ ┌─────────────────┐ ┌─────────────────┐ │ 应用服务器01 │ │ 应用服务器02 │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │ SpringBoot │ │ │ │ SpringBoot │ │ │ └─────────────┘ │ │ └─────────────┘ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │ Redis │ │ │ │ Redis │ │ │ └─────────────┘ │ │ └─────────────┘ │ └─────────────────┘ └─────────────────┘ ↓ ↓ ┌─────────────────────────────────────────┐ │ MySQL集群 │ │ ┌───────────┐ ┌───────────┐ │ │ │ Master │───────│ Slave │ │ │ └───────────┘ └───────────┘ │ └─────────────────────────────────────────┘5.3 监控指标设置
基础监控:
- CPU使用率(预警阈值>80%持续5分钟)
- 内存使用(预警阈值>75%)
- 磁盘空间(预警阈值>85%)
业务监控:
- 订单创建TPS(异常波动>±30%)
- 支付成功率(预警阈值<85%)
- 平均响应时间(API>500ms告警)
日志收集:
- ELK收集错误日志
- 关键操作审计日志单独存储
这套系统从设计到上线历时4个月,期间经历了3次架构调整和无数次深夜调试。最大的收获是认识到一个好的系统不仅要技术过关,更要深入理解业务场景。比如最初设计的库存扣减是同步操作,后来发现食堂场景中"临时加菜"很常见,才改为异步预占模式。