Commit 6ec72d2c authored by 332784038@qq.com's avatar 332784038@qq.com

Merge remote-tracking branch 'origin/dev' into dev

parents 91d4a4a9 03417dfe
......@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.redis.helper.RedisDistributedLock;
import cn.iocoder.yudao.framework.redis.helper.RedisHelper;
import cn.iocoder.yudao.framework.redis.helper.RedisLockTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
......
package cn.iocoder.yudao.framework.apollo.core.event;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 订单起运事件
*
* @author zhangfeng
*/
@Data
@AllArgsConstructor
public class OrderInShippingEvent {
private Long orderId;
private String orderNo;
}
package cn.iocoder.yudao.framework.apollo.core.event.export;
import lombok.Data;
import java.util.List;
@Data
public class MemberScoreDetailExpireEvent {
private List<Long> detailIds;
}
......@@ -3,6 +3,8 @@ 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 cn.iocoder.yudao.module.member.api.score.dto.ReleationScoreExpireInfoDTO;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import java.util.List;
......@@ -12,4 +14,5 @@ public interface MemberUserScoreApi {
List<MemberUserScoreOperateRespDTO> batchOperateScore(MemberUserScoreBatchOperateReqDTO req);
List<ReleationScoreExpireInfoDTO> getScoreExpireInfo(String releation, ScoreSourceTypeEnum scoreSourceTypeEnum);
}
......@@ -14,4 +14,5 @@ public class MemberUserScoreDetailUpdateReqDto {
private ScoreSourceTypeEnum sourceType;
private Long scoreLogId;
private Integer expireDays;
private String releationId;
}
......@@ -28,6 +28,10 @@ public class MemberUserScoreOperateReqDTO {
* 人工操作时必传,其他来源不需要传
*/
private ScoreOperateTypeEnum operateType;
/**
* 若操作的积分后续存在回退逻辑时必传, 目前存在 礼品兑换后, 兑换取消,扣除积分的回退
*/
private String releationId;
/**
* 积分规则id
*/
......
package cn.iocoder.yudao.module.member.api.score.dto;
import lombok.Data;
import java.util.Date;
@Data
public class ReleationScoreExpireInfoDTO {
private Long detailId;
private Date expireDate;
private Integer scoreCount;
}
......@@ -52,4 +52,7 @@ public interface ErrorCodeConstants {
ErrorCode SCORE_RULE_FIELD_ERROR = new ErrorCode(1004008007, "score.rule.field.error");
ErrorCode REVERSE_SOURCE_NO_RELEATION_ID = new ErrorCode(1004008008, "reverse.source.no.releation.id");
ErrorCode LEVEL_BOUND_RANGE_ERROR = new ErrorCode(1004008009, "level.bound.range.error");
ErrorCode LEVEL_BOUND_RANGE_CONFLICT = new ErrorCode(1004008010, "level.bound.range.conflict");
}
package cn.iocoder.yudao.module.member.enums;
public enum MemberScoreDetailReleationStatueEnum {
AVAILABLE(1, "有效"),
NOT_AVAILABLE(2, "失效")
;
private final int value;
private final String name;
MemberScoreDetailReleationStatueEnum(int value, String name) {
this.value = value;
this.name = name;
}
public int getValue() {
return value;
}
public String getName() {
return name;
}
}
......@@ -5,11 +5,13 @@ import com.google.common.collect.Sets;
import java.util.Set;
public enum ScoreSourceTypeEnum {
MANUAL_OPERATE(1, "人工操作", null),
MANUAL_OPERATE(1, "人工操作", null, null),
EXCHANGE_REWARD(2, "兑换礼品", ScoreOperateTypeEnum.REDUCE),
EXCHANGE_REWARD(2, "兑换礼品", ScoreOperateTypeEnum.REDUCE, null),
SYSTEM_EXPIRED(3, "系统失效", ScoreOperateTypeEnum.REDUCE),
SYSTEM_EXPIRED(3, "系统失效", ScoreOperateTypeEnum.REDUCE, null),
EXCHANGE_REWARD_CANCEL(4, "兑换礼品撤销", ScoreOperateTypeEnum.ADD, EXCHANGE_REWARD),
;
private final int value;
......@@ -18,16 +20,23 @@ public enum ScoreSourceTypeEnum {
private final ScoreOperateTypeEnum operateType;
ScoreSourceTypeEnum(int value, String name, ScoreOperateTypeEnum operateType) {
private final ScoreSourceTypeEnum reverseSource;
ScoreSourceTypeEnum(int value, String name, ScoreOperateTypeEnum operateType, ScoreSourceTypeEnum reverseSource) {
this.value = value;
this.name = name;
this.operateType = operateType;
this.reverseSource = reverseSource;
}
public int getValue() {
return value;
}
public ScoreSourceTypeEnum getReverseSource() {
return reverseSource;
}
public String getName() {
return name;
}
......
package cn.iocoder.yudao.module.member.enums;
public enum TransportType {
public enum TransportTypeEnum {
OCEAN_LCL(1, "海运拼柜"),
SPECIAL_LINE_AIR_FREIGHT(3, "专线空运");
......@@ -8,7 +8,7 @@ public enum TransportType {
private final String name;
TransportType(int value, String name) {
TransportTypeEnum(int value, String name) {
this.value = value;
this.name = name;
}
......
......@@ -49,5 +49,10 @@
<artifactId>yudao-module-depository-core</artifactId>
<version>${revision}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.boot</groupId>-->
<!-- <artifactId>yudao-module-order-core</artifactId>-->
<!-- <version>${revision}</version>-->
<!-- </dependency>-->
</dependencies>
</project>
package cn.iocoder.yudao.module.member.api.score;
import cn.iocoder.yudao.framework.redis.helper.RedisDistributedLock;
import cn.iocoder.yudao.framework.apollo.core.event.export.MemberScoreDetailExpireEvent;
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.dal.dataobject.score.MemberUserScoreDO;
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.dto.ScoreDetailChangeDto;
import cn.iocoder.yudao.module.member.enums.MemberScoreDetailReleationStatueEnum;
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.service.score.MemberUserScoreService;
import cn.iocoder.yudao.module.member.service.scoreDetail.MemberUserScoreDetailService;
import cn.iocoder.yudao.module.member.service.scoreDetailReleation.MemberUserScoreDetailReleationService;
import cn.iocoder.yudao.module.member.service.scoreLog.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.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
......@@ -36,7 +49,12 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
@Resource
private MemberUserScoreService memberUserScoreService;
@Resource
private RedisDistributedLock redisDistributedLock;
private MemberUserScoreDetailReleationService memberUserScoreDetailReleationService;
@Resource
private ApplicationContext applicationContext;
@Resource
private RedissonClient redissonClient;
@Override
@Transactional(rollbackFor = Exception.class)
public MemberUserScoreOperateRespDTO operateScore(MemberUserScoreOperateReqDTO req) {
......@@ -46,21 +64,43 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
if (req.getScoreCount() <= 0) {
throw exception(SCORE_COUNT_ERROR);
}
String lockKey = "member:operate:socre:" + req.getMemberId();
boolean lock = redisDistributedLock.lock(lockKey, 5000, 3, 100);
if (!lock) {
String lockKey = "member:operate:score:" + req.getMemberId();
RLock lock = redissonClient.getLock(lockKey);
try {
boolean lockSuccess = lock.tryLock(2, 2, TimeUnit.SECONDS);
if (!lockSuccess) {
throw exception(GET_LOCK_FAILED);
}
try {
Long logId = saveScoreLog(req);
saveScoreDetail(req, logId);
List<ScoreDetailChangeDto> detailInfos = saveScoreDetail(req, logId);
saveScoreDetailReleation(req, detailInfos);
updateUserScore(req);
publishDetailExpireEvent(req,detailInfos);
} catch (InterruptedException e) {
throw exception(GET_LOCK_FAILED);
} finally {
redisDistributedLock.releaseLock(lockKey);
lock.unlock();
}
return MemberUserScoreOperateRespDTO.success(req);
}
private void publishDetailExpireEvent(MemberUserScoreOperateReqDTO req, List<ScoreDetailChangeDto> detailInfos) {
if (req.getSourceType().getReverseSource() == null || req.getOperateType() ==ScoreOperateTypeEnum.REDUCE) {
return;
}
//判断回退流程,回退的积分是否过期.如果过期再走正常的过期扣积分流程
MemberScoreDetailExpireEvent memberScoreDetailExpireEvent = new MemberScoreDetailExpireEvent();
memberScoreDetailExpireEvent.setDetailIds(detailInfos.stream().map(ScoreDetailChangeDto::getDetailId).collect(Collectors.toList()));
applicationContext.publishEvent(memberScoreDetailExpireEvent);
}
private void saveScoreDetailReleation(MemberUserScoreOperateReqDTO req, List<ScoreDetailChangeDto> detailInfos) {
if (StringUtils.isBlank(req.getReleationId())) {
return;
}
memberUserScoreDetailReleationService.updateReleation(req.getReleationId(), req.getSourceType(), detailInfos);
}
@Override
@Transactional
public List<MemberUserScoreOperateRespDTO> batchOperateScore(MemberUserScoreBatchOperateReqDTO req) {
......@@ -91,6 +131,31 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
.collect(Collectors.toList());
}
@Override
public List<ReleationScoreExpireInfoDTO> getScoreExpireInfo(String releationId, ScoreSourceTypeEnum scoreSourceTypeEnum) {
if (StringUtils.isBlank(releationId)) {
return Collections.emptyList();
}
LambdaQueryWrapper<MemberUserScoreDetailReleationDO> wrapper = Wrappers.lambdaQuery();
wrapper.eq(MemberUserScoreDetailReleationDO::getReleationId, releationId);
wrapper.eq(MemberUserScoreDetailReleationDO::getSourceType, scoreSourceTypeEnum.getValue());
wrapper.eq(MemberUserScoreDetailReleationDO::getStatus, MemberScoreDetailReleationStatueEnum.AVAILABLE.getValue());
List<MemberUserScoreDetailReleationDO> releationDOList = memberUserScoreDetailReleationService.list(wrapper);
Set<Long> detailIds = releationDOList.stream().map(MemberUserScoreDetailReleationDO::getDetailId).collect(Collectors.toSet());
LambdaQueryWrapper<MemberUserScoreDetailDO> detailWrappers = Wrappers.lambdaQuery();
detailWrappers.in(MemberUserScoreDetailDO::getId, detailIds);
Map<Long, MemberUserScoreDetailDO> detailIdInfoMap = scoreDetailService.list(detailWrappers).stream()
.collect(Collectors.toMap(MemberUserScoreDetailDO::getId, Function.identity(), (c1, c2) -> c1));
return releationDOList.stream().map(releation -> {
ReleationScoreExpireInfoDTO expireInfoDTO = new ReleationScoreExpireInfoDTO();
MemberUserScoreDetailDO detailDO = detailIdInfoMap.getOrDefault(releation.getDetailId(), new MemberUserScoreDetailDO());
expireInfoDTO.setDetailId(detailDO.getId());
expireInfoDTO.setExpireDate(detailDO.getExpireTime());
expireInfoDTO.setScoreCount(releation.getScoreCount());
return expireInfoDTO;
}).collect(Collectors.toList());
}
private void updateUserScore(MemberUserScoreOperateReqDTO req) {
memberUserScoreService.updateUserScore(MemberUserScoreUpdateReqDTO.builder()
.memberId(req.getMemberId())
......@@ -101,8 +166,8 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
.build());
}
private void saveScoreDetail(MemberUserScoreOperateReqDTO req, Long scoreLogId) {
scoreDetailService.updateScoreDetail(MemberUserScoreDetailUpdateReqDto.builder()
private List<ScoreDetailChangeDto> saveScoreDetail(MemberUserScoreOperateReqDTO req, Long scoreLogId) {
return scoreDetailService.updateScoreDetail(MemberUserScoreDetailUpdateReqDto.builder()
.memberId(req.getMemberId())
.scoreCount(req.getScoreCount())
.operateType(req.getSourceType() == ScoreSourceTypeEnum.MANUAL_OPERATE ?
......@@ -110,6 +175,7 @@ public class MemberUserScoreApiImpl implements MemberUserScoreApi{
.sourceType(req.getSourceType())
.scoreLogId(scoreLogId)
.expireDays(req.getExpireDays())
.releationId(req.getReleationId())
.build());
}
......
package cn.iocoder.yudao.module.member.controller.admin.job;
import cn.hutool.json.JSONArray;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApi;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.enums.MemberScoreStatueEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import cn.iocoder.yudao.module.member.service.memberUserScoreDetail.MemberUserScoreDetailService;
import cn.iocoder.yudao.module.member.service.scoreDetail.MemberUserScoreDetailService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
......@@ -46,7 +47,7 @@ public class MemberUserScoreExpireTask implements JobHandler {
LambdaQueryWrapper<MemberUserScoreDetailDO> wrapper = Wrappers.lambdaQuery();
wrapper.in(MemberUserScoreDetailDO::getStatus, Lists.newArrayList(MemberScoreStatueEnum.AVAILABLE.getValue(),
MemberScoreStatueEnum.PART_AVAILABLE.getValue()));
wrapper.eq(MemberUserScoreDetailDO::getExpireTime, DateUtils.getNextNDayStart(new Date(), 0));
wrapper.le(MemberUserScoreDetailDO::getExpireTime, DateUtils.getNextNDayStart(new Date(), 0));
wrapper.orderByAsc(MemberUserScoreDetailDO::getCreateTime);
List<MemberUserScoreDetailDO> todoList = scoreDetailService.list(wrapper);
log.info("member user score expire task, to expire record count :{}", todoList.size());
......@@ -56,9 +57,9 @@ public class MemberUserScoreExpireTask implements JobHandler {
for (MemberUserScoreDetailDO memberUserScoreDetailDO : todoList) {
try {
log.info("score expire, score detail id :{}", memberUserScoreDetailDO.getId());
List logIds = (List) memberUserScoreDetailDO.getExtParamByKey(MemberUserScoreDetailDO.MemberUserScoreDetailExtKey.LOG_IDS);
JSONArray logIds = memberUserScoreDetailDO.getExtParamByKey(MemberUserScoreDetailDO.MemberUserScoreDetailExtKey.LOG_IDS);
Map<String, Object> extParam = new HashMap<>();
extParam.put("scoreLogIds", logIds);
extParam.put("scoreLogIds", logIds.toList(Long.class));
memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(memberUserScoreDetailDO.getMemberId())
.scoreCount(memberUserScoreDetailDO.getRemainCount())
......
package cn.iocoder.yudao.module.member.controller.admin.level;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.service.level.MemberUserLevelConfigService;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelBaseRespVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelReqVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelUpdateVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Validated
@RestController
@Api(tags = "管理后台 - 会员等级配置")
@RequestMapping("/member/user-level")
public class MemberUserLevelConfigController {
private final MemberUserLevelConfigService userLevelConfigService;
public MemberUserLevelConfigController(MemberUserLevelConfigService userLevelConfigService) {
this.userLevelConfigService = userLevelConfigService;
}
@PostMapping("/page")
@ApiOperation("列表")
public CommonResult<PageResult<MemberUserLevelBaseRespVO>> getLevelPage(@Valid @RequestBody MemberUserLevelReqVO query) {
return success(userLevelConfigService.getLevelPage(query));
}
@PostMapping("/delete")
@ApiOperation("删除")
public CommonResult<Boolean> delete(@Valid @RequestBody List<Long> ids) {
return success(userLevelConfigService.delete(ids));
}
@PostMapping("/add")
@ApiOperation("新增")
public CommonResult<Boolean> add(@Valid @RequestBody MemberUserLevelUpdateVO query) {
return success(userLevelConfigService.add(query));
}
@PostMapping("/update")
@ApiOperation("编辑")
public CommonResult<Boolean> update(@Valid @RequestBody MemberUserLevelUpdateVO query) {
return success(userLevelConfigService.updateLevel(query));
}
}
......@@ -110,9 +110,9 @@ public class ScoreRuleController {
return success(true);
}
@GetMapping("/getWarehouseTreeRegionList")
@GetMapping("/warehouse-tree-region-list")
@ApiOperation("获得目的国、目的城市、目的仓列表")
public CommonResult<List<WarehouseTreeRegionVO>> WarehouseTreeRegionList() {
public CommonResult<List<WarehouseTreeRegionVO>> warehouseTreeRegionList() {
List<WarehouseTreeRegionVO> listIn = warehouseService.getWarehouseTreeRegionList(1);
List<WarehouseTreeRegionVO> listOut = warehouseService.getWarehouseTreeRegionList(2);
List<WarehouseTreeRegionVO> list = ListUtils.sum(listIn, listOut);
......
package cn.iocoder.yudao.module.member.controller.admin.memberUserScore;
package cn.iocoder.yudao.module.member.controller.admin.userScore;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
......@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreBatchOperateR
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;
import cn.iocoder.yudao.module.member.service.memberUserScore.MemberUserScoreService;
import cn.iocoder.yudao.module.member.service.score.MemberUserScoreService;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreOperateQueryVO;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreQueryVO;
......
package cn.iocoder.yudao.module.member.controller.admin.memberUserScoreLog;
package cn.iocoder.yudao.module.member.controller.admin.userScoreLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.service.memberUserScoreLog.MemberUserScoreLogService;
import cn.iocoder.yudao.module.member.service.scoreLog.MemberUserScoreLogService;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
import io.swagger.annotations.Api;
......
package cn.iocoder.yudao.module.member.dal.dataobject.level;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("member_user_level_config")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserLevelConfigDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 名称
*/
private String name;
/**
* 积分范围 下界
*/
private Integer lowerCount;
/**
* 积分范围 上界
*/
private Integer upperCount;
/**
* 图标
*/
private String icon;
}
package cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore;
package cn.iocoder.yudao.module.member.dal.dataobject.score;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
......
package cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreDetail;
package cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.vavr.collection.List;
import lombok.*;
import org.apache.commons.lang.StringUtils;
import java.util.Date;
import java.util.Map;
/**
* 会员积分 DO
......@@ -55,8 +53,8 @@ public class MemberUserScoreDetailDO extends BaseDO {
}
public Object getExtParamByKey(MemberUserScoreDetailExtKey key) {
public JSONArray getExtParamByKey(MemberUserScoreDetailExtKey key) {
JSONObject extParamJsonObject = JSONUtil.parseObj(extParam);
return extParamJsonObject.get(key.getKey(), List.class);
return extParamJsonObject.get(key.getKey(), JSONArray.class);
}
}
package cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 会员积分 DO
*
* @author 系统管理员
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("member_user_score_detail_releation")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserScoreDetailReleationDO extends BaseDO {
@TableId
private Long id;
private String releationId;
private Integer sourceType;
private Long detailId;
private Integer scoreCount;
private Integer status;
}
package cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreLog;
package cn.iocoder.yudao.module.member.dal.dataobject.scoreLog;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
......
package cn.iocoder.yudao.module.member.dal.mysql.memberUserLevel;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberUserLevelConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员等级 Mapper
* @author 系统管理员
*/
@Mapper
public interface MemberUserLevelConfigMapper extends AbstractMapper<MemberUserLevelConfigDO> {
}
package cn.iocoder.yudao.module.member.dal.mysql.memberUserScore;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreQueryVO;
import org.apache.ibatis.annotations.Mapper;
......
package cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreDetail;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 会员积分详情 Mapper
......
package cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreDetailReleation;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation.MemberUserScoreDetailReleationDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员积分详情 Mapper
* @author 系统管理员
*/
@Mapper
public interface MemberUserScoreDetailReleationMapper extends AbstractMapper<MemberUserScoreDetailReleationDO> {
}
package cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreLog;
import cn.iocoder.yudao.framework.mybatis.core.mapper.AbstractMapper;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
import org.apache.ibatis.annotations.Mapper;
......
package cn.iocoder.yudao.module.member.dto;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class ScoreDetailChangeDto {
private Long detailId;
private Integer scoreCount;
}
package cn.iocoder.yudao.module.member.listener;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.apollo.core.event.export.MemberScoreDetailExpireEvent;
import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApi;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import cn.iocoder.yudao.module.member.service.scoreDetail.MemberUserScoreDetailService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@AllArgsConstructor
@Slf4j
public class MemberUserScoreDetailExpireListener {
@Resource
private MemberUserScoreDetailService scoreDetailService;
@Resource
private MemberUserScoreApi memberUserScoreApi;
@Async
@EventListener(MemberScoreDetailExpireEvent.class)
public void listen(MemberScoreDetailExpireEvent event) {
log.info("member user score expire listen,event:{}", JSONUtil.toJsonStr(event));
if (CollectionUtils.isEmpty(event.getDetailIds())) {
return;
}
//暂停一会确保那边事务执行完成
ThreadUtil.safeSleep(3000L);
LambdaQueryWrapper<MemberUserScoreDetailDO> wrapper = Wrappers.lambdaQuery();
wrapper.in(MemberUserScoreDetailDO::getId, event.getDetailIds());
wrapper.le(MemberUserScoreDetailDO::getExpireTime, new Date());
List<MemberUserScoreDetailDO> todoList = scoreDetailService.list(wrapper);
todoList.forEach(detail -> {
try {
log.info("score expire, score detail id :{}", detail.getId());
JSONArray jsonArray = detail.getExtParamByKey(MemberUserScoreDetailDO.MemberUserScoreDetailExtKey.LOG_IDS);
Map<String, Object> extParam = new HashMap<>();
extParam.put("scoreLogIds", jsonArray.toList(Long.class));
memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(detail.getMemberId())
.scoreCount(detail.getRemainCount())
.sourceType(ScoreSourceTypeEnum.SYSTEM_EXPIRED)
.extParam(extParam)
.build());
} catch (Exception e) {
log.error("member user score expire exception, data:{}", detail, e);
}
});
log.info("member user score expire listen finished");
}
}
//package cn.iocoder.yudao.module.member.listener;
//
//import cn.hutool.core.util.ArrayUtil;
//import cn.hutool.json.JSONUtil;
//import cn.iocoder.yudao.framework.apollo.core.event.OrderInShippingEvent;
//import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApi;
//import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
//import cn.iocoder.yudao.module.member.dal.dataobject.scoreRule.ScoreRuleDO;
//import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
//import cn.iocoder.yudao.module.member.enums.TransportTypeEnum;
//import cn.iocoder.yudao.module.member.enums.YesOrNoTypeEnum;
//import cn.iocoder.yudao.module.member.service.memberUserScoreLog.MemberUserScoreLogService;
//import cn.iocoder.yudao.module.member.service.scoreRule.ScoreRuleService;
//import cn.iocoder.yudao.module.member.vo.scoreRule.extra.ScoreRuleOrderVExtraVO;
//import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
//import cn.iocoder.yudao.module.order.dal.dataobject.orderObjective.OrderObjectiveDO;
//import cn.iocoder.yudao.module.order.enums.OrderStatusEnum;
//import cn.iocoder.yudao.module.order.service.order.OrderService;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import lombok.AllArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.context.event.EventListener;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.Resource;
//import java.math.BigDecimal;
//import java.math.RoundingMode;
//import java.util.Date;
//import java.util.List;
//import java.util.Objects;
//
//@Component
//@AllArgsConstructor
//@Slf4j
//public class OrderInShippingListener {
// @Resource
// private OrderService orderService;
// @Resource
// private ScoreRuleService scoreRuleService;
// @Resource
// private MemberUserScoreApi memberUserScoreApi;
// @Resource
// private MemberUserScoreLogService memberUserScoreLogService;
//
// @EventListener(OrderInShippingEvent.class)
// public void listen(OrderInShippingEvent event) {
// Long orderId = event.getOrderId();
// String orderNo = event.getOrderNo();
// OrderDO orderDO;
// if (orderId != null) {
// orderDO = orderService.getById(orderId);
// } else {
// orderDO = orderService.selectOne(new LambdaQueryWrapper<OrderDO>().eq(OrderDO::getOrderNo, orderNo).last("limit 1"));
// }
// if (Objects.isNull(orderDO)) {
// log.error("Order in shipping listening error: The order does not exist,orderId:{},orderNo:{}", orderId, orderNo);
// }
// // 目的地信息
// OrderObjectiveDO orderObjectiveDO = orderDO.getOrderObjectiveDO();
// // 订单状态应为起运
// if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.IN_SHIPPING.getValue())) {
// return;
// }
// // 判断海运空运
// Integer transportId = orderDO.getTransportId();
// ScoreRuleDO scoreRuleDO = scoreRuleService.getEnabledOrderVScoreRuleByTransportType(transportId);
// if (Objects.isNull(scoreRuleDO)) {
// log.info("No score rule match");
// return;
// }
// // 是否过期
// if (scoreRuleDO.getEndTime().after(new Date())) {
// log.info("The score rule has expired");
// return;
// }
// ScoreRuleOrderVExtraVO extraInfo = JSONUtil.toBean(scoreRuleDO.getExtra(), ScoreRuleOrderVExtraVO.class);
// // 是否首单,首单只加一次
// if (extraInfo.getFirstOrder() == YesOrNoTypeEnum.YES.ordinal()){
// Long count = orderService.selectCount(new LambdaQueryWrapper<OrderDO>().eq(OrderDO::getUserId, orderDO.getUserId()));
// if (count > 1) {
// log.info("Not first order");
// return;
// }
// }
// // 提货点是否包含
// String[] warehouseIds = extraInfo.getReceiveAddr().split(",");
// if (!ArrayUtil.contains(warehouseIds, orderObjectiveDO.getObjectiveWarehouseId().toString())) {
// return;
// }
// // 计算积分
// Integer scoreCount = 0;
// List<ScoreRuleOrderVExtraVO.OrderVRule> orderVRule = extraInfo.getOrderVRule();
// if (transportId == TransportTypeEnum.OCEAN_LCL.getValue()) {
// // 海运算重量
// BigDecimal orgVWeight = orderDO.getOrgVWeight();
// int w = orgVWeight.setScale(0, RoundingMode.HALF_UP).intValue();
// for (ScoreRuleOrderVExtraVO.OrderVRule rule : orderVRule) {
// if (rule.getLow() <= w && rule.getHigh() >= w) {
// scoreCount = rule.getScore();
// break;
// }
// }
// } else if (transportId == TransportTypeEnum.SPECIAL_LINE_AIR_FREIGHT.getValue()) {
// // 空运校验渠道
// // 渠道id
// Long channelId = orderDO.getChannelId();
// String[] channels = extraInfo.getChannel().split(",");
// if (!ArrayUtil.contains(channels, channelId.toString())) {
// return;
// }
// // 空运算体积
// BigDecimal orgWVolume = orderDO.getOrgWVolume();
// int v = orgWVolume.setScale(0, RoundingMode.HALF_UP).intValue();
// for (ScoreRuleOrderVExtraVO.OrderVRule rule : orderVRule) {
// if (rule.getLow() <= v && rule.getHigh() >= v) {
// scoreCount = rule.getScore();
// break;
// }
// }
// }
// if (scoreCount > scoreRuleDO.getGetScoreOnce()){
// scoreCount = scoreRuleDO.getGetScoreOnce();
// }
// // 校验累计最高分,查member_user_score_log
//
// // 增加积分
// // 会员id
// Long userId = orderDO.getUserId();
// memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
// .memberId(userId)
// .scoreCount(scoreCount)
// .sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD)
// .ruleId(scoreRuleDO.getId())
// .expireDays(scoreRuleDO.getScorePeriod())
// .build()
// );
// }
//}
package cn.iocoder.yudao.module.member.service.level;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberUserLevelConfigDO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelBaseRespVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelReqVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelUpdateVO;
import java.util.List;
/**
* 会员等级 Service 接口
*
* @author 系统管理员
*/
public interface MemberUserLevelConfigService extends IService<MemberUserLevelConfigDO> {
PageResult<MemberUserLevelBaseRespVO> getLevelPage(MemberUserLevelReqVO query);
Boolean delete(List<Long> ids);
Boolean add(MemberUserLevelUpdateVO query);
Boolean updateLevel(MemberUserLevelUpdateVO query);
}
package cn.iocoder.yudao.module.member.service.level;
import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.module.member.dal.dataobject.level.MemberUserLevelConfigDO;
import cn.iocoder.yudao.module.member.dal.mysql.memberUserLevel.MemberUserLevelConfigMapper;
import cn.iocoder.yudao.module.member.vo.memberUserScore.MemberUserScoreBackVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelBaseRespVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelReqVO;
import cn.iocoder.yudao.module.member.vo.userLevel.MemberUserLevelUpdateVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
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.infra.enums.ErrorCodeConstants.GET_LOCK_FAILED;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.LEVEL_BOUND_RANGE_CONFLICT;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.LEVEL_BOUND_RANGE_ERROR;
/**
* 会员积分 Service 实现类
*
* @author 系统管理员
*/
@Service
@Validated
@Lazy
public class MemberUserLevelConfigServiceImpl extends AbstractService<MemberUserLevelConfigMapper, MemberUserLevelConfigDO> implements MemberUserLevelConfigService {
private final MemberUserLevelConfigMapper memberUserLevelConfigMapper;
public MemberUserLevelConfigServiceImpl(MemberUserLevelConfigMapper memberUserLevelConfigMapper) {
this.memberUserLevelConfigMapper = memberUserLevelConfigMapper;
}
@Override
public PageResult<MemberUserLevelBaseRespVO> getLevelPage(MemberUserLevelReqVO query) {
int start = (query.getPageNo() - 1) * query.getPageSize();
int size = query.getPageSize();
LambdaQueryWrapper<MemberUserLevelConfigDO> wrapper = Wrappers.lambdaQuery();
wrapper.eq(MemberUserLevelConfigDO::getDeleted, 0);
wrapper.last(String.format("limit %s, %s", start, size));
List<MemberUserLevelConfigDO> list = this.list(wrapper);
List<MemberUserLevelBaseRespVO> result = list.stream().map(i -> BeanUtil.copyProperties(i, MemberUserLevelBaseRespVO.class)).collect(Collectors.toList());
Long total = memberUserLevelConfigMapper.selectCount();
return new PageResult<>(result, total, query.getPageSize(), query.getPageNo(), (total + query.getPageSize() - 1) / query.getPageSize());
}
@Override
public Boolean delete(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return true;
}
LambdaUpdateWrapper<MemberUserLevelConfigDO> wrapper = Wrappers.lambdaUpdate();
wrapper.in(MemberUserLevelConfigDO::getId, ids);
wrapper.set(MemberUserLevelConfigDO::getDeleted, 1);
return this.update(wrapper);
}
@Override
public Boolean add(MemberUserLevelUpdateVO query) {
validate(query);
MemberUserLevelConfigDO memberUserLevelConfigDO = BeanUtil.copyProperties(query, MemberUserLevelConfigDO.class);
return this.saveOrUpdate(memberUserLevelConfigDO);
}
private void validate(MemberUserLevelUpdateVO query) {
if (query.getLowerCount() > query.getUpperCount()) {
throw exception(LEVEL_BOUND_RANGE_ERROR);
}
LambdaQueryWrapper<MemberUserLevelConfigDO> wrapper = Wrappers.lambdaQuery();
wrapper.eq(MemberUserLevelConfigDO::getDeleted, 0);
if (query.getId() != null) {
wrapper.ne(MemberUserLevelConfigDO::getId, query.getId());
}
List<MemberUserLevelConfigDO> list = this.list(wrapper);
for (MemberUserLevelConfigDO memberUserLevelConfigDO : list) {
if (query.getLowerCount() >= memberUserLevelConfigDO.getLowerCount() && query.getLowerCount() <= memberUserLevelConfigDO.getUpperCount()) {
throw exception(LEVEL_BOUND_RANGE_CONFLICT);
}
if (query.getUpperCount() >= memberUserLevelConfigDO.getLowerCount() && query.getUpperCount() <= memberUserLevelConfigDO.getUpperCount()) {
throw exception(LEVEL_BOUND_RANGE_CONFLICT);
}
}
}
@Override
public Boolean updateLevel(MemberUserLevelUpdateVO query) {
validate(query);
MemberUserLevelConfigDO memberUserLevelConfigDO = BeanUtil.copyProperties(query, MemberUserLevelConfigDO.class);
return this.saveOrUpdate(memberUserLevelConfigDO);
}
}
package cn.iocoder.yudao.module.member.service.memberUserScore;
package cn.iocoder.yudao.module.member.service.score;
import java.util.*;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreUpdateReqDTO;
import cn.iocoder.yudao.module.member.vo.memberUserScore.*;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
......
package cn.iocoder.yudao.module.member.service.memberUserScore;
package cn.iocoder.yudao.module.member.service.score;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreUpdateReqDTO;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO;
import cn.iocoder.yudao.module.member.dal.mysql.memberUserScore.MemberUserScoreMapper;
import cn.iocoder.yudao.module.member.enums.ScoreOperateTypeEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
......@@ -76,6 +76,15 @@ public class MemberUserScoreServiceImpl extends AbstractService<MemberUserScoreM
if (memberUserScoreDO == null) {
memberUserScoreDO = initMemberUserScore(query.getMemberId());
}
// 回退流程增加积分
if (query.getSourceType().getReverseSource() != null && query.getOperateType() == ScoreOperateTypeEnum.ADD) {
// 回退流程增加积分,不仅需要新增持有积分,还需要减少使用积分
memberUserScoreDO.setHoldScore(memberUserScoreDO.getHoldScore() + query.getScoreCount());
memberUserScoreDO.setUsedScore(memberUserScoreDO.getUsedScore() - query.getScoreCount());
this.saveOrUpdate(memberUserScoreDO);
return;
}
//正常流程 增加积分 减少积分
if (query.getOperateType() == ScoreOperateTypeEnum.ADD) {
memberUserScoreDO.setHoldScore(memberUserScoreDO.getHoldScore() + query.getScoreCount());
this.saveOrUpdate(memberUserScoreDO);
......
package cn.iocoder.yudao.module.member.service.memberUserScoreDetail;
package cn.iocoder.yudao.module.member.service.scoreDetail;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreDetailUpdateReqDto;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetail.MemberUserScoreDetailDO;
import cn.iocoder.yudao.module.member.dto.ScoreDetailChangeDto;
import java.util.List;
/**
* 会员积分日志 Service 接口
......@@ -11,5 +14,5 @@ import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreDetail.Membe
*/
public interface MemberUserScoreDetailService extends IService<MemberUserScoreDetailDO> {
void updateScoreDetail(MemberUserScoreDetailUpdateReqDto reqDto);
List<ScoreDetailChangeDto> updateScoreDetail(MemberUserScoreDetailUpdateReqDto reqDto);
}
package cn.iocoder.yudao.module.member.service.scoreDetailReleation;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation.MemberUserScoreDetailReleationDO;
import cn.iocoder.yudao.module.member.dto.ScoreDetailChangeDto;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import java.util.List;
/**
* 会员积分日志 Service 接口
*
* @author 系统管理员
*/
public interface MemberUserScoreDetailReleationService extends IService<MemberUserScoreDetailReleationDO> {
void updateReleation(String releationId, ScoreSourceTypeEnum sourceType, List<ScoreDetailChangeDto> detailInfos);
List<MemberUserScoreDetailReleationDO> listByReleationIdAndSource(String releationId, int value);
}
package cn.iocoder.yudao.module.member.service.scoreDetailReleation;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreDetailReleation.MemberUserScoreDetailReleationDO;
import cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreDetailReleation.MemberUserScoreDetailReleationMapper;
import cn.iocoder.yudao.module.member.dto.ScoreDetailChangeDto;
import cn.iocoder.yudao.module.member.enums.MemberScoreDetailReleationStatueEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 会员积分详情 Service 实现类
*
* @author 系统管理员
*/
@Service
@Validated
public class MemberUserScoreDetailReleationServiceImpl extends AbstractService<MemberUserScoreDetailReleationMapper, MemberUserScoreDetailReleationDO> implements MemberUserScoreDetailReleationService {
@Override
public void updateReleation(String releationId, ScoreSourceTypeEnum sourceType, List<ScoreDetailChangeDto> detailInfos) {
if (StringUtils.isBlank(releationId) || CollectionUtils.isEmpty(detailInfos)) {
return;
}
if (sourceType.getReverseSource() != null) {
//回退流程, 失效掉之前的关联关系
LambdaUpdateWrapper<MemberUserScoreDetailReleationDO> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(MemberUserScoreDetailReleationDO::getReleationId, releationId);
updateWrapper.eq(MemberUserScoreDetailReleationDO::getSourceType, sourceType.getReverseSource().getValue());
updateWrapper.in(MemberUserScoreDetailReleationDO::getDetailId, detailInfos.stream().map(ScoreDetailChangeDto::getDetailId).collect(Collectors.toList()));
updateWrapper.set(MemberUserScoreDetailReleationDO::getStatus, MemberScoreDetailReleationStatueEnum.NOT_AVAILABLE.getValue());
this.update(updateWrapper);
return;
}
List<MemberUserScoreDetailReleationDO> dos = detailInfos.stream().map(scoreDetailChangeDto -> {
MemberUserScoreDetailReleationDO releationDO = new MemberUserScoreDetailReleationDO();
releationDO.setReleationId(releationId);
releationDO.setSourceType(sourceType.getValue());
releationDO.setDetailId(scoreDetailChangeDto.getDetailId());
releationDO.setScoreCount(scoreDetailChangeDto.getScoreCount());
releationDO.setStatus(MemberScoreDetailReleationStatueEnum.AVAILABLE.getValue());
return releationDO;
}).collect(Collectors.toList());
this.saveOrUpdateBatch(dos);
}
@Override
public List<MemberUserScoreDetailReleationDO> listByReleationIdAndSource(String releationId, int sourceType) {
if (StringUtils.isBlank(releationId)) {
return Collections.emptyList();
}
LambdaQueryWrapper<MemberUserScoreDetailReleationDO> wrapper = Wrappers.lambdaQuery();
wrapper.eq(MemberUserScoreDetailReleationDO::getReleationId, releationId);
wrapper.eq(MemberUserScoreDetailReleationDO::getSourceType, sourceType);
wrapper.eq(MemberUserScoreDetailReleationDO::getStatus, MemberScoreDetailReleationStatueEnum.AVAILABLE.getValue());
return this.list(wrapper);
}
}
package cn.iocoder.yudao.module.member.service.memberUserScoreLog;
package cn.iocoder.yudao.module.member.service.scoreLog;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.IService;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogCreateReq;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
......
package cn.iocoder.yudao.module.member.service.memberUserScoreLog;
package cn.iocoder.yudao.module.member.service.scoreLog;
import cn.hutool.core.lang.generator.SnowflakeGenerator;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.module.member.dal.dataobject.memberUserScoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.scoreLog.MemberUserScoreLogDO;
import cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreLog.MemberUserScoreLogMapper;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogBackVO;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogCreateReq;
import cn.iocoder.yudao.module.member.vo.memberUserScoreLog.MemberUserScoreLogQueryVO;
import cn.iocoder.yudao.module.system.service.dict.DictTypeService;
import com.alibaba.excel.util.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
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;
......
......@@ -87,4 +87,11 @@ public interface ScoreRuleService extends IService<ScoreRuleDO> {
* @return
*/
void delayScoreRule(ScoreDelayReqVO scoreDelayReqVO);
/**
* 根据运输方式获取已启用订单V值规则
* @param transportType
* @return
*/
ScoreRuleDO getEnabledOrderVScoreRuleByTransportType(Integer transportType);
}
......@@ -4,6 +4,7 @@ import java.time.Instant;
import java.util.*;
import javax.annotation.Resource;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQuery;
......@@ -13,7 +14,7 @@ import cn.iocoder.yudao.module.member.dal.dataobject.scoreRule.ScoreRuleDO;
import cn.iocoder.yudao.module.member.dal.mysql.scoreRule.ScoreRuleMapper;
import cn.iocoder.yudao.module.member.enums.ScoreRuleStatusEnum;
import cn.iocoder.yudao.module.member.enums.ScoreRuleTypeEnum;
import cn.iocoder.yudao.module.member.enums.TransportType;
import cn.iocoder.yudao.module.member.enums.TransportTypeEnum;
import cn.iocoder.yudao.module.member.enums.YesOrNoTypeEnum;
import cn.iocoder.yudao.module.member.vo.scoreRule.*;
import cn.iocoder.yudao.module.member.vo.scoreRule.extra.ScoreRuleOrderVExtraVO;
......@@ -44,10 +45,10 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
@Override
public Long createScoreRule(ScoreRuleCreateReqVO createReqVO) {
ScoreRuleDO scoreRule = ScoreRuleConvert.INSTANCE.convert(createReqVO);
if (scoreRule.getStatus() != ScoreRuleStatusEnum.DISABLED.getValue() && scoreRule.getStatus() != ScoreRuleStatusEnum.ENABLED.getValue()){
if (scoreRule.getStatus() != ScoreRuleStatusEnum.DISABLED.getValue() && scoreRule.getStatus() != ScoreRuleStatusEnum.ENABLED.getValue()) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
verifyCommon(scoreRule);
verifyCommon(createReqVO);
Integer scoreRuleType = scoreRule.getType();
verifyAndSetExtraDO(createReqVO, scoreRuleType, scoreRule);
scoreRuleMapper.insert(scoreRule);
......@@ -55,32 +56,51 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
return scoreRule.getId();
}
private void verifyCommon(ScoreRuleDO scoreRule) {
private void verifyCommon(ScoreRuleBaseVO scoreRuleIn) {
//校验公共入参
if (scoreRule.getGetScoreOnce() <= 0) {
if (scoreRuleIn.getGetScoreOnce() <= 0) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
if (scoreRule.getMaxScoreTotal() <= 0) {
if (scoreRuleIn.getMaxScoreTotal() <= 0) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
if (scoreRule.getStartTime().after((scoreRule.getEndTime())) || scoreRule.getEndTime().before(Date.from(Instant.now()))) {
if (scoreRuleIn.getStartTime().after((scoreRuleIn.getEndTime())) || scoreRuleIn.getEndTime().before(Date.from(Instant.now()))) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
if (scoreRule.getScorePeriod() <= 0) {
if (scoreRuleIn.getScorePeriod() <= 0) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
//如果是启用,校验同一个规则下,有效期内有没有重复的规则设置,分享不校验
if (scoreRule.getStatus() == ScoreRuleStatusEnum.ENABLED.getValue() && scoreRule.getType() != ScoreRuleTypeEnum.SHARE.getValue()) {
//如果是启用,校验同一个规则下,有效期内有没有重复的规则设置,分享不校验。海运空运分开算
if (scoreRuleIn.getStatus() == ScoreRuleStatusEnum.ENABLED.getValue() && scoreRuleIn.getType() != ScoreRuleTypeEnum.SHARE.getValue()) {
LambdaQuery<ScoreRuleDO> scoreRuleDOLambdaQuery = new LambdaQuery<>();
scoreRuleDOLambdaQuery.eq(ScoreRuleDO::getStatus, ScoreRuleStatusEnum.ENABLED.getValue())
.eq(ScoreRuleDO::getType, scoreRule.getType())
.eq(ScoreRuleDO::getStatus, ScoreRuleStatusEnum.ENABLED.getValue());
.eq(ScoreRuleDO::getType, scoreRuleIn.getType());
if (scoreRuleIn.getType() == ScoreRuleTypeEnum.ORDER_V.getValue()) {
ScoreRuleOrderVExtraVO extraOrderV = scoreRuleIn.getExtraOrderV();
if (extraOrderV == null) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
Integer transportType = extraOrderV.getTransportType();
if (transportType != TransportTypeEnum.OCEAN_LCL.getValue() && transportType != TransportTypeEnum.SPECIAL_LINE_AIR_FREIGHT.getValue()) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
List<ScoreRuleDO> scoreRuleDOS = scoreRuleMapper.selectList(scoreRuleDOLambdaQuery);
if (scoreRuleDOS != null && !scoreRuleDOS.isEmpty()) {
for (ScoreRuleDO scoreRuleDO : scoreRuleDOS) {
ScoreRuleOrderVExtraVO bean = JSONUtil.toBean(scoreRuleDO.getExtra(), ScoreRuleOrderVExtraVO.class);
if (Objects.equals(bean.getTransportType(), transportType)) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
}
}
} else {
Long count = scoreRuleMapper.selectCount(scoreRuleDOLambdaQuery);
if (count > 0) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
}
}
}
@Override
......@@ -93,7 +113,7 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
throw exception(SCORE_RULE_UPDATE_ERROR);
}
ScoreRuleDO scoreRule = ScoreRuleConvert.INSTANCE.convert(updateReqVO);
verifyCommon(scoreRule);
verifyCommon(updateReqVO);
verifyAndSetExtraDO(updateReqVO, updateReqVO.getType(), scoreRule);
// 更新
scoreRuleMapper.updateById(scoreRule);
......@@ -115,7 +135,7 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
if (extraOrderV.getFirstOrder() != YesOrNoTypeEnum.YES.ordinal() && extraOrderV.getFirstOrder() != YesOrNoTypeEnum.NO.ordinal()) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
if (extraOrderV.getTransportType() != TransportType.OCEAN_LCL.getValue() && extraOrderV.getFirstOrder() != TransportType.SPECIAL_LINE_AIR_FREIGHT.getValue()) {
if (extraOrderV.getTransportType() != TransportTypeEnum.OCEAN_LCL.getValue() && extraOrderV.getTransportType() != TransportTypeEnum.SPECIAL_LINE_AIR_FREIGHT.getValue()) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
if (StringUtils.isAnyBlank(extraOrderV.getTargetCountry(), extraOrderV.getTargetCity(), extraOrderV.getReceiveAddr(), extraOrderV.getOrderEntry())) {
......@@ -208,15 +228,13 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
@Override
public List<ScoreRuleBackVO> getScoreRuleList(Collection<Long> ids) {
List<ScoreRuleDO> scoreRuleDOS = scoreRuleMapper.selectBatchIds(ids);
List<ScoreRuleBackVO> scoreRuleBackVOS = ScoreRuleConvert.INSTANCE.convertList(scoreRuleDOS);
return scoreRuleBackVOS;
return ScoreRuleConvert.INSTANCE.convertList(scoreRuleDOS);
}
@Override
public PageResult<ScoreRuleBackVO> getScoreRulePage(ScoreRuleQueryVO query, PageVO page) {
PageResult<ScoreRuleDO> pageResult = scoreRuleMapper.selectPage(page, query);
PageResult<ScoreRuleBackVO> scoreRuleBackVOPageResult = ScoreRuleConvert.INSTANCE.convertPage(pageResult);
return scoreRuleBackVOPageResult;
return ScoreRuleConvert.INSTANCE.convertPage(pageResult);
}
/**
......@@ -307,4 +325,23 @@ public class ScoreRuleServiceImpl extends AbstractService<ScoreRuleMapper, Score
upScoreRuleDO.setEndTime(delayReqVO.getEndTime());
scoreRuleMapper.updateById(upScoreRuleDO);
}
@Override
public ScoreRuleDO getEnabledOrderVScoreRuleByTransportType(Integer transportType) {
if (transportType != TransportTypeEnum.OCEAN_LCL.getValue() && transportType != TransportTypeEnum.SPECIAL_LINE_AIR_FREIGHT.getValue()) {
throw exception(SCORE_RULE_FIELD_ERROR);
}
LambdaQuery<ScoreRuleDO> lambdaQuery = new LambdaQuery<>();
lambdaQuery.eq(ScoreRuleDO::getStatus, ScoreRuleStatusEnum.ENABLED.getValue())
.eq(ScoreRuleDO::getType, ScoreRuleTypeEnum.ORDER_V.getValue());
List<ScoreRuleDO> scoreRuleDOS = scoreRuleMapper.selectList(lambdaQuery);
if (!CollectionUtil.isEmpty(scoreRuleDOS)) {
for (ScoreRuleDO scoreRuleDO : scoreRuleDOS) {
if (Objects.equals(JSONUtil.toBean(scoreRuleDO.getExtra(), ScoreRuleOrderVExtraVO.class).getTransportType(), transportType)) {
return scoreRuleDO;
}
}
}
return null;
}
}
......@@ -4,15 +4,14 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.apollo.core.event.MemberRegEvent;
import cn.iocoder.yudao.framework.common.util.spring.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.spring.enums.CommonStatusEnum;
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;
......@@ -49,6 +48,8 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.security.crypto.password.PasswordEncoder;
......@@ -114,8 +115,7 @@ public class MemberUserServiceImpl implements MemberUserService {
private RedisHelper redisHelper;
@Resource
private RedisDistributedLock redisDistributedLock;
private RedissonClient redissonClient;
@Override
public MemberUserDO getUserByMobile(String mobile) {
......@@ -581,15 +581,15 @@ public class MemberUserServiceImpl implements MemberUserService {
redisScript.setScriptText(incrIfExistScript);
redisScript.setResultType(Long.class);
Long nextMemberCodeNumber = redisHelper.execute4Long(redisScript, Collections.singletonList(key));
if (nextMemberCodeNumber != null) {
return MemberUserCodeUtils.generateMemberCode(nextMemberCodeNumber);
}
boolean lock = redisDistributedLock.lock("next:member:code:lock", 2000, 3, 500);
if (!lock) {
RLock lock = redissonClient.getLock("next:member:code:lock");
try {
boolean lockSuccess = lock.tryLock(2, 2, TimeUnit.SECONDS);
if (!lockSuccess) {
throw exception(GET_LOCK_FAILED);
}
try {
nextMemberCodeNumber = redisHelper.execute4Long(redisScript, Collections.singletonList(key));
if (nextMemberCodeNumber != null) {
return MemberUserCodeUtils.generateMemberCode(nextMemberCodeNumber);
......@@ -598,8 +598,10 @@ 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));
} catch (InterruptedException e) {
throw exception(GET_LOCK_FAILED);
} finally {
redisDistributedLock.releaseLock("next:member:code:lock");
lock.unlock();
}
}
......
package cn.iocoder.yudao.module.member.vo.userLevel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("管理后台 - 会员等级查询返回 VO")
public class MemberUserLevelBaseRespVO {
@ApiModelProperty(value = "id")
private Long id;
/**
* 名称
*/
@ApiModelProperty(value = "等级名称")
private String name;
/**
* 积分范围 下界
*/
@ApiModelProperty(value = "积分范围 下界")
private Integer lowerCount;
/**
* 积分范围 上界
*/
@ApiModelProperty(value = "积分范围 上界")
private Integer upperCount;
/**
* 图标
*/
@ApiModelProperty(value = "图标")
private String icon;
}
package cn.iocoder.yudao.module.member.vo.userLevel;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author zhaobiyan
*/
@Data
@ApiModel("管理后台 - 会员等级配置查询 VO")
public class MemberUserLevelReqVO extends PageParam {
@ApiModelProperty(value = "id")
private Long id;
}
package cn.iocoder.yudao.module.member.vo.userLevel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@ApiModel("管理后台 - 会员等级编辑 VO")
public class MemberUserLevelUpdateVO {
@ApiModelProperty(value = "id")
private Long id;
/**
* 名称
*/
@ApiModelProperty(value = "等级名称")
@NotNull(message = "name is null")
private String name;
/**
* 积分范围 下界
*/
@ApiModelProperty(value = "积分范围 下界")
@NotNull(message = "lowerCount is null")
private Integer lowerCount;
/**
* 积分范围 上界
*/
@ApiModelProperty(value = "积分范围 上界")
@NotNull(message = "upperCount is null")
private Integer upperCount;
/**
* 图标
*/
@ApiModelProperty(value = "图标")
@NotNull(message = "icon is null")
private String icon;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.member.dal.mysql.memberUserLevel.MemberUserLevelConfigMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.member.dal.mysql.memberUserScoreDetailReleation.MemberUserScoreDetailReleationMapper">
</mapper>
......@@ -36,7 +36,7 @@
<include refid="scoreCondition"/>
</select>
<select id="getByMemberId"
resultType="cn.iocoder.yudao.module.member.dal.dataobject.memberUserScore.MemberUserScoreDO">
resultType="cn.iocoder.yudao.module.member.dal.dataobject.score.MemberUserScoreDO">
select * from member_user_score where member_id = #{memberId} and deleted = 0
</select>
......
......@@ -29,5 +29,6 @@ public interface ErrorCodeConstants {
ErrorCode REWARD_REDEEM_BATCH_VERIFY_ERROR = new ErrorCode(1001011021, "reward.redeem.batch.verify.error");
ErrorCode REWARD_REDEEM_VERIFY_BACK_STATUS = new ErrorCode(1001011022, "reward.redeem.verify.back.status");
ErrorCode REDEEM_IMPORT_MAX_COUNT = new ErrorCode(1001011023, "redeem.import.max.count");
ErrorCode REDEEM_CANCEL_STATUS_ERROR = new ErrorCode(1001011024, "redeem.cancel.status.error");
}
......@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApi;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.enums.PlatformTypeEnum;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import cn.iocoder.yudao.module.reward.api.reward.dto.RedeemRewardReqVO;
import cn.iocoder.yudao.module.reward.api.reward.dto.RedeemRewardRespDTO;
......@@ -16,6 +17,7 @@ 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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
......@@ -30,6 +32,7 @@ import static cn.iocoder.yudao.module.reward.enums.ErrorCodeConstants.*;
@Service
@Validated
@Slf4j
public class RedeemRewardApiImpl implements RedeemRewardApi {
@Resource
......@@ -74,12 +77,12 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
if (!lock) {
throw exception(GET_LOCK_FAILED);
}
// 更新礼品
redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
// 添加兑换记录
Long redeemId = addRedeemRecord(redeemRewardReqVO);
Long redeemId = addRedeemRecord(redeemRewardReqVO, rewardDO);
// 更新会员积分
updateMemberScore(redeemRewardReqVO, rewardDO, redeemId);
// 更新礼品
redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
......@@ -106,14 +109,16 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
.memberId(redeemRewardReqVO.getMemberId())
.sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD)
.scoreCount(redeemRewardReqVO.getRewardCount() * rewardDO.getPointsRequire())
.releationId(String.valueOf(redeemId))
.extParam(extParam)
.build());
}
private Long addRedeemRecord(RedeemRewardReqVO redeemRewardReqVO) {
private Long addRedeemRecord(RedeemRewardReqVO redeemRewardReqVO, RewardDO rewardDO) {
RewardRedeemDO rewardRedeemDO = BeanUtil.copyProperties(redeemRewardReqVO, RewardRedeemDO.class);
rewardRedeemDO.setId(snowflakeGenerator.next());
rewardRedeemDO.setStatus(RewardRedeemStatusEnum.REDEEMING.getValue());
rewardRedeemDO.setScoreCount(redeemRewardReqVO.getRewardCount()* rewardDO.getPointsRequire());
rewardRedeemMapper.insert(rewardRedeemDO);
return rewardRedeemDO.getId();
}
......@@ -135,12 +140,14 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
int totalCount = 0;
for (RedeemRewardReqVO redeemRewardReqVO : redeemRewardReqVOList) {
// 每个兑换VO校验一遍
// 兑换方式不匹配
// 兑换方式不匹配,后台不校验
if (redeemRewardReqVO.getEntrance() != PlatformTypeEnum.BACKEND.getValue()) {
String pickMethod = rewardDO.getPickMethod();
String[] split = pickMethod.split(",");
if (!Arrays.asList(split).contains(String.valueOf(redeemRewardReqVO.getRedeemType()))) {
throw exception(REWARD_PICK_METHOD_NOT_ALLOW_CREATE);
}
}
verifyMemberUser(redeemRewardReqVO, rewardDO);
// 记录兑换总数
totalCount += redeemRewardReqVO.getRewardCount();
......@@ -156,15 +163,16 @@ public class RedeemRewardApiImpl implements RedeemRewardApi {
throw exception(GET_LOCK_FAILED);
}
for (RedeemRewardReqVO redeemRewardReqVO : redeemRewardReqVOList) {
// 更新礼品
redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
// 添加兑换记录
Long redeemId = addRedeemRecord(redeemRewardReqVO);
Long redeemId = addRedeemRecord(redeemRewardReqVO, rewardDO);
// 更新会员积分
updateMemberScore(redeemRewardReqVO, rewardDO, redeemId);
// 更新礼品
redeemReward(rewardDO, redeemRewardReqVO.getRewardCount());
}
} catch (Exception e) {
throw exception(REWARD_REDEEM_FAIL);
log.error("redeem reward exception",e);
throw e;
} finally {
redisDistributedLock.releaseLock("reward:redeem:lock:" + rewardId);
}
......
......@@ -84,6 +84,18 @@ public class RedeemRewardController {
return success(rewardRedeemService.export(reqVO));
}
@PostMapping("record/cancel/check")
@ApiOperation("撤销检查")
public CommonResult<RedeemCancelCheckRespVO> cancelCheck(@Valid @RequestBody RewardRedeemUpdateReqVO reqVO) {
return success(rewardRedeemService.cancelCheck(reqVO));
}
@PostMapping("record/cancel")
@ApiOperation("撤销")
public CommonResult<Boolean> cancel(@Valid @RequestBody RewardRedeemUpdateReqVO reqVO) {
return success(rewardRedeemService.cancel(reqVO));
}
@PostMapping("record/import/template")
@ApiOperation("导入模板下载")
public void importTemplate(HttpServletResponse response) throws IOException {
......
......@@ -33,6 +33,7 @@ public class RewardRedeemDO extends BaseDO {
* 兑换状态
*/
private Integer status;
private Integer scoreCount;
/**
* 兑换数量
*/
......
......@@ -90,7 +90,7 @@ public class RewardRedeemRecordExportListener {
exportVO.setStatus(item.getStatus());
exportVO.setRewardCode(item.getRewardCode());
exportVO.setRewardTitle(item.getRewardTitleZh());
exportVO.setMemberName(item.getMemberName());
exportVO.setMemberName(item.getMemberNameZh());
exportVO.setRewardCount(item.getRewardCount());
exportVO.setRedeemType(item.getRedeemType());
exportVO.setEntrance(item.getEntrance());
......
......@@ -33,4 +33,8 @@ public interface RewardRedeemService extends IService<RewardRedeemDO> {
Integer exportCount(RewardRedeemPageReqVO request);
RecordInfoImportRespVO recordImport(List<RedeemInfoImportExcelVO> dataList);
Boolean cancel(RewardRedeemUpdateReqVO reqVO);
RedeemCancelCheckRespVO cancelCheck(RewardRedeemUpdateReqVO reqVO);
}
......@@ -12,6 +12,10 @@ import cn.iocoder.yudao.module.ecw.api.currency.CurrencyApi;
import cn.iocoder.yudao.module.ecw.api.currency.dto.CurrencyRespDTO;
import cn.iocoder.yudao.module.ecw.api.express.ExpressApi;
import cn.iocoder.yudao.module.ecw.api.express.dto.ExpressRespDTO;
import cn.iocoder.yudao.module.member.api.score.MemberUserScoreApi;
import cn.iocoder.yudao.module.member.api.score.dto.MemberUserScoreOperateReqDTO;
import cn.iocoder.yudao.module.member.api.score.dto.ReleationScoreExpireInfoDTO;
import cn.iocoder.yudao.module.member.enums.ScoreSourceTypeEnum;
import cn.iocoder.yudao.module.reward.dal.dataobject.redeem.RewardRedeemDO;
import cn.iocoder.yudao.module.reward.dal.mysql.redeem.RewardRedeemMapper;
import cn.iocoder.yudao.module.reward.dto.RewardRedeemVerifyDTO;
......@@ -29,6 +33,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.function.Function;
......@@ -53,13 +58,15 @@ public class RewardRedeemServiceImpl extends AbstractService<RewardRedeemMapper,
private final FileMakeApi fileMakeApi;
private final ExpressApi expressApi;
private final CurrencyApi currencyApi;
private final MemberUserScoreApi memberUserScoreApi;
public RewardRedeemServiceImpl(RewardRedeemMapper rewardRedeemMapper, FileMakeApi fileMakeApi,
ExpressApi expressApi, CurrencyApi currencyApi) {
ExpressApi expressApi, CurrencyApi currencyApi, MemberUserScoreApi memberUserScoreApi) {
this.rewardRedeemMapper = rewardRedeemMapper;
this.fileMakeApi = fileMakeApi;
this.expressApi = expressApi;
this.currencyApi = currencyApi;
this.memberUserScoreApi = memberUserScoreApi;
}
@Override
......@@ -237,6 +244,59 @@ public class RewardRedeemServiceImpl extends AbstractService<RewardRedeemMapper,
return RecordInfoImportRespVO.builder().build();
}
@Override
@Transactional
public Boolean cancel(RewardRedeemUpdateReqVO reqVO) {
//取消兑换 状态改为已取消 回退扣减的积分 ,如果存在已过期的积分,则直接过期
if (reqVO.getId() == null) {
throw exception(ErrorCodeConstants.REWARD_REDEEM_NOT_EXIST);
}
RewardRedeemDO rewardRedeemDO = this.getById(reqVO.getId());
if (rewardRedeemDO == null) {
throw exception(ErrorCodeConstants.REWARD_REDEEM_NOT_EXIST);
}
if (rewardRedeemDO.getStatus() != RewardRedeemStatusEnum.REDEEMING.getValue()) {
throw exception(ErrorCodeConstants.REDEEM_CANCEL_STATUS_ERROR);
}
LambdaUpdateWrapper<RewardRedeemDO> updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(RewardRedeemDO::getId, reqVO.getId());
updateWrapper.eq(RewardRedeemDO::getStatus, RewardRedeemStatusEnum.REDEEMING.getValue());
updateWrapper.set(RewardRedeemDO::getStatus, RewardRedeemStatusEnum.CANCELED.getValue());
boolean updateSuccess = this.update(null, updateWrapper);
if (!updateSuccess) {
throw exception(ErrorCodeConstants.REDEEM_CANCEL_STATUS_ERROR);
}
Map<String, Object> extParam = new HashMap<>();
extParam.put("redeemId", reqVO.getId());
memberUserScoreApi.operateScore(MemberUserScoreOperateReqDTO.builder()
.memberId(rewardRedeemDO.getMemberId())
.sourceType(ScoreSourceTypeEnum.EXCHANGE_REWARD_CANCEL)
.scoreCount(rewardRedeemDO.getScoreCount())
.extParam(extParam)
.releationId(String.valueOf(reqVO.getId()))
.build());
return true;
}
@Override
public RedeemCancelCheckRespVO cancelCheck(RewardRedeemUpdateReqVO reqVO) {
if (reqVO.getId() == null) {
throw exception(ErrorCodeConstants.REWARD_REDEEM_NOT_EXIST);
}
RewardRedeemDO rewardRedeemDO = this.getById(reqVO.getId());
if (rewardRedeemDO == null) {
throw exception(ErrorCodeConstants.REWARD_REDEEM_NOT_EXIST);
}
if (rewardRedeemDO.getStatus() != RewardRedeemStatusEnum.REDEEMING.getValue()) {
throw exception(ErrorCodeConstants.REDEEM_CANCEL_STATUS_ERROR);
}
List<ReleationScoreExpireInfoDTO> scoreExpireInfo = memberUserScoreApi
.getScoreExpireInfo(String.valueOf(reqVO.getId()), ScoreSourceTypeEnum.EXCHANGE_REWARD);
int totalExpireScore = scoreExpireInfo.stream().filter(e -> e.getExpireDate() != null && e.getExpireDate().compareTo(new Date()) < 0)
.mapToInt(ReleationScoreExpireInfoDTO::getScoreCount).sum();
return RedeemCancelCheckRespVO.builder().expireCount(totalExpireScore).build();
}
private Map<String, String> validate(List<RedeemInfoImportExcelVO> dataList, Map<String, CurrencyRespDTO> titleZhCurrencyMap,
Map<String, CurrencyRespDTO> titleEnCurrencyMap,
Map<String, ExpressRespDTO> nameExpressMap) {
......
package cn.iocoder.yudao.module.reward.vo.reward;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class RedeemCancelCheckRespVO {
@ExcelProperty("过期积分数量")
private int expireCount;
}
......@@ -7,8 +7,10 @@ import lombok.Data;
@Data
@ApiModel("管理后台 - 礼品兑换记录分页结果 VO")
public class RewardRedeemPageRespVO extends RewardRedeemBaseVO {
@ApiModelProperty(value = "会员信息")
private String memberName;
@ApiModelProperty(value = "会员名称中文")
private String memberNameZh;
@ApiModelProperty(value = "会员名称英文")
private String memberNameEn;
@ApiModelProperty(value = "礼品ID")
private String rewardCode;
@ApiModelProperty(value = "礼品名称(中文)")
......
......@@ -95,14 +95,15 @@
err.deleted as deleted,
err.verify_user as verifyUser,
err.verify_time as verifyTime,
mu.nickname as memberName,
mu.nickname as memberNameZh,
mu.english_name as memberNameEn,
er.code as rewardCode,
er.title_zh as rewardTitleZh,
er.title_en as rewardTitleEn,
er.title_fr as rewardTitleFr,
en.title_zh as nodeTitleZh,
en.title_en as nodeTitleEn,
er.points_require*err.reward_count as totalCount,
err.score_count as totalCount,
suc.username as creatorName,
suu.username as updaterName,
cc.title_zh as currencyTitleZh,
......
......@@ -1023,3 +1023,6 @@ dict.unknown.error = Not in dict {0}: {1}
express.not.exist = express not exist
currency.not.exist = currency not exist
date.format.error = date format error, for example : 2024-01-01 12:11:11
redeem.cancel.status.error = record status must be redeeming
level.bound.range.error = upper count must greater than lower count
level.bound.range.conflict = score range exist conflict
\ No newline at end of file
......@@ -1025,3 +1025,6 @@ dict.unknown.error = \u4E0D\u5728{0}\u5B57\u5178\u4E2D: {1}
express.not.exist = \u5FEB\u9012\u516C\u53F8\u4E0D\u5B58\u5728
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
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.conflict = \u79EF\u5206\u8303\u56F4\u5B58\u5728\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