Commit 8d3d0413 authored by zhaobiyan's avatar zhaobiyan

积分操作新增唯一键幂等处理

parent c259ec47
...@@ -41,4 +41,6 @@ public class MemberUserScoreBatchOperateReqDTO { ...@@ -41,4 +41,6 @@ public class MemberUserScoreBatchOperateReqDTO {
* 扩展参数 * 扩展参数
*/ */
private Map<String, Object> extParam; private Map<String, Object> extParam;
private String uniqueId;
} }
...@@ -12,6 +12,10 @@ import java.util.Map; ...@@ -12,6 +12,10 @@ import java.util.Map;
@Builder @Builder
@ToString @ToString
public class MemberUserScoreOperateReqDTO { public class MemberUserScoreOperateReqDTO {
/**
* 唯一键 增加积分时必须,做幂等判断使用
*/
private String uniqueId;
/** /**
* 会员id * 会员id
*/ */
......
...@@ -48,4 +48,7 @@ public interface ErrorCodeConstants { ...@@ -48,4 +48,7 @@ public interface ErrorCodeConstants {
ErrorCode REVERSE_SOURCE_NO_RELEATION_ID = new ErrorCode(1004008004, "reverse.source.no.releation.id"); ErrorCode REVERSE_SOURCE_NO_RELEATION_ID = new ErrorCode(1004008004, "reverse.source.no.releation.id");
ErrorCode LEVEL_BOUND_RANGE_ERROR = new ErrorCode(1004008005, "level.bound.range.error"); ErrorCode LEVEL_BOUND_RANGE_ERROR = new ErrorCode(1004008005, "level.bound.range.error");
ErrorCode LEVEL_BOUND_RANGE_CONFLICT = new ErrorCode(1004008006, "level.bound.range.conflict"); ErrorCode LEVEL_BOUND_RANGE_CONFLICT = new ErrorCode(1004008006, "level.bound.range.conflict");
ErrorCode SOURCE_TYPE_IS_NULL = new ErrorCode(1004008007, "source.type.is.null");
ErrorCode SCORE_OPERATE_MUST_HAVE_UNIQUE_ID = new ErrorCode(1004008008, "score.operate.must.have.unique.id");
ErrorCode SCORE_OPERATE_IDEMPOTENT_ERROR = new ErrorCode(1004008008, "score.operate.idempotent.error");
} }
...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.member.api.score.dto.*; ...@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.member.api.score.dto.*;
import cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO; import cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO; import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation.MemberUserScoreDetailReleationDO; import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation.MemberUserScoreDetailReleationDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.dto.ScoreDetailChangeDto; import cn.iocoder.yudao.module.member.dto.ScoreDetailChangeDto;
import cn.iocoder.yudao.module.member.enums.MemberScoreDetailReleationStatueEnum; import cn.iocoder.yudao.module.member.enums.MemberScoreDetailReleationStatueEnum;
import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum; import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum;
...@@ -64,6 +65,16 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{ ...@@ -64,6 +65,16 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
if (req.getScoreCount() <= 0) { if (req.getScoreCount() <= 0) {
throw exception(SCORE_COUNT_ERROR); throw exception(SCORE_COUNT_ERROR);
} }
if (req.getSourceType() == null) {
throw exception(SOURCE_TYPE_IS_NULL);
}
if (StringUtils.isBlank(req.getUniqueId())) {
throw exception(SCORE_OPERATE_MUST_HAVE_UNIQUE_ID);
}
MemberUserScoreLogDO memberUserScoreLogDO = logService.getByUniqueId(req.getUniqueId());
if (memberUserScoreLogDO != null) {
throw exception(SCORE_OPERATE_IDEMPOTENT_ERROR);
}
String lockKey = "member:operate:score:" + req.getMemberId(); String lockKey = "member:operate:score:" + req.getMemberId();
RLock lock = redissonClient.getLock(lockKey); RLock lock = redissonClient.getLock(lockKey);
try { try {
...@@ -127,6 +138,7 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{ ...@@ -127,6 +138,7 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
.operateType(req.getOperateType()) .operateType(req.getOperateType())
.sourceType(req.getSourceType()) .sourceType(req.getSourceType())
.extParam(req.getExtParam()) .extParam(req.getExtParam())
.uniqueId(req.getUniqueId() + "_" + memberId)
.build())) .build()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
...@@ -187,6 +199,7 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{ ...@@ -187,6 +199,7 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
req.getOperateType().getValue() : req.getSourceType().getOperateType().getValue()) req.getOperateType().getValue() : req.getSourceType().getOperateType().getValue())
.sourceType(req.getSourceType().getValue()) .sourceType(req.getSourceType().getValue())
.extParam(req.getExtParam()) .extParam(req.getExtParam())
.uniqueId(req.getUniqueId())
.build()); .build());
} }
} }
...@@ -64,6 +64,7 @@ public class MemberUserScoreExpireTask implements JobHandler { ...@@ -64,6 +64,7 @@ public class MemberUserScoreExpireTask implements JobHandler {
.memberId(memberUserScoreDetailDO.getMemberId()) .memberId(memberUserScoreDetailDO.getMemberId())
.scoreCount(memberUserScoreDetailDO.getRemainCount()) .scoreCount(memberUserScoreDetailDO.getRemainCount())
.sourceType(ScoreSourceTypeEnum.SYSTEM_EXPIRED) .sourceType(ScoreSourceTypeEnum.SYSTEM_EXPIRED)
.uniqueId(ScoreSourceTypeEnum.SYSTEM_EXPIRED +"_"+ memberUserScoreDetailDO.getId() + "_" + System.currentTimeMillis())
.extParam(extParam) .extParam(extParam)
.build()); .build());
} catch (Exception e) { } catch (Exception e) {
......
...@@ -60,6 +60,7 @@ public class MemberUserScoreController { ...@@ -60,6 +60,7 @@ public class MemberUserScoreController {
.operateType(ScoreOperateTypeEnum.parseByValue(query.getOperateType())) .operateType(ScoreOperateTypeEnum.parseByValue(query.getOperateType()))
.sourceType(ScoreSourceTypeEnum.MANUAL_OPERATE) .sourceType(ScoreSourceTypeEnum.MANUAL_OPERATE)
.extParam(extParam) .extParam(extParam)
.uniqueId(ScoreSourceTypeEnum.MANUAL_OPERATE + "_" + System.currentTimeMillis())
.build()); .build());
return success(null); return success(null);
} }
......
...@@ -32,4 +32,6 @@ public class MemberUserScoreLogDO extends BaseDO { ...@@ -32,4 +32,6 @@ public class MemberUserScoreLogDO extends BaseDO {
private Long ruleId; private Long ruleId;
private String extParam = "{}"; private String extParam = "{}";
private String uniqueId;
} }
...@@ -55,6 +55,7 @@ public class MemberUserScoreDetailExpireListener { ...@@ -55,6 +55,7 @@ public class MemberUserScoreDetailExpireListener {
.memberId(detail.getMemberId()) .memberId(detail.getMemberId())
.scoreCount(detail.getRemainCount()) .scoreCount(detail.getRemainCount())
.sourceType(ScoreSourceTypeEnum.SYSTEM_EXPIRED) .sourceType(ScoreSourceTypeEnum.SYSTEM_EXPIRED)
.uniqueId(ScoreSourceTypeEnum.SYSTEM_EXPIRED +"_"+ detail.getId() + "_" + System.currentTimeMillis())
.extParam(extParam) .extParam(extParam)
.build()); .build());
} catch (Exception e) { } catch (Exception e) {
......
...@@ -17,4 +17,6 @@ public interface MemberUserScoreLogService extends IService<MemberUserScoreLogDO ...@@ -17,4 +17,6 @@ public interface MemberUserScoreLogService extends IService<MemberUserScoreLogDO
PageResult<MemberUserScoreLogBackVO> getPage(MemberUserScoreLogQueryVO query); PageResult<MemberUserScoreLogBackVO> getPage(MemberUserScoreLogQueryVO query);
Long createScoreLog(MemberUserScoreLogCreateReq createReq); Long createScoreLog(MemberUserScoreLogCreateReq createReq);
MemberUserScoreLogDO getByUniqueId(String uniqueId);
} }
...@@ -10,6 +10,8 @@ import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBa ...@@ -10,6 +10,8 @@ import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBa
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogCreateReq; import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogCreateReq;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO; import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
import cn.iocoder.yudao.module.system.service.dict.DictTypeService; import cn.iocoder.yudao.module.system.service.dict.DictTypeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -59,10 +61,18 @@ public class MemberUserScoreLogServiceImpl extends AbstractService<MemberUserSco ...@@ -59,10 +61,18 @@ public class MemberUserScoreLogServiceImpl extends AbstractService<MemberUserSco
userScoreLogDO.setOperateType(createReq.getOperateType()); userScoreLogDO.setOperateType(createReq.getOperateType());
userScoreLogDO.setSourceType(createReq.getSourceType()); userScoreLogDO.setSourceType(createReq.getSourceType());
userScoreLogDO.setRuleId(createReq.getRuleId()); userScoreLogDO.setRuleId(createReq.getRuleId());
userScoreLogDO.setUniqueId(createReq.getUniqueId());
if (createReq.getExtParam() != null) { if (createReq.getExtParam() != null) {
userScoreLogDO.setExtParam(JSONUtil.toJsonStr(createReq.getExtParam())); userScoreLogDO.setExtParam(JSONUtil.toJsonStr(createReq.getExtParam()));
} }
this.saveOrUpdate(userScoreLogDO); this.saveOrUpdate(userScoreLogDO);
return userScoreLogDO.getId(); return userScoreLogDO.getId();
} }
@Override
public MemberUserScoreLogDO getByUniqueId(String uniqueId) {
LambdaQueryWrapper<MemberUserScoreLogDO> wrapper = Wrappers.lambdaQuery();
wrapper.eq(MemberUserScoreLogDO::getUniqueId, uniqueId);
return this.selectOne(wrapper);
}
} }
...@@ -17,4 +17,5 @@ public class MemberUserScoreLogCreateReq { ...@@ -17,4 +17,5 @@ public class MemberUserScoreLogCreateReq {
private Integer sourceType; private Integer sourceType;
private Long ruleId; private Long ruleId;
private Map<String, Object> extParam; private Map<String, Object> extParam;
private String uniqueId;
} }
...@@ -112,6 +112,7 @@ public class RedeemRewardApiImpl implements RedeemRewardApi { ...@@ -112,6 +112,7 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
.sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD) .sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD)
.scoreCount(redeemRewardReqVO.getRewardCount() * rewardDO.getPointsRequire()) .scoreCount(redeemRewardReqVO.getRewardCount() * rewardDO.getPointsRequire())
.releationId(String.valueOf(redeemId)) .releationId(String.valueOf(redeemId))
.uniqueId(ScoreSourceTypeEnum.EXCHANGE_REWARD + "_" + redeemId)
.extParam(extParam) .extParam(extParam)
.build()); .build());
} }
......
...@@ -274,6 +274,7 @@ public class RewardRedeemServiceImpl extends AbstractService<RewardRedeemMapper, ...@@ -274,6 +274,7 @@ public class RewardRedeemServiceImpl extends AbstractService<RewardRedeemMapper,
.scoreCount(rewardRedeemDO.getScoreCount()) .scoreCount(rewardRedeemDO.getScoreCount())
.extParam(extParam) .extParam(extParam)
.releationId(String.valueOf(req.getId())) .releationId(String.valueOf(req.getId()))
.uniqueId(ScoreSourceTypeEnum.EXCHANGE_REWARD_CANCEL + "_" + req.getId())
.build()); .build());
return true; return true;
} }
......
...@@ -1023,4 +1023,5 @@ currency.not.exist = currency not exist ...@@ -1023,4 +1023,5 @@ currency.not.exist = currency not exist
date.format.error = date format error, for example : 2024-01-01 12:11:11 date.format.error = date format error, for example : 2024-01-01 12:11:11
redeem.cancel.status.error = record status must be redeeming redeem.cancel.status.error = record status must be redeeming
level.bound.range.error = upper count must greater than lower count level.bound.range.error = upper count must greater than lower count
level.bound.range.conflict = score range exist conflict level.bound.range.conflict = score range exist conflict
\ No newline at end of file score.operate.idempotent.error = idempotent key conflict
\ No newline at end of file
...@@ -1027,4 +1027,5 @@ currency.not.exist = \u5E01\u79CD\u4E0D\u5B58\u5728 ...@@ -1027,4 +1027,5 @@ currency.not.exist = \u5E01\u79CD\u4E0D\u5B58\u5728
date.format.error = \u65E5\u671F\u683C\u5F0F\u4E0D\u6B63\u786E, \u6B63\u786E\u683C\u5F0F\u53C2\u8003: 2024-01-01 12:11:11 date.format.error = \u65E5\u671F\u683C\u5F0F\u4E0D\u6B63\u786E, \u6B63\u786E\u683C\u5F0F\u53C2\u8003: 2024-01-01 12:11:11
redeem.cancel.status.error = \u5151\u6362\u4E2D\u72B6\u6001\u7684\u8BB0\u5F55\u624D\u80FD\u64A4\u9500 redeem.cancel.status.error = \u5151\u6362\u4E2D\u72B6\u6001\u7684\u8BB0\u5F55\u624D\u80FD\u64A4\u9500
level.bound.range.error = \u4E0A\u754C\u6570\u503C\u5FC5\u987B\u5927\u4E8E\u4E0B\u754C\u6570\u503C level.bound.range.error = \u4E0A\u754C\u6570\u503C\u5FC5\u987B\u5927\u4E8E\u4E0B\u754C\u6570\u503C
level.bound.range.conflict = \u79EF\u5206\u8303\u56F4\u5B58\u5728\u51B2\u7A81 level.bound.range.conflict = \u79EF\u5206\u8303\u56F4\u5B58\u5728\u51B2\u7A81
\ No newline at end of file score.operate.idempotent.error = \u5E42\u7B49key\u51B2\u7A81
\ No newline at end of file
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