Browse Source

refactor(hrmp): 重构岗位申请单服务模块

- 重命名多个服务类从business.service.hr包到business.hr.service.impl包
- 更新PositionBillBeforeBindDataService中的权限获取逻辑,替换旧的OrgPermHelper为PermissionServiceHelper
- 修复AdminOrgDetailConfirmChangeOpPlugin中的变量名拼写错误changtype改为changeType
- 添加HOMS_APP常量用于组织管理应用标识
- 优化PositionBillAfterBindDataService和PositionBillConfirmCallBackService中的分录数据删除逻辑
- 更新PatternUtil的导入路径从kd.hr.homs到nckd.jxccl.hrmp.hbpm.common.hr
- 修正PositionBillAfterBindDataService中分录转换键值映射的获取逻辑
- 添加PositionBillSaveHelper类用于岗位申请单保存服务的完整实现
- 在PositionBillConstant中添加新的常量定义包括NCKD_POSITIONBOID_KEY、NCKD_ENTRYENTITYUNDERLINE_KEY等
- 更新PositionBillServiceHelper中获取岗位基础信息的方法实现
- 优化PositionBillServiceHelper中获取单据岗位名称字符串的逻辑
- 移除过时的HAOS_ORGTREELISTF7_ENTITY常量定义
- 修正多处注释中"转换"为"要转换"的表述
- 添加PermissionStatus导入用于权限状态管理
jtd 1 tuần trước cách đây
mục cha
commit
458ec40004
27 tập tin đã thay đổi với 858 bổ sung138 xóa
  1. 5 1
      code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java
  2. 4 4
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/haos/plugin/operate/adminorg/AdminOrgDetailConfirmChangeOpPlugin.java
  3. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PosBillEntryFastChgHelper.java
  4. 5 5
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillAfterBindDataService.java
  5. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBaseService.java
  6. 4 4
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBeforeBindDataService.java
  7. 3 3
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillClosedCallBackService.java
  8. 3 3
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillConfirmCallBackService.java
  9. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillPropertyChangedService.java
  10. 214 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillSaveHelper.java
  11. 51 3
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillServiceHelper.java
  12. 1 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/utils/PositionBillUtil.java
  13. 0 45
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillSaveHelper.java
  14. 12 10
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionBillConstant.java
  15. 29 9
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionChangeTypeEnum.java
  16. 9 5
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryAddFormPlugin.java
  17. 10 18
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryUpdatePlugin.java
  18. 3 3
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillEntryFormPlugin.java
  19. 10 12
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillFormPlugin.java
  20. 2 2
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillListPlugin.java
  21. 2 2
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PosBillEntryNewPositionTempSaveOpPlugin.java
  22. 10 1
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillDeleteOpPlugin.java
  23. 53 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillSaveOpPlugin.java
  24. 4 4
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/AbsBillPositionSaveValidator.java
  25. 46 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillDeleteStatusValidator.java
  26. 175 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillSaveAndSubmitValidator.java
  27. 200 0
      code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillSaveValidator.java

+ 5 - 1
code/base/nckd-jxccl-base-common/src/main/java/nckd/jxccl/base/common/constant/FormConstant.java

@@ -15,8 +15,10 @@ public class FormConstant {
     public static final String DATA_MIGRATION = "dataMigration";
 
     //====================================== 标品应用ID ======================================
-    /** 消息中心应用ID */
+    /** 消息中心 */
     public static final String WFTASK_APP = "wftask";
+    /** 组织管理 */
+    public static final String HOMS_APP = "homs";
 
     //====================================== 标品实体标识(需要小写) ======================================
     /**学历-实体标识*/
@@ -209,6 +211,8 @@ public class FormConstant {
     public static final String REPORTLISTAP = "reportlistap";
     /** 多选基础资料*/
     public static final String BASEDATAID_KEY = "fbasedataid";
+    /** 多选基础资料*/
+    public static final String FBASEDATAID_KEY = "fbasedataid";
     /** 多选基础资料的ID*/
     public static final String FBASEDATAID_ID_KEY = "fbasedataid_id";
     /**工具栏*/

+ 4 - 4
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/haos/plugin/operate/adminorg/AdminOrgDetailConfirmChangeOpPlugin.java

@@ -53,8 +53,8 @@ public class AdminOrgDetailConfirmChangeOpPlugin extends AbstractOperationServic
             return;
         }
 
-        String changtype = data.getDynamicObject(AdminOrgConstant.CHANGETYPE).getString(AdminOrgConstant.NUMBER_KEY);
-        if (CHANGETYPE_PARENT.equals(changtype)) {
+        String changeType = data.getDynamicObject(AdminOrgConstant.CHANGETYPE).getString(AdminOrgConstant.NUMBER_KEY);
+        if (CHANGETYPE_PARENT.equals(changeType)) {
             beginOpParentHandler(dataEntities);
         }
     }
@@ -83,8 +83,8 @@ public class AdminOrgDetailConfirmChangeOpPlugin extends AbstractOperationServic
             return;
         }
 
-        String changtype = data.getDynamicObject(AdminOrgConstant.CHANGETYPE).getString(AdminOrgConstant.NUMBER_KEY);
-        switch (changtype) {
+        String changeType = data.getDynamicObject(AdminOrgConstant.CHANGETYPE).getString(AdminOrgConstant.NUMBER_KEY);
+        switch (changeType) {
             case CHANGETYPE_PARENT:
                 endOpParentHandler();
                 break;

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PosBillEntryFastChgHelper.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PosBillEntryFastChgHelper.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.dataentity.entity.DynamicObjectCollection;

+ 5 - 5
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillAfterBindDataService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillAfterBindDataService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import com.google.common.collect.Maps;
 import kd.bos.dataentity.entity.DynamicObject;
@@ -19,7 +19,7 @@ import kd.bos.orm.util.CollectionUtils;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRArrayUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import kd.hr.homs.business.utils.PatternUtil;
+import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
@@ -105,7 +105,7 @@ public class PositionBillAfterBindDataService extends PositionBillBaseService {
         Object importPageClose = pageCache.get(PositionBillConstant.PC_CACHE_KEY_IMPORT_START_CLOSE);
         Object entryImport = pageCache.get(PositionBillConstant.PC_CACHE_KEY_IMPORT_DATA_FLAG);
         if (!Boolean.TRUE.toString().equals(importPageClose) || Boolean.TRUE.toString().equals(entryImport)) {
-            Arrays.stream(PositionChangeTypeEnum.values()).forEach((changTypeEnum) -> getModel().deleteEntryData(String.join("_", PositionBillConstant.NCKD_ENTRYENTITY, changTypeEnum.getTag())));
+            Arrays.stream(PositionChangeTypeEnum.values()).forEach((changeTypeEnum) -> getModel().deleteEntryData(changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY)));
         }
     }
 
@@ -127,7 +127,7 @@ public class PositionBillAfterBindDataService extends PositionBillBaseService {
                 changeTypeForBillDynMap = Arrays.stream(batchOrgEntryEntityDynArr).collect(Collectors.groupingBy((dyn) -> dyn.getString(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))));
             }
 
-            // 获取分录要转换的键值
+            // 获取分录要转换的键值
             Map<String, String> posBillEntryTransKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
             for(Map.Entry<String, List<DynamicObject>> stringListEntry : changeTypeForBillDynMap.entrySet()) {
                 String changeTypeNumber = stringListEntry.getKey();
@@ -165,7 +165,7 @@ public class PositionBillAfterBindDataService extends PositionBillBaseService {
                     String propName = property.getName();
                     if (PatternUtil.isExProperty(propName) && propName.endsWith("_"+suffix)) {
                         String propNameSub = PositionBillServiceHelper.getNoLineSuffixProp(propName, "_"+suffix);
-                        tableValueSetter.set(propName, getValue(entryEntityDynList.get(i).get(posBillEntryTransKeyMap.getOrDefault(propName, propNameSub))), i);
+                        tableValueSetter.set(propName, getValue(entryEntityDynList.get(i).get(posBillEntryTransKeyMap.getOrDefault(propNameSub, propNameSub))), i);
                     }
                 }
                 tableValueSetter.set(PositionBillConstant.ID_KEY, entryEntityDynList.get(i).get(PositionBillConstant.ID_KEY), i);

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillBaseService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBaseService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import kd.bos.entity.datamodel.IDataModel;
 import kd.bos.form.IFormView;

+ 4 - 4
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillBeforeBindDataService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillBeforeBindDataService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.context.RequestContext;
@@ -7,10 +7,10 @@ import kd.bos.form.container.Container;
 import kd.bos.logging.Log;
 import kd.bos.logging.LogFactory;
 import kd.bos.permission.api.HasPermOrgResult;
+import kd.bos.servicehelper.model.PermissionStatus;
 import kd.bos.servicehelper.org.OrgUnitServiceHelper;
 import kd.bos.servicehelper.org.OrgViewType;
 import kd.bos.servicehelper.permission.PermissionServiceHelper;
-import kd.hr.haos.business.util.OrgPermHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
 import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
@@ -63,7 +63,7 @@ public class PositionBillBeforeBindDataService extends PositionBillBaseService {
             if (businessUnit != null) {
                 getModel().setValue(PositionBillConstant.ORG_KEY, businessUnit);
             } else if (!Boolean.TRUE.toString().equals(getView().getFormShowParameter().getCustomParam(PositionBillConstant.CP_ISFROMBILLCLICK))) {
-                HasPermOrgResult hrPermOrg = OrgPermHelper.getHRPermOrg(false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {
@@ -78,7 +78,7 @@ public class PositionBillBeforeBindDataService extends PositionBillBaseService {
             getView().cacheFormShowParameter();
         } else if (Boolean.TRUE.toString().equals(getView().getPageCache().get(PositionBillConstant.PC_ISFROMIMPORT)) || getView().getParentView() != null && (PositionBillConstant.NCKD_POSTMGR_APPHOME_ENTITY.equals(getView().getParentView().getEntityId()) || getView().getParentView().getEntityId().startsWith("wf_"))) {
             if (getView().getParentView() != null && PositionBillConstant.NCKD_POSTMGR_APPHOME_ENTITY.equals(getView().getParentView().getEntityId()) && getView().getPageCache().get(PositionBillConstant.PC_ISFROMHOMEPAGE) == null) {
-                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, getView().getFormShowParameter().getCheckRightAppId(), PositionBillConstant.HBPM_POSITIONHR, "47150e89000000ac", false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {

+ 3 - 3
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillClosedCallBackService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillClosedCallBackService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import com.google.common.collect.Lists;
 import kd.bos.dataentity.entity.DynamicObject;
@@ -16,7 +16,7 @@ import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRDyObjectPropUtil;
-import kd.hr.homs.business.utils.PatternUtil;
+import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
 
@@ -68,7 +68,7 @@ public class PositionBillClosedCallBackService extends PositionBillBaseService {
         Set<String> toSetValueEntryPropList = new HashSet<String>();
 
         String lineSuffix = "_"+PositionBillConstant.ADD_TAG;
-        // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
+        // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
         Map<String, String> posBillEntryTransKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
         for(IDataEntityProperty iDataEntityProperty : entryEntityInfo) {
             String entryPropName = iDataEntityProperty.getName();

+ 3 - 3
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillConfirmCallBackService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillConfirmCallBackService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -84,8 +84,8 @@ public class PositionBillConfirmCallBackService extends PositionBillBaseService
                     return;
                 }
 
-                for (PositionChangeTypeEnum positionChangeTypeEnum : PositionChangeTypeEnum.values()) {
-                    getModel().deleteEntryData(String.join("_", PositionBillConstant.NCKD_ENTRYENTITY, positionChangeTypeEnum.getTag()));
+                for (PositionChangeTypeEnum changeTypeEnum : PositionChangeTypeEnum.values()) {
+                    getModel().deleteEntryData(changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY));
                 }
 
                 long billId = getModel().getDataEntity().getLong(PositionBillConstant.ID_KEY);

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillPropertyChangedService.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillPropertyChangedService.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import kd.bos.common.enums.EnableEnum;
 import kd.bos.dataentity.entity.DynamicObject;

+ 214 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillSaveHelper.java

@@ -0,0 +1,214 @@
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.dataentity.metadata.clr.DataEntityPropertyCollection;
+import kd.bos.entity.property.BigIntProp;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRDynamicObjectUtils;
+import kd.hr.hbp.common.util.HRStringUtils;
+import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+;
+
+/**
+ * 岗位申请单保存服务
+ * @from: kd.hr.homs.business.domain.orgbatch.service.impl.OrgBatchBillSaveHelper
+ * @author: jtd
+ * @date: 2025/12/27 15:04
+ */
+public class PositionBillSaveHelper {
+    private static PositionBillSaveHelper INSTANCE = new PositionBillSaveHelper();
+    private static final Log logger = LogFactory.getLog(PositionBillSaveHelper.class);
+    private static final HRBaseServiceHelper positionBillEntryEntityHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+
+    public static PositionBillSaveHelper getInstance() {
+        return INSTANCE;
+    }
+
+    public void positionBillSave(DynamicObject dataEntity) {
+        long startTime = System.currentTimeMillis();
+        HRBaseServiceHelper positionBillEntryEntityHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+        Long billId = dataEntity.getLong(PositionBillConstant.ID_KEY);
+        QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, billId);
+        DynamicObject[] positionBillEntryEntityDyns = positionBillEntryEntityHelper.loadDynamicObjectArray(new QFilter[]{billIdFilter});
+        Set<Long> positionIdSetFromDB = Arrays.stream(positionBillEntryEntityDyns).map((dyn) -> dyn.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY))).collect(Collectors.toSet());
+        Map<Long, DynamicObject> positionId2DynFromDBMap = Arrays.stream(positionBillEntryEntityDyns).collect(Collectors.toMap((dyn) -> dyn.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY)), (dyn) -> dyn, (k, v) -> k));
+        DynamicObjectCollection allEntryEntityDyn = getEntryEntityData(dataEntity);
+        if (allEntryEntityDyn.size() != 0) {
+            List<Long> positionIdList = getAllEntryPositionId(allEntryEntityDyn);
+            Map<Long, DynamicObject> positionId2BasicInfoDynMap = PositionBillServiceHelper.getPositionId2BasicInfoDynMapByIds(positionIdList);
+            List<DynamicObject> needSaveEntryDynList = Lists.newArrayListWithExpectedSize(allEntryEntityDyn.size());
+
+            for(DynamicObject entryEntityDyn : allEntryEntityDyn) {
+                String suffix = getSuffixFromEntryEntityDyn(entryEntityDyn);
+                Long positionId = getPositionId(entryEntityDyn, suffix);
+                DynamicObject positionBillEntryDyn;
+                if (positionIdSetFromDB.contains(positionId)) {
+                    positionBillEntryDyn = positionId2DynFromDBMap.get(positionId);
+                } else {
+                    positionBillEntryDyn = positionBillEntryEntityHelper.generateEmptyDynamicObject();
+                    positionBillEntryDyn.set(PositionBillConstant.NCKD_BILLID, dataEntity.getLong(PositionBillConstant.ID_KEY));
+                    DynamicObject basicInfoDyn = positionId2BasicInfoDynMap.get(positionId);
+                    assembleSaveEntryDyn(positionBillEntryDyn, basicInfoDyn);
+                }
+
+                boolean neeSave = coverdEntryValue(positionBillEntryDyn, entryEntityDyn, suffix);
+                if (neeSave || !positionIdSetFromDB.contains(positionId)) {
+                    needSaveEntryDynList.add(positionBillEntryDyn);
+                }
+            }
+
+            if (needSaveEntryDynList.isEmpty()) {
+                logger.info("needSaveEntryDynList is empty");
+            }
+
+            resetSequence(needSaveEntryDynList);
+            setPositionBoId(needSaveEntryDynList, positionId2BasicInfoDynMap);
+            if (needSaveEntryDynList.isEmpty()) {
+                logger.info("needSaveEntryDynList is empty");
+            } else {
+                positionBillEntryEntityHelper.save(needSaveEntryDynList.toArray(new DynamicObject[0]));
+                billIdFilter.and(PositionBillConstant.CREATOR_KEY, QCP.equals, 0);
+                positionBillEntryEntityHelper.deleteByFilter(billIdFilter.toArray());
+                logger.info(String.format(Locale.ROOT, "positionBillSave() time cost is: %s", System.currentTimeMillis() - startTime));
+            }
+        }
+    }
+
+    private void resetSequence(List<DynamicObject> needSaveEntryDynList) {
+        Map<Long, Integer> map = Maps.newHashMapWithExpectedSize(3);
+        needSaveEntryDynList.stream().forEach((dyn) -> {
+            long changeTypeId = dyn.getLong(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.ID_KEY));
+            if (changeTypeId == 0L) {
+                changeTypeId = dyn.getLong(PositionBillConstant.NCKD_CHANGETYPE);
+            }
+
+            if (Objects.isNull(map.get(changeTypeId))) {
+                int sequence = 0;
+                QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, dyn.get(PositionBillConstant.NCKD_BILLID));
+                QFilter changeTypeFilter = new QFilter(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.ID_KEY), QCP.equals, changeTypeId);
+                DynamicObject[] dys = positionBillEntryEntityHelper.query(PositionBillConstant.NCKD_SEQUENCE, new QFilter[]{billIdFilter, changeTypeFilter}, String.format("%s DESC", PositionBillConstant.NCKD_SEQUENCE), 1);
+                if (dys != null && dys.length > 0) {
+                    sequence = dys[0].getInt(PositionBillConstant.NCKD_SEQUENCE);
+                }
+
+                ++sequence;
+                dyn.set(PositionBillConstant.NCKD_SEQUENCE, sequence);
+                map.put(changeTypeId, sequence);
+            } else {
+                Integer sequence = map.get(changeTypeId);
+                sequence = sequence + 1;
+                dyn.set(PositionBillConstant.NCKD_SEQUENCE, sequence);
+                map.put(changeTypeId, sequence);
+            }
+
+        });
+    }
+
+    public Long getPositionId(DynamicObject entryEntityDynFromPage, String suffix) {
+        String idFieldName = String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY);
+        if (HRStringUtils.equals(entryEntityDynFromPage.getString(spliceTwoStringByUnderLine(suffix, String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))), PositionChangeTypeEnum.ADD.getNumber())) {
+            idFieldName = PositionBillConstant.NCKD_POSITION_KEY;
+        }
+
+        String positionFiledName = spliceTwoStringByUnderLine(suffix, idFieldName);
+        return entryEntityDynFromPage.getLong(positionFiledName);
+    }
+
+    public String getSuffixFromEntryEntityDyn(DynamicObject entryEntityDynFromPage) {
+        String entryEntityName = entryEntityDynFromPage.getDynamicObjectType().getName();
+        int lastUnderLineIndex = entryEntityName.lastIndexOf("_");
+        return entryEntityName.substring(lastUnderLineIndex + 1);
+    }
+
+    public void assembleSaveEntryDyn(DynamicObject positionBillEntryDyn, DynamicObject basicInfoDyn) {
+        // 获取岗位要转换的键值
+        Map<String, String> positionReverseTransKeyMap = PositionBillServiceHelper.getPositionTransKeyMap().entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, (oldValue, newValue) -> newValue));
+        HRDynamicObjectUtils.copy(basicInfoDyn, positionBillEntryDyn, positionReverseTransKeyMap);
+
+        // 原上级岗位特殊处理
+        positionBillEntryDyn.set(PositionBillConstant.NCKD_ORIPARENT_KEY, positionBillEntryDyn.get(PositionBillConstant.NCKD_PARENT));
+    }
+
+    public DynamicObjectCollection getEntryEntityData(DynamicObject dataEntity) {
+        List<String> entryExcludeAddName = Arrays.stream(PositionChangeTypeEnum.values()).filter((changeType) -> changeType != PositionChangeTypeEnum.ADD).map((changeType) -> changeType.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY)).collect(Collectors.toList());
+        DynamicObjectCollection dynColl = new DynamicObjectCollection();
+        entryExcludeAddName.forEach((entryName) -> dynColl.addAll(dataEntity.getDynamicObjectCollection(entryName)));
+        return dynColl;
+    }
+
+    public List<Long> getAllEntryPositionId(DynamicObjectCollection allEntryEntityDyns) {
+        List<Long> positionIdList = Lists.newArrayListWithExpectedSize(allEntryEntityDyns.size());
+        allEntryEntityDyns.forEach((dyn) -> {
+            String suffix = getSuffixFromEntryEntityDyn(dyn);
+            Long positionId = getPositionId(dyn, suffix);
+            positionIdList.add(positionId);
+        });
+        return positionIdList;
+    }
+
+    private void setPositionBoId(List<DynamicObject> needSaveEntryDynList, Map<Long, DynamicObject> positionId2BasicInfoDynMap) {
+        needSaveEntryDynList.forEach((entryDyn) -> {
+            long positionId = entryDyn.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY));
+            DynamicObject basicInfoDyn = positionId2BasicInfoDynMap.get(positionId);
+            entryDyn.set(PositionBillConstant.NCKD_POSITIONBOID_KEY, basicInfoDyn.getLong(PositionBillConstant.BOID_KEY));
+        });
+    }
+
+    public boolean coverdEntryValue(DynamicObject positionBillEntryDyn, DynamicObject entryEntity, String suffix) {
+        boolean needSave = false;
+        // 获取分录要转换的键值
+        Map<String, String> posBillEntryTransKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
+        DataEntityPropertyCollection props = positionBillEntryDyn.getDataEntityType().getProperties();
+
+        for(IDataEntityProperty property : entryEntity.getDataEntityType().getProperties()) {
+            String propName = property.getName();
+            // 如果岗位是长整数则不能覆盖从数据库中拿到的对象,否则后续获取岗位对象的时候就会类型转换异常
+            if (property instanceof BigIntProp && HRStringUtils.equals(String.join("_", PositionBillConstant.NCKD_POSITION_KEY, suffix), propName)) {
+                continue;
+            }
+
+            if (PatternUtil.isExProperty(propName) && propName.endsWith("_"+suffix)) {
+                String propNameSub = PositionBillServiceHelper.getNoLineSuffixProp(propName, "_"+suffix);
+                propNameSub = posBillEntryTransKeyMap.getOrDefault(propNameSub, propNameSub);
+                IDataEntityProperty prop = props.get(propNameSub);
+                Object oldVal = positionBillEntryDyn.get(propNameSub);
+                Object newVal = entryEntity.get(propName);
+                if (prop != null && !(oldVal == null && newVal == null)) {
+                    if ((newVal == null && oldVal != null) || (newVal != null && oldVal == null) || !HRDynamicObjectUtils.compareValues(newVal, oldVal)) {
+                        needSave = true;
+                    }
+                }
+
+                positionBillEntryDyn.set(propNameSub, newVal);
+            }
+        }
+        positionBillEntryDyn.set(PositionBillConstant.ID_KEY, entryEntity.get(PositionBillConstant.ID_KEY));
+
+        return needSave;
+    }
+
+    public String spliceTwoStringByUnderLine(String oneString, String otherString) {
+        String[] str = otherString.split("\\.");
+        return str.length > 1 ? str[0] + "_" + oneString + "." + str[1] : otherString + "_" + oneString;
+    }
+
+}

+ 51 - 3
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillServiceHelper.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/service/impl/PositionBillServiceHelper.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.collect.Lists;
@@ -11,6 +11,7 @@ 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.entity.ILocaleString;
 import kd.bos.dataentity.serialization.SerializationUtils;
 import kd.bos.entity.constant.StatusEnum;
 import kd.bos.entity.datamodel.IDataModel;
@@ -28,6 +29,7 @@ import kd.bos.logging.LogFactory;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.bos.servicehelper.model.PermissionStatus;
 import kd.bos.servicehelper.operation.OperationServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRMServiceHelper;
@@ -58,7 +60,6 @@ import java.util.stream.Collectors;
  * @date: 2025-10-31 14:33
  */
 public class PositionBillServiceHelper {
-
     private static final Log log = LogFactory.getLog(PositionBillServiceHelper.class);
 
     public static String getNoLineSuffixProp(String prop, String lineSuffix) {
@@ -174,12 +175,20 @@ public class PositionBillServiceHelper {
 
     public static AuthorizedOrgResult getOrgAuth(IFormView iFormView) {
         Long userId = RequestContext.get().getCurrUserId();
-        String permItemId = "47150e89000000ac";// 查询权限
+        String permItemId = PermissionStatus.View;// 查询权限
         String appId = iFormView.getFormShowParameter().getCheckRightAppId();
         AuthorizedOrgResult permResult = HRMServiceHelper.invokeHRMPService("hrcs", "IHRCSBizDataPermissionService", "getUserAdminOrgsF7", new Object[]{userId, appId, PositionBillConstant.NCKD_POSITIONBILL_ENTITY, permItemId, PositionBillConstant.NCKD_ADMINORGBOID_KEY, Maps.newHashMapWithExpectedSize(0)});
         return permResult;
     }
 
+    public static Map<Long, DynamicObject> getPositionId2BasicInfoDynMapByIds(List<Long> positionIdList) {
+        HRBaseServiceHelper serviceHelper = new HRBaseServiceHelper(PositionBillConstant.HBPM_POSITIONHR);
+        QFilter qFilter = new QFilter(PositionBillConstant.ID_KEY, QCP.in, positionIdList);
+        DynamicObject[] dynamicObjects = serviceHelper.loadDynamicObjectArray(qFilter.toArray());
+        Map<Long, DynamicObject> positionId2DynMap = Arrays.stream(dynamicObjects).collect(Collectors.toMap((dyn) -> dyn.getLong(PositionBillConstant.ID_KEY), (dyn) -> dyn));
+        return positionId2DynMap;
+    }
+
     public static void setParentFilter(IFormView view, DynamicObject positionObject, BeforeF7SelectEvent event) {
         DynamicObject dataEntityParent = view.getModel().getDataEntity(true);
         QFilter isCurrentVersion = new QFilter(PositionBillConstant.IS_CURRENT_VERSION, QCP.equals, EnableEnum.NO.getCode());
@@ -507,6 +516,45 @@ public class PositionBillServiceHelper {
         }
     }
 
+    public static String getThisBillPositionNamesStr(DynamicObject bill) {
+        if (bill == null) {
+            return "";
+        } else {
+            DynamicObjectCollection positionNumberEntryDys = new DynamicObjectCollection();
+            positionNumberEntryDys.addAll(bill.getDynamicObjectCollection(PositionBillConstant.NCKD_ENTRYENTITY_ADD_KEY));
+            positionNumberEntryDys.addAll(bill.getDynamicObjectCollection(PositionBillConstant.NCKD_ENTRYENTITY_CHANGE_KEY));
+            PositionBillSaveHelper helper = PositionBillSaveHelper.getInstance();
+            Map<String, Map<String, ILocaleString>> adminOrgBoIdToSiblingNameMap = Maps.newHashMapWithExpectedSize(positionNumberEntryDys.size());
+
+            for(DynamicObject positionEntryDy : positionNumberEntryDys) {
+                String suffix = helper.getSuffixFromEntryEntityDyn(positionEntryDy);
+                long positionId = 0L;
+                DynamicObject adminOrgDy = positionEntryDy.getDynamicObject(helper.spliceTwoStringByUnderLine(suffix, PositionBillConstant.NCKD_ADMINORG));
+                if (HRStringUtils.equals(PositionChangeTypeEnum.ADD.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY) , positionEntryDy.getDynamicObjectType().getName())) {
+                    positionId = positionEntryDy.getLong(helper.spliceTwoStringByUnderLine(suffix, PositionBillConstant.NCKD_POSITION_KEY));
+                } else {
+                    DynamicObject positionDy = positionEntryDy.getDynamicObject(helper.spliceTwoStringByUnderLine(suffix, PositionBillConstant.NCKD_POSITION_KEY));
+                    if (positionDy != null) {
+                        positionId = positionDy.getLong(PositionBillConstant.BOID_KEY);
+                    }
+                }
+
+                if (adminOrgDy != null) {
+                    long boId = adminOrgDy.getLong(PositionBillConstant.BOID_KEY);
+                    Map<String, ILocaleString> siblingNameMap = adminOrgBoIdToSiblingNameMap.get(String.valueOf(boId));
+                    if (siblingNameMap == null) {
+                        siblingNameMap = Maps.newHashMapWithExpectedSize(16);
+                    }
+
+                    siblingNameMap.put(String.valueOf(positionId), positionEntryDy.getLocaleString(helper.spliceTwoStringByUnderLine(suffix, PositionBillConstant.NCKD_NAME_KEY)));
+                    adminOrgBoIdToSiblingNameMap.put(String.valueOf(boId), siblingNameMap);
+                }
+            }
+
+            return JSONObject.toJSONString(adminOrgBoIdToSiblingNameMap);
+        }
+    }
+
     /**
      * @param operationKey
      * @param positionDys

+ 1 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/utils/hr/PositionBillUtil.java → code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/hr/utils/PositionBillUtil.java

@@ -1,4 +1,4 @@
-package nckd.jxccl.hrmp.hbpm.business.utils.hr;
+package nckd.jxccl.hrmp.hbpm.business.hr.utils;
 
 import kd.bos.exception.KDBizException;
 import kd.bos.form.IFormView;

+ 0 - 45
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/business/service/hr/PositionBillSaveHelper.java

@@ -1,45 +0,0 @@
-package nckd.jxccl.hrmp.hbpm.business.service.hr;
-
-import kd.bos.dataentity.entity.DynamicObject;
-import kd.bos.logging.Log;
-import kd.bos.logging.LogFactory;
-import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
-import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
-
-/**
- * 岗位申请单保存服务
- * @from: kd.hr.homs.business.domain.orgbatch.service.impl.OrgBatchBillSaveHelper
- * @author: jtd
- * @date: 2025/12/27 15:04
- */
-public class PositionBillSaveHelper {
-    private static PositionBillSaveHelper INSTANCE = new PositionBillSaveHelper();
-    private static final Log logger = LogFactory.getLog(PositionBillSaveHelper.class);
-
-    public static PositionBillSaveHelper getInstance() {
-        return INSTANCE;
-    }
-
-    public Long getPositionId(DynamicObject entryEntityDynFromPage, String suffix) {
-        String idFieldName = String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY);
-        if (HRStringUtils.equals(entryEntityDynFromPage.getString(spliceTwoStringByUnderLine(suffix, String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))), PositionChangeTypeEnum.ADD.getNumber())) {
-            idFieldName = PositionBillConstant.NCKD_POSITION_KEY;
-        }
-
-        String positionFiledName = spliceTwoStringByUnderLine(suffix, idFieldName);
-        return entryEntityDynFromPage.getLong(positionFiledName);
-    }
-
-    public String getSuffixFromEntryEntityDyn(DynamicObject entryEntityDynFromPage) {
-        String entryEntityName = entryEntityDynFromPage.getDynamicObjectType().getName();
-        int lastUnderLineIndex = entryEntityName.lastIndexOf("_");
-        return entryEntityName.substring(lastUnderLineIndex + 1);
-    }
-
-    public String spliceTwoStringByUnderLine(String oneString, String otherString) {
-        String[] str = otherString.split("\\.");
-        return str.length > 1 ? str[0] + "_" + oneString + "." + str[1] : otherString + "_" + oneString;
-    }
-
-}

+ 12 - 10
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionBillConstant.java

@@ -13,8 +13,6 @@ public class PositionBillConstant extends FormConstant {
     public static final String NCKD_HBPM_POSORGTREELISTF_ENTITY = "nckd_hbpm_posorgtreelistf";
     /** 岗位信息键值转换实体标识 */
     public static final String NCKD_POSITION_TRANSKEY_ENTITY = "nckd_position_transkey";
-    /** 行政组织树实体标识 */
-    public static final String HAOS_ORGTREELISTF7_ENTITY = "haos_orgtreelistf7";
     /** 岗位申请单实体标识 */
     public static final String NCKD_POSITIONBILL_ENTITY = "nckd_positionbill";
     /** 岗位申请单分录实体标识 */
@@ -77,6 +75,8 @@ public class PositionBillConstant extends FormConstant {
     public static final String NCKD_ADMINORG = "nckd_adminorg";
     /** 岗位ID */
     public static final String NCKD_POSITIONID_KEY = "nckd_positionid";
+    /** 岗位BOID */
+    public static final String NCKD_POSITIONBOID_KEY = "nckd_positionboid";
     /** 上级岗位 */
     public static final String NCKD_PARENT = "nckd_parent";
     /** 上级岗位 */
@@ -101,8 +101,10 @@ public class PositionBillConstant extends FormConstant {
     public static final String NCKD_DISORG_KEY = "nckd_disorg";
     /** 原上级岗位 */
     public static final String NCKD_ORIPARENT_KEY = "nckd_oriparent";
-    /** 所有分录数据单据体标识 */
-    public static final String NCKD_ENTRYENTITY_ALL_KEY = "nckd_entryentity_all";
+    /** 岗位申请单分录 */
+    public static final String NCKD_POSBILLENTRY_KEY = "nckd_posbillentry";
+    /** 岗位申请单分录 */
+    public static final String NCKD_ENTRYENTITYUNDERLINE_KEY = "nckd_entryentity_";
 
     /** 岗位生效时间页面自定义参数 */
     public static final String CP_POSITION_BSED = "position_bsed";
@@ -124,8 +126,8 @@ public class PositionBillConstant extends FormConstant {
     public static final String CP_BUSINESSUNIT = "businessUnit";
     /** 是否来自单据列表点击页面自定义参数 */
     public static final String CP_ISFROMBILLCLICK = "isFromBillClick";
-
-
+    /** 上级岗位ID页面自定义参数 */
+    public static final String CP_PARENTID = "parentId";
 
     /** 新增岗位上级变更页面缓存参数 */
     public static final String PC_ADDPOSITIONPARENTCHANGE = "addPositionParentChange";
@@ -197,9 +199,9 @@ public class PositionBillConstant extends FormConstant {
     public static final String OP_OPERATE_KEY = "OP_OPERATE_KEY";
     /** 删除行数据OP参数 */
     public static final String OP_ROW_INDEX = "OP_ROW_INDEX";
-    /** 返回数据OP参数 */
-    public static final String OP_RETURNDATA = "returnData";
-    /** 后置确认回调OP参数 */
-    public static final String OP_AFTERCONFIRM = "afterconfirm";
+    /** 校验数据(忽略后缀) */
+    public static final String OP_VALIDATOR_SUFFIX_IGNORE = "OP_VALIDATOR_SUFFIX_IGNORE";
+    /** 跳过必录校验OP参数 */
+    public static final String OP_SKIPMUSTINPUT = "SkipMustInput";
 
 }

+ 29 - 9
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/common/hr/PositionChangeTypeEnum.java

@@ -27,21 +27,37 @@ public enum PositionChangeTypeEnum {
         if (tag == null) {
             return "";
         }
-        for (PositionChangeTypeEnum value : values()) {
-            if (HRStringUtils.equals(value.getTag(), tag)) {
-                return value.getNumber();
+        for (PositionChangeTypeEnum changeTypeEnum : values()) {
+            if (HRStringUtils.equals(changeTypeEnum.getTag(), tag)) {
+                return changeTypeEnum.getNumber();
             }
         }
         return "";
     }
 
     public static String getTagByNumber(String number) {
+        return getTagByNumber("", number);
+    }
+
+    public static String getTagByNumber(String prefix, String number) {
+        if (number == null) {
+            return "";
+        }
+        for (PositionChangeTypeEnum changeTypeEnum : values()) {
+            if (HRStringUtils.equals(changeTypeEnum.getNumber(), number)) {
+                return prefix + changeTypeEnum.getTag();
+            }
+        }
+        return "";
+    }
+
+    public static String getTipByNumber(String number) {
         if (number == null) {
             return "";
         }
-        for (PositionChangeTypeEnum value : values()) {
-            if (HRStringUtils.equals(value.getNumber(), number)) {
-                return value.getTag();
+        for (PositionChangeTypeEnum changeTypeEnum : values()) {
+            if (HRStringUtils.equals(changeTypeEnum.getNumber(), number)) {
+                return changeTypeEnum.getTip();
             }
         }
         return "";
@@ -51,9 +67,9 @@ public enum PositionChangeTypeEnum {
         if (tag == null) {
             return "";
         }
-        for (PositionChangeTypeEnum value : values()) {
-            if (HRStringUtils.equals(value.getTag(), tag)) {
-                return value.getTip();
+        for (PositionChangeTypeEnum changeTypeEnum : values()) {
+            if (HRStringUtils.equals(changeTypeEnum.getTag(), tag)) {
+                return changeTypeEnum.getTip();
             }
         }
         return "";
@@ -71,6 +87,10 @@ public enum PositionChangeTypeEnum {
         return tag;
     }
 
+    public String getTag(String prefix) {
+        return prefix+tag;
+    }
+
     public String getTip() {
         return tip;
     }

+ 9 - 5
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryAddFormPlugin.java

@@ -28,9 +28,9 @@ import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import kd.hr.homs.business.utils.PatternUtil;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PosBillEntryFastChgHelper;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PosBillEntryFastChgHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.ArrayList;
@@ -62,6 +62,10 @@ public class PosBillEntryAddFormPlugin extends AbstractFormPlugin {
             getPageCache().put(PositionBillConstant.PC_INITFLAG, Boolean.TRUE.toString());
         }
 
+        if (!HRStringUtils.isEmpty(getView().getFormShowParameter().getCustomParam(PositionBillConstant.CP_PARENTID))) {
+            getModel().setValue(PositionBillConstant.NCKD_PARENT, Long.valueOf(getView().getFormShowParameter().getCustomParam(PositionBillConstant.CP_PARENTID)));
+        }
+
         // 设置生效时间
         Object positionBsedStr = getView().getFormShowParameter().getCustomParam(PositionBillConstant.CP_POSITION_BSED);
         if (positionBsedStr != null) {
@@ -218,7 +222,7 @@ public class PosBillEntryAddFormPlugin extends AbstractFormPlugin {
                 }
             }
 
-            // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
+            // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
             Map<String, String> transKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
             transKeyMap.forEach((key, value) -> {
                 noSuffixMap.put(value, noSuffixMap.get(key));
@@ -255,7 +259,7 @@ public class PosBillEntryAddFormPlugin extends AbstractFormPlugin {
                         dy.set(PositionBillConstant.FBASEDATAID_ID_KEY, ((Map) map).get(PositionBillConstant.FBASEDATAID_ID_KEY));
                         DynamicObject emptyDynamicObject = serviceHelper.generateEmptyDynamicObject();
                         emptyDynamicObject.set(PositionBillConstant.ID_KEY, ((Map) map).get(PositionBillConstant.FBASEDATAID_ID_KEY));
-                        dy.set(PositionBillConstant.BASEDATAID_KEY, emptyDynamicObject);
+                        dy.set(PositionBillConstant.FBASEDATAID_KEY, emptyDynamicObject);
                         dynamicObjectCollection.add(dy);
                     });
                     getModel().setValue(key, !dynamicObjectCollection.isEmpty() ? dynamicObjectCollection : null);

+ 10 - 18
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PosBillEntryUpdatePlugin.java

@@ -10,7 +10,6 @@ import kd.bos.dataentity.metadata.clr.DataEntityPropertyCollection;
 import kd.bos.dataentity.metadata.dynamicobject.DynamicProperty;
 import kd.bos.dataentity.serialization.SerializationUtils;
 import kd.bos.entity.MainEntityType;
-import kd.bos.entity.datamodel.AbstractFormDataModel;
 import kd.bos.entity.datamodel.events.PropertyChangedArgs;
 import kd.bos.entity.operate.result.OperationResult;
 import kd.bos.entity.property.BasedataProp;
@@ -40,9 +39,8 @@ import kd.hr.hbp.common.model.AuthorizedOrgResult;
 import kd.hr.hbp.common.util.HRDateTimeUtils;
 import kd.hr.hbp.common.util.HRDynamicObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import kd.hr.homs.formplugin.web.orgbatch.AdminOrgBatchDetailUpdatePlugin;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PosBillEntryFastChgHelper;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PosBillEntryFastChgHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PatternUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import org.apache.commons.lang3.time.DateUtils;
@@ -65,7 +63,7 @@ import java.util.stream.Stream;
  * @date: 2025/12/28 15:48
  */
 public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements BeforeF7SelectListener {
-    private static final Log LOG = LogFactory.getLog(AdminOrgBatchDetailUpdatePlugin.class);
+    private static final Log LOG = LogFactory.getLog(PosBillEntryUpdatePlugin.class);
     public static String suffix;
     public static String lineSuffix;
 
@@ -248,7 +246,7 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                 }
             }
 
-            // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
+            // 获取分录要转换的键值,将单据分录字段转换转成分录页面字段
             Map<String, String> transKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
             transKeyMap.forEach((key, value) -> {
                 noSuffixMap.put(value, noSuffixMap.get(key));
@@ -297,9 +295,9 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                 DataEntityPropertyCollection properties = dataEntityType.getProperties();
                 Map<String, IDataEntityProperty> allFields = ((MainEntityType) positionMasterInfo.getDataEntityType()).getAllFields();
 
-                // 获取分录要转换的键值
+                // 获取分录要转换的键值
                 Map<String, String> posBillEntryTransKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
-                // 获取岗位要转换的键值
+                // 获取岗位要转换的键值
                 Map<String, String> positionTransKeyMap = PositionBillServiceHelper.getPositionTransKeyMap();
                 // 获取分录属性
                 Map<String, IDataEntityProperty> entryPropMap = new HashMap<String, IDataEntityProperty>();
@@ -323,9 +321,6 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                 Map<String, IDataEntityProperty> dataEntryPropMap = positionProperties.stream().filter(property -> entryPropMap.containsKey(property.getName())).collect(Collectors.toMap(property -> entryPropMap.get(property.getName()).getName(), Function.identity()));
 
                 for (Map.Entry<String, IDataEntityProperty> dataEntryProp : dataEntryPropMap.entrySet()) {
-                    AbstractFormDataModel model = (AbstractFormDataModel) getModel();
-                    model.beginInit();
-
                     DynamicObjectCollection fromCollection = positionMasterInfo.getDynamicObjectCollection(dataEntryProp.getValue());
                     DynamicObjectCollection toCollection = getModel().getDataEntity().getDynamicObjectCollection(dataEntryProp.getKey());
                     if (fromCollection == null || fromCollection.isEmpty()) {
@@ -335,9 +330,6 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                     for (DynamicObject fromObj : fromCollection) {
                         HRDynamicObjectUtils.copy(fromObj, toCollection.addNew());
                     }
-
-                    model.endInit();
-                    getView().updateView(dataEntryProp.getKey());
                 }
 
                 getModel().setDataChanged(false);
@@ -370,16 +362,16 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                             dy.set(PositionBillConstant.FBASEDATAID_ID_KEY, ((Map) map).get(PositionBillConstant.FBASEDATAID_ID_KEY));
                             DynamicObject emptyDynamicObject = serviceHelper.generateEmptyDynamicObject();
                             emptyDynamicObject.set(PositionBillConstant.ID_KEY, ((Map) map).get(PositionBillConstant.FBASEDATAID_ID_KEY));
-                            dy.set(PositionBillConstant.BASEDATAID_KEY, emptyDynamicObject);
+                            dy.set(PositionBillConstant.FBASEDATAID_KEY, emptyDynamicObject);
                             dynamicObjectCollection.add(dy);
                         } else {
-                            Object fbasedataid = ((Map) map).get(PositionBillConstant.BASEDATAID_KEY);
+                            Object fbasedataid = ((Map) map).get(PositionBillConstant.FBASEDATAID_KEY);
                             if (fbasedataid instanceof Map && ((Map) fbasedataid).get(PositionBillConstant.ID_KEY) instanceof Long) {
                                 Long id = (Long) ((Map) fbasedataid).get(PositionBillConstant.ID_KEY);
                                 dy.set(PositionBillConstant.FBASEDATAID_ID_KEY, id);
                                 DynamicObject emptyDynamicObject = serviceHelper.generateEmptyDynamicObject();
                                 emptyDynamicObject.set(PositionBillConstant.ID_KEY, id);
-                                dy.set(PositionBillConstant.BASEDATAID_KEY, emptyDynamicObject);
+                                dy.set(PositionBillConstant.FBASEDATAID_KEY, emptyDynamicObject);
                                 dynamicObjectCollection.add(dy);
                             }
                         }
@@ -411,7 +403,7 @@ public class PosBillEntryUpdatePlugin extends AbstractFormPlugin implements Befo
                 }
             }
 
-            // 获取分录要转换的键值
+            // 获取分录要转换的键值
             Map<String, String> posBillEntryTransKeyMap = PositionBillServiceHelper.getPosBillEntryTransKeyMap();
             posBillEntryTransKeyMap.forEach((k, v) -> {
                 if (map.containsKey(v)) {

+ 3 - 3
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillEntryFormPlugin.java

@@ -30,8 +30,8 @@ import kd.bos.form.operate.AbstractOperate;
 import kd.bos.form.plugin.AbstractFormPlugin;
 import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
-import nckd.jxccl.hrmp.hbpm.business.utils.hr.PositionBillUtil;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.utils.PositionBillUtil;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.EventObject;
@@ -104,7 +104,7 @@ public class PositionBillEntryFormPlugin extends AbstractFormPlugin implements C
             switch (fieldKey) {
                 case PositionBillConstant.NCKD_ADMINORG:
                     // 替换组织F7模板
-                    beforeF7SelectEvent.getFormShowParameter().setFormId(PositionBillConstant.HAOS_ORGTREELISTF7_ENTITY);
+                    beforeF7SelectEvent.getFormShowParameter().setFormId(PositionBillConstant.HAOS_ORGBATCHTREELISTF7_ENTITY);
                     beforeF7SelectEvent.getFormShowParameter().setCaption("行政组织");
                     break;
                 case PositionBillConstant.NCKD_POSITION_KEY:

+ 10 - 12
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillFormPlugin.java

@@ -70,12 +70,12 @@ import kd.hr.hbp.common.util.HRObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
 import kd.hr.hbp.common.util.concurrent.NullableConcurrentHashMap;
 import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillAfterBindDataService;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillBeforeBindDataService;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillClosedCallBackService;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillConfirmCallBackService;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillPropertyChangedService;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillAfterBindDataService;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillBeforeBindDataService;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillClosedCallBackService;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillConfirmCallBackService;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillPropertyChangedService;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
 import org.apache.commons.lang3.time.DateUtils;
@@ -106,7 +106,7 @@ public class PositionBillFormPlugin extends AbstractFormPlugin implements Before
         DynamicObject org = getModel().getDataEntity().getDynamicObject(PositionBillConstant.ORG_KEY);
         if (org == null) {
             // 获取是否有查询权限
-            HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, getView().getFormShowParameter().getCheckRightAppId(), PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, true);
+            HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, true);
             List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
             if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                 if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {
@@ -242,8 +242,8 @@ public class PositionBillFormPlugin extends AbstractFormPlugin implements Before
             boolean hasEntryData = false;
             DynamicObject dataEntity = getModel().getDataEntity(Boolean.TRUE);
 
-            for (PositionChangeTypeEnum positionChangeTypeEnum : PositionChangeTypeEnum.values()) {
-                DynamicObjectCollection entryDynColl = dataEntity.getDynamicObjectCollection(String.join("_", PositionBillConstant.NCKD_ENTRYENTITY, positionChangeTypeEnum.getTag()));
+            for (PositionChangeTypeEnum changeTypeEnum : PositionChangeTypeEnum.values()) {
+                DynamicObjectCollection entryDynColl = dataEntity.getDynamicObjectCollection(changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY));
                 hasEntryData = entryDynColl != null && entryDynColl.size() > 0;
                 if (hasEntryData) {
                     break;
@@ -622,8 +622,6 @@ public class PositionBillFormPlugin extends AbstractFormPlugin implements Before
     }
 
     private int getAllEntrySize() {
-        DynamicObjectCollection addDyS = getModel().getEntryEntity(PositionBillConstant.NCKD_ENTRYENTITY_ADD_KEY);
-        DynamicObjectCollection changeDyS = getModel().getEntryEntity(PositionBillConstant.NCKD_ENTRYENTITY_CHANGE_KEY);
-        return addDyS.size() + changeDyS.size();
+        return Arrays.stream(PositionChangeTypeEnum.values()).mapToInt(changeTypeEnum -> getModel().getEntryEntity(changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY)).size()).sum();
     }
 }

+ 2 - 2
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/form/hr/PositionBillListPlugin.java

@@ -36,9 +36,9 @@ public class PositionBillListPlugin extends AbstractListPlugin {
         if (Objects.isNull(orgId)) {
             if (getView().getPageCache().get(PositionBillConstant.PC_ISFROMBILLCLICK) != null) {
                 getView().getPageCache().remove(PositionBillConstant.PC_ISFROMBILLCLICK);
-                e.getParameter().setCustomParam(PositionBillConstant.PC_ISFROMBILLCLICK, Boolean.TRUE.toString());
+                e.getParameter().setCustomParam(PositionBillConstant.CP_ISFROMBILLCLICK, Boolean.TRUE.toString());
             } else {
-                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, getView().getFormShowParameter().getCheckRightAppId(), PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
+                HasPermOrgResult hrPermOrg = PermissionServiceHelper.getAllPermOrgs(RequestContext.get().getCurrUserId(), OrgViewType.HR_OD, PositionBillConstant.HOMS_APP, PositionBillConstant.HAOS_ADMINORGDETAIL_ENTITY, PermissionStatus.View, false);
                 List<Long> hasPermOrgs = hrPermOrg.getHasPermOrgs();
                 if (!Objects.isNull(hasPermOrgs) && hasPermOrgs.size() != 0) {
                     if (hasPermOrgs.contains(RequestContext.get().getOrgId())) {

+ 2 - 2
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PosBillEntryNewPositionTempSaveOpPlugin.java

@@ -19,7 +19,7 @@ import kd.bos.servicehelper.operation.SaveServiceHelper;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.constants.history.HisModelDataStatusEnum;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.List;
@@ -76,7 +76,7 @@ public class PosBillEntryNewPositionTempSaveOpPlugin extends AbstractOperationSe
         // 构建岗位对象
         OperateOption operateOption = OperateOption.create();
         List<DynamicObject> positionList = Lists.newArrayListWithExpectedSize(positionEntities.length);
-        // 获取岗位要转换的键值
+        // 获取岗位要转换的键值
         Map<String, String> positionTransKeyMap = PositionBillServiceHelper.getPositionTransKeyMap();
 
         for (DynamicObject positionEntity : positionEntities) {

+ 10 - 1
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillDeleteOpPlugin.java

@@ -2,14 +2,16 @@ package nckd.jxccl.hrmp.hbpm.plugin.operate.hr;
 
 import kd.bos.dataentity.entity.DynamicObject;
 import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
 import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
 import kd.bos.orm.query.QCP;
 import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRStringUtils;
 import nckd.jxccl.base.common.utils.QueryFieldBuilder;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator.PositionBillDeleteStatusValidator;
 
 import java.util.Arrays;
 import java.util.List;
@@ -24,6 +26,13 @@ import java.util.stream.Collectors;
  * @date: 2025/12/31 21:29
  */
 public class PositionBillDeleteOpPlugin extends AbstractOperationServicePlugIn {
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+        e.addValidator(new PositionBillDeleteStatusValidator());
+    }
+
     @Override
     public void beginOperationTransaction(BeginOperationTransactionArgs args) {
         super.beginOperationTransaction(args);

+ 53 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/PositionBillSaveOpPlugin.java

@@ -0,0 +1,53 @@
+package nckd.jxccl.hrmp.hbpm.plugin.operate.hr;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
+import kd.bos.entity.plugin.args.EndOperationTransactionArgs;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillSaveHelper;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator.PositionBillSaveAndSubmitValidator;
+import nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator.PositionBillSaveValidator;
+
+/**
+ * 岗位申请单保存操作插件
+ * @entity: nckd_positionbill
+ * @operate: save
+ * @from: kd.hr.homs.business.domain.orgbatch.service.impl.OrgBatchBillSaveHelper
+ * @author: jtd
+ * @date: 2026/1/1 17:57
+ */
+public class PositionBillSaveOpPlugin extends AbstractOperationServicePlugIn {
+    private static final Log logger = LogFactory.getLog(PositionBillSaveOpPlugin.class);
+
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        logger.info("PositionBillSaveOpPlugin.onAddValidators start.");
+        super.onAddValidators(e);
+        e.addValidator(new PositionBillSaveAndSubmitValidator());
+        e.addValidator(new PositionBillSaveValidator());
+    }
+
+    public void beginOperationTransaction(BeginOperationTransactionArgs e) {
+        logger.info("PositionBillSaveOpPlugin.beginOperationTransaction start.");
+        if (e.getDataEntities().length != 0) {
+            DynamicObject dataEntity = e.getDataEntities()[0];
+            PositionBillSaveHelper.getInstance().positionBillSave(dataEntity);
+        }
+    }
+
+    public void endOperationTransaction(EndOperationTransactionArgs e) {
+        Long billId = e.getDataEntities()[0].getLong(PositionBillConstant.ID_KEY);
+        HRBaseServiceHelper positionBillEntityHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+        QFilter positionIdFilter = new QFilter(PositionBillConstant.CREATOR_KEY, QCP.equals, 0);
+        QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, billId);
+        positionIdFilter.and(billIdFilter);
+        positionBillEntityHelper.deleteByFilter(positionIdFilter.toArray());
+    }
+
+}

+ 4 - 4
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/AbsBillPositionSaveValidator.java

@@ -17,7 +17,7 @@ import kd.bos.orm.query.QFilter;
 import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
 import kd.hr.hbp.common.util.HRObjectUtils;
 import kd.hr.hbp.common.util.HRStringUtils;
-import nckd.jxccl.hrmp.hbpm.business.service.hr.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
 import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
 
 import java.util.List;
@@ -36,7 +36,7 @@ public abstract class AbsBillPositionSaveValidator extends AbstractValidator  {
     public void initialize() {
         super.initialize();
 
-        String ignoreBillNoForContent = getOption().getVariableValue("OP_VALIDATOR_PREFIX_IGNORE", Boolean.FALSE.toString());
+        String ignoreBillNoForContent = getOption().getVariableValue(PositionBillConstant.OP_VALIDATOR_SUFFIX_IGNORE, Boolean.FALSE.toString());
         if (Boolean.TRUE.toString().equals(ignoreBillNoForContent)) {
             setAddBillNoForContent(false);
         }
@@ -53,7 +53,7 @@ public abstract class AbsBillPositionSaveValidator extends AbstractValidator  {
         // 跳过系统默认的权限校验,数据、操作权限等
         operateOption.setVariableValue(OperateOptionConst.SKIPCHECKPERMISSION, Boolean.TRUE.toString());
         // 跳过字段值合规性校验
-        operateOption.setVariableValue("SkipMustInput", Boolean.TRUE.toString());
+        operateOption.setVariableValue(PositionBillConstant.OP_SKIPMUSTINPUT, Boolean.TRUE.toString());
         Map<Object, ExtendedDataEntity> entityMap = Maps.newHashMapWithExpectedSize(entities.length);
 
         for(ExtendedDataEntity entity : entities) {
@@ -80,7 +80,7 @@ public abstract class AbsBillPositionSaveValidator extends AbstractValidator  {
                             .and(PositionBillConstant.ID_KEY, QCP.equals, parentOrgBoId);
                     DynamicObject currentParentDy = positionHrServiceHelper.loadDynamicObject(qFilter);
                     if (currentParentDy != null) {
-                        addPositionDy.set(PositionBillConstant.NCKD_PARENT, currentParentDy);
+                        addPositionDy.set(PositionBillConstant.PARENT_KEY, currentParentDy);
                     }
                 }
             }

+ 46 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillDeleteStatusValidator.java

@@ -0,0 +1,46 @@
+package nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.sdk.hr.hdm.common.enums.reg.RegBillStatusEnum;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位申请单删除校验器
+ * @from: kd.hr.homs.opplugin.web.orgbatch.validator.AdminOrgBatchBillDeleteStatusValidator
+ * @author: jtd
+ * @date: 2026/1/2 15:56
+ */
+public class PositionBillDeleteStatusValidator extends AbstractValidator {
+    @Override
+    public void validate() {
+        ExtendedDataEntity[] dataEntities = getDataEntities();
+        if (dataEntities.length != 0) {
+            List<Long> selectedRows = Arrays.stream(dataEntities).map((dataEntityx) -> dataEntityx.getDataEntity().getLong(PositionBillConstant.ID_KEY)).collect(Collectors.toList());
+            HRBaseServiceHelper hrBaseServiceHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILL_ENTITY);
+            QFilter qFilter = new QFilter(PositionBillConstant.ID_KEY, QCP.in, selectedRows);
+            qFilter.and(PositionBillConstant.BILL_STATUS_KEY, QCP.in, Arrays.asList(RegBillStatusEnum.ALREADYSUBMIT.getCode(), RegBillStatusEnum.APPROVING.getCode(), RegBillStatusEnum.APPROVEPASSED.getCode(), RegBillStatusEnum.APPROVEREJECTED.getCode(), RegBillStatusEnum.ABANDONED.getCode()));
+            DynamicObject[] dynamicObjects = hrBaseServiceHelper.queryOriginalArray(PositionBillConstant.ID_KEY, new QFilter[]{qFilter});
+            String msg = "只允许删除“暂存”或“待重新提交”状态的数据。";
+            if (dynamicObjects.length > 0) {
+                Set<Long> ids = Arrays.stream(dynamicObjects).map((dynamicObject) -> dynamicObject.getLong(PositionBillConstant.ID_KEY)).collect(Collectors.toSet());
+
+                for(ExtendedDataEntity dataEntity : dataEntities) {
+                    if (ids.contains(dataEntity.getDataEntity().getLong(PositionBillConstant.ID_KEY))) {
+                        addErrorMessage(dataEntity, msg);
+                    }
+                }
+            }
+
+        }
+    }
+}

+ 175 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillSaveAndSubmitValidator.java

@@ -0,0 +1,175 @@
+package nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator;
+
+import com.google.common.collect.Maps;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.entity.validate.ValidateResultCollection;
+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.orm.util.CollectionUtils;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.constants.history.HisModelDataStatusEnum;
+import kd.hr.hbp.common.util.HRStringUtils;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位社区内单保存并提交校验器
+ * @from: kd.hr.homs.opplugin.web.orgbatch.OrgBatchBillSaveAndSubmitValidator
+ * @author: jtd
+ * @date: 2026/1/2 16:01
+ */
+public class PositionBillSaveAndSubmitValidator extends AbstractValidator {
+    private static final Log logger = LogFactory.getLog(PositionBillSaveAndSubmitValidator.class);
+    private String operateKey;
+
+    /**
+     * 获取所有岗位的历史版本数据
+     * @param billId
+     * @return
+     */
+    private Map<Long, Long> getBoIdPositionIdMap(Long billId) {
+        // from: kd.hr.homs.business.domain.batchbill.service.impl.OrgBatchBillConverter.processDynamicObject
+        // unSafeAndDeleteDataMap获取到是空的,因为mergesplitstatus默认是2,而岗位并不需要考虑拆分合并的数据所以视作无数据即可
+        // 拿到所有岗位ID,实际上并不需要新增的岗位
+        HRBaseServiceHelper baseHelper = new HRBaseServiceHelper(PositionBillConstant.NCKD_POSITIONBILLENTRY_ENTITY);
+        QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, billId);
+        DynamicObject[] objects = baseHelper.loadDynamicObjectArray(billIdFilter.toArray());
+        // from: kd.hr.homs.business.domain.orgbatch.service.impl.OrgBatchChgFeasibleCheckServiceImpl.check
+        // 获取所有岗位的BOID
+        Set<Long> ownBoIdSet = Arrays.stream(objects).map(object -> object.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.BOID_KEY))).collect(Collectors.toSet());
+        // from: kd.hr.homs.business.domain.orgbatch.repository.OrgBatchBillCheckHelper.queryNewBoIdOrgIdMap
+        // 查询出对应的历史版本
+        QFilter adminFilter = new QFilter(PositionBillConstant.BOID_KEY, QCP.in, ownBoIdSet);
+        adminFilter.and(new QFilter(PositionBillConstant.DATA_STATUS, QCP.in, HisModelDataStatusEnum.EFFECTING));
+        adminFilter.and(new QFilter(PositionBillConstant.ENABLE, QCP.equals, EnableEnum.YES.getCode()));
+        adminFilter.and(new QFilter(PositionBillConstant.IS_CURRENT_VERSION, QCP.equals, EnableEnum.NO.getCode()));
+        HRBaseServiceHelper positionHelper = new HRBaseServiceHelper(PositionBillConstant.HBPM_POSITIONHR);
+        DynamicObjectCollection positionColl = positionHelper.queryOriginalCollection(String.join(",", PositionBillConstant.BOID_KEY, PositionBillConstant.ID_KEY), new QFilter[]{adminFilter}, String.format("%s desc", PositionBillConstant.BSED_KEY));
+        Map<Long, List<DynamicObject>> positionGroupMap = positionColl.stream().collect(Collectors.groupingBy((dy) -> dy.getLong(PositionBillConstant.BOID_KEY)));
+        Map<Long, Long> boIdPositionIdMap = Maps.newHashMapWithExpectedSize(positionColl.size());
+
+        for(Map.Entry<Long, List<DynamicObject>> entry : positionGroupMap.entrySet()) {
+            Long positionId = entry.getValue().get(0).getLong(PositionBillConstant.ID_KEY);
+            boIdPositionIdMap.put(entry.getKey(), positionId);
+        }
+
+        return boIdPositionIdMap;
+    }
+
+    /**
+     * 校验变更信息中的岗位是否最新一笔历史版本
+     * @from: kd.hr.homs.business.domain.orgbatch.service.impl.OrgBatchChgFeasibleCheckServiceImpl#checkInfoChg(java.lang.String, kd.bos.dataentity.entity.DynamicObject, java.util.List)
+     * @param dynamicObject
+     * @return
+     */
+    private String checkChangeHandler(DynamicObject dynamicObject, Map<Long, Long> boIdOrgIdMap) {
+        DynamicObject position = dynamicObject.getDynamicObject(String.join("_", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.CHANGE_TAG));
+        Long boId = position.getLong(PositionBillConstant.BOID_KEY);
+        Long positionId = position.getLong(PositionBillConstant.ID_KEY);
+        String positionName = position.getString(PositionBillConstant.NAME_KEY);
+        Long newPositionId = boIdOrgIdMap.get(boId);
+        if (!Objects.isNull(newPositionId) && !newPositionId.equals(positionId)) {
+            return String.format("“变更信息”中“%s”:当前岗位已发生变化,请基于最新的信息进行调整。", new Object[]{positionName});
+        }
+
+        return "";
+    }
+
+    @Override
+    public void validate() {
+        this.operateKey = getOption().getVariableValue(PositionBillConstant.OP_OPERATE_KEY, "");
+        long startTime = System.currentTimeMillis();
+        ValidateResultCollection validateResultCollection = getValidateContext().getValidateResults();
+        if (validateResultCollection == null || CollectionUtils.isEmpty(validateResultCollection.getValidateErrors()) || CollectionUtils.isEmpty((validateResultCollection.getValidateErrors().get(0)).getAllErrorInfo())) {
+            ExtendedDataEntity[] dataEntities = getDataEntities();
+            List<String> entryKeyList = new ArrayList(8);
+
+            for(ExtendedDataEntity dataEntity : dataEntities) {
+                Map<Long, Long> boIdPositionIdMap = getBoIdPositionIdMap(dataEntity.getDataEntity().getLong(PositionBillConstant.ID_KEY));
+                if (entryKeyList.isEmpty()) {
+                    entryKeyList = Arrays.stream(PositionChangeTypeEnum.values()).map(changeTypeEnum -> changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY)).collect(Collectors.toList());
+                }
+
+                DynamicObject checkObject = dataEntity.getDataEntity();
+                String message = showMessageBeforeCheck(checkObject, entryKeyList);
+                if (HRStringUtils.isEmpty(message)) {
+                    // 这里不校验新增岗位的数据
+                    for (DynamicObject dynamicObject : dataEntity.getDataEntity().getDynamicObjectCollection(PositionBillConstant.NCKD_ENTRYENTITY_CHANGE_KEY)) {
+                        String errorMessage = checkChangeHandler(dynamicObject, boIdPositionIdMap);
+                        if (HRStringUtils.isNotBlank(errorMessage)) {
+                            addErrorMessage(dataEntity, errorMessage);
+                        }
+                    }
+                } else {
+                    addErrorMessage(dataEntity, message);
+                }
+            }
+
+            logger.info(String.format(Locale.ROOT, "PositionBillSaveAndSubmitValidator() time cost is: %s", System.currentTimeMillis() - startTime));
+        }
+    }
+
+    private String showMessageBeforeCheck(DynamicObject checkObject, List<String> entryKeyList) {
+        String message = null;
+        boolean isChange = isChange(checkObject, entryKeyList);
+        this.operateKey = HRStringUtils.isEmpty(this.operateKey) ? getOperateKey() : this.operateKey;
+        switch (this.operateKey) {
+            case PositionBillConstant.SAVE_OP:
+                if (!isChange) {
+                    message = "没有“岗位调整”的数据,请调整后再操作“保存”";
+                }
+                break;
+            case PositionBillConstant.SUBMIT_OP:
+                if (!getIsHasWorkFlow()) {
+                    message = "提交失败,当前单据没有可用的工作流,请保存单据后前往“流程服务云>工作流服务>设计中心>流程设计”中新增或发布流程。";
+                }
+            case "wftask_save":
+                if (!isChange) {
+                    message = "没有“岗位调整”的数据,请调整后再操作“提交”。";
+                }
+                break;
+            case PositionBillConstant.SUBMITEFFECT_OP:
+                if (!isChange) {
+                    message = "没有“岗位调整”的数据,请调整后再操作“提交并生效”。";
+                }
+        }
+
+        return message;
+    }
+
+    private boolean getIsHasWorkFlow() {
+        String entityNumber = getDataEntities()[0].getDataEntity().getDataEntityType().getName();
+        HRBaseServiceHelper wfHelper = new HRBaseServiceHelper("wf_model");
+        QFilter qFilter = new QFilter("entrabill", "=", entityNumber);
+        qFilter.and("publish", "=", '1').and("discard", "=", '0');
+        return wfHelper.isExists(qFilter);
+    }
+
+    private boolean isChange(DynamicObject checkObject, List<String> entryKeyList) {
+        logger.info("isChange entryKeys:{}", entryKeyList);
+
+        for(String entryKey : entryKeyList) {
+            DynamicObjectCollection entryColl = checkObject.getDynamicObjectCollection(entryKey);
+            if (!CollectionUtils.isEmpty(entryColl)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 200 - 0
code/hrmp/nckd-jxccl-hrmp/src/main/java/nckd/jxccl/hrmp/hbpm/plugin/operate/hr/validator/PositionBillSaveValidator.java

@@ -0,0 +1,200 @@
+package nckd.jxccl.hrmp.hbpm.plugin.operate.hr.validator;
+
+import com.google.common.collect.Lists;
+import kd.bos.common.enums.EnableEnum;
+import kd.bos.dataentity.OperateOption;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.operate.OperateOptionConst;
+import kd.bos.entity.operate.result.OperateErrorInfo;
+import kd.bos.entity.operate.result.OperationResult;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.entity.validate.ValidateResult;
+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.operation.OperationServiceHelper;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.hr.hbp.common.util.HRStringUtils;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillSaveHelper;
+import nckd.jxccl.hrmp.hbpm.business.hr.service.impl.PositionBillServiceHelper;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionBillConstant;
+import nckd.jxccl.hrmp.hbpm.common.hr.PositionChangeTypeEnum;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * 岗位申请单保存校验器
+ * @from: kd.hr.homs.opplugin.web.orgbatch.validator.AdminOrgBillSaveValidator
+ * @author: jtd
+ * @date: 2026/1/2 16:21
+ */
+public class PositionBillSaveValidator extends AbstractValidator {
+    private static final Log LOG = LogFactory.getLog(PositionBillSaveValidator.class);
+    private final Map<String, HRBaseServiceHelper> helperMap = new ConcurrentHashMap(PositionChangeTypeEnum.values().length);
+    private final String suffix = "“%s”中:";
+    private final String suffix_with_name = "“%s”中“%s”:";
+
+    @Override
+    public void validate() {
+        ExtendedDataEntity[] entities = getDataEntities();
+
+        for(ExtendedDataEntity entity : entities) {
+            checkNotCommonEntryData(entity);
+        }
+    }
+
+    private void checkNotCommonEntryData(ExtendedDataEntity entity) {
+        PositionBillSaveHelper positionBillSaveHelper = PositionBillSaveHelper.getInstance();
+        DynamicObject dataEntity = entity.getDataEntity();
+        long billId = dataEntity.getLong(PositionBillConstant.ID_KEY);
+        Date effDate = dataEntity.getDate(PositionBillConstant.NCKD_EFFDT);
+        List<DynamicObject> positionBillEntryEntityDyns = getAllDbEntryDys(billId);
+        Map<Long, List<DynamicObject>> positionId2DynFromDBListMap = positionBillEntryEntityDyns.stream().collect(Collectors.groupingBy((dyn) -> dyn.getLong(String.join(".", PositionBillConstant.NCKD_POSITION_KEY, PositionBillConstant.ID_KEY))));
+        DynamicObjectCollection allEntryEntityDyn = Arrays.stream(PositionChangeTypeEnum.values()).map(changeTypeEnum -> dataEntity.getDynamicObjectCollection(changeTypeEnum.getTag(PositionBillConstant.NCKD_ENTRYENTITYUNDERLINE_KEY))).collect(DynamicObjectCollection::new, DynamicObjectCollection::addAll, DynamicObjectCollection::addAll);
+        if (!allEntryEntityDyn.isEmpty()) {
+            List<Long> positionIdList = positionBillSaveHelper.getAllEntryPositionId(allEntryEntityDyn);
+            Map<Long, DynamicObject> positionId2BasicInfoDynMap = PositionBillServiceHelper.getPositionId2BasicInfoDynMapByIds(positionIdList);
+            List<DynamicObject> needSaveEntryDynList = Lists.newArrayListWithExpectedSize(allEntryEntityDyn.size());
+
+            for(DynamicObject entryEntityDyn : allEntryEntityDyn) {
+                String suffix = positionBillSaveHelper.getSuffixFromEntryEntityDyn(entryEntityDyn);
+                String changeTypeNumber = PositionChangeTypeEnum.getNumberByTag(suffix);
+                Long positionId = positionBillSaveHelper.getPositionId(entryEntityDyn, suffix);
+                if (positionId != null && positionId != 0L) {
+                    DynamicObject positionBillEntryDyn = null;
+                    List<DynamicObject> dbEntryDys = positionId2DynFromDBListMap.get(positionId);
+                    if (dbEntryDys != null) {
+                        for(DynamicObject dbEntryDy : dbEntryDys) {
+                            String dbChangeTypeNumber = dbEntryDy.getString(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY));
+                            if (HRStringUtils.equals(dbChangeTypeNumber, changeTypeNumber)) {
+                                positionBillEntryDyn = dbEntryDy;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (positionBillEntryDyn == null) {
+                        HRBaseServiceHelper helper = getHelperByChangeType(changeTypeNumber);
+                        if (helper == null) {
+                            continue;
+                        }
+
+                        positionBillEntryDyn = helper.generateEmptyDynamicObject();
+                        positionBillEntryDyn.set(PositionBillConstant.NCKD_BILLID, dataEntity.getLong(PositionBillConstant.ID_KEY));
+                        DynamicObject basicInfoDyn = positionId2BasicInfoDynMap.get(positionId);
+                        positionBillSaveHelper.assembleSaveEntryDyn(positionBillEntryDyn, basicInfoDyn);
+                    }
+
+                    positionBillSaveHelper.coverdEntryValue(positionBillEntryDyn, entryEntityDyn, suffix);
+                    Object enable = positionBillEntryDyn.get(PositionBillConstant.ENABLE);
+                    if (enable == null) {
+                        positionBillEntryDyn.set(PositionBillConstant.ENABLE, EnableEnum.YES.getCode());
+                    }
+
+                    positionBillEntryDyn.set(PositionBillConstant.NCKD_BSED, effDate);
+                    needSaveEntryDynList.add(positionBillEntryDyn);
+                }
+            }
+
+            Map<String, List<DynamicObject>> groupByMap = needSaveEntryDynList.stream().collect(Collectors.groupingBy((dy) -> dy.getString(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY))));
+            OperateOption operateOption = OperateOption.create();
+            operateOption.setVariableValue(PositionBillConstant.OP_VALIDATOR_BILL_POSITION_ALL_CHANGE_POSITION_KEY, PositionBillServiceHelper.getThisBillAllValidateDataStr(dataEntity));
+            operateOption.setVariableValue(PositionBillConstant.OP_VALIDATOR_BILL_POSITION_NEW_POSITION_NUMBERS_KEY, PositionBillServiceHelper.getThisBillPositionNumbersStr(dataEntity));
+            operateOption.setVariableValue(PositionBillConstant.OP_BILL_SIBLING_NAMES_MAP, PositionBillServiceHelper.getThisBillPositionNamesStr(dataEntity));
+            operateOption.setVariableValue(PositionBillConstant.OP_VALIDATOR_SUFFIX_IGNORE, Boolean.TRUE.toString());
+
+            for(Map.Entry<String, List<DynamicObject>> entry : groupByMap.entrySet()) {
+                String changeTypeNumber = entry.getKey();
+                List<DynamicObject> entryDynList = entry.getValue();
+                Map<Object, DynamicObject> saveMap = entryDynList.stream().collect(Collectors.toMap((dy) -> dy.get(PositionBillConstant.ID_KEY), (dy) -> dy, (v1, v2) -> v1));
+                String tagName = PositionChangeTypeEnum.getTipByNumber(changeTypeNumber);
+                String entityNumber = PositionChangeTypeEnum.getTagByNumber(PositionBillConstant.NCKD_POSBILLENTRY_KEY, changeTypeNumber);
+                OperationResult operationResult = executeOperateOnlyValidate(entryDynList.toArray(new DynamicObject[0]), entityNumber, operateOption, PositionBillConstant.SAVE_OP);
+                if (operationResult != null && !operationResult.isSuccess()) {
+                    for(ValidateResult validateError : operationResult.getValidateResult().getValidateErrors()) {
+                        for(OperateErrorInfo operateErrorInfo : validateError.getAllErrorInfo()) {
+                            Object pkValue = operateErrorInfo.getPkValue();
+                            DynamicObject errorDy = saveMap.get(pkValue);
+                            String message = operateErrorInfo.getMessage();
+                            if (errorDy == null) {
+                                addErrorMessage(entity, String.format(this.suffix, tagName) + message);
+                            } else {
+                                DynamicObject versionDy = errorDy.getDynamicObject(PositionBillConstant.NCKD_POSITION_KEY);
+                                if (HRStringUtils.isNotEmpty(message) && message.contains(versionDy.getString(PositionBillConstant.NAME_KEY))) {
+                                    addErrorMessage(entity, String.format(this.suffix, tagName) + message);
+                                } else {
+                                    addErrorMessage(entity, String.format(this.suffix_with_name, tagName, versionDy.getString(PositionBillConstant.NAME_KEY)) + message);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+
+    private OperationResult executeOperateOnlyValidate(DynamicObject[] positionBillEntries, String entityNumber, OperateOption option, String opKey) {
+        OperateOption operateOption = option;
+        if (option == null) {
+            operateOption = OperateOption.create();
+        }
+
+        operateOption.setVariableValue(OperateOptionConst.ISHASRIGHT, Boolean.TRUE.toString());
+        operateOption.setVariableValue(OperateOptionConst.ONLY_VALIDATE, Boolean.TRUE.toString());
+
+        try {
+            return OperationServiceHelper.executeOperate(opKey, entityNumber, positionBillEntries, operateOption);
+        } catch (Exception ex) {
+            LOG.error("executeSaveOperateOnlyValidate_error", ex);
+            throw ex;
+        }
+    }
+
+    private List<DynamicObject> getAllDbEntryDys(long billId) {
+        List<DynamicObject> entryDys = Lists.newArrayListWithExpectedSize(10);
+
+        for(PositionChangeTypeEnum changeTypeEnum : PositionChangeTypeEnum.values()) {
+            DynamicObject[] entries = getDbEntryDysByChangeType(billId, changeTypeEnum.getNumber());
+            if (entries != null) {
+                entryDys.addAll(Arrays.asList(entries));
+            }
+        }
+
+        return entryDys;
+    }
+
+    private DynamicObject[] getDbEntryDysByChangeType(long billId, String changeTypeNumber) {
+        HRBaseServiceHelper helper = getHelperByChangeType(changeTypeNumber);
+        if (helper == null) {
+            return new DynamicObject[0];
+        } else {
+            QFilter billIdFilter = new QFilter(PositionBillConstant.NCKD_BILLID, QCP.equals, billId);
+            QFilter changeTypeFilter = new QFilter(String.join(".", PositionBillConstant.NCKD_CHANGETYPE, PositionBillConstant.NUMBER_KEY), QCP.equals, changeTypeNumber);
+            return helper.loadDynamicObjectArray(new QFilter[]{billIdFilter, changeTypeFilter});
+        }
+    }
+
+    private HRBaseServiceHelper getHelperByChangeType(String changeTypeNumber) {
+        String entityNumber = PositionChangeTypeEnum.getTagByNumber(PositionBillConstant.NCKD_POSBILLENTRY_KEY, changeTypeNumber);
+        if (HRStringUtils.isNotEmpty(entityNumber)) {
+            HRBaseServiceHelper helper = helperMap.get(entityNumber);
+            if (helper == null) {
+                helper = new HRBaseServiceHelper(entityNumber);
+                helperMap.put(entityNumber, helper);
+            }
+
+            return helper;
+        } else {
+            return null;
+        }
+    }
+}