1. 项目背景与核心需求
最近在开发一个外卖套餐管理系统时,遇到了一个有趣的技术挑战。这个系统需要处理大量实时订单数据,同时还要保证套餐组合的灵活性和库存管理的准确性。作为一个从零开始的项目,我们需要自己实现所有核心功能模块。
这个外卖套餐项目的核心需求可以归纳为以下几点:
- 支持动态配置套餐组合(主餐+小食+饮料的任意搭配)
- 实时库存管理(防止超卖)
- 订单处理流水线(从下单到配送的全流程跟踪)
- 商家后台管理系统(菜品管理、套餐配置、数据统计)
2. 技术架构设计
2.1 整体架构方案
经过多次技术选型讨论,我们最终确定了以下技术栈:
- 前端:Vue3 + Element Plus(管理后台) + Uni-app(小程序端)
- 后端:Spring Boot 2.7 + MyBatis Plus
- 数据库:MySQL 8.0(主业务库) + Redis(缓存和计数器)
- 消息队列:RabbitMQ(订单异步处理)
- 部署:Docker + Jenkins CI/CD
选择这套技术栈主要基于以下考虑:
- 团队成员对这些技术都比较熟悉,开发效率有保障
- 成熟的生态圈和社区支持
- 良好的性能表现和扩展性
2.2 数据库设计要点
套餐系统的数据库设计有几个关键点需要特别注意:
-- 套餐基础表 CREATE TABLE `combo` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '套餐名称', `description` text COMMENT '套餐描述', `price` decimal(10,2) NOT NULL COMMENT '套餐价格', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:1-上架 0-下架', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 套餐项关联表 CREATE TABLE `combo_item_relation` ( `id` bigint NOT NULL AUTO_INCREMENT, `combo_id` bigint NOT NULL COMMENT '套餐ID', `item_id` bigint NOT NULL COMMENT '商品ID', `item_type` tinyint NOT NULL COMMENT '商品类型:1-主餐 2-小食 3-饮料', `is_required` tinyint NOT NULL DEFAULT '1' COMMENT '是否必选:1-必选 0-可选', PRIMARY KEY (`id`), KEY `idx_combo_id` (`combo_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意:套餐与商品的关联关系设计是系统的核心,我们采用了灵活的关联表结构,支持不同类型的商品组合,并且可以标记是否为必选项。
3. 核心功能实现
3.1 动态套餐配置
套餐配置的后台管理界面需要支持灵活的拖拽式组合。我们基于Vue的draggable组件实现了这个功能:
// 套餐配置组件核心逻辑 export default { data() { return { comboItems: { mains: [], // 主餐列表 sides: [], // 小食列表 drinks: [] // 饮料列表 }, selectedItems: [] // 已选中的商品 } }, methods: { handleDragEnd(evt) { // 处理拖拽结束后的逻辑 const item = evt.item.__vue__.$attrs['data-item'] if (this.validateSelection(item)) { this.selectedItems.push(item) } else { this.$message.error('已达到该类型商品的最大选择数量') } }, validateSelection(item) { // 验证选择是否合法 const typeCount = this.selectedItems.filter( i => i.type === item.type ).length return typeCount < this.getMaxSelection(item.type) } } }3.2 库存扣减方案
库存管理是外卖系统的关键难点,我们实现了多层次的库存控制:
- 前端限流:在加入购物车时就进行库存预检查
- 缓存计数:使用Redis原子操作保证库存扣减的原子性
- 数据库最终一致性:通过定时任务补偿Redis和MySQL的数据差异
Redis库存扣减的核心代码:
public boolean reduceStock(Long itemId, int quantity) { String key = "stock:" + itemId; long value = redisTemplate.opsForValue().increment(key, -quantity); if (value >= 0) { // 扣减成功,发送MQ消息异步更新数据库 mqTemplate.convertAndSend("stock.update", new StockUpdateMessage(itemId, quantity)); return true; } else { // 库存不足,回滚 redisTemplate.opsForValue().increment(key, quantity); return false; } }4. 订单处理流水线
4.1 订单状态机设计
我们采用状态机模式管理订单生命周期:
public enum OrderStatus { CREATED, // 已创建 PAID, // 已支付 PREPARING, // 备餐中 DELIVERING, // 配送中 COMPLETED, // 已完成 CANCELLED // 已取消 } // 状态转换规则 public class OrderStateMachine extends StateMachine<OrderStatus, OrderEvent> { public OrderStateMachine() { super(OrderStatus.CREATED); addTransition(OrderStatus.CREATED, OrderEvent.PAY, OrderStatus.PAID); addTransition(OrderStatus.PAID, OrderEvent.PREPARE, OrderStatus.PREPARING); addTransition(OrderStatus.PREPARING, OrderEvent.DELIVER, OrderStatus.DELIVERING); addTransition(OrderStatus.DELIVERING, OrderEvent.COMPLETE, OrderStatus.COMPLETED); // 取消相关的转换规则 addTransition(OrderStatus.CREATED, OrderEvent.CANCEL, OrderStatus.CANCELLED); addTransition(OrderStatus.PAID, OrderEvent.CANCEL, OrderStatus.CANCELLED); } }4.2 分布式事务处理
订单创建涉及多个服务调用,我们采用Saga模式保证最终一致性:
- 订单服务:创建订单记录
- 库存服务:扣减库存
- 支付服务:处理支付
- 通知服务:发送订单确认通知
每个步骤都有对应的补偿操作,任一步骤失败都会触发前面步骤的回滚。
5. 性能优化实践
5.1 缓存策略
我们实现了多级缓存来提升系统响应速度:
- 本地缓存:使用Caffeine缓存热点数据(如套餐基本信息)
- 分布式缓存:Redis缓存库存数据、用户常用地址等
- 数据库缓存:MySQL查询缓存优化
缓存更新策略采用"先更新数据库,再删除缓存"的方式,避免缓存一致性问题。
5.2 SQL优化案例
在订单查询接口中,我们发现了一个性能瓶颈:
-- 优化前的慢查询 SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time DESC LIMIT 10; -- 优化后的查询 SELECT * FROM orders WHERE user_id = 123 AND create_time > '2023-01-01' -- 添加时间范围条件 ORDER BY create_time DESC LIMIT 10;通过添加合理的时间范围条件,这个查询的性能提升了5倍以上。
6. 踩坑与经验总结
6.1 库存超卖问题
初期我们遇到了严重的库存超卖问题,主要原因是:
- 前端只做了一次库存检查
- 高并发下多个请求同时通过库存检查
- 数据库更新存在延迟
解决方案:
- 引入Redis原子操作保证扣减的原子性
- 实现二次校验机制
- 添加库存预警阈值
6.2 套餐配置的灵活性
最初的设计中,套餐配置过于死板,无法满足商家的多样化需求。我们通过以下改进解决了这个问题:
- 将"固定套餐"改为"可配置模板"
- 支持设置必选和可选项目
- 允许设置不同类型商品的最大选择数量
7. 扩展与未来优化
虽然当前版本已经实现了核心功能,但还有几个值得优化的方向:
- 智能推荐:基于用户历史订单推荐套餐组合
- 动态定价:根据时段、库存情况自动调整价格
- 备餐预测:利用历史数据预测备餐量
这个外卖套餐项目从零开始开发,前后历时32天,期间遇到了各种技术挑战。最大的收获是深刻理解了分布式系统中的数据一致性问题,以及如何在保证系统性能的同时提供灵活的业务功能。