Merge remote-tracking branch 'base-version/test' into dev
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user