|
|
@@ -1,33 +1,29 @@
|
|
|
/*
|
|
|
* Copyright [2021] [MaxKey of copyright http://www.maxkey.top]
|
|
|
- *
|
|
|
+ *
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
* You may obtain a copy of the License at
|
|
|
- *
|
|
|
+ *
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
- *
|
|
|
+ *
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
* See the License for the specific language governing permissions and
|
|
|
* limitations under the License.
|
|
|
*/
|
|
|
-
|
|
|
|
|
|
-package org.dromara.maxkey.synchronizer.workweixin;
|
|
|
|
|
|
-import java.lang.reflect.InvocationTargetException;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
+package org.dromara.maxkey.synchronizer.workweixin;
|
|
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.dromara.maxkey.constants.ConstsStatus;
|
|
|
+import org.dromara.maxkey.entity.SyncJobConfigField;
|
|
|
import org.dromara.maxkey.entity.SynchroRelated;
|
|
|
import org.dromara.maxkey.entity.idm.UserInfo;
|
|
|
import org.dromara.maxkey.synchronizer.AbstractSynchronizerService;
|
|
|
import org.dromara.maxkey.synchronizer.ISynchronizerService;
|
|
|
-import org.dromara.maxkey.entity.SyncJobConfigField;
|
|
|
import org.dromara.maxkey.synchronizer.service.SyncJobConfigFieldService;
|
|
|
import org.dromara.maxkey.synchronizer.workweixin.entity.WorkWeixinUsers;
|
|
|
import org.dromara.maxkey.synchronizer.workweixin.entity.WorkWeixinUsersResponse;
|
|
|
@@ -38,95 +34,202 @@ import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
|
+import java.sql.Types;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
import static org.dromara.maxkey.synchronizer.utils.FieldUtil.*;
|
|
|
|
|
|
@Service
|
|
|
-public class WorkweixinUsersService extends AbstractSynchronizerService implements ISynchronizerService{
|
|
|
+public class WorkweixinUsersService extends AbstractSynchronizerService implements ISynchronizerService {
|
|
|
final static Logger _logger = LoggerFactory.getLogger(WorkweixinUsersService.class);
|
|
|
|
|
|
@Autowired
|
|
|
public SyncJobConfigFieldService syncJobConfigFieldService;
|
|
|
private static final Integer USER_TYPE = 1;
|
|
|
String access_token;
|
|
|
-
|
|
|
- static String USERS_URL="https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=%s&department_id=%s&fetch_child=0";
|
|
|
-
|
|
|
+
|
|
|
+ static String USERS_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=%s&department_id=%s&fetch_child=0";
|
|
|
+
|
|
|
public void sync() {
|
|
|
_logger.info("Sync Workweixin Users...");
|
|
|
try {
|
|
|
- List<SynchroRelated> synchroRelateds =
|
|
|
- synchroRelatedService.findOrgs(this.synchronizer);
|
|
|
-
|
|
|
- for(SynchroRelated relatedOrg : synchroRelateds) {
|
|
|
- HttpRequestAdapter request =new HttpRequestAdapter();
|
|
|
- String responseBody = request.get(String.format(USERS_URL, access_token,relatedOrg.getOriginId()));
|
|
|
- WorkWeixinUsersResponse usersResponse =JsonUtils.gsonStringToObject(responseBody, WorkWeixinUsersResponse.class);
|
|
|
- _logger.trace("response : " + responseBody);
|
|
|
-
|
|
|
- for(WorkWeixinUsers user : usersResponse.getUserlist()) {
|
|
|
- UserInfo userInfo = buildUserInfoByFiledMap(user);
|
|
|
- _logger.debug("userInfo : " + userInfo);
|
|
|
- userInfo.setPassword(userInfo.getUsername() + UserInfo.DEFAULT_PASSWORD_SUFFIX);
|
|
|
- userInfoService.saveOrUpdate(userInfo);
|
|
|
-
|
|
|
+ // 获取前面已经拉取下来的微信部门同步记录
|
|
|
+ List<SynchroRelated> synchroRelatedOrgList = synchroRelatedService.findOrgs(this.synchronizer);
|
|
|
+ // 加载同步器的历史数据
|
|
|
+ Map<String, SynchroRelated> userRelationHistoryMap = loadUserRelationHistory();
|
|
|
+
|
|
|
+ // 获取同步器的字段映射
|
|
|
+ Map<String, String> fieldMap = getFieldMap(Long.parseLong(synchronizer.getId()));
|
|
|
+ // 拿到微信用户Id映射后的MaxKey字段名称
|
|
|
+ String wxUserIdInMaxkeyField = getWxUserMappingField(fieldMap, "userid");
|
|
|
+
|
|
|
+ boolean fetchFailed = false;
|
|
|
+ for (SynchroRelated relatedOrg : synchroRelatedOrgList) {
|
|
|
+ // 根据微信部门ID,拉取微信用户列表, 这里拉取的是直属员工
|
|
|
+ HttpRequestAdapter request = new HttpRequestAdapter();
|
|
|
+ String responseBody = request.get(String.format(USERS_URL, access_token, relatedOrg.getOriginId()));
|
|
|
+ WorkWeixinUsersResponse usersResponse = JsonUtils.gsonStringToObject(responseBody, WorkWeixinUsersResponse.class);
|
|
|
+ _logger.trace("response : {}", responseBody);
|
|
|
+
|
|
|
+ if (usersResponse == null || usersResponse.getErrcode() != 0 || usersResponse.getUserlist() == null) {
|
|
|
+ fetchFailed = true;
|
|
|
+ _logger.warn("[同步微信用户] 拉取部门 {} (originId={}) 用户失败, errcode={}, errmsg={}",
|
|
|
+ relatedOrg.getObjectName(), relatedOrg.getOriginId(),
|
|
|
+ usersResponse == null ? "null" : usersResponse.getErrcode(),
|
|
|
+ usersResponse == null ? "null" : usersResponse.getErrmsg());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (WorkWeixinUsers wxUser : usersResponse.getUserlist()) {
|
|
|
+ userRelationHistoryMap.remove(wxUser.getUserid());
|
|
|
+ // 依次处理每个员工
|
|
|
+ // 根据员工信息,构建MaxKey用户信息
|
|
|
+ UserInfo maxkeyUserNew = buildUserInfoByFiledMap(wxUser, fieldMap);
|
|
|
+
|
|
|
+ // 一个企业微信可能属于多个部门, 所以需要从 wxUser 中获取主部门
|
|
|
+ String mainDepartment = String.valueOf(wxUser.getMain_department());
|
|
|
+ // 从 synchroRelatedOrgList 中找到对应的 MaxKey 部门ID
|
|
|
+ synchroRelatedOrgList.stream().filter(syncInfo -> StringUtils.equals(syncInfo.getOriginId(), mainDepartment))
|
|
|
+ .findFirst()
|
|
|
+ .ifPresent(orgRelInfo -> {
|
|
|
+ maxkeyUserNew.setDepartmentId(orgRelInfo.getObjectId());
|
|
|
+ maxkeyUserNew.setDepartment(orgRelInfo.getObjectName());
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ // 根据企业微信用户Id映射字段获取用户信息
|
|
|
+ UserInfo existingUser = getUserByWxUserIdMappingField(wxUserIdInMaxkeyField, wxUser.getUserid());
|
|
|
+ // 加载一下历史的同步记录, 查看这个用户是否历史同步过
|
|
|
+ SynchroRelated userSyncRecord = synchroRelatedService.findByOriginId(synchronizer, wxUser.getUserid(), UserInfo.CLASS_TYPE);
|
|
|
+
|
|
|
+ // 现在需要进行一些场景判断 , 大致应该是分成了4种情况
|
|
|
+ // 1. 有同步记录, 用户存在 -> 更新用户信息
|
|
|
+ // 2. 有同步记录, 用户不存在 -> 重新创建用户
|
|
|
+ // 3. 无同步记录, 用户存在 -> 说明是手工创建的用户, 手动创建的用户应当是不能直接关联, 因为无法确认 这个用户和企业微信同步过来的用户是同一个人
|
|
|
+ // 4. 无同步记录, 用户不存在 -> 正常创建用户
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
+ if (userSyncRecord != null) {
|
|
|
+ // 说明之前 已经同步过, 但是同步过也不代表用户一定存在, 可能被删除了
|
|
|
+ if (existingUser != null) {
|
|
|
+ _logger.info("[同步微信用户] 用户 {} {} 已存在, 进行信息更新", wxUser.getUserid(), wxUser.getName());
|
|
|
+ // 用户存在, 那么就是这个用户, 不需要重新创建了, 但是用户的信息需要更新一下, fieldMap key 中的字段是需要更新的, 但是以防配置有问题 部分字段还是需要排除的
|
|
|
+ updateExistingUserInfo(fieldMap, maxkeyUserNew, existingUser);
|
|
|
+ // 直接更新到数据库就完事了
|
|
|
+ userInfoService.update(existingUser);
|
|
|
+ continue;
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+ _logger.info("[同步微信用户] 用户 {} {} 被删除了, 重新创建用户", wxUser.getUserid(), wxUser.getName());
|
|
|
+ // 用户不存在, 说明被删除了, 那么就重新创建一个用户 仍然使用原用户的Id objectId maxkey 里面的用户ID
|
|
|
+ maxkeyUserNew.setId(userSyncRecord.getObjectId());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 没有同步记录不代表用户不存在, 可能是以前手工创建的用户,需要判断一下
|
|
|
+ if (existingUser != null) {
|
|
|
+ _logger.warn("[同步微信用户] 用户 {} {} 无法确认和本地的用户是否是同一人 跳过同步", wxUser.getUserid(), wxUser.getName());
|
|
|
+ // 手工创建的用户应当是不能直接关联, 因为无法确认 这个用户和企业微信同步过来的用户是同一个人
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ _logger.info("[同步微信用户] 用户 {} {} 不存在, 正常创建用户", wxUser.getUserid(), wxUser.getName());
|
|
|
+ // 到这里应该是正常创建用户的流程
|
|
|
+ maxkeyUserNew.setId(maxkeyUserNew.generateId()); // 使用一个新的Id
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ _logger.debug("userInfo : {}", maxkeyUserNew);
|
|
|
+ // 设置密码
|
|
|
+ maxkeyUserNew.setPassword(maxkeyUserNew.getUsername() + UserInfo.DEFAULT_PASSWORD_SUFFIX);
|
|
|
+ // 按照 username + instId 保持唯一 , username 是判重字段
|
|
|
+ userInfoService.saveOrUpdate(maxkeyUserNew);
|
|
|
+
|
|
|
SynchroRelated synchroRelated = new SynchroRelated(
|
|
|
- userInfo.getId(),
|
|
|
- userInfo.getUsername(),
|
|
|
- userInfo.getDisplayName(),
|
|
|
- UserInfo.CLASS_TYPE,
|
|
|
- synchronizer.getId(),
|
|
|
- synchronizer.getName(),
|
|
|
- user.getUserid(),
|
|
|
- user.getName(),
|
|
|
- user.getUserid(),
|
|
|
- "",
|
|
|
- synchronizer.getInstId());
|
|
|
-
|
|
|
+ maxkeyUserNew.getId(), // objectId: maxkey 里面的用户ID
|
|
|
+ maxkeyUserNew.getUsername(), // objectName: maxkey 里面的用户名
|
|
|
+ maxkeyUserNew.getDisplayName(), // displayName: maxkey 里面的显示名称
|
|
|
+ UserInfo.CLASS_TYPE, // objectType: 对象类型 是用户
|
|
|
+ synchronizer.getId(), // jobId: 同步器ID
|
|
|
+ synchronizer.getName(), // jobName: 同步器名称
|
|
|
+ wxUser.getUserid(), // originId: 企业微信用户ID
|
|
|
+ wxUser.getName(), // originName: 企业微信用户名
|
|
|
+ wxUser.getUserid(), // originId2: 企业微信用户ID
|
|
|
+ "", // originId3: 暂无
|
|
|
+ synchronizer.getInstId()); // instId: 机构ID
|
|
|
+
|
|
|
synchroRelatedService.updateSynchroRelated(
|
|
|
- this.synchronizer,synchroRelated,UserInfo.CLASS_TYPE);
|
|
|
-
|
|
|
- socialsAssociate(synchroRelated,"workweixin");
|
|
|
+ this.synchronizer, synchroRelated, UserInfo.CLASS_TYPE);
|
|
|
+
|
|
|
+ socialsAssociate(synchroRelated, "workweixin");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ if (fetchFailed) {
|
|
|
+ _logger.warn("[同步微信用户] 存在部门用户拉取失败,跳过缺失用户禁用流程。");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ disableMissingUsers(userRelationHistoryMap);
|
|
|
} catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
+ _logger.error("[同步微信用户] 同步用户失败", e);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ private static void updateExistingUserInfo(Map<String, String> fieldMap, UserInfo maxkeyUserNew, UserInfo existingUser) {
|
|
|
+ fieldMap.keySet().forEach(fieldName -> {
|
|
|
+ // 排除这些字段不更新
|
|
|
+ if (!StringUtils.equalsAny(fieldName, "id", "password", "instId", "userType")) {
|
|
|
+ try {
|
|
|
+ Object newValue = getFieldValue(maxkeyUserNew, fieldName);
|
|
|
+ setFieldValue(existingUser, fieldName, newValue);
|
|
|
+ } catch (NoSuchMethodException | InvocationTargetException |
|
|
|
+ IllegalAccessException e) {
|
|
|
+ _logger.error("update existingUser error: fieldName: {}, error: {}", fieldName, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 额外设置一下 部门
|
|
|
+ existingUser.setDepartmentId(maxkeyUserNew.getDepartmentId());
|
|
|
+ existingUser.setDepartment(maxkeyUserNew.getDepartment());
|
|
|
+ }
|
|
|
+
|
|
|
public void postSync(UserInfo userInfo) {
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
|
|
|
public UserInfo buildUserInfo(WorkWeixinUsers user) {
|
|
|
- UserInfo userInfo = new UserInfo();
|
|
|
+ UserInfo userInfo = new UserInfo();
|
|
|
userInfo.setUsername(user.getUserid());//账号
|
|
|
userInfo.setNickName(user.getAlias());//名字
|
|
|
userInfo.setDisplayName(user.getName());//名字
|
|
|
-
|
|
|
+
|
|
|
userInfo.setMobile(user.getMobile());//手机
|
|
|
userInfo.setEmail(user.getEmail());
|
|
|
userInfo.setGender(Integer.parseInt(user.getGender()));
|
|
|
-
|
|
|
+
|
|
|
userInfo.setWorkPhoneNumber(user.getTelephone());//工作电话
|
|
|
- userInfo.setDepartmentId(user.getMain_department()+"");
|
|
|
+ userInfo.setDepartmentId(user.getMain_department() + "");
|
|
|
userInfo.setJobTitle(user.getPosition());//职务
|
|
|
userInfo.setWorkAddressFormatted(user.getAddress());//工作地点
|
|
|
|
|
|
//激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业。
|
|
|
- if(user.getStatus() == 1) {
|
|
|
+ if (user.getStatus() == 1) {
|
|
|
userInfo.setStatus(ConstsStatus.ACTIVE);
|
|
|
- }else {
|
|
|
+ } else {
|
|
|
userInfo.setStatus(ConstsStatus.INACTIVE);
|
|
|
}
|
|
|
userInfo.setInstId(this.synchronizer.getInstId());
|
|
|
return userInfo;
|
|
|
}
|
|
|
|
|
|
- public UserInfo buildUserInfoByFiledMap(WorkWeixinUsers user){
|
|
|
+ public UserInfo buildUserInfoByFiledMap(WorkWeixinUsers user, Map<String, String> fieldMap) {
|
|
|
UserInfo userInfo = new UserInfo();
|
|
|
- Map<String, String> fieldMap = getFieldMap(Long.parseLong(synchronizer.getId()));
|
|
|
for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
|
|
|
|
|
|
String userInfoProperty = entry.getKey();
|
|
|
@@ -134,8 +237,8 @@ public class WorkweixinUsersService extends AbstractSynchronizerService implemen
|
|
|
|
|
|
try {
|
|
|
Object sourceValue = null;
|
|
|
- if(sourceProperty.equals("status")){
|
|
|
- userInfo.setStatus(user.getStatus() == 1?ConstsStatus.ACTIVE:ConstsStatus.INACTIVE);
|
|
|
+ if (sourceProperty.equals("status")) {
|
|
|
+ userInfo.setStatus(user.getStatus() == 1 ? ConstsStatus.ACTIVE : ConstsStatus.INACTIVE);
|
|
|
continue;
|
|
|
}
|
|
|
if (hasField(user.getClass(), sourceProperty)) {
|
|
|
@@ -146,7 +249,7 @@ public class WorkweixinUsersService extends AbstractSynchronizerService implemen
|
|
|
}
|
|
|
|
|
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
|
|
- e.printStackTrace();
|
|
|
+ _logger.error("buildUserInfoByFiledMap error: sourceProperty: {}, error: {}", sourceProperty, e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -156,22 +259,105 @@ public class WorkweixinUsersService extends AbstractSynchronizerService implemen
|
|
|
return userInfo;
|
|
|
}
|
|
|
|
|
|
- public Map<String,String> getFieldMap(Long jobId){
|
|
|
- Map<String,String> userFieldMap = new HashMap<>();
|
|
|
+ public Map<String, String> getFieldMap(Long jobId) {
|
|
|
+ Map<String, String> userFieldMap = new HashMap<>();
|
|
|
//根据job id查询属性映射表
|
|
|
List<SyncJobConfigField> syncJobConfigFieldList = syncJobConfigFieldService.findByJobId(jobId);
|
|
|
//获取用户属性映射
|
|
|
- for(SyncJobConfigField element:syncJobConfigFieldList){
|
|
|
- if(Integer.parseInt(element.getObjectType()) == USER_TYPE.intValue()){
|
|
|
+ for (SyncJobConfigField element : syncJobConfigFieldList) {
|
|
|
+ if (Integer.parseInt(element.getObjectType()) == USER_TYPE.intValue()) {
|
|
|
userFieldMap.put(element.getTargetField(), element.getSourceField());
|
|
|
}
|
|
|
}
|
|
|
return userFieldMap;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从字段映射中获取企业微信字段映射后的MaxKey字段名称
|
|
|
+ *
|
|
|
+ * @param fieldMap 字段映射
|
|
|
+ * @param wxField 企业微信字段名称
|
|
|
+ * @return MaxKey字段名称
|
|
|
+ */
|
|
|
+ public String getWxUserMappingField(Map<String, String> fieldMap, String wxField) {
|
|
|
+ for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
|
|
|
+ String userInfoProperty = entry.getKey();
|
|
|
+ String sourceProperty = entry.getValue();
|
|
|
+ if (sourceProperty.equals(wxField)) {
|
|
|
+ return userInfoProperty;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new RuntimeException(String.format("未找到企业微信字段'%s'映射后的本地字段,请检查同步器(ID: %s)的字段映射配置", wxField, this.synchronizer.getId()));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据企业微信用户Id映射字段获取用户信息
|
|
|
+ *
|
|
|
+ * @param fieldName 字段映射中和企业微信用户Id对应的MaxKey的字段名称
|
|
|
+ * @param fieldValue 企业微信用户Id
|
|
|
+ * @return 用户信息
|
|
|
+ */
|
|
|
+ public UserInfo getUserByWxUserIdMappingField(String fieldName, String fieldValue) {
|
|
|
+ return userInfoService.findOne(fieldName + " = ? and instId = ?",
|
|
|
+ new Object[]{fieldValue, this.synchronizer.getInstId()},
|
|
|
+ new int[]{Types.VARCHAR, Types.VARCHAR}
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public void setAccess_token(String access_token) {
|
|
|
this.access_token = access_token;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载当前同步器历史同步的企业微信用户映射,用于判定本次缺失用户。
|
|
|
+ *
|
|
|
+ * @return 以企业微信用户 originId 为键的同步关系映射
|
|
|
+ */
|
|
|
+ private Map<String, SynchroRelated> loadUserRelationHistory() {
|
|
|
+ Map<String, SynchroRelated> _userRelationHistoryMap = new HashMap<>();
|
|
|
+ List<SynchroRelated> historyRecords = synchroRelatedService.find(
|
|
|
+ "instid = ? and syncId = ? and objecttype = ?",
|
|
|
+ new Object[]{synchronizer.getInstId(), synchronizer.getId(), UserInfo.CLASS_TYPE},
|
|
|
+ new int[]{Types.VARCHAR, Types.VARCHAR, Types.VARCHAR}
|
|
|
+ );
|
|
|
+ if (historyRecords != null) {
|
|
|
+ for (SynchroRelated related : historyRecords) {
|
|
|
+ _userRelationHistoryMap.put(related.getOriginId(), related);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return _userRelationHistoryMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 禁用本次企业微信未返回的历史同步用户,并将 userState 标记为 WITHDRAWN。
|
|
|
+ *
|
|
|
+ * @param _userRelationHistoryMap 尚未在本次同步中匹配到的历史同步关系
|
|
|
+ */
|
|
|
+ private void disableMissingUsers(Map<String, SynchroRelated> _userRelationHistoryMap) {
|
|
|
+ if (_userRelationHistoryMap.isEmpty()) {
|
|
|
+ _logger.info("[同步微信用户] 本次同步未发现需要禁用的历史用户。");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _logger.info("[同步微信用户] 发现 {} 个历史同步用户未出现在本次企业微信返回,开始禁用。", _userRelationHistoryMap.size());
|
|
|
+ for (SynchroRelated orphanRelation : _userRelationHistoryMap.values()) {
|
|
|
+ UserInfo localUser = userInfoService.get(orphanRelation.getObjectId());
|
|
|
+ if (localUser == null) {
|
|
|
+ _logger.warn("[同步微信用户] 历史同步用户 originId={} (objectId={}) 在本地不存在,跳过禁用。", orphanRelation.getOriginId(), orphanRelation.getObjectId());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (localUser.getStatus() == ConstsStatus.DISABLED && "WITHDRAWN".equals(localUser.getUserState())) {
|
|
|
+ _logger.debug("[同步微信用户] 用户 {} (originId={}) 已处于禁用且离职状态,跳过更新。", localUser.getUsername(), orphanRelation.getOriginId());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ localUser.setStatus(ConstsStatus.DISABLED);
|
|
|
+ localUser.setUserState("WITHDRAWN");
|
|
|
+ userInfoService.update(localUser);
|
|
|
+ _logger.info("[同步微信用户] 用户 {} (originId={}) 已标记为禁用,userState=WITHDRAWN。", localUser.getUsername(), orphanRelation.getOriginId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public SyncJobConfigFieldService getSyncJobConfigFieldService() {
|
|
|
return syncJobConfigFieldService;
|
|
|
}
|