浏览代码

feat(hr): 优化年度贡献积分计算逻辑

- 修改积分累计分数获取方法为sumScoreItemAccumulateScore
- 更新最大允许分数计算方法为calculateMaxAllowedScore1
- 增加科研与创新类积分规则匹配逻辑
- 技能竞赛积分默认生成三行分录
- 修复积分项目名次字段引用错误
- 添加积分限制配置检查和警告日志
- 更新年度新增贡献积分日志描述
wyc 15 小时之前
父节点
当前提交
f429c468c7

+ 5 - 4
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/AnnualAdjustmentService.java

@@ -351,9 +351,9 @@ public class AnnualAdjustmentService {
 
         //4、获取年度贡献积分
         //对应SHR:291行;utils.YearContributeScoreBillEntryScoreSumBypersonidAndYear
-        List<ContributionHelper.ScoreItemValidScore> scoreItemAccumulateScore = ContributionHelper.getApprovedScoreItemAccumulateScore(new Long[]{ac.personId}, DateUtil.toDate(lastYearDateTime));
+        List<ContributionHelper.ScoreItemValidScore> scoreItemAccumulateScore = ContributionHelper.sumScoreItemAccumulateScore(new Long[]{ac.personId}, DateUtil.toDate(lastYearDateTime));
         //计算出最多可得的分数(根据积分配置项)
-        List<ContributionHelper.ScoreItemValidScore> allowedScoreList = ContributionHelper.calculateMaxAllowedScore(scoreItemAccumulateScore);
+        List<ContributionHelper.ScoreItemValidScore> allowedScoreList = ContributionHelper.calculateMaxAllowedScore1(scoreItemAccumulateScore);
         BigDecimal allowedScoreSum = BigDecimal.ZERO;
         for (ContributionHelper.ScoreItemValidScore allowedScore : allowedScoreList) {
             String scoreItemNumber = allowedScore.getScoreItemNumber();
@@ -385,7 +385,8 @@ public class AnnualAdjustmentService {
             //比较实际累计总分和未受限制的实际累计总分
             if(sumScore.compareTo(validScore) != 0){
                 //实际累计积分与有效积分不符,说明被限高
-                ac.whyAdjust1.add(StrFormatter.format("上年度【{}】【{}】积分实际累计总分为【{}】但达到项目最高分限制,最终有效应用分为【{}】;", scoreItemEnum.getCode(), sumScore, validScore));
+                ac.whyAdjust1.add(StrFormatter.format("上年度【{}】【{}】积分实际累计总分为【{}】但达到项目最高分限制,最终有效应用分为【{}】;", scoreItemEnum.getName(), sumScore, validScore));
+                logger.warn(StrFormatter.format("上年度【{}】积分实际累计总分为【{}】但达到项目最高分限制,最终有效应用分为【{}】;", scoreItemEnum.getName(), sumScore, validScore));
             }
 
         }
@@ -594,7 +595,7 @@ public class AnnualAdjustmentService {
         logger.info("上年度贡献综合评价分: " + ac.lastYearContributeScore);
 
         ac.addYearContributeScore = ac.lastYearContributeScore.multiply(new BigDecimal("0.1"));
-        logger.info("年度新增的贡献积分: " + ac.addYearContributeScore);
+        logger.info("年度新增的贡献积分(*10%): " + ac.addYearContributeScore);
 
         BigDecimal lastsum = ac.data.getLastSumScore() == null ? BigDecimal.ZERO : ac.data.getLastSumScore();
         logger.info("上年累计积分池的分: " + lastsum);

+ 2 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/ContributionConstant.java

@@ -39,6 +39,8 @@ public class ContributionConstant extends FormConstant {
     public static final String NCKD_SCOREITEMSUB = "nckd_scoreitemsub";
     /** 积分项目名次 */
     public static final String NCKD_SCOREITEMRANK = "nckd_scoreitemrank";
+    /** 分录-积分项目名次 */
+    public static final String NCKD_SCOREITEMRANKEN = "nckd_scoreitemranken";
 
 
 

+ 131 - 3
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/helper/ContributionHelper.java

@@ -5,13 +5,17 @@ import kd.bos.algo.AlgoContext;
 import kd.bos.algo.DataSet;
 import kd.bos.algo.Row;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
 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.enums.psms.ScoreItemEnum;
 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.business.AnnualAdjustmentService;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
 
 import java.math.BigDecimal;
@@ -32,6 +36,8 @@ import java.util.stream.Collectors;
  */
 public class ContributionHelper {
 
+    protected final static Log logger = LogFactory.getLog(AnnualAdjustmentService.class);
+
     /**
      * 根据人员获取积分项目有效分数
      * 依据人员某年度已获得的积分(含在途单据)+积分配置的最高分计算出可获得的分数
@@ -186,7 +192,8 @@ public class ContributionHelper {
                             String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.ID_KEY),
                             String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.NUMBER_KEY),
                             String.join(".", ContributionConstant.NCKD_SCOREITEMSUB, FormConstant.ID_KEY),
-                            String.join(".", ContributionConstant.NCKD_SCOREITEMRANK, FormConstant.ID_KEY)}) // 添加名次分组
+                            String.join(".", ContributionConstant.NCKD_SCOREITEMRANK, FormConstant.ID_KEY)
+                    })
                     .sum(String.join(".", ContributionConstant.NCKD_ENTRYENTITY, FormConstant.NCKD_SCORE),"sumScore").finish();
 
             while (sumDateSet.hasNext()) {
@@ -205,6 +212,67 @@ public class ContributionHelper {
         return scoreItemValidScoreList;
     }
 
+
+    /**
+     * 根据人员和年度获取积分项目已累计分数
+     * @param personIds 人员
+     * @param year 年度
+     * @return: List<ScoreItemValidScore> 积分项目已累计分数
+     * @author W.Y.C
+     * @date: 2025/10/26 16:20
+     */
+    public static List<ScoreItemValidScore> sumScoreItemAccumulateScore(Long[] personIds, Date year) {
+        if (personIds == null || personIds.length == 0) {
+            return new ArrayList<>();
+        }
+
+        LocalDateTime yearStarDateTime = DateUtil.beginOfYear(DateUtil.toLocalDateTime(year));
+        QueryFieldBuilder scoreItemConfFieldBuilder = QueryFieldBuilder.create()
+                .add(ContributionConstant.NCKD_YEAR)
+                .addIdNumberName(ContributionConstant.NCKD_SCOREITEM)
+                .addIdNumberName(ContributionConstant.NCKD_SCOREITEMSUB)
+                .addIdNumberName(ContributionConstant.NCKD_SCOREITEMRANK) // 添加积分项目名次字段
+                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_PERSON},FormConstant.ID_KEY)
+                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, ContributionConstant.NCKD_SCORE, ContributionConstant.NCKD_ORISCORE);
+
+        //只查询"已审批通过"年度贡献积分
+        QFilter filter = QFilterCommonHelper.getBillStatusFilter()
+                .and(ContributionConstant.NCKD_YEAR,QCP.large_equals,DateUtil.beginOfDay(yearStarDateTime))
+                .and(ContributionConstant.NCKD_YEAR,QCP.less_equals, DateUtil.endOfDay(yearStarDateTime))
+                .and(String.join( ".",FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_PERSON),QCP.in,personIds);
+
+        List<ScoreItemValidScore> scoreItemValidScoreList = new ArrayList<>();
+        try(AlgoContext context = Algo.newContext()) {
+            DataSet dateSet = QueryServiceHelper.queryDataSet(ContributionHelper.class.getName(),
+                    ContributionConstant.CONTRIBBILL_ENTITYID,
+                    scoreItemConfFieldBuilder.buildSelect(),
+                    new QFilter[]{filter}, null, 5000);
+
+            DataSet sumDateSet = dateSet.copy()
+                    // 按人员、积分项目、积分项目子项、积分项目名次进行分组
+                    .groupBy(new String[]{String.join(".", ContributionConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_PERSON, FormConstant.ID_KEY),
+                            String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.ID_KEY),
+                            String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.NUMBER_KEY)
+                    })
+                    .sum(String.join(".", ContributionConstant.NCKD_ENTRYENTITY, FormConstant.NCKD_SCORE),"sumScore").finish();
+
+            while (sumDateSet.hasNext()) {
+                Row row = sumDateSet.next();
+                Long personId = row.getLong(String.join(".", ContributionConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                Long scoreItemId = row.getLong(String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.ID_KEY));
+                String scoreItemNumber = row.getString(String.join(".", ContributionConstant.NCKD_SCOREITEM, FormConstant.NUMBER_KEY));
+                //已获得的累计积分
+                BigDecimal sumScore = row.getBigDecimal("sumScore");
+                ScoreItemValidScore scoreItemValidScore = new ScoreItemValidScore(personId, scoreItemId,scoreItemNumber, null, null, sumScore);
+                scoreItemValidScoreList.add(scoreItemValidScore);
+            }
+        }
+        return scoreItemValidScoreList;
+    }
+
+
+
+
     /**
      * 根据人员和年度获取积分项目已累计分数(包含所有未废弃记录)
      * @param personIds 人员
@@ -439,6 +507,65 @@ public class ContributionHelper {
         return result;
     }
 
+
+    /**
+     * 计算最多可得的分数
+     * 根据累计积分和分数配置映射表,如果超过配置的限制分数则返回限制分最大分数,
+     * 计算每个积分大项在考虑三重限制条件下的最大可获得分数:
+     * 1. 名次限制:每个积分项目名次可能有分数上限(最高优先级)
+     * 2. 子项限制:每个具体积分项目子项可能有分数上限
+     * 3. 大项限制:整个积分大项可能有总分上限
+     *
+     * @param scoreItemValidScoreList 某个人员所有积分项目的累计积分(personId、scoreItemId、scoreItemSubId、scoreItemRankId、sumScore有值)
+     * @return 最多可得的分数列表(按大项维度聚合)
+     * @author W.Y.C
+     * @date: 2025/10/27
+     */
+    public static List<ScoreItemValidScore> calculateMaxAllowedScore1(List<ScoreItemValidScore> scoreItemValidScoreList) {
+
+        //获取积分限分配置
+        Map<String, BigDecimal> scoreConfMap = ContributionHelper.getScoreConf();
+        /*Map<String, BigDecimal> scoreConfMap = Maps.newConcurrentMap();
+         scoreConfMap.put("11-111-1111",new BigDecimal(2));
+         scoreConfMap.put("11-121",new BigDecimal(5));
+         scoreConfMap.put("11",new BigDecimal(20));*/
+
+        List<ScoreItemValidScore> result = new ArrayList<>();
+
+        // 按大类分组
+        Map<Long, List<ScoreItemValidScore>> groupedByScoreItemId = scoreItemValidScoreList.stream()
+                .collect(Collectors.groupingBy(ScoreItemValidScore::getScoreItemId));
+
+        for (Map.Entry<Long, List<ScoreItemValidScore>> entry : groupedByScoreItemId.entrySet()) {
+            Long scoreItemId = entry.getKey();
+            List<ScoreItemValidScore> valueList = entry.getValue();
+
+            // 获取大项限制分数
+            BigDecimal scoreItemMaxScore = scoreConfMap.get(scoreItemId + "");
+
+            BigDecimal totalAllowedScore = BigDecimal.ZERO;
+            for (ScoreItemValidScore item : valueList) {
+                BigDecimal score = item.getSumScore() == null ? BigDecimal.ZERO : item.getSumScore();
+                // 应用大项限制(如果设置了大项限制)
+                if (scoreItemMaxScore != null && scoreItemMaxScore.compareTo(BigDecimal.ZERO) >= 0) {
+                    totalAllowedScore = scoreItemMaxScore.min(score);
+                }else{
+                    //没有配置限制,则直接使用录入的分数
+                    ScoreItemEnum scoreItemEnum = ScoreItemEnum.getByCode(item.getScoreItemNumber());
+                    logger.warn("积分【{}】没有配置积分项限制,使用实际录入的分数:{}",scoreItemEnum.getName() ,score);
+                    totalAllowedScore = score;
+                }
+                // 创建结果对象,只包含scoreItemId和validScore
+                ScoreItemValidScore resultItem = new ScoreItemValidScore(item.getPersonId(), scoreItemId, null, null);
+                resultItem.setScoreItemNumber(item.getScoreItemNumber());
+                resultItem.setValidScore(totalAllowedScore);
+                resultItem.setSumScore(score);
+                result.add(resultItem);
+            }
+        }
+        return result;
+    }
+
     /**
      * 决定使用哪种累计分数
      * @param scoreConfMap 分数配置映射
@@ -619,7 +746,7 @@ public class ContributionHelper {
         }
 
         public BigDecimal getValidScore() {
-            //为空时说明不限制分数
+           /* //为空时说明不限制分数
             BigDecimal trulyValidScore = this.currentScore;
             if(this.validScore != null && this.currentScore != null){
                 //如果可使用分数超过录入分数则使用录入分数(oriScore),否则使用可使用分数
@@ -627,7 +754,8 @@ public class ContributionHelper {
             }else{
                 return BigDecimal.ZERO;
             }
-            return trulyValidScore;
+            return trulyValidScore;*/
+            return this.validScore;
         }
 
         public void setValidScore(BigDecimal validScore) {

+ 136 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/contribution/ContribBillFormPlugin.java

@@ -1,9 +1,11 @@
 package nckd.jxccl.hr.psms.plugin.form.contribution;
 
+import kd.bos.bill.OperationStatus;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.datamodel.events.ChangeData;
 import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.FormShowParameter;
 import kd.bos.form.field.BasedataEdit;
 import kd.bos.form.field.events.BeforeF7SelectEvent;
 import kd.bos.form.field.events.BeforeF7SelectListener;
@@ -11,9 +13,12 @@ import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.list.ListShowParameter;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.psms.ScoreItemEnum;
 import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
 import nckd.jxccl.hr.psms.helper.ContributionHelper;
 
@@ -22,6 +27,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.EventObject;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -33,6 +39,38 @@ import java.util.Objects;
 */
 public class ContribBillFormPlugin extends AbstractFormPlugin implements Plugin, BeforeF7SelectListener {
 
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        FormShowParameter formShowParameter = this.getView().getFormShowParameter();
+        if (formShowParameter.getStatus() == OperationStatus.ADDNEW) {
+            Object value = this.getModel().getValue(ContributionConstant.NCKD_SCOREITEM);
+            if (value != null) {
+                DynamicObject scoreItem = ConvertUtil.toDynamicObjectOrNull(value);
+                if (scoreItem != null) {
+                    String scoreItemNumber = scoreItem.getString(FormConstant.NUMBER_KEY);
+                    if (ScoreItemEnum.SKILL_CONTEST_SCORE.getCode().equalsIgnoreCase(scoreItemNumber)) {
+                        //技能竞赛积分默认三行分录
+                        DynamicObjectCollection dynamicObjectCollection = this.getModel().getDataEntity(true).getDynamicObjectCollection(ContributionConstant.NCKD_ENTRYENTITY);
+                        DynamicObjectCollection scoreItemRankColl = QueryServiceHelper.query(ContributionConstant.NCKD_SCOREITEMRANK,
+                                FormConstant.ID_KEY+","+FormConstant.NAME_KEY+","+FormConstant.NUMBER_KEY,
+                                new QFilter[]{new QFilter(ContributionConstant.NCKD_SCOREITEM, QCP.equals, scoreItem.getLong(FormConstant.ID_KEY))},
+                                ContributionConstant.INDEX_KEY
+                        );
+                        for (DynamicObject scoreItemRank : scoreItemRankColl) {
+                            DynamicObject dynamicObject = dynamicObjectCollection.addNew();
+                            DynamicObject newScoreItemRank = EntityHelper.newEntity(ContributionConstant.NCKD_SCOREITEMRANK, scoreItemRank.getLong(FormConstant.ID_KEY));
+                            newScoreItemRank.set(ContributionConstant.NAME_KEY, scoreItemRank.getString(FormConstant.NAME_KEY));
+                            newScoreItemRank.set(ContributionConstant.NUMBER_KEY, scoreItemRank.getString(FormConstant.NUMBER_KEY));
+
+                            dynamicObject.set(ContributionConstant.NCKD_SCOREITEMRANKEN,newScoreItemRank);
+                        }
+                        this.getView().updateView(ContributionConstant.NCKD_ENTRYENTITY);
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     public void registerListener(EventObject e) {
         super.registerListener(e);
@@ -118,6 +156,49 @@ public class ContribBillFormPlugin extends AbstractFormPlugin implements Plugin,
         DynamicObject scoreItemRank = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMRANK));
         DynamicObject person = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_PERSON, rowIndex));
         BigDecimal oriScore = ConvertUtil.toBigDecimal(this.getModel().getValue(ContributionConstant.NCKD_ORISCORE, rowIndex));
+        if (date != null && scoreItem != null && scoreItemSub != null) {
+            String scoreItemNumber = scoreItem.getString(FormConstant.NUMBER_KEY);
+            if (ScoreItemEnum.RESEARCH_SCORE.getCode().equalsIgnoreCase(scoreItemNumber)) {
+                if (person != null  && oriScore != null && oriScore.compareTo(BigDecimal.ZERO) > 0) {
+                    //科研与创新需要需要匹配积分规则
+                    QFilter filter = new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEM), QCP.equals, scoreItem.getLong(FormConstant.ID_KEY))
+                            .and(new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEMSUB), QCP.equals, scoreItemSub.getLong(FormConstant.ID_KEY)))
+                            .and(new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEMRANK), QCP.equals, scoreItemRank.getLong(FormConstant.ID_KEY)));
+                    Map<String, BigDecimal> scoreConf = ContributionHelper.getScoreConf(filter);
+                    String key = scoreItem.getLong(FormConstant.ID_KEY) + "-" + scoreItemSub.getLong(FormConstant.ID_KEY) + "-" + scoreItemRank.getLong(FormConstant.ID_KEY);
+                    BigDecimal validScore = oriScore;
+                    if (!scoreConf.isEmpty() && scoreConf.containsKey(key)) {
+                        BigDecimal maxScore = scoreConf.get(key);
+                        if (maxScore != null) {
+                            validScore = oriScore.compareTo(maxScore) > 0 ? maxScore : oriScore;
+                        }
+                    }
+                    this.getModel().setValue(ContributionConstant.NCKD_SCORE, validScore, rowIndex);
+                    this.getView().updateView(ContributionConstant.NCKD_SCORE, rowIndex);
+                }
+            } else {
+                this.getModel().setValue(ContributionConstant.NCKD_SCORE, oriScore, rowIndex);
+                this.getView().updateView(ContributionConstant.NCKD_SCORE, rowIndex);
+            }
+        }
+    }
+
+    /**
+     * 处理分录行变更
+     * 分录变更则针对当前分录计算积分
+     * @param rowIndex 行索引
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/10/26 19:47
+     */
+    private void handleEntryRowChange1(int rowIndex) {
+        Long id = ConvertUtil.toLong(this.getModel().getValue(FormConstant.ID_KEY));
+        Date date = ConvertUtil.toDate(this.getModel().getValue(ContributionConstant.NCKD_YEAR));
+        DynamicObject scoreItem = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEM));
+        DynamicObject scoreItemSub = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMSUB));
+        DynamicObject scoreItemRank = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMRANK));
+        DynamicObject person = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_PERSON, rowIndex));
+        BigDecimal oriScore = ConvertUtil.toBigDecimal(this.getModel().getValue(ContributionConstant.NCKD_ORISCORE, rowIndex));
 
         if (date != null && scoreItem != null && scoreItemSub != null && person != null
                 && oriScore != null && oriScore.compareTo(BigDecimal.ZERO) > 0) {
@@ -149,6 +230,61 @@ public class ContribBillFormPlugin extends AbstractFormPlugin implements Plugin,
         DynamicObject scoreItemSub = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMSUB));
         DynamicObject scoreItemRank = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMRANK));
 
+
+        if (date != null && scoreItem != null && scoreItemSub != null) {
+
+            DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(FormConstant.NCKD_ENTRYENTITY);
+            String scoreItemNumber = scoreItem.getString(FormConstant.NUMBER_KEY);
+            if(ScoreItemEnum.RESEARCH_SCORE.getCode().equalsIgnoreCase(scoreItemNumber)){
+                if(scoreItemRank != null) {
+                    //科研与创新需要需要匹配积分规则
+                    QFilter filter = new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEM), QCP.equals, scoreItem.getLong(FormConstant.ID_KEY))
+                            .and(new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEMSUB), QCP.equals, scoreItemSub.getLong(FormConstant.ID_KEY)))
+                            .and(new QFilter(String.join(".", FormConstant.NCKD_ENTRYENTITY, ContributionConstant.NCKD_SCOREITEMRANK), QCP.equals, scoreItemRank.getLong(FormConstant.ID_KEY)));
+                    Map<String, BigDecimal> scoreConf = ContributionHelper.getScoreConf(filter);
+                    String key = scoreItem.getLong(FormConstant.ID_KEY)+"-"+scoreItemSub.getLong(FormConstant.ID_KEY)+"-"+scoreItemRank.getLong(FormConstant.ID_KEY);
+                    for (int i = 0; i < entryEntity.size(); i++) {
+                        DynamicObject entry = entryEntity.get(i);
+                        BigDecimal oriScore = entry.getBigDecimal(ContributionConstant.NCKD_ORISCORE);
+                        BigDecimal validScore = oriScore;
+                        if(!scoreConf.isEmpty() && scoreConf.containsKey(key)){
+                            BigDecimal maxScore = scoreConf.get(key);
+                            if (maxScore != null && oriScore != null) {
+                                validScore = oriScore.compareTo(maxScore) > 0 ? maxScore : oriScore;
+                            } else {
+                                // 根据业务需求处理 null 值情况
+                                validScore = oriScore != null ? oriScore : BigDecimal.ZERO;
+                            }
+                        }
+                        this.getModel().setValue(ContributionConstant.NCKD_SCORE, validScore, i);
+                    }
+                }
+            }else{
+                for (int i = 0; i < entryEntity.size(); i++) {
+                    DynamicObject entry = entryEntity.get(i);
+                    BigDecimal oriScore = entry.getBigDecimal(ContributionConstant.NCKD_ORISCORE);
+                    this.getModel().setValue(ContributionConstant.NCKD_SCORE, oriScore, i);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 处理表头字段变更
+     * 表头变更则重新计算所有分录(已维护人员和原积分的数据)的积分
+     * @param
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/10/26 19:47
+     */
+    private void handleHeadFieldChange1() {
+        Long id = ConvertUtil.toLong(this.getModel().getValue(FormConstant.ID_KEY));
+        Date date = ConvertUtil.toDate(this.getModel().getValue(ContributionConstant.NCKD_YEAR));
+        DynamicObject scoreItem = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEM));
+        DynamicObject scoreItemSub = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMSUB));
+        DynamicObject scoreItemRank = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(ContributionConstant.NCKD_SCOREITEMRANK));
+
         if (date != null && scoreItem != null && scoreItemSub != null) {
             //重置分录所有分录的"录用积分"字段
             DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(FormConstant.NCKD_ENTRYENTITY);

+ 27 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/contribution/ContribBillOpPlugin.java

@@ -16,6 +16,7 @@ import kd.bos.entity.validate.ErrorLevel;
 import kd.bos.form.MessageBoxResult;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.psms.ScoreItemEnum;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
@@ -26,6 +27,7 @@ import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,6 +48,7 @@ public class ContribBillOpPlugin extends AbstractOperationServicePlugIn implemen
             @Override
             public void validate() {
                 //校验参与人数
+                Map<String, BigDecimal> scoreConf = ContributionHelper.getScoreConf();
                 for (ExtendedDataEntity dataEntity : this.getDataEntities()) {
                     DynamicObject data = dataEntity.getDataEntity();
                     int participants = data.getInt(ContributionConstant.NCKD_PARTICIPANTS);
@@ -62,7 +65,29 @@ public class ContribBillOpPlugin extends AbstractOperationServicePlugIn implemen
                             DynamicObject scoreItem = data.getDynamicObject(ContributionConstant.NCKD_SCOREITEM);
                             DynamicObject scoreItemSub = data.getDynamicObject(ContributionConstant.NCKD_SCOREITEMSUB);
                             DynamicObject scoreItemRank = data.getDynamicObject(ContributionConstant.NCKD_SCOREITEMRANK);
-                            List<ContributionHelper.ScoreItemValidScore> scoreItemValidScoreList = new ArrayList<>(entryEntity.size());
+                            String scoreItemNumber = scoreItem.getString(FormConstant.NUMBER_KEY);
+                            if(ScoreItemEnum.RESEARCH_SCORE.getCode().equalsIgnoreCase(scoreItemNumber)){
+                                String key = scoreItem.getLong(FormConstant.ID_KEY)+"-"+scoreItemSub.getLong(FormConstant.ID_KEY)+"-"+scoreItemRank.getLong(FormConstant.ID_KEY);
+                                if(!scoreConf.isEmpty() && scoreConf.containsKey(key)){
+                                    BigDecimal maxScore = scoreConf.get(key);
+                                    for (DynamicObject entry : entryEntity) {
+                                        DynamicObject person = entry.getDynamicObject(ContributionConstant.NCKD_PERSON);
+                                        long personId = person.getLong(FormConstant.ID_KEY);
+                                        BigDecimal oriScore = entry.getBigDecimal(ContributionConstant.NCKD_ORISCORE);
+                                        BigDecimal score = entry.getBigDecimal(ContributionConstant.NCKD_SCORE);
+                                        if(oriScore.compareTo(maxScore) > 0) {
+                                            this.addFatalErrorMessage(dataEntity, StrFormatter.format("参与人【{}】的原始分数【{}】超出限制积分【{}】。请重新填写“原始积分”,系统将会自动重算“录入积分”;",
+                                                    person.getString(FormConstant.NAME_KEY),
+                                                    oriScore.setScale(2, RoundingMode.HALF_UP),
+                                                    maxScore.setScale(2, RoundingMode.HALF_UP)));
+                                        }
+                                    }
+
+                                }
+                            }
+
+
+                           /* List<ContributionHelper.ScoreItemValidScore> scoreItemValidScoreList = new ArrayList<>(entryEntity.size());
                             for (DynamicObject entry : entryEntity) {
                                 DynamicObject person = entry.getDynamicObject(ContributionConstant.NCKD_PERSON);
                                 BigDecimal oriScore = entry.getBigDecimal(ContributionConstant.NCKD_ORISCORE);
@@ -94,7 +119,7 @@ public class ContribBillOpPlugin extends AbstractOperationServicePlugIn implemen
                                         }
                                     }
                                 }
-                            }
+                            }*/
                         }
                     }
                 }