cup 2 жил өмнө
parent
commit
125b522523

+ 1 - 1
mp-admin/src/main/java/com/qs/mp/web/controller/api/user/MarketingController.java

@@ -140,7 +140,7 @@ public class MarketingController extends BaseApiController {
 
         String lockKey = String.format(MARKETING_REAL_NUM_LOCK, marketing.getId());
 
-        if (!distributedLocker.tryLock(lockKey,3,30, TimeUnit.SECONDS)) {
+        if (!distributedLocker.tryLock(lockKey)) {
             return AjaxResult.error("活动太火爆了,请稍后重试!");
         }
 

+ 20 - 1
mp-admin/src/main/java/com/qs/mp/web/controller/api/user/UserTicketController.java

@@ -13,10 +13,13 @@ import com.qs.mp.admin.domain.vo.*;
 import com.qs.mp.admin.service.*;
 import com.qs.mp.common.core.domain.AjaxResult;
 import com.qs.mp.common.core.page.TableDataInfo;
+import com.qs.mp.common.core.redis.DistributedLocker;
 import com.qs.mp.common.enums.*;
+import com.qs.mp.common.exception.ServiceException;
 import com.qs.mp.common.utils.DateUtils;
 import com.qs.mp.common.utils.LogUtil;
 import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.framework.redis.RedisLockKey;
 import com.qs.mp.framework.security.handle.HostHolder;
 import com.qs.mp.user.domain.UserHitPrize;
 import com.qs.mp.user.domain.UserTicketOrderItem;
@@ -87,6 +90,9 @@ public class UserTicketController extends BaseApiController {
   @Autowired
   private ITicketAwardsService ticketAwardsService;
 
+  @Autowired
+  private DistributedLocker distributedLocker;
+
   /**
    * 盲票进货列表
    */
@@ -309,7 +315,20 @@ public class UserTicketController extends BaseApiController {
       return AjaxResult.error(ErrorCodeEnum.ERROR_CODE_1019);
     }
 
-    userHitPrizeService.cashPrize(ticket, userId, param.getAwardsId(), param.getPrizeId());
+    // 兑奖
+    String lockKey = RedisLockKey.build(RedisLockKey.USER_TICKET_CASH_LOCK, param.getPrizeId());
+    if (distributedLocker.tryLock(lockKey)) {
+      return AjaxResult.error("系统繁忙,请稍后再试");
+    }
+    try {
+      // 兑奖
+      userHitPrizeService.cashPrize(ticket, userId, param.getAwardsId(), param.getPrizeId());
+    } catch (Exception e) {
+      LogUtil.error(logger,"盲票兑奖异常",e);
+    } finally {
+      distributedLocker.unlock(lockKey);
+    }
+
     return AjaxResult.success();
   }
 

+ 1 - 2
mp-common/src/main/java/com/qs/mp/common/core/redis/RedissonDistributedLocker.java

@@ -45,8 +45,7 @@ public class RedissonDistributedLocker implements DistributedLocker {
 
     @Override
     public boolean tryLock(String lockKey) {
-        // leaseTime = -1则会自动续期
-        return tryLock(lockKey, 0, -1, TimeUnit.SECONDS);
+        return tryLock(lockKey, 3, 10, TimeUnit.SECONDS);
     }
 
     @Override

+ 27 - 42
mp-quartz/src/main/java/com/qs/mp/quartz/task/MarketingTask.java

@@ -15,6 +15,8 @@ import com.qs.mp.common.enums.UserTypeEnum;
 import com.qs.mp.common.utils.DateUtils;
 import com.qs.mp.common.utils.LogUtil;
 import com.qs.mp.framework.domain.WxSubscribeMessage;
+import com.qs.mp.framework.redis.RedisKey;
+import com.qs.mp.framework.redis.RedisLockKey;
 import com.qs.mp.framework.service.IWxSubscribeMessage;
 import com.qs.mp.user.domain.MarketingUserCode;
 import com.qs.mp.user.service.IMarketingUserCodeService;
@@ -41,10 +43,6 @@ import java.util.concurrent.TimeUnit;
 @Component("marketingTask")
 public class MarketingTask {
 
-    public static final String MARKETING_LOTTERY_KEY = "MARKETING_LOTTERY_KEY:%s";
-
-    public static final String MARKETING_SEND_KEY = "MARKETING_SEND_KEY";
-
 
     @Autowired
     private DistributedLocker distributedLocker;
@@ -76,9 +74,9 @@ public class MarketingTask {
         }
 
         for (Marketing marketing : marketingList) {
-            String lockKey = String.format(MARKETING_LOTTERY_KEY, marketing.getId());
-            // 加锁
-            if (!distributedLocker.tryLock(lockKey)) {
+            String lockKey = RedisLockKey.build(RedisLockKey.MARKETING_LOTTERY_KEY, marketing.getId());
+            // 加锁,自动续期
+            if (!distributedLocker.tryLock(lockKey,0,-1,TimeUnit.SECONDS)) {
                 continue;
             }
             try {
@@ -108,48 +106,35 @@ public class MarketingTask {
      * 发送活动开始通知
      */
     public void sendMessage() {
-        // 加锁防止频繁发送
-        if (!distributedLocker.tryLock(MARKETING_SEND_KEY)) {
+        // 获取开始时间小于等于当前时间且未发开始通知的活动
+        Date now = DateUtils.getNowDate();
+        List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
+                .eq(Marketing::getTriggerStatus, 0)
+                .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
+                .eq(Marketing::getIsSend, 0)
+                .le(Marketing::getStartTime, now));
+        if (CollectionUtils.isEmpty(marketingList)) {
             return;
         }
 
-        try{
-            // 获取开始时间小于等于当前时间且未发开始通知的活动
-            Date now = DateUtils.getNowDate();
-            List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
-                    .eq(Marketing::getTriggerStatus, 0)
-                    .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
-                    .eq(Marketing::getIsSend, 0)
-                    .le(Marketing::getStartTime, now));
-            if (CollectionUtils.isEmpty(marketingList)) {
-                return;
-            }
-
-            // 获取所有参与过活动的普通用户
-            List<MarketingUserCode> userCodeList = marketingUserCodeService.list(new LambdaQueryWrapper<MarketingUserCode>()
-                    .eq(MarketingUserCode::getUserType, UserTypeEnum.ORDINARY.getValue()));
-            if (CollectionUtils.isEmpty(userCodeList)) {
-                return;
-            }
-
-            for (Marketing marketing : marketingList) {
-                for (MarketingUserCode marketingUserCode : userCodeList) {
-                    // 发送开始订阅通知
-                    wxSubscribeMessage.sendMarketingStart(marketingUserCode.getUserId(), marketing);
-                }
+        // 获取所有参与过活动的普通用户
+        List<MarketingUserCode> userCodeList = marketingUserCodeService.list(new LambdaQueryWrapper<MarketingUserCode>()
+                .eq(MarketingUserCode::getUserType, UserTypeEnum.ORDINARY.getValue()));
+        if (CollectionUtils.isEmpty(userCodeList)) {
+            return;
+        }
 
-                // 更新活动通知发送状态
-                marketingService.update(new LambdaUpdateWrapper<Marketing>()
-                        .set(Marketing::getIsSend, 1)
-                        .eq(Marketing::getId, marketing.getId()));
+        for (Marketing marketing : marketingList) {
+            for (MarketingUserCode marketingUserCode : userCodeList) {
+                // 发送开始订阅通知
+                wxSubscribeMessage.sendMarketingStart(marketingUserCode.getUserId(), marketing);
             }
 
-
-        } finally {
-            // 释放锁
-            distributedLocker.unlock(MARKETING_SEND_KEY);
+            // 更新活动通知发送状态
+            marketingService.update(new LambdaUpdateWrapper<Marketing>()
+                    .set(Marketing::getIsSend, 1)
+                    .eq(Marketing::getId, marketing.getId()));
         }
-
     }
 
     /**

+ 28 - 0
mp-service/src/main/java/com/qs/mp/framework/redis/RedisLockKey.java

@@ -0,0 +1,28 @@
+package com.qs.mp.framework.redis;
+
+import com.qs.mp.common.utils.MessageHelper;
+
+/**
+ * redis Lock Key管理模版
+ *
+ * @author duota
+ *
+ */
+public enum RedisLockKey {
+
+    MARKETING_LOTTERY_KEY("marketing_lottery_key_{0}","免费抽奖活动开奖"),
+    USER_TICKET_CASH_LOCK("user_ticket_cash_lock_{0}", "盲票兑奖锁");
+
+    public String keyTemplate;
+    public String desc;
+
+    RedisLockKey(String keyTemplate, String desc) {
+        this.keyTemplate = keyTemplate;
+        this.desc = desc;
+    }
+
+    public static String build(RedisLockKey redisLockKey, Object... param) {
+        return MessageHelper.formatMsg(redisLockKey.keyTemplate, param);
+    }
+
+}

+ 20 - 3
mp-service/src/main/java/com/qs/mp/user/service/impl/UserHitPrizeServiceImpl.java

@@ -10,10 +10,12 @@ import com.qs.mp.admin.domain.dto.TicketDrawNumDTO;
 import com.qs.mp.admin.domain.vo.TicketListVO;
 import com.qs.mp.admin.service.*;
 import com.qs.mp.common.core.domain.AjaxResult;
+import com.qs.mp.common.core.redis.DistributedLocker;
 import com.qs.mp.common.enums.*;
 import com.qs.mp.common.exception.ServiceException;
 import com.qs.mp.common.utils.LogUtil;
 import com.qs.mp.common.utils.StringUtils;
+import com.qs.mp.framework.redis.RedisLockKey;
 import com.qs.mp.system.service.id.BizIdGenerator;
 import com.qs.mp.user.domain.UserHitPrize;
 import com.qs.mp.user.domain.UserTicketOrderItem;
@@ -85,6 +87,9 @@ public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, Use
     @Autowired
     private IUserHitPrizeService userHitPrizeService;
 
+    @Autowired
+    private DistributedLocker distributedLocker;
+
     @Override
     public List<TicketAwardsPrize> listPrize(Ticket ticket, Long userId) {
         UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));
@@ -120,7 +125,6 @@ public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, Use
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class)
     public UserHitPrizeDetailVO autoCashPrize(Long userId, TicketAutoCashPrizeParam param) {
 
         UserHitPrizeDetailVO userHitPrizeDetailVO = new UserHitPrizeDetailVO();
@@ -205,7 +209,18 @@ public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, Use
             userHitPrizeDetailVO.setRefId(ticketAwardsPrize.getRefId());
 
             // 兑奖
-            this.cashPrize(ticket, userId, ticketAwardsPrize.getAwardsId(), ticketAwardsPrize.getPrizeId());
+            String lockKey = RedisLockKey.build(RedisLockKey.USER_TICKET_CASH_LOCK, ticketAwardsPrize.getPrizeId());
+            if (distributedLocker.tryLock(lockKey)) {
+                throw new ServiceException("系统繁忙,请稍后再试");
+            }
+            try {
+                // 兑奖
+                this.cashPrize(ticket, userId, ticketAwardsPrize.getAwardsId(), ticketAwardsPrize.getPrizeId());
+            } catch (Exception e) {
+                LogUtil.error(logger,"线上盲票兑奖异常",e);
+            } finally {
+                distributedLocker.unlock(lockKey);
+            }
         }
 
         return userHitPrizeDetailVO;
@@ -258,9 +273,11 @@ public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, Use
             throw new ServiceException("奖品已兑完,请重新选择");
         }
         // 乐观锁
-        ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty() + 1)
+        boolean result = ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty() + 1)
                 .set(TicketAwardsPrize::getRemainQty, ticketAwardsPrize.getRemainQty() - 1)
                 .eq(TicketAwardsPrize::getPrizeId, prizeId).eq(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty()));
+        Assert.isTrue(result,"兑奖后更新奖品库存失败。prizeId:" + prizeId);
+
 
         // 保存奖品到中奖记录中
         UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));