订单与购物车服务
约 787 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › Spring Cloud › 实战项目:微服务电商系统
购物车服务
购物车使用 Redis 存储,数据结构为 Hash(商品 ID → 数量)。
@Service
public class CartService {
@Autowired
private StringRedisTemplate redis;
private String cartKey(Long userId) {
return "cart:user:" + userId;
}
// 添加商品到购物车
public void addItem(Long userId, Long productId, Integer quantity) {
String key = cartKey(userId);
redis.opsForHash().increment(key, String.valueOf(productId), quantity);
// 10 天过期
redis.expire(key, 10, TimeUnit.DAYS);
}
// 修改数量
public void updateItem(Long userId, Long productId, Integer quantity) {
String key = cartKey(userId);
if (quantity <= 0) {
redis.opsForHash().delete(key, String.valueOf(productId));
} else {
redis.opsForHash().put(key, String.valueOf(productId), String.valueOf(quantity));
}
}
// 获取购物车列表(调用商品服务获取详情)
public Result<List<CartItemVO>> getCart(Long userId) {
String key = cartKey(userId);
Map<Object, Object> entries = redis.opsForHash().entries(key);
List<CartItemVO> items = new ArrayList<>();
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
Long productId = Long.valueOf(entry.getKey().toString());
Integer quantity = Integer.valueOf(entry.getValue().toString());
// Feign 调用商品服务
Result<Product> productResult = productFeignClient.getProduct(productId);
if (productResult.isSuccess() && productResult.getData() != null) {
Product product = productResult.getData();
items.add(new CartItemVO(productId, product.getName(),
product.getPrice(), quantity,
product.getPrice().multiply(BigDecimal.valueOf(quantity)),
product.getImageUrl()));
}
}
return Result.success(items);
}
// 清空购物车
public void clearCart(Long userId) {
redis.delete(cartKey(userId));
}
}订单服务
下单流程
订单服务实现
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private RedissonClient redisson;
@Transactional
public Result<Order> createOrder(CreateOrderRequest req) {
// 1. 分布式锁(防止重复下单)
RLock lock = redisson.getLock("lock:order:user:" + req.getUserId());
lock.lock(10, TimeUnit.SECONDS);
try {
// 2. 查询商品(Feign 调用)
Result<Product> productResult = productFeignClient.getProduct(req.getProductId());
if (!productResult.isSuccess() || productResult.getData() == null) {
return Result.error("商品不存在");
}
Product product = productResult.getData();
// 3. 扣减库存(Feign 调用)
Result<Void> stockResult = productFeignClient.deductStock(
req.getProductId(), req.getQuantity());
if (!stockResult.isSuccess()) {
return Result.error("库存不足");
}
// 4. 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(req.getUserId());
order.setProductId(req.getProductId());
order.setQuantity(req.getQuantity());
order.setAmount(product.getPrice()
.multiply(BigDecimal.valueOf(req.getQuantity())));
order.setStatus("PAYING");
order.setCreateTime(LocalDateTime.now());
orderMapper.insert(order);
// 5. 清空购物车
cartService.clearCart(req.getUserId());
return Result.success(order);
} catch (Exception e) {
log.error("订单创建失败", e);
throw new RuntimeException("下单失败", e);
} finally {
lock.unlock();
}
}
private String generateOrderNo() {
return "ORD" + LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ RandomStringUtils.randomNumeric(6);
}
// Sentinel 注解保护
@SentinelResource(value = "createOrder",
blockHandler = "createOrderBlockHandler")
public Result<Order> createOrderWithSentinel(CreateOrderRequest req) {
return createOrder(req);
}
public Result<Order> createOrderBlockHandler(
CreateOrderRequest req, BlockException e) {
return Result.error("订单服务繁忙,请稍后重试");
}
}订单查询
@GetMapping("/{id}")
public Result<Order> getOrder(@PathVariable Long id) {
Order order = orderMapper.selectById(id);
return order != null ? Result.success(order) : Result.error("订单不存在");
}
@GetMapping("/list")
public Result<List<Order>> listOrders(
@RequestParam Long userId,
@RequestParam(defaultValue = "1") Integer page) {
List<Order> orders = orderMapper.listByUserId(userId, (page - 1) * 10);
return Result.success(orders);
}网关路由
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- id: cart-service
uri: lb://cart-service
predicates:
- Path=/api/cart/**
filters:
- StripPrefix=1小结
- 购物车:Redis Hash 存储,支持增删改查,10 天自动过期。
- 下单流程:分布式锁防止重复 → Feign 查询商品 → 扣库存 → 创建订单 → 清空购物车。
- 订单号:
ORD+ 时间戳 + 6 位随机数。 @SentinelResource保护下单接口,降级时返回友好提示。- Redisson 分布式锁保证同一用户不会重复下单。
