Merge branch 'dev' into test
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package com.zt.plat.framework.common.util.asyncTask;
|
package com.zt.plat.framework.common.util.asyncTask;
|
||||||
|
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@@ -9,7 +11,7 @@ import java.util.concurrent.*;
|
|||||||
* 多次提交,一次等待
|
* 多次提交,一次等待
|
||||||
*/
|
*/
|
||||||
public class AsyncLatchUtils {
|
public class AsyncLatchUtils {
|
||||||
private static final ThreadLocal<List<TaskInfo>> THREAD_LOCAL = ThreadLocal.withInitial(LinkedList::new);
|
private static final TransmittableThreadLocal<List<TaskInfo>> THREAD_LOCAL = TransmittableThreadLocal.withInitial(LinkedList::new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交一个异步任务
|
* 提交一个异步任务
|
||||||
|
|||||||
@@ -18,19 +18,24 @@ import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
|||||||
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
import com.zt.plat.framework.tenant.core.context.DeptContextHolder;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.sf.jsqlparser.JSQLParserException;
|
||||||
import net.sf.jsqlparser.expression.Alias;
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.LongValue;
|
import net.sf.jsqlparser.expression.LongValue;
|
||||||
import net.sf.jsqlparser.expression.NullValue;
|
import net.sf.jsqlparser.expression.NullValue;
|
||||||
|
import net.sf.jsqlparser.expression.StringValue;
|
||||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||||
import net.sf.jsqlparser.expression.operators.relational.*;
|
import net.sf.jsqlparser.expression.operators.relational.*;
|
||||||
|
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||||
import net.sf.jsqlparser.schema.Column;
|
import net.sf.jsqlparser.schema.Column;
|
||||||
import net.sf.jsqlparser.schema.Table;
|
import net.sf.jsqlparser.schema.Table;
|
||||||
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
|
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
|
||||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||||
import net.sf.jsqlparser.statement.select.SelectItem;
|
import net.sf.jsqlparser.statement.select.SelectItem;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -67,7 +72,16 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
private static final String DEPT_COLUMN_NAME = "dept_id";
|
private static final String DEPT_COLUMN_NAME = "dept_id";
|
||||||
private static final String USER_COLUMN_NAME = "user_id";
|
private static final String USER_COLUMN_NAME = "user_id";
|
||||||
|
|
||||||
static final Expression EXPRESSION_NULL = new NullValue();
|
static final Expression EXPRESSION_NULL;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
EXPRESSION_NULL = CCJSqlParserUtil.parseCondExpression("1 = 0");
|
||||||
|
} catch (JSQLParserException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final String SYSTEM_USERS = "system_users";
|
public static final String SYSTEM_USERS = "system_users";
|
||||||
|
|
||||||
private final PermissionCommonApi permissionApi;
|
private final PermissionCommonApi permissionApi;
|
||||||
@@ -177,7 +191,9 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
// 情况三,拼接 Dept 和 Company User 的条件,最后组合
|
// 情况三,拼接 Dept 和 Company User 的条件,最后组合
|
||||||
Expression deptExpression = buildDeptExpression(tableName, tableAlias, effectiveDeptIds);
|
Expression deptExpression = buildDeptExpression(tableName, tableAlias, effectiveDeptIds);
|
||||||
// Expression deptExpression = buildDeptExpression(tableName, tableAlias, deptDataPermission.getDeptIds());
|
// Expression deptExpression = buildDeptExpression(tableName, tableAlias, deptDataPermission.getDeptIds());
|
||||||
Expression userExpression = buildUserExpression(tableName, tableAlias, effectiveSelf, loginUser.getId());
|
// 使用工号替换 UserId
|
||||||
|
String userWorkCode = SecurityFrameworkUtils.getLoginUserWorkCode();
|
||||||
|
Expression userExpression = buildUserExpression(tableName, tableAlias, effectiveSelf, loginUser.getId(), userWorkCode);
|
||||||
if (deptExpression == null && userExpression == null) {
|
if (deptExpression == null && userExpression == null) {
|
||||||
// TODO ZT:获得不到条件的时候,暂时不抛出异常,而是不返回数据
|
// TODO ZT:获得不到条件的时候,暂时不抛出异常,而是不返回数据
|
||||||
log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
|
log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
|
||||||
@@ -241,7 +257,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));
|
new ParenthesedExpressionList(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {
|
private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId, String workCode) {
|
||||||
// 如果不查看自己,则无需作为条件
|
// 如果不查看自己,则无需作为条件
|
||||||
if (Boolean.FALSE.equals(self)) {
|
if (Boolean.FALSE.equals(self)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -250,8 +266,13 @@ public class DeptDataPermissionRule implements DataPermissionRule {
|
|||||||
if (StrUtil.isEmpty(columnName)) {
|
if (StrUtil.isEmpty(columnName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接条件
|
// 拼接条件
|
||||||
return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
|
if (StrUtil.isBlank(workCode)) {
|
||||||
|
return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
|
||||||
|
} else {
|
||||||
|
return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new StringValue(workCode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 添加配置 ====================
|
// ==================== 添加配置 ====================
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.zt.plat.framework.security.core.LoginUser;
|
|||||||
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
|
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.ibatis.reflection.MetaObject;
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
@@ -48,14 +49,16 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Long userId = getUserId();
|
Long userId = getUserId();
|
||||||
|
String userWorkCode = SecurityFrameworkUtils.getLoginUserWorkCode();
|
||||||
|
String savedUserWorkCodeOrUserId = StringUtils.isNotEmpty(userWorkCode) ? userWorkCode : userId == null ? null : userId.toString();
|
||||||
String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
|
String userNickname = SecurityFrameworkUtils.getLoginUserNickname();
|
||||||
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人
|
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人
|
||||||
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
|
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
|
||||||
baseDO.setCreator(userId.toString());
|
baseDO.setCreator(savedUserWorkCodeOrUserId);
|
||||||
}
|
}
|
||||||
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
|
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
|
||||||
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
|
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
|
||||||
baseDO.setUpdater(userId.toString());
|
baseDO.setUpdater(savedUserWorkCodeOrUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BusinessBaseDO businessBaseDO) {
|
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BusinessBaseDO businessBaseDO) {
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public class LoginUser {
|
|||||||
// 用户关联的岗位信息
|
// 用户关联的岗位信息
|
||||||
public static final String INFO_KEY_POST_IDS = "postIds";
|
public static final String INFO_KEY_POST_IDS = "postIds";
|
||||||
|
|
||||||
|
// 工号
|
||||||
|
public static final String INFO_KEY_WORK_CODE = "workCode";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户编号
|
* 用户编号
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全服务工具类
|
* 安全服务工具类
|
||||||
@@ -93,6 +94,19 @@ public class SecurityFrameworkUtils {
|
|||||||
return loginUser != null ? loginUser.getVisitCompanyId() : null;
|
return loginUser != null ? loginUser.getVisitCompanyId() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static String getLoginUserWorkCode() {
|
||||||
|
LoginUser loginUser = getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<String, String> info = loginUser.getInfo();
|
||||||
|
if (info == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return MapUtil.getStr(info, LoginUser.INFO_KEY_WORK_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得当前用户的编号,从上下文中
|
* 获得当前用户的编号,从上下文中
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import java.time.Duration;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static com.zt.plat.framework.common.util.security.CryptoSignatureUtils.SIGNATURE_FIELD;
|
import static com.zt.plat.framework.common.util.security.CryptoSignatureUtils.SIGNATURE_FIELD;
|
||||||
import static com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties.*;
|
import static com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties.*;
|
||||||
@@ -471,11 +472,13 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
securedRequest.removeHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN);
|
securedRequest.removeHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN);
|
||||||
securedRequest.removeHeader(HttpHeaders.AUTHORIZATION);
|
securedRequest.removeHeader(HttpHeaders.AUTHORIZATION);
|
||||||
anonymousUserService.issueAccessToken(anonymousDetails)
|
Optional<String> tokenOptional = anonymousUserService.issueAccessToken(anonymousDetails);
|
||||||
.ifPresent(token -> {
|
if (tokenOptional.isEmpty()) {
|
||||||
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
throw new SecurityValidationException(HttpStatus.UNAUTHORIZED, "匿名访问获取token失败");
|
||||||
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
|
}
|
||||||
});
|
String token = tokenOptional.get();
|
||||||
|
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||||
|
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class SecurityValidationException extends RuntimeException {
|
private static final class SecurityValidationException extends RuntimeException {
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ public class ApiAnonymousUserService {
|
|||||||
private final AdminUserApi adminUserApi;
|
private final AdminUserApi adminUserApi;
|
||||||
private final OAuth2TokenCommonApi oauth2TokenApi;
|
private final OAuth2TokenCommonApi oauth2TokenApi;
|
||||||
|
|
||||||
|
private static final int RETRY_ATTEMPTS = 10;
|
||||||
|
private static final Duration RETRY_DELAY = Duration.ofSeconds(5);
|
||||||
|
|
||||||
private LoadingCache<Long, Optional<AnonymousUserDetails>> cache;
|
private LoadingCache<Long, Optional<AnonymousUserDetails>> cache;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -105,18 +108,33 @@ public class ApiAnonymousUserService {
|
|||||||
if (details == null) {
|
if (details == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
try {
|
OAuth2AccessTokenCreateReqDTO req = buildAccessTokenRequest(details);
|
||||||
OAuth2AccessTokenCreateReqDTO req = buildAccessTokenRequest(details);
|
Exception lastException = null;
|
||||||
OAuth2AccessTokenRespDTO resp = oauth2TokenApi.createAccessToken(req).getCheckedData();
|
for (int attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
|
||||||
if (resp == null || !StringUtils.hasText(resp.getAccessToken())) {
|
try {
|
||||||
log.warn("[ANONYMOUS] 获取用户 {} 的访问令牌失败: 响应为空", details.getUserId());
|
OAuth2AccessTokenRespDTO resp = oauth2TokenApi.createAccessToken(req).getCheckedData();
|
||||||
return Optional.empty();
|
if (resp == null || !StringUtils.hasText(resp.getAccessToken())) {
|
||||||
|
log.warn("[ANONYMOUS] 获取用户 {} 的访问令牌失败: 响应为空", details.getUserId());
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(resp.getAccessToken());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
lastException = ex;
|
||||||
|
if (attempt < RETRY_ATTEMPTS) {
|
||||||
|
log.warn("[ANONYMOUS] 获取用户 {} 的访问令牌失败,开始第 {} 次重试,原因:{}",
|
||||||
|
details.getUserId(), attempt, ex.getMessage());
|
||||||
|
try {
|
||||||
|
Thread.sleep(RETRY_DELAY.toMillis());
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.error("[ANONYMOUS] 获取用户 {} 的访问令牌重试被中断", details.getUserId());
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Optional.of(resp.getAccessToken());
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.error("[ANONYMOUS] 获取用户 {} 的访问令牌时发生异常", details.getUserId(), ex);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
log.error("[ANONYMOUS] 获取用户 {} 的访问令牌时发生异常", details.getUserId(), lastException);
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OAuth2AccessTokenCreateReqDTO buildAccessTokenRequest(AnonymousUserDetails details) {
|
private OAuth2AccessTokenCreateReqDTO buildAccessTokenRequest(AnonymousUserDetails details) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.zt.plat.module.system.api.dept;
|
|||||||
|
|
||||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
import com.zt.plat.framework.common.util.collection.CollectionUtils;
|
import com.zt.plat.framework.common.util.collection.CollectionUtils;
|
||||||
|
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||||
import com.zt.plat.module.system.api.dept.dto.*;
|
import com.zt.plat.module.system.api.dept.dto.*;
|
||||||
import com.zt.plat.module.system.enums.ApiConstants;
|
import com.zt.plat.module.system.enums.ApiConstants;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -15,6 +16,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory =
|
@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory =
|
||||||
@Tag(name = "RPC 服务 - 部门")
|
@Tag(name = "RPC 服务 - 部门")
|
||||||
public interface DeptApi {
|
public interface DeptApi {
|
||||||
@@ -86,6 +89,11 @@ public interface DeptApi {
|
|||||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||||
CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId);
|
CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId);
|
||||||
|
|
||||||
|
@GetMapping(PREFIX+"/up-find-company-node")
|
||||||
|
@Operation(summary = "获取公司节点信息", description = "通过部门编号,向上追溯部门信息,直到上级部门是公司,返回追溯到的部门信息列表")
|
||||||
|
@Parameter(name = "deptId", description = "部门编号", required = true, example = "1024")
|
||||||
|
CommonResult<List<DeptRespDTO>> upFindCompanyNode(@RequestParam("deptId") Long deptId);
|
||||||
|
|
||||||
// ========== 数据同步专用接口 ==========
|
// ========== 数据同步专用接口 ==========
|
||||||
|
|
||||||
@PostMapping(PREFIX + "/sync")
|
@PostMapping(PREFIX + "/sync")
|
||||||
|
|||||||
@@ -107,6 +107,12 @@ public class DeptApiImpl implements DeptApi {
|
|||||||
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<List<DeptRespDTO>> upFindCompanyNode(Long deptId) {
|
||||||
|
List<DeptDO> depts = deptService.upFindCompanyNode(deptId);
|
||||||
|
return success(BeanUtils.toBean(depts, DeptRespDTO.class));
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 数据同步专用接口 ==========
|
// ========== 数据同步专用接口 ==========
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -165,4 +165,11 @@ public class DeptController {
|
|||||||
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/up-find-company-node")
|
||||||
|
@Operation(summary = "获取公司节点信息", description = "通过部门编号,向上追溯部门信息,直到上级部门是公司,返回追溯到的部门信息列表")
|
||||||
|
@Parameter(name = "deptId", description = "部门编号", required = true, example = "1024")
|
||||||
|
public CommonResult<List<DeptRespVO>> upFindCompanyNode(@RequestParam("deptId") Long deptId) {
|
||||||
|
List<DeptDO> list = deptService.upFindCompanyNode(deptId);
|
||||||
|
return success(BeanUtils.toBean(list, DeptRespVO.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
||||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -167,4 +169,9 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
SELECT sd.* FROM SYSTEM_DEPT sd START WITH sd.id = #{deptId}
|
||||||
|
CONNECT BY PRIOR sd.parent_id = sd.id AND PRIOR sd.is_company <> 1 ;
|
||||||
|
""")
|
||||||
|
List<DeptDO> upFindCompanyNode(@Param("deptId") Long deptId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface PostMapper extends BaseMapperX<PostDO> {
|
public interface PostMapper extends BaseMapperX<PostDO> {
|
||||||
@@ -35,4 +36,12 @@ public interface PostMapper extends BaseMapperX<PostDO> {
|
|||||||
return selectOne(PostDO::getCode, code);
|
return selectOne(PostDO::getCode, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<PostDO> selectByCodes(Collection<String> codes) {
|
||||||
|
if (codes == null || codes.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return selectList(new LambdaQueryWrapperX<PostDO>()
|
||||||
|
.in(PostDO::getCode, codes));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,4 +185,11 @@ public interface DeptService {
|
|||||||
// ========== 数据同步专用接口 ==========
|
// ========== 数据同步专用接口 ==========
|
||||||
|
|
||||||
void syncDept(DeptSaveReqVO syncReqVO);
|
void syncDept(DeptSaveReqVO syncReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向上查找公司节点信息
|
||||||
|
* @param deptId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<DeptDO> upFindCompanyNode(Long deptId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -960,4 +960,9 @@ public class DeptServiceImpl implements DeptService {
|
|||||||
// 注意:不发布变更事件,避免循环同步
|
// 注意:不发布变更事件,避免循环同步
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DeptDO> upFindCompanyNode(Long deptId) {
|
||||||
|
return deptMapper.upFindCompanyNode(deptId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,7 +322,41 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
if (records.isEmpty()) {
|
if (records.isEmpty()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
long batchStart = System.currentTimeMillis();
|
||||||
result.increasePulled(records.size());
|
result.increasePulled(records.size());
|
||||||
|
// 预取已有用户,避免逐条查询
|
||||||
|
long preloadUsersStart = System.currentTimeMillis();
|
||||||
|
Map<Long, AdminUserDO> existingUsers = new HashMap<>();
|
||||||
|
List<Long> userIds = records.stream()
|
||||||
|
.map(IWorkHrUserPageRespVO.User::getId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(Integer::longValue)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
if (!userIds.isEmpty()) {
|
||||||
|
List<AdminUserDO> users = adminUserMapper.selectBatchIds(userIds);
|
||||||
|
if (CollUtil.isNotEmpty(users)) {
|
||||||
|
users.forEach(user -> existingUsers.put(user.getId(), user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long preloadUsersMs = System.currentTimeMillis() - preloadUsersStart;
|
||||||
|
|
||||||
|
// 预取岗位,避免逐条按编码查询
|
||||||
|
long preloadPostsStart = System.currentTimeMillis();
|
||||||
|
List<String> postCodes = records.stream()
|
||||||
|
.map(IWorkHrUserPageRespVO.User::getJobtitleid)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(this::buildJobCode)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
if (!postCodes.isEmpty()) {
|
||||||
|
List<PostDO> posts = postMapper.selectByCodes(postCodes);
|
||||||
|
if (CollUtil.isNotEmpty(posts)) {
|
||||||
|
posts.forEach(post -> postCache.put(buildPostCacheKey(post.getCode()), post));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long preloadPostsMs = System.currentTimeMillis() - preloadPostsStart;
|
||||||
|
|
||||||
for (IWorkHrUserPageRespVO.User user : records) {
|
for (IWorkHrUserPageRespVO.User user : records) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
continue;
|
continue;
|
||||||
@@ -344,7 +378,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
|
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
|
||||||
// 直接沿用 iWork 原始密码,避免重复格式化造成校验偏差
|
// 直接沿用 iWork 原始密码,避免重复格式化造成校验偏差
|
||||||
String externalPassword = trimToNull(user.getPassword());
|
String externalPassword = trimToNull(user.getPassword());
|
||||||
AdminUserDO existing = adminUserMapper.selectById(user.getId());
|
AdminUserDO existing = user.getId() == null ? null : existingUsers.get(user.getId().longValue());
|
||||||
UserSyncOutcome outcome;
|
UserSyncOutcome outcome;
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
if (!options.isCreateIfMissing()) {
|
if (!options.isCreateIfMissing()) {
|
||||||
@@ -377,6 +411,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
result.withMessage("同步人员失败: " + ex.getMessage());
|
result.withMessage("同步人员失败: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
long totalMs = System.currentTimeMillis() - batchStart;
|
||||||
|
log.info("[iWork] 人员批次同步完成 size={} preloadUsersMs={} preloadPostsMs={} totalMs={}",
|
||||||
|
records.size(), preloadUsersMs, preloadPostsMs, totalMs);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
//TODO
|
//TODO
|
||||||
@@ -495,11 +532,43 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
|||||||
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||||
req.setId(existing.getId());
|
req.setId(existing.getId());
|
||||||
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
||||||
adminUserService.updateUser(req);
|
boolean infoChanged = isUserInfoChanged(existing, req);
|
||||||
syncPassword(existing, externalPassword);
|
boolean passwordChanged = isPasswordChanged(existing, externalPassword);
|
||||||
|
|
||||||
|
if (!infoChanged && !passwordChanged) {
|
||||||
|
return new UserSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infoChanged) {
|
||||||
|
adminUserService.updateUser(req);
|
||||||
|
}
|
||||||
|
if (passwordChanged) {
|
||||||
|
syncPassword(existing, externalPassword);
|
||||||
|
}
|
||||||
return new UserSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
return new UserSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUserInfoChanged(AdminUserDO existing, UserSaveReqVO req) {
|
||||||
|
if (existing == null || req == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !Objects.equals(existing.getUsername(), req.getUsername())
|
||||||
|
|| !Objects.equals(existing.getWorkcode(), req.getWorkcode())
|
||||||
|
|| !Objects.equals(existing.getNickname(), req.getNickname())
|
||||||
|
|| !Objects.equals(existing.getRemark(), req.getRemark())
|
||||||
|
|| !Objects.equals(existing.getEmail(), req.getEmail())
|
||||||
|
|| !Objects.equals(existing.getMobile(), req.getMobile())
|
||||||
|
|| !Objects.equals(existing.getSex(), req.getSex())
|
||||||
|
|| !Objects.equals(existing.getStatus(), req.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPasswordChanged(AdminUserDO existing, String externalPassword) {
|
||||||
|
if (existing == null || StrUtil.isBlank(externalPassword)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !StrUtil.equals(externalPassword, existing.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
private DeptSaveReqVO buildSubcompanySaveReq(IWorkHrSubcompanyPageRespVO.Subcompany data,
|
private DeptSaveReqVO buildSubcompanySaveReq(IWorkHrSubcompanyPageRespVO.Subcompany data,
|
||||||
Long deptId,
|
Long deptId,
|
||||||
Long parentId,
|
Long parentId,
|
||||||
|
|||||||
@@ -217,11 +217,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
||||||
int processedPages = 0;
|
int processedPages = 0;
|
||||||
for (int page = startPage; processedPages < pagesLimit; page++) {
|
for (int page = startPage; processedPages < pagesLimit; page++) {
|
||||||
|
long pageStart = System.currentTimeMillis();
|
||||||
BatchExecution execution = executor.execute(page, pageSize);
|
BatchExecution execution = executor.execute(page, pageSize);
|
||||||
|
long pageMs = System.currentTimeMillis() - pageStart;
|
||||||
if (execution == null || execution.totalPulled == 0) {
|
if (execution == null || execution.totalPulled == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
processedPages++;
|
processedPages++;
|
||||||
|
log.info("[iWork] 全量同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
|
||||||
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
||||||
batchStat.setEntityType(type);
|
batchStat.setEntityType(type);
|
||||||
batchStat.setPageNumber(page);
|
batchStat.setPageNumber(page);
|
||||||
@@ -403,11 +406,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
||||||
int processedPages = 0;
|
int processedPages = 0;
|
||||||
for (int page = startPage; processedPages < pagesLimit; page++) {
|
for (int page = startPage; processedPages < pagesLimit; page++) {
|
||||||
|
long pageStart = System.currentTimeMillis();
|
||||||
BatchExecution execution = executor.execute(page, pageSize);
|
BatchExecution execution = executor.execute(page, pageSize);
|
||||||
|
long pageMs = System.currentTimeMillis() - pageStart;
|
||||||
if (execution == null || execution.totalPulled == 0) {
|
if (execution == null || execution.totalPulled == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
processedPages++;
|
processedPages++;
|
||||||
|
log.info("[iWork] 手动同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
|
||||||
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
||||||
batchStat.setEntityType(type);
|
batchStat.setEntityType(type);
|
||||||
batchStat.setPageNumber(page);
|
batchStat.setPageNumber(page);
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
.put(LoginUser.INFO_KEY_TENANT_ID, user.getTenantId().toString())
|
.put(LoginUser.INFO_KEY_TENANT_ID, user.getTenantId().toString())
|
||||||
.put(LoginUser.INFO_KEY_USERNAME, user.getUsername())
|
.put(LoginUser.INFO_KEY_USERNAME, user.getUsername())
|
||||||
.put(LoginUser.INFO_KEY_PHONE, user.getMobile())
|
.put(LoginUser.INFO_KEY_PHONE, user.getMobile())
|
||||||
|
.put(LoginUser.INFO_KEY_WORK_CODE, user.getWorkcode())
|
||||||
.put(LoginUser.INFO_KEY_POST_IDS, CollUtil.isEmpty(user.getPostIds()) ? "[]" : JsonUtils.toJsonString(user.getPostIds()))
|
.put(LoginUser.INFO_KEY_POST_IDS, CollUtil.isEmpty(user.getPostIds()) ? "[]" : JsonUtils.toJsonString(user.getPostIds()))
|
||||||
.build();
|
.build();
|
||||||
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
||||||
|
|||||||
Reference in New Issue
Block a user