|
|
@@ -0,0 +1,397 @@
|
|
|
+package nckd.jxccl.hr.psms.helper;
|
|
|
+
|
|
|
+import kd.bos.algo.Algo;
|
|
|
+import kd.bos.algo.AlgoContext;
|
|
|
+import kd.bos.algo.DataSet;
|
|
|
+import kd.bos.algo.Row;
|
|
|
+import kd.bos.dataentity.entity.DynamicObjectCollection;
|
|
|
+import kd.bos.orm.query.QCP;
|
|
|
+import kd.bos.orm.query.QFilter;
|
|
|
+import kd.bos.servicehelper.QueryServiceHelper;
|
|
|
+import nckd.jxccl.base.common.constant.FormConstant;
|
|
|
+import nckd.jxccl.base.common.utils.DateUtil;
|
|
|
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
|
|
|
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
|
|
|
+import nckd.jxccl.hr.psms.common.ContributionEvaluationConstant;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 年度贡献积分辅助类
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date 2025/10/26 13:34
|
|
|
+ * @version 1.0
|
|
|
+ */
|
|
|
+public class ContributionHelper {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据人员获取积分项目有效分数
|
|
|
+ * 依据人员某年度已获得的积分(含在途单据)+积分配置的最高分计算出可获得的分数
|
|
|
+ *
|
|
|
+ * @param scoreItemValidScoreList 待计算列表
|
|
|
+ * @param year 年份
|
|
|
+ * @param excludeBillId 排除的单据ID(计算有效分数不含当前单据),可为空
|
|
|
+ * @return: ScoreItemValidScore
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/26 13:38
|
|
|
+ */
|
|
|
+ public static List<ScoreItemValidScore> getScoreItemValidScore(List<ScoreItemValidScore> scoreItemValidScoreList, Date year, Long excludeBillId) {
|
|
|
+ if (scoreItemValidScoreList == null || scoreItemValidScoreList.isEmpty()) {
|
|
|
+ return scoreItemValidScoreList;
|
|
|
+ }
|
|
|
+
|
|
|
+ //获取积分项目分数配置
|
|
|
+ Map<Long, BigDecimal> scoreConfMap = getScoreConf();
|
|
|
+ //统计人员各项目已累计积分
|
|
|
+ Long[] personIdArray = scoreItemValidScoreList.stream()
|
|
|
+ .map(ScoreItemValidScore::getPersonId)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .toArray(Long[]::new);
|
|
|
+
|
|
|
+ List<ScoreItemValidScore> scoreItemAccumulateScoreList = getScoreItemAccumulateScore(personIdArray, year, excludeBillId);
|
|
|
+
|
|
|
+ //按人员分组已累计积分数据,提高查找效率
|
|
|
+ Map<Long, List<ScoreItemValidScore>> personAccumulateScoreMap = scoreItemAccumulateScoreList.stream()
|
|
|
+ .collect(Collectors.groupingBy(ScoreItemValidScore::getPersonId));
|
|
|
+
|
|
|
+ for (ScoreItemValidScore scoreItemValidScore : scoreItemValidScoreList) {
|
|
|
+ if (scoreItemValidScore == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Long personId = scoreItemValidScore.getPersonId();
|
|
|
+ Long scoreItemId = scoreItemValidScore.getScoreItemId();
|
|
|
+ Long scoreItemSubId = scoreItemValidScore.getScoreItemSubId();
|
|
|
+
|
|
|
+ //获取该人员的累计积分数据
|
|
|
+ List<ScoreItemValidScore> personAccumulateScores = personAccumulateScoreMap.getOrDefault(personId, new ArrayList<>());
|
|
|
+
|
|
|
+ //计算积分项目和具体项目累计分数
|
|
|
+ BigDecimal scoreItemSumScore = BigDecimal.ZERO;
|
|
|
+ BigDecimal scoreItemSubSumScore = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ for (ScoreItemValidScore accumulateScore : personAccumulateScores) {
|
|
|
+ Long tempScoreItemId = accumulateScore.getScoreItemId();
|
|
|
+ Long tempScoreItemSubId = accumulateScore.getScoreItemSubId();
|
|
|
+
|
|
|
+ if (Objects.equals(tempScoreItemId, scoreItemId)) {
|
|
|
+ scoreItemSumScore = scoreItemSumScore.add(accumulateScore.getSumScore());
|
|
|
+ if (Objects.equals(tempScoreItemSubId, scoreItemSubId)) {
|
|
|
+ scoreItemSubSumScore = accumulateScore.getSumScore();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //根据配置决定使用哪种累计分数
|
|
|
+ BigDecimal sumScore = determineSumScore(scoreConfMap, scoreItemId, scoreItemSumScore, scoreItemSubSumScore);
|
|
|
+
|
|
|
+ BigDecimal validScore = calculateAvailableScore(scoreConfMap, scoreItemId, scoreItemSubId, sumScore);
|
|
|
+ scoreItemValidScore.setValidScore(validScore);
|
|
|
+ scoreItemValidScore.setSumScore(sumScore);
|
|
|
+ }
|
|
|
+ return scoreItemValidScoreList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据人员、年度、积分项目、具体积分项目获取有效分数
|
|
|
+ * 依据人员某年度已获得的积分(含在途单据)+积分配置的最高分计算出可获得的分数
|
|
|
+ *
|
|
|
+ * @param scoreItemValidScore 待计算积分信息
|
|
|
+ * @param year 年份
|
|
|
+ * @param excludeBillId 排除的单据ID(计算有效分数不含当前单据),可为空
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/26 13:38
|
|
|
+ */
|
|
|
+ public static void getScoreItemValidScore(ScoreItemValidScore scoreItemValidScore, Date year, Long excludeBillId) {
|
|
|
+ if (scoreItemValidScore == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //复用批量处理方法
|
|
|
+ List<ScoreItemValidScore> inputList = new ArrayList<>();
|
|
|
+ inputList.add(scoreItemValidScore);
|
|
|
+ List<ScoreItemValidScore> result = getScoreItemValidScore(inputList, year, excludeBillId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据人员和年度获取积分项目已累计分数
|
|
|
+ * @param personIds 人员
|
|
|
+ * @param year 年度
|
|
|
+ * @param excludeBillId 排除的单据ID(计算有效分数不含当前单据),可为空
|
|
|
+ * @return: List<ScoreItemValidScore> 积分项目已累计分数
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/26 16:20
|
|
|
+ */
|
|
|
+ public static List<ScoreItemValidScore> getScoreItemAccumulateScore(Long[] personIds, Date year, Long excludeBillId) {
|
|
|
+ if (personIds == null || personIds.length == 0) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDateTime yearStarDateTime = DateUtil.beginOfYear(DateUtil.toLocalDateTime(year));
|
|
|
+ QueryFieldBuilder scoreItemConfFieldBuilder = QueryFieldBuilder.create()
|
|
|
+ .add(ContributionEvaluationConstant.NCKD_YEAR)
|
|
|
+ .addIdNumberName(ContributionEvaluationConstant.NCKD_SCOREITEM)
|
|
|
+ .addIdNumberName(ContributionEvaluationConstant.NCKD_SCOREITEMSUB)
|
|
|
+ .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY,ContributionEvaluationConstant.NCKD_PERSON},FormConstant.ID_KEY)
|
|
|
+ .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, ContributionEvaluationConstant.NCKD_SCORE,ContributionEvaluationConstant.NCKD_ORISCORE);
|
|
|
+
|
|
|
+ //查询非"审批不通过"、"已废弃"年度贡献积分
|
|
|
+ QFilter filter = new QFilter(FormConstant.BILL_STATUS_KEY, QCP.not_in, new String[]{"E", "F"})
|
|
|
+ .and(ContributionEvaluationConstant.NCKD_YEAR,QCP.large_equals,DateUtil.beginOfDay(yearStarDateTime))
|
|
|
+ .and(ContributionEvaluationConstant.NCKD_YEAR,QCP.less_equals, DateUtil.endOfDay(yearStarDateTime))
|
|
|
+ .and(String.join( ".",FormConstant.NCKD_ENTRYENTITY,ContributionEvaluationConstant.NCKD_PERSON),QCP.in,personIds);
|
|
|
+
|
|
|
+ if(excludeBillId != null && excludeBillId > 0){
|
|
|
+ filter.and(FormConstant.ID_KEY,QCP.not_equals,excludeBillId);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ScoreItemValidScore> scoreItemValidScoreList = new ArrayList<>();
|
|
|
+ try(AlgoContext context = Algo.newContext()) {
|
|
|
+ DataSet dateSet = QueryServiceHelper.queryDataSet(ContributionHelper.class.getName(),
|
|
|
+ ContributionEvaluationConstant.CONTRIBBILL_ENTITYID,
|
|
|
+ scoreItemConfFieldBuilder.buildSelect(),
|
|
|
+ new QFilter[]{filter}, null, 5000);
|
|
|
+
|
|
|
+ DataSet sumDateSet = dateSet.copy()
|
|
|
+ // 按人员、积分项目、积分项目子项进行分组
|
|
|
+ .groupBy(new String[]{String.join(".", ContributionEvaluationConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_PERSON, FormConstant.ID_KEY),
|
|
|
+ String.join(".", ContributionEvaluationConstant.NCKD_SCOREITEM, FormConstant.ID_KEY),
|
|
|
+ String.join(".", ContributionEvaluationConstant.NCKD_SCOREITEMSUB, FormConstant.ID_KEY)})
|
|
|
+ .sum(String.join(".", ContributionEvaluationConstant.NCKD_ENTRYENTITY, FormConstant.NCKD_SCORE),"sumScore").finish();
|
|
|
+
|
|
|
+ while (sumDateSet.hasNext()) {
|
|
|
+ Row row = sumDateSet.next();
|
|
|
+ Long personId = row.getLong(String.join(".", ContributionEvaluationConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_PERSON, FormConstant.ID_KEY));
|
|
|
+ Long scoreItemId = row.getLong(String.join(".", ContributionEvaluationConstant.NCKD_SCOREITEM, FormConstant.ID_KEY));
|
|
|
+ Long scoreItemSubId = row.getLong(String.join(".", ContributionEvaluationConstant.NCKD_SCOREITEMSUB, FormConstant.ID_KEY));
|
|
|
+ //已获得的累计积分
|
|
|
+ BigDecimal sumScore = row.getBigDecimal("sumScore");
|
|
|
+ ScoreItemValidScore scoreItemValidScore = new ScoreItemValidScore(personId, scoreItemId, scoreItemSubId, sumScore);
|
|
|
+ scoreItemValidScoreList.add(scoreItemValidScore);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return scoreItemValidScoreList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算可获得的有效分数
|
|
|
+ * 基于累计总分和限制分数,计算还可以获得多少分数(不超过限制)
|
|
|
+ *
|
|
|
+ * @param scoreConfMap 分数配置映射表,key为配置ID,value为限分数值
|
|
|
+ * @param scoreItemId 积分项目ID
|
|
|
+ * @param scoreItemSubId 具体积分项目ID
|
|
|
+ * @param accumulatedScore 累计总分
|
|
|
+ * @return 可获得的有效分数
|
|
|
+ * 如果没有任何限制返回null
|
|
|
+ * 如果已超过限制返回BigDecimal.ZERO
|
|
|
+ * 否则返回剩余可加分数
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/26 16:53
|
|
|
+ */
|
|
|
+ private static BigDecimal calculateAvailableScore(Map<Long, BigDecimal> scoreConfMap, Long scoreItemId, Long scoreItemSubId, BigDecimal accumulatedScore) {
|
|
|
+ // 积分项目限分 - 如果没有找到则不限分
|
|
|
+ BigDecimal scoreItemMaxScore = scoreConfMap.get(scoreItemId);
|
|
|
+
|
|
|
+ // 具体积分项目限分 - 如果没有找到则不限分
|
|
|
+ BigDecimal scoreItemSubMaxScore = null;
|
|
|
+ if (scoreItemSubId != null && scoreItemSubId > 0) {
|
|
|
+ scoreItemSubMaxScore = scoreConfMap.get(scoreItemSubId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理累计分数的空值
|
|
|
+ BigDecimal actualAccumulatedScore = accumulatedScore == null ? BigDecimal.ZERO : accumulatedScore;
|
|
|
+
|
|
|
+ // 计算实际的最大限制
|
|
|
+ BigDecimal actualMaxScore = null;
|
|
|
+
|
|
|
+ // 检查具体积分项目限分是否存在且大于0
|
|
|
+ if (scoreItemSubMaxScore != null && scoreItemSubMaxScore.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ actualMaxScore = scoreItemSubMaxScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查积分项目限分是否存在且大于0
|
|
|
+ if (scoreItemMaxScore != null && scoreItemMaxScore.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ if (actualMaxScore == null) {
|
|
|
+ actualMaxScore = scoreItemMaxScore;
|
|
|
+ } else {
|
|
|
+ // 双重限制,取较小值
|
|
|
+ actualMaxScore = actualMaxScore.min(scoreItemMaxScore);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有限制,返回null
|
|
|
+ if (actualMaxScore == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算可获得的有效分数
|
|
|
+ BigDecimal availableScore;
|
|
|
+ if (actualAccumulatedScore.compareTo(actualMaxScore) >= 0) {
|
|
|
+ // 已经超过限制,不能再获得分数
|
|
|
+ availableScore = BigDecimal.ZERO;
|
|
|
+ } else {
|
|
|
+ // 计算剩余可加分数
|
|
|
+ availableScore = actualMaxScore.subtract(actualAccumulatedScore);
|
|
|
+ }
|
|
|
+
|
|
|
+ return availableScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 决定使用哪种累计分数
|
|
|
+ * @param scoreConfMap 分数配置映射
|
|
|
+ * @param scoreItemId 积分项目ID
|
|
|
+ * @param scoreItemSumScore 积分项目累计分数
|
|
|
+ * @param scoreItemSubSumScore 积分项目具体项目累计分数
|
|
|
+ * @return 应该使用的累计分数
|
|
|
+ */
|
|
|
+ private static BigDecimal determineSumScore(Map<Long, BigDecimal> scoreConfMap, Long scoreItemId,
|
|
|
+ BigDecimal scoreItemSumScore, BigDecimal scoreItemSubSumScore) {
|
|
|
+ BigDecimal scoreItemMaxScore = scoreConfMap.get(scoreItemId);
|
|
|
+ if (scoreItemMaxScore != null) {
|
|
|
+ //如果设置积分项目大类分数限制,则取大类的累计积分
|
|
|
+ return scoreItemSumScore;
|
|
|
+ } else {
|
|
|
+ //如果没有设置积分项目大类分数限制,则取小类的累计积分
|
|
|
+ return scoreItemSubSumScore;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取积分配置
|
|
|
+ * @return: java.util.Map<java.lang.Long, java.math.BigDecimal>;key为积分项目子项ID(项目子项为空时key为项目大项),value为积分项目对应的最大分数
|
|
|
+ * @author W.Y.C
|
|
|
+ * @date: 2025/10/26 13:38
|
|
|
+ */
|
|
|
+ public static Map<Long, BigDecimal> getScoreConf() {
|
|
|
+ //查询积分项目分数配置
|
|
|
+ QueryFieldBuilder scoreItemConfFieldBuilder = QueryFieldBuilder.create()
|
|
|
+ .addIdNumberName(FormConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_SCOREITEM)
|
|
|
+ .addIdNumberName(FormConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_SCOREITEMSUB)
|
|
|
+ .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, ContributionEvaluationConstant.NCKD_MAXSCORE);
|
|
|
+
|
|
|
+ DynamicObjectCollection scoreItemConfQuery = QueryServiceHelper.query(ContributionEvaluationConstant.SCOREITEMCONF_ENTITYID,
|
|
|
+ scoreItemConfFieldBuilder.buildSelect(),
|
|
|
+ new QFilter[]{QFilterCommonHelper.getEnableFilter()});
|
|
|
+
|
|
|
+ //scoreMap:key为积分项目子项ID(项目子项为空时key为项目大项),value为积分项目对应的最大分数
|
|
|
+ Map<Long, BigDecimal> scoreMap = scoreItemConfQuery.stream().collect(Collectors.toMap(
|
|
|
+ item -> {
|
|
|
+ long scoreItemSubId = item.getLong(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_SCOREITEMSUB, FormConstant.ID_KEY));
|
|
|
+ if (scoreItemSubId <= 0) {
|
|
|
+ return item.getLong(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_SCOREITEM, FormConstant.ID_KEY));
|
|
|
+ } else {
|
|
|
+ return scoreItemSubId;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ item -> item.getBigDecimal(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionEvaluationConstant.NCKD_MAXSCORE)),
|
|
|
+ //处理重复key的情况,取最大值
|
|
|
+ (existing, replacement) -> existing.max(replacement)
|
|
|
+ ));
|
|
|
+ return scoreMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class ScoreItemValidScore{
|
|
|
+ private Long personId;
|
|
|
+ private Long scoreItemId;
|
|
|
+ private Long scoreItemSubId;
|
|
|
+ /**本次积分*/
|
|
|
+ private BigDecimal currentScore;
|
|
|
+ private BigDecimal sumScore;
|
|
|
+ /** 可获得的有效分数(如果没有任何限制返回null,如果已超过限制返回0,否则返回剩余可加分数) */
|
|
|
+ private BigDecimal validScore;
|
|
|
+ //没有业务含义,只用来记录分录索引号;
|
|
|
+ private int rowIndex;
|
|
|
+
|
|
|
+ public ScoreItemValidScore(Long personId, Long scoreItemId, Long scoreItemSubId) {
|
|
|
+ this.personId = personId;
|
|
|
+ this.scoreItemId = scoreItemId;
|
|
|
+ this.scoreItemSubId = scoreItemSubId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ScoreItemValidScore(Long personId, Long scoreItemId, Long scoreItemSubId, BigDecimal sumScore) {
|
|
|
+ this.personId = personId;
|
|
|
+ this.scoreItemId = scoreItemId;
|
|
|
+ this.scoreItemSubId = scoreItemSubId;
|
|
|
+ this.sumScore = sumScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ScoreItemValidScore(Long personId, Long scoreItemId, Long scoreItemSubId, BigDecimal sumScore, BigDecimal validScore) {
|
|
|
+ this.personId = personId;
|
|
|
+ this.scoreItemId = scoreItemId;
|
|
|
+ this.scoreItemSubId = scoreItemSubId;
|
|
|
+ this.sumScore = sumScore;
|
|
|
+ this.validScore = validScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long getPersonId() {
|
|
|
+ return personId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setPersonId(Long personId) {
|
|
|
+ this.personId = personId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long getScoreItemId() {
|
|
|
+ return scoreItemId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setScoreItemId(Long scoreItemId) {
|
|
|
+ this.scoreItemId = scoreItemId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Long getScoreItemSubId() {
|
|
|
+ return scoreItemSubId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setScoreItemSubId(Long scoreItemSubId) {
|
|
|
+ this.scoreItemSubId = scoreItemSubId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getSumScore() {
|
|
|
+ return sumScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setSumScore(BigDecimal sumScore) {
|
|
|
+ this.sumScore = sumScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getValidScore() {
|
|
|
+ //为空时说明不限制分数
|
|
|
+ BigDecimal trulyValidScore = this.currentScore;
|
|
|
+ if(this.validScore != null){
|
|
|
+ //如果可使用分数超过录入分数则使用录入分数(oriScore),否则使用可使用分数
|
|
|
+ trulyValidScore = this.validScore.min(this.currentScore);
|
|
|
+ }
|
|
|
+ return trulyValidScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setValidScore(BigDecimal validScore) {
|
|
|
+ this.validScore = validScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getCurrentScore() {
|
|
|
+ return currentScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setCurrentScore(BigDecimal currentScore) {
|
|
|
+ this.currentScore = currentScore;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getRowIndex() {
|
|
|
+ return rowIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setRowIndex(int rowIndex) {
|
|
|
+ this.rowIndex = rowIndex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|