package nckd.jxccl.hr.psms.business;

import kd.bos.algo.Algo;
import kd.bos.algo.AlgoContext;
import kd.bos.algo.DataSet;
import kd.bos.algo.Row;
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.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.QueryServiceHelper;
import nckd.jxccl.base.common.constant.FormConstant;
import nckd.jxccl.base.common.enums.AppraisalResultEnum;
import nckd.jxccl.base.common.enums.psms.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;
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.PositionFileHelper;
import nckd.jxccl.hr.psms.helper.data.JobLevelRuleConfig;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Comparator;
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;

/**
 * 职级计算器，用于计算员工的职级
 * 功能：根据职称等级、技能等级、学历、年度贡献分、绩效考核结果、R排名等信息，动态计算员工应获得的职级
 * @author W.Y.C
 * @date 2025/9/19 13:56
 * @version 1.0
 */
public class JobLevelCalculatorService {

    protected final static Log logger = LogFactory.getLog(JobLevelCalculatorService.class);

    /** 职称等级与技能等级映射关系*/
    public static final Map<String, String> TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP = new HashMap<>();

    static {
        // 正高级---高级技师
        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("001", "1");
        // 副高级---高级技师
        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("002", "1");
        // 中级---技师
        TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.put("003", "2");
        // 助理级---高级工
        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,  PositionAppointmentBO positionAppointment) {
        logger.info("开始计算员工职级 - 人员ID: {}, 姓名: {}, 计算日期: {}",
                person.getLong(FormConstant.ID_KEY),
                person.getString(FormConstant.NAME_KEY),
                date);

        //资格三要素：
        // • 职称/技能等级：硬性门槛（如工程师职称、技师等级）
        // • 积分：量化能力总分（由学历、职称、年度贡献分累计）
        // • 年度绩效考核结果（R排名）：决定升降的关键绩效指标
        Long personId = person.getLong(FormConstant.ID_KEY);
        String personName = person.getString(FormConstant.NAME_KEY);

        logger.info("人员信息 - ID: {}, 姓名: {}", personId, personName);

        //对应SHR：hrjobfamilyid
        long jobSeqId = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ,FormConstant.ID_KEY));
        DynamicObject jobSeq = BusinessDataServiceHelper.loadSingle(jobSeqId, FormConstant.HBJM_JOBSEQHR);

        logger.info("原始职位序列 - ID: {}, 编码: {}, 名称: {}",
                jobSeqId,
                jobSeq.getString(FormConstant.NUMBER_KEY),
                jobSeq.getString(FormConstant.NAME_KEY));

        JobLevelResult jobLevelResult = new JobLevelResult();

        // 1. 获取最近的职位档案（必须存在，否则抛错）
        //对应SHR：personpositionfileInfo
        logger.info("开始查询最新职位档案 - 人员ID: {}", personId);
        DynamicObject currentPersonPosFile = PositionFileHelper.getLatsPersonPosFileByPerson(personId);
        if(currentPersonPosFile == null){
            String errorMsg = StrFormatter.format("当前无法为【{}】进行调整，因为他/她尚未建立职位档案。请前往职位及积分初定 -> 进行初定！", personName);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        logger.info("找到最新职位档案 - ID: {}, 执行年度: {}, 开始日期: {}",
                currentPersonPosFile.getLong(FormConstant.ID_KEY),
                currentPersonPosFile.getInt(PositionStructureConstant.NCKD_EXECUTEYEAR),
                currentPersonPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE));

        //对应SHR：oldHRJobFamilyid
        long currentJobSeqId = currentPersonPosFile.getLong(String.join(".",PositionStructureConstant.NCKD_JOBSEQHR,FormConstant.ID_KEY));
        //oldHRJobFamilynumber
        String currentJobSeqNumber = currentPersonPosFile.getString(String.join(".",PositionStructureConstant.NCKD_JOBSEQHR,FormConstant.NUMBER_KEY));
        DynamicObject appraisalResult = currentPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_APPRAISALRESULT);
        //appraisalresultnumber
        String appraisalResultNumber = appraisalResult != null ? appraisalResult.getString(FormConstant.NUMBER_KEY) : null;

        logger.info("当前职位档案信息 - 序列ID: {}, 序列编码: {}, 考核结果: {}",
                currentJobSeqId, currentJobSeqNumber, appraisalResultNumber);

        // 2. 处理职位序列（如果是管理序列，则按职能序列进行变动）
        //对应SHR：hrjobfamilynumber
        /*jobSeq = handleJobSeq(jobSeq);
        // 3. 检查是否缺少聘任条件（没有聘任的职称或技能按最低职级定）
        if (isInvalidRankingConditions(proTitleLevelNumber, ocpQualLevelNumber, jobSeq.getString(FormConstant.NUMBER_KEY))) {
            logger.warn("人员【{}】职位序列【{}】无聘任职称/技能，按最低职级", personName,jobSeq.getString(FormConstant.NAME_KEY));
            DynamicObject lowestJobGrade = getLowestJobLevel(jobSeq);
            //对应SHR：selMap.put("adjusttype","7")
            jobLevelResult.adjustType = "7";
            jobLevelResult.adjustMsg = "不符合三要素【无聘任职称/技能】，定最低职级";
            return jobLevelResult;
        }*/

        // 4. 读取并校验上年度考核结果
        // 对应SHR：874~911
        logger.info("开始校验考核结果一致性");
        appraisalResultNumber = validateAppraisalResultConsistency(date, personId, personName, appraisalResultNumber);
        logger.info("考核结果校验完成 - 最终考核结果编码: {}", appraisalResultNumber);

        //调动前累计的积分池（生涯积分）
        BigDecimal sumScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE);
        logger.info("当前累计积分池: {}", sumScore);

        //当前职级（变动前）
        //对应SHR：JobGrade；916~919行
        DynamicObject currentJobLevel = currentPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
        if(currentJobLevel == null){
            String errorMsg = StrFormatter.format("数据异常，人员【{}】最新职位档案【档案ID：{}】没有职级。请联系管理员处理！",
                    personName, currentPersonPosFile.getLong(FormConstant.ID_KEY));
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        //对应SHR：lastjobgradeindex
        int currentJobLevelIndex = currentJobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("当前职级 - ID: {}, 编码: {}, 名称: {}, 序号: {}",
                currentJobLevel.getLong(FormConstant.ID_KEY),
                currentJobLevel.getString(FormConstant.NUMBER_KEY),
                currentJobLevel.getString(FormConstant.NAME_KEY),
                currentJobLevelIndex);

        // 5.是否首次聘任
        //对应SHR：fistPR；922~926行
        logger.info("检查是否首次聘任");
        boolean firstPr = isFirstPr(personId);
        logger.info("首次聘任检查结果: {}", firstPr);

        // 6. 是否已使用过考核结果
        //对应SHR：useappraisalresult；926行
        logger.info("检查考核结果是否已被使用");
        boolean usedAppraisalResult = useAppraisalResult(personId,date);
        logger.info("考核结果使用情况: {}", usedAppraisalResult);

        // 7. 计算降级数
        //对应SHR：minuspersonappraisal；930~945行
        logger.info("计算考核结果对职级的影响");
        int minusPersonAppraisal = calcMinusPersonAppraisal(appraisalResultNumber, firstPr, usedAppraisalResult, jobLevelResult);
        logger.info("考核结果影响 - 降级数: {}, 调整类型: {}", minusPersonAppraisal, jobLevelResult.adjustType);

        // 8.如果是管理序列,则按职能序列进行调整
        //对应SHR：hrjobfamilynumber；989~993
        logger.info("处理职位序列转换");
        jobSeq = handleJobSeq(jobSeq);
        logger.info("转换后职位序列 - ID: {}, 编码: {}, 名称: {}",
                jobSeq.getLong(FormConstant.ID_KEY),
                jobSeq.getString(FormConstant.NUMBER_KEY),
                jobSeq.getString(FormConstant.NAME_KEY));

        // 9. 处理序列相关信息
        //对应SHR：995~1013
        logger.info("处理序列相关信息");
        JobSeqInfo jobSeqInfo = processJobFamilyInfo(jobSeq, currentPersonPosFile,date);
        logger.info("序列信息 - 是否序列转换: {}, 是否跨单位调动: {}",
                jobSeqInfo.isSequenceChange, jobSeqInfo.isCrossUnitTransfer);

        // 10. 处理职称/技能等级积分
        // 对应SHR：1015~1036
        logger.info("处理职称/技能等级积分");
        JobScoreInfo jobScoreInfo = handleJobScores(currentPersonPosFile,jobSeq, positionAppointment);
        logger.info("职称技能积分 - 职称分: {}, 技能分: {}, 是否缺少聘任: {}",
                jobScoreInfo.perProTitleScore, jobScoreInfo.quaLevelScore, jobScoreInfo.isEndGainJobGrade);

        // 11. 处理学历积分
        //对应SHR：DiplomaScore；947~978行
        logger.info("处理学历积分");
        BigDecimal diplomaScore = handleDiplomaScore(currentPersonPosFile, positionAppointment,jobScoreInfo);
        logger.info("学历积分 - 学历名称: {}, 学历分: {}, 是否变化: {}",
                jobScoreInfo.diplomaName, diplomaScore, jobScoreInfo.isDiplomaChange);

        // 计算总积分；累计积分池的分 + 学历 + (职称分/技能分)
        // 对应SHR：allsumScore；1041行
        BigDecimal allSumScore = sumScore.add(diplomaScore)
                .add(jobScoreInfo.perProTitleScore)
                .add(jobScoreInfo.quaLevelScore);
        jobScoreInfo.allSumScore = allSumScore;
        logger.info("总积分计算 - 累计积分池: {}, 学历分: {}, 职称分: {}, 技能分: {}, 总积分: {}",
                sumScore, diplomaScore, jobScoreInfo.perProTitleScore, jobScoreInfo.quaLevelScore, allSumScore);

        // 12. 获取序列对应的职级
        //对应SHR：jobgradeMap
        logger.info("获取职位序列对应的职级映射");
        Map<Integer, DynamicObject> jobLevelMap = getJobLevelByJobSeqMap(jobSeq);
        logger.info("获取到职级数量: {}", jobLevelMap.size());

        // 13. 根据不同情况计算职级ID
        //对应SHR：JobGradeInfo；1044~1071行
        logger.info("开始计算职级ID");
        DynamicObject jobLevel = calculateJobGradeId(jobSeq,jobScoreInfo, jobSeqInfo,allSumScore,currentJobLevelIndex,minusPersonAppraisal, firstPr,jobLevelResult);
        //对应SHR：newjobgradeindex
        int jobLevelIndex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("初步计算职级结果 - 职级ID: {}, 职级序号: {}, 调整类型: {}",
                jobLevel.getLong(FormConstant.ID_KEY), jobLevelIndex, jobLevelResult.adjustType);

        // 14. 处理排名相关信息
        //对应SHR：allowancerankpercent、toprankpercent、allowancerank、toprank；1073~1109行
        logger.info("处理排名相关信息");
        RankingResultInfo rankingInfo = getRankingInfo(personId, personName, date);
        if (rankingInfo != null) {
            logger.info("排名信息 - R排名: {}/{}, R排名百分比: {}, 总排名: {}/{}, 总排名百分比: {}",
                    rankingInfo.allowanceRank, rankingInfo.countR, rankingInfo.allowanceRankPercent,
                    rankingInfo.topRank, rankingInfo.count, rankingInfo.topRankPercent);
        } else {
            logger.warn("未找到排名信息");
        }

        // 15. 处理序列转换或跨单位调动的特殊逻辑
        // SHR对应：1110~1136行
        if (jobSeqInfo.isSequenceChange) {
            logger.info("检测到序列转换，开始处理");
            handleSequenceChange(jobLevelIndex,currentJobLevelIndex,rankingInfo,jobLevelMap,jobLevelResult,jobSeq);
            jobLevel = jobLevelResult.jobLevel;
            jobLevelResult.jobSeqInfo = jobSeqInfo;
            jobLevelResult.jobScoreInfo = jobScoreInfo;
            jobLevelResult.rankingResultInfo = rankingInfo;
            logger.info("序列转换处理完成 - 最终职级序号: {}, 调整类型: {}",
                    jobLevel != null ? jobLevel.getInt(FormConstant.JOBLEVELSEQ) : StringUtils.EMPTY, jobLevelResult != null ? jobLevelResult.adjustType : StringUtils.EMPTY);
            return jobLevelResult;
        } else if (jobSeqInfo.isCrossUnitTransfer) {
            logger.info("检测到跨单位调动，开始处理");
            handleCrossUnitTransfer(jobLevelIndex,currentJobLevelIndex,rankingInfo,jobLevelMap,jobLevelResult,jobSeq);
            jobLevel = jobLevelResult.jobLevel;
            jobLevelResult.jobSeqInfo = jobSeqInfo;
            jobLevelResult.jobScoreInfo = jobScoreInfo;
            jobLevelResult.rankingResultInfo = rankingInfo;
            logger.info("跨单位调动处理完成 - 最终职级序号: {}, 调整类型: {}",
                    jobLevel != null ? jobLevel.getInt(FormConstant.JOBLEVELSEQ) : StringUtils.EMPTY, jobLevelResult != null ? jobLevelResult.adjustType : StringUtils.EMPTY);
            return jobLevelResult;
        }

        // 16. 获取最近一次聘任信息
        //对应SHR：lastAppointmentJobGradeIndex；1139~1147行
        logger.info("获取最近一次聘任信息");
        int lastAppointmentJobGradeIndex = getLastAppointmentJobGradeIndex(personId, currentJobLevelIndex,jobLevelIndex);
        logger.info("最近一次聘任职级序号: {}", lastAppointmentJobGradeIndex);

        if (jobLevelIndex - lastAppointmentJobGradeIndex > 1) {
            logger.info("职级跃升超过一级，限制为只升一级");
            jobLevelIndex = lastAppointmentJobGradeIndex + 1;
        }

        // 17. 根据考核结果使用情况处理职级计算
        //对应SHR行：1149~1180行
        logger.info("根据考核结果使用情况处理职级计算");
        jobLevelIndex = handleAppraisalUsage(jobSeq,rankingInfo,jobLevelIndex,lastAppointmentJobGradeIndex,currentJobLevelIndex,minusPersonAppraisal, firstPr,usedAppraisalResult,jobLevelResult);
        logger.info("考核结果使用处理后的职级序号: {}", jobLevelIndex);

        // 18. 处理首次聘任情况 & 处理聘任相关限制
        //对应SHR：1182~1295行
        logger.info("处理聘任相关限制");
        jobLevelIndex = handleAppointmentRestrictions(personId, firstPr,usedAppraisalResult,jobSeq,jobSeqId,currentJobSeqId,currentJobSeqNumber,jobScoreInfo,currentJobLevelIndex,jobLevelIndex,allSumScore,jobLevelResult,minusPersonAppraisal,currentPersonPosFile);
        logger.info("聘任限制处理后的职级序号: {}", jobLevelIndex);

        // 19.最终确定职级ID
        //对应SHR行：1297~1320行
        logger.info("最终确定职级ID");
        jobLevel = determineFinalJobGradeId(jobLevelIndex, jobScoreInfo, jobLevelMap, jobSeq, jobLevelResult, appraisalResultNumber);
        jobLevelResult.jobLevel = jobLevel;

        jobLevelResult.jobSeqInfo = jobSeqInfo;
        jobLevelResult.jobScoreInfo = jobScoreInfo;
        jobLevelResult.rankingResultInfo = rankingInfo;

        logger.info("职级计算完成 - 人员: {}, 最终职级: {} (序号: {}), 调整类型: {}, 调整说明: {}",
                personName,
                jobLevel.getString(FormConstant.NAME_KEY),
                jobLevel.getInt(FormConstant.JOBLEVELSEQ),
                jobLevelResult.adjustType,
                jobLevelResult.adjustMsg);

        return jobLevelResult;
    }

    /**
     * 读取并校验上年度考核结果
     * 目的：如果不是新年度首次定级，则必须存在上年考核结果；若本年度已有人为创建记录，则要保证"上年考核结果在系统中的值"与上笔记录一致，避免数据不一致导致错误定级。
     * @param date 日期
     * @param personId 人员ID
     * @param personName 人员名称
     * @param appraisalResultNumber 最近应用的考核结果
     * @return: java.lang.String 如果当前年度没有记录，则使用去年的考核结果
     * @author W.Y.C
     * @date: 2025/09/20 20:41
     */
    public static String validateAppraisalResultConsistency(Date date, Long personId, String personName, String appraisalResultNumber) {
        logger.info("开始校验考核结果一致性 - 人员: {}, 日期: {}, 当前考核结果: {}",
                personName, date, appraisalResultNumber);

        //SHR原代码：com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#getJobGradeid：874~911行
        //对应SHR：beginyear
        int year = DateUtil.getYear(date);
        //对应SHR：lastyear
        LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(date), 1);

        logger.info("年度信息 - 当前年度: {}, 上年度: {}", year, lastYearDateTime.getYear());

        //对应SHR：selpersonappraisalresultByyear()方法
        DynamicObject lastYearAppraisalResult = PerformanceManagerHelper.getPerformanceResult(personId, lastYearDateTime);
        //上年度考核结果；
        // 对应SHR：personappraisalresultMap
        String lastYearAppraisalResultNumber = lastYearAppraisalResult != null ? lastYearAppraisalResult.getString(FormConstant.NUMBER_KEY) : null;

        logger.info("上年度考核结果: {}", lastYearAppraisalResultNumber);

        //当前年度职位档案；
        // 对应SHR：nowyearpersonpositionfileCollection
        DynamicObject currentYearPersonPosFileByPerson = PositionFileHelper.getLatsPersonPosFileByPerson(personId,new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR,QCP.equals,year));

        logger.info("当前年度职位档案是否存在: {}", currentYearPersonPosFileByPerson != null);

        //对应SHR：newpersonfirstrank
        //对应SHR：878行
        boolean newPersonFirstRank = isNewPersonFirstRank(date, personId);
        logger.info("是否新员工初定: {}", newPersonFirstRank);

        // 目的：如果不是新年度首次定级，则必须存在上年考核结果；若本年度已有人为创建记录，则要保证"上年考核结果在系统中的值"与上笔记录一致，避免数据不一致导致错误定级。
        // 检查是否存在去年的考核结果
        //对应SHR：if (personappraisalresultMap == null || personappraisalresultMap.size() == 0 || personappraisalresultMap.get("appraisalresultnumber") == null)；884行
        boolean hasLastYearAppraisal = StringUtils.isNotBlank(lastYearAppraisalResultNumber);
        logger.info("是否有上年度考核结果: {}", hasLastYearAppraisal);

        // 检查当前年度是否已有职位档案记录
        //对应SHR：nowyearpersonpositionfileCollection == null || nowyearpersonpositionfileCollection.size() == 0；888行
        boolean hasCurrentYearRecord = currentYearPersonPosFileByPerson != null && currentYearPersonPosFileByPerson.getDataEntityType() != null;
        logger.info("是否有当前年度记录: {}", hasCurrentYearRecord);

        // 如果不是新员工初定情况，必须有去年考核结果
        if (!newPersonFirstRank && !hasLastYearAppraisal) {
            String errorMsg = StrFormatter.format("人员【{}】缺少【{}】年考核结果", personName,lastYearDateTime.getYear());
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        // 如果当前年度没有记录，则使用去年的考核结果
        if (!hasCurrentYearRecord) {
            appraisalResultNumber = hasLastYearAppraisal ? lastYearAppraisalResultNumber : StringUtils.EMPTY;
            logger.info("当前年度无记录，使用上年度考核结果: {}", appraisalResultNumber);
        } else {
            // 如果当前年度已有记录，则验证考核结果是否一致
            String lastYearAppraisalNumber = hasLastYearAppraisal ? lastYearAppraisalResultNumber : null;

            // 对于新员工，特殊处理"无"考核结果（无考核结果）
            if (newPersonFirstRank) {
                // 如果去年没有考核结果，但当前有非"无"的考核结果，则报错
                if (StringUtils.isBlank(lastYearAppraisalNumber) &&
                        StringUtils.isNotBlank(appraisalResultNumber) &&
                        !AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber)) {
                    String errorMsg = StrFormatter.format("人员【{}】上年考度核结果存在变更，需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year);
                    logger.error(errorMsg);
                    throw new ValidationException(errorMsg);
                }
                // 如果去年有考核结果，但与当前不一致，则报错
                if (StringUtils.isNotBlank(lastYearAppraisalNumber) &&
                        !lastYearAppraisalNumber.equals(appraisalResultNumber)) {
                    String errorMsg = StrFormatter.format("人员【{}】上年考度核结果存在变更，需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year);
                    logger.error(errorMsg);
                    throw new ValidationException(errorMsg);
                }
                logger.info("新员工初定考核结果校验通过");
            } else {
                // 对于非新员工，必须有去年考核结果且与当前一致
                if (!lastYearAppraisalNumber.equals(appraisalResultNumber)) {
                    String errorMsg = StrFormatter.format("人员【{}】上年考度核结果存在变更，需删除【{}】年度创建的【员工职位档案调整】或 【职位及积分年度调整】记录 ", personName,year);
                    logger.error(errorMsg);
                    throw new ValidationException(errorMsg);
                }
                logger.info("非新员工考核结果校验通过");
            }
        }

        logger.info("考核结果校验完成 - 最终考核结果: {}", appraisalResultNumber);
        return appraisalResultNumber;
    }

    /**
     * 处理职位序列（如果是管理序列，则按职能序列进行调整）
     * @param jobSeq 序列
     * @return: java.lang.String
     * @author W.Y.C
     * @date: 2025/09/19 14:07
     */
    public static DynamicObject handleJobSeq(DynamicObject jobSeq) {
        logger.info("处理职位序列转换 - 原始序列编码: {}, 名称: {}",
                jobSeq.getString(FormConstant.NUMBER_KEY), jobSeq.getString(FormConstant.NAME_KEY));

        if(JobSeqEnum.MANAGE.getCode().equalsIgnoreCase(jobSeq.getString(FormConstant.NUMBER_KEY))){
            logger.info("管理序列转换为职能序列");
            QFilter filter = QFilterCommonHelper.getEnableFilter().and(QFilterCommonHelper.getDataStatusFilter()).and(QFilterCommonHelper.getCurrentVersionFilter())
                    .and(new QFilter(FormConstant.NUMBER_KEY, QCP.equals, JobSeqEnum.FUNCTIONAL.getCode()));
            QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                    .addIdNumberName(FormConstant.HBJM_JOBSEQHR);

            DynamicObject[] load = BusinessDataServiceHelper.load(
                    FormConstant.HBJM_JOBSEQHR,
                    queryFieldBuilder.buildSelect(),
                    new QFilter[]{filter}
            );

            if(load.length == 0){
                String errorMsg = StrFormatter.format("管理序列转职能序列错误，未找到编码为【{}】的职能序列。请维护编码为【{}】的职能序列！", JobSeqEnum.FUNCTIONAL.getCode(),JobSeqEnum.FUNCTIONAL.getCode());
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }

            DynamicObject convertedJobSeq = load[0];
            logger.info("管理序列转换完成 - 转换后序列编码: {}, 名称: {}",
                    convertedJobSeq.getString(FormConstant.NUMBER_KEY), convertedJobSeq.getString(FormConstant.NAME_KEY));
            return convertedJobSeq;
        }else{
            //其他不转换
            logger.info("非管理序列，无需转换");
            return jobSeq;
        }
    }


    /**
     * 检查职称/技能等级是否有效（避免职级计算错误）
     * @param proTitleLevelNumber 职称等级编码
     * @param ocpQualLevel 技能等级编码
     * @param jobSeqNumber 职位序列编码
     * @return: boolean
     * @author W.Y.C
     * @date: 2025/09/19 15:35
     */
    private boolean isInvalidRankingConditions(String proTitleLevelNumber, String ocpQualLevel, String jobSeqNumber) {
        // 情况1：无职称且无技能等级（或非技能序列）
        if (StringUtils.isBlank(proTitleLevelNumber) && (StringUtils.isBlank(ocpQualLevel) || JobSeqEnum.SKILL.getCode().equals(jobSeqNumber))) {
            return true;
        }
        // 情况2：技能序列下无技能等级，但有职称等级且职称无对应技能等级
        return JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) &&
                StringUtils.isBlank(ocpQualLevel) &&
                StringUtils.isNotBlank(proTitleLevelNumber) &&
                TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber) == null;
    }



    public static boolean useAppraisalResult(Long personId, Date date) {
        return useAppraisalResult(personId, date,null);
    }

    /**
     * 检查某个人在特定年份的考核结果是否被使用
     * 对应SHR：useAppraisalresult方法
     * @param personId 人员ID
     * @param date 日期
     * @return: boolean
     * @author W.Y.C
     * @date: 2025/09/20 15:10
     */
    public static boolean useAppraisalResult(Long personId, Date date,StringBuilder reasons) {
        logger.info("检查考核结果是否已被使用 - 人员ID: {}, 日期: {}", personId, date);

        //对应SHR代码：com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfileFluctuationListHandler#useAppraisalresult

        //查询条件：查询某个人员职位档案非失效状态 并且 (初定 或者 类型状态为：年度调整3;职位调动4) 并且 职称分数或技能分数>0的数据
        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)
                .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
                .and(new QFilter(PositionStructureConstant.NCKD_FIRSTRANK,QCP.equals, EnableEnum.YES.getCode()).or(new QFilter(PositionStructureConstant.NCKD_TYPESTATE,QCP.in, new String[]{"3","4"})))
                .and(rankScore.or(jobScore));
        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .add(PositionStructureConstant.NCKD_EXECUTEYEAR)
                .add(PositionStructureConstant.NCKD_FIRSTRANK)
                .add(PositionStructureConstant.NCKD_ADJUSTTYPE)
                .add(PositionStructureConstant.NCKD_BEGINDATE)
                .addIdNumberNameWithExtras(
                        new String[]{PositionStructureConstant.NCKD_JOBLEVELHR},
                        FormConstant.JOBLEVELSEQ
                )
                .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR)
                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);

        int year = DateUtil.getYear(date);
        logger.info("检查年度: {}", year);

        try (AlgoContext context = Algo.newContext()) {
            DataSet dataSet = QueryServiceHelper.queryDataSet(
                    JobLevelCalculatorService.class.getName() + "#useAppraisalResult",
                    PositionStructureConstant.PERSONPOSFILE_ENTITYID,
                    queryFieldBuilder.buildSelect(),
                    new QFilter[]{filer},
                    queryFieldBuilder.buildOrder()
            );


            // 条件1：如果调整年与初定年相同，则考核结果被使用；返回true
            if(dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_FIRSTRANK + " = true and " + PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
                    .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false) == 1){
                logger.info("调整年与初定年相同，考核结果已使用");
                if(reasons != null) {
                    reasons.append(StrFormatter.format("调整年【{}】与初定年度【】相同", DateUtil.getYear(date), DateUtil.getYear(date)));
                }
                return Boolean.TRUE;
            }

            //条件2：如果调整年没有任何调整记录，则考核结果未被使用。（Personpositionfiletypestate：3、4）；返回false
            int nowyeardo  = dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
                    .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false);
            logger.info("调整年记录数: {}", nowyeardo);

            if(nowyeardo == 0){
                logger.info("调整年无调整记录，考核结果未使用");
                return Boolean.FALSE;
            }

            //条件3：调整年是否有升降级或首聘（adjusttype：0、2），如果调整年有升降级记录（调整类型为0-降级或2-升级），则考核结果被使用。；返回true
            if(dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year +" and "+PositionStructureConstant.NCKD_ADJUSTTYPE + " in ('0','2')")
                    .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false) > 0){
                logger.info("调整年存在职级升降记录，考核结果已使用");
                if(reasons != null) {
                    reasons.append(StrFormatter.format("调整年【{}】存在职级升降", DateUtil.getYear(date)));
                }
                return Boolean.TRUE;
            }

            //条件4：最近一笔是否为首次聘任（adjusttype：3），如果最近一次调整是首次聘任（调整类型为3），则考核结果未被使用。；返回false
            DataSet firstRow = dataSet.copy().topBy(1, new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"});
            if (firstRow.hasNext()) {
                Row row = firstRow.next();
                String adjustType = row.getString(PositionStructureConstant.NCKD_ADJUSTTYPE);
                if("3".equalsIgnoreCase(adjustType)){
                    logger.info("最近一次调整为首次聘任，考核结果未使用");
                    return Boolean.FALSE;
                }
            }

            //条件5：调整年是否只有保级/序列变化等记录，如果调整年只有保级、序列变化、聘任下调、总分不足或无聘任的记录，则考核结果未被使用（adjusttype in ('1','4','5','6','7')）；返回false
            int count5 = dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year +" and "+PositionStructureConstant.NCKD_ADJUSTTYPE + " in ('1','4','5','6','7')")
                    .count(PositionStructureConstant.NCKD_EXECUTEYEAR, false);
            logger.info("调整年保级等记录数: {}", count5);

            if(count5 > 0 && count5 == nowyeardo){
                logger.info("调整年只有保级等记录，考核结果未使用");
                return Boolean.FALSE;
            }


            //条件6：调整年聘任职级是否变化，如果调整年职级有变化（最大职级-最小职级），则考核结果被使用。返回true
            DataSet maxSeqDs = dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
                    .groupBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR})
                    .max(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ), "maxSeq").finish();
            int maxSeq = 0;
            if(maxSeqDs.hasNext()){
                Row row = maxSeqDs.next();
                maxSeq = row.getInteger("maxSeq");
            }
            DataSet minSeqDs = dataSet.copy()
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
                    .groupBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR})
                    .min(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ), "maxSeq").finish();
            int minSeq = 0;
            if(minSeqDs.hasNext()){
                Row row = minSeqDs.next();
                minSeq = row.getInteger("maxSeq");
            }
            Integer differ = maxSeq - minSeq;
            logger.info("调整年职级变化 - 最大序号: {}, 最小序号: {}, 差值: {}", maxSeq, minSeq, differ);

            if (differ != 0) {
                logger.info("调整年存在职级变化，考核结果已使用");
                if(reasons != null) {
                    reasons.append(StrFormatter.format("调整年【{}】存在职级变化，职级变化数【{}】", DateUtil.getYear(date), differ));
                }
                return Boolean.TRUE;
            }


            //条件7：相比往年聘任职级是否有变化，如果调整年相比往年聘任职级有变化，则考核结果被使用。返回true
            //获取当年最大职级
            DataSet currentYear = dataSet.copy()
                    .orderBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"})
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "=" + year)
                    .groupBy(new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR})
                    .max(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ), "maxLevel").finish();

            Integer maxLevel = 0;
            if (currentYear.hasNext()) {
                Row row = currentYear.next();
                maxLevel = row.getInteger("maxLevel");
            }

            //获取往年职级
            DataSet pastYear = dataSet.copy()
                    .select(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ) +" as level,"+PositionStructureConstant.NCKD_EXECUTEYEAR+","+PositionStructureConstant.NCKD_BEGINDATE)
                    .filter(PositionStructureConstant.NCKD_EXECUTEYEAR + "<" + year)
                    .topBy(1, new String[]{PositionStructureConstant.NCKD_EXECUTEYEAR + " desc", PositionStructureConstant.NCKD_BEGINDATE + " desc"});
            Integer level = 0;
            if (pastYear.hasNext()) {
                Row row = pastYear.next();
                level = row.getInteger("level");
            }

            logger.info("往年职级对比 - 当年最大职级: {}, 往年职级: {}, 差值: {}", maxLevel, level, maxLevel - level);

            if(maxLevel - level != 0){
                logger.info("相比往年存在职级变化，考核结果已使用");
                if(reasons != null) {
                    reasons.append(StrFormatter.format("调整年【{}】相比往年存在职级变化，职级变化数【{}】", DateUtil.getYear(date), maxLevel - level));
                }
                return Boolean.TRUE;
            }

        }

        logger.info("所有条件均不满足，考核结果未使用");
        return Boolean.FALSE;
    }

    /**
     * 是否首次聘任
     * @param personId 人员ID
     * @return: boolean
     * @author W.Y.C
     * @date: 2025/09/20 18:20
     */
    private static boolean isFirstPr(long personId) {
        logger.info("检查是否首次聘任 - 人员ID: {}", 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)
                .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
                .and(rankScore.or(jobScore))
                .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));
        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .add(PositionStructureConstant.NCKD_FIRSTRANK)
                .addIdNumberName(PositionStructureConstant.NCKD_APPRAISALRESULT)
                .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR)
                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);

        //对应SHR：PRpersonpositionfileCollection
        DynamicObjectCollection query = QueryServiceHelper.query(
                PositionStructureConstant.PERSONPOSFILE_ENTITYID,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{filer},
                queryFieldBuilder.buildOrder()
        );

        logger.info("查询到相关职位档案记录数: {}", query.size());

        //历史档案没有聘任过职称或技能（根据技能分与职称分判定），或者只有一条记录且为初定且考核结果为空或为考核结果为"无"，为首次聘任
        if(query.isEmpty()){
            logger.info("无聘任记录，判定为首次聘任");
            return Boolean.TRUE;
        }else if(query.size() == 1){
            DynamicObject dynamicObject = query.get(0);
            boolean firstRank = dynamicObject.getBoolean(PositionStructureConstant.NCKD_FIRSTRANK);
            long appraisalResult = dynamicObject.getLong(String.join(".",PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.ID_KEY));
            String appraisalResultNumber = dynamicObject.getString(String.join(".",PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
            //考核结果为空或为考核结果为"无"
            boolean none = StringUtils.isBlank(appraisalResultNumber)  || AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber);

            boolean isFirstPr = firstRank && none;
            logger.info("单条记录检查 - 初定: {}, 考核结果为空或无: {}, 判定为首次聘任: {}", firstRank, none, isFirstPr);
            return isFirstPr;

        }

        logger.info("有多条聘任记录，判定为非首次聘任");
        return Boolean.FALSE;
    }

    /**
     * 检查是否为新员工初定且时间在第二年内
     * @param beginYear 日期
     * @param personId 人员
     * @return: boolean
     * @author W.Y.C
     * @date: 2025/09/20 19:04
     */
    private static boolean isNewPersonFirstRank(Date beginYear, long personId) {
        logger.info("检查是否为新员工初定 - 人员ID: {}, 日期: {}", personId, beginYear);

        DynamicObject firstRankDynamicObject = PositionFileHelper.getFirstRank(personId);
        if(firstRankDynamicObject == null){
            String errorMsg = StrFormatter.format("未获取到人员【{}】首次初定档案", personId);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        String typeState = firstRankDynamicObject.getString(PositionStructureConstant.NCKD_TYPESTATE);
        boolean firstRank = firstRankDynamicObject.getBoolean(PositionStructureConstant.NCKD_FIRSTRANK);
        int executeYear = firstRankDynamicObject.getInt(PositionStructureConstant.NCKD_EXECUTEYEAR);
        int year = DateUtil.getYear(beginYear);

        logger.info("首次初定信息 - 执行年度: {}, 类型状态: {}, 是否初定: {}, 当前年度: {}",
                executeYear, typeState, firstRank, year);

        if(firstRank && typeState.equals("1")){
            logger.info("为新员工初定");
            return Boolean.TRUE;
        }
        //新员工初定次年调整也允许没有考核结果
        if(year - 1 == executeYear){
            logger.info("新员工初定次年调整");
            return Boolean.TRUE;
        }

        logger.info("非新员工初定");
        return Boolean.FALSE;
    }


    /**
     * 计算考核结果对职级的影响
     * @param appraisalResultNumber 考核结果
     * @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) {
        logger.info("计算考核结果影响 - 考核结果: {}, 是否首次聘任: {}, 是否使用考核结果: {}",
                appraisalResultNumber, firstPr, usedAppraisalResult);

        int minuspersonappraisal = 0;
        if (!usedAppraisalResult || firstPr) {
            if (AppraisalResultEnum.EXCELLENT.getCode().equals(appraisalResultNumber)) {
                // 优秀 -> 升一级（动态调整阶段允许升）
                minuspersonappraisal = -1;
                jobLevelResult.adjustType = "2";
                logger.info("考核优秀，升一级");
            } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(appraisalResultNumber)) {
                // 基本合格 -> 降一级（但动态调整阶段一般保级）
                minuspersonappraisal = 1;
                jobLevelResult.adjustType = "1";
                logger.info("考核基本合格，降一级");
            } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(appraisalResultNumber)) {
                // 不合格 -> 降两级（但动态调整阶段一般保级）
                minuspersonappraisal = 2;
                jobLevelResult.adjustType = "1";
                logger.info("考核不合格，降两级");
            } else {
                logger.info("考核结果无升降级影响");
            }
        } else {
            logger.info("考核结果已被使用，不计算升降级影响");
        }

        logger.info("考核结果影响计算完成 - 降级数: {}, 调整类型: {}", minuspersonappraisal, jobLevelResult.adjustType);
        return minuspersonappraisal;
    }



    /**
     * 处理学历积分
     * 根据当前最新学历与上一次职位档案中的学历对比，计算学历积分
     * 如果学历未变化，则使用原有积分；如果学历有变化，则根据积分差值调整总积分
     * @param currentPersonPosFileByPerson 最近一次职位档案
     * @param positionAppointment 人员最信息（最新学历）
     * @return: int
     * @author W.Y.C
     * @date: 2025/09/20 22:06
     */
    public static BigDecimal handleDiplomaScore(DynamicObject currentPersonPosFileByPerson, PositionAppointmentBO positionAppointment,JobScoreInfo scoreInfo) {
        logger.info("开始处理学历积分");

        scoreInfo.whyDiplomaScore.put("基准时间", DateUtil.format(new Date(),DateUtil.NORM_DATETIME_PATTERN));
        //获取上一笔职位档案变动记录学历信息
        //对应SHR：lastDiplomaInfo
        DynamicObject diploma = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
        if(diploma == null){
            logger.warn("上一笔记录无学历信息");
            return BigDecimal.ZERO;
        }
        //对应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.diplomaName = diplomaName;
        scoreInfo.diplomaId = diplomaId;
        scoreInfo.diplomaScore = diplomaScore;
        //获取上一笔职位档案变动记录学历积分
        //对应SHR：lastDiplomaScore
        BigDecimal lastDiplomaScore = currentPersonPosFileByPerson.getBigDecimal(PositionStructureConstant.NCKD_DIPLOMASCORE);

        logger.info("上一笔记录学历信息 - ID: {}, 名称: {}, 配置分: {}, 实际分: {}",
                diplomaId, diplomaName, diplomaScore, lastDiplomaScore);

        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));

            logger.info("最新学历信息 - ID: {}, 名称: {}, 配置分: {}",
                    currentDiplomaId, currentDiplomaName, currentDiplomaScore);
        } else {
            logger.warn("未获取到最新学历信息");
        }

        // 对应SHR：DiplomaScore
        BigDecimal resultScore = currentDiplomaScore;

        logger.info("学历对比 - 原学历：【{}】，积分【{}】；最新学历：【{}】，积分【{}】",
                diplomaName, diplomaScore, currentDiplomaName, currentDiplomaScore);

        if (Objects.equals(currentDiplomaId,diplomaId)) {
            //学历不变，分数保持不变
            resultScore = lastDiplomaScore;
            logger.info("学历没有变化，分数保持不变: {}", resultScore);
            scoreInfo.whyDiplomaScore.put("学历没有变化", "分数保持不变");
        } else {
            // 分差 = 最新学历配置分 - 上一笔学历的最新配置分
            // 本次学历分 = 上一笔学历分 + 分差
            BigDecimal diff = resultScore.subtract(diplomaScore);
            resultScore = lastDiplomaScore.add(diff);
            scoreInfo.diplomaId = currentDiplomaId;
            scoreInfo.diplomaName = currentDiplomaName;
            scoreInfo.diplomaScore = currentDiplomaScore;
            scoreInfo.isDiplomaChange = Boolean.TRUE;
            scoreInfo.whyDiplomaScore.put("学历变化",
                    "学历分 = 上一笔记录学历分数(" + lastDiplomaScore + ") + (最新配置分("
                            + ConvertUtil.toStr(currentDiplomaScore) + ") - 上一学历最新配置分(" + ConvertUtil.toStr(diplomaScore) + "))");
            logger.info("学历发生变化，计算新分数: {} = {} + ({} - {})",
                    resultScore, lastDiplomaScore, currentDiplomaScore, diplomaScore);
        }

        logger.info("学历积分处理完成 - 最终分数: {}", resultScore);
        return resultScore;
    }

    /**
     * 处理序列相关信息
     */
    private static JobSeqInfo processJobFamilyInfo(DynamicObject newJobSeq,
                                                   DynamicObject currentPersonPosFileByPerson,
                                                   Date date) {
        logger.info("开始处理序列相关信息");

        JobSeqInfo jobSeqInfo = new JobSeqInfo();

        long newJonSeqId = newJobSeq.getLong(FormConstant.ID_KEY);
        String newJonSeqNumber = newJobSeq.getString(FormConstant.NUMBER_KEY);
        // 获取原序列信息
        DynamicObject currentJobSeq = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR);
        jobSeqInfo.currentJobSeq = currentJobSeq;
        //如果是管理序列,则按职能序列进行调整
        //对应SHR：986~993行
        DynamicObject convertJobSeq = handleJobSeq(currentJobSeq);
        currentJobSeq = convertJobSeq;
        jobSeqInfo.convertJobSeq = convertJobSeq;
        long currentJonSeqId = currentJobSeq.getLong(FormConstant.ID_KEY);
        String currentJonSeqNumber = currentJobSeq.getString(FormConstant.NUMBER_KEY);

        logger.info("序列信息 - 新序列ID: {}, 编码: {}; 当前序列ID: {}, 编码: {}",
                newJonSeqId, newJonSeqNumber, currentJonSeqId, currentJonSeqNumber);

        // 判断是否为序列转换
        //对应SHR：isadjusttype_4
        jobSeqInfo.isSequenceChange = !currentJonSeqNumber.equals(newJonSeqNumber);
        logger.info("是否序列转换: {}", jobSeqInfo.isSequenceChange);

        // 判断是否为跨单位调动
        jobSeqInfo.isCrossUnitTransfer = false;
        //上一段员工档案开始时间至当前开始时间有没有跨单位调动
        QFilter filter = QFilterCommonHelper.getBillStatusFilter()
                .and(String.join(".",FormConstant.BB_EM_TID,FormConstant.ID_KEY),QCP.equals,currentPersonPosFileByPerson.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)))
                //跨单位调动
                .and(String.join(".",FormConstant.AFFACTION_KEY,FormConstant.NUMBER_KEY),QCP.equals,"101060_S")
                .and(FormConstant.B_EFFECTIVEDATE,QCP.large_equals,currentPersonPosFileByPerson.getDate(PositionStructureConstant.NCKD_BEGINDATE))
                .and(FormConstant.B_EFFECTIVEDATE,QCP.less_equals,date);
        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                //调出公司
                .addIdNumberName("bcompany")
                //调入公司
                .addIdNumberName("acompany");
        DynamicObjectCollection query = QueryServiceHelper.query("hdm_transferapply", queryFieldBuilder.buildSelect(), new QFilter[]{filter});
        if(!query.isEmpty()){
            jobSeqInfo.isCrossUnitTransfer = true;
            logger.info("查询到跨单位调动记录数: {}", query.size());
        }

        logger.info("序列信息处理完成 - 是否序列转换: {}, 是否跨单位调动: {}",
                jobSeqInfo.isSequenceChange, jobSeqInfo.isCrossUnitTransfer);

        return jobSeqInfo;
    }


    /**
     * 处理员工职称和技能等级分数
     * 根据职位序列类型（技能序列/非技能序列）确定采用职称等级分还是技能等级分
     * @param currentPersonPosFileByPerson 最近一次职位档案
     * @param newJobSeq 最新职位序列
     * @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 currentPersonPosFileByPerson,DynamicObject newJobSeq,PositionAppointmentBO positionAppointment) {
        logger.info("开始处理职称和技能等级分数");

        //对应SHR：hrjobfamilynumber
        String newJobSeqNumber = newJobSeq.getString(FormConstant.NUMBER_KEY);
        logger.info("职位序列编码: {}", newJobSeqNumber);

        DynamicObject lastPerProTitle = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_PROTITLELEVEL);
        Long lastPerProTitleId = null;
        if(lastPerProTitle != null){
            lastPerProTitleId = lastPerProTitle.getLong(FormConstant.ID_KEY);
        }
        DynamicObject lastQuaLevel = currentPersonPosFileByPerson.getDynamicObject(PositionStructureConstant.NCKD_OCPQUALLEVEL);
        Long lastQuaLevelId = null;
        if(lastQuaLevel != null){
            lastQuaLevelId = lastQuaLevel.getLong(FormConstant.ID_KEY);
        }

        logger.info("上一笔记录 - 职称ID: {}, 技能ID: {}", lastPerProTitleId, lastQuaLevelId);

        // 当前人员最新职称等级
        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));

            logger.info("最新职称信息 - ID: {}, 编码: {}, 名称: {}, 分数: {}",
                    newPerProTitleId, newPerProTitleNumber, newPerProTitleName, newPerProTitleScore);
        } else {
            logger.info("未获取到最新职称信息");
        }

        // 当前人员最新技能等级
        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));

            logger.info("最新技能信息 - ID: {}, 编码: {}, 名称: {}, 分数: {}",
                    newQuaLevelId, newQuaLevelNumber, newQuaLevelName, newQuaLevelScore);
        } else {
            logger.info("未获取到最新技能信息");
        }

        JobScoreInfo jobScoreInfo = new JobScoreInfo();

        jobScoreInfo.isEndGainJobGrade = true;

        // 职称等级分
        jobScoreInfo.perProTitleScore = BigDecimal.ZERO;
        // 技能等级分
        jobScoreInfo.quaLevelScore = BigDecimal.ZERO;
        jobScoreInfo.perProTitleNumber = "";
        jobScoreInfo.quaLevelNumber = "";

        if (!JobSeqEnum.SKILL.getCode().equals(newJobSeqNumber) && (newPerProTitleScore != null && newPerProTitleScore.compareTo(BigDecimal.ZERO) > 0)) {
            // 获取当前最新职称等级分
            jobScoreInfo.perProTitleScore = newPerProTitleScore;
            jobScoreInfo.perProTitleId = newPerProTitleId;
            jobScoreInfo.perProTitleNumber = StringUtils.isNotBlank(newPerProTitleNumber) ? newPerProTitleNumber : StringUtils.EMPTY;
            jobScoreInfo.perProTitleName = StringUtils.isNotBlank(newPerProTitleName) ? newPerProTitleName : StringUtils.EMPTY;
            jobScoreInfo.rankName = rankName;
            jobScoreInfo.isEndGainJobGrade = false;
            jobScoreInfo.isPerProTitleChange = !Objects.equals(newPerProTitleId,lastPerProTitleId);

            logger.info("使用职称等级分: {}, 是否变化: {}", newPerProTitleScore, jobScoreInfo.isPerProTitleChange);
        } else if (JobSeqEnum.SKILL.getCode().equals(newJobSeqNumber)) {
            if (newQuaLevelScore != null && newQuaLevelScore.compareTo(BigDecimal.ZERO) > 0) {
                //获取当前最新技能等级分
                jobScoreInfo.quaLevelScore = newQuaLevelScore;
                jobScoreInfo.quaLevelId = newQuaLevelId;
                jobScoreInfo.quaLevelNumber = StringUtils.isNotBlank(newQuaLevelNumber) ? newQuaLevelNumber: "";
                jobScoreInfo.quaLevelName = StringUtils.isNotBlank(newQuaLevelName) ? newQuaLevelName: "";
                jobScoreInfo.jobStatusName = jobStatusName;
                jobScoreInfo.isPerProTitleChange = !Objects.equals(newQuaLevelId,lastQuaLevelId);
                jobScoreInfo.isEndGainJobGrade = false;

                logger.info("使用技能等级分: {}, 是否变化: {}", newQuaLevelScore, jobScoreInfo.isPerProTitleChange);
            } else {
                logger.warn("技能序列但无有效技能等级分");
            }
        } else {
            logger.warn("非技能序列但无有效职称等级分");
        }

        logger.info("职称技能分数处理完成 - 职称分: {}, 技能分: {}, 是否缺少聘任: {}",
                jobScoreInfo.perProTitleScore, jobScoreInfo.quaLevelScore, jobScoreInfo.isEndGainJobGrade);

        return jobScoreInfo;
    }

    /**
     * 计算职级ID
     */
    private static DynamicObject calculateJobGradeId(DynamicObject newJobSeq, JobScoreInfo jobScoreInfo, JobSeqInfo jobSeqInfo,
                                                     BigDecimal allSumScore, int currentJobLevelIndex, int minusPersonAppraisal, boolean fistPr, JobLevelResult jobLevelResult){
        logger.info("开始计算职级ID - 总积分: {}, 当前职级序号: {}, 降级数: {}, 是否首次聘任: {}",
                allSumScore, currentJobLevelIndex, minusPersonAppraisal, fistPr);

        String perProTitleNumber = jobScoreInfo.perProTitleNumber;
        String quaLevelNumber = jobScoreInfo.quaLevelNumber;
        JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(newJobSeq.getString(FormConstant.NUMBER_KEY));
        DynamicObject jobLevel = null;

        if (jobScoreInfo.isEndGainJobGrade) {
            logger.warn("缺少聘任,技能序列必需聘任技能等级,其它序列必需聘任职称等级!");
            //对应SHR：JobGradeid = personpositionfileUtils.endGainJobGrade(ctx, allsumScore, hrjobfamilynumber);
            jobLevel = getJobLevel(newJobSeq, null, null, null, null, Boolean.TRUE,Boolean.FALSE);
            //对应SHR：selMap.put("adjusttype", "7");
            jobLevelResult.adjustType = "7";
            jobLevelResult.adjustMsg = "缺少聘任";
        } else if (jobSeqInfo.isSequenceChange) {
            logger.info("序列转换情况");
            //对应SHR：JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, 0);
            jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, 0, Boolean.FALSE,Boolean.FALSE);
            //序列转换
            jobLevelResult.adjustType = "4";
            jobLevelResult.adjustMsg = "序列转换";
        } else if (jobSeqInfo.isCrossUnitTransfer) {
            logger.info("跨单位调动情况");
            //对应SHR：JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, 0);
            jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, 0, Boolean.FALSE,Boolean.FALSE);
            //保级
            jobLevelResult.adjustType = "1";
            jobLevelResult.adjustMsg = "跨单位调动保级";
        } else if (fistPr) {
            logger.info("首次聘任情况");
            //对应SHR：JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, minuspersonappraisal);
            jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, minusPersonAppraisal, Boolean.FALSE,Boolean.FALSE);
            jobLevelResult.adjustMsg = "首次聘任";
        } else {
            logger.info("普通情况");
            //对应SHR：JobGradeid = personpositionfileUtils.GainJobGrade(ctx, allsumScore, hrjobfamilynumber, zgjbnumber, zyjndjnumber, minuspersonappraisal < 0 ? minuspersonappraisal : 0);
            jobLevel = getJobLevel(newJobSeq, allSumScore, perProTitleNumber, quaLevelNumber, minusPersonAppraisal < 0 ? minusPersonAppraisal : 0, Boolean.FALSE,Boolean.FALSE);
        }

        if (jobLevel == null) {
            logger.error("计算职级ID失败，返回null");
            throw new ValidationException("计算职级ID失败");
        }

        int newJobGradeIndex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("初步职级计算结果 - 职级ID: {}, 职级序号: {}, 调整类型: {}",
                jobLevel.getLong(FormConstant.ID_KEY), newJobGradeIndex, jobLevelResult.adjustType);

        if (minusPersonAppraisal == -1 && currentJobLevelIndex == newJobGradeIndex) {
            logger.warn("考核优秀但总分不足，无法升级");
            jobLevelResult.adjustType = "6";
            jobLevelResult.adjustMsg = "总分不足无法升级";
        }

        return jobLevel;
    }

    /**
     * 获取排名信息
     * @param personId 人员ID
     * @param personName 人员名称
     * @param date 日期
     * @return: nckd.jxccl.hr.psms.business.JobLevelCalculatorService.RankingResult
     * @author W.Y.C
     * @date: 2025/09/22 14:19
     */
    public static RankingResultInfo getRankingInfo(Long personId, String personName, Date date) {
        logger.info("开始获取排名信息 - 人员: {}, ID: {}", personName, personId);

        LocalDateTime lastYearDateTime = DateUtil.minusYears(DateUtil.toLocalDateTime(date), 1);
        //取上年度绩效排名
        QFilter groupFilter = new QFilter(PerfRankMgmtConstant.NCKD_THEYEAR,QCP.equals,lastYearDateTime.getYear());
        groupFilter.and(QFilterCommonHelper.getStatusFilter());
        groupFilter.and(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_PERSON),QCP.equals, personId);
        groupFilter.and(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING),QCP.equals,EnableEnum.YES.getCode());
        //获取该员工上年度所在的排名分组
        DynamicObject[] groupArray = BusinessDataServiceHelper.load(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, FormConstant.ID_KEY+","+PerfRankMgmtConstant.NAME_KEY,
                new QFilter[]{groupFilter},
                FormConstant.CREATE_TIME_KEY + " desc," + FormConstant.MODIFY_TIME_KEY + " desc");
        //避免一个员工存在多个排名分组中，只取最新排名分组
        Long groupId = null;
        String groupName = null;
        if(groupArray != null && groupArray.length > 0){
            groupId = groupArray[0].getLong(FormConstant.ID_KEY);
            groupName = groupArray[0].getString(PerfRankMgmtConstant.NAME_KEY);
            logger.info("找到排名分组 - ID: {}, 名称: {}", groupId, groupName);
        } else {
            logger.warn("未找到排名分组");
        }

        if(groupId != null && groupId > 0){
            QFilter perfRankMgmtFilter = new QFilter(FormConstant.ID_KEY,QCP.equals,groupId);

            QueryFieldBuilder selectBuilder = QueryFieldBuilder.create()
                    .add(FormConstant.ID_KEY)
                    .addGroup(
                            new String[]{PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON},
                            FormConstant.ID_KEY
                    ).addGroup(
                            new String[]{PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY},
                            PerfRankMgmtConstant.NCKD_TOPRANK,
                            PerfRankMgmtConstant.NCKD_POSTALLOWANCE,
                            PerfRankMgmtConstant.NCKD_ALLOWANCERANK,
                            PerfRankMgmtConstant.NCKD_APPRAISALRESULT,
                            PerfRankMgmtConstant.NCKD_ISRANKING
                    );
            String selectFields = selectBuilder.buildSelect();

            QueryFieldBuilder orderByBuilder = QueryFieldBuilder.create()
                    .orderAsc(new String[]{PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY},
                            PerfRankMgmtConstant.NCKD_TOPRANK,PerfRankMgmtConstant.NCKD_ALLOWANCERANK);

            String orderBy = orderByBuilder.buildOrder();
            //对应SHR：allowancerankpercentSQL()方法
            try(AlgoContext context = Algo.newContext()){
                DataSet dataSet = QueryServiceHelper.queryDataSet(JobLevelCalculatorService.class.getName() + "#processRankingInfo", PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID,
                        selectFields, new QFilter[]{perfRankMgmtFilter},
                        String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_TOPRANK) + "," + String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ALLOWANCERANK));

                int totalCount = dataSet.copy().count(FormConstant.ID_KEY, false);
                logger.info("排名分组总人数: {}", totalCount);

                //获取R排名总人数
                //对应SHR：countallowancerank
                int countR = dataSet.copy().filter(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING) +" = true and "+String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_POSTALLOWANCE)+" = true")
                        .count(FormConstant.ID_KEY, false);
                //获取总排名总人数
                int count = dataSet.copy().filter(String.join(".",PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_ISRANKING) +" = true")
                        .count(FormConstant.ID_KEY, false);
                logger.info("R排名人数: {}, 总排名人数: {}", countR, count);

                //获取排名
                DataSet topRankDataSet = dataSet.copy().select(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_TOPRANK)+","+String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY))
                        .filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY) + " = " + personId);
                //对应SHR：topRank
                Integer topRank = null;
                if(topRankDataSet.hasNext()){
                    Row row = topRankDataSet.next();
                    topRank = row.getInteger(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_TOPRANK));
                }

                //获取R排名
                QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                        .addGroup(new String[]{PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY},
                                PerfRankMgmtConstant.NCKD_ALLOWANCERANK)
                        .addGroup(new String[]{PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY,PerfRankMgmtConstant.NCKD_PERSON},
                                FormConstant.ID_KEY);
                DataSet allowanceRankDataSet = dataSet.copy().select(queryFieldBuilder.buildSelect())
                        .filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_PERSON, FormConstant.ID_KEY) + " = " + personId);
                //对应SHR：allowancerank
                Integer allowanceRank = null;
                if(allowanceRankDataSet.hasNext()){
                    Row row = allowanceRankDataSet.next();
                    allowanceRank = row.getInteger(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ALLOWANCERANK));
                }

                logger.info("排名结果 - 总排名: {}, R排名: {}", topRank, allowanceRank);

                //计算公式：R排名百分比 = (个人R排名 / 分组总人数) × 100%
                //对应SHR：toprankpercent
                BigDecimal topRankPercent = null;
                if (topRank != null  && count > 0) {
                    topRankPercent = new BigDecimal(topRank).multiply(new BigDecimal(100)).divide(new BigDecimal(count), 6, RoundingMode.HALF_UP);
                }
                if (topRankPercent != null && (topRankPercent.compareTo(BigDecimal.ZERO) < 0 || topRankPercent.compareTo(new BigDecimal(100)) > 0)) {
                    String errorMsg = StrFormatter.format("全排名百分比必须在0-100之间，请检查排名分组配置是否有误。人员【{}】-排名分组【{}】-R排名总人数【{}】-排名【{}】",personName,groupName,count,topRank);
                    logger.error(errorMsg);
                    throw new ValidationException(errorMsg);
                }
                //对应SHR：allowancerankpercent
                BigDecimal allowanceRankPercent = null;
                if (allowanceRank != null && countR > 0) {
                    allowanceRankPercent = new BigDecimal(allowanceRank).multiply(new BigDecimal(100)).divide(new BigDecimal(countR), 6, RoundingMode.HALF_UP);
                }
                if (allowanceRankPercent != null && (allowanceRankPercent.compareTo(BigDecimal.ZERO) < 0 || allowanceRankPercent.compareTo(new BigDecimal(100)) > 0)) {
                    String errorMsg = StrFormatter.format("R排名百分比必须在0-100之间，请检查排名分组配置是否有误。人员【{}】-排名分组【{}】-R排名总人数【{}】-排名【{}】",personName,groupName,countR,allowanceRank);
                    logger.error(errorMsg);
                    throw new ValidationException(errorMsg);
                }

                RankingResultInfo rankingResultInfo = new RankingResultInfo();
                //获取R排名最后名次
                DataSet maxAllowanceRank = dataSet.copy().filter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ISRANKING) + " = true and " + String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_POSTALLOWANCE) + " = true")
                        .groupBy()
                        .max(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY, PerfRankMgmtConstant.NCKD_ALLOWANCERANK), "maxAllowanceRank").finish();
                if(maxAllowanceRank.hasNext()){
                    Row row = maxAllowanceRank.next();
                    rankingResultInfo.maxAllowanceRank = row.getInteger("maxAllowanceRank");
                }
                rankingResultInfo.topRank = topRank;
                rankingResultInfo.allowanceRank = allowanceRank;
                rankingResultInfo.count = count > 0 ? count : null;
                rankingResultInfo.countR = countR > 0 ? countR : null;
                rankingResultInfo.topRankPercent = topRankPercent;
                rankingResultInfo.allowanceRankPercent = allowanceRankPercent;
                rankingResultInfo.groupName = groupName;

                logger.info("排名信息获取完成 - 分组: {}, 总排名: {}/{}, R排名: {}/{}",
                        groupName, topRank, count, allowanceRank, countR);
                return rankingResultInfo;
            }
        }else{
            logger.warn("员工不存在排名分组");
        }
        return null;
    }

    /**
     * 处理序列转换
     * 对应SHR：PV.getnewjobgradeindexByrank
     */
    private static void handleSequenceChange(int jobLevelIndex, int currentJobLevelIndex, RankingResultInfo rankingResultInfo, Map<Integer, DynamicObject> jobLevelMap, JobLevelResult jobLevelResult, DynamicObject jobSeq){
        logger.info("处理序列转换 - 当前职级序号: {}, 计算职级序号: {}", currentJobLevelIndex, jobLevelIndex);

        if (jobLevelIndex > currentJobLevelIndex) {
            logger.info("序列切换,新职级号不能大于原序列职级序号，从 {} 调整为 {}", jobLevelIndex, currentJobLevelIndex);
            jobLevelIndex = currentJobLevelIndex;
        }
        jobLevelResult.jobLevel = jobLevelMap.get(jobLevelIndex);
        // 序列转换
        jobLevelResult.adjustType = "4";
        jobLevelResult.adjustMsg = "序列转换";

        int newIndex = getnewjobgradeindexByrank(jobLevelIndex,currentJobLevelIndex, rankingResultInfo,jobSeq);
        logger.info("序列转换处理完成 - 最终职级序号: {}, 调整类型: {}", newIndex, jobLevelResult.adjustType);
    }

    /**
     * 处理跨单位调动
     */
    private static void handleCrossUnitTransfer(int jobLevelIndex, int currentJobLevelIndex, RankingResultInfo rankingResultInfo, Map<Integer, DynamicObject> jobLevelMap, JobLevelResult jobLevelResult, DynamicObject jobSeq){
        logger.info("处理跨单位调动 - 当前职级序号: {}, 计算职级序号: {}", currentJobLevelIndex, jobLevelIndex);

        if (jobLevelIndex > currentJobLevelIndex) {
            logger.info("跨单位调动且未跨序列,平移处理，从 {} 调整为 {}", jobLevelIndex, currentJobLevelIndex);
            jobLevelIndex = currentJobLevelIndex;
        }
        jobLevelResult.jobLevel = jobLevelMap.get(jobLevelIndex);
        // 保级
        jobLevelResult.adjustType = "1";
        jobLevelResult.adjustMsg = "跨单位调动保级";

        int newIndex = getnewjobgradeindexByrank(jobLevelIndex,currentJobLevelIndex, rankingResultInfo,jobSeq);
        logger.info("跨单位调动处理完成 - 最终职级序号: {}, 调整类型: {}", newIndex, jobLevelResult.adjustType);
    }

    /**
     * 计算R排名升降级
     * 对应SHR：com.kingdee.shr.customer.web.handler.ContributeScore.PersonpositionfilecreateViewListHandler#getnewjobgradeindexByrank
     * @param jobLevelIndex
     * @param currentJobLevelIndex
     * @param rankingResultInfo
     * @param jobSeq
     * @return: int
     * @author W.Y.C
     * @date: 2025/09/22 19:47
     */
    public static int getnewjobgradeindexByrank(int jobLevelIndex, int currentJobLevelIndex, RankingResultInfo rankingResultInfo, DynamicObject jobSeq){
        logger.info("计算R排名升降级 - 当前职级序号: {}, 计算职级序号: {}", currentJobLevelIndex, jobLevelIndex);

        JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeq.getString(FormConstant.NUMBER_KEY));
        logger.info("职位序列: {}", jobSeqEnum.getName());

        if(rankingResultInfo == null || rankingResultInfo.countR == null || rankingResultInfo.allowanceRank == null){
            logger.warn("排名信息不完整，无法计算R排名升降级");
            if(rankingResultInfo == null){
                rankingResultInfo = new RankingResultInfo();
            }
            rankingResultInfo.allowanceRankMark = "无";
            rankingResultInfo.allowanceRankSel = "无";
            return jobLevelIndex;
        }

        rankingResultInfo.allowanceRankMark = rankingResultInfo.allowanceRank +"/" + rankingResultInfo.countR;
        Integer allowanceRank = rankingResultInfo.allowanceRank;
        Integer countR = rankingResultInfo.countR;
        Integer maxAllowanceRank = rankingResultInfo.maxAllowanceRank;

        logger.info("R排名信息 - 排名: {}/{}, 最后名次: {}", allowanceRank, countR, maxAllowanceRank);

        if(rankingResultInfo.countR < 10 && maxAllowanceRank != null && maxAllowanceRank.equals(rankingResultInfo.allowanceRank) &&
                rankingResultInfo.topRankPercent != null && rankingResultInfo.topRankPercent.compareTo(new BigDecimal(70)) >= 0){
            logger.info("R排名人数少于10人且处于末尾，全员绩效排名后30%，降一级");

            //当参与R排名的员工人数少于10人时，只对同时满足以下两个条件的员工降级：
            //1、R排名处于末尾（即排名最后）。
            //2、全员绩效排名位于后30%。
            jobLevelIndex--;
        }else{
            // 使用动态配置规则替换硬编码规则
            List<JobLevelRuleConfig> ruleConfigs = PositionFileHelper.getLevelConditionConf();
            logger.info("获取到职级规则配置数量: {}", ruleConfigs.size());

            // 查找匹配的规则
            JobLevelRuleConfig matchingRule = ruleConfigs.stream()
                    .filter(rule -> rule.isMatchJobSeq(jobSeqEnum.getCode()) &&
                            rule.isMatchJobLevel(currentJobLevelIndex))
                    .findFirst()
                    .orElse(null);

            if (matchingRule != null) {
                logger.info("找到匹配的职级规则: {}", matchingRule.getName());
                logger.info("职位序列【{}】职级【{}】应用的升保降级规则配置为【{}】，最大职级【{}】，最小职级【{}】，升级百分比阈值【{}】，降级百分比阈值【{}】",
                        matchingRule.getName(), currentJobLevelIndex, matchingRule.getName(),
                        matchingRule.getMaxJobLevelSeq(), matchingRule.getMinJobLevel(),
                        matchingRule.getUpGradeThreshold(), matchingRule.getDownGradeThreshold());

                int upGradeThreshold = matchingRule.getUpGradeThreshold() != null ?
                        matchingRule.getUpGradeThreshold() : 0;
                int downGradeThreshold = matchingRule.getDownGradeThreshold() != null ?
                        matchingRule.getDownGradeThreshold() : 0;

                // 使用配置的阈值进行判断
                int maxAllowanceRankThreshold = selMaxAllowanceRank(upGradeThreshold, countR);
                int minAllowanceRankThreshold = selMinAllowanceRank(downGradeThreshold, countR);

                logger.info("阈值计算 - 升级阈值排名: {}, 降级阈值排名: {}", maxAllowanceRankThreshold, minAllowanceRankThreshold);

                if (allowanceRank <= maxAllowanceRankThreshold) {
                    logger.info("R排名前{}%，符合升级条件", upGradeThreshold);
                    jobLevelIndex++;
                } else if (allowanceRank >= minAllowanceRankThreshold && minAllowanceRankThreshold > 0) {
                    logger.info("R排名后{}%，符合降级条件", downGradeThreshold);
                    jobLevelIndex--;
                } else {
                    logger.info("R排名在{}%到{}%之间，保持原级", upGradeThreshold, 100-downGradeThreshold);
                }
            }else{
                String errorMsg = StrFormatter.format("未匹配到符合职位序列【{}】职级【{}】对应的升保降级规则配置，请检查配置是否正确！",
                        jobSeqEnum.getName(), currentJobLevelIndex);
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }

           /* //技术或职能序列
            if (JobSeqEnum.TECHNICALS.getCode().equals(jobSeqEnum.getCode()) || JobSeqEnum.FUNCTIONAL.getCode().equals(jobSeqEnum.getCode())) {

                if (currentJobLevelIndex > 9) {
                    // 职级>9：前60%升职，后5%降职
                    if (allowanceRank <= selMaxAllowanceRank(60,countR)) {
                        // selMap.put("allowanceranksel", "前60%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(5,countR) && selMinAllowanceRank(5,countR) > 0) {
                        // selMap.put("allowanceranksel", "后5%");
                        jobLevelIndex--;
                    }
                } else if (currentJobLevelIndex > 5 && currentJobLevelIndex <= 9) {
                    // 职级5-9：前70%升职，后5%降职
                    if (allowanceRank <= selMaxAllowanceRank(70,countR)) {
                        // selMap.put("allowanceranksel", "前70%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(5,countR) && selMinAllowanceRank(5,countR) > 0) {
                        // selMap.put("allowanceranksel", "后5%");
                        jobLevelIndex--;
                    }
                }else if (currentJobLevelIndex <= 5) {
                    // 职级≤5：前80%升职，后3%降职
                    if (allowanceRank <= selMaxAllowanceRank(80,countR)) {
                        // selMap.put("allowanceranksel", "前80%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(3,countR) && selMinAllowanceRank(3,countR) > 0) {
                        // selMap.put("allowanceranksel", "后3%");
                        jobLevelIndex--;
                    }
                }
            }else if(JobSeqEnum.SKILL.getCode().equals(jobSeqEnum.getCode())){
                // 技能序列：
                if (currentJobLevelIndex > 8) {
                    //职级>8：前60%升职，后5%降职
                    if (allowanceRank <= selMaxAllowanceRank(60,countR)) {
                        // selMap.put("allowanceranksel", "前60%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(5,countR) && selMinAllowanceRank(5,countR) > 0) {
                        // selMap.put("allowanceranksel", "后5%");
                        jobLevelIndex--;
                    }
                }else if (currentJobLevelIndex > 4 && currentJobLevelIndex <= 8) {
                    // 职级4-8：前70%升职，后5%降职
                    if (allowanceRank <= selMaxAllowanceRank(70,countR)) {
                        // selMap.put("allowanceranksel", "前70%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(5,countR) && selMinAllowanceRank(5,countR) > 0) {
                        // selMap.put("allowanceranksel", "后5%");
                        jobLevelIndex--;
                    }
                } else if (currentJobLevelIndex <= 4) {
                    // 职级≤4：前80%升职，后3%降职
                    if (allowanceRank <= selMaxAllowanceRank(80,countR)) {
                        // selMap.put("allowanceranksel", "前80%");
                        jobLevelIndex++;
                    }
                    else if (allowanceRank >= selMinAllowanceRank(3,countR) && selMinAllowanceRank(3,countR) > 0) {
                        // selMap.put("allowanceranksel", "后3%");
                        jobLevelIndex--;
                    }
                }
            }*/
        }

        if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex > 1) {
            logger.info("限制只能升一级，从 {} 调整为 {}", jobLevelIndex, currentJobLevelIndex + 1);
            jobLevelIndex = currentJobLevelIndex + 1;
        }

        // 设置排名百分比描述
        if (allowanceRank <= selMaxAllowanceRank(60, countR)) {
            rankingResultInfo.allowanceRankSel = "前60%";
        }
        else if (allowanceRank <= selMaxAllowanceRank(70, countR)) {
            rankingResultInfo.allowanceRankSel = "前60-70%";
        }
        else if (allowanceRank <= selMaxAllowanceRank(80, countR)) {
            rankingResultInfo.allowanceRankSel = "前70-80%";
        }
        else if (allowanceRank <= selMaxAllowanceRank(95, countR)) {
            rankingResultInfo.allowanceRankSel = "前80-95%";
        }
        else if (allowanceRank >= selMinAllowanceRank(3, countR)) {
            rankingResultInfo.allowanceRankSel = "后3%";
        }
        else if (allowanceRank >= selMinAllowanceRank(5, countR)) {
            rankingResultInfo.allowanceRankSel = "后5%";
        }

        logger.info("R排名升降级计算完成 - 原始序号: {}, 新序号: {}, 排名描述: {}",
                currentJobLevelIndex, jobLevelIndex, rankingResultInfo.allowanceRankSel);
        return jobLevelIndex;
    }

    /**
     * 计算R排名的最大允许排名（前百分之几对应的排名）
     * 用于确定员工R排名是否在前百分之几的范围内，以判断是否符合升级条件
     *
     * @param percent 百分比阈值（如60表示前60%）
     * @param countAllowanceRank 参与R排名的总人数
     * @return int 对应百分比的最大排名位置
     * @author W.Y.C
     * @date: 2025/09/22 19:47
     */
    public static int selMaxAllowanceRank(int percent, int countAllowanceRank) {
        int maxAllowanceRank = 0;
        double maxAllowancerRankDouble = Double.parseDouble(countAllowanceRank + "") / 100.0 * Double.parseDouble(percent + "");
        maxAllowanceRank = (int) Math.round(maxAllowancerRankDouble);
        logger.info("计算最大允许排名 - 百分比: {}%, 总人数: {}, 结果: {}", percent, countAllowanceRank, maxAllowanceRank);
        return maxAllowanceRank;
    }


    /**
     * 计算R排名的最小允许排名（后百分之几对应的排名）
     * 用于确定员工R排名是否在后百分之几的范围内，以判断是否符合降级条件
     *
     * @param percent 百分比阈值（如5表示后5%）
     * @param countAllowanceRank 参与R排名的总人数
     * @return int 对应百分比的最小排名位置
     * @author W.Y.C
     * @date: 2025/09/22 19:47
     */
    public static int selMinAllowanceRank(int percent, int countAllowanceRank) {
        int minAllowanceRank = 0;
        double minAllowanceRankDouble = Double.parseDouble(countAllowanceRank + "") / 100.0 * Double.parseDouble(percent + "");
        minAllowanceRank = countAllowanceRank - ((int) Math.round(minAllowanceRankDouble));
        logger.info("计算最小允许排名 - 百分比: {}%, 总人数: {}, 结果: {}", percent, countAllowanceRank, minAllowanceRank);
        return minAllowanceRank;
    }



    /**
     * 获取最近一次聘任的职级索引
     */
    private static int getLastAppointmentJobGradeIndex(Long personId, int currentJobLevelIndex,int jobLevelIndex){
        logger.info("获取最近一次聘任的职级索引 - 人员ID: {}", 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)
                .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
                .and(rankScore.or(jobScore))
                .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));
        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .add(PositionStructureConstant.NCKD_FIRSTRANK)
                .addIdNumberName(PositionStructureConstant.NCKD_APPRAISALRESULT)
                .addIdNumberNameWithExtras(
                        new String[]{PositionStructureConstant.NCKD_JOBLEVELHR},
                        FormConstant.JOBLEVELSEQ
                )
                .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR)
                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);

        //对应SHR：PRpersonpositionfileCollection
        DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.PERSONPOSFILE_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filer}, queryFieldBuilder.buildOrder());

        logger.info("查询到聘任记录数: {}", query.size());

        if(!query.isEmpty()){
            DynamicObject personPosFile = query.get(0);
            int lastAppointmentJobGradeIndex = personPosFile.getInt(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
            logger.info("最近一次聘任职级索引: {}", lastAppointmentJobGradeIndex);
            return lastAppointmentJobGradeIndex;
        }

        logger.info("无聘任记录，使用当前职级索引: {}", currentJobLevelIndex);
        return currentJobLevelIndex;
    }

    /**
     * 根据考核结果使用情况处理职级计算
     */
    private static int handleAppraisalUsage(DynamicObject jobSeq, RankingResultInfo rankingResultInfo, int jobLevelIndex,
                                            int lastAppointmentJobGradeIndex, int currentJobLevelIndex, int minuspersonappraisal, boolean fistPr, boolean usedAppraisalResult, JobLevelResult jobLevelResult){
        logger.info("根据考核结果使用情况处理职级计算 - 当前序号: {}, 最近聘任序号: {}", jobLevelIndex, lastAppointmentJobGradeIndex);

        JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeq.getString(FormConstant.NUMBER_KEY));
        if (!usedAppraisalResult) {
            if (minuspersonappraisal == 0) {
                if (!fistPr) {
                    // 考核结果为保级 所以相等
                    logger.info("考核结果为保级，职级序号保持为最近聘任序号: {}", lastAppointmentJobGradeIndex);
                    jobLevelIndex = lastAppointmentJobGradeIndex;
                }
                if (fistPr && ((rankingResultInfo == null || rankingResultInfo.countR == null || rankingResultInfo.countR == 0) || (rankingResultInfo.allowanceRank == null || rankingResultInfo.allowanceRank == 0))) {
                    logger.info("首次聘任而且无R排名,保持分数算出来的职级");
                } else {
                    logger.info("应用R排名结果");
                    jobLevelIndex = getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResultInfo, jobSeq);
                }
                logger.info("R排名处理后的职级序号: {}", jobLevelIndex);
                logger.info("最近聘任职级序号: {}", lastAppointmentJobGradeIndex);

                if (currentJobLevelIndex > 1 && jobLevelIndex - lastAppointmentJobGradeIndex == 1) {
                    // R排名升级
                    jobLevelResult.adjustType = "2";
                    jobLevelResult.adjustMsg = "R排名升级";
                    logger.info("R排名升级");
                } else if (jobLevelIndex == lastAppointmentJobGradeIndex) {
                    // 保级
                    jobLevelResult.adjustType = "1";
                    jobLevelResult.adjustMsg = "R排名保级";
                    logger.info("R排名保级");
                }
            } else {
                logger.info("考核结果有升降级影响，计算R排名");
                getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResultInfo, jobSeq);
            }
        } else {
            logger.info("考核结果已被使用，计算R排名并保级");
            getnewjobgradeindexByrank(jobLevelIndex, currentJobLevelIndex, rankingResultInfo, jobSeq);
            // 保级
            jobLevelResult.adjustType = "1";
            jobLevelResult.adjustMsg = "考核结果已使用保级";
        }

        logger.info("考核结果使用处理完成 - 最终职级序号: {}, 调整类型: {}", jobLevelIndex, jobLevelResult.adjustType);
        return jobLevelIndex;
    }

    /**
     * 处理聘任相关限制
     */
    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) {
        logger.info("开始处理聘任相关限制 - 当前职级序号: {}, 计算职级序号: {}", currentJobLevelIndex, jobLevelIndex);
        /*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)) " +
                        "and Appraisalresult.number<>'06' order by beginDate desc, createTime desc");*/
        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)
                .and(new QFilter(PositionStructureConstant.NCKD_DISABLE, QCP.equals, EnableEnum.NO.getCode()))
                .and(rankScore.or(jobScore))
                .and(new QFilter(String.join(".", PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY), QCP.not_equals, AppraisalResultEnum.NONE.getCode()));

        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .add(PositionStructureConstant.NCKD_FIRSTRANK)
                .addIdNumberName(PositionStructureConstant.NCKD_APPRAISALRESULT)
                .addIdNumberNameWithExtras(
                        new String[]{PositionStructureConstant.NCKD_JOBLEVELHR},
                        FormConstant.JOBLEVELSEQ
                )
                .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR)
                .orderDesc(PositionStructureConstant.NCKD_BEGINDATE);

        //对应SHR：PRpersonpositionfileCollection
        DynamicObjectCollection query = QueryServiceHelper.query(
                PositionStructureConstant.PERSONPOSFILE_ENTITYID,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{filer},
                queryFieldBuilder.buildOrder());

        logger.info("查询到聘任记录数: {}", query.size());

        if (firstPr) {
            logger.info("前一笔为初定且考核结果为无,按首次聘任处理,所以放开限制");
            // 首次聘任
            jobLevelResult.adjustType = "3";
//            jobLevelResult.adjustMsg = "首次聘任";
        }else if (!query.isEmpty()){
            long hrjobfamilyid = jobSeqId;
            long oldHRJobFamilyId = currentJobSeqId;
            String hrjobfamilynumber = jobSeq.getString(FormConstant.NUMBER_KEY);
            String oldHRJobFamilyNumber = currentJobSeqNumber;
            BigDecimal currentRankScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_RANKSCORE);
            BigDecimal currentJobStatusScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_JOBSTATUSSCORE);

            logger.info("序列信息 - 新序列ID: {}, 编码: {}; 旧序列ID: {}, 编码: {}",
                    hrjobfamilyid, hrjobfamilynumber, oldHRJobFamilyId, oldHRJobFamilyNumber);
            logger.info("当前记录分数 - 职称分: {}, 技能分: {}", currentRankScore, currentJobStatusScore);

            if (!(currentRankScore.compareTo(BigDecimal.ZERO) > 0 || currentJobStatusScore.compareTo(BigDecimal.ZERO) > 0)) {
                logger.info("上条记录未聘任,取最近一条聘任");
                /* PersonpositionfileInfo prpersonpositionfileInfo = PRpersonpositionfileCollection.get(0);
                CoreBaseInfo JobGrade = (CoreBaseInfo) prpersonpositionfileInfo.get("JobGrade");*/
                //对应SHR：prpersonpositionfileInfo
                DynamicObject personPositionFile = query.get(0);
                //对应SHR：JobGrade.getInt("index")
                int index = personPositionFile.getInt(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
                currentJobLevelIndex = index;
                logger.info("使用最近聘任职级序号: {}", currentJobLevelIndex);

                // 2021-09-15 补充判断 如果之前有过年度调整,考核结果是否已被应用
                if (!usedAppraisalResult) {
                    //对应SHR：appraisalresultnumber
                    String appraisalResultNumber = personPositionFile.getString(String.join(PositionStructureConstant.NCKD_APPRAISALRESULT,FormConstant.NUMBER_KEY));
                    if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(appraisalResultNumber)) {
                        currentJobLevelIndex -= 1;
                        // 降级
                        jobLevelResult.adjustType = "0";
//                        jobLevelResult.adjustMsg = "考核基本合格降级";
                        logger.info("考核基本合格，降一级至: {}", currentJobLevelIndex);
                    } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(appraisalResultNumber)) {
                        currentJobLevelIndex -= 2;
                        // 降级
                        jobLevelResult.adjustType = "0";
//                        jobLevelResult.adjustMsg = "考核不合格降级";
                        logger.info("考核不合格，降两级至: {}", currentJobLevelIndex);
                    }
                }
            }else{
                logger.info("上条记录已聘任");
            }

            //这里为什么用ID判断又用number判断。因为ID至始至终都没有变过（如管理序列会被转换为职能序列），number可能会被转换。
            // 换了序列
            if (hrjobfamilyid != oldHRJobFamilyId) {
                logger.info("检查到有序列切换");
                //对应SHR：if (hrjobfamilynumber.equals(oldHRJobFamilyNumber)) {；1214行
                if (hrjobfamilynumber.equals(oldHRJobFamilyNumber)) {
                    logger.info("管理序列与职能序列之间切换,不做序列切换限制");
                    if (!usedAppraisalResult && jobLevelIndex >= currentJobLevelIndex + 1) {
                        // 升级
                        jobLevelResult.adjustType = "2";
//                        jobLevelResult.adjustMsg = "管理转职能序列升级";
                        jobLevelIndex = currentJobLevelIndex + 1;
                        logger.info("管理转职能序列，升级至: {}", jobLevelIndex);
                    }
                    else {
                        if (StringUtils.isNotBlank(jobLevelResult.adjustType) && "6".equals(jobLevelResult.adjustType)) {
                            logger.info("前面判断需要升级但总分未达到");
                        }
                        else {
                            // 保级
                            jobLevelResult.adjustType = "1";
//                            jobLevelResult.adjustMsg = "管理转职能序列保级";
                        }
                        jobLevelIndex = currentJobLevelIndex;
                        logger.info("管理转职能序列，保级: {}", jobLevelIndex);
                    }
                }
                else {
                    // 序列转换
                    jobLevelResult.adjustType = "4";
//                    jobLevelResult.adjustMsg = "序列转换";
                    logger.info("序列切换,新职级号不能大于原序列职级序号");
                    if (jobLevelIndex > currentJobLevelIndex) {
                        logger.info("序列转换限制，从 {} 调整为 {}", jobLevelIndex, currentJobLevelIndex);
                        jobLevelIndex = currentJobLevelIndex;
                    }
                }
            } else {
                if (usedAppraisalResult) {
                    logger.info("非年度首次调整,且考核结果做过升降级,所以以原职级做为限定范围");
                    // 保级
                    jobLevelResult.adjustType = "1";
//                    jobLevelResult.adjustMsg = "考核结果已使用保级";
                    jobLevelIndex = currentJobLevelIndex;
                } else {
                    if (minusPersonAppraisal > 0 && jobLevelIndex >= currentJobLevelIndex) {
                        logger.info("考核结果未使用且为降级,动态调整时按保级处理,在年度调整处理");
                        // 保级
                        jobLevelResult.adjustType = "1";
//                        jobLevelResult.adjustMsg = "考核降级动态调整保级";
                        jobLevelIndex = currentJobLevelIndex;
                    }
                    else if (jobLevelIndex - currentJobLevelIndex == 1) {
                        // 升级
                        jobLevelResult.adjustType = "2";
//                        jobLevelResult.adjustMsg = "正常升级";
                        logger.info("符合升级条件");
                    }
                }
            }
        }else{
            // 首次聘任
            jobLevelResult.adjustType = "3";
//            jobLevelResult.adjustMsg = "首次聘任";
            logger.info("首次聘任,所以放开限制");
        }

        /**
         * 确定是否超出总分最高任命职级
         */
        logger.info("检查是否超出总分最高任命职级 - 总积分: {}", allsumScore);
        //对应SHR：String personMaxjobgradeindexstr = personpositionfileUtils.MaxGainJobGrade(ctx, allsumScore, jobFamilyInfo.processedJobFamilyNumber);
        DynamicObject maxJobLevel = getMaxJobLevel(jobSeq, allsumScore);
        if(maxJobLevel == null){
            String errorMsg = StrFormatter.format("根据职位序列【{}】和积分【{}】未获取到符合条件的职级，请检查职级配置是否有误！",jobSeq.getString(FormConstant.NAME_KEY),allsumScore);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        //对应SHR：personMaxjobgradeindex
        int maxJobLevelIndex = maxJobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("总分最高可任命职级序号: {}", maxJobLevelIndex);

        // 超出总分能任命最高职职级则该最高职级就是要任命的职级
        if (jobLevelIndex > maxJobLevelIndex) {
            logger.warn("职级序号 {} 超出总分最高可任命职级序号 {}，调整为最高可任命职级", jobLevelIndex, maxJobLevelIndex);
            // 总分不足
            jobLevelResult.adjustType = "6";
//            jobLevelResult.adjustMsg = "总分不足限制";
            jobLevelIndex = maxJobLevelIndex;
            if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex == 1) {
                // R排名升级
                jobLevelResult.adjustType = "2";
//                jobLevelResult.adjustMsg = "总分不足但R排名升级";
                logger.info("总分不足但R排名升级");
            }
        }

        /**
         * 确定是否超出该职称等级或技能等级最高任命职级
         */
        logger.info("检查是否超出职称/技能等级最高任命职级");
        DynamicObject maxJobLevel1 = getMaxJobLevel(jobSeq,null,null,jobScoreInfo.perProTitleNumber, jobScoreInfo.quaLevelNumber);
        if(maxJobLevel1 == null){
            String levelName = "";
            if (JobSeqEnum.SKILL.getCode().equals(jobSeq.getString(FormConstant.NUMBER_KEY))) {
                levelName = StringUtils.isNotBlank(jobScoreInfo.quaLevelName) ? jobScoreInfo.quaLevelName : "";
            } else {
                levelName = StringUtils.isNotBlank(jobScoreInfo.perProTitleName) ? jobScoreInfo.perProTitleName : "";
            }
            String errorMsg = StrFormatter.format("根据职位序列【{}】和职称等级/技能等级【{}】未获取到符合条件的职级，请检查职级配置是否有误！",
                    jobSeq.getString(FormConstant.NAME_KEY), levelName);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);

        }
        int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("职称/技能等级最高可任命职级序号: {}", maxJobGradeIndex);

        // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
        if (jobLevelIndex > maxJobGradeIndex) {
            logger.warn("职级序号 {} 超出职称/技能等级最高可任命职级序号 {}，调整为最高可任命职级", jobLevelIndex, maxJobGradeIndex);
            if (StringUtils.isBlank(jobLevelResult.adjustType)) {
                // 聘任下调
                jobLevelResult.adjustType = "5";
                jobLevelResult.adjustMsg = "聘任下调限制";
            }
            jobLevelIndex = maxJobGradeIndex;
            if (currentJobLevelIndex > 1 && jobLevelIndex - currentJobLevelIndex == 1) {
                // R排名升级
                jobLevelResult.adjustType = "2";
//                jobLevelResult.adjustMsg = "聘任下调但R排名升级";
                logger.info("聘任下调但R排名升级");
            }
        }

        // 检查聘任分是否减少
        //对应SHR：personpositionfileInfo.getInt("jobstatusScore")
        BigDecimal currentJobStatusScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
        //对应SHR：personpositionfileInfo.getInt("RankScore")
        BigDecimal currentRankScore = currentPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_RANKSCORE);
        BigDecimal currentSum = currentJobStatusScore.add(currentRankScore);
        BigDecimal newSum = jobScoreInfo.perProTitleScore.add(jobScoreInfo.quaLevelScore);
        logger.info("聘任分对比 - 上一笔: {}, 当前: {}", currentSum, newSum);

        if (currentSum.compareTo(newSum) > 0) {
            logger.warn("聘任分减少，从 {} 减少到 {}", currentSum, newSum);
            //聘任下调
            jobLevelResult.adjustType = "5";
            jobLevelResult.adjustMsg = "聘任分减少下调";
        }

        logger.info("聘任限制处理完成 - 最终职级序号: {}, 调整类型: {}, 调整说明: {}",
                jobLevelIndex, jobLevelResult.adjustType, jobLevelResult.adjustMsg);

        //currentJobLevelIndex有变更，但是后续没有使用所以就不返回了
        return jobLevelIndex;
    }


    /**
     * 最终确定职级ID
     */
    private static DynamicObject determineFinalJobGradeId(int jobLevelIndex,
                                                          JobScoreInfo jobScoreInfo, Map<Integer, DynamicObject> jobLevelMap,
                                                          DynamicObject jobSeq, JobLevelResult jobLevelResult,
                                                          String appraisalResultNumber){
        logger.info("最终确定职级ID - 职级序号: {}, 考核结果: {}", jobLevelIndex, appraisalResultNumber);

        DynamicObject jobLevel = null;

        if (jobScoreInfo.isEndGainJobGrade) {
            logger.warn("缺少聘任,技能序列必需聘任技能等级,其它序列必需聘任职称等级!");
            jobLevel = getLowestJobLevel(jobSeq);
            jobLevelResult.adjustType = "7";
//            jobLevelResult.adjustMsg = "缺少聘任定最低职级";
        } else if (jobLevelMap.get(jobLevelIndex) != null) {
            jobLevel = jobLevelMap.get(jobLevelIndex);
            BigDecimal newSum = jobScoreInfo.perProTitleScore.add(jobScoreInfo.quaLevelScore);
            if (newSum.compareTo(BigDecimal.ZERO) <= 0 && jobLevelIndex == 1) {
                logger.warn("聘任分为0且职级为最低级，按无聘任处理");
                jobLevelResult.adjustType = "7";
//                jobLevelResult.adjustMsg = "聘任分为0定最低职级";
            }
        } else {
            logger.warn("职级映射中未找到对应序号 {} 的职级，返回最低职级", jobLevelIndex);
            jobLevel = getLowestJobLevel(jobSeq);
            jobLevelResult.adjustType = "7";
//            jobLevelResult.adjustMsg = "未找到对应职级定最低职级";
        }

        if (StringUtils.isBlank(appraisalResultNumber) || AppraisalResultEnum.NONE.getCode().equals(appraisalResultNumber)) {
            logger.warn("无考核结果，返回最低职级");
            jobLevel = getLowestJobLevel(jobSeq);
            jobLevelResult.adjustType = "8";
//            jobLevelResult.adjustMsg = "无考核结果定最低职级";
        }

        if (jobLevel != null) {
            logger.info("最终确定职级 - ID: {}, 编码: {}, 名称: {}, 序号: {}",
                    jobLevel.getLong(FormConstant.ID_KEY),
                    jobLevel.getString(FormConstant.NUMBER_KEY),
                    jobLevel.getString(FormConstant.NAME_KEY),
                    jobLevel.getInt(FormConstant.JOBLEVELSEQ));
        }

        jobLevelResult.jobLevel = jobLevel;
        return jobLevel;
    }


    /**
     * 判断是否符合三要素
     * @param jobSeq 序列
     * @param score 积分
     * @param proTitleLevel 职称等级
     * @param ocpQualLevel 技能等级
     * @param appraisalResultEnum 考核结果
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/09/26 11:44
     */
    public static boolean checkThreeElementsRequirement(DynamicObject jobSeq, BigDecimal score, DynamicObject proTitleLevel, DynamicObject ocpQualLevel,AppraisalResultEnum appraisalResultEnum){
        logger.info("检查三要素要求");

        //三要素说明：
        //1、【技术序列、职能序列】：有聘任的职称等级；【技能序列】：有聘任的技能等级
        //2、有考核结果
        //3、积分数能够达到职级
        String jobSeqNumber = jobSeq.getString(FormConstant.NUMBER_KEY);
        JobSeqEnum jobSeqEnum = JobSeqEnum.getByCode(jobSeqNumber);
        boolean isEligibleForJobLevel = Boolean.TRUE;

        logger.info("三要素检查 - 序列: {}, 积分: {}, 职称等级: {}, 技能等级: {}, 考核结果: {}",
                jobSeqEnum.getName(), score,
                proTitleLevel != null ? "有" : "无",
                ocpQualLevel != null ? "有" : "无",
                appraisalResultEnum != null ? appraisalResultEnum.getName() : "无");

        if ((jobSeqEnum == JobSeqEnum.TECHNICALS || jobSeqEnum == JobSeqEnum.FUNCTIONAL) && proTitleLevel == null) {
            //【技术序列或职能序列】无职称等级
            logger.warn("【技术序列或职能序列】无职称等级");
            isEligibleForJobLevel = Boolean.FALSE;
        } else if (jobSeqEnum == JobSeqEnum.SKILL && ocpQualLevel == null) {
            //【技能序列】无技能等级
            logger.warn("【技能序列】无技能等级");
            isEligibleForJobLevel = Boolean.FALSE;
        }
        if (appraisalResultEnum == null || AppraisalResultEnum.NONE.getCode().equals(appraisalResultEnum.getCode())) {
            //无考核结果
            logger.warn("无考核结果");
            isEligibleForJobLevel = Boolean.FALSE;
        }
        if(score == null || score.compareTo(BigDecimal.ZERO) <= 0) {
            logger.warn("积分为空或小于0");
            isEligibleForJobLevel = Boolean.FALSE;
        }

        logger.info("三要素检查结果: {}", isEligibleForJobLevel ? "符合" : "不符合");
        return isEligibleForJobLevel;
    }


    /**
     * 计算职级
     * 对应SHR：com.kingdee.shr.customer.web.handler.PersonpositionfileUtils#GainJobGrade 和 com.kingdee.shr.customer.web.handler.PersonpositionfileUtils#endGainJobGrade
     * @param jobSeq 职位序列（转换过的序列，例如管理序列转职能序列）
     * @param score 积分数；可为空，为空则不限高；不为空则根据传入的积分限定积分所处的职级
     * @param proTitleLevelNumber 职称等级编码
     * @param quaLevelNumber 技能等级编码
     * @param downgradeNum 降级数（根据绩效计算：基本合格：降1级、不合格：降2级）
     * @param useMinLevel 是否强制按最低职级定级；如果传入true时则按最低级返回；否则按正常定级
     * @param useNonLevel 是否强制按无职级定级；如果传入true时则按"无"职级返回；否则按正常定级
     * @return: kd.bos.dataentity.entity.DynamicObject 返回：匹配的职级（joblevelhr对象）
     * @author W.Y.C
     * @date: 2025/09/13 14:21
     */
    public static DynamicObject getJobLevel(DynamicObject jobSeq, BigDecimal score, String proTitleLevelNumber, String quaLevelNumber,
                                            Integer downgradeNum, boolean useMinLevel, boolean useNonLevel) {
        logger.info("开始计算职级 - 序列: {}, 积分: {}, 职称编码: {}, 技能编码: {}, 降级数: {}, 是否最低级: {}, 是否无职级: {}",
                jobSeq.getString(FormConstant.NAME_KEY), score, proTitleLevelNumber, quaLevelNumber,
                downgradeNum, useMinLevel, useNonLevel);

        DynamicObject jobLevel = null;
        if(useNonLevel) {
            //查询该序列"无职级"的数据并返回
            logger.info("强制按无职级定级");
            return getNonJobLevel(jobSeq);
        } else if(useMinLevel) {
            //最低职级
            logger.info("强制按最低职级定级");
            return getLowestJobLevel(jobSeq);
        }else{
            logger.info("正常计算职级");
            return getMaxJobLevel(jobSeq, score,downgradeNum, proTitleLevelNumber, quaLevelNumber);
        }
    }

    /**
     * 根据职位序列获取"无"职级
     * @param jobSeq 职位序列
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/09/20 16:52
     */
    public static DynamicObject getNonJobLevel(DynamicObject jobSeq){
        logger.info("获取无职级 - 序列: {}", jobSeq.getString(FormConstant.NAME_KEY));

        DynamicObjectCollection jobLevels = queryJobLevels(jobSeq.getString(FormConstant.NUMBER_KEY), null, 0, String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
        DynamicObject jobLevel = !jobLevels.isEmpty() ? jobLevels.get(0) : null;
        if( jobLevel != null) {
            long jobLevelId = jobLevel.getLong(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY));
            logger.info("找到无职级 - ID: {}", jobLevelId);
            //返回职级对象
            return BusinessDataServiceHelper.loadSingle(jobLevelId, FormConstant.HBJM_JOBLEVELHR);
        }
        logger.warn("未找到无职级");
        return null;
    }

    /**
     * 获取职位序列最低职级
     * @param jobSeq 职位序列
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/09/20 16:52
     */
    public static DynamicObject getLowestJobLevel(DynamicObject jobSeq) {
        logger.info("获取最低职级 - 序列: {}", jobSeq.getString(FormConstant.NAME_KEY));

        DynamicObjectCollection jobLevels = queryJobLevels(jobSeq.getString(FormConstant.NUMBER_KEY), null,
                null, String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
        DynamicObject jobLevel = !jobLevels.isEmpty() ? jobLevels.get(0) : null;
        if( jobLevel != null) {
            long jobLevelId = jobLevel.getLong(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY));
            logger.info("找到最低职级 - ID: {}", jobLevelId);
            //返回职级对象
            return BusinessDataServiceHelper.loadSingle(jobLevelId, FormConstant.HBJM_JOBLEVELHR);
        }
        logger.warn("未找到最低职级");
        return null;
    }

    /**
     * 获取职位序列和积分最高职级
     * @param jobSeq 职位序列
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/09/20 16:52
     */
    public static DynamicObject getMaxJobLevel(DynamicObject jobSeq,BigDecimal score) {
        logger.info("获取积分最高职级 - 序列: {}, 积分: {}", jobSeq.getString(FormConstant.NAME_KEY), score);

        DynamicObjectCollection jobLevels = queryJobLevels(jobSeq.getString(FormConstant.NUMBER_KEY), score,
                null, String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ +" desc"));
        DynamicObject jobLevel = !jobLevels.isEmpty() ? jobLevels.get(0) : null;
        if( jobLevel != null) {
            long jobLevelId = jobLevel.getLong(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY));
            logger.info("找到积分最高职级 - ID: {}, 序号: {}", jobLevelId, jobLevel.getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ)));
            //返回职级对象
            return BusinessDataServiceHelper.loadSingle(jobLevelId, FormConstant.HBJM_JOBLEVELHR);
        }
        logger.warn("未找到积分最高职级");
        return null;
    }



    /**
     * 根据[职位序列][积分]和[职称等级/技能等级]获取职位序列最高职级
     * @param jobSeq 职位序列
     * @param score 积分（可为空，为空则不限高）
     * @param downgradeNum 需降级数（可为空，为空则不降级）
     * @param proTitleLevelNumber 职称等级
     * @param ocpQualLevelNumber 技能等级
     * @return: kd.bos.dataentity.entity.DynamicObject 如没有获取到预期职级则返回最低职级
     * @author W.Y.C
     * @date: 2025/09/20 16:52
     */
    public static DynamicObject getMaxJobLevel(DynamicObject jobSeq, BigDecimal score, Integer downgradeNum, String proTitleLevelNumber, String ocpQualLevelNumber) {
        logger.info("获取最高职级 - 序列: {}, 积分: {}, 降级数: {}, 职称编码: {}, 技能编码: {}",
                jobSeq.getString(FormConstant.NAME_KEY), score, downgradeNum, proTitleLevelNumber, ocpQualLevelNumber);

        jobSeq = handleJobSeq(jobSeq);
        String jobSeqNumber = jobSeq.getString(FormConstant.NUMBER_KEY);
        /**
         * 如果没有职称等级与技能等级,则倒取该序列最低职等 如果没有职称且是非技能序列,则倒取该序列最低职等 如果是技能序列,没有技能等级但有职称等级,但职称等级没有对应的技能等级,则取该序列最低职等
         */
        if (StringUtils.isBlank(proTitleLevelNumber) && (StringUtils.isBlank(ocpQualLevelNumber) || !JobSeqEnum.SKILL.getCode().equals(jobSeqNumber))) {
            logger.warn("无职称等级且(无技能等级或非技能序列)，返回最低职级");
            return getLowestJobLevel(jobSeq);
        }
        else if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber) && StringUtils.isBlank(ocpQualLevelNumber) && StringUtils.isNotBlank(proTitleLevelNumber) && TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber) == null) {
            logger.warn("技能序列无技能等级且职称等级无对应技能等级，返回最低职级");
            return getLowestJobLevel(jobSeq);
        }
        String likename = "";
        String likenumber = "";
        if (JobSeqEnum.SKILL.getCode().equals(jobSeqNumber)) {
            if (StringUtils.isBlank(ocpQualLevelNumber)) {
                likenumber = TECH_POST_LEVEL_TO_JOB_STATUS_LEVEL_MAP.get(proTitleLevelNumber);
                likename = "职称等级【" + proTitleLevelNumber + "】对应的技能等级";
                logger.info("技能序列但无技能等级,使用职称等级【{}】对应的技能等级,编码【{}】", proTitleLevelNumber, likenumber);
            }
            else {
                likenumber = ocpQualLevelNumber;
                likename = "技能等级";
                logger.info("技能序列使用技能等级,编码【{}】", likenumber);
            }
        }
        else {
            likenumber = proTitleLevelNumber;
            likename = "职称等级";
            logger.info("非技能序列使用职称等级,编码【{}】", likenumber);
        }

        DynamicObject jobLevels = getJobLevelByJobSeqAndQualAndScore(jobSeqNumber, score, null,
                likenumber, String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ + " desc"));

        if(jobLevels != null){
            if(downgradeNum != null && downgradeNum > 0) {
                //降级
                Map<Integer, DynamicObject> jobLevelMap = getJobLevelByJobSeqMap(jobSeq);
                //当前分数能达到的最大职级减去需降级数
                int maxJobLevelSeq = jobLevels.getInt(FormConstant.JOBLEVELSEQ);
                DynamicObject jobLevel = jobLevelMap.get(maxJobLevelSeq - downgradeNum);
                if (jobLevel == null) {
                    //降级数超出则返回最低职级
                    logger.warn("降级数 {} 超出范围，返回最低职级", downgradeNum);
                    return getLowestJobLevel(jobSeq);
                }
                logger.info("应用降级数 {}，从序号 {} 降至 {}", downgradeNum, maxJobLevelSeq, maxJobLevelSeq - downgradeNum);
                return jobLevel;
            }
            long jobLevelId = jobLevels.getLong(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY));
            logger.info("找到最高职级 - ID: {}, 序号: {}", jobLevelId, jobLevels.getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ)));
            return BusinessDataServiceHelper.loadSingle(jobLevelId, FormConstant.HBJM_JOBLEVELHR);
        }else{
            //没有获取到预期的职级则返回最低职级
            logger.warn("未获取到预期职级，返回最低职级");
            return getLowestJobLevel(jobSeq);
        }
    }

    /**
     * 获取职位序列对应的职级
     * @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) {
        logger.info("获取职位序列职级映射 - 序列: {}", jobSeq.getString(FormConstant.NAME_KEY));

        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)
                    );
            logger.info("获取到职级数量: {}", jobLevelMap.size());
        }
        return jobLevelMap;
    }

    /**
     * 获取职位序列对应的职级
     * @param jobSeq 职位序列
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/09/20 16:52
     */
    public static DynamicObject[] getJobLevelByJobSeq(DynamicObject jobSeq) {
        logger.info("获取职位序列对应的职级 - 序列: {}", jobSeq.getString(FormConstant.NAME_KEY));

        DynamicObjectCollection jobLevels = queryJobLevels(jobSeq.getString(FormConstant.NUMBER_KEY), null,
                null, String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
        List<Long> jobLevelIds = jobLevels.stream().map(dynamicObject -> dynamicObject.getLong(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.ID_KEY))).collect(Collectors.toList());
        QFilter idInFilter = QFilterCommonHelper.getIdInFilter(jobLevelIds);

        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .addIdNumberNameWithExtras(
                        FormConstant.JOBLEVELSEQ,
                        PositionStructureConstant.NCKD_ALIAS,
                        FormConstant.NCKD_COEFFICIENT,
                        FormConstant.NCKD_SCORE
                );

        DynamicObject[] result = BusinessDataServiceHelper.load(
                FormConstant.HBJM_JOBLEVELHR,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{idInFilter},
                FormConstant.JOBLEVELSEQ
        );

        logger.info("查询到职级数量: {}", result != null ? result.length : 0);
        return result;
    }

    /**
     * 根据职位序列、资格条件、积分或序列号筛选职级
     * @param jobSeqNumber 职位序列编码
     * @param score 积分
     * @param jobLevelSeq 职级序号
     * @param orderBy 排序
     * @return: kd.bos.dataentity.entity.DynamicObjectCollection
     * @author W.Y.C
     * @date: 2025/09/27 13:49
     */
    private static DynamicObjectCollection queryJobLevels(String jobSeqNumber, BigDecimal score, Integer jobLevelSeq, String orderBy) {
        logger.info("查询职级 - 序列编码: {}, 积分: {}, 职级序号: {}, 排序: {}", jobSeqNumber, score, jobLevelSeq, orderBy);

        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .addIdNumberNameWithExtras(
                        new String[]{FormConstant.HBJM_JOBLEVELHR},
                        FormConstant.JOBLEVELSEQ,
                        PositionStructureConstant.NCKD_ALIAS,
                        FormConstant.NCKD_COEFFICIENT,
                        FormConstant.NCKD_SCORE
                );


        QFilter filter = new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR,FormConstant.NCKD_SCORE), QCP.is_notnull, null)
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBLEVELHR,FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBLEVELHR,FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()))
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_ISPARTICIPATE), QCP.equals, EnableEnum.YES.getCode()));
        if(score != null){
            filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE), QCP.less_equals, score));
        }
        if(jobLevelSeq != null){
            filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.less_equals, jobLevelSeq));
        }else{
            //只查询有效的序号（无职级是-1，避免查出来）
            filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0));
        }

        DynamicObjectCollection query = null;
        //根据职位序列查询
        filter.and(new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY), QCP.equals, jobSeqNumber))
                .and(new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ, FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.NCKD_JOBSEQ,FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.NCKD_JOBSEQ,FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()));
        DynamicObjectCollection result = QueryServiceHelper.query(PositionStructureConstant.GETLEVELBYJOBSEQ_QUERY,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{filter},
                orderBy);

        logger.info("查询到职级记录数: {}", result.size());
        return result;
    }

    /**
     * /根据职位序列、资格级别和分数获取符合条件的职级
     * @param jobSeqNumber 职位序列编码
     * @param score 可为空，为空则不限制分数
     * @param jobLevelSeq 职级序号
     * @param jobLevelNumber 任职资格（职称级别或技能等级编码）
     * @param orderBy
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/12/20 18:20
     */
    public static DynamicObject getJobLevelByJobSeqAndQualAndScore(String jobSeqNumber, BigDecimal score, Integer jobLevelSeq, String jobLevelNumber, String orderBy) {
        logger.info("根据资格级别和分数获取职级 - 序列编码: {}, 积分: {}, 职级序号: {}, 资格级别: {}",
                jobSeqNumber, score, jobLevelSeq, jobLevelNumber);

        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                .addIdNumberNameWithExtras(
                        new String[]{FormConstant.HBJM_JOBLEVELHR},
                        FormConstant.JOBLEVELSEQ,
                        PositionStructureConstant.NCKD_ALIAS,
                        FormConstant.NCKD_COEFFICIENT,
                        FormConstant.NCKD_SCORE
                );

        QFilter filter = new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR,FormConstant.NCKD_SCORE), QCP.is_notnull, null)
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBLEVELHR,FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBLEVELHR,FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()))
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.is_notnull, null))
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_ISPARTICIPATE), QCP.equals, EnableEnum.YES.getCode()));
        if(jobLevelSeq != null){
            filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.less_equals, jobLevelSeq));
        }else{
            //只查询有效的序号（无职级是-1，避免查出来）
            filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ), QCP.large_than, 0));
        }

        QFilter filter1 = filter.copy();
        filter1.and(new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY), QCP.equals, jobSeqNumber))
                .and(new QFilter(String.join(".",FormConstant.NCKD_JOBSEQ, FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.NCKD_JOBSEQ,FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.NCKD_JOBSEQ,FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()));
        if(score != null){
            filter1.and(new QFilter(String.join(".",FormConstant.HBJM_JOBLEVELHR, FormConstant.NCKD_SCORE), QCP.less_equals, score));
        }

        //获取分数可以达到的职级列表
        DynamicObjectCollection query1 = QueryServiceHelper.query(PositionStructureConstant.GETLEVELBYJOBSEQ_QUERY,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{filter1},
                orderBy);

        logger.info("积分限制查询到记录数: {}", query1.size());

        Map<Integer, List<DynamicObject>> resultMap = query1.stream()
                .collect(Collectors.groupingBy(
                        obj -> obj.getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ)),
                        LinkedHashMap::new,
                        Collectors.toList()
                ))
                .entrySet()
                .stream()
                .sorted(Map.Entry.<Integer, List<DynamicObject>>comparingByKey().reversed())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (existing, replacement) -> existing,
                        LinkedHashMap::new
                ));

        //获取职称级别/技能级别可以达到的最大职级
        filter.and(new QFilter(String.join(".",FormConstant.HBJM_JOBGRADESCMHR,FormConstant.NCKD_JOBSEQ,FormConstant.NUMBER_KEY), QCP.equals, jobSeqNumber))
                .and(new QFilter(String.join(".",FormConstant.HBJM_JOBGRADESCMHR,FormConstant.NCKD_JOBSEQ, FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBGRADESCMHR,FormConstant.NCKD_JOBSEQ,FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.HBJM_JOBGRADESCMHR,FormConstant.NCKD_JOBSEQ,FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()))
                .and(new QFilter(String.join(".",FormConstant.IS_CURRENT_VERSION), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.ENABLE), QCP.equals, EnableEnum.YES.getCode()))
                .and(new QFilter(String.join( ".",FormConstant.STATUS), QCP.equals, StatusEnum.C.toString()));
        filter.and(new QFilter(FormConstant.NCKD_JOBLEVELNUMBER, QCP.like, "%"+jobLevelNumber+"%"));
        DynamicObjectCollection query = QueryServiceHelper.query(PositionStructureConstant.GETLEVELBYJOBSEQQUERYANDQUAL_QUERY,
                queryFieldBuilder.buildSelect(),
                new QFilter[]{filter},
                orderBy);

        logger.info("资格级别限制查询到记录数: {}", query.size());

        if(!query.isEmpty()){
            int max = query.get(0).getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ));
            logger.info("资格级别最大可任命职级序号: {}", max);

            //找到 resultMap 中最接近 max 值的职级
            DynamicObject closestObject = resultMap.entrySet().stream()
                    .min(Comparator.comparingInt(entry -> Math.abs(entry.getKey() - max)))
                    .map(Map.Entry::getValue)
                    .filter(list -> !list.isEmpty())
                    .map(list -> list.get(0))
                    .orElse(null);

            if (closestObject != null) {
                logger.info("找到最接近职级 - 序号: {}", closestObject.getInt(String.join(".", FormConstant.HBJM_JOBLEVELHR, FormConstant.JOBLEVELSEQ)));
            }
            return closestObject;
        }

        logger.warn("未找到符合条件的职级");
        return null;
    }

    // 辅助类定义
    public static class JobSeqInfo {
        /** 是否为序列转换 */
        //对应SHR：isadjusttype_4
        public boolean isSequenceChange;
        /** 是否为跨单位调动*/
        //对应SHR：isadjusttype_1
        public boolean isCrossUnitTransfer;

        public DynamicObject currentJobSeq;
        public DynamicObject convertJobSeq;
    }

    public static class JobScoreInfo {
        /** 职称等级分 */
        public BigDecimal perProTitleScore;
        /** 职称级别ID */
        public Long perProTitleId;
        /** 职称级别编码 */
        public String perProTitleNumber;
        /** 职称等级名称 */
        public String perProTitleName;
        /** 职称名称*/
        public String rankName;
        /** 职称是否变化 */
        public boolean isPerProTitleChange;

        /** 技能等级分 */
        public BigDecimal quaLevelScore;
        /** 技能等级ID */
        public Long quaLevelId;
        /** 技能等级编码 */
        public String quaLevelNumber;
        /** 技能等级名称 */
        public String quaLevelName;
        /** 技能名称*/
        public String jobStatusName;
        /** 技能是否变化 */
        public boolean isQuaLeveleChange;

        /** 是否缺少聘任 */
        public boolean isEndGainJobGrade;

        /** 学历名次 */
        public String diplomaName;
        /** 学历ID */
        public Long diplomaId;
        /** 学历分 */
        public BigDecimal diplomaScore;
        /** 计算后的得分 */
        public BigDecimal allSumScore;
        /** 学历是否变化 */
        public boolean isDiplomaChange;

        /** 学历得分说明 */
        Map<String, String> whyDiplomaScore = new LinkedHashMap<>();
    }

    public static class RankingResultInfo {
        /** 排名 */
        public Integer topRank;
        /** R排名 */
        //对应SHR：allowancerank
        public Integer allowanceRank;
        /** 最后名次 */
        //对应SHR：maxallowancerank
        public Integer maxAllowanceRank;
        /** 全排名百分比 */
        public BigDecimal topRankPercent;
        /** R排名百分比 */
        public BigDecimal allowanceRankPercent;
        /** 全排名人数 */
        public Integer count;
        /** R排名人数 */
        //对应SHR：countallowancerank
        public Integer countR;
        //排名分组名称
        /** R排名人数 */
        public String groupName;
        /** R排名名次/R排名总人数 */
        //对应SHR：allowancerankmark
        public String allowanceRankMark;
        /** R排名百分比 */
        //对应SHR：allowanceranksel
        public String allowanceRankSel;
    }

    public static class JobLevelResult {
        /** 调整类别（升级2;保级1;降级0;首次聘任3;序列变化4;聘任下调5;总分不足6;无聘任7;无考核结果8）*/
        public String adjustType;
        public String adjustMsg;
        /** 计算后的职级 */
        public DynamicObject jobLevel;


        /** 序列信息 */
        public JobSeqInfo jobSeqInfo = new JobSeqInfo();
        /** 积分信息 */
        public JobScoreInfo jobScoreInfo = new JobScoreInfo();
        /** 排名信息 */
        public RankingResultInfo rankingResultInfo = new RankingResultInfo();
    }
}