阿飞商城 · afei-mall
技术栈
框架
Spring Boot 3.5 / Spring Cloud Alibaba
持久层
MyBatis-Plus 3.5.15 / MySQL 8
缓存
Redis / Redisson
消息
RabbitMQ(延迟队列)
分布式
Nacos / Seata AT / Gateway
鉴权
JWT 自定义注解
文件
Aliyun OSS
技术亮点
这是什么
一个从 0 搭建的 Spring Cloud Alibaba 微服务电商后端。7 个业务服务各自独立部署、各自持有数据库,
跨服务只走 HTTP 调用,不允许直接读别人的库——这是我把「服务边界」真正落地过的第一个项目。
架构
Gateway 统一入口 → Nacos 注册与配置中心 → 各业务服务。
服务之间用 RestTemplate 互调(刻意没用 Feign:我想先把「一次远程调用到底发生了什么」搞明白)。
| 服务 | 端口 | 职责 |
|---|---|---|
| mall-auth | 8010 | 注册 / 登录 / JWT 签发 |
| mall-user | 8020 | 用户资料、收货地址、头像 |
| mall-product | 8030 | 分类 / 品牌 / SPU / SKU |
| mall-cart | — | 购物车(只操作 Redis,不碰 MySQL) |
| mall-order | 8040 | 订单创建、快照、超时关闭 |
| mall-payment | 8050 | 支付编排 |
四个真正花时间的地方
1. 防超卖:分布式锁 + 原子 SQL
一万人抢一百件库存,光靠 synchronized 显然不够。方案分两层:
Redisson 分布式锁把同一商品的并发请求串行化,原子 SQL(UPDATE ... SET stock = stock - 1 WHERE stock >= 1)
作为最后一道防线。两层缺一不可——锁会因故失效,SQL 不会。
2. 订单超时关闭:延迟队列 + 定时任务双保险
只靠延迟队列,消息可能丢;只靠定时任务,精度又不够。
所以两条路一起走:下单时发一条 30 分钟的延迟消息做正常取消,
同时定时任务每分钟扫一遍超时订单兜底。两者都用幂等的状态判断收口,不会重复取消。
3. 订单快照
订单表不 JOIN 商品表取价,而是把下单那一刻的商品名、价格、缩略图冗余进订单项。
商品后来改名涨价都不影响历史订单——这是我在这个项目里最满意的一个决定。
4. 跨服务一致性:Seata AT 模式
下单要同时写订单库和库存库。两个方案对比过:最终选了 Seata AT(基于 undo_log 回滚),
因为业务侵入小、可读性好,代价是每个参与方都要能开事务。
踩过的坑
- JWT 载荷必须带
jti:iat/exp只精确到秒,同一秒内签发的 Token 完全相同,
导致「登出 → 立刻重新登录」会拿到刚被拉黑的那个 Token。 - 购物车用 Redis Hash 存是性能考虑,但也意味着它没有事务保护,
登录态与匿名态的合并逻辑要自己想清楚。
项目截图



