Pārlūkot izejas kodu

益寿缘app-完善App奖品重复新增的问题

cgp 6 dienas atpakaļ
vecāks
revīzija
9019117560

+ 5 - 0
fs-service/src/main/java/com/fs/his/domain/FsUserRewards.java

@@ -61,6 +61,11 @@ public class FsUserRewards {
      * */
     private Long rewardPoints;
 
+    /**
+     * 视频id
+     * */
+    private Long videoId;
+
     /**
      * 奖品实际领取时间
      */

+ 7 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserRewardsMapper.java

@@ -122,4 +122,11 @@ public interface FsUserRewardsMapper {
      * @param activityType 活动类型
      * */
     List<FsUserRewards> selectTodayListDataByUserIdAndActivityType(@Param("fsUserId") Long fsUserId,@Param("activityType") String activityType);
+
+    /**
+     * 根据用户ID和视频ID查询数据
+     * @param fsUserId 用户ID
+     * @param videoId 视频ID
+     * */
+    FsUserRewards selectByUserIdAndVideoId(@Param("fsUserId")Long fsUserId, @Param("videoId")Long videoId);
 }

+ 2 - 1
fs-service/src/main/java/com/fs/his/service/IAppUserRewardService.java

@@ -23,8 +23,9 @@ public interface IAppUserRewardService {
     /**
      * 为当前用户增加看课奖励待领取的奖励
      * @param fsUserId 用户ID
+     * @param videoId 视频小节id
      * */
-    public void addUserWatchCourseRewards(Long fsUserId);
+    public void addUserWatchCourseRewards(Long fsUserId,Long videoId);
 
     /**
      * 获取当前用户所有的奖励

+ 76 - 23
fs-service/src/main/java/com/fs/his/service/impl/AppUserRewardServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.his.service.impl;
 
+import me.chanjar.weixin.common.util.locks.RedisTemplateSimpleDistributedLock;
 import com.fs.app.domain.FsAppRole;
 import com.fs.app.mapper.FsAppRoleMapper;
 import com.fs.common.core.redis.RedisCache;
@@ -21,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DuplicateKeyException;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -53,8 +55,13 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
     private static final String APP_WATCH_COURSE_DAY_KEY ="app:watch:course:day:";
 
+    private static final String LOCK_WATCH_REWARD_KEY ="lock:watch:reward:";
+
     @Override
     public void claimRewards(ClaimRewardsAddDTO claimRewardsAddDTO) {
         Long fsUserId=claimRewardsAddDTO.getFsUserId();
@@ -98,11 +105,11 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
 
         FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
         if (appRoleConfig == null) {
-            log.info("当前用户未匹配到App奖励角色");
+            log.info("登录注册奖励-当前用户未匹配到App奖励角色");
             return;
         }
         // 构建奖品记录
-        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig);
+        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig,null);
         try {
             // 插入奖品表
             int insertResult = rewardsMapper.insertFsUserRewards(reward);
@@ -131,26 +138,73 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void addUserWatchCourseRewards(Long fsUserId) {
-        String activityType = ActivityTypeEnum.WATCH_COURSE.getCode();
+    public void addUserWatchCourseRewards(Long fsUserId,Long videoId) {
+        // 1. 生成锁的key:用户ID+视频ID
+        String lockKey = LOCK_WATCH_REWARD_KEY + fsUserId + ":" + videoId;
+        // 2. 创建分布式锁,设置10秒过期
+        RedisTemplateSimpleDistributedLock lock = new RedisTemplateSimpleDistributedLock(
+                stringRedisTemplate, lockKey, 10000); // 10秒
+        boolean locked = false;
+        try {
+            // 3. 尝试获取锁
+            locked = lock.tryLock();
+            if (!locked) {
+                log.warn("获取分布式锁失败,可能有其他请求正在处理: userId={}, videoId={}", fsUserId, videoId);
+                throw new CustomException("系统处理中,请勿重复操作");
+            }
 
-        //根据配置获取奖品类型、金额等
-        FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
-        if (appRoleConfig == null) {
-            log.info("当前用户未匹配到App奖励角色");
-            return;
-        }
+            log.info("获取分布式锁成功: userId={}, videoId={}, secret={}",
+                    fsUserId, videoId, lock.getLockSecretValue());
 
-        // 构建奖品记录 - 把appRoleConfig传进去,让build方法自己处理
-        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig);
+            // 4. 双重检查:再次确认是否已发放过奖励(防止锁内重复)
+            FsUserRewards existingReward = rewardsMapper.selectByUserIdAndVideoId(fsUserId, videoId);
+            if (existingReward != null) {
+                log.info("看课奖励已存在,不再重复发放: userId={}, videoId={}", fsUserId, videoId);
+                return; // 已存在,直接返回
+            }
 
-        // 插入奖品表
-        int insertResult = rewardsMapper.insertFsUserRewards(reward);
-        if (insertResult > 0) {
-            log.info("用户看课奖励发放成功: userId={}, rewardType={}, rewardsId={}",
-                    fsUserId, reward.getRewardType(), reward.getId());
-        } else {
-            log.error("用户看课奖励发放失败: userId={}", fsUserId);
+            // 5. 原有的业务逻辑
+            String activityType = ActivityTypeEnum.WATCH_COURSE.getCode();
+
+            // 根据配置获取奖品类型、金额等
+            FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
+            if (appRoleConfig == null) {
+                log.info("看课奖励-当前用户未匹配到App角色");
+                return;
+            }
+
+            // 构建奖品记录
+            FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig, videoId);
+
+            // 插入奖品表
+            try {
+                int insertResult = rewardsMapper.insertFsUserRewards(reward);
+                if (insertResult > 0) {
+                    log.info("用户看课奖励发放成功: userId={}, rewardType={}, rewardsId={}",
+                            fsUserId, reward.getRewardType(), reward.getId());
+                } else {
+                    log.error("用户看课奖励发放失败: userId={}", fsUserId);
+                }
+            } catch (DuplicateKeyException e) {
+                // 6. 唯一索引兜底
+                log.warn("看课奖励唯一索引冲突,已存在记录: userId={}, videoId={}", fsUserId, videoId);
+            }
+
+        } catch (DuplicateKeyException e) {
+            log.warn("看课奖励唯一索引冲突: userId={}, videoId={}", fsUserId, videoId);
+        } catch (Exception e) {
+            log.error("看课奖励发放异常: userId={}, videoId={}", fsUserId, videoId, e);
+            throw new CustomException("奖励发放失败,请稍后重试");
+        } finally {
+            // 7. 释放锁
+            if (locked) {
+                try {
+                    lock.unlock();
+                    log.debug("释放分布式锁: key={}", lockKey);
+                } catch (Exception e) {
+                    log.error("释放分布式锁异常: key={}", lockKey, e);
+                }
+            }
         }
     }
 
@@ -160,13 +214,12 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
      * @param activityType 活动类型
      * @param appRoleConfig 角色配置
      */
-    private FsUserRewards buildAppUserReward(Long fsUserId, String activityType, FsAppRole appRoleConfig) {
+    private FsUserRewards buildAppUserReward(Long fsUserId, String activityType, FsAppRole appRoleConfig,Long videoId) {
         FsUserRewards reward = new FsUserRewards();
         reward.setFsUserId(fsUserId);
         reward.setActivityType(activityType);
         reward.setStatus(0); // 待领取
         reward.setCreateTime(DateUtils.getNowDate());
-
         // 根据活动类型设置不同字段
         if (ActivityTypeEnum.FIRST_LOGIN.getCode().equals(activityType)) {
             // 首次登录奖励
@@ -190,14 +243,14 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
         } else if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(activityType)) {
             // 看课奖励
             reward.setRewardType(appRoleConfig.getCourseRewardType());
-
+            reward.setVideoId(videoId);
             // 获取当前用户的有效看课天数
             FsUser fsUser = fsUserMapper.selectFsUserById(fsUserId);
             if (fsUser == null) {
                 log.info("用户不存在,用户id:{}", fsUserId);
                 return reward;
             }
-            Integer targetDay = fsUser.getAppRewardsViewedDays() == null ? 0 : fsUser.getAppRewardsViewedDays();
+            //Integer targetDay = fsUser.getAppRewardsViewedDays() == null ? 0 : fsUser.getAppRewardsViewedDays();
 
             // 根据奖品类型设置具体值
             if (appRoleConfig.getCourseRewardType() == 1) { // 红包

+ 10 - 1
fs-service/src/main/resources/mapper/his/FsUserRewardsMapper.xml

@@ -21,13 +21,14 @@
         <result property="createTime" column="create_time"/>
         <result property="updateTime" column="update_time"/>
         <result property="isFirstLogin" column="is_first_login"/>
+        <result property="videoId" column="video_id"/>
     </resultMap>
 
     <!-- 基础字段列表 -->
     <sql id="selectFsUserRewardsVo">
         select id, fs_user_id, activity_type, reward_type, status,
                order_code, goods_id, product_type, reward_amount, reward_points,
-               goods_name, goods_url, grant_time, create_time, update_time, is_first_login
+               goods_name, goods_url, grant_time, create_time, update_time, is_first_login,video_id
         from fs_user_rewards
     </sql>
 
@@ -89,6 +90,7 @@
             <if test="createTime != null"> and create_time = #{createTime}</if>
             <if test="updateTime != null"> and update_time = #{updateTime}</if>
             <if test="isFirstLogin != null"> and is_first_login = #{isFirstLogin}</if>
+            <if test="videoId != null"> and video_id = #{videoId}</if>
         </where>
         order by create_time desc
     </select>
@@ -96,6 +98,10 @@
         <include refid="selectFsUserRewardsVo"/>
         where fs_user_id = #{fsUserId} and activity_type = #{activityType} and create_time &gt;= CURDATE() and create_time &lt; DATE_ADD(CURDATE(), INTERVAL 1 DAY)
     </select>
+    <select id="selectByUserIdAndVideoId" resultType="com.fs.his.domain.FsUserRewards">
+        <include refid="selectFsUserRewardsVo"/>
+        where fs_user_id = #{fsUserId} and video_id = #{videoId}
+    </select>
 
     <!-- 新增记录 -->
     <insert id="insertFsUserRewards" parameterType="com.fs.his.domain.FsUserRewards" useGeneratedKeys="true" keyProperty="id">
@@ -114,6 +120,7 @@
             <if test="goodsUrl != null and goodsUrl != ''">goods_url,</if>
             <if test="grantTime != null">grant_time,</if>
             <if test="isFirstLogin != null">is_first_login,</if>
+            <if test="videoId != null">video_id,</if>
             create_time,
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -130,6 +137,7 @@
             <if test="goodsUrl != null and goodsUrl != ''">#{goodsUrl},</if>
             <if test="grantTime != null">#{grantTime},</if>
             <if test="isFirstLogin != null">#{isFirstLogin},</if>
+            <if test="videoId != null">#{videoId},</if>
             sysdate(),
         </trim>
     </insert>
@@ -151,6 +159,7 @@
             <if test="goodsUrl != null and goodsUrl != ''">goods_url = #{goodsUrl},</if>
             <if test="grantTime != null">grant_time = #{grantTime},</if>
             <if test="isFirstLogin != null">is_first_login = #{isFirstLogin},</if>
+            <if test="videoId != null">video_id = #{videoId},</if>
             update_time = sysdate()
         </trim>
         where id = #{id}

+ 15 - 8
fs-user-app/src/main/java/com/fs/app/controller/AppUserRewardController.java

@@ -60,21 +60,28 @@ public class AppUserRewardController  extends AppBaseController{
     @ApiOperation("跳转下发App连续看课奖励")
     @GetMapping("/issueWatchCourseReward")
     @NoRepeatSubmit(expire = 10, message = "请勿重复点击,正在处理中...")
-    public R issueWatchCourseReward() {
+    public R issueWatchCourseReward(@RequestBody FsUserRewards queryRewards) {
         String loginUserId = getUserId();
-        if (StringUtils.isEmpty(loginUserId)){
+        if (StringUtils.isEmpty(loginUserId)) {
             return R.error("请登录");
         }
+        if (queryRewards.getVideoId() == null) {
+            return R.error("缺少视频参数");
+        }
         Long fsUserId = Long.valueOf(loginUserId);
-        boolean rewardAdded = appUserRewardService.checkFirstLoginRewardStatus(fsUserId);
-        if (rewardAdded) {
+        try {
             //添加看课奖励
-            appUserRewardService.addUserWatchCourseRewards(fsUserId);
-            log.info("用户App看课奖励添加成功: userId={}", loginUserId);
+            appUserRewardService.addUserWatchCourseRewards(fsUserId, queryRewards.getVideoId());
+            log.info("用户App看课奖励添加成功: userId={}, videoId={}", loginUserId, queryRewards.getVideoId());
+            return R.ok();
+        } catch (CustomException e) {
+            log.warn("看课奖励发放被拦截: {}", e.getMessage());
+            return R.error(e.getMessage());
+        } catch (Exception e) {
+            log.error("看课奖励发放异常", e);
+            return R.error("系统异常,请稍后重试");
         }
-        return R.ok();
     }
-
     /**
      * 查询我的奖品列表
      */