|
@@ -1,5 +1,6 @@
|
|
|
package com.fs.his.service.impl;
|
|
package com.fs.his.service.impl;
|
|
|
|
|
|
|
|
|
|
+import me.chanjar.weixin.common.util.locks.RedisTemplateSimpleDistributedLock;
|
|
|
import com.fs.app.domain.FsAppRole;
|
|
import com.fs.app.domain.FsAppRole;
|
|
|
import com.fs.app.mapper.FsAppRoleMapper;
|
|
import com.fs.app.mapper.FsAppRoleMapper;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
@@ -21,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.collections4.CollectionUtils;
|
|
import org.apache.commons.collections4.CollectionUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
import org.springframework.dao.DuplicateKeyException;
|
|
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
@@ -53,8 +55,13 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private RedisCache redisCache;
|
|
private RedisCache redisCache;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private StringRedisTemplate stringRedisTemplate;
|
|
|
|
|
+
|
|
|
private static final String APP_WATCH_COURSE_DAY_KEY ="app:watch:course:day:";
|
|
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
|
|
@Override
|
|
|
public void claimRewards(ClaimRewardsAddDTO claimRewardsAddDTO) {
|
|
public void claimRewards(ClaimRewardsAddDTO claimRewardsAddDTO) {
|
|
|
Long fsUserId=claimRewardsAddDTO.getFsUserId();
|
|
Long fsUserId=claimRewardsAddDTO.getFsUserId();
|
|
@@ -98,11 +105,11 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
|
|
|
|
|
|
|
|
FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
|
|
FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
|
|
|
if (appRoleConfig == null) {
|
|
if (appRoleConfig == null) {
|
|
|
- log.info("当前用户未匹配到App奖励角色");
|
|
|
|
|
|
|
+ log.info("登录注册奖励-当前用户未匹配到App奖励角色");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
// 构建奖品记录
|
|
// 构建奖品记录
|
|
|
- FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig);
|
|
|
|
|
|
|
+ FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig,null);
|
|
|
try {
|
|
try {
|
|
|
// 插入奖品表
|
|
// 插入奖品表
|
|
|
int insertResult = rewardsMapper.insertFsUserRewards(reward);
|
|
int insertResult = rewardsMapper.insertFsUserRewards(reward);
|
|
@@ -131,26 +138,73 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@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 activityType 活动类型
|
|
|
* @param appRoleConfig 角色配置
|
|
* @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();
|
|
FsUserRewards reward = new FsUserRewards();
|
|
|
reward.setFsUserId(fsUserId);
|
|
reward.setFsUserId(fsUserId);
|
|
|
reward.setActivityType(activityType);
|
|
reward.setActivityType(activityType);
|
|
|
reward.setStatus(0); // 待领取
|
|
reward.setStatus(0); // 待领取
|
|
|
reward.setCreateTime(DateUtils.getNowDate());
|
|
reward.setCreateTime(DateUtils.getNowDate());
|
|
|
-
|
|
|
|
|
// 根据活动类型设置不同字段
|
|
// 根据活动类型设置不同字段
|
|
|
if (ActivityTypeEnum.FIRST_LOGIN.getCode().equals(activityType)) {
|
|
if (ActivityTypeEnum.FIRST_LOGIN.getCode().equals(activityType)) {
|
|
|
// 首次登录奖励
|
|
// 首次登录奖励
|
|
@@ -190,14 +243,14 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
|
|
|
} else if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(activityType)) {
|
|
} else if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(activityType)) {
|
|
|
// 看课奖励
|
|
// 看课奖励
|
|
|
reward.setRewardType(appRoleConfig.getCourseRewardType());
|
|
reward.setRewardType(appRoleConfig.getCourseRewardType());
|
|
|
-
|
|
|
|
|
|
|
+ reward.setVideoId(videoId);
|
|
|
// 获取当前用户的有效看课天数
|
|
// 获取当前用户的有效看课天数
|
|
|
FsUser fsUser = fsUserMapper.selectFsUserById(fsUserId);
|
|
FsUser fsUser = fsUserMapper.selectFsUserById(fsUserId);
|
|
|
if (fsUser == null) {
|
|
if (fsUser == null) {
|
|
|
log.info("用户不存在,用户id:{}", fsUserId);
|
|
log.info("用户不存在,用户id:{}", fsUserId);
|
|
|
return reward;
|
|
return reward;
|
|
|
}
|
|
}
|
|
|
- Integer targetDay = fsUser.getAppRewardsViewedDays() == null ? 0 : fsUser.getAppRewardsViewedDays();
|
|
|
|
|
|
|
+ //Integer targetDay = fsUser.getAppRewardsViewedDays() == null ? 0 : fsUser.getAppRewardsViewedDays();
|
|
|
|
|
|
|
|
// 根据奖品类型设置具体值
|
|
// 根据奖品类型设置具体值
|
|
|
if (appRoleConfig.getCourseRewardType() == 1) { // 红包
|
|
if (appRoleConfig.getCourseRewardType() == 1) { // 红包
|