zhangqin 1 month ago
parent
commit
1ac6f6cfa7

+ 8 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveLotteryRegistrationMapper.java

@@ -37,6 +37,14 @@ public interface LiveLotteryRegistrationMapper {
      */
     int insertLiveLotteryRegistration(LiveLotteryRegistration liveLotteryRegistration);
 
+    /**
+     * 批量插入抽奖注册记录
+     * @param registrationList 抽奖注册记录列表
+     * @return 结果
+     */
+    int insertLiveLotteryRegistrationBatch(List<LiveLotteryRegistration> registrationList);
+
+
     /**
      * 修改直播抽奖登记
      *

+ 2 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveUserRedRecordMapper.java

@@ -80,4 +80,6 @@ public interface LiveUserRedRecordMapper {
 
     @Select("SELECT EXISTS(SELECT 1 FROM live_user_red_record WHERE user_id = #{userId} AND red_id = #{redId})")
     boolean existsByUserIdAndRedId(Long userId, String redId);
+
+    void insertLiveUserRedRecordBatch(List<LiveUserRedRecord> liveUserRedRecords);
 }

+ 3 - 0
fs-service-system/src/main/java/com/fs/live/service/ILiveRedConfService.java

@@ -83,4 +83,7 @@ public interface ILiveRedConfService {
 
     // 结算掉红包
     void finishRedStatusBySetIds(Set<String> range);
+
+    // 更新红包数量
+    void redStatusUpdate();
 }

+ 108 - 61
fs-service-system/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -1,12 +1,15 @@
 package com.fs.live.service.impl;
 
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisUtil;
 import com.fs.common.utils.DateUtils;
+import com.fs.live.domain.LiveLotteryRegistration;
 import com.fs.live.domain.LiveRedConf;
 import com.fs.live.domain.LiveUserRedRecord;
 import com.fs.live.mapper.LiveRedConfMapper;
@@ -22,12 +25,10 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * 直播红包记录配置Service业务层处理
@@ -180,68 +181,66 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     @Override
     @Transactional
     public R claimRedPacket(RedPO red) {
-        String claimKey = REDPACKET_CLAIM_KEY + red.getRedId();
-
-        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("手慢了,红包已被抢完~");
-            }
-            //获取红包锁
+        // String claimKey = REDPACKET_CLAIM_KEY + red.getRedId();
+        Object o = redisUtil.hashGet(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()));
+        if (ObjectUtil.isNotEmpty(o)) {
+            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("您已经领取过红包了!");
-            }
+            }*/
 
-            LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
-            if (conf == null || conf.getRedStatus() != 1) {
-                return R.error("手慢了,红包已被抢完~");
-            }
-            //redis剩余红包数
-            // 平均分 暂时不适用redis 记录红包数
+        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
+        if (conf == null || conf.getRedStatus() != 1) {
+            return R.error("手慢了,红包已被抢完~");
+        }
+        //redis剩余红包数
+        // 平均分 暂时不适用redis 记录红包数
+        Long integral = calculateIntegralAverage(conf);
+        if (0L == integral) {
+            return R.error("手慢了,红包已被抢完~");
+        }
 
-            Long integral = calculateIntegralAverage(conf);
-            if (0L == integral) {
-                return R.error("手慢了,红包已被抢完~");
-            }
-            Date now = new Date();
-            //剩余金额
-//            Integer remainingNum = getRemainingNum(red.getRedId());
-
-
-            conf.setTotalSend(conf.getTotalSend() + 1);
-            conf.setRemaining(Math.toIntExact(conf.getTotalLots() - conf.getTotalSend()));
-            conf.setUpdateTime(now);
-
-            userService.incrIntegral(Collections.singletonList(red.getUserId()), integral);
-
-            // 更新数据库和缓存
-            baseMapper.updateLiveRedConf(conf);
-            decreaseRemainingLotsIfPossible(red.getRedId());
-//            decreaseRemainingNumIfPossible(red.getRedId(), integral);
-
-            // 记录用户红包
-            LiveUserRedRecord record = new LiveUserRedRecord();
-            record.setRedId(red.getRedId());
-            record.setLiveId(red.getLiveId());
-            record.setUserId(red.getUserId());
-            record.setIntegral(integral);
-            record.setCreateTime(now);
-            userRedRecordMapper.insertLiveUserRedRecord(record);
-
-            // WebSocket 通知
-            //String msg = String.format("用户 %d 抢到了红包 %d,获得 %d 芳华币", userId, redId, integral);
-            //WebSocketServer.notifyUsers(msg);
-            redisUtil.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
-            return R.ok("恭喜您成功抢到"+ integral+"芳华币");
-        } catch (Exception e) {
+        // 更新数据库
+/*
+        Date now = new Date();
+        conf.setTotalSend(conf.getTotalSend() + 1);
+        conf.setRemaining(Math.toIntExact(conf.getTotalLots() - conf.getTotalSend()));
+        conf.setUpdateTime(now);
+        baseMapper.updateLiveRedConf(conf);
+*/
+
+        // 记录用户红包
+        LiveUserRedRecord record = new LiveUserRedRecord();
+        record.setRedId(red.getRedId());
+        record.setLiveId(red.getLiveId());
+        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);
+        redisUtil.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
+        return R.ok("恭喜您成功抢到" + integral + "芳华币");
+/*        } catch (Exception e) {
             e.printStackTrace();
             log.error("抢红包异常:" + e.getMessage());
-        }
-        return R.error("抢红包异常");
+        }*/
+        // return R.error("抢红包异常");
     }
 
     @Override
@@ -269,7 +268,55 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
 
     @Override
     public void finishRedStatusBySetIds(Set<String> range) {
-        baseMapper.finishRedStatusBySetIds(range);
+        try {
+            log.info("开始结束红包状态:{}",range);
+            baseMapper.finishRedStatusBySetIds(range);
+            // 插入抽奖记录
+            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);
+                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<>();
+                if (CollUtil.isNotEmpty(hashEntries)) {
+                    liveUserRedRecords = hashEntries.values().stream()
+                            .map(value -> JSONUtil.toBean(JSONUtil.parseObj(value), LiveUserRedRecord.class))
+                            .collect(Collectors.toList());
+                    userRedRecordMapper.insertLiveUserRedRecordBatch(liveUserRedRecords);
+                    for (LiveUserRedRecord liveUserRedRecord : liveUserRedRecords) {
+                        userService.incrIntegral(Collections.singletonList(liveUserRedRecord.getUserId()), liveUserRedRecord.getIntegral());
+                    }
+                }
+                redisUtil.delete(hashKey);
+            }
+            log.info("结束红包状态完成");
+        }catch (Exception e){
+            log.info("红包状态结束异常",e);
+        }
+
+    }
+
+    @Override
+    public void redStatusUpdate() {
+        LiveRedConf liveRedConf = new LiveRedConf();
+        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);
+        }
     }
 
     // 初始化剩余数量

+ 29 - 0
fs-service-system/src/main/resources/mapper/live/LiveLotteryRegistrationMapper.xml

@@ -58,6 +58,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
          </trim>
     </insert>
 
+
+    <!-- 批量插入抽奖注册记录 -->
+    <insert id="insertLiveLotteryRegistrationBatch" parameterType="java.util.List">
+        INSERT INTO live_lottery_registration (
+        live_id,
+        user_id,
+        lottery_id,
+        is_win,
+        rize_level,
+        create_time,
+        update_time,
+        create_by,
+        update_by
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.liveId},
+            #{item.userId},
+            #{item.lotteryId},
+            #{item.isWin},
+            #{item.rizeLevel},
+            #{item.createTime},
+            #{item.updateTime},
+            #{item.createBy},
+            #{item.updateBy}
+            )
+        </foreach>
+    </insert>
+
     <update id="updateLiveLotteryRegistration" parameterType="LiveLotteryRegistration">
         update live_lottery_registration
         <trim prefix="SET" suffixOverrides=",">

+ 20 - 0
fs-service-system/src/main/resources/mapper/live/LiveUserRedRecordMapper.xml

@@ -83,4 +83,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <!-- 批量插入用户红包记录 -->
+    <insert id="insertLiveUserRedRecordBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO live_user_red_record
+        (red_id, live_id, user_id, integral, create_time, update_time, create_by, update_by)
+        VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.redId},
+            #{item.liveId},
+            #{item.userId},
+            #{item.integral},
+            #{item.createTime},
+            #{item.updateTime},
+            #{item.createBy},
+            #{item.updateBy}
+            )
+        </foreach>
+    </insert>
+
 </mapper>

+ 3 - 3
fs-user-app/src/main/java/com/fs/app/controller/LiveLotteryController.java

@@ -21,13 +21,13 @@ public class LiveLotteryController extends AppBaseController{
     private LiveFacadeService liveFacadeService;
 
 
-    /**
+/*    *//**
      * 参与抽奖
-     * */
+     * *//*
     @RequestMapping("/participate")
     public String participate(Long liveId, Long userId,Long lotteryId) {
         return null;
-    }
+    }*/
 
     /**
      * 参与抽奖

+ 3 - 2
fs-user-app/src/main/java/com/fs/app/exception/FSExceptionHandler.java

@@ -63,12 +63,13 @@ public class FSExceptionHandler {
 			return R.error(defaultMessage);
 		}
 
-		if(e instanceof IllegalArgumentException){
+/*		if(e instanceof IllegalArgumentException){
 			return R.error(e.getMessage());
 		} else {
 			return R.error();
-		}
+		}*/
 
+		return R.error(e.getMessage());
 	}
 	@ExceptionHandler(BindException.class)
 	public R bindExceptionHandler(BindException e) {

+ 7 - 3
fs-user-app/src/main/java/com/fs/app/facade/impl/LiveFacadeServiceImpl.java

@@ -53,6 +53,9 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
     @Autowired
     private ILiveRedConfService iLiveRedConfService;
 
+    @Autowired
+    private ILiveLotteryConfService liveLotteryConfService;
+
     @Override
     public R liveList(PageRequest pageRequest) {
         int start = (pageRequest.getCurrentPage() - 1) * pageRequest.getPageSize();
@@ -154,12 +157,12 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
     }
 
     @Override
+    @DistributeLock(keyExpression = "#red.redId +'_'+#red.userId", scene = "red_claim", waitTime = 1000, errorMsg = "红包领取失败")
     public R redClaim(RedPO red) {
         return iLiveRedConfService.claimRedPacket(red);
     }
-
     @Override
-    @DistributeLock(keyExpression = "#lottery.liveId +'_' #lottery.userId", scene = "draw_claim")
+    @DistributeLock(keyExpression = "#lottery.liveId +'_'+#lottery.userId", scene = "draw_claim")
     public R drawClaim(LotteryPO lottery) {
         Object o = redisUtil.hashGet(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_DRAW, lottery.getLiveId(), lottery.getLotteryId()), String.valueOf(lottery.getUserId()));
         if (ObjectUtil.isNotEmpty(o)) {
@@ -175,7 +178,8 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
         registration.setCreateTime(now);
         registration.setUpdateTime(now);
         redisUtil.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_DRAW, lottery.getLiveId(), lottery.getLotteryId()), String.valueOf(lottery.getUserId()), JSONUtil.toJsonStr(registration));
-        liveLotteryRegistrationService.insertLiveLotteryRegistration(registration);
+        // 通过定时任务入库
+        // liveLotteryRegistrationService.insertLiveLotteryRegistration(registration);
         return R.ok("参与抽奖成功!请在直播间等待开奖");
     }
 }

+ 33 - 1
fs-user-app/src/main/java/com/fs/app/task/Task.java

@@ -1,14 +1,20 @@
 package com.fs.app.task;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.app.facade.LiveFacadeService;
 import com.fs.app.vo.LotteryVo;
 import com.fs.app.websocket.bean.SendMsgVo;
 import com.fs.app.websocket.service.WebSocketServer;
 import com.fs.common.annotation.QuartzRunnable;
+import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.core.redis.RedisUtil;
 import com.fs.common.utils.StringUtils;
+import com.fs.core.aspectj.lock.DistributeLock;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
 import com.fs.erp.dto.ErpOrderQueryRequert;
@@ -20,6 +26,7 @@ import com.fs.live.domain.*;
 import com.fs.live.mapper.LiveLotteryRegistrationMapper;
 import com.fs.live.param.LiveReplayParam;
 import com.fs.live.service.*;
+import com.fs.live.vo.LiveConfigVo;
 import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductListVo;
 import com.fs.store.domain.FsExpress;
@@ -96,8 +103,10 @@ public class Task {
 
     @Autowired
     public FsJstAftersalePushService fsJstAftersalePushService;
-
+    @Autowired
+    public RedisUtil redisUtil;
     @Scheduled(cron = "0 0/1 * * * ?")
+    @DistributeLock(key = "updateLiveStatusByTime", scene = "task")
     //public void selectSopUserLogsListByTime() {
     public void updateLiveStatusByTime() {
         List<Live> list = liveService.selectNoEndLiveList();
@@ -161,6 +170,7 @@ public class Task {
         }
     }
     @Scheduled(cron = "0/1 * * * * ?")
+    @DistributeLock(key = "liveLotteryTask", scene = "task")
     public void liveLotteryTask() {
         long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
         String lotteryKey = "live:lottery_task:*";
@@ -207,6 +217,17 @@ public class Task {
             List<LiveLotteryProductListVo> products = liveLottery.getProducts();
             Integer totalLots = products.stream().mapToInt(liveLotteryProductListVo -> Math.toIntExact(liveLotteryProductListVo.getTotalLots())).sum();
             if(totalLots <= 0) continue;
+            // 先将参与记录插入数据库
+            String hashKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_DRAW, liveLottery.getLiveId(), liveLottery.getLotteryId());
+            Map<Object, Object> hashEntries = redisUtil.hashEntries(hashKey);
+            List<LiveLotteryRegistration> registrationList = new ArrayList<>();
+            if (CollUtil.isNotEmpty(hashEntries)) {
+                registrationList = hashEntries.values().stream()
+                        .map(value -> JSONUtil.toBean(JSONUtil.parseObj(value), LiveLotteryRegistration.class))
+                        .collect(Collectors.toList());
+                liveLotteryRegistrationMapper.insertLiveLotteryRegistrationBatch(registrationList);
+            }
+
             // 查询在线用户 并且参与了抽奖的用户
             List<LiveWatchUser> liveWatchUsers = liveWatchUserService.selectLiveWatchAndRegisterUser(liveLottery.getLiveId(),liveLottery.getLotteryId(), totalLots);
             if(liveWatchUsers.isEmpty()) continue;
@@ -254,6 +275,9 @@ public class Task {
             sendMsgVo.setCmd("LotteryDetail");
             sendMsgVo.setData(JSON.toJSONString(lotteryVos));
             webSocketServer.broadcastMessage(liveLottery.getLiveId(), JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+
+            // 删除缓存 同步抽奖记录
+            redisUtil.delete(hashKey);
         }
 
         List<Long> collect = liveLotteries.stream().map(LiveLotteryConfVo::getLotteryId).collect(Collectors.toList());
@@ -261,6 +285,7 @@ public class Task {
     }
 
     @Scheduled(cron = "0/1 * * * * ?")
+    @DistributeLock(key = "liveAutoTask", scene = "task")
     public void liveAutoTask() {
         long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
 
@@ -292,6 +317,7 @@ public class Task {
     }
 
     @Scheduled(cron = "0 0/1 * * * ?")
+    @DistributeLock(key = "autoUpdateWatchReward", scene = "task")
     @Transactional
     public void autoUpdateWatchReward() {
 
@@ -341,4 +367,10 @@ public class Task {
         }
     }
 
+
+    @Scheduled(cron = "0 0/1 * * * ?")
+    @DistributeLock(key = "redStatusUpdate", scene = "task")
+    public void redStatusUpdate() {
+        liveRedConfService.redStatusUpdate();
+    }
 }

+ 2 - 0
fs-user-app/src/main/java/com/fs/core/aspectj/lock/DistributeLock.java

@@ -47,6 +47,8 @@ public @interface DistributeLock {
      */
     public int expireTime() default DistributeLockConstant.DEFAULT_EXPIRE_TIME;
 
+    public String errorMsg() default DistributeLockConstant.ERROR_MSG;
+
     /**
      * 加锁等待时长,毫秒
      * 默认情况下不设置等待时长,会一直等待直到获取到锁

+ 3 - 5
fs-user-app/src/main/java/com/fs/core/aspectj/lock/DistributeLockAspect.java

@@ -33,7 +33,7 @@ public class DistributeLockAspect {
     private static final Logger LOG = LoggerFactory.getLogger(DistributeLockAspect.class);
 
     @Around("@annotation(com.fs.core.aspectj.lock.DistributeLock)")
-    public Object process(ProceedingJoinPoint pjp) throws Exception {
+    public Object process(ProceedingJoinPoint pjp) throws Throwable {
         Object response = null;
         Method method = ((MethodSignature) pjp.getSignature()).getMethod();
         DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
@@ -96,15 +96,13 @@ public class DistributeLockAspect {
 
             if (!lockResult) {
                 LOG.warn(String.format("lock failed for key : %s , expire : %s", lockKey, expireTime));
-                throw new DistributeLockException("acquire lock failed... key : " + lockKey);
+                throw new DistributeLockException(distributeLock.errorMsg());
             }
 
 
             LOG.info(String.format("lock success for key : %s , expire : %s", lockKey, expireTime));
             response = pjp.proceed();
-        } catch (Throwable e) {
-            throw new Exception(e);
-        } finally {
+        }  finally {
             if (rLock.isHeldByCurrentThread()) {
                 rLock.unlock();
                 LOG.info(String.format("unlock for key : %s , expire : %s", lockKey, expireTime));

+ 1 - 0
fs-user-app/src/main/java/com/fs/core/aspectj/lock/DistributeLockConstant.java

@@ -9,4 +9,5 @@ public class DistributeLockConstant {
     public static final int DEFAULT_EXPIRE_TIME = -1;
 
     public static final int DEFAULT_WAIT_TIME = Integer.MAX_VALUE;
+    public static final String ERROR_MSG  = "请勿重复操作";
 }