Explorar el Código

feat(performance): 增强绩效管理归档功能并优化校验逻辑

- 在年度绩效归档记录中新增年份字段(NCKD_YEAR)
- 优化考核周期删除与保存操作,增加对已归档数据的校验逻辑
- 完善归档/撤销归档操作插件,支持按年份维度判断归档状态
- 修复归档状态下不允许修改考核周期及考核结果的控制逻辑
- 增加获取日期年份工具方法,提升代码复用性
- 调整表单可见性控制参数,完善界面展示逻辑
wyc hace 1 semana
padre
commit
51592496bd

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

@@ -478,7 +478,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
     private void importResultStep() {
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_STEP,1);
         this.getView().setVisible(false, FormConstant.NUMBER_KEY, PerfRankMgmtConstant.NCKD_GETRANKLIST);
-        this.getView().setVisible(true, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6");
+        this.getView().setVisible(true, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6","nckd_advconbaritemap");
         DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
         for (int i = 0; i < entryEntity.size(); i++) {
             this.getView().setEnable(Boolean.FALSE, i, FormConstant.NCKD_PERSON);
@@ -488,7 +488,7 @@ public class PerfRankMgmtFormPlugin extends AbstractFormPlugin implements Wizard
     private void generatePersonList() {
         this.getModel().setValue(PerfRankMgmtConstant.NCKD_STEP,0);
         this.getView().setVisible(true, FormConstant.NUMBER_KEY, PerfRankMgmtConstant.NCKD_GETRANKLIST);
-        this.getView().setVisible(false, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6");
+        this.getView().setVisible(false, PerfRankMgmtConstant.NCKD_TOPRANKS, PerfRankMgmtConstant.NCKD_ALLOWANCERANKS, PerfRankMgmtConstant.NCKD_FAILS, PerfRankMgmtConstant.NCKD_BASICS, PerfRankMgmtConstant.NCKD_EXCELLENTS, "nckd_advconbaritemap6","nckd_advconbaritemap");
         DynamicObjectCollection entryEntity = this.getModel().getEntryEntity(PerfRankMgmtConstant.NCKD_PERFRANKMGMTENTRY);
         for (int i = 0; i < entryEntity.size(); i++) {
             this.getView().setEnable(Boolean.TRUE, i, FormConstant.NCKD_PERSON);

+ 1 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/common/PerfManagerFormConstant.java

@@ -83,5 +83,6 @@ public class PerfManagerFormConstant extends FormConstant {
     /*-------------------------------------- 年度绩效归档记录 begin --------------------------------------*/
     /** 年度绩效归档记录-实体标识 */
     public static final String POSTWAGEUNITLIST_ENTITYID = "nckd_postwageunitlist";
+    public static final String NCKD_YEAR = "nckd_year";
     /*-------------------------------------- 年度绩效归档记录 end --------------------------------------*/
 }

+ 6 - 11
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/form/result/AnnualPerfDetailFormPlugin.java

@@ -2,38 +2,29 @@ package nckd.jxccl.opmc.pm.plugin.form.result;
 
 import kd.bos.dataentity.OperateOption;
 import kd.bos.dataentity.entity.DynamicObject;
-import kd.bos.entity.datamodel.ListSelectedRowCollection;
+import kd.bos.dataentity.utils.StringUtils;
+import kd.bos.entity.operate.OperateOptionConst;
 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.report.AbstractReportColumn;
 import kd.bos.entity.validate.ErrorLevel;
-import kd.bos.form.CloseCallBack;
 import kd.bos.form.FormShowParameter;
 import kd.bos.form.MessageBoxOptions;
 import kd.bos.form.ShowType;
-import kd.bos.form.StyleCss;
 import kd.bos.form.events.AfterDoOperationEventArgs;
-import kd.bos.form.events.BeforeDoOperationEventArgs;
 import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.report.ReportList;
-import kd.bos.report.events.SortAndFilterEvent;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
-import kd.bos.servicehelper.operation.SaveServiceHelper;
 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.ConvertUtil;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
-import nckd.jxccl.opmc.pm.common.SalAdjTrackerConstant;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.StringJoiner;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /**
@@ -72,6 +63,7 @@ public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Pl
                     }
                     OperateOption operateOption = OperateOption.create();
                     operateOption.setVariableValue("operateKey", operateKey);
+                    operateOption.setVariableValue(OperateOptionConst.IGNOREWARN, true+"");
                     OperationResult result = OperationServiceHelper.executeOperate(
                             "archiveorunarchive",
                             "nckd_annualperfdetail",
@@ -89,6 +81,9 @@ public class AnnualPerfDetailFormPlugin extends AbstractFormPlugin implements Pl
                                 warningError.add(operateErrorInfo.getMessage());
                             }
                         }
+                        if(StringUtils.isNotEmpty(result.getMessage())){
+                            error.add(result.getMessage());
+                        }
                         if(error.length() > 0){
                             error.add(warningError.toString());
                             throw new ValidationException(error.toString());

+ 61 - 0
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerDeleteOpPlugin.java

@@ -1,6 +1,7 @@
 package nckd.jxccl.opmc.pm.plugin.operate.cycle;
 
 import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
 import kd.bos.entity.ExtendedDataEntity;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
 import kd.bos.entity.plugin.AddValidatorsEventArgs;
@@ -9,14 +10,24 @@ import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
 import kd.bos.entity.validate.AbstractValidator;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
 import kd.sdk.plugin.Plugin;
 import nckd.jxccl.base.common.constant.FormConstant;
+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.opmc.pm.common.PerfManagerFormConstant;
 import nckd.jxccl.opmc.pm.helper.PerfManagerHelper;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
 * 考核周期删除OP
@@ -29,6 +40,9 @@ public class PerfManagerDeleteOpPlugin extends AbstractOperationServicePlugIn im
 
     @Override
     public void onPreparePropertys(PreparePropertysEventArgs e) {
+        e.getFieldKeys().add(PerfManagerFormConstant.NCKD_BEGINYEAR);
+        e.getFieldKeys().add(PerfManagerFormConstant.NCKD_ENDYEAR);
+        e.getFieldKeys().add(FormConstant.NCKD_PERSON);
         e.getFieldKeys().add(FormConstant.NCKD_PERSON);
         e.getFieldKeys().add(PerfManagerFormConstant.NCKD_THESTATUS);
         e.getFieldKeys().add(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST);
@@ -39,6 +53,29 @@ public class PerfManagerDeleteOpPlugin extends AbstractOperationServicePlugIn im
         e.addValidator(new AbstractValidator() {
             @Override
             public void validate() {
+                List<Long> personIds = new ArrayList<>();
+                for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
+                    DynamicObject data = rowDataEntity.getDataEntity();
+                    long personId = data.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY));
+                    personIds.add(personId);
+                }
+
+                //获取已归档人员,已归档人员不能修改考核结果
+                QueryFieldBuilder perfArchiveRecordQueryFieldBuilder = QueryFieldBuilder.create()
+                        .addIdNumberName(FormConstant.NCKD_PERSON)
+                        .add(PerfManagerFormConstant.NCKD_YEAR);
+                QFilter perfArchiveRecordFilter = new QFilter(FormConstant.NCKD_PERSON, QCP.in, personIds);
+                //查找已归档的记录(状态为1)
+                perfArchiveRecordFilter = perfArchiveRecordFilter.and(new QFilter(PerfManagerFormConstant.NCKD_ARCHIVESTATUS, QCP.equals, "1"));
+                DynamicObjectCollection perfArchiveRecordQuery = QueryServiceHelper.query(PerfManagerFormConstant.PERFARCHIVERECORD_ENTITYID, perfArchiveRecordQueryFieldBuilder.buildSelect(), new QFilter[]{perfArchiveRecordFilter});
+                // 创建已归档人员ID和年度的映射集合
+                Map<Long, List<Date>> archivedPersonYearPairs = perfArchiveRecordQuery.stream()
+                        .collect(Collectors.groupingBy(
+                                obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
+                                Collectors.mapping(obj -> obj.getDate(PerfManagerFormConstant.NCKD_YEAR), Collectors.toList())
+                        ));
+
+
                 for (ExtendedDataEntity rowDataEntity : getDataEntities()) {
                     DynamicObject data = rowDataEntity.getDataEntity();
                     boolean isCurrentNewest = data.getBoolean(PerfManagerFormConstant.NCKD_ISCURRENTNEWEST);
@@ -49,6 +86,30 @@ public class PerfManagerDeleteOpPlugin extends AbstractOperationServicePlugIn im
                     if(!"1".equalsIgnoreCase(theStatus)){
                         addMessage(rowDataEntity, StrFormatter.format("考核周期状态【{}】,不能删除","2".equals(theStatus) ? "已锁定":"已结束"));
                     }
+
+
+                    //校验已归档不能删除
+                    LocalDateTime beginYear = ConvertUtil.toLocalDateTime(data.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                    LocalDateTime endYear = ConvertUtil.toLocalDateTime(data.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                    DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
+                    long personId = person.getLong(FormConstant.ID_KEY);
+                    String personName = person.getString(FormConstant.NAME_KEY);
+                    List<Date> dates = archivedPersonYearPairs.get(personId);
+                    // 获取dates中年份最大的日期
+                    Date maxDate = dates.stream()
+                            .filter(Objects::nonNull)
+                            .max(Comparator.comparing(date -> DateUtil.toLocalDateTime(date).getYear()))
+                            .orElse(null);
+                    if (maxDate != null) {
+                        LocalDateTime maxLocalDate = DateUtil.toLocalDateTime(maxDate);
+                        if (maxLocalDate.getYear() >= beginYear.getYear() || maxLocalDate.getYear() >= endYear.getYear()) {
+                            addFatalErrorMessage(rowDataEntity,
+                                    StrFormatter.format("人员【{}】考核周期已归档,不能修改删除【{}】年及之前的考核周期。",
+                                            personName,
+                                            maxLocalDate.getYear()));
+                        }
+
+                    }
                 }
             }
         });

+ 105 - 14
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/cycle/PerfManagerSaveOpPlugin.java

@@ -32,10 +32,12 @@ import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 import nckd.jxccl.opmc.pm.helper.PerfManagerHelper;
 import org.apache.commons.lang3.StringUtils;
 
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -161,29 +163,36 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
                 }
 
                 //获取已归档人员,已归档人员不能修改考核结果
-                QueryFieldBuilder perfArchiveRecordQueryFieldBuilder = QueryFieldBuilder.create().addIdNumberName(FormConstant.NCKD_PERSON);
+                QueryFieldBuilder perfArchiveRecordQueryFieldBuilder = QueryFieldBuilder.create()
+                        .addIdNumberName(FormConstant.NCKD_PERSON)
+                        .add(PerfManagerFormConstant.NCKD_YEAR);
                 QFilter perfArchiveRecordFilter = new QFilter(FormConstant.NCKD_PERSON, QCP.in, personBeginYearMap.keySet());
                 //查找已归档的记录(状态为1)
                 perfArchiveRecordFilter = perfArchiveRecordFilter.and(new QFilter(PerfManagerFormConstant.NCKD_ARCHIVESTATUS, QCP.equals, "1"));
                 DynamicObjectCollection perfArchiveRecordQuery = QueryServiceHelper.query(PerfManagerFormConstant.PERFARCHIVERECORD_ENTITYID, perfArchiveRecordQueryFieldBuilder.buildSelect(), new QFilter[]{perfArchiveRecordFilter});
-                // 创建已归档人员ID集合
-                Set<Long> archivedPersonIds = perfArchiveRecordQuery.stream()
-                        .map(obj -> obj.getLong(String.join( ".",FormConstant.NCKD_PERSON,FormConstant.ID_KEY)))
-                        .collect(Collectors.toSet());
+                // 创建已归档人员ID和年度的映射集合
+                Map<Long, List<Date>> archivedPersonYearPairs = perfArchiveRecordQuery.stream()
+                        .collect(Collectors.groupingBy(
+                                obj -> obj.getLong(String.join(".", FormConstant.NCKD_PERSON, FormConstant.ID_KEY)),
+                                Collectors.mapping(obj -> obj.getDate(PerfManagerFormConstant.NCKD_YEAR), Collectors.toList())
+                        ));
                 //加载数据库中考核结果(用于后续判断是否变更)
-                Map<Long,String> entryResultMap = new HashMap<>();
-                if(!archivedPersonIds.isEmpty() && !entryIds.isEmpty()){
+                Map<Long,List<DynamicObject>> entryResultMap = new HashMap<>();
+                if(!archivedPersonYearPairs.isEmpty()){
                     QueryFieldBuilder perfManagerEntryQueryFieldBuilder = QueryFieldBuilder.create()
+                            .add(FormConstant.ID_KEY)
+                            .add(PerfManagerFormConstant.NCKD_BEGINYEAR)
+                            .add(PerfManagerFormConstant.NCKD_ENDYEAR)
                             .add(PerfManagerFormConstant.NCKD_SALARYADJUSTGENFLAG)
                             .add(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,FormConstant.ID_KEY)
+                            .add(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALYEAR)
                             .addIdNumberName(PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,PerfManagerFormConstant.NCKD_APPRAISALRESULT);
-                    QFilter perfManagerEntryFilter = new QFilter(String.join( ".",PerfManagerFormConstant.NCKD_PERFMANAGERENTRY,FormConstant.ID_KEY), QCP.in, entryIds);
+                    QFilter perfManagerEntryFilter = new QFilter(FormConstant.ID_KEY, QCP.in, ids);
                     DynamicObjectCollection query = QueryServiceHelper.query(PerfManagerFormConstant.PERFMANAGER_ENTITYID, perfManagerEntryQueryFieldBuilder.buildSelect(), new QFilter[]{perfManagerEntryFilter});
                     entryResultMap = query.stream()
-                            .collect(Collectors.toMap(
-                                    obj -> obj.getLong(String.join(".", PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, FormConstant.ID_KEY)),
-                                    obj -> obj.getString(String.join(".", PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY))
-                            ));
+                            .collect(Collectors.groupingBy(
+                                    obj -> obj.getLong(FormConstant.ID_KEY))
+                            );
 
                 }
                 Map<Long, DynamicObject> dbMap = new HashMap<>();
@@ -308,7 +317,9 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
                         //如果是更新则需要校验是否更改已归档的考核结果
                         if(isUpdate) {
-                            if(!entryIds.isEmpty() && !entryResultMap.isEmpty()) {
+
+
+                           /* if(!entryIds.isEmpty() && !entryResultMap.isEmpty()) {
                                 for (DynamicObject entry : entrys) {
                                     long entryId = entry.getLong(FormConstant.ID_KEY);
                                     if (entryId > 0) {
@@ -326,7 +337,7 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
                                         }
                                     }
                                 }
-                            }
+                            }*/
                             if(!dbMap.isEmpty()){
                                 DynamicObject dbPerManager = dbMap.get(id);
                                 boolean salaryAdjustGenFlag = dbPerManager.getBoolean(PerfManagerFormConstant.NCKD_SALARYADJUSTGENFLAG);
@@ -339,6 +350,69 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
 
                     }
 
+                    if(isUpdate){
+                        //如果已归档则不能修改考核周期和考核结果
+                        List<DynamicObject> dbPefList = entryResultMap.get(id);
+                        if(dbPefList != null) {
+                            boolean isArchived = false;
+                            for (DynamicObject dbPef : dbPefList) {
+                                LocalDateTime dbBeginYear = DateUtil.toLocalDateTime(dbPef.getDate(PerfManagerFormConstant.NCKD_BEGINYEAR));
+                                LocalDateTime dbEndDate = DateUtil.toLocalDateTime(dbPef.getDate(PerfManagerFormConstant.NCKD_ENDYEAR));
+                                List<Date> dates = archivedPersonYearPairs.get(personId);
+                                // 获取dates中年份最大的日期
+                                Date maxDate = dates.stream()
+                                        .filter(Objects::nonNull)
+                                        .max(Comparator.comparing(date -> DateUtil.toLocalDateTime(date).getYear()))
+                                        .orElse(null);
+                                if (maxDate != null) {
+                                    LocalDateTime maxLocalDate = DateUtil.toLocalDateTime(maxDate);
+                                    if (maxLocalDate.getYear() >= dbBeginYear.getYear() || maxLocalDate.getYear() >= dbEndDate.getYear()) {
+                                        //判断本次是否有变更,如果有变更返回错误
+                                        if(!isArchived) {
+                                            if (dbBeginYear.getYear() != beginYear.getYear() || dbEndDate.getYear() != endYear.getYear()) {
+                                                addFatalErrorMessage(rowDataEntity,
+                                                        StrFormatter.format("人员【{}】考核周期已归档,不能修改【{}】年及之前的考核周期范围。",
+                                                                personName,
+                                                                maxLocalDate.getYear()));
+                                            }
+                                            isArchived = true;
+                                        }
+                                    }
+
+                                    Date appraisalYear = dbPef.getDate(String.join(".", PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALYEAR));
+                                    if (appraisalYear != null) {
+                                        LocalDateTime appraisalYearLocalDate = DateUtil.toLocalDateTime(appraisalYear);
+                                        String dbAppraisalResultNumber = dbPef.getString(String.join(".", PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NUMBER_KEY));
+                                        String dbAppraisalResultName = dbPef.getString(String.join(".", PerfManagerFormConstant.NCKD_PERFMANAGERENTRY, PerfManagerFormConstant.NCKD_APPRAISALRESULT, FormConstant.NAME_KEY));
+                                        if(entrys != null && !entrys.isEmpty()) {
+                                            for (DynamicObject entry : entrys) {
+                                                DynamicObject appraisalResult = entry.getDynamicObject(PerfManagerFormConstant.NCKD_APPRAISALRESULT);
+                                                String appraisalResultNumber = appraisalResult != null ? appraisalResult.getString(FormConstant.NUMBER_KEY) : null;
+                                                if (maxLocalDate.getYear() >= appraisalYearLocalDate.getYear()) {
+                                                    if (!Objects.equals(appraisalResultNumber, dbAppraisalResultNumber)) {
+                                                        addFatalErrorMessage(rowDataEntity,
+                                                                StrFormatter.format("人员【{}】考核周期已归档,不能修改【{}】年的考核结果,修改前考核结果【{}】。",
+                                                                        personName,
+                                                                        appraisalYearLocalDate.getYear(),
+                                                                        dbAppraisalResultName));
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }else{
+                                        if(entrys != null && !entrys.isEmpty()) {
+                                            //原来没有考核结果
+                                            addFatalErrorMessage(rowDataEntity,
+                                                    StrFormatter.format("人员【{}】考核周期已归档,不能修改【{}】年及之前的考核结果。",
+                                                            personName,
+                                                            maxLocalDate.getYear()));
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
                     //规则: 如果年度排名管理中已存在某年度的考核结果,则不允许在人员考评管理中修改该年度的考核结果。
                     //规则: 对于已处理的调薪情况,不允许修改或删除相关的年度考核结果。
                 }
@@ -859,4 +933,21 @@ public class PerfManagerSaveOpPlugin extends AbstractOperationServicePlugIn impl
             this.actEndYear = actEndYear;
         }
     }
+
+    /**
+     * 从日期对象中提取年份
+     *
+     * @param date 日期对象
+     * @return 年份
+     */
+    private int getYearFromDate(Object date) {
+        if (date == null) {
+            return 0;
+        }
+        if (date instanceof LocalDate) {
+            return ((LocalDate) date).getYear();
+        }
+        // 如果是其他日期类型,可以添加相应的处理逻辑
+        return 0;
+    }
 }

+ 57 - 14
code/opmc/nckd-jxccl-opmc/src/main/java/nckd/jxccl/opmc/pm/plugin/operate/result/PerfArchiveOrUnArchiveOpPlugin.java

@@ -22,11 +22,13 @@ 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.DateUtil;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.base.common.utils.StrFormatter;
 import nckd.jxccl.base.entity.helper.EntityHelper;
 import nckd.jxccl.opmc.pm.common.PerfManagerFormConstant;
 
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -36,14 +38,31 @@ import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 /**
-* 年度绩效结果明细-归档/撤销归档
-* 实体标识:nckd_annualperfdetail
-* @author W.Y.C
-* @date 2025/11/26 17:15
-* @version 1.0
-*/
+ * 年度绩效结果明细-归档/撤销归档
+ * 实体标识:nckd_annualperfdetail
+ * @author W.Y.C
+ * @date 2025/11/26 17:15
+ * @version 1.0
+ */
 public class PerfArchiveOrUnArchiveOpPlugin extends AbstractOperationServicePlugIn implements Plugin {
 
+    /**
+     * 从日期对象中提取年份
+     *
+     * @param date 日期对象
+     * @return 年份
+     */
+    private int getYearFromDate(Object date) {
+        if (date == null) {
+            return 0;
+        }
+        if (date instanceof LocalDate) {
+            return ((LocalDate) date).getYear();
+        }
+        // 如果是其他日期类型,可以添加相应的处理逻辑
+        return 0;
+    }
+
     @Override
     public void onAddValidators(AddValidatorsEventArgs e) {
         e.addValidator(new AbstractValidator() {
@@ -55,39 +74,58 @@ public class PerfArchiveOrUnArchiveOpPlugin extends AbstractOperationServicePlug
                     DynamicObject data = rowDataEntity.getDataEntity();
                     DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
                     personMap.put(person.getLong(FormConstant.ID_KEY),rowDataEntity.getDataEntityIndex());
+
                 }
 
-                QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create().addIdNumberName(FormConstant.NCKD_PERSON);
+                int year = DateUtil.now().getYear();
+                year = year--;
+                QueryFieldBuilder queryFieldBuilder = QueryFieldBuilder.create()
+                        .addIdNumberName(FormConstant.NCKD_PERSON)
+                        .add(PerfManagerFormConstant.NCKD_YEAR);
                 QFilter qFilter = new QFilter(FormConstant.NCKD_PERSON, QCP.in, personMap.keySet().toArray());
                 //查找已归档的记录(状态为1)
                 qFilter = qFilter.and(new QFilter(PerfManagerFormConstant.NCKD_ARCHIVESTATUS, QCP.equals, "1"));
 
                 DynamicObjectCollection perfArchiveRecordQuery = QueryServiceHelper.query(PerfManagerFormConstant.PERFARCHIVERECORD_ENTITYID, queryFieldBuilder.buildSelect(), new QFilter[]{qFilter});
-                // 创建已归档人员ID集合
-                Set<Long> archivedPersonIds = perfArchiveRecordQuery.stream()
-                        .map(obj -> obj.getLong(String.join( ".",FormConstant.NCKD_PERSON,FormConstant.ID_KEY)))
+                // 创建已归档人员ID和年度的映射集合
+                Set<String> archivedPersonYearPairs = perfArchiveRecordQuery.stream()
+                        .map(obj -> obj.getLong(String.join( ".",FormConstant.NCKD_PERSON,FormConstant.ID_KEY)) + "_" + getYearFromDate(obj.getDate(PerfManagerFormConstant.NCKD_YEAR)))
                         .collect(Collectors.toSet());
                 // 处理归档操作
                 if(PerfManagerFormConstant.ARCHIVE_OP.equalsIgnoreCase(operateKey)){
                     for (DynamicObject dynamicObject : perfArchiveRecordQuery) {
                         String personName = dynamicObject.getString(String.join( ".",FormConstant.NCKD_PERSON,FormConstant.NAME_KEY));
                         long personId = dynamicObject.getLong(String.join( ".",FormConstant.NCKD_PERSON,FormConstant.ID_KEY));
-                        this.addErrorMessage(getDataEntities()[personMap.get(personId)], StrFormatter.format("【{}】已归档,忽略此次处理;", personName));
+                        String pairKey = personId + "_" + year;
+
+                        // 检查当前处理的数据中是否有相同的人员和年度
+                        Integer dataIndex = personMap.get(personId);
+                        if (dataIndex != null) {
+                            DynamicObject data = getDataEntities()[dataIndex].getDataEntity();
+                            String dataPairKey = personId + "_" + year;
+
+                            if (dataPairKey.equals(pairKey)) {
+                                this.addErrorMessage(getDataEntities()[personMap.get(personId)], StrFormatter.format("【{}】【{}】已归档,忽略此条处理;", personName, year));
+                            }
+                        }
                     }
                 }
                 // 处理撤销归档操作
                 else if(PerfManagerFormConstant.UNARCHIVE_OP.equalsIgnoreCase(operateKey)){
                     // 找出在personMap中存在但未归档的人员
                     personMap.entrySet().stream()
-                            .filter(entry -> !archivedPersonIds.contains(entry.getKey()))
+                            .filter(entry -> {
+                                Long personId = entry.getKey();
+                                String dataPairKey = personId+"";
+                                return archivedPersonYearPairs.isEmpty() || archivedPersonYearPairs.stream().noneMatch(s -> s.indexOf(dataPairKey) > -1);
+                            })
                             .forEach(entry -> {
                                 Long personId = entry.getKey();
                                 Integer dataIndex = entry.getValue();
-
                                 DynamicObject data = getDataEntities()[dataIndex].getDataEntity();
                                 DynamicObject person = data.getDynamicObject(FormConstant.NCKD_PERSON);
                                 String personName = person.getString(FormConstant.NAME_KEY);
-                                this.addErrorMessage(getDataEntities()[dataIndex], StrFormatter.format("【{}】未归档无需撤销,忽略此处理;", personName));
+                                this.addErrorMessage(getDataEntities()[dataIndex], StrFormatter.format("【{}】未归档无需撤销,忽略此处理;", personName));
                             });
                 }
             }
@@ -100,6 +138,9 @@ public class PerfArchiveOrUnArchiveOpPlugin extends AbstractOperationServicePlug
         List<DynamicObject> archiveList = new ArrayList<>();
         List<Long> unArchivePersonList = new ArrayList<>();
         StringJoiner personNameList = new StringJoiner(StrFormatter.LINE_SEPARATOR);
+        int year = DateUtil.now().getYear();
+        year = year - 1 ;
+        LocalDate localDate = LocalDate.of(year, 1, 1);
         for (DynamicObject dataEntity : e.getDataEntities()) {
             if(PerfManagerFormConstant.ARCHIVE_OP.equalsIgnoreCase(operateKey)){
                 DynamicObject newPerfArchiveRecord = EntityHelper.newEntity(PerfManagerFormConstant.PERFARCHIVERECORD_ENTITYID);
@@ -107,8 +148,10 @@ public class PerfArchiveOrUnArchiveOpPlugin extends AbstractOperationServicePlug
                 newPerfArchiveRecord.set(PerfManagerFormConstant.NCKD_PERSON, person);
                 newPerfArchiveRecord.set(PerfManagerFormConstant.NCKD_ARCHIVESTATUS, "1");
                 newPerfArchiveRecord.set(FormConstant.CREATOR_KEY, UserServiceHelper.getCurrentUserId());
+                newPerfArchiveRecord.set(PerfManagerFormConstant.NCKD_YEAR, DateUtil.toDate(localDate));
                 archiveList.add(newPerfArchiveRecord);
                 personNameList.add(StrFormatter.format("【{}】归档成功;", person.getString(FormConstant.NAME_KEY)));
+
             }else if(PerfManagerFormConstant.UNARCHIVE_OP.equalsIgnoreCase(operateKey)){
                 DynamicObject person = dataEntity.getDynamicObject(FormConstant.NCKD_PERSON);
                 unArchivePersonList.add(person.getLong(FormConstant.ID_KEY));