package cn.fw.morax.server.task; import cn.fw.common.cache.locker.DistributedLocker; import cn.fw.morax.common.constant.Constant; import cn.fw.morax.common.constant.TimeTaskConstant; import cn.fw.morax.common.utils.PublicUtil; 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.data.eval.*; import cn.fw.morax.service.data.kpi.IndicatorUserValueService; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.Maps; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.time.YearMonth; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.function.Function; import java.util.stream.Collectors; /** * @author : jiangchao * @className : KpiReportRatioTask * @description : 绩效报表定时器 * @date : 2022-04-07 15:29 */ @Component @Slf4j @RequiredArgsConstructor @ConditionalOnProperty(prefix = "task", name = "switch", havingValue = "on") public class EvalUserReportTask { private final EvalGroupIndicatorTargetHitLogService evalGroupIndicatorTargetHitLogService; private final EvalGroupIndicatorHitLogService evalGroupIndicatorHitLogService; private final EvalGroupIndicatorParamService evalGroupIndicatorParamService; private final EvalUserRankCodeReportService evalUserRankCodeReportService; private final EvalUserValueReportService evalUserValueReportService; private final EvalGroupIndicatorService evalGroupIndicatorService; private final IndicatorUserValueService indicatorUserValueService; private final EvalIndicatorValueService evalIndicatorValueService; private final PlatformTransactionManager platformTransactionManager; private final EvalUserRankReportService evalUserRankReportService; private final EvalUserReportService evalUserReportService; private final TransactionDefinition transactionDefinition; private final EvalUserPoolService evalUserPoolService; private final EvalGroupRankService evalGroupRankService; private final EvalGroupService evalGroupService; private final DistributedLocker distributedLocker; private final CommonService commonService; @Value("${spring.cache.custom.global-prefix}:eval:user:report") @Getter private String evalUserReportDistKey; /** * 考评报表定时任务 * */ @Scheduled(cron = TimeTaskConstant.EVAL_REPORT) @Transactional(rollbackFor = Exception.class) public void evalUserReportTask() { // this.evalUserReport(LocalDate.now().minusDays(1)); } /** * 绩效报表定时任务 * 1. 遍历考评排名组 * 2. 创建考评排名组报表 * 3. 创建考评排名组编码 * 4. 统计数据 * * @param date */ public void evalUserReport(LocalDate date) { Lock lock = distributedLocker.lock(getEvalUserReportDistKey()); if (! ((RLock) lock).isLocked()) { return; } TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition); try { log.info("定时任务【考评人员报表数据抽取】开始执行"); Map> groupEvalGroupMap = queryEffectRank(date); for (Map.Entry> entry : groupEvalGroupMap.entrySet()) { for (EvalGroupRank rank : entry.getValue()) { //清除当月数据(当月只有一条数据) cleanRankCurMonthData(rank.getId(), date); //抽取排名组数据 createRankReport(rank, entry.getKey(), date); } } platformTransactionManager.commit(transactionStatus); } catch (Exception e){ platformTransactionManager.rollback(transactionStatus); log.error("考评人员报表数据抽取失败, " + e.getMessage(), e); } finally { lock.unlock(); } } /** * 计算排名组报表数据 * @param rank * @param groupId * @param date */ public void createRankReport(EvalGroupRank rank, Long groupId, LocalDate date) { List evalGroups = evalGroupService.getEffectsByRankId(rank.getId(), date); List evalGroupIds = evalGroups.stream().map(EvalGroup::getId).collect(Collectors.toList()); if (PublicUtil.isEmpty(evalGroupIds)) { return; } List pools = evalUserPoolService.list(Wrappers.lambdaQuery() .in(EvalUserPool::getEvalGroupId, evalGroupIds) .eq(EvalUserPool::getMonthly, YearMonth.from(date)) .eq(EvalUserPool::getYn, Boolean.TRUE) ); if (PublicUtil.isEmpty(pools)) { return; } List evalGroupIndicators = evalGroupIndicatorService.list(Wrappers.lambdaQuery() .in(EvalGroupIndicator::getEvalGroupId, evalGroupIds) .eq(EvalGroupIndicator::getYn, Boolean.TRUE) .orderByAsc(EvalGroupIndicator::getId) ); //排名组 EvalUserRankReport reportRank = this.saveRankReport(rank, evalGroupIds, evalGroups, date, groupId); final Long evalUserRankReportId = reportRank.getId(); //排名组指标编码、人员、门店 List rankCodes = this.saveRankCodeReports(evalGroupIndicators, evalUserRankReportId, date); List evalUserReports = this.saveStaffReports(pools, date, evalUserRankReportId); List evalShopReports = this.saveShopReports(pools, date, evalUserRankReportId); //人员维度 List reportUserValues = calcUser(pools, evalGroupIndicators, evalUserReports, date); //门店维度 calcShop(evalShopReports, rankCodes, reportUserValues, evalUserReports, date); } public List calcUser(List pools, List evalGroupIndicators, List evalUserReports, LocalDate date) { List reportUserValues = new ArrayList<>(); List userIds = pools.stream().map(EvalUserPool::getUserId).distinct().collect(Collectors.toList()); List poolIds = pools.stream().map(EvalUserPool::getId).collect(Collectors.toList()); List evalGroupIndicatorIds = evalGroupIndicators.stream().map(EvalGroupIndicator::getId).collect(Collectors.toList()); List hitLogs = evalGroupIndicatorHitLogService.getHitLogs(evalGroupIndicatorIds, poolIds, EvalScopeEnum.STAFF, date); Map> indicatorHitLogMap = hitLogs.stream().collect(Collectors.groupingBy(EvalGroupIndicatorHitLog::getEvalGroupIndicatorId)); Map userPoolReportIdMap = evalUserReports.stream().collect(Collectors.toMap(EvalUserReport::getEvalUserPoolId, EvalUserReport::getId, (v1, v2) -> v1)); for (EvalGroupIndicator groupIndicator : evalGroupIndicators) { final ScoreWayEnum scoreWay = groupIndicator.getScoreWay(); final IndicatorCodeTypeEnum codeType = groupIndicator.getCodeType(); final Long evalGroupIndicatorId = groupIndicator.getId(); List params = evalGroupIndicatorParamService.list(Wrappers.lambdaQuery() .eq(EvalGroupIndicatorParam::getEvalGroupIndicatorId, evalGroupIndicatorId) .eq(EvalGroupIndicatorParam::getYn, Boolean.TRUE) ); List userHitLogs = indicatorHitLogMap.getOrDefault(evalGroupIndicatorId, new ArrayList<>()); Map poolHitLogMap = userHitLogs.stream() .collect(Collectors.toMap(EvalGroupIndicatorHitLog::getPoolId, Function.identity(), (v1, v2) -> v1)); for (EvalUserPool userPool : pools) { if (! userPool.getEvalGroupId().equals(groupIndicator.getEvalGroupId())) { continue; } Optional hitLog = Optional.ofNullable(poolHitLogMap.get(userPool.getId())); BigDecimal value = (ScoreWayEnum.LADDER.equals(scoreWay)) ? hitLog.map(EvalGroupIndicatorHitLog::getHitLadderValue).orElse(BigDecimal.ZERO) : hitLog.map(EvalGroupIndicatorHitLog::getHitCommissionValue).orElse(BigDecimal.ZERO); Long evalUserReportId = userPoolReportIdMap.get(userPool.getId()); reportUserValues.add(convertDB(evalUserReportId, groupIndicator, value, date, params)); } //是否有子指标 if (IndicatorCodeTypeEnum.COMBINE_INDICATOR.equals(codeType)) { for (EvalGroupIndicatorParam param : params) { if (TargetTypeEnum.NO.equals(param.getTargetType())) { Map userValueMap = queryUserOriginValue(param, userIds, date); for (EvalUserPool userPool : pools) { BigDecimal value = userValueMap.getOrDefault(userPool.getUserId(), BigDecimal.ZERO); Long evalUserReportId = userPoolReportIdMap.get(userPool.getId()); reportUserValues.add(convertParamDB(evalUserReportId, param, value, date)); } } else { Map hitLogMap = queryUserTargetValue(param.getId(), poolIds, date); for (EvalUserPool userPool : pools) { EvalGroupIndicatorTargetHitLog hitLog = hitLogMap.get(userPool.getId()); BigDecimal value = Optional.ofNullable(hitLog) .map(EvalGroupIndicatorTargetHitLog::getReachValue).orElse(BigDecimal.ZERO).multiply(Constant.ONE_HUNDRED); Long evalUserReportId = userPoolReportIdMap.get(userPool.getId()); reportUserValues.add(convertParamDB(evalUserReportId, param, value, date)); } } } } } if (PublicUtil.isNotEmpty(reportUserValues)) { evalUserValueReportService.saveBatch(reportUserValues); } return reportUserValues; } public void calcShop(List evalShopReports, List rankCodes, List reportUserValues, List evalUserReports, LocalDate date) { List reportShopValues = new ArrayList<>(); for (EvalUserReport shopReport : evalShopReports) { Set shopReportIds = evalUserReports.stream() .filter(user -> shopReport.getShopId().equals(user.getShopId()) && Boolean.TRUE.equals(user.getInclusion())) .map(EvalUserReport::getId).collect(Collectors.toSet()); final Long evalUserReportId = shopReport.getId(); for (EvalUserRankCodeReport rankCode : rankCodes) { final String code = rankCode.getCode(); final IndicatorCodeTypeEnum codeType = rankCode.getCodeType(); Double averageRatioDouble = reportUserValues.stream() .filter(user -> shopReportIds.contains(user.getEvalUserReportId()) && code.equals(user.getCode())) .mapToDouble(userValue -> Optional.ofNullable(userValue.getValue()).orElse(BigDecimal.ZERO).doubleValue()) .average() .orElse(0); BigDecimal value = new BigDecimal(averageRatioDouble.toString()); EvalUserValueReport shopValue = new EvalUserValueReport(evalUserReportId, code, codeType, value, date); reportShopValues.add(shopValue); } } if (PublicUtil.isNotEmpty(reportShopValues)) { evalUserValueReportService.saveBatch(reportShopValues); } } public List saveStaffReports(List pools, LocalDate date, Long evalUserRankReportId) { List evalUserReports = new ArrayList<>(); for (EvalUserPool userPool : pools) { EvalUserReport report = new EvalUserReport(); report.setEvalUserRankReportId(evalUserRankReportId); report.setDimension(ReportDimensionEnum.STAFF); report.setUserId(userPool.getUserId()); report.setUserName(userPool.getUserName()); report.setPostId(userPool.getPostId()); report.setPostName(userPool.getPostName()); report.setShopId(userPool.getShopId()); report.setShopName(userPool.getShopName()); report.setEvalUserPoolId(userPool.getId()); report.setDataDate(date); report.setInclusion(userPool.getInclusion()); evalUserReports.add(report); } evalUserReportService.saveBatch(evalUserReports); return evalUserReports; } public List saveShopReports(List pools, LocalDate date, Long evalUserRankReportId) { List evalShopReports = new ArrayList<>(); Map shopMap = pools.stream().collect(Collectors.toMap(EvalUserPool::getShopId, EvalUserPool::getShopName, (v1, v2) -> v1)); for (Long shopId : shopMap.keySet()) { EvalUserReport report = new EvalUserReport(); report.setDimension(ReportDimensionEnum.SHOP); report.setEvalUserRankReportId(evalUserRankReportId); report.setShopId(shopId); report.setShopName(shopMap.get(shopId)); report.setDataDate(date); evalShopReports.add(report); } evalUserReportService.saveBatch(evalShopReports); return evalShopReports; } public Map queryUserOriginValue(EvalGroupIndicatorParam param, List userIds, LocalDate dataDate) { final String code = param.getCode(); final IndicatorCodeTypeEnum codeType = param.getCodeType(); Map referValueMap = Maps.newHashMapWithExpectedSize(userIds.size()); //无目标 if (IndicatorCodeTypeEnum.INDICATOR.equals(codeType)) { List indicatorUserValues = indicatorUserValueService.list(Wrappers.lambdaQuery() .eq(IndicatorUserValue::getIndicatorCode, code) .eq(IndicatorUserValue::getDataDate, dataDate) .in(IndicatorUserValue::getUserId, userIds) .eq(IndicatorUserValue::getDimensionType, DimensionTypeEnum.STAFF) .eq(IndicatorUserValue::getYn, Boolean.TRUE) ); for (IndicatorUserValue userValue : indicatorUserValues) { referValueMap.put(userValue.getUserId(), commonService.queryIndicatorValueVO(code, userValue, param.getTargetType(), param.getDataType())); } return referValueMap; } for (Long userId : userIds) { EvalIndicatorValue evalIndicatorValue = evalIndicatorValueService.queryLastValue(userId, DimensionTypeEnum.STAFF, code, dataDate); referValueMap.put(userId, commonService.queryEvalIndicatorValueVO(code, evalIndicatorValue, param.getTargetType(), param.getDataType())); } return referValueMap; } public Map queryUserTargetValue(Long referId, List poolIds, LocalDate dataDate) { List targetHitLogs = evalGroupIndicatorTargetHitLogService.list(Wrappers.lambdaQuery() .eq(EvalGroupIndicatorTargetHitLog::getReferId, referId) .eq(EvalGroupIndicatorTargetHitLog::getTargetType, IndicatorTypeEnum.EXAMINE) .eq(EvalGroupIndicatorTargetHitLog::getDataDate, dataDate) .in(EvalGroupIndicatorTargetHitLog::getPoolId, poolIds) .eq(EvalGroupIndicatorTargetHitLog::getScopeType, EvalScopeEnum.STAFF) .eq(EvalGroupIndicatorTargetHitLog::getYn, Boolean.TRUE) ); return targetHitLogs.stream() .collect(Collectors.toMap(EvalGroupIndicatorTargetHitLog::getPoolId, Function.identity(), (v1, v2) -> v1)); } public void cleanTodayData(LocalDate date) { evalUserRankReportService.remove(Wrappers.lambdaUpdate() .eq(EvalUserRankReport::getDataDate, date) ); evalUserRankCodeReportService.remove(Wrappers.lambdaUpdate() .eq(EvalUserRankCodeReport::getDataDate, date) ); evalUserValueReportService.remove(Wrappers.lambdaUpdate() .eq(EvalUserValueReport::getDataDate, date) ); evalUserReportService.remove(Wrappers.lambdaUpdate() .eq(EvalUserReport::getDataDate, date) ); } public void cleanRankCurMonthData(Long rankId, LocalDate endDate) { LocalDate startDate = endDate.with(TemporalAdjusters.firstDayOfMonth()); List rankReports = evalUserRankReportService.list(Wrappers.lambdaQuery() .eq(EvalUserRankReport::getRankId, rankId) .ge(EvalUserRankReport::getDataDate, startDate) .le(EvalUserRankReport::getDataDate, endDate) ); if (PublicUtil.isEmpty(rankReports)) { return; } List rankReportIds = rankReports.stream().map(EvalUserRankReport::getId).collect(Collectors.toList()); evalUserRankReportService.removeByIds(rankReportIds); evalUserRankCodeReportService.remove(Wrappers.lambdaUpdate() .in(EvalUserRankCodeReport::getEvalUserRankReportId, rankReportIds) ); List userReports = evalUserReportService.list(Wrappers.lambdaQuery() .in(EvalUserReport::getEvalUserRankReportId, rankReportIds) ); List userReportIds = userReports.stream().map(EvalUserReport::getId).collect(Collectors.toList()); if (PublicUtil.isEmpty(userReportIds)) { return; } evalUserReportService.removeByIds(userReportIds); evalUserValueReportService.remove(Wrappers.lambdaUpdate() .in(EvalUserValueReport::getEvalUserReportId, userReportIds) ); } public List saveRankCodeReports(List evalGroupIndicators, Long reportRankId, LocalDate date) { Set codes = new HashSet<>(); List rankCodes = new ArrayList<>(); AtomicInteger order = new AtomicInteger(); for (EvalGroupIndicator groupIndicator : evalGroupIndicators) { //一个编码只能展示一次 if (! codes.add(groupIndicator.getCode())) { continue; } rankCodes.add(this.convertRankCodeDB(groupIndicator, date, reportRankId, order)); //组合指标 if (PublicUtil.isNotEmpty(groupIndicator.getCodeType()) && IndicatorCodeTypeEnum.COMBINE_INDICATOR.equals(groupIndicator.getCodeType())) { //子指标 List params = evalGroupIndicatorParamService.list(Wrappers.lambdaQuery() .eq(EvalGroupIndicatorParam::getEvalGroupIndicatorId, groupIndicator.getId()) .eq(EvalGroupIndicatorParam::getYn, Boolean.TRUE) ); for (EvalGroupIndicatorParam indicatorParam : params) { rankCodes.add(this.convertParamToRankCodeDB(indicatorParam, date, reportRankId, order, groupIndicator)); } } } evalUserRankCodeReportService.saveBatch(rankCodes); return rankCodes; } public Map> queryEffectRank(LocalDate date) { List status = new ArrayList(){{ add(SettingStatusEnum.EFFECTIVE); add(SettingStatusEnum.BE_EFFECTIVE); }}; List evalGroupRanks = evalGroupRankService.list(Wrappers.lambdaQuery() .in(EvalGroupRank::getStatus, status) .ge(EvalGroupRank::getOverTime, date) .eq(EvalGroupRank::getYn, Boolean.TRUE) ); return evalGroupRanks.stream().collect(Collectors.groupingBy(EvalGroupRank::getGroupId)); } public EvalUserRankReport saveRankReport(EvalGroupRank rank, List evalGroupIds, List evalGroups, LocalDate date, Long groupId) { List shopIds = evalGroups.stream().map(EvalGroup::getShopIds).distinct().collect(ArrayList::new, List::addAll, List::addAll); EvalUserRankReport reportRank = new EvalUserRankReport(); reportRank.setRankId(rank.getId()); reportRank.setRankName(rank.getName()); reportRank.setEvalGroupIds(evalGroupIds); reportRank.setShopIds(shopIds); reportRank.setDataDate(date); reportRank.setMonthly(YearMonth.from(date)); reportRank.setGroupId(groupId); evalUserRankReportService.save(reportRank); return reportRank; } public EvalUserValueReport convertDB(Long evalUserReportId, EvalGroupIndicator groupIndicator, BigDecimal value, LocalDate date, List params) { EvalUserValueReport userValue = new EvalUserValueReport(); userValue.setEvalUserReportId(evalUserReportId); userValue.setCode(groupIndicator.getCode()); userValue.setCodeType(groupIndicator.getCodeType()); userValue.setValue(Optional.ofNullable(value).orElse(BigDecimal.ZERO)); userValue.setDataDate(date); if (IndicatorCodeTypeEnum.COMBINE_INDICATOR.equals(groupIndicator.getCodeType())) { userValue.setValue(userValue.getValue().multiply(Constant.ONE_HUNDRED)); } else { Optional paramOptional = params.stream().findFirst(); if (paramOptional.isPresent()) { EvalGroupIndicatorParam param = paramOptional.get(); if (DataTypeEnum.RATIO.equals(param.getDataType()) || (! TargetTypeEnum.NO.equals(param.getTargetType()))) { userValue.setValue(userValue.getValue().multiply(Constant.ONE_HUNDRED)); } } } return userValue; } public EvalUserValueReport convertParamDB(Long evalUserReportId, EvalGroupIndicatorParam param, BigDecimal value, LocalDate date) { EvalUserValueReport userValue = new EvalUserValueReport(); userValue.setEvalUserReportId(evalUserReportId); userValue.setCode(param.getCode()); userValue.setCodeType(param.getCodeType()); userValue.setValue(Optional.ofNullable(value).orElse(BigDecimal.ZERO)); userValue.setDataDate(date); return userValue; } public EvalUserRankCodeReport convertRankCodeDB(EvalGroupIndicator groupIndicator, LocalDate date, Long reportRankId, AtomicInteger order) { EvalUserRankCodeReport rankCode = new EvalUserRankCodeReport(); rankCode.setEvalUserRankReportId(reportRankId); rankCode.setEvalGroupIndicatorId(groupIndicator.getId()); rankCode.setCode(groupIndicator.getCode()); rankCode.setCodeType(groupIndicator.getCodeType()); // String indicatorName = (ScoreWayEnum.LADDER.equals(groupIndicator.getScoreWay())) ? // groupIndicator.getLadderParamAlias() : groupIndicator.getCommissionParamAlias(); // if (PublicUtil.isEmpty(indicatorName)) { // indicatorName = groupIndicator.getName(); // } // rankCode.setIndicatorName(indicatorName); rankCode.setIndicatorName(groupIndicator.getName()); rankCode.setDataDate(date); rankCode.setOrderNum(order.incrementAndGet()); return rankCode; } public EvalUserRankCodeReport convertParamToRankCodeDB(EvalGroupIndicatorParam indicatorParam, LocalDate date, Long reportRankId, AtomicInteger order, EvalGroupIndicator groupIndicator) { EvalUserRankCodeReport rankCode = new EvalUserRankCodeReport(); rankCode.setEvalUserRankReportId(reportRankId); rankCode.setEvalGroupIndicatorId(indicatorParam.getEvalGroupIndicatorId()); rankCode.setCode(indicatorParam.getCode()); rankCode.setCodeType(indicatorParam.getCodeType()); rankCode.setIndicatorName(indicatorParam.getName()); if (IndicatorCodeTypeEnum.COMBINE_INDICATOR.equals(groupIndicator.getCodeType()) && PublicUtil.isNotEmpty(groupIndicator.getCode())) { rankCode.setParentCode(groupIndicator.getCode()); } rankCode.setDataDate(date); rankCode.setOrderNum(order.incrementAndGet()); return rankCode; } }