Browse Source

feat(hr): 实现升保降级动态配置功能

- 新增升保降级条件配置实体及常量定义
- 创建JobLevelRuleConfig配置类用于存储规则- 在JobLevelCalculatorService中引入动态规则匹配逻辑
- 实现PositionFileHelper中获取配置的方法
- 添加升保降级配置表单插件及校验逻辑-优化BigDecimal转换避免精度丢失
- 增加描述字段常量支持配置说明信息- 完善职位序列与职级范围联动过滤逻辑- 提供配置重复性校验防止职级区间重叠
- 更新年度调整表单操作后刷新父级页面逻辑
wyc 1 month ago
parent
commit
f71b5c89c6

+ 2 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -248,5 +248,7 @@ public class FormConstant {
     public static final String NCKD_STARTDATE = "nckd_startdate";
     public static final String NCKD_STARTDATE = "nckd_startdate";
     /** 结束日期*/
     /** 结束日期*/
     public static final String NCKD_ENDDATE = "nckd_enddate";
     public static final String NCKD_ENDDATE = "nckd_enddate";
+    /** 描述*/
+    public static final String DESCRIPTION_KEY = "description";
 
 
 }
 }

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

@@ -863,10 +863,10 @@ public class AnnualAdjustmentService {
         if(ac.data.getRankingResultInfo() != null) {
         if(ac.data.getRankingResultInfo() != null) {
             newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANK, ac.data.getRankingResultInfo().topRank);
             newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANK, ac.data.getRankingResultInfo().topRank);
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANK, ac.data.getRankingResultInfo().allowanceRank);
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANK, ac.data.getRankingResultInfo().allowanceRank);
-            newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANKPERCENT, ac.data.getRankingResultInfo().topRankPercent);
+            newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANKPERCENT,  new BigDecimal(ac.data.getRankingResultInfo().topRankPercent.toString()));
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKMARK, ac.data.getRankingResultInfo().allowanceRankMark);
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKMARK, ac.data.getRankingResultInfo().allowanceRankMark);
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKSEL, ac.data.getRankingResultInfo().allowanceRankSel);
             newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKSEL, ac.data.getRankingResultInfo().allowanceRankSel);
-            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKPCT, ac.data.getRankingResultInfo().allowanceRankPercent);
+            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKPCT, new BigDecimal(ac.data.getRankingResultInfo().allowanceRankPercent.toString()));
         }
         }
         //上年度考核结果
         //上年度考核结果
         newPersonPosFile.set(PositionStructureConstant.NCKD_APPRAISALRESULT, ac.data.getAppraisalResult());
         newPersonPosFile.set(PositionStructureConstant.NCKD_APPRAISALRESULT, ac.data.getAppraisalResult());

+ 61 - 7
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/JobLevelCalculatorService.java

@@ -28,6 +28,7 @@ import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
+import nckd.jxccl.hr.psms.helper.data.JobLevelRuleConfig;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
@@ -1049,7 +1050,40 @@ public class JobLevelCalculatorService {
             //2、全员绩效排名位于后30%。
             //2、全员绩效排名位于后30%。
             jobLevelIndex--;
             jobLevelIndex--;
         }else{
         }else{
-            //技术或职能序列
+
+            // 使用动态配置规则替换硬编码规则
+            List<JobLevelRuleConfig> ruleConfigs = PositionFileHelper.getLevelConditionConf();
+
+            // 查找匹配的规则
+            JobLevelRuleConfig matchingRule = ruleConfigs.stream()
+                    .filter(rule -> rule.isMatchJobSeq(jobSeqEnum.getCode()) &&
+                            rule.isMatchJobLevel(currentJobLevelIndex))
+                    .findFirst()
+                    .orElse(null);
+
+
+            if (matchingRule != null) {
+                StrFormatter.format("职位序列【{}】职级【{}】应用的升保降级规则配置为【{}】,最大职级【{}】,最小职级【{}】,升级百分比阈值【{}】,降级百分比阈值",
+                        matchingRule.getName(),matchingRule.getMaxJobLevelSeq(),matchingRule.getMinJobLevel(),matchingRule.getUpGradeThreshold(),matchingRule.getDownGradeThreshold());
+
+                int upGradeThreshold = matchingRule.getUpGradeThreshold() != null ?
+                        matchingRule.getUpGradeThreshold() : 0;
+                int downGradeThreshold = matchingRule.getDownGradeThreshold() != null ?
+                        matchingRule.getDownGradeThreshold() : 0;
+
+                // 使用配置的阈值进行判断
+                if (allowanceRank <= selMaxAllowanceRank(upGradeThreshold, countR)) {
+                    jobLevelIndex++;
+                } else if (allowanceRank >= selMinAllowanceRank(downGradeThreshold, countR) &&
+                        selMinAllowanceRank(downGradeThreshold, countR) > 0) {
+                    jobLevelIndex--;
+                }
+            }else{
+                throw new ValidationException(StrFormatter.format("未匹配到符合职位序列【{}】职级【{}】对应的升保降级规则配置,请检查配置是否正确!",
+                        jobSeqEnum.getName(), currentJobLevelIndex));
+            }
+
+           /* //技术或职能序列
             if (JobSeqEnum.TECHNICALS.getCode().equals(jobSeqEnum.getCode()) || JobSeqEnum.FUNCTIONAL.getCode().equals(jobSeqEnum.getCode())) {
             if (JobSeqEnum.TECHNICALS.getCode().equals(jobSeqEnum.getCode()) || JobSeqEnum.FUNCTIONAL.getCode().equals(jobSeqEnum.getCode())) {
 
 
                 if (currentJobLevelIndex > 9) {
                 if (currentJobLevelIndex > 9) {
@@ -1116,7 +1150,7 @@ public class JobLevelCalculatorService {
                         jobLevelIndex--;
                         jobLevelIndex--;
                     }
                     }
                 }
                 }
-            }
+            }*/
         }
         }
 
 
         if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex > 1) {
         if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex > 1) {
@@ -1145,18 +1179,38 @@ public class JobLevelCalculatorService {
         return jobLevelIndex;
         return jobLevelIndex;
     }
     }
 
 
-    public static int selMaxAllowanceRank(int percent, int countallowancerank) {
+    /**
+     * 计算R排名的最大允许排名(前百分之几对应的排名)
+     * 用于确定员工R排名是否在前百分之几的范围内,以判断是否符合升级条件
+     *
+     * @param percent 百分比阈值(如60表示前60%)
+     * @param countAllowanceRank 参与R排名的总人数
+     * @return int 对应百分比的最大排名位置
+     * @author W.Y.C
+     * @date: 2025/09/22 19:47
+     */
+    public static int selMaxAllowanceRank(int percent, int countAllowanceRank) {
         int maxAllowanceRank = 0;
         int maxAllowanceRank = 0;
-        double maxAllowancerRankDouble = Double.parseDouble(countallowancerank + "") / 100.0 * Double.parseDouble(percent + "");
+        double maxAllowancerRankDouble = Double.parseDouble(countAllowanceRank + "") / 100.0 * Double.parseDouble(percent + "");
         maxAllowanceRank = (int) Math.round(maxAllowancerRankDouble);
         maxAllowanceRank = (int) Math.round(maxAllowancerRankDouble);
         return maxAllowanceRank;
         return maxAllowanceRank;
     }
     }
 
 
 
 
-    public static int selMinAllowanceRank(int percent, int countallowancerank) {
+    /**
+     * 计算R排名的最小允许排名(后百分之几对应的排名)
+     * 用于确定员工R排名是否在后百分之几的范围内,以判断是否符合降级条件
+     *
+     * @param percent 百分比阈值(如5表示后5%)
+     * @param countAllowanceRank 参与R排名的总人数
+     * @return int 对应百分比的最小排名位置
+     * @author W.Y.C
+     * @date: 2025/09/22 19:47
+     */
+    public static int selMinAllowanceRank(int percent, int countAllowanceRank) {
         int minAllowanceRank = 0;
         int minAllowanceRank = 0;
-        double minAllowanceRankDouble = Double.parseDouble(countallowancerank + "") / 100.0 * Double.parseDouble(percent + "");
-        minAllowanceRank = countallowancerank - ((int) Math.round(minAllowanceRankDouble));
+        double minAllowanceRankDouble = Double.parseDouble(countAllowanceRank + "") / 100.0 * Double.parseDouble(percent + "");
+        minAllowanceRank = countAllowanceRank - ((int) Math.round(minAllowanceRankDouble));
         return minAllowanceRank;
         return minAllowanceRank;
     }
     }
 
 

+ 13 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PerfRankMgmtConstant.java

@@ -65,4 +65,17 @@ public class PerfRankMgmtConstant extends FormConstant {
     public static final String NCKD_PERFRANKUNLOCKENTRY = "nckd_perfrankunlockentry";
     public static final String NCKD_PERFRANKUNLOCKENTRY = "nckd_perfrankunlockentry";
     public static final String NCKD_PERFRANKMGMT = "NCKD_PERFRANKMGMT";
     public static final String NCKD_PERFRANKMGMT = "NCKD_PERFRANKMGMT";
     /*-------------------------------------- 年度绩效排名解锁单据 begin --------------------------------------*/
     /*-------------------------------------- 年度绩效排名解锁单据 begin --------------------------------------*/
+
+    /*-------------------------------------- 升保降级条件配置 begin --------------------------------------*/
+    /** 升保降级条件配置-实体标识 */
+    public static final String LEVELCONDITIONCON_ENTITYID = "nckd_levelconditionconf";
+    /** 最小职级 */
+    public static final String NCKD_MINJOBLEVEL = "NCKD_MINJOBLEVEL";
+    /** 最大职级 */
+    public static final String NCKD_MAXJOBLEVEL = "NCKD_MAXJOBLEVEL";
+    /** 升级百分比阈值 */
+    public static final String NCKD_UPGRADETHRESHOLD = "NCKD_UPGRADETHRESHOLD";
+    /** 降级百分比阈值 */
+    public static final String NCKD_DOWNGRADETHRESHOLD = "NCKD_DOWNGRADETHRESHOLD";
+    /*-------------------------------------- 升保降级条件配置 begin --------------------------------------*/
 }
 }

+ 74 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/helper/PositionFileHelper.java

@@ -10,8 +10,11 @@ import kd.bos.servicehelper.QueryServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.data.JobLevelRuleConfig;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.Date;
@@ -525,4 +528,75 @@ public class PositionFileHelper {
         }
         }
         SaveServiceHelper.update(updatePersonPosFileColl);
         SaveServiceHelper.update(updatePersonPosFileColl);
     }
     }
+
+    /**
+     * 获取升保降级条件配置
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @author W.Y.C
+     * @date: 2025/11/02 21:32
+     */
+    public static List<JobLevelRuleConfig> getLevelConditionConf() {
+        return getLevelConditionConf(null);
+    }
+
+    /**
+     * 获取升保降级条件配置
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @author W.Y.C
+     * @date: 2025/11/02 21:32
+     */
+    public static List<JobLevelRuleConfig> getLevelConditionConf(QFilter otherFilter) {
+        // 查询条件:启用状态的规则,按职位序列和职级范围排序
+        QFilter filter = QFilterCommonHelper.getEnableFilter()
+                .and(QFilterCommonHelper.getValidDateFilter(FormConstant.NCKD_STARTDATE,FormConstant.NCKD_ENDDATE));
+        if(otherFilter != null){
+            filter = filter.and(otherFilter);
+        }
+
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(FormConstant.NAME_KEY)
+                .add(FormConstant.DESCRIPTION_KEY)
+                .addIdNumberName(FormConstant.NCKD_JOBSEQ)
+                .addIdNumberNameWithExtras(new String[]{PerfRankMgmtConstant.NCKD_MINJOBLEVEL}, FormConstant.JOBLEVELSEQ)
+                .addIdNumberNameWithExtras(new String[]{PerfRankMgmtConstant.NCKD_MAXJOBLEVEL}, FormConstant.JOBLEVELSEQ)
+                .add(PerfRankMgmtConstant.NCKD_UPGRADETHRESHOLD)
+                .add(PerfRankMgmtConstant.NCKD_DOWNGRADETHRESHOLD);
+
+
+
+        DynamicObject[] dynamicObjects = BusinessDataServiceHelper.load(
+                PerfRankMgmtConstant.LEVELCONDITIONCON_ENTITYID,
+                queryFieldBuilder.buildSelect(),
+                new QFilter[]{filter},
+                queryFieldBuilder.buildOrder()
+        );
+
+        // 转换为实体对象列表
+        List<JobLevelRuleConfig> ruleConfigs = new ArrayList<>();
+        for (DynamicObject dynamicObject : dynamicObjects) {
+            JobLevelRuleConfig config = new JobLevelRuleConfig();
+            DynamicObject jobSeq = dynamicObject.getDynamicObject(FormConstant.NCKD_JOBSEQ);
+            DynamicObject minJobLevel = dynamicObject.getDynamicObject(PerfRankMgmtConstant.NCKD_MINJOBLEVEL);
+            DynamicObject maxJobLevel = dynamicObject.getDynamicObject(PerfRankMgmtConstant.NCKD_MAXJOBLEVEL);
+            String name = dynamicObject.getString(FormConstant.NAME_KEY);
+            String description = dynamicObject.getString(FormConstant.DESCRIPTION_KEY);
+            int upGradeThreshold = dynamicObject.getInt(PerfRankMgmtConstant.NCKD_UPGRADETHRESHOLD);
+            int downGradeThreshold = dynamicObject.getInt(PerfRankMgmtConstant.NCKD_DOWNGRADETHRESHOLD);
+            config.setName(name);
+            config.setDescription(description);
+            config.setJobSeqNumber(jobSeq.getString(FormConstant.NUMBER_KEY));
+            config.setMinJobLevel(minJobLevel);
+            config.setMinJobLevelSeq(minJobLevel.getInt(FormConstant.JOBLEVELSEQ));
+            config.setMaxJobLevel(maxJobLevel);
+            config.setMaxJobLevelSeq(maxJobLevel != null ? maxJobLevel.getInt(FormConstant.JOBLEVELSEQ) : Integer.MAX_VALUE);
+            config.setUpGradeThreshold(upGradeThreshold);
+            config.setDownGradeThreshold(downGradeThreshold);
+            ruleConfigs.add(config);
+        }
+
+        return ruleConfigs;
+    }
+
+
 }
 }

+ 126 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/helper/data/JobLevelRuleConfig.java

@@ -0,0 +1,126 @@
+package nckd.jxccl.hr.psms.helper.data;
+
+import kd.bos.dataentity.entity.DynamicObject;
+
+/**
+* 获取升保降级条件配置
+* @author W.Y.C
+* @date 2025/11/2 21:39
+* @version 1.0
+*/
+public class JobLevelRuleConfig {
+    private String name;
+    private String description;
+    /**职位序列编码*/
+    private String jobSeqNumber;
+    /**最小职级*/
+    private Integer minJobLevelSeq;
+    /**最小职级*/
+    private DynamicObject minJobLevel;
+    /**最大职级*/
+    private Integer maxJobLevelSeq;
+    /**最大职级*/
+    private DynamicObject maxJobLevel;
+    /**升级百分比阈值*/
+    private Integer upGradeThreshold;
+    /**降级百分比阈值*/
+    private Integer downGradeThreshold;
+
+    /**
+     * 判断给定的职级是否在该规则的适用范围内
+     * @param jobLevelIndex 职级
+     * @return: boolean
+     * @author W.Y.C
+     * @date: 2025/11/02 21:54
+     */
+    public boolean isMatchJobLevel(int jobLevelIndex) {
+        if (minJobLevelSeq == null || maxJobLevelSeq == null) {
+            return false;
+        }
+        return jobLevelIndex >= minJobLevelSeq && jobLevelIndex <= maxJobLevelSeq;
+    }
+
+
+    /**
+     * 判断给定的职位序列是否匹配该规则
+     * @param jobSeqNumber 职位序列编码
+     * @return: boolean
+     * @author W.Y.C
+     * @date: 2025/11/02 21:54
+     */
+    public boolean isMatchJobSeq(String jobSeqNumber) {
+        return this.jobSeqNumber != null && this.jobSeqNumber.equals(jobSeqNumber);
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getDownGradeThreshold() {
+            return downGradeThreshold;
+        }
+
+        public void setDownGradeThreshold(Integer downGradeThreshold) {
+            this.downGradeThreshold = downGradeThreshold;
+        }
+
+        public String getJobSeqNumber() {
+            return jobSeqNumber;
+        }
+
+        public void setJobSeqNumber(String jobSeqNumber) {
+            this.jobSeqNumber = jobSeqNumber;
+        }
+
+        public DynamicObject getMaxJobLevel() {
+            return maxJobLevel;
+        }
+
+        public void setMaxJobLevel(DynamicObject maxJobLevel) {
+            this.maxJobLevel = maxJobLevel;
+        }
+
+        public Integer getMaxJobLevelSeq() {
+            return maxJobLevelSeq;
+        }
+
+        public void setMaxJobLevelSeq(Integer maxJobLevelSeq) {
+            this.maxJobLevelSeq = maxJobLevelSeq;
+        }
+
+        public DynamicObject getMinJobLevel() {
+            return minJobLevel;
+        }
+
+        public void setMinJobLevel(DynamicObject minJobLevel) {
+            this.minJobLevel = minJobLevel;
+        }
+
+        public Integer getMinJobLevelSeq() {
+            return minJobLevelSeq;
+        }
+
+        public void setMinJobLevelSeq(Integer minJobLevelSeq) {
+            this.minJobLevelSeq = minJobLevelSeq;
+        }
+
+        public Integer getUpGradeThreshold() {
+            return upGradeThreshold;
+        }
+
+        public void setUpGradeThreshold(Integer upGradeThreshold) {
+            this.upGradeThreshold = upGradeThreshold;
+        }
+    }

+ 3 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/annualadjust/NewAnnualAdjustFormPlugin.java

@@ -57,8 +57,11 @@ public class NewAnnualAdjustFormPlugin extends AbstractFormPlugin implements Plu
         if(success && PositionStructureConstant.OP_CONFIRMADJUST.equalsIgnoreCase(operateKey)){
         if(success && PositionStructureConstant.OP_CONFIRMADJUST.equalsIgnoreCase(operateKey)){
             Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
             Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
             String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
             String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
+            //刷新父级页面
+            this.getView().getParentView().invokeOperation(FormConstant.REFRESH_OP);
             this.getView().returnDataToParent("true");
             this.getView().returnDataToParent("true");
             this.getView().showConfirm("提示","新建调整成功,职位档案已生成。",jobLeveStr, MessageBoxOptions.OK,null,null,null,null);
             this.getView().showConfirm("提示","新建调整成功,职位档案已生成。",jobLeveStr, MessageBoxOptions.OK,null,null,null,null);
+
         }
         }
     }
     }
 }
 }

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

@@ -1,6 +1,8 @@
 package nckd.jxccl.hr.psms.plugin.form.contribution;
 package nckd.jxccl.hr.psms.plugin.form.contribution;
 
 
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
 import kd.bos.form.field.BasedataEdit;
 import kd.bos.form.field.BasedataEdit;
 import kd.bos.form.field.events.BeforeF7SelectEvent;
 import kd.bos.form.field.events.BeforeF7SelectEvent;
 import kd.bos.form.field.events.BeforeF7SelectListener;
 import kd.bos.form.field.events.BeforeF7SelectListener;
@@ -11,8 +13,10 @@ import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
 import nckd.jxccl.hr.psms.common.ContributionConstant;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 
 
 import java.util.EventObject;
 import java.util.EventObject;
+import java.util.Objects;
 
 
 /**
 /**
 * 积分项目分数配置插件
 * 积分项目分数配置插件
@@ -39,6 +43,10 @@ public class ScoreItemConfFormPlugin extends AbstractFormPlugin implements Plugi
         }
         }
     }
     }
 
 
+    @Override
+    public void afterBindData(EventObject e) {
+        this.getModel().setValue(FormConstant.STATUS, "A");
+    }
 
 
     @Override
     @Override
     public void beforeF7Select(BeforeF7SelectEvent e) {
     public void beforeF7Select(BeforeF7SelectEvent e) {
@@ -64,4 +72,26 @@ public class ScoreItemConfFormPlugin extends AbstractFormPlugin implements Plugi
         }
         }
     }
     }
 
 
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        String name = e.getProperty().getName();
+        if (ContributionConstant.NCKD_SCOREITEMSUB.equalsIgnoreCase(name) || ContributionConstant.NCKD_SCOREITEM.equalsIgnoreCase(name)) {
+
+            ChangeData[] changeSet = e.getChangeSet();
+            Object newValue = changeSet[0].getNewValue();
+            Object oldValue = changeSet[0].getOldValue();
+            if(newValue != null && !Objects.equals(newValue, oldValue)) {
+                if(ContributionConstant.NCKD_SCOREITEMSUB.equalsIgnoreCase(name)){
+                    this.getModel().setValue(ContributionConstant.NCKD_SCOREITEMRANK, null,changeSet[0].getRowIndex());
+                    this.getView().updateView(ContributionConstant.NCKD_SCOREITEMRANK,changeSet[0].getRowIndex());
+                }else{
+                    this.getModel().setValue(ContributionConstant.NCKD_SCOREITEMRANK, null,changeSet[0].getRowIndex());
+                    this.getView().updateView(ContributionConstant.NCKD_SCOREITEMRANK,changeSet[0].getRowIndex());
+                    this.getModel().setValue(ContributionConstant.NCKD_SCOREITEMSUB, null,changeSet[0].getRowIndex());
+                    this.getView().updateView(ContributionConstant.NCKD_SCOREITEMSUB,changeSet[0].getRowIndex());
+                }
+            }
+        }
+    }
+
 }
 }

+ 88 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/LevelConditionConfFormPlugin.java

@@ -0,0 +1,88 @@
+package nckd.jxccl.hr.psms.plugin.form.performance;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.field.BasedataEdit;
+import kd.bos.form.field.events.BeforeF7SelectEvent;
+import kd.bos.form.field.events.BeforeF7SelectListener;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
+import nckd.jxccl.hr.psms.common.ContributionConstant;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.Arrays;
+import java.util.EventObject;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+* 升保降级条件配置
+* 实体标识:nckd_levelconditionconf
+* @author W.Y.C
+* @date 2025/11/2 21:01
+* @version 1.0
+*/
+public class LevelConditionConfFormPlugin extends AbstractFormPlugin implements Plugin, BeforeF7SelectListener {
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+
+        // 为分录中的A字段添加值变化监听
+        BasedataEdit minJobLevelEdit = this.getView().getControl(PerfRankMgmtConstant.NCKD_MINJOBLEVEL);
+        if (minJobLevelEdit != null) {
+            minJobLevelEdit.addBeforeF7SelectListener(this);
+        }
+
+        BasedataEdit maxJobLevelEdit = this.getView().getControl(PerfRankMgmtConstant.NCKD_MAXJOBLEVEL);
+        if (maxJobLevelEdit != null) {
+            maxJobLevelEdit.addBeforeF7SelectListener(this);
+        }
+    }
+
+    @Override
+    public void beforeF7Select(BeforeF7SelectEvent e) {
+        String fieldKey = e.getProperty().getName();
+
+        //基础资料联动
+        // 仅处理B字段的F7事件
+        if (PerfRankMgmtConstant.NCKD_MINJOBLEVEL.equalsIgnoreCase(fieldKey) || PerfRankMgmtConstant.NCKD_MAXJOBLEVEL.equalsIgnoreCase(fieldKey)) {
+            DynamicObject jobSeq = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(PositionStructureConstant.NCKD_JOBSEQ));
+            if(jobSeq != null){
+                DynamicObject[] jobLevelByJobSeq = JobLevelCalculatorService.getJobLevelByJobSeq(jobSeq);
+                List<Long> jobLevelIds = Arrays.stream(jobLevelByJobSeq).map(dynamicObject -> dynamicObject.getLong(FormConstant.ID_KEY)).collect(Collectors.toList());
+                QFilter filter = new QFilter(ContributionConstant.ID_KEY, QCP.in, jobLevelIds);
+                e.addCustomQFilter(filter);
+
+            }else{
+                this.getView().showTipNotification("请先选择“职位序列”");
+                e.setCancel( true);
+            }
+
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        String name = e.getProperty().getName();
+        if(FormConstant.NCKD_JOBSEQ.equalsIgnoreCase(name)){
+            ChangeData[] changeSet = e.getChangeSet();
+            Object newValue = changeSet[0].getNewValue();
+            Object oldValue = changeSet[0].getOldValue();
+            if(newValue != null && !Objects.equals(newValue, oldValue)) {
+                this.getModel().setValue(PerfRankMgmtConstant.NCKD_MINJOBLEVEL, null);
+                this.getModel().setValue(PerfRankMgmtConstant.NCKD_MAXJOBLEVEL, null);
+                this.getView().updateView(PerfRankMgmtConstant.NCKD_MINJOBLEVEL);
+                this.getView().updateView(PerfRankMgmtConstant.NCKD_MAXJOBLEVEL);
+            }
+        }
+    }
+}

+ 0 - 8
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfrankMgmtFormPlugin.java

@@ -1,14 +1,10 @@
 package nckd.jxccl.hr.psms.plugin.form.performance;
 package nckd.jxccl.hr.psms.plugin.form.performance;
 
 
-import com.google.common.collect.Lists;
-import kd.bos.bill.IBillView;
-import kd.bos.bill.OperationStatus;
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.RefObject;
 import kd.bos.dataentity.RefObject;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
-import kd.bos.dataentity.metadata.IDataEntityProperty;
 import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
 import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.QueryEntityType;
 import kd.bos.entity.QueryEntityType;
@@ -29,8 +25,6 @@ import kd.bos.form.events.AfterDoOperationEventArgs;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.operate.FormOperate;
 import kd.bos.form.operate.FormOperate;
-import kd.bos.form.plugin.AbstractFormPlugin;
-import kd.bos.form.plugin.IFormPlugin;
 import kd.bos.logging.Log;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QCP;
@@ -48,8 +42,6 @@ import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.Strings;
 
 
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
 import java.util.Arrays;

+ 59 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/contribution/validate/LevelConditionConfValidate.java

@@ -0,0 +1,59 @@
+package nckd.jxccl.hr.psms.plugin.operate.contribution.validate;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
+import nckd.jxccl.hr.psms.helper.PositionFileHelper;
+import nckd.jxccl.hr.psms.helper.data.JobLevelRuleConfig;
+
+import java.util.List;
+
+/**
+* 升保降级条件配置-校验
+* 实体标识:nckd_levelconditionconf
+* @author W.Y.C
+* @date 2025/11/3 13:05
+* @version 1.0
+*/
+public class LevelConditionConfValidate extends AbstractValidator {
+
+    @Override
+    public void validate() {
+        for (ExtendedDataEntity dataEntity : this.getDataEntities()) {
+            DynamicObject data = dataEntity.getDataEntity();
+            long id = data.getLong(FormConstant.ID_KEY);
+            List<JobLevelRuleConfig> levelConditionConf = PositionFileHelper.getLevelConditionConf(new QFilter(FormConstant.ID_KEY, QCP.not_equals, id));
+            DynamicObject jobSeq = data.getDynamicObject(FormConstant.NCKD_JOBSEQ);
+            DynamicObject minJobLevel = data.getDynamicObject(PerfRankMgmtConstant.NCKD_MINJOBLEVEL);
+            int minJobLevelSeq = minJobLevel.getInt(FormConstant.JOBLEVELSEQ);
+            DynamicObject maxJobLevel = data.getDynamicObject(PerfRankMgmtConstant.NCKD_MAXJOBLEVEL);
+            int maxJobLevelSeq = maxJobLevel != null ? maxJobLevel.getInt(FormConstant.JOBLEVELSEQ) : Integer.MAX_VALUE;
+            for (JobLevelRuleConfig jobLevelRuleConfig : levelConditionConf) {
+                String jobSeqNumber = jobLevelRuleConfig.getJobSeqNumber();
+                Integer maxJobLevelSeq1 = jobLevelRuleConfig.getMaxJobLevelSeq();
+                Integer minJobLevelSeq1 = jobLevelRuleConfig.getMinJobLevelSeq();
+                //相同序列,最大职级和最小职级不能重叠
+                if (jobSeq != null && jobSeq.getString(FormConstant.NUMBER_KEY).equals(jobSeqNumber)) {
+                    // 处理空值情况,null表示无限制
+                    int existingMaxLevel = maxJobLevelSeq1 != null ? maxJobLevelSeq1 : Integer.MAX_VALUE;
+                    int existingMinLevel = minJobLevelSeq1 != null ? minJobLevelSeq1 : Integer.MIN_VALUE;
+                    // 判断职级范围是否重叠
+                    boolean overlap = !(minJobLevelSeq > existingMaxLevel || maxJobLevelSeq < existingMinLevel);
+                    if (overlap) {
+                        this.addMessage(dataEntity, StrFormatter.format("职级范围与已有配置重叠【{}】,请调整最小职级或最大职级。", jobLevelRuleConfig.getName()));
+                    }
+                }
+            }
+
+        }
+    }
+
+}