Commit ab8f42f6 authored by zhangfeng's avatar zhangfeng

feature-reward:批量兑换

parent f6a68ca2
...@@ -6,8 +6,6 @@ import lombok.extern.slf4j.Slf4j; ...@@ -6,8 +6,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import sun.util.locale.BaseLocale;
import sun.util.locale.LocaleUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
......
...@@ -20,7 +20,6 @@ import com.alibaba.fastjson.JSONObject; ...@@ -20,7 +20,6 @@ import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.sun.org.apache.xpath.internal.operations.Or;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
......
...@@ -21,5 +21,7 @@ public interface ErrorCodeConstants { ...@@ -21,5 +21,7 @@ public interface ErrorCodeConstants {
ErrorCode REWARD_SCORE_NOT_ENOUGH = new ErrorCode(1001011013, "会员积分不够"); ErrorCode REWARD_SCORE_NOT_ENOUGH = new ErrorCode(1001011013, "会员积分不够");
ErrorCode REWARD_COUNT_NOT_ENOUGH = new ErrorCode(1001011014, "礼品数量不够"); ErrorCode REWARD_COUNT_NOT_ENOUGH = new ErrorCode(1001011014, "礼品数量不够");
ErrorCode REWARD_REDEEM_FAIL = new ErrorCode(1001011015, "批量兑换失败"); ErrorCode REWARD_REDEEM_FAIL = new ErrorCode(1001011015, "批量兑换失败");
ErrorCode REWARD_REDEEM_COUNT_NOT_ALLOW = new ErrorCode(1001011016, "批量兑换每次最多十条");
ErrorCode REWARD_REDEEM_ALLOW_COUNT_ERROR = new ErrorCode(1001011017, "超出允许兑换次数");
} }
...@@ -15,7 +15,9 @@ import cn.iocoder.yudao.module.reward.dal.dataobject.reward.RewardDO; ...@@ -15,7 +15,9 @@ import cn.iocoder.yudao.module.reward.dal.dataobject.reward.RewardDO;
import cn.iocoder.yudao.module.reward.dal.mysql.redeem.RewardRedeemMapper; import cn.iocoder.yudao.module.reward.dal.mysql.redeem.RewardRedeemMapper;
import cn.iocoder.yudao.module.reward.dal.mysql.reward.RewardMapper; import cn.iocoder.yudao.module.reward.dal.mysql.reward.RewardMapper;
import cn.iocoder.yudao.module.reward.enums.RewardRedeemStatusEnum; import cn.iocoder.yudao.module.reward.enums.RewardRedeemStatusEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
...@@ -42,22 +44,10 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -42,22 +44,10 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
private MemberUserScoreApi memberUserScoreApi; private MemberUserScoreApi memberUserScoreApi;
@Resource @Resource
private SnowflakeGenerator snowflakeGenerator; private SnowflakeGenerator snowflakeGenerator;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public RedeemRewardRespDTO redeemReward(RedeemRewardReqVO redeemRewardReqVO) { public RedeemRewardRespDTO redeemReward(RedeemRewardReqVO redeemRewardReqVO) {
boolean lock = false;
try {
lock = redisDistributedLock.lock("reward:redeem:lock:" + redeemRewardReqVO.getRewardId());
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
// 查询会员积分
UserRespDTO memberUser = memberUserApi.getUser(redeemRewardReqVO.getRewardId());
if (memberUser == null) {
throw exception(USER_NOT_EXISTS);
}
Integer holdScore = memberUser.getHoldScore();
// 查询礼品 // 查询礼品
RewardDO rewardDO = rewardMapper.selectById(redeemRewardReqVO.getRewardId()); RewardDO rewardDO = rewardMapper.selectById(redeemRewardReqVO.getRewardId());
if (rewardDO == null) { if (rewardDO == null) {
...@@ -67,10 +57,6 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -67,10 +57,6 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
if (rewardDO.getStatus() != 1) { if (rewardDO.getStatus() != 1) {
throw exception(REWARD_NOT_ENABLE); throw exception(REWARD_NOT_ENABLE);
} }
// 会员积分不够
if (holdScore < rewardDO.getPointsRequire() * redeemRewardReqVO.getRewardCount()) {
throw exception(REWARD_SCORE_NOT_ENOUGH);
}
// 礼品数量不够 // 礼品数量不够
if (rewardDO.getQuantityRemain() < redeemRewardReqVO.getRewardCount()) { if (rewardDO.getQuantityRemain() < redeemRewardReqVO.getRewardCount()) {
throw exception(REWARD_COUNT_NOT_ENOUGH); throw exception(REWARD_COUNT_NOT_ENOUGH);
...@@ -79,16 +65,19 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -79,16 +65,19 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
if (!Objects.equals(rewardDO.getPickMethod(), redeemRewardReqVO.getRedeemType())) { if (!Objects.equals(rewardDO.getPickMethod(), redeemRewardReqVO.getRedeemType())) {
throw exception(REWARD_PICK_METHOD_NOT_ALLOW_CREATE); throw exception(REWARD_PICK_METHOD_NOT_ALLOW_CREATE);
} }
//TODO:校验兑换次数(查兑换记录) verifyMemberUser(redeemRewardReqVO, rewardDO);
boolean lock = false;
try {
lock = redisDistributedLock.lock("reward:redeem:lock:" + redeemRewardReqVO.getRewardId());
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
// 更新礼品 // 更新礼品
rewardMapper.updateById(rewardDO); redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
// 添加兑换记录 // 添加兑换记录
Long redeemId = addRedeemRecord(redeemRewardReqVO); Long redeemId = addRedeemRecord(redeemRewardReqVO);
// 更新会员积分 // 更新会员积分
updateMemberScore(redeemRewardReqVO, rewardDO, redeemId); updateMemberScore(redeemRewardReqVO, rewardDO, redeemId);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
...@@ -97,13 +86,24 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -97,13 +86,24 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
return null; return null;
} }
private void redeemReward(RewardDO rewardDO, Integer rewardCount) {
// 更新时校验礼品数量是否足够
RewardDO rewardDO1 = rewardMapper.selectById(rewardDO.getId());
if (rewardDO1.getQuantityRemain() < rewardCount) {
throw exception(REWARD_COUNT_NOT_ENOUGH);
}
rewardDO.setExchangeCount(rewardDO.getExchangeCount() + 1);
rewardDO.setQuantityRemain(rewardDO.getQuantityRemain() - rewardCount);
rewardMapper.updateById(rewardDO);
}
private void updateMemberScore(RedeemRewardReqVO redeemRewardReqVO, RewardDO rewardDO, Long redeemId) { private void updateMemberScore(RedeemRewardReqVO redeemRewardReqVO, RewardDO rewardDO, Long redeemId) {
Map<String, Object> extParam = new HashMap<>(); Map<String, Object> extParam = new HashMap<>();
extParam.put("redeemId", redeemId); extParam.put("redeemId", redeemId);
memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder() memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(redeemRewardReqVO.getMemberId()) .memberId(redeemRewardReqVO.getMemberId())
.sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD) .sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD)
.scoreCount(redeemRewardReqVO.getRewardCount()* rewardDO.getPointsRequire()) .scoreCount(redeemRewardReqVO.getRewardCount() * rewardDO.getPointsRequire())
.extParam(extParam) .extParam(extParam)
.build()); .build());
} }
...@@ -117,7 +117,12 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -117,7 +117,12 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public List<RedeemRewardRespDTO> redeemRewards(List<RedeemRewardReqVO> redeemRewardReqVOList) { public List<RedeemRewardRespDTO> redeemRewards(List<RedeemRewardReqVO> redeemRewardReqVOList) {
// 批量兑换每次最多十条
if (redeemRewardReqVOList.size() > 10) {
throw exception(REWARD_REDEEM_COUNT_NOT_ALLOW);
}
Long rewardId = redeemRewardReqVOList.get(0).getRewardId(); Long rewardId = redeemRewardReqVOList.get(0).getRewardId();
// 查询礼品 // 查询礼品
RewardDO rewardDO = rewardMapper.selectById(rewardId); RewardDO rewardDO = rewardMapper.selectById(rewardId);
...@@ -125,44 +130,61 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -125,44 +130,61 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
if (rewardDO.getStatus() != 1) { if (rewardDO.getStatus() != 1) {
throw exception(REWARD_NOT_ENABLE); throw exception(REWARD_NOT_ENABLE);
} }
boolean lock = false;
try {
lock = redisDistributedLock.lock("reward:redeem:lock:" + rewardId);
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
// 校验礼品数量
//
ArrayList<Long> memberIds = new ArrayList<>();
int totalCount = 0; int totalCount = 0;
ArrayList<UserRespDTO> members = new ArrayList<>();
for (RedeemRewardReqVO redeemRewardReqVO : redeemRewardReqVOList) { for (RedeemRewardReqVO redeemRewardReqVO : redeemRewardReqVOList) {
// 每个兑换VO校验一遍 // 每个兑换VO校验一遍
// 兑换方式不匹配 // 兑换方式不匹配
if (!Objects.equals(rewardDO.getPickMethod(), redeemRewardReqVO.getRedeemType())) { if (!Objects.equals(rewardDO.getPickMethod(), redeemRewardReqVO.getRedeemType())) {
throw exception(REWARD_PICK_METHOD_NOT_ALLOW_CREATE); throw exception(REWARD_PICK_METHOD_NOT_ALLOW_CREATE);
} }
// 查询会员id列表,暂时挨个查 verifyMemberUser(redeemRewardReqVO, rewardDO);
memberIds.add(redeemRewardReqVO.getMemberId());
members.add(memberUserApi.getUser(redeemRewardReqVO.getMemberId()));
// 记录兑换总数 // 记录兑换总数
totalCount+=redeemRewardReqVO.getRewardCount(); totalCount += redeemRewardReqVO.getRewardCount();
}
// 查询会员列表,获取积分列表
// 判断兑换总数是否大于礼物数量 // 判断兑换总数是否大于礼物数量
if (totalCount > rewardDO.getQuantityRemain()){ if (totalCount > rewardDO.getQuantityRemain()) {
throw exception(REWARD_COUNT_NOT_ENOUGH); throw exception(REWARD_COUNT_NOT_ENOUGH);
} }
// 判断会员积分是否够(初步校验,实际兑换时会再加锁校验) }
boolean lock = false;
try {
lock = redisDistributedLock.lock("reward:redeem:lock:" + rewardId);
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
for (RedeemRewardReqVO redeemRewardReqVO : redeemRewardReqVOList) {
// 更新礼品
redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
// 添加兑换记录
Long redeemId = addRedeemRecord(redeemRewardReqVO);
// 更新会员积分
updateMemberScore(redeemRewardReqVO, rewardDO, redeemId);
}
} catch (Exception e) { } catch (Exception e) {
throw exception(REWARD_REDEEM_FAIL); throw exception(REWARD_REDEEM_FAIL);
} } finally {
finally {
redisDistributedLock.releaseLock("reward:redeem:lock:" + rewardId); redisDistributedLock.releaseLock("reward:redeem:lock:" + rewardId);
} }
return null; return null;
} }
private void verifyMemberUser(RedeemRewardReqVO redeemRewardReqVO, RewardDO rewardDO) {
// 查询会员积分
UserRespDTO memberUser = memberUserApi.getUser(redeemRewardReqVO.getMemberId());
if (memberUser == null) {
throw exception(USER_NOT_EXISTS);
}
Integer holdScore = memberUser.getHoldScore();
// 会员积分不够
if (holdScore < rewardDO.getPointsRequire() * redeemRewardReqVO.getRewardCount()) {
throw exception(REWARD_SCORE_NOT_ENOUGH);
}
// 校验兑换次数
LambdaQueryWrapper<RewardRedeemDO> rewardRedeemDOWrapper = new LambdaQueryWrapper<>();
rewardRedeemDOWrapper.eq(RewardRedeemDO::getRewardId, redeemRewardReqVO.getRewardId())
.eq(RewardRedeemDO::getMemberId, redeemRewardReqVO.getMemberId());
Long count = rewardRedeemMapper.selectCount(rewardRedeemDOWrapper);
if (count > rewardDO.getAllowCount()) {
throw exception(REWARD_REDEEM_ALLOW_COUNT_ERROR);
}
}
} }
...@@ -80,6 +80,7 @@ public class RewardDO extends BaseDO { ...@@ -80,6 +80,7 @@ public class RewardDO extends BaseDO {
private Date endTime; private Date endTime;
/** /**
* 领取方式(1上门领取,2包邮到家,3邮寄到付) * 领取方式(1上门领取,2包邮到家,3邮寄到付)
* TODO :改为枚举
*/ */
private Integer pickMethod; private Integer pickMethod;
/** /**
...@@ -100,6 +101,7 @@ public class RewardDO extends BaseDO { ...@@ -100,6 +101,7 @@ public class RewardDO extends BaseDO {
private String remarkFr; private String remarkFr;
/** /**
* 礼品状态(1已启用,2未启用,3已关闭,4已过期) * 礼品状态(1已启用,2未启用,3已关闭,4已过期)
* TODO :改为枚举
*/ */
private Integer status; private Integer status;
......
...@@ -56,6 +56,10 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i ...@@ -56,6 +56,10 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i
// 插入 // 插入
RewardDO rewardDO = RewardConvert.INSTANCE.convert(createReqVO); RewardDO rewardDO = RewardConvert.INSTANCE.convert(createReqVO);
rewardDO.setCode(generateRewardCode()); rewardDO.setCode(generateRewardCode());
// 剩余数量若没传给默认值0
if (rewardDO.getQuantityRemain() == null) {
rewardDO.setQuantityRemain(0);
}
//如果插入失败,重新生成code再次插入 //如果插入失败,重新生成code再次插入
try { try {
rewardMapper.insert(rewardDO); rewardMapper.insert(rewardDO);
...@@ -68,6 +72,7 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i ...@@ -68,6 +72,7 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i
} }
@Override @Override
// TODO :完善校验
public void update(RewardUpdateReqVO updateReqVO) { public void update(RewardUpdateReqVO updateReqVO) {
// 校验存在 // 校验存在
RewardDO rewardDO = rewardMapper.selectById(updateReqVO.getId()); RewardDO rewardDO = rewardMapper.selectById(updateReqVO.getId());
...@@ -273,6 +278,7 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i ...@@ -273,6 +278,7 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i
//校验礼品是否过期并修改礼品状态 //校验礼品是否过期并修改礼品状态
//TODO:异步更新礼品状态 //TODO:异步更新礼品状态
private void validateExpire(RewardDO rewardDO) { private void validateExpire(RewardDO rewardDO) {
if (rewardDO.getEndTime() != null){
if (rewardDO.getStatus() == 1 && rewardDO.getEndTime().toInstant().isBefore(Instant.now())) { if (rewardDO.getStatus() == 1 && rewardDO.getEndTime().toInstant().isBefore(Instant.now())) {
RewardDO expireReward = new RewardDO(); RewardDO expireReward = new RewardDO();
expireReward.setId(rewardDO.getId()); expireReward.setId(rewardDO.getId());
...@@ -280,4 +286,5 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i ...@@ -280,4 +286,5 @@ public class RewardServiceImpl extends AbstractService<RewardMapper, RewardDO> i
rewardMapper.updateById(expireReward); rewardMapper.updateById(expireReward);
} }
} }
}
} }
...@@ -51,6 +51,7 @@ public class RewardUpdateReqVO { ...@@ -51,6 +51,7 @@ public class RewardUpdateReqVO {
private Integer nodeId; private Integer nodeId;
@ApiModelProperty(value = "剩余数量") @ApiModelProperty(value = "剩余数量")
@NotNull(message = "剩余数量不能为空")
@Min(value = 0) @Min(value = 0)
private Integer quantityRemain; private Integer quantityRemain;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment