Commit ab8f42f6 authored by zhangfeng's avatar zhangfeng

feature-reward:批量兑换

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