Description or Example
# 整体逻辑
> **seckill首先对请求进行 登录, 超买, 重复, 随机码, 信号量等判断, 若判断成功, 则发送消息, 给MQ微服务慢慢处理**
> 首先, 构建订单数据并保存, 然后构建订单项数据并保存, 然后锁定库存, 再然后发送消息(自动取消订单的消息), 最后发送自动释放信号量的消息
# Bug修复
## 为什么保存在MQ中的对象属性丢失了?
> 因为调用远程微服务的时候, 没有指定`Post`请求, 这就会导致对象里面的信息丢失, 本质上是feign的原因
## 订单项详情的构建细节
> 因为用户列表页面渲染的缘故, 必须要有SKU的相关信息, 因此, 我们在构建的时候, 既是获取不到, 也需要赋予空串, 避免JSON反序列化的时候, 缺失某些参数导致的视图渲染错误
## 释放信号量的整体逻辑
> 首先判断订单是否存在, 如果订单不存在或订单状态为已取消, 都是要释放信号量
```java
@Bean
public Queue orderSeckillSemaphoreDelayQueue() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", "order-event-exchange");
arguments.put("x-dead-letter-routing-key", "order-secKill-order");
arguments.put("x-message-ttl", 1800000); // 30分钟后释放信号量
return new Queue("order-seckill-semaphore-delay-queue", true, false, false, arguments);
}
@Bean
public Binding orderSeckillSemaphore() {
return new Binding("order-seckill-semaphore-delay-queue",
Binding.DestinationType.QUEUE,
"order-event-exchange",
"order-seckill-semaphore",
null);
}
```
# 核心代码
## 主要逻辑
> 秒杀微服务主要负责对秒杀对象的合法性判断, 而MQ监听器则是创建对应的数据库数据和保存, 并发送消息, 自动取消和解锁等...
```java
@Component
@RabbitListener(queues = "order-secKill-order-queue")
public class SecKillListener {
@Autowired
private MemberFeignService memberFeignService;
@Autowired
private OrderFeignService orderFeignService;
@Autowired
private WareFeignService wareFeignService;
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private OrderSender orderSender;
@Autowired
private RedissonClient redissonClient;
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 主动释放信号量
* @param killId
* @param message
* @param channel
* @throws IOException
*/
@RabbitHandler
public void handleSemaphore(String killId, Message message, Channel channel) throws IOException {
String[] killBody = killId.split("_");
String sessionId = killBody[0];
String orderSn = killBody[1];
String skuId = killBody[2];
// 首先判断订单状态
R info = orderFeignService.getOrderStatus(orderSn);
if (!ifNoProblem(message, channel, info)) return;
Integer status = info.getData(new TypeReference<Integer>(){});
if (status == null || OrderStatusEnum.CANCLED.getCode().equals(status)) { // 需要释放信号量
BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps(SecKillConstant.SEC_KILL_SKU_MAP_SKU_INFO);
String json = operations.get(sessionId + "_" + skuId);
SeckillSkuTO seckillSkuTO = JSON.parseObject(json, SeckillSkuTO.class);
if (seckillSkuTO != null) {
String randomToken = seckillSkuTO.getRandomToken();
RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstant.SEC_KILL_RANDOM_CODE_SEMAPHORE + randomToken);
semaphore.release();
}
}
}
@RabbitHandler
public void handelSecKillOrder(SecKillOrderTO secKillOrderTO, Message message, Channel channel) throws IOException {
try {
CompletableFuture<Void> task1 = CompletableFuture.supplyAsync(() -> {
// 0. 查询用户的地址信息
List<MemberAddressBO> allAddresses = memberFeignService.getMemberAllAddressesWithOrder(secKillOrderTO.getMemberId());
MemberAddressBO memberAddressBO = new MemberAddressBO();
if (allAddresses != null && !allAddresses.isEmpty()) {
memberAddressBO = allAddresses.stream().filter(address -> address.getDefaultStatus().equals(1)).collect(Collectors.toList()).get(0);
}
return memberAddressBO;
}, threadPoolExecutor).thenApplyAsync(memberAddressBO -> {
// 1. 构建订单
OrderTO orderTO = buildOrder(secKillOrderTO, memberAddressBO);
orderFeignService.saveOrder(orderTO); // 保存订单
return orderTO;
}, threadPoolExecutor).thenAcceptAsync(orderTO -> {
// 2. 构建订单详情
OrderItemBO orderItemBO = buildOrderItem(secKillOrderTO, orderTO);
orderFeignService.saveOrderItem(orderItemBO); // 保存订单详情
}, threadPoolExecutor);
CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
// 3. 锁定库存
WareLockTO.OrderItemInfo orderItemInfo = new WareLockTO.OrderItemInfo().setSkuId(secKillOrderTO.getSkuId()).setNum(secKillOrderTO.getSecKillCount());
WareLockTO wareLockTO = new WareLockTO().setOrderSn(secKillOrderTO.getOrderSn()).setOrderItemInfoList(Collections.singletonList(orderItemInfo));
wareFeignService.lockSkuStock(wareLockTO);
}, threadPoolExecutor);
CompletableFuture.allOf(task1, task2).join();
// 发送消息 -> 自动取消订单
orderSender.sendOrderMsg(secKillOrderTO.getOrderSn());
// 发送消息 -> 释放信号量
String killId = secKillOrderTO.getPromotionSessionId() + "_" + secKillOrderTO.getOrderSn() + "_" + secKillOrderTO.getSkuId();
orderSender.sendReleaseSemaphore(killId);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}catch (Exception e) {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
}
}
private OrderItemBO buildOrderItem(SecKillOrderTO secKillOrderTO, OrderTO orderTO) {
OrderItemBO orderItemBO = new OrderItemBO();
return orderItemBO.setOrderId(orderTO.getId())
.setOrderSn(secKillOrderTO.getOrderSn())
.setSkuId(secKillOrderTO.getSkuId())
.setSkuPrice(secKillOrderTO.getSecKillPrice())
.setSkuQuantity(secKillOrderTO.getSecKillCount())
.setSkuName("")
.setSkuPic("")
.setSkuAttrsVals("")
.setPromotionAmount(BigDecimal.ZERO)
.setCouponAmount(BigDecimal.ZERO)
.setIntegrationAmount(BigDecimal.ZERO)
.setRealAmount(secKillOrderTO.getSecKillPrice())
.setGiftGrowth(0)
.setGiftIntegration(0);
}
private OrderTO buildOrder(SecKillOrderTO secKillOrderTO, MemberAddressBO memberAddressBO) {
// 1. 快速构建订单
OrderTO order = new OrderTO();
return order.setMemberId(secKillOrderTO.getMemberId())
.setOrderSn(secKillOrderTO.getOrderSn())
.setCreateTime(new Date())
.setMemberUsername(secKillOrderTO.getMemberId().toString()) // 没有用户名, 用用户的id替代
.setTotalAmount(secKillOrderTO.getSecKillPrice())
.setPayAmount(secKillOrderTO.getSecKillPrice()) // 应付金额 和 订单总额 都是秒杀价格, 自动忽略运费信息
.setFreightAmount(BigDecimal.ZERO)
.setPromotionAmount(BigDecimal.ZERO)
.setIntegrationAmount(BigDecimal.ZERO)
.setCouponAmount(BigDecimal.ZERO)
.setDiscountAmount(BigDecimal.ZERO)
.setPayType(1).setSourceType(0)
.setStatus(OrderStatusEnum.CREATE_NEW.getCode())
.setIntegration(0)
.setGrowth(0)
.setReceiverName(memberAddressBO.getName())
.setReceiverPhone(memberAddressBO.getPhone())
.setReceiverPostCode(memberAddressBO.getPostCode())
.setReceiverProvince(memberAddressBO.getProvince())
.setReceiverCity(memberAddressBO.getCity())
.setReceiverRegion(memberAddressBO.getRegion())
.setReceiverDetailAddress(memberAddressBO.getDetailAddress());
}
private Boolean ifNoProblem(Message message, Channel channel, R info) throws IOException {
if (info.getCode() != 0) {
// 有问题, 需要重新入队
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
return false;
}
// true是没有问题
return true;
}
}
```
```java
@RequestMapping("/ware/wareinfo/lock/sku/stock")
R lockSkuStock(@RequestBody WareLockTO wareLockTO);
```
```java
@RequestMapping("/order/order/save")
R saveOrder(@RequestBody OrderTO order);
@RequestMapping("/order/orderitem/save")
R saveOrderItem(@RequestBody OrderItemBO orderItem);
```
```java
@FeignClient("bitmall-member")
public interface MemberFeignService {
@RequestMapping("/member/memberreceiveaddress/{memberId}")
List<MemberAddressBO> getMemberAllAddressesWithOrder(@PathVariable Long memberId);
}
```