1. 项目背景与核心需求
飞天外卖配送系统是一个典型的互联网O2O应用,它需要解决的核心业务场景包括:餐厅管理、订单处理、骑手调度、路径规划和用户交互。这个毕业设计项目选择SpringBoot作为技术栈,主要考虑到以下几个因素:
- 快速开发:SpringBoot的自动配置和起步依赖特性能够大幅减少XML配置
- 微服务友好:便于后期扩展为分布式架构
- 生态丰富:整合MyBatis、Redis等中间件非常方便
- 约定优于配置:适合学生快速上手实现业务逻辑
提示:选择SpringBoot 2.7.x稳定版本而非最新的3.x系列,可以避免Lombok等常用库的兼容性问题
2. 系统架构设计
2.1 技术栈选型
| 组件类型 | 技术选型 | 选择理由 |
|---|---|---|
| 后端框架 | SpringBoot 2.7.12 | 稳定、文档丰富 |
| 数据库 | MySQL 8.0 | 支持JSON字段和GIS空间数据 |
| ORM框架 | MyBatis-Plus 3.5.3 | 简化CRUD操作 |
| 缓存 | Redis 6.x | 高频数据缓存 |
| 消息队列 | RabbitMQ 3.11 | 订单状态异步通知 |
| 地图API | 高德地图Web服务 | 国内访问稳定 |
2.2 模块划分
code复制com.flytakeout
├── common # 公共模块
├── gateway # 网关模块
├── service # 业务服务
│ ├── order # 订单服务
│ ├── user # 用户服务
│ ├── shop # 商家服务
│ └── delivery # 配送服务
└── admin # 管理后台
3. 核心功能实现
3.1 订单状态机设计
外卖订单的典型状态流转:
java复制public enum OrderStatus {
UNPAID, // 待支付
PAID, // 已支付
MERCHANT_ACCEPT,// 商家接单
DISPATCHING, // 配送中
COMPLETED, // 已完成
CANCELLED // 已取消
}
使用状态模式实现状态转换:
java复制public interface OrderState {
void handle(OrderContext context);
}
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final Map<OrderStatus, OrderState> stateHandlers;
public void changeStatus(Long orderId, OrderStatus newStatus) {
Order order = getById(orderId);
stateHandlers.get(order.getStatus()).handle(new OrderContext(order, newStatus));
}
}
3.2 配送路径规划
集成高德地图API实现:
- 注册开发者账号获取Key
- 添加Maven依赖:
xml复制<dependency>
<groupId>com.amap.api</groupId>
<artifactId>maps-java</artifactId>
<version>3.0.0</version>
</dependency>
- 路径规划核心代码:
java复制public class DeliveryRouteService {
public RoutePlan calculateOptimalRoute(Location restaurant,
Location customer,
List<Location> riders) {
// 1. 获取所有骑手到餐厅的距离
Map<Rider, Integer> distances = riders.stream()
.collect(Collectors.toMap(
rider -> rider,
rider -> amapService.getDistance(rider.getLocation(), restaurant)
));
// 2. 选择最近的骑手
Rider nearest = Collections.min(distances.entrySet(),
Map.Entry.comparingByValue()).getKey();
// 3. 规划餐厅到客户的路线
return amapService.getRoutePlan(restaurant, customer);
}
}
4. 关键问题解决方案
4.1 并发下单控制
使用Redis分布式锁防止超卖:
java复制public boolean placeOrder(OrderDTO orderDTO) {
String lockKey = "order:lock:" + orderDTO.getGoodsId();
String requestId = UUID.randomUUID().toString();
try {
// 获取锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(locked)) {
// 检查库存
Integer stock = goodsMapper.selectStock(orderDTO.getGoodsId());
if (stock >= orderDTO.getQuantity()) {
// 扣减库存
goodsMapper.updateStock(orderDTO.getGoodsId(),
stock - orderDTO.getQuantity());
// 创建订单
orderMapper.insert(convertToOrder(orderDTO));
return true;
}
}
return false;
} finally {
// 释放锁
if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
4.2 订单超时处理
使用RabbitMQ延迟队列实现:
- 配置延迟交换机:
yaml复制spring:
rabbitmq:
template:
exchange: order.event.exchange
listener:
simple:
default-requeue-rejected: false
- 订单创建时发送延迟消息:
java复制public void createOrder(Order order) {
orderMapper.insert(order);
// 发送延迟消息(15分钟未支付自动取消)
rabbitTemplate.convertAndSend(
"order.event.exchange",
"order.create",
order.getId(),
message -> {
message.getMessageProperties()
.setExpiration("900000"); // 15分钟
return message;
});
}
- 消费端处理:
java复制@RabbitListener(queues = "order.timeout.queue")
public void handleOrderTimeout(Long orderId) {
Order order = orderService.getById(orderId);
if (order.getStatus() == OrderStatus.UNPAID) {
orderService.cancelOrder(orderId, "超时未支付");
}
}
5. 系统优化实践
5.1 缓存设计策略
采用多级缓存架构:
- 本地缓存:使用Caffeine缓存店铺信息
java复制@Bean
public Cache<String, Shop> shopCache() {
return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
}
- 分布式缓存:Redis缓存热门商品
java复制public Goods getGoodsWithCache(Long goodsId) {
String key = "goods:" + goodsId;
Goods goods = redisTemplate.opsForValue().get(key);
if (goods == null) {
goods = goodsMapper.selectById(goodsId);
redisTemplate.opsForValue().set(key, goods, 1, TimeUnit.HOURS);
}
return goods;
}
- 缓存一致性:通过RabbitMQ消息通知更新
java复制@RabbitListener(queues = "goods.update.queue")
public void handleGoodsUpdate(Long goodsId) {
redisTemplate.delete("goods:" + goodsId);
shopCache().invalidate(goodsId);
}
5.2 性能优化指标
通过Actuator监控关键指标:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
tags:
application: ${spring.application.name}
重点关注:
- 平均响应时间(<500ms)
- 错误率(<0.1%)
- JVM内存使用(<70%)
- 数据库连接池使用率
6. 部署方案
6.1 本地开发环境
使用Docker Compose快速搭建依赖服务:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672"
- "15672:15672"
6.2 生产环境部署
使用Jenkins Pipeline实现CI/CD:
groovy复制pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
stage('Deploy') {
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'production-server',
transfers: [
sshTransfer(
sourceFiles: 'target/*.jar',
removePrefix: 'target',
remoteDirectory: '/app/flytakeout',
execCommand: '''
cd /app/flytakeout
docker-compose down
docker-compose up -d --build
'''
)
]
)
]
)
}
}
}
}
7. 毕业设计扩展建议
- 智能调度算法:引入强化学习优化骑手分配
- 实时追踪:WebSocket实现订单状态推送
- 数据分析:使用ELK分析订单数据
- 压力测试:JMeter模拟高峰时段流量
- 安全加固:Spring Security OAuth2实现RBAC
我在实际开发中发现几个容易忽视的问题:
- 高德地图API有QPS限制,需要做好本地缓存
- 订单状态变更要保证幂等性
- 金额计算使用BigDecimal避免精度丢失
- 分布式环境下系统时间要同步
- 日志收集建议使用Logstash统一处理
