五一節秒殺、團購 -- 商城總結

        大家好,好長時間沒有更新博客。是因爲單位五一節針對廣大用戶發起了一次秒殺以及拼團的活動。 在肝了2周後活動正式上線並取得不錯的銷售成績,今天正好來總結一下相關的業務以及使用的相關技術。 
      
        說起秒殺、拼團作爲程序員的小夥伴們肯定都十分熟悉,但是親力親爲完成秒殺的可能不多,比如我!在開發的時候也遇到了很多坑,今天就給小夥伴們總結下其中遇到的坑。
        
        秒殺:所謂“秒殺”,就是網絡賣家發佈一些超低價格的商品,所有買家在同一時間網上搶購的一種銷售方式。由於商品價格低廉,往往一上架就被搶購一空,有時只用一秒鐘。秒殺就會涉及到大量的用戶在同一個時間段對同一個商品進行操作,這也就是我們說的高併發。由於高併發的場景,由之衍生出來的就會有另一個問題--數據的可靠性。


        首先我們先總結一下整個流程:
        管理端結構:

 

       app端操作流程:

        

 

管理端的業務在上圖處理的比較清晰,這裏我就不做過多的介紹。重點是放在app端的業務和設計到的相關技術做一個大致的描寫。


app端拼團:

     1.首先是用戶有意向發起拼團,下單成功後生成訂單,此時的訂單狀態爲 待成團 ,同時會生成一個口令,這個口令裏面存了該用戶相關信息、產品信息。同時會在mq中啓動一個定時任務,如果早指定的拼團時間內沒有人拼團就會發起退款退券操作。並將團長訂單信息存入拼團活動表。

mq配置如下:

/**
 * Author: sangfor
 * Date: 2020/4/21 18:20
 */
@Configuration
public class RabbitmqQueue {
    /*------------------------拼團--------------------------------*/
    public final static String GROUP_EXCHANGE = "GROUP_EXCHANGE";
    public final static String DELAY_GROUP_EXCHANGE = "DELAY_GROUP_EXCHANGE";

    public final static String GROUP_QUEUE = "GROUP_QUEUE";
    public final static String DELAY_GROUP_QUEUE = "DELAY_GROUP_QUEUE";

    public final static String GROUP_ROUTINGKEY = "GROUP_ROUTINGKEY";

    @Bean
    public Queue delayDonationQueue() {
        return QueueBuilder.durable(DELAY_GROUP_QUEUE)
                .withArgument("x-dead-letter-exchange", GROUP_EXCHANGE)
                .withArgument("x-dead-letter-routing-key", GROUP_ROUTINGKEY)
                .build();
    }

    @Bean
    public Queue donationQueue() {
        return QueueBuilder.durable(GROUP_QUEUE)
                .build();
    }
    @Bean
    public Exchange delayDonationExchange() {
        return ExchangeBuilder
                .directExchange(DELAY_GROUP_EXCHANGE)
                .durable(true)
                .build();
    }
    @Bean
    public Exchange donationExchange() {
        return ExchangeBuilder
                .directExchange(GROUP_EXCHANGE)
                .durable(true)
                .build();
    }
    @Bean
    public Binding delayDonationBinding() {
        return BindingBuilder.bind(delayDonationQueue())
                .to(delayDonationExchange())
                .with(GROUP_ROUTINGKEY).noargs();
    }
    @Bean
    public Binding donationBinding() {
        return BindingBuilder.bind(donationQueue())
                .to(donationExchange())
                .with(GROUP_ROUTINGKEY).noargs();
    }


}

 

提供者和消費者:

/**
 * Author: sangfor
 * Date: 2020/4/21 18:25
 */
@Component
@Slf4j
public class RabbitMqService {

    private static final Logger logger = LoggerFactory.getLogger(RabbitMqService.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private SalesOrderInfoServiceImpl salesOrderInfoService;

    /**
     *
     * @param data 數據
     * @param expiration 延時 單位毫秒
     */
    public void sendMessage(Object data, String expiration) {
        // 消息發送時間
        logger.info("準備發送拼團延時消息" + JSONObject.toJSONString(data));
        // 設置發送時間,開始發送
        try {
            CorrelationData correlationId = new CorrelationData(String.valueOf(ObjectId.get()));
            //根據任務類型,進不同隊列
            //指定路由,指定routingKey,指定傳輸數據,指定消息,CorrelationData(可靠性投遞)
            rabbitTemplate.convertAndSend(RabbitmqQueue.DELAY_GROUP_EXCHANGE, RabbitmqQueue.GROUP_ROUTINGKEY, data,
                    // 設置message過期時間
                    message -> {
                        message.getMessageProperties().setExpiration(expiration);
                        return message;
                    },
                    // 消息可靠性投遞
                    correlationId
            );
            logger.info("------------------------發送拼團延時消息成功,延時" + expiration);
        } catch (AmqpException e) {
            logger.error("已購代幫消息發送失敗: {}", e);
        }
    }


    @RabbitListener(queues = RabbitmqQueue.GROUP_QUEUE)
    @RabbitHandler  // 此註解加上之後可以接受對象型消息
    public void messageListener(@Payload String data, Message message, Channel channel)
            throws Exception {
        log.info("---------------------------接受成團延時消息----------------------------");
        try {
            salesOrderInfoService.groupFail(data);
        } catch (Exception e) {
            log.error("成團失敗業務處理異常", e);
        }
        // 消息確認機制
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }


}

     2.口令解析:將這個口令複製到app中就能解析出來分享人以及分享的產品信息。

     3.拼單成功,首先通過數據庫中的拼團活動中的團長及其訂單信息取出並修改訂單狀態,然後再對團員下單。

     4.下單失敗,對團長的訂單進行退款退券,同時修改訂單狀態。

   

秒殺:

       1.秒殺列表,秒殺列表是放在redis中的,採用了兩個結構,hash 、String。目前採用這兩種redis的數據結構比較合理的。

        list:

             key -- 活動的時間段id :periodId  

             vaule -- 商品信息  goodsVo

       String:

              key -- 商品id :goodsId

             value -- 庫存 :count

 

       2.秒殺活動中的高併發和數據安全性處理。

          處理秒殺活動的核心處理點就是對秒殺商品的庫存加鎖以及利用mq做延時下單處理。廢話不多說直接上代碼:

 commitOrderByActivityRequestVo.setSourceType(goodsGoodsService.selectMallTypeByGoodsId(commitOrderByActivityRequestVo.getGoodsId()));

        JSONObject json = new JSONObject();
        try {
            Optional<CustomerLoginResponse> optional = Optional.ofNullable(LoginSessionUtils.getUser());
            optional.ifPresent(m -> commitOrderByActivityRequestVo.setAppUserId(m.getCustomerId()));
        } catch (Exception e) {
            log.error("==>  團購秒殺訂單獲取用戶ID異常 " + e.getMessage());
            return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "團購秒殺訂單獲取用戶ID異常");
        }

        if (commitOrderByActivityRequestVo.getGoodsCount() == 0) {
            return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "商品數量不能爲0");
        }
        if (commitOrderByActivityRequestVo.getArea() == "" || commitOrderByActivityRequestVo.getCity() == "") {
            return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "收貨地址不完整");
        }

        // ====================驗證是否達到了購買上限======================
        CheckActivityLimtStuatusReqVo checkActivityLimtStuatusReqVo = new CheckActivityLimtStuatusReqVo();
        checkActivityLimtStuatusReqVo.setType(commitOrderByActivityRequestVo.getOrderType());
        BeanUtils.copyProperties(commitOrderByActivityRequestVo, checkActivityLimtStuatusReqVo);
        if (checkActivityLimtStuatusReqVo.getType() == 1) {
            int limit;
            //區分商城和電子城秒殺
            if (commitOrderByActivityRequestVo.getSourceType() < 2) {
                limit = seckillService.querySeckillLimitStuatus(checkActivityLimtStuatusReqVo);
            } else {
                //同名類
                com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CheckActivityLimtStuatusReqVo checkActivityLimtStuatusReqVo2 = new com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CheckActivityLimtStuatusReqVo();
                BeanCopyUtil.copyProperties(checkActivityLimtStuatusReqVo, checkActivityLimtStuatusReqVo2);
                limit = cooperationSeckillService.querySeckillLimitStuatus(checkActivityLimtStuatusReqVo2);
            }
            String goodsId = checkActivityLimtStuatusReqVo.getGoodsId();
            String activityId = checkActivityLimtStuatusReqVo.getActivityId();
            String appUserId = checkActivityLimtStuatusReqVo.getAppUserId();
            int limtGoodsCount = orderService.queryOrderNumberbyActivitySeckill(goodsId, activityId, appUserId);
            if (commitOrderByActivityRequestVo.getGoodsCount() > limit) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "超過活動購買上限!");
            }
            limtGoodsCount = limtGoodsCount + commitOrderByActivityRequestVo.getGoodsCount();
            if (limtGoodsCount > limit) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "已達到活動購買上限!");
            }

        } else {
            int limit;
            List<String> list = new ArrayList<>();
            //區分商城來源
            if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CheckActivityLimtStuatusReqVo checkActivityLimtStuatusReqVo2 =
                        new com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CheckActivityLimtStuatusReqVo();
                BeanCopyUtil.copyProperties(checkActivityLimtStuatusReqVo, checkActivityLimtStuatusReqVo2);

                limit = cooperationGroupPurchaseService.queryGroupPurchaseLimitStuatus(checkActivityLimtStuatusReqVo2);
                list = cooperationGroupPurchaseService.queryGroupPurchaseOrder(checkActivityLimtStuatusReqVo2);
            } else {
                limit = groupPurchaseService.queryGroupPurchaseLimitStuatus(checkActivityLimtStuatusReqVo);
                list = groupPurchaseService.queryGroupPurchaseOrder(checkActivityLimtStuatusReqVo);
            }


            if (list.size() > 0 && !list.isEmpty()) {
                int i = orderService.queryOrderNumberbyActivity(list);
                if (i >= limit) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "已達到活動購買上限!");
                }
            }
        }

        // ====================驗證完畢======================


        Object object = redisTemplate.opsForValue().get(commitOrderByActivityRequestVo.getPayCode());

        if (object == null) {
            return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "交易超時,請重新返回結算頁面進行提交");
        }
        if (object.toString().equals("0")) {
            redisTemplate.opsForValue().set(commitOrderByActivityRequestVo.getPayCode(), "1", 600, TimeUnit.SECONDS);
        } else {
            return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "請勿重複提交");
        }

        // 驗證活動是否在進行中
        if (commitOrderByActivityRequestVo.getOrderType() == 1) {
            Object querySecKillActivityStatusRespVo;
            if (commitOrderByActivityRequestVo.getSourceType() < 2) {
                querySecKillActivityStatusRespVo = seckillService.QuerySecKillActivityStatus(commitOrderByActivityRequestVo.getActivityId());
            } else {
                querySecKillActivityStatusRespVo = cooperationSeckillService.QuerySecKillActivityStatus(commitOrderByActivityRequestVo.getActivityId());
            }
            if (querySecKillActivityStatusRespVo == null) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "該秒殺活動未在進行中!");
            }
        } else {
            //區分商城來源
            int i = 0;
            if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                i = cooperationGroupPurchaseService.queryGroupPurchaseStatus(commitOrderByActivityRequestVo.getActivityId());
            } else {
                i = groupPurchaseService.queryGroupPurchaseStatus(commitOrderByActivityRequestVo.getActivityId());
            }
            if (i != 2) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "該團購活動未在進行中!");
            }
        }


        // 拿出SKUID 保持和以前一樣的接口傳值對象,所以用list
        List<String> skuIdList = new ArrayList<>();
        skuIdList.add(commitOrderByActivityRequestVo.getSkuId());

        // 查詢商品信息
        List<GoodsByCartRepVo> goodsCartBySkuId = goodsSkuService.findGoodsCartBySkuId(skuIdList);


        //拆分數據生成子訂單
        List<AddOrderItemByActivityRequest> addOrderItemList = new ArrayList<>();
        AddOrderItemByActivityRequest addOrderItemRequest = new AddOrderItemByActivityRequest();

        // 設置子訂單參數
        String payNumber = String.valueOf(ObjectId.get());
        commitOrderByActivityRequestVo.setId(String.valueOf(ObjectId.get()));
        commitOrderByActivityRequestVo.setOrderId(String.valueOf(ObjectId.get()));
        commitOrderByActivityRequestVo.setOrderNumber(String.valueOf(ObjectId.get()));
        commitOrderByActivityRequestVo.setPayNumber(payNumber);
        commitOrderByActivityRequestVo.setSourceType(goodsCartBySkuId.get(0).getIsWelfareGoods());
        BeanUtils.copyProperties(commitOrderByActivityRequestVo, addOrderItemRequest);

        // 運算訂單總金額
        BigDecimal totalMoney = new BigDecimal(commitOrderByActivityRequestVo.getGoodsRealPrice()).multiply(new BigDecimal((commitOrderByActivityRequestVo.getGoodsCount()))).add(new BigDecimal(commitOrderByActivityRequestVo.getFreight()));
        addOrderItemRequest.setTotalMoney(totalMoney);
        addOrderItemList.add(addOrderItemRequest);

        // 子訂單金額參數設置完畢

        int totalOriginalPrice = commitOrderByActivityRequestVo.getGoodsOriginalPrice() * commitOrderByActivityRequestVo.getGoodsCount() + commitOrderByActivityRequestVo.getFreight();


        // 金額校驗開始 1 秒殺 2 團購

        JSONObject jsonObject1 = new JSONObject();
        if (commitOrderByActivityRequestVo.getOrderType() == 1) {
            GoodsSkuReqVo goodsSkuReqVo = new GoodsSkuReqVo();
            goodsSkuReqVo.setSeckillId(commitOrderByActivityRequestVo.getActivityId());
            goodsSkuReqVo.setSkuId(skuIdList.get(0));
            GoodsSkuInfoDTO seckillGoodsSkuInfo = null;
            if (commitOrderByActivityRequestVo.getSourceType() < 2) {
                seckillGoodsSkuInfo = seckillService.getGoodsSkuInfo(goodsSkuReqVo);
            } else {
                com.cdzg.shop.cooperation.service.api.vo.request.activity.seckill.GoodsSkuReqVo goodsSkuReqVo2 = new com.cdzg.shop.cooperation.service.api.vo.request.activity.seckill.GoodsSkuReqVo();
                BeanCopyUtil.copyProperties(goodsSkuReqVo, goodsSkuReqVo2);
                seckillGoodsSkuInfo = BeanCopyUtil.copyProperties(cooperationSeckillService.getGoodsSkuInfo(goodsSkuReqVo2), GoodsSkuInfoDTO.class, 0);
            }
            if (seckillGoodsSkuInfo == null) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "沒有查詢到該活動或商品!");
            }
            long activitySelling = seckillGoodsSkuInfo.getSeckillPrice().multiply(BigDecimal.valueOf(100)).multiply(BigDecimal.valueOf(commitOrderByActivityRequestVo.getGoodsCount())).add(BigDecimal.valueOf(commitOrderByActivityRequestVo.getFreight())).longValue();
            if (!(totalMoney.longValue() == activitySelling)) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "訂單金額不匹配!提交失敗");
            }
        } else {
            String skuId = skuIdList.get(0);
            String groupPurchaseId = commitOrderByActivityRequestVo.getActivityId();
            QueryGoodsPriceRespVo queryGoodsPriceRespVo = new QueryGoodsPriceRespVo();
            //區分商城來源
            if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                com.cdzg.shop.cooperation.service.api.vo.response.activity.group.QueryGoodsPriceRespVo
                        queryGoodsPriceRespVo2 = cooperationGroupPurchaseService.queryGoodsPrice(skuId, groupPurchaseId);
                BeanCopyUtil.copyProperties(queryGoodsPriceRespVo2, queryGoodsPriceRespVo);
            } else {
                queryGoodsPriceRespVo = groupPurchaseService.queryGoodsPrice(skuId, groupPurchaseId);
            }

            if (queryGoodsPriceRespVo == null) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "沒有查詢到該活動或商品!");
            }
            long activitySelling = queryGoodsPriceRespVo.getGroupPurchasePrice().multiply(BigDecimal.valueOf(100)).multiply(BigDecimal.valueOf(commitOrderByActivityRequestVo.getGoodsCount())).add(BigDecimal.valueOf(commitOrderByActivityRequestVo.getFreight())).longValue();
            if (!(totalMoney.longValue() == activitySelling)) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "訂單金額不匹配!提交失敗");
            }
        }

        // 金額校驗結束


        // 提交訂單
        int i = 0;
        if (commitOrderByActivityRequestVo.getOrderType() == 1) {
            addOrderItemRequest.setOrderType(1);
            String skuId = commitOrderByActivityRequestVo.getSkuId();
            String key = STOCK + commitOrderByActivityRequestVo.getActivityId() + ":" + skuId;
            String lockKey = redissonLock + commitOrderByActivityRequestVo.getActivityId() + ":" + skuId;
            Boolean hasKey = redisTemplate.hasKey(key);
            /**
             * 提交訂單,預減庫存,利用分佈式鎖預減庫存
             */
            if (hasKey) {
                Boolean flag = RedissonUtil.tryLock(lockKey, TimeUnit.MILLISECONDS, 1000, 1000);
                if (!flag) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "服務器繁忙,請稍後重試");
                }
//                Boolean flag =redisLock.lock(key, 2000);
                log.info("秒殺鎖------------>" + flag);
                Integer stock = (Integer) redisTemplate.opsForValue().get(key);
                if (stock < commitOrderByActivityRequestVo.getGoodsCount()) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "商品庫存不足");
                }
                if (stock <= 0) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "當前商品已售罄");
                }
            } else {
                log.error("==>  團購秒殺訂單獲取商品skuId {} 庫存異常", skuId);
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "檢測商品庫存異常");
            }
            // 需要考慮訂單取消、訂單關閉、訂單超時後的緩存庫存返還,使用redis分佈式鎖+1
            try {
                // 庫存-1
                redisTemplate.opsForValue().increment(key, -commitOrderByActivityRequestVo.getGoodsCount());
            } catch (Exception e) {
                log.error("=>  分佈式鎖操作異常 " + e.getMessage());
                log.error("==>  商品SKU {} 預減庫存失敗 ");
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "提交異常");
            } finally {
                RedissonUtil.unlock(lockKey);
//                redisLock.releaseLock(key);
            }

            log.info("==>  商品SKU {} 預減庫存成功 ", skuId);

            // 秒殺訂單提交到MQ處理
            Map<String, Object> addOrderItemListMap = new HashMap<>();
            List<Map<String, Object>> secKillOrderMqList = new ArrayList<>();
            CommitOrderSeckillReqVo commitOrderSeckillReqVo = new CommitOrderSeckillReqVo();
            // 建立order_id和秒殺ID的關聯關係 用於支付回調的時候
            commitOrderSeckillReqVo.setOrderId(addOrderItemList.get(0).getOrderId());
            commitOrderSeckillReqVo.setSeckillId(commitOrderByActivityRequestVo.getActivityId());
            commitOrderSeckillReqVo.setGoodsId(commitOrderByActivityRequestVo.getGoodsId());
            commitOrderSeckillReqVo.setAppUserId(commitOrderByActivityRequestVo.getAppUserId());
            commitOrderSeckillReqVo.setPayNumber(commitOrderByActivityRequestVo.getPayNumber());
            commitOrderSeckillReqVo.setGoodsCount(commitOrderByActivityRequestVo.getGoodsCount());
            commitOrderSeckillReqVo.setSkuId(commitOrderByActivityRequestVo.getSkuId());
            Map<String, Object> commitOrderSeckillReqVoMap = new HashMap<>();
            addOrderItemListMap.put("addOrderItemListMap", addOrderItemList);
            commitOrderSeckillReqVoMap.put("commitOrderSeckillReqVoMap", commitOrderSeckillReqVo);
            secKillOrderMqList.add(addOrderItemListMap);
            secKillOrderMqList.add(commitOrderSeckillReqVoMap);
            new Thread() {
                @Override
                public void run() {
                    logger.info("==> 開始處理秒殺活動的訂單, id: {}");
                    orderService.commitOrderByActivity(addOrderItemList);
                    orderService.commitOrderSeckill(commitOrderSeckillReqVo);
                }
            }.start();
            QueryCallBackAddressRequestVo queryCallBackAddressRequestVo = new QueryCallBackAddressRequestVo();
            queryCallBackAddressRequestVo.setType((short) 5);
            ApiResponse apiResponse = orderItemService.queryCallbackAddress(queryCallBackAddressRequestVo);
            logger.info(apiResponse.getData().toString());
            String jsonObject = JSONObject.toJSONString(apiResponse.getData());
            logger.info(jsonObject);
            jsonObject1 = JSONObject.parseObject(jsonObject);
            logger.info(jsonObject1.toString());
        } else {
            addOrderItemRequest.setOrderType(2);
            String peroidId = commitOrderByActivityRequestVo.getPeroidId();
            String activityId = commitOrderByActivityRequestVo.getActivityId();
            String groupGoodsId = commitOrderByActivityRequestVo.getGroupGoodsId();

            String groupSmallId = null;
            Boolean flag = redisTemplate.hasKey(peroidId + "groupPurchase" + groupGoodsId);
            if (!flag) {
                String groupId = ObjectId.get().toString();
                redisTemplate.opsForValue().set(peroidId + "groupPurchase" + groupGoodsId, groupId);
                redisTemplate.opsForValue().set(groupId, 0);
                groupSmallId = groupId;
                QueryUserGroupPurchaseStatusReqVo queryUserGroupPurchaseStatusReqVo = new QueryUserGroupPurchaseStatusReqVo();
                queryUserGroupPurchaseStatusReqVo.setAppUserId(commitOrderByActivityRequestVo.getAppUserId());
                queryUserGroupPurchaseStatusReqVo.setGroupSmallId(groupId);
                queryUserGroupPurchaseStatusReqVo.setGoodsId(commitOrderByActivityRequestVo.getGoodsId());
                boolean flagUser;
                //區分商城來源
                if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                    com.cdzg.shop.cooperation.service.api.vo.request.activity.group.QueryUserGroupPurchaseStatusReqVo
                            queryUserGroupPurchaseStatusReqVo2 = new com.cdzg.shop.cooperation.service.api.vo.request.activity.group.QueryUserGroupPurchaseStatusReqVo();
                    BeanCopyUtil.copyProperties(queryUserGroupPurchaseStatusReqVo, queryUserGroupPurchaseStatusReqVo2);
                    flagUser = cooperationGroupPurchaseService.queryUserGroupPurchaseStatus(queryUserGroupPurchaseStatusReqVo2);
                } else {
                    flagUser = groupPurchaseService.queryUserGroupPurchaseStatus(queryUserGroupPurchaseStatusReqVo);
                }

                if (flagUser) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "您已經參與了該團購活動!");
                }
            } else {
//                redisLock.lock(peroidId+"groupPurchase"+groupGoodsId, 10);
                String groupId = redisTemplate.opsForValue().get(peroidId + "groupPurchase" + groupGoodsId).toString();
//                redisLock.lock(groupId, 10);
                if (!RedissonUtil.tryLock(redissonLock + peroidId + "groupPurchase" + groupGoodsId, TimeUnit.MILLISECONDS, 1000, 1000) || !RedissonUtil.tryLock(redissonLock + groupId, 1000, 1000)) {
                    return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "服務器繁忙!請稍後重試");
                }
                int groupCompeleteNumber = Integer.parseInt(redisTemplate.opsForValue().get(groupId).toString());
                if (groupCompeleteNumber == commitOrderByActivityRequestVo.getGroupNumber()) {
                    String groupIdNew = ObjectId.get().toString();
                    redisTemplate.opsForValue().set(peroidId + "groupPurchase" + groupGoodsId, groupIdNew);
                    redisTemplate.opsForValue().set(groupIdNew, 0);
                    RedissonUtil.unlock(redissonLock + peroidId + "groupPurchase" + groupGoodsId);
                    RedissonUtil.unlock(redissonLock + groupId);
//                    redisLock.releaseLock(peroidId+"groupPurchase"+groupGoodsId);
//                    redisLock.releaseLock(groupId);
                    groupSmallId = groupIdNew;
                } else {
                    groupSmallId = groupId;
                    RedissonUtil.unlock(redissonLock + peroidId + "groupPurchase" + groupGoodsId);
                    RedissonUtil.unlock(redissonLock + groupId);
//                    redisLock.releaseLock(peroidId+"groupPurchase"+groupGoodsId);
//                    redisLock.releaseLock(groupId);
                    QueryUserGroupPurchaseStatusReqVo queryUserGroupPurchaseStatusReqVo = new QueryUserGroupPurchaseStatusReqVo();
                    queryUserGroupPurchaseStatusReqVo.setAppUserId(commitOrderByActivityRequestVo.getAppUserId());
                    queryUserGroupPurchaseStatusReqVo.setGroupSmallId(groupId);
                    queryUserGroupPurchaseStatusReqVo.setGoodsId(commitOrderByActivityRequestVo.getGoodsId());
                    boolean flagUser;
                    //區分商城來源
                    if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                        com.cdzg.shop.cooperation.service.api.vo.request.activity.group.QueryUserGroupPurchaseStatusReqVo
                                queryUserGroupPurchaseStatusReqVo2 = new com.cdzg.shop.cooperation.service.api.vo.request.activity.group.QueryUserGroupPurchaseStatusReqVo();
                        BeanCopyUtil.copyProperties(queryUserGroupPurchaseStatusReqVo, queryUserGroupPurchaseStatusReqVo2);
                        flagUser = cooperationGroupPurchaseService.queryUserGroupPurchaseStatus(queryUserGroupPurchaseStatusReqVo2);
                    } else {
                        flagUser = groupPurchaseService.queryUserGroupPurchaseStatus(queryUserGroupPurchaseStatusReqVo);
                    }
                    if (flagUser) {
                        return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "您已經參與了該團購活動!");
                    }
                }
            }
            // 團購校驗和減庫存
            int groupPurchaseCount;
            //區分商城來源
            if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                groupPurchaseCount = cooperationGroupPurchaseService.reduceCount(commitOrderByActivityRequestVo.getGroupSkuId(), commitOrderByActivityRequestVo.getGoodsCount());
            } else {
                groupPurchaseCount = groupPurchaseService.reduceCount(commitOrderByActivityRequestVo.getGroupSkuId(), commitOrderByActivityRequestVo.getGoodsCount());
            }

            if (groupPurchaseCount == 0) {
                return ApiResponse.buildResponse(ApiConst.Code.CODE_COMMON_ERROR.code(), json, "團購商品庫存不足!");
            }
            orderService.commitOrderByActivity(addOrderItemList);
            CommitGroupOrderReqVo commitGroupOrderReqVo = new CommitGroupOrderReqVo();
            commitGroupOrderReqVo.setAppUserId(commitOrderByActivityRequestVo.getAppUserId());
            commitGroupOrderReqVo.setOrderId(commitOrderByActivityRequestVo.getOrderId());
            commitGroupOrderReqVo.setGroupId(activityId);
            commitGroupOrderReqVo.setGroupNumber(commitOrderByActivityRequestVo.getGroupNumber());
            commitGroupOrderReqVo.setGoodsId(commitOrderByActivityRequestVo.getGoodsId());
            commitGroupOrderReqVo.setGroupSmallId(groupSmallId);
            commitGroupOrderReqVo.setGroupPurchaseEndTime(commitOrderByActivityRequestVo.getGroupPurchaseEndTime());
            commitGroupOrderReqVo.setPeroidId(peroidId);
            commitGroupOrderReqVo.setPayNumber(commitOrderByActivityRequestVo.getPayNumber());
            commitGroupOrderReqVo.setGroupGoodsId(groupGoodsId);
            //區分商城來源
            if (commitOrderByActivityRequestVo.getSourceType() > 1) {
                com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CommitGroupOrderReqVo commitGroupOrderReqVo2 =
                        new com.cdzg.shop.cooperation.service.api.vo.request.activity.group.CommitGroupOrderReqVo();
                BeanCopyUtil.copyProperties(commitGroupOrderReqVo, commitGroupOrderReqVo2);
                cooperationGroupPurchaseService.commitGroupOrder(commitGroupOrderReqVo2);
            } else {
                groupPurchaseService.commitGroupOrder(commitGroupOrderReqVo);
            }
            // 查詢回調地址
            QueryCallBackAddressRequestVo queryCallBackAddressRequestVo = new QueryCallBackAddressRequestVo();
            queryCallBackAddressRequestVo.setType((short) 6);
            ApiResponse apiResponse = orderItemService.queryCallbackAddress(queryCallBackAddressRequestVo);
            logger.info(apiResponse.getData().toString());
            String jsonObject = JSONObject.toJSONString(apiResponse.getData());
            logger.info(jsonObject);
            jsonObject1 = JSONObject.parseObject(jsonObject);
            logger.info(jsonObject1.toString());
        }
        // 提交訂單成功返回參數
        // 查詢支付回調地址與數據一起返回給前段

        //  加銷量  ygf
        List<GoodsSaleLogInsertReqVo> batchGoodsSale = new ArrayList<>();
        addOrderItemList.stream().forEach(a -> {
            GoodsSaleLogInsertReqVo goodsSaleLogInsertReqVo = new GoodsSaleLogInsertReqVo();
            goodsSaleLogInsertReqVo.setCreateBy(a.getAppUserId());
            goodsSaleLogInsertReqVo.setGoodsId(a.getGoodsId());
            goodsSaleLogInsertReqVo.setHandleType(0);
            goodsSaleLogInsertReqVo.setSaleCount(a.getGoodsCount());
            goodsSaleLogInsertReqVo.setOrderId(a.getOrderId());
            batchGoodsSale.add(goodsSaleLogInsertReqVo);
        });
//        goodsGoodsService.batchGoodsSaleLogInsert(batchGoodsSale);
        List<Map<String, Object>> listMq = new ArrayList<>();
        Map<String, Object> mapMqRepertory = new HashMap<>();
        Map<String, Object> mapMqSale = new HashMap<>();
        mapMqRepertory.put("batchRepertoryOperationReqVo", "null");
        mapMqSale.put("batchGoodsSale", batchGoodsSale);
        listMq.add(mapMqRepertory);
        listMq.add(mapMqSale);
        rabbitmqUtil.orderUpdateGoods(listMq);
        String url = jsonObject1.getString("url");
        CommitOrderByActivityResponseVo commitOrderByActivityResponseVo = new CommitOrderByActivityResponseVo();
        commitOrderByActivityResponseVo.setPayNumber(payNumber);
        commitOrderByActivityResponseVo.setTotalCouponsAmount(commitOrderByActivityRequestVo.getCouponsAmount());
        commitOrderByActivityResponseVo.setTotalUnionAmount(commitOrderByActivityRequestVo.getUnionAmount());
        commitOrderByActivityResponseVo.setTotalSelling((int) totalMoney.longValue());
        commitOrderByActivityResponseVo.setTotalOriginalPrice(totalOriginalPrice);
        commitOrderByActivityResponseVo.setGoodsFavorablePrice(commitOrderByActivityRequestVo.getGoodsFavorablePrice());
        commitOrderByActivityResponseVo.setBackUrl(url);
        return ApiResponse.buildResponse(ApiConst.Code.CODE_SUCCESS.code(), commitOrderByActivityResponseVo, "提交成功");

          這上面這段代碼就是我的五一節電商活動中的秒殺、團購(拼單)的核心代碼,希望在做電商的夥伴有幫助!

          鐵汁們,三連走一波!愛你們

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章