Merge branch 'dev' into test
This commit is contained in:
@@ -3560,7 +3560,7 @@ COMMENT ON TABLE system_users IS '用户信息表';
|
|||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- @formatter:off
|
-- @formatter:off
|
||||||
-- SET IDENTITY_INSERT system_users ON;
|
-- SET IDENTITY_INSERT system_users ON;
|
||||||
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '后台管理', NULL, '管理员', '[1,2]', '11aoteman@126.com', '18818260277', 2, 1, 'http://test.zt.iocoder.cn/test/20250502/avatar_1746154660449.png', 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);
|
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (1, 'admin', '$2a$04$KljJDa/LK7QfDm0lF5OhuePhlPfjRH3tB2Wu351Uidz.oQGJXevPi', '后台管理', NULL, '管理员', '[1,2]', '11aoteman@126.com', '18818260277', 2, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-05-10 18:03:15', 'admin', '2021-01-05 17:03:47', NULL, '2025-05-10 18:03:15', '0', 1);
|
||||||
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, 'zt', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', 'ZT', NULL, '不要吓我', '[1]', 'zt@iocoder.cn', '15601691300', 1, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);
|
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (100, 'zt', '$2a$04$h.aaPKgO.odHepnk5PCsWeEwKdojFWdTItxGKfx1r0e1CSeBzsTJ6', 'ZT', NULL, '不要吓我', '[1]', 'zt@iocoder.cn', '15601691300', 1, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-04-08 09:36:40', '', '2021-01-07 09:07:17', NULL, '2025-04-21 14:23:08', '0', 1);
|
||||||
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, NULL, NULL, 'yuanma@iocoder.cn', '15601701300', 0, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);
|
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, NULL, NULL, 'yuanma@iocoder.cn', '15601701300', 0, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2025-04-21 14:23:08', '0', 1);
|
||||||
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, NULL, '[1,2]', '111@qq.com', '15601691200', 1, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);
|
INSERT INTO system_users (id, username, password, nickname, workcode, remark, post_ids, email, mobile, sex, user_source, avatar, status, login_ip, login_date, creator, create_time, updater, update_time, deleted, tenant_id) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, NULL, '[1,2]', '111@qq.com', '15601691200', 1, 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-03-28 20:01:16', '', '2021-01-21 02:13:53', NULL, '2025-04-21 14:23:08', '0', 1);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
|
|||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -18,14 +20,12 @@ import java.util.Set;
|
|||||||
public class BusinessDataPermissionConfiguration {
|
public class BusinessDataPermissionConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BusinessDataPermissionEntityScanner businessDataPermissionEntityScanner(BeanFactory beanFactory, ApplicationContext applicationContext) {
|
public BusinessDataPermissionEntityScanner businessDataPermissionEntityScanner(BeanFactory beanFactory, ApplicationContext applicationContext, Environment environment) {
|
||||||
Set<String> basePackages = new LinkedHashSet<>();
|
Set<String> basePackages = new LinkedHashSet<>();
|
||||||
|
addConfiguredBasePackages(environment, basePackages);
|
||||||
if (AutoConfigurationPackages.has(beanFactory)) {
|
if (AutoConfigurationPackages.has(beanFactory)) {
|
||||||
basePackages.addAll(AutoConfigurationPackages.get(beanFactory));
|
basePackages.addAll(AutoConfigurationPackages.get(beanFactory));
|
||||||
}
|
}
|
||||||
if (basePackages.isEmpty()) {
|
|
||||||
basePackages.add("com.zt");
|
|
||||||
}
|
|
||||||
ClassLoader classLoader = applicationContext != null
|
ClassLoader classLoader = applicationContext != null
|
||||||
? applicationContext.getClassLoader()
|
? applicationContext.getClassLoader()
|
||||||
: Thread.currentThread().getContextClassLoader();
|
: Thread.currentThread().getContextClassLoader();
|
||||||
@@ -35,6 +35,21 @@ public class BusinessDataPermissionConfiguration {
|
|||||||
return new BusinessDataPermissionEntityScanner(basePackages, classLoader);
|
return new BusinessDataPermissionEntityScanner(basePackages, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addConfiguredBasePackages(Environment environment, Set<String> basePackages) {
|
||||||
|
if (environment == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String configured = environment.getProperty("zt.info.base-package");
|
||||||
|
if (!StringUtils.hasText(configured)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String pkg : configured.split("[,;\\s]+")) {
|
||||||
|
if (StringUtils.hasText(pkg)) {
|
||||||
|
basePackages.add(pkg.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CompanyDataPermissionRuleCustomizer autoCompanyDataPermissionRuleCustomizer(BusinessDataPermissionEntityScanner scanner) {
|
public CompanyDataPermissionRuleCustomizer autoCompanyDataPermissionRuleCustomizer(BusinessDataPermissionEntityScanner scanner) {
|
||||||
return rule -> scanner.getEntityMetadata().forEach(metadata -> {
|
return rule -> scanner.getEntityMetadata().forEach(metadata -> {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
|
|||||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
import com.zt.plat.module.system.api.permission.dto.*;
|
import com.zt.plat.module.system.api.permission.dto.*;
|
||||||
import com.zt.plat.module.system.enums.ApiConstants;
|
import com.zt.plat.module.system.enums.ApiConstants;
|
||||||
|
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -50,4 +51,9 @@ public interface PermissionApi extends PermissionCommonApi {
|
|||||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||||
CommonResult<Set<Long>> getUserRoleIdListByUserId(@RequestParam("userId") Long userId);
|
CommonResult<Set<Long>> getUserRoleIdListByUserId(@RequestParam("userId") Long userId);
|
||||||
|
|
||||||
|
@GetMapping(PREFIX + "/user-data-permission-level")
|
||||||
|
@Operation(summary = "获得用户的数据权限级别")
|
||||||
|
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||||
|
CommonResult<DataScopeEnum> getUserDataPermissionLevel(@RequestParam("userId") Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package com.zt.plat.module.system.enums.permission;
|
package com.zt.plat.module.system.enums.permission;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据范围枚举类
|
* 数据范围枚举类
|
||||||
@@ -33,6 +35,26 @@ public enum DataScopeEnum implements ArrayValuable<Integer> {
|
|||||||
|
|
||||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jackson 序列化时输出整数 code,兼容旧客户端
|
||||||
|
*/
|
||||||
|
@JsonValue
|
||||||
|
public Integer getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataScopeEnum findByScope(Integer scope) {
|
||||||
|
if (scope == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (DataScopeEnum value : values()) {
|
||||||
|
if (Objects.equals(value.scope, scope)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer[] array() {
|
public Integer[] array() {
|
||||||
return ARRAYS;
|
return ARRAYS;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.zt.plat.framework.common.util.object.BeanUtils;
|
|||||||
import com.zt.plat.module.system.api.permission.dto.*;
|
import com.zt.plat.module.system.api.permission.dto.*;
|
||||||
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO;
|
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
|
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
|
||||||
|
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||||
import com.zt.plat.module.system.service.permission.PermissionService;
|
import com.zt.plat.module.system.service.permission.PermissionService;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@@ -65,6 +66,11 @@ public class PermissionApiImpl implements PermissionApi {
|
|||||||
return success(permissionService.getUserRoleIdListByUserIdFromCache(userId));
|
return success(permissionService.getUserRoleIdListByUserIdFromCache(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<DataScopeEnum> getUserDataPermissionLevel(Long userId) {
|
||||||
|
return success(permissionService.getUserDataPermissionLevel(userId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult<Boolean> hasAnyPermissions(Long userId, String... permissions) {
|
public CommonResult<Boolean> hasAnyPermissions(Long userId, String... permissions) {
|
||||||
return success(permissionService.hasAnyPermissions(userId, permissions));
|
return success(permissionService.hasAnyPermissions(userId, permissions));
|
||||||
|
|||||||
@@ -174,4 +174,11 @@ public interface DeptService {
|
|||||||
* @return 部门列表
|
* @return 部门列表
|
||||||
*/
|
*/
|
||||||
List<DeptDO> searchDeptTree(String keyword);
|
List<DeptDO> searchDeptTree(String keyword);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回填缺失的部门编码(不触发事件)。
|
||||||
|
*
|
||||||
|
* @param deptIds 需要回填的部门 ID 列表
|
||||||
|
*/
|
||||||
|
void backfillMissingCodesWithoutEvent(Collection<Long> deptIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -900,4 +900,27 @@ public class DeptServiceImpl implements DeptService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@DataPermission(enable = false)
|
||||||
|
public void backfillMissingCodesWithoutEvent(Collection<Long> deptIds) {
|
||||||
|
if (CollUtil.isEmpty(deptIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<DeptDO> targets = deptMapper.selectBatchIds(deptIds);
|
||||||
|
for (DeptDO dept : targets) {
|
||||||
|
if (dept == null || StrUtil.isNotBlank(dept.getCode())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Integer source = ObjectUtil.defaultIfNull(dept.getDeptSource(), DeptSourceEnum.EXTERNAL.getSource());
|
||||||
|
try {
|
||||||
|
String code = generateDeptCode(dept.getParentId(), source);
|
||||||
|
validateDeptCodeUnique(dept.getId(), code);
|
||||||
|
updateDeptCode(dept.getId(), code);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.warn("[iWork] 回填部门编码失败 id={} name={} msg={}", dept.getId(), dept.getName(), ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,42 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJo
|
|||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for applying iWork entities into local persistence.
|
* Abstraction for applying iWork entities into local persistence.
|
||||||
*/
|
*/
|
||||||
public interface IWorkSyncProcessor {
|
public interface IWorkSyncProcessor {
|
||||||
|
|
||||||
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options);
|
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||||
|
SyncOptions options);
|
||||||
|
|
||||||
|
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context);
|
||||||
|
|
||||||
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options);
|
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options);
|
||||||
|
|
||||||
|
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context);
|
||||||
|
|
||||||
BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options);
|
BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options);
|
||||||
|
|
||||||
BatchResult syncUsers(List<IWorkHrUserPageRespVO.User> data, SyncOptions options);
|
BatchResult syncUsers(List<IWorkHrUserPageRespVO.User> data, SyncOptions options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对当次同步累计的待处理/占位部门做最终补偿(跨页父子依赖)。
|
||||||
|
*/
|
||||||
|
default BatchResult flushDeptPending(DeptSyncContext context, SyncOptions options) {
|
||||||
|
return BatchResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execution options shared by batch and single sync flows.
|
* Execution options shared by batch and single sync flows.
|
||||||
*/
|
*/
|
||||||
@@ -53,6 +72,32 @@ public interface IWorkSyncProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门/分部跨页同步上下文,用于累计待处理记录与已就绪父级。
|
||||||
|
*/
|
||||||
|
final class DeptSyncContext {
|
||||||
|
private final Set<Long> readyParentIds = new HashSet<>();
|
||||||
|
private final List<IWorkHrSubcompanyPageRespVO.Subcompany> pendingSubcompanies = new ArrayList<>();
|
||||||
|
private final List<IWorkHrDepartmentPageRespVO.Department> pendingDepartments = new ArrayList<>();
|
||||||
|
private final Set<Long> placeholderDeptIds = new HashSet<>();
|
||||||
|
|
||||||
|
public Set<Long> getReadyParentIds() {
|
||||||
|
return readyParentIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IWorkHrSubcompanyPageRespVO.Subcompany> getPendingSubcompanies() {
|
||||||
|
return pendingSubcompanies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IWorkHrDepartmentPageRespVO.Department> getPendingDepartments() {
|
||||||
|
return pendingDepartments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> getPlaceholderDeptIds() {
|
||||||
|
return placeholderDeptIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregated result for a sync batch.
|
* Aggregated result for a sync batch.
|
||||||
*/
|
*/
|
||||||
@@ -170,11 +215,11 @@ public interface IWorkSyncProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default BatchResult syncSubcompany(IWorkHrSubcompanyPageRespVO.Subcompany data, SyncOptions options) {
|
default BatchResult syncSubcompany(IWorkHrSubcompanyPageRespVO.Subcompany data, SyncOptions options) {
|
||||||
return syncSubcompanies(Collections.singletonList(data), options);
|
return syncSubcompanies(Collections.singletonList(data), options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
default BatchResult syncDepartment(IWorkHrDepartmentPageRespVO.Department data, SyncOptions options) {
|
default BatchResult syncDepartment(IWorkHrDepartmentPageRespVO.Department data, SyncOptions options) {
|
||||||
return syncDepartments(Collections.singletonList(data), options);
|
return syncDepartments(Collections.singletonList(data), options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
default BatchResult syncJobTitle(IWorkHrJobTitlePageRespVO.JobTitle data, SyncOptions options) {
|
default BatchResult syncJobTitle(IWorkHrJobTitlePageRespVO.JobTitle data, SyncOptions options) {
|
||||||
|
|||||||
@@ -50,14 +50,34 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options) {
|
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options) {
|
||||||
|
return syncSubcompanies(data, options, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context) {
|
||||||
|
return syncSubcompaniesInternal(data, options, context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatchResult syncSubcompaniesInternal(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context,
|
||||||
|
boolean allowPlaceholderOnRemaining) {
|
||||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> records = CollUtil.emptyIfNull(data);
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> records = CollUtil.emptyIfNull(data);
|
||||||
BatchResult result = BatchResult.empty();
|
BatchResult result = BatchResult.empty();
|
||||||
if (records.isEmpty()) {
|
if (records.isEmpty()
|
||||||
|
&& (context == null || CollUtil.isEmpty(context.getPendingSubcompanies()))) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result.increasePulled(records.size());
|
result.increasePulled(records.size());
|
||||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> queue = new ArrayList<>(records);
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> queue = new ArrayList<>();
|
||||||
Set<Long> readyParentIds = new HashSet<>();
|
if (context != null && CollUtil.isNotEmpty(context.getPendingSubcompanies())) {
|
||||||
|
queue.addAll(context.getPendingSubcompanies());
|
||||||
|
context.getPendingSubcompanies().clear();
|
||||||
|
}
|
||||||
|
queue.addAll(records);
|
||||||
|
Set<Long> readyParentIds = context != null ? context.getReadyParentIds() : new HashSet<>();
|
||||||
int guard = 0;
|
int guard = 0;
|
||||||
int maxPasses = Math.max(1, queue.size() * 2);
|
int maxPasses = Math.max(1, queue.size() * 2);
|
||||||
while (!queue.isEmpty() && guard++ < maxPasses) {
|
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||||
@@ -106,6 +126,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
if (context != null && !allowPlaceholderOnRemaining) {
|
||||||
|
context.getPendingSubcompanies().addAll(queue);
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!queue.isEmpty()) {
|
if (!queue.isEmpty()) {
|
||||||
for (IWorkHrSubcompanyPageRespVO.Subcompany remaining : queue) {
|
for (IWorkHrSubcompanyPageRespVO.Subcompany remaining : queue) {
|
||||||
log.warn("[iWork] 分部父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getSubcompanyname());
|
log.warn("[iWork] 分部父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getSubcompanyname());
|
||||||
@@ -117,6 +143,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
try {
|
try {
|
||||||
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
||||||
applyDeptOutcome(result, outcome, "分部", remaining.getSubcompanyname());
|
applyDeptOutcome(result, outcome, "分部", remaining.getSubcompanyname());
|
||||||
|
if (context != null && outcome.deptId() != null) {
|
||||||
|
context.getPlaceholderDeptIds().add(outcome.deptId());
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("[iWork] 分部占位插入失败: id={} name={}", remaining.getId(), remaining.getSubcompanyname(), ex);
|
log.error("[iWork] 分部占位插入失败: id={} name={}", remaining.getId(), remaining.getSubcompanyname(), ex);
|
||||||
result.increaseFailed();
|
result.increaseFailed();
|
||||||
@@ -128,14 +157,34 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options) {
|
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options) {
|
||||||
|
return syncDepartments(data, options, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context) {
|
||||||
|
return syncDepartmentsInternal(data, options, context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatchResult syncDepartmentsInternal(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||||
|
SyncOptions options,
|
||||||
|
DeptSyncContext context,
|
||||||
|
boolean allowPlaceholderOnRemaining) {
|
||||||
List<IWorkHrDepartmentPageRespVO.Department> records = CollUtil.emptyIfNull(data);
|
List<IWorkHrDepartmentPageRespVO.Department> records = CollUtil.emptyIfNull(data);
|
||||||
BatchResult result = BatchResult.empty();
|
BatchResult result = BatchResult.empty();
|
||||||
if (records.isEmpty()) {
|
if (records.isEmpty()
|
||||||
|
&& (context == null || CollUtil.isEmpty(context.getPendingDepartments()))) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result.increasePulled(records.size());
|
result.increasePulled(records.size());
|
||||||
List<IWorkHrDepartmentPageRespVO.Department> queue = new ArrayList<>(records);
|
List<IWorkHrDepartmentPageRespVO.Department> queue = new ArrayList<>();
|
||||||
Set<Long> readyParentIds = new HashSet<>();
|
if (context != null && CollUtil.isNotEmpty(context.getPendingDepartments())) {
|
||||||
|
queue.addAll(context.getPendingDepartments());
|
||||||
|
context.getPendingDepartments().clear();
|
||||||
|
}
|
||||||
|
queue.addAll(records);
|
||||||
|
Set<Long> readyParentIds = context != null ? context.getReadyParentIds() : new HashSet<>();
|
||||||
int guard = 0;
|
int guard = 0;
|
||||||
int maxPasses = Math.max(1, queue.size() * 2);
|
int maxPasses = Math.max(1, queue.size() * 2);
|
||||||
while (!queue.isEmpty() && guard++ < maxPasses) {
|
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||||
@@ -184,6 +233,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
if (context != null && !allowPlaceholderOnRemaining) {
|
||||||
|
context.getPendingDepartments().addAll(queue);
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!queue.isEmpty()) {
|
if (!queue.isEmpty()) {
|
||||||
for (IWorkHrDepartmentPageRespVO.Department remaining : queue) {
|
for (IWorkHrDepartmentPageRespVO.Department remaining : queue) {
|
||||||
log.warn("[iWork] 部门父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getDepartmentname());
|
log.warn("[iWork] 部门父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getDepartmentname());
|
||||||
@@ -195,6 +250,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
try {
|
try {
|
||||||
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
||||||
applyDeptOutcome(result, outcome, "部门", remaining.getDepartmentname());
|
applyDeptOutcome(result, outcome, "部门", remaining.getDepartmentname());
|
||||||
|
if (context != null && outcome.deptId() != null) {
|
||||||
|
context.getPlaceholderDeptIds().add(outcome.deptId());
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("[iWork] 部门占位插入失败: id={} name={}", remaining.getId(), remaining.getDepartmentname(), ex);
|
log.error("[iWork] 部门占位插入失败: id={} name={}", remaining.getId(), remaining.getDepartmentname(), ex);
|
||||||
result.increaseFailed();
|
result.increaseFailed();
|
||||||
@@ -204,6 +262,17 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult flushDeptPending(DeptSyncContext context, SyncOptions options) {
|
||||||
|
BatchResult result = BatchResult.empty();
|
||||||
|
if (context == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.merge(syncSubcompaniesInternal(Collections.emptyList(), options, context, true));
|
||||||
|
result.merge(syncDepartmentsInternal(Collections.emptyList(), options, context, true));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options) {
|
public BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options) {
|
||||||
List<IWorkHrJobTitlePageRespVO.JobTitle> records = CollUtil.emptyIfNull(data);
|
List<IWorkHrJobTitlePageRespVO.JobTitle> records = CollUtil.emptyIfNull(data);
|
||||||
@@ -444,7 +513,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
req.setIsGroup(Boolean.FALSE);
|
req.setIsGroup(Boolean.FALSE);
|
||||||
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||||
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
||||||
req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getSubcompanycode()), String.valueOf(data.getId())));
|
req.setExternalDeptCode(trimToNull(data.getSubcompanycode()));
|
||||||
req.setExternalDeptName(data.getSubcompanyname());
|
req.setExternalDeptName(data.getSubcompanyname());
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
@@ -464,7 +533,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
req.setIsGroup(Boolean.FALSE);
|
req.setIsGroup(Boolean.FALSE);
|
||||||
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||||
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
||||||
req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getDepartmentcode()), String.valueOf(data.getId())));
|
req.setExternalDeptCode(trimToNull(data.getDepartmentcode()));
|
||||||
req.setExternalDeptName(data.getDepartmentname());
|
req.setExternalDeptName(data.getDepartmentname());
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
||||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import com.zt.plat.module.system.service.dept.DeptService;
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||||
@@ -31,6 +32,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
|
|
||||||
private final IWorkOrgRestService orgRestService;
|
private final IWorkOrgRestService orgRestService;
|
||||||
private final IWorkSyncProcessor syncProcessor;
|
private final IWorkSyncProcessor syncProcessor;
|
||||||
|
private final DeptService deptService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) {
|
public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) {
|
||||||
@@ -64,11 +66,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE);
|
boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE);
|
||||||
int processedPages = 0;
|
int processedPages = 0;
|
||||||
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
|
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
|
||||||
|
IWorkSyncProcessor.DeptSyncContext deptSyncContext = (syncDepartments || syncSubcompanies)
|
||||||
|
? new IWorkSyncProcessor.DeptSyncContext()
|
||||||
|
: null;
|
||||||
if (syncSubcompanies) {
|
if (syncSubcompanies) {
|
||||||
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats);
|
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats, deptSyncContext);
|
||||||
}
|
}
|
||||||
if (syncDepartments) {
|
if (syncDepartments) {
|
||||||
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats);
|
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats, deptSyncContext);
|
||||||
}
|
}
|
||||||
if (syncJobTitle) {
|
if (syncJobTitle) {
|
||||||
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
|
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
|
||||||
@@ -76,6 +81,13 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
if (syncUsers) {
|
if (syncUsers) {
|
||||||
processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats);
|
processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats);
|
||||||
}
|
}
|
||||||
|
if (deptSyncContext != null) {
|
||||||
|
IWorkSyncProcessor.BatchResult flushResult = syncProcessor.flushDeptPending(deptSyncContext, options);
|
||||||
|
updateStat(respVO.getDepartmentStat(), flushResult, 0);
|
||||||
|
if (CollUtil.isNotEmpty(deptSyncContext.getPlaceholderDeptIds())) {
|
||||||
|
deptService.backfillMissingCodesWithoutEvent(deptSyncContext.getPlaceholderDeptIds());
|
||||||
|
}
|
||||||
|
}
|
||||||
respVO.setProcessedPages(processedPages);
|
respVO.setProcessedPages(processedPages);
|
||||||
return respVO;
|
return respVO;
|
||||||
}
|
}
|
||||||
@@ -83,7 +95,8 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO,
|
private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
IWorkSyncEntityStatVO stat,
|
IWorkSyncEntityStatVO stat,
|
||||||
List<IWorkSyncBatchStatVO> batches) {
|
List<IWorkSyncBatchStatVO> batches,
|
||||||
|
IWorkSyncProcessor.DeptSyncContext context) {
|
||||||
return executePaged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> {
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> {
|
||||||
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
||||||
query.setCurpage(page);
|
query.setCurpage(page);
|
||||||
@@ -92,7 +105,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
||||||
ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());
|
ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());
|
||||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options);
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options, context);
|
||||||
updateStat(stat, result, dataList.size());
|
updateStat(stat, result, dataList.size());
|
||||||
return new BatchExecution(result, dataList.size());
|
return new BatchExecution(result, dataList.size());
|
||||||
});
|
});
|
||||||
@@ -101,7 +114,8 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
private int executeDepartmentFullSync(IWorkFullSyncReqVO reqVO,
|
private int executeDepartmentFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
IWorkSyncEntityStatVO stat,
|
IWorkSyncEntityStatVO stat,
|
||||||
List<IWorkSyncBatchStatVO> batches) {
|
List<IWorkSyncBatchStatVO> batches,
|
||||||
|
IWorkSyncProcessor.DeptSyncContext context) {
|
||||||
return executePaged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> {
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> {
|
||||||
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
||||||
query.setCurpage(page);
|
query.setCurpage(page);
|
||||||
@@ -110,7 +124,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
||||||
ensureIWorkSuccess("拉取部门", pageResp.isSuccess(), pageResp.getMessage());
|
ensureIWorkSuccess("拉取部门", pageResp.isSuccess(), pageResp.getMessage());
|
||||||
List<IWorkHrDepartmentPageRespVO.Department> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
List<IWorkHrDepartmentPageRespVO.Department> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options);
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options, context);
|
||||||
updateStat(stat, result, dataList.size());
|
updateStat(stat, result, dataList.size());
|
||||||
return new BatchExecution(result, dataList.size());
|
return new BatchExecution(result, dataList.size());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.zt.plat.module.system.service.permission;
|
package com.zt.plat.module.system.service.permission;
|
||||||
|
|
||||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||||
|
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -143,4 +144,12 @@ public interface PermissionService {
|
|||||||
*/
|
*/
|
||||||
DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
|
DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得用户的数据权限级别
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @return 数据权限范围枚举
|
||||||
|
*/
|
||||||
|
DataScopeEnum getUserDataPermissionLevel(Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.zt.plat.module.system.enums.permission.RoleTypeEnum;
|
|||||||
import com.zt.plat.module.system.service.dept.DeptService;
|
import com.zt.plat.module.system.service.dept.DeptService;
|
||||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||||
import com.zt.plat.module.system.service.userdept.UserDeptService;
|
import com.zt.plat.module.system.service.userdept.UserDeptService;
|
||||||
|
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -57,6 +58,15 @@ import static com.zt.plat.module.system.enums.ErrorCodeConstants.ROLE_CAN_NOT_UP
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class PermissionServiceImpl implements PermissionService {
|
public class PermissionServiceImpl implements PermissionService {
|
||||||
|
|
||||||
|
private static final List<DataScopeEnum> DATA_SCOPE_PRIORITY = Arrays.asList(
|
||||||
|
DataScopeEnum.ALL,
|
||||||
|
DataScopeEnum.COMPANY_AND_DEPT,
|
||||||
|
DataScopeEnum.DEPT_AND_CHILD,
|
||||||
|
DataScopeEnum.DEPT_ONLY,
|
||||||
|
DataScopeEnum.DEPT_CUSTOM,
|
||||||
|
DataScopeEnum.SELF
|
||||||
|
);
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private RoleMenuMapper roleMenuMapper;
|
private RoleMenuMapper roleMenuMapper;
|
||||||
@Resource
|
@Resource
|
||||||
@@ -404,6 +414,40 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DataPermission(enable = false)
|
||||||
|
@TenantIgnore
|
||||||
|
public DataScopeEnum getUserDataPermissionLevel(Long userId) {
|
||||||
|
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
|
||||||
|
if (CollUtil.isEmpty(roles)) {
|
||||||
|
return DataScopeEnum.SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataScopeEnum best = null;
|
||||||
|
for (RoleDO role : roles) {
|
||||||
|
DataScopeEnum scopeEnum = DataScopeEnum.findByScope(role.getDataScope());
|
||||||
|
if (scopeEnum == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (best == null || compareScope(scopeEnum, best) < 0) {
|
||||||
|
best = scopeEnum;
|
||||||
|
if (DataScopeEnum.ALL.equals(best)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best != null ? best : DataScopeEnum.SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareScope(DataScopeEnum left, DataScopeEnum right) {
|
||||||
|
return getScopePriority(left) - getScopePriority(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getScopePriority(DataScopeEnum scope) {
|
||||||
|
int idx = DATA_SCOPE_PRIORITY.indexOf(scope);
|
||||||
|
return idx >= 0 ? idx : Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得自身的代理对象,解决 AOP 生效问题
|
* 获得自身的代理对象,解决 AOP 生效问题
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.dal.mysql.dept.PostMapper;
|
||||||
|
import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper;
|
||||||
|
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 org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for cross-page pending handling and placeholder backfill in IWorkSyncProcessorImpl.
|
||||||
|
*/
|
||||||
|
class IWorkSyncProcessorImplTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private IWorkSyncProcessorImpl processor;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DeptService deptService;
|
||||||
|
@Mock
|
||||||
|
private PostService postService;
|
||||||
|
@Mock
|
||||||
|
private PostMapper postMapper;
|
||||||
|
@Mock
|
||||||
|
private AdminUserService adminUserService;
|
||||||
|
@Mock
|
||||||
|
private AdminUserMapper adminUserMapper;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldProcessPendingChildWhenParentArrivesInLaterPage() {
|
||||||
|
IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext();
|
||||||
|
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||||
|
|
||||||
|
IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department();
|
||||||
|
child.setId(200);
|
||||||
|
child.setDepartmentname("child");
|
||||||
|
child.setSupdepid(100); // parent comes later
|
||||||
|
|
||||||
|
IWorkHrDepartmentPageRespVO.Department parent = new IWorkHrDepartmentPageRespVO.Department();
|
||||||
|
parent.setId(100);
|
||||||
|
parent.setDepartmentname("parent");
|
||||||
|
parent.setSupdepid(0); // root
|
||||||
|
|
||||||
|
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||||
|
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(100L, 200L);
|
||||||
|
|
||||||
|
processor.syncDepartments(List.of(child), options, context);
|
||||||
|
|
||||||
|
verify(deptService, never()).createDept(any());
|
||||||
|
assertEquals(1, context.getPendingDepartments().size());
|
||||||
|
|
||||||
|
processor.syncDepartments(List.of(parent), options, context);
|
||||||
|
|
||||||
|
verify(deptService, times(2)).createDept(any());
|
||||||
|
assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after parent processed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldInsertPlaceholderWhenParentMissingAfterFlush() {
|
||||||
|
IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext();
|
||||||
|
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||||
|
|
||||||
|
IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department();
|
||||||
|
child.setId(300);
|
||||||
|
child.setDepartmentname("orphan");
|
||||||
|
child.setSupdepid(9999); // never provided
|
||||||
|
|
||||||
|
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||||
|
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(300L);
|
||||||
|
|
||||||
|
processor.syncDepartments(List.of(child), options, context);
|
||||||
|
assertEquals(1, context.getPendingDepartments().size());
|
||||||
|
|
||||||
|
IWorkSyncProcessor.BatchResult flushResult = processor.flushDeptPending(context, options);
|
||||||
|
assertNotNull(flushResult);
|
||||||
|
|
||||||
|
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||||
|
verify(deptService, times(1)).createDept(captor.capture());
|
||||||
|
DeptSaveReqVO placeholderReq = captor.getValue();
|
||||||
|
assertTrue(Boolean.TRUE.equals(placeholderReq.getDelayCodeGeneration()));
|
||||||
|
assertNull(placeholderReq.getCode());
|
||||||
|
|
||||||
|
assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after placeholder insert");
|
||||||
|
assertTrue(context.getPlaceholderDeptIds().contains(300L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldKeepExternalCodeNullWhenDepartmentCodeBlank() {
|
||||||
|
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||||
|
|
||||||
|
IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department();
|
||||||
|
dept.setId(500);
|
||||||
|
dept.setDepartmentname("blank-code-dept");
|
||||||
|
dept.setDepartmentcode(" ");
|
||||||
|
dept.setSupdepid(0);
|
||||||
|
|
||||||
|
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||||
|
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(500L);
|
||||||
|
|
||||||
|
processor.syncDepartments(List.of(dept), options, null);
|
||||||
|
|
||||||
|
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||||
|
verify(deptService, times(1)).createDept(captor.capture());
|
||||||
|
DeptSaveReqVO req = captor.getValue();
|
||||||
|
assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldKeepExternalCodeNullWhenSubcompanyCodeBlank() {
|
||||||
|
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||||
|
|
||||||
|
IWorkHrSubcompanyPageRespVO.Subcompany subcompany = new IWorkHrSubcompanyPageRespVO.Subcompany();
|
||||||
|
subcompany.setId(600);
|
||||||
|
subcompany.setSubcompanyname("blank-code-sub");
|
||||||
|
subcompany.setSubcompanycode(null);
|
||||||
|
subcompany.setSupsubcomid(0);
|
||||||
|
|
||||||
|
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||||
|
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(600L);
|
||||||
|
|
||||||
|
processor.syncSubcompanies(List.of(subcompany), options, null);
|
||||||
|
|
||||||
|
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||||
|
verify(deptService, times(1)).createDept(captor.capture());
|
||||||
|
DeptSaveReqVO req = captor.getValue();
|
||||||
|
assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is null or blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import com.zt.plat.module.system.service.dept.DeptService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class IWorkSyncServiceImplTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private IWorkSyncServiceImpl syncService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private IWorkOrgRestService orgRestService;
|
||||||
|
@Mock
|
||||||
|
private IWorkSyncProcessor syncProcessor;
|
||||||
|
@Mock
|
||||||
|
private DeptService deptService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBackfillCodesWhenPlaceholdersExistAfterFullSync() {
|
||||||
|
IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO();
|
||||||
|
reqVO.setPageSize(1);
|
||||||
|
reqVO.setMaxPages(1);
|
||||||
|
|
||||||
|
IWorkHrDepartmentPageRespVO pageResp = new IWorkHrDepartmentPageRespVO();
|
||||||
|
pageResp.setSuccess(true);
|
||||||
|
IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department();
|
||||||
|
dept.setId(1);
|
||||||
|
pageResp.setDataList(List.of(dept));
|
||||||
|
when(orgRestService.listDepartments(any())).thenReturn(pageResp);
|
||||||
|
|
||||||
|
// 在部门同步时标记占位 ID
|
||||||
|
doAnswer((Answer<IWorkSyncProcessor.BatchResult>) invocation -> {
|
||||||
|
IWorkSyncProcessor.DeptSyncContext context = invocation.getArgument(2);
|
||||||
|
if (context != null) {
|
||||||
|
context.getPlaceholderDeptIds().add(500L);
|
||||||
|
}
|
||||||
|
return IWorkSyncProcessor.BatchResult.empty();
|
||||||
|
}).when(syncProcessor).syncDepartments(any(), any(), any(IWorkSyncProcessor.DeptSyncContext.class));
|
||||||
|
|
||||||
|
when(syncProcessor.flushDeptPending(any(), any())).thenReturn(IWorkSyncProcessor.BatchResult.empty());
|
||||||
|
|
||||||
|
syncService.fullSyncDepartments(reqVO);
|
||||||
|
|
||||||
|
verify(deptService, times(1)).backfillMissingCodesWithoutEvent(argThat(set -> set.contains(500L)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.zt.plat.module.system.service.permission;
|
package com.zt.plat.module.system.service.permission;
|
||||||
|
|
||||||
import com.zt.plat.framework.common.exception.ServiceException;
|
import com.zt.plat.framework.common.exception.ServiceException;
|
||||||
|
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||||
import com.zt.plat.framework.test.core.ut.BaseDbUnitTest;
|
import com.zt.plat.framework.test.core.ut.BaseDbUnitTest;
|
||||||
|
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||||
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
||||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
|
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
|
||||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDO;
|
import com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDO;
|
||||||
@@ -11,6 +13,7 @@ import com.zt.plat.module.system.dal.mysql.permission.RoleMapper;
|
|||||||
import com.zt.plat.module.system.dal.mysql.permission.RoleMenuMapper;
|
import com.zt.plat.module.system.dal.mysql.permission.RoleMenuMapper;
|
||||||
import com.zt.plat.module.system.dal.mysql.permission.UserRoleMapper;
|
import com.zt.plat.module.system.dal.mysql.permission.UserRoleMapper;
|
||||||
import com.zt.plat.module.system.dal.mysql.rolemenuexclusion.RoleMenuExclusionMapper;
|
import com.zt.plat.module.system.dal.mysql.rolemenuexclusion.RoleMenuExclusionMapper;
|
||||||
|
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||||
import com.zt.plat.module.system.enums.permission.RoleTypeEnum;
|
import com.zt.plat.module.system.enums.permission.RoleTypeEnum;
|
||||||
import com.zt.plat.module.system.service.dept.DeptService;
|
import com.zt.plat.module.system.service.dept.DeptService;
|
||||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||||
@@ -408,4 +411,54 @@ public class PermissionServiceTest extends BaseDbUnitTest {
|
|||||||
assertEquals(1, exclusionDOS.size());
|
assertEquals(1, exclusionDOS.size());
|
||||||
assertEquals(101L, exclusionDOS.get(0).getMenuId());
|
assertEquals(101L, exclusionDOS.get(0).getMenuId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserDataPermissionLevel_noRolesReturnSelf() {
|
||||||
|
Long userId = 1000L;
|
||||||
|
|
||||||
|
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||||
|
|
||||||
|
assertEquals(DataScopeEnum.SELF, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserDataPermissionLevel_pickHighestPriority() {
|
||||||
|
Long userId = 2000L;
|
||||||
|
RoleDO roleCustom = randomPojo(RoleDO.class, o -> o
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||||
|
.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
|
||||||
|
.setId(110L)
|
||||||
|
.setTenantId(0L));
|
||||||
|
roleMapper.insert(roleCustom);
|
||||||
|
RoleDO roleCompany = randomPojo(RoleDO.class, o -> o
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||||
|
.setDataScope(DataScopeEnum.COMPANY_AND_DEPT.getScope())
|
||||||
|
.setId(120L)
|
||||||
|
.setTenantId(0L));
|
||||||
|
roleMapper.insert(roleCompany);
|
||||||
|
|
||||||
|
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleCustom.getId())));
|
||||||
|
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleCompany.getId())));
|
||||||
|
|
||||||
|
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||||
|
|
||||||
|
assertEquals(DataScopeEnum.COMPANY_AND_DEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserDataPermissionLevel_serializeAsNumber() {
|
||||||
|
Long userId = 3000L;
|
||||||
|
RoleDO roleAll = randomPojo(RoleDO.class, o -> o
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||||
|
.setDataScope(DataScopeEnum.ALL.getScope())
|
||||||
|
.setId(210L)
|
||||||
|
.setTenantId(0L));
|
||||||
|
roleMapper.insert(roleAll);
|
||||||
|
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleAll.getId())));
|
||||||
|
|
||||||
|
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||||
|
|
||||||
|
assertEquals(DataScopeEnum.ALL, result);
|
||||||
|
assertEquals("1", JsonUtils.toJsonString(result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user