Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
qianshijiang
2025-11-26 10:44:28 +08:00
8 changed files with 77 additions and 54 deletions

View File

@@ -14,6 +14,9 @@ public class AdminUserRespDTO implements VO {
@Schema(description = "用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "zhangsan")
private String username;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小王")
private String nickname;

View File

@@ -114,17 +114,23 @@ public class IWorkIntegrationController {
// ----------------- 同步到本地 -----------------
@PostMapping("/hr/full-sync")
@Operation(summary = "手动触发 iWork 组织/人员全量同步")
@Operation(summary = "手动触发 iWork 组织/人员同步")
public CommonResult<IWorkFullSyncRespVO> fullSync(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSync(reqVO));
}
@PostMapping("/hr/departments/full-sync")
@Operation(summary = "手动触发 iWork 部门全量同步")
@Operation(summary = "手动触发 iWork 部门同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncDepartments(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSyncDepartments(reqVO));
}
@PostMapping("/hr/subcompanies/full-sync")
@Operation(summary = "手动触发 iWork 分部同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncSubcompanies(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSyncSubcompanies(reqVO));
}
@PostMapping("/hr/job-titles/full-sync")
@Operation(summary = "手动触发 iWork 岗位全量同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncJobTitles(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {

View File

@@ -159,13 +159,9 @@ public class IWorkHrDepartmentPageRespVO {
private String supsubcomname;
@Schema(description = "父部门 ID")
@JsonProperty("parentdeptid")
@JsonProperty("supdepid")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer parentdeptid;
@Schema(description = "父部门名称")
@JsonProperty("parentdeptname")
private String parentdeptname;
private Integer supdepid;
@Schema(description = "层级路径")
@JsonProperty("alllevel")

View File

@@ -5,11 +5,10 @@ import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
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 lombok.Data;
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -50,9 +49,10 @@ public class IWorkHrSubcompanyPageRespVO {
@Schema(description = "分部信息")
public static class Subcompany {
@Schema(description = "分部唯一 ID")
@JsonProperty("subcompanyid1")
private Integer subcompanyid1;
@Schema(description = "部门 IDiWork 主键)")
@JsonProperty("id")
@JsonDeserialize(using = LenientIntegerDeserializer.class)
private Integer id;
@Schema(description = "分部编码")
@JsonProperty("subcompanycode")

View File

@@ -74,16 +74,23 @@ public class DeptServiceImpl implements DeptService {
// 校验部门名的唯一性
validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
// 生成并校验部门编码
Long effectiveParentId = normalizeParentId(createReqVO.getParentId());
boolean isTopLevel = Objects.equals(effectiveParentId, DeptDO.PARENT_ID_ROOT);
String resolvedCode;
if (isTopLevel) {
resolvedCode = resolveTopLevelCode(null, createReqVO.getCode());
boolean isIWorkSource = Objects.equals(createReqVO.getDeptSource(), DeptSourceEnum.IWORK.getSource());
if (isIWorkSource) {
// iWork 来源直接使用提供的编码,不再生成
String providedCode = StrUtil.blankToDefault(createReqVO.getCode(), null);
createReqVO.setCode(providedCode);
} else {
resolvedCode = generateDeptCode(effectiveParentId);
validateDeptCodeUnique(null, resolvedCode);
Long effectiveParentId = normalizeParentId(createReqVO.getParentId());
boolean isTopLevel = Objects.equals(effectiveParentId, DeptDO.PARENT_ID_ROOT);
String resolvedCode;
if (isTopLevel) {
resolvedCode = resolveTopLevelCode(null, createReqVO.getCode());
} else {
resolvedCode = generateDeptCode(effectiveParentId);
validateDeptCodeUnique(null, resolvedCode);
}
createReqVO.setCode(resolvedCode);
}
createReqVO.setCode(resolvedCode);
// 插入部门
DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
@@ -110,28 +117,35 @@ public class DeptServiceImpl implements DeptService {
// 校验部门名的唯一性
validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
// 如果上级发生变化,需要重新生成编码并同步子级
boolean isIWorkSource = Objects.equals(originalDept.getDeptSource(), DeptSourceEnum.IWORK.getSource());
Long newParentId = normalizeParentId(updateReqVO.getParentId());
Long oldParentId = normalizeParentId(originalDept.getParentId());
boolean parentChanged = !Objects.equals(newParentId, oldParentId);
if (parentChanged) {
String newCode;
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
newCode = resolveTopLevelCode(updateReqVO.getId(), updateReqVO.getCode());
} else {
newCode = generateDeptCode(updateReqVO.getParentId());
validateDeptCodeUnique(updateReqVO.getId(), newCode);
}
updateReqVO.setCode(newCode);
if (isIWorkSource) {
// iWork 来源直接使用提供的编码,不再生成
String providedCode = StrUtil.blankToDefault(updateReqVO.getCode(), null);
updateReqVO.setCode(providedCode);
} else {
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
String requestedCode = updateReqVO.getCode();
if (StrUtil.isNotBlank(requestedCode) && !StrUtil.equals(requestedCode.trim(), originalDept.getCode())) {
updateReqVO.setCode(resolveTopLevelCode(updateReqVO.getId(), requestedCode));
if (parentChanged) {
String newCode;
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
newCode = resolveTopLevelCode(updateReqVO.getId(), updateReqVO.getCode());
} else {
newCode = generateDeptCode(updateReqVO.getParentId());
validateDeptCodeUnique(updateReqVO.getId(), newCode);
}
updateReqVO.setCode(newCode);
} else {
if (Objects.equals(newParentId, DeptDO.PARENT_ID_ROOT)) {
String requestedCode = updateReqVO.getCode();
if (StrUtil.isNotBlank(requestedCode) && !StrUtil.equals(requestedCode.trim(), originalDept.getCode())) {
updateReqVO.setCode(resolveTopLevelCode(updateReqVO.getId(), requestedCode));
} else {
updateReqVO.setCode(originalDept.getCode());
}
} else {
updateReqVO.setCode(originalDept.getCode());
}
} else {
updateReqVO.setCode(originalDept.getCode());
}
}

View File

@@ -16,10 +16,15 @@ public interface IWorkSyncService {
IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO);
/**
* 仅同步部门(会自动包含依赖的分部)
* 仅同步部门
*/
IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO);
/**
* 仅同步分部
*/
IWorkFullSyncRespVO fullSyncSubcompanies(IWorkFullSyncReqVO reqVO);
/**
* 仅同步岗位
*/

View File

@@ -28,14 +28,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@@ -72,12 +65,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
while (iterator.hasNext()) {
IWorkHrSubcompanyPageRespVO.Subcompany sub = iterator.next();
if (shouldSkipByCanceled(sub.getCanceled(), options)) {
logSkip("分部", sub.getSubcompanyid1(), "iWork 标记为失效且当前不同步失效记录");
logSkip("分部", sub.getId(), "iWork 标记为失效且当前不同步失效记录");
result.increaseSkipped();
iterator.remove();
continue;
}
Integer externalId = sub.getSubcompanyid1();
Integer externalId = sub.getId();
if (externalId == null) {
log.warn("[iWork] 分部缺少标识,跳过:{}", sub.getSubcompanyname());
result.increaseFailed();
@@ -95,7 +88,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
options);
applyDeptOutcome(result, outcome, "分部", sub.getSubcompanyname());
} catch (Exception ex) {
log.error("[iWork] 同步分部失败: id={} name={}", sub.getSubcompanyid1(), sub.getSubcompanyname(), ex);
log.error("[iWork] 同步分部失败: id={} name={}", sub.getId(), sub.getSubcompanyname(), ex);
result.increaseFailed();
result.withMessage("同步分部失败: " + ex.getMessage());
}
@@ -108,7 +101,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
}
if (!queue.isEmpty()) {
for (IWorkHrSubcompanyPageRespVO.Subcompany remaining : queue) {
log.warn("[iWork] 分部因父级缺失未同步: id={} name={}", remaining.getSubcompanyid1(), remaining.getSubcompanyname());
log.warn("[iWork] 分部因父级缺失未同步: id={} name={}", remaining.getId(), remaining.getSubcompanyname());
result.increaseFailed();
}
}
@@ -397,7 +390,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
DeptSaveReqVO req = new DeptSaveReqVO();
req.setId(deptId);
req.setName(limitLength(StrUtil.blankToDefault(data.getSubcompanyname(), "未命名分部"), 30));
req.setShortName(limitLength(data.getSubcompanyname(), 20));
// req.setShortName(limitLength(data.getSubcompanyname(), 20));
req.setCode(trimToNull(data.getSubcompanycode()));
req.setParentId(parentId == null ? DeptDO.PARENT_ID_ROOT : parentId);
req.setSort(defaultSort(data.getShoworder()));
@@ -415,7 +408,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
DeptSaveReqVO req = new DeptSaveReqVO();
req.setId(deptId);
req.setName(limitLength(StrUtil.blankToDefault(data.getDepartmentname(), "未命名部门"), 30));
req.setShortName(limitLength(StrUtil.blankToDefault(data.getDepartmentmark(), data.getDepartmentname()), 20));
// req.setShortName(limitLength(StrUtil.blankToDefault(data.getDepartmentmark(), data.getDepartmentname()), 20));
req.setCode(trimToNull(data.getDepartmentcode()));
req.setParentId(parentId == null ? DeptDO.PARENT_ID_ROOT : parentId);
req.setSort(defaultSort(data.getShoworder()));
@@ -472,8 +465,8 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
}
private ParentHolder resolveDepartmentParent(IWorkHrDepartmentPageRespVO.Department dept) {
Long parentDeptId = toLong(dept.getParentdeptid());
if (parentDeptId != null) {
Long parentDeptId = toLong(dept.getSupdepid());
if (parentDeptId != null && parentDeptId > 0) {
return new ParentHolder(parentDeptId);
}
Long subcompanyId = toLong(dept.getSubcompanyid1());

View File

@@ -37,6 +37,11 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.DEPARTMENT));
}
@Override
public IWorkFullSyncRespVO fullSyncSubcompanies(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.SUBCOMPANY));
}
@Override
public IWorkFullSyncRespVO fullSyncJobTitles(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.JOB_TITLE));
@@ -56,6 +61,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
boolean syncUsers = scopes.contains(IWorkSyncEntityTypeEnum.USER);
boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT);
boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY);
boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE);
int processedPages = 0;
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
if (syncSubcompanies) {
@@ -64,7 +70,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
if (syncDepartments) {
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats);
}
if (scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE)) {
if (syncJobTitle) {
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
}
if (syncUsers) {