Merge remote-tracking branch 'base-version/test' into dev

This commit is contained in:
chenbowen
2026-01-20 08:58:56 +08:00
5 changed files with 123 additions and 18 deletions

View File

@@ -41,6 +41,7 @@ import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.List;
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.module.databus.framework.integration.config.ApiGatewayProperties.*;
@@ -471,11 +472,13 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
}
securedRequest.removeHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN);
securedRequest.removeHeader(HttpHeaders.AUTHORIZATION);
anonymousUserService.issueAccessToken(anonymousDetails)
.ifPresent(token -> {
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
});
Optional<String> tokenOptional = anonymousUserService.issueAccessToken(anonymousDetails);
if (tokenOptional.isEmpty()) {
throw new SecurityValidationException(HttpStatus.UNAUTHORIZED, "匿名访问获取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 {

View File

@@ -40,6 +40,9 @@ public class ApiAnonymousUserService {
private final AdminUserApi adminUserApi;
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;
@PostConstruct
@@ -105,18 +108,33 @@ public class ApiAnonymousUserService {
if (details == null) {
return Optional.empty();
}
try {
OAuth2AccessTokenCreateReqDTO req = buildAccessTokenRequest(details);
OAuth2AccessTokenRespDTO resp = oauth2TokenApi.createAccessToken(req).getCheckedData();
if (resp == null || !StringUtils.hasText(resp.getAccessToken())) {
log.warn("[ANONYMOUS] 获取用户 {} 的访问令牌失败: 响应为空", details.getUserId());
return Optional.empty();
OAuth2AccessTokenCreateReqDTO req = buildAccessTokenRequest(details);
Exception lastException = null;
for (int attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
try {
OAuth2AccessTokenRespDTO resp = oauth2TokenApi.createAccessToken(req).getCheckedData();
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) {

View File

@@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
@Mapper
public interface PostMapper extends BaseMapperX<PostDO> {
@@ -35,4 +36,12 @@ public interface PostMapper extends BaseMapperX<PostDO> {
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));
}
}

View File

@@ -322,7 +322,41 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
if (records.isEmpty()) {
return result;
}
long batchStart = System.currentTimeMillis();
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) {
if (user == null) {
continue;
@@ -344,7 +378,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
// 直接沿用 iWork 原始密码,避免重复格式化造成校验偏差
String externalPassword = trimToNull(user.getPassword());
AdminUserDO existing = adminUserMapper.selectById(user.getId());
AdminUserDO existing = user.getId() == null ? null : existingUsers.get(user.getId().longValue());
UserSyncOutcome outcome;
if (existing == null) {
if (!options.isCreateIfMissing()) {
@@ -377,6 +411,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
result.withMessage("同步人员失败: " + ex.getMessage());
}
}
long totalMs = System.currentTimeMillis() - batchStart;
log.info("[iWork] 人员批次同步完成 size={} preloadUsersMs={} preloadPostsMs={} totalMs={}",
records.size(), preloadUsersMs, preloadPostsMs, totalMs);
return result;
}
//TODO
@@ -495,11 +532,43 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
req.setId(existing.getId());
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
adminUserService.updateUser(req);
syncPassword(existing, externalPassword);
boolean infoChanged = isUserInfoChanged(existing, req);
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());
}
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,
Long deptId,
Long parentId,

View File

@@ -217,11 +217,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
int processedPages = 0;
for (int page = startPage; processedPages < pagesLimit; page++) {
long pageStart = System.currentTimeMillis();
BatchExecution execution = executor.execute(page, pageSize);
long pageMs = System.currentTimeMillis() - pageStart;
if (execution == null || execution.totalPulled == 0) {
break;
}
processedPages++;
log.info("[iWork] 全量同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
batchStat.setEntityType(type);
batchStat.setPageNumber(page);
@@ -403,11 +406,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
int processedPages = 0;
for (int page = startPage; processedPages < pagesLimit; page++) {
long pageStart = System.currentTimeMillis();
BatchExecution execution = executor.execute(page, pageSize);
long pageMs = System.currentTimeMillis() - pageStart;
if (execution == null || execution.totalPulled == 0) {
break;
}
processedPages++;
log.info("[iWork] 手动同步 {} 页={} pulled={} costMs={}", type, page, execution.totalPulled, pageMs);
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
batchStat.setEntityType(type);
batchStat.setPageNumber(page);