EvalKpiBaseCalculator.java 16.2 KB
package cn.fw.morax.service.biz.calculator.eval.kpi;

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.*;
import cn.fw.morax.domain.enums.*;
import cn.fw.morax.service.biz.calculator.Calculator;
import cn.fw.morax.service.data.eval.*;
import cn.fw.morax.service.data.kpi.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

/**
 * 考评基础计算器
 *
 * @author : kurisu
 * @version : 2.0
 * @className : BaseCalculator
 * @description : 考评基础计算器
 * @date : 2022-12-26 14:17
 */
@Slf4j
public abstract class EvalKpiBaseCalculator implements Calculator<EvalGroupUserShop, EvalGroupIndicator, BigDecimal> {

    @Autowired
    protected EvalGroupIndicatorHitLogService evalGroupIndicatorHitLogService;
    @Autowired
    protected IndicatorUserValueService indicatorUserValueService;
    @Autowired
    protected IndicatorUserStageValueService indicatorUserStageValueService;
    @Autowired
    protected EvalIndicatorValueService evalIndicatorValueService;
    @Autowired
    protected EvalGroupIndicatorParamService evalGroupIndicatorParamService;
    @Autowired
    protected EvalGroupIndicatorPreconditionService evalGroupIndicatorPreconditionService;
    @Autowired
    protected EvalGroupIndicatorPreconditionHitLogService preconditionHitLogService;
    @Autowired
    protected EvalGroupIndicatorPreconditionLaddersService preconditionLaddersService;
    @Autowired
    protected EvalGroupIndicatorTargetHitLogService evalGroupIndicatorTargetHitLogService;

    /**
     * 获取设置类型
     *
     * @return
     */
    public abstract ScoreWayEnum getCalMethod();

    /**
     * 通过前置条件计算绩效得分系数
     *
     * @param preconditions
     * @return
     */
    protected BigDecimal calcCoefficient(EvalGroupUserShop obj, List<EvalGroupIndicatorPrecondition> preconditions) {
        BigDecimal coefficient = BigDecimal.ONE;
        if (CollectionUtils.isEmpty(preconditions)) {
            return coefficient;
        }
        for (int i = 0; i < preconditions.size(); i++) {
            EvalGroupIndicatorPrecondition precondition = preconditions.get(i);
            TargetCalcTypeEnum targetCalcType = precondition.getTargetCalcType();
            TargetTypeEnum targetType = precondition.getTargetType();
            Optional<BigDecimal> valueOptional = queryValue(obj, precondition.getCodeType(), precondition.getCode());
            if (! valueOptional.isPresent()) {
                return BigDecimal.ZERO;
            }
            final BigDecimal originValue = valueOptional.get();//queryIndicatorValue(precondition.getCode(), valueOptional.get());
            BigDecimal indicatorValue = originValue;
            if (!TargetTypeEnum.NO.equals(targetType)) {
                BigDecimal targetValue = precondition.getTargetValue();
                if (TargetCalcTypeEnum.TARGET_VALUE.equals(targetCalcType) || TargetCalcTypeEnum.STAGE_TARGET.equals(targetCalcType)) {
                    indicatorValue = indicatorValue.divide(targetValue, 4, RoundingMode.HALF_UP);
                } else if (TargetCalcTypeEnum.MINIMUM.equals(targetCalcType)){
                    BigDecimal difference = BigDecimal.ONE.subtract(targetValue);
                    if (BigDecimal.ZERO.compareTo(difference) == 0) {
                        difference = BigDecimal.ONE;
                    }
                    BigDecimal userDiffValue = indicatorValue.subtract(targetValue);
                    if (userDiffValue.compareTo(BigDecimal.ZERO) <= 0) {
                        indicatorValue = BigDecimal.ZERO;
                    } else {
                        indicatorValue = userDiffValue.divide(difference, 4, RoundingMode.HALF_UP);
                    }
                }

                saveTargetHitLog(obj, precondition.getId(), IndicatorTypeEnum.PRE, originValue, indicatorValue);
            }
            coefficient = coefficient(obj, precondition.getId(), indicatorValue);
        }
        return coefficient;
    }

    protected BigDecimal coefficient(EvalGroupUserShop obj, Long preconditionId, BigDecimal value) {
        EvalGroupIndicatorPreconditionLadders ladder = preconditionLaddersService.getOne(Wrappers.<EvalGroupIndicatorPreconditionLadders>lambdaQuery()
                        .eq(EvalGroupIndicatorPreconditionLadders::getPreconditionId, preconditionId)
                        .gt(EvalGroupIndicatorPreconditionLadders::getUpper, value)
                        .le(EvalGroupIndicatorPreconditionLadders::getLower, value)
                        .eq(EvalGroupIndicatorPreconditionLadders::getYn, Boolean.TRUE),
                Boolean.FALSE
        );
        if (Objects.isNull(ladder)) {
            savePreconditionHitLog(obj, preconditionId, hitLog -> {
                hitLog.setPreconditionLaddersId(-1L);
                hitLog.setValue(value);
            });
            return BigDecimal.ZERO;
        }

        savePreconditionHitLog(obj, preconditionId, hitLog -> {
            hitLog.setPreconditionLaddersId(ladder.getId());
            hitLog.setValue(value);
        });
        return ladder.getScorePercent();
    }

    /**
     * 计算最终的指标值
     *
     * @param params
     * @param userShop
     * @param evalGroupIndicatorId
     * @return
     */
    protected BigDecimal calculateFinalIndValue(List<EvalGroupIndicatorParam> params, EvalGroupUserShop userShop, Long evalGroupIndicatorId) {
        BigDecimal rate = BigDecimal.ZERO;
        if (CollectionUtils.isEmpty(params)) {
            log.info("指标[{}]配置不存在", evalGroupIndicatorId);
            return rate;
        }
        for (EvalGroupIndicatorParam param : params) {
            Optional<BigDecimal> valueOptional = queryValue(userShop, param.getCodeType(), param.getCode());
            if (! valueOptional.isPresent()) {
                log.info("[{}] [{}]指标[{}]值不存在", userShop.getScopeType().getName(), userShop.getReferId(), param.getCode());
                continue;
            }
            TargetTypeEnum targetType = param.getTargetType();
            TargetTypeEnum extraTargetType = param.getExtraTargetType();
            final BigDecimal originValue = valueOptional.get();
            BigDecimal indicatorValue = null;
            BigDecimal extraIndicatorValue = null;
            if (!TargetTypeEnum.NO.equals(targetType)) {
                indicatorValue = calcTarget(param.getTargetCalcType(), originValue, param.getTargetValue());
            }
            if (PublicUtil.isNotEmpty(extraTargetType) && (!TargetTypeEnum.NO.equals(extraTargetType))) {
                extraIndicatorValue = calcTarget(param.getExtraTargetCalcType(), originValue, param.getExtraTargetValue());
                //额外计算类型值  原始计算类型值  用大的那个值
                if (PublicUtil.isEmpty(indicatorValue) || (extraIndicatorValue.compareTo(indicatorValue) > 0)) {
                    indicatorValue = extraIndicatorValue;
                }
            }

            saveTargetHitLog(userShop, param.getId(), IndicatorTypeEnum.EXAMINE, originValue, indicatorValue);

            boolean isCap = Boolean.TRUE.equals(param.getCap());
            BigDecimal proportion = param.getProportion();
            if (isCap) {
                indicatorValue = BigDecimal.ONE.compareTo(indicatorValue) > 0 ? indicatorValue : BigDecimal.ONE;
            }
            rate = rate.add(proportion.multiply(indicatorValue));
        }
        return rate;
    }

    /**
     * 计算目标
     */
    public BigDecimal calcTarget(TargetCalcTypeEnum targetCalcType, BigDecimal indicatorValue, BigDecimal targetValue) {
        if (PublicUtil.isEmpty(targetValue) || BigDecimal.ZERO.compareTo(targetValue) == 0) {
            return BigDecimal.ZERO;
        }
       if (TargetCalcTypeEnum.MINIMUM.equals(targetCalcType)) {
            BigDecimal difference = BigDecimal.ONE.subtract(targetValue);
            if (BigDecimal.ZERO.compareTo(difference) == 0) {
                difference = BigDecimal.ONE;
            }
            BigDecimal userDiffValue = indicatorValue.subtract(targetValue);
            if (userDiffValue.compareTo(BigDecimal.ZERO) <= 0) {
                return BigDecimal.ZERO;
            } else {
                return userDiffValue.divide(difference, 4, RoundingMode.HALF_UP);
            }
       }

        return  indicatorValue.divide(targetValue, 4, RoundingMode.HALF_UP);
    }

    /**
     * 查询指标值
     *
     * @return
     */
    protected Optional<BigDecimal> queryValue(EvalGroupUserShop obj, IndicatorCodeTypeEnum codeType, String indicatorCode) {
        DimensionTypeEnum dimensionType = EvalScopeEnum.STAFF.equals(obj.getScopeType()) ? DimensionTypeEnum.STAFF : DimensionTypeEnum.SHOP;
        if (IndicatorCodeTypeEnum.INDICATOR.equals(codeType)) {
            LambdaQueryWrapper<IndicatorUserStageValue> queryWrapper = Wrappers.<IndicatorUserStageValue>lambdaQuery()
                    .eq(IndicatorUserStageValue::getIndicatorCode, indicatorCode)
                    .eq(IndicatorUserStageValue::getBeginDate, obj.getBeginDate())
                    .eq(IndicatorUserStageValue::getEndDate, obj.getDataDate())
                    .eq(IndicatorUserStageValue::getDimensionType, dimensionType)
                    .eq(IndicatorUserStageValue::getYn, Boolean.TRUE)
                    .eq(IndicatorUserStageValue::getGroupId, obj.getGroupId());
            if (EvalScopeEnum.STAFF.equals(obj.getScopeType())) {
                queryWrapper.eq(IndicatorUserStageValue::getUserId, obj.getReferId());
            } else {
                queryWrapper.eq(IndicatorUserStageValue::getShopId, obj.getReferId());
            }
            IndicatorUserStageValue indicatorUserStageValue = indicatorUserStageValueService.getOne(queryWrapper, Boolean.FALSE);
            return queryKpiIndicatorValue(indicatorUserStageValue);
        }

        EvalIndicatorValue evalIndicatorValue = evalIndicatorValueService.queryLastValue(obj.getReferId(),
                dimensionType, indicatorCode, obj.getDataDate());
        return Optional.ofNullable(evalIndicatorValue).map(EvalIndicatorValue::getIndicatorValue);
    }

    /**
     * 查询前置条件
     *
     * @param evalGroupIndicatorId
     * @return
     */
    protected List<EvalGroupIndicatorPrecondition> queryPrecondition(Long evalGroupIndicatorId) {
        List<EvalGroupIndicatorPrecondition> preList = evalGroupIndicatorPreconditionService.list(Wrappers.<EvalGroupIndicatorPrecondition>lambdaQuery()
                .eq(EvalGroupIndicatorPrecondition::getEvalGroupIndicatorId, evalGroupIndicatorId)
                .eq(EvalGroupIndicatorPrecondition::getYn, Boolean.TRUE)
                .orderByAsc(EvalGroupIndicatorPrecondition::getSort)
        );
        return Optional.ofNullable(preList).orElse(new ArrayList<>());
    }

    /**
     * 查询指标配置
     *
     * @param evalGroupIndicatorId
     * @return
     */
    protected List<EvalGroupIndicatorParam> queryParam(Long evalGroupIndicatorId) {
        List<EvalGroupIndicatorParam> list = evalGroupIndicatorParamService.list(Wrappers.<EvalGroupIndicatorParam>lambdaQuery()
                .eq(EvalGroupIndicatorParam::getEvalGroupIndicatorId, evalGroupIndicatorId)
                .eq(EvalGroupIndicatorParam::getYn, Boolean.TRUE)
        );
        return Optional.ofNullable(list).orElse(new ArrayList<>());
    }

    /**
     * 查询具体指标值
     *
     * @param indicatorUserStageValue
     * @return
     */
    protected Optional<BigDecimal> queryKpiIndicatorValue(IndicatorUserStageValue indicatorUserStageValue) {
        if (PublicUtil.isEmpty(indicatorUserStageValue) || PublicUtil.isEmpty(indicatorUserStageValue.getIndicatorValue())) {
            return Optional.empty();
        }
        BigDecimal value = BigDecimal.ZERO;
        try {
            JSONObject jsonObject = JSONObject.parseObject(indicatorUserStageValue.getIndicatorValue());
            value = Optional.ofNullable(jsonObject.getBigDecimal(indicatorUserStageValue.getIndicatorCode())).orElse(BigDecimal.ZERO);
        } catch (Exception e) {
            log.error("[{}]指标值转化失败", indicatorUserStageValue, e);
        }
        return Optional.of(value);
    }

    /**
     * 查询具体指标值
     *
     * @param indicatorCode
     * @param originalValue
     * @return
     */
    protected BigDecimal queryIndicatorValue(String indicatorCode, String originalValue) {
        BigDecimal value = BigDecimal.ZERO;
        try {
            JSONObject jsonObject = JSONObject.parseObject(originalValue);
            value = Optional.ofNullable(jsonObject.getBigDecimal(indicatorCode)).orElse(BigDecimal.ZERO);
        } catch (Exception e) {
            log.error("[{}]指标值转化失败", indicatorCode, e);
        }
        return value;
    }

    protected void savePreconditionHitLog(EvalGroupUserShop userShop, Long preconditionId, Consumer<EvalGroupIndicatorPreconditionHitLog> consumer) {
        Long poolId = userShop.getPoolId();
        EvalScopeEnum evalScope = userShop.getScopeType();

        preconditionHitLogService.remove(Wrappers.<EvalGroupIndicatorPreconditionHitLog>lambdaQuery()
                .eq(EvalGroupIndicatorPreconditionHitLog::getPoolId, poolId)
                .eq(EvalGroupIndicatorPreconditionHitLog::getPreconditionId, preconditionId)
                .eq(EvalGroupIndicatorPreconditionHitLog::getDataDate, userShop.getDataDate())
                .eq(EvalGroupIndicatorPreconditionHitLog::getGroupId, userShop.getGroupId())
        );
        EvalGroupIndicatorPreconditionHitLog hitLog = new EvalGroupIndicatorPreconditionHitLog();
        hitLog.setPreconditionId(preconditionId);
        hitLog.setPoolId(poolId);
        hitLog.setScopeType(evalScope);
        hitLog.setDataDate(userShop.getDataDate());
        hitLog.setGroupId(userShop.getGroupId());
        if (consumer != null) {
            consumer.accept(hitLog);
        }
        preconditionHitLogService.save(hitLog);
    }

    protected void saveTargetHitLog(EvalGroupUserShop userShop, Long referId, IndicatorTypeEnum targetType, BigDecimal value, BigDecimal reachValue) {
        evalGroupIndicatorTargetHitLogService.remove(Wrappers.<EvalGroupIndicatorTargetHitLog>lambdaQuery()
                .eq(EvalGroupIndicatorTargetHitLog::getPoolId, userShop.getPoolId())
                .eq(EvalGroupIndicatorTargetHitLog::getScopeType, userShop.getScopeType())
                .eq(EvalGroupIndicatorTargetHitLog::getTargetType, targetType)
                .eq(EvalGroupIndicatorTargetHitLog::getDataDate, userShop.getDataDate())
                .eq(EvalGroupIndicatorTargetHitLog::getReferId, referId)
        );
        EvalGroupIndicatorTargetHitLog log = new EvalGroupIndicatorTargetHitLog();
        log.setPoolId(userShop.getPoolId());
        log.setScopeType(userShop.getScopeType());
        log.setReferId(referId);
        log.setTargetType(targetType);
        log.setValue(value);
        log.setReachValue(reachValue);
        log.setDataDate(userShop.getDataDate());
        log.setGroupId(userShop.getGroupId());
        log.setYn(Boolean.TRUE);
        evalGroupIndicatorTargetHitLogService.save(log);
    }

    /**
     * 储存命中记录
     *
     * @param evalGroupIndicatorId
     * @param userShop
     */
    protected void saveParamHitLog(Long evalGroupIndicatorId, EvalGroupUserShop userShop,
                                   Consumer<EvalGroupIndicatorHitLog> consumer) {
        EvalGroupIndicatorHitLog hitLog = new EvalGroupIndicatorHitLog();
        hitLog.setEvalGroupIndicatorId(evalGroupIndicatorId);
        hitLog.setPoolId(userShop.getPoolId());
        hitLog.setScopeType(userShop.getScopeType());
        hitLog.setDataDate(userShop.getDataDate());
        hitLog.setGroupId(userShop.getGroupId());
        hitLog.setYn(Boolean.TRUE);
        if (consumer != null) {
            consumer.accept(hitLog);
        }
        evalGroupIndicatorHitLogService.saveUnique(hitLog);
    }

}