Merge branch 'dev' into test

This commit is contained in:
chenbowen
2025-11-26 01:06:13 +08:00
6 changed files with 90 additions and 25 deletions

View File

@@ -119,6 +119,24 @@ public class IWorkIntegrationController {
return success(syncService.fullSync(reqVO));
}
@PostMapping("/hr/departments/full-sync")
@Operation(summary = "手动触发 iWork 部门全量同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncDepartments(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSyncDepartments(reqVO));
}
@PostMapping("/hr/job-titles/full-sync")
@Operation(summary = "手动触发 iWork 岗位全量同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncJobTitles(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSyncJobTitles(reqVO));
}
@PostMapping("/hr/users/full-sync")
@Operation(summary = "手动触发 iWork 人员全量同步")
public CommonResult<IWorkFullSyncRespVO> fullSyncUsers(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
return success(syncService.fullSyncUsers(reqVO));
}
@PostMapping("/hr/single-sync")
@Operation(summary = "按 iWork ID 同步单条组织/人员")
public CommonResult<IWorkSingleSyncRespVO> singleSync(@Valid @RequestBody IWorkSingleSyncReqVO reqVO) {

View File

@@ -84,6 +84,10 @@ public class UserSaveReqVO {
@Schema(description = "用户来源类型", example = "1")
private Integer userSource;
@Schema(hidden = true)
@JsonIgnore
private boolean skipAssociationValidation;
// ========== 仅【创建】时,需要传递的字段 ==========
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")

View File

@@ -15,6 +15,21 @@ public interface IWorkSyncService {
*/
IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO);
/**
* 仅同步部门(会自动包含依赖的分部)
*/
IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO);
/**
* 仅同步岗位
*/
IWorkFullSyncRespVO fullSyncJobTitles(IWorkFullSyncReqVO reqVO);
/**
* 仅同步人员(会自动包含依赖的分部、部门)
*/
IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO);
/**
* 根据 iWork ID 进行单条同步
*/

View File

@@ -229,17 +229,16 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
}
String username = resolveUsername(user);
if (StrUtil.isBlank(username)) {
log.warn("[iWork] 人员缺少可用账号跳过id={} name={}", user.getId(), user.getLastname());
log.warn("[iWork] 人员缺少可用账号(工号={}, 登录账号={})跳过id={} name={}",
user.getWorkcode(), user.getLoginid(), user.getId(), user.getLastname());
result.increaseFailed();
continue;
}
try {
Long deptId = resolveUserDeptId(user);
if (deptId == null) {
log.warn("[iWork] 人员未找到匹配部门,跳过id={} name={} deptId={} subcompany={}",
log.warn("[iWork] 人员未找到匹配部门,继续同步id={} name={} deptId={} subcompany={}",
user.getId(), user.getLastname(), user.getDepartmentid(), user.getSubcompanyid1());
result.increaseFailed();
continue;
}
Long postId = resolveUserPostId(user);
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
@@ -313,21 +312,26 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
return new JobSyncOutcome(SyncAction.SKIPPED, false, null);
}
PostSaveReqVO createReq = new PostSaveReqVO();
Long desiredId = job.getJobtitleid() == null ? null : job.getJobtitleid().longValue();
if (desiredId != null) {
createReq.setId(desiredId);
}
createReq.setCode(code);
createReq.setName(name);
createReq.setSort(defaultSort(job.getShoworder()));
createReq.setStatus(status);
createReq.setRemark(StrUtil.blankToDefault(job.getJobfunction(), job.getDescription()));
Long postId = postService.createPost(createReq);
Long effectivePostId = desiredId != null ? desiredId : postId;
PostDO created = new PostDO();
created.setId(postId);
created.setId(effectivePostId);
created.setCode(code);
created.setName(createReq.getName());
created.setSort(createReq.getSort());
created.setStatus(status);
created.setRemark(createReq.getRemark());
postCache.put(buildPostCacheKey(code), created);
return new JobSyncOutcome(SyncAction.CREATED, disabled, postId);
return new JobSyncOutcome(SyncAction.CREATED, disabled, effectivePostId);
}
if (!options.isAllowUpdate()) {
return new JobSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
@@ -355,10 +359,15 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
Long postId,
CommonStatusEnum status) {
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
Long desiredUserId = source.getId() == null ? null : source.getId().longValue();
if (desiredUserId != null) {
req.setId(desiredUserId);
}
req.setPassword(DEFAULT_USER_PASSWORD);
req.setUserSource(UserSourceEnum.IWORK.getSource());
Long userId = adminUserService.createUser(req);
return new UserSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(req.getStatus()), userId);
Long effectiveUserId = desiredUserId != null ? desiredUserId : userId;
return new UserSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(req.getStatus()), effectiveUserId);
}
private UserSyncOutcome updateUser(AdminUserDO existing,
@@ -419,7 +428,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
req.setUsername(username);
req.setNickname(limitLength(StrUtil.blankToDefault(source.getLastname(), username), 30));
req.setRemark(buildUserRemark(source));
req.setDeptIds(singletonSet(deptId));
if (deptId != null) {
req.setDeptIds(singletonSet(deptId));
}
if (postId != null) {
req.setPostIds(singletonSet(postId));
}
@@ -427,6 +438,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
req.setMobile(trimToNull(source.getMobile()));
req.setSex(resolveSex(source.getSex()));
req.setStatus(status.getStatus());
req.setSkipAssociationValidation(true);
return req;
}
@@ -590,12 +602,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
}
private String resolveUsername(IWorkHrUserPageRespVO.User user) {
String candidate = sanitizeUsername(user.getLoginid());
String candidate = sanitizeUsername(user.getWorkcode());
if (candidate == null) {
candidate = sanitizeUsername(user.getWorkcode());
}
if (candidate == null && user.getId() != null) {
candidate = sanitizeUsername("IWORK" + user.getId());
candidate = sanitizeUsername(user.getLoginid());
}
return candidate;
}

View File

@@ -12,10 +12,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.List;
import java.util.Set;
import java.util.*;
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_ORG_REMOTE_FAILED;
@@ -32,15 +29,33 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
@Override
public IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, reqVO.resolveScopes());
}
@Override
public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.DEPARTMENT));
}
@Override
public IWorkFullSyncRespVO fullSyncJobTitles(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.JOB_TITLE));
}
@Override
public IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO) {
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER));
}
private IWorkFullSyncRespVO runFullSync(IWorkFullSyncReqVO reqVO, Set<IWorkSyncEntityTypeEnum> scopes) {
IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO();
respVO.setPageSize(reqVO.getPageSize());
List<IWorkSyncBatchStatVO> batchStats = new ArrayList<>();
respVO.setBatches(batchStats);
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;
boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT);
boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY);
int processedPages = 0;
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
if (syncSubcompanies) {

View File

@@ -108,7 +108,8 @@ public class AdminUserServiceImpl implements AdminUserService {
});
// 1.2 校验正确性
validateUserForCreateOrUpdate(null, createReqVO.getUsername(),
createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptIds(), createReqVO.getPostIds());
createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptIds(), createReqVO.getPostIds(),
createReqVO.isSkipAssociationValidation());
// 2.1 插入用户
AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
user.setStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -154,7 +155,7 @@ public class AdminUserServiceImpl implements AdminUserService {
}
});
// 1.3 校验正确性
validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null);
validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null, false);
// 2. 插入用户
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
@@ -173,7 +174,8 @@ public class AdminUserServiceImpl implements AdminUserService {
updateReqVO.setPassword(null); // 特殊:此处不更新密码
// 1. 校验正确性
AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptIds(), updateReqVO.getPostIds());
updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptIds(),
updateReqVO.getPostIds(), updateReqVO.isSkipAssociationValidation());
// 2.1 只更新非空字段
AdminUserDO updateObj = new AdminUserDO();
@@ -512,7 +514,7 @@ public class AdminUserServiceImpl implements AdminUserService {
}
private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
Set<Long> deptIds, Set<Long> postIds) {
Set<Long> deptIds, Set<Long> postIds, boolean skipAssociationValidation) {
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
return DataPermissionUtils.executeIgnore(() -> {
// 校验用户存在
@@ -524,7 +526,9 @@ public class AdminUserServiceImpl implements AdminUserService {
// 校验邮箱唯一
validateEmailUnique(id, email);
// 校验岗位处于开启状态
postService.validatePostList(postIds);
if (!skipAssociationValidation) {
postService.validatePostList(postIds);
}
return user;
});
}