package nckd.jxccl.hr.psms.business;

import kd.bos.common.enums.EnableEnum;
import kd.bos.context.RequestContext;
import kd.bos.dataentity.entity.DynamicObject;
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 nckd.jxccl.base.common.constant.FormConstant;
import nckd.jxccl.base.common.enums.AppraisalResultEnum;
import nckd.jxccl.base.common.enums.psms.AdjustTypeEnum;
import nckd.jxccl.base.common.enums.psms.JobSeqEnum;
import nckd.jxccl.base.common.enums.psms.ScoreItemEnum;
import nckd.jxccl.base.common.enums.psms.TypeStateEnum;
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.hr.psms.common.PositionStructureConstant;
import nckd.jxccl.hr.psms.common.bo.PositionAppointmentBO;
import nckd.jxccl.hr.psms.helper.ContributionHelper;
import nckd.jxccl.hr.psms.helper.PositionFileHelper;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;

/**
* 年度调整服务类
* @author W.Y.C
* @date 2025/10/08 22:12
* @version 1.0
*/
public class AnnualAdjustmentService {

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

    /**
     * 生成年度调整记录
     * @param person    员工
     * @param beginDate   生效日期
     * @return 成功返回 新的职位档案
     * @note 对应SHR：PersonpositionfilecreateViewListHandler#addNewYear_PersonpositionfileInfo（277~815行）
     */
    public static DynamicObject addNewYearPersonPositionFileInfo(DynamicObject person, Date beginDate,String remark,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile) {
        logger.info("开始执行年度调整记录生成，员工ID：" + (person != null ? person.getLong(FormConstant.ID_KEY) : "未知")
                + "，员工姓名：" + (person != null ? person.getString(FormConstant.NAME_KEY) : "未知"));

        int executeYear = beginDate != null ? DateUtil.getYear(beginDate) : DateUtil.getYear(new Date());

        // 1、 初始化上下文并加载基础数据。
        // 对应SHR：291~339行
        Date adjustDate = beginDate;
        if (adjustDate != null) {
            // 如果adjustDate不为空且为年初日期(xxxx-01-01)，则转换为当年7月1日
            LocalDateTime adjustDateTime = DateUtil.toLocalDateTime(adjustDate);
            if (adjustDateTime.getMonthValue() == 1 && adjustDateTime.getDayOfMonth() == 1) {
                //前端只选年份，这里默认为XXX-07-01
                adjustDate = DateUtil.toDate(adjustDateTime.withMonth(7).withDayOfMonth(1));
                logger.info("调整日期为年初日期，已转换为当年7月1日");
            }
        }else{
            //获取当年07-01的日期
            /*LocalDate julyFirst = LocalDate.of(DateUtil.getYear(new Date()), 7, 1);
            adjustDate = Date.from(julyFirst.atStartOfDay(ZoneId.systemDefault()).toInstant());*/
            adjustDate = new Date();
            logger.info("未指定调整日期，默认使用当前日期");
        }
        String adjustDateStr = DateUtil.format(adjustDate,DateUtil.NORM_DATE_PATTERN);
        AdjustmentContext ac = initAndLoad(executeYear, person, beginDate,adjustDate,positionAppointment,performanceResult,firstPersonPosFile);
        ac.remark = remark;

        logger.info("初始化上下文完成，执行年份：" + executeYear + "，员工：" + ac.personName);

        //2、 判断是否为本年首次调整、考核是否已被使用，并根据规则决定是否使用R排名。
        //对应SHR：341~391行
        evaluateFirstAndAppraisalUsage(ac);
        logger.info("评估是否为本年首次调整及考核使用情况完成");

        // 3、加载上一条记录并校验生效日期。
        //对应SHR：393~421行
        loadLastRecordAndValidateBeginDate(ac);
        logger.info("加载上一条记录并校验生效日期完成");

        // 4、处理职位序列（如果是管理序列，则按职能序列进行调整）
        //对应SHR：521~527行
        DynamicObject convertJobSeq = JobLevelCalculatorService.handleJobSeq(ac.jobSeq);
        ac.convertJobSeq = convertJobSeq;
        DynamicObject convertLastJobSeq = JobLevelCalculatorService.handleJobSeq(ac.data.getLastJobSeq());
        ac.data.setConvertLastJobSeq(convertLastJobSeq);
        logger.info("处理职位序列完成");
        // 5、获取技能/职称分
        //对应SHR：451~481行
        JobLevelCalculatorService.JobScoreInfo jobScoreInfo = JobLevelCalculatorService.handleJobScores(ac.lastRecordInfo,convertJobSeq, ac.positionAppointment);
        ac.data.setJobScoreInfo(jobScoreInfo);
        logger.info("获取技能/职称分完成");

        // 6、计算学历得分并生成说明
        //对应SHR：423~449行
        ac.diplomaScore = JobLevelCalculatorService.handleDiplomaScore(ac.lastRecordInfo, ac.positionAppointment, jobScoreInfo);
        ac.whyDiplomaScore.putAll(jobScoreInfo.whyDiplomaScore);
        logger.info("计算学历得分完成");

        //---------------------------------- 调整日志 begin ----------------------------------
        ac.whyAdjust1.add(StrFormatter.format("毕业时间小于等于【{}】的最高学历【{}】，学历分【{}】{}",
                adjustDateStr, ConvertUtil.toStr(jobScoreInfo.diplomaName,"无"), ConvertUtil.toStr(jobScoreInfo.diplomaScore,"无"),jobScoreInfo.isDiplomaChange ? "（变化）":""));
        ac.whyAdjust1.add(StrFormatter.format("聘任时间小于等于【{}】的职称【{}】，职称等级【{}】，职称等级分【{}】{}",
                adjustDateStr,ConvertUtil.toStr(jobScoreInfo.rankName,"无"),ConvertUtil.toStr(jobScoreInfo.perProTitleName,"无"),ConvertUtil.toStr(jobScoreInfo.perProTitleScore,"无"),
                jobScoreInfo.isPerProTitleChange ? "（变化）":""));
        ac.whyAdjust1.add(StrFormatter.format("聘任时间小于等于【{}】的技能【{}】，技能等级【{}】，技能等级分【{}】{}",
                adjustDateStr,ConvertUtil.toStr(jobScoreInfo.jobStatusName,"无"),ConvertUtil.toStr(jobScoreInfo.quaLevelName,"无"),ConvertUtil.toStr(jobScoreInfo.quaLevelScore,"无"),
                jobScoreInfo.isQuaLeveleChange ? "（变化）":""));
        //---------------------------------- 调整日志 end ----------------------------------

        // 7、汇总年度积分池与综合分数。
        //对应SHR：483~519行
        aggregateScores(ac);
        logger.info("汇总年度积分池与综合分数完成，总分：" + ac.allSumScore);

        //8.确定目标职级（含有R排名和无R排名两条路径）。
        //对应SHR：540~702行
        DynamicObject jobLevel = decideTargetJobGrade(ac);
        logger.info("确定目标职级完成");

        // 9、上年度考核结果为"无"时，取最低职级
        //对应SHR：703~707行
        if (AppraisalResultEnum.NONE.getCode().equals(ac.data.getAppraisalResultNumber())) {
            // 考核结果为无时取最低职级
            ac.adjustType = "8";
            ac.adjustInt = 0;
            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
            logger.info("上年度考核结果为'无'，采用最低职级");
        }
        if(jobLevel == null){
            String errorMsg = StrFormatter.format("人员【{}】，职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                    ac.personName,
                    ac.jobSeq != null ? ac.jobSeq.getString(FormConstant.NAME_KEY) : "未知",
                    ac.allSumScore.toString(),
                    ac.data.getZgjbName(),
                    ac.data.getZyjndjName(),
                    ac.data.getAppraisalResultName());
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        JobLevelCalculatorService.JobLevelResult jobLevelResult = JobLevelCalculatorService.calculateJobLevel(person, beginDate == null ? new Date() : beginDate, ac.positionAppointment);
        if(jobLevelResult.rankingResultInfo != null && ac.data.getRankingResultInfo() != null) {
            ac.data.getRankingResultInfo().allowanceRankMark = jobLevelResult.rankingResultInfo.allowanceRankMark;
            ac.data.getRankingResultInfo().allowanceRankSel = jobLevelResult.rankingResultInfo.allowanceRankSel;
        }

        //【三期需求】：计算保级原因
        judgeLevelKeepReason(ac, jobLevel);
        logger.info("计算保级原因完成");

        //10、构建职位档案
        DynamicObject result = buildPersonPositionFile(ac, jobLevel);
        logger.info("年度调整记录生成完成，员工：" + ac.personName + "，调整类型：" + ac.adjustType);
        return result;



    }

    private static void judgeLevelKeepReason(AdjustmentContext ac, DynamicObject jobLevel) {
        logger.info("开始计算保级原因，调整类型：" + ac.adjustType);
        if(AdjustTypeEnum.KEEP_LEVEL.getCode().equals(ac.adjustType)){
            if(ac.useAppraisalresult){
                ac.levelKeepReason = 1;
                logger.info("保级原因：本自然年度内已升过级，达到升级限制");
            }else{
                BigDecimal allSumScore = ac.allSumScore;
                int currentSeq = jobLevel.getInt(PositionStructureConstant.JOBLEVELSEQ);
                //下一级所需的积分
                DynamicObject nextJobLevel = ac.jobLevelByJobSeqMap.get(currentSeq + 1);
                if(nextJobLevel != null) {
                    BigDecimal nextScore = nextJobLevel.getBigDecimal(FormConstant.NCKD_SCORE);
                    //如果未达到下一级积分则认定：总积分没有达到下一级最低分数要求
                    if (allSumScore.compareTo(nextScore) < 0) {
                        //allSumScore小于nextScore
                        if (ac.rankNoUpGrad) {
                            //总积分及排名均未达到
                            ac.levelKeepReason = 6;
                            logger.info("保级原因：总积分及排名均未达到");
                        } else {
                            //总积分没有达到下一级最低分数要求
                            ac.levelKeepReason = 2;
                            logger.info("保级原因：总积分没有达到下一级最低分数要求");
                        }
                    } else {
                        if(ac.rankNoUpGrad) {
                            //R排名与全员绩效考核等级均未达到升级标准
                            ac.levelKeepReason = 5;
                            logger.info("保级原因：R排名与全员绩效考核等级均未达到升级标准");
                        }else{
                            //判断职称等级或技能等级是否达到
                            //1、获取当前职称或技能等级序号
                            //2、获取>当前职级的所有职级的资格条件的序号，取最小序号
                            //3、判断当前职称或技能等级序号是否大于最小序号
                            //4、如果当前职级序号小于最小序号则认为未达到
                            //这里先不判断，因为其他所有保级情况都已涵盖。剩下的就只有职称或技能达不到了
                            if (ac.jobSeq != null && JobSeqEnum.SKILL.getCode().equals(ac.jobSeq.getString(FormConstant.NUMBER_KEY))) {
                                ac.levelKeepReason = 4;
                                logger.info("保级原因：未聘任高一级的技能");
                            }else{
                                ac.levelKeepReason = 3;
                                logger.info("保级原因：未聘任高一级的职称");
                            }
                        }
                    }
                }else {
                    //达到职级上限
                    ac.levelKeepReason = 7;
                    logger.info("保级原因：达到职级上限");
                }
            }
        }
        logger.info("保级原因计算完成，原因代码：" + ac.levelKeepReason);
    }


    /**
     * 初始化上下文并加载基础数据。
     * @param executeYear 执行年份
     * @param person 员工
     * @param beginDate 生效日期（可以为空，为空默认为不生效状态）
     * @param positionAppointment 员工最新任职信息
     * @param performanceResult 上年度考核结果
     * @param firstPersonPosFile 初定档案
     * @return: nckd.jxccl.hr.psms.business.AnnualAdjustmentService.AdjustmentContext
     * @author W.Y.C
     * @date: 2025/10/08 21:15
     */
    private static AdjustmentContext initAndLoad(Integer executeYear, DynamicObject person, Date beginDate,Date adjustDate,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile){
        logger.info("开始初始化上下文，执行年份：" + executeYear);
        AdjustmentContext ac = new AdjustmentContext();
        ac.executeYear = executeYear;
        ac.nowYear = executeYear;
        ac.personId = person.getLong(FormConstant.ID_KEY);
        ac.personName = person.getString(FormConstant.NAME_KEY);
        ac.beginDate = beginDate;
        ac.personInfo = person;

        int lastYear = executeYear - 1;
        LocalDateTime lastYearDateTime = LocalDateTime.of(lastYear, 1, 1, 0, 0);

        ac.whyAdjust1.add(StrFormatter.format("选择的调整时间：【{}】", DateUtil.format(adjustDate,DateUtil.NORM_DATE_PATTERN)));

        AnnualAdjustmentData data = new AnnualAdjustmentData();


        //1、获取上年度考核结果（对应SHR：PersonpositionfileUtils：884~914行）
        // DynamicObject performanceResult = PerformanceManagerHelper.getPerformanceResult(ac.personId, lastYearDateTime);
        if(performanceResult == null){
            String errorMsg = StrFormatter.format("人员【{}】缺少【{}】年考核结果", ac.personName,lastYear);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        data.setAppraisalResult(performanceResult);
        data.setAppraisalResultNumber(performanceResult.getString(FormConstant.NUMBER_KEY));
        data.setAppraisalResultName(performanceResult.getString(FormConstant.NAME_KEY));
        data.setAppraisalResultId(performanceResult.getLong(FormConstant.ID_KEY));
        data.setAppraisalResultScore(performanceResult.getBigDecimal(FormConstant.NCKD_SCORE));


        //2.获取员工上年度年度R排名（对应SHR：PersonpositionfileUtils：915~945行）
        DynamicObject lastPersonPosFile = null;
        JobLevelCalculatorService.RankingResultInfo rankingInfo = JobLevelCalculatorService.getRankingInfo(ac.personId, ac.personName, beginDate == null ? new Date() : beginDate);
        data.setRankingResultInfo(rankingInfo);

        //3.查询上一条有效年度调整记录（对应SHR：PersonpositionfileUtils：946~997行）
        DynamicObject[] personPosFileByPersonAndState = PositionFileHelper.getPersonPosFileByPersonAndState(ac.personId,
                new String[]{"3","4"},
                null,null,
                QueryFieldBuilder.create()
                        .orderDesc(PositionStructureConstant.NCKD_EXECUTEYEAR,PositionStructureConstant.MODIFY_TIME_KEY,PositionStructureConstant.NCKD_BEGINDATE).buildOrderArray());
        if(personPosFileByPersonAndState == null || personPosFileByPersonAndState.length < 1){
            //没有年度调整记录取初定记录
            if(firstPersonPosFile == null){
                String errorMsg = StrFormatter.format("当前无法为【{}】进行调整，因为他/她尚未建立职位档案。请前往职位及积分初定 -> 进行初定！", ac.personName);
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }
            lastPersonPosFile = firstPersonPosFile;
            data.setFirstId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
            String typeState = lastPersonPosFile.getString(PositionStructureConstant.NCKD_TYPESTATE);
            ac.whyAdjust1.add(StrFormatter.format("上一条有效职位档案为【{}】档案，初定时间：【{}】；", TypeStateEnum.getByCode(typeState).getName(), DateUtil.format(lastPersonPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE),DateUtil.NORM_DATE_PATTERN)));
        }else{
            lastPersonPosFile = personPosFileByPersonAndState[0];
            data.setLastId(lastPersonPosFile.getLong(FormConstant.ID_KEY));
            String typeState = lastPersonPosFile.getString(PositionStructureConstant.NCKD_TYPESTATE);
            ac.whyAdjust1.add(StrFormatter.format("上一条有效职位档案为【{}】档案，调整时间：【{}】", TypeStateEnum.getByCode(typeState).getName(),DateUtil.format(lastPersonPosFile.getDate(PositionStructureConstant.NCKD_BEGINDATE),DateUtil.NORM_DATE_PATTERN)));
        }
        if(lastPersonPosFile != null) {
            //（firstid、lastsumscore、lastdiplomascore、lastrankscore、lastjobstatusscore、lasthrjobfamilynumber、lastjobgradefid、lastjobgradeindex）
            data.setLastSumScore(lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_SUMSCORE));

            //lastdiplomascore
            //lastrankscore
            //lastjobstatusscore
            DynamicObject jobSeq = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR);
            data.setLastHrJobFamilyNumber(jobSeq.getString(FormConstant.NUMBER_KEY));
            data.setLastJobSeq(jobSeq);
            DynamicObject jobLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_JOBLEVELHR);
            data.setLastJobLevel(jobLevel);
            data.setLastJobGradeIndex(jobLevel.getInt(FormConstant.JOBLEVELSEQ));

            //---------------------------------- 调整日志 begin ----------------------------------
            ac.whyAdjust1.add(StrFormatter.format("上一职位序列【{}】", jobSeq != null ? jobSeq.getString(FormConstant.NAME_KEY) : "未知"));
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案总积分【{}】", lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_ALLSUMSCORE)));
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案积分池积分【{}】", data.getLastSumScore()));

            DynamicObject diploma = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_DIPLOMA);
            BigDecimal diplomaScore = lastPersonPosFile.getBigDecimal(PositionStructureConstant.NCKD_DIPLOMASCORE);
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案学历【{}】，学历分【{}】", diploma == null ? "无" : diploma.getString(FormConstant.NAME_KEY), diplomaScore == null ? "无" : diplomaScore));

            String rankName = lastPersonPosFile.getString(PositionStructureConstant.NCKD_RANKNAME);
            rankName = StringUtils.isEmpty(rankName) ? "无" : rankName;
            DynamicObject proTitleLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_PROTITLELEVEL);
            String ranksScore = lastPersonPosFile.getString(PositionStructureConstant.NCKD_RANKSCORE);
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案职称【{}】，职称等级【{}】，职称等级分【{}】", rankName,proTitleLevel == null ? "无" : proTitleLevel.getString(FormConstant.NAME_KEY), ranksScore == null ? "无" : ranksScore));

            String jobStatusName = lastPersonPosFile.getString(PositionStructureConstant.NCKD_JOBSTATUSNAME);
            jobStatusName = StringUtils.isEmpty(jobStatusName) ? "无" : jobStatusName;
            DynamicObject ocpQualLevel = lastPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_OCPQUALLEVEL);
            String jobStatusScore = lastPersonPosFile.getString(PositionStructureConstant.NCKD_JOBSTATUSSCORE);
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案技能【{}】，技能等级【{}】，技能等级分【{}】", jobStatusName,ocpQualLevel == null ? "无" : ocpQualLevel.getString(FormConstant.NAME_KEY), jobStatusScore == null ? "无" : jobStatusScore));
            ac.whyAdjust1.add(StrFormatter.format("上一职位档案职级【{}（{}）】", jobLevel != null ? jobLevel.getString(FormConstant.NAME_KEY) : "未知",data.getLastJobGradeIndex()));
            ac.whyAdjust1.add("----------------------------------------------------------");
            //---------------------------------- 调整日志 end ----------------------------------
        }

        ac.whyAdjust1.add(StrFormatter.format("上年度【{}】考核结果为【{}】；", lastYear, performanceResult != null ? performanceResult.getString(FormConstant.NAME_KEY) : "未知"));
        if(rankingInfo == null) {
            ac.whyAdjust1.add("无全排名");
        }else{
            ac.whyAdjust1.add(StrFormatter.format("上年度【{}】排名单元【{}】，全排名名次【{}】，全排名总人数【{}】，R排名名次【{}】，R排名总人数【{}】；",
                    lastYear, rankingInfo.groupName,rankingInfo.topRank,rankingInfo.allowanceRank,rankingInfo.count,rankingInfo.countR));
        }


        //4、获取年度贡献积分
        //对应SHR：291行；utils.YearContributeScoreBillEntryScoreSumBypersonidAndYear
        List<ContributionHelper.ScoreItemValidScore> scoreItemAccumulateScore = ContributionHelper.sumScoreItemAccumulateScore(new Long[]{ac.personId}, DateUtil.toDate(lastYearDateTime));
        //计算出最多可得的分数（根据积分配置项）
        List<ContributionHelper.ScoreItemValidScore> allowedScoreList = ContributionHelper.calculateMaxAllowedScore1(scoreItemAccumulateScore);
        BigDecimal allowedScoreSum = BigDecimal.ZERO;
        for (ContributionHelper.ScoreItemValidScore allowedScore : allowedScoreList) {
            String scoreItemNumber = allowedScore.getScoreItemNumber();
            ScoreItemEnum scoreItemEnum = ScoreItemEnum.getByCode(scoreItemNumber);
            BigDecimal validScore = allowedScore.getValidScore();
            if(scoreItemEnum != null && ScoreItemEnum.RESEARCH_SCORE == scoreItemEnum){
                data.setYearscoresuma(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.PATENT_SCORE == scoreItemEnum){
                data.setYearscoresumb(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.PAPER_SCORE == scoreItemEnum){
                data.setYearscoresumc(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.SKILL_CONTEST_SCORE == scoreItemEnum){
                data.setYearscoresumd(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.TRAINING_MATERIAL_SCORE == scoreItemEnum) {
                data.setYearscoresume(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.TECH_STANDARD_SCORE == scoreItemEnum){
                data.setYearscoresumf(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.MGMT_SPEC_SCORE == scoreItemEnum){
                data.setYearscoresumg(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.MENTOR_SCORE == scoreItemEnum){
                data.setYearscoresumh(validScore);
            } else if(scoreItemEnum != null && ScoreItemEnum.TRAINING_SCORE == scoreItemEnum){
                data.setYearscoresumi(validScore);
            }
            allowedScoreSum = allowedScoreSum.add(validScore);

            //未受限制的实际累计总分
            BigDecimal sumScore = allowedScore.getSumScore();
            //比较实际累计总分和未受限制的实际累计总分
            if(sumScore.compareTo(validScore) != 0){
                //实际累计积分与有效积分不符，说明被限高
                String scoreItemName = scoreItemEnum != null ? scoreItemEnum.getName() : scoreItemNumber;
                ac.whyAdjust1.add(StrFormatter.format("上年度【{}】【{}】积分实际累计总分为【{}】但达到项目最高分限制，最终有效应用分为【{}】；", scoreItemName, sumScore, validScore));
                logger.warn(StrFormatter.format("上年度【{}】积分实际累计总分为【{}】但达到项目最高分限制，最终有效应用分为【{}】；", scoreItemName, sumScore, validScore));
            }

        }
        data.setAllYearScoreSum(allowedScoreSum);


        //5.获取员工任职信息（对应SHR：PersonpositionfileUtils：998~1009行）
        // PositionAppointmentBO positionAppointment = PositionStructureHelper.positionAppointmentQuery(ac.personId, beginDate == null ? new Date() : beginDate);
        DynamicObject perEduExp = positionAppointment.getPerEduExp();
        if(perEduExp != null){
            data.setDiplomaId(perEduExp.getLong(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.ID_KEY)));
            data.setDiplomaScore(perEduExp.getBigDecimal(String.join(".", FormConstant.EDUCATION_KEY, FormConstant.NCKD_SCORE)));
        }
        DynamicObject empPosOrgRel = positionAppointment.getEmpPosOrgRel();
        if(empPosOrgRel != null){
            data.setPersonDepId(empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
            data.setHrJobFamilyId(empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY)));
            data.setHrJobFamilyNumber(empPosOrgRel.getString(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.NUMBER_KEY)));
            data.setPositionId(empPosOrgRel.getLong(String.join(".", FormConstant.POSITION_KEY,  FormConstant.ID_KEY)));
            data.setPositionName(empPosOrgRel.getString(String.join(".", FormConstant.POSITION_KEY,  FormConstant.NAME_KEY)));
            data.setPersonName(empPosOrgRel.getString(String.join(".", FormConstant.EMPLOYEE_KEY,  FormConstant.NAME_KEY)));
            // data.setHrOrgUnitId();
        }else{
            String errorMsg = StrFormatter.format("当前无法为【{}】进行调整，因为根据时间【{}】未获取到人员任职和聘任信息！", ac.personName, DateUtil.format(beginDate == null ? new Date() : beginDate,DateUtil.NORM_DATE_PATTERN));
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        DynamicObject perProTitle = positionAppointment.getPerProTitle();
        if(perProTitle != null){
            data.setRankName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
            data.setZgjbId(perProTitle.getLong(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.ID_KEY)));
            data.setZgjbNumber(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NUMBER_KEY)));
            data.setZgjbName(perProTitle.getString(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NAME_KEY)));
            data.setZgjbScore(perProTitle.getBigDecimal(String.join(".", FormConstant.PROLEVEL_KEY, FormConstant.NCKD_SCORE)));
        }
        DynamicObject perOcpQual = positionAppointment.getPerOcpQual();
        if(perOcpQual != null){
            data.setJobStatusName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
            data.setZyjndjId(perOcpQual.getLong(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.ID_KEY)));
            data.setZyjndjNumber(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NUMBER_KEY)));
            data.setZyjndjName(perOcpQual.getString(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NAME_KEY)));
            data.setZyjndjScore(perOcpQual.getBigDecimal(String.join(".", FormConstant.QUALEVEL_KEY, FormConstant.NCKD_SCORE)));
        }
        if (data.getHrJobFamilyId() == null || data.getHrJobFamilyId() == 0) {
            String errorMsg = StrFormatter.format("无职位序列，请检查当前人员【{}】任职的岗位【{}】是否有职位序列",ac.personName,data.getPositionName());
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        ac.positionAppointment = positionAppointment;
        long jobSeq = positionAppointment.getEmpPosOrgRel().getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
        ac.jobSeq = BusinessDataServiceHelper.loadSingle(jobSeq,FormConstant.HBJM_JOBSEQHR);

        ac.data = data;

        // 对应SHR：307~322行
        // 判断R位次存在性
        if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().topRank == null) {
            //如果没有排名与R排名,则按保级处理
            ac.keep = true;
            logger.info("没有排名与R排名信息，按保级处理");
        }
        if (ac.data.getRankingResultInfo() == null || ac.data.getRankingResultInfo().allowanceRankPercent == null || ac.data.getRankingResultInfo().allowanceRankPercent.compareTo(BigDecimal.ZERO) <= 0) {
            ac.haveRp = false;
            logger.info("R排名不存在或小于等于0");
        }

        // 对应SHR：323~339行
        // 考核结果对应升降级逻辑
        if (AppraisalResultEnum.EXCELLENT.getCode().equals(ac.data.getAppraisalResultNumber())) {
            ac.minusByAppraisal = 1;
            ac.whyAdjust.append("【考核结果优秀】升1级");
            logger.info("考核结果为优秀，升1级");
        } else if (AppraisalResultEnum.BASICALLY_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
            ac.minusByAppraisal = -1;
            ac.whyAdjust.append("【考核结果基本合格】降1级");
            logger.info("考核结果为基本合格，降1级");
        } else if (AppraisalResultEnum.UN_QUALIFIED.getCode().equals(ac.data.getAppraisalResultNumber())) {
            ac.minusByAppraisal = -2;
            ac.whyAdjust.append("【考核结果不合格】降2级");
            logger.info("考核结果为不合格，降2级");
        }
        logger.info("初始化上下文完成");
        return ac;
    }

    /**
     * 判断是否为本年首次调整、考核是否已被使用，并根据规则决定是否使用R排名。
     * @param ac 上下文对象
     * @return: void
     * @author W.Y.C
     * @date: 2025/10/08 22:12
     */
    private static void evaluateFirstAndAppraisalUsage(AdjustmentContext ac) {
        logger.info("开始评估是否为本年首次调整及考核使用情况");

        DynamicObject nowYearPersonPosFile = PositionFileHelper.getLatsPersonPosFileByPerson(ac.personId, new QFilter(PositionStructureConstant.NCKD_EXECUTEYEAR, QCP.equals, ac.nowYear));

        boolean isyearfirstdo = false;
        if (nowYearPersonPosFile != null) {
            DynamicObject nowYearAppraisalResult = nowYearPersonPosFile.getDynamicObject(PositionStructureConstant.NCKD_APPRAISALRESULT);
            String nowYearAppraisalResultNumber = nowYearAppraisalResult.getString(FormConstant.NUMBER_KEY);
            String appraisalResultNumber = ac.data.getAppraisalResultNumber();
            if (!StringUtils.equals(nowYearAppraisalResultNumber, appraisalResultNumber)) {
                String errorMsg = StrFormatter.format("人员【{}】，上年考度核结果存在变更,需删除【{}】年度创建的【员工职位档案调整】记录才能继续操作 !", ac.personName,ac.nowYear);
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }
            LocalDateTime nowYearDateTime = LocalDateTime.of(ac.nowYear, 1, 1, 0, 0);
            StringBuilder reasons = new StringBuilder();
            ac.useAppraisalresult = JobLevelCalculatorService.useAppraisalResult(ac.personId, DateUtil.toDate(nowYearDateTime),reasons);
            ac.useAppraisalResultReasons = reasons;
            if (ac.useAppraisalresult && ac.minusByAppraisal != 0) {
                ac.whyAdjust.append(" 非").append(ac.nowYear)
                        .append("年度首次调整,且升降【考核结果】已被使用,不需要考虑R排名,保级处理");
                ac.haveRp = false;
                ac.keep = true;
                logger.info("非本年首次调整，且升降考核结果已被使用，不需要考虑R排名，保级处理");
            }
            ac.whyAdjust1.add("【非】本年度首次调整，"+reasons.toString());
        } else {
            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度首次调整,【考核结果】未被使用");
            ac.whyAdjust1.add("本年度首次调整");
            isyearfirstdo = true;
            logger.info("本年度首次调整，考核结果未被使用");
        }

        if (ac.useAppraisalresult) {
            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】已被使用,保级处理");
            isyearfirstdo = false;
            ac.keep = true;
            logger.info("考核结果已被使用，保级处理");
        } else {
            ac.whyAdjust.append(" ").append(ac.nowYear).append("年度【考核结果】未被使用");
            ac.keep = false;
            isyearfirstdo = true;
            if (ac.minusByAppraisal == 0) {
                ac.whyAdjust.append("【考核结果】为保级,");
                BigDecimal p = Optional.ofNullable(ac.data.getRankingResultInfo())
                        .map(info -> info.allowanceRankPercent)
                        .orElse(null);
                if (p == null || p.compareTo(BigDecimal.ZERO) <= 0) {
                    ac.whyAdjust.append("无R排名");
                    ac.haveRp = false;
                    logger.info("考核结果为保级，无R排名");
                } else {
                    ac.whyAdjust.append("使用R排名进行计算 ");
                    ac.haveRp = true;
                    logger.info("考核结果为保级，使用R排名进行计算");
                }
            }
        }

        ac.isYearFirstDo = isyearfirstdo;
        logger.info("评估完成，是否本年首次调整：" + isyearfirstdo + "，是否使用考核结果：" + ac.useAppraisalresult);
    }

    /**
     * 加载上一条记录并校验生效日期。
     * @param ac 上下文
     * @return: void
     * @author W.Y.C
     * @date: 2025/10/08 23:13
     */
    private static void loadLastRecordAndValidateBeginDate(AdjustmentContext ac) {
        logger.info("开始加载上一条记录并校验生效日期");
        ac.lastRecordId = ac.data.getLastId() != null && ac.data.getLastId() != 0 ? ac.data.getLastId() : ac.data.getFirstId();
        if (ac.lastRecordId == null || ac.lastRecordId == 0) {
            String errorMsg = StrFormatter.format("人员【{}】，缺少职位及积分初定信息，请先完成初定，第二年再操作年度调整！", ac.personName);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        ac.lastRecordInfo = BusinessDataServiceHelper.loadSingle(ac.lastRecordId, PositionStructureConstant.PERSONPOSFILE_ENTITYID);

        if (ac.beginDate != null
                && ac.lastRecordInfo != null
                && ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE) != null) {
            Date lastBegin = ac.lastRecordInfo.getDate(PositionStructureConstant.NCKD_BEGINDATE);
            if (ac.beginDate.compareTo(lastBegin) <= 0) {
                String errorMsg = StrFormatter.format("当前无法为【{}】进行调整，因为最近调整时间为【{}】，不能早于最近一次职位调整时间。", ac.personName,DateUtil.format(lastBegin,DateUtil.NORM_DATE_PATTERN));
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }
        }
        logger.info("加载上一条记录并校验生效日期完成");
    }


    /**
     * 汇总年度积分池与综合分数。
     * @param ac 上下文对象
     * @return: void
     * @author W.Y.C
     * @date: 2025/10/08 23:46
     */
    private static void aggregateScores(AdjustmentContext ac) {
        logger.info("开始汇总年度积分池与综合分数");
        BigDecimal allyearscoresum =
                ac.data.getAllYearScoreSum() == null ? BigDecimal.ZERO : ac.data.getAllYearScoreSum();
        logger.info("上年所有贡献单据分数之和: " + allyearscoresum);

        BigDecimal appraisalresultscore =
                ac.data.getAppraisalResultScore() == null ? BigDecimal.ZERO : ac.data.getAppraisalResultScore();
        logger.info("上年年度考核得分: " + appraisalresultscore);

        ac.lastYearContributeScore = allyearscoresum.add(appraisalresultscore);
        logger.info("上年度贡献综合评价分: " + ac.lastYearContributeScore);

        ac.addYearContributeScore = ac.lastYearContributeScore.multiply(new BigDecimal("0.1"));
        logger.info("年度新增的贡献积分（*10%）: " + ac.addYearContributeScore);

        BigDecimal lastsum = ac.data.getLastSumScore() == null ? BigDecimal.ZERO : ac.data.getLastSumScore();
        logger.info("上年累计积分池的分: " + lastsum);

        ac.sumScore = lastsum.add(ac.addYearContributeScore);
        logger.info("累计积分池的分: " + ac.sumScore);


        ac.allSumScore = ac.sumScore
                .add(ac.diplomaScore)
                .add(ac.data.getJobScoreInfo() != null ? ac.data.getJobScoreInfo().perProTitleScore : BigDecimal.ZERO)
                .add(ac.data.getJobScoreInfo() != null ? ac.data.getJobScoreInfo().quaLevelScore : BigDecimal.ZERO);
        logger.info("累计总积分: " + ac.allSumScore);
    }

    /**
     * 确定目标职级（含有R排名和无R排名两条路径）。
     * @param ac 上下文对象
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @note 对应原 PersonpositionfilecreateViewListHandler:540~707 行
     * @author W.Y.C
     * @date: 2025/10/10 14:17
     */
    private static DynamicObject decideTargetJobGrade(AdjustmentContext ac) {
        logger.info("开始确定目标职级");

        //上一条职位档案的职位序列（如果是管理序列，则转换为职能序列）
        String newJobSeqNumber = ac.convertJobSeq.getString(FormConstant.NUMBER_KEY);
        String newLastJobSeqNumber = ac.data.getConvertLastJobSeq() != null ? ac.data.getConvertLastJobSeq().getString(FormConstant.NUMBER_KEY) : "未知";
        if (!newJobSeqNumber.equalsIgnoreCase(newLastJobSeqNumber)) {
            //序列发生变化
            String errorMsg = StrFormatter.format("人员【{}】，序列发生变化！请先到【员工职位档案调整】中进行操作", ac.personName);
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }

        DynamicObject jobLevel;
        if (ac.haveRp) {
            jobLevel = decideWithR(ac);
            logger.info("使用R排名方式确定目标职级完成");
        } else {
            jobLevel = decideWithoutR(ac);
            logger.info("不使用R排名方式确定目标职级完成");
        }
        
        logger.info("目标职级确定完成");
        return jobLevel;
    }

    /**
     * 有 R 排名时的定位路径。
     * @param ac 上下文对象
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/10/10 14:18
     */
    private static DynamicObject decideWithR(AdjustmentContext ac) {
        logger.info("开始使用R排名方式确定目标职级");
        //根据[职位序列][积分]和[职称等级/技能等级]获取职位序列最高职级
        DynamicObject jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
        if(jobLevel == null){
            String errorMsg = StrFormatter.format("人员【{}】，职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                    ac.personName,
                    ac.jobSeq != null ? ac.jobSeq.getString(FormConstant.NAME_KEY) : "未知",
                    ac.allSumScore.toString(),
                    ac.data.getZgjbName(),
                    ac.data.getZyjndjName(),
                    ac.data.getAppraisalResultName());
            logger.error(errorMsg);
            throw new ValidationException(errorMsg);
        }
        int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("当前积分所得职级顺序号: " + jobGradeindex);

        Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.convertJobSeq);
        ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
        int newjobgradeindex = ac.data.getLastJobGradeIndex();
        logger.info("上一条记录的职级序号: " + newjobgradeindex);

        // 补充根据R排名确定是否升级
        if (ac.minusByAppraisal == 0) {
            // 如果考评结果不需要升降级则验证R排名
            if (!ac.keep) {
                newjobgradeindex = JobLevelCalculatorService.getnewjobgradeindexByrank(ac.data.getLastJobGradeIndex(), newjobgradeindex, ac.data.getRankingResultInfo(), ac.convertJobSeq);
                if(ac.data.getLastJobGradeIndex() == newjobgradeindex){
                    ac.rankNoUpGrad = true;
                    logger.info("R排名未达到升级标准");
                }
            }

            if (newjobgradeindex > jobGradeindex) {
                //超过职位序列最高职级则退回职位序列最高职级
                newjobgradeindex = jobGradeindex;
                logger.info("超过职位序列最高职级，退回最高职级");
                //但超出『积分』和『职称等级、技能等级』的所能定级的最高职级，按
            }
        } else {
            // 考核结果需要升降级则直接执行
            newjobgradeindex = ac.data.getLastJobGradeIndex() + ac.minusByAppraisal;
            logger.info("根据考核结果调整职级，调整前序号：" + ac.data.getLastJobGradeIndex() + "，调整后序号：" + newjobgradeindex);
            if (jobGradeindex <= newjobgradeindex) {
                //超过职位序列最高职级则退回职位序列最高职级
                newjobgradeindex = jobGradeindex;
                logger.info("超过职位序列最高职级，退回最高职级");
            }
        }

        // 确定是否超出总分最高任命职级
        DynamicObject maxJobLevel = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore);
        int personMaxjobgradeindex = maxJobLevel.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("总分能任命的最高职级序号: " + personMaxjobgradeindex);
        // 超出总分能任命最高职职级则该最高职级就是要任命的职级
        if (newjobgradeindex > personMaxjobgradeindex) {
            newjobgradeindex = personMaxjobgradeindex;
            logger.info("超出总分能任命的最高职级，调整至最高职级");
        }

        // 确定是否超出该职称等级或技能等级最高任命职级
        DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
        int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
        logger.info("职称/技能等级能任命的最高职级序号: " + maxJobGradeIndex);
        // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
        if (newjobgradeindex > maxJobGradeIndex) {
            newjobgradeindex = maxJobGradeIndex;
            logger.info("超出职称/技能等级能任命的最高职级，调整至最高职级");
        }

        if((ac.data.getZgjbId() == null || ac.data.getZgjbId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
            //如果没有聘任则取最低职级
            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
            if(jobLevel != null) {
                newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
            }
            logger.info("无职称/技能聘任，取最低职级");
        }else if(jobLevelByJobSeqMap.get(newjobgradeindex) != null){
            jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
            logger.info("根据计算结果获取职级，职级序号：" + newjobgradeindex);
        }else{
            //兜底；如果没有获取到合适的职级则取最低级
            jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
            if(jobLevel != null) {
                newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
            }
            logger.info("未找到合适职级，取最低职级，职级序号：" + newjobgradeindex);
        }


        if((ac.data.getZgjbId() == null || ac.data.getZgjbId() == 0) && (ac.data.getZyjndjId() == null || ac.data.getZyjndjId() == 0)) {
            //无聘任
            ac.adjustType = "7";
            logger.info("调整类型：无聘任");
        }else if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
            //保级
            ac.adjustType = "1";
            logger.info("调整类型：保级");
        }else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
            //升级
            ac.adjustType = "2";
            logger.info("调整类型：升级");
        } else {
            //降级
            ac.adjustType = "0";
            logger.info("调整类型：降级");
        }
        // 升降级数
        ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
        logger.info("升降级数：" + ac.adjustInt);

        return jobLevel;

    }

    /**
     * 无 R 排名时的定位路径。
     * @param ac 上下文对象
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @author W.Y.C
     * @date: 2025/10/10 14:18
     */
    private static DynamicObject decideWithoutR(AdjustmentContext ac) {
        logger.info("开始不使用R排名方式确定目标职级");
        // String JobGrade = "";
        DynamicObject jobLevel = null;

        if (ac.minusByAppraisal == 0 && ac.keep) {
            logger.info("绩效分组排名,没有勾选享受职位津贴,保持原职级不变,没有则为最低档");
            ac.whyAdjust.append("绩效分组排名,没有勾选享受职位津贴,保持原职级不变");
            jobLevel = ac.data.getLastJobLevel();
            ac.adjustType = "1";
            if(ac.useAppraisalresult){
                ac.levelKeepReason = 1;
            }

            if (jobLevel == null) {
                jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
                ac.adjustType = "0";
                logger.info("未找到上一条记录的职级，取最低职级");
            }
            logger.info("保持原职级不变");

        } else {
            jobLevel = JobLevelCalculatorService.getJobLevel(ac.convertJobSeq, ac.allSumScore, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber(), 0, Boolean.FALSE,Boolean.FALSE);
            if(jobLevel == null){
                String errorMsg = StrFormatter.format("人员【{}】，职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
                        ac.personName,
                        ac.jobSeq != null ? ac.jobSeq.getString(FormConstant.NAME_KEY) : "未知",
                        ac.allSumScore.toString(),
                        ac.data.getZgjbName(),
                        ac.data.getZyjndjName(),
                        ac.data.getAppraisalResultName());
                logger.error(errorMsg);
                throw new ValidationException(errorMsg);
            }

            int jobGradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
            logger.info("当前积分所得职级顺序号:::" + jobGradeindex);

            Map<Integer, DynamicObject> jobLevelByJobSeqMap = JobLevelCalculatorService.getJobLevelByJobSeqMap(ac.data.getConvertLastJobSeq());
            ac.jobLevelByJobSeqMap = jobLevelByJobSeqMap;
            int newjobgradeindex = ac.data.getLastJobGradeIndex();
            logger.info("上一条记录的职级序号: " + newjobgradeindex);

            if (ac.isYearFirstDo) {
                newjobgradeindex += ac.minusByAppraisal;
                logger.info("根据考核结果调整职级，调整量：" + ac.minusByAppraisal + "，调整后序号：" + newjobgradeindex);
            }

            if (jobGradeindex <= newjobgradeindex) {
                newjobgradeindex = jobGradeindex;
                logger.info("超过职位序列最高职级，退回最高职级");
            }

            // 确定是否超出该职称等级或技能等级最高任命职级
            DynamicObject maxJobLevel1 = JobLevelCalculatorService.getMaxJobLevel(ac.convertJobSeq, ac.allSumScore, null, ac.data.getZgjbNumber(), ac.data.getZyjndjNumber());
            int maxJobGradeIndex = maxJobLevel1.getInt(FormConstant.JOBLEVELSEQ);
            logger.info("职称/技能等级能任命的最高职级序号: " + maxJobGradeIndex);
            // 职级超出职称等级或技能等级能任命最高职职级则该最高职级就是要任命的职级
            if (newjobgradeindex > maxJobGradeIndex) {
                newjobgradeindex = maxJobGradeIndex;
                logger.info("超出职称/技能等级能任命的最高职级，调整至最高职级");
            }

            // 如果最后超出最低排名则按最低排名
            if (jobLevelByJobSeqMap.get(newjobgradeindex) != null) {
                jobLevel = jobLevelByJobSeqMap.get(newjobgradeindex);
                logger.info("根据计算结果获取职级，职级序号：" + newjobgradeindex);
            } else {
                jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
                if(jobLevel != null) {
                    newjobgradeindex = jobLevel.getInt(FormConstant.JOBLEVELSEQ);
                }
                logger.info("未找到合适职级，取最低职级，职级序号：" + newjobgradeindex);
            }

            // 设置调整类型和调整级别数
            if (newjobgradeindex == ac.data.getLastJobGradeIndex()) {
                //保级
                ac.adjustType = "1";
                logger.info("调整类型：保级");
            } else if (newjobgradeindex > ac.data.getLastJobGradeIndex()) {
                //升级
                ac.adjustType = "2";
                logger.info("调整类型：升级");
            } else {
                //降级
                ac.adjustType = "0";
                logger.info("调整类型：降级");
            }
            // 升降级数
            ac.adjustInt = newjobgradeindex - ac.data.getLastJobGradeIndex();
            logger.info("升降级数：" + ac.adjustInt);
        }

        return jobLevel;
    }


    /**
     * 构建职位档案
     * @param ac 上下文对象
     * @param jobLevel 目标职级对象
     * @return: kd.bos.dataentity.entity.DynamicObject
     * @note 对应原 710~803行
     * @author W.Y.C
     * @date: 2025/10/10 15:44
     */
    private static DynamicObject buildPersonPositionFile(AdjustmentContext ac, DynamicObject jobLevel) {
        logger.info("开始构建职位档案");

        DynamicObject newPersonPosFile = BusinessDataServiceHelper.newDynamicObject(
                PositionStructureConstant.PERSONPOSFILE_ENTITYID);

        DynamicObject empPosOrgRel = ac.positionAppointment.getEmpPosOrgRel();
        newPersonPosFile.set(PositionStructureConstant.NCKD_PERSON, ac.personInfo);

       /* //管理组织
        newPersonPosFile.set(PositionStructureConstant.ORG_KEY, UserServiceHelper.getUserMainOrgId(UserServiceHelper.getCurrentUserId()));

        DynamicObject company = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
        company.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.COMPANY_KEY,FormConstant.ID_KEY)));
        //创建组织
        newPersonPosFile.set(PositionStructureConstant.CREATEORG_KEY, company);
        //原创建组织
        newPersonPosFile.set(PositionStructureConstant.SRCCREATEORG_KEY, company);

        //使用组织
        DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
        dep.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
        newPersonPosFile.set(PositionStructureConstant.USEORG_KEY, dep);*/
        DynamicObject dep = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORG_ENTITYID);
        dep.set(FormConstant.ID_KEY, empPosOrgRel.getLong(String.join(".",FormConstant.ADMINORG,FormConstant.ID_KEY)));
        newPersonPosFile.set(PositionStructureConstant.NCKD_DEP, dep);

        newPersonPosFile.set(PositionStructureConstant.NCKD_TYPESTATE, "3");
        newPersonPosFile.set(PositionStructureConstant.NCKD_EXECUTEYEAR, ac.executeYear);
        Long positionId = empPosOrgRel.getLong(String.join(".",FormConstant.POSITION_KEY,FormConstant.ID_KEY));
        DynamicObject position = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBPM_POSITIONHR);
        position.set(FormConstant.ID_KEY, positionId);
        Long jobSeqId = empPosOrgRel.getLong(String.join(".", FormConstant.HBPM_POSITIONHR, FormConstant.NCKD_JOBSEQ, FormConstant.ID_KEY));
        DynamicObject jobSeq = BusinessDataServiceHelper.newDynamicObject(FormConstant.HBJM_JOBSEQHR);
        jobSeq.set(FormConstant.ID_KEY, jobSeqId);
        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSEQHR, jobSeq);
        newPersonPosFile.set(PositionStructureConstant.NCKD_POSITIONHR, position);
        newPersonPosFile.set(PositionStructureConstant.NCKD_RANKNAME, "无".equalsIgnoreCase(ac.data.getRankName()) ? null : ac.data.getRankName());
        DynamicObject proTitleLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_PROTITLELEVEL);
        proTitleLevel.set(FormConstant.ID_KEY, ac.data.getZgjbId());
        newPersonPosFile.set(PositionStructureConstant.NCKD_PROTITLELEVEL, proTitleLevel);
        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSNAME, "无".equalsIgnoreCase(ac.data.getZyjndjName()) ? null : ac.data.getZyjndjName());
        DynamicObject ocpQualLevel = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_OCPQUALLEVEL);
        ocpQualLevel.set(FormConstant.ID_KEY, ac.data.getZyjndjId());
        newPersonPosFile.set(PositionStructureConstant.NCKD_OCPQUALLEVEL, ocpQualLevel);
        DynamicObject diploma = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.HBSS_DIPLOMA);
        diploma.set(FormConstant.ID_KEY, ac.data.getDiplomaId());
        newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMA, diploma);
        newPersonPosFile.set(PositionStructureConstant.NCKD_JOBLEVELHR, jobLevel);
        newPersonPosFile.set(PositionStructureConstant.NCKD_LASTPERSONPOSFILE, ac.lastRecordInfo);
        newPersonPosFile.set(PositionStructureConstant.NCKD_DIPLOMASCORE, ac.diplomaScore);
        newPersonPosFile.set(PositionStructureConstant.NCKD_WHYDIPLOMASCORE, ac.whyDiplomaScore.toString());
        if(ac.data.getJobScoreInfo() != null) {
            newPersonPosFile.set(PositionStructureConstant.NCKD_RANKSCORE, ac.data.getJobScoreInfo().perProTitleScore);
            newPersonPosFile.set(PositionStructureConstant.NCKD_JOBSTATUSSCORE, ac.data.getJobScoreInfo().quaLevelScore);
        }
        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLSUMSCORE, ac.allSumScore);
        newPersonPosFile.set(PositionStructureConstant.NCKD_SUMSCORE,ac.sumScore);
        newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTTYPE,ac.adjustType);
        newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSSTATUS, EnableEnum.NO.getCode());
        if(ac.beginDate != null){
            newPersonPosFile.set(PositionStructureConstant.NCKD_BEGINDATE, ac.beginDate);
            newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSSTATUS, EnableEnum.YES.getCode());
        }
        newPersonPosFile.set(PositionStructureConstant.NCKD_ADJUSTINT, ac.adjustInt);
        newPersonPosFile.set(PositionStructureConstant.NCKD_FIRSTRANK, EnableEnum.NO.getCode());

        if(ac.data.getRankingResultInfo() != null) {
            newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANK, ac.data.getRankingResultInfo().topRank);
            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANK, ac.data.getRankingResultInfo().allowanceRank);
            BigDecimal topRankPercent = Optional.ofNullable(ac.data.getRankingResultInfo().topRankPercent)
                    .orElse(BigDecimal.ZERO);
            newPersonPosFile.set(PositionStructureConstant.NCKD_TOPRANKPERCENT,  topRankPercent);
            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKMARK, ac.data.getRankingResultInfo().allowanceRankMark);
            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKSEL, ac.data.getRankingResultInfo().allowanceRankSel);
            BigDecimal allowanceRankPercent = Optional.ofNullable(ac.data.getRankingResultInfo().allowanceRankPercent)
                    .orElse(BigDecimal.ZERO);
            newPersonPosFile.set(PositionStructureConstant.NCKD_ALLOWANCERANKPCT, allowanceRankPercent);
        }
        //上年度考核结果
        newPersonPosFile.set(PositionStructureConstant.NCKD_APPRAISALRESULT, ac.data.getAppraisalResult());
        newPersonPosFile.set(PositionStructureConstant.NCKD_RESULTSCORE, ac.data.getAppraisalResultScore());

        newPersonPosFile.set(PositionStructureConstant.NCKD_DISABLE, EnableEnum.NO.getCode());
        // newPersonPosFile.set(PositionStructureConstant.NCKD_ISCURRENTNEWEST, EnableEnum.YES.getCode());
        newPersonPosFile.set(PositionStructureConstant.STATUS, StatusEnum.C.toString());
        newPersonPosFile.set(PositionStructureConstant.ENABLE, EnableEnum.YES.getCode());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMA, ac.data.getYearscoresuma());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMB, ac.data.getYearscoresumb());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMC, ac.data.getYearscoresumc());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMD, ac.data.getYearscoresumd());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUME, ac.data.getYearscoresume());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMF, ac.data.getYearscoresumf());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMG, ac.data.getYearscoresumg());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMH, ac.data.getYearscoresumh());
        newPersonPosFile.set(PositionStructureConstant.NCKD_YEARSCORESUMI, ac.data.getYearscoresumi());
        newPersonPosFile.set(PositionStructureConstant.NCKD_LYRCONTRIBSCORE, ac.lastYearContributeScore);
        newPersonPosFile.set(PositionStructureConstant.NCKD_ALLYEARSCORESUM, ac.data.getAllYearScoreSum());
        newPersonPosFile.set(PositionStructureConstant.NCKD_ADDYCONTRIBSCORE, ac.addYearContributeScore);
        newPersonPosFile.set(PositionStructureConstant.CREATOR_KEY, RequestContext.get().getCurrUserId());
        // 备注
        newPersonPosFile.set(PositionStructureConstant.KEY_NCKD_CAUSEREMARK, ac.remark);
        if(AdjustTypeEnum.KEEP_LEVEL.getCode().equals(ac.adjustType)){
            //保级原因
            newPersonPosFile.set(PositionStructureConstant.NCKD_LEVELKEEPREASON, ac.levelKeepReason+"");
        }

        logger.info("职位档案构建完成");
        return newPersonPosFile;
    }
    /**
     * 内部运行时上下文。
     */
    private static class AdjustmentContext {
        /** 执行年份 */
        Integer executeYear;

        /** 当前年份 */
        Integer nowYear;

        /** 调整生效日期 */
        Date beginDate;

        /** 员工ID */
        Long personId;

        /** 员工Name */
        String personName;

        /** 员工信息对象 */
        DynamicObject personInfo;

        /** 强类型数据对象（替代原 selMap） */
        AnnualAdjustmentData data;

        /** 是否有R位次（allowancerankpercent≤0则为false） */
        boolean haveRp = true;

        /** 是否保级（缺R位次时为true） */
        boolean keep = false;

        /** 本年考核是否已使用 */
        boolean useAppraisalresult = false;

        /** 本年考核被使用的原因*/
        StringBuilder useAppraisalResultReasons;

        /** 是否本年首次调整 */
        boolean isYearFirstDo = false;

        /** 考核升降级标记（+1升/-1降/-2降两级） */
        int minusByAppraisal = 0;

        /** 固定类型（默认3） */
        String typestate = "3";

        /** 上一条档案记录ID */
        Long lastRecordId;

        /** 上一条记录对象 */
        DynamicObject lastRecordInfo;

        /** 上年度贡献综合评价分 */
        BigDecimal lastYearContributeScore = BigDecimal.ZERO;
        /** 年度新增贡献积分 */
        BigDecimal addYearContributeScore = BigDecimal.ZERO;
        /** 当前累计积分池 */
        BigDecimal sumScore = BigDecimal.ZERO;
        /** 总分（池+学历+职称/技能） */
        BigDecimal allSumScore = BigDecimal.ZERO;

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

        /** 职称得分 */
        BigDecimal rankScore = BigDecimal.ZERO;
        /** 技能得分 */
        BigDecimal jobStatusScore = BigDecimal.ZERO;

        /** 调整说明 */
        StringBuffer whyAdjust = new StringBuffer();

        /** 升降类型：0降/1保/2升/7固定/8无考核结果 */
        String adjustType;
        /** 升降数量：正数升、负数降、0保 */
        Integer adjustInt;

        PositionAppointmentBO positionAppointment;
        /**当前任职的序列（未转换前，这里转换指的是：如果是管理序列，则按职能序列进行调整）*/
        DynamicObject jobSeq;
        /**当前任职的序列（转换后，这里转换指的是：如果是管理序列，则按职能序列进行调整）*/
        DynamicObject convertJobSeq;

        String remark;

        /** 调整说明 */
        StringJoiner whyAdjust1 = new StringJoiner(StrFormatter.LINE_SEPARATOR);

        /**标记排名未升级*/
        boolean rankNoUpGrad = false;

        /***1：本自然年度内已升过级，达到升级限制；2:总积分没有达到下一级最低分数要求；3：未聘任高一级的职称；4：未聘任高一级的技能，5：R排名与全员绩效考核等级均未达到升级标准；6：总积分及排名均未达到；7：达到职级上限*/
        Integer levelKeepReason;

        Map<Integer, DynamicObject> jobLevelByJobSeqMap;
    }
}