iwork 人员组织同步相关

This commit is contained in:
chenbowen
2025-11-25 23:26:26 +08:00
parent dc1db47d07
commit f754b1c694
5 changed files with 118 additions and 80 deletions

View File

@@ -5,11 +5,10 @@ import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@@ -31,20 +30,89 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "是否成功") @Schema(description = "是否成功")
private boolean success; private boolean success;
@Schema(description = "总条数") @Schema(description = "iWork 返回的数据体")
private Integer totalSize; @JsonProperty("data")
private PageData data;
@Schema(description = "总页数") @JsonIgnore
private Integer totalPage; public Integer getTotalSize() {
return data == null ? null : data.getTotalSize();
}
@Schema(description = "每页条数") @JsonIgnore
private Integer pageSize; public void setTotalSize(Integer totalSize) {
ensureData().setTotalSize(totalSize);
}
@Schema(description = "当前页码") @JsonIgnore
private Integer pageNumber; public Integer getTotalPage() {
return data == null ? null : data.getTotalPage();
}
@Schema(description = "部门数据列表") @JsonIgnore
private List<Department> dataList; public void setTotalPage(Integer totalPage) {
ensureData().setTotalPage(totalPage);
}
@JsonIgnore
public Integer getPageSize() {
return data == null ? null : data.getPageSize();
}
@JsonIgnore
public void setPageSize(Integer pageSize) {
ensureData().setPageSize(pageSize);
}
@JsonIgnore
public Integer getPageNumber() {
return data == null ? null : data.getPageNumber();
}
@JsonIgnore
public void setPageNumber(Integer pageNumber) {
ensureData().setPageNumber(pageNumber);
}
@JsonIgnore
public List<Department> getDataList() {
return data == null ? null : data.getDataList();
}
@JsonIgnore
public void setDataList(List<Department> dataList) {
ensureData().setDataList(dataList);
}
@JsonIgnore
private PageData ensureData() {
if (data == null) {
data = new PageData();
}
return data;
}
@Data
@Schema(description = "iWork 部门分页数据体")
public static class PageData {
@Schema(description = "总条数")
private Integer totalSize;
@Schema(description = "总页数")
private Integer totalPage;
@Schema(description = "每页条数")
private Integer pageSize;
@Schema(description = "当前页码")
@JsonProperty("page")
private Integer pageNumber;
@Schema(description = "部门数据列表")
@JsonProperty("dataList")
private List<Department> dataList;
}
@Data @Data
@Schema(description = "部门信息") @Schema(description = "部门信息")
@@ -52,8 +120,14 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "部门 ID") @Schema(description = "部门 ID")
@JsonProperty("departmentid") @JsonProperty("departmentid")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer departmentid; private Integer departmentid;
@Schema(description = "部门 IDiWork 主键)")
@JsonProperty("id")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer id;
@Schema(description = "部门编码") @Schema(description = "部门编码")
@JsonProperty("departmentcode") @JsonProperty("departmentcode")
private String departmentcode; private String departmentcode;
@@ -68,6 +142,7 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "所属分部 ID") @Schema(description = "所属分部 ID")
@JsonProperty("subcompanyid1") @JsonProperty("subcompanyid1")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer subcompanyid1; private Integer subcompanyid1;
@Schema(description = "所属分部名称") @Schema(description = "所属分部名称")
@@ -76,6 +151,7 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "上级分部 ID") @Schema(description = "上级分部 ID")
@JsonProperty("supsubcomid") @JsonProperty("supsubcomid")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer supsubcomid; private Integer supsubcomid;
@Schema(description = "上级分部名称") @Schema(description = "上级分部名称")
@@ -84,6 +160,7 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "父部门 ID") @Schema(description = "父部门 ID")
@JsonProperty("parentdeptid") @JsonProperty("parentdeptid")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer parentdeptid; private Integer parentdeptid;
@Schema(description = "父部门名称") @Schema(description = "父部门名称")
@@ -113,6 +190,7 @@ public class IWorkHrDepartmentPageRespVO {
@Schema(description = "负责人 ID") @Schema(description = "负责人 ID")
@JsonProperty("managerid") @JsonProperty("managerid")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer managerid; private Integer managerid;
@Schema(description = "负责人名称") @Schema(description = "负责人名称")

View File

@@ -189,7 +189,7 @@ public class DeptServiceImpl implements DeptService {
// 2. 父部门不存在 // 2. 父部门不存在
DeptDO parentDept = deptMapper.selectById(parentId); DeptDO parentDept = deptMapper.selectById(parentId);
if (parentDept == null) { if (parentDept == null) {
throw exception(DEPT_PARENT_NOT_EXITS); return;
} }
// 3. 递归校验父部门,如果父部门是自己的子部门,则报错,避免形成环路 // 3. 递归校验父部门,如果父部门是自己的子部门,则报错,避免形成环路
if (id == null) { // id 为空,说明新增,不需要考虑环路 if (id == null) { // id 为空,说明新增,不需要考虑环路
@@ -251,19 +251,18 @@ public class DeptServiceImpl implements DeptService {
private String generateDeptCode(Long parentId) { private String generateDeptCode(Long parentId) {
Long effectiveParentId = normalizeParentId(parentId); Long effectiveParentId = normalizeParentId(parentId);
Long codeParentId = effectiveParentId;
String prefix = ROOT_CODE_PREFIX; String prefix = ROOT_CODE_PREFIX;
if (!DeptDO.PARENT_ID_ROOT.equals(effectiveParentId)) { if (!DeptDO.PARENT_ID_ROOT.equals(effectiveParentId)) {
DeptDO parentDept = deptMapper.selectById(effectiveParentId); DeptDO parentDept = deptMapper.selectById(effectiveParentId);
if (parentDept == null) { if (parentDept == null || StrUtil.isBlank(parentDept.getCode())) {
throw exception(DEPT_PARENT_NOT_EXITS); codeParentId = DeptDO.PARENT_ID_ROOT;
} else {
prefix = parentDept.getCode();
} }
if (StrUtil.isBlank(parentDept.getCode())) {
throw exception(DEPT_PARENT_CODE_NOT_INITIALIZED);
}
prefix = parentDept.getCode();
} }
int nextSequence = determineNextSequence(effectiveParentId, prefix); int nextSequence = determineNextSequence(codeParentId, prefix);
assertSequenceRange(nextSequence); assertSequenceRange(nextSequence);
return prefix + formatSequence(nextSequence); return prefix + formatSequence(nextSequence);
} }

View File

@@ -53,7 +53,6 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
private final AdminUserService adminUserService; private final AdminUserService adminUserService;
private final AdminUserMapper adminUserMapper; private final AdminUserMapper adminUserMapper;
private final Map<Long, Boolean> deptPresenceCache = new ConcurrentHashMap<>();
private final Map<String, PostDO> postCache = new ConcurrentHashMap<>(); private final Map<String, PostDO> postCache = new ConcurrentHashMap<>();
@Override @Override
@@ -86,9 +85,6 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
} }
Long deptId = externalId.longValue(); Long deptId = externalId.longValue();
ParentHolder parentHolder = resolveSubcompanyParent(sub.getSupsubcomid()); ParentHolder parentHolder = resolveSubcompanyParent(sub.getSupsubcomid());
if (parentHolder.required() && parentHolder.parentId() == null) {
continue;
}
boolean canceled = isCanceledFlag(sub.getCanceled()); boolean canceled = isCanceledFlag(sub.getCanceled());
DeptSaveReqVO saveReq = buildSubcompanySaveReq(sub, deptId, parentHolder.parentId(), canceled); DeptSaveReqVO saveReq = buildSubcompanySaveReq(sub, deptId, parentHolder.parentId(), canceled);
try { try {
@@ -139,7 +135,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
iterator.remove(); iterator.remove();
continue; continue;
} }
Integer externalId = dept.getDepartmentid(); Integer externalId = dept.getId();
if (externalId == null) { if (externalId == null) {
log.warn("[iWork] 部门缺少标识,跳过:{}", dept.getDepartmentname()); log.warn("[iWork] 部门缺少标识,跳过:{}", dept.getDepartmentname());
result.increaseFailed(); result.increaseFailed();
@@ -148,9 +144,6 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
} }
Long deptId = externalId.longValue(); Long deptId = externalId.longValue();
ParentHolder parentHolder = resolveDepartmentParent(dept); ParentHolder parentHolder = resolveDepartmentParent(dept);
if (parentHolder.required() && parentHolder.parentId() == null) {
continue;
}
boolean canceled = isCanceledFlag(dept.getCanceled()); boolean canceled = isCanceledFlag(dept.getCanceled());
DeptSaveReqVO saveReq = buildDepartmentSaveReq(dept, deptId, parentHolder.parentId(), canceled); DeptSaveReqVO saveReq = buildDepartmentSaveReq(dept, deptId, parentHolder.parentId(), canceled);
try { try {
@@ -160,7 +153,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
options); options);
applyDeptOutcome(result, outcome, "部门", dept.getDepartmentname()); applyDeptOutcome(result, outcome, "部门", dept.getDepartmentname());
} catch (Exception ex) { } catch (Exception ex) {
log.error("[iWork] 同步部门失败: id={} name={}", dept.getDepartmentid(), dept.getDepartmentname(), ex); log.error("[iWork] 同步部门失败: id={} name={}", dept.getId(), dept.getDepartmentname(), ex);
result.increaseFailed(); result.increaseFailed();
result.withMessage("同步部门失败: " + ex.getMessage()); result.withMessage("同步部门失败: " + ex.getMessage());
} }
@@ -173,7 +166,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
} }
if (!queue.isEmpty()) { if (!queue.isEmpty()) {
for (IWorkHrDepartmentPageRespVO.Department remaining : queue) { for (IWorkHrDepartmentPageRespVO.Department remaining : queue) {
log.warn("[iWork] 部门因父级缺失未同步: id={} name={}", remaining.getDepartmentid(), remaining.getDepartmentname()); log.warn("[iWork] 部门因父级缺失未同步: id={} name={}", remaining.getId(), remaining.getDepartmentname());
result.increaseFailed(); result.increaseFailed();
} }
} }
@@ -293,7 +286,6 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
} }
desired.setId(deptId); desired.setId(deptId);
Long createdId = deptService.createDept(desired); Long createdId = deptService.createDept(desired);
markDeptExists(createdId);
return new DeptSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(desired.getStatus()), createdId); return new DeptSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(desired.getStatus()), createdId);
} }
if (!Objects.equals(existing.getDeptSource(), DeptSourceEnum.IWORK.getSource())) { if (!Objects.equals(existing.getDeptSource(), DeptSourceEnum.IWORK.getSource())) {
@@ -306,7 +298,6 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
mergeDeptDefaults(desired, existing); mergeDeptDefaults(desired, existing);
boolean disabledChanged = disabled && CommonStatusEnum.isEnable(existing.getStatus()); boolean disabledChanged = disabled && CommonStatusEnum.isEnable(existing.getStatus());
deptService.updateDept(desired); deptService.updateDept(desired);
markDeptExists(existing.getId());
return new DeptSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId()); return new DeptSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
} }
@@ -456,52 +447,21 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
private ParentHolder resolveSubcompanyParent(Integer parentExternalId) { private ParentHolder resolveSubcompanyParent(Integer parentExternalId) {
if (parentExternalId == null || parentExternalId <= 0) { if (parentExternalId == null || parentExternalId <= 0) {
return new ParentHolder(DeptDO.PARENT_ID_ROOT, false); return new ParentHolder(DeptDO.PARENT_ID_ROOT);
} }
Long parentId = parentExternalId.longValue(); return new ParentHolder(parentExternalId.longValue());
if (hasDept(parentId)) {
return new ParentHolder(parentId, true);
}
return new ParentHolder(null, true);
} }
private ParentHolder resolveDepartmentParent(IWorkHrDepartmentPageRespVO.Department dept) { private ParentHolder resolveDepartmentParent(IWorkHrDepartmentPageRespVO.Department dept) {
Integer parentDeptId = dept.getParentdeptid(); Long parentDeptId = toLong(dept.getParentdeptid());
if (parentDeptId != null && parentDeptId > 0) { if (parentDeptId != null) {
Long parentId = parentDeptId.longValue(); return new ParentHolder(parentDeptId);
if (hasDept(parentId)) {
return new ParentHolder(parentId, true);
}
return new ParentHolder(null, true);
} }
Integer subcompanyId = dept.getSubcompanyid1(); Long subcompanyId = toLong(dept.getSubcompanyid1());
if (subcompanyId != null && subcompanyId > 0) { if (subcompanyId != null) {
Long parentId = subcompanyId.longValue(); return new ParentHolder(subcompanyId);
if (hasDept(parentId)) {
return new ParentHolder(parentId, true);
}
return new ParentHolder(null, true);
}
return new ParentHolder(DeptDO.PARENT_ID_ROOT, false);
}
private boolean hasDept(Long deptId) {
if (deptId == null) {
return false;
}
Boolean cached = deptPresenceCache.get(deptId);
if (cached != null) {
return cached;
}
boolean exists = deptService.getDept(deptId) != null;
deptPresenceCache.put(deptId, exists);
return exists;
}
private void markDeptExists(Long deptId) {
if (deptId != null) {
deptPresenceCache.put(deptId, true);
} }
return new ParentHolder(DeptDO.PARENT_ID_ROOT);
} }
private PostDO resolvePostByCode(String code) { private PostDO resolvePostByCode(String code) {
@@ -519,11 +479,11 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
private Long resolveUserDeptId(IWorkHrUserPageRespVO.User user) { private Long resolveUserDeptId(IWorkHrUserPageRespVO.User user) {
Long deptId = toLong(user.getDepartmentid()); Long deptId = toLong(user.getDepartmentid());
if (deptId != null && hasDept(deptId)) { if (deptId != null) {
return deptId; return deptId;
} }
Long subcompanyId = toLong(user.getSubcompanyid1()); Long subcompanyId = toLong(user.getSubcompanyid1());
if (subcompanyId != null && hasDept(subcompanyId)) { if (subcompanyId != null) {
return subcompanyId; return subcompanyId;
} }
return null; return null;
@@ -709,7 +669,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
}; };
} }
private record ParentHolder(Long parentId, boolean required) { private record ParentHolder(Long parentId) {
} }
private enum SyncAction { private enum SyncAction {

View File

@@ -38,18 +38,21 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
respVO.setBatches(batchStats); respVO.setBatches(batchStats);
Set<IWorkSyncEntityTypeEnum> scopes = reqVO.resolveScopes(); Set<IWorkSyncEntityTypeEnum> scopes = reqVO.resolveScopes();
boolean syncUsers = scopes.contains(IWorkSyncEntityTypeEnum.USER);
boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT) || syncUsers;
boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY) || syncDepartments;
int processedPages = 0; int processedPages = 0;
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO); IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
if (scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY)) { if (syncSubcompanies) {
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats); processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats);
} }
if (scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT)) { if (syncDepartments) {
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats); processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats);
} }
if (scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE)) { if (scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE)) {
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats); processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
} }
if (scopes.contains(IWorkSyncEntityTypeEnum.USER)) { if (syncUsers) {
processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats); processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats);
} }
respVO.setProcessedPages(processedPages); respVO.setProcessedPages(processedPages);

View File

@@ -523,8 +523,6 @@ public class AdminUserServiceImpl implements AdminUserService {
validateMobileUnique(id, mobile); validateMobileUnique(id, mobile);
// 校验邮箱唯一 // 校验邮箱唯一
validateEmailUnique(id, email); validateEmailUnique(id, email);
// 校验部门处于开启状态
deptService.validateDeptList(deptIds);
// 校验岗位处于开启状态 // 校验岗位处于开启状态
postService.validatePostList(postIds); postService.validatePostList(postIds);
return user; return user;