فهرست منبع

feat(hr): 新增年度调整功能

- 在 FormConstant 中新增 NCKD_ENTRYENTITY 和 IS_SEQLATESTRECORD 常量- 修改标品实体标识为小写格式
- 更新 StrFormatter 中的 LINE_SEPARATOR 值
- 在 QueryFieldBuilder 中添加 buildSelectArray 相关方法- 在 PerformanceManagerHelper 中使用新的 QueryFieldBuilder 构建查询字段
- 移除 HonorStudentConstant 中重复定义的 NCKD_ENTRYENTITY 常量
- 新增 AnnualAdjustmentData 强类型数据承载类
- 新增 AnnualAdjustmentService 年度调整服务类及相关业务逻辑实现
wyc 3 هفته پیش
والد
کامیت
3413b6f679
28فایلهای تغییر یافته به همراه2832 افزوده شده و 724 حذف شده
  1. 7 4
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  2. 64 0
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/QueryFieldBuilder.java
  3. 1 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/StrFormatter.java
  4. 6 6
      code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/pm/helper/PerformanceManagerHelper.java
  5. 1 2
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/common/HonorStudentConstant.java
  6. 292 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/AnnualAdjustmentData.java
  7. 755 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/AnnualAdjustmentService.java
  8. 134 91
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/JobLevelCalculatorService.java
  9. 22 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java
  10. 71 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/bo/PositionAppointmentBO.java
  11. 99 230
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/helper/PositionStructureHelper.java
  12. 25 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/AdjustQueryListPlugin.java
  13. 88 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/DynamicAdjustmentFormPlugin.java
  14. 82 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/NewDynamicAdjustmentBatchDiaLogFormPlugin.java
  15. 9 6
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/NewDynamicAdjustmentDiaLogFormPlugin.java
  16. 27 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/GradedPersonQueryListPlugin.java
  17. 73 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/NewHireInitialBatchFormPlugin.java
  18. 39 30
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/NewHireInitialFormPlugin.java
  19. 73 0
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/ServingInitialBatchFormPlugin.java
  20. 54 31
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/ServingInitialFormPlugin.java
  21. 91 7
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/UngradedPersonQueryListPlugin.java
  22. 176 72
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/adjust/NewDynamicAdjustmentOperationPlugIn.java
  23. 129 37
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/BaseInitialOperationPlugIn.java
  24. 107 85
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/NewHireInitialOperationPlugIn.java
  25. 104 85
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/ServingInitialOperationPlugIn.java
  26. 32 11
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportFormPlugin.java
  27. 266 26
      code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportReportListDataPlugin.java
  28. 5 0
      code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerWizardFormPlugin.java

+ 7 - 4
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -8,7 +8,7 @@ package nckd.jxccl.base.common.constant;
  */
 public class FormConstant {
 
-    //====================================== 标品实体标识 ======================================
+    //====================================== 标品实体标识(需要小写) ======================================
     /**学历-实体标识*/
     public static final String HBSS_DIPLOMA = "hbss_diploma";
     /**教育经历-实体标识*/
@@ -38,9 +38,9 @@ public class FormConstant {
     /** 组织分配-实体标识 */
     public static final String ASSIGNMENT_ENTITYID = "hrpi_assignment";
     /** 岗位-实体标识*/
-    public static final String HBPM_POSITIONHR = "HBPM_POSITIONHR";
+    public static final String HBPM_POSITIONHR = "hbpm_positionhr";
     /** 服务年限-实体标识*/
-    public static final String HRPI_PERSERLEN = "HRPI_PERSERLEN";
+    public static final String HRPI_PERSERLEN = "hrpi_perserlen";
 
 
     //====================================== 标品op ======================================
@@ -85,7 +85,8 @@ public class FormConstant {
     public static final String TBMAIN = "tbmain";
     /** 左树右表-是否包含子部门*/
     public static final String CHKINCLUDECHILD = "chkincludechild";
-
+    /** 单据体 */
+    public static final String NCKD_ENTRYENTITY = "NCKD_ENTRYENTITY";
 
     //====================================== 通用字段 ======================================
     /** ID标识 */
@@ -158,6 +159,8 @@ public class FormConstant {
     public static final String IS_PRIMARY = "isprimary";
     /** 是否删除 */
     public static final String IS_DELETED = "isdeleted";
+    /** 最新任职记录 */
+    public static final String IS_SEQLATESTRECORD = "isseqlatestrecord";
     /** 组织分配 */
     public static final String ASSIGNMENT = "assignment";
     /** 开始时间 */

+ 64 - 0
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/QueryFieldBuilder.java

@@ -3,6 +3,8 @@ package nckd.jxccl.base.common.utils;
 import nckd.jxccl.base.common.constant.FormConstant;
 
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.StringJoiner;
 
 /**
@@ -233,6 +235,64 @@ public final class QueryFieldBuilder {
     public String buildSelect() {
         return selectJoiner.toString();
     }
+    /**
+     * 构建并返回 SELECT 字段数组
+     * 使用方式:
+     * String[] fields = builder.buildSelectArray();
+     * 输出结果:
+     * ["ID_KEY", "COMPANY_KEY.ID", "COMPANY_KEY.NUMBER", "COMPANY_KEY.NAME", ...]
+     * @return String[]
+     * @author W.Y.C
+     * @date: 2025/09/27
+     */
+    public String[] buildSelectArray() {
+        return selectJoiner.toString().split(",");
+    }
+
+
+    /**
+     * 构建 SELECT 字段数组,并排除指定字段
+     * 使用方式:
+     * String[] fields = builder.buildSelectArrayExclude(FormConstant.ID_KEY, "COMPANY_KEY.NUMBER");
+     * 输出结果:
+     * ["COMPANY_KEY.ID", "COMPANY_KEY.NAME", ...] (排除了ID_KEY和COMPANY_KEY.NUMBER)
+     * @param excludeFields 需要排除的字段数组
+     * @return String[]
+     * @author W.Y.C
+     * @date: 2025/09/27
+     */
+    public String[] buildSelectArrayExclude(String... excludeFields) {
+        if (excludeFields == null || excludeFields.length == 0) {
+            return buildSelectArray();
+        }
+
+        Set<String> excludeSet = new HashSet<>(Arrays.asList(excludeFields));
+        return Arrays.stream(buildSelectArray())
+                .filter(field -> !excludeSet.contains(field))
+                .toArray(String[]::new);
+    }
+
+    /**
+     * 构建 SELECT 字段数组,并排除指定前缀的字段
+     * 使用方式:
+     * String[] fields = builder.buildSelectArrayExcludePrefix("COMPANY_KEY", "HRPI_PEREDUEXP");
+     * 输出结果:
+     * ["ID_KEY", "EDUCATION_KEY.ID_KEY", "EDUCATION_KEY.NAME_KEY", ...] (排除了所有以COMPANY_KEY和HRPI_PEREDUEXP开头的字段)
+     * @param excludePrefixes 需要排除的字段前缀数组
+     * @return String[]
+     * @author W.Y.C
+     * @date: 2025/09/27
+     */
+    public String[] buildSelectArrayExcludePrefix(String... excludePrefixes) {
+        if (excludePrefixes == null || excludePrefixes.length == 0) {
+            return buildSelectArray();
+        }
+
+        Set<String> excludePrefixSet = new HashSet<>(Arrays.asList(excludePrefixes));
+        return Arrays.stream(buildSelectArray())
+                .filter(field -> excludePrefixSet.stream().noneMatch(prefix -> field.startsWith(prefix + ".")))
+                .toArray(String[]::new);
+    }
 
     /**
      * 构建 ORDER BY 字段字符串
@@ -248,6 +308,10 @@ public final class QueryFieldBuilder {
         return orderJoiner.toString();
     }
 
+    public String[] buildOrderArray() {
+        return orderJoiner.toString().split(",");
+    }
+
     @Override
     public String toString() {
         return "SELECT: " + buildSelect() + "\nORDER: " + buildOrder();

+ 1 - 1
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/utils/StrFormatter.java

@@ -16,7 +16,7 @@ public class StrFormatter {
 	private static final char BACKSLASH = '\\';
 	private static final String EMPTY_JSON = "{}";
 	private static final String NULL_STRING = "null";
-	public static final String LINE_SEPARATOR = "null";
+	public static final String LINE_SEPARATOR = "\r\n";
 
 	/**
 	 * 格式化字符串(使用默认占位符 {})

+ 6 - 6
code/base/nckd-jxccl-base-helper/src/main/java/nckd/jxccl/base/pm/helper/PerformanceManagerHelper.java

@@ -7,6 +7,7 @@ import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 
 import java.time.LocalDateTime;
 import java.util.Date;
@@ -43,12 +44,11 @@ public class PerformanceManagerHelper {
         QFilter filter = new QFilter(String.join(".",FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals,personId)
                 .and(new QFilter(String.join(".",PERFMANAGER_ENTRY_ENTITYID, NCKD_APPRAISALYEAR),QCP.equals,beginOfYear));
 
-        String selectField = String.join(",",
-                String.join(".",PERFMANAGER_ENTRY_ENTITYID, NCKD_APPRAISALYEAR),
-                String.join(".",PERFMANAGER_ENTRY_ENTITYID, NCKD_APPRAISALRESULT),
-                String.join(".",PERFMANAGER_ENTRY_ENTITYID, FormConstant.NCKD_SCORE)
-        );
-        DynamicObject[] load = BusinessDataServiceHelper.load(PERFMANAGER_ENTITYID, selectField, new QFilter[]{filter}, FormConstant.CREATE_TIME_KEY + " desc");
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .addGroup(new String[]{PERFMANAGER_ENTRY_ENTITYID}, NCKD_APPRAISALYEAR,NCKD_APPRAISALRESULT,FormConstant.NCKD_SCORE)
+                .addIdNumberNameWithExtras(new String[]{PERFMANAGER_ENTRY_ENTITYID, NCKD_APPRAISALRESULT}, FormConstant.NCKD_SCORE)
+                .orderDesc(FormConstant.CREATE_TIME_KEY);
+        DynamicObject[] load = BusinessDataServiceHelper.load(PERFMANAGER_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter},queryFieldBuilder.buildOrder() );
         if(load != null && load.length > 0){
             DynamicObject perfManager = load[0];
             DynamicObjectCollection perfManagerEntryColl = perfManager.getDynamicObjectCollection(PERFMANAGER_ENTRY_ENTITYID);

+ 1 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/hstu/common/HonorStudentConstant.java

@@ -190,8 +190,7 @@ public class HonorStudentConstant extends FormConstant {
 
     /** 综合测评答卷-实体标识 */
     public static final String NCKD_EVALRESULT_ENTITYID = "nckd_evalresult";
-    /** 单据体 */
-    public static final String NCKD_ENTRYENTITY = "NCKD_ENTRYENTITY";
+
     /** 缺省id字段 */
     public static final String ID = "ID";
     /** 分录行号 */

+ 292 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/AnnualAdjustmentData.java

@@ -0,0 +1,292 @@
+package nckd.jxccl.hr.psms.business;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * 强类型数据承载类(替代原 selMap)。
+ * <p>来源:utils.YearContributeScoreBillEntryScoreSumBypersonidAndYear(...) 的查询结果。</p>
+ * <p>所有字段与原 addNewYear_PersonpositionfileInfo(277~815) 所用键一一对应。</p>
+ */
+public class AnnualAdjustmentData {
+
+    /** 员工姓名(personname) */
+    private String personName;
+
+    /** 主任职位名称(positonname) */
+    private String positionName;
+
+    /** 职位序列FID(hrjobfamilyid) */
+    private Long hrJobFamilyId;
+
+    /** 职位序列编码(hrjobfamilynumber),例如:01技术、02职能、03技能、04管理 */
+    private String hrJobFamilyNumber;
+
+    /** 职位FID(positonid) */
+    private Long positionId;
+
+    /** 行政组织FID(persondepid) */
+    private Long personDepId;
+
+    /** HR组织FID(hrorgunitid) */
+    private String hrOrgUnitId;
+
+    /** 考核结果 */
+    private DynamicObject appraisalResult;
+
+    /** 考核结果ID(appraisalresultid) */
+    private Long appraisalResultId;
+
+    /** 考核结果编号(appraisalresultnumber) */
+    private String appraisalResultNumber;
+
+    /** 考核结果名称 */
+    private String appraisalResultName;
+
+    /**R排名*/
+    private JobLevelCalculatorService.RankingResultInfo rankingResultInfo;
+    /**积分数据*/
+    public JobLevelCalculatorService.JobScoreInfo jobScoreInfo;
+
+    /** 上一条年度记录ID(lastid) */
+    private Long lastId;
+
+    /** 首条年度记录ID(firstid) */
+    private Long firstId;
+
+    /** 学历ID(diplomaid) */
+    private Long diplomaId;
+
+    /** 学历配置分(diplomascore) */
+    private BigDecimal diplomaScore;
+
+    /** 职称名称(rankname) */
+    private String rankName;
+
+    /** 职称等级ID(zgjbid) */
+    private Long zgjbId;
+
+    /** 职称等级编码(zgjbnumber) */
+    private String zgjbNumber;
+
+    /** 职称等级名称 */
+    private String zgjbName;
+
+    /** 职称等级配置分(zgjbscore) */
+    private BigDecimal zgjbScore;
+
+    /** 技能岗位名称(jobstatusname) */
+    private String jobStatusName;
+
+
+    /** 技能等级ID(zyjndjid) */
+    private Long zyjndjId;
+
+    /** 技能等级编码(zyjndjnumber) */
+    private String zyjndjNumber;
+
+    /** 技能等级名称(zyjndjnumber) */
+    private String zyjndjName;
+
+    /** 技能等级配置分(zyjndjscore) */
+    private BigDecimal zyjndjScore;
+
+    /** 上年所有贡献单据分之和(allyearscoresum) */
+    private BigDecimal allYearScoreSum;
+
+    /** 上年度考核得分(appraisalresultscore) */
+    private BigDecimal appraisalResultScore;
+
+    /** 上年度累计积分池(lastsumscore) */
+    private BigDecimal lastSumScore;
+
+    /** 上一条记录的职位序列编码(lasthrjobfamilynumber) */
+    private String lastHrJobFamilyNumber;
+
+    /** 上一条记录的职位序列 */
+    private DynamicObject lastJobSeq;
+
+    /** 上一条记录的职位序列(转换后,这里转换指的是:如果是管理序列,则按职能序列进行调整) */
+    private DynamicObject convertLastJobSeq;
+
+    /** 上一条记录的职级顺序号(lastjobgradeindex) */
+    private Integer lastJobGradeIndex;
+
+    /** 上一条记录的职级(lastjobgradefid) */
+    private DynamicObject lastJobLevel;
+
+    /** 年度分项得分A(yearscoresuma) */
+    private Double yearscoresuma;
+
+    /** 年度分项得分B(yearscoresumb) */
+    private Double yearscoresumb;
+
+    /** 年度分项得分C(yearscoresumc) */
+    private Double yearscoresumc;
+
+    /** 年度分项得分D(yearscoresumd) */
+    private Double yearscoresumd;
+
+    /** 年度分项得分E(yearscoresume) */
+    private Double yearscoresume;
+
+    /** 年度分项得分F(yearscoresumf) */
+    private Double yearscoresumf;
+
+    /** 年度分项得分G(yearscoresumg) */
+    private Double yearscoresumg;
+
+    /** 年度分项得分H(yearscoresumh) */
+    private Double yearscoresumh;
+
+    /** 年度分项得分I(yearscoresumi) */
+    private Double yearscoresumi;
+
+    public String getPersonName() { return personName; }
+    public void setPersonName(String personName) { this.personName = personName; }
+    public String getPositionName() { return positionName; }
+    public void setPositionName(String positionName) { this.positionName = positionName; }
+    public Long getHrJobFamilyId() { return hrJobFamilyId; }
+    public void setHrJobFamilyId(Long hrJobFamilyId) { this.hrJobFamilyId = hrJobFamilyId; }
+    public String getHrJobFamilyNumber() { return hrJobFamilyNumber; }
+    public void setHrJobFamilyNumber(String hrJobFamilyNumber) { this.hrJobFamilyNumber = hrJobFamilyNumber; }
+    public Long getPositionId() { return positionId; }
+    public void setPositionId(Long positionId) { this.positionId = positionId; }
+    public Long getPersonDepId() { return personDepId; }
+    public void setPersonDepId(Long personDepId) { this.personDepId = personDepId; }
+    public String getHrOrgUnitId() { return hrOrgUnitId; }
+    public void setHrOrgUnitId(String hrOrgUnitId) { this.hrOrgUnitId = hrOrgUnitId; }
+
+    public DynamicObject getAppraisalResult() {
+        return appraisalResult;
+    }
+
+    public void setAppraisalResult(DynamicObject appraisalResult) {
+        this.appraisalResult = appraisalResult;
+    }
+
+    public Long getAppraisalResultId() { return appraisalResultId; }
+    public void setAppraisalResultId(Long appraisalResultId) { this.appraisalResultId = appraisalResultId; }
+    public String getAppraisalResultNumber() { return appraisalResultNumber; }
+    public void setAppraisalResultNumber(String appraisalResultNumber) { this.appraisalResultNumber = appraisalResultNumber; }
+    public Long getLastId() { return lastId; }
+    public void setLastId(Long lastId) { this.lastId = lastId; }
+    public Long getFirstId() { return firstId; }
+    public void setFirstId(Long firstId) { this.firstId = firstId; }
+    public Long getDiplomaId() { return diplomaId; }
+    public void setDiplomaId(Long diplomaId) { this.diplomaId = diplomaId; }
+    public BigDecimal getDiplomaScore() { return diplomaScore; }
+    public void setDiplomaScore(BigDecimal diplomaScore) { this.diplomaScore = diplomaScore; }
+    public String getRankName() { return rankName; }
+    public void setRankName(String rankName) { this.rankName = rankName; }
+    public Long getZgjbId() { return zgjbId; }
+    public void setZgjbId(Long zgjbId) { this.zgjbId = zgjbId; }
+    public String getZgjbNumber() { return zgjbNumber; }
+    public void setZgjbNumber(String zgjbNumber) { this.zgjbNumber = zgjbNumber; }
+    public BigDecimal getZgjbScore() { return zgjbScore; }
+    public void setZgjbScore(BigDecimal zgjbScore) { this.zgjbScore = zgjbScore; }
+    public String getJobStatusName() { return jobStatusName; }
+    public void setJobStatusName(String jobStatusName) { this.jobStatusName = jobStatusName; }
+    public Long getZyjndjId() { return zyjndjId; }
+    public void setZyjndjId(Long zyjndjId) { this.zyjndjId = zyjndjId; }
+    public String getZyjndjNumber() { return zyjndjNumber; }
+    public void setZyjndjNumber(String zyjndjNumber) { this.zyjndjNumber = zyjndjNumber; }
+    public BigDecimal getZyjndjScore() { return zyjndjScore; }
+    public void setZyjndjScore(BigDecimal zyjndjScore) { this.zyjndjScore = zyjndjScore; }
+    public BigDecimal getAllYearScoreSum() { return allYearScoreSum; }
+    public void setAllYearScoreSum(BigDecimal allYearScoreSum) { this.allYearScoreSum = allYearScoreSum; }
+    public BigDecimal getAppraisalResultScore() { return appraisalResultScore; }
+    public void setAppraisalResultScore(BigDecimal appraisalResultScore) { this.appraisalResultScore = appraisalResultScore; }
+    public BigDecimal getLastSumScore() { return lastSumScore; }
+    public void setLastSumScore(BigDecimal lastSumScore) { this.lastSumScore = lastSumScore; }
+    public String getLastHrJobFamilyNumber() { return lastHrJobFamilyNumber; }
+    public void setLastHrJobFamilyNumber(String lastHrJobFamilyNumber) { this.lastHrJobFamilyNumber = lastHrJobFamilyNumber; }
+    public Integer getLastJobGradeIndex() { return lastJobGradeIndex; }
+    public void setLastJobGradeIndex(Integer lastJobGradeIndex) { this.lastJobGradeIndex = lastJobGradeIndex; }
+
+    public DynamicObject getLastJobLevel() {
+        return lastJobLevel;
+    }
+
+    public void setLastJobLevel(DynamicObject lastJobLevel) {
+        this.lastJobLevel = lastJobLevel;
+    }
+
+    public Double getYearscoresuma() { return yearscoresuma; }
+    public void setYearscoresuma(Double yearscoresuma) { this.yearscoresuma = yearscoresuma; }
+    public Double getYearscoresumb() { return yearscoresumb; }
+    public void setYearscoresumb(Double yearscoresumb) { this.yearscoresumb = yearscoresumb; }
+    public Double getYearscoresumc() { return yearscoresumc; }
+    public void setYearscoresumc(Double yearscoresumc) { this.yearscoresumc = yearscoresumc; }
+    public Double getYearscoresumd() { return yearscoresumd; }
+    public void setYearscoresumd(Double yearscoresumd) { this.yearscoresumd = yearscoresumd; }
+    public Double getYearscoresume() { return yearscoresume; }
+    public void setYearscoresume(Double yearscoresume) { this.yearscoresume = yearscoresume; }
+    public Double getYearscoresumf() { return yearscoresumf; }
+    public void setYearscoresumf(Double yearscoresumf) { this.yearscoresumf = yearscoresumf; }
+    public Double getYearscoresumg() { return yearscoresumg; }
+    public void setYearscoresumg(Double yearscoresumg) { this.yearscoresumg = yearscoresumg; }
+    public Double getYearscoresumh() { return yearscoresumh; }
+    public void setYearscoresumh(Double yearscoresumh) { this.yearscoresumh = yearscoresumh; }
+    public Double getYearscoresumi() { return yearscoresumi; }
+    public void setYearscoresumi(Double yearscoresumi) { this.yearscoresumi = yearscoresumi; }
+
+    public DynamicObject getLastJobSeq() {
+        return lastJobSeq;
+    }
+
+    public void setLastJobSeq(DynamicObject lastJobSeq) {
+        this.lastJobSeq = lastJobSeq;
+    }
+
+    public JobLevelCalculatorService.RankingResultInfo getRankingResultInfo() {
+        return rankingResultInfo;
+    }
+
+    public void setRankingResultInfo(JobLevelCalculatorService.RankingResultInfo rankingResultInfo) {
+        this.rankingResultInfo = rankingResultInfo;
+    }
+
+    public JobLevelCalculatorService.JobScoreInfo getJobScoreInfo() {
+        return jobScoreInfo;
+    }
+
+    public void setJobScoreInfo(JobLevelCalculatorService.JobScoreInfo jobScoreInfo) {
+        this.jobScoreInfo = jobScoreInfo;
+    }
+
+    public String getZgjbName() {
+        return StringUtils.isNotBlank(zgjbName) ? zgjbName : "无";
+    }
+
+    public void setZgjbName(String zgjbName) {
+        this.zgjbName = zgjbName;
+    }
+
+    public String getZyjndjName() {
+        return StringUtils.isNotBlank(zyjndjName) ? zyjndjName : "无";
+    }
+
+    public void setZyjndjName(String zyjndjName) {
+        this.zyjndjName = zyjndjName;
+    }
+
+    public String getAppraisalResultName() {
+        return appraisalResultName = StringUtils.isNotBlank(appraisalResultName) ? appraisalResultName : "无";
+    }
+
+    public void setAppraisalResultName(String appraisalResultName) {
+        this.appraisalResultName = appraisalResultName;
+    }
+
+    public DynamicObject getConvertLastJobSeq() {
+        return convertLastJobSeq;
+    }
+
+    public void setConvertLastJobSeq(DynamicObject convertLastJobSeq) {
+        this.convertLastJobSeq = convertLastJobSeq;
+    }
+}

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

@@ -0,0 +1,755 @@
+package nckd.jxccl.hr.psms.business;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.constant.StatusEnum;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.enums.AppraisalResultEnum;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 年度调整服务类。
+ * <p>重构目标:逻辑清晰、职责单一、条件判断明确、可维护可测试;保持原始业务逻辑完整性。</p>
+ */
+public class AnnualAdjustmentService {
+
+
+    /**
+     * 生成年度调整记录
+     * @param person    员工
+     * @param beginDate   生效日期
+     * @return 成功返回 null;失败返回错误信息
+     * @note 对应SHR:PersonpositionfilecreateViewListHandler#addNewYear_PersonpositionfileInfo(277~815行)
+     */
+    public DynamicObject addNewYearPersonPositionFileInfo(DynamicObject person, Date beginDate,String remark) {
+        int executeYear = beginDate != null ? DateUtil.getYear(beginDate) : DateUtil.getYear(new Date());
+        //判断当前年是否已执行过年度调整
+        DynamicObject[] personPosFileByYear = PositionStructureHelper.getPersonPosFileByPersonAndState(person.getLong(FormConstant.ID_KEY), new String[]{"3"}, null, new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR, QCP.equals, executeYear));
+        if(personPosFileByYear != null && personPosFileByYear.length > 0){
+            throw new ValidationException(StrFormatter.format("人员【{}】已存在【{}】年的年度调整!", person.getString(FormConstant.NAME_KEY), executeYear));
+        }
+
+        // 1、 初始化上下文并加载基础数据。
+        // 对应SHR:291~339行
+        AdjustmentContext ac = initAndLoad(executeYear, person, beginDate);
+        ac.remark = remark;
+
+        //2、 判断是否为本年首次调整、考核是否已被使用,并根据规则决定是否使用R排名。
+        //对应SHR:341~391行
+        evaluateFirstAndAppraisalUsage(ac);
+
+        // 3、加载上一条记录并校验生效日期。
+        //对应SHR:393~421行
+        loadLastRecordAndValidateBeginDate(ac);
+
+        // 4、处理职位序列(如果是管理序列,则按职能序列进行调整)
+        //对应SHR:521~527行
+        DynamicObject convertJobSeq = JobLevelCalculatorService.handleJobSeq(ac.jobSeq);
+        ac.convertJobSeq = convertJobSeq;
+        DynamicObject convertLastJobSeq = JobLevelCalculatorService.handleJobSeq(ac.data.getLastJobSeq());
+        ac.data.setConvertLastJobSeq(convertLastJobSeq);
+
+        // 5、获取技能/职称分
+        //对应SHR:451~481行
+        JobLevelCalculatorService.JobScoreInfo jobScoreInfo = JobLevelCalculatorService.handleJobScores(convertJobSeq, ac.positionAppointment);
+        ac.data.setJobScoreInfo(jobScoreInfo);
+
+        // 6、计算学历得分并生成说明
+        //对应SHR:423~449行
+        ac.diplomaScore = JobLevelCalculatorService.handleDiplomaScore(ac.lastRecordInfo, ac.positionAppointment, jobScoreInfo);
+        ac.whyDiplomaScore.putAll(jobScoreInfo.whyDiplomaScore);
+
+        // 7、汇总年度积分池与综合分数。
+        //对应SHR:483~519行
+        aggregateScores(ac);
+
+
+        //8.确定目标职级(含有R排名和无R排名两条路径)。
+        //对应SHR:540~702行
+        DynamicObject jobLevel = decideTargetJobGrade(ac);
+
+        // 9、上年度考核结果为“无”时,取最低职级
+        //对应SHR:703~707行
+        if (AppraisalResultEnum.NONE.getCode().equals(ac.data.getAppraisalResultNumber())) {
+            // 考核结果为无时取最低职级
+            ac.adjustType = "8";
+            ac.adjustInt = 0;
+            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+        }
+        if(jobLevel == null){
+            throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+                    ac.personName,
+                    ac.jobSeq.getString(FormConstant.NAME_KEY),
+                    ac.allSumScore.toString(),
+                    ac.data.getZgjbName(),
+                    ac.data.getZyjndjName(),
+                    ac.data.getAppraisalResultName()));
+        }
+
+        JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, beginDate, ac.positionAppointment);
+        ac.data.getRankingResultInfo().allowanceRankMark = jobLevelResult.rankingResultInfo.allowanceRankMark;
+        ac.data.getRankingResultInfo().allowanceRankSel = jobLevelResult.rankingResultInfo.allowanceRankSel;
+
+        //10、构建职位档案
+        return buildPersonPositionFile(ac, jobLevel);
+    }
+
+
+    /**
+     * 初始化上下文并加载基础数据。
+     * @param executeYear 执行年份
+     * @param person 员工
+     * @param beginDate 生效日期
+     * @return: nckd.jxccl.hr.psms.business.AnnualAdjustmentService.AdjustmentContext
+     * @author W.Y.C
+     * @date: 2025/10/08 21:15
+     */
+    private AdjustmentContext initAndLoad(Integer executeYear, DynamicObject person, Date beginDate){
+        AdjustmentContext ac = new AdjustmentContext();
+        ac.executeYear = executeYear;
+        ac.nowYear = executeYear;
+        ac.personId = person.getLong(FormConstant.ID_KEY);
+        ac.personName = person.getString(FormConstant.NAME_KEY);
+        ac.beginDate = beginDate;
+        ac.personInfo = person;
+
+        int lastYear = executeYear - 1;
+        LocalDateTime lastYearDateTime = LocalDateTime.of(lastYear, 1, 1, 0, 0);
+
+
+        //对应SHR:291行;utils.YearContributeScoreBillEntryScoreSumBypersonidAndYear
+        //TODO 1、获取年度贡献积分
+        //yearscoresuma、yearscoresumb、yearscoresumc、yearscoresumd、yearscoresume、yearscoresumf、yearscoresumg、yearscoresumh、yearscoresumi、allyearscoresum、yearscoresuma
+
+        //2、获取上年度考核结果(对应SHR:PersonpositionfileUtils:884~914行)
+        DynamicObject performanceResult = PerformanceManagerHelper.getPerformanceResult(ac.personId, lastYearDateTime);
+        if(performanceResult == null){
+            throw new ValidationException(StrFormatter.format("人员【{}】缺少【{}】年考核结果", ac.personName,lastYear));
+        }
+        AnnualAdjustmentData data = new AnnualAdjustmentData();
+        data.setAppraisalResult(performanceResult);
+        data.setAppraisalResultNumber(performanceResult.getString(FormConstant.ID_KEY));
+        data.setAppraisalResultName(performanceResult.getString(FormConstant.NAME_KEY));
+        data.setAppraisalResultId(performanceResult.getLong(FormConstant.ID_KEY));
+        data.setAppraisalResultScore(performanceResult.getBigDecimal(FormConstant.NCKD_SCORE));
+
+
+        //3.获取员工上年度年度R排名(对应SHR:PersonpositionfileUtils:915~945行)
+        DynamicObject lastPersonPosFile = null;
+        JobLevelCalculatorService.RankingResultInfo rankingInfo = JobLevelCalculatorService.getRankingInfo(ac.personId, ac.personName, beginDate);
+        data.setRankingResultInfo(rankingInfo);
+
+        //4.查询上一条有效年度调整记录(对应SHR:PersonpositionfileUtils:946~997行)
+        DynamicObject[] personPosFileByPersonAndState = PositionStructureHelper.getPersonPosFileByPersonAndState(ac.personId,
+                new String[]{"3","4"},
+                null,null,
+                QueryFieldBuilder.create()
+                        .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR,PositionStructureConstant.MODIFY_TIME_KEY,PositionStructureConstant.NCKD_BEGINDATE).buildOrderArray());
+        if(personPosFileByPersonAndState == null || personPosFileByPersonAndState.length < 1){
+            //没有年度调整记录取初定记录
+            lastPersonPosFile = PositionStructureHelper.getFirstRank(ac.personId);
+            if(lastPersonPosFile == null){
+                throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", ac.personName));
+            }
+            data.setFirstId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
+        }else{
+            lastPersonPosFile = personPosFileByPersonAndState[0];
+            data.setLastId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
+        }
+        if(lastPersonPosFile != null) {
+            //(firstid、lastsumscore、lastdiplomascore、lastrankscore、lastjobstatusscore、lasthrjobfamilynumber、lastjobgradefid、lastjobgradeindex)
+
+
+            data.setLastSumScore(lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE));
+            //lastdiplomascore
+            //lastrankscore
+            //lastjobstatusscore
+            DynamicObject jobSeq = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR);
+            data.setLastHrJobFamilyNumber(jobSeq.getString(FormConstant.NUMBER_KEY));
+            data.setLastJobSeq(jobSeq);
+            DynamicObject jobLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
+            data.setLastJobLevel(jobLevel);
+            data.setLastJobGradeIndex(jobLevel.getInt(FormConstant.JOBLEVELSEQ));
+            data.setLastJobGradeIndex(jobLevel.getInt(FormConstant.JOBLEVELSEQ));
+        }
+
+        //5.获取员工任职信息(对应SHR:PersonpositionfileUtils:998~1009行)
+        PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(ac.personId, beginDate == null ? new Date() : beginDate);
+        DynamicObject perEduExp = positionAppointment.getPerEduExp();
+        if(perEduExp != null){
+            data.setDiplomaId(perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY)));
+            data.setDiplomaScore(perEduExp.getBigDecimal(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.NCKD_SCORE)));
+        }
+        DynamicObject empPosOrgRel = positionAppointment.getEmpPosOrgRel();
+        if(empPosOrgRel != null){
+            data.setPersonDepId(empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
+            data.setHrJobFamilyId(empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY)));
+            data.setHrJobFamilyNumber(empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY)));
+            data.setPositionId(empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR,  FormConstant.ID_KEY)));
+            data.setPositionName(empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR,  FormConstant.NAME_KEY)));
+            data.setPersonName(empPosOrgRel.getString(String.join(".", FormConstant.EMPLOYEE_KEY,  FormConstant.NAME_KEY)));
+            // data.setHrOrgUnitId();
+        }else{
+            throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", ac.personName, DateUtil.format(beginDate == null ? new Date() : beginDate,DateUtil.NORM_DATE_PATTERN)));
+        }
+        DynamicObject perProTitle = positionAppointment.getPerProTitle();
+        if(perProTitle != null){
+            data.setRankName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
+            data.setZgjbId(perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY)));
+            data.setZgjbNumber(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY)));
+            data.setZgjbName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
+            data.setZgjbScore(perProTitle.getBigDecimal(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE)));
+        }
+        DynamicObject perOcpQual = positionAppointment.getPerOcpQual();
+        if(perOcpQual != null){
+            data.setJobStatusName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
+            data.setZyjndjId(perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY)));
+            data.setZyjndjNumber(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY)));
+            data.setZyjndjName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
+            data.setZyjndjScore(perOcpQual.getBigDecimal(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE)));
+        }
+        if (data.getHrJobFamilyId() == null || data.getHrJobFamilyId() == 0) {
+            throw new ValidationException(StrFormatter.format("无职位序列,请检查当前人员【{}】任职的岗位【{}】是否有职位序列",ac.personName,data.getPositionName()));
+        }
+        ac.positionAppointment = positionAppointment;
+        long jobSeq = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+        ac.jobSeq = BusinessDataServiceHelper.loadSingle(jobSeq,FormConstant.HBJM_JOBSEQHR);
+
+
+        // 对应SHR:307~322行
+        // 判断R位次存在性
+        if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().topRank == null) {
+            ac.keep = true;
+        }
+        if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().allowanceRankPercent <= 0) {
+            ac.haveRp = false;
+        }
+
+        // 对应SHR:323~339行
+        // 考核结果对应升降级逻辑
+        if (AppraisalResultEnum.EXCELLENT.getCode().equals(ac.data.getAppraisalResultNumber())) {
+            ac.minusByAppraisal = 1;
+            ac.whyAdjust.append("【考核结果优秀】升1级");
+        } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
+            ac.minusByAppraisal = -1;
+            ac.whyAdjust.append("【考核结果基本合格】降1级");
+        } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
+            ac.minusByAppraisal = -2;
+            ac.whyAdjust.append("【考核结果不合格】降2级");
+        }
+        ac.data = data;
+        return ac;
+    }
+
+    /**
+     * 判断是否为本年首次调整、考核是否已被使用,并根据规则决定是否使用R排名。
+     * @param ac 上下文对象
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/10/08 22:12
+     */
+    private void evaluateFirstAndAppraisalUsage(AdjustmentContext ac) {
+
+        DynamicObject nowYearPersonPosFile = PositionStructureHelper.getLatsPersonPosFileByPerson(ac.personId, new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR, QCP.equals, ac.nowYear));
+
+        boolean isyearfirstdo = false;
+        if (nowYearPersonPosFile != null) {
+            DynamicObject nowYearAppraisalResult = nowYearPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_APPRAISALRESULT);
+            String nowYearAppraisalResultNumber = nowYearAppraisalResult.getString(FormConstant.NUMBER_KEY);
+            String appraisalResultNumber = ac.data.getAppraisalResultNumber();
+            if (!StringUtils.equals(nowYearAppraisalResultNumber, appraisalResultNumber)) {
+                throw new ValidationException(StrFormatter.format("人员【{}】,上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】记录才能继续操作 !", ac.personName,ac.nowYear));
+            }
+            LocalDateTime nowYearDateTime = LocalDateTime.of(ac.nowYear, 1, 1, 0, 0);
+            ac.useAppraisalresult = JobLevelCalculatorService.useAppraisalResult(ac.personId, DateUtil.toDate(nowYearDateTime));
+            if (ac.useAppraisalresult && ac.minusByAppraisal != 0) {
+                ac.whyAdjust.append(" 非").append(ac.nowYear)
+                        .append("年度首次调整,且升降【考核结果】已被使用,不需要考虑R排名,保级处理");
+                ac.haveRp = false;
+                ac.keep = true;
+            }
+        } else {
+            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度首次调整,【考核结果】未被使用");
+            isyearfirstdo = true;
+        }
+
+        if (ac.useAppraisalresult) {
+            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】已被使用,保级处理");
+            isyearfirstdo = false;
+            ac.keep = true;
+        } else {
+            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】未被使用");
+            ac.keep = false;
+            isyearfirstdo = true;
+            if (ac.minusByAppraisal == 0) {
+                ac.whyAdjust.append("【考核结果】为保级,");
+                Double p = ac.data.getRankingResultInfo().allowanceRankPercent;
+                if (p == null || p <= 0) {
+                    ac.whyAdjust.append("无R排名");
+                    ac.haveRp = false;
+                } else {
+                    ac.whyAdjust.append("使用R排名进行计算 ");
+                    ac.haveRp = true;
+                }
+            }
+        }
+
+        ac.isYearFirstDo = isyearfirstdo;
+    }
+
+    /**
+     * 加载上一条记录并校验生效日期。
+     * @param ac 上下文
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/10/08 23:13
+     */
+    private void loadLastRecordAndValidateBeginDate(AdjustmentContext ac) {
+        ac.lastRecordId = ac.data.getLastId() != null && ac.data.getLastId() != 0 ? ac.data.getLastId() : ac.data.getFirstId();
+        if (ac.lastRecordId == null || ac.lastRecordId == 0) {
+            throw new ValidationException(StrFormatter.format("人员【{}】,缺少职位及积分初定信息,请先完成初定,第二年再操作年度调整!", ac.personName));
+        }
+        ac.lastRecordInfo = BusinessDataServiceHelper.loadSingle(ac.lastRecordId, PositionStructureConstant.PERSONPOSFILE_ENTITYID);
+
+        if (ac.beginDate != null
+                && ac.lastRecordInfo != null
+                && ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE) != null) {
+            Date lastBegin = ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE);
+            if (ac.beginDate.before(lastBegin)) {
+                throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为最近调整时间为【{}】,不能早于最近一次职位调整时间。", ac.personName,DateUtil.format(lastBegin,DateUtil.NORM_DATE_PATTERN)));
+            }
+        }
+    }
+
+
+    /**
+     * 汇总年度积分池与综合分数。
+     * @param ac 上下文对象
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/10/08 23:46
+     */
+    private void aggregateScores(AdjustmentContext ac) {
+        BigDecimal allyearscoresum =
+                ac.data.getAllYearScoreSum() == null ? BigDecimal.ZERO : ac.data.getAllYearScoreSum();
+        System.out.println("上年所有贡献单据分数之和" + allyearscoresum);
+
+        BigDecimal appraisalresultscore =
+                ac.data.getAppraisalResultScore() == null ? BigDecimal.ZERO : ac.data.getAppraisalResultScore();
+        System.out.println("上年年度考核得分" + appraisalresultscore);
+
+        ac.lastYearContributeScore = allyearscoresum.add(appraisalresultscore);
+        System.out.println("上年度贡献综合评价分" + ac.lastYearContributeScore);
+
+        ac.addYearContributeScore = ac.lastYearContributeScore.multiply(new BigDecimal("0.1"));
+        System.out.println("年度新增的贡献积分" + ac.addYearContributeScore);
+
+        BigDecimal lastsum = ac.data.getLastSumScore() == null ? BigDecimal.ZERO : ac.data.getLastSumScore();
+        System.out.println("上年累计积分池的分" + lastsum);
+
+        ac.sumScore = lastsum.add(ac.addYearContributeScore);
+        System.out.println("累计积分池的分" + ac.sumScore);
+
+
+        ac.allSumScore = ac.sumScore
+                .add(ac.diplomaScore)
+                .add(ac.data.getJobScoreInfo().perProTitleScore)
+                .add(ac.data.getJobScoreInfo().quaLevelScore);
+        System.out.println("累计总积分" + ac.allSumScore);
+    }
+
+    /**
+     * 确定目标职级(含有R排名和无R排名两条路径)。
+     * @param ac 上下文对象
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @note 对应原 PersonpositionfilecreateViewListHandler:540~707 行
+     * @author W.Y.C
+     * @date: 2025/10/10 14:17
+     */
+    private DynamicObject decideTargetJobGrade(AdjustmentContext ac) {
+
+        //上一条职位档案的职位序列(如果是管理序列,则转换为职能序列)
+        String newJobSeqNumber = ac.convertJobSeq.getString(FormConstant.NUMBER_KEY);
+        String newLastJobSeqNumber = ac.data.getConvertLastJobSeq().getString(FormConstant.NUMBER_KEY);
+        if (!newJobSeqNumber.equalsIgnoreCase(newLastJobSeqNumber)) {
+            //序列发生变化
+            throw new ValidationException(StrFormatter.format("人员【{}】,序列发生变化!请先到【员工职位档案调整】中进行操作", ac.personName));
+        }
+
+        if (ac.haveRp) {
+            return decideWithR(ac);
+        } else {
+            return decideWithoutR(ac);
+        }
+    }
+
+    /**
+     * 有 R 排名时的定位路径。
+     * @param ac 上下文对象
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @author W.Y.C
+     * @date: 2025/10/10 14:18
+     */
+    private DynamicObject decideWithR(AdjustmentContext ac) {
+        DynamicObject jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
+        if(jobLevel == null){
+            throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+                    ac.personName,
+                    ac.jobSeq.getString(FormConstant.NAME_KEY),
+                    ac.allSumScore.toString(),
+                    ac.data.getZgjbName(),
+                    ac.data.getZyjndjName(),
+                    ac.data.getAppraisalResultName()));
+        }
+        int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
+        System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
+
+        Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.convertJobSeq);
+        int newjobgradeindex = ac.data.getLastJobGradeIndex();
+
+        // 补充根据R排名确定是否升级
+        if (ac.minusByAppraisal == 0) {
+            // 如果考评结果不需要升降级则验证R排名
+            if (!ac.keep) {
+                newjobgradeindex = JobLevelCalculatorService.getnewjobgradeindexByrank(ac.data.getLastJobGradeIndex(), newjobgradeindex, ac.data.getRankingResultInfo(), ac.convertJobSeq);
+            }
+
+            if (newjobgradeindex > jobGradeindex) {
+                newjobgradeindex = jobGradeindex;
+            }
+        } else {
+            // 考核结果需要升降级则直接执行
+            newjobgradeindex = ac.data.getLastJobGradeIndex() + ac.minusByAppraisal;
+            if (jobGradeindex <= newjobgradeindex) {
+                newjobgradeindex = jobGradeindex;
+            }
+        }
+
+        // 确定是否超出总分最高任命职级
+        DynamicObject maxJobLevel = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore);
+        int personMaxjobgradeindex = maxJobLevel.getInt(FormConstant.JOBLEVELSEQ);
+        // 超出总分能任命最高职职级则该最高职级就是要任命的职级
+        if (newjobgradeindex > personMaxjobgradeindex) {
+            newjobgradeindex = personMaxjobgradeindex;
+        }
+
+        // 确定是否超出该职称等级或技能等级最高任命职级
+        DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
+        int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
+        // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
+        if (newjobgradeindex > maxJobGradeIndex) {
+            newjobgradeindex = maxJobGradeIndex;
+        }
+
+        if((ac.data.getZgjbId() == null || ac.data.getZyjndjId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
+            //如果没有聘任则取最低职级
+            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+            if(jobLevel != null) {
+                newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
+            }
+        }else if(jobLevelByJobSeqMap.get(newjobgradeindex) != null){
+            jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
+        }else{
+            //兜底;如果没有获取到合适的职级则取最低级
+            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+            if(jobLevel != null) {
+                newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
+            }
+        }
+
+
+        if((ac.data.getZgjbId() == null || ac.data.getZyjndjId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
+            //无聘任
+            ac.adjustType = "7";
+        }else if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
+            //保级
+            ac.adjustType = "1";
+        }else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
+            //升级
+            ac.adjustType = "2";
+        } else {
+            //降级
+            ac.adjustType = "0";
+        }
+        // 升降级数
+        ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
+
+        return jobLevel;
+
+    }
+
+    /**
+     * 无 R 排名时的定位路径。
+     * @param ac 上下文对象
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @author W.Y.C
+     * @date: 2025/10/10 14:18
+     */
+    private DynamicObject decideWithoutR(AdjustmentContext ac) {
+        // String JobGrade = "";
+        DynamicObject jobLevel = null;
+
+        if (ac.minusByAppraisal == 0 && ac.keep) {
+            System.out.println("绩效分组排名,没有勾选享受职位津贴,保持原职级不变,没有则为最低档");
+            ac.whyAdjust.append("绩效分组排名,没有勾选享受职位津贴,保持原职级不变");
+            jobLevel = ac.data.getLastJobLevel();
+            ac.adjustType = "0";
+
+            if (jobLevel == null) {
+                jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+            }
+        } else {
+            jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
+            if(jobLevel == null){
+                throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+                        ac.personName,
+                        ac.jobSeq.getString(FormConstant.NAME_KEY),
+                        ac.allSumScore.toString(),
+                        ac.data.getZgjbName(),
+                        ac.data.getZyjndjName(),
+                        ac.data.getAppraisalResultName()));
+            }
+
+            int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
+            System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
+
+            //这里不知道为什么用上一档案的序列?这里沿用SHR的逻辑,避免业务错误
+            Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.data.getConvertLastJobSeq());
+
+            int newjobgradeindex = ac.data.getLastJobGradeIndex();
+
+            if (ac.isYearFirstDo) {
+                newjobgradeindex += ac.minusByAppraisal;
+            }
+
+            if (jobGradeindex <= newjobgradeindex) {
+                newjobgradeindex = jobGradeindex;
+            }
+
+            // 确定是否超出该职称等级或技能等级最高任命职级
+            DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
+            int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
+            // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
+            if (newjobgradeindex > maxJobGradeIndex) {
+                newjobgradeindex = maxJobGradeIndex;
+            }
+
+            // 如果最后超出最低排名则按最低排名
+            if (jobLevelByJobSeqMap.get(newjobgradeindex) != null) {
+                jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
+            } else {
+                jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+                if(jobLevel != null) {
+                    newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
+                }
+            }
+
+            // 设置调整类型和调整级别数
+            if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
+                //保级
+                ac.adjustType = "1";
+            } else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
+                //升级
+                ac.adjustType = "2";
+            } else {
+                //降级
+                ac.adjustType = "0";
+            }
+            // 升降级数
+            ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
+        }
+
+        return jobLevel;
+    }
+
+
+    /**
+     * 构建职位档案
+     * @param ac 上下文对象
+     * @param jobLevel 目标职级对象
+     * @return: kd.bos.dataentity.entity.DynamicObject
+     * @note 对应原 710~803行
+     * @author W.Y.C
+     * @date: 2025/10/10 15:44
+     */
+    private DynamicObject buildPersonPositionFile(AdjustmentContext ac, DynamicObject jobLevel) {
+
+        DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
+                PositionStructureConstant.PERSONPOSFILE_ENTITYID);
+
+        DynamicObject empPosOrgRel = ac.positionAppointment.getEmpPosOrgRel();
+        newPersonPosFile.set(PositionStructureConstant.NCKD_PERSON, ac.personInfo);
+        DynamicObject company = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+        company.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.COMPANY_KEY,FormConstant.ID_KEY)));
+        newPersonPosFile.set(PositionStructureConstant.USEORG_KEY, company);
+        DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+        dep.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
+        newPersonPosFile.set(PositionStructureConstant.ORG_KEY, dep);
+        newPersonPosFile.set(PositionStructureConstant.CREATEORG_KEY, dep);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_TYPESTATE, "3");
+        newPersonPosFile.set(PositionStructureConstant.NCKD_EXECUTEYEAR, ac.executeYear);
+        Long positionId = empPosOrgRel.getLong(String.join(".",FormConstant.POSITION_KEY,FormConstant.ID_KEY));
+        DynamicObject position = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBPM_POSITIONHR);
+        position.set(FormConstant.ID_KEY, positionId);
+        Long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+        DynamicObject jobSeq = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBJM_JOBSEQHR);
+        jobSeq.set(FormConstant.ID_KEY, jobSeqId);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSEQHR, jobSeq);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_POSITIONHR, position);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_RANKNAME, "无".equalsIgnoreCase(ac.data.getRankName()) ? null : ac.data.getRankName());
+        DynamicObject proTitleLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_PROTITLELEVEL);
+        proTitleLevel.set(FormConstant.ID_KEY, ac.data.getZgjbId());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_PROTITLELEVEL, proTitleLevel);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSNAME, "无".equalsIgnoreCase(ac.data.getZyjndjName()) ? null : ac.data.getZyjndjName());
+        DynamicObject ocpQualLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_OCPQUALLEVEL);
+        ocpQualLevel.set(FormConstant.ID_KEY, ac.data.getZyjndjId());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_OCPQUALLEVEL, ocpQualLevel);
+        DynamicObject diploma = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_DIPLOMA);
+        diploma.set(FormConstant.ID_KEY, ac.data.getDiplomaId());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMA, diploma);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBLEVELHR, jobLevel);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_LASTPERSONPOSFILE, ac.lastRecordInfo);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMASCORE, ac.diplomaScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_WHYDIPLOMASCORE, ac.whyDiplomaScore.toString());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_RANKSCORE, ac.data.getJobScoreInfo().perProTitleScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSSCORE, ac.data.getJobScoreInfo().quaLevelScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLSUMSCORE, ac.allSumScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_SUMSCORE,ac.sumScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_BEGINDATE, ac.beginDate);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTINT, ac.adjustInt);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_FIRSTRANK, EnableEnum.NO.getCode());
+
+        newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANK, ac.data.getRankingResultInfo().topRank);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANK, ac.data.getRankingResultInfo().allowanceRank);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANKPERCENT, ac.data.getRankingResultInfo().topRankPercent);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKMARK, ac.data.getRankingResultInfo().allowanceRankMark);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKSEL, ac.data.getRankingResultInfo().allowanceRankSel);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKPCT, ac.data.getRankingResultInfo().allowanceRankPercent);
+        //上年度考核结果
+        newPersonPosFile.set(PositionStructureConstant.NCKD_APPRAISALRESULT, ac.data.getAppraisalResult());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_RESULTSCORE, ac.data.getAppraisalResultScore());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTTYPE, ac.adjustType);
+
+        newPersonPosFile.set(PositionStructureConstant.NCKD_DISABLE, EnableEnum.NO.getCode());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ISCURRENTNEWEST, EnableEnum.YES.getCode());
+        newPersonPosFile.set(PositionStructureConstant.STATUS, StatusEnum.C.toString());
+        newPersonPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMA, ac.data.getYearscoresuma());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMB, ac.data.getYearscoresumb());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMC, ac.data.getYearscoresumc());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMD, ac.data.getYearscoresumd());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUME, ac.data.getYearscoresume());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMF, ac.data.getYearscoresumf());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMG, ac.data.getYearscoresumg());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMH, ac.data.getYearscoresumh());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMI, ac.data.getYearscoresumi());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_LYRCONTRIBSCORE, ac.lastYearContributeScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLYEARSCORESUM, ac.data.getAllYearScoreSum());
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE, ac.addYearContributeScore);
+        // 备注
+        newPersonPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, ac.remark);
+
+        return newPersonPosFile;
+    }
+    /**
+     * 内部运行时上下文。
+     */
+    private static class AdjustmentContext {
+        /** 执行年份 */
+        Integer executeYear;
+
+        /** 当前年份 */
+        Integer nowYear;
+
+        /** 调整生效日期 */
+        Date beginDate;
+
+        /** 员工ID */
+        Long personId;
+
+        /** 员工Name */
+        String personName;
+
+        /** 员工信息对象 */
+        DynamicObject personInfo;
+
+        /** 强类型数据对象(替代原 selMap) */
+        AnnualAdjustmentData data;
+
+        /** 是否有R位次(allowancerankpercent≤0则为false) */
+        boolean haveRp = true;
+
+        /** 是否保级(缺R位次时为true) */
+        boolean keep = false;
+
+        /** 本年考核是否已使用 */
+        boolean useAppraisalresult = false;
+
+        /** 是否本年首次调整 */
+        boolean isYearFirstDo = false;
+
+        /** 考核升降级标记(+1升/-1降/-2降两级) */
+        int minusByAppraisal = 0;
+
+        /** 固定类型(默认3) */
+        String typestate = "3";
+
+        /** 上一条档案记录ID */
+        Long lastRecordId;
+
+        /** 上一条记录对象 */
+        DynamicObject lastRecordInfo;
+
+        /** 上年度贡献综合评价分 */
+        BigDecimal lastYearContributeScore = BigDecimal.ZERO;
+        /** 年度新增贡献积分 */
+        BigDecimal addYearContributeScore = BigDecimal.ZERO;
+        /** 当前累计积分池 */
+        BigDecimal sumScore = BigDecimal.ZERO;
+        /** 总分(池+学历+职称/技能) */
+        BigDecimal allSumScore = BigDecimal.ZERO;
+
+        /** 学历得分 */
+        BigDecimal diplomaScore = BigDecimal.ZERO;
+        /** 学历得分说明 */
+        Map<String, String> whyDiplomaScore = new LinkedHashMap<>();
+
+        /** 职称得分 */
+        BigDecimal rankScore = BigDecimal.ZERO;
+        /** 技能得分 */
+        BigDecimal jobStatusScore = BigDecimal.ZERO;
+
+        /** 调整说明 */
+        StringBuffer whyAdjust = new StringBuffer();
+
+        /** 升降类型:0降/1保/2升/7固定/8无考核结果 */
+        String adjustType;
+        /** 升降数量:正数升、负数降、0保 */
+        Integer adjustInt;
+
+        PositionAppointmentBO positionAppointment;
+        /**当前任职的序列(未转换前,这里转换指的是:如果是管理序列,则按职能序列进行调整)*/
+        DynamicObject jobSeq;
+        /**当前任职的序列(转换后,这里转换指的是:如果是管理序列,则按职能序列进行调整)*/
+        DynamicObject convertJobSeq;
+
+        String remark;
+    }
+}

+ 134 - 91
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/business/JobLevelCalculatorService.java

@@ -18,6 +18,7 @@ import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.enums.JobSeqEnum;
 import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
@@ -25,6 +26,7 @@ import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 import org.apache.commons.lang3.StringUtils;
 
@@ -33,8 +35,10 @@ import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -49,37 +53,37 @@ public class JobLevelCalculatorService {
     protected final static Log logger = LogFactory.getLog(JobLevelCalculatorService.class);
 
     /** 职称等级与技能等级映射关系*/
-    public static final Map<String, String> TechPostLevelNumberTojobstatusNumberMap = new HashMap<>();
+    public static final Map<String, String> TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP = new HashMap<>();
 
     static {
         // 正高级---高级技师
-        TechPostLevelNumberTojobstatusNumberMap.put("001", "1");
+        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("001", "1");
         // 副高级---高级技师
-        TechPostLevelNumberTojobstatusNumberMap.put("002", "1");
+        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("002", "1");
         // 中级---技师
-        TechPostLevelNumberTojobstatusNumberMap.put("003", "2");
+        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("003", "2");
         // 助理级---高级工
-        TechPostLevelNumberTojobstatusNumberMap.put("004", "3");
+        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("004", "3");
     }
 
 
-
     /**
      * 计算员工职级(单条生成年度调记录、新建调整查询职级、新建动态调整)
+     * 对应SHR:PersonpositionfileFluctuationListHandler#getJobGradeid
      * @param positionAppointment 人员最新信息(最新学历、最新聘任....)
      * @return: java.lang.String
      * @author W.Y.C
      * @date: 2025/09/19 14:19
      */
-    public static JobLevelResult calculateJobLevel(DynamicObject person, Date date,  DynamicObject positionAppointment) {
+    public static JobLevelResult calculateJobLevel(DynamicObject person, Date date,  PositionAppointmentBO positionAppointment) {
         //资格三要素:
         // • 职称/技能等级:硬性门槛(如工程师职称、技师等级)
-        // • 职业生涯累计积分:量化能力总分(由学历、职称、年度贡献分累计)
+        // • 积分:量化能力总分(由学历、职称、年度贡献分累计)
         // • 年度绩效考核结果(R排名):决定升降的关键绩效指标
         Long personId = person.getLong(FormConstant.ID_KEY);
         String personName = person.getString(FormConstant.NAME_KEY);
         //对应SHR:hrjobfamilyid
-        long jobSeqId = positionAppointment.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY));
+        long jobSeqId = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY));
         DynamicObject jobSeq = BusinessDataServiceHelper.loadSingle(jobSeqId, FormConstant.HBJM_JOBSEQHR);
         JobLevelResult jobLevelResult = new JobLevelResult();
         //对应SHR:JobGradeid
@@ -131,7 +135,7 @@ public class JobLevelCalculatorService {
 
         // 5.是否首次聘任
         //对应SHR:fistPR;922~926行
-        boolean firstPR = isFirstPR(personId);
+        boolean firstPr = isFirstPr(personId);
 
         // 6. 是否已使用过考核结果
         //对应SHR:useappraisalresult;926行
@@ -139,7 +143,7 @@ public class JobLevelCalculatorService {
 
         // 7. 计算降级数
         //对应SHR:minuspersonappraisal;930~945行
-        int minusPersonAppraisal = calcMinusPersonAppraisal(appraisalResultNumber, firstPR, usedAppraisalResult, jobLevelResult);
+        int minusPersonAppraisal = calcMinusPersonAppraisal(appraisalResultNumber, firstPr, usedAppraisalResult, jobLevelResult);
 
         // 9.如果是管理序列,则按职能序列进行调整
         //对应SHR:hrjobfamilynumber;989~993
@@ -151,7 +155,7 @@ public class JobLevelCalculatorService {
 
         // 10. 处理职称/技能等级积分
         // 对应SHR:1015~1036
-        JobScoreInfo jobScoreInfo = handleJobScores(jobSeq, currentPersonPosFile, positionAppointment);
+        JobScoreInfo jobScoreInfo = handleJobScores(jobSeq, positionAppointment);
 
         // 8. 处理学历积分
         //对应SHR:DiplomaScore;947~978行
@@ -165,22 +169,12 @@ public class JobLevelCalculatorService {
         jobScoreInfo.allSumScore = allSumScore;
 
         // 11. 获取序列对应的职级
-        DynamicObject[] jobLevelByJobSeq = getJobLevelByJobSeq(jobSeq);
         //对应SHR:jobgradeMap
-        Map<Integer, DynamicObject> jobLevelMap = new HashMap<>();
-        if (jobLevelByJobSeq != null) {
-            String jobLevelSeqKey = FormConstant.JOBLEVELSEQ;
-            jobLevelMap = Arrays.stream(jobLevelByJobSeq)
-                    .collect(Collectors.toMap(
-                            dynamicObject -> dynamicObject.getInt(jobLevelSeqKey),
-                            dynamicObject -> dynamicObject,
-                            (existing, replacement) -> existing)
-                    );
-        }
+        Map<Integer, DynamicObject> jobLevelMap = getJobLevelByJobSeqMap(jobSeq);
 
         // 12. 根据不同情况计算职级ID
         //对应SHR:JobGradeInfo;1044~1071行
-        DynamicObject jobLevel = calculateJobGradeId(jobSeq,jobScoreInfo, jobSeqInfo,allSumScore,currentJobLevelIndex,minusPersonAppraisal,firstPR,jobLevelResult);
+        DynamicObject jobLevel = calculateJobGradeId(jobSeq,jobScoreInfo, jobSeqInfo,allSumScore,currentJobLevelIndex,minusPersonAppraisal, firstPr,jobLevelResult);
         //对应SHR:newjobgradeindex
         int jobLevelIndex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
         // 13. 处理排名相关信息
@@ -216,11 +210,11 @@ public class JobLevelCalculatorService {
 
         // 16. 根据考核结果使用情况处理职级计算
         //对应SHR行:1149~1180行
-        jobLevelIndex = handleAppraisalUsage(jobSeq,rankingInfo,jobLevelIndex,lastAppointmentJobGradeIndex,currentJobLevelIndex,minusPersonAppraisal,firstPR,usedAppraisalResult,jobLevelResult);
+        jobLevelIndex = handleAppraisalUsage(jobSeq,rankingInfo,jobLevelIndex,lastAppointmentJobGradeIndex,currentJobLevelIndex,minusPersonAppraisal, firstPr,usedAppraisalResult,jobLevelResult);
 
         // 17. 处理首次聘任情况 & 处理聘任相关限制
         //对应SHR:1182~1295行
-        jobLevelIndex = handleAppointmentRestrictions(personId, firstPR,usedAppraisalResult,jobSeq,jobSeqId,currentJobSeqId,currentJobSeqNumber,jobScoreInfo,currentJobLevelIndex,jobLevelIndex,allSumScore,jobLevelResult,minusPersonAppraisal,currentPersonPosFile);
+        jobLevelIndex = handleAppointmentRestrictions(personId, firstPr,usedAppraisalResult,jobSeq,jobSeqId,currentJobSeqId,currentJobSeqNumber,jobScoreInfo,currentJobLevelIndex,jobLevelIndex,allSumScore,jobLevelResult,minusPersonAppraisal,currentPersonPosFile);
 
         // 18.最终确定职级ID
         //对应SHR行:1297~1320行
@@ -354,7 +348,7 @@ public class JobLevelCalculatorService {
         return JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) &&
                 StringUtils.isBlank(ocpQualLevel) &&
                 StringUtils.isNotBlank(proTitleLevelNumber) &&
-                TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber) == null;
+                TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber) == null;
     }
 
 
@@ -368,7 +362,7 @@ public class JobLevelCalculatorService {
      * @author W.Y.C
      * @date: 2025/09/20 15:10
      */
-    private static boolean useAppraisalResult(Long personId, Date date) {
+    public static boolean useAppraisalResult(Long personId, Date date) {
         //对应SHR代码:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#useAppraisalresult
 
         //查询条件:查询某个人员职位档案非失效状态 并且 (初定 或者 类型状态为:年度调整3;职位调动4) 并且 职称分数或技能分数>0的数据
@@ -507,7 +501,7 @@ public class JobLevelCalculatorService {
      * @author W.Y.C
      * @date: 2025/09/20 18:20
      */
-    private static boolean isFirstPR(long personId) {
+    private static boolean isFirstPr(long personId) {
         QFilter rankScore = new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_RANKSCORE, QCP.large_than, 0));
         QFilter jobScore = new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.is_notnull, null).and(new QFilter(PositionStructureConstant.NCKD_JOBSTATUSSCORE, QCP.large_than, 0));
         QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
@@ -574,16 +568,16 @@ public class JobLevelCalculatorService {
     /**
      * 计算考核结果对职级的影响
      * @param appraisalResultNumber 考核结果
-     * @param firstPR 是否首次聘任
+     * @param firstPr 是否首次聘任
      * @param usedAppraisalResult 是否使用考核结果
      * @param jobLevelResult 职级计算结果
      * @return: int
      * @author W.Y.C
      * @date: 2025/09/20 21:16
      */
-    private static int calcMinusPersonAppraisal(String appraisalResultNumber, boolean firstPR,boolean usedAppraisalResult,JobLevelResult jobLevelResult) {
+    private static int calcMinusPersonAppraisal(String appraisalResultNumber, boolean firstPr, boolean usedAppraisalResult, JobLevelResult jobLevelResult) {
         int minuspersonappraisal = 0;
-        if (!usedAppraisalResult || firstPR) {
+        if (!usedAppraisalResult || firstPr) {
             if (AppraisalResultEnum.EXCELLENT.getCode().equals(appraisalResultNumber)) {
                 // 优秀 -> 升一级(动态调整阶段允许升)
                 minuspersonappraisal = -1;
@@ -614,17 +608,22 @@ public class JobLevelCalculatorService {
      * @author W.Y.C
      * @date: 2025/09/20 22:06
      */
-    public static BigDecimal handleDiplomaScore(DynamicObject currentPersonPosFileByPerson, DynamicObject positionAppointment,JobScoreInfo scoreInfo) {
+    public static BigDecimal handleDiplomaScore(DynamicObject currentPersonPosFileByPerson, PositionAppointmentBO positionAppointment,JobScoreInfo scoreInfo) {
 
+        scoreInfo.whyDiplomaScore.put("基准时间", DateUtil.format(new Date(),DateUtil.NORM_DATETIME_PATTERN));
         //获取上一笔职位档案变动记录学历信息
         //对应SHR:lastDiplomaInfo
         DynamicObject diploma = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
+
         //对应SHR:lastDiplomaInfo.getId()
         long diplomaId = diploma.getLong(FormConstant.ID_KEY);
         //对应SHR:lastDiplomaInfo.getName()
         String diplomaName = diploma.getString(FormConstant.NAME_KEY);
         //对应SHR:lastDiplomaInfo.getDescription()
         BigDecimal diplomaScore = diploma.getBigDecimal(FormConstant.NCKD_SCORE);
+        scoreInfo.whyDiplomaScore.put("上一笔记录学历", diplomaName);
+        scoreInfo.whyDiplomaScore.put("上一笔记录学历分数", ConvertUtil.toStr(diplomaScore));
+        scoreInfo.whyDiplomaScore.put("学历" + diplomaName + "最新配置分数", ConvertUtil.toStr(diplomaScore));
         scoreInfo.diplomaId = diplomaId;
         scoreInfo.diplomaScore = diplomaScore;
         //获取上一笔职位档案变动记录学历积分
@@ -632,27 +631,42 @@ public class JobLevelCalculatorService {
         BigDecimal lastDiplomaScore = currentPersonPosFileByPerson.getBigDecimal(PositionStructureConstant.NCKD_DIPLOMASCORE);
 
 
+        DynamicObject perEduExp = positionAppointment.getPerEduExp();
+        // 对应SHR:Diploma
+        Long currentDiplomaId = null;
+        String currentDiplomaName = "";
+        BigDecimal currentDiplomaScore = BigDecimal.ZERO;
+        if (perEduExp != null) {
+            currentDiplomaId = perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
+            // 对应SHR:newDiplomaInfo.getName()
+            currentDiplomaName = perEduExp.getString(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.NAME_KEY));
+            // 对应SHR:DiplomaScore
+            // 人员最新学历的积分
+            currentDiplomaScore = perEduExp.getBigDecimal(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.NCKD_SCORE));
+            scoreInfo.whyDiplomaScore.put("最新学历", currentDiplomaName);
+            scoreInfo.whyDiplomaScore.put("最新学历配置分", ConvertUtil.toStr(currentDiplomaScore));
+        }
+
 
-        //对应SHR:Diploma
-        Long currentDiplomaId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
-        //对应SHR:newDiplomaInfo.getName()
-        String currentDiplomaName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.NAME_KEY));
-        //对应SHR:DiplomaScore
-        //人员最新学历的积分
-        BigDecimal currentDiplomaScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.NCKD_SCORE));
-        //对应SHR:DiplomaScore
+        // 对应SHR:DiplomaScore
         BigDecimal resultScore = currentDiplomaScore;
 
         logger.info("原学历:ID【{}】,名称【{}】,积分【{}】;最新学历:ID【{}】,名称【{}】,积分【{}】", diplomaId,diplomaName,diplomaScore,currentDiplomaId,currentDiplomaName,currentDiplomaScore);
-        if (currentDiplomaId.equals(diplomaId)) {
+        if (Objects.equals(currentDiplomaId,diplomaId)) {
             //学历不变,分数保持不变
             resultScore = lastDiplomaScore;
             logger.info("");
+            scoreInfo.whyDiplomaScore.put("学历没有变化", "分数保持不变");
         } else {
             // 分差 = 最新学历配置分 - 上一笔学历的最新配置分
             // 本次学历分 = 上一笔学历分 + 分差
             BigDecimal diff = resultScore.subtract(diplomaScore);
             resultScore = lastDiplomaScore.add(diff);
+            scoreInfo.diplomaId = currentDiplomaId;
+            scoreInfo.diplomaScore = currentDiplomaScore;
+            scoreInfo.whyDiplomaScore.put("学历变化",
+                    "学历分 = 上一笔记录学历分数(" + lastDiplomaScore + ") + (最新配置分("
+                            + ConvertUtil.toStr(currentDiplomaScore) + ") - 上一学历最新配置分(" + ConvertUtil.toStr(diplomaScore) + "))");
         }
         return resultScore;
     }
@@ -691,37 +705,50 @@ public class JobLevelCalculatorService {
      * 处理员工职称和技能等级分数
      * 根据职位序列类型(技能序列/非技能序列)确定采用职称等级分还是技能等级分
      * @param newJobSeq 最新职位序列
-     * @param currentPersonPosFileByPerson 最近一次职位档案
      * @param positionAppointment 人员最信息(最新学历)
      * @return: nckd.jxccl.hr.psms.business.JobLevelCalculatorService.JobScoreInfo
      * @author W.Y.C
      * @date: 2025/09/22 09:39
      */
-    public static JobScoreInfo handleJobScores(DynamicObject newJobSeq,
-                                         DynamicObject currentPersonPosFileByPerson,DynamicObject positionAppointment) {
+    public static JobScoreInfo handleJobScores(DynamicObject newJobSeq,PositionAppointmentBO positionAppointment) {
         //对应SHR:hrjobfamilynumber
         String newJobSeqNumber = newJobSeq.getString(FormConstant.NUMBER_KEY);
 
+        // 当前人员最新职称等级
+        Long newPerProTitleId = null;
+        BigDecimal newPerProTitleScore = BigDecimal.ZERO;
+        String newPerProTitleNumber = "";
+        String newPerProTitleName = "";
+        String rankName = "";
+        DynamicObject perProTitle = positionAppointment.getPerProTitle();
+        if (perProTitle != null) {
+            newPerProTitleId = perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
+            // 对应SHR:selMap.get("zgjbscore")
+            newPerProTitleScore = perProTitle.getBigDecimal(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE));
+            // 对应SHR:selMap.get("zgjbnumber")
+            newPerProTitleNumber = perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY));
+            newPerProTitleName = perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY));
+            // 职称名称
+            rankName = perProTitle.getString(String.join(".", FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY));
+        }
 
-        //当前人员最新职称等级
-        Long newPerProTitleId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
-        //对应SHR:selMap.get("zgjbscore")
-        BigDecimal newPerProTitleScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE));
-        //对应SHR:selMap.get("zgjbnumber")
-        String newPerProTitleNumber = positionAppointment.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY));
-        String newPerProTitleName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY));
-        //职称名称
-        String rankName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY));
-
-        //当前人员最新技能等级
-        Long newQuaLevelId = positionAppointment.getLong(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
-        //对应SHR:selMap.get("zyjndjscore")
-        BigDecimal newQuaLevelScore = positionAppointment.getBigDecimal(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE));
-        //对应SHR:selMap.get("zyjndjnumber")
-        String newQuaLevelNumber = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY));
-        String newQuaLevelName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY));
-        //技能名称
-        String jobStatusName = positionAppointment.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY));
+        // 当前人员最新技能等级
+        Long newQuaLevelId = null;
+        BigDecimal newQuaLevelScore = BigDecimal.ZERO;
+        String newQuaLevelNumber = "";
+        String newQuaLevelName = "";
+        String jobStatusName = "";
+        DynamicObject perOcpQual = positionAppointment.getPerOcpQual();
+        if (perOcpQual != null) {
+            // 对应SHR:selMap.get("zyjndjnumber")
+            newQuaLevelId = perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
+            // 对应SHR:selMap.get("zyjndjscore")
+            newQuaLevelScore = perOcpQual.getBigDecimal(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE));
+            newQuaLevelNumber = perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY));
+            newQuaLevelName = perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY));
+            // 技能名称
+            jobStatusName = perOcpQual.getString(String.join(".", FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY));
+        }
 
         JobScoreInfo jobScoreInfo = new JobScoreInfo();
 
@@ -763,7 +790,7 @@ public class JobLevelCalculatorService {
      * 计算职级ID
      */
     private static DynamicObject calculateJobGradeId(DynamicObject newJobSeq, JobScoreInfo jobScoreInfo, JobSeqInfo jobSeqInfo,
-                                                     BigDecimal allSumScore, int currentJobLevelIndex, int minusPersonAppraisal, boolean fistPR, JobLevelResult jobLevelResult){
+                                                     BigDecimal allSumScore, int currentJobLevelIndex, int minusPersonAppraisal, boolean fistPr, JobLevelResult jobLevelResult){
 
         String perProTitleNumber = jobScoreInfo.perProTitleNumber;
         String quaLevelNumber = jobScoreInfo.quaLevelNumber;
@@ -785,7 +812,7 @@ public class JobLevelCalculatorService {
             jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, 0, Boolean.FALSE,Boolean.FALSE);
             //保级
             jobLevelResult.adjustType = "1";
-        } else if (fistPR) {
+        } else if (fistPr) {
             //对应SHR:JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, minuspersonappraisal);
             jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, minusPersonAppraisal, Boolean.FALSE,Boolean.FALSE);
         } else {
@@ -955,6 +982,7 @@ public class JobLevelCalculatorService {
     }
 
     /**
+     * 计算R排名升降级
      * 对应SHR:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfilecreateViewListHandler#getnewjobgradeindexByrank
      * @param jobLevelIndex
      * @param currentJobLevelIndex
@@ -1133,15 +1161,15 @@ public class JobLevelCalculatorService {
      * 根据考核结果使用情况处理职级计算
      */
     private static int handleAppraisalUsage(DynamicObject jobSeq, RankingResultInfo rankingResultInfo, int jobLevelIndex,
-                                            int lastAppointmentJobGradeIndex, int currentJobLevelIndex, int minuspersonappraisal, boolean fistPR, boolean usedAppraisalResult, JobLevelResult jobLevelResult){
+                                            int lastAppointmentJobGradeIndex, int currentJobLevelIndex, int minuspersonappraisal, boolean fistPr, boolean usedAppraisalResult, JobLevelResult jobLevelResult){
         JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeq.getString(FormConstant.NUMBER_KEY));
         if (!usedAppraisalResult) {
             if (minuspersonappraisal == 0) {
-                if (!fistPR) {
+                if (!fistPr) {
                     // 考核结果为保级 所以相 等
                     jobLevelIndex = lastAppointmentJobGradeIndex;
                 }
-                if (fistPR && ((rankingResultInfo == null || rankingResultInfo.countR == null || rankingResultInfo.countR == 0) || (rankingResultInfo.allowanceRank == null || rankingResultInfo.allowanceRank == 0))) {
+                if (fistPr && ((rankingResultInfo == null || rankingResultInfo.countR == null || rankingResultInfo.countR == 0) || (rankingResultInfo.allowanceRank == null || rankingResultInfo.allowanceRank == 0))) {
                     System.out.println("首次聘任而且无R排名,保持分数算出来的职级");
                 } else {
                     System.out.println("应用R排名结果");
@@ -1173,9 +1201,9 @@ public class JobLevelCalculatorService {
     /**
      * 处理聘任相关限制
      */
-    private static int handleAppointmentRestrictions(long personId, boolean firstPR,boolean usedAppraisalResult,
-                                               DynamicObject jobSeq,long jobSeqId,long currentJobSeqId,String currentJobSeqNumber, JobScoreInfo jobScoreInfo,
-                                               int currentJobLevelIndex,int jobLevelIndex, BigDecimal allsumScore, JobLevelResult jobLevelResult,int minusPersonAppraisal,DynamicObject currentPersonPosFile) {
+    private static int handleAppointmentRestrictions(long personId, boolean firstPr, boolean usedAppraisalResult,
+                                                     DynamicObject jobSeq, long jobSeqId, long currentJobSeqId, String currentJobSeqNumber, JobScoreInfo jobScoreInfo,
+                                                     int currentJobLevelIndex, int jobLevelIndex, BigDecimal allsumScore, JobLevelResult jobLevelResult, int minusPersonAppraisal, DynamicObject currentPersonPosFile) {
         /*PersonpositionfileCollection PRpersonpositionfileCollection = PersonpositionfileFactory.getLocalInstance(ctx)
                 .getPersonpositionfileCollection("select *,Appraisalresult.*,JobGrade.* where person='" + personid +
                         "' and (disable is null or disable<>1) and ((jobstatusScore is not null and jobstatusScore >0) or (RankScore is not null and RankScore >0)) " +
@@ -1204,15 +1232,15 @@ public class JobLevelCalculatorService {
                 new QFilter[]{filer},
                 queryFieldBuilder.buildOrder());
 
-        if (firstPR) {
+        if (firstPr) {
             System.out.println("前一笔为初定且考核结果为无,按首次聘任处理,所以放开限制");
             // 首次聘任
             jobLevelResult.adjustType = "3";
         }else if (!query.isEmpty()){
             long hrjobfamilyid = jobSeqId;
-            long oldHRJobFamilyid = currentJobSeqId;
+            long oldHRJobFamilyId = currentJobSeqId;
             String hrjobfamilynumber = jobSeq.getString(FormConstant.NUMBER_KEY);
-            String oldHRJobFamilynumber = currentJobSeqNumber;
+            String oldHRJobFamilyNumber = currentJobSeqNumber;
             BigDecimal currentRankScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_RANKSCORE);
             BigDecimal currentJobStatusScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
             if (!(currentRankScore.compareTo(BigDecimal.ZERO) > 0 || currentJobStatusScore.compareTo(BigDecimal.ZERO) > 0)) {
@@ -1244,10 +1272,10 @@ public class JobLevelCalculatorService {
 
             //这里为什么用ID判断又用number判断。因为ID至始至终都没有变过(如管理序列会被转换为职能序列),number可能会被转换。
             // 换了序列
-            if (hrjobfamilyid != oldHRJobFamilyid) {
+            if (hrjobfamilyid != oldHRJobFamilyId) {
                 System.out.println("检查到有序列切换");
-                //对应SHR:if (hrjobfamilynumber.equals(oldHRJobFamilynumber)) {;1214行
-                if (hrjobfamilynumber.equals(oldHRJobFamilynumber)) {
+                //对应SHR:if (hrjobfamilynumber.equals(oldHRJobFamilyNumber)) {;1214行
+                if (hrjobfamilynumber.equals(oldHRJobFamilyNumber)) {
                     System.out.println("管理序列与职能序列之间切换,不做序列切换限制---pass");
                     if (!usedAppraisalResult && jobLevelIndex >= currentJobLevelIndex + 1) {
                         // 升级
@@ -1549,14 +1577,14 @@ public class JobLevelCalculatorService {
         if (StringUtils.isBlank(proTitleLevelNumber) && (StringUtils.isBlank(ocpQualLevelNumber) || !JobSeqEnum.SKILL.getCode().equals(jobSeqNumber))) {
             return getLowestJobLevel(jobSeq);
         }
-        else if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) && StringUtils.isBlank(ocpQualLevelNumber) && StringUtils.isNotBlank(proTitleLevelNumber) && TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber) == null) {
+        else if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) && StringUtils.isBlank(ocpQualLevelNumber) && StringUtils.isNotBlank(proTitleLevelNumber) && TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber) == null) {
             return getLowestJobLevel(jobSeq);
         }
         String likename = "";
         String likenumber = "";
         if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber)) {
             if (StringUtils.isBlank(ocpQualLevelNumber)) {
-                likenumber = TechPostLevelNumberTojobstatusNumberMap.get(proTitleLevelNumber);
+                likenumber = TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber);
                 likename = "职称等级【" + proTitleLevelNumber + "】对应的技能等级";
                 System.out.println("技能序列但无技能等级,使用职称等级【" + proTitleLevelNumber + "】对应的技能等级,编码【" + likenumber + "】");
             }
@@ -1578,17 +1606,7 @@ public class JobLevelCalculatorService {
         if(!jobLevels.isEmpty()){
             if(downgradeNum != null && downgradeNum > 0) {
                 //降级
-                DynamicObject[] jobLevelByJobSeq = getJobLevelByJobSeq(jobSeq);
-                Map<Integer, DynamicObject> jobLevelMap = new HashMap<>();
-                if (jobLevelByJobSeq != null) {
-                    String jobLevelSeqKey = FormConstant.JOBLEVELSEQ;
-                    jobLevelMap = Arrays.stream(jobLevelByJobSeq)
-                            .collect(Collectors.toMap(
-                                    dynamicObject -> dynamicObject.getInt(jobLevelSeqKey),
-                                    dynamicObject -> dynamicObject,
-                                    (existing, replacement) -> existing)
-                            );
-                }
+                Map<Integer, DynamicObject> jobLevelMap = getJobLevelByJobSeqMap(jobSeq);
                 //当前分数能达到的最大职级减去需降级数
                 int maxJobLevelSeq = jobLevels.get(0).getInt(FormConstant.JOBLEVELSEQ);
                 DynamicObject jobLevel = jobLevelMap.get(maxJobLevelSeq - downgradeNum);
@@ -1606,6 +1624,28 @@ public class JobLevelCalculatorService {
         }
     }
 
+    /**
+     * 获取职位序列对应的职级
+     * @param jobSeq 职位序列
+     * @return: Map<Integer, DynamicObject>;key:职级序列,value:职级对象
+     * @author W.Y.C
+     * @date: 2025/09/20 16:52
+     */
+    public static Map<Integer, DynamicObject> getJobLevelByJobSeqMap(DynamicObject jobSeq) {
+        DynamicObject[] jobLevelByJobSeq = getJobLevelByJobSeq(jobSeq);
+        Map<Integer, DynamicObject> jobLevelMap = new HashMap<>();
+        if (jobLevelByJobSeq != null) {
+            String jobLevelSeqKey = FormConstant.JOBLEVELSEQ;
+            jobLevelMap = Arrays.stream(jobLevelByJobSeq)
+                    .collect(Collectors.toMap(
+                            dynamicObject -> dynamicObject.getInt(jobLevelSeqKey),
+                            dynamicObject -> dynamicObject,
+                            (existing, replacement) -> existing)
+                    );
+        }
+        return jobLevelMap;
+    }
+
     /**
      * 获取职位序列对应的职级
      * @param jobSeq 职位序列
@@ -1739,6 +1779,9 @@ public class JobLevelCalculatorService {
         public BigDecimal diplomaScore;
         /** 计算后的得分 */
         public BigDecimal allSumScore;
+
+        /** 学历得分说明 */
+        Map<String, String> whyDiplomaScore = new LinkedHashMap<>();
     }
 
     public static class RankingResultInfo {

+ 22 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PositionStructureConstant.java

@@ -114,6 +114,8 @@ public class PositionStructureConstant extends FormConstant {
     public static final String NCKD_ISCURRENTNEWEST = "NCKD_ISCURRENTNEWEST";
     /** 查询人员任职信息及聘任信息*/
     public static final String POSITIONAPPOINTMENTQUERY = "positionappointmentquery";
+    /** 动态调整状态*/
+    public static final String NCKD_DYADJUSSTATUS = "nckd_dyadjusstatus";
     /*-------------------------------------- 员工职位档案 end --------------------------------------*/
 
 
@@ -147,6 +149,8 @@ public class PositionStructureConstant extends FormConstant {
     /*-------------------------------------- 在职人员初定(弹窗) begin --------------------------------------*/
     /** 在职人员初定(弹窗)-实体标识 */
     public static final String SERVINGINITIAL_ENTITYID = "nckd_servinginitial";
+    /** 批量在职人员初定(弹窗)-实体标识 */
+    public static final String SERVINGINITIALBATH_ENTITYID = "nckd_servinginitialbatch";
     /** 职位序列(根据岗位需要自动带出) */
     public static final String NCKD_JOBSEQ = "nckd_jobseq";
     /** 优秀生 */
@@ -159,6 +163,8 @@ public class PositionStructureConstant extends FormConstant {
     /*-------------------------------------- 新入职人员初定(弹窗) begin --------------------------------------*/
     /** 新入职人员初定(弹窗)-实体标识 */
     public static final String NEWHIREINITIAL_ENTITYID = "nckd_newhireinitial";
+    /** 批量新入职人员初定(弹窗)-实体标识 */
+    public static final String NEWHIREINITIALBATCH_ENTITYID = "nckd_newhireinitialbatch";
     /** 本次加入集团日期 */
     public static final String NCKD_JOINCOMDATE = "nckd_joincomdate";
     /** 确认定级 */
@@ -168,6 +174,8 @@ public class PositionStructureConstant extends FormConstant {
     /*-------------------------------------- 新建动态调整(弹窗) begin --------------------------------------*/
     /** 新建动态调整(弹窗)-实体标识 */
     public static final String NEWDYNAMICADJUDIALOG_ENTITYID = "nckd_newdynamicadjudialog";
+    /** 批量新建动态调整(弹窗)-实体标识 */
+    public static final String NEWDYNAMICADJUBATCH_ENTITYID = "nckd_newdynamicadjubatch";
     /** 本次加入集团日期 */
     public static final String NCKD_ADJUSTDATE = "nckd_adjustdate";
     //调动后职级
@@ -182,6 +190,20 @@ public class PositionStructureConstant extends FormConstant {
     public static final String NCKD_QUALEVELNAME = "NCKD_QUALEVELNAME";
 
     /*-------------------------------------- 新建动态调整(弹窗) end --------------------------------------*/
+
+
+    /*-------------------------------------- 职位及积分初定 begin --------------------------------------*/
+    /** 职位及积分初定-实体标识 */
+    public static final String DYNAMICADJUSTMENT_ENTITYID = "nckd_dynamicadjustment";
+    /** 页签控件-已生成动态调整 */
+    public static final String NCKD_ADJUSTED = "nckd_adjusted";
+    /** 页签控件-未生成动态调整 */
+    public static final String NCKD_UNADJUSTED = "nckd_unadjusted";
+    public static final String ADJUST_QUERY = "adjustquery";
+    public static final String NCKD_UNADJUSTREPORT = "nckd_unadjustreport";
+    /*-------------------------------------- 职位及积分初定 end --------------------------------------*/
+
+
     /**职位序列对应职级查询*/
     public static final String JOBSEQTOJOBLEVEL_QUERY = "jobseqtojoblevelquery";
     /**根据职位序列查询职级*/

+ 71 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/bo/PositionAppointmentBO.java

@@ -0,0 +1,71 @@
+package nckd.jxccl.hr.psms.common.bo;
+
+import kd.bos.dataentity.entity.DynamicObject;
+
+/**
+ * 员工任职
+ * @author W.Y.C
+ * @date 2025/10/5 20:13
+ * @version 1.0
+ */
+public  class PositionAppointmentBO {
+
+    /**
+     * 员工任职关系
+     */
+    private DynamicObject empPosOrgRel;
+    /**
+     * 员工学历信息
+     */
+    private DynamicObject perEduExp;
+    /**
+     * 员工职称信息
+     */
+    private DynamicObject perProTitle;
+    /**
+     * 员工职业资格信息
+     */
+    private DynamicObject perOcpQual;
+
+    public PositionAppointmentBO() {
+    }
+
+    public PositionAppointmentBO(DynamicObject empPosOrgRel, DynamicObject perEduExp, DynamicObject perProTitle, DynamicObject perOcpQual) {
+        this.empPosOrgRel = empPosOrgRel;
+        this.perEduExp = perEduExp;
+        this.perProTitle = perProTitle;
+        this.perOcpQual = perOcpQual;
+    }
+
+    public DynamicObject getEmpPosOrgRel() {
+        return empPosOrgRel;
+    }
+
+    public void setEmpPosOrgRel(DynamicObject empPosOrgRel) {
+        this.empPosOrgRel = empPosOrgRel;
+    }
+
+    public DynamicObject getPerEduExp() {
+        return perEduExp;
+    }
+
+    public void setPerEduExp(DynamicObject perEduExp) {
+        this.perEduExp = perEduExp;
+    }
+
+    public DynamicObject getPerOcpQual() {
+        return perOcpQual;
+    }
+
+    public void setPerOcpQual(DynamicObject perOcpQual) {
+        this.perOcpQual = perOcpQual;
+    }
+
+    public DynamicObject getPerProTitle() {
+        return perProTitle;
+    }
+
+    public void setPerProTitle(DynamicObject perProTitle) {
+        this.perProTitle = perProTitle;
+    }
+}

+ 99 - 230
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/helper/PositionStructureHelper.java

@@ -11,6 +11,8 @@ import kd.bos.servicehelper.operation.SaveServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.Date;
 
@@ -38,203 +40,22 @@ public class PositionStructureHelper {
         return QueryServiceHelper.exists(PositionStructureConstant.PERSONPOSFILE_ENTITYID, new QFilter[]{filter});
     }
 
-
     /**
      * 根据人员和状态过滤职位档案
      * @param personId 人员ID
      * @param typeState 类型状态(新入职人员初定1;在职人员初定2;年度调整3;职位调动4;管理序列的聘任5;高级职称的聘任6)
      * @param adjustType 调整类别(升级2;保级1;降级0;首次聘任3;序列变化4;聘任下调5;总分不足6;无聘任7;无考核结果8)
-     * @param otherFilter 其他过滤条件
+     * @param otherFilter
      * @return: kd.bos.dataentity.entity.DynamicObject[]
      * @author W.Y.C
      * @date: 2025/09/23 21:08
      */
-    public static DynamicObject[] getPersonPosFileByPersonAndState(Long personId,String[] typeState,String[] adjustType,QFilter otherFilter) {
-        return (DynamicObject[]) getPersonPosFile(personId, typeState, adjustType, otherFilter, false, false);
-    }
-
-    /**
-     * 获取该人员最新的职位档案
-     * @param personId 人员ID
-     * @return: kd.bos.dataentity.entity.DynamicObject
-     * @author W.Y.C
-     * @date: 2025/09/20 20:09
-     */
-    public static DynamicObject getLatsPersonPosFileByPerson(Long personId) {
-        return (DynamicObject) getPersonPosFile(personId, null, null, null, true, false);
-    }
-
-    /**
-     * 获取该人员最新的职位档案
-     * @param personId 人员ID
-     * @param otherFilter 其他自定义条件
-     * @return: kd.bos.dataentity.entity.DynamicObject 树形结构
-     * @author W.Y.C
-     * @date: 2025/09/20 20:09
-     */
-    public static DynamicObject getLatsPersonPosFileByPerson(Long personId, QFilter otherFilter) {
-        return (DynamicObject) getPersonPosFile(personId, null, null, otherFilter, true, false);
-    }
-
-    /**
-     * 获取首次初定的职位档案
-     * @param personId 人员ID
-     * @return: kd.bos.dataentity.entity.DynamicObject
-     * @author W.Y.C
-     * @date: 2025/09/20 18:57
-     */
-    public static DynamicObject getFirstRank(Long personId) {
-        return (DynamicObject) getPersonPosFile(personId, null, null, null, true, true);
-    }
-
-    /**
-     * 统一的职位档案查询方法
-     * @param personId 人员ID
-     * @param typeState 类型状态过滤条件
-     * @param adjustType 调整类别过滤条件
-     * @param otherFilter 其他过滤条件
-     * @param needLatest 是否只需要最新记录
-     * @param isFirstRank 是否查询首次初定记录
-     * @return 职位档案数组或单个职位档案
-     */
-    private static Object getPersonPosFile(Long personId, String[] typeState, String[] adjustType,
-                                           QFilter otherFilter, boolean needLatest, boolean isFirstRank) {
-        QFilter filter = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
-                .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()));
-
-        // 根据是否查询首次初定记录添加条件
-        if (isFirstRank) {
-            filter.and(new QFilter(PositionStructureConstant.NCKD_FIRSTRANK, QCP.equals, EnableEnum.YES.getCode()));
-        }
-        // 如果不是查询首次初定,则使用有效档案条件
-        else if (!isFirstRank && typeState == null && adjustType == null) {
-            // 只有状态为:【初定】、【年度调整】和【职位调动】的职位档案才是有效档案;
-            // 条件:(初定 或者 (年度调整 并且 已生效) 或者 职位调动)
-            filter.and(
-                    new QFilter(PositionStructureConstant.NCKD_FIRSTRANK, QCP.equals, EnableEnum.YES.getCode())
-                            .or(
-                                    new QFilter(PositionStructureConstant.NCKD_TYPESTATE, QCP.equals, "3")
-                                            .and(new QFilter(PositionStructureConstant.NCKD_ADJUSSTATUS, QCP.in, new String[]{"1", "2"}))
-                            )
-                            .or(new QFilter(PositionStructureConstant.NCKD_TYPESTATE, QCP.equals, "4"))
-            );
-        }
-
-        // 添加其他过滤条件
-        if (typeState != null) {
-            filter.and(new QFilter(PositionStructureConstant.NCKD_TYPESTATE, QCP.in, typeState));
-        }
-        if (adjustType != null) {
-            filter.and(new QFilter(PositionStructureConstant.NCKD_ADJUSSTATUS, QCP.in, adjustType));
-        }
-        if (otherFilter != null) {
-            filter.and(otherFilter);
-        }
 
-        // 构建查询字段
-        QueryFieldBuilder queryFieldBuilder = buildQueryField(isFirstRank, !isFirstRank);
-
-        // 添加排序
-        queryFieldBuilder.orderDesc(PositionStructureConstant.NCKD_BEGINDATE);
-
-        // 执行查询
-        DynamicObject[] result = BusinessDataServiceHelper.load(
-                PositionStructureConstant.PERSONPOSFILE_ENTITYID,
-                queryFieldBuilder.buildSelect(),
-                new QFilter[]{filter},
-                queryFieldBuilder.buildOrder()
-        );
-
-        // 根据需要返回单个或多个结果
-        if (needLatest || isFirstRank) {
-            return result.length > 0 ? result[0] : null;
-        } else {
-            return result;
-        }
+    public static DynamicObject[] getPersonPosFileByPersonAndState(Long personId,String[] typeState,String[] adjustType,QFilter otherFilter) {
+        return getPersonPosFileByPersonAndState(personId,typeState,adjustType,otherFilter, new String[0]);
     }
 
     /**
-     * 构建查询字段
-     * @param isMinimal 是否为最小字段集(用于首次初定查询)
-     * @param isFull 是否为完整字段集(用于最新记录查询)
-     * @return QueryFieldBuilder
-     */
-    private static QueryFieldBuilder buildQueryField(boolean isMinimal, boolean isFull) {
-        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                .add(FormConstant.ID_KEY)
-                .add(PositionStructureConstant.NCKD_BEGINDATE)
-                .add(PositionStructureConstant.NCKD_EXECUTEYEAR)
-                .add(PositionStructureConstant.NCKD_TYPESTATE)
-                .add(PositionStructureConstant.NCKD_FIRSTRANK)
-                .add(PositionStructureConstant.NCKD_DIPLOMASCORE)
-                .add(PositionStructureConstant.NCKD_RANKSCORE)
-                .add(PositionStructureConstant.NCKD_JOBSTATUSSCORE)
-                .addIdNumberName(PositionStructureConstant.NCKD_APPRAISALRESULT)
-                .add(PositionStructureConstant.NCKD_ALLOWANCERANKPCT)
-                .add(PositionStructureConstant.NCKD_ALLOWANCERANKSEL)
-                .add(PositionStructureConstant.NCKD_ALLOWANCERANKMARK)
-                .add(PositionStructureConstant.NCKD_ALLSUMSCORE)
-                .add(PositionStructureConstant.NCKD_SUMSCORE)
-                .add(PositionStructureConstant.NCKD_JOBLEVELHR)
-                .add(PositionStructureConstant.NCKD_RANKNAME)
-                .addIdNumberName(PositionStructureConstant.NCKD_PROTITLELEVEL)
-                .add(PositionStructureConstant.NCKD_JOBSTATUSNAME)
-                .addIdNumberName(PositionStructureConstant.NCKD_OCPQUALLEVEL)
-                .addIdNumberNameWithExtras(
-                        new String[]{PositionStructureConstant.NCKD_JOBLEVELHR},
-                        FormConstant.JOBLEVELSEQ
-                )
-                // 学历
-                .addIdNumberNameWithExtras(
-                        new String[]{PositionStructureConstant.NCKD_DIPLOMA},
-                        FormConstant.NCKD_SCORE
-                )
-                // 职位序列
-                .addIdNumberName(PositionStructureConstant.NCKD_JOBSEQHR);
-        if (isMinimal) {
-            // 首次初定查询只需要基础字段
-            return queryFieldBuilder;
-        }
-
-        // 完整字段集
-        queryFieldBuilder
-                // 岗位
-                .addIdNumberName(PositionStructureConstant.NCKD_POSITIONHR)
-                //其他字段
-                .add(PositionStructureConstant.NCKD_RESULTSCORE)
-                .add(PositionStructureConstant.NCKD_ADJUSTTYPE)
-                .add(PositionStructureConstant.NCKD_ADJUSTINT)
-                .add(PositionStructureConstant.NCKD_ALLOWANCERANK)
-                .add(PositionStructureConstant.NCKD_TOPRANK)
-                .add(PositionStructureConstant.NCKD_TOPRANKPERCENT)
-                .add(PositionStructureConstant.NCKD_ISCURRENTNEWEST)
-                .add(PositionStructureConstant.NCKD_LASTPERSONPOSFILE)
-                .add(PositionStructureConstant.NCKD_EMPLOYMENTSTATUS)
-                .add(PositionStructureConstant.NCKD_EMPLOYMENTYEARS)
-                .add(PositionStructureConstant.NCKD_ORGINSSCORE)
-                .add(PositionStructureConstant.NCKD_LYRCONTRIBSCORE)
-                .add(PositionStructureConstant.NCKD_ENDDATE)
-                .add(PositionStructureConstant.NCKD_APPOINTSTATUS)
-                .add(PositionStructureConstant.NCKD_ADJUSSTATUS)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMA)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMB)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMC)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMD)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUME)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMF)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMG)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMH)
-                .add(PositionStructureConstant.NCKD_YEARSCORESUMI)
-                .add(PositionStructureConstant.NCKD_ALLYEARSCORESUM)
-                .add(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE);
-
-        return queryFieldBuilder;
-    }
-
-/*
-
-    */
-/**
      * 根据人员和状态过滤职位档案
      * @param personId 人员ID
      * @param typeState 类型状态(新入职人员初定1;在职人员初定2;年度调整3;职位调动4;管理序列的聘任5;高级职称的聘任6)
@@ -243,9 +64,10 @@ public class PositionStructureHelper {
      * @return: kd.bos.dataentity.entity.DynamicObject[]
      * @author W.Y.C
      * @date: 2025/09/23 21:08
-     *//*
+     */
+
+    public static DynamicObject[] getPersonPosFileByPersonAndState(Long personId,String[] typeState,String[] adjustType,QFilter otherFilter,String... orderBy) {
 
-    public static DynamicObject[] getPersonPosFileByPersonAndState(Long personId,String[] typeState,String[] adjustType,QFilter otherFilter) {
         QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
                 .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()));
         if(typeState != null){
@@ -285,8 +107,13 @@ public class PositionStructureHelper {
                         new String[]{PositionStructureConstant.NCKD_DIPLOMA},
                         FormConstant.NCKD_SCORE
                 )
-                .addIdNumberName(PositionStructureConstant.NCKD_JOBSEQHR)
-                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);
+                .addIdNumberName(PositionStructureConstant.NCKD_JOBSEQHR);
+        if(orderBy != null && orderBy.length > 0){
+            queryFieldBuilder.orderBy(orderBy);
+        }else{
+            queryFieldBuilder.orderDesc(PositionStructureConstant.NCKD_BEGINDATE);
+        }
+
 
         return BusinessDataServiceHelper.load(
                 PositionStructureConstant.PERSONPOSFILE_ENTITYID,
@@ -298,28 +125,26 @@ public class PositionStructureHelper {
 
     }
 
-    */
-/**
+    /**
      * 获取该人员最新的职位档案
      * @param personId 人员ID
      * @return: kd.bos.dataentity.entity.DynamicObject
      * @author W.Y.C
      * @date: 2025/09/20 20:09
-     *//*
+     */
 
     public static DynamicObject getLatsPersonPosFileByPerson(Long personId) {
         return getLatsPersonPosFileByPerson(personId,null);
     }
-    */
-/**
+
+    /**
      * 获取该人员最新的职位档案
      * @param personId 人员ID
      * @param otherFilter 其他条件
      * @return: kd.bos.dataentity.entity.DynamicObject 树形结构
      * @author W.Y.C
      * @date: 2025/09/20 20:09
-     *//*
-
+     */
     public static DynamicObject getLatsPersonPosFileByPerson(Long personId,QFilter otherFilter) {
         QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
                 // .and(new QFilter(PositionStructureConstant.NCKD_ISCURRENTNEWEST, QCP.equals, EnableEnum.YES.getCode()))
@@ -379,9 +204,9 @@ public class PositionStructureHelper {
                 .add(PositionStructureConstant.NCKD_ALLOWANCERANK)
                 .add(PositionStructureConstant.NCKD_TOPRANK)
                 .add(PositionStructureConstant.NCKD_TOPRANKPERCENT)
-                */
-/*.add(PositionStructureConstant.KEY_NCKD_CAUSEREMARK)
-                .add(PositionStructureConstant.NCKD_WHYDIPLOMASCORE)*//*
+
+                /*.add(PositionStructureConstant.KEY_NCKD_CAUSEREMARK)
+                .add(PositionStructureConstant.NCKD_WHYDIPLOMASCORE)*/
 
                 .add(PositionStructureConstant.NCKD_ISCURRENTNEWEST)
                 .add(PositionStructureConstant.NCKD_LASTPERSONPOSFILE)
@@ -416,14 +241,13 @@ public class PositionStructureHelper {
 
     }
 
-    */
-/**
+    /**
      * 获取首次初定的职位档案
      * @param personId 人员ID
      * @return: kd.bos.dataentity.entity.DynamicObject
      * @author W.Y.C
      * @date: 2025/09/20 18:57
-     *//*
+     */
 
     public static DynamicObject getFirstRank(Long personId) {
         QFilter filer = new QFilter(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), QCP.equals, personId)
@@ -449,6 +273,7 @@ public class PositionStructureHelper {
                         new String[]{PositionStructureConstant.NCKD_JOBLEVELHR},
                         FormConstant.JOBLEVELSEQ
                 )
+                .addIdNumberName(PositionStructureConstant.NCKD_JOBSEQHR)
                 .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);
 
         DynamicObject[] load = BusinessDataServiceHelper.load(
@@ -459,7 +284,6 @@ public class PositionStructureHelper {
         );
         return load.length > 0 ? load[0] : null;
     }
-*/
 
     /**
      * 根据时间获取员工任职(所属公司、岗位、职位序列)、聘任信息(职称等级、技能等级)和学历
@@ -469,9 +293,10 @@ public class PositionStructureHelper {
      * @author W.Y.C
      * @date: 2025/09/17 10:27
      */
-    public static DynamicObject positionAppointmentQuery(Long personId, Date date) {
+    public static PositionAppointmentBO positionAppointmentQuery(Long personId, Date date) {
         QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                 .add(FormConstant.ID_KEY)
+                .addIdNumberName(FormConstant.EMPLOYEE_KEY)
                 // 组织分配
                 .addGroup(new String[]{FormConstant.ASSIGNMENT}, FormConstant.ID_KEY)
                 // 所属公司
@@ -482,30 +307,13 @@ public class PositionStructureHelper {
                 .addIdNumberName(FormConstant.POSITION_KEY)
                 // 职位序列
                 .addIdNumberName(new String[]{FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ})
-                // 学历
-                .addIdNumberNameWithExtras(
-                        new String[]{FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY},
-                        FormConstant.NCKD_SCORE
-                )
-                // 职称
-                .addGroup(new String[]{FormConstant.HRPI_PERPROTITLE, FormConstant.PROFESSIONAL_KEY}, FormConstant.NAME_KEY)
-                // 职称等级
-                .addIdNumberNameWithExtras(
-                        new String[]{FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY},
-                        FormConstant.NCKD_SCORE
-                )
-                // 技能
-                .addGroup(new String[]{FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY}, FormConstant.NAME_KEY)
-                // 技能等级
-                .addIdNumberNameWithExtras(
-                        new String[]{FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY},
-                        FormConstant.NCKD_SCORE
-                )
                 // 服务年限
                 .addGroup(new String[]{FormConstant.HRPI_PERSERLEN},
                         FormConstant.JOINCOMDATE_KEY,
                         FormConstant.FIRSTJOINCOMDATE_KEY
-                );
+                )
+                .orderDesc(FormConstant.STARTDATE)
+                .orderDesc(FormConstant.ENDDATE);
 
         //获取时间范围内的任职信息
         QFilter filer = new QFilter(String.join(".", FormConstant.EMPLOYEE_KEY,FormConstant.ID_KEY),QCP.equals,personId)
@@ -515,17 +323,78 @@ public class PositionStructureHelper {
                 .and(new QFilter(String.join(".", FormConstant.STARTDATE),QCP.less_equals,date))
                 .and(new QFilter(String.join(".", FormConstant.ENDDATE),QCP.large_equals,date));
 
-        //教育经历毕业时间
-        filer.and(new QFilter(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.GRADUTIONDATE), QCP.less_equals,date)
-                .or(new QFilter(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.ID_KEY), QCP.is_null,null)));
 
-        //TODO 职称聘任时间
-        //TODO 技能聘任时间
+        // 1、查询任职经历(取调整时间范围内的主任职)
+        DynamicObjectCollection empPosOrgRelDynamicObject = QueryServiceHelper.query(PositionStructureConstant.POSITIONAPPOINTMENTQUERY, 
+                queryFieldBuilder.buildSelect(), 
+                new QFilter[]{filer}, 
+                queryFieldBuilder.buildOrder(), 
+                1);
 
-        DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.POSITIONAPPOINTMENTQUERY, queryFieldBuilder.buildSelect(), new QFilter[]{filer});
-        return !query.isEmpty() ? query.get(0) : null;
+        // 2、查询学历(取毕业时间小于等于调整时间内的学历)
+        QueryFieldBuilder perEduExpQueryFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(FormConstant.EMPLOYEE_KEY)
+                .addIdNumberNameWithExtras(
+                        new String[]{FormConstant.EDUCATION_KEY},
+                        FormConstant.NCKD_SCORE
+                )
+                .orderDesc(FormConstant.GRADUTIONDATE);
+        QFilter perEduExpFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.equals,personId)
+                .and(new QFilter(FormConstant.GRADUTIONDATE, QCP.less_equals,date))
+                .and(new QFilter(FormConstant.GRADUTIONDATE, QCP.is_notnull,null));
+        DynamicObjectCollection perEduExpDynamicObject = QueryServiceHelper.query(
+                FormConstant.HRPI_PEREDUEXP, perEduExpQueryFieldBuilder.buildSelect(), new QFilter[]{perEduExpFilter}, perEduExpQueryFieldBuilder.buildOrder(), 1);
+
+
+        // 3、查询职称
+        QueryFieldBuilder perProTitleQueryFieldBuilder = QueryFieldBuilder.create()
+                .addIdNumberName(FormConstant.EMPLOYEE_KEY)
+                .addGroup(new String[]{FormConstant.PROFESSIONAL_KEY}, FormConstant.NAME_KEY)
+                .addIdNumberNameWithExtras(
+                        new String[]{FormConstant.PROLEVEL_KEY},
+                        FormConstant.NCKD_SCORE
+                )
+                .orderDesc("awardtime");
+        QFilter perProTitleFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.equals,personId)
+                .and(new QFilter("awardtime", QCP.less_equals,date))
+                .and(new QFilter("awardtime", QCP.is_notnull,null));
+        DynamicObjectCollection perProTitleDynamicObject = QueryServiceHelper.query(
+                FormConstant.HRPI_PERPROTITLE, perProTitleQueryFieldBuilder.buildSelect(), new QFilter[]{perProTitleFilter}, perProTitleQueryFieldBuilder.buildOrder(), 1);
+
+        // 4、查询技能
+        QueryFieldBuilder perOcpQualQueryFieldBuilder = QueryFieldBuilder.create()
+                .addGroup(new String[]{FormConstant.QUALIFICATION_KEY}, FormConstant.NAME_KEY)
+                .addIdNumberNameWithExtras(
+                        new String[]{FormConstant.QUALEVEL_KEY},
+                        FormConstant.NCKD_SCORE
+                )
+            .orderDesc("registratedate");
+        QFilter perOcpQualFilter = new QFilter(FormConstant.EMPLOYEE_KEY,QCP.equals,personId)
+                .and(new QFilter("registratedate", QCP.less_equals,date))
+                .and(new QFilter("registratedate", QCP.is_notnull,null));
+        DynamicObjectCollection perOcpQualDynamicObject = QueryServiceHelper.query(
+                FormConstant.HRPI_PEROCPQUAL, perOcpQualQueryFieldBuilder.buildSelect(), new QFilter[]{perOcpQualFilter}, perOcpQualQueryFieldBuilder.buildOrder(), 1);
+
+        PositionAppointmentBO positionAppointmentBO = new PositionAppointmentBO();
+        if (empPosOrgRelDynamicObject != null && !empPosOrgRelDynamicObject.isEmpty()) {
+            positionAppointmentBO.setEmpPosOrgRel(empPosOrgRelDynamicObject.get(0));
+        }
+        if (perEduExpDynamicObject != null && !perEduExpDynamicObject.isEmpty()) {
+            positionAppointmentBO.setPerEduExp(perEduExpDynamicObject.get(0));
+        }
+        if (perProTitleDynamicObject != null && !perProTitleDynamicObject.isEmpty()) {
+            positionAppointmentBO.setPerProTitle(perProTitleDynamicObject.get(0));
+        }
+        if (perOcpQualDynamicObject != null && !perOcpQualDynamicObject.isEmpty()) {
+            positionAppointmentBO.setPerOcpQual(perOcpQualDynamicObject.get(0));
+        }
+
+
+        return positionAppointmentBO;
     }
 
+
+
     /**
      * 根据人员将所有IsCurrentNewest更新为非最新的
      * @param personId 人员ID(可传多个)

+ 25 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/AdjustQueryListPlugin.java

@@ -0,0 +1,25 @@
+package nckd.jxccl.hr.psms.plugin.form.adjust;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.form.events.SetFilterEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+/**
+* 已生成动态调整-查询列表插件
+* @author W.Y.C
+* @date 2025/10/12 17:51
+* @version 1.0
+*/
+public class AdjustQueryListPlugin extends AbstractListPlugin implements Plugin {
+
+    @Override
+    public void setFilter(SetFilterEvent e) {
+        e.addCustomQFilter(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()));
+        //只查询动态调整的数据
+        e.addCustomQFilter(new QFilter(PositionStructureConstant.NCKD_TYPESTATE, QCP.equals, "4"));
+    }
+}

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

@@ -0,0 +1,88 @@
+package nckd.jxccl.hr.psms.plugin.form.adjust;
+
+import kd.bos.form.ShowType;
+import kd.bos.form.container.Tab;
+import kd.bos.form.control.events.TabSelectEvent;
+import kd.bos.form.control.events.TabSelectListener;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.list.ListShowParameter;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.report.ReportShowParameter;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.EventObject;
+
+/**
+* 职位体系-动态调整
+* @author W.Y.C
+* @date 2025/10/11 18:28
+* @version 1.0
+*/
+public class DynamicAdjustmentFormPlugin extends AbstractFormPlugin implements TabSelectListener {
+
+    private final static Log logger = LogFactory.getLog(DynamicAdjustmentFormPlugin.class);
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        // 获取页签控件并添加监听
+        Tab tab = (Tab)this.getControl(PositionStructureConstant.NCKD_TABAP);
+        tab.addTabSelectListener(this);
+    }
+
+    @Override
+    public void afterBindData(EventObject e) {
+        //页面加载完成默认打开已生成动态调整列表
+        switchTab();
+    }
+    @Override
+    public void tabSelected(TabSelectEvent tabSelectEvent) {
+        //切换tab页签
+        switchTab();
+
+    }
+
+    /**
+     * 切换页签(根据当前页签焦点加载对应页面)
+     * @param
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/09/10 17:16
+     */
+    private void switchTab() {
+        Tab tab = (Tab)this.getView().getControl(PositionStructureConstant.NCKD_TABAP);
+        String currentTab = tab.getCurrentTab();
+        if (PositionStructureConstant.NCKD_ADJUSTED.equalsIgnoreCase(currentTab)) {
+            //打开“已生成动态调整”列表
+            openTargetPage(PositionStructureConstant.ADJUST_QUERY, PositionStructureConstant.NCKD_ADJUSTED);
+        } else if (PositionStructureConstant.NCKD_UNADJUSTED.equalsIgnoreCase(currentTab)) {
+            //打开“未生成动态调整”列表
+            // openTargetPage(PositionStructureConstant.NCKD_UNADJUSTREPORT,PositionStructureConstant.NCKD_UNADJUSTED);
+            ReportShowParameter reportShowParameter = new ReportShowParameter();
+            reportShowParameter.setFormId(PositionStructureConstant.NCKD_UNADJUSTREPORT);
+            reportShowParameter.getOpenStyle().setShowType(ShowType.InContainer);
+            reportShowParameter.getOpenStyle().setTargetKey(PositionStructureConstant.NCKD_UNADJUSTED);
+            reportShowParameter.setCaption("未生成动态调整");
+            this.getView().showForm(reportShowParameter);
+        }
+    }
+
+    /**
+     * 在页签中打开表单
+     * @param formId 需要打开的表单标识
+     * @param targetKey 目标容器标识
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/09/10 17:15
+     */
+    private void openTargetPage(String formId, String targetKey) {
+        ListShowParameter parameter = new ListShowParameter();
+        parameter.setBillFormId(formId);
+        // 在容器内打开
+        parameter.getOpenStyle().setShowType(ShowType.InContainer);
+        // 指定页签容器标识
+        parameter.getOpenStyle().setTargetKey(targetKey);
+        this.getView().showForm(parameter);
+    }
+}

+ 82 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/NewDynamicAdjustmentBatchDiaLogFormPlugin.java

@@ -0,0 +1,82 @@
+package nckd.jxccl.hr.psms.plugin.form.adjust;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.EventObject;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+* 批量新建动态调整(弹窗)-插件
+* @author W.Y.C
+* @date 2025/9/17 15:56
+* @version 1.0
+*/
+public class NewDynamicAdjustmentBatchDiaLogFormPlugin extends AbstractFormPlugin {
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+    }
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        // 获取当前页面的FormShowParameter对象
+        FormShowParameter showParameter = this.getView().getFormShowParameter();
+        //获取列表选择的人员
+        Object personId = showParameter.getCustomParam(FormConstant.NCKD_PERSON);
+        if(personId != null) {
+            initValue(ConvertUtil.toList(personId));
+        }
+    }
+    /**
+     * 初始化表单信息
+     * @param personIds 人员ID
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/09/25 21:40
+     */
+    private void initValue(List<Long> personIds){
+        if(personIds.size() >1) {
+            this.getModel().batchCreateNewEntryRow(FormConstant.NCKD_ENTRYENTITY, personIds.size() - 1);
+        }
+        for (int i = 0; i < personIds.size(); i++) {
+            this.getModel().setValue(FormConstant.NCKD_PERSON,personIds.get(i), i);
+        }
+
+        this.getView().updateView(FormConstant.NCKD_ENTRYENTITY);
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
+        if(success && PositionStructureConstant.OP_CONFIRMADJUST.equalsIgnoreCase(operateKey)){
+            Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
+            String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
+            this.getView().returnDataToParent("true");
+            this.getView().showConfirm("提示","新建调整成功,职位档案已生成。",jobLeveStr, MessageBoxOptions.OK,null,null,null,null);
+        }
+    }
+}

+ 9 - 6
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/adjust/NewDynamicAdjustmentDiaLogFormPlugin.java

@@ -16,6 +16,7 @@ import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 
 import java.time.LocalDateTime;
@@ -75,7 +76,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
                         }else{
                             try{
                                 LocalDateTime endDay = DateUtil.endOfDay(DateUtil.toLocalDateTime(adjustDate));
-                                DynamicObject positionAppointment = PositionStructureHelper.positionAppointmentQuery(person.getLong(FormConstant.ID_KEY), DateUtil.toDate(endDay));
+                                PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(person.getLong(FormConstant.ID_KEY), DateUtil.toDate(endDay));
                                 JobLevelCalculatorService.JobLevelResult jobLevelResult = getJobLevel(person, adjustDate,positionAppointment);
 
                                 updateFormInfo(person, adjustDate, latsPersonPosFileByPerson,jobLevelResult,positionAppointment);
@@ -93,7 +94,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
 
 
 
-    public static JobLevelCalculatorService.JobLevelResult getJobLevel(DynamicObject person,Date date,DynamicObject positionAppointment){
+    public static JobLevelCalculatorService.JobLevelResult getJobLevel(DynamicObject person,Date date,PositionAppointmentBO positionAppointment){
         //移植代码:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#selaboutJobGradeAction
         long personId = person.getLong(FormConstant.ID_KEY);
         String personName = person.getString(FormConstant.NAME_KEY);
@@ -115,7 +116,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
         // 3、根据时间获取员工信息(任职经历、聘任、学历)
         //对应SHR:resultMap
         // DynamicObject positionAppointment = PositionStructureHelper.positionAppointmentQuery(personId, DateUtil.toDate(endDay));
-        if(positionAppointment == null || positionAppointment.getDataEntityType() == null){
+        if(positionAppointment == null || positionAppointment.getEmpPosOrgRel() == null){
             throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", personName,DateUtil.format(date,DateUtil.NORM_DATE_PATTERN)));
         }
 
@@ -174,7 +175,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
     public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
         String operateKey = afterDoOperationEventArgs.getOperateKey();
         boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
-        if(success && PositionStructureConstant.OP_CONFIRMINITIAL.equalsIgnoreCase(operateKey)){
+        if(success && PositionStructureConstant.OP_CONFIRMADJUST.equalsIgnoreCase(operateKey)){
             Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
             String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
             this.getView().returnDataToParent("true");
@@ -182,10 +183,10 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
         }
     }
 
-    private void updateFormInfo(DynamicObject person, Date adjustDate, DynamicObject personPosFile,JobLevelCalculatorService.JobLevelResult jobLevelResult,DynamicObject positionAppointment) {
+    private void updateFormInfo(DynamicObject person, Date adjustDate, DynamicObject personPosFile,JobLevelCalculatorService.JobLevelResult jobLevelResult,PositionAppointmentBO positionAppointment) {
         LocalDateTime endDay = DateUtil.endOfDay(DateUtil.toLocalDateTime(adjustDate));
         this.getModel().setValue(PositionStructureConstant.NCKD_POSITIONHR,
-                positionAppointment.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY)));
+                positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY)));
         this.getModel().setValue(PositionStructureConstant.NCKD_ALLOWANCERANKMARK,
                 jobLevelResult.rankingResultInfo.allowanceRankMark);
         this.getModel().setValue(PositionStructureConstant.NCKD_ALLOWANCERANKPERCENT,
@@ -200,6 +201,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
                 jobLevelResult.jobScoreInfo.jobStatusName);
         this.getModel().setValue(PositionStructureConstant.PERSONPOSFILE_ENTITYID, personPosFile);
         this.getModel().setValue(PositionStructureConstant.NCKD_JOBLEVEL, jobLevelResult.jobLevel);
+        this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, jobLevelResult.jobScoreInfo.diplomaId);
         this.getView().setEnable(true, FormConstant.BTN_OK_OP);
 
     }
@@ -214,6 +216,7 @@ public class NewDynamicAdjustmentDiaLogFormPlugin extends AbstractFormPlugin {
         this.getModel().setValue(PositionStructureConstant.NCKD_QUALEVELNAME, null);
         this.getModel().setValue(PositionStructureConstant.NCKD_JOBSTATUSNAME, null);
         this.getModel().setValue(PositionStructureConstant.NCKD_JOBLEVEL, null);
+        this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, null);
         this.getView().setEnable(false, FormConstant.BTN_OK_OP);
     }
 }

+ 27 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/GradedPersonQueryListPlugin.java

@@ -0,0 +1,27 @@
+package nckd.jxccl.hr.psms.plugin.form.initial;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.form.events.SetFilterEvent;
+import kd.bos.list.plugin.AbstractListPlugin;
+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.hr.psms.common.PositionStructureConstant;
+
+/**
+* 位体系-已定级人员列表
+* @author W.Y.C
+* @date 2025/10/5 23:34
+* @version 1.0
+*/
+public class GradedPersonQueryListPlugin extends AbstractListPlugin implements Plugin {
+
+    @Override
+    public void setFilter(SetFilterEvent setFilterEvent) {
+        //只查询初定的档案
+        QFilter filter = new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals,EnableEnum.NO.getCode())
+                .and(new QFilter(PositionStructureConstant.NCKD_FIRSTRANK, QCP.equals,EnableEnum.YES.getCode()));
+        setFilterEvent.addCustomQFilter(filter);
+    }
+}

+ 73 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/NewHireInitialBatchFormPlugin.java

@@ -0,0 +1,73 @@
+package nckd.jxccl.hr.psms.plugin.form.initial;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
+
+import java.util.Date;
+import java.util.EventObject;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+* 新入职人员初定(弹窗)-弹窗页面插件
+* @author W.Y.C
+* @date 2025/9/15 14:17
+* @version 1.0
+*/
+public class NewHireInitialBatchFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        FormShowParameter showParameter = this.getView().getFormShowParameter();
+        //获取列表选择的人员
+        Object personId = showParameter.getCustomParam("personId");
+        if(personId != null) {
+            initValue(ConvertUtil.toList(personId));
+        }
+    }
+
+
+    /**
+     * 初始化表单信息
+     * @param personIds 人员ID
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/09/25 21:40
+     */
+    private void initValue(List<Long> personIds){
+        if(personIds.size() >1) {
+            this.getModel().batchCreateNewEntryRow(FormConstant.NCKD_ENTRYENTITY, personIds.size() - 1);
+        }
+        for (int i = 0; i < personIds.size(); i++) {
+            this.getModel().setValue(FormConstant.NCKD_PERSON,personIds.get(i), i);
+        }
+
+        this.getView().updateView(FormConstant.NCKD_ENTRYENTITY);
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
+        if(success && PositionStructureConstant.OP_CONFIRMINITIAL.equalsIgnoreCase(operateKey)){
+            Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
+            String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
+            this.getView().returnDataToParent("true");
+            this.getView().showConfirm("提示","初定成功,职位档案已生成。",jobLeveStr, MessageBoxOptions.OK,null,null,null,null);
+        }
+    }
+}

+ 39 - 30
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/NewHireInitialFormPlugin.java

@@ -11,6 +11,7 @@ import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 
 import java.util.Date;
@@ -73,39 +74,47 @@ public class NewHireInitialFormPlugin extends AbstractFormPlugin implements Plug
         if(date == null){
             date = new Date();
         }
-        DynamicObject positionAppointMen = PositionStructureHelper.positionAppointmentQuery(personId, date);
+        PositionAppointmentBO positionAppointMen = PositionStructureHelper.positionAppointmentQuery(personId, date);
         if(positionAppointMen != null) {
             //学历
-            long diplomaId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, diplomaId == 0?null:diplomaId);
-            //岗位
-            long positionId = positionAppointMen.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_POSITIONHR, positionId == 0 ? null : positionId);
-            //职位序列
-            long jobSeqId = positionAppointMen.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_JOBSEQ, jobSeqId == 0 ? null : jobSeqId);
-            //职称名称
-            this.getModel().setValue(PositionStructureConstant.NCKD_RANKNAME, positionAppointMen.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY)));
-            //职称等级
-            long proLevelId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_PROTITLELEVEL, proLevelId == 0 ? null : proLevelId);
-            //技能名称
-            this.getModel().setValue(PositionStructureConstant.NCKD_JOBSTATUSNAME, positionAppointMen.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY)));
-            //技能等级
-            long quaLevelId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_OCPQUALLEVEL, quaLevelId == 0 ? null : quaLevelId);
-            //组织分配
-            long assignmentId = positionAppointMen.getLong(String.join(".", FormConstant.ASSIGNMENT, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_ASSIGNMENT, assignmentId == 0 ? null : assignmentId);
-            //任职信息
-            long empPosOrgRelId = positionAppointMen.getLong(FormConstant.ID_KEY);
-            this.getModel().setValue(PositionStructureConstant.NCKD_EMPPOSORGREL, assignmentId == 0 ? null : empPosOrgRelId);
-            //本次加入集团日期
-            Date joinComDate = positionAppointMen.getDate(String.join(".", FormConstant.HRPI_PERSERLEN,FormConstant.JOINCOMDATE_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_JOINCOMDATE, joinComDate);
-            //是否优秀生
-            // this.getModel().setValue(PositionStructureConstant.NCKD_EXCELLENT, );
+            DynamicObject perEduExp = positionAppointMen.getPerEduExp();
+            if(perEduExp != null) {
+                long diplomaId = perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, diplomaId == 0?null:diplomaId);
+            }
+
+            //岗位、职位序列、组织分配、任职信息、本次加入集团日期
+            DynamicObject empPosOrgRel = positionAppointMen.getEmpPosOrgRel();
+            if(empPosOrgRel != null) {
+                long positionId = empPosOrgRel.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_POSITIONHR, positionId == 0 ? null : positionId);
+
+                long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_JOBSEQ, jobSeqId == 0 ? null : jobSeqId);
+            }
+
+            //职称名称和职称等级
+            DynamicObject perProTitle = positionAppointMen.getPerProTitle();
+            if(perProTitle != null) {
+                String rankName = perProTitle.getString(String.join(".", FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_RANKNAME, rankName);
+
+                long proLevelId = perProTitle.getLong(String.join(".",  FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_PROTITLELEVEL, proLevelId == 0 ? null : proLevelId);
+            }
+
+            //技能名称和技能等级
+            DynamicObject perOcpQual = positionAppointMen.getPerOcpQual();
+            if(perOcpQual != null) {
+                String jobStatusName = perOcpQual.getString(String.join(".",  FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_JOBSTATUSNAME, jobStatusName);
+
+                long quaLevelId = perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_OCPQUALLEVEL, quaLevelId == 0 ? null : quaLevelId);
+            }
         }
+        // TODO 【待修改】优秀生
+        // this.getModel().setValue(PositionStructureConstant.NCKD_EXCELLENT, );
     }
 
     @Override

+ 73 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/ServingInitialBatchFormPlugin.java

@@ -0,0 +1,73 @@
+package nckd.jxccl.hr.psms.plugin.form.initial;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.FormShowParameter;
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.ConvertUtil;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
+
+import java.util.Date;
+import java.util.EventObject;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+* 在职人员初定(弹窗)-弹窗页面插件
+* @author W.Y.C
+* @date 2025/9/15 14:47
+* @version 1.0
+*/
+public class ServingInitialBatchFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void afterCreateNewData(EventObject e) {
+        FormShowParameter showParameter = this.getView().getFormShowParameter();
+        //获取列表选择的人员
+        Object personId = showParameter.getCustomParam("personId");
+        if(personId != null) {
+            initValue(ConvertUtil.toList(personId));
+        }
+    }
+
+    /**
+     * 初始化表单数据
+     * @param personIds 人员ID
+     * @return: void
+     * @author W.Y.C
+     * @date: 2025/09/25 21:39
+     */
+    private void initValue(List<Long> personIds) {
+        if(personIds.size() >1) {
+            this.getModel().batchCreateNewEntryRow(FormConstant.NCKD_ENTRYENTITY, personIds.size() - 1);
+        }
+        for (int i = 0; i < personIds.size(); i++) {
+            this.getModel().setValue(FormConstant.NCKD_PERSON,personIds.get(i), i);
+        }
+
+        this.getView().updateView(FormConstant.NCKD_ENTRYENTITY);
+    }
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
+        if(success && PositionStructureConstant.OP_CONFIRMINITIAL.equalsIgnoreCase(operateKey)){
+            Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
+            String jobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
+            this.getView().returnDataToParent("true");
+            this.getView().showConfirm("提示","初定成功,职位档案已生成。",jobLeveStr, MessageBoxOptions.OK,null,null,null,null);
+
+        }
+    }
+}

+ 54 - 31
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/ServingInitialFormPlugin.java

@@ -11,6 +11,7 @@ import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 
 import java.util.Date;
@@ -69,39 +70,61 @@ public class ServingInitialFormPlugin extends AbstractFormPlugin implements Plug
      * @author W.Y.C
      * @date: 2025/09/25 21:39
      */
-    private void initValue(Long personId,Date date){
-        if(date == null){
+    private void initValue(Long personId, Date date) {
+        if (date == null) {
             date = new Date();
         }
-        DynamicObject positionAppointMen = PositionStructureHelper.positionAppointmentQuery(personId, date);
-        if(positionAppointMen != null) {
-            //学历
-            long diplomaId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, diplomaId == 0?null:diplomaId);
-            //岗位
-            long positionId = positionAppointMen.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_POSITIONHR, positionId == 0 ? null : positionId);
-            //职位序列
-            long jobSeqId = positionAppointMen.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_JOBSEQ, jobSeqId == 0 ? null : jobSeqId);
-            //职称名称
-            this.getModel().setValue(PositionStructureConstant.NCKD_RANKNAME, positionAppointMen.getString(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY)));
-            //职称等级
-            long proLevelId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_PROTITLELEVEL, proLevelId == 0 ? null : proLevelId);
-            //技能名称
-            this.getModel().setValue(PositionStructureConstant.NCKD_JOBSTATUSNAME, positionAppointMen.getString(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY)));
-            //技能等级
-            long quaLevelId = positionAppointMen.getLong(String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_OCPQUALLEVEL, quaLevelId == 0 ? null : quaLevelId);
-            //组织分配
-            long assignmentId = positionAppointMen.getLong(String.join(".", FormConstant.ASSIGNMENT, FormConstant.ID_KEY));
-            this.getModel().setValue(PositionStructureConstant.NCKD_ASSIGNMENT, assignmentId == 0 ? null : assignmentId);
-            //任职信息
-            long empPosOrgRelId = positionAppointMen.getLong(FormConstant.ID_KEY);
-            this.getModel().setValue(PositionStructureConstant.NCKD_EMPPOSORGREL, assignmentId == 0 ? null : empPosOrgRelId);
-            //是否优秀生
-        // this.getModel().setValue(PositionStructureConstant.NCKD_EXCELLENT, );
+        PositionAppointmentBO positionAppointMen = PositionStructureHelper.positionAppointmentQuery(personId, date);
+        if (positionAppointMen != null) {
+            // 学历
+            DynamicObject perEduExp = positionAppointMen.getPerEduExp();
+            if (perEduExp != null) {
+                long diplomaId = perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_DIPLOMA, diplomaId == 0 ? null : diplomaId);
+            }
+
+            // 职称信息
+            DynamicObject perProTitle = positionAppointMen.getPerProTitle();
+            if (perProTitle != null) {
+                // 职称名称
+                this.getModel().setValue(PositionStructureConstant.NCKD_RANKNAME,
+                        perProTitle.getString(String.join(".", FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY)));
+
+                // 职称等级
+                long proLevelId = perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_PROTITLELEVEL, proLevelId == 0 ? null : proLevelId);
+            }
+
+            // 技能信息
+            DynamicObject perOcpQual = positionAppointMen.getPerOcpQual();
+            if (perOcpQual != null) {
+                // 技能名称
+                this.getModel().setValue(PositionStructureConstant.NCKD_JOBSTATUSNAME,
+                        perOcpQual.getString(String.join(".", FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY)));
+
+                // 技能等级
+                long quaLevelId = perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_OCPQUALLEVEL, quaLevelId == 0 ? null : quaLevelId);
+            }
+
+            // 任职信息
+            DynamicObject empPosOrgRel = positionAppointMen.getEmpPosOrgRel();
+            if (empPosOrgRel != null) {
+                // 岗位
+                long positionId = empPosOrgRel.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_POSITIONHR, positionId == 0 ? null : positionId);
+
+                // 职位序列
+                long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_JOBSEQ, jobSeqId == 0 ? null : jobSeqId);
+
+                // 本次加入集团日期
+                Date joinComDate = empPosOrgRel.getDate(String.join(".", FormConstant.HRPI_PERSERLEN, FormConstant.JOINCOMDATE_KEY));
+                this.getModel().setValue(PositionStructureConstant.NCKD_JOINCOMDATE, joinComDate);
+
+            }
+            // TODO 【待修改】优秀生
+            // this.getModel().setValue(PositionStructureConstant.NCKD_EXCELLENT, );
         }
     }
 

+ 91 - 7
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/initial/UngradedPersonQueryListPlugin.java

@@ -1,10 +1,14 @@
 package nckd.jxccl.hr.psms.plugin.form.initial;
 
 import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.datamodel.ListSelectedRow;
 import kd.bos.entity.datamodel.ListSelectedRowCollection;
 import kd.bos.form.CloseCallBack;
 import kd.bos.form.FormShowParameter;
 import kd.bos.form.ShowType;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.control.events.ItemClickListener;
 import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.ClosedCallBackEvent;
 import kd.bos.form.events.SetFilterEvent;
@@ -14,34 +18,114 @@ 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.BusinessDataServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.hr.hstu.common.HonorStudentConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
 * 职位体系-未定级人员列表
 * @author W.Y.C
 * @date 2025/9/6 14:05
 * @version 1.0
 */
-public class UngradedPersonQueryListPlugin extends AbstractListPlugin implements Plugin {
+public class UngradedPersonQueryListPlugin extends AbstractListPlugin implements Plugin, ItemClickListener {
 
     private final static Log logger = LogFactory.getLog(UngradedPersonQueryListPlugin.class);
 
     @Override
     public void setFilter(SetFilterEvent setFilterEvent) {
         //只查询没有初定的人员
-        setFilterEvent.addCustomQFilter(new QFilter(String.join(".",PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.ID_KEY), QCP.is_null,null));
+        QFilter filter = new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.ID_KEY), QCP.is_null, null)
+                //组织分配为主组织分配的人员
+                .and(new QFilter(String.join(".",FormConstant.ASSIGNMENT_ENTITYID, FormConstant.IS_PRIMARY), QCP.equals,EnableEnum.YES.getCode()))
+                .and(new QFilter(FormConstant.IS_SEQLATESTRECORD, QCP.equals,EnableEnum.YES.getCode()))
+                .and(new QFilter(FormConstant.IS_DELETED, QCP.equals,EnableEnum.NO.getCode()))
+                .and(new QFilter(FormConstant.IS_PRIMARY, QCP.equals,EnableEnum.YES.getCode()));
+        setFilterEvent.addCustomQFilter(filter);
+    }
 
-        //组织分配为主组织分配的人员
-        setFilterEvent.addCustomQFilter(new QFilter(String.join(".",FormConstant.ASSIGNMENT_ENTITYID, FormConstant.IS_PRIMARY), QCP.equals,EnableEnum.YES.getCode()));
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        String itemKey = evt.getItemKey();
 
-    }
+        if("nckd_settingjobgrade".equals(itemKey) || "nckd_settingjobgradenew".equals(itemKey) || "nckd_settingjobgradeb".equals(itemKey) || "nckd_settingjobgradenewb".equals(itemKey)) {
+            ListSelectedRowCollection selectedRows = this.getSelectedRows();
+            if(selectedRows.isEmpty()){
+                this.getView().showTipNotification("请至少选择一条数据");
+                return;
+            }
+            //任职经历ID
+            List<Long> empPosOrgRelIds = new ArrayList<>(selectedRows.size());
+            for (ListSelectedRow selectedRow : selectedRows) {
+                empPosOrgRelIds.add(ConvertUtil.toLong(selectedRow.getPrimaryKeyValue()));
+            }
+            QFilter qFilter = new QFilter(FormConstant.ID_KEY, QCP.in, empPosOrgRelIds);
+            DynamicObject[] load = BusinessDataServiceHelper.load(FormConstant.HRPI_EMPPOSORGREL, String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY), new QFilter[]{qFilter});
+            List<Long> personIds = new ArrayList<>(selectedRows.size());
+            for (DynamicObject dynamicObject : load) {
+                DynamicObject employee = dynamicObject.getDynamicObject(FormConstant.EMPLOYEE_KEY);
+                personIds.add(employee.getLong(FormConstant.ID_KEY));
+            }
 
+            if(personIds.isEmpty()){
+                this.getView().showErrorNotification("请选择正确的数据");
+                return;
+            }
+            if ("nckd_settingjobgrade".equals(itemKey) || "nckd_settingjobgradeb".equals(itemKey)) {
+                if(selectedRows.size() > 1 || "nckd_settingjobgradeb".equals(itemKey)){
+                    //弹出【批量】在职人员初定窗口
+                    FormShowParameter showParameter = new FormShowParameter();
+                    showParameter.setFormId(PositionStructureConstant.SERVINGINITIALBATH_ENTITYID);
+                    showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                    showParameter.setCaption("【批量】在职人员初定");
+                    showParameter.setSendToClient(true);
+                    showParameter.setCustomParam("personId", personIds);
+                    showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.SERVINGINITIALBATH_ENTITYID));
+                    this.getView().showForm(showParameter);
+                }else {
+                    //弹出在职人员初定窗口
+                    FormShowParameter showParameter = new FormShowParameter();
+                    showParameter.setFormId(PositionStructureConstant.SERVINGINITIAL_ENTITYID);
+                    showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                    showParameter.setCaption("在职人员初定-定级信息确认");
+                    showParameter.setSendToClient(true);
+                    showParameter.setCustomParam("personId", personIds.get(0));
+                    showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.SERVINGINITIAL_ENTITYID));
+                    this.getView().showForm(showParameter);
+                }
+            } else if ("nckd_settingjobgradenew".equals(itemKey) || "nckd_settingjobgradenewb".equals(itemKey)) {
+                if(selectedRows.size() > 1 || "nckd_settingjobgradenewb".equals(itemKey)){
+                    //弹出【批量】新入职人员初定窗口
+                    FormShowParameter showParameter = new FormShowParameter();
+                    showParameter.setFormId(PositionStructureConstant.NEWHIREINITIALBATCH_ENTITYID);
+                    showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                    showParameter.setCaption("【批量】新入职人员初定");
+                    showParameter.setSendToClient(true);
+                    showParameter.setCustomParam("personId", personIds);
+                    showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.NEWHIREINITIALBATCH_ENTITYID));
+                    this.getView().showForm(showParameter);
+                }else {
+                    //弹出【单人】新入职人员初定窗口
+                    FormShowParameter showParameter = new FormShowParameter();
+                    showParameter.setFormId(PositionStructureConstant.NEWHIREINITIAL_ENTITYID);
+                    showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                    showParameter.setCaption("新入职人员初定-定级信息确认");
+                    showParameter.setCustomParam("personId", personIds.get(0));
+                    showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.NEWHIREINITIAL_ENTITYID));
+                    this.getView().showForm(showParameter);
+                }
+            }
+        }
+    }
 
-    @Override
+    /*@Override
     public void beforeDoOperation(BeforeDoOperationEventArgs args) {
         FormOperate formOperate = (FormOperate) args.getSource();
         String operateKey = formOperate.getOperateKey();
@@ -70,7 +154,7 @@ public class UngradedPersonQueryListPlugin extends AbstractListPlugin implements
             showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.NEWHIREINITIAL_ENTITYID));
             this.getView().showForm(showParameter);
         }
-    }
+    }*/
     @Override
     public void closedCallBack(ClosedCallBackEvent closedCallBackEvent) {
         String actionId = closedCallBackEvent.getActionId();

+ 176 - 72
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/adjust/NewDynamicAdjustmentOperationPlugIn.java

@@ -1,7 +1,9 @@
 package nckd.jxccl.hr.psms.plugin.operate.adjust;
 
 import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.entity.CloneUtils;
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
@@ -10,6 +12,8 @@ import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
 import kd.bos.entity.validate.AbstractValidator;
 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.BusinessDataServiceHelper;
 import kd.bos.servicehelper.operation.SaveServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
@@ -19,6 +23,7 @@ import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 import nckd.jxccl.hr.psms.plugin.operate.initial.BaseInitialOperationPlugIn;
 
@@ -27,9 +32,12 @@ import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.StringJoiner;
 
 /**
 * 新建动态调整OP
@@ -41,10 +49,10 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
 
     protected final static Log logger = LogFactory.getLog(BaseInitialOperationPlugIn.class);
 
-    /** 记录上一次职位档案 */
+    /** 记录上一次职位档案(key:人员ID) */
     Map<Long,DynamicObject> latsPersonPosFileMap;
 
-    /** 记录上年度考核结果*/
+    /** 记录上年度考核结果(key:人员ID)*/
     Map<Long,DynamicObject> lastYearPerformanceResultMap;
 
     @Override
@@ -58,115 +66,186 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
                     //前面的校验器失败跳过本校验器
                     return;
                 }
-                Date currentDate = new Date();
                 for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
                     DynamicObject data = rowDataEntity.getDataEntity();
-                    Date adjustDate = data.getDate(PositionStructureConstant.NCKD_ADJUSTDATE);
-                    if (adjustDate == null) {
-                        addFatalErrorMessage(rowDataEntity,"调整时间不能为空");
-                        return;
-                    }
-                    DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
-                    if(person == null){
-                        addFatalErrorMessage(rowDataEntity,"请选择要调整的员工");
-                        return;
+                    if(data.containsProperty(FormConstant.NCKD_ENTRYENTITY)){
+                        //批量调整
+                        DynamicObjectCollection dynamicObjectCollection = data.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+                        // 检查人员ID重复
+                        Set<Long> personIds = new HashSet<>();
+                        for (DynamicObject dynamicObject : dynamicObjectCollection) {
+                            if (dynamicObject.containsProperty(FormConstant.NCKD_PERSON)) {
+                                DynamicObject person = dynamicObject.getDynamicObject(FormConstant.NCKD_PERSON);
+                                Long personId = person.getLong(FormConstant.ID_KEY);
+                                if (!personIds.add(personId)) {
+                                    addFatalErrorMessage(rowDataEntity,
+                                            StrFormatter.format("人员【{}】在列表中重复,请保留一条信息",
+                                                    person.getString(FormConstant.NAME_KEY)));
+                                }
+                            }
+                        }
+                        if(!this.getValidateResult().isSuccess()){
+                            return;
+                        }
+
+                        for (DynamicObject dynamicObject : dynamicObjectCollection) {
+                            validator(rowDataEntity, dynamicObject);
+                        }
+                    }else{
+                        //单次调整
+                        validator(rowDataEntity, data);
                     }
-                    DynamicObject latsPersonPosFileByPerson = PositionStructureHelper.getLatsPersonPosFileByPerson(person.getLong(FormConstant.ID_KEY));
-                    if(latsPersonPosFileByPerson == null){
-                        addFatalErrorMessage(rowDataEntity, StrFormatter.format("当前无法为【{}】进行动态调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", person.getString(FormConstant.NAME_KEY)));
-                        return;
+                }
+            }
+
+            private void validator(ExtendedDataEntity rowDataEntity, DynamicObject data) {
+                DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
+                if(person == null){
+                    addFatalErrorMessage(rowDataEntity,"请选择要调整的员工");
+                    return;
+                }
+                Date adjustDate = data.getDate(PositionStructureConstant.NCKD_ADJUSTDATE);
+                if (adjustDate == null) {
+                    addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】调整时间不能为空",person.getString(FormConstant.NAME_KEY)));
+                }
+                DynamicObject latsPersonPosFileByPerson = PositionStructureHelper.getLatsPersonPosFileByPerson(person.getLong(FormConstant.ID_KEY));
+                if(latsPersonPosFileByPerson == null){
+                    addFatalErrorMessage(rowDataEntity, StrFormatter.format("当前无法为【{}】进行动态调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", person.getString(FormConstant.NAME_KEY)));
+                }
+                //判断当前年是否已执行过年度调整
+                DynamicObject[] personPosFileByAdjust = PositionStructureHelper.getPersonPosFileByPersonAndState(person.getLong(FormConstant.ID_KEY), new String[]{"4"},
+                        null,
+                        new QFilter(PositionStructureConstant.NCKD_DYADJUSSTATUS, QCP.equals, EnableEnum.NO.getCode()));
+                if(personPosFileByAdjust != null && personPosFileByAdjust.length > 0){
+                    StringJoiner executeYear = new StringJoiner(",");
+                    for (DynamicObject dynamicObject : personPosFileByAdjust) {
+                        executeYear.add(dynamicObject.getInt(PositionStructureConstant.NCKD_EXECUTEYEAR)+"");
                     }
-                    //最近一次职位档案开始时间
+                    addFatalErrorMessage(rowDataEntity, StrFormatter.format("当前无法为【{}】进行动态调整,因为存在【{}】年度未生效的动态调整!", person.getString(FormConstant.NAME_KEY), executeYear.toString()));
+                }
+                //最近一次职位档案开始时间
+                if(latsPersonPosFileByPerson != null) {
                     Date date = latsPersonPosFileByPerson.getDate(PositionStructureConstant.NCKD_BEGINDATE);
                     //校验【本次调整时间】不得早于上一笔时间
-                    if (adjustDate.before(date)) {
-                        addFatalErrorMessage(rowDataEntity, StrFormatter.format("【{}】最近一次的职位档案变动时间是【{}】,请勿选择早于该时间段的调整时间!", person.getString(FormConstant.NAME_KEY), DateUtil.format(date,DateUtil.NORM_DATE_PATTERN)));
+                    if (adjustDate != null && adjustDate.before(date)) {
+                        addFatalErrorMessage(rowDataEntity, StrFormatter.format("人员【{}】最近一次的职位档案变动时间是【{}】,请勿选择早于该时间段的调整时间!", person.getString(FormConstant.NAME_KEY), DateUtil.format(date, DateUtil.NORM_DATE_PATTERN)));
                     }
-                    //获取上年度绩效结果
+                }
+                //获取上年度绩效结果
+                if(adjustDate != null) {
                     LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(adjustDate), 1);
                     DynamicObject lastYearPerformanceResult = PerformanceManagerHelper.getPerformanceResult(person.getLong(FormConstant.ID_KEY), lastYearDateTime);
-                    if(lastYearPerformanceResult == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("员工【{}】没有【{}】年度考核结果,无法新建调整",DateUtil.getYear(lastYearDateTime),person.getString(FormConstant.NAME_KEY)));
-                        return;
+                    if (lastYearPerformanceResult == null) {
+                        addFatalErrorMessage(rowDataEntity, StrFormatter.format("人员【{}】没有【{}】年度考核结果,无法新建调整", DateUtil.getYear(lastYearDateTime), person.getString(FormConstant.NAME_KEY)));
                     }
-
-                    latsPersonPosFileMap.put(person.getLong(FormConstant.ID_KEY),latsPersonPosFileByPerson);
                     lastYearPerformanceResultMap.put(person.getLong(FormConstant.ID_KEY),lastYearPerformanceResult);
-
                 }
+                latsPersonPosFileMap.put(person.getLong(FormConstant.ID_KEY),latsPersonPosFileByPerson);
+
             }
         });
     }
 
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        if(!this.getOperationResult().isSuccess()){
+            return;
+        }
         logger.info("【职位体系】-动态调整-开始");
         List<Long> personIds = new ArrayList<>(e.getDataEntities().length);
         List<DynamicObject> newPersonPosFiles = new ArrayList<>(e.getDataEntities().length);
         for (DynamicObject data : e.getDataEntities()) {
-            DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
-            long personId = person.getLong(FormConstant.ID_KEY);
-            String personName = person.getString(FormConstant.NAME_KEY);
-            //最近一次档案
-            DynamicObject latsPersonPosFile = latsPersonPosFileMap.get(personId);
-            //上一次职级
-            DynamicObject jobLevelHr = latsPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
-            //选择的调整时间
-            Date adjustDate = data.getDate(PositionStructureConstant.NCKD_ADJUSTDATE);
-            LocalDateTime adjustDateEnd = DateUtil.endOfDay(DateUtil.toLocalDateTime(adjustDate));
-            //查询员工在调整日期内的最新信息(职位、部门、序列、学历、职称、技能)
-            DynamicObject positionAppointment = PositionStructureHelper.positionAppointmentQuery(personId, DateUtil.toDate(adjustDateEnd));
-            if(positionAppointment == null || positionAppointment.getDataEntityType() == null){
-                throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", personName,DateUtil.format(adjustDateEnd,DateUtil.NORM_DATE_PATTERN)));
-            }
-            //最新档案总积分
-            BigDecimal allSumScore = latsPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_ALLSUMSCORE);
-            //计算本次职级
-            JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, adjustDate, positionAppointment);
-            long jobLevelId = jobLevelResult.jobLevel.getLong(FormConstant.ID_KEY);
-
-            Long frontendJobLevelId = null;
-            if(data.containsProperty(PositionStructureConstant.NCKD_JOBLEVEL)){
-                //前端传入的职级
-                DynamicObject frontendJobLevel = data.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVEL);
-                if(frontendJobLevel != null){
-                    frontendJobLevelId = frontendJobLevel.getLong(FormConstant.ID_KEY);
+            if(data.containsProperty(FormConstant.NCKD_ENTRYENTITY)){
+                //批量调整
+                DynamicObjectCollection dynamicObjectCollection = data.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+                for (DynamicObject dynamicObject : dynamicObjectCollection) {
+                    execute(dynamicObject, personIds, newPersonPosFiles);
                 }
-            }
-            String remark = null;
-            if(data.containsProperty(PositionStructureConstant.NCKD_REMARK)){
-                //前端传入的职级
-                remark = data.getString(PositionStructureConstant.NCKD_REMARK);
+            }else{
+                //单次调整
+                execute(data, personIds, newPersonPosFiles);
             }
 
-            createPersonPosFile(person, positionAppointment, adjustDate, jobLevelResult, latsPersonPosFile, allSumScore, personId, frontendJobLevelId, jobLevelId, remark, personIds, newPersonPosFiles,lastYearPerformanceResultMap);
         }
 
         PositionStructureHelper.markAsNotCurrentNewest(personIds.toArray(new Long[0]));
         SaveServiceHelper.save(newPersonPosFiles.toArray(new DynamicObject[0]));
     }
 
-    private static void createPersonPosFile(DynamicObject person, DynamicObject positionAppointment, Date adjustDate,
+    private void execute(DynamicObject data, List<Long> personIds, List<DynamicObject> newPersonPosFiles) {
+        DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
+        long personId = person.getLong(FormConstant.ID_KEY);
+        String personNumber = person.getString(FormConstant.NUMBER_KEY);
+        String personName = person.getString(FormConstant.NAME_KEY);
+        String logPrefix = StrFormatter.format("【职位体系】-动态调整-人员【{}({})】",
+                personName,
+                personNumber);
+        //最近一次档案
+        DynamicObject latsPersonPosFile = latsPersonPosFileMap.get(personId);
+        //上一次职级
+        DynamicObject jobLevelHr = latsPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
+        //选择的调整时间
+        Date adjustDate = data.getDate(PositionStructureConstant.NCKD_ADJUSTDATE);
+        LocalDateTime adjustDateEnd = DateUtil.endOfDay(DateUtil.toLocalDateTime(adjustDate));
+        //查询员工在调整日期内的最新信息(职位、部门、序列、学历、职称、技能)
+        PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(personId, DateUtil.toDate(adjustDateEnd));
+        if(positionAppointment.getEmpPosOrgRel() == null){
+            throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", personName,DateUtil.format(adjustDateEnd,DateUtil.NORM_DATE_PATTERN)));
+        }
+        //最新档案总积分
+        BigDecimal allSumScore = latsPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_ALLSUMSCORE);
+        //计算本次职级
+        JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, adjustDate, positionAppointment);
+        long jobLevelId = jobLevelResult.jobLevel.getLong(FormConstant.ID_KEY);
+
+        Long frontendJobLevelId = null;
+        if(data.containsProperty(PositionStructureConstant.NCKD_JOBLEVEL)){
+            //前端传入的职级
+            DynamicObject frontendJobLevel = data.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVEL);
+            if(frontendJobLevel != null){
+                frontendJobLevelId = frontendJobLevel.getLong(FormConstant.ID_KEY);
+            }
+        }
+        String remark = null;
+        if(data.containsProperty(PositionStructureConstant.NCKD_REMARK)){
+            //前端传入的职级
+            remark = data.getString(PositionStructureConstant.NCKD_REMARK);
+        }
+
+        createPersonPosFile(person, positionAppointment, adjustDate, jobLevelResult, latsPersonPosFile, allSumScore, personId, frontendJobLevelId, jobLevelId, remark, personIds, newPersonPosFiles,lastYearPerformanceResultMap);
+        setJobLevelResult(person,jobLevelHr);
+    }
+
+    private static void createPersonPosFile(DynamicObject person, PositionAppointmentBO positionAppointment, Date adjustDate,
                                             JobLevelCalculatorService.JobLevelResult jobLevelResult, DynamicObject latsPersonPosFile,
                                             BigDecimal allSumScore, long personId, Long frontendJobLevelId,
                                             long jobLevelId, String remark, List<Long> personIds,
                                             List<DynamicObject> newPersonPosFiles,Map<Long,DynamicObject> lastYearPerformanceResultMap) {
+
+        DynamicObject currentPersonPosFile = BusinessDataServiceHelper.loadSingle(latsPersonPosFile.getLong(FormConstant.ID_KEY), PositionStructureConstant.PERSONPOSFILE_ENTITYID);
+        //创建新对象:克隆整个数据包
+        DynamicObject newPersonPosFile = (DynamicObject) new CloneUtils(false, true).clone(currentPersonPosFile);
         // 构建职位档案
-        DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
-                PositionStructureConstant.PERSONPOSFILE_ENTITYID);
+        /*DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
+                PositionStructureConstant.PERSONPOSFILE_ENTITYID);*/
 
+        DynamicObject empPosOrgRel = positionAppointment.getEmpPosOrgRel();
         newPersonPosFile.set(PositionStructureConstant.NCKD_PERSON, person);
         DynamicObject company = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
-        company.set(FormConstant.ID_KEY, positionAppointment.getLong(String.join(".",FormConstant.COMPANY_KEY,FormConstant.ID_KEY)));
+        company.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.COMPANY_KEY,FormConstant.ID_KEY)));
         newPersonPosFile.set(PositionStructureConstant.USEORG_KEY, company);
         DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
-        dep.set(FormConstant.ID_KEY, positionAppointment.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
+        dep.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
         newPersonPosFile.set(PositionStructureConstant.ORG_KEY, dep);
         newPersonPosFile.set(PositionStructureConstant.CREATEORG_KEY, dep);
         newPersonPosFile.set(PositionStructureConstant.NCKD_TYPESTATE, "4");
         newPersonPosFile.set(PositionStructureConstant.NCKD_EXECUTEYEAR, DateUtil.getYear(adjustDate));
-        DynamicObject position = positionAppointment.getDynamicObject(FormConstant.HBPM_POSITIONHR);
-        DynamicObject jobSeq = position.getDynamicObject(FormConstant.NCKD_JOBSEQ);
+        Long positionId = empPosOrgRel.getLong(String.join(".",FormConstant.POSITION_KEY,FormConstant.ID_KEY));
+        DynamicObject position = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBPM_POSITIONHR);
+        position.set(FormConstant.ID_KEY, positionId);
+        Long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+        DynamicObject jobSeq = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBJM_JOBSEQHR);
+        jobSeq.set(FormConstant.ID_KEY, jobSeqId);
         newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSEQHR, jobSeq);
         newPersonPosFile.set(PositionStructureConstant.NCKD_POSITIONHR, position);
         newPersonPosFile.set(PositionStructureConstant.NCKD_RANKNAME, jobLevelResult.jobScoreInfo.rankName);
@@ -186,7 +265,7 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
         newPersonPosFile.set(PositionStructureConstant.NCKD_WHYDIPLOMASCORE, jobLevelResult.adjustMsg);
         newPersonPosFile.set(PositionStructureConstant.NCKD_RANKSCORE, jobLevelResult.jobScoreInfo.perProTitleScore);
         newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSSCORE, jobLevelResult.jobScoreInfo.quaLevelScore);
-        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLSUMSCORE, allSumScore);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLSUMSCORE, jobLevelResult.jobScoreInfo.allSumScore);
         //积分池(生涯积分)使用最后一次职位档案中的,因为动态调整不涉及生涯积分变动
         BigDecimal sumScore = latsPersonPosFile.containsProperty(PositionStructureConstant.NCKD_SUMSCORE) ? latsPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE) : null;
         newPersonPosFile.set(PositionStructureConstant.NCKD_SUMSCORE,sumScore);
@@ -222,9 +301,13 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
         newPersonPosFile.set(PositionStructureConstant.NCKD_ISCURRENTNEWEST, EnableEnum.YES.getCode());
         newPersonPosFile.set(PositionStructureConstant.STATUS, StatusEnum.C.toString());
         newPersonPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
+        // 备注
+        newPersonPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, remark);
+        //动态调整状态;默认为否
+        newPersonPosFile.set(PositionStructureConstant.NCKD_DYADJUSSTATUS, EnableEnum.NO.getCode());
 
         // 聘任状态
-        String employmentStatus = latsPersonPosFile.getString(PositionStructureConstant.NCKD_EMPLOYMENTSTATUS);
+        /*String employmentStatus = latsPersonPosFile.getString(PositionStructureConstant.NCKD_EMPLOYMENTSTATUS);
         newPersonPosFile.set(PositionStructureConstant.NCKD_EMPLOYMENTSTATUS, employmentStatus);
         // 连续聘任年限
         int employmentYears = latsPersonPosFile.getInt(PositionStructureConstant.NCKD_EMPLOYMENTYEARS);
@@ -276,12 +359,33 @@ public class NewDynamicAdjustmentOperationPlugIn extends AbstractOperationServic
         newPersonPosFile.set(PositionStructureConstant.NCKD_ALLYEARSCORESUM, allYearScoreSum);
         // 年度新增的贡献积分
         BigDecimal addYContribScore = latsPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE);
-        newPersonPosFile.set(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE, addYContribScore);
-        // 备注
-        String causeRemark = latsPersonPosFile.getString(PositionStructureConstant.KEY_NCKD_CAUSEREMARK);
-        newPersonPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, remark);
+        newPersonPosFile.set(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE, addYContribScore);*/
+
 
         personIds.add(personId);
         newPersonPosFiles.add(newPersonPosFile);
     }
+
+    /**
+     * 设置操作结果中的职级信息
+     */
+    protected void setJobLevelResult(DynamicObject person, DynamicObject jobLeve) {
+        String jobLeveStr = StrFormatter.format("{}:{}({}级)",
+                person.getString(FormConstant.NAME_KEY),
+                jobLeve.getString(FormConstant.NAME_KEY),
+                jobLeve.getString(FormConstant.JOBLEVELSEQ));
+
+        Map<String, String> customData = this.getOperationResult().getCustomData();
+        if(customData == null) {
+            this.getOperationResult().setCustomData(new HashMap<>());
+            customData = this.getOperationResult().getCustomData();
+        }
+
+        String oldJobLeveStr = customData.get(PositionStructureConstant.NCKD_JOBLEVELHR);
+        if (oldJobLeveStr == null || oldJobLeveStr.isEmpty()) {
+            customData.put(PositionStructureConstant.NCKD_JOBLEVELHR, jobLeveStr);
+        } else {
+            customData.put(PositionStructureConstant.NCKD_JOBLEVELHR, oldJobLeveStr + StrFormatter.LINE_SEPARATOR + jobLeveStr);
+        }
+    }
 }

+ 129 - 37
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/BaseInitialOperationPlugIn.java

@@ -2,6 +2,7 @@ package nckd.jxccl.hr.psms.plugin.operate.initial;
 
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.logging.Log;
@@ -14,15 +15,21 @@ import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.enums.JobSeqEnum;
 import nckd.jxccl.base.common.utils.DateUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
 import nckd.jxccl.base.pm.helper.PerformanceManagerHelper;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
+import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
+import org.jetbrains.annotations.NotNull;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
 
@@ -36,6 +43,8 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
     
     protected final static Log logger = LogFactory.getLog(BaseInitialOperationPlugIn.class);
 
+    public List<BaseInitialData> baseInitialData;
+
     /**
      * 基本信息数据对象
      */
@@ -100,49 +109,131 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
     }
 
     /**
-     * 提取基本信息
+     * 提取基本信息(支持批量)
+     * @param initialData 页面数据
+     * @return: java.util.List<nckd.jxccl.hr.psms.plugin.operate.initial.BaseInitialOperationPlugIn.BaseInitialData>
+     * @author W.Y.C
+     * @date: 2025/10/12 14:20
      */
-    protected BaseInitialData extractBasicInfo(DynamicObject initialData) {
+    protected List<BaseInitialData> extractBasicInfo(DynamicObject initialData) {
+        if(baseInitialData != null){
+            return baseInitialData;
+        }
+        List<BaseInitialData> dataList = new ArrayList<>();
+        if(initialData.containsProperty(FormConstant.NCKD_ENTRYENTITY)){
+            DynamicObjectCollection dynamicObjectCollection = initialData.getDynamicObjectCollection(FormConstant.NCKD_ENTRYENTITY);
+            for (DynamicObject dynamicObject : dynamicObjectCollection) {
+                //批量初定
+                BaseInitialData data = getBaseInitialData(dynamicObject);
+                dataList.add(data);
+            }
+        }else{
+            BaseInitialData data = getBaseInitialData(initialData);
+            dataList.add(data);
+        }
+
+        baseInitialData = dataList;
+        return baseInitialData;
+    }
+
+    @NotNull
+    private BaseInitialData getBaseInitialData(DynamicObject initialData) {
         BaseInitialData data = new BaseInitialData();
 
         data.beginDate = initialData.getDate(PositionStructureConstant.NCKD_BEGINDATE);
         data.causeRemark = initialData.getString(PositionStructureConstant.KEY_NCKD_CAUSEREMARK);
-        data.rankName = initialData.getString(PositionStructureConstant.NCKD_RANKNAME);
-        data.jobStatusName = initialData.getString(PositionStructureConstant.NCKD_JOBSTATUSNAME);
         data.person = initialData.getDynamicObject(PositionStructureConstant.NCKD_PERSON);
-        data.positionHr = initialData.getDynamicObject(PositionStructureConstant.NCKD_POSITIONHR);
-
-        // 主任职经历(在业务规则中配置)
-        DynamicObject empPosOrgRel = initialData.getDynamicObject(PositionStructureConstant.NCKD_EMPPOSORGREL);
-        data.empPosOrgRel = empPosOrgRel;
-        if(empPosOrgRel != null) {
-            data.company = empPosOrgRel.getDynamicObject(FormConstant.COMPANY_KEY);
-            data.dep = empPosOrgRel.getDynamicObject(FormConstant.ADMINORG);
+
+        PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(data.person.getLong(FormConstant.ID_KEY), data.beginDate);
+        // 学历
+        DynamicObject perEduExp = positionAppointment.getPerEduExp();
+        if (perEduExp != null) {
+            // 学历
+            long diplomaId = perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY));
+            if(diplomaId > 0) {
+                DynamicObject diploma = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_DIPLOMA);
+                diploma.set(FormConstant.ID_KEY, diplomaId);
+                data.diploma = diploma;
+            }
+        }
+        // 职称信息
+        DynamicObject perProTitle = positionAppointment.getPerProTitle();
+        if (perProTitle != null) {
+            data.rankName = perProTitle.getString(String.join(".", FormConstant.PROFESSIONAL_KEY, FormConstant.NAME_KEY));
+            // 职称等级
+            long perProTitleId = perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY));
+            if(perProTitleId > 0) {
+                DynamicObject proTitleLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_PROTITLELEVEL);
+                proTitleLevel.set(FormConstant.ID_KEY, perProTitleId);
+                data.proTitleLevel = proTitleLevel;
+            }
         }
 
-        // 优秀生
-        data.isExcellent = initialData.getBoolean(PositionStructureConstant.NCKD_EXCELLENT);
+        // 技能信息
+        DynamicObject perOcpQual = positionAppointment.getPerOcpQual();
+        if (perOcpQual != null) {
+            data.jobStatusName = perOcpQual.getString(String.join(".", FormConstant.QUALIFICATION_KEY, FormConstant.NAME_KEY));
+            // 技能等级
+            long quaLevelId = perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY));
+            if(quaLevelId > 0) {
+                DynamicObject ocpQualLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_OCPQUALLEVEL);
+                ocpQualLevel.set(FormConstant.ID_KEY, quaLevelId);
+                data.ocpQualLevel = ocpQualLevel;
+            }
 
-        // 职位序列
-        data.jobSeq = initialData.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQ);
-        if(data.jobSeq != null) {
-            data.jobSeqNumber = data.jobSeq.getString(FormConstant.NUMBER_KEY);
-            data.jobSeqName = data.jobSeq.getString(FormConstant.NAME_KEY);
-            data.jobSeqEnum = JobSeqEnum.getByCode(data.jobSeqNumber);
         }
+        // 任职信息
+        DynamicObject empPosOrgRel = positionAppointment.getEmpPosOrgRel();
+        if (empPosOrgRel != null) {
+            data.empPosOrgRel = empPosOrgRel;
+            //岗位
+            long positionId = empPosOrgRel.getLong(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY));
+            if(positionId > 0) {
+                DynamicObject position = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBPM_POSITIONHR);
+                position.set(FormConstant.ID_KEY, positionId);
+                position.set(FormConstant.NAME_KEY, empPosOrgRel.getString(String.join(".", FormConstant.POSITION_KEY, FormConstant.NAME_KEY)));
+                data.positionHr = position;
+            }
+
+            long companyId = empPosOrgRel.getLong(String.join(".", FormConstant.COMPANY_KEY, FormConstant.ID_KEY));
+            if(companyId > 0) {
+                DynamicObject company = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+                company.set(FormConstant.ID_KEY, companyId);
+                data.company = company;
+            }
+            long adminOrgId = empPosOrgRel.getLong(String.join(".", FormConstant.ADMINORG, FormConstant.ID_KEY));
+            if(adminOrgId > 0) {
+                DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+                dep.set(FormConstant.ID_KEY, adminOrgId);
+                data.dep = dep;
+            }
 
-        // 职称等级
-        data.proTitleLevel = initialData.getDynamicObject(PositionStructureConstant.NCKD_PROTITLELEVEL);
+            // 职位序列
+            Long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
+            if(jobSeqId > 0) {
+                String jobSeqNumber = empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY));
+                String jobSeqName = empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.NAME_KEY));
+                DynamicObject jobSeq = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBJM_JOBSEQHR);
+                jobSeq.set(FormConstant.ID_KEY, jobSeqId);
+                jobSeq.set(FormConstant.NUMBER_KEY, jobSeqNumber);
+                jobSeq.set(FormConstant.NAME_KEY, jobSeqName);
+                data.jobSeq = jobSeq;
+                data.jobSeqNumber = jobSeqNumber;
+                data.jobSeqName = jobSeqName;
+                data.jobSeqEnum = JobSeqEnum.getByCode(data.jobSeqNumber);
+            }
 
-        // 技能等级
-        data.ocpQualLevel = initialData.getDynamicObject(PositionStructureConstant.NCKD_OCPQUALLEVEL);
 
-        // 学历
-        data.diploma = initialData.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
+
+            //本次加入集团时间
+            data.joinComDate = empPosOrgRel.getDate(String.join(".", FormConstant.HRPI_PERSERLEN, FormConstant.JOINCOMDATE_KEY));
+        }
+        // TODO 【待修改】优秀生
+        // data.isExcellent = initialData.getBoolean(PositionStructureConstant.NCKD_EXCELLENT);
+
 
         // 特定字段由子类处理
         extractSpecificFields(data, initialData);
-
         return data;
     }
 
@@ -186,15 +277,13 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
     protected ScoreData getScoreData(BaseInitialData data, String logPrefix) {
         ScoreData scoreData = new ScoreData();
 
-        StringJoiner selectFields = new StringJoiner(",").add(FormConstant.NAME_KEY)
-                .add(FormConstant.NUMBER_KEY)
-                .add(FormConstant.NCKD_SCORE);
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create().addIdNumberNameWithExtras(FormConstant.NCKD_SCORE);
 
         // 学历积分
         scoreData.educationScore = BigDecimal.ZERO;
         if (data.diploma != null) {
-            DynamicObject education = QueryServiceHelper.queryOne(FormConstant.HBSS_DIPLOMA,
-                    selectFields.toString(),
+            DynamicObject education = BusinessDataServiceHelper.loadSingle(FormConstant.HBSS_DIPLOMA,
+                    queryFieldBuilder.buildSelect(),
                     new QFilter[]{QFilterCommonHelper.getIdEqFilter(data.diploma.getLong(FormConstant.ID_KEY))});
             if (education != null) {
                 scoreData.educationScore = education.getBigDecimal(FormConstant.NCKD_SCORE);
@@ -217,19 +306,21 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
 
         if (data.jobSeqEnum != JobSeqEnum.SKILL && data.proTitleLevel != null) {
             // 非技能序列获取职称积分
-            scoreData.dbProTitleLevel = QueryServiceHelper.queryOne(FormConstant.HBSS_PROTITLELEVEL,
-                    selectFields.toString(),
+            scoreData.dbProTitleLevel = BusinessDataServiceHelper.loadSingle(FormConstant.HBSS_PROTITLELEVEL,
+                    queryFieldBuilder.buildSelect(),
                     new QFilter[]{QFilterCommonHelper.getIdEqFilter(data.proTitleLevel.getLong(FormConstant.ID_KEY))});
             if (scoreData.dbProTitleLevel != null) {
                 scoreData.proTitleScore = scoreData.dbProTitleLevel.getBigDecimal(FormConstant.NCKD_SCORE);
+                data.jobStatusName = null;
             }
         } else if (data.jobSeqEnum == JobSeqEnum.SKILL && data.ocpQualLevel != null) {
             // 技能序列获取技能积分
-            scoreData.dbOcpQualLevel = QueryServiceHelper.queryOne(FormConstant.HBSS_OCPQUALLEVEL,
-                    selectFields.toString(),
+            scoreData.dbOcpQualLevel = BusinessDataServiceHelper.loadSingle(FormConstant.HBSS_OCPQUALLEVEL,
+                    queryFieldBuilder.buildSelect(),
                     new QFilter[]{QFilterCommonHelper.getIdEqFilter(data.ocpQualLevel.getLong(FormConstant.ID_KEY))});
             if (scoreData.dbOcpQualLevel != null) {
                 scoreData.perOcpQualScore = scoreData.dbOcpQualLevel.getBigDecimal(FormConstant.NCKD_SCORE);
+                data.rankName = null;
             }
         }
 
@@ -294,12 +385,13 @@ public abstract class BaseInitialOperationPlugIn extends AbstractOperationServic
         personPosFile.set(PositionStructureConstant.NCKD_DIPLOMASCORE, scoreData.educationScore);
         personPosFile.set(PositionStructureConstant.NCKD_EXECUTEYEAR, DateUtil.getYear(data.beginDate));
         personPosFile.set(PositionStructureConstant.NCKD_ORGINSSCORE, excellentScore);
+        personPosFile.set(PositionStructureConstant.NCKD_EMPLOYMENTYEARS, data.employmentYears);
         personPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, data.causeRemark);
         personPosFile.set(PositionStructureConstant.NCKD_DISABLE, EnableEnum.NO.getCode());
         personPosFile.set(PositionStructureConstant.NCKD_ISCURRENTNEWEST, EnableEnum.YES.getCode());
         personPosFile.set(PositionStructureConstant.STATUS, StatusEnum.C.toString());
         personPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
-        personPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
+
 
         SaveServiceHelper.save(new DynamicObject[]{personPosFile});
     }

+ 107 - 85
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/NewHireInitialOperationPlugIn.java

@@ -11,11 +11,13 @@ import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.hr.psms.business.JobLevelCalculatorService;
-import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 
 import java.math.BigDecimal;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * 新入职人员初定-确认定级操作OP
@@ -38,47 +40,61 @@ public class NewHireInitialOperationPlugIn extends BaseInitialOperationPlugIn {
                 Date currentDate = new Date();
                 for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
                     DynamicObject data = rowDataEntity.getDataEntity();
-
                     //提取信息
-                    BaseInitialData newHireInitialData = (BaseInitialData) extractBasicInfo(data);
-                    Date beginDate = newHireInitialData.beginDate;
-                    DynamicObject person = newHireInitialData.person;
-                    //------ 1.非空校验 ------
-                    if(person == null){
-                        addFatalErrorMessage(rowDataEntity,"请选择人员");
-                        return;
-                    }
-                    if(newHireInitialData.empPosOrgRel == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】无任职信息",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    if (beginDate == null) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能为空",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    DynamicObject positionHr = newHireInitialData.positionHr;
-                    DynamicObject jobSeq = newHireInitialData.jobSeq;
-                    if(positionHr == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("无岗位,请检查当前员工【{}】任职的岗位信息",person.getString(FormConstant.NAME_KEY)));
-                        return;
+                    List<BaseInitialData> newHireInitialDataList =  extractBasicInfo(data);
+
+                    // 检查人员ID重复
+                    Set<Long> personIds = new HashSet<>();
+                    for (BaseInitialData newHireInitialData : newHireInitialDataList) {
+                        if (newHireInitialData.person != null) {
+                            Long personId = newHireInitialData.person.getLong(FormConstant.ID_KEY);
+                            if (!personIds.add(personId)) {
+                                addFatalErrorMessage(rowDataEntity,
+                                        StrFormatter.format("人员【{}】在列表中重复,请保留一条信息",
+                                                newHireInitialData.person.getString(FormConstant.NAME_KEY)));
+                            }
+                        }
                     }
-                    if (jobSeq == null) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("无职位序列,请检查当前员工【{}】任职的岗位【{}】是否有职位序列",person.getString(FormConstant.NAME_KEY),positionHr.getString(FormConstant.NAME_KEY)));
+                    if(!this.getValidateResult().isSuccess()){
                         return;
                     }
 
-                    //------ 3、验证初定时间不能超过当前日期 ------
-                    if(newHireInitialData.joinComDate == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("无本次加入集团日期,请检查当前员工【{}】服务年限信息",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    if (newHireInitialData.joinComDate.after(beginDate)) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能早于本次加入集团日期,本次加入集团时间:【{}】",person.getString(FormConstant.NAME_KEY),DateUtil.format(newHireInitialData.joinComDate, DateUtil.NORM_DATE_PATTERN)));
-                        return;
-                    }
-                    if (beginDate.after(currentDate)) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能晚于当前日期,请检查输入的日期是否正确",person.getString(FormConstant.NAME_KEY)));
-                        return;
+                    for (BaseInitialData newHireInitialData : newHireInitialDataList) {
+                        Date beginDate = newHireInitialData.beginDate;
+                        DynamicObject person = newHireInitialData.person;
+                        //------ 1.非空校验 ------
+                        if(person == null){
+                            addFatalErrorMessage(rowDataEntity,"请选择人员");
+                            return;
+                        }
+                        if(PositionStructureHelper.isInitial(person.getLong(FormConstant.ID_KEY))){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】已初定,不能重复初定",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if(newHireInitialData.empPosOrgRel == null){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】无任职信息",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if (beginDate == null) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能为空",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        DynamicObject positionHr = newHireInitialData.positionHr;
+                        DynamicObject jobSeq = newHireInitialData.jobSeq;
+                        if(positionHr == null){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("无岗位,请检查当前人员【{}】任职的岗位信息",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if (positionHr != null && jobSeq == null) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("无职位序列,请检查当前人员【{}】任职的岗位【{}】是否有职位序列",person.getString(FormConstant.NAME_KEY),positionHr.getString(FormConstant.NAME_KEY)));
+                        }
+
+                        //------ 3、验证初定时间不能超过当前日期 ------
+                        if(newHireInitialData.joinComDate == null){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("无本次加入集团日期,请检查当前人员【{}】服务年限信息",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if (newHireInitialData.joinComDate != null && newHireInitialData.joinComDate.after(beginDate)) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能早于本次加入集团日期,本次加入集团时间:【{}】",person.getString(FormConstant.NAME_KEY),DateUtil.format(newHireInitialData.joinComDate, DateUtil.NORM_DATE_PATTERN)));
+                        }
+                        if (beginDate != null && beginDate.after(currentDate)) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能晚于当前日期,请检查输入的日期是否正确",person.getString(FormConstant.NAME_KEY)));
+                        }
                     }
                 }
             }
@@ -87,9 +103,12 @@ public class NewHireInitialOperationPlugIn extends BaseInitialOperationPlugIn {
 
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
-        logger.info("【职位体系】-新入职人员初定-开始");
-        for (DynamicObject servingInitial : e.getDataEntities()) {
-            processNewHireInitialData(servingInitial);
+        if(this.getOperationResult().isSuccess()) {
+            //对应SHR:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileListHandler#addNewPersonpositionfileInfo
+            logger.info("【职位体系】-新入职人员初定-开始");
+            for (DynamicObject servingInitial : e.getDataEntities()) {
+                processNewHireInitialData(servingInitial);
+            }
         }
     }
 
@@ -110,55 +129,58 @@ public class NewHireInitialOperationPlugIn extends BaseInitialOperationPlugIn {
     private void processNewHireInitialData(DynamicObject newHireInitial) {
 
         //提取信息
-        BaseInitialData data = (BaseInitialData) extractBasicInfo(newHireInitial);
-
-        String logPrefix = StrFormatter.format("【职位体系】-新入职人员初定-员工【{}({})】",
-                data.person.getString(FormConstant.NAME_KEY),
-                data.person.getString(FormConstant.EMP_NUMBER_KEY));
-        logger.info("{}-初定日期【{}】-职位序列【{}】-岗位【{}】",
-                logPrefix,
-                DateUtil.format(data.beginDate, DateUtil.NORM_DATE_PATTERN),
-                data.jobSeqName,
-                data.positionHr.getString(FormConstant.NAME_KEY));
-
-        // 获取上年度绩效结果
-        getPreviousYearPerformance(data, logPrefix);
-
-        // 获取学历、职称、技能对应的积分
-        ScoreData scoreData = getScoreData(data, logPrefix);
-
-        // 优秀生得2分
-        BigDecimal excellentScore = data.isExcellent ? new BigDecimal(2) : BigDecimal.ZERO;
-
-        // 累计总积分 = 学历分 + 职称/技能分 + 优秀生分
-        BigDecimal allSumScore = scoreData.educationScore.add(scoreData.proTitleScore).add(scoreData.perOcpQualScore).add(excellentScore);
-        logger.info("{}-学历分 + 职称/技能分 + 优秀生分 = {}", logPrefix, allSumScore);
-        // 计算累计积分池(生涯积分)
-        BigDecimal sumScore = calculateSumScore(allSumScore, scoreData, logPrefix);
-
-        // 计算职级
-        //当考核结果为无时,职级定为最低级
-        boolean useMinLevel = data.lastYearAppraisalResultEnum == nckd.jxccl.base.common.enums.AppraisalResultEnum.NONE;
-        //【三期需求】-不满足三要素(聘任职称/技能、考核结果、积分)按"无职级"初定
-        String perProTitleNumber = scoreData.dbProTitleLevel != null ? scoreData.dbProTitleLevel.getString(FormConstant.NUMBER_KEY) : null;
-        String quaLevelNumber = scoreData.dbOcpQualLevel != null ? scoreData.dbOcpQualLevel.getString(FormConstant.NUMBER_KEY) : null;
-        boolean threeElementMeet = JobLevelCalculatorService.checkThreeElementsRequirement(data.jobSeq, allSumScore, scoreData.dbProTitleLevel,
-                scoreData.dbOcpQualLevel, data.lastYearAppraisalResultEnum);
-        DynamicObject jobLeve = JobLevelCalculatorService.getJobLevel(
-                data.jobSeq, sumScore, perProTitleNumber,
-                quaLevelNumber,data.downgradeNum,useMinLevel,!threeElementMeet);
-        if(jobLeve != null) {
-            // 构建职位档案并存入数据库
-            createAndSavePersonPosFile(data, scoreData, allSumScore, sumScore, jobLeve, logPrefix, "1");
-
-            //返回定级后的职级
-            setJobLevelResult(data.person, jobLeve);
+        List<BaseInitialData> dataList =  extractBasicInfo(newHireInitial);
+        for (BaseInitialData data : dataList) {
+
+            String logPrefix = StrFormatter.format("【职位体系】-新入职人员初定-人员【{}({})】",
+                    data.person.getString(FormConstant.NAME_KEY),
+                    data.person.getString(FormConstant.EMP_NUMBER_KEY));
+            logger.info("{}-初定日期【{}】-职位序列【{}】-岗位【{}】",
+                    logPrefix,
+                    DateUtil.format(data.beginDate, DateUtil.NORM_DATE_PATTERN),
+                    data.jobSeqName,
+                    data.positionHr.getString(FormConstant.NAME_KEY));
+
+            // 获取上年度绩效结果
+            getPreviousYearPerformance(data, logPrefix);
+
+            // 获取学历、职称、技能对应的积分
+            ScoreData scoreData = getScoreData(data, logPrefix);
+
+            // 优秀生得2分
+            BigDecimal excellentScore = data.isExcellent ? new BigDecimal(2) : BigDecimal.ZERO;
+
+            // 累计总积分 = 学历分 + 职称/技能分 + 优秀生分
+            BigDecimal allSumScore = scoreData.educationScore.add(scoreData.proTitleScore).add(scoreData.perOcpQualScore).add(excellentScore);
+            logger.info("{}-学历分 + 职称/技能分 + 优秀生分 = {}", logPrefix, allSumScore);
+            // 计算累计积分池(生涯积分)
+            BigDecimal sumScore = calculateSumScore(allSumScore, scoreData, logPrefix);
+
+            // 计算职级
+            //当考核结果为无时,职级定为最低级
+            boolean useMinLevel = data.lastYearAppraisalResultEnum == nckd.jxccl.base.common.enums.AppraisalResultEnum.NONE;
+            //【三期需求】-不满足三要素(聘任职称/技能、考核结果、积分)按"无职级"初定
+            String perProTitleNumber = scoreData.dbProTitleLevel != null ? scoreData.dbProTitleLevel.getString(FormConstant.NUMBER_KEY) : null;
+            String quaLevelNumber = scoreData.dbOcpQualLevel != null ? scoreData.dbOcpQualLevel.getString(FormConstant.NUMBER_KEY) : null;
+            boolean threeElementMeet = JobLevelCalculatorService.checkThreeElementsRequirement(data.jobSeq, allSumScore, scoreData.dbProTitleLevel,
+                    scoreData.dbOcpQualLevel, data.lastYearAppraisalResultEnum);
+            DynamicObject jobLeve = JobLevelCalculatorService.getJobLevel(
+                    data.jobSeq, allSumScore, perProTitleNumber,
+                    quaLevelNumber,data.downgradeNum,useMinLevel,!threeElementMeet);
+            if(jobLeve != null) {
+                // 构建职位档案并存入数据库
+                createAndSavePersonPosFile(data, scoreData, allSumScore, sumScore, jobLeve, logPrefix, "1");
+
+                //返回定级后的职级
+                setJobLevelResult(data.person, jobLeve);
+
+                //TODO 【待修改】 这里可能有协同,初定完成后需要将待办任务置为已完成
+            }
         }
+
     }
 
     @Override
     protected void extractSpecificFields(BaseInitialData data, DynamicObject initialData) {
-        // 本次加入集团时间
-        data.joinComDate = initialData.getDate(PositionStructureConstant.NCKD_JOINCOMDATE);
     }
 }

+ 104 - 85
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/initial/ServingInitialOperationPlugIn.java

@@ -18,6 +18,9 @@ import nckd.jxccl.hr.psms.helper.PositionStructureHelper;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * 在职人员初定-确认定级操作OP
@@ -39,59 +42,71 @@ public class ServingInitialOperationPlugIn extends BaseInitialOperationPlugIn {
                 Date currentDate = new Date();
                 for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
                     DynamicObject data = rowDataEntity.getDataEntity();
-
                     //提取信息
-                    BaseInitialData servingInitialData = (BaseInitialData) extractBasicInfo(data);
-                    Date beginDate = servingInitialData.beginDate;
-                    DynamicObject person = servingInitialData.person;
-                    //------ 1.非空校验 ------
-                    if(person == null){
-                        addFatalErrorMessage(rowDataEntity,"请选择人员");
-                        return;
+                    List<BaseInitialData> servingInitialDataList = extractBasicInfo(data);
+
+                    // 检查人员ID重复
+                    Set<Long> personIds = new HashSet<>();
+                    for (BaseInitialData servingInitialData : servingInitialDataList) {
+                        if (servingInitialData.person != null) {
+                            Long personId = servingInitialData.person.getLong(FormConstant.ID_KEY);
+                            if (!personIds.add(personId)) {
+                                addFatalErrorMessage(rowDataEntity,
+                                        StrFormatter.format("人员【{}】在列表中重复,请保留一条信息",
+                                                servingInitialData.person.getString(FormConstant.NAME_KEY)));
+                                return;
+                            }
+                        }
                     }
-                    if(servingInitialData.empPosOrgRel == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】无任职信息",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    if (beginDate == null) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能为空",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    BigDecimal allSumScore = servingInitialData.allSumScore;
-                    if(allSumScore == null || allSumScore.compareTo(BigDecimal.ZERO) == 0){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】请填写总积分",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    DynamicObject positionHr = servingInitialData.positionHr;
-                    DynamicObject jobSeq = servingInitialData.jobSeq;
-                    if(positionHr == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("无岗位,请检查当前员工【{}】任职的岗位信息",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    if (jobSeq == null) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("无职位序列,请检查当前员工【{}】任职的岗位【{}】是否有职位序列",person.getString(FormConstant.NAME_KEY),positionHr.getString(FormConstant.NAME_KEY)));
+                    if(!this.getValidateResult().isSuccess()){
                         return;
                     }
 
-                    //------ 2、判断是否已存在初定记录 ------
-                    boolean exists = PositionStructureHelper.isInitial(person.getLong(FormConstant.ID_KEY));
-                    if(exists){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】已初定,不能重复初定",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
-                    //------ 3、验证初定时间不能超过当前日期 ------
-                    if (beginDate.after(currentDate)) {
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("员工【{}】初定时间不能晚于当前日期,请检查输入的日期是否正确",person.getString(FormConstant.NAME_KEY)));
-                        return;
+                    for (BaseInitialData servingInitialData : servingInitialDataList) {
+                        Date beginDate = servingInitialData.beginDate;
+                        DynamicObject person = servingInitialData.person;
+                        //------ 1.非空校验 ------
+                        if(person == null){
+                            addFatalErrorMessage(rowDataEntity,"请选择人员");
+                            return;
+                        }
+                        if(PositionStructureHelper.isInitial(person.getLong(FormConstant.ID_KEY))){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】已初定,不能重复初定",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if(servingInitialData.empPosOrgRel == null){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】无任职信息",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if (beginDate == null) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能为空",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        BigDecimal allSumScore = servingInitialData.allSumScore;
+                        if(allSumScore == null || allSumScore.compareTo(BigDecimal.ZERO) == 0){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】请填写总积分",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        DynamicObject positionHr = servingInitialData.positionHr;
+                        DynamicObject jobSeq = servingInitialData.jobSeq;
+                        if(positionHr == null){
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("无岗位,请检查当前人员【{}】任职的岗位信息",person.getString(FormConstant.NAME_KEY)));
+                        }
+                        if (positionHr != null && jobSeq == null) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("无职位序列,请检查当前人员【{}】任职的岗位【{}】是否有职位序列",person.getString(FormConstant.NAME_KEY),positionHr.getString(FormConstant.NAME_KEY)));
+                        }
+
+                        //------ 3、验证初定时间不能超过当前日期 ------
+                        if (beginDate != null && beginDate.after(currentDate)) {
+                            addFatalErrorMessage(rowDataEntity,StrFormatter.format("人员【{}】初定时间不能晚于当前日期,请检查输入的日期是否正确",person.getString(FormConstant.NAME_KEY)));
+                        }
+
+                        //------ 4.获取上年度绩效结果 ------
+                        if(beginDate != null) {
+                            LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(beginDate), 1);
+                            DynamicObject lastYearPerformanceResult = PerformanceManagerHelper.getPerformanceResult(person.getLong(FormConstant.ID_KEY), lastYearDateTime);
+                            if (lastYearPerformanceResult == null) {
+                                addFatalErrorMessage(rowDataEntity, StrFormatter.format("人员【{}】没有上年度考核结果,无法初定", person.getString(FormConstant.NAME_KEY)));
+                            }
+                        }
                     }
 
-                    //------ 4.获取上年度绩效结果 ------
-                    LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(beginDate), 1);
-                    DynamicObject lastYearPerformanceResult = PerformanceManagerHelper.getPerformanceResult(person.getLong(FormConstant.ID_KEY), lastYearDateTime);
-                    if(lastYearPerformanceResult == null){
-                        addFatalErrorMessage(rowDataEntity,StrFormatter.format("员工【{}】没有上年度考核结果,无法初定",person.getString(FormConstant.NAME_KEY)));
-                        return;
-                    }
                 }
             }
         });
@@ -99,9 +114,12 @@ public class ServingInitialOperationPlugIn extends BaseInitialOperationPlugIn {
 
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs e) {
-        logger.info("【职位体系】-在职人员初定-开始");
-        for (DynamicObject servingInitial : e.getDataEntities()) {
-            processServingInitial(servingInitial);
+        if(this.getOperationResult().isSuccess()) {
+            //对应SHR:com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileListHandler#addNewPersonpositionfileInfo_older
+            logger.info("【职位体系】-在职人员初定-开始");
+            for (DynamicObject servingInitial : e.getDataEntities()) {
+                processServingInitial(servingInitial);
+            }
         }
     }
 
@@ -121,42 +139,43 @@ public class ServingInitialOperationPlugIn extends BaseInitialOperationPlugIn {
      */
     private void processServingInitial(DynamicObject servingInitial) {
         // 提取基本信息
-        BaseInitialData data = extractBasicInfo(servingInitial);
-
-        String logPrefix = StrFormatter.format("【职位体系】-在职人员初定-员工【{}({})】",
-                data.person.getString(FormConstant.NAME_KEY),
-                data.person.getString(FormConstant.EMP_NUMBER_KEY));
-        logger.info("{}-初定日期【{}】-职位序列【{}】-岗位【{}】-总积分【{}】",
-                logPrefix,
-                DateUtil.format(data.beginDate, DateUtil.NORM_DATE_PATTERN),
-                data.jobSeqName,
-                data.positionHr.getString(FormConstant.NAME_KEY),
-                data.allSumScore.toString());
-
-        // 获取上年度绩效结果
-        getPreviousYearPerformance(data, logPrefix);
-
-        // 获取学历、职称、技能对应的积分
-        ScoreData scoreData = getScoreData(data, logPrefix);
-
-        // 计算累计积分池(生涯积分)
-        BigDecimal sumScore = calculateSumScore(data.allSumScore, scoreData, logPrefix);
-
-        // 计算职级
-        //当考核结果为无时,职级定为最低级
-        boolean useMinLevel = data.lastYearAppraisalResultEnum == nckd.jxccl.base.common.enums.AppraisalResultEnum.NONE;
-        String perProTitleNumber = scoreData.dbProTitleLevel != null ? scoreData.dbProTitleLevel.getString(FormConstant.NUMBER_KEY) : null;
-        String quaLevelNumber = scoreData.dbOcpQualLevel != null ? scoreData.dbOcpQualLevel.getString(FormConstant.NUMBER_KEY) : null;
-        DynamicObject jobLeve = JobLevelCalculatorService.getJobLevel(
-                data.jobSeq, sumScore, perProTitleNumber,
-                quaLevelNumber,  data.downgradeNum,useMinLevel,Boolean.FALSE);
-
-        if(jobLeve != null) {
-            // 构建职位档案并存入数据库
-            createAndSavePersonPosFile(data, scoreData, data.allSumScore, sumScore, jobLeve, logPrefix, "2");
-
-            //返回定级后的职级。张三:职位级为"初级(1)"
-            setJobLevelResult(data.person, jobLeve);
+        List<BaseInitialData> dataList = extractBasicInfo(servingInitial);
+        for (BaseInitialData data : dataList) {
+            String logPrefix = StrFormatter.format("【职位体系】-在职人员初定-人员【{}({})】",
+                    data.person.getString(FormConstant.NAME_KEY),
+                    data.person.getString(FormConstant.EMP_NUMBER_KEY));
+            logger.info("{}-初定日期【{}】-职位序列【{}】-岗位【{}】-总积分【{}】",
+                    logPrefix,
+                    DateUtil.format(data.beginDate, DateUtil.NORM_DATE_PATTERN),
+                    data.jobSeqName,
+                    data.positionHr.getString(FormConstant.NAME_KEY),
+                    data.allSumScore.toString());
+
+            // 获取上年度绩效结果
+            getPreviousYearPerformance(data, logPrefix);
+
+            // 获取学历、职称、技能对应的积分
+            ScoreData scoreData = getScoreData(data, logPrefix);
+
+            // 计算累计积分池(生涯积分)
+            BigDecimal sumScore = calculateSumScore(data.allSumScore, scoreData, logPrefix);
+
+            // 计算职级
+            //当考核结果为无时,职级定为最低级
+            boolean useMinLevel = data.lastYearAppraisalResultEnum == nckd.jxccl.base.common.enums.AppraisalResultEnum.NONE;
+            String perProTitleNumber = scoreData.dbProTitleLevel != null ? scoreData.dbProTitleLevel.getString(FormConstant.NUMBER_KEY) : null;
+            String quaLevelNumber = scoreData.dbOcpQualLevel != null ? scoreData.dbOcpQualLevel.getString(FormConstant.NUMBER_KEY) : null;
+            DynamicObject jobLeve = JobLevelCalculatorService.getJobLevel(
+                    data.jobSeq, data.allSumScore, perProTitleNumber,
+                    quaLevelNumber,  data.downgradeNum,useMinLevel,Boolean.FALSE);
+
+            if(jobLeve != null) {
+                // 构建职位档案并存入数据库
+                createAndSavePersonPosFile(data, scoreData, data.allSumScore, sumScore, jobLeve, logPrefix, "2");
+
+                //返回定级后的职级。张三:职位级为"初级(1)"
+                setJobLevelResult(data.person, jobLeve);
+            }
         }
     }
 

+ 32 - 11
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportFormPlugin.java

@@ -1,19 +1,29 @@
 package nckd.jxccl.hr.psms.plugin.report.adjust;
 
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.report.FilterItemInfo;
+import kd.bos.entity.report.ReportQueryParam;
+import kd.bos.filter.CommonFilterColumn;
+import kd.bos.filter.FilterColumn;
 import kd.bos.form.CloseCallBack;
 import kd.bos.form.FormShowParameter;
 import kd.bos.form.ShowType;
+import kd.bos.form.control.events.FilterContainerInitEvent;
 import kd.bos.form.control.events.ItemClickEvent;
 import kd.bos.form.control.events.ItemClickListener;
 import kd.bos.form.events.ClosedCallBackEvent;
+import kd.bos.form.events.FilterContainerSearchClickArgs;
 import kd.bos.report.ReportList;
+import kd.bos.report.events.CreateFilterInfoEvent;
 import kd.bos.report.plugin.AbstractReportFormPlugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.ArrayList;
 import java.util.EventObject;
+import java.util.List;
+import java.util.Map;
 
 /**
 * 未生成动态调整-报表插件
@@ -23,6 +33,11 @@ import java.util.EventObject;
 */
 public class UnAdjustedReportFormPlugin extends AbstractReportFormPlugin implements ItemClickListener {
 
+    @Override
+    public void initialize() {
+        super.initialize();
+    }
+
     @Override
     public void afterBindData(EventObject e) {
         /*super.afterBindData(e);
@@ -39,12 +54,26 @@ public class UnAdjustedReportFormPlugin extends AbstractReportFormPlugin impleme
     public void itemClick(ItemClickEvent evt) {
         String itemKey = evt.getItemKey();
         String operationKey = evt.getOperationKey();
-        if("nckd_new".equalsIgnoreCase(itemKey)){
+        if("nckd_new".equalsIgnoreCase(itemKey) || "nckd_newbatch".equalsIgnoreCase(itemKey)){
             //创建调整
             ReportList reportList = this.getView().getControl(FormConstant.REPORTLISTAP);
             //获取报表选中的行数据
             int[] selectedRowIndexes = reportList.getEntryState().getSelectedRows();
-            if(selectedRowIndexes.length == 1){
+            if(selectedRowIndexes.length > 1 || "nckd_newbatch".equals(itemKey)){
+                //多人调整
+                List<Long> personIds = new ArrayList<>();
+                for (int selectedRowIndex : selectedRowIndexes) {
+                    DynamicObject rowData = reportList.getReportModel().getRowData(selectedRowIndex);
+                    personIds.add(rowData.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY));
+                }
+                FormShowParameter showParameter = new FormShowParameter();
+                showParameter.setFormId(PositionStructureConstant.NEWDYNAMICADJUBATCH_ENTITYID);
+                showParameter.getOpenStyle().setShowType(ShowType.Modal);
+                showParameter.setCaption("批量调整");
+                showParameter.setCustomParam(FormConstant.NCKD_PERSON,personIds);
+                showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.NEWDYNAMICADJUBATCH_ENTITYID));
+                this.getView().showForm(showParameter);
+            }else if(selectedRowIndexes.length == 1){{
                 DynamicObject rowData = reportList.getReportModel().getRowData(selectedRowIndexes[0]);
                 //单人调整
                 FormShowParameter showParameter = new FormShowParameter();
@@ -54,16 +83,8 @@ public class UnAdjustedReportFormPlugin extends AbstractReportFormPlugin impleme
                 showParameter.setCustomParam(FormConstant.NCKD_PERSON,rowData.getDynamicObject(FormConstant.NCKD_PERSON).getLong(FormConstant.ID_KEY));
                 showParameter.setCloseCallBack(new CloseCallBack(this, PositionStructureConstant.NEWDYNAMICADJUDIALOG_ENTITYID));
                 this.getView().showForm(showParameter);
-            }else if(selectedRowIndexes.length > 1){
-                //多人调整
-                FormShowParameter showParameter = new FormShowParameter();
-                showParameter.setFormId(PositionStructureConstant.SERVINGINITIAL_ENTITYID);
-                showParameter.getOpenStyle().setShowType(ShowType.Modal);
-                showParameter.setCaption("批量调整");
-                this.getView().showForm(showParameter);
-            }else{
+            }}else{
                 this.getView().showErrorNotification("请选择要调整的员工");
-                return;
             }
         }
     }

+ 266 - 26
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/report/adjust/UnAdjustedReportReportListDataPlugin.java

@@ -1,44 +1,284 @@
 package nckd.jxccl.hr.psms.plugin.report.adjust;
 
 import kd.bos.algo.DataSet;
-import kd.bos.entity.report.AbstractReportListDataPluginExt;
-import kd.bos.event.AfterQueryEvent;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.entity.report.AbstractReportListDataPlugin;
+import kd.bos.entity.report.FastFilter;
+import kd.bos.entity.report.FilterItemInfo;
+import kd.bos.entity.report.ReportQueryParam;
+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.ConvertUtil;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 
+import java.util.List;
+import java.util.Map;
 import java.util.StringJoiner;
+import java.util.stream.Collectors;
 
 /**
-* 未生成动态调整-查询扩展插件
-* @author W.Y.C
-* @date 2025/9/17 17:01
-* @version 1.0
-*/
-public class UnAdjustedReportReportListDataPlugin extends AbstractReportListDataPluginExt {
-
+ * 未生成动态调整-查询插件
+ * @author W.Y.C
+ * @date 2025/10/10 19:51
+ * @version 1.0
+ */
+public class UnAdjustedReportReportListDataPlugin extends AbstractReportListDataPlugin {
     @Override
-    public void afterQuery(AfterQueryEvent event) {
-        //由于普通查询列表无法对两个实体的字段对比过滤,这里使用报表方式实现。查询结果之后使用algo进一步过滤数据
+    public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable {
+        // 构建查询字段
+        QueryFieldBuilder queryFieldBuilder = buildQueryFieldBuilder();
+
+        // 构建基础查询条件
+        QFilter qFilter = buildBaseQueryFilter();
+
+        // 处理快速过滤条件
+        processFastFilter(reportQueryParam, qFilter);
+
+        //其他过滤条件
+        processFilter(reportQueryParam, qFilter);
+
+
+        // 执行基础查询
+        DataSet dataSet = QueryServiceHelper.queryDataSet(PositionStructureConstant.PERSONPOSFILE_ENTITYID, "unadjustquery",
+                queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, null,10000);
+
+        // 添加扩展过滤条件
+        DataSet filteredDataSet = addExtendedFilters(dataSet);
+
+        // 添加调整类型字段
+        DataSet dataSetWithAdjustTypes = addAdjustTypeFields(filteredDataSet);
+
+        // 处理报表过滤条件
+        String filterCondition = processOtherFilter(reportQueryParam);
+
+        return dataSetWithAdjustTypes.filter(filterCondition);
+    }
+
+    /**
+     * 构建查询字段
+     */
+    private QueryFieldBuilder buildQueryFieldBuilder() {
+        return QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .addGroup(new String[]{FormConstant.ASSIGNMENT_ENTITYID}, FormConstant.IS_PRIMARY)
+                .addGroup(new String[]{FormConstant.EMPLOYEE_KEY}, FormConstant.EMP_NUMBER_KEY, FormConstant.NAME_KEY)
+                .addGroup(new String[]{PositionStructureConstant.PERSONPOSFILE_ENTITYID},
+                        FormConstant.USEORG_KEY,
+                        FormConstant.ORG_KEY,
+                        PositionStructureConstant.NCKD_POSITIONHR,
+                        PositionStructureConstant.NCKD_JOBSEQHR,
+                        PositionStructureConstant.NCKD_PROTITLELEVEL,
+                        PositionStructureConstant.NCKD_OCPQUALLEVEL,
+                        PositionStructureConstant.NCKD_DIPLOMA,
+                        PositionStructureConstant.NCKD_ALLSUMSCORE,
+                        PositionStructureConstant.NCKD_BEGINDATE,
+                        PositionStructureConstant.NCKD_JOBLEVELHR,
+                        PositionStructureConstant.NCKD_PERSON)
+                .addGroup(new String[]{FormConstant.HRPI_PERPROTITLE}, FormConstant.PROLEVEL_KEY)
+                .addGroup(new String[]{FormConstant.HRPI_PEROCPQUAL}, FormConstant.QUALIFICATION_KEY)
+                .addGroup(new String[]{FormConstant.HRPI_PEREDUEXP}, FormConstant.EDUCATION_KEY)
+                .add(FormConstant.COMPANY_KEY)
+                .addIdNumberName(FormConstant.COMPANY_KEY)
+                .add(FormConstant.ADMINORG)
+                .addIdNumberName(FormConstant.POSITION_KEY)
+                .addGroup(new String[]{FormConstant.HBPM_POSITIONHR}, PositionStructureConstant.NCKD_JOBSEQ)
+                .addGroup(new String[]{FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ}, FormConstant.NUMBER_KEY);
+    }
+
+    /**
+     * 构建基础查询条件
+     */
+    private QFilter buildBaseQueryFilter() {
+        return new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID,
+                PositionStructureConstant.NCKD_ISCURRENTNEWEST), QCP.equals, EnableEnum.YES.getCode())
+                .and(new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID,
+                        PositionStructureConstant.NCKD_DISABLE), QCP.equals, EnableEnum.NO.getCode()))
+                .and(new QFilter(String.join(".", FormConstant.ASSIGNMENT_ENTITYID, FormConstant.IS_PRIMARY),
+                        QCP.equals, EnableEnum.YES.getCode()))
+                .and(new QFilter(String.join(".", FormConstant.HRPI_PERPROTITLE, "iscompany"),
+                        QCP.equals, EnableEnum.YES.getCode()))
+                .and(new QFilter(String.join(".", FormConstant.HRPI_PEROCPQUAL, "ismajor"),
+                        QCP.equals, EnableEnum.YES.getCode()))
+                .and(new QFilter(FormConstant.IS_PRIMARY, QCP.equals, EnableEnum.YES.getCode()))
+                .and(new QFilter(String.join(".", FormConstant.HRPI_PEREDUEXP, "ishighestdegree"),
+                        QCP.equals, EnableEnum.YES.getCode()));
+    }
+
+    /**
+     * 处理快速过滤条件
+     */
+    private void processFastFilter(ReportQueryParam reportQueryParam, QFilter qFilter) {
+        FastFilter fastFilter = reportQueryParam.getFilter().getFastFilter();
+        if (fastFilter != null) {
+            List<Map<String, List<Object>>> fastFilterList = fastFilter.getFastFilter();
+            for (Map<String, List<Object>> stringListMap : fastFilterList) {
+                //nckd_empnumfastfilter转为employee.empnumber,nckd_namefastfilter转换为employee.name,转换后添加到fields中
+                List<Object> fieldList = stringListMap.get("FieldName");
+                List<Object> valueList = stringListMap.get("Value");
+                String[] fields = new String[fieldList.size()];
+                for (int i = 0; i < fieldList.size(); i++) {
+                    fields[i] = fieldList.get(i).toString()
+                            .replace("nckd_empnumfastfilter", "employee.empnumber")
+                            .replace("nckd_namefastfilter", "employee.name");
+                }
+                //valueList转到values
+                String[] values = new String[valueList.size()];
+                for (int i = 0; i < valueList.size(); i++) {
+                    values[i] = valueList.get(i).toString();
+                }
+                qFilter.and(QFilter.ftlike(values, fields));
+            }
+        }
+    }
+
+    /**
+     * 处理过滤条件
+     */
+    private void processFilter(ReportQueryParam reportQueryParam, QFilter qFilter) {
+        List<QFilter> qFilters = reportQueryParam.getFilter().getQFilters();
+        for (QFilter filter : qFilters) {
+            //由于页面命名限制不允许配置对应数据库字段(例如:position.id的格式),这里只能手动转换为数据库实际的字段
+            String property = filter.getProperty();
+            if ("nckd_positionfilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", FormConstant.POSITION_KEY, FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_orgfilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", FormConstant.COMPANY_KEY, FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_joblevelhrffilter.id".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_JOBLEVELHR,FormConstant.ID_KEY),filter.getCP(),filter.getValue()));
+            }else if("nckd_begindatefilter".equalsIgnoreCase(property)){
+                qFilter.and(new QFilter(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_BEGINDATE),filter.getCP(),filter.getValue()));
+            }
+        }
+    }
 
-        //TODO 【待修改】-快速筛选不生效
-        DataSet dataSet = event.getDataSet();
-        // 对标准报表结果进行扩展:新增过滤条件
+    /**
+     * 添加扩展过滤条件
+     */
+    private DataSet addExtendedFilters(DataSet dataSet) {
         StringJoiner filters = new StringJoiner(" and ");
-        //主组织分配
-        filters.add("hrpi_assignment.isprimary = true");
         //职位档案不为空
-        filters.add("nckd_personposfile.nckd_person is not null");
+        filters.add(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_PERSON) + " is not null");
+
         StringJoiner joinOr = new StringJoiner(" or ");
         //职称等级变化
-        joinOr.add("nckd_personposfile.nckd_protitlelevel <> hrpi_perprotitle.prolevel");
-        //技能等级不变化
-        joinOr.add("nckd_personposfile.nckd_ocpquallevel <> hrpi_perocpqual.qualification");
+        joinOr.add("(" + String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY) + " <> '03' and " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_PROTITLELEVEL) + " <> " +
+                String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY) + ")");
+        //技能等级变化
+        joinOr.add("(" + String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY) + " = '03' and " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_OCPQUALLEVEL) + " <> " +
+                String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY) + ")");
         //学历变化
-        joinOr.add("nckd_personposfile.nckd_diploma <> hrpi_pereduexp.education");
+        joinOr.add(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_DIPLOMA) + " <> " +
+                String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY));
         //公司变化
-        joinOr.add("nckd_personposfile.useorg <> hrpi_empposorgrel.company");
+        joinOr.add(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.USEORG_KEY) + " <> " + FormConstant.COMPANY_KEY);
+        //部门变化
+        joinOr.add(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.ORG_KEY) + " <> " + FormConstant.ADMINORG);
         //序列变化
-        // filters.add("nckd_personposfile.nckd_diploma <> hrpi_pereduexp.education");
-        filters.add("("+joinOr.toString()+")");
-        DataSet filter = dataSet.filter(filters.toString());
-        event.setDataSet(filter);
+        joinOr.add(String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_JOBSEQHR) + " <> " +
+                String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ));
+
+        filters.add("(" + joinOr.toString() + ")");
+        return dataSet.filter(filters.toString());
+    }
+
+    /**
+     * 添加调整类型字段
+     */
+    private DataSet addAdjustTypeFields(DataSet dataSet) {
+        DataSet newDataSet = dataSet.addField("CASE WHEN " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_DIPLOMA) +
+                " <> " +
+                String.join(".", FormConstant.HRPI_PEREDUEXP, FormConstant.EDUCATION_KEY) +
+                " THEN '学历变化' END", "a");
+
+        newDataSet = newDataSet.addField("CASE WHEN (" +
+                String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY) +
+                " <> '03' and " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_PROTITLELEVEL) +
+                " <> " +
+                String.join(".", FormConstant.HRPI_PERPROTITLE, FormConstant.PROLEVEL_KEY) +
+                ") THEN '职称变化' END", "b");
+
+        newDataSet = newDataSet.addField("CASE WHEN (" +
+                String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY) +
+                " = '03' and " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_OCPQUALLEVEL) +
+                " <> " +
+                String.join(".", FormConstant.HRPI_PEROCPQUAL, FormConstant.QUALIFICATION_KEY) +
+                ") THEN '技能变化' END", "c");
+
+        newDataSet = newDataSet.addField("CASE WHEN " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.USEORG_KEY) +
+                " <> " + FormConstant.COMPANY_KEY +
+                " THEN '跨单位变动' END", "d");
+
+        newDataSet = newDataSet.addField("CASE WHEN " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, FormConstant.ORG_KEY) +
+                " <> " + FormConstant.ADMINORG +
+                " THEN '单位内变动' END", "e");
+
+        newDataSet = newDataSet.addField("CASE WHEN " +
+                String.join(".", PositionStructureConstant.PERSONPOSFILE_ENTITYID, PositionStructureConstant.NCKD_JOBSEQHR) +
+                " <> " +
+                String.join(".", FormConstant.HBPM_POSITIONHR, PositionStructureConstant.NCKD_JOBSEQ) +
+                " THEN '跨序列变动' END", "f");
+
+        // 合并调整类型字符串
+        newDataSet = newDataSet.addField(buildAdjustTypeConcatExpression(), "nckd_adjusttypestr");
+        return newDataSet;
     }
+
+    /**
+     * 构建调整类型连接表达式
+     */
+    private String buildAdjustTypeConcatExpression() {
+        return "CONCAT(" +
+                "CASE WHEN a IS NOT NULL AND a != '' THEN CONCAT(a,',') ELSE '' END," +
+                "CASE WHEN b IS NOT NULL AND b != '' THEN CONCAT(b,',') ELSE '' END," +
+                "CASE WHEN c IS NOT NULL AND c != '' THEN CONCAT(c,',') ELSE '' END," +
+                "CASE WHEN d IS NOT NULL AND d != '' THEN CONCAT(d,',') ELSE '' END," +
+                "CASE WHEN e IS NOT NULL AND e != '' THEN CONCAT(e,',') ELSE '' END," +
+                "CASE WHEN f IS NOT NULL AND f != '' THEN CONCAT(f,',') ELSE '' END" +
+                ")";
+    }
+
+    /**
+     * 处理报表过滤条件
+     */
+    private String processOtherFilter(ReportQueryParam reportQueryParam) {
+        StringJoiner joinAnd = new StringJoiner(" and ");
+        joinAnd.add("1=1");
+
+        List<FilterItemInfo> filterItems = reportQueryParam.getFilter().getFilterItems();
+        for (FilterItemInfo filterItem : filterItems) {
+            String propName = filterItem.getPropName();
+            if ("nckd_adjusttypefilter".equalsIgnoreCase(propName)) {
+                processAdjustTypeFilter(filterItem, joinAnd);
+            }
+        }
+
+        return joinAnd.toString();
+    }
+
+    /**
+     * 处理调整类型过滤条件
+     */
+    private void processAdjustTypeFilter(FilterItemInfo filterItem, StringJoiner joinAnd) {
+        if (filterItem.getValue() instanceof List) {
+            List<String> values = ConvertUtil.toList(filterItem.getValue());
+            StringJoiner or = new StringJoiner(" or ");
+            for (String value : values) {
+                or.add("nckd_adjusttypestr like '%" + value + "%'");
+            }
+            joinAnd.add("(" + or.toString() + ")");
+        } else {
+            joinAnd.add("nckd_adjusttypestr like '%" + filterItem.getValue() + "%'");
+        }
+    }
+
 }

+ 5 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/PerfManagerWizardFormPlugin.java

@@ -94,6 +94,11 @@ public class PerfManagerWizardFormPlugin extends AbstractFormPlugin implements T
                 break;
             case BTN_PREV_KEY:
                 this.previous();
+            default:
+                // 处理未预期的按钮点击事件
+                this.getView().showTipNotification(ResManager.loadKDString(
+                        "不支持的操作", "WizardPlugin_unsupported_action", "bos-designer-plugin", new Object[0]));
+                break;
         }
 
     }