Browse Source

perf:红包库存原子操作

zhangqin 1 month ago
parent
commit
a0905744a6

+ 39 - 30
fs-service-system/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -187,14 +187,6 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             return R.error("您已经领取过红包了!");
         }
         /*try {*/
-        Integer remaining = getRemaining(red.getRedId());
-        if (remaining <= 0) {
-            LiveRedConf liveRedConf = new LiveRedConf();
-            liveRedConf.setRedId(red.getRedId());
-            liveRedConf.setRedStatus(2L);
-            baseMapper.updateLiveRedConf(liveRedConf);
-            return R.error("手慢了,红包已被抢完~");
-        }
 /*            //获取红包锁
             if (!tryLock(claimKey, red.getUserId().toString(), 5)) {
                 return R.error("您已经领取过红包了!");
@@ -202,13 +194,13 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
 
         LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
         if (conf == null || conf.getRedStatus() != 1) {
-            return R.error("手慢了,红包已被抢完~");
+            return R.error("手慢了,红包已结束~");
         }
         //redis剩余红包数
         // 平均分 暂时不适用redis 记录红包数
         Long integral = calculateIntegralAverage(conf);
         if (0L == integral) {
-            return R.error("手慢了,红包被抢完~");
+            return R.error("手慢了,红包被抢完~");
         }
 
         // 更新数据库
@@ -220,6 +212,16 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         baseMapper.updateLiveRedConf(conf);
 */
 
+        // 最后更新缓存
+        if (getRemaining(red.getRedId()) <= 0 || !decreaseRemainingLotsIfPossible(red.getRedId())) {
+            LiveRedConf liveRedConf = new LiveRedConf();
+            liveRedConf.setRedId(red.getRedId());
+            liveRedConf.setRedStatus(2L);
+            baseMapper.updateLiveRedConf(liveRedConf);
+            Set<String> range = CollUtil.newHashSet(String.valueOf(red.getRedId()));
+            finishRedStatusBySetIds(range);
+            return R.error("手慢了,红包已被抢完~");
+        }
         // 记录用户红包
         LiveUserRedRecord record = new LiveUserRedRecord();
         record.setRedId(red.getRedId());
@@ -227,10 +229,6 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         record.setUserId(red.getUserId());
         record.setIntegral(integral);
         record.setCreateTime(new Date());
-        // userRedRecordMapper.insertLiveUserRedRecord(record);
-
-        // 最后更新缓存
-        decreaseRemainingLotsIfPossible(red.getRedId());
         // WebSocket 通知
         //String msg = String.format("用户 %d 抢到了红包 %d,获得 %d 芳华币", userId, redId, integral);
         //WebSocketServer.notifyUsers(msg);
@@ -275,12 +273,7 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             for (String s : range) {
                 LiveRedConf liveRedConf = baseMapper.selectLiveRedConfByRedId(Long.valueOf(s));
                 // 更新数据库
-                Date now = new Date();
-                Integer remaining = getRemaining(liveRedConf.getRedId());
-                liveRedConf.setTotalSend(liveRedConf.getTotalLots() - remaining);
-                liveRedConf.setRemaining(remaining);
-                liveRedConf.setUpdateTime(now);
-                baseMapper.updateLiveRedConf(liveRedConf);
+                updateDbByRed(liveRedConf);
                 String hashKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, liveRedConf.getLiveId(), liveRedConf.getRedId());
                 Map<Object, Object> hashEntries = redisUtil.hashEntries(hashKey);
                 List<LiveUserRedRecord> liveUserRedRecords = new ArrayList<>();
@@ -308,17 +301,22 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         liveRedConf.setRedStatus(1L);
         List<LiveRedConf> conf = baseMapper.selectLiveRedConfList(liveRedConf);
         for (LiveRedConf red : conf) {
-            // 更新数据库
-            Date now = new Date();
-            Integer remaining = getRemaining(red.getRedId());
-            red.setTotalSend(red.getTotalLots() - remaining);
-            red.setRemaining(remaining);
-            red.setUpdateTime(now);
-            baseMapper.updateLiveRedConf(red);
-            log.info("更新红包数据完成 {} {}",red.getRedId(),remaining);
+            updateDbByRed(red);
+
         }
     }
 
+    private void updateDbByRed(LiveRedConf red) {
+        // 更新数据库
+        Date now = new Date();
+        Integer remaining = getRemaining(red.getRedId());
+        red.setTotalSend(red.getTotalLots() - remaining);
+        red.setRemaining(remaining);
+        red.setUpdateTime(now);
+        baseMapper.updateLiveRedConf(red);
+        log.info("更新红包数据完成 {} {}",red.getRedId(),remaining);
+    }
+
     // 初始化剩余数量
     public void initRemainingLots(Long redId, Integer totalLots) {
         String key = REDPACKET_REMAININGLOTS_KEY + redId;
@@ -349,9 +347,20 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     }
 
     // 减少剩余数量(原子操作)
-    public void decreaseRemainingLotsIfPossible(Long redId) {
+    public boolean decreaseRemainingLotsIfPossible(Long redId) {
         String key = REDPACKET_REMAININGLOTS_KEY + redId;
-        redisCache.incrementCacheValue(key, -1);
+        // 通过lua脚本校验剩余数量是否大于0
+        String script = "local current = redis.call('GET', KEYS[1]) " +
+                "if current and tonumber(current) > 0 then " +
+                "   return redis.call('DECRBY', KEYS[1], 1) " +
+                "else " +
+                "   return -1 " +
+                "end";
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(script);
+        redisScript.setResultType(Long.class);
+        Object result = redisCache.redisTemplate.execute(redisScript, Collections.singletonList(key));
+        return result != null && Long.valueOf(result.toString()) >= 0;
     }
     // 减少剩余数量(原子操作)
     public void decreaseRemainingNumIfPossible(Long redId, Long integral) {