2 Commits 54ebff7fb8 ... ea39793654

Tác giả SHA1 Thông báo Ngày
  wyc ea39793654 Merge branch 'refs/heads/feat-hr-psms_1.0' 4 ngày trước cách đây
  wyc cba705216d feat(hr): 新增年度绩效排名管理功能 4 ngày trước cách đây

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

@@ -111,6 +111,8 @@ public class FormConstant {
     public static final String BILL_NO_KEY = "BILLNO";
     /** 单据状态 */
     public static final String BILL_STATUS_KEY = "billstatus";
+    /** 单据状态 */
+    public static final String AUDIT_STATUS = "auditstatus";
     /** 创建人 */
     public static final String CREATOR_KEY = "CREATOR";
     /** 创建时间 */

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

@@ -3,6 +3,8 @@ package nckd.jxccl.hr.psms.business;
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.entity.constant.StatusEnum;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.BusinessDataServiceHelper;
@@ -41,6 +43,7 @@ import java.util.StringJoiner;
 */
 public class AnnualAdjustmentService {
 
+    protected final static Log logger = LogFactory.getLog(AnnualAdjustmentService.class);
 
     /**
      * 生成年度调整记录
@@ -50,6 +53,9 @@ public class AnnualAdjustmentService {
      * @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、 初始化上下文并加载基础数据。
@@ -61,26 +67,30 @@ public class AnnualAdjustmentService {
             if (adjustDateTime.getMonthValue() == 1 && adjustDateTime.getDayOfMonth() == 1) {
                 //前端只选年份,这里默认为XXX-07-01
                 adjustDate = DateUtil.toDate(adjustDateTime.withMonth(7).withDayOfMonth(1));
+                logger.debug("调整日期为年初日期,已转换为当年7月1日");
             }
         }else{
             //获取当年07-01的日期
             /*LocalDate julyFirst = LocalDate.of(DateUtil.getYear(new Date()), 7, 1);
             adjustDate = Date.from(julyFirst.atStartOfDay(ZoneId.systemDefault()).toInstant());*/
             adjustDate = new Date();
+            logger.debug("未指定调整日期,默认使用当前日期");
         }
         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.debug("评估是否为本年首次调整及考核使用情况完成");
 
         // 3、加载上一条记录并校验生效日期。
         //对应SHR:393~421行
         loadLastRecordAndValidateBeginDate(ac);
+        logger.debug("加载上一条记录并校验生效日期完成");
 
         // 4、处理职位序列(如果是管理序列,则按职能序列进行调整)
         //对应SHR:521~527行
@@ -88,16 +98,18 @@ public class AnnualAdjustmentService {
         ac.convertJobSeq = convertJobSeq;
         DynamicObject convertLastJobSeq = JobLevelCalculatorService.handleJobSeq(ac.data.getLastJobSeq());
         ac.data.setConvertLastJobSeq(convertLastJobSeq);
-
+        logger.debug("处理职位序列完成");
         // 5、获取技能/职称分
         //对应SHR:451~481行
         JobLevelCalculatorService.JobScoreInfo jobScoreInfo = JobLevelCalculatorService.handleJobScores(ac.lastRecordInfo,convertJobSeq, ac.positionAppointment);
         ac.data.setJobScoreInfo(jobScoreInfo);
+        logger.debug("获取技能/职称分完成");
 
         // 6、计算学历得分并生成说明
         //对应SHR:423~449行
         ac.diplomaScore = JobLevelCalculatorService.handleDiplomaScore(ac.lastRecordInfo, ac.positionAppointment, jobScoreInfo);
         ac.whyDiplomaScore.putAll(jobScoreInfo.whyDiplomaScore);
+        logger.debug("计算学历得分完成");
 
         //---------------------------------- 调整日志 begin ----------------------------------
         ac.whyAdjust1.add(StrFormatter.format("毕业时间小于等于【{}】的最高学历【{}】,学历分【{}】{}",
@@ -113,11 +125,12 @@ public class AnnualAdjustmentService {
         // 7、汇总年度积分池与综合分数。
         //对应SHR:483~519行
         aggregateScores(ac);
-
+        logger.info("汇总年度积分池与综合分数完成,总分:" + ac.allSumScore);
 
         //8.确定目标职级(含有R排名和无R排名两条路径)。
         //对应SHR:540~702行
         DynamicObject jobLevel = decideTargetJobGrade(ac);
+        logger.debug("确定目标职级完成");
 
         // 9、上年度考核结果为“无”时,取最低职级
         //对应SHR:703~707行
@@ -126,6 +139,7 @@ public class AnnualAdjustmentService {
             ac.adjustType = "8";
             ac.adjustInt = 0;
             jobLevel = JobLevelCalculatorService.getLowestJobLevel(ac.convertJobSeq);
+            logger.info("上年度考核结果为'无',采用最低职级");
         }
         if(jobLevel == null){
             throw new ValidationException(StrFormatter.format("人员【{}】,职位序列【{}】总积分【{}】职称等级【{}】技能等级【{}】考核结果【{}】没有匹配到符合的职级",
@@ -145,9 +159,12 @@ public class AnnualAdjustmentService {
 
         //【三期需求】:计算保级原因
         judgeLevelKeepReason(ac, jobLevel);
+        logger.debug("计算保级原因完成");
 
         //10、构建职位档案
-        return buildPersonPositionFile(ac, jobLevel);
+        DynamicObject result = buildPersonPositionFile(ac, jobLevel);
+        logger.info("年度调整记录生成完成,员工:" + ac.personName + ",调整类型:" + ac.adjustType);
+        return result;
 
 
 
@@ -214,6 +231,7 @@ public class AnnualAdjustmentService {
      * @date: 2025/10/08 21:15
      */
     private static AdjustmentContext initAndLoad(Integer executeYear, DynamicObject person, Date beginDate,Date adjustDate,PositionAppointmentBO positionAppointment,DynamicObject performanceResult,DynamicObject firstPersonPosFile){
+        logger.debug("开始初始化上下文,执行年份:" + executeYear);
         AdjustmentContext ac = new AdjustmentContext();
         ac.executeYear = executeYear;
         ac.nowYear = executeYear;
@@ -426,6 +444,7 @@ public class AnnualAdjustmentService {
             ac.minusByAppraisal = -2;
             ac.whyAdjust.append("【考核结果不合格】降2级");
         }
+        logger.debug("初始化上下文完成");
         return ac;
     }
 

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

@@ -883,7 +883,7 @@ public class JobLevelCalculatorService {
         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.NCKD_GROUPNAME,
+        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");
         //避免一个员工存在多个排名分组中,只取最新排名分组
@@ -891,7 +891,7 @@ public class JobLevelCalculatorService {
         String groupName = null;
         if(groupArray != null && groupArray.length > 0){
             groupId = groupArray[0].getLong(FormConstant.ID_KEY);
-            groupName = groupArray[0].getString(PerfRankMgmtConstant.NCKD_GROUPNAME);
+            groupName = groupArray[0].getString(PerfRankMgmtConstant.NAME_KEY);
         }
         if(groupId != null && groupId > 0){
             QFilter perfRankMgmtFilter = new QFilter(FormConstant.ID_KEY,QCP.equals,groupId);

+ 8 - 2
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/common/PerfRankMgmtConstant.java

@@ -30,8 +30,6 @@ public class PerfRankMgmtConstant extends FormConstant {
     public static final String NCKD_EXCLUDERANKREASON = "NCKD_EXCLUDERANKREASON";
     /** 排名来源(0:系统生成,1:手动添加) */
     public static final String NCKD_RANKSOURCE = "NCKD_RANKSOURCE";
-    /** 组名 */
-    public static final String NCKD_GROUPNAME = "NCKD_GROUPNAME";
     /** 年度 */
     public static final String NCKD_THEYEAR = "NCKD_THEYEAR";
     /** 参加排名总人数 */
@@ -59,4 +57,12 @@ public class PerfRankMgmtConstant extends FormConstant {
     /** 比例 */
     public static final String NCKD_RATIO = "nckd_ratio";
     /*-------------------------------------- 排名考核结果比例配置 end --------------------------------------*/
+
+
+    /*-------------------------------------- 年度绩效排名解锁单据 begin --------------------------------------*/
+    /** 年度绩效排名解锁单据-实体标识 */
+    public static final String PERFRANKUNLOCK_ENTITYID = "nckd_perfrankunlock";
+    public static final String NCKD_PERFRANKUNLOCKENTRY = "nckd_perfrankunlockentry";
+    public static final String NCKD_PERFRANKMGMT = "NCKD_PERFRANKMGMT";
+    /*-------------------------------------- 年度绩效排名解锁单据 begin --------------------------------------*/
 }

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

@@ -1,17 +1,23 @@
 package nckd.jxccl.hr.psms.plugin.form.performance;
 
+import com.google.common.collect.Lists;
 import kd.bos.bill.IBillView;
+import kd.bos.bill.OperationStatus;
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.RefObject;
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
 import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
 import kd.bos.entity.EntityMetadataCache;
 import kd.bos.entity.QueryEntityType;
+import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.datamodel.IDataModel;
 import kd.bos.entity.datamodel.RowDataEntity;
 import kd.bos.entity.datamodel.events.AfterAddRowEventArgs;
+import kd.bos.entity.datamodel.events.BeforeImportEntryEventArgs;
+import kd.bos.entity.datamodel.events.ChangeData;
 import kd.bos.entity.datamodel.events.PropertyChangedArgs;
 import kd.bos.ext.hr.service.query.QueryEntityHelper;
 import kd.bos.form.ConfirmCallBackListener;
@@ -24,28 +30,36 @@ import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.events.MessageBoxClosedEvent;
 import kd.bos.form.operate.FormOperate;
 import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.form.plugin.IFormPlugin;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.plugin.sample.dynamicform.pcform.form.template.ConfirmCallBack;
 import kd.bos.servicehelper.QueryServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
 import nckd.jxccl.base.common.constant.SystemQueryConstant;
 import nckd.jxccl.base.common.enums.AppraisalResultEnum;
+import nckd.jxccl.base.common.enums.psms.JobSeqEnum;
 import nckd.jxccl.base.common.utils.ConvertUtil;
 import nckd.jxccl.base.common.utils.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
 import nckd.jxccl.hr.psms.common.PositionStructureConstant;
 import nckd.jxccl.hr.psms.helper.PositionFileHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Strings;
 
 import java.time.temporal.ChronoUnit;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.EventObject;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -56,7 +70,7 @@ import java.util.stream.Stream;
 * @date 2025/10/20 15:11
 * @version 1.0
 */
-public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin {
+public class PerfrankMgmtFormPlugin extends ConfirmCallBack implements Plugin {
 
     private static final Log logger = LogFactory.getLog(PerfrankMgmtFormPlugin.class);
 
@@ -65,6 +79,8 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
     @Override
     public void afterBindData(EventObject e) {
         sortEntry();
+        // 该单据不需要审批,默认为暂存状态
+        this.getModel().setValue(FormConstant.STATUS, StatusEnum.A.toString());
     }
 
     @Override
@@ -84,6 +100,37 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
     }
 
 
+
+    @Override
+    public void beforePropertyChanged(PropertyChangedArgs e) {
+        String name = e.getProperty().getName();
+        if (PerfRankMgmtConstant.NCKD_ADMINORG.equalsIgnoreCase(name)) {
+            ChangeData[] changeSet = e.getChangeSet();
+            Long newValueId = ConvertUtil.toLong(changeSet[0].getNewValue(),null);
+            Object oldValue = ConvertUtil.toDynamicObjectOrNull(changeSet[0].getOldValue());
+            Long oldValueId = oldValue == null ? null : ConvertUtil.toLong(oldValue, null);
+            if(!Objects.equals(newValueId, oldValueId)) {
+                DynamicObjectCollection entryEntities = getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                boolean hasNonEmptyPerson = entryEntities.stream()
+                        .anyMatch(entry -> entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERSON) != null);
+                if(hasNonEmptyPerson) {
+                    // 直接弹出确认框,无需回滚
+                    // 表单字段修改前交互
+                    this.getView().showConfirm(
+                            "确认修改【所属组织】吗?",
+                            "请注意:修改【所属组织】会导致排名单元范围变化会清空现有排名名单。您需要在之后重新生成。确认要修改吗?",
+                            MessageBoxOptions.OKCancel,
+                            ConfirmTypes.Delete,
+                            new ConfirmCallBackListener(PerfRankMgmtConstant.NCKD_ADMINORG, this)
+                    );
+                }
+            }
+        }
+    }
+
+
+
+
     @Override
     public void propertyChanged(PropertyChangedArgs e) {
         String name = e.getProperty().getName();
@@ -91,17 +138,43 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
                         PerfRankMgmtConstant.NCKD_ALLOWANCERANK, PerfRankMgmtConstant.NCKD_POSTALLOWANCE,
                         PerfRankMgmtConstant.NCKD_TOPRANK)
                 .anyMatch(prop -> prop.equalsIgnoreCase(name))){
+            //排名信息变化计算排名数
             calcRankCount();
+        }else if(FormConstant.NCKD_PERSON.equalsIgnoreCase(name)){
+            //变更或选择人员重新获取是否享受职位津贴
+            ChangeData[] changeSet = e.getChangeSet();
+            Object newValue = changeSet[0].getNewValue();
+            Object oldValue = changeSet[0].getOldValue();
+            if(newValue != null && !Objects.equals(newValue, oldValue)) {
+                DynamicObject newPerson = ConvertUtil.toDynamicObject(newValue);
+                long newValueId = newPerson.getLong(FormConstant.ID_KEY);
+                Date now = new Date();
+                Map<Long, Boolean> entitledToAllowance = isEntitledToAllowance(Collections.singletonList(newValueId), now);
+                boolean hasAllowance = entitledToAllowance.get(newValueId);
+                this.getModel().setValue(PerfRankMgmtConstant.NCKD_POSTALLOWANCE, hasAllowance,changeSet[0].getRowIndex());
+                this.getView().updateView(PerfRankMgmtConstant.NCKD_POSTALLOWANCE,changeSet[0].getRowIndex());
+            }
+
         }
 
     }
 
+    @Override
+    public void beforeImportEntry(BeforeImportEntryEventArgs e) {
+        super.beforeImportEntry(e);
+    }
 
     @Override
     public void beforeDoOperation(BeforeDoOperationEventArgs args) {
         FormOperate operate = (FormOperate) args.getSource();
         String itemKey = operate.getOperateKey();
         if(PerfRankMgmtConstant.GETRANKLIST_OP.equalsIgnoreCase(itemKey)) {
+            DynamicObject adminOrg = ConvertUtil.toDynamicObjectOrNull(this.getModel().getValue(FormConstant.NCKD_ADMINORG));
+            if(adminOrg == null) {
+                this.getView().showTipNotification("请先选择“所属组织”");
+                args.setCancel(true);
+                return;
+            }
             RefObject<String> afterConfirm = new RefObject<>();
             if (!operate.getOption().tryGetVariableValue("afterConfirm", afterConfirm)) {
                 //判断是否已获取过名单,如获取过再获取提示用户数据会丢失
@@ -119,7 +192,7 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
                 if (isGetRank) {
                     // 取消当前操作,等待用户确认
                     args.setCancel(true);
-                    //询问对话
+                    //询问对话,交互
                     this.getView().showConfirm(
                             "已获取过排名名单了,重新获取将会覆盖(系统生成部分)现有人员名单是否继续操作?",
                             MessageBoxOptions.OKCancel,
@@ -147,6 +220,14 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
                 // 用户选择“否”,中断操作
                 this.getView().showTipNotification("操作已取消");
             }
+        }else if(PerfRankMgmtConstant.NCKD_ADMINORG.equalsIgnoreCase(evt.getCallBackId())){
+            // 用户确认:允许修改继续,并清空分录
+            if (evt.getResult() == MessageBoxResult.Yes) {
+                DynamicObjectCollection entryEntities = getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                entryEntities.clear();
+                getModel().updateEntryCache(entryEntities);
+                getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+            }
         }
     }
 
@@ -154,116 +235,161 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
     public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
         String itemKey = afterDoOperationEventArgs.getOperateKey();
         if(PerfRankMgmtConstant.GETRANKLIST_OP.equalsIgnoreCase(itemKey)){
-            IBillView billView = (IBillView)this.getView();
+            /*IBillView billView = (IBillView)this.getView();
+            //判断单据是否已保存
             boolean isFormDb = billView.getModel().getDataEntity().getDataEntityState().getFromDatabase();
+            // 获取修改但还没保存的字段
+            DynamicObject dataEntity = this.getModel().getDataEntity(true);
+            List<IDataEntityProperty> iDataEntityProperties = dataEntity.getDataEntityState().GetDirtyProperties();
+            boolean hasAdminOrgId = iDataEntityProperties.stream()
+                    .anyMatch(property -> property.getName().equalsIgnoreCase(FormConstant.NCKD_ADMINORG + "_" + FormConstant.ID_KEY));
             if (!isFormDb) {
                 billView.showTipNotification("请保存后再获取排名名单");
-            }else{
+            } else if(hasAdminOrgId){
+                billView.showTipNotification("由于您修改了【所属组织】但还没有保存,请保存之后在获取排名名单");
+            }else{*/
                 IDataModel model = this.getModel();
-                DynamicObject adminOrg = ConvertUtil.toDynamicObject(model.getValue(FormConstant.NCKD_ADMINORG));
-                String structLongNumber = adminOrg.getString(FormConstant.STRUCTLONGNUMBER);
-                //查询所选组织下的人员
-                QFilter qFilter = new QFilter(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.STRUCTLONGNUMBER), QCP.like, structLongNumber + "%");
-                //在职人员
-                qFilter.and(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS,FormConstant.IS_HIRED), QCP.equals, EnableEnum.YES.getCode());
-                QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
-                        //用工关系状态.编码(1005_S	在职-试用中、1010_S	在职、1020_S	离职、1030_S	已退休),istrial:是否试用,ishired:是否在职
-                        .addGroup(new String[]{FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS}, FormConstant.NUMBER_KEY, FormConstant.NAME_KEY, FormConstant.IS_TRIAL, FormConstant.IS_HIRED)
-                        //任职状态分类(1010_S:在岗,1030_S:不在岗)
-                        .addGroup(new String[]{FormConstant.HRPI_EMPPOSORGREL, FormConstant.POS_STATUS, FormConstant.POST_STATE_CLS}, FormConstant.NUMBER_KEY)
-                        //入职日期
-                        .addGroup(new String[]{FormConstant.HRPI_EMPENTREL}, FormConstant.ENTRYDATE)
-                        //职务级别
-                        .addGroup(new String[]{FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE}, FormConstant.NAME_KEY, FormConstant.NUMBER_KEY, FormConstant.NCKD_SORTNUM)
-                        .addGroup(new String[]{FormConstant.EMPLOYEE_KEY}, FormConstant.ID_KEY, FormConstant.NAME_KEY, FormConstant.EMP_NUMBER_KEY);
-
-                // -------------------------------- 1、查询组织下的在职人员 --------------------------------
-                QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(SystemQueryConstant.HSPM_ASSIGNMENTQUERY);
-                DynamicObjectCollection personList = QueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter},queryFieldBuilder.buildOrder());
-                //添加分录
-                DynamicObjectCollection entryEntityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
-                //根据条件删除分录
-
-                // -------------------------------- 2、删除系统生成部分人员 --------------------------------
-                //删除系统生成部分人员
-                entryEntityCols.removeIf(obj -> EnableEnum.NO.getCode().equals(obj.getString(PerfRankMgmtConstant.NCKD_RANKSOURCE)));
-
-                List<Long> personIds = personList.stream()
-                        .map(person -> person.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)))
-                        .collect(Collectors.toList());
-                if(!personIds.isEmpty()) {
-
-                    // -------------------------------- 3、获取人员对应的职级 --------------------------------
-                    //获取职位积分档案,用于判断是否有职级;职级大于0默认勾选“职位津贴”
-                    DynamicObject[] newestPersonPosFileByPerson = PositionFileHelper.getNewestPersonPosFileByPerson(personIds.toArray(new Long[0]));
-                    Map<Long, Long> personJobLevelMap = Arrays.stream(newestPersonPosFileByPerson)
-                            .collect(Collectors.toMap(
-                                    obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
-                                    obj -> obj.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
-                            ));
-
-                    // -------------------------------- 4、获取管理人员职位津贴配置 --------------------------------
-                    //获取《管理人员职位津贴》,存在则默认勾选“职位津贴”
-                    //查询在当前时间范围内的数据。NCKD_STARTDATE,NCKD_ENDDATE
-                    Date now = new Date();
-                        QFilter managerAllowanceFilter = new QFilter(FormConstant.NCKD_STARTDATE, QCP.less_equals, now)
-                            .and(FormConstant.NCKD_ENDDATE, QCP.large_equals,now)
-                            .and(FormConstant.NCKD_PERSON, QCP.in, personIds);
-                    DynamicObjectCollection managerAllowancQuery = QueryServiceHelper.query(PositionStructureConstant.MANAGERALLOWANCE_ENTITYID, String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY), new QFilter[]{managerAllowanceFilter});
-                    Map<Long, Boolean> managerAllowanceMap = managerAllowancQuery.stream()
-                            .collect(Collectors.toMap(
-                                    obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
-                                    obj -> Boolean.TRUE
-                            ));
-
-                    // -------------------------------- 4、加载数据到分录 --------------------------------
-                    for (DynamicObject person : personList) {
-                        DynamicObject entryCol = entryEntityCols.addNew();
-                        long personId = person.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
-                        String personName = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.NAME_KEY));
-                        String personNumber = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.EMP_NUMBER_KEY));
-
-                        DynamicObjectType type = EntityMetadataCache.getDataEntityType(FormConstant.HRPI_EMPLOYEE);
-                        DynamicObject personObj = (DynamicObject)type.createInstance();
-                        personObj.set(FormConstant.ID_KEY, personId);
-                        personObj.set(FormConstant.NAME_KEY, personName);
-                        personObj.set(FormConstant.EMP_NUMBER_KEY, personNumber);
-                        entryCol.set(FormConstant.NCKD_PERSON, personObj);
-                        //是否试用
-                        boolean isTrial = person.getBoolean(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_TRIAL));
-                        //入职日期
-                        Date entryDate = person.getDate(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.ENTRYDATE));
-                        //职务级别编码
-                        Integer posGradeSort = null;
-                        if (person.containsProperty(FormConstant.NCKD_HRPI_PARTYPOSH)
-                                && person.containsProperty(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE))
-                                && person.containsProperty(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE, FormConstant.NCKD_SORTNUM))) {
-                            posGradeSort = person.getInt(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE, FormConstant.NCKD_SORTNUM));
-                        }
-                        StringBuilder reason = new StringBuilder();
-                        boolean participateRank = isParticipateRank(isTrial, entryDate, posGradeSort, reason);
-                        entryCol.set(PerfRankMgmtConstant.NCKD_ISRANKING, participateRank);
-                        entryCol.set(PerfRankMgmtConstant.NCKD_EXCLUDERANKREASON, reason.toString());
-                        entryCol.set(PerfRankMgmtConstant.NCKD_RANKSOURCE, EnableEnum.NO.getCode());
-                        //判断是否有职位津贴
-                        boolean hasAllowance = personJobLevelMap.containsKey(personId) && personJobLevelMap.get(personId) > 0 || managerAllowanceMap.containsKey(personId);
-                        entryCol.set(PerfRankMgmtConstant.NCKD_POSTALLOWANCE, hasAllowance);
+                DynamicObject adminOrg = ConvertUtil.toDynamicObjectOrNull(model.getValue(FormConstant.NCKD_ADMINORG));
+                if(adminOrg != null) {
+                    String structLongNumber = adminOrg.getString(FormConstant.STRUCTLONGNUMBER);
+                    //查询所选组织下的人员
+                    QFilter qFilter = new QFilter(String.join(".", FormConstant.HRPI_EMPPOSORGREL, FormConstant.ADMINORG, FormConstant.STRUCTLONGNUMBER), QCP.like, structLongNumber + "%");
+                    //在职人员
+                    qFilter.and(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_HIRED), QCP.equals, EnableEnum.YES.getCode());
+                    QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                            //用工关系状态.编码(1005_S	在职-试用中、1010_S	在职、1020_S	离职、1030_S	已退休),istrial:是否试用,ishired:是否在职
+                            .addGroup(new String[]{FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS}, FormConstant.NUMBER_KEY, FormConstant.NAME_KEY, FormConstant.IS_TRIAL, FormConstant.IS_HIRED)
+                            //任职状态分类(1010_S:在岗,1030_S:不在岗)
+                            .addGroup(new String[]{FormConstant.HRPI_EMPPOSORGREL, FormConstant.POS_STATUS, FormConstant.POST_STATE_CLS}, FormConstant.NUMBER_KEY)
+                            //入职日期
+                            .addGroup(new String[]{FormConstant.HRPI_EMPENTREL}, FormConstant.ENTRYDATE)
+                            //职务级别
+                            .addGroup(new String[]{FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE}, FormConstant.NAME_KEY, FormConstant.NUMBER_KEY, FormConstant.NCKD_SORTNUM)
+                            .addGroup(new String[]{FormConstant.EMPLOYEE_KEY}, FormConstant.ID_KEY, FormConstant.NAME_KEY, FormConstant.EMP_NUMBER_KEY);
+
+                    // -------------------------------- 1、查询组织下的在职人员 --------------------------------
+                    QueryEntityType queryEntityType = (QueryEntityType) EntityMetadataCache.getDataEntityType(SystemQueryConstant.HSPM_ASSIGNMENTQUERY);
+                    DynamicObjectCollection personList = QueryEntityHelper.getInstance().getQueryDyoColl(queryEntityType, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter}, queryFieldBuilder.buildOrder());
+                    //添加分录
+                    DynamicObjectCollection entryEntityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                    //根据条件删除分录
+
+                    // -------------------------------- 2、删除系统生成部分人员 --------------------------------
+                    //删除系统生成部分人员
+                    entryEntityCols.removeIf(obj -> EnableEnum.NO.getCode().equals(obj.getString(PerfRankMgmtConstant.NCKD_RANKSOURCE)));
+
+                    List<Long> personIds = personList.stream()
+                            .map(person -> person.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY)))
+                            .collect(Collectors.toList());
+                    if (!personIds.isEmpty()) {
+                        Date now = new Date();
+                        // -------------------------------- 3、获取人员是否享受职位津贴 --------------------------------
+                        Map<Long, Boolean> entitledToAllowance = isEntitledToAllowance(personIds, now);
+
+                        // -------------------------------- 4、加载数据到分录 --------------------------------
+                        for (DynamicObject person : personList) {
+                            DynamicObject entryCol = entryEntityCols.addNew();
+                            long personId = person.getLong(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.ID_KEY));
+                            String personName = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.NAME_KEY));
+                            String personNumber = person.getString(String.join(".", FormConstant.EMPLOYEE_KEY, FormConstant.EMP_NUMBER_KEY));
+
+                            DynamicObjectType type = EntityMetadataCache.getDataEntityType(FormConstant.HRPI_EMPLOYEE);
+                            DynamicObject personObj = (DynamicObject) type.createInstance();
+                            personObj.set(FormConstant.ID_KEY, personId);
+                            personObj.set(FormConstant.NAME_KEY, personName);
+                            personObj.set(FormConstant.EMP_NUMBER_KEY, personNumber);
+                            entryCol.set(FormConstant.NCKD_PERSON, personObj);
+                            //是否试用
+                            boolean isTrial = person.getBoolean(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.LABOR_REL_STATUS, FormConstant.IS_TRIAL));
+                            //入职日期
+                            Date entryDate = person.getDate(String.join(".", FormConstant.HRPI_EMPENTREL, FormConstant.ENTRYDATE));
+                            //职务级别编码
+                            Integer posGradeSort = null;
+                            if (person.containsProperty(FormConstant.NCKD_HRPI_PARTYPOSH)
+                                    && person.containsProperty(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE))
+                                    && person.containsProperty(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE, FormConstant.NCKD_SORTNUM))) {
+                                posGradeSort = person.getInt(String.join(".", FormConstant.NCKD_HRPI_PARTYPOSH, FormConstant.NCKD_POSGRADE, FormConstant.NCKD_SORTNUM));
+                            }
+                            StringBuilder reason = new StringBuilder();
+                            boolean participateRank = isParticipateRank(isTrial, entryDate, posGradeSort, reason);
+                            entryCol.set(PerfRankMgmtConstant.NCKD_ISRANKING, participateRank);
+                            entryCol.set(PerfRankMgmtConstant.NCKD_EXCLUDERANKREASON, reason.toString());
+                            entryCol.set(PerfRankMgmtConstant.NCKD_RANKSOURCE, EnableEnum.NO.getCode());
+                            //判断是否有职位津贴
+                            boolean hasAllowance = entitledToAllowance.get(personId);
+                            entryCol.set(PerfRankMgmtConstant.NCKD_POSTALLOWANCE, hasAllowance);
 
+                        }
+                        this.getView().showSuccessNotification("名单获取完成");
+                    } else {
+                        this.getView().showTipNotification("未获取到人员");
                     }
-                    this.getView().showSuccessNotification("名单获取完成");
-                }else{
-                    this.getView().showTipNotification("未获取到人员");
                 }
             }
-        }
+       /* }*/
         if(Arrays.asList(FormConstant.DELETEENTRY_OP, PerfRankMgmtConstant.GETRANKLIST_OP).contains(itemKey)){
             sortEntry();
             calcRankCount();
         }
+        // this.getView().setStatus(OperationStatus.EDIT);
+    }
+
+    /**
+     * 判断人员是否享受职位津贴
+     * @param personIds 人员ID列表
+     * @param date 查询时间
+     * @return Map<Long, Boolean> 人员ID到是否享受津贴的映射
+     * @author W.Y.C
+     * @date: 2025/10/21
+     */
+    private Map<Long, Boolean> isEntitledToAllowance(List<Long> personIds, Date date) {
+        Map<Long, Boolean> result = new HashMap<>();
+
+        if (personIds == null || personIds.isEmpty()) {
+            return result;
+        }
+
+        //获取职位积分档案,用于判断是否有职级;职级大于0默认勾选“职位津贴”
+        //属于技能序列、技术序列或职能序列,并且职级不为0。
+        DynamicObject[] personPosFiles = PositionFileHelper.getNewestPersonPosFileByPerson(personIds.toArray(new Long[0]));
+        Map<Long, Long> personJobLevelMap = Arrays.stream(personPosFiles)
+                .filter(obj -> Stream.of(JobSeqEnum.TECHNICALS.getCode(), JobSeqEnum.FUNCTIONAL.getCode(), JobSeqEnum.SKILL.getCode())
+                        .anyMatch(s -> s.equalsIgnoreCase(obj.getDynamicObject(PositionStructureConstant.NCKD_JOBSEQHR).getString(FormConstant.NUMBER_KEY))))
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
+                        obj -> obj.getLong(String.join(".", PositionStructureConstant.NCKD_JOBLEVELHR, FormConstant.JOBLEVELSEQ))
+                ));
+
+        // 查询管理人员职位津贴配置
+        // 获取《管理人员职位津贴》,存在则默认勾选“职位津贴”
+        QFilter managerAllowanceFilter = new QFilter(FormConstant.NCKD_STARTDATE, QCP.less_equals, date)
+                .and(FormConstant.NCKD_ENDDATE, QCP.large_equals, date)
+                .and(FormConstant.NCKD_PERSON, QCP.in, personIds);
+
+        DynamicObjectCollection managerAllowanceQuery = QueryServiceHelper.query(
+                PositionStructureConstant.MANAGERALLOWANCE_ENTITYID,
+                String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY),
+                new QFilter[]{managerAllowanceFilter});
+
+        Map<Long, Boolean> managerAllowanceMap = managerAllowanceQuery.stream()
+                .collect(Collectors.toMap(
+                        obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
+                        obj -> Boolean.TRUE
+                ));
+
+        // 合并判断结果
+        for (Long personId : personIds) {
+            boolean hasAllowance = (personJobLevelMap.containsKey(personId) && personJobLevelMap.get(personId) > 0)
+                    || managerAllowanceMap.containsKey(personId);
+            result.put(personId, hasAllowance);
+        }
+
+        return result;
     }
 
 
 
+
     /**
      * 判断是否参与排名
      * @param isTrial 是否试用
@@ -301,45 +427,47 @@ public class PerfrankMgmtFormPlugin extends AbstractFormPlugin implements Plugin
      */
     private void sortEntry(){
         DynamicObjectCollection entryEntityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
-        int allowanceRank = entryEntityCols.get(0).getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
-        entryEntityCols.sort((o1, o2) -> {
-            // 先按 NCKD_TOPRANK 排序,空值或 0 视为最大值(排在最后)
-            Integer topRank1 = o1.getInt(PerfRankMgmtConstant.NCKD_TOPRANK);
-            Integer topRank2 = o2.getInt(PerfRankMgmtConstant.NCKD_TOPRANK);
-            if (topRank1 == 0) {
-                topRank1 = Integer.MAX_VALUE;
-            }
-            if (topRank2 == 0) {
-                topRank2 = Integer.MAX_VALUE;
-            }
+        if(!entryEntityCols.isEmpty()) {
+            int allowanceRank = entryEntityCols.get(0).getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
+            entryEntityCols.sort((o1, o2) -> {
+                // 先按 NCKD_TOPRANK 排序,空值或 0 视为最大值(排在最后)
+                Integer topRank1 = o1.getInt(PerfRankMgmtConstant.NCKD_TOPRANK);
+                Integer topRank2 = o2.getInt(PerfRankMgmtConstant.NCKD_TOPRANK);
+                if (topRank1 == 0) {
+                    topRank1 = Integer.MAX_VALUE;
+                }
+                if (topRank2 == 0) {
+                    topRank2 = Integer.MAX_VALUE;
+                }
 
-            int topRankCompare = topRank1.compareTo(topRank2);
-            if (topRankCompare != 0) {
-                return topRankCompare;
-            }
+                int topRankCompare = topRank1.compareTo(topRank2);
+                if (topRankCompare != 0) {
+                    return topRankCompare;
+                }
 
-            // 再按 NCKD_ALLOWANCERANK 排序,空值或 0 视为最大值(排在最后)
-            Integer allowanceRank1 = o1.getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
-            Integer allowanceRank2 = o2.getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
-            if (allowanceRank1 == 0) {
-                allowanceRank1 = Integer.MAX_VALUE;
-            }
-            if (allowanceRank2 == 0) {
-                allowanceRank2 = Integer.MAX_VALUE;
-            }
+                // 再按 NCKD_ALLOWANCERANK 排序,空值或 0 视为最大值(排在最后)
+                Integer allowanceRank1 = o1.getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
+                Integer allowanceRank2 = o2.getInt(PerfRankMgmtConstant.NCKD_ALLOWANCERANK);
+                if (allowanceRank1 == 0) {
+                    allowanceRank1 = Integer.MAX_VALUE;
+                }
+                if (allowanceRank2 == 0) {
+                    allowanceRank2 = Integer.MAX_VALUE;
+                }
 
-            int allowanceRankCompare = allowanceRank1.compareTo(allowanceRank2);
-            if (allowanceRankCompare != 0) {
-                return allowanceRankCompare;
-            }
+                int allowanceRankCompare = allowanceRank1.compareTo(allowanceRank2);
+                if (allowanceRankCompare != 0) {
+                    return allowanceRankCompare;
+                }
 
-            // 最后按 NCKD_ISRANKING 排序,true 在前,false 在后
-            Boolean isRanking1 = o1.getBoolean(PerfRankMgmtConstant.NCKD_ISRANKING);
-            Boolean isRanking2 = o2.getBoolean(PerfRankMgmtConstant.NCKD_ISRANKING);
-            return Boolean.compare(isRanking2, isRanking1);
-        });
-        this.getModel().updateEntryCache(entryEntityCols);
-        this.getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+                // 最后按 NCKD_ISRANKING 排序,true 在前,false 在后
+                Boolean isRanking1 = o1.getBoolean(PerfRankMgmtConstant.NCKD_ISRANKING);
+                Boolean isRanking2 = o2.getBoolean(PerfRankMgmtConstant.NCKD_ISRANKING);
+                return Boolean.compare(isRanking2, isRanking1);
+            });
+            this.getModel().updateEntryCache(entryEntityCols);
+            this.getView().updateView(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
+        }
     }
 
     /**

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

@@ -0,0 +1,40 @@
+package nckd.jxccl.hr.psms.plugin.form.performance;
+
+import kd.bos.form.IFormView;
+import kd.bos.form.control.Button;
+import kd.bos.form.control.Control;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.sdk.plugin.Plugin;
+import java.util.EventObject;
+
+/**
+* 年度绩效排名导入引导页面
+* 实体标识:nckd_perfrankmgmt_imp
+* @author W.Y.C
+* @date 2025/10/30 10:56
+* @version 1.0
+*/
+public class PerfrankMgmtImpFormPlugin extends AbstractFormPlugin implements Plugin {
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        Button button = (Button) this.getControl("nckd_btndowntpl");
+        button.addClickListener(this);
+    }
+
+
+    @Override
+    public void click(EventObject e) {
+        super.click(e);
+        Control control = (Control) e.getSource();
+        if ("nckd_btndowntpl".equalsIgnoreCase(control.getKey())) {
+            // 1. 获取父页面视图
+            IFormView parentView = this.getView().getParentView();
+            // 2. 调用父页面的操作(例如刷新操作)
+            parentView.invokeOperation("export_multientry_hr");
+            this.getView().sendFormAction(parentView);
+        }
+    }
+
+}

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

@@ -0,0 +1,47 @@
+package nckd.jxccl.hr.psms.plugin.form.performance;
+
+import com.alibaba.fastjson.JSONObject;
+import kd.hrmp.hies.multientry.common.dto.EntryImptBillData;
+import kd.hrmp.hies.multientry.common.plugin.impt.AfterBackFillDataEventArgs;
+import kd.hrmp.hies.multientry.common.plugin.impt.AfterValidateEventArgs;
+import kd.hrmp.hies.multientry.common.plugin.impt.BeforeInitValidatorEventArgs;
+import kd.hrmp.hies.multientry.common.plugin.impt.BeforeValidateEventArgs;
+import kd.hrmp.hies.multientry.common.plugin.impt.HREntryImportPlugin;
+
+import java.util.List;
+
+/**
+* 多分录导入插件
+* 实体标识:nckd_perfrankmgmt
+* @author W.Y.C
+* @date 2025/10/30 14:16
+* @version 1.0
+*/
+public class PerfrankMgmtImportEntryPlugin implements HREntryImportPlugin {
+
+
+    @Override
+    public void beforeInitValidator(BeforeInitValidatorEventArgs args) {
+        HREntryImportPlugin.super.beforeInitValidator(args);
+    }
+
+    @Override
+    public void beforeValidate(BeforeValidateEventArgs args) {
+        HREntryImportPlugin.super.beforeValidate(args);
+    }
+
+    @Override
+    public void afterValidate(AfterValidateEventArgs args) {
+        List<EntryImptBillData> billDatas = args.getBillDatas();
+        for (EntryImptBillData billData : billDatas) {
+            JSONObject data = billData.getData();
+            Long aLong = data.getLong("nckd_perfrankmgmtentry_id");
+        }
+        HREntryImportPlugin.super.afterValidate(args);
+    }
+
+    @Override
+    public void afterBackFillData(AfterBackFillDataEventArgs args) {
+        HREntryImportPlugin.super.afterBackFillData(args);
+    }
+}

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

@@ -0,0 +1,53 @@
+package nckd.jxccl.hr.psms.plugin.form.performance;
+
+import kd.bos.form.MessageBoxOptions;
+import kd.bos.form.events.AfterDoOperationEventArgs;
+import kd.bos.list.plugin.AbstractListPlugin;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Map;
+
+/**
+* 年度绩效排名管理列表插件
+* 实体标识:nckd_perfrankmgmtentry
+* @author W.Y.C
+* @date 2025/10/31 10:16
+* @version 1.0
+*/
+public class PerfrankMgmtListPlugin extends AbstractListPlugin implements Plugin {
+
+    @Override
+    public void afterDoOperation(AfterDoOperationEventArgs afterDoOperationEventArgs) {
+        String operateKey = afterDoOperationEventArgs.getOperateKey();
+        boolean success = afterDoOperationEventArgs.getOperationResult() != null && afterDoOperationEventArgs.getOperationResult().isSuccess();
+        if(success){
+            if(PositionStructureConstant.ISLOCKED_OP.equalsIgnoreCase(operateKey)){
+                Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
+                String message = customData.get("message");
+                if(StringUtils.isNotBlank( message)){
+                    this.getView().showConfirm("提示","锁定成功!",message, MessageBoxOptions.OK,null,null,null,null);
+                }else{
+                    this.getView().showSuccessNotification("锁定成功!");
+                }
+                this.getView().invokeOperation(FormConstant.REFRESH_OP);
+            }else if(PositionStructureConstant.UNLOCKED_OP.equalsIgnoreCase(operateKey)){
+                Map<String, String> customData = afterDoOperationEventArgs.getOperationResult().getCustomData();
+                if(customData != null && customData.containsKey("message")){
+                    String message = customData.get("message");
+                    if(StringUtils.isNotBlank( message)){
+                        this.getView().showConfirm("提示","解锁成功!",message, MessageBoxOptions.OK,null,null,null,null);
+                    }else{
+                        this.getView().showSuccessNotification("解锁成功!");
+                    }
+                    this.getView().invokeOperation(FormConstant.REFRESH_OP);
+                }else{
+                    this.getView().showSuccessNotification("发起流程成功,待审批通过后自动解锁!");
+                }
+            }
+        }
+
+    }
+}

+ 37 - 38
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/annualadjust/AnnualLockOrUnLockedOpPlugin.java

@@ -44,9 +44,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.StringJoiner;
 
-/**
- * 单据操作插件
- */
 /**
 * 年度调整锁定解锁
 * @author W.Y.C
@@ -134,45 +131,47 @@ public class AnnualLockOrUnLockedOpPlugin extends AbstractOperationServicePlugIn
             }
             SaveServiceHelper.update(load);
         }else {
-            //操作原因
-            String reason = this.getOption().getVariableValue("reason", "");
-            //发起解锁单据流程
-            DynamicObject billObj = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.POSFILEUNLOCK_ENTITYID);
-            billObj.set(FormConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
-            billObj.set(FormConstant.CREATE_TIME_KEY, System.currentTimeMillis());
-            billObj.set(FormConstant.BILL_STATUS_KEY, BillStatus.A.toString());
-            DynamicObject org = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
-            org.set(FormConstant.ID_KEY, UserServiceHelper.getUserMainOrgId(UserServiceHelper.getCurrentUserId()));
-            billObj.set(FormConstant.ORG_KEY, org);
-            billObj.set(PositionStructureConstant.NCKD_REASON, reason);
+            if(load != null && load.length >0) {
+                //操作原因
+                String reason = this.getOption().getVariableValue("reason", "");
+                //发起解锁单据流程
+                DynamicObject billObj = BusinessDataServiceHelper.newDynamicObject(PositionStructureConstant.POSFILEUNLOCK_ENTITYID);
+                billObj.set(FormConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
+                billObj.set(FormConstant.CREATE_TIME_KEY, System.currentTimeMillis());
+                billObj.set(FormConstant.BILL_STATUS_KEY, BillStatus.A.toString());
+                DynamicObject org = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+                org.set(FormConstant.ID_KEY, UserServiceHelper.getUserMainOrgId(UserServiceHelper.getCurrentUserId()));
+                billObj.set(FormConstant.ORG_KEY, org);
+                billObj.set(PositionStructureConstant.NCKD_REASON, reason);
 
-            DynamicObjectCollection entryColl = billObj.getDynamicObjectCollection(PositionStructureConstant.NCKD_POSFILEUNLOCKENTRY);
-            DynamicObjectType entryType = entryColl.getDynamicObjectType();
-            for (DynamicObject dynamicObject : load) {
-                DynamicObject entryObj = new DynamicObject(entryType);
-                entryObj.set(PositionStructureConstant.NCKD_PERSONPOSFILE, dynamicObject);
-                entryColl.add(entryObj);
-            }
-            //提交单据
-            OperateOption option = OperateOption.create();
-            OperationResult result = OperationServiceHelper.executeOperate(
-                    "submit",
-                    PositionStructureConstant.POSFILEUNLOCK_ENTITYID,
-                    new DynamicObject[]{billObj},
-                    option
-            );
-            if (!result.isSuccess()) {
-                StringJoiner joiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
-                for (IOperateInfo iOperateInfo : result.getAllErrorOrValidateInfo()) {
-                    joiner.add(iOperateInfo.getMessage());
+                DynamicObjectCollection entryColl = billObj.getDynamicObjectCollection(PositionStructureConstant.NCKD_POSFILEUNLOCKENTRY);
+                DynamicObjectType entryType = entryColl.getDynamicObjectType();
+                for (DynamicObject dynamicObject : load) {
+                    DynamicObject entryObj = new DynamicObject(entryType);
+                    entryObj.set(PositionStructureConstant.NCKD_PERSONPOSFILE, dynamicObject);
+                    entryColl.add(entryObj);
                 }
-                throw new ValidationException(StrFormatter.format("提交解锁申请单失败,原因:{}", joiner.toString()));
+                //提交单据
+                OperateOption option = OperateOption.create();
+                OperationResult result = OperationServiceHelper.executeOperate(
+                        "submit",
+                        PositionStructureConstant.POSFILEUNLOCK_ENTITYID,
+                        new DynamicObject[]{billObj},
+                        option
+                );
+                if (!result.isSuccess()) {
+                    StringJoiner joiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+                    for (IOperateInfo iOperateInfo : result.getAllErrorOrValidateInfo()) {
+                        joiner.add(iOperateInfo.getMessage());
+                    }
+                    throw new ValidationException(StrFormatter.format("提交解锁申请单失败,原因:{}", joiner.toString()));
 
-            } else {
-                if (this.getOperationResult().getCustomData() == null) {
-                    this.getOperationResult().setCustomData(new HashMap<>());
+                } else {
+                    if (this.getOperationResult().getCustomData() == null) {
+                        this.getOperationResult().setCustomData(new HashMap<>());
+                    }
+                    this.getOperationResult().getCustomData().put("bill", "true");
                 }
-                this.getOperationResult().getCustomData().put("bill", "true");
             }
         }
 

+ 72 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/PerfRankUnlockBillOpPlugin.java

@@ -0,0 +1,72 @@
+package nckd.jxccl.hr.psms.plugin.operate.performance;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+* 年度绩效排名解锁单据-解锁/反解锁
+* 实体标识:nckd_perfrankunlock
+* @author W.Y.C
+* @date 2025/10/31 10:34
+* @version 1.0
+*/
+public class PerfRankUnlockBillOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().add(PerfRankMgmtConstant.NCKD_PERFRANKMGMT);
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        String operateKey = e.getOperationKey();
+        List<Long> ids = new ArrayList<>();
+        for (DynamicObject data : e.getDataEntities()) {
+            DynamicObjectCollection entrys = data.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKUNLOCKENTRY);
+
+            for (DynamicObject entry : entrys) {
+                DynamicObject personPosFile = entry.getDynamicObject(PerfRankMgmtConstant.NCKD_PERFRANKMGMT);
+                long id = personPosFile.getLong(FormConstant.ID_KEY);
+                ids.add(id);
+            }
+        }
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(PositionStructureConstant.NCKD_LOCKSTATUS)
+                .add(PositionStructureConstant.KEY_NCKD_LOCKUSER)
+                .add(PositionStructureConstant.NCKD_LOCKDATETIME);
+        DynamicObject[] load = BusinessDataServiceHelper.load(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{QFilterCommonHelper.getIdInFilter(ids)});
+        for (DynamicObject dynamicObject : load) {
+            if(PositionStructureConstant.UNLOCKED_OP.equals(operateKey)) {
+                //审批通过-解锁
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKSTATUS, EnableEnum.NO.getCode());
+                dynamicObject.set(PositionStructureConstant.KEY_NCKD_LOCKUSER, null);
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKDATETIME, null);
+            }else{
+                //反向撤回-反解锁
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKSTATUS, EnableEnum.YES.getCode());
+                dynamicObject.set(PositionStructureConstant.KEY_NCKD_LOCKUSER, RequestContext.get().getCurrUserId());
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKDATETIME, new Date());
+            }
+        }
+        SaveServiceHelper.update(load);
+    }
+}

+ 231 - 0
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/PerfrankMgmtLockOrUnLockedOpPlugin.java

@@ -0,0 +1,231 @@
+package nckd.jxccl.hr.psms.plugin.operate.performance;
+
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.interaction.InteractionConfirmResult;
+import kd.bos.entity.operate.interaction.InteractionContext;
+import kd.bos.entity.operate.interaction.KDInteractionException;
+import kd.bos.entity.operate.result.IOperateInfo;
+import kd.bos.entity.operate.result.OperateErrorInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.plugin.args.BeforeOperationArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.entity.validate.BillStatus;
+import kd.bos.entity.validate.ErrorLevel;
+import kd.bos.form.MessageBoxResult;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.OperationServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.servicehelper.user.UserServiceHelper;
+import kd.sdk.plugin.Plugin;
+import nckd.jxccl.base.common.constant.FormConstant;
+import nckd.jxccl.base.common.exception.ValidationException;
+import nckd.jxccl.base.common.utils.QueryFieldBuilder;
+import nckd.jxccl.base.common.utils.StrFormatter;
+import nckd.jxccl.base.orm.helper.QFilterCommonHelper;
+import nckd.jxccl.hr.psms.common.PerfRankMgmtConstant;
+import nckd.jxccl.hr.psms.common.PositionStructureConstant;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+* 年度绩效管理锁定/解锁操作插件
+* 实体标识:nckd_perfrankmgmt
+* @author W.Y.C
+* @date 2025/10/31 9:21
+* @version 1.0
+*/
+public class PerfrankMgmtLockOrUnLockedOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
+
+    private final static String INTERACTION_SPONORE = PerfrankMgmtLockOrUnLockedOpPlugin.class.getName();
+    private final static String INTERACTION_SPONORE1 = PerfrankMgmtLockOrUnLockedOpPlugin.class.getName() + "#1";
+
+
+    private List<Long> ids;
+
+    StringJoiner pendingMsgJoiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+
+    private StringJoiner message = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().add(FormConstant.NAME_KEY);
+        e.getFieldKeys().add(PositionStructureConstant.ENABLE);
+        e.getFieldKeys().add(PositionStructureConstant.NCKD_LOCKSTATUS);
+    }
+
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e){
+        ids = new ArrayList<>();
+        e.addValidator(new AbstractValidator() {
+            @Override
+            public void validate() {
+                String operateKey = this.getOperateKey();
+                //islocked:锁定
+                //unlocked:解锁
+                for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                    DynamicObject data = rowDataEntity.getDataEntity();
+                    long id = data.getLong(FormConstant.ID_KEY);
+                    String enable = data.getString(FormConstant.ENABLE);
+                    boolean lockStatus = data.getBoolean(PositionStructureConstant.NCKD_LOCKSTATUS);
+                    String groupName = data.getString(FormConstant.NAME_KEY);
+                    if(PositionStructureConstant.ISLOCKED_OP.equals(operateKey)) {
+                        if(!lockStatus) {
+                            ids.add(id);
+                        }else{
+                            message.add(StrFormatter.format("排名单元【{}】已是“锁定”状态,忽略此条处理。", groupName));
+                        }
+                    } else if(PositionStructureConstant.UNLOCKED_OP.equals(operateKey)) {
+                        if(!lockStatus) {
+                            message.add(StrFormatter.format("排名单元【{}】已是“解锁”状态,忽略此条处理。", groupName));
+                        }else{
+                            ids.add(id);
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        String operateKey = e.getOperationKey();
+
+        QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                .add(FormConstant.ID_KEY)
+                .add(PositionStructureConstant.NCKD_LOCKSTATUS)
+                .add(PositionStructureConstant.KEY_NCKD_LOCKUSER)
+                .add(PositionStructureConstant.NCKD_LOCKDATETIME);
+        DynamicObject[] load = BusinessDataServiceHelper.load(PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{QFilterCommonHelper.getIdInFilter(ids)});
+        boolean isProcessFlag = true;
+        if(PositionStructureConstant.ISLOCKED_OP.equals(operateKey)) {
+            for (DynamicObject dynamicObject : load) {
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKSTATUS, EnableEnum.YES.getCode());
+                dynamicObject.set(PositionStructureConstant.KEY_NCKD_LOCKUSER, RequestContext.get().getCurrUserId());
+                dynamicObject.set(PositionStructureConstant.NCKD_LOCKDATETIME, new Date());
+            }
+            SaveServiceHelper.update(load);
+        }else {
+            if(load != null && load.length >0) {
+                //操作原因
+                String reason = this.getOption().getVariableValue("reason", "");
+                //发起解锁单据流程
+                DynamicObject billObj = BusinessDataServiceHelper.newDynamicObject(PerfRankMgmtConstant.PERFRANKUNLOCK_ENTITYID);
+                billObj.set(FormConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
+                billObj.set(FormConstant.CREATE_TIME_KEY, System.currentTimeMillis());
+                billObj.set(FormConstant.BILL_STATUS_KEY, BillStatus.A.toString());
+                billObj.set(FormConstant.AUDIT_STATUS, BillStatus.B.toString());
+                DynamicObject org = BusinessDataServiceHelper.newDynamicObject(FormConstant.ADMINORGHR_ENTITYID);
+                org.set(FormConstant.ID_KEY, UserServiceHelper.getUserMainOrgId(UserServiceHelper.getCurrentUserId()));
+                billObj.set(FormConstant.ORG_KEY, org);
+                billObj.set(PositionStructureConstant.NCKD_REASON, reason);
+
+                DynamicObjectCollection entryColl = billObj.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKUNLOCKENTRY);
+                DynamicObjectType entryType = entryColl.getDynamicObjectType();
+                for (DynamicObject dynamicObject : load) {
+                    DynamicObject entryObj = new DynamicObject(entryType);
+                    entryObj.set(PerfRankMgmtConstant.NCKD_PERFRANKMGMT, dynamicObject);
+                    entryColl.add(entryObj);
+                }
+                //提交单据
+                OperateOption option = OperateOption.create();
+                OperationResult result = OperationServiceHelper.executeOperate(
+                        "submit",
+                        PerfRankMgmtConstant.NCKD_PERFRANKMGMT_ENTITYID,
+                        new DynamicObject[]{billObj},
+                        option
+                );
+                if (!result.isSuccess()) {
+                    StringJoiner joiner = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+                    for (IOperateInfo iOperateInfo : result.getAllErrorOrValidateInfo()) {
+                        joiner.add(iOperateInfo.getMessage());
+                    }
+                    throw new ValidationException(StrFormatter.format("提交解锁申请单失败,原因:{}", joiner.toString()));
+
+                } else {
+                    if (this.getOperationResult().getCustomData() == null) {
+                        this.getOperationResult().setCustomData(new HashMap<>());
+                    }
+                    this.getOperationResult().getCustomData().put("bill", "true");
+                }
+            }
+        }
+
+
+        if(message.length() > 0) {
+            if (this.getOperationResult().getCustomData() == null) {
+                this.getOperationResult().setCustomData(new HashMap<>());
+            }
+            this.getOperationResult().getCustomData().put("message", message.toString());
+        }
+    }
+
+    @Override
+    public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
+        String operationKey = e.getOperationKey();
+        if(PositionStructureConstant.ISLOCKED_OP.equals(operationKey)) {
+
+        }else if(PositionStructureConstant.UNLOCKED_OP.equals(operationKey)){
+            //从流程启动条件判断非集团本部的不需要审批
+            //校验选中的档案是否已经有申请单据
+            QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                    .addGroup(new String[]{PerfRankMgmtConstant.NCKD_PERFRANKUNLOCKENTRY, PerfRankMgmtConstant.NCKD_PERFRANKMGMT},
+                            FormConstant.NAME_KEY);
+            QFilter filter = new QFilter(FormConstant.BILL_STATUS_KEY, QCP.not_equals, BillStatus.C.toString())
+                    .and(new QFilter(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKUNLOCKENTRY, PerfRankMgmtConstant.NCKD_PERFRANKMGMT, FormConstant.ID_KEY), QCP.in, ids));
+            DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.PERFRANKUNLOCK_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{filter});
+            if(!query.isEmpty()){
+                List<String> groupNames = new ArrayList<>(query.size());
+                for (DynamicObject dynamicObject : query) {
+                    String groupName = dynamicObject.getString(String.join(".", PerfRankMgmtConstant.NCKD_PERFRANKUNLOCKENTRY, PerfRankMgmtConstant.NCKD_PERFRANKMGMT, FormConstant.NAME_KEY));
+                    groupNames.add(groupName);
+                }
+                throw new ValidationException(StrFormatter.format("排名单元【{}】已存在待解锁的申请,请勿重复申请。", String.join(",", groupNames)));
+            }else{
+                e.cancel = !this.unlockNeedApprovalInteractionMessage();
+            }
+        }
+
+    }
+
+
+    private boolean unlockNeedApprovalInteractionMessage() {
+        //交互式操作提示
+        // 检查是否为回调:避免死循环
+        String confirmResultString = this.getOption().getVariableValue(OperateOptionConst.INTERACTIONCONFIRMRESULT, "");
+        InteractionConfirmResult confirmResult = InteractionConfirmResult.fromJsonString(confirmResultString);
+        if (confirmResult.getResults().containsKey(INTERACTION_SPONORE1)) {
+            // 已是回调,直接返回结果
+            MessageBoxResult result = MessageBoxResult.valueOf(confirmResult.getResults().get(INTERACTION_SPONORE1));
+            return result == MessageBoxResult.Yes;
+        }
+
+        // 首次执行:抛出交互异常
+        InteractionContext interactionContext = new InteractionContext();
+        interactionContext.setSimpleMessage("存在“未生效的”档案");
+        OperateErrorInfo errorInfo = new OperateErrorInfo();
+        errorInfo.setMessage("非总部解锁需要经过总部审批,审批通过之后自动解锁;点击“是”系统自动发起解锁审批流程");
+        errorInfo.setLevel(ErrorLevel.Warning);
+        interactionContext.addOperateInfo(errorInfo);
+        throw new KDInteractionException(INTERACTION_SPONORE1, interactionContext);
+    }
+
+}

+ 8 - 3
code/hr/nckd-jxccl-hr/src/main/java/nckd/jxccl/hr/psms/plugin/operate/performance/validate/PerfRankMgmtSaveValidate.java

@@ -39,7 +39,7 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
         QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
                 .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY}, PerfRankMgmtConstant.NCKD_RATIO)
                 .addGroup(new String[]{FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT}, FormConstant.NUMBER_KEY);
-        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{QFilterCommonHelper.getEnableFilter()});
+        DynamicObjectCollection query = QueryServiceHelper.query(PerfRankMgmtConstant.RANKRATIOCONF_ENTITYID, queryFieldBuilder.buildSelect(), null);
 
         query.forEach(dynamicObject -> {
             String key = dynamicObject.getString(String.join(".",FormConstant.NCKD_ENTRYENTITY, PositionStructureConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
@@ -57,8 +57,11 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
                 // 获取明细数据并验证是否为空
                 DynamicObjectCollection entries = data.getDynamicObjectCollection(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
                 if (entries.isEmpty()) {
-                    this.addMessage(rowDataEntity, "请添加排名名单");
-                    continue;
+                    String operateKey = this.getOperateKey();
+                    if(!operateKey.equals(FormConstant.SAVE_OP)) {
+                        this.addMessage(rowDataEntity, "请添加排名名单");
+                        continue;
+                    }
                 }
 
                 // 验证明细数据
@@ -167,6 +170,8 @@ public class PerfRankMgmtSaveValidate extends AbstractValidator {
             validateEntry(entry, rowDataEntity, i + 1, personIds, context);
         }
 
+        //根据年度校验人员只能在同一个组
+
         return context;
     }