diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java index 4a4befdb..60951994 100644 --- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java +++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java @@ -268,10 +268,10 @@ public class DeptDataPermissionRule implements DataPermissionRule { } // 拼接条件 - if (StrUtil.isBlank(workCode)) { - return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId)); + if (StrUtil.isNotBlank(workCode) && "system_users".equals(tableName)) { + return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, "workcode"), new StringValue(workCode)); } else { - return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new StringValue(workCode)); + return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId)); } } diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/job/sync/SyncIWorkOrgChangeJob.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/job/sync/SyncIWorkOrgChangeJob.java new file mode 100644 index 00000000..0e2c23e2 --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/job/sync/SyncIWorkOrgChangeJob.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.system.job.sync; + +import com.xxl.job.core.handler.annotation.XxlJob; +import com.zt.plat.framework.tenant.core.job.TenantJob; +import com.zt.plat.module.system.service.sync.SyncIWorkOrgChangeService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 用于定时同步 iWork 当日变更的组织数据 + * 同步时间:每日23:00 + */ +@Component +@Slf4j +public class SyncIWorkOrgChangeJob { + + @Resource + private SyncIWorkOrgChangeService syncIWorkOrgChangeService; + + /** + * 执行定时任务 + * 配置执行频率:每日23:00时执行一次 + * cron表达式:0 0 23 * * ? + */ + @XxlJob("syncIWorkOrgChangeJob") + @TenantJob + public void execute() { + log.info("[syncIWorkOrgChangeJob][开始执行同步 iWork 当日变更组织任务]"); + try { + int processedCount = syncIWorkOrgChangeService.process(); + if (processedCount > 0) { + log.info("[syncIWorkOrgChangeJob][同步任务执行完成,处理了 {} 条记录]", processedCount); + } + } catch (Exception e) { + log.error("[syncIWorkOrgChangeJob][同步任务执行失败]", e); + throw e; + } + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java index 50c1bd92..a32be23a 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkSyncProcessorImpl.java @@ -13,8 +13,11 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUs import com.zt.plat.module.system.controller.admin.user.vo.user.UserSaveReqVO; import com.zt.plat.module.system.dal.dataobject.dept.DeptDO; import com.zt.plat.module.system.dal.dataobject.dept.PostDO; +import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO; import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO; +import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO; import com.zt.plat.module.system.dal.mysql.dept.PostMapper; +import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper; import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper; import com.zt.plat.module.system.enums.common.SexEnum; import com.zt.plat.module.system.enums.dept.DeptSourceEnum; @@ -24,6 +27,7 @@ import com.zt.plat.module.system.service.dept.DeptService; import com.zt.plat.module.system.service.dept.PostService; import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor; import com.zt.plat.module.system.service.user.AdminUserService; +import com.zt.plat.module.system.service.userdept.UserDeptService; import com.zt.plat.module.system.util.sync.SyncVerifyUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -33,6 +37,7 @@ import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Slf4j @Service @@ -47,8 +52,10 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { private final DeptService deptService; private final PostService postService; private final PostMapper postMapper; + private final UserPostMapper userPostMapper; private final AdminUserService adminUserService; private final AdminUserMapper adminUserMapper; + private final UserDeptService userDeptService; private final Map postCache = new ConcurrentHashMap<>(); @@ -531,11 +538,16 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { String externalPassword) { UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status); req.setId(existing.getId()); + Long iworkDeptId = resolveIWorkDeptId(deptId); + req.setDeptIds(null); + req.setPostIds(null); boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus()); boolean infoChanged = isUserInfoChanged(existing, req); boolean passwordChanged = isPasswordChanged(existing, externalPassword); + boolean deptChanged = syncIWorkUserDeptRelations(existing.getId(), iworkDeptId); + boolean postChanged = syncIWorkUserPostRelations(existing.getId(), postId); - if (!infoChanged && !passwordChanged) { + if (!infoChanged && !passwordChanged && !deptChanged && !postChanged) { return new UserSyncOutcome(SyncAction.SKIPPED, false, existing.getId()); } @@ -619,8 +631,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { req.setWorkcode(resolveWorkcode(source)); req.setNickname(limitLength(StrUtil.blankToDefault(source.getLastname(), username), 30)); req.setRemark(buildUserRemark(source)); - if (deptId != null) { - req.setDeptIds(singletonSet(deptId)); + Long iworkDeptId = resolveIWorkDeptId(deptId); + if (iworkDeptId != null) { + req.setDeptIds(singletonSet(iworkDeptId)); } if (postId != null) { req.setPostIds(singletonSet(postId)); @@ -635,6 +648,74 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor { return req; } + private boolean syncIWorkUserDeptRelations(Long userId, Long iworkDeptId) { + if (userId == null) { + return false; + } + List relations = userDeptService.getValidUserDeptListByUserIds(Collections.singleton(userId)); + Set existingDeptIds = relations.stream() + .map(UserDeptDO::getDeptId) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + Set existingIworkDeptIds = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(existingDeptIds)) { + Map deptMap = deptService.getDeptMap(existingDeptIds); + existingIworkDeptIds = deptMap.values().stream() + .filter(this::isIWorkDept) + .map(DeptDO::getId) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + Set desiredIworkDeptIds = iworkDeptId == null + ? Collections.emptySet() + : Collections.singleton(iworkDeptId); + if (existingIworkDeptIds.equals(desiredIworkDeptIds)) { + return false; + } + Collection toDelete = CollUtil.subtract(existingIworkDeptIds, desiredIworkDeptIds); + Collection toAdd = CollUtil.subtract(desiredIworkDeptIds, existingIworkDeptIds); + if (CollUtil.isNotEmpty(toDelete)) { + userDeptService.deleteUserDeptByUserIdAndDeptIds(userId, toDelete); + } + if (CollUtil.isNotEmpty(toAdd)) { + userDeptService.batchCreateUserDept(Collections.singletonList( + new UserDeptDO().setUserId(userId).setDeptId(iworkDeptId))); + } + return true; + } + + private boolean syncIWorkUserPostRelations(Long userId, Long postId) { + if (userId == null || postId == null) { + return false; + } + List relations = userPostMapper.selectListByUserId(userId); + Set existingPostIds = relations.stream() + .map(UserPostDO::getPostId) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (existingPostIds.contains(postId)) { + return false; + } + userPostMapper.insertBatch(Collections.singletonList( + new UserPostDO().setUserId(userId).setPostId(postId))); + return true; + } + + private Long resolveIWorkDeptId(Long deptId) { + if (deptId == null) { + return null; + } + DeptDO dept = deptService.getDept(deptId); + return isIWorkDept(dept) ? deptId : null; + } + + private boolean isIWorkDept(DeptDO dept) { + if (dept == null) { + return false; + } + return Objects.equals(dept.getDeptSource(), DeptSourceEnum.IWORK.getSource()); + } + private void mergeDeptDefaults(DeptSaveReqVO target, DeptDO existing) { target.setCode(StrUtil.blankToDefault(target.getCode(), existing.getCode())); target.setShortName(StrUtil.blankToDefault(target.getShortName(), existing.getShortName())); diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeService.java new file mode 100644 index 00000000..532a3b1d --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeService.java @@ -0,0 +1,13 @@ +package com.zt.plat.module.system.service.sync; + +/** + * 定时同步 iWork 组织变更服务 + */ +public interface SyncIWorkOrgChangeService { + + /** + * 执行同步 + * @return 拉取记录数量 + */ + int process(); +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeServiceImpl.java new file mode 100644 index 00000000..3b81c13f --- /dev/null +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/sync/SyncIWorkOrgChangeServiceImpl.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.system.service.sync; + +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO; +import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO; +import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +@Service +public class SyncIWorkOrgChangeServiceImpl implements SyncIWorkOrgChangeService { + + @Resource + private IWorkSyncService iWorkSyncService; + + @Override + public int process() { + IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO(); + reqVO.setPageSize(10); + ZoneId zone = ZoneId.of("Asia/Shanghai"); + String startOfToday = LocalDate.now(zone) + .atStartOfDay(zone) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + reqVO.setModified(startOfToday); + IWorkFullSyncRespVO subcompanyResp = iWorkSyncService.fullSyncSubcompanies(reqVO); + IWorkFullSyncRespVO departmentResp = iWorkSyncService.fullSyncDepartments(reqVO); + return countPulled(subcompanyResp) + countPulled(departmentResp); + } + + private int countPulled(IWorkFullSyncRespVO respVO) { + if (respVO == null || respVO.getBatches() == null) { + return 0; + } + return respVO.getBatches().stream() + .mapToInt(batch -> batch.getPulled() == null ? 0 : batch.getPulled()) + .sum(); + } +} diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptService.java index 26c728b7..7a4e027c 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptService.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptService.java @@ -70,6 +70,13 @@ public interface UserDeptService { */ void deleteUserDeptByUserId(Long userId); + /** + * 根据用户ID与部门ID集合删除用户与部门关系 + * @param userId 用户ID + * @param deptIds 部门ID集合 + */ + void deleteUserDeptByUserIdAndDeptIds(Long userId, Collection deptIds); + /** * 批量创建用户与部门关系 * @param createReqVOList 创建信息列表 diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptServiceImpl.java index 8c71a020..bbc78645 100644 --- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptServiceImpl.java +++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/userdept/UserDeptServiceImpl.java @@ -3,6 +3,7 @@ package com.zt.plat.module.system.service.userdept; import cn.hutool.core.collection.CollUtil; import com.zt.plat.framework.common.util.object.BeanUtils; import com.zt.plat.framework.security.core.LoginUser; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO; import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper; import jakarta.annotation.Resource; @@ -128,10 +129,20 @@ public class UserDeptServiceImpl implements UserDeptService { @Override public void deleteUserDeptByUserId(Long userId) { if (userId == null) return; - userDeptMapper.delete(new com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX() + userDeptMapper.delete(new LambdaQueryWrapperX() .eq(UserDeptDO::getUserId, userId)); } + @Override + public void deleteUserDeptByUserIdAndDeptIds(Long userId, Collection deptIds) { + if (userId == null || CollUtil.isEmpty(deptIds)) { + return; + } + userDeptMapper.delete(new LambdaQueryWrapperX() + .eq(UserDeptDO::getUserId, userId) + .in(UserDeptDO::getDeptId, deptIds)); + } + @Override @Transactional(rollbackFor = Exception.class) public void batchCreateUserDept(List createReqVOList) {