1. 苍穹外卖项目概述与核心价值
苍穹外卖是一款专为餐饮企业设计的全链路数字化解决方案,由管理后台和小程序端构成完整闭环。作为黑马程序员的Java实战项目,它相比同类教学项目(如瑞吉外卖)具有三大差异化优势:
- 业务完整性:覆盖从用户下单、支付到商家接单、配送的全流程,新增来单语音播报、客户催单、多维数据统计等真实餐饮场景功能
- 技术栈升级:采用SpringBoot+SSM主流框架组合,集成微信小程序生态、WebSocket实时通信、阿里云OSS等企业级技术
- 教学友好性:通过清晰的模块划分(sky-common公共模块、sky-pojo实体模块、sky-server业务模块)降低学习曲线
提示:该项目GitHub仓库已获得1.4k星标,330个Fork,技术选型和代码结构经过大量学习者验证,适合作为SpringBoot企业级开发的入门到进阶项目。
2. 技术架构深度解析
2.1 分层架构设计
苍穹外卖采用标准四层架构,各层技术选型如下表所示:
| 层级 | 核心技术栈 | 典型应用场景 |
|---|---|---|
| 用户层 | 微信小程序+Vue.js+ElementUI | 用户点餐、商家管理界面渲染 |
| 网关层 | Nginx(反向代理+负载均衡) | 请求路由、静态资源托管 |
| 应用层 | SpringBoot+SpringMVC+JWT+WebSocket+Spring Cache | 业务逻辑处理、实时通信、缓存管理 |
| 数据层 | MySQL+Redis+MyBatis+PageHelper | 数据持久化、缓存加速、分页查询 |
2.2 关键技术实现原理
2.2.1 JWT无状态认证
java复制// JWT令牌生成示例
public String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
JwtBuilder builder = Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date(nowMillis))
.signWith(signatureAlgorithm, secretKey);
if (ttlMillis > 0) {
long expMillis = nowMillis + ttlMillis;
builder.setExpiration(new Date(expMillis));
}
return builder.compact();
}
该实现通过HS256算法对员工ID等声明(claims)进行签名,前端获取token后需在每次请求的Header中携带(Authorization: Bearer token)
2.2.2 WebSocket实时通信
来单提醒功能采用STOMP子协议实现:
- 服务端配置消息代理
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
- 客户端订阅特定主题
javascript复制stompClient.subscribe('/topic/order', function(message) {
playNotificationSound(message.body);
});
3. 开发环境搭建实战
3.1 后端工程结构
code复制sky-take-out(父POM)
├── sky-common # 公共模块
│ ├── constant # 常量类
│ ├── utils # 工具类
│ └── exception # 自定义异常
├── sky-pojo # 实体模块
│ ├── Entity # 数据库实体
│ ├── DTO # 数据传输对象
│ └── VO # 视图对象
└── sky-server # 业务模块
├── config # 配置类
├── mapper # MyBatis接口
└── service # 业务逻辑
3.2 数据库设计要点
核心表关系如下图所示(简化为文字描述):
- 员工表(employee):存储商家后台操作人员信息
- 分类表(category):支持多级菜品分类(如热菜/凉菜)
- 菜品表(dish)与口味表(dish_flavor):一对多关系,实现同一菜品多规格选择
- 订单表(orders)与明细表(order_detail):一个订单包含多个菜品明细
注意:所有密码字段均采用MD5加密存储,示例加密逻辑:
java复制DigestUtils.md5DigestAsHex("123456".getBytes()); // 输出:e10adc3949ba59abbe56e057f20f883e
4. 典型业务场景实现
4.1 购物车功能实现路径
- Redis数据结构设计
java复制// 使用Hash结构存储用户购物车
// Key: "cart:用户ID"
// Field: 菜品ID_口味组合(如"1397851099502260226_不要辣")
// Value: 商品数量
- 并发控制方案
java复制@Transactional
public void addCartItem(CartItemDTO cartItemDTO) {
String key = "cart:" + cartItemDTO.getUserId();
String itemKey = buildItemKey(cartItemDTO); // 构建唯一商品标识
// 使用Redis原子操作保证线程安全
redisTemplate.opsForHash().increment(key, itemKey, cartItemDTO.getNumber());
// 设置过期时间(7天)
redisTemplate.expire(key, 7, TimeUnit.DAYS);
}
4.2 订单状态机设计
订单流转包含以下状态:
code复制待付款 → 已付款/已取消 → 派送中 → 已完成
使用枚举类实现状态约束:
java复制public enum OrderStatus {
PENDING(1, "待付款"),
PAID(2, "已付款"),
CANCELLED(3, "已取消"),
DELIVERING(4, "派送中"),
COMPLETED(5, "已完成");
private final int value;
private final String desc;
// 状态校验方法
public static boolean isValidTransition(int from, int to) {
// 实现状态转换规则校验...
}
}
5. 性能优化实践
5.1 缓存策略组合
-
多级缓存架构
- 第一层:本地缓存(Caffeine)
- 第二层:分布式缓存(Redis)
- 第三层:数据库(MySQL)
-
菜品缓存示例
java复制@Cacheable(value = "dishCache", key = "#categoryId")
public List<Dish> list(Long categoryId) {
// 数据库查询
return dishMapper.getByCategoryId(categoryId);
}
5.2 SQL优化案例
分页查询使用PageHelper实现物理分页:
java复制public PageResult pageQuery(EmployeePageQueryDTO dto) {
PageHelper.startPage(dto.getPage(), dto.getPageSize());
Page<Employee> page = employeeMapper.pageQuery(dto);
return new PageResult(page.getTotal(), page.getResult());
}
对应Mapper XML配置:
xml复制<select id="pageQuery" resultType="com.sky.entity.Employee">
SELECT * FROM employee
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%',#{name},'%')
</if>
</where>
ORDER BY create_time DESC
</select>
6. 项目踩坑实录
6.1 文件上传陷阱
使用阿里云OSS时需注意:
- 前端必须设置
enctype="multipart/form-data" - 后端限制文件类型:
java复制String originalFilename = file.getOriginalFilename();
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
if(!Arrays.asList(".jpg", ".png").contains(extension.toLowerCase())) {
throw new FileUploadException("仅支持JPG/PNG格式");
}
6.2 事务失效场景
以下情况会导致@Transactional失效:
- 方法内部调用(非代理对象调用)
- 异常类型非RuntimeException且未指定rollbackFor
- 方法为private/final/static
推荐写法:
java复制@Service
public class OrderServiceImpl implements OrderService {
@Transactional(rollbackFor = Exception.class)
public void completeOrder(Long id) {
// 业务逻辑
}
}
7. 扩展思考方向
-
灰度发布方案:通过Nginx+Redis实现AB测试
nginx复制# nginx配置示例 location /api/ { set $group "A"; if ($http_user_id ~* "1[0-9]$") { set $group "B"; } proxy_pass http://$group.backend; } -
智能推荐系统:基于用户历史订单数据,使用协同过滤算法实现菜品推荐
-
压力测试指标:使用JMeter测试关键接口性能
- 登录接口:TPS≥200
- 下单接口:TPS≥100(带分布式锁)
这个项目最让我印象深刻的是其完备的异常处理体系。在sky-common模块中预定义了十余种业务异常类型,如OrderBusinessException、ShoppingCartBusinessException等,配合全局异常处理器GlobalExceptionHandler,使得业务代码可以保持高度简洁。建议学习时重点关注其异常设计思想,这对企业级开发至关重要。
