1. 项目背景与核心价值
私房菜上门服务正在经历一场数字化革命。去年我在帮朋友策划生日宴时,发现要找到一位靠谱的私厨需要辗转多个微信群比价沟通,整个过程耗时耗力。这促使我开始思考:为什么不能像打车软件一样,把私厨服务标准化、线上化?
这个基于SpringBoot的预约系统,本质上解决的是餐饮服务中的三个核心痛点:
- 信息不对称:用户无法快速获取厨师资质、菜品风格等关键信息
- 流程低效:从询价到确认往往需要多次来回沟通
- 信任缺失:缺乏评价体系和平台保障机制
技术选型上,我们采用SpringBoot+MySQL的主流组合,主要考虑:
- 开发效率:SpringBoot的自动配置特性让我们的团队在两周内就搭好了基础框架
- 运维成本:MySQL社区资源丰富,遇到性能问题容易找到解决方案
- 扩展性:后期要接入支付、地图等服务时,SpringCloud生态能平滑集成
关键决策:为什么不用Python或PHP?
虽然Python开发速度更快,但在高并发场景下,Java的线程模型更可靠。而PHP在复杂业务逻辑的处理上,其弱类型特性反而会成为维护的负担。
2. 系统架构设计解析
2.1 分层架构实现
系统采用经典的三层架构,但针对餐饮业务特点做了特殊优化:
code复制表现层
├── Web MVC (Thymeleaf模板)
├── REST API (供小程序调用)
└── Admin Console (Vue+ElementUI)
业务层
├── 预约服务模块 (含冲突检测)
├── 支付对账模块
├── 智能推荐模块 (基于用户历史行为)
└── 消息通知模块 (短信/站内信)
数据层
├── MySQL主从集群
├── Redis缓存 (菜单数据、厨师评分)
└── Elasticsearch (菜品搜索)
2.2 数据库设计精要
最核心的预约业务采用了状态机设计模式。这是我们的订单状态流转图:
| 状态 | 触发条件 | 后续动作 |
|---|---|---|
| 待支付 | 用户提交预约 | 生成15分钟倒计时 |
| 待确认 | 支付完成 | 通知厨师接单 |
| 已接单 | 厨师确认 | 发送备餐提醒 |
| 服务中 | 厨师点击开工 | 开启GPS轨迹记录 |
| 已完成 | 双方确认 | 开放评价入口 |
| 已取消 | 超时/主动取消 | 触发退款流程 |
java复制// 状态变更的核心代码示例
public class OrderStateMachine {
@Transactional
public void changeState(Long orderId, OrderEvent event) {
Order order = repository.findById(orderId);
OrderState newState = order.getState().nextState(event);
order.setState(newState);
// 记录状态变更日志
auditLog.save(new StateChangeLog(orderId, event));
}
}
3. 关键业务实现细节
3.1 预约冲突检测算法
厨师的时间安排是系统最复杂的业务规则,我们实现了三维度校验:
- 时间重叠检测(核心算法)
sql复制SELECT COUNT(*) FROM reservation
WHERE chef_id = ?
AND ((start_time BETWEEN ? AND ?)
OR (end_time BETWEEN ? AND ?))
- 地理位置校验:通过Haversine公式计算距离
- 备餐时间缓冲:硬菜类菜品需预留额外准备时间
3.2 支付对账流程
支付模块接入了微信和支付宝双渠道,关键点在于:
- 采用分布式事务保证订单状态与支付结果一致
- 每日凌晨跑对账Job,修复异常状态
- 退款操作记录操作流水(防纠纷)
mermaid复制graph TD
A[用户支付] --> B{支付成功?}
B -->|是| C[更新订单状态]
B -->|否| D[释放库存]
C --> E[通知厨师]
4. 性能优化实战
4.1 缓存策略设计
采用多级缓存解决菜单访问热点问题:
- 本地缓存 (Caffeine):存储基础菜品信息,TTL=5分钟
- 分布式缓存 (Redis):存储完整菜品详情,TTL=1小时
- 静态化处理:将热门菜品的HTML片段预渲染后存入CDN
java复制@Cacheable(value = "dishes", key = "#id")
public DishDetail getDishDetail(Long id) {
// 加入防穿透逻辑
DishDetail detail = repository.findById(id);
if(detail == null) {
return new EmptyDishDetail(); // 特殊空对象
}
return detail;
}
4.2 数据库优化案例
在用户查询界面遇到最棘手的N+1查询问题,通过以下方案解决:
问题SQL:
sql复制-- 原始查询(性能杀手)
SELECT * FROM dishes WHERE chef_id IN (
SELECT id FROM chefs WHERE region = ?
)
优化方案:
- 使用JOIN改写:
sql复制SELECT d.* FROM dishes d
JOIN chefs c ON d.chef_id = c.id
WHERE c.region = ?
- 添加复合索引:
sql复制ALTER TABLE chefs ADD INDEX idx_region_status (region, is_approved);
5. 安全防护体系
5.1 认证授权方案
采用改良版的RBAC模型:
- 用户角色:普通用户、VIP用户、厨师、管理员
- 资源权限:细粒度到按钮级别(如"取消订单"按钮)
- 访问控制:Spring Security + JWT
java复制@PreAuthorize("hasRole('CHEF') or #userId == authentication.principal.id")
public void cancelOrder(Long orderId, Long userId) {
// 业务逻辑
}
5.2 敏感数据保护
- 支付信息加密:采用HSM硬件加密模块
- 日志脱敏:自定义Logback过滤器
- 隐私计算:厨师联系方式仅在订单确认后显示
6. 踩坑实录与解决方案
6.1 分布式事务陷阱
问题现象:
在订单创建流程中,可能出现:
- 扣减库存成功但订单创建失败
- 支付成功但状态未更新
解决方案:
引入Seata AT模式,关键配置:
properties复制# application.properties
seata.tx-service-group=my_test_tx_group
seata.service.vgroup-mapping.my_test_tx_group=default
6.2 缓存一致性难题
典型场景:
厨师修改菜品价格后,部分用户仍看到旧价格
最终方案:
采用"先更新DB再删除缓存"策略,配合本地缓存失效
java复制public void updateDishPrice(Long id, BigDecimal price) {
// 1. 更新数据库
dishRepository.updatePrice(id, price);
// 2. 删除缓存
redisTemplate.delete("dish:" + id);
// 3. 广播失效消息
eventPublisher.publish(new CacheEvictEvent("dish", id));
}
7. 扩展性设计
7.1 插件化架构
将核心功能设计为可插拔模块:
code复制resources/META-INF/services
├── PaymentProvider (微信/支付宝实现)
├── NotifyChannel (短信/邮件/小程序消息)
└── RecommendStrategy (基于内容/协同过滤)
7.2 灰度发布方案
通过Nginx+Redis实现AB测试:
nginx复制location /api/menu {
set $group A;
if ($http_cookie ~* "experiment_group=B") {
set $group B;
}
proxy_pass http://backend_$group;
}
8. 运维监控体系
8.1 监控指标看板
我们配置了以下关键指标告警:
- 订单创建QPS > 500
- 平均响应时间 > 800ms
- MySQL连接数 > 80%
8.2 日志分析架构
采用ELK Stack处理每日10GB+日志:
- Filebeat收集日志
- Logstash解析字段
- Elasticsearch建立索引
- Kibana可视化分析
9. 测试策略
9.1 自动化测试套件
| 测试类型 | 工具 | 覆盖率目标 |
|---|---|---|
| 单元测试 | JUnit5 | 80%+ |
| API测试 | RestAssured | 核心流程100% |
| UI测试 | Selenium | 关键路径覆盖 |
9.2 压力测试数据
使用JMeter模拟200并发:
- 订单创建接口:TPS 150/s
- 菜单查询接口:TPS 1200/s
- 99%响应时间:< 1.2s
10. 项目演进路线
当前已规划的三个迭代阶段:
- v1.2:接入智能客服系统
- v1.5:实现食材供应链对接
- v2.0:开放平台API生态
在实际开发中,最让我意外的是用户对"厨师直播备餐"功能的强烈需求。这提醒我们:技术方案永远要服务于真实的用户场景,而不是追求所谓的技术先进性。下次迭代我会优先考虑如何用最简单的技术实现这个功能点——也许WebRTC会比专业的直播方案更合适。