package cn.fw.morax.service.biz.calculator.eval.reward; import cn.fw.morax.common.utils.DateUtil; import cn.fw.morax.common.utils.PublicUtil; import cn.fw.morax.domain.bo.eval.EvalGroupUserShop; import cn.fw.morax.domain.db.eval.*; import cn.fw.morax.domain.db.kpi.IndicatorUserValue; import cn.fw.morax.domain.enums.*; import cn.fw.morax.service.biz.CommonService; import cn.fw.morax.service.biz.calculator.Calculator; import cn.fw.morax.service.data.eval.*; import cn.fw.morax.service.data.kpi.IndicatorUserValueService; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.Lists; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.BoundZSetOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; /** * 考评基础计算器 * * @author : kurisu * @version : 2.0 * @className : BaseCalculator * @description : 考评基础计算器 * @date : 2022-12-26 14:17 */ @Slf4j public abstract class EvalRewardBaseCalculator implements Calculator { @Autowired protected EvalGroupRewardRankLogService evalGroupRewardRankLogService; @Autowired protected EvalGroupRewardHitLogService evalGroupRewardHitLogService; @Autowired protected EvalGroupRewardPreconditionHitLogService evalGroupRewardPreconditionHitLogService; @Autowired protected IndicatorUserValueService indicatorUserValueService; @Autowired protected EvalGroupRewardParamService evalGroupRewardParamService; @Autowired protected EvalGroupRewardPreconditionService evalGroupRewardPreconditionService; @Autowired protected EvalGroupRewardPreconditionHitLogService preconditionHitLogService; @Autowired protected EvalGroupRewardTargetHitLogService evalGroupRewardTargetHitLogService; @Autowired protected EvalIndicatorValueService evalIndicatorValueService; @Autowired protected EvalGroupRewardLaddersService evalGroupRewardLaddersService; @Autowired protected EvalGroupRewardService evalGroupRewardService; @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private CommonService commonService; @Value("${spring.cache.custom.global-prefix}:eval:reward:cond:rank") @Getter private String preconditionRank; /** * 获取设置类型 * * @return */ public abstract EvalRewardCalMethodEnum getCalMethod(); /** * 查询前置条件 * * @param evalGroupRewardId * @return */ protected List queryPrecondition(Long evalGroupRewardId) { List preList = evalGroupRewardPreconditionService.list(Wrappers.lambdaQuery() .eq(EvalGroupRewardPrecondition::getEvalGroupRewardId, evalGroupRewardId) .eq(EvalGroupRewardPrecondition::getYn, Boolean.TRUE) .orderByAsc(EvalGroupRewardPrecondition::getSort) ); return Optional.ofNullable(preList).orElse(new ArrayList<>()); } /** * 查询前置条件 * * @param evalGroupRewardId * @return */ protected List queryRankPrecondition(Long evalGroupRewardId) { List preList = evalGroupRewardPreconditionService.list(Wrappers.lambdaQuery() .eq(EvalGroupRewardPrecondition::getEvalGroupRewardId, evalGroupRewardId) .eq(EvalGroupRewardPrecondition::getCondType, ConditionTypeEnum.RANK) .eq(EvalGroupRewardPrecondition::getYn, Boolean.TRUE) .orderByAsc(EvalGroupRewardPrecondition::getSort) ); return Optional.ofNullable(preList).orElse(new ArrayList<>()); } /** * 查询考评指标详细配置 * * @param evalGroupRewardId 考评组指标配置id * @return */ protected List queryRewardLadders(final Long evalGroupRewardId) { List ladders = evalGroupRewardLaddersService.list(Wrappers.lambdaQuery() .eq(EvalGroupRewardLadders::getEvalGroupRewardId, evalGroupRewardId) .eq(EvalGroupRewardLadders::getYn, Boolean.TRUE)); if (CollectionUtils.isEmpty(ladders)) { return Collections.emptyList(); } return ladders; } /** * 校验前置条件 * * @return */ protected boolean examinePrecondition(EvalGroupUserShop userShop, List preconditions) { if (CollectionUtils.isEmpty(preconditions)) { return Boolean.TRUE; } for (EvalGroupRewardPrecondition precondition : preconditions) { if (ConditionTypeEnum.RANK.equals(precondition.getCondType())) { return Optional.ofNullable(userShop.getMeetRankCondIds()).orElse(new ArrayList<>()).contains(precondition.getId()); } String indicatorCode = precondition.getCode(); final BigDecimal indicatorValue = queryValue(userShop, precondition.getCodeType(), indicatorCode).orElse(BigDecimal.ZERO); BigDecimal userValue = indicatorValue; TargetTypeEnum targetType = precondition.getTargetType(); final BigDecimal condValue = precondition.getCondValue(); if (!TargetTypeEnum.NO.equals(targetType)) { TargetCalcTypeEnum targetCalcType = precondition.getTargetCalcType(); BigDecimal targetValue = precondition.getTargetValue(); userValue = calculateTargetValue(targetCalcType, targetValue, userValue); saveTargetHitLog(userShop, precondition.getId(), IndicatorTypeEnum.PRE, indicatorValue, userValue); } if (userValue.compareTo(condValue) < 0) { return Boolean.FALSE; } } return Boolean.TRUE; } @Override public void calculateRankPrecondition(List params, EvalGroupReward reward) { List rankPreconditions = queryRankPrecondition(reward.getId()); examineRankPreconditions(params, rankPreconditions); } /** * 校验前置条件 * * @return */ @Transactional(rollbackFor = Exception.class) protected void examineRankPreconditions(List userShops, List preconditions) { if (CollectionUtils.isEmpty(preconditions)) { return; } for (EvalGroupRewardPrecondition precondition : preconditions) { final Long preconditionId = precondition.getId(); final BigDecimal personCount = BigDecimal.valueOf(userShops.size()); final BigDecimal condValue = precondition.getCondValue(); final Integer startIndex = 0; final Integer endIndex = condValue.multiply(personCount).divide(BigDecimal.ONE, 0, RoundingMode.FLOOR).intValue() - 1; //计算排名 List rankLogs = calculateRankPreconditionValue(userShops, precondition); Collections.sort(rankLogs); commonService.calcRank(rankLogs); saveRankLogs(preconditionId, rankLogs, RankIndicatorTypeEnum.PRE); Map poolUserShopMap = userShops.stream().collect(Collectors.toMap(EvalGroupUserShop::getPoolId, Function.identity(), (v1, v2) -> v1)); if (startIndex > endIndex) { continue; } Integer _startIndex = startIndex, _endIndex = endIndex;; List matchRankLogs = rankLogs.stream().filter(rankLog -> { return rankLog.getRank() > _startIndex && rankLog.getRank() <= _endIndex; }).collect(Collectors.toList()); updateHitRankLog(matchRankLogs); AtomicInteger rank = new AtomicInteger(0); for (EvalGroupRewardRankLog rankLog : matchRankLogs) { EvalGroupUserShop userShop = poolUserShopMap.get(rankLog.getPoolId()); if (PublicUtil.isNotEmpty(userShop)) { List meetRankCondIds = Optional.ofNullable(userShop.getMeetRankCondIds()).orElse(new ArrayList<>()); meetRankCondIds.add(preconditionId); userShop.setMeetRankCondIds(meetRankCondIds); savePreHitLog(userShop, precondition, log -> { log.setReachValue(rankLog.getReachValue()); log.setCondValue(new BigDecimal(endIndex + 1)); log.setRank(rank.incrementAndGet()); }); } } } } /** * 查询提成指标项 * * @param evalGroupRewardId * @param paramType * @return */ protected List queryProjectParam(final Long evalGroupRewardId, final ParamTypeEnum paramType) { List params = evalGroupRewardParamService.list(Wrappers.lambdaQuery() .eq(EvalGroupRewardParam::getEvalGroupRewardId, evalGroupRewardId) .eq(EvalGroupRewardParam::getParamType, paramType) .eq(EvalGroupRewardParam::getYn, Boolean.TRUE) ); if (CollectionUtils.isEmpty(params)) { return Collections.emptyList(); } return params; } @Transactional(rollbackFor = Exception.class) protected void saveTargetHitLog(EvalGroupUserShop userShop, Long rewardReferId, IndicatorTypeEnum targetType, BigDecimal value, BigDecimal reachValue) { evalGroupRewardTargetHitLogService.remove(Wrappers.lambdaQuery() .eq(EvalGroupRewardTargetHitLog::getPoolId, userShop.getPoolId()) .eq(EvalGroupRewardTargetHitLog::getScopeType, userShop.getScopeType()) .eq(EvalGroupRewardTargetHitLog::getReferId, rewardReferId) .eq(EvalGroupRewardTargetHitLog::getTargetType, targetType) .eq(EvalGroupRewardTargetHitLog::getDataDate, userShop.getDataDate()) ); EvalGroupRewardTargetHitLog log = new EvalGroupRewardTargetHitLog(); log.setPoolId(userShop.getPoolId()); log.setScopeType(userShop.getScopeType()); log.setReferId(rewardReferId); log.setTargetType(targetType); log.setValue(value); log.setReachValue(reachValue); log.setDataDate(userShop.getDataDate()); log.setGroupId(userShop.getGroupId()); log.setYn(Boolean.TRUE); evalGroupRewardTargetHitLogService.save(log); } @Transactional(rollbackFor = Exception.class) protected void savePreHitLog(EvalGroupUserShop userShop, EvalGroupRewardPrecondition precondition, Consumer consumer) { evalGroupRewardPreconditionHitLogService.remove(Wrappers.lambdaQuery() .eq(EvalGroupRewardPreconditionHitLog::getPoolId, userShop.getPoolId()) .eq(EvalGroupRewardPreconditionHitLog::getScopeType, userShop.getScopeType()) .eq(EvalGroupRewardPreconditionHitLog::getPreconditionId, precondition.getId()) .eq(EvalGroupRewardPreconditionHitLog::getDataDate, userShop.getDataDate()) ); EvalGroupRewardPreconditionHitLog log = new EvalGroupRewardPreconditionHitLog(); log.setEvalGroupId(userShop.getEvalGroupId()); log.setEvalGroupRewardId(precondition.getEvalGroupRewardId()); log.setPoolId(userShop.getPoolId()); log.setScopeType(userShop.getScopeType()); log.setPreconditionId(precondition.getId()); log.setDataDate(userShop.getDataDate()); log.setGroupId(userShop.getGroupId()); log.setYn(Boolean.TRUE); if (consumer != null) { consumer.accept(log); } evalGroupRewardPreconditionHitLogService.save(log); } /** * 查询指标值 * * @param indicatorCode * @return */ protected Optional queryValue(EvalGroupUserShop obj, IndicatorCodeTypeEnum codeType, String indicatorCode) { DimensionTypeEnum dimensionType = EvalScopeEnum.STAFF.equals(obj.getScopeType()) ? DimensionTypeEnum.STAFF : DimensionTypeEnum.SHOP; if (IndicatorCodeTypeEnum.INDICATOR.equals(codeType)) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() .eq(IndicatorUserValue::getIndicatorCode, indicatorCode) .eq(IndicatorUserValue::getDataDate, obj.getDataDate()) .eq(IndicatorUserValue::getDimensionType, dimensionType) .eq(IndicatorUserValue::getYn, Boolean.TRUE) .eq(IndicatorUserValue::getGroupId, obj.getGroupId()); if (EvalScopeEnum.STAFF.equals(obj.getScopeType())) { queryWrapper.eq(IndicatorUserValue::getUserId, obj.getReferId()); } else { queryWrapper.eq(IndicatorUserValue::getShopId, obj.getReferId()); } IndicatorUserValue indicatorUserValue = indicatorUserValueService.getOne(queryWrapper, Boolean.FALSE); return queryKpiIndicatorValue(indicatorUserValue); } else { EvalIndicatorValue evalIndicatorValue = evalIndicatorValueService.queryLastValue(obj.getReferId(), dimensionType, indicatorCode, obj.getDataDate()); return Optional.ofNullable(evalIndicatorValue).map(EvalIndicatorValue::getIndicatorValue); } } /** * 查询具体指标值 * * @param indicatorUserValue * @return */ protected Optional queryKpiIndicatorValue(IndicatorUserValue indicatorUserValue) { if (PublicUtil.isEmpty(indicatorUserValue) || PublicUtil.isEmpty(indicatorUserValue.getIndicatorValue())) { return Optional.empty(); } BigDecimal value = BigDecimal.ZERO; try { JSONObject jsonObject = JSONObject.parseObject(indicatorUserValue.getIndicatorValue()); value = Optional.ofNullable(jsonObject.getBigDecimal(indicatorUserValue.getIndicatorCode())).orElse(BigDecimal.ZERO); } catch (Exception e) { log.error("[{}]指标值转化失败", indicatorUserValue, e); } return Optional.of(value); } /** * 经过目标计算后的用户指标值 * * @param targetCalcType * @param targetValue * @param userValue * @return */ protected BigDecimal calculateTargetValue(TargetCalcTypeEnum targetCalcType, BigDecimal targetValue, BigDecimal userValue) { if (Objects.isNull(targetValue) || targetValue.compareTo(BigDecimal.ZERO) <= 0) { return userValue; } if (TargetCalcTypeEnum.TARGET_VALUE.equals(targetCalcType)) { return userValue.divide(targetValue, 4, RoundingMode.HALF_UP); } BigDecimal diffValue = BigDecimal.ONE.subtract(targetValue); BigDecimal userDiffValue = userValue.subtract(targetValue); if (userDiffValue.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO; } if (diffValue.compareTo(BigDecimal.ZERO) <= 0) { return userDiffValue; } return userDiffValue.divide(diffValue, 4, RoundingMode.HALF_UP); } /** * 清除project记录命中配置 * * @param evalGroupRewardId 指标配置id * @param param 人员信息 */ @Transactional(rollbackFor = Exception.class) public void clearProjectHitLog(Long evalGroupRewardId, EvalGroupUserShop param) { final Long poolId = param.getPoolId(); final EvalScopeEnum scopeType = param.getScopeType(); final LocalDate dataDate = param.getDataDate(); final Long groupId = param.getGroupId(); evalGroupRewardHitLogService.remove(Wrappers.lambdaQuery() .eq(EvalGroupRewardHitLog::getPoolId, poolId) .eq(EvalGroupRewardHitLog::getScopeType, scopeType) .eq(EvalGroupRewardHitLog::getEvalGroupRewardId, evalGroupRewardId) .eq(EvalGroupRewardHitLog::getDataDate, dataDate) .eq(EvalGroupRewardHitLog::getGroupId, groupId) ); } /** * 记录命中配置 * * @param evalGroupRewardId 指标配置id * @param userShop 信息 * @param consumer */ @Transactional(rollbackFor = Exception.class) public void saveProjectHitLog(Long evalGroupRewardId, EvalGroupUserShop userShop, Consumer consumer) { final EvalScopeEnum scopeType = userShop.getScopeType(); final LocalDate dataDate = userShop.getDataDate(); final Long groupId = userShop.getGroupId(); EvalGroupRewardHitLog hitLog = EvalGroupRewardHitLog.builder() .evalGroupRewardId(evalGroupRewardId) .poolId(userShop.getPoolId()) .scopeType(scopeType) .evalGroupRewardLaddersId(-1L) .rewardValue(BigDecimal.ZERO) .hitCommissionValue(BigDecimal.ZERO) .dataDate(dataDate) .groupId(groupId) .build(); if (consumer != null) { consumer.accept(hitLog); } evalGroupRewardHitLogService.save(hitLog); } /** * 计算提成参数最终值 * * @param rewardObjectBO * @param params * @return */ protected BigDecimal calculateParamValue(EvalGroupUserShop rewardObjectBO, List params) { BigDecimal commissionValue = BigDecimal.ZERO; for (EvalGroupRewardParam param: params) { String indicatorCode = param.getCode(); boolean isCap = Boolean.TRUE.equals(param.getCap()); BigDecimal proportion = param.getProportion(); TargetTypeEnum targetType = param.getTargetType(); ParamTypeEnum paramType = param.getParamType(); final BigDecimal userOriginValue = queryValue(rewardObjectBO, param.getCodeType(), indicatorCode).orElse(BigDecimal.ZERO); BigDecimal _calcValue = userOriginValue; if (!TargetTypeEnum.NO.equals(targetType)) { _calcValue = calculateTargetValue(param.getTargetCalcType(), param.getTargetValue(), _calcValue); saveTargetHitLog(rewardObjectBO, param.getId(), IndicatorTypeEnum.EXAMINE, userOriginValue, _calcValue); } if (isCap) { _calcValue = _calcValue.compareTo(BigDecimal.ONE) > 0 ? BigDecimal.ONE : _calcValue; } commissionValue = commissionValue.add(proportion.multiply(_calcValue)); } return commissionValue; } /** * 计算排名提成参数最终值 * * @param userShops * @param evalGroupRewardId * @param params * @return */ protected List calculateRankParamValue(List userShops, Long evalGroupRewardId, List params) { List rankLogs = Lists.newArrayListWithCapacity(userShops.size()); for (EvalGroupUserShop userShop : userShops) { BigDecimal commissionValue = BigDecimal.ZERO; for (EvalGroupRewardParam param: params) { String indicatorCode = param.getCode(); boolean isCap = Boolean.TRUE.equals(param.getCap()); BigDecimal proportion = param.getProportion(); TargetTypeEnum targetType = param.getTargetType(); final BigDecimal userOriginValue = queryValue(userShop, param.getCodeType(), indicatorCode).orElse(BigDecimal.ZERO); BigDecimal _calcValue = userOriginValue; if (!TargetTypeEnum.NO.equals(targetType)) { _calcValue = calculateTargetValue(param.getTargetCalcType(), param.getTargetValue(), _calcValue); saveTargetHitLog(userShop, param.getId(), IndicatorTypeEnum.EXAMINE, userOriginValue, _calcValue); } if (isCap) { _calcValue = _calcValue.compareTo(BigDecimal.ONE) > 0 ? BigDecimal.ONE : _calcValue; } commissionValue = commissionValue.add(proportion.multiply(_calcValue)); } EvalGroupRewardRankLog rankLog = EvalGroupRewardRankLog.builder() .poolId(userShop.getPoolId()) .name(userShop.getName()) .scopeType(userShop.getScopeType()) .referId(evalGroupRewardId) .targetType(RankIndicatorTypeEnum.REWARD_COMMISSION) .value(commissionValue) .reachValue(commissionValue) .hit(Boolean.FALSE) .dataDate(userShop.getDataDate()) .groupId(userShop.getGroupId()) .build(); rankLogs.add(rankLog); } return rankLogs; } /** * 计算排名条件最终值 * * @param userShops * @param precondition * @return */ protected List calculateRankPreconditionValue(List userShops, EvalGroupRewardPrecondition precondition) { final String indicatorCode = precondition.getCode(); final Long preconditionId = precondition.getId(); final TargetTypeEnum targetType = precondition.getTargetType(); final BigDecimal targetValue = precondition.getTargetValue(); final TargetCalcTypeEnum targetCalcType = precondition.getTargetCalcType(); List rankLogs = Lists.newArrayListWithCapacity(userShops.size()); for (EvalGroupUserShop userShop : userShops) { final BigDecimal indicatorValue = queryValue(userShop, precondition.getCodeType(), indicatorCode).orElse(BigDecimal.ZERO); BigDecimal reachValue = indicatorValue; if (!TargetTypeEnum.NO.equals(targetType)) { reachValue = calculateTargetValue(targetCalcType, targetValue, indicatorValue); } EvalGroupRewardRankLog rankLog = EvalGroupRewardRankLog.builder() .name(userShop.getName()) .poolId(userShop.getPoolId()) .scopeType(userShop.getScopeType()) .referId(preconditionId) .targetType(RankIndicatorTypeEnum.PRE) .value(indicatorValue) .reachValue(reachValue) .dataDate(userShop.getDataDate()) .groupId(userShop.getGroupId()) .build(); rankLogs.add(rankLog); } return rankLogs; } @Transactional(rollbackFor = Exception.class) public void saveRankLogs(Long evalGroupRewardId, List rankLogs, RankIndicatorTypeEnum targetType) { Set poolIds = rankLogs.stream().map(EvalGroupRewardRankLog::getPoolId).collect(Collectors.toSet()); EvalGroupRewardRankLog rankLog = rankLogs.stream().findFirst().get(); evalGroupRewardRankLogService.remove(Wrappers.lambdaQuery() .in(EvalGroupRewardRankLog::getPoolId, poolIds) .eq(EvalGroupRewardRankLog::getScopeType, rankLog.getScopeType()) .eq(EvalGroupRewardRankLog::getReferId, evalGroupRewardId) .eq(EvalGroupRewardRankLog::getTargetType, targetType) .eq(EvalGroupRewardRankLog::getGroupId, rankLog.getGroupId()) ); evalGroupRewardRankLogService.saveBatch(rankLogs); } @Transactional(rollbackFor = Exception.class) public void updateHitRankLog(List matchRankLogs) { if (PublicUtil.isEmpty(matchRankLogs)) { return; } evalGroupRewardRankLogService.update(Wrappers.lambdaUpdate() .in(EvalGroupRewardRankLog::getId, matchRankLogs.stream().map(EvalGroupRewardRankLog::getId).collect(Collectors.toList())) .set(EvalGroupRewardRankLog::getHit, Boolean.TRUE) ); } /** * 初始化奖惩值 * * @param userShops * @return */ protected void initReward(List userShops) { if (PublicUtil.isEmpty(userShops)) { return; } for (EvalGroupUserShop userShop : userShops) { userShop.setEvalGroupRewardAmount(BigDecimal.ZERO); } } }