瀏覽代碼

feat(hr): 年度调整服务优化及绩效排名管理增强

- 将调试日志级别从 debug 提升为 info,增强关键流程追踪
- 完善异常处理逻辑,统一错误信息记录与抛出方式
- 新增保级原因详细日志记录,明确各类保级场景
- 重构绩效排名管理表单插件,分离计算逻辑并优化界面交互
- 增强排名人数统计功能,支持按配置比例自动计算期望人数
- 修复保存和提交操作时排名计算缺失的问题
- 优化向导步骤切换后的视图刷新与数据展示控制
- 强化表格列排序逻辑,确保排名相关字段正确排序
- 移除无用的 UserServiceHelper 导入依赖
wyc 11 小時之前
父節點
當前提交
6f064484b6

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

@@ -8,7 +8,6 @@ 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.user.UserServiceHelper;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
 import nckd.jxccl.base.common.enums.psms.AdjustTypeEnum;
@@ -67,14 +66,14 @@ public class AnnualAdjustmentService {
             if (adjustDateTime.getMonthValue() == 1 && adjustDateTime.getDayOfMonth() == 1) {
                 //前端只选年份,这里默认为XXX-07-01
                 adjustDate = DateUtil.toDate(adjustDateTime.withMonth(7).withDayOfMonth(1));
-                logger.debug("调整日期为年初日期,已转换为当年7月1日");
+                logger.info("调整日期为年初日期,已转换为当年7月1日");
             }
         }else{
             //获取当年07-01的日期
             /*LocalDate julyFirst = LocalDate.of(DateUtil.getYear(new Date()), 7, 1);
             adjustDate = Date.from(julyFirst.atStartOfDay(ZoneId.systemDefault()).toInstant());*/
             adjustDate = new Date();
-            logger.debug("未指定调整日期,默认使用当前日期");
+            logger.info("未指定调整日期,默认使用当前日期");
         }
         String adjustDateStr = DateUtil.format(adjustDate,DateUtil.NORM_DATE_PATTERN);
         AdjustmentContext ac = initAndLoad(executeYear, person, beginDate,adjustDate,positionAppointment,performanceResult,firstPersonPosFile);
@@ -85,12 +84,12 @@ public class AnnualAdjustmentService {
         //2、 判断是否为本年首次调整、考核是否已被使用,并根据规则决定是否使用R排名。
         //对应SHR:341~391行
         evaluateFirstAndAppraisalUsage(ac);
-        logger.debug("评估是否为本年首次调整及考核使用情况完成");
+        logger.info("评估是否为本年首次调整及考核使用情况完成");
 
         // 3、加载上一条记录并校验生效日期。
         //对应SHR:393~421行
         loadLastRecordAndValidateBeginDate(ac);
-        logger.debug("加载上一条记录并校验生效日期完成");
+        logger.info("加载上一条记录并校验生效日期完成");
 
         // 4、处理职位序列(如果是管理序列,则按职能序列进行调整)
         //对应SHR:521~527行
@@ -98,18 +97,18 @@ public class AnnualAdjustmentService {
         ac.convertJobSeq = convertJobSeq;
         DynamicObject convertLastJobSeq = JobLevelCalculatorService.handleJobSeq(ac.data.getLastJobSeq());
         ac.data.setConvertLastJobSeq(convertLastJobSeq);
-        logger.debug("处理职位序列完成");
+        logger.info("处理职位序列完成");
         // 5、获取技能/职称分
         //对应SHR:451~481行
         JobLevelCalculatorService.JobScoreInfo jobScoreInfo = JobLevelCalculatorService.handleJobScores(ac.lastRecordInfo,convertJobSeq, ac.positionAppointment);
         ac.data.setJobScoreInfo(jobScoreInfo);
-        logger.debug("获取技能/职称分完成");
+        logger.info("获取技能/职称分完成");
 
         // 6、计算学历得分并生成说明
         //对应SHR:423~449行
         ac.diplomaScore = JobLevelCalculatorService.handleDiplomaScore(ac.lastRecordInfo, ac.positionAppointment, jobScoreInfo);
         ac.whyDiplomaScore.putAll(jobScoreInfo.whyDiplomaScore);
-        logger.debug("计算学历得分完成");
+        logger.info("计算学历得分完成");
 
         //---------------------------------- 调整日志 begin ----------------------------------
         ac.whyAdjust1.add(StrFormatter.format("毕业时间小于等于【{}】的最高学历【{}】,学历分【{}】{}",
@@ -130,9 +129,9 @@ public class AnnualAdjustmentService {
         //8.确定目标职级(含有R排名和无R排名两条路径)。
         //对应SHR:540~702行
         DynamicObject jobLevel = decideTargetJobGrade(ac);
-        logger.debug("确定目标职级完成");
+        logger.info("确定目标职级完成");
 
-        // 9、上年度考核结果为“无”时,取最低职级
+        // 9、上年度考核结果为"无"时,取最低职级
         //对应SHR:703~707行
         if (AppraisalResultEnum.NONE.getCode().equals(ac.data.getAppraisalResultNumber())) {
             // 考核结果为无时取最低职级
@@ -142,13 +141,15 @@ public class AnnualAdjustmentService {
             logger.info("上年度考核结果为'无',采用最低职级");
         }
         if(jobLevel == null){
-            throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+            String errorMsg = StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                     ac.personName,
                     ac.jobSeq.getString(FormConstant.NAME_KEY),
                     ac.allSumScore.toString(),
                     ac.data.getZgjbName(),
                     ac.data.getZyjndjName(),
-                    ac.data.getAppraisalResultName()));
+                    ac.data.getAppraisalResultName());
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
 
         JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, beginDate == null ? new Date() : beginDate, ac.positionAppointment);
@@ -159,7 +160,7 @@ public class AnnualAdjustmentService {
 
         //【三期需求】:计算保级原因
         judgeLevelKeepReason(ac, jobLevel);
-        logger.debug("计算保级原因完成");
+        logger.info("计算保级原因完成");
 
         //10、构建职位档案
         DynamicObject result = buildPersonPositionFile(ac, jobLevel);
@@ -171,9 +172,11 @@ public class AnnualAdjustmentService {
     }
 
     private static void judgeLevelKeepReason(AdjustmentContext ac, DynamicObject jobLevel) {
+        logger.info("开始计算保级原因,调整类型:" + ac.adjustType);
         if(AdjustTypeEnum.KEEP_LEVEL.getCode().equals(ac.adjustType)){
             if(ac.useAppraisalresult){
                 ac.levelKeepReason = 1;
+                logger.info("保级原因:本自然年度内已升过级,达到升级限制");
             }else{
                 BigDecimal allSumScore = ac.allSumScore;
                 int currentSeq = jobLevel.getInt(PositionStructureConstant.JOBLEVELSEQ);
@@ -187,14 +190,17 @@ public class AnnualAdjustmentService {
                         if (ac.rankNoUpGrad) {
                             //总积分及排名均未达到
                             ac.levelKeepReason = 6;
+                            logger.info("保级原因:总积分及排名均未达到");
                         } else {
                             //总积分没有达到下一级最低分数要求
                             ac.levelKeepReason = 2;
+                            logger.info("保级原因:总积分没有达到下一级最低分数要求");
                         }
                     } else {
                         if(ac.rankNoUpGrad) {
                             //R排名与全员绩效考核等级均未达到升级标准
                             ac.levelKeepReason = 5;
+                            logger.info("保级原因:R排名与全员绩效考核等级均未达到升级标准");
                         }else{
                             //判断职称等级或技能等级是否达到
                             //1、获取当前职称或技能等级序号
@@ -204,17 +210,21 @@ public class AnnualAdjustmentService {
                             //这里先不判断,因为其他所有保级情况都已涵盖。剩下的就只有职称或技能达不到了
                             if (JobSeqEnum.SKILL.getCode().equals(ac.jobSeq.getString(FormConstant.NUMBER_KEY))) {
                                 ac.levelKeepReason = 4;
+                                logger.info("保级原因:未聘任高一级的技能");
                             }else{
                                 ac.levelKeepReason = 3;
+                                logger.info("保级原因:未聘任高一级的职称");
                             }
                         }
                     }
                 }else {
                     //达到职级上限
                     ac.levelKeepReason = 7;
+                    logger.info("保级原因:达到职级上限");
                 }
             }
         }
+        logger.info("保级原因计算完成,原因代码:" + ac.levelKeepReason);
     }
 
 
@@ -231,7 +241,7 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/08 21:15
      */
     private static AdjustmentContext initAndLoad(Integer executeYear, DynamicObject person, Date beginDate,Date adjustDate,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile){
-        logger.debug("开始初始化上下文,执行年份:" + executeYear);
+        logger.info("开始初始化上下文,执行年份:" + executeYear);
         AdjustmentContext ac = new AdjustmentContext();
         ac.executeYear = executeYear;
         ac.nowYear = executeYear;
@@ -251,7 +261,9 @@ public class AnnualAdjustmentService {
         //1、获取上年度考核结果(对应SHR:PersonpositionfileUtils:884~914行)
         // DynamicObject performanceResult = PerformanceManagerHelper.getPerformanceResult(ac.personId, lastYearDateTime);
         if(performanceResult == null){
-            throw new ValidationException(StrFormatter.format("人员【{}】缺少【{}】年考核结果", ac.personName,lastYear));
+            String errorMsg = StrFormatter.format("人员【{}】缺少【{}】年考核结果", ac.personName,lastYear);
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
 
         data.setAppraisalResult(performanceResult);
@@ -275,7 +287,9 @@ public class AnnualAdjustmentService {
         if(personPosFileByPersonAndState == null || personPosFileByPersonAndState.length < 1){
             //没有年度调整记录取初定记录
             if(firstPersonPosFile == null){
-                throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为他/她尚未建立职位档案。请前往“职位及积分初定” -> 进行初定!", ac.personName));
+                String errorMsg = StrFormatter.format("当前无法为【{}】进行调整,因为他/她尚未建立职位档案。请前往职位及积分初定 -> 进行初定!", ac.personName);
+                logger.error(errorMsg);
+                throw new ValidationException(errorMsg);
             }
             lastPersonPosFile = firstPersonPosFile;
             data.setFirstId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
@@ -395,7 +409,9 @@ public class AnnualAdjustmentService {
             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)));
+            String errorMsg = StrFormatter.format("当前无法为【{}】进行调整,因为根据时间【{}】未获取到人员任职和聘任信息!", ac.personName, DateUtil.format(beginDate == null ? new Date() : beginDate,DateUtil.NORM_DATE_PATTERN));
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
         DynamicObject perProTitle = positionAppointment.getPerProTitle();
         if(perProTitle != null){
@@ -414,7 +430,9 @@ public class AnnualAdjustmentService {
             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()));
+            String errorMsg = StrFormatter.format("无职位序列,请检查当前人员【{}】任职的岗位【{}】是否有职位序列",ac.personName,data.getPositionName());
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
         ac.positionAppointment = positionAppointment;
         long jobSeq = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
@@ -427,9 +445,11 @@ public class AnnualAdjustmentService {
         if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().topRank == null) {
             //如果没有排名与R排名,则按保级处理
             ac.keep = true;
+            logger.info("没有排名与R排名信息,按保级处理");
         }
         if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().allowanceRankPercent.compareTo(BigDecimal.ZERO) <= 0) {
             ac.haveRp = false;
+            logger.info("R排名不存在或小于等于0");
         }
 
         // 对应SHR:323~339行
@@ -437,14 +457,17 @@ public class AnnualAdjustmentService {
         if (AppraisalResultEnum.EXCELLENT.getCode().equals(ac.data.getAppraisalResultNumber())) {
             ac.minusByAppraisal = 1;
             ac.whyAdjust.append("【考核结果优秀】升1级");
+            logger.info("考核结果为优秀,升1级");
         } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
             ac.minusByAppraisal = -1;
             ac.whyAdjust.append("【考核结果基本合格】降1级");
+            logger.info("考核结果为基本合格,降1级");
         } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
             ac.minusByAppraisal = -2;
             ac.whyAdjust.append("【考核结果不合格】降2级");
+            logger.info("考核结果为不合格,降2级");
         }
-        logger.debug("初始化上下文完成");
+        logger.info("初始化上下文完成");
         return ac;
     }
 
@@ -456,6 +479,7 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/08 22:12
      */
     private static void evaluateFirstAndAppraisalUsage(AdjustmentContext ac) {
+        logger.info("开始评估是否为本年首次调整及考核使用情况");
 
         DynamicObject nowYearPersonPosFile = PositionFileHelper.getLatsPersonPosFileByPerson(ac.personId, new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR, QCP.equals, ac.nowYear));
 
@@ -465,7 +489,9 @@ public class AnnualAdjustmentService {
             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));
+                String errorMsg = StrFormatter.format("人员【{}】,上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】记录才能继续操作 !", ac.personName,ac.nowYear);
+                logger.error(errorMsg);
+                throw new ValidationException(errorMsg);
             }
             LocalDateTime nowYearDateTime = LocalDateTime.of(ac.nowYear, 1, 1, 0, 0);
             StringBuilder reasons = new StringBuilder();
@@ -476,18 +502,21 @@ public class AnnualAdjustmentService {
                         .append("年度首次调整,且升降【考核结果】已被使用,不需要考虑R排名,保级处理");
                 ac.haveRp = false;
                 ac.keep = true;
+                logger.info("非本年首次调整,且升降考核结果已被使用,不需要考虑R排名,保级处理");
             }
             ac.whyAdjust1.add("【非】本年度首次调整,"+reasons.toString());
         } else {
             ac.whyAdjust.append(" ").append(ac.nowYear).append("年度首次调整,【考核结果】未被使用");
             ac.whyAdjust1.add("本年度首次调整");
             isyearfirstdo = true;
+            logger.info("本年度首次调整,考核结果未被使用");
         }
 
         if (ac.useAppraisalresult) {
             ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】已被使用,保级处理");
             isyearfirstdo = false;
             ac.keep = true;
+            logger.info("考核结果已被使用,保级处理");
         } else {
             ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】未被使用");
             ac.keep = false;
@@ -500,14 +529,17 @@ public class AnnualAdjustmentService {
                 if (p == null || p.compareTo(BigDecimal.ZERO) <= 0) {
                     ac.whyAdjust.append("无R排名");
                     ac.haveRp = false;
+                    logger.info("考核结果为保级,无R排名");
                 } else {
                     ac.whyAdjust.append("使用R排名进行计算 ");
                     ac.haveRp = true;
+                    logger.info("考核结果为保级,使用R排名进行计算");
                 }
             }
         }
 
         ac.isYearFirstDo = isyearfirstdo;
+        logger.info("评估完成,是否本年首次调整:" + isyearfirstdo + ",是否使用考核结果:" + ac.useAppraisalresult);
     }
 
     /**
@@ -518,9 +550,12 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/08 23:13
      */
     private static void loadLastRecordAndValidateBeginDate(AdjustmentContext ac) {
+        logger.info("开始加载上一条记录并校验生效日期");
         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));
+            String errorMsg = StrFormatter.format("人员【{}】,缺少职位及积分初定信息,请先完成初定,第二年再操作年度调整!", ac.personName);
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
         ac.lastRecordInfo = BusinessDataServiceHelper.loadSingle(ac.lastRecordId, PositionStructureConstant.PERSONPOSFILE_ENTITYID);
 
@@ -529,9 +564,12 @@ public class AnnualAdjustmentService {
                 && ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE) != null) {
             Date lastBegin = ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE);
             if (ac.beginDate.compareTo(lastBegin) <= 0) {
-                throw new ValidationException(StrFormatter.format("当前无法为【{}】进行调整,因为最近调整时间为【{}】,不能早于最近一次职位调整时间。", ac.personName,DateUtil.format(lastBegin,DateUtil.NORM_DATE_PATTERN)));
+                String errorMsg = StrFormatter.format("当前无法为【{}】进行调整,因为最近调整时间为【{}】,不能早于最近一次职位调整时间。", ac.personName,DateUtil.format(lastBegin,DateUtil.NORM_DATE_PATTERN));
+                logger.error(errorMsg);
+                throw new ValidationException(errorMsg);
             }
         }
+        logger.info("加载上一条记录并校验生效日期完成");
     }
 
 
@@ -543,32 +581,33 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/08 23:46
      */
     private static void aggregateScores(AdjustmentContext ac) {
+        logger.info("开始汇总年度积分池与综合分数");
         BigDecimal allyearscoresum =
                 ac.data.getAllYearScoreSum() == null ? BigDecimal.ZERO : ac.data.getAllYearScoreSum();
-        System.out.println("上年所有贡献单据分数之和" + allyearscoresum);
+        logger.info("上年所有贡献单据分数之和: " + allyearscoresum);
 
         BigDecimal appraisalresultscore =
                 ac.data.getAppraisalResultScore() == null ? BigDecimal.ZERO : ac.data.getAppraisalResultScore();
-        System.out.println("上年年度考核得分" + appraisalresultscore);
+        logger.info("上年年度考核得分: " + appraisalresultscore);
 
         ac.lastYearContributeScore = allyearscoresum.add(appraisalresultscore);
-        System.out.println("上年度贡献综合评价分" + ac.lastYearContributeScore);
+        logger.info("上年度贡献综合评价分: " + ac.lastYearContributeScore);
 
         ac.addYearContributeScore = ac.lastYearContributeScore.multiply(new BigDecimal("0.1"));
-        System.out.println("年度新增的贡献积分" + ac.addYearContributeScore);
+        logger.info("年度新增的贡献积分: " + ac.addYearContributeScore);
 
         BigDecimal lastsum = ac.data.getLastSumScore() == null ? BigDecimal.ZERO : ac.data.getLastSumScore();
-        System.out.println("上年累计积分池的分" + lastsum);
+        logger.info("上年累计积分池的分: " + lastsum);
 
         ac.sumScore = lastsum.add(ac.addYearContributeScore);
-        System.out.println("累计积分池的分" + ac.sumScore);
+        logger.info("累计积分池的分: " + ac.sumScore);
 
 
         ac.allSumScore = ac.sumScore
                 .add(ac.diplomaScore)
                 .add(ac.data.getJobScoreInfo().perProTitleScore)
                 .add(ac.data.getJobScoreInfo().quaLevelScore);
-        System.out.println("累计总积分" + ac.allSumScore);
+        logger.info("累计总积分: " + ac.allSumScore);
     }
 
     /**
@@ -580,20 +619,29 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/10 14:17
      */
     private static DynamicObject decideTargetJobGrade(AdjustmentContext ac) {
+        logger.info("开始确定目标职级");
 
         //上一条职位档案的职位序列(如果是管理序列,则转换为职能序列)
         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));
+            String errorMsg = StrFormatter.format("人员【{}】,序列发生变化!请先到【员工职位档案调整】中进行操作", ac.personName);
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
 
+        DynamicObject jobLevel;
         if (ac.haveRp) {
-            return decideWithR(ac);
+            jobLevel = decideWithR(ac);
+            logger.info("使用R排名方式确定目标职级完成");
         } else {
-            return decideWithoutR(ac);
+            jobLevel = decideWithoutR(ac);
+            logger.info("不使用R排名方式确定目标职级完成");
         }
+        
+        logger.info("目标职级确定完成");
+        return jobLevel;
     }
 
     /**
@@ -604,23 +652,27 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/10 14:18
      */
     private static DynamicObject decideWithR(AdjustmentContext ac) {
+        logger.info("开始使用R排名方式确定目标职级");
         //根据[职位序列][积分]和[职称等级/技能等级]获取职位序列最高职级
         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("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+            String errorMsg = StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                     ac.personName,
                     ac.jobSeq.getString(FormConstant.NAME_KEY),
                     ac.allSumScore.toString(),
                     ac.data.getZgjbName(),
                     ac.data.getZyjndjName(),
-                    ac.data.getAppraisalResultName()));
+                    ac.data.getAppraisalResultName());
+            logger.error(errorMsg);
+            throw new ValidationException(errorMsg);
         }
         int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
-        System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
+        logger.info("当前积分所得职级顺序号: " + jobGradeindex);
 
         Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.convertJobSeq);
         ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
         int newjobgradeindex = ac.data.getLastJobGradeIndex();
+        logger.info("上一条记录的职级序号: " + newjobgradeindex);
 
         // 补充根据R排名确定是否升级
         if (ac.minusByAppraisal == 0) {
@@ -629,37 +681,45 @@ public class AnnualAdjustmentService {
                 newjobgradeindex = JobLevelCalculatorService.getnewjobgradeindexByrank(ac.data.getLastJobGradeIndex(), newjobgradeindex, ac.data.getRankingResultInfo(), ac.convertJobSeq);
                 if(ac.data.getLastJobGradeIndex() == newjobgradeindex){
                     ac.rankNoUpGrad = true;
+                    logger.info("R排名未达到升级标准");
                 }
             }
 
             if (newjobgradeindex > jobGradeindex) {
                 //超过职位序列最高职级则退回职位序列最高职级
                 newjobgradeindex = jobGradeindex;
+                logger.info("超过职位序列最高职级,退回最高职级");
                 //但超出『积分』和『职称等级、技能等级』的所能定级的最高职级,按
             }
         } else {
             // 考核结果需要升降级则直接执行
             newjobgradeindex = ac.data.getLastJobGradeIndex() + ac.minusByAppraisal;
+            logger.info("根据考核结果调整职级,调整前序号:" + ac.data.getLastJobGradeIndex() + ",调整后序号:" + newjobgradeindex);
             if (jobGradeindex <= newjobgradeindex) {
                 //超过职位序列最高职级则退回职位序列最高职级
                 newjobgradeindex = jobGradeindex;
+                logger.info("超过职位序列最高职级,退回最高职级");
             }
         }
 
         // 确定是否超出总分最高任命职级
         DynamicObject maxJobLevel = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore);
         int personMaxjobgradeindex = maxJobLevel.getInt(FormConstant.JOBLEVELSEQ);
+        logger.info("总分能任命的最高职级序号: " + personMaxjobgradeindex);
         // 超出总分能任命最高职职级则该最高职级就是要任命的职级
         if (newjobgradeindex > personMaxjobgradeindex) {
             newjobgradeindex = personMaxjobgradeindex;
+            logger.info("超出总分能任命的最高职级,调整至最高职级");
         }
 
         // 确定是否超出该职称等级或技能等级最高任命职级
         DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
         int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
+        logger.info("职称/技能等级能任命的最高职级序号: " + maxJobGradeIndex);
         // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
         if (newjobgradeindex > maxJobGradeIndex) {
             newjobgradeindex = maxJobGradeIndex;
+            logger.info("超出职称/技能等级能任命的最高职级,调整至最高职级");
         }
 
         if((ac.data.getZgjbId() == null || ac.data.getZgjbId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
@@ -668,32 +728,40 @@ public class AnnualAdjustmentService {
             if(jobLevel != null) {
                 newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
             }
+            logger.info("无职称/技能聘任,取最低职级");
         }else if(jobLevelByJobSeqMap.get(newjobgradeindex) != null){
             jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
+            logger.info("根据计算结果获取职级,职级序号:" + newjobgradeindex);
         }else{
             //兜底;如果没有获取到合适的职级则取最低级
             jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
             if(jobLevel != null) {
                 newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
             }
+            logger.info("未找到合适职级,取最低职级,职级序号:" + newjobgradeindex);
         }
 
 
         if((ac.data.getZgjbId() == null || ac.data.getZgjbId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
             //无聘任
             ac.adjustType = "7";
+            logger.info("调整类型:无聘任");
         }else if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
             //保级
             ac.adjustType = "1";
+            logger.info("调整类型:保级");
         }else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
             //升级
             ac.adjustType = "2";
+            logger.info("调整类型:升级");
         } else {
             //降级
             ac.adjustType = "0";
+            logger.info("调整类型:降级");
         }
         // 升降级数
         ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
+        logger.info("升降级数:" + ac.adjustInt);
 
         return jobLevel;
 
@@ -707,11 +775,12 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/10 14:18
      */
     private static DynamicObject decideWithoutR(AdjustmentContext ac) {
+        logger.info("开始不使用R排名方式确定目标职级");
         // String JobGrade = "";
         DynamicObject jobLevel = null;
 
         if (ac.minusByAppraisal == 0 && ac.keep) {
-            System.out.println("绩效分组排名,没有勾选享受职位津贴,保持原职级不变,没有则为最低档");
+            logger.info("绩效分组排名,没有勾选享受职位津贴,保持原职级不变,没有则为最低档");
             ac.whyAdjust.append("绩效分组排名,没有勾选享受职位津贴,保持原职级不变");
             jobLevel = ac.data.getLastJobLevel();
             ac.adjustType = "1";
@@ -722,66 +791,81 @@ public class AnnualAdjustmentService {
             if (jobLevel == null) {
                 jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
                 ac.adjustType = "0";
+                logger.info("未找到上一条记录的职级,取最低职级");
             }
+            logger.info("保持原职级不变");
 
         } 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("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
+                String errorMsg = StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                         ac.personName,
                         ac.jobSeq.getString(FormConstant.NAME_KEY),
                         ac.allSumScore.toString(),
                         ac.data.getZgjbName(),
                         ac.data.getZyjndjName(),
-                        ac.data.getAppraisalResultName()));
+                        ac.data.getAppraisalResultName());
+                logger.error(errorMsg);
+                throw new ValidationException(errorMsg);
             }
 
             int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
-            System.out.println("当前积分所得职级顺序号:::" + jobGradeindex);
+            logger.info("当前积分所得职级顺序号:::" + jobGradeindex);
 
             Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.data.getConvertLastJobSeq());
             ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
             int newjobgradeindex = ac.data.getLastJobGradeIndex();
+            logger.info("上一条记录的职级序号: " + newjobgradeindex);
 
             if (ac.isYearFirstDo) {
                 newjobgradeindex += ac.minusByAppraisal;
+                logger.info("根据考核结果调整职级,调整量:" + ac.minusByAppraisal + ",调整后序号:" + newjobgradeindex);
             }
 
             if (jobGradeindex <= newjobgradeindex) {
                 newjobgradeindex = jobGradeindex;
+                logger.info("超过职位序列最高职级,退回最高职级");
             }
 
             // 确定是否超出该职称等级或技能等级最高任命职级
             DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
             int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
+            logger.info("职称/技能等级能任命的最高职级序号: " + maxJobGradeIndex);
             // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
             if (newjobgradeindex > maxJobGradeIndex) {
                 newjobgradeindex = maxJobGradeIndex;
+                logger.info("超出职称/技能等级能任命的最高职级,调整至最高职级");
             }
 
             // 如果最后超出最低排名则按最低排名
             if (jobLevelByJobSeqMap.get(newjobgradeindex) != null) {
                 jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
+                logger.info("根据计算结果获取职级,职级序号:" + newjobgradeindex);
             } else {
                 jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
                 if(jobLevel != null) {
                     newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
                 }
+                logger.info("未找到合适职级,取最低职级,职级序号:" + newjobgradeindex);
             }
 
             // 设置调整类型和调整级别数
             if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
                 //保级
                 ac.adjustType = "1";
+                logger.info("调整类型:保级");
             } else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
                 //升级
                 ac.adjustType = "2";
+                logger.info("调整类型:升级");
             } else {
                 //降级
                 ac.adjustType = "0";
+                logger.info("调整类型:降级");
             }
             // 升降级数
             ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
+            logger.info("升降级数:" + ac.adjustInt);
         }
 
         return jobLevel;
@@ -798,6 +882,7 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/10 15:44
      */
     private static DynamicObject buildPersonPositionFile(AdjustmentContext ac, DynamicObject jobLevel) {
+        logger.info("开始构建职位档案");
 
         DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
                 PositionStructureConstant.PERSONPOSFILE_ENTITYID);
@@ -902,6 +987,7 @@ public class AnnualAdjustmentService {
             newPersonPosFile.set(PositionStructureConstant.NCKD_LEVELKEEPREASON, ac.levelKeepReason+"");
         }
 
+        logger.info("职位档案构建完成");
         return newPersonPosFile;
     }
     /**
@@ -1002,4 +1088,4 @@ public class AnnualAdjustmentService {
 
         Map<Integer, DynamicObject> jobLevelByJobSeqMap;
     }
-}
+}

+ 48 - 31
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/form/performance/PerfRankMgmtFormPlugin.java

@@ -194,38 +194,42 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
             Object oldValue = changeSet[0].getOldValue();
 //            优秀人数、基本合格人数、不合格人数
             if(!Objects.equals(newValue, oldValue)) {
-                QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                        .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, PerfRankMgmtConstant.NCKD_RATIO)
-                        .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT}, FormConstant.NUMBER_KEY);
-                DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), null);
-                Map<String, BigDecimal> appraisalResultRatioMap = new HashMap<>();
-                query.forEach(dynamicObject -> {
-                    String key = dynamicObject.getString(String.join(".", FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
-                    BigDecimal value = dynamicObject.getBigDecimal(String.join(".", FormConstant.NCKD_ENTRYENTITY, PerfRankMgmtConstant.NCKD_RATIO));
-                    appraisalResultRatioMap.put(key, value);
-                });
-
-                // 获取配置比例
-                BigDecimal excellentRatio = appraisalResultRatioMap.get(AppraisalResultEnum.EXCELLENT.getCode());
-                BigDecimal qualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.QUALIFIED.getCode());
-                BigDecimal basicallyQualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.BASICALLY_QUALIFIED.getCode());
-                BigDecimal unQualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.UN_QUALIFIED.getCode());
-
-                // 计算各等级的理论人数(四舍五入)
-                int expectedExcellent = calculateExpectedCount(ConvertUtil.toInt(newValue, 0), excellentRatio);
-                int expectedQualified = calculateExpectedCount(ConvertUtil.toInt(newValue, 0), qualifiedRatio);
-                int expectedBasic = calculateExpectedCount(ConvertUtil.toInt(newValue, 0), basicallyQualifiedRatio);
-                int expectedFail = calculateExpectedCount(ConvertUtil.toInt(newValue, 0), unQualifiedRatio);
-
-                this.getModel().setValue(PerfRankMgmtConstant.NCKD_EXCELLENTS, expectedExcellent);
-//                this.getModel().setValue(PerfRankMgmtConstant.NCKD_BASICS, expectedQualified);
-                this.getModel().setValue(PerfRankMgmtConstant.NCKD_BASICS, expectedBasic);
-                this.getModel().setValue(PerfRankMgmtConstant.NCKD_FAILS, expectedFail);
+                calculateExpectedCount(ConvertUtil.toInt(newValue, 0));
             }
         }
 
     }
 
+    private void calculateExpectedCount(int totalCount) {
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, PerfRankMgmtConstant.NCKD_RATIO)
+                .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT}, FormConstant.NUMBER_KEY);
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), null);
+        Map<String, BigDecimal> appraisalResultRatioMap = new HashMap<>();
+        query.forEach(dynamicObject -> {
+            String key = dynamicObject.getString(String.join(".", FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
+            BigDecimal value = dynamicObject.getBigDecimal(String.join(".", FormConstant.NCKD_ENTRYENTITY, PerfRankMgmtConstant.NCKD_RATIO));
+            appraisalResultRatioMap.put(key, value);
+        });
+
+        // 获取配置比例
+        BigDecimal excellentRatio = appraisalResultRatioMap.get(AppraisalResultEnum.EXCELLENT.getCode());
+        BigDecimal qualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.QUALIFIED.getCode());
+        BigDecimal basicallyQualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.BASICALLY_QUALIFIED.getCode());
+        BigDecimal unQualifiedRatio = appraisalResultRatioMap.get(AppraisalResultEnum.UN_QUALIFIED.getCode());
+
+        // 计算各等级的理论人数(四舍五入)
+        int expectedExcellent = calculateExpectedCount(totalCount, excellentRatio);
+        int expectedQualified = calculateExpectedCount(totalCount, qualifiedRatio);
+        int expectedBasic = calculateExpectedCount(totalCount, basicallyQualifiedRatio);
+        int expectedFail = calculateExpectedCount(totalCount, unQualifiedRatio);
+
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_EXCELLENTS, expectedExcellent);
+//                this.getModel().setValue(PerfRankMgmtConstant.NCKD_BASICS, expectedQualified);
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_BASICS, expectedBasic);
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_FAILS, expectedFail);
+    }
+
     /**
      * 计算理论人数(四舍五入)
      */
@@ -280,7 +284,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
             }
         } else if(Arrays.asList(FormConstant.SAVE_OP, FormConstant.SUBMIT_OP).contains(itemKey)){
             //保存或提交先计算一遍
-            // calcRankCount();
+             calcRankCount();
         } else if(FormConstant.DELETEENTRY_OP.equalsIgnoreCase(itemKey)){
             //校验人员考评,若考评周期已结束,则禁止删除。
             EntryGrid entryGrid = getView().getControl(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
@@ -393,6 +397,8 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
         }
     }
 
+
+
     @Override
     public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
         String itemKey = afterDoOperationEventArgs.getOperateKey();
@@ -514,10 +520,15 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
             if(afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess()){
                 String wizadap = this.getView().getPageCache().get(FormConstant.NCKD_WIZARDAP);
                 if(StringUtils.isBlank(wizadap) || "0".equals(wizadap)){
+
                     setStepStatus(0,Steps.FINISH);
                     setStepStatus(1,Steps.PROCESS);
                     //控制显示隐藏
                     importResultStep();
+                    applyRankingFilter(true);
+                    this.getView().invokeOperation(FormConstant.REFRESH_OP);
+
+
                 }else {
                     this.getView().showSuccessNotification("同步考核周期成功。");
                 }
@@ -537,7 +548,11 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
         DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
         for (int i = 0; i < entryEntity.size(); i++) {
             this.getView().setEnable(Boolean.FALSE, i, FormConstant.NCKD_PERSON);
+            this.getView().setEnable(Boolean.FALSE, i, PerfRankMgmtConstant.NCKD_ISRANKING);
         }
+        this.getModel().updateEntryCache(entryEntity);
+        this.getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+
     }
 
     private void generatePersonList() {
@@ -547,6 +562,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
         DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
         for (int i = 0; i < entryEntity.size(); i++) {
             this.getView().setEnable(Boolean.TRUE, i, FormConstant.NCKD_PERSON);
+            this.getView().setEnable(Boolean.TRUE, i, PerfRankMgmtConstant.NCKD_ISRANKING);
         }
     }
 
@@ -644,7 +660,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
      */
     private void sortEntry(){
         DynamicObjectCollection entryEntityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
-        if(!entryEntityCols.isEmpty()) {
+            if(!entryEntityCols.isEmpty()) {
             int allowanceRank = entryEntityCols.get(0).getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
             entryEntityCols.sort((o1, o2) -> {
                 // 先按 NCKD_TOPRANK 排序,空值或 0 视为最大值(排在最后)
@@ -744,14 +760,15 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
         }
         //参加排名总人数
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_TOPRANKS, totalRankCount);
-        //参加R排名人数
+        calculateExpectedCount(totalRankCount);
+        /*//参加R排名人数
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, allowanceRankCount);
         //不合格人数
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_FAILS, failCount);
         //基本合格人数
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_BASICS, basicCount);
         //优秀人数
-        this.getModel().setValue(PerfRankMgmtConstant.NCKD_EXCELLENTS, excellentCount);
+        this.getModel().setValue(PerfRankMgmtConstant.NCKD_EXCELLENTS, excellentCount);*/
     }