Explorar el Código

同步人员到北森

qingwu hace 8 meses
padre
commit
60ee39d795

+ 165 - 0
metadata/com/kingdee/eas/custom/beisen/syncperson/SyncPersonToBeisenFacade.facade

@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8"?>
+<facade xmlns="com.kingdee.bos.metadata">
+  <package>com.kingdee.eas.custom.beisen.syncperson</package>
+  <name>SyncPersonToBeisenFacade</name>
+  <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].alias</alias>
+  <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].description</description>
+  <userDefined>true</userDefined>
+  <bosType>7CCEF719</bosType>
+  <stereoType>false</stereoType>
+  <businessImplName>com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade</businessImplName>
+  <businessControllerName>com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacadeController</businessControllerName>
+  <accessLevel>public</accessLevel>
+  <subClassingMode>normal</subClassingMode>
+  <methods>
+    <method>
+      <name>syncPerson</name>
+      <isListenerMethod>false</isListenerMethod>
+      <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].alias</alias>
+      <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].description</description>
+      <innerID>48de92db-8c4a-43db-a6ed-52c9519a9f60</innerID>
+      <accessLevel>public</accessLevel>
+      <subClassingMode>normal</subClassingMode>
+      <returnValueType />
+      <metadataRef />
+      <transactionAttribute>Supports</transactionAttribute>
+      <userDefined>true</userDefined>
+      <userDefinedLogic />
+      <parameters>
+        <parameter>
+          <name>personId</name>
+          <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[personId].alias</alias>
+          <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[personId].description</description>
+          <direction>in</direction>
+          <dataType>String</dataType>
+          <metadataRef />
+          <userDefined>true</userDefined>
+        </parameter>
+        <parameter>
+          <name>day</name>
+          <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[day].alias</alias>
+          <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[day].description</description>
+          <direction>in</direction>
+          <dataType>Integer</dataType>
+          <metadataRef />
+          <userDefined>true</userDefined>
+        </parameter>
+      </parameters>
+      <exceptions>
+        <bizException>
+          <key name="package" value="com.kingdee.eas.common" />
+          <key name="name" value="EASBizException" />
+        </bizException>
+      </exceptions>
+      <configured>false</configured>
+    </method>
+    <method>
+      <name>beforeSyncPerson</name>
+      <isListenerMethod>false</isListenerMethod>
+      <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].alias</alias>
+      <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].description</description>
+      <innerID>9bab83e1-671d-4f0a-8f4f-24c695102d5b</innerID>
+      <accessLevel>public</accessLevel>
+      <subClassingMode>normal</subClassingMode>
+      <returnValueType />
+      <metadataRef />
+      <transactionAttribute>Supports</transactionAttribute>
+      <userDefined>true</userDefined>
+      <userDefinedLogic />
+      <parameters>
+        <parameter>
+          <name>day</name>
+          <alias>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].parameters.parameter[day].alias</alias>
+          <description>facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].parameters.parameter[day].description</description>
+          <direction>in</direction>
+          <dataType>Integer</dataType>
+          <metadataRef />
+          <userDefined>true</userDefined>
+        </parameter>
+      </parameters>
+      <exceptions>
+        <bizException>
+          <key name="package" value="com.kingdee.eas.common" />
+          <key name="name" value="EASBizException" />
+        </bizException>
+      </exceptions>
+      <configured>false</configured>
+    </method>
+  </methods>
+  <resource>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="同步人员至北森" />
+      <lang locale="zh_HK" value="同步人員至北森" />
+      <lang locale="zh_TW" value="同步人員至北森" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="null" />
+      <lang locale="zh_HK" value="null" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].extendedProperty.userDefined">
+      <lang locale="en_US" value="true" />
+      <lang locale="zh_CN" value="true" />
+      <lang locale="zh_TW" value="true" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="前置同步人员" />
+      <lang locale="zh_HK" value="前置同步人員" />
+      <lang locale="zh_TW" value="前置同步人員" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="null" />
+      <lang locale="zh_HK" value="null" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].parameters.parameter[day].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="前延天" />
+      <lang locale="zh_HK" value="前延天" />
+      <lang locale="zh_TW" value="前延天" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[beforeSyncPerson].parameters.parameter[day].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="同步人员" />
+      <lang locale="zh_HK" value="同步人員" />
+      <lang locale="zh_TW" value="同步人員" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="null" />
+      <lang locale="zh_HK" value="null" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[day].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="前延天" />
+      <lang locale="zh_HK" value="前延天" />
+      <lang locale="zh_TW" value="前延天" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[day].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[personId].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="人员ID" />
+      <lang locale="zh_HK" value="人員ID" />
+      <lang locale="zh_TW" value="人員ID" />
+    </rs>
+    <rs key="facade[com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacade].methods.method[syncPerson].parameters.parameter[personId].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+  </resource>
+</facade>

+ 27 - 0
metadata/com/kingdee/eas/custom/beisen/syncperson/syncperson.package

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<package xmlns="com.kingdee.bos.metadata">
+  <package>com.kingdee.eas.custom.beisen.syncperson</package>
+  <name>syncperson</name>
+  <alias>package[com.kingdee.eas.custom.beisen.syncperson.syncperson].alias</alias>
+  <description>package[com.kingdee.eas.custom.beisen.syncperson.syncperson].description</description>
+  <basicCode />
+  <resource>
+    <rs key="package[com.kingdee.eas.custom.beisen.syncperson.syncperson].alias">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="同步人员" />
+      <lang locale="zh_HK" value="同步人員" />
+      <lang locale="zh_TW" value="同步人員" />
+    </rs>
+    <rs key="package[com.kingdee.eas.custom.beisen.syncperson.syncperson].description">
+      <lang locale="en_US" value="null" />
+      <lang locale="zh_CN" value="null" />
+      <lang locale="zh_HK" value="null" />
+      <lang locale="zh_TW" value="null" />
+    </rs>
+    <rs key="package[com.kingdee.eas.custom.beisen.syncperson.syncperson].extendedProperty.userDefined">
+      <lang locale="en_US" value="true" />
+      <lang locale="zh_CN" value="true" />
+      <lang locale="zh_TW" value="true" />
+    </rs>
+  </resource>
+</package>

+ 83 - 0
src/com/kingdee/eas/custom/beisen/syncperson/AbstractSyncPersonToBeisenFacadeControllerBean.java

@@ -0,0 +1,83 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import javax.ejb.*;
+import java.rmi.RemoteException;
+import com.kingdee.bos.*;
+import com.kingdee.bos.util.BOSObjectType;
+import com.kingdee.bos.metadata.IMetaDataPK;
+import com.kingdee.bos.metadata.rule.RuleExecutor;
+import com.kingdee.bos.metadata.MetaDataPK;
+//import com.kingdee.bos.metadata.entity.EntityViewInfo;
+import com.kingdee.bos.framework.ejb.AbstractEntityControllerBean;
+import com.kingdee.bos.framework.ejb.AbstractBizControllerBean;
+//import com.kingdee.bos.dao.IObjectPK;
+import com.kingdee.bos.dao.IObjectValue;
+import com.kingdee.bos.dao.IObjectCollection;
+import com.kingdee.bos.service.ServiceContext;
+import com.kingdee.bos.service.IServiceContext;
+import com.kingdee.eas.framework.Result;
+import com.kingdee.eas.framework.LineResult;
+import com.kingdee.eas.framework.exception.EASMultiException;
+import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
+
+import com.kingdee.eas.common.EASBizException;
+import java.lang.String;
+
+
+
+public abstract class AbstractSyncPersonToBeisenFacadeControllerBean extends AbstractBizControllerBean implements SyncPersonToBeisenFacadeController
+{
+    protected AbstractSyncPersonToBeisenFacadeControllerBean()
+    {
+    }
+
+    protected BOSObjectType getBOSType()
+    {
+        return new BOSObjectType("7CCEF719");
+    }
+
+    public void syncPerson(Context ctx, String personId, int day) throws BOSException, EASBizException
+    {
+        try {
+            ServiceContext svcCtx = createServiceContext(new MetaDataPK("48de92db-8c4a-43db-a6ed-52c9519a9f60"), new Object[]{ctx, personId, new Integer(day)});
+            invokeServiceBefore(svcCtx);
+              if(!svcCtx.invokeBreak()) {
+            _syncPerson(ctx, personId, day);
+            }
+            invokeServiceAfter(svcCtx);
+        } catch (BOSException ex) {
+            throw ex;
+        } catch (EASBizException ex0) {
+            throw ex0;
+        } finally {
+            super.cleanUpServiceState();
+        }
+    }
+    protected void _syncPerson(Context ctx, String personId, int day) throws BOSException, EASBizException
+    {    	
+        return;
+    }
+
+    public void beforeSyncPerson(Context ctx, int day) throws BOSException, EASBizException
+    {
+        try {
+            ServiceContext svcCtx = createServiceContext(new MetaDataPK("9bab83e1-671d-4f0a-8f4f-24c695102d5b"), new Object[]{ctx, new Integer(day)});
+            invokeServiceBefore(svcCtx);
+              if(!svcCtx.invokeBreak()) {
+            _beforeSyncPerson(ctx, day);
+            }
+            invokeServiceAfter(svcCtx);
+        } catch (BOSException ex) {
+            throw ex;
+        } catch (EASBizException ex0) {
+            throw ex0;
+        } finally {
+            super.cleanUpServiceState();
+        }
+    }
+    protected void _beforeSyncPerson(Context ctx, int day) throws BOSException, EASBizException
+    {    	
+        return;
+    }
+
+}

+ 20 - 0
src/com/kingdee/eas/custom/beisen/syncperson/ISyncPersonToBeisenFacade.java

@@ -0,0 +1,20 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import com.kingdee.bos.BOSException;
+//import com.kingdee.bos.metadata.*;
+import com.kingdee.bos.framework.*;
+import com.kingdee.bos.util.*;
+import com.kingdee.bos.Context;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.util.*;
+import com.kingdee.eas.common.EASBizException;
+import java.lang.String;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.framework.*;
+
+public interface ISyncPersonToBeisenFacade extends IBizCtrl
+{
+    public void syncPerson(String personId, int day) throws BOSException, EASBizException;
+    public void beforeSyncPerson(int day) throws BOSException, EASBizException;
+}

+ 64 - 0
src/com/kingdee/eas/custom/beisen/syncperson/SyncPersonToBeisenFacade.java

@@ -0,0 +1,64 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import com.kingdee.bos.framework.ejb.EJBRemoteException;
+import com.kingdee.bos.util.BOSObjectType;
+import java.rmi.RemoteException;
+import com.kingdee.bos.framework.AbstractBizCtrl;
+import com.kingdee.bos.orm.template.ORMObject;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.util.*;
+import com.kingdee.eas.common.EASBizException;
+import java.lang.String;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.framework.*;
+import com.kingdee.eas.custom.beisen.syncperson.*;
+
+public class SyncPersonToBeisenFacade extends AbstractBizCtrl implements ISyncPersonToBeisenFacade
+{
+    public SyncPersonToBeisenFacade()
+    {
+        super();
+        registerInterface(ISyncPersonToBeisenFacade.class, this);
+    }
+    public SyncPersonToBeisenFacade(Context ctx)
+    {
+        super(ctx);
+        registerInterface(ISyncPersonToBeisenFacade.class, this);
+    }
+    public BOSObjectType getType()
+    {
+        return new BOSObjectType("7CCEF719");
+    }
+    private SyncPersonToBeisenFacadeController getController() throws BOSException
+    {
+        return (SyncPersonToBeisenFacadeController)getBizController();
+    }
+    /**
+     *同步人员-User defined method
+     *@param personId 人员ID
+     *@param day 前延天
+     */
+    public void syncPerson(String personId, int day) throws BOSException, EASBizException
+    {
+        try {
+            getController().syncPerson(getContext(), personId, day);
+        }
+        catch(RemoteException err) {
+            throw new EJBRemoteException(err);
+        }
+    }
+    /**
+     *前置同步人员-User defined method
+     *@param day 前延天
+     */
+    public void beforeSyncPerson(int day) throws BOSException, EASBizException
+    {
+        try {
+            getController().beforeSyncPerson(getContext(), day);
+        }
+        catch(RemoteException err) {
+            throw new EJBRemoteException(err);
+        }
+    }
+}

+ 23 - 0
src/com/kingdee/eas/custom/beisen/syncperson/SyncPersonToBeisenFacadeController.java

@@ -0,0 +1,23 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import com.kingdee.bos.BOSException;
+//import com.kingdee.bos.metadata.*;
+import com.kingdee.bos.framework.*;
+import com.kingdee.bos.util.*;
+import com.kingdee.bos.Context;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.util.*;
+import com.kingdee.eas.common.EASBizException;
+import java.lang.String;
+import com.kingdee.bos.Context;
+import com.kingdee.bos.framework.*;
+
+import java.rmi.RemoteException;
+import com.kingdee.bos.framework.ejb.BizController;
+
+public interface SyncPersonToBeisenFacadeController extends BizController
+{
+    public void syncPerson(Context ctx, String personId, int day) throws BOSException, EASBizException, RemoteException;
+    public void beforeSyncPerson(Context ctx, int day) throws BOSException, EASBizException, RemoteException;
+}

+ 394 - 0
src/com/kingdee/eas/custom/beisen/syncperson/SyncPersonToBeisenFacadeControllerBean.java

@@ -0,0 +1,394 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.kingdee.bos.metadata.entity.*;
+import com.kingdee.bos.metadata.query.util.CompareType;
+import com.kingdee.eas.base.permission.IUser;
+import com.kingdee.eas.base.permission.UserCollection;
+import com.kingdee.eas.base.permission.UserFactory;
+import com.kingdee.eas.base.permission.UserInfo;
+import com.kingdee.eas.basedata.org.AdminOrgUnitInfo;
+import com.kingdee.eas.basedata.org.PositionInfo;
+import com.kingdee.eas.basedata.person.IPerson;
+import com.kingdee.eas.basedata.person.PersonCollection;
+import com.kingdee.eas.basedata.person.PersonFactory;
+import com.kingdee.eas.basedata.person.PersonInfo;
+import com.kingdee.eas.custom.beisen.utils.BeisenApiClient;
+import com.kingdee.eas.custom.beisen.utils.BeisenParamByPropertiesUtil;
+import com.kingdee.eas.hr.ats.AtsUtil;
+import com.kingdee.eas.hr.base.*;
+import com.kingdee.eas.hr.emp.*;
+import com.kingdee.eas.util.ToolUtils;
+import com.kingdee.eas.util.app.DbUtil;
+import com.kingdee.jdbc.rowset.IRowSet;
+import com.kingdee.util.DateTimeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import javax.ejb.*;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.rmi.RemoteException;
+
+import com.kingdee.bos.*;
+import com.kingdee.bos.util.BOSObjectType;
+import com.kingdee.bos.metadata.IMetaDataPK;
+import com.kingdee.bos.metadata.rule.RuleExecutor;
+import com.kingdee.bos.metadata.MetaDataPK;
+// import com.kingdee.bos.metadata.entity.EntityViewInfo;
+import com.kingdee.bos.framework.ejb.AbstractEntityControllerBean;
+import com.kingdee.bos.framework.ejb.AbstractBizControllerBean;
+// import com.kingdee.bos.dao.IObjectPK;
+import com.kingdee.bos.dao.IObjectValue;
+import com.kingdee.bos.dao.IObjectCollection;
+import com.kingdee.bos.service.ServiceContext;
+import com.kingdee.bos.service.IServiceContext;
+
+import com.kingdee.eas.common.EASBizException;
+
+import java.lang.String;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * qingwu
+ */
+public class SyncPersonToBeisenFacadeControllerBean extends AbstractSyncPersonToBeisenFacadeControllerBean {
+    private static Logger logger = Logger.getLogger(SyncPersonToBeisenFacadeControllerBean.class);
+    private BeisenApiClient beisenApiClient = null;
+
+    public SyncPersonToBeisenFacadeControllerBean() {
+        beisenApiClient = BeisenApiClient.getInstance();
+    }
+
+    @Override
+    protected void _beforeSyncPerson(Context ctx, int day) throws BOSException, EASBizException {
+        // 获取到所有需要的personid
+        Set<String> personSet = new HashSet();
+        // 获取当前日期
+        Date now = new Date();
+        // 获取指定天数前的日期
+        Date dayBefore = getDayBefore(now, day);
+        FilterInfo filterInfo = new FilterInfo();
+        FilterItemCollection filterItems = filterInfo.getFilterItems();
+        filterItems.add(new FilterItemInfo("lastUpdateTime", dayBefore, CompareType.GREATER_EQUALS));
+        filterItems.add(new FilterItemInfo("lastUpdateTime", now, CompareType.LESS_EQUALS));
+        SelectorItemCollection sic = new SelectorItemCollection();
+        sic.add(new SelectorItemInfo("id"));
+        IPerson iPerson = PersonFactory.getLocalInstance(ctx);
+        EntityViewInfo entityViewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
+        PersonCollection personCol = iPerson.getPersonCollection(entityViewInfo);
+        // 循环获取所有人员id
+        for (int i = 0; i < personCol.size(); i++) {
+            PersonInfo personInfo = personCol.get(i);
+            personSet.add(personInfo.getId().toString());
+        }
+
+        sic = new SelectorItemCollection();
+        sic.add(new SelectorItemInfo("person.id"));
+        entityViewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
+        // 获取员工变动记录的personid
+        IEmpPosOrgRelation empIns = EmpPosOrgRelationFactory.getLocalInstance(ctx);
+        EmpPosOrgRelationCollection empCol = empIns.getEmpPosOrgRelationCollection(entityViewInfo);
+        // 循环获取所有人员id
+        for (int i = 0; i < empCol.size(); i++) {
+            EmpPosOrgRelationInfo empPosOrgRelation = empCol.get(i);
+            String personIdStr = empPosOrgRelation.getPerson().getId().toString();
+            personSet.add(personIdStr);
+        }
+        // 同步到beisen
+        String s = convertSetToString(personSet);
+        _syncPerson(ctx, s, day);
+
+    }
+
+    public static String convertSetToString(Set set) {
+        if (set != null && set.size() > 0) {
+            Iterator iter = set.iterator();
+            StringBuffer sql = new StringBuffer();
+
+            while (iter.hasNext()) {
+//                sql.append("'");
+                sql.append(iter.next());
+//                sql.append("',");
+                sql.append(",");
+            }
+
+            sql.deleteCharAt(sql.length() - 1);
+            return sql.toString();
+        } else {
+            return "''";
+        }
+    }
+
+    /**
+     * 同步人员信息
+     *
+     * @param ctx
+     * @param personId
+     * @throws BOSException
+     * @throws EASBizException
+     */
+    @Override
+    protected void _syncPerson(Context ctx, String personId, int day) throws BOSException, EASBizException {
+        JSONArray jsonArray = new JSONArray();
+        FilterInfo filterInfo = new FilterInfo();
+        FilterItemCollection filterItems = filterInfo.getFilterItems();
+        Set personIds = new HashSet();
+        SelectorItemCollection sic = getSelectorItemCollection();
+        // 如果为空查询所有数据
+        if (!StringUtils.isEmpty(personId)) {
+            personIds = AtsUtil.toSet(personId);
+            logger.error("syncPersonToIot---personIds---" + personIds);
+            filterItems.add(new FilterItemInfo("person", personIds, CompareType.INCLUDE));
+        }
+        try {
+            BeisenParamByPropertiesUtil beisenParamByProperties = new BeisenParamByPropertiesUtil("/server/properties/beisen/BeiSenConfig.properties");
+            Map<String, String> config = beisenParamByProperties.getConfig();
+            logger.error("config---" + config.size());
+            // 员工
+            IPerson iPerson = PersonFactory.getLocalInstance(ctx);
+            // 查询所有人的user用户
+            IUser iUser = UserFactory.getLocalInstance(ctx);
+            UserCollection userCollection = iUser.getUserCollection();
+            Map userMap = new HashMap();
+            for (int i = 0; i < userCollection.size(); i++) {
+                UserInfo userInfo = userCollection.get(i);
+                PersonInfo person = userInfo.getPerson();
+                if (person != null) {
+                    String userName = userInfo.getName();
+                    userMap.put(person.getId(), userName);
+                }
+            }
+            // 查询所有人的直接上级
+            Map personParentMap = getPersonParent(ctx);
+            IPersonPosition iPersonPosition = PersonPositionFactory.getLocalInstance(ctx);
+            EntityViewInfo entityViewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
+            PersonPositionCollection personPositionCollection = iPersonPosition.getPersonPositionCollection(entityViewInfo);
+            if (personPositionCollection.isEmpty()) {
+                jsonArray.add("[" + personId + "]未查询到人员信息数据,请检查参数是否url编码");
+            }
+            for (int i = 0; i < personPositionCollection.size(); i++) {
+                Map msgMap = new HashMap();
+                JSONObject personJson = new JSONObject();
+                PersonPositionInfo personPositionInfo = personPositionCollection.get(i);
+                PersonInfo person = personPositionInfo.getPerson();
+                // 入职ID
+                Object bsUserId = person.get("bsUserId");
+                logger.error("bsUserId--" + bsUserId);
+                String originalId = URLEncoder.encode(person.getId().toString(), StandardCharsets.UTF_8.name());
+                String nCell = person.getNCell();
+                String name = person.getName();
+                msgMap.put("number", person.getNumber());
+                msgMap.put("name", name);
+                msgMap.put("originalId", originalId);
+                EmployeeTypeInfo employeeType = person.getEmployeeType();
+                // 调用新增接口---禁用系统用户账号 状态1:在职 3:离职
+                EmployeeTypeEnum inServiceEnum = employeeType.getInService();
+                Object beisenId = person.get("beisenId");
+                String email = person.getEmail();
+                if (StringUtils.isEmpty(email)) {
+                    // throw new BOSException(person.getName() + "同步失败:邮箱不能为空,请维护!!");
+                    msgMap.put("message", person.getName() + "同步失败:邮箱不能为空,请维护!!");
+                    jsonArray.add(msgMap);
+                    continue;
+                }
+                personJson.put("email", email);// 邮箱
+                personJson.put("userName", email);// 用户名
+                personJson.put("name", name);// 姓名
+                personJson.put("originalId", person.getId().toString());// 来源方用户ID
+                // 来源(创建更新必填字段) 员工创建时的业务来源。0:其他;1:平台;2:测评;3:招聘;4:360;5:自由注册;6:Tita;7:Tita移动端;1001:PeopleSoft
+                personJson.put("origin", 1);
+                personJson.put("mobile", nCell);// 手机号
+                personJson.put(config.get("EMPLOYEETYPENUMBER"), employeeType.getNumber());// 用工关系状态
+                AdminOrgUnitInfo personDep = personPositionInfo.getPersonDep();
+                personJson.put("departmentOriginalId", personDep.getId());// 部门外部标识
+                personJson.put(config.get("HIREDATE"), DateTimeUtils.format(person.getHireDate()));// 入职日期
+                Object lineManagerOriginalId = personParentMap.get(personId) == null ? null : URLEncoder.encode(personParentMap.get(personId).toString(), StandardCharsets.UTF_8.name());
+                personJson.put(config.get("lineManagerOriginalId"), lineManagerOriginalId);//  直线上级外部标识
+                PositionInfo primaryPosition = personPositionInfo.getPrimaryPosition();
+                personJson.put(config.get("positionNumber"), primaryPosition.getNumber());//  岗位编码
+                personJson.put(config.get("positionName"), primaryPosition.getName());//  岗位名称
+
+                // 判断是否在北森存在 如果beisenId为空并且是在职状态需要再北森创建员工
+                if (beisenId == null) {
+                    // 判断是否在职
+                    if (inServiceEnum.getValue() == 2) {
+                        msgMap.put("message",person.getName() + "已离职无需同步北森!!");
+                        jsonArray.add(msgMap);
+                        continue;
+                    }
+                    // 创建员工信息
+                    String staffsPost = config.get("STAFFSPOST");
+                    logger.error("personJson--" + personJson);
+                    JSONObject staffs = beisenApiClient.callApi(staffsPost, personJson);
+                    if (!"200".equals(staffs.getString("code"))) {
+                        msgMap.put("message","创建员工信息失败,原因:" + staffs.get("message"));
+                        jsonArray.add(msgMap);
+                        continue;
+                    }
+                    String data = staffs.getString("data");
+                    person.put("beisenId", data);
+                    SelectorItemCollection personUpdSic = new SelectorItemCollection();
+                    personUpdSic.add("beisenId");
+                    iPerson.updatePartial(person, personUpdSic);
+                    msgMap.put("staffsMessage:", staffs.get("message"));
+                } else {
+                    // 通过userId更新员工信息
+                    if (inServiceEnum.getValue() == 2 || inServiceEnum.getValue() == 0) {
+                        // 通过OriginalId设置员工离职
+                        String putSetdimission = config.get("PUTSETDIMISSION") + "=" + originalId;
+                        logger.error("putSetdimission--" + putSetdimission);
+                        JSONObject setdimission = beisenApiClient.callPutApi(putSetdimission, new JSONObject());
+                        msgMap.put("通过OriginalId设置员工离职:", setdimission.get("message"));
+                        // 查询待入职人员信息 通过bsUserId 入职员工ID获取入职记录guid
+                        String guid = getGuid(config, bsUserId);
+                        logger.error("guid--" + guid);
+                        // 入职管理设置离职
+                        String recruitOnBoardingPost = config.get("RECRUITONBOARDINGPOST") + "?entryId=" + guid;
+                        logger.error("recruitOnBoardingPost--" + recruitOnBoardingPost);
+                        JSONObject recruitOnBoarding = beisenApiClient.callApi(recruitOnBoardingPost, new JSONObject());
+                        msgMap.put("入职单设置离职:", recruitOnBoarding.get("message"));
+                    } else if (inServiceEnum.getValue() == 1) {
+                        // 入职管理设置已入职  需求变更无需调用该接口
+                        // 设置员工在职
+                        String putSetinduction = config.get("PUTSETINDUCTION") + "?originalId=" + originalId + "&accountEnable=true";
+                        logger.error("putSetinduction--" + putSetinduction);
+                        JSONObject setinduction = beisenApiClient.callPutApi(putSetinduction, new JSONObject());
+                        msgMap.put("设置员工在职:", setinduction.get("message"));
+                    }
+                    // 通过OriginalId更新员工信息
+                    JSONObject udpatePersonJson = personJson;
+                    // 删除用户名,不修改用户名
+                    udpatePersonJson.remove("userName");
+                    String putByOriginalId = config.get("PUTBYORIGINALID") + "?originalId=" + originalId;
+                    logger.error("putByOriginalId--" + putByOriginalId);
+                    logger.error("udpatePersonJson--" + udpatePersonJson);
+                    JSONObject updateStaffs = beisenApiClient.callPutApi(putByOriginalId, udpatePersonJson);
+                    msgMap.put("通过OriginalId更新员工信息:", updateStaffs.get("message"));
+                    // 更新待入职人员信息
+                    if (bsUserId != null) {
+                        JSONObject updateStaffInfo = new JSONObject();
+                        JSONObject staffInfos = new JSONObject();
+                        staffInfos.put("id", bsUserId);
+                        staffInfos.put("name", name);
+                        staffInfos.put("email", email);
+                        staffInfos.put("mobilePhone", nCell);
+                        staffInfos.put("staffStatus", 1);
+                        updateStaffInfo.put("staffInfos", staffInfos);
+                        logger.error("updateStaffInfo--" + updateStaffInfo);
+                        JSONObject updateStaffRet = beisenApiClient.callApi(config.get("UPDATESTAFFURL"), updateStaffInfo);
+                        msgMap.put("更新待入职人员信息:", updateStaffRet.get("code"));
+                    }
+                }
+                jsonArray.add(msgMap);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error(e.getMessage());
+            jsonArray.add(e.getMessage());
+            // throw new RuntimeException(e);
+        }
+        logger.error("jsonArray--" + jsonArray);
+    }
+
+    /**
+     * 查询待入职人员信息 通过bsUserId 入职员工ID获取入职记录guid
+     *
+     * @param config
+     * @param bsUserId
+     * @return
+     */
+    public String getGuid(Map<String, String> config, Object bsUserId) throws IOException {
+        String guid = null;
+        JSONArray jsonArray = new JSONArray();
+        jsonArray.add(bsUserId);
+        JSONObject staffInfos = beisenApiClient.callApi(config.get("GETSTAFFINFOS"), jsonArray);
+        logger.error("staffInfos--" + staffInfos);
+        if (staffInfos.getInteger("code") == 200) {
+            JSONArray data = staffInfos.getJSONArray("data");
+            for (int i = 0; i < data.size(); i++) {
+                JSONObject dataJson = data.getJSONObject(i);
+                logger.error("dataJson--" + dataJson);
+                JSONArray entryRecord = dataJson.getJSONArray("entryRecord");
+                for (int j = 0; j < entryRecord.size(); j++) {
+                    JSONObject entry = entryRecord.getJSONObject(j);
+                    guid = entry.getString("id");
+                }
+            }
+        }
+        return guid;
+    }
+
+    /**
+     * 获取指定日期前 x 天的日期的方法。
+     *
+     * @param specifiedDay 指定日期
+     * @param x            天数
+     * @return 指定日期前 x 天的日期
+     */
+    public Date getDayBefore(Date specifiedDay, int x) {
+        // 获取 Calendar 实例
+        Calendar c = Calendar.getInstance();
+        // 设置 Calendar 的时间为指定日期
+        c.setTime(specifiedDay);
+        // 将日期向前移动 x 天
+        c.add(Calendar.DATE, -x);
+        // 返回移动后的日期
+        return c.getTime();
+    }
+
+    /**
+     * 获取所有人的直接上级
+     *
+     * @return
+     */
+    public Map getPersonParent(Context ctx) throws BOSException, SQLException {
+        Map personParentMap = new HashMap();
+        StringBuffer sb = new StringBuffer();
+        sb.append("select person.fid as personId , personParent.fid as personParentId\n").append("\n");
+        sb.append("from T_ORG_PositionMember positioonMenber\n").append("\n");
+        sb.append("left join T_ORG_Position position on position.fid = positioonMenber.FPositionID\n").append("\n");
+        sb.append("left join T_ORG_Position positionParent on position.fid = positionParent.FPARENTID\n").append("\n");
+        sb.append("left join T_HR_EmpOrgRelation empOrgRelation on empOrgRelation.FPOSITIONID = positionParent.fid\n").append("\n");
+        sb.append("left join t_Bd_person person on person.fid = empOrgRelation.FPERSONID and empOrgRelation.FIsSystem='1'\n").append("\n");
+        sb.append("left join t_Bd_person personParent on personParent.fid = positioonMenber.FPERSONID").append("\n");
+        IRowSet iRowSet = DbUtil.executeQuery(ctx, sb.toString());
+        while (iRowSet.next()) {
+            personParentMap.put(iRowSet.getString("personId"), iRowSet.getString("personParentId"));
+        }
+        return personParentMap;
+    }
+
+    /**
+     * 查询字段
+     *
+     * @return
+     */
+    public SelectorItemCollection getSelectorItemCollection() {
+        SelectorItemCollection sic = new SelectorItemCollection();
+        sic.add("*");
+        sic.add("person.number");
+        sic.add("person.name");
+        sic.add("person.beisenId");
+        sic.add("person.bsUserId");
+        sic.add("person.id");
+        sic.add("person.hireDate");
+        sic.add("person.nCell");
+        sic.add("person.gender");
+        sic.add("person.email");
+        sic.add("person.employeeType.name");
+        sic.add("person.employeeType.number");
+        sic.add("person.employeeType.inService");
+        sic.add("personDep.name");
+        sic.add("personDep.number");
+        sic.add("personDep.id");
+        sic.add("personDep.parent.id");
+        sic.add("personDep.primaryPosition.name");
+        sic.add("personDep.primaryPosition.number");
+        return sic;
+    }
+
+}

+ 30 - 0
src/com/kingdee/eas/custom/beisen/syncperson/SyncPersonToBeisenFacadeFactory.java

@@ -0,0 +1,30 @@
+package com.kingdee.eas.custom.beisen.syncperson;
+
+import com.kingdee.bos.BOSException;
+import com.kingdee.bos.BOSObjectFactory;
+import com.kingdee.bos.util.BOSObjectType;
+import com.kingdee.bos.Context;
+
+public class SyncPersonToBeisenFacadeFactory
+{
+    private SyncPersonToBeisenFacadeFactory()
+    {
+    }
+    public static com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade getRemoteInstance() throws BOSException
+    {
+        return (com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade)BOSObjectFactory.createRemoteBOSObject(new BOSObjectType("7CCEF719") ,com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade.class);
+    }
+    
+    public static com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade getRemoteInstanceWithObjectContext(Context objectCtx) throws BOSException
+    {
+        return (com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade)BOSObjectFactory.createRemoteBOSObjectWithObjectContext(new BOSObjectType("7CCEF719") ,com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade.class, objectCtx);
+    }
+    public static com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade getLocalInstance(Context ctx) throws BOSException
+    {
+        return (com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade)BOSObjectFactory.createBOSObject(ctx, new BOSObjectType("7CCEF719"));
+    }
+    public static com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade getLocalInstance(String sessionID) throws BOSException
+    {
+        return (com.kingdee.eas.custom.beisen.syncperson.ISyncPersonToBeisenFacade)BOSObjectFactory.createBOSObject(sessionID, new BOSObjectType("7CCEF719"));
+    }
+}

+ 230 - 0
src/com/kingdee/eas/custom/beisen/utils/BeisenApiClient.java

@@ -0,0 +1,230 @@
+package com.kingdee.eas.custom.beisen.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import okhttp3.*;
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 北森API调用工具类(单例模式)
+ */
+public class BeisenApiClient {
+    private static final Logger logger = Logger.getLogger(BeisenApiClient.class);
+    private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
+    private static final int MAX_RETRIES = 3;
+
+    // 单例实例
+    private static volatile BeisenApiClient instance;
+
+    private final OkHttpClient httpClient;
+    private final BeisenTokenManager tokenManager;
+
+    // 私有构造函数
+    private BeisenApiClient() {
+        this.httpClient = buildHttpClient();
+        this.tokenManager = BeisenTokenManager.getInstance();
+    }
+
+    /**
+     * 获取单例实例
+     */
+    public static BeisenApiClient getInstance() {
+        if (instance == null) {
+            synchronized (BeisenApiClient.class) {
+                if (instance == null) {
+                    instance = new BeisenApiClient();
+                }
+            }
+        }
+        return instance;
+    }
+
+
+    /**
+     * api调用qun
+     *
+     * @param apiUrl
+     * @param requestData
+     * @return
+     * @throws IOException
+     */
+    public JSONObject callApi(String apiUrl, JSONObject requestData) throws IOException {
+        return callApi(apiUrl, requestData, "POST");
+    }
+
+    public JSONObject callApi(String apiUrl, JSONArray dataArray) throws IOException {
+        return callApi(apiUrl, dataArray, "POST");
+    }
+
+    public JSONObject callPutApi(String apiUrl, JSONObject requestData) throws IOException {
+        return callApi(apiUrl, requestData, "PUT");
+    }
+
+    public JSONObject callGetApi(String apiUrl, JSONObject requestData) throws IOException {
+        return callApi(apiUrl, requestData, "GET");
+    }
+
+    public JSONObject callGetApi(String apiUrl, JSONArray dataArray) throws IOException {
+        return callApi(apiUrl, dataArray, "GET");
+    }
+
+    /**
+     * api调用
+     *
+     * @param apiUrl
+     * @param requestData
+     * @return
+     * @throws IOException
+     */
+    public JSONObject callApi(String apiUrl, JSONObject requestData, String method) throws IOException {
+        return callApiInternal(apiUrl, requestData, true, method);
+    }
+
+    public JSONObject callApi(String apiUrl, JSONArray dataArray, String method) throws IOException {
+        return callApiInternal(apiUrl, dataArray, true, method);
+    }
+
+
+    // 核心API调用逻辑----------------------------------------------------------
+
+    /**
+     * 通用API调用方法
+     *
+     * @param requestBody 支持JSONObject/JSONArray
+     */
+    private JSONObject callApiInternal(String apiUrl, Object requestBody, boolean retryOnTokenExpired, String method)
+            throws IOException {
+        int retryCount = 0;
+        JSONObject handleResponse = new JSONObject();
+        while (retryCount <= MAX_RETRIES) {
+            try {
+                String accessToken = tokenManager.getAccessToken();
+                Request request = null;
+                    request = buildRequest(apiUrl, requestBody, accessToken, method);
+
+                try (Response response = httpClient.newCall(request).execute()) {
+                    handleResponse = handleResponse(response, apiUrl, method);
+                    return handleResponse;
+                }
+            } catch (IOException e) {
+                if (shouldRetry(retryOnTokenExpired, retryCount, e)) {
+                    retryCount++;
+                    handleTokenRefresh(retryCount);
+                    continue;
+                }
+            }
+        }
+        return handleResponse;
+        //不抛出,使用调用的地方存储msg
+//        throw new IOException("API request failed after " + MAX_RETRIES + " attempts");
+    }
+
+    // 响应处理逻辑-------------------------------------------------------------
+
+    private JSONObject handleResponse(Response response, String apiUrl, String method) throws IOException {
+        if (!response.isSuccessful()) {
+            handleHttpError(response, apiUrl, method);
+        }
+
+        String responseBody = response.body().string();
+        JSONObject jsonResponse = parseResponse(responseBody, apiUrl);
+
+        validateBusinessStatus(jsonResponse, apiUrl);
+        return jsonResponse;
+    }
+
+    private void handleHttpError(Response response, String apiUrl, String method) throws IOException {
+        String errorBody = response.body() != null ? response.body().string() : "null";
+        logger.error("API request failed. Code: " + response.code() +
+                ", URL: " + apiUrl + ", Method: " + method +
+                ", Body: " + errorBody);
+//        throw new IOException("HTTP error: " + response.code());
+    }
+
+    private JSONObject parseResponse(String responseBody, String apiUrl) throws IOException {
+        try {
+            return JSON.parseObject(responseBody);
+        } catch (Exception e) {
+            logger.error("Invalid JSON response from API. URL: " + apiUrl + ", Response: " + responseBody);
+            throw new IOException("Failed to parse API response", e);
+        }
+    }
+
+    private void validateBusinessStatus(JSONObject jsonResponse, String apiUrl) throws IOException {
+        int apiCode = jsonResponse.getIntValue("code");
+        if (apiCode != 200) {
+            String apiMessage = jsonResponse.getString("message");
+            logger.error("API business error. Code: " + apiCode +
+                    ", Message: " + apiMessage + ", URL: " + apiUrl);
+//            throw new IOException("Business error: " + apiMessage + " (code: " + apiCode + ")");
+        }
+    }
+
+    // Token重试逻辑-----------------------------------------------------------
+
+    private boolean shouldRetry(boolean retryOnTokenExpired, int retryCount, Exception e) {
+        return retryOnTokenExpired &&
+                isTokenExpiredError(e) &&
+                retryCount < MAX_RETRIES;
+    }
+
+    private void handleTokenRefresh(int retryCount) {
+        logger.warn("Token may have expired, refreshing and retrying... (" +
+                retryCount + "/" + MAX_RETRIES + ")");
+        try {
+            tokenManager.refreshToken();
+        } catch (IOException ex) {
+            logger.error("Failed to refresh token during retry", ex);
+        }
+    }
+
+    // 请求构建----------------------------------------------------------------
+
+    private Request buildRequest(String apiUrl, Object requestBody, String accessToken, String method) {
+        String jsonString = convertToJsonString(requestBody);
+        RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, jsonString);
+        if (requestBody instanceof JSONObject) {
+            if (((JSONObject) requestBody).size() == 0) {
+                body = RequestBody.create(JSON_MEDIA_TYPE,"");
+            }
+        }
+        return new Request.Builder()
+                .url(apiUrl)
+                .addHeader("Authorization", "Bearer " + accessToken)
+                .addHeader("Content-Type", "application/json")
+                .method(method, body)
+                .build();
+    }
+
+    private String convertToJsonString(Object data) {
+        if (data instanceof JSONObject) {
+            return ((JSONObject) data).toJSONString();
+        } else if (data instanceof JSONArray) {
+            return ((JSONArray) data).toJSONString();
+        }
+        throw new IllegalArgumentException("Unsupported request data type: " + data.getClass());
+    }
+
+    // 辅助方法---------------------------------------------------------------
+
+    private boolean isTokenExpiredError(Exception e) {
+        String msg = e.getMessage();
+        return msg != null && (msg.contains("401") ||
+                msg.contains("Unauthorized") ||
+                msg.contains("token"));
+    }
+
+    private OkHttpClient buildHttpClient() {
+        return new OkHttpClient.Builder()
+                .connectTimeout(15, TimeUnit.SECONDS)
+                .writeTimeout(15, TimeUnit.SECONDS)
+                .readTimeout(45, TimeUnit.SECONDS)
+                .connectionPool(new ConnectionPool(50, 5, TimeUnit.MINUTES))
+                .retryOnConnectionFailure(true)
+                .build();
+    }
+}

+ 186 - 0
src/com/kingdee/eas/custom/beisen/utils/BeisenTokenManager.java

@@ -0,0 +1,186 @@
+package com.kingdee.eas.custom.beisen.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import okhttp3.*;
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BeisenTokenManager {
+    private static final Logger logger = Logger.getLogger(BeisenTokenManager.class);
+    private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
+    private static final long EXPIRY_BUFFER = 300_000; // 5分钟缓冲时间
+
+    private final OkHttpClient httpClient;
+    private final Lock lock = new ReentrantLock();
+    private volatile TokenState tokenState;
+
+    private final String appKey;
+    private final String appSecret;
+    private final String tokenUrl;
+
+    // 添加单例实例
+    private static volatile BeisenTokenManager instance;
+
+    // 私有化构造器
+    private BeisenTokenManager() {
+        this(Config.APP_KEY, Config.APP_SECRET, Config.TOKEN_URL);
+    }
+
+
+    // 获取单例方法
+    public static BeisenTokenManager getInstance() {
+        if (instance == null) {
+            synchronized (BeisenTokenManager.class) {
+                if (instance == null) {
+                    instance = new BeisenTokenManager();
+                }
+            }
+        }
+        return instance;
+    }
+
+    public BeisenTokenManager(String appKey, String appSecret, String tokenUrl) {
+        this.appKey = appKey;
+        this.appSecret = appSecret;
+        this.tokenUrl = tokenUrl;
+        this.httpClient = buildHttpClient();
+        this.tokenState = loadInitialToken();
+    }
+
+    public String getAccessToken() throws IOException {
+        TokenState currentState = tokenState;
+        if (currentState != null && !isTokenExpired(currentState)) {
+            return currentState.getAccessToken();
+        }
+
+        if (currentState == null || isTokenExpired(currentState)) {
+            lock.lock();
+            try {
+                currentState = tokenState;
+                if (currentState == null || isTokenExpired(currentState)) {
+                    currentState = fetchNewToken();
+                    tokenState = currentState;
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+        return currentState.getAccessToken();
+    }
+
+    public void refreshToken() throws IOException {
+        TokenState currentState = tokenState;
+        if (currentState == null || currentState.getRefreshToken() == null) {
+            tokenState = fetchNewToken();
+            return;
+        }
+
+        if (isTokenExpired(currentState)) {
+            lock.lock();
+            try {
+                currentState = tokenState;
+                if (isTokenExpired(currentState)) {
+                    tokenState = refreshToken(currentState.getRefreshToken());
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    private boolean isTokenExpired(TokenState state) {
+        // 添加缓冲时间,避免在token即将过期时使用
+        return System.currentTimeMillis() > (state.getExpireTime() - EXPIRY_BUFFER);
+    }
+
+    private TokenState loadInitialToken() {
+        try {
+            return fetchNewToken();
+        } catch (Exception e) {
+            logger.error("Failed to initialize token", e);
+            throw new RuntimeException("Token initialization failed", e);
+        }
+    }
+
+    private TokenState fetchNewToken() throws IOException {
+        JSONObject requestBody = new JSONObject();
+        requestBody.put("grant_type", "client_credentials");
+        requestBody.put("app_key", appKey);
+        requestBody.put("app_secret", appSecret);
+
+        return executeTokenRequest(requestBody);
+    }
+
+    private TokenState refreshToken(String refreshToken) throws IOException {
+        JSONObject requestBody = new JSONObject();
+        requestBody.put("grant_type", "refresh_token");
+        requestBody.put("refresh_token", refreshToken);
+
+        return executeTokenRequest(requestBody);
+    }
+
+    private TokenState executeTokenRequest(JSONObject requestBody) throws IOException {
+        Request request = new Request.Builder().url(tokenUrl).post(RequestBody.create(JSON_MEDIA_TYPE, requestBody.toJSONString())).build();
+
+        try (Response response = httpClient.newCall(request).execute()) {
+            if (!response.isSuccessful()) {
+                String errorBody = response.body() != null ? response.body().string() : "null";
+                logger.error("Token request failed. Code: " + response.code() + ", Body: " + errorBody);
+                throw new IOException("Token request failed with code: " + response.code());
+            }
+
+            String responseBody = response.body().string();
+            JSONObject jsonResponse = JSON.parseObject(responseBody);
+
+            String accessToken = jsonResponse.getString("access_token");
+            String refreshToken = jsonResponse.getString("refresh_token");
+            Long expiresIn = jsonResponse.getLong("expires_in");
+
+            // 更健壮的空值检查
+            if (accessToken == null || expiresIn == null || expiresIn <= 0) {
+                throw new IOException("Invalid token response: " + responseBody);
+            }
+
+            return new TokenState(accessToken, System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiresIn), refreshToken);
+        }
+    }
+
+    private OkHttpClient buildHttpClient() {
+        return new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).retryOnConnectionFailure(true).build();
+    }
+
+    private static class TokenState {
+        private final String accessToken;
+        private final long expireTime;
+        private final String refreshToken;
+
+        public TokenState(String accessToken, long expireTime, String refreshToken) {
+            this.accessToken = accessToken;
+            this.expireTime = expireTime;
+            this.refreshToken = refreshToken;
+        }
+
+        public String getAccessToken() {
+            return accessToken;
+        }
+
+        public long getExpireTime() {
+            return expireTime;
+        }
+
+        public String getRefreshToken() {
+            return refreshToken;
+        }
+    }
+
+    public static class Config {
+        public static String APP_KEY = "FFEC5C62AB444567AFC11E8D394CC072";
+        public static String APP_SECRET = "74FE75BEE476496EAF531C2219EC10E57EDCE01D74F64557BADEEC78FBFF3B26";
+        public static String TOKEN_URL = "https://openapi.italent.cn/token";
+    }
+}

+ 39 - 0
websrc/com/kingdee/eas/custom/beisen/syncperson/handler/EmployeeListHandlerEx.java

@@ -0,0 +1,39 @@
+package com.kingdee.eas.custom.beisen.syncperson.handler;
+
+import com.kingdee.bos.Context;
+import com.kingdee.eas.custom.beisen.syncperson.SyncPersonToBeisenFacadeFactory;
+import com.kingdee.eas.hr.emp.web.handler.EmployeeListHandler;
+import com.kingdee.shr.base.syssetting.context.SHRContext;
+import com.kingdee.shr.base.syssetting.exception.SHRWebException;
+import com.kingdee.shr.base.syssetting.exception.ShrWebBizException;
+import org.springframework.ui.ModelMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author ÇàÎà
+ * @version 1.0
+ * @description: EmployeeListHandlerEx
+ * @date 2025/7/23 14:40
+ */
+public class EmployeeListHandlerEx extends EmployeeListHandler {
+    /**
+     * ͬ²½ÈËÔ±ÖÁ±±É­
+     *
+     * @param request
+     * @param response
+     * @param modelMap
+     * @throws SHRWebException
+     */
+    public void syncPersonToBeisenAction(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws SHRWebException {
+        Context ctx = SHRContext.getInstance().getContext();
+        String billId = getBillId(request);
+        try {
+            SyncPersonToBeisenFacadeFactory.getLocalInstance(ctx).syncPerson(billId, 0);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ShrWebBizException(e);
+        }
+    }
+}