Commit 827dcc50 authored by zhaobiyan's avatar zhaobiyan

会员操作积分接口

parent 00d72aad
......@@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_SENSITIVE = new ErrorCode(1001000004, "不允许获取敏感配置到前端");
ErrorCode GET_LOCK_FAILED = new ErrorCode(1001000005, "get.lock.failed");
// ========== 定时任务 1001001000 ==========
ErrorCode JOB_NOT_EXISTS = new ErrorCode(1001001000, "定时任务不存在");
......
package cn.iocoder.yudao.module.member.api.score;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreBatchOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateRespDTO;
import java.util.List;
public interface MemberUserScoreApi {
MemberUserScoreOperateRespDTO operateScore(MemberUserScoreOperateReqDTO req);
List<MemberUserScoreOperateRespDTO> batchOperateScore(MemberUserScoreBatchOperateReqDTO req);
}
package cn.iocoder.yudao.module.member.api.score.dto;
import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import java.util.List;
import java.util.Map;
@Data
@Builder
@ToString
public class MemberUserScoreBatchOperateReqDTO {
/**
* 会员id
*/
private List<Long> memberIds;
/**
* 积分数量
*/
private Integer scoreCount;
/**
* 积分来源
*/
private ScoreSourceTypeEnum sourceType;
/**
* 人工操作时必传,其他来源不需要传
*/
private ScoreOperateTypeEnum operateType;
/**
* 积分规则id
*/
private Long ruleId;
/**
* 积分过期时间
*/
private Integer expireDays;
/**
* 扩展参数
*/
private Map<String, Object> extParam;
}
package cn.iocoder.yudao.module.member.api.score;
import cn.iocoder.yudao.framework.redis.helper.RedisDistributedLock;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreDetailUpdateReqDto;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateRespDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreUpdateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.*;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import cn.iocoder.yudao.module.member.service.memberUserScore.MemberUserScoreService;
import cn.iocoder.yudao.module.member.service.memberUserScoreDetail.MemberUserScoreDetailService;
import cn.iocoder.yudao.module.member.service.memberUserScoreLog.MemberUserScoreLogService;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogCreateReq;
import com.alibaba.excel.util.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.MEMBER_ID_IS_NULL;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.SCORE_COUNT_ERROR;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.GET_LOCK_FAILED;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
@Slf4j
@Service
......@@ -42,11 +46,10 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
if (req.getScoreCount() <= 0) {
throw exception(SCORE_COUNT_ERROR);
}
//todo 校验
String lockKey = "member:operate:socre:" + req.getMemberId();
boolean lock = redisDistributedLock.lock(lockKey, 5000, 3, 100);
if (!lock) {
return MemberUserScoreOperateRespDTO.builder().success(false).code(500).reqDTO(req).build();
throw exception(GET_LOCK_FAILED);
}
try {
Long logId = saveScoreLog(req);
......@@ -58,6 +61,36 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
return MemberUserScoreOperateRespDTO.success(req);
}
@Override
@Transactional
public List<MemberUserScoreOperateRespDTO> batchOperateScore(MemberUserScoreBatchOperateReqDTO req) {
if (CollectionUtils.isEmpty(req.getMemberIds())) {
throw exception(MEMBER_ID_IS_NULL);
}
if (req.getScoreCount() <= 0) {
throw exception(SCORE_COUNT_ERROR);
}
// 校验用户当前积分是否满足扣减要求
if (req.getOperateType() == ScoreOperateTypeEnum.REDUCE) {
LambdaQueryWrapper<MemberUserScoreDO> wrapper = Wrappers.lambdaQuery();
wrapper.in(MemberUserScoreDO::getMemberId, req.getMemberIds());
List<MemberUserScoreDO> userScoreDOList = memberUserScoreService.list(wrapper);
List<MemberUserScoreDO> notEnoughScoreList = userScoreDOList.stream()
.filter(item -> item.getHoldScore() < req.getScoreCount()).collect(Collectors.toList());
if (!notEnoughScoreList.isEmpty()) {
throw exception(MEMBER_SCORE_NOT_ENOUGH);
}
}
return req.getMemberIds().stream().map(memberId -> operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(memberId)
.scoreCount(req.getScoreCount())
.operateType(req.getOperateType())
.sourceType(req.getSourceType())
.extParam(req.getExtParam())
.build()))
.collect(Collectors.toList());
}
private void updateUserScore(MemberUserScoreOperateReqDTO req) {
memberUserScoreService.updateUserScore(MemberUserScoreUpdateReqDTO.builder()
.memberId(req.getMemberId())
......
......@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.member.controller.admin.memberUserScore;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApiImpl;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreBatchOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateRespDTO;
import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
......@@ -22,8 +22,8 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
......@@ -52,17 +52,15 @@ public class MemberUserScoreController {
@ApiOperation("操作积分")
@PreAuthorize("@ss.hasPermission('member:user-score:operate')")
public CommonResult<Boolean> operate(@Valid @RequestBody MemberUserScoreOperateQueryVO query) {
query.getMemberIds().forEach(memberId -> {
Map<String,Object> extParam = new HashMap<>();
extParam.put("comment", query.getComment());
MemberUserScoreOperateRespDTO memberUserScoreOperateRespDTO = scoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(memberId)
List<MemberUserScoreOperateRespDTO> memberUserScoreOperateRespDTOS = scoreApi.batchOperateScore(MemberUserScoreBatchOperateReqDTO.builder()
.memberIds(query.getMemberIds())
.scoreCount(query.getScoreCount())
.operateType(ScoreOperateTypeEnum.parseByValue(query.getOperateType()))
.sourceType(ScoreSourceTypeEnum.MANUAL_OPERATE)
.extParam(extParam)
.build());
});
return success(null);
}
}
......@@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.redis.helper.RedisDistributedLock;
import cn.iocoder.yudao.framework.redis.helper.RedisHelper;
import cn.iocoder.yudao.module.ecw.api.customer.CustomerApi;
import cn.iocoder.yudao.module.ecw.api.internalMessage.ClientInternalMessageApi;
......@@ -63,6 +64,7 @@ import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.GET_LOCK_FAILED;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
/**
......@@ -111,6 +113,9 @@ public class MemberUserServiceImpl implements MemberUserService {
@Resource
private RedisHelper redisHelper;
@Resource
private RedisDistributedLock redisDistributedLock;
@Override
public MemberUserDO getUserByMobile(String mobile) {
......@@ -580,7 +585,11 @@ public class MemberUserServiceImpl implements MemberUserService {
if (nextMemberCodeNumber != null) {
return MemberUserCodeUtils.generateMemberCode(nextMemberCodeNumber);
}
synchronized(this) {
boolean lock = redisDistributedLock.lock("next:member:code:lock", 2000, 3, 500);
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
try {
nextMemberCodeNumber = redisHelper.execute4Long(redisScript, Collections.singletonList(key));
if (nextMemberCodeNumber != null) {
return MemberUserCodeUtils.generateMemberCode(nextMemberCodeNumber);
......@@ -589,6 +598,8 @@ public class MemberUserServiceImpl implements MemberUserService {
Long memberCodeMaxNumber = MemberUserCodeUtils.getMemberCodeNumber(currentMaxMemberCode);
redisHelper.set(key, String.valueOf(memberCodeMaxNumber), 5, TimeUnit.MINUTES);
return MemberUserCodeUtils.generateMemberCode(redisHelper.incrBy(key, 1));
} finally {
redisDistributedLock.releaseLock("next:member:code:lock");
}
}
......
......@@ -1010,3 +1010,4 @@ reward.status.change.error=The reward status change error
reward.status.not.allow.delay=Only enable status can delay
reward.status.not.allow.create=Only enabled or disabled reward status can be created
reward.time.not.allow=The reward time is not allow
get.lock.failed = The service is busy, please try again later
\ No newline at end of file
......@@ -1014,3 +1014,4 @@ reward.status.change.error=\u793C\u54C1\u72B6\u6001\u64CD\u4F5C\u4E0D\u7B26\u540
reward.status.not.allow.delay=\u53EA\u5141\u8BB8\u5EF6\u671F\u542F\u7528\u72B6\u6001\u793C\u54C1
reward.status.not.allow.create=\u521B\u5EFA\u7684\u793C\u54C1\u72B6\u6001\u53EA\u80FD\u662F\u542F\u7528\u6216\u672A\u542F\u7528
reward.time.not.allow=\u6D3B\u52A8\u65F6\u95F4\u4E0D\u5408\u6CD5
get.lock.failed = \u670D\u52A1\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5
\ 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