3 Commits b42a9d63eb ... 903d38cb9e

Autore SHA1 Messaggio Data
  Tyx 903d38cb9e MDM同步代码 1 settimana fa
  Tyx 8b446cd932 Merge branch 'master' of http://111.75.220.136:10030/turborao/jyyy 1 settimana fa
  Tyx 6688e77b90 编制调整申请相关插件 2 settimane fa

+ 736 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/form/PrepareAdjustApplicationFormPlugin.java

@@ -0,0 +1,736 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.form;
+
+
+import kd.bos.algo.DataSet;
+import kd.bos.bill.AbstractBillPlugIn;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.db.DBRoute;
+import kd.bos.entity.datamodel.AbstractFormDataModel;
+import kd.bos.entity.datamodel.IDataModel;
+import kd.bos.entity.datamodel.events.ChangeData;
+import kd.bos.entity.datamodel.events.PropertyChangedArgs;
+import kd.bos.form.IFormView;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.control.events.RowClickEvent;
+import kd.bos.form.control.events.RowClickEventListener;
+import kd.bos.form.field.BasedataEdit;
+import kd.bos.form.field.events.BeforeF7SelectEvent;
+import kd.bos.form.field.events.BeforeF7SelectListener;
+import kd.bos.list.ListShowParameter;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.ORM;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.basedata.BaseDataServiceHelper;
+import kd.hr.haos.business.servicehelper.OrgBatchBillHelper;
+import kd.hr.haos.common.constants.masterdata.AdminOrgConstants;
+import kd.hr.hbp.common.util.DatePattern;
+import kd.hr.hbp.common.util.DateUtils;
+import kd.hr.hbp.common.util.HRDBUtil;
+import nckd.jimin.jyyy.hr.haos.staff.plugin.operate.PrepareAdjustApplicationAuditOpPlugin;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Module           :HR中控服务云-HR基础组织-人力编制-编制调整申请
+ * Description      :编制调整申请单据插件
+ *
+ * @author Tyx
+ * @date  2025/4/14 16:33:30
+ * 标识 nckd_preadjustapplic
+ */
+
+
+public class PrepareAdjustApplicationFormPlugin extends AbstractBillPlugIn implements BeforeF7SelectListener, RowClickEventListener {
+
+    private static Log logger = LogFactory.getLog(PrepareAdjustApplicationFormPlugin.class);
+
+    private final List<String> COMPANY_LIST = Arrays.asList(new String[]{"Orgform01","Orgform01-100","Orgform02","Orgform03"});
+
+    private static final String ADJUSTTYPE_ADD = "A";
+    private static final String ADJUSTTYPE_MINUS = "B";
+
+    // 编制信息单
+    private static final String NCKD_HAOS_STAFF = "nckd_haos_staff";
+    // 组织 直属调整人数
+    private static final String NCKD_ADJUSTDIRE = "nckd_adjustdire";
+    // 组织 直属调整后人数
+    private static final String NCKD_RELBDIRECTNUM = "nckd_relbdirectnum";
+    // 组织 含下级调整人数
+    private static final String NCKD_ADJUSTNUM = "nckd_adjustnum";
+    // 组织 调整类型
+    private static final String NCKD_ADJUSTTYPE = "nckd_adjusttype";
+    // 组织 现有编制人数
+    private static final String NCKD_BREALNUM = "nckd_brealnum";
+    // 组织 调整后编制人数
+    private static final String NCKD_ADJUSTLATENUM = "nckd_adjustlatenum";
+    // 岗位调整类型
+    private static final String NCKD_POSTADJUSTTYPE = "nckd_postadjusttype";
+    // 组织 现在编制直属人数
+    private static final String NCKD_BDIRECTNUM = "nckd_bdirectnum";
+    // 岗位 现有含下级编制人数
+    private static final String NCKD_RELCYEARSTAFF = "nckd_relcyearstaff";
+    // 岗位调整人数
+    private static final String NCKD_POSTADJUSTNUM = "nckd_postadjustnum";
+    // 岗位调整后编制人数
+    private static final String NCKD_POSTADJUSTLATENUM = "nckd_postadjustlatenum";
+
+
+
+
+    @Override
+    public void propertyChanged(PropertyChangedArgs e) {
+        super.propertyChanged(e);
+        // 监听 nckd_haos_staff 的变化
+        String key = e.getProperty().getName();
+        ChangeData[] changeData = e.getChangeSet();
+        Object newValue = changeData[0].getNewValue();
+        Object oldValue = changeData[0].getOldValue();
+        int iRow = changeData[0].getRowIndex();
+        IDataModel model = this.getModel();
+        switch (key) {
+            case NCKD_HAOS_STAFF:
+                this.getModel().deleteEntryData("nckd_centryentity");
+                this.getModel().deleteEntryData("nckd_bentryentity");
+                this.initEntry(model.getValue("org"),newValue);
+                break;
+//            case NCKD_ADJUSTNUM:
+//                // 调整人数
+////                this.adjustnumChange(newValue,oldValue,iRow);
+//                break;
+//            case NCKD_ADJUSTDIRE:
+//                // 直属调整人数
+////                this.adjustdireChange(newValue,oldValue,iRow);
+//                break;
+//            case NCKD_ADJUSTTYPE:
+//                //调整类型
+////                this.adjusttype(newValue,oldValue,iRow);
+//                break;
+//            case NCKD_POSTADJUSTTYPE:
+//                //岗位调整类型
+////                this.postadjusttypeChange(newValue,oldValue,iRow);
+//                break;
+//            case NCKD_POSTADJUSTNUM:
+//                // 岗位调整人数
+////                this.postadjustnumChange(newValue,oldValue,iRow);
+//                break;
+
+            case "nckd_adjustlatenum":
+                // 组织调整后含下级编制人数
+                this.nckdadjustlatenum(newValue,oldValue,iRow);
+                break;
+            case "nckd_relbdirectnum":
+                // 组织调整后直属编制人数
+                this.relbdirectnum(newValue,oldValue,iRow);
+                break;
+            case "nckd_postadjustlatenum":
+                // 岗位调整后直属编制人数
+                this.adjustlatenum(newValue,oldValue,iRow);
+                break;
+            default:
+                break;
+        }
+
+    }
+    // 岗位计算调整人数
+    private void adjustlatenum(Object newValue,Object oldValue,int row){
+
+        if(ObjectUtils.isEmpty(newValue)){
+            // 调整后编制人数为空,给调整人数也为空
+            this.getModel().setValue("nckd_postadjustnum",null,row);
+        }else{
+            // 调整后编制人数不为空,计算调整人数,获取现有编制人数
+            int relcyearstaff = getIntValue("nckd_relcyearstaff", row);
+            int newValueInt = (int) newValue;
+            if(newValueInt<0){
+                this.getView().showErrorNotification("编制人数不能小于0!");
+                this.getModel().setValue("nckd_postadjustlatenum",oldValue,row);
+                return;
+            }
+            this.getModel().setValue("nckd_postadjustnum",newValueInt - relcyearstaff,row);
+        }
+    }
+
+    // 组织调整后直属编制人数
+    private void relbdirectnum(Object newValue,Object oldValue,int row){
+        if(ObjectUtils.isEmpty(newValue)){
+            // 调整后编制人数为空,给调整人数也为空
+            this.getModel().setValue("nckd_adjustdire",null,row);
+        }else{
+            int relcyearstaff = getIntValue("nckd_bdirectnum", row);
+            int newValueInt = (int) newValue;
+            if(newValueInt<0){
+                this.getView().showErrorNotification("编制人数不能小于0!");
+                this.getModel().setValue("nckd_relbdirectnum",oldValue,row);
+                return;
+            }
+            this.getModel().setValue("nckd_adjustdire",newValueInt - relcyearstaff,row);
+        }
+    }
+
+    // 组织调整后直属编制人数
+    private void nckdadjustlatenum(Object newValue,Object oldValue,int row){
+        if(ObjectUtils.isEmpty(newValue)){
+            // 调整后编制人数为空,给调整人数也为空
+            this.getModel().setValue("nckd_adjustnum",null,row);
+        }else{
+            int relcyearstaff = getIntValue("nckd_brealnum", row);
+            int newValueInt = (int) newValue;
+            if(newValueInt<0){
+                this.getView().showErrorNotification("编制人数不能小于0!");
+                this.getModel().setValue("nckd_adjustlatenum",oldValue,row);
+                return;
+            }
+            this.getModel().setValue("nckd_adjustnum",newValueInt - relcyearstaff,row);
+        }
+    }
+
+
+    // 组织 直属调整人数修改
+    private void adjustdireChange(Object newValue,Object oldValue,int row) {
+        // 获取直属编制人数
+        int nckdBrealnum =getIntValue(NCKD_BDIRECTNUM,row);
+        int nckdAdjustlatenum = 0;
+        // 调整人数
+        int i = newValue == null ? 0 : (int) newValue;
+        nckdAdjustlatenum = nckdBrealnum +  i;
+        if(nckdAdjustlatenum<0){
+            this.getView().showErrorNotification("编制人数不能小于0!");
+            this.getModel().setValue(NCKD_ADJUSTDIRE,oldValue,row);
+            return;
+        }
+        this.getModel().setValue(NCKD_RELBDIRECTNUM,nckdAdjustlatenum,row);
+    }
+
+
+    // 组织含下级调整人数修改
+    private void adjustnumChange(Object newValue,Object oldValue,int row) {
+        // 获取现有编制人数
+        int nckdBrealnum =getIntValue(NCKD_BREALNUM,row);
+        int nckdAdjustlatenum = 0;
+        // 调整人数
+        int i = newValue == null ? 0 : (int) newValue;
+        nckdAdjustlatenum = nckdBrealnum +  i;
+        if(nckdAdjustlatenum<0){
+            this.getView().showErrorNotification("编制人数不能小于0!");
+            this.getModel().setValue(NCKD_ADJUSTNUM,oldValue,row);
+            return;
+        }
+        this.getModel().setValue(NCKD_ADJUSTLATENUM,nckdAdjustlatenum,row);
+    }
+
+    private void adjusttype(Object newValue,Object oldValue,int row) {
+        int nckdAdjustlatenum = 0;
+        // 调整人数
+        int i = this.getModel().getValue(NCKD_ADJUSTNUM,row) == null ? 0 : (int) this.getModel().getValue(NCKD_ADJUSTNUM,row);
+        // 获取现有编制人数
+        int nckdBrealnum =getIntValue(NCKD_BREALNUM,row);
+        if(StringUtils.isEmpty((String)newValue)){
+            this.getModel().setValue(NCKD_ADJUSTLATENUM,null,row);
+            return;
+        }else if(ADJUSTTYPE_ADD.equals(newValue)){
+            nckdBrealnum = nckdBrealnum +  i;
+        }else{
+            nckdBrealnum = nckdBrealnum - i;
+        }
+        if(nckdBrealnum<0){
+            this.getView().showErrorNotification("编制人数不能小于0!");
+            this.getModel().setValue(NCKD_ADJUSTTYPE,oldValue,row);
+            return;
+        }
+        this.getModel().setValue(NCKD_ADJUSTLATENUM,nckdBrealnum,row);
+    }
+
+    private void postadjusttypeChange(Object newValue,Object oldValue,int row) {
+        int nckdAdjustlatenum = 0;
+        // 调整人数
+        int i = this.getModel().getValue(NCKD_POSTADJUSTNUM,row) == null ? 0 : (int) this.getModel().getValue(NCKD_POSTADJUSTNUM,row);
+        // 获取现有编制人数
+        int nckdBrealnum =getIntValue(NCKD_RELCYEARSTAFF,row);
+        // 获取对应调整类型
+        if(StringUtils.isEmpty((String)newValue)){
+            this.getModel().setValue(NCKD_POSTADJUSTLATENUM,null,row);
+            return;
+        }else if(ADJUSTTYPE_ADD.equals(newValue)){
+            nckdBrealnum = nckdBrealnum +  i;
+        }else{
+            nckdBrealnum = nckdBrealnum - i;
+        }
+        if(nckdBrealnum<0){
+            this.getView().showErrorNotification("编制人数不能小于0!");
+            this.getModel().setValue(NCKD_POSTADJUSTTYPE,oldValue,row);
+            return;
+        }
+        this.getModel().setValue(NCKD_POSTADJUSTLATENUM,nckdBrealnum,row);
+
+    }
+
+    private void postadjustnumChange(Object newValue,Object oldValue,int row) {
+        // 获取对应调整类型
+        String nckdAdjusttype = (String) this.getModel().getValue(NCKD_POSTADJUSTTYPE);
+        if(StringUtils.isEmpty(nckdAdjusttype)){
+            this.getView().showErrorNotification("请先维护调整类型!");
+        }else{
+            // 获取现有编制人数
+            int nckdBrealnum =getIntValue(NCKD_RELCYEARSTAFF,row);
+            int nckdAdjustlatenum = 0;
+            // 调整人数
+            int i = newValue == null ? 0 : (int) newValue;
+            if(ADJUSTTYPE_ADD.equals(nckdAdjusttype)){
+                // 增加调整人数
+                nckdAdjustlatenum = nckdBrealnum +  i;
+            }else{
+                nckdAdjustlatenum = nckdBrealnum -  i;
+            }
+            if(nckdAdjustlatenum<0){
+                this.getView().showErrorNotification("编制人数不能小于0!");
+                this.getModel().setValue(NCKD_POSTADJUSTNUM,oldValue,row);
+                return;
+            }
+            this.getModel().setValue(NCKD_POSTADJUSTLATENUM,nckdAdjustlatenum,row);
+        }
+    }
+
+
+    public int getIntValue(String key,int row) {
+
+        Object value = this.getModel().getValue(key, row);
+        if (value == null) {
+            return 0; // 返回默认值 0
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        try {
+            return Integer.parseInt(value.toString());
+        } catch (NumberFormatException e) {
+            return 0; // 如果解析失败,返回默认值 0
+        }
+    }
+
+
+    private void initEntry(Object org,Object staff) {
+        // 初始化申请单据
+        if(isNotEmpty(org) && isNotEmpty(staff)){
+            // 填充申请单据数据
+            DynamicObject staff1 = (DynamicObject) staff;
+//            DynamicObject haosStaff = BusinessDataServiceHelper.loadSingle(staff1.getPkValue(), "haos_staff");
+            IFormView view = this.getView();
+            AbstractFormDataModel model = (AbstractFormDataModel)this.getModel();
+            List<Long> orgIds = Arrays.asList((Long) ((DynamicObject) org).getPkValue());
+            // 组织id
+            Long orgid = (Long) ((DynamicObject) org).getPkValue();
+            // 编制维护id
+            Long pkValue = (Long) staff1.getPkValue();
+            Object[] objects3 = new Object[1];
+            objects3[0] = pkValue;
+//            HRMServiceResult haosStaffResponse =  DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IStaffExternalService","queryStaffById",objects3);
+
+            Date date = new Date();
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String dateStr = sdf.format(date);
+
+            // 拼接 SQL 查询
+            StringBuilder sql = new StringBuilder("SELECT a.fid AS id, a.fboid AS boid, A.fparentid as parentorg, "
+                    + "t.flevel as level1, t.fstructlongnumber as structlongnumber, ST.fcount as count,ST.fcontainsubcount as containsubcount, "
+//                    + "(select top 1 N.fstaffcount  from t_haos_adminorg M where a.fparentid = M.fboid ) as "
+//                    + " (select top 1 CASE WHEN N.fstaffcount IS NULL THEN 999999 ELSE N.fstaffcount END AS fstaffcount  "
+//                    + "from t_haos_adminorg M left join t_haos_dutyorgdetail N on N.fdutyorgid = M.fid "
+//                    + "where M.fenable ='1'  and M.fboid  =a.fboid and N.fenable = '1' and N.fleffdt >='" + dateStr + "' "
+//                    + "AND M.fbsed <= '" + dateStr + "' AND M.fbsled >= '" + dateStr + "'  "
+//                    + "order by M.fhisversion desc) as staffcount "
+                    + "CASE "
+                    + "    WHEN (SELECT TOP 1 CASE WHEN N.fstaffcount IS NULL THEN 999999 ELSE N.fstaffcount END "
+                    + "          FROM t_haos_adminorg M "
+                    + "          LEFT JOIN t_haos_dutyorgdetail N ON N.fdutyorgid = M.fid "
+                    + "          WHERE M.fenable = '1' "
+                    + "          AND M.fboid = a.fboid "
+                    + "          AND N.fenable = '1' "
+                    + "          AND N.fid = ?  "
+                    + "          AND N.fleffdt >= '" + dateStr + "' "
+                    + "          AND M.fbsed <= '" + dateStr + "' "
+                    + "          AND M.fbsled >= '" + dateStr + "' "
+                    + "          ORDER BY M.fhisversion DESC) IS NULL "
+                    + "    THEN 999999 "
+                    + "    ELSE (SELECT TOP 1 CASE WHEN N.fstaffcount IS NULL THEN 999999 ELSE N.fstaffcount END "
+                    + "          FROM t_haos_adminorg M "
+                    + "          LEFT JOIN t_haos_dutyorgdetail N ON N.fdutyorgid = M.fid "
+                    + "          WHERE M.fenable = '1' "
+                    + "          AND M.fboid = a.fboid "
+                    + "          AND N.fenable = '1' "
+                    + "          AND N.fid = ?  "
+                    + "          AND N.fleffdt >= '" + dateStr + "' "
+                    + "          AND M.fbsed <= '" + dateStr + "' "
+                    + "          AND M.fbsled >= '" + dateStr + "' "
+                    + "          ORDER BY M.fhisversion DESC) "
+                    + "END AS staffcount "
+
+                    + "FROM T_HAOS_ADMINORG A "
+                    + "LEFT JOIN T_HAOS_STAFFORGEMPCOUNT ST on A.fboid = ST.fuseorgboid "
+                    + "LEFT JOIN T_HAOS_ADMINSTRUCT T ON A.fboid = T.fadminorgid "
+                    + "AND T.fiscurrentversion = '0' AND T.fdatastatus = '1' AND T.fstructprojectid = 1010 "
+                    + "AND T.finitstatus = '2' AND T.fbsed <= '" + dateStr + "' "
+                    + "AND T.fbsled >= '" + dateStr + "' AND T.fenable = '1' "
+                    + "LEFT JOIN T_HAOS_ORGSORTCODE S ON S.FADMINORGID = A.fboid "
+                    + "AND S.fiscurrentversion = '0' AND S.fdatastatus = '1' AND S.finitstatus = '2' "
+                    + "AND S.fbsed <= '" + dateStr + "' AND S.fbsled >= '" + dateStr + "' "
+                    + "AND S.fenable = '1' "
+                    + "LEFT JOIN T_HAOS_DUTYORGDETAIL M ON M.fdutyorgid = A.fboid "
+                    + "WHERE A.fiscurrentversion = '0' AND A.fdatastatus = '1' AND A.finitstatus = '2' "
+                    + "AND A.fbsed <= '" + dateStr + "' AND A.fbsled >= '" + dateStr + "' "
+                    + "AND A.fenable = '1' "
+                    + "AND ( T.fstructlongnumber LIKE ( select top 1 concat(F.fstructlongnumber,'%')  from  T_HAOS_ADMINSTRUCT F where  F.fadminorgid = ? "
+                    + "AND F.fiscurrentversion = '0' AND F.fdatastatus = '1' AND F.fstructprojectid = 1010 "
+                    + "AND F.finitstatus = '2' AND F.fbsed <= '" + dateStr + "' "
+                    + "AND F.fbsled >= '" + dateStr + "' AND F.fenable = '1' "
+                    +") " +") "
+                    + "ORDER BY S.fsortcode");
+//            Object[] param = new Object[]{(Long) orgid,haosStaff.getPkValue()};
+            Object[] param = new Object[]{pkValue,pkValue,(Long) orgid};
+            DataSet dataSet = HRDBUtil.queryDataSet("haos_adminOrgHisSearch", new DBRoute("hr"), sql.toString(), param);
+//            DataSet dataSet = DB.queryDataSet("leaseContractPushCard", DBRoute.of("hr"), sql);
+
+            ORM orm = ORM.create();
+            DynamicObjectCollection retDynCol = orm.toPlainDynamicObjectCollection(dataSet.copy());
+            List<Long> collect2 = new ArrayList<Long>();
+            Set<Long> longSet = new HashSet<Long>();
+            // 处理实际人数
+            for (int i = 0; i < retDynCol.size(); i++) {
+                int index = this.getModel().insertEntryRow("nckd_bentryentity", i);
+                this.getModel().setValue("nckd_adminorg",retDynCol.get(i).get("BOID"),index);
+                this.getModel().setValue("nckd_parentorg",retDynCol.get(i).get("parentorg"),index);
+                // 编制人数
+                int staffcount = (int)retDynCol.get(i).get("staffcount");
+                Object staffcountobj = staffcount == 999999 ? null : staffcount;
+                this.getModel().setValue("nckd_brealnum",staffcountobj,index);
+                this.getModel().setValue("nckd_adjustlatenum",staffcountobj,index);
+                // 实际人数
+//                this.getModel().setValue("nckd_rellownum",retDynCol.get(i).get("containsubcount"),index);
+                this.getModel().setValue("nckd_relnum",retDynCol.get(i).get("count"),index);
+                collect2.add((Long) retDynCol.get(i).get("BOID"));
+                longSet.add((Long) retDynCol.get(i).get("parentorg"));
+            }
+            // 获取到部门key,根据部门key获取到岗位信息,HR岗位hbpm_positionhr
+            QFilter qFilter = new QFilter("adminorg.id", QCP.in, collect2);
+            // 查询岗位信息
+            Date date2 = DateUtils.stringToDate(dateStr, DatePattern.YYYY_MM_DD_HH_MM_SS);
+            qFilter.and(new QFilter("bsled", QCP.large_equals, date2)).and("disabler",QCP.equals,0);
+            DynamicObjectCollection query = QueryServiceHelper.query("hbpm_positionhr", "id,adminorg,adminorg.id,number,name,hisversion,createtime", new QFilter[]{qFilter}, "number,createtime desc,hisversion");
+            // 岗位id
+            Set<Long> positionIds = new HashSet<Long>();
+
+            //  创建一个Map来存储结果
+            Map<String, List<DynamicObject>> resultMap = Arrays.stream(query.toArray(new DynamicObject[0]))
+                    .collect(Collectors.toMap(
+                            obj -> String.valueOf(obj.get("adminorg.id")),
+                            obj -> {
+                                // 使用一个列表来存储当前 adminorg.id 下的对象
+                                List<DynamicObject> list = new ArrayList<>();
+                                list.add(obj);
+                                positionIds.add((Long) obj.get("id"));
+                                return list;
+                            },
+                            (existingList, newList) -> {
+                                // 使用一个Set来记录已有的number值
+                                Set<String> existingNumbers = existingList.stream()
+                                        .map(o -> String.valueOf(o.get("number")))
+                                        .collect(Collectors.toSet());
+
+                                // 只添加那些number值不在existingNumbers中的对象
+                                newList.forEach(o -> {
+                                    String newNumber = String.valueOf(o.get("number"));
+                                    if (!existingNumbers.contains(newNumber)) {
+                                        existingList.add(o);
+                                        positionIds.add((Long) o.get("id"));
+                                    }
+                                });
+                                return existingList;
+                            }
+                    ));
+            // 调用服务,获取岗位编制信息
+            Object[] objects2 = new Object[2];
+            objects2[0] = new Date();
+
+            List<Long> positionIdList = positionIds.stream()
+                    .collect(Collectors.toList());
+            objects2[1] = positionIdList;
+//            StaffResponse<Map<String, Map<String, Object>>> staffpositionResponse =  DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IHAOSStaffService","queryPositionStaffInfo",objects2);
+//            BusinessDataServiceHelper.load("haos_dutyorgdetail","id,useorgbo",new QFilter[]{});
+
+            QFilter qFilter3 = new QFilter("staff.id", QCP.equals, pkValue)
+                    .and("enable", QCP.not_equals, "0")
+                    .and("datastatus",QCP.not_equals,"-2")
+                    .and("datastatus",QCP.not_equals,"2")
+                    .and("datastatus",QCP.not_equals,"-1")
+                    .and("dutyworkrole.boid",QCP.in,positionIdList);
+
+
+            DynamicObject[] haosDutyorgdetails = BusinessDataServiceHelper.load("haos_muldimendetail", "id,staff,dutyworkrole,dutyworkrole.boid,yearstaff", new QFilter[]{qFilter3});
+            Map<String, DynamicObject> map2 =
+                    Arrays.stream(haosDutyorgdetails)
+                            .collect(Collectors.toMap(
+                                    detail -> detail.get("dutyworkrole.boid").toString(), // 获取 useorg.id 作为 key
+                                    detail -> detail, // 整个 DynamicObject 作为 value
+                                    (existing, replacement) -> existing // 保留前面的值
+                            ));
+
+
+            Object[] objects = new Object[2];
+            objects[0] = new Date();
+            objects[1] = collect2;
+            QFilter qFilter1 = new QFilter("useorgbo", QCP.in, collect2)
+                    .and("enable", QCP.equals,"1");
+            // 使用组织明细
+            DynamicObject[] haosUseorgdetails = BusinessDataServiceHelper.load("haos_useorgdetail", "id,useorgbo,useorg.id,yearstaff", new QFilter[]{qFilter1});
+            Map<String, DynamicObject> map =
+                    Arrays.stream(haosUseorgdetails)
+                            .collect(Collectors.toMap(
+                                    detail -> detail.get("useorgbo").toString(), // 获取 useorg.id 作为 key
+                                    detail -> detail, // 整个 DynamicObject 作为 value
+                                    (existing, replacement) -> existing // 保留前面的值
+                            ));
+
+//            StaffResponse<Map<String, Map<String, Object>>> staffResponse = (StaffResponse<Map<String, Map<String, Object>>>) DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IHAOSStaffService","queryUseStaffInfo",objects);
+            Map<Long, String> orgLongNameMap = OrgBatchBillHelper.getOrgLongName(longSet, new Date(), String.valueOf(AdminOrgConstants.ADMINORG_STRUCT));
+            this.initTree(map,map2,resultMap,orgLongNameMap);
+            this.getView().updateView("nckd_bentryentity");
+            this.getView().updateView("nckd_centryentity");
+
+        }
+    }
+
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        String itemKey = evt.getItemKey();
+        if (StringUtils.equalsIgnoreCase("bentryentity", itemKey)) {
+//            this.subEntryEntitySetVal1();
+            // 点击事件之后需更新单据体&子单据体控件视图
+            this.getView().updateView("nckd_bentryentity");
+            this.getView().updateView("nckd_centryentity");
+        }
+        super.itemClick(evt);
+    }
+
+
+    // 分配树形结构
+    private void initTree(Map<String, DynamicObject> staffResponse,Map<String, DynamicObject> staffpositionResponse,Map<String, List<DynamicObject>> postMap,Map<Long, String> orgLongNameMap) {
+        // 获取他的单据体,循环单据体
+        DynamicObjectCollection entryentityCols = this.getModel().getDataEntity(true).getDynamicObjectCollection("nckd_bentryentity");
+        for (int i = 0; i < entryentityCols.size(); i++) {
+            //  给单据体添加子单据体数据
+
+            DynamicObject centrydynamicObject = entryentityCols.get(i);
+            List<DynamicObject> nckdAdminorg1 = postMap.get(String.valueOf(centrydynamicObject.getDynamicObject("nckd_adminorg").getPkValue()));
+
+
+            DynamicObject enObj = entryentityCols.get(i);
+            // 子单据体标识nckd_centryentity
+            DynamicObjectCollection cntryEntity = enObj.getDynamicObjectCollection("nckd_centryentity");
+            if(ObjectUtils.isNotEmpty(nckdAdminorg1)){
+                for (DynamicObject object : nckdAdminorg1) {
+                    DynamicObject positionMap = staffpositionResponse.get(String.valueOf(object.get("id")));
+                    if(ObjectUtils.isNotEmpty(positionMap)){
+                        DynamicObject dynamicObject = new DynamicObject(cntryEntity.getDynamicObjectType());
+                        DynamicObject dynamicObject2 = BusinessDataServiceHelper.newDynamicObject("hbpm_positionhr");
+                        dynamicObject2.set("id",object.get("id"));
+                        BaseDataServiceHelper.clearCache(dynamicObject);
+                        dynamicObject.set("nckd_cdutyworkrole",dynamicObject2);
+                        dynamicObject.set("nckd_cdutyworknumber",object.getString("number"));
+                        dynamicObject.set("nckd_relcyearstaff",positionMap.get("yearstaff"));
+
+                        dynamicObject.set("nckd_postadjustlatenum",positionMap.get("yearstaff"));
+                        cntryEntity.add(dynamicObject);
+                    }
+                }
+            }
+
+            // 使用组织上级id
+            Long aLong = (Long) entryentityCols.get(i).get("nckd_parentorg");
+
+
+            if(null == aLong){
+                centrydynamicObject.set("pid",0);
+                continue;
+            }
+            if(StringUtils.isNotEmpty(orgLongNameMap.get(aLong))){
+                centrydynamicObject.set("nckd_bparentlongname",orgLongNameMap.get(aLong));
+            }
+            Optional<DynamicObject> matchingObject = entryentityCols.stream()
+                    .filter(obj -> {
+                        long id = obj.getDynamicObject("nckd_adminorg").getLong("id");
+                        return id == aLong;
+                    })
+                    .findFirst();  // 找到第一个匹配的对象
+
+            // 处理匹配的结果
+            if (matchingObject.isPresent()) {
+                // 处理匹配的 DynamicObject
+                DynamicObject result = matchingObject.get();
+                // 执行所需操作
+                centrydynamicObject.set("pid",result.getPkValue());
+
+            } else {
+                // 处理未找到匹配的情况
+                centrydynamicObject.set("pid",0);
+            }
+            DynamicObject nckdAdminorg = staffResponse.get(String.valueOf(centrydynamicObject.getDynamicObject("nckd_adminorg").getPkValue()));
+            if(ObjectUtils.isNotEmpty(nckdAdminorg)){
+                // 编制人数
+                centrydynamicObject.set("nckd_bdirectnum",nckdAdminorg.get("yearstaff"));
+                centrydynamicObject.set(NCKD_RELBDIRECTNUM,nckdAdminorg.get("yearstaff"));
+            }
+        }
+
+        DynamicObjectCollection entryentityCols2 = this.getModel().getDataEntity(true).getDynamicObjectCollection("nckd_bentryentity");
+        DynamicObjectCollection entryentityCols3 = entryentityCols2;
+        Map<Long, List<DynamicObject>> resultMap = Arrays.stream(entryentityCols2.toArray(new DynamicObject[0]))
+                .collect(Collectors.groupingBy(
+                        obj -> (Long) obj.get("pid") // 使用 pid 作为 key
+                ));
+
+        entryentityCols3.stream().forEach(obj -> {
+
+            List<DynamicObject> dynamicObjects = resultMap.get(obj.getPkValue());
+            if(ObjectUtils.isEmpty(dynamicObjects)){
+                obj.set("nckd_lowermost","A");
+            }
+        });
+
+        DynamicObjectCollection useOrgInfos = this.getModel().getEntryEntity("nckd_bentryentity");
+        DynamicObjectCollection useOrgInfos2 = useOrgInfos;
+        Map<Long, DynamicObject> idVsDynMap = (Map)useOrgInfos.stream().collect(Collectors.toMap((info) -> {
+            return info.getLong("id");
+        }, (info) -> {
+            return info;
+        }, (o1, o2) -> {
+            return o1;
+        }));
+        List<DynamicObject> useOrgInfoSortList = (List)useOrgInfos2.stream().sorted(Comparator.comparing((o) -> {
+            return String.valueOf(o.get("nckd_parentorg"));
+        })).collect(Collectors.toList());
+
+        for(int i = useOrgInfoSortList.size() - 1; i >= 0; --i) {
+            DynamicObject useOrgInfo = (DynamicObject)useOrgInfoSortList.get(i);
+            Object directNumObject = useOrgInfo.get("nckd_relnum");
+            Object realNumWithSubObject = useOrgInfo.get("nckd_rellownum");
+
+            Object nckdBdirectNum = useOrgInfo.get("nckd_bdirectnum");// 直属
+            Object nckdBrealNumWithSub = useOrgInfo.get("nckd_brealnum");// 含下级
+
+            if (!Objects.isNull(directNumObject) || !Objects.isNull(realNumWithSubObject)) {
+                int realNumWithSub = 0;
+                if (Objects.nonNull(realNumWithSubObject)) {
+                    realNumWithSub = (Integer)realNumWithSubObject;
+                }
+
+                int directNum = 0;
+                if (Objects.nonNull(directNumObject)) {
+                    directNum = (Integer)directNumObject;
+                }
+
+                useOrgInfo.set("nckd_rellownum", directNum + realNumWithSub);
+                long parentEntryId = useOrgInfo.getLong("pid");
+                if (parentEntryId != 0L) {
+                    DynamicObject parentDyn = (DynamicObject)idVsDynMap.get(parentEntryId);
+                    if (!Objects.isNull(parentDyn)) {
+                        Object parentRealNumWithSubObject = parentDyn.get("nckd_rellownum");
+                        if (Objects.nonNull(parentRealNumWithSubObject)) {
+                            parentDyn.set("nckd_rellownum", useOrgInfo.getInt("nckd_rellownum") + (Integer)parentRealNumWithSubObject);
+                        }
+                    }
+                }
+            }
+
+//            if (!Objects.isNull(nckdBdirectNum) || !Objects.isNull(nckdBrealNumWithSub)) {
+//                int realNumWithSub = 0;
+//                if (Objects.nonNull(nckdBrealNumWithSub)) {
+//                    realNumWithSub = (Integer)nckdBrealNumWithSub;
+//                }
+//
+//                int directNum = 0;
+//                if (Objects.nonNull(nckdBdirectNum)) {
+//                    directNum = (Integer)nckdBdirectNum;
+//                }
+//                useOrgInfo.set("nckd_brealnum", directNum + realNumWithSub);
+//                long parentEntryId = useOrgInfo.getLong("pid");
+//                if (parentEntryId != 0L) {
+//                    DynamicObject parentDyn = (DynamicObject)idVsDynMap.get(parentEntryId);
+//                    if (!Objects.isNull(parentDyn)) {
+//                        Object parentRealNumWithSubObject = parentDyn.get("nckd_brealnum");
+//                        if (Objects.nonNull(parentRealNumWithSubObject)) {
+//                            parentDyn.set("nckd_brealnum", useOrgInfo.getInt("nckd_brealnum") + (Integer)parentRealNumWithSubObject);
+//                        }
+//                    }
+//                }
+//            }
+
+
+        }
+
+
+    }
+
+
+
+    public static boolean isNotEmpty(Object  key) {
+        // 基础资料判空
+        if(ObjectUtils.isEmpty(key) || ObjectUtils.isEmpty(((DynamicObject)key).getDataStorage())){
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void beforeF7Select(BeforeF7SelectEvent e) {
+
+        String fieldKey = e.getProperty().getName();
+        if (StringUtils.equals(fieldKey, NCKD_HAOS_STAFF)){
+            ListShowParameter showParameter = (ListShowParameter)e.getFormShowParameter();
+            //是否展示审核的改为false
+            showParameter.setShowApproved(false);
+        }else if(StringUtils.equals(fieldKey, "org")){
+            ListShowParameter showParameter = (ListShowParameter)e.getFormShowParameter();
+            //是否展示审核的改为false
+            QFilter qFilter = new QFilter("orgpattern.number", "in", COMPANY_LIST);
+            showParameter.getListFilterParameter().setFilter(qFilter);
+        }
+
+
+    }
+
+    @Override
+    public void entryRowClick(RowClickEvent evt) {
+        // 获取选中的行
+        EntryGrid entryentity = this.getView().getControl("nckd_bentryentity");
+        int[] selectRows = entryentity.getSelectRows();
+        boolean flag = false;
+        if (selectRows.length > 0) {
+            DynamicObject rowEntity = this.getModel().getEntryRowEntity("nckd_bentryentity", selectRows[0]);
+            DynamicObject nckdAdminorg = rowEntity.getDynamicObject("nckd_adminorg");
+            this.getModel().setValue("nckd_useorg",nckdAdminorg);
+            return;
+        }
+        this.getModel().setValue("nckd_useorg",null);
+    }
+
+    @Override
+    public void registerListener(EventObject e) {
+        // 监听单据体行点击事件
+        EntryGrid entryGrid = this.getView().getControl("nckd_bentryentity");
+        entryGrid.addRowClickListener(this);
+        BasedataEdit fieldEdit = this.getView().getControl(NCKD_HAOS_STAFF);//基础资料字段标识
+        BasedataEdit fieldEdit2 = this.getView().getControl("org");//基础资料字段标识
+        fieldEdit.addBeforeF7SelectListener(this);
+        fieldEdit2.addBeforeF7SelectListener(this);
+    }
+
+}

+ 141 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/operate/PrepareAdjustApplicationAuditOpPlugin.java

@@ -0,0 +1,141 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.operate;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.DispatchServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.hr.haos.business.service.staff.externalInterface.bean.StaffBo;
+import kd.hr.haos.business.service.staff.externalInterface.bean.StaffUseOrgBo;
+import kd.hr.hbp.common.mservice.HRMServiceResult;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * Module           :HR中控服务云-HR基础组织-人力编制-编制调整审批同步操作
+ * Description      :编制调整审批同步操作
+ *
+ * @author Tyx
+ * @date  2025/4/15
+ * 标识 nckd_preadjustapplic
+ */
+
+public class PrepareAdjustApplicationAuditOpPlugin extends AbstractOperationServicePlugIn {
+
+
+    private static Log logger = LogFactory.getLog(PrepareAdjustApplicationAuditOpPlugin.class);
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+        // 提前加载表单里的字段
+        List<String> fieldKeys = e.getFieldKeys();
+        MainEntityType dt = MetadataServiceHelper.getDataEntityType("nckd_preadjustapplic");
+        Map<String, IDataEntityProperty> fields = dt.getAllFields();
+        fields.forEach((Key, value) -> {
+            fieldKeys.add(Key);
+        });
+    }
+
+
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+        e.addValidator(new AbstractValidator() {
+            @Override
+            public void validate() {
+                ExtendedDataEntity[] dataEntities = this.getDataEntities();
+                for (ExtendedDataEntity dataEntity : dataEntities) {
+                    DynamicObject dataEntityObj = dataEntity.getDataEntity();
+
+                    Long staffid = (Long) dataEntityObj.getDynamicObject("nckd_haos_staff").getPkValue();
+                    Object[] objects3 = new Object[1];
+                    objects3[0] = staffid;
+                    String errorMsg = null;
+                    // 获取编制信息
+                    HRMServiceResult haosStaffResponse =  DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IStaffExternalService","queryStaffById",objects3);
+                    logger.info("调用查询服务获取编制信息返回结果:"+haosStaffResponse);
+                    if("success".equals(haosStaffResponse.getReturnCode())){
+                        // 调用成功
+                        StaffBo returnData = (StaffBo) haosStaffResponse.getReturnData();
+                        List<StaffUseOrgBo> useOrgEntryBoList = returnData.getUseOrgEntryBoList();
+                        // 获取循环单据体数据,找到编制中匹配的数据,然后进行
+                        DynamicObjectCollection entryentityCols = dataEntityObj.getDynamicObjectCollection("nckd_bentryentity");
+                        // 使用流构建一个Map<Long,DynamicObject>
+                        Map<Long, DynamicObject> resultMap = Arrays.stream(entryentityCols.toArray(new DynamicObject[0]))
+                                .collect(Collectors.toMap(
+                                        obj -> (Long) obj.get("nckd_adminorg.id"),
+                                        obj -> {
+                                            return obj;
+                                        }
+                                ));
+                        useOrgEntryBoList.stream().forEach(useOrgEntryBo -> {
+                            DynamicObject dynamicObject = resultMap.get(useOrgEntryBo.getAdminOrgBoId());
+                            if(ObjectUtils.isNotEmpty(dynamicObject)){
+                                // 含上级的编制人数
+                                Object nckdAdjustlatenum = dynamicObject.get("nckd_adjustlatenum");
+                                // 直属人数
+                                Object nckdRelbdirectnum = dynamicObject.get("nckd_relbdirectnum");
+                                if(!StringUtils.equals("A", (String) dynamicObject.get("nckd_lowermost"))){
+                                    // 更新组织编制人数
+                                    useOrgEntryBo.setYearStaffNumWithSub(ObjectUtils.isNotEmpty(nckdAdjustlatenum) ? (Integer) nckdAdjustlatenum : null);
+                                    useOrgEntryBo.setYearStaff(ObjectUtils.isNotEmpty(nckdRelbdirectnum) ? (Integer) nckdRelbdirectnum : null);
+                                }else{
+                                    useOrgEntryBo.setYearStaff(ObjectUtils.isNotEmpty(nckdRelbdirectnum) ? (Integer) nckdRelbdirectnum : null);
+                                }
+                                // 更新岗位编制人数,如果不存在岗位则跳过
+                                DynamicObjectCollection nckdCentryentity = dynamicObject.getDynamicObjectCollection("nckd_centryentity");
+                                if(ObjectUtils.isNotEmpty(nckdCentryentity)){
+                                    Map<Long, DynamicObject> centrMap = Arrays.stream(nckdCentryentity.toArray(new DynamicObject[0]))
+                                            .collect(Collectors.toMap(
+                                                    obj -> (Long) obj.get("nckd_cdutyworkrole.id"),
+                                                    obj -> {
+                                                        return obj;
+                                                    }
+                                            ));
+                                    useOrgEntryBo.getPositionDimensionBoList().stream().forEach(positionDimensionBo -> {
+                                        DynamicObject centerDynamicObject = centrMap.get(positionDimensionBo.getKeyFieldId());
+                                        if(ObjectUtils.isNotEmpty(centerDynamicObject)){
+                                            Object nckdPostadjustlatenum = centerDynamicObject.get("nckd_postadjustlatenum");
+                                            positionDimensionBo.setYearStaff(ObjectUtils.isNotEmpty(nckdPostadjustlatenum) ? (int) nckdPostadjustlatenum : null);
+                                        }
+                                    });
+                                }
+                            }
+                        });
+                        returnData.setUseOrgEntryBoList(useOrgEntryBoList);
+                        Object[] objects = new Object[1];
+                        objects[0] = returnData;
+                        // 调用更新服务
+                        HRMServiceResult haoSaveResult =DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IStaffExternalService","saveStaff",objects);
+                        logger.info("调用保存服务返回结果:"+haoSaveResult);
+                        if(!"success".equals(haoSaveResult.getReturnCode())){
+                            errorMsg = haoSaveResult.getMessage();
+                        }
+                    }else{
+                        errorMsg = haosStaffResponse.getMessage();
+                    }
+                    if (StringUtils.isNotEmpty(errorMsg)) {
+                        this.addErrorMessage(dataEntity, "单据" + dataEntityObj.getString("billno") +","+ errorMsg);
+                    }
+                }
+            }
+        });
+    }
+
+}

+ 142 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/operate/PrepareAdjustApplicationValidator.java

@@ -0,0 +1,142 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.operate;
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.IDataEntityProperty;
+import kd.bos.entity.ExtendedDataEntity;
+import kd.bos.entity.MainEntityType;
+import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
+import kd.bos.entity.plugin.AddValidatorsEventArgs;
+import kd.bos.entity.plugin.PreparePropertysEventArgs;
+import kd.bos.entity.validate.AbstractValidator;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.servicehelper.DispatchServiceHelper;
+import kd.bos.servicehelper.MetadataServiceHelper;
+import kd.hr.haos.business.service.staff.externalInterface.bean.StaffBo;
+import kd.hr.haos.business.service.staff.externalInterface.bean.StaffUseOrgBo;
+import kd.hr.hbp.common.mservice.HRMServiceResult;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Module           :HR中控服务云-HR基础组织-人力编制-编制调整申请审核校验
+ * Description      :编制调整申请操作校验
+ *
+ * @author Tyx
+ * @date  2025/4/15
+ * 标识 nckd_preadjustapplic
+ */
+
+public class PrepareAdjustApplicationValidator extends AbstractOperationServicePlugIn {
+
+    private static Log logger = LogFactory.getLog(PrepareAdjustApplicationValidator.class);
+
+    @Override
+    public void onPreparePropertys(PreparePropertysEventArgs e) {
+        super.onPreparePropertys(e);
+        // 提前加载表单里的字段
+        List<String> fieldKeys = e.getFieldKeys();
+        MainEntityType dt = MetadataServiceHelper.getDataEntityType("nckd_preadjustapplic");
+        Map<String, IDataEntityProperty> fields = dt.getAllFields();
+        fields.forEach((Key, value) -> {
+            fieldKeys.add(Key);
+        });
+    }
+
+
+
+    @Override
+    public void onAddValidators(AddValidatorsEventArgs e) {
+        super.onAddValidators(e);
+        e.addValidator(new AbstractValidator() {
+            @Override
+            public void validate() {
+                ExtendedDataEntity[] dataEntities = this.getDataEntities();
+                for (ExtendedDataEntity dataEntity : dataEntities) {
+                    DynamicObject dataEntityObj = dataEntity.getDataEntity();
+                    boolean flag = false;
+                    // 判断是否为上周单据
+                    Long staffid = (Long) dataEntityObj.getDynamicObject("nckd_haos_staff").getPkValue();
+                    Object[] objects3 = new Object[1];
+                    objects3[0] = staffid;
+                    String errorMsg = null;
+                    // 获取编制信息
+                    HRMServiceResult haosStaffResponse =  DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IStaffExternalService","queryStaffById",objects3);
+                    logger.info("调用查询服务获取编制信息返回结果:"+haosStaffResponse);
+                    if("success".equals(haosStaffResponse.getReturnCode())){
+                        // 调用成功
+                        StaffBo returnData = (StaffBo) haosStaffResponse.getReturnData();
+                        List<StaffUseOrgBo> useOrgEntryBoList = returnData.getUseOrgEntryBoList();
+                        // 获取循环单据体数据,找到编制中匹配的数据,然后进行
+                        DynamicObjectCollection entryentityCols = dataEntityObj.getDynamicObjectCollection("nckd_bentryentity");
+                        // 使用流构建一个Map<Long,DynamicObject>
+                        Map<Long, DynamicObject> resultMap = Arrays.stream(entryentityCols.toArray(new DynamicObject[0]))
+                                .collect(Collectors.toMap(
+                                        obj -> (Long) obj.get("nckd_adminorg.id"),
+                                        obj -> {
+                                            return obj;
+                                        }
+                                ));
+                        useOrgEntryBoList.stream().forEach(useOrgEntryBo -> {
+                            DynamicObject dynamicObject = resultMap.get(useOrgEntryBo.getAdminOrgBoId());
+                            if(ObjectUtils.isNotEmpty(dynamicObject)){
+                                // 含上级的编制人数
+                                Object nckdAdjustlatenum = dynamicObject.get("nckd_adjustlatenum");
+                                // 直属人数
+                                Object nckdRelbdirectnum = dynamicObject.get("nckd_relbdirectnum");
+                                if(!StringUtils.equals("A", (String) dynamicObject.get("nckd_lowermost"))){
+                                    // 更新组织编制人数
+                                    useOrgEntryBo.setYearStaffNumWithSub(ObjectUtils.isNotEmpty(nckdAdjustlatenum) ? (Integer) nckdAdjustlatenum : null);
+                                    useOrgEntryBo.setYearStaff(ObjectUtils.isNotEmpty(nckdRelbdirectnum) ? (Integer) nckdRelbdirectnum : null);
+                                }else{
+                                    useOrgEntryBo.setYearStaff(ObjectUtils.isNotEmpty(nckdRelbdirectnum) ? (Integer) nckdRelbdirectnum : null);
+                                }
+                                // 更新岗位编制人数,如果不存在岗位则跳过
+                                DynamicObjectCollection nckdCentryentity = dynamicObject.getDynamicObjectCollection("nckd_centryentity");
+                                if(ObjectUtils.isNotEmpty(nckdCentryentity)){
+                                    Map<Long, DynamicObject> centrMap = Arrays.stream(nckdCentryentity.toArray(new DynamicObject[0]))
+                                            .collect(Collectors.toMap(
+                                                    obj -> (Long) obj.get("nckd_cdutyworkrole.id"),
+                                                    obj -> {
+                                                        return obj;
+                                                    }
+                                            ));
+                                    useOrgEntryBo.getPositionDimensionBoList().stream().forEach(positionDimensionBo -> {
+                                        DynamicObject centerDynamicObject = centrMap.get(positionDimensionBo.getKeyFieldId());
+                                        if(ObjectUtils.isNotEmpty(centerDynamicObject)){
+                                            Object nckdPostadjustlatenum = centerDynamicObject.get("nckd_postadjustlatenum");
+                                            positionDimensionBo.setYearStaff(ObjectUtils.isNotEmpty(nckdPostadjustlatenum) ? (int) nckdPostadjustlatenum : null);
+                                        }
+                                    });
+                                }
+                            }
+                        });
+                        returnData.setUseOrgEntryBoList(useOrgEntryBoList);
+                        Object[] objects = new Object[1];
+                        objects[0] = returnData;
+                        // 调用更新服务
+
+                        // 调用校验服务
+                        HRMServiceResult haosValidateResult = DispatchServiceHelper.invokeService("kd.hrmp.haos.servicehelper","haos","IStaffExternalService","validateStaff",objects);
+                        logger.info("调用校验服务返回结果:"+haosValidateResult);
+                        if(!"success".equals(haosValidateResult.getReturnCode())){
+                            errorMsg = haosValidateResult.getMessage();
+                        }
+                    }else{
+                        errorMsg = haosStaffResponse.getMessage();
+                    }
+                    // 判断是否在本周内
+                    if (StringUtils.isNotEmpty(errorMsg)) {
+                        this.addErrorMessage(dataEntity, "单据" + dataEntityObj.getString("billno") +","+ errorMsg);
+                    }
+                }
+            }
+        });
+    }
+
+}

+ 89 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/task/PositionTree.java

@@ -0,0 +1,89 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.task;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PositionTree implements Serializable
+{
+
+    /** 节点ID */
+    private Long id;
+
+    /** 节点名称 */
+    private String label;
+
+    /** 父ID */
+    private Long parentId;
+
+    /** 子节点 */
+    private List<PositionTree> children = new ArrayList<>();
+    private Integer level;
+
+    public PositionTree()
+    {
+
+    }
+
+    public PositionTree(Long id, String label, Long parentId) {
+        this.id = id;
+        this.label = label;
+        this.parentId = parentId;
+    }
+
+    public PositionTree(PositionTree treeSelect)
+    {
+        this.id = treeSelect.getId();
+        this.label = treeSelect.getLabel();
+        this.children = treeSelect.getChildren();
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public void setLabel(String label)
+    {
+        this.label = label;
+    }
+
+    public Integer getLevel() {
+        return level;
+    }
+
+    public void setLevel(Integer level) {
+        this.level = level;
+    }
+
+    public List<PositionTree> getChildren()
+    {
+        return children;
+    }
+
+    public void setChildren(List<PositionTree> children)
+    {
+        this.children = children;
+    }
+}
+

+ 224 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/task/SyncAdminOrgTask.java

@@ -0,0 +1,224 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.task;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.exception.KDException;
+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.bos.schedule.executor.AbstractTask;
+import kd.bos.util.StringUtils;
+import kd.sdk.plugin.Plugin;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 2025-04-16 Tyx
+ * 组织变动数据同步MDM
+ * 1.组织名称变化/组织调整时,需要把该组织下所有岗位及人员也传给MDM
+ * 2.其余变化时,只需要传递组织信息,岗位人员不需要
+ */
+public class SyncAdminOrgTask extends AbstractTask implements Plugin {
+
+    private static final Log log = LogFactory.getLog(SyncAdminOrgTask.class);
+    private static String changeOperDetail_entity = "haos_changeoperdetail";
+    private static String adminOrgDetail_entity = "haos_adminorgdetail";
+    private static String orgChgRecord_entity = "homs_orgchgrecord";
+    private static String orgChgRecord_e_entity = "orgchgentry";
+    private static String startTime = "";
+    private static String endTime = "";
+
+
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        /**
+         * 获取调度作业下配置的参数:
+         * isAuto : 如果配置为是,则取日志中上一次同步时间至今发生变化的组织信息,为否则优先取以下两个手工配置的时间参数
+         * startTime : 手工配置开始时间 yyyy-MM-dd hh24:mi:ss
+         * endTime : 手工配置结束时间 yyyy-MM-dd hh24:mi:ss
+         */
+        log.info("-------- SyncAdminOrgTask 开始执行同步行政组织 --------");
+        //最后调MDM的jsonObject
+        JSONObject ob = new JSONObject();
+        initDateRange(map);
+        HRBaseServiceHelper helper = new HRBaseServiceHelper(orgChgRecord_entity);
+        String selectProperties = "adminorg,orgchgentry.changescene.number,createtime";
+        //查出发生过变化的组织
+        DynamicObjectCollection changeOperateCol = helper.queryOriginalCollection(selectProperties, this.getQFilters());
+        //查询出来为空直接返回
+        if(CollectionUtils.isEmpty(changeOperateCol)) {
+            log.info("-------- 未查询到变化组织 -------- ");
+            SyncUtil.createLog(SyncUtil.v_error, startTime, endTime, "未查询到变化组织", "未调用", SyncUtil.SyncOrg);
+        }
+        else {
+            //发生变化的组织Map
+            //按照变动场景编码分成不同的List,后续根据场景分开处理
+            //ToDo 如果查询时间内,某个组织先发生组织调整,再修改除名称外其他信息怎么处理?
+            //按照组织分组,只取时间段内最新的一条记录
+            //暂时只处理(10,20,30,40,50);
+            //ToDo(80,90)目前前台界面做不了该业务,后续考虑;
+            //ToDo 60尚不明确,需确认;
+            //1010_S	    组织新设
+            //1020_S	    上级调整
+            //1030_S	    组织更名
+            //1040_S	    组织停用
+            //1050_S	    初始化新增
+            //1060_S	    初始化禁用
+            //1080_S	    组织合并
+            //1090_S	    组织拆分
+            //CS_1110_SY01	组织修订
+//            Map<String, List<DynamicObject>> changeOperateMap = (Map)changeOperateCol.stream().collect(Collectors.groupingBy(
+//                            dyx -> dyx.getString("orgchgentry.changescene.number")
+//                    ));
+            //按组织ID汇总
+            Map<Long, DynamicObject> changeOperateMap = (Map)changeOperateCol.stream().collect(Collectors.toMap((dyx) -> {
+                return dyx.getLong("adminorg");
+            }, (dyx) -> {
+                return dyx;
+            }, (key1, key2) -> {
+                return key2;
+            }));
+            log.info("-------- 变化组织数(不去重):" + changeOperateCol.size() + " --------");
+            log.info("-------- 变化组织数(去重):" + changeOperateMap.keySet().size() + " --------");
+            log.info("-------- 变化组织Id:" + changeOperateMap.keySet() + " --------");
+
+            // ToDo 根据变动操作字段获取发生了组织调整的组织,后续需要根据这些组织同步下级岗位和人员
+            //组织历史查询,如果查询时间内发生多次变化,
+            //haos_adminorgdetail 组织历史查询 所属表t_haos_adminorg Id与t_org_org一样
+            selectProperties = "id,boid,number,name,orglongname,level,nckd_easid,parentorg.id,parentorg.name,parentorg.number,establishmentdate,bsed,enable,adminorgtype.name,disabledate,modifytime";
+            QFilter idQFilter = new QFilter("id", "in", changeOperateMap.keySet());
+            DynamicObject[] orgDyArr = new HRBaseServiceHelper(adminOrgDetail_entity).query(selectProperties, new QFilter[]{idQFilter});
+            //处理下长编码/长名称,星瀚内是4VTX1ACRU8A9!4VU/45/K1PJZ!4W5LG9NNW9EW这种格式 -- 直接取行政组织结构里面的长编码
+            Map<Long, DynamicObject> bosOrgMap = SyncUtil.initOrgInfo();
+            log.info("-------- 查询完组织详细信息数:" + orgDyArr.length + " --------");
+            //构建入参
+            buildJSON(ob, orgDyArr, changeOperateMap, bosOrgMap);
+            //获取调用接口地址
+            String url = SyncUtil.getUrl("org_url");
+            log.info("-------- 组织同步调用url : " + url + " --------");
+            //调用接口
+            JSONObject response = SyncUtil.doPostByHttpClient(url, ob);
+            //处理返回结果
+            String status = SyncUtil.dealResponseStatus(response);
+            //记录日志
+            SyncUtil.createLog(status, startTime, endTime, ob.toJSONString(), response.toJSONString(), SyncUtil.SyncOrg);
+        }
+    }
+
+    /**
+     * 构建最后调用MDM接口的参数
+     * @param ob
+     */
+    public void buildJSON(JSONObject ob, DynamicObject[] orgDyArr, Map<Long, DynamicObject> changeOperateMap, Map<Long, DynamicObject> bosOrgMap) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        JSONArray obj = new JSONArray();
+        for(DynamicObject org : orgDyArr) {
+            JSONObject data = new JSONObject(true);
+            //主体标签 默认JY
+            data.put("MAIN_BODY_ID", "JY");
+            //主键 EAS老组织用EASID 否则用星瀚ID
+            String primary_key = StringUtils.isBlank(org.getString("nckd_easid")) ? org.getPkValue().toString() : org.getString("nckd_easid");
+            data.put("ADM_PRIMARY_KEY", primary_key);
+            //长编码
+            data.put("ADM_LONG_CODE", bosOrgMap.get(org.getLong("id")).getString("structure.longnumber"));
+            //组织名称
+            data.put("MD_DESCRIPTION", org.getString("name"));
+            //上级行政组织ID 看是否有easid 有就传EASID 否则传星瀚ID
+            data.put("ADM_PARENT_NUMBER", org.getString("parentorg.id"));
+            //上级组织名称
+            data.put("ADM_PARENT_NAME", org.getString("parentorg.name"));
+            //行政组织长名称 orglongname没值 去平台那边取
+            data.put("ADM_LONG_NAME", bosOrgMap.get(org.getLong("id")).getString("structure.fullname"));
+            //业务板块 默认11-济煜医药
+            data.put("ADM_BUSINESS_SECTOR", "11");
+            //级别
+            data.put("MD_LEVEL", org.getInt("level"));
+            //行政组织类型
+            data.put("ADM_TYPE", org.getString("adminorgtype.name"));
+            //行政组织旧编码,s-HR中的组织导到星瀚中前面加了JY-,后续新增的使用星瀚的编码规则
+            String orgNumber = org.getString("number");
+            if(orgNumber.indexOf("JY-") == 0) {
+                data.put("ADM_OLD_NUMER", orgNumber.substring(3, orgNumber.length()));
+            }
+            else {
+                data.put("ADM_OLD_NUMER", orgNumber);
+            }
+            //创建时间 取行政组织的创建时间
+            Date createDate = bosOrgMap.get(org.getLong("id")).getDate("createtime");
+            data.put("ADM_CREATION_TIME", sdf.format(createDate));
+            //最后修改时间
+            data.put("ADM_LAST_UPTIME", sdf.format(org.getDate("modifytime")));
+            //行政组织是否封存 enable 0-已停用 1-已启用
+            int enable = org.getInt("enable");
+            if(enable == 0) {
+                data.put("ADM_SEALUP", "是");
+                //行政组织封存时间
+                Date disableDate = org.getDate("disabledate");
+                data.put("ADM_STORAGE_TIME", sdf.format(disableDate));
+            }
+            else {
+                data.put("ADM_SEALUP", "否");
+                data.put("ADM_STORAGE_TIME", null);
+            }
+
+            //是否成本中心实体组织 TODO
+            data.put("ADM_ENTITY_COST_CENTER", null);
+            //是否成本中心组织 TODO
+            data.put("ADM_COST_CENTER", null);
+            //成本中心是否封存 TODO
+            data.put("ADM_COST_CENTER_SEALUP", null);
+            obj.add(data);
+        }
+        SyncUtil.setJSONArraySorted(obj, "MD_LEVEL");
+        ob.put("obj", obj);
+    }
+
+
+    /**
+     * 根据调度作业上配置的参数初始化时间范围
+     * @param map
+     */
+    public void initDateRange(Map<String, Object> map) {
+        boolean isAuto = Boolean.valueOf(map.get("isAuto").toString());
+        if(!isAuto) {
+            startTime = map.get("startTime").toString();
+            endTime = map.get("endTime").toString();
+        }
+        //取最近一次日志的时间
+        else {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            // ToDo
+            //startTime = "";
+            endTime = sdf.format(new Date());
+        }
+    }
+
+    /**
+     * 构建查询变化组织的过滤条件
+     * @return
+     */
+    public QFilter[] getQFilters() {
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            QFilter filter = new QFilter("createtime", ">=", sdf.parse(startTime));
+            filter.and("createtime", "<", sdf.parse(endTime));
+            //filter.and("org.otclassify", "=", 1010L);
+            //filter.and("adminorg.number", QCP.equals, "JY-20240612-0009");
+            log.info("-------- 查询组织变化过滤条件:" + filter.toString() + " --------");
+            return new QFilter[]{filter};
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 20 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/task/SyncPersonTask.java

@@ -0,0 +1,20 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.task;
+
+import kd.bos.context.RequestContext;
+import kd.bos.exception.KDException;
+import kd.bos.schedule.executor.AbstractTask;
+import kd.sdk.plugin.Plugin;
+
+import java.util.Map;
+
+/**
+ * 2025-04-16 Tyx
+ * 人员变动数据同步MDM
+ */
+public class SyncPersonTask extends AbstractTask implements Plugin {
+
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+
+    }
+}

+ 194 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/task/SyncPositionTask.java

@@ -0,0 +1,194 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.task;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.context.RequestContext;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.exception.KDException;
+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.bos.schedule.executor.AbstractTask;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.util.StringUtils;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+import kd.sdk.plugin.Plugin;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 2025-04-16 Tyx
+ * 岗位变动数据同步MDM
+ */
+public class SyncPositionTask extends AbstractTask implements Plugin {
+    private static final Log log = LogFactory.getLog(SyncPositionTask.class);
+    private static String posChgRecord_entity = "hbpm_chgrecord";   //岗位变动明细
+    private static String posDetail_entity = "homs_positionhis";    //岗位历史查询
+    private static String startTime = "";
+    private static String endTime = "";
+    @Override
+    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
+        log.info("-------- SyncPositionTask 开始执行同步岗位 --------");
+        JSONObject ob = new JSONObject();
+        initDateRange(map);
+        HRBaseServiceHelper helper = new HRBaseServiceHelper(posChgRecord_entity);
+        String selectProperties = "position,evententry.changescene.number,createtime";
+        //String selectProperties = "aaa";
+        //查出发生过变化的岗位
+        DynamicObjectCollection changeOperateCol = helper.queryOriginalCollection(selectProperties, this.getQFilters());
+        //查询出来为空直接返回
+        if(CollectionUtils.isEmpty(changeOperateCol)) {
+            log.info("-------- 未查询到变化岗位 -------- ");
+            SyncUtil.createLog(SyncUtil.v_error, startTime, endTime, "未查询到变化岗位", "未调用", SyncUtil.SyncPosition);
+        }
+        else {
+            //按岗位ID汇总,查询时间段内发生变化的岗位
+            Map<Long, DynamicObject> changeOperateMap = (Map)changeOperateCol.stream().collect(Collectors.toMap((dyx) -> {
+                return dyx.getLong("position");
+            }, (dyx) -> {
+                return dyx;
+            }, (key1, key2) -> {
+                return key2;
+            }));
+            log.info("-------- 变化岗位数(不去重):" + changeOperateCol.size() + " --------");
+            log.info("-------- 变化岗位数(去重):" + changeOperateMap.keySet().size() + " --------");
+            log.info("-------- 变化岗位Id:" + changeOperateMap.keySet() + " --------");
+
+            //查询出变化岗位之后,找到对应岗位数据以及所属组织数据
+            selectProperties = "id,adminorg.id,adminorg.name,adminorg.number,adminorg.nckd_easid,parent.name,parent.id,parent.number,number,name,enable,isleader,modifytime";
+            QFilter idQFilter = new QFilter("id", "in", changeOperateMap.keySet());
+            DynamicObjectCollection posDyArr = new HRBaseServiceHelper(posDetail_entity).queryOriginalCollection(selectProperties, new QFilter[]{idQFilter});
+            log.info("-------- 查询完岗位详细信息数:" + posDyArr.size() + " --------");
+            //处理下组织长名称
+            Map<Long, DynamicObject> orgMap = (Map)posDyArr.stream().collect(Collectors.toMap((dyx) -> {
+                return dyx.getLong("adminorg.id");
+            }, (dyx) -> {
+                return dyx;
+            }, (key1, key2) -> {
+                return key2;
+            }));
+            //工具类查询组织相关信息返回Map key为组织ID
+            Map<Long, DynamicObject> orgDyxMap = SyncUtil.initOrgInfo(orgMap);
+            //获取调用接口地址
+            String url = SyncUtil.getUrl("position_url");
+            log.info("-------- 岗位同步调用url : " + url + " --------");
+            //构建入参
+            buildJSON(ob, posDyArr, changeOperateMap, orgDyxMap);
+            //调用接口
+            JSONObject response = SyncUtil.doPostByHttpClient(url, ob);
+            //处理返回结果
+            String status = SyncUtil.dealResponseStatus(response);
+            //记录日志
+            SyncUtil.createLog(status, startTime, endTime, ob.toJSONString(), response.toJSONString(), SyncUtil.SyncPosition);
+        }
+    }
+
+    /**
+     * 构建调MDM接口的入参
+     * @param ob                //最后调用的JSONObject
+     * @param posDyArr          //岗位历史查询,查询出来的岗位相关信息
+     * @param changeOperateMap  //岗位变动明细,备用,暂用不上
+     * @param orgDyxMap         //组织信息,用于处理长名称
+     */
+    public void buildJSON(JSONObject ob, DynamicObjectCollection posDyArr, Map<Long, DynamicObject> changeOperateMap, Map<Long, DynamicObject> orgDyxMap) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        //构造岗位的树形结构
+        Map<Long, Integer> levelMap = SyncUtil.initPositionTree();
+        JSONArray obj = new JSONArray();
+        for(DynamicObject pos : posDyArr) {
+            JSONObject data = new JSONObject(true);
+            //主体标签
+            data.put("MAIN_BODY_ID", "JY");
+            //级别
+            data.put("MD_LEVEL", levelMap.get(pos.getLong("id")));
+            //所属行政组织名称
+            data.put("POSI_SUB_DEP_NAME",pos.getString("adminorg.name"));
+            //所属行政组织ID TODO 是EASID还是星瀚ID?
+            String parentId = StringUtils.isBlank(pos.getString("adminorg.nckd_easid")) ? pos.getString("adminorg.id") : pos.getString("adminorg.nckd_easid");
+            data.put("POSI_SUB_DEP_CODE",parentId);
+            //所属行政组织全程 长名称
+            data.put("POSI_SUB_DEP_LNAME",orgDyxMap.get(pos.getLong("adminorg.id")).getString("structure.fullname"));
+            //汇报岗位名称
+            data.put("POSI_REPPOST_NAME",pos.getString("parent.name"));
+            //汇报岗位编码
+            data.put("POSI_REPPOST_CODE",pos.getString("parent.number"));
+            //岗位编码
+            data.put("MD_CODE",pos.getString("number"));
+            //岗位名称
+            data.put("MD_DESCRIPTION",pos.getString("name"));
+            //岗位状态 MDM:1-启用,2-禁用;星瀚:1-启用,0-禁用
+            int enable = pos.getInt("enable");
+            if(enable == 1) {
+                data.put("POSI_STATE","1");
+            }
+            else if(enable == 0){
+                data.put("POSI_STATE","2");
+            }
+            else {
+                data.put("POSI_STATE","0");
+            }
+
+            //是否负责人职位
+            int isLeader = pos.getInt("isleader");
+            if(isLeader == 1) {
+                data.put("POSI_POSIT_CHARGE","是");
+            }
+            else {
+                data.put("POSI_POSIT_CHARGE","否");
+            }
+            //最后更新时间
+            data.put("POSI_LAST_UPTIME",sdf.format(pos.getDate("modifytime")));
+            obj.add(data);
+        }
+        //入参排序
+        SyncUtil.setJSONArraySorted(obj, "MD_LEVEL");
+        ob.put("obj", obj);
+    }
+
+
+    /**
+     * 根据调度作业上配置的参数初始化时间范围
+     * @param map
+     */
+    public void initDateRange(Map<String, Object> map) {
+        boolean isAuto = Boolean.valueOf(map.get("isAuto").toString());
+        if(!isAuto) {
+            startTime = map.get("startTime").toString();
+            endTime = map.get("endTime").toString();
+        }
+        //取最近一次日志的时间
+        else {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            // ToDo
+            //startTime = "";
+            endTime = sdf.format(new Date());
+        }
+    }
+    /**
+     * 构建查询变化岗位的过滤条件
+     * @return
+     */
+    public QFilter[] getQFilters() {
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            QFilter filter = new QFilter("createtime", ">=", sdf.parse(startTime));
+            filter.and("createtime", "<", sdf.parse(endTime));
+            //filter.and("org.otclassify", "=", 1010L);
+            //filter.and("position.number", QCP.equals, "JY-20250407-01772");
+            log.info("-------- 查询岗位变化过滤条件:" + filter.toString() + " --------");
+            return new QFilter[]{filter};
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+
+}

+ 282 - 0
code/jyyy/nckd-jimin-jyyy-hr/src/main/java/nckd/jimin/jyyy/hr/haos/staff/plugin/task/SyncUtil.java

@@ -0,0 +1,282 @@
+package nckd.jimin.jyyy.hr.haos.staff.plugin.task;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.exception.KDException;
+import kd.bos.logging.Log;
+import kd.bos.logging.LogFactory;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.BusinessDataServiceHelper;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.bos.servicehelper.operation.SaveServiceHelper;
+import kd.bos.util.HttpClientUtils;
+import kd.bos.workflow.exception.WFErrorCode;
+import kd.hr.hbp.business.servicehelper.HRBaseServiceHelper;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class SyncUtil {
+    private static Log logger = LogFactory.getLog(SyncUtil.class);
+    private static String KEY_ENTITY_LOG = "nckd_syncmdmlog";
+    public static String v_success = "A";
+    public static String v_partsuccess = "B";
+    public static String v_error = "C";
+    public static String SyncOrg = "组织同步";
+    public static String SyncPerson = "人员同步";
+    public static String SyncPosition = "岗位同步";
+
+
+    /**
+     * 获取调用MDM的地址,取nckd_commonparams中编码=MDM的参数
+     * @param type
+     * @return
+     */
+    public static String getUrl(String type) {
+        QFilter filter = new QFilter("number", QCP.equals, "MDM");
+        filter.and("nckd_entryentity.nckd_key",QCP.equals, type);
+        DynamicObject bill = QueryServiceHelper.queryOne("nckd_commonparams", "nckd_entryentity.nckd_value", new QFilter[]{filter});
+        logger.info("-------- 获取MDM url:" + bill.getString("nckd_entryentity.nckd_value") + " -------- ");
+        return bill.getString("nckd_entryentity.nckd_value");
+    }
+
+    /**
+     * 初始化组织Id和长编码的关系
+     *
+     * @return
+     */
+    public static Map<Long, DynamicObject> initOrgInfo() {
+        String selectFields = "id,structure.vieworg.id,structure.longnumber,structure.fullname,createtime";
+        QFilter filter = new QFilter("structure.view.id",QCP.equals,1L);
+        DynamicObjectCollection orgs = QueryServiceHelper.query("bos_org", selectFields, new QFilter[]{filter});
+
+        Map<Long, DynamicObject> orgMap = (Map)orgs.stream().collect(Collectors.toMap((org) -> {
+            return org.getLong("id");
+        }, (org) -> {
+            return org;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+
+        return orgMap;
+    }
+
+    public static Map<Long, DynamicObject> initOrgInfo(Map orgMap) {
+        String selectFields = "id,structure.vieworg.id,structure.longnumber,structure.fullname,createtime";
+        QFilter filter = new QFilter("structure.view.id",QCP.equals,1L);
+        filter.and("id",QCP.in,orgMap.keySet());
+        DynamicObjectCollection orgs = QueryServiceHelper.query("bos_org", selectFields, new QFilter[]{filter});
+
+        Map<Long, DynamicObject> orgDyxMap = (Map)orgs.stream().collect(Collectors.toMap((org) -> {
+            return org.getLong("id");
+        }, (org) -> {
+            return org;
+        }, (key1, key2) -> {
+            return key2;
+        }));
+
+        return orgDyxMap;
+    }
+
+    /**
+     * 岗位树形结构,为了解决level字段问题
+     *
+     * @return
+     */
+    public static Map<Long, Integer> initPositionTree() {
+        String selectProperties = "id,parent.id,name,iscurrentversion";
+        QFilter filter = new QFilter("iscurrentversion", QCP.equals, "1");
+        DynamicObjectCollection posDyArr = new HRBaseServiceHelper("homs_positionhis").queryOriginalCollection(selectProperties, new QFilter[]{filter});
+        List<PositionTree> list = new ArrayList<PositionTree>();
+        for (DynamicObject pos : posDyArr) {
+            PositionTree pTree = new PositionTree(pos.getLong("id"), pos.getString("name"), pos.getLong("parent.id"));
+            list.add(pTree);
+        }
+        //构造树形结构,带level
+        List<PositionTree> tree = buildTree(list);
+        //处理实现结构,返回一个id-level的Map
+        Map<Long, Integer> levelMap = convertToIdLevelMap(tree);
+        return levelMap;
+    }
+
+    public static Map<Long, Integer> convertToIdLevelMap(List<PositionTree> roots) {
+        Map<Long, Integer> idLevelMap = new HashMap<>();
+        for(PositionTree root : roots) {
+            traverseTree(root, idLevelMap);
+        }
+        return idLevelMap;
+    }
+
+    private static void traverseTree(PositionTree node, Map<Long, Integer> map) {
+        map.put(node.getId(), node.getLevel());
+        for (PositionTree child : node.getChildren()) {
+            traverseTree(child, map); // 递归处理子节点‌:ml-citation{ref="1,3" data="citationList"}
+        }
+    }
+
+    // 递归打印树
+    private static void printTree(PositionTree node, int indent) {
+        String spaces = String.join("", Collections.nCopies(indent, "  "));
+        System.out.println(spaces + "└─ " + node.getLabel());
+        for (PositionTree child : node.getChildren()) {
+            printTree(child, indent + 1);
+        }
+    }
+
+    /**
+     * 构建完整树形结构
+     * @param nodes 所有平铺节点
+     * @return 根节点列表(支持多根)
+     */
+    public static List<PositionTree> buildTree(List<PositionTree> nodes) {
+        // 1. 创建快速查找的哈希表
+        Map<Long, PositionTree> nodeMap = nodes.stream()
+                .collect(Collectors.toMap(PositionTree::getId, node -> node));
+
+        // 2. 构建父子关系
+        List<PositionTree> roots = new ArrayList<>();
+        for (PositionTree node : nodes) {
+            Long parentId = node.getParentId();
+            if (parentId == null || parentId == 0L) {
+                roots.add(node); // 根节点
+            } else {
+                PositionTree parent = nodeMap.get(parentId);
+                if (parent != null) {
+                    parent.getChildren().add(node);
+                } else {
+                    // 处理孤儿节点(可选)
+                    roots.add(node); // 将无父节点的视为根节点
+                }
+            }
+        }
+
+        // 3. 计算层级(可选扩展)
+        calculateLevels(roots, 0);
+
+        return roots;
+    }
+
+    /** 递归计算层级深度 */
+    private static void calculateLevels(List<PositionTree> nodes, int level) {
+        for (PositionTree node : nodes) {
+            node.setLevel(level);
+            calculateLevels(node.getChildren(), level + 1);
+        }
+    }
+
+    /**
+     * JSONArray排序,按照级次排序,避免下级数据新增的时候上级还没有
+     * @param arr
+     * @param fieldName
+     */
+    public static void setJSONArraySorted(JSONArray arr, String fieldName) {
+        arr.sort((a, b) -> {
+            JSONObject objA = (JSONObject) a;
+            JSONObject objB = (JSONObject) b;
+            return objA.getIntValue(fieldName) - objB.getIntValue(fieldName);
+        });
+    }
+
+    /**
+     *
+     * @param url
+     * @param bodyData
+     * @return
+     */
+    public static JSONObject doPostByHttpClient(String url, JSONObject bodyData) {
+        try {
+            Map<String, String> headers = new HashMap();
+            headers.put("Content-Type", "application/json");
+            headers.put("Accept", "*/*");
+            logger.info(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+            System.out.println(String.format("url[%s],data[%s]", url, bodyData.toJSONString()));
+
+            String responseEntify = HttpClientUtils.postjson(url, headers, bodyData.toJSONString());
+
+            logger.info(responseEntify);
+            JSONObject result = (JSONObject)JSONObject.parse(responseEntify);
+            return result;
+        } catch (IOException var5) {
+            throw new KDException(var5, WFErrorCode.httpRequestException(), new Object[]{var5.getMessage()});
+        }
+    }
+
+    /**
+     * 处理MDM返回结果 A-成功 B-部分成功 C-失败
+     * @param response
+     * @return
+     */
+    public static String dealResponseStatus (JSONObject response) {
+        String status = "";
+        int successCount = 0;
+        int errorCount = 0;
+        JSONArray responseData = response.getJSONArray("responseData");
+        //根据明细数据status分组
+        Map<String, JSONArray> responseDataMap = responseData.stream().map(obj -> (JSONObject) obj)
+                .collect(Collectors.groupingBy(
+                        json -> json.getString("status"),
+                        () -> new HashMap<>(),
+                        Collectors.mapping(JSONObject::toJSON, Collectors.toCollection(JSONArray::new))
+                ));
+        JSONArray sucArr = responseDataMap.get("S");
+        JSONArray errArr = responseDataMap.get("E");
+        if(sucArr != null)
+            successCount = sucArr.size();
+        if(errArr != null)
+            errorCount = errArr.size();
+
+        if(errorCount > 0 && successCount > 0) {
+            status = "B";
+        }
+        else if(successCount == 0  && errorCount > 0) {
+            status = "C";
+        }
+        else if(successCount > 0 && errorCount == 0) {
+            status = "A";
+        }
+
+        return status;
+    }
+
+    /**
+     * 创建同步日志
+     * @param status 同步状态 A-成功 B-部分成功 C-失败
+     * @param startTime 变动开始时间
+     * @param endTime   变动结束时间
+     * @param request   请求报文
+     * @param response  返回报文
+     * @param syncType  同步类型
+     */
+    public static void createLog (String status, String startTime, String endTime, String request, String response, String syncType) {
+        DynamicObject dynamicObject = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_LOG);
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        dynamicObject.set("enable", "1");
+        dynamicObject.set("status", "C");
+        dynamicObject.set("number", uuid.substring(0,29));
+        dynamicObject.set("nckd_starttime", startTime);
+        dynamicObject.set("nckd_endtime", endTime);
+        dynamicObject.set("nckd_status", status);
+        dynamicObject.set("nckd_synctype", syncType);
+        if (request.length() < 200) {
+            dynamicObject.set("nckd_request", request);
+        } else {
+            dynamicObject.set("nckd_request", request.substring(0, 200) + "...");
+        }
+        dynamicObject.set("nckd_request_tag", request);
+
+        if (response.length() < 200) {
+            dynamicObject.set("nckd_response", response);
+        } else {
+            dynamicObject.set("nckd_response", response.substring(0, 200) + "...");
+        }
+        dynamicObject.set("nckd_response_tag", response);
+        SaveServiceHelper.save(new DynamicObject[]{dynamicObject});
+        logger.info("-------- 保存日志 --------");
+    }
+
+
+}