diff --git a/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateCalculateDTO.java b/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateCalculateDTO.java new file mode 100644 index 0000000..02498a3 --- /dev/null +++ b/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateCalculateDTO.java @@ -0,0 +1,32 @@ +package com.qs.serve.modules.tzc.entity.dto; + +import com.qs.serve.modules.tzc.entity.TzcRebateLevels; +import lombok.Data; + +import java.util.List; + +/** + * + * @author YenHex + * @since 2025/5/7 + */ +@Data +public class TzcRebateCalculateDTO { + + /** + * 当前条件,命中的最大级坎级 + */ + private TzcRebateLevels maxLevels; + + /** + * 同期或不同期的所有坎级 + */ + private List samePeriodLevelList; + + /** + * 通过配置叠加的坎级(包含参数及初始的返利金额) + * 初始的返利金额还要过滤一次,不能超过金额上限 + */ + private List levelAmountList; + +} diff --git a/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateLevelAmountDTO.java b/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateLevelAmountDTO.java new file mode 100644 index 0000000..5ec1446 --- /dev/null +++ b/cms-modules/cms-policy/cms-policy-api/src/main/java/com/qs/serve/modules/tzc/entity/dto/TzcRebateLevelAmountDTO.java @@ -0,0 +1,45 @@ +package com.qs.serve.modules.tzc.entity.dto; + +import com.qs.serve.modules.tzc.entity.TzcRebateLevels; +import com.qs.serve.modules.tzc.entity.TzcRebateLevelsArgument; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +/** + * @author YenHex + * @since 2025/5/7 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TzcRebateLevelAmountDTO extends TzcRebateLevels { + + /** + * 坎级返利金额(最终结果) + */ + private BigDecimal returnAmt; + + /** + * 最大返利金额 + */ + private BigDecimal maxReturn; + + /** + * 已经核销金额 + */ + private BigDecimal verificationAmt; + + /** + * 计算所得的返利金额(非最终结果) + */ + private BigDecimal levelReturnAmt; + + /** + * 坎级的返利参数 + */ + @NotNull + private TzcRebateLevelsArgument argument; + +} diff --git a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4CostService.java b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4CostService.java index cbbfc3a..1905477 100644 --- a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4CostService.java +++ b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4CostService.java @@ -1,8 +1,9 @@ package com.qs.serve.modules.tzc.service; +import com.qs.serve.modules.tzc.consts.TzcArgTypes; import com.qs.serve.modules.tzc.entity.*; -import com.qs.serve.modules.tzc.entity.bo.TzcRebateParam; -import com.qs.serve.modules.tzc.entity.vo.TzcRebateVo; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateCalculateDTO; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateLevelAmountDTO; import java.math.BigDecimal; import java.util.List; @@ -35,16 +36,31 @@ public interface TzcRebateApplication4CostService { List centerList, List levelsList); + + /** - * 获取最高命中返利坎级的信息 + * 获取最高命中返利坎级的信息及坎级列表 * @param dispatchAmt 当前客户的发货金额 * @param sameDispatchAmt 同期发货金额 * @param rebatePeriodId 返利期间id * @return */ - TzcRebateLevels getHitRebateMaxLevel(BigDecimal dispatchAmt, - BigDecimal sameDispatchAmt, - Long rebatePeriodId); + TzcRebateCalculateDTO getHitRebateMaxLevel(BigDecimal dispatchAmt, + BigDecimal sameDispatchAmt, + Long rebatePeriodId); + + /** + * 计算坎级返利 + * @param dto 计算DTO + * @param dispatchAmt + * @param sameDispatchAmt + * @param argType 参数类型:用于区分 + * @return + */ + List calculateRebate(TzcRebateCalculateDTO dto, + BigDecimal dispatchAmt, + BigDecimal sameDispatchAmt, + TzcArgTypes argType); } diff --git a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4VerificationService.java b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4VerificationService.java new file mode 100644 index 0000000..1b29e64 --- /dev/null +++ b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/TzcRebateApplication4VerificationService.java @@ -0,0 +1,26 @@ +package com.qs.serve.modules.tzc.service; + +import com.qs.serve.modules.tzc.consts.TzcArgTypes; +import com.qs.serve.modules.tzc.entity.*; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateCalculateDTO; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 返利信息 服务接口 + * @author YenHex + * @date 2025-04-15 + */ +public interface TzcRebateApplication4VerificationService { + + /** + * 获取活动返利金额 + * @param activityId + * @param dispatchAmt + * @return + */ + TzcRebateCalculateDTO getActivityRebateAmount(Long activityId,BigDecimal dispatchAmt); + +} + diff --git a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4CostServiceImpl.java b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4CostServiceImpl.java index 298fed5..8966a23 100644 --- a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4CostServiceImpl.java +++ b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4CostServiceImpl.java @@ -2,7 +2,7 @@ package com.qs.serve.modules.tzc.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.qs.serve.common.exception.Assert; +import com.qs.serve.common.utils.CopierUtil; import com.qs.serve.modules.tbs.common.TbsActivityState; import com.qs.serve.modules.tbs.common.TbsCostApplyState; import com.qs.serve.modules.tbs.entity.*; @@ -10,6 +10,8 @@ import com.qs.serve.modules.tbs.mapper.TbsActivityMapper; import com.qs.serve.modules.tbs.service.*; import com.qs.serve.modules.tzc.consts.TzcArgTypes; import com.qs.serve.modules.tzc.entity.*; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateCalculateDTO; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateLevelAmountDTO; import com.qs.serve.modules.tzc.service.*; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -329,7 +331,10 @@ public class TzcRebateApplication4CostServiceImpl implements TzcRebateApplicatio } @Override - public TzcRebateLevels getHitRebateMaxLevel(BigDecimal dispatchAmt,BigDecimal sameDispatchAmt,Long rebatePeriodId) { + public TzcRebateCalculateDTO getHitRebateMaxLevel(BigDecimal dispatchAmt, + BigDecimal sameDispatchAmt, + Long rebatePeriodId) { + TzcRebateCalculateDTO dto = new TzcRebateCalculateDTO(); // 判断是否同期 int samePeriodFlag = sameDispatchAmt.compareTo(BigDecimal.ZERO)==0?0:1; TzcRebatePeriod rebatePeriod = tzcRebatePeriodService.getById(rebatePeriodId); @@ -340,6 +345,7 @@ public class TzcRebateApplication4CostServiceImpl implements TzcRebateApplicatio .eq(TzcRebateLevels::getSamePeriodFlag, samePeriodFlag) .orderByDesc(TzcRebateLevels::getLevelNum) ); + dto.setSamePeriodLevelList(levelsList); // 命中的最高坎级 for (TzcRebateLevels levels : levelsList) { // 条件选择:0-百分比;1-固定金额 @@ -357,21 +363,118 @@ public class TzcRebateApplication4CostServiceImpl implements TzcRebateApplicatio .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP) )>=0; if (!ok){ - log.debug("命中坎级:"+levels.getLevelNum()+",条件比重:"+levels.getConditionRate()+"%"); + log.debug("命中最高坎级:"+levels.getLevelNum()+",条件比重:"+levels.getConditionRate()+"%"); continue; } - return levels; + dto.setMaxLevels(levels); + return dto; }else if (levels.getConditionSelect().equals(1)){ // 发货金额大于坎级制定的固定金额 boolean ok = dispatchAmt.compareTo(levels.getConditionAmount())>=0; if (!ok){ - log.debug("命中坎级:"+levels.getLevelNum()+",条件固定金额:"+levels.getConditionAmount()); + log.debug("命中最高坎级:"+levels.getLevelNum()+",条件固定金额:"+levels.getConditionAmount()); continue; } - return levels; + dto.setMaxLevels(levels); + return dto; } } return null; } + @Override + public List calculateRebate(TzcRebateCalculateDTO dto, + BigDecimal dispatchAmt, + BigDecimal sameDispatchAmt, + TzcArgTypes argType) { + if (dto==null){ + log.debug("没有命中坎级"); + return null; + } + TzcRebateLevels levels = dto.getMaxLevels(); + List allRebateLvList = dto.getSamePeriodLevelList(); + + TzcRebateLevelsConfig levelsConfig = tzcRebateLevelsConfigService.getOne( + new LambdaQueryWrapper() + .eq(TzcRebateLevelsConfig::getRebateId, levels.getRebateId()) + .eq(TzcRebateLevelsConfig::getLevelNum, levels.getLevelNum()) + ,false + ); + String[] configArr; + if (levelsConfig==null){ + log.warn("没有坎级配置:"+levels.getLevelNum()+";返利ID:"+levels.getRebateId()); + configArr = new String[]{levels.getLevelNum()+""}; + }else { + log.debug("读取坎级配置,坎级:"+levels.getLevelNum()); + configArr = levelsConfig.getLevelConfigs(); + } + // 获取命中的坎级 + List hitLevelsList = new ArrayList<>(); + for (TzcRebateLevels lv : allRebateLvList) { + for (String levelNum : configArr) { + if (lv.getLevelNum().toString().equals(levelNum)){ + hitLevelsList.add(lv); + break; + } + } + } + + // 加载返利参数 + List hitLevelsIds = hitLevelsList.stream().map(TzcRebateLevels::getId).collect(Collectors.toList()); + List argumentList = tzcRebateLevelsArgumentService.list( + new LambdaQueryWrapper() + .eq(TzcRebateLevelsArgument::getRebateId, levels.getRebateId()) + .in(TzcRebateLevelsArgument::getLevelId,hitLevelsIds) + ); + Map> argumentListMap = argumentList.stream() + .collect(Collectors.groupingBy(TzcRebateLevelsArgument::getLevelId)); + + // 进行返利叠加 + List levelAmountDTOList = new ArrayList<>(); + for (TzcRebateLevels rebateLevels : hitLevelsList) { + log.debug("读取坎级配置,坎级:"+rebateLevels.getLevelNum()); + List arguments = argumentListMap.get(rebateLevels.getId()); + TzcRebateLevelsArgument currentArgument = null; + for (TzcRebateLevelsArgument argument : arguments) { + if (argument.getBusinessType().equals(argType.name())){ + currentArgument = argument; + break; + } + } + if (currentArgument==null){ + log.error("没有命中坎级参数:"+argType.name()); + continue; + } + TzcRebateLevelAmountDTO rebateLevelsDTO = CopierUtil.copy(rebateLevels,new TzcRebateLevelAmountDTO()); + rebateLevelsDTO.setArgument(currentArgument); + rebateLevelsDTO.setMaxReturn(currentArgument.getMaxReturn()); + // 选择的策略:0-百分比;1-固定金额 + if (currentArgument.getRateAmtFlag().equals(1)){ + rebateLevelsDTO.setReturnAmt(currentArgument.getReturnFixed()); + }else if (currentArgument.getRateAmtFlag().equals(0)){ + //目标销量维度:0-总量;1-增量 + if (currentArgument.getSumAddFlag().equals(0)){ + BigDecimal rs = dispatchAmt.multiply(currentArgument.getReturnRate()) + .divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP); + rebateLevelsDTO.setReturnAmt(rs); + }else if (currentArgument.getSumAddFlag().equals(1)){ + // 增量 = (发货金额 - 同比发货金额) * 返利比例 / 100 + BigDecimal rs = dispatchAmt.subtract(sameDispatchAmt) + .multiply(currentArgument.getReturnRate()) + .divide(BigDecimal.valueOf(100),2,RoundingMode.HALF_UP); + if (rs.compareTo(BigDecimal.ZERO)>0){ + rebateLevelsDTO.setReturnAmt(rs); + }else { + rebateLevelsDTO.setReturnAmt(BigDecimal.ZERO); + log.warn("返利参数TzcRebateLevelsArgument-ID:{},返利金额小于0",currentArgument.getId()); + } + } + } + // 记录坎级返利金额 + rebateLevelsDTO.setLevelReturnAmt(rebateLevelsDTO.getReturnAmt()); + levelAmountDTOList.add(rebateLevelsDTO); + } + return levelAmountDTOList; + } + } diff --git a/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4VerificationServiceImpl.java b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4VerificationServiceImpl.java new file mode 100644 index 0000000..06eda7e --- /dev/null +++ b/cms-modules/cms-system/src/main/java/com/qs/serve/modules/tzc/service/impl/TzcRebateApplication4VerificationServiceImpl.java @@ -0,0 +1,82 @@ +package com.qs.serve.modules.tzc.service.impl; + +import com.qs.serve.modules.tbs.entity.TbsActivity; +import com.qs.serve.modules.tbs.mapper.TbsActivityMapper; +import com.qs.serve.modules.tbs.service.*; +import com.qs.serve.modules.tzc.consts.TzcArgTypes; +import com.qs.serve.modules.tzc.entity.TzcRebatePeriod; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateCalculateDTO; +import com.qs.serve.modules.tzc.entity.dto.TzcRebateLevelAmountDTO; +import com.qs.serve.modules.tzc.service.*; +import com.qs.serve.modules.vtb.entity.VtbVerification; +import com.qs.serve.modules.vtb.mapper.VtbVerificationMapper; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author YenHex + * @since 2025/5/7 + */ +@Slf4j +@Service +@AllArgsConstructor +public class TzcRebateApplication4VerificationServiceImpl implements TzcRebateApplication4VerificationService { + + private final TzcRebateApplication4CostService tzcRebateApplication4CostService; + + private final VtbVerificationMapper vtbVerificationMapper; + + private final TbsActivityMapper tbsActivityMapper; + private final TbsCostApplyService tbsCostApplyService; + private final TbsActivityService tbsActivityService; + private final TbsActivityCenterService tbsActivityCenterService; + private final TbsActivitySubjectService tbsActivitySubjectService; + private final TbsActivityGoodsService tbsActivityGoodsService; + private final TbsActivityCenterGoodsService tbsActivityCenterGoodsService; + + private final TzcRebateService tzcRebateService; + private final TzcRebateSubjectService tzcRebateSubjectService; + private final TzcRebateCenterService tzcRebateCenterService; + private final TzcRebatePeriodService tzcRebatePeriodService; + private final TzcRebateLevelsArgumentService tzcRebateLevelsArgumentService; + private final TzcRebateGoodsService tzcRebateGoodsService; + private final TzcRebateLevelsConfigService tzcRebateLevelsConfigService; + private final TzcRebateLevelsService tzcRebateLevelsService; + + + @Override + public TzcRebateCalculateDTO getActivityRebateAmount(Long activityId, BigDecimal dispatchAmt) { + TbsActivity activity = tbsActivityMapper.selectById(activityId); + TzcRebatePeriod rebatePeriod = tzcRebatePeriodService.getById(activity.getRebatePeriodId()); + BigDecimal sameDispatchAmt = rebatePeriod.getSamePeriodAmount(); + // 获取最高命中的坎级 + TzcRebateCalculateDTO calculateDTO = tzcRebateApplication4CostService.getHitRebateMaxLevel( + dispatchAmt, sameDispatchAmt, activity.getRebatePeriodId()); + if (calculateDTO != null){ + List levelAmountList = tzcRebateApplication4CostService.calculateRebate( + calculateDTO,dispatchAmt, sameDispatchAmt, TzcArgTypes.LevelReturn); + // 计算已用的优惠 + for (TzcRebateLevelAmountDTO amountDTO : levelAmountList) { + // 返利金额 + BigDecimal returnAmt = amountDTO.getReturnAmt(); + // 设置的最高返利金额 + BigDecimal maxReturn = amountDTO.getMaxReturn(); + // 已核销金额 + BigDecimal verificationAmt = vtbVerificationMapper.totalActivityVerification(activityId+""); + // 剩余的可返利金额 + BigDecimal surplusReturnAmt = maxReturn.subtract(verificationAmt); + if (returnAmt.compareTo(surplusReturnAmt) > 0){ + amountDTO.setReturnAmt(surplusReturnAmt); + } + amountDTO.setVerificationAmt(verificationAmt); + } + calculateDTO.setLevelAmountList(levelAmountList); + } + return calculateDTO; + } + +}