⚡ OA人员同步改为按工号匹配本地用户

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-02 16:44:06 +08:00
parent 4d88f8913b
commit 0b2348f8fc
3 changed files with 82 additions and 183 deletions
@@ -70,14 +70,14 @@ public interface UserConvert {
@Mapping(source = "lastname", target = "realName") @Mapping(source = "lastname", target = "realName")
@Mapping(source = "mobile", target = "phone") @Mapping(source = "mobile", target = "phone")
@Mapping(source = "email", target = "email") @Mapping(source = "email", target = "email")
@Mapping(source = "loginid", target = "oaLoginId") @Mapping(target = "oaLoginId", ignore = true)
@Mapping(source = "modified", target = "oaModified") @Mapping(source = "modified", target = "oaModified")
User baseConvert(OAPersonResponse person); User baseConvert(OAPersonResponse person);
@Mapping(source = "subcompanyname", target = "companyName") @Mapping(source = "subcompanyname", target = "companyName")
@Mapping(source = "departmentname", target = "deptName") @Mapping(source = "departmentname", target = "deptName")
@Mapping(source = "id", target = "oaId") @Mapping(source = "id", target = "oaId")
@Mapping(source = "loginid", target = "oaLoginId") @Mapping(target = "oaLoginId", ignore = true)
UserDept userDeptConvert(OAPersonResponse person); UserDept userDeptConvert(OAPersonResponse person);
/** /**
@@ -88,6 +88,7 @@ public interface UserConvert {
*/ */
default User person2user(OAPersonResponse person, String defaultPassword) { default User person2user(OAPersonResponse person, String defaultPassword) {
User user = baseConvert(person); User user = baseConvert(person);
user.setOaLoginId(resolveOaLoginId(person));
user.setAccount(resolveAccount(person)); user.setAccount(resolveAccount(person));
// OA 可能返回多号(如 133.../137...),只保留首个号码再入库加密 // OA 可能返回多号(如 133.../137...),只保留首个号码再入库加密
user.setPhone(normalizeMobile(user.getPhone())); user.setPhone(normalizeMobile(user.getPhone()));
@@ -106,6 +107,22 @@ public interface UserConvert {
return user; return user;
} }
/**
* 解析 OA 登录名:优先工号 workcode,其次 loginid
*/
default String resolveOaLoginId(OAPersonResponse person) {
if (person == null) {
return null;
}
if (StringUtils.isNotBlank(person.getWorkcode())) {
return person.getWorkcode().trim();
}
if (StringUtils.isNotBlank(person.getLoginid())) {
return person.getLoginid().trim();
}
return null;
}
/** /**
* 规范化手机号:去空白,多号时取第一个 * 规范化手机号:去空白,多号时取第一个
*/ */
@@ -124,7 +141,7 @@ public interface UserConvert {
} }
/** /**
* 解析本系统登录账号:优先 OA loginid,其次工号,最后手机号 * 解析本系统登录账号:优先工号 workcode,其次 OA loginid,最后手机号
* *
* @param person OA人员 * @param person OA人员
* @return 账号,无法识别时返回 null * @return 账号,无法识别时返回 null
@@ -133,12 +150,12 @@ public interface UserConvert {
if (person == null) { if (person == null) {
return null; return null;
} }
if (StringUtils.isNotBlank(person.getLoginid())) {
return person.getLoginid().trim();
}
if (StringUtils.isNotBlank(person.getWorkcode())) { if (StringUtils.isNotBlank(person.getWorkcode())) {
return person.getWorkcode().trim(); return person.getWorkcode().trim();
} }
if (StringUtils.isNotBlank(person.getLoginid())) {
return person.getLoginid().trim();
}
if (StringUtils.isNotBlank(person.getMobile())) { if (StringUtils.isNotBlank(person.getMobile())) {
return person.getMobile().trim(); return person.getMobile().trim();
} }
@@ -148,7 +165,7 @@ public interface UserConvert {
/** /**
* oa人员转本系统用户部门 * oa人员转本系统用户部门
* @param person * @param person
* @param userMap OA登录名与用户id * @param userMap 同步键(W:工号)与用户id
* @return * @return
*/ */
default UserDept person2userDept(OAPersonResponse person, Map<String, Long> userMap) { default UserDept person2userDept(OAPersonResponse person, Map<String, Long> userMap) {
@@ -162,9 +179,11 @@ public interface UserConvert {
userDept.setStatus(status); userDept.setStatus(status);
// 排序 // 排序
userDept.setSort(OAUtils.parseInt(person.getDsporder())); userDept.setSort(OAUtils.parseInt(person.getDsporder()));
// 用户id,按 OA 登录名关联本批已新增或更新的用户 // 用户id,按工号同步键关联本批已新增或更新的用户
String oaLoginId = person == null ? null : StringUtils.trimToNull(person.getLoginid()); String oaLoginId = resolveOaLoginId(person);
userDept.setUserId(oaLoginId == null ? null : userMap.get(oaLoginId)); userDept.setOaLoginId(oaLoginId);
String workcode = person == null ? null : StringUtils.trimToNull(person.getWorkcode());
userDept.setUserId(workcode == null ? null : userMap.get("W:" + workcode));
userDept.setSyncTime(new Date()); userDept.setSyncTime(new Date());
return userDept; return userDept;
} }
@@ -278,9 +278,7 @@ public class OASyncServiceImpl implements IOASyncService {
OaUserListSyncHelper.OaOrgIndex orgIndex = oaUserListSyncHelper.syncOrgsFromPersons(oaPersons); OaUserListSyncHelper.OaOrgIndex orgIndex = oaUserListSyncHelper.syncOrgsFromPersons(oaPersons);
OaUserListSyncHelper.PersonSyncCount personSyncCount = oaUserListSyncHelper.handlePerson(oaPersons, orgIndex); OaUserListSyncHelper.PersonSyncCount personSyncCount = oaUserListSyncHelper.handlePerson(oaPersons, orgIndex);
if (startTime == null) { if (startTime == null) {
oaUserListSyncHelper.removeOaUsersAbsentFrom( oaUserListSyncHelper.removeOaUsersAbsentFrom(oaUserListSyncHelper.extractWorkcodes(oaPersons));
oaUserListSyncHelper.extractOaLoginIds(oaPersons),
oaUserListSyncHelper.extractWorkcodes(oaPersons));
} }
CacheUtil.clear(USER_CACHE); CacheUtil.clear(USER_CACHE);
CacheUtil.clear(SYS_CACHE); CacheUtil.clear(SYS_CACHE);
@@ -300,7 +298,7 @@ public class OASyncServiceImpl implements IOASyncService {
private OaPersonSyncPageVO syncPersonFromOaPage(int current, int size, boolean incremental, String modifiedSince) { private OaPersonSyncPageVO syncPersonFromOaPage(int current, int size, boolean incremental, String modifiedSince) {
int pageNo = current < 1 ? 1 : current; int pageNo = current < 1 ? 1 : current;
int pageSize = size < 1 ? 20 : Math.min(size, 200); int pageSize = size < 1 ? 20 : Math.min(size, 200);
// 全量同步首页开启原始数据收集,并开始累积登录名,最后一页再删除本地多余人员 // 全量同步首页开启原始数据收集,并开始累积工号,最后一页再删除本地多余人员
if (!incremental && pageNo == 1) { if (!incremental && pageNo == 1) {
oaSyncRawDumpHelper.beginFullDump(); oaSyncRawDumpHelper.beginFullDump();
oaUserListSyncHelper.beginFullSyncReconcile(); oaUserListSyncHelper.beginFullSyncReconcile();
@@ -87,10 +87,8 @@ public class OaUserListSyncHelper {
private static final String OA_PERSON_STATUS_RESIGNED = "5"; private static final String OA_PERSON_STATUS_RESIGNED = "5";
/** /**
* 全量同步跨页累积的 OA 登录名。分页接口每页独立事务,删除必须等最后一页再执行。 * 全量同步跨页累积的 OA 工号。分页接口每页独立事务,删除必须等最后一页再执行。
*/ */
private final Set<String> fullSyncLoginIds = ConcurrentHashMap.newKeySet();
private final Set<String> fullSyncWorkcodes = ConcurrentHashMap.newKeySet(); private final Set<String> fullSyncWorkcodes = ConcurrentHashMap.newKeySet();
private volatile boolean fullSyncCollecting; private volatile boolean fullSyncCollecting;
@@ -409,16 +407,15 @@ public class OaUserListSyncHelper {
} }
/** /**
* 全量同步从第一页开始收集 OA 登录名,最后一页再删除本地多余的 is_oa=2 人员。 * 全量同步从第一页开始收集 OA 工号,最后一页再删除本地多余的 is_oa=2 人员。
*/ */
public synchronized void beginFullSyncReconcile() { public synchronized void beginFullSyncReconcile() {
this.fullSyncLoginIds.clear();
this.fullSyncWorkcodes.clear(); this.fullSyncWorkcodes.clear();
this.fullSyncCollecting = true; this.fullSyncCollecting = true;
} }
/** /**
* 累积本页 OA 人员登录名,供全量同步结束后比对删除。 * 累积本页 OA 人员工号,供全量同步结束后比对删除。
* *
* @param oaPersons 本页 OA 人员 * @param oaPersons 本页 OA 人员
*/ */
@@ -426,49 +423,26 @@ public class OaUserListSyncHelper {
if (!this.fullSyncCollecting || CollectionUtil.isEmpty(oaPersons)) { if (!this.fullSyncCollecting || CollectionUtil.isEmpty(oaPersons)) {
return; return;
} }
this.fullSyncLoginIds.addAll(this.extractOaLoginIds(oaPersons));
this.fullSyncWorkcodes.addAll(this.extractWorkcodes(oaPersons)); this.fullSyncWorkcodes.addAll(this.extractWorkcodes(oaPersons));
} }
/** /**
* 全量同步结束后,删除登录名不在本次 OA 数据中的 is_oa=2 人员。 * 全量同步结束后,删除工号不在本次 OA 数据中的 is_oa=2 人员。
* *
* @return 删除人数;未开始收集或登录名集合为空时返回 0,避免误删全部 OA 人员 * @return 删除人数;未开始收集或工号集合为空时返回 0,避免误删全部 OA 人员
*/ */
public synchronized int finishFullSyncReconcile() { public synchronized int finishFullSyncReconcile() {
if (!this.fullSyncCollecting) { if (!this.fullSyncCollecting) {
return 0; return 0;
} }
this.fullSyncCollecting = false; this.fullSyncCollecting = false;
Set<String> oaLoginIds = new LinkedHashSet<>(this.fullSyncLoginIds);
Set<String> workcodes = new LinkedHashSet<>(this.fullSyncWorkcodes); Set<String> workcodes = new LinkedHashSet<>(this.fullSyncWorkcodes);
this.fullSyncLoginIds.clear();
this.fullSyncWorkcodes.clear(); this.fullSyncWorkcodes.clear();
return this.removeOaUsersAbsentFrom(oaLoginIds, workcodes); return this.removeOaUsersAbsentFrom(workcodes);
} }
/** /**
* 提取仍在职的 OA 人员登录名。离职人员不计入,全量同步结束时会删掉对应本地账号。 * 提取仍在职人员的工号。离职人员不计入,全量同步结束时会删掉对应本地账号。
*
* @param oaPersons OA人员
* @return 去重后的登录名
*/
public Set<String> extractOaLoginIds(List<OAPersonResponse> oaPersons) {
Set<String> oaLoginIds = new LinkedHashSet<>();
if (CollectionUtil.isEmpty(oaPersons)) {
return oaLoginIds;
}
for (OAPersonResponse oaPerson : oaPersons) {
String oaLoginId = this.resolveOaLoginId(oaPerson);
if (oaLoginId != null && !this.isOaResigned(oaPerson)) {
oaLoginIds.add(oaLoginId);
}
}
return oaLoginIds;
}
/**
* 提取仍在职人员的工号。没有登录名时靠工号保留本地账号。
* *
* @param oaPersons OA人员 * @param oaPersons OA人员
* @return 去重后的工号 * @return 去重后的工号
@@ -488,25 +462,21 @@ public class OaUserListSyncHelper {
} }
/** /**
* 删除当前租户中 is_oa=2、且既不在本次登录名里、也不是无登录名工号人员的账号。 * 删除当前租户中 is_oa=2、且工号不在本次同步工号集合中的账号。
* 登录名和工号都没收集到时不删除。当前登录账号即使不在同步数据中也保留。 * 未收集到工号时不删除。当前登录账号即使不在同步数据中也保留。
* *
* @param oaLoginIds 本次全量同步出现过的 OA 登录名
* @param workcodes 本次全量同步出现过的工号 * @param workcodes 本次全量同步出现过的工号
* @return 删除人数 * @return 删除人数
*/ */
public int removeOaUsersAbsentFrom(Set<String> oaLoginIds, Set<String> workcodes) { public int removeOaUsersAbsentFrom(Set<String> workcodes) {
boolean noLoginIds = CollectionUtil.isEmpty(oaLoginIds); if (CollectionUtil.isEmpty(workcodes)) {
boolean noWorkcodes = CollectionUtil.isEmpty(workcodes); ComposeLogUtil.getLastLog().warn("全量同步未收集到OA工号,跳过删除本地 is_oa=2 人员");
if (noLoginIds && noWorkcodes) {
ComposeLogUtil.getLastLog().warn("全量同步未收集到OA登录名和工号,跳过删除本地 is_oa=2 人员");
return 0; return 0;
} }
Set<String> loginIdSet = noLoginIds ? Set.of() : oaLoginIds; Set<String> workcodeSet = workcodes;
Set<String> workcodeSet = noWorkcodes ? Set.of() : workcodes;
String tenantId = this.resolveTenantId(); String tenantId = this.resolveTenantId();
List<User> oaUsers = this.userService.list(Wrappers.<User>lambdaQuery() List<User> oaUsers = this.userService.list(Wrappers.<User>lambdaQuery()
.select(User::getId, User::getOaLoginId, User::getCode) .select(User::getId, User::getCode)
.eq(User::getTenantId, tenantId) .eq(User::getTenantId, tenantId)
.eq(User::getIsOa, CommonConstant.YES)); .eq(User::getIsOa, CommonConstant.YES));
Long currentUserId = AuthUtil.getUserId(); Long currentUserId = AuthUtil.getUserId();
@@ -519,11 +489,8 @@ public class OaUserListSyncHelper {
ComposeLogUtil.getLastLog().warn("全量同步跳过删除当前登录账号 userId={}", oaUser.getId()); ComposeLogUtil.getLastLog().warn("全量同步跳过删除当前登录账号 userId={}", oaUser.getId());
continue; continue;
} }
String oaLoginId = StringUtils.trimToNull(oaUser.getOaLoginId());
String workcode = StringUtils.trimToNull(oaUser.getCode()); String workcode = StringUtils.trimToNull(oaUser.getCode());
boolean keptByLoginId = oaLoginId != null && loginIdSet.contains(oaLoginId); if (workcode == null || !workcodeSet.contains(workcode)) {
boolean keptByWorkcode = oaLoginId == null && workcode != null && workcodeSet.contains(workcode);
if (!keptByLoginId && !keptByWorkcode) {
removeIds.add(oaUser.getId()); removeIds.add(oaUser.getId());
} }
} }
@@ -535,54 +502,13 @@ public class OaUserListSyncHelper {
} }
/** /**
* 按 OA 登录名删除已有 is_oa=2 人员。用于离职人员,全量和增量同步都会执行。 * 按工号删除已有 is_oa=2 人员。用于离职人员,全量和增量同步都会执行。
*
* @param tenantId 租户
* @param oaLoginIds 离职人员登录名
* @return 删除人数
*/
private int removeOaUsersByLoginIds(String tenantId, Collection<String> oaLoginIds) {
if (CollectionUtil.isEmpty(oaLoginIds)) {
return 0;
}
List<String> oaLoginIdList = oaLoginIds.stream()
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
if (oaLoginIdList.isEmpty()) {
return 0;
}
Long currentUserId = AuthUtil.getUserId();
List<Long> removeIds = new ArrayList<>();
for (int fromIndex = 0; fromIndex < oaLoginIdList.size(); fromIndex += OA_LOGIN_ID_BATCH_SIZE) {
List<String> oaLoginIdBatch = oaLoginIdList.subList(fromIndex, Math.min(fromIndex + OA_LOGIN_ID_BATCH_SIZE, oaLoginIdList.size()));
this.userService.list(Wrappers.<User>lambdaQuery()
.select(User::getId, User::getOaLoginId)
.eq(User::getTenantId, tenantId)
.eq(User::getIsOa, CommonConstant.YES)
.in(User::getOaLoginId, oaLoginIdBatch))
.forEach(oaUser -> {
if (oaUser.getId() == null) {
return;
}
if (currentUserId != null && currentUserId > 0 && currentUserId.equals(oaUser.getId())) {
ComposeLogUtil.getLastLog().warn("同步跳过删除当前登录账号 userId={}", oaUser.getId());
return;
}
removeIds.add(oaUser.getId());
});
}
return this.deleteOaUsers(removeIds, "同步删除OA离职人员");
}
/**
* 删除没有登录名、且工号属于离职人员的 is_oa=2 账号。
* *
* @param tenantId 租户 * @param tenantId 租户
* @param workcodes 离职人员工号 * @param workcodes 离职人员工号
* @return 删除人数 * @return 删除人数
*/ */
private int removeOaUsersByBlankLoginWorkcodes(String tenantId, Collection<String> workcodes) { private int removeOaUsersByWorkcodes(String tenantId, Collection<String> workcodes) {
if (CollectionUtil.isEmpty(workcodes)) { if (CollectionUtil.isEmpty(workcodes)) {
return 0; return 0;
} }
@@ -598,11 +524,10 @@ public class OaUserListSyncHelper {
for (int fromIndex = 0; fromIndex < workcodeList.size(); fromIndex += OA_LOGIN_ID_BATCH_SIZE) { for (int fromIndex = 0; fromIndex < workcodeList.size(); fromIndex += OA_LOGIN_ID_BATCH_SIZE) {
List<String> workcodeBatch = workcodeList.subList(fromIndex, Math.min(fromIndex + OA_LOGIN_ID_BATCH_SIZE, workcodeList.size())); List<String> workcodeBatch = workcodeList.subList(fromIndex, Math.min(fromIndex + OA_LOGIN_ID_BATCH_SIZE, workcodeList.size()));
this.userService.list(Wrappers.<User>lambdaQuery() this.userService.list(Wrappers.<User>lambdaQuery()
.select(User::getId, User::getOaLoginId, User::getCode) .select(User::getId, User::getCode)
.eq(User::getTenantId, tenantId) .eq(User::getTenantId, tenantId)
.eq(User::getIsOa, CommonConstant.YES) .eq(User::getIsOa, CommonConstant.YES)
.in(User::getCode, workcodeBatch) .in(User::getCode, workcodeBatch))
.and(wrapper -> wrapper.isNull(User::getOaLoginId).or().eq(User::getOaLoginId, "")))
.forEach(oaUser -> { .forEach(oaUser -> {
if (oaUser.getId() == null) { if (oaUser.getId() == null) {
return; return;
@@ -614,7 +539,7 @@ public class OaUserListSyncHelper {
removeIds.add(oaUser.getId()); removeIds.add(oaUser.getId());
}); });
} }
return this.deleteOaUsers(removeIds, "同步删除无登录名的OA离职人员"); return this.deleteOaUsers(removeIds, "同步删除OA离职人员");
} }
/** /**
@@ -645,7 +570,8 @@ public class OaUserListSyncHelper {
} }
/** /**
* 同步人员并绑定到三级部门。有登录名按登录名更新,没有登录名但有工号则按工号更新且不回填登录名。离职人员直接删除。 * 同步人员并绑定到三级部门。按工号 workcode(对应本地 code)匹配已有用户;
* OA登录名仍优先填 workcode,其次 loginid。无工号则跳过。离职人员按工号删除。
* *
* @param oaPersons OA人员 * @param oaPersons OA人员
* @param orgIndex 组织索引 * @param orgIndex 组织索引
@@ -656,7 +582,6 @@ public class OaUserListSyncHelper {
return new PersonSyncCount(0, 0, 0); return new PersonSyncCount(0, 0, 0);
} }
Map<String, OAPersonResponse> uniquePersonMap = new LinkedHashMap<>(); Map<String, OAPersonResponse> uniquePersonMap = new LinkedHashMap<>();
Set<String> resignedLoginIds = new LinkedHashSet<>();
Set<String> resignedWorkcodes = new LinkedHashSet<>(); Set<String> resignedWorkcodes = new LinkedHashSet<>();
int skippedCount = 0; int skippedCount = 0;
for (OAPersonResponse oaPerson : oaPersons) { for (OAPersonResponse oaPerson : oaPersons) {
@@ -665,49 +590,36 @@ public class OaUserListSyncHelper {
skippedCount++; skippedCount++;
continue; continue;
} }
String oaLoginId = this.resolveOaLoginId(oaPerson);
String workcode = this.resolveWorkcode(oaPerson); String workcode = this.resolveWorkcode(oaPerson);
if (this.isOaResigned(oaPerson)) { if (this.isOaResigned(oaPerson)) {
uniquePersonMap.remove(syncKey); uniquePersonMap.remove(syncKey);
if (oaLoginId != null) { if (workcode != null) {
resignedLoginIds.add(oaLoginId);
} else if (workcode != null) {
resignedWorkcodes.add(workcode); resignedWorkcodes.add(workcode);
} }
continue; continue;
} }
if (oaLoginId != null) { if (workcode != null) {
resignedLoginIds.remove(oaLoginId);
} else if (workcode != null) {
resignedWorkcodes.remove(workcode); resignedWorkcodes.remove(workcode);
} }
uniquePersonMap.put(syncKey, oaPerson); uniquePersonMap.put(syncKey, oaPerson);
} }
String tenantId = resolveTenantId(); String tenantId = resolveTenantId();
int removedCount = this.removeOaUsersByLoginIds(tenantId, resignedLoginIds) int removedCount = this.removeOaUsersByWorkcodes(tenantId, resignedWorkcodes);
+ this.removeOaUsersByBlankLoginWorkcodes(tenantId, resignedWorkcodes);
if (uniquePersonMap.isEmpty()) { if (uniquePersonMap.isEmpty()) {
if (resignedLoginIds.isEmpty() && resignedWorkcodes.isEmpty()) { if (resignedWorkcodes.isEmpty()) {
ComposeLogUtil.getLastLog().warn("OA人员均缺少登录名和工号,跳过人员同步"); ComposeLogUtil.getLastLog().warn("OA人员均缺少工号,跳过人员同步");
} }
return new PersonSyncCount(0, skippedCount, removedCount); return new PersonSyncCount(0, skippedCount, removedCount);
} }
String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD)); String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD));
Set<String> oaLoginIds = new LinkedHashSet<>();
Set<String> workcodes = new LinkedHashSet<>(); Set<String> workcodes = new LinkedHashSet<>();
for (OAPersonResponse oaPerson : uniquePersonMap.values()) { for (OAPersonResponse oaPerson : uniquePersonMap.values()) {
String oaLoginId = this.resolveOaLoginId(oaPerson);
if (oaLoginId != null) {
oaLoginIds.add(oaLoginId);
continue;
}
String workcode = this.resolveWorkcode(oaPerson); String workcode = this.resolveWorkcode(oaPerson);
if (workcode != null) { if (workcode != null) {
workcodes.add(workcode); workcodes.add(workcode);
} }
} }
Map<String, User> existingByOaLoginId = this.loadUsersByOaLoginId(tenantId, oaLoginIds); Map<String, User> existingByWorkcode = this.loadUsersByWorkcode(tenantId, workcodes);
Map<String, User> existingByWorkcode = this.loadUsersByBlankLoginWorkcode(tenantId, workcodes);
List<User> users = new ArrayList<>(); List<User> users = new ArrayList<>();
Map<String, Long> userMap = new HashMap<>(); Map<String, Long> userMap = new HashMap<>();
Set<Long> existsUserIds = new HashSet<>(); Set<Long> existsUserIds = new HashSet<>();
@@ -717,7 +629,7 @@ public class OaUserListSyncHelper {
String workcode = this.resolveWorkcode(oaPerson); String workcode = this.resolveWorkcode(oaPerson);
User user = this.userConvert.person2user(oaPerson, defaultPassword); User user = this.userConvert.person2user(oaPerson, defaultPassword);
user.setTenantId(tenantId); user.setTenantId(tenantId);
User existingUser = oaLoginId != null ? existingByOaLoginId.get(oaLoginId) : existingByWorkcode.get(workcode); User existingUser = workcode == null ? null : existingByWorkcode.get(workcode);
if (oaLoginId != null) { if (oaLoginId != null) {
user.setOaLoginId(oaLoginId); user.setOaLoginId(oaLoginId);
user.setAccount(oaLoginId); user.setAccount(oaLoginId);
@@ -757,7 +669,7 @@ public class OaUserListSyncHelper {
ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size()); ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size());
userService.updateBatchById(updateUsers); userService.updateBatchById(updateUsers);
} }
ComposeLogUtil.getLastLog().info("缺少登录名和工号已跳过的人员:{}", skippedCount); ComposeLogUtil.getLastLog().info("缺少工号已跳过的人员:{}", skippedCount);
int syncedCount = addUsers.size() + updateUsers.size(); int syncedCount = addUsers.size() + updateUsers.size();
if (userMap.isEmpty()) { if (userMap.isEmpty()) {
return new PersonSyncCount(0, skippedCount, removedCount); return new PersonSyncCount(0, skippedCount, removedCount);
@@ -882,50 +794,9 @@ public class OaUserListSyncHelper {
} }
/** /**
* 按 OA 登录名加载已有 is_oa=2 用户。同一登录名有多条时保留较早的一条,后续同步都更新这一条。 * 按工号(User.code)加载已有 is_oa=2 用户。同一工号有多条时保留较早的一条,后续同步都更新这一条。
*/ */
private Map<String, User> loadUsersByOaLoginId(String tenantId, Collection<String> oaLoginIds) { private Map<String, User> loadUsersByWorkcode(String tenantId, Collection<String> workcodes) {
Map<String, User> existingByOaLoginId = new HashMap<>();
if (CollectionUtil.isEmpty(oaLoginIds)) {
return existingByOaLoginId;
}
List<String> oaLoginIdList = oaLoginIds.stream()
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
for (int fromIndex = 0; fromIndex < oaLoginIdList.size(); fromIndex += OA_LOGIN_ID_BATCH_SIZE) {
List<String> oaLoginIdBatch = oaLoginIdList.subList(fromIndex, Math.min(fromIndex + OA_LOGIN_ID_BATCH_SIZE, oaLoginIdList.size()));
userService.list(Wrappers.<User>lambdaQuery()
.eq(User::getTenantId, tenantId)
.eq(User::getIsOa, CommonConstant.YES)
.in(User::getOaLoginId, oaLoginIdBatch))
.forEach(user -> {
String oaLoginId = StringUtils.trimToNull(user.getOaLoginId());
if (oaLoginId == null) {
return;
}
User previous = existingByOaLoginId.get(oaLoginId);
if (previous == null) {
existingByOaLoginId.put(oaLoginId, user);
return;
}
if (user.getId() != null && previous.getId() != null && user.getId() < previous.getId()) {
ComposeLogUtil.getLastLog().warn("OA登录名重复,保留较早用户 oaLoginId={} keepUserId={} dropUserId={}",
oaLoginId, user.getId(), previous.getId());
existingByOaLoginId.put(oaLoginId, user);
return;
}
ComposeLogUtil.getLastLog().warn("OA登录名重复,保留较早用户 oaLoginId={} keepUserId={} dropUserId={}",
oaLoginId, previous.getId(), user.getId());
});
}
return existingByOaLoginId;
}
/**
* 按工号加载没有登录名的 is_oa=2 用户。同一工号有多条时保留较早的一条。
*/
private Map<String, User> loadUsersByBlankLoginWorkcode(String tenantId, Collection<String> workcodes) {
Map<String, User> existingByWorkcode = new HashMap<>(); Map<String, User> existingByWorkcode = new HashMap<>();
if (CollectionUtil.isEmpty(workcodes)) { if (CollectionUtil.isEmpty(workcodes)) {
return existingByWorkcode; return existingByWorkcode;
@@ -939,26 +810,41 @@ public class OaUserListSyncHelper {
this.userService.list(Wrappers.<User>lambdaQuery() this.userService.list(Wrappers.<User>lambdaQuery()
.eq(User::getTenantId, tenantId) .eq(User::getTenantId, tenantId)
.eq(User::getIsOa, CommonConstant.YES) .eq(User::getIsOa, CommonConstant.YES)
.in(User::getCode, workcodeBatch) .in(User::getCode, workcodeBatch))
.and(wrapper -> wrapper.isNull(User::getOaLoginId).or().eq(User::getOaLoginId, "")))
.forEach(user -> { .forEach(user -> {
String workcode = StringUtils.trimToNull(user.getCode()); String workcode = StringUtils.trimToNull(user.getCode());
if (workcode == null) { if (workcode == null) {
return; return;
} }
User previous = existingByWorkcode.get(workcode); User previous = existingByWorkcode.get(workcode);
if (previous == null || (user.getId() != null && previous.getId() != null && user.getId() < previous.getId())) { if (previous == null) {
existingByWorkcode.put(workcode, user); existingByWorkcode.put(workcode, user);
return;
} }
if (user.getId() != null && previous.getId() != null && user.getId() < previous.getId()) {
ComposeLogUtil.getLastLog().warn("OA工号重复,保留较早用户 workcode={} keepUserId={} dropUserId={}",
workcode, user.getId(), previous.getId());
existingByWorkcode.put(workcode, user);
return;
}
ComposeLogUtil.getLastLog().warn("OA工号重复,保留较早用户 workcode={} keepUserId={} dropUserId={}",
workcode, previous.getId(), user.getId());
}); });
} }
return existingByWorkcode; return existingByWorkcode;
} }
/**
* OA登录名:优先 workcode,其次 loginid。仅用于填充 oaLoginId/account,不参与匹配。
*/
private String resolveOaLoginId(OAPersonResponse oaPerson) { private String resolveOaLoginId(OAPersonResponse oaPerson) {
if (oaPerson == null) { if (oaPerson == null) {
return null; return null;
} }
String workcode = StringUtils.trimToNull(oaPerson.getWorkcode());
if (workcode != null) {
return workcode;
}
return StringUtils.trimToNull(oaPerson.getLoginid()); return StringUtils.trimToNull(oaPerson.getLoginid());
} }
@@ -970,13 +856,9 @@ public class OaUserListSyncHelper {
} }
/** /**
* 有登录名用登录名,没有登录名但有工号用工号。两者都没有则不同步。 * 同步去重与匹配键:仅使用工号。无工号则不同步。
*/ */
private String resolveSyncKey(OAPersonResponse oaPerson) { private String resolveSyncKey(OAPersonResponse oaPerson) {
String oaLoginId = this.resolveOaLoginId(oaPerson);
if (oaLoginId != null) {
return "L:" + oaLoginId;
}
String workcode = this.resolveWorkcode(oaPerson); String workcode = this.resolveWorkcode(oaPerson);
if (workcode != null) { if (workcode != null) {
return "W:" + workcode; return "W:" + workcode;