Merge branch 'dev' into test
This commit is contained in:
@@ -49,4 +49,13 @@ public interface OAuth2TokenCommonApi {
|
|||||||
CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam("refreshToken") String refreshToken,
|
CommonResult<OAuth2AccessTokenRespDTO> refreshAccessToken(@RequestParam("refreshToken") String refreshToken,
|
||||||
@RequestParam("clientId") String clientId);
|
@RequestParam("clientId") String clientId);
|
||||||
|
|
||||||
|
@DeleteMapping(PREFIX + "/remove-by-user")
|
||||||
|
@Operation(summary = "按 userId + clientId 失效访问令牌")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(name = "userId", description = "用户编号", required = true, example = "1"),
|
||||||
|
@Parameter(name = "clientId", description = "客户端编号", required = true, example = "default")
|
||||||
|
})
|
||||||
|
CommonResult<Boolean> removeAccessTokensByUserIdAndClientId(@RequestParam("userId") Long userId,
|
||||||
|
@RequestParam("clientId") String clientId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,4 +48,9 @@ public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi {
|
|||||||
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
|
return success(BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> removeAccessTokensByUserIdAndClientId(Long userId, String clientId) {
|
||||||
|
return success(oauth2TokenService.removeAccessTokensByUserIdAndClientId(userId, clientId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ public class AuthLoginReqVO extends CaptchaVerificationReqVO {
|
|||||||
// @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
// @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "default")
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
// ========== 绑定社交登录时,需要传递如下参数 ==========
|
// ========== 绑定社交登录时,需要传递如下参数 ==========
|
||||||
|
|
||||||
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
|
|||||||
@@ -24,4 +24,7 @@ public class AuthSmsLoginReqVO {
|
|||||||
@NotEmpty(message = "验证码不能为空")
|
@NotEmpty(message = "验证码不能为空")
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "default")
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,7 @@ public class AuthSocialLoginReqVO {
|
|||||||
@NotEmpty(message = "state 不能为空")
|
@NotEmpty(message = "state 不能为空")
|
||||||
private String state;
|
private String state;
|
||||||
|
|
||||||
|
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "default")
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,7 @@ public class AuthTestLoginReqVO {
|
|||||||
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "default")
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -176,16 +176,43 @@ public class IWorkIntegrationController {
|
|||||||
return success(syncService.fullSyncUsers(reqVO));
|
return success(syncService.fullSyncUsers(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------- 根据ID同步到本地 -----------------
|
// ----------------- 根据ID同步到本地(仅传 ID) -----------------
|
||||||
|
|
||||||
@PostMapping("/syncById")
|
@PostMapping("/sync/user-by-id")
|
||||||
@Operation(summary = "根据ID触发 iWork 同步公司")
|
@Operation(summary = "根据ID同步 iWork 人员(单条)")
|
||||||
public CommonResult<IWorkFullSyncRespVO> syncById(@Valid @RequestBody IWorkSyncByIdReqVO reqVO) {
|
public CommonResult<IWorkFullSyncRespVO> syncUserById(@Valid @RequestBody IWorkSyncByIdReqVO reqVO) {
|
||||||
|
IWorkFullSyncReqVO fullReq = new IWorkFullSyncReqVO();
|
||||||
log.error("IWork集成后端手动录入syncById{}",reqVO);
|
fullReq.setId(reqVO.getId());
|
||||||
return success(syncService.manuallySyncData(reqVO));
|
fullReq.setStartPage(1);
|
||||||
|
fullReq.setPageSize(1);
|
||||||
|
fullReq.setMaxPages(1);
|
||||||
|
fullReq.setAllowUpdate(Boolean.TRUE);
|
||||||
|
return success(syncService.fullSyncUsers(fullReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sync/department-by-id")
|
||||||
|
@Operation(summary = "根据ID同步 iWork 部门(单条)")
|
||||||
|
public CommonResult<IWorkFullSyncRespVO> syncDepartmentById(@Valid @RequestBody IWorkSyncByIdReqVO reqVO) {
|
||||||
|
IWorkFullSyncReqVO fullReq = new IWorkFullSyncReqVO();
|
||||||
|
fullReq.setId(reqVO.getId());
|
||||||
|
fullReq.setStartPage(1);
|
||||||
|
fullReq.setPageSize(1);
|
||||||
|
fullReq.setMaxPages(1);
|
||||||
|
fullReq.setAllowUpdate(Boolean.TRUE);
|
||||||
|
return success(syncService.fullSyncDepartments(fullReq));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sync/subcompany-by-id")
|
||||||
|
@Operation(summary = "根据ID同步 iWork 分部(单条)")
|
||||||
|
public CommonResult<IWorkFullSyncRespVO> syncSubcompanyById(@Valid @RequestBody IWorkSyncByIdReqVO reqVO) {
|
||||||
|
IWorkFullSyncReqVO fullReq = new IWorkFullSyncReqVO();
|
||||||
|
fullReq.setId(reqVO.getId());
|
||||||
|
fullReq.setStartPage(1);
|
||||||
|
fullReq.setPageSize(1);
|
||||||
|
fullReq.setMaxPages(1);
|
||||||
|
fullReq.setAllowUpdate(Boolean.TRUE);
|
||||||
|
return success(syncService.fullSyncSubcompanies(fullReq));
|
||||||
|
}
|
||||||
|
|
||||||
private ResponseEntity<String> buildOaResponse(IWorkOaRawResponse resp) {
|
private ResponseEntity<String> buildOaResponse(IWorkOaRawResponse resp) {
|
||||||
if (resp == null) {
|
if (resp == null) {
|
||||||
|
|||||||
@@ -1,99 +1,15 @@
|
|||||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.Max;
|
|
||||||
import jakarta.validation.constraints.Min;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iWork 手动同步请求
|
* iWork 手动同步请求
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class IWorkSyncByIdReqVO {
|
public class IWorkSyncByIdReqVO {
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "起始页码,从 1 开始", example = "1")
|
|
||||||
@Min(1)
|
|
||||||
private Integer startPage = 1;
|
|
||||||
|
|
||||||
@Schema(description = "最大处理页数,null 表示处理至 iWork 返回的末页", example = "10")
|
|
||||||
@Min(1)
|
|
||||||
private Integer maxPages;
|
|
||||||
|
|
||||||
@Schema(description = "每次分页从 iWork 拉取的记录数", example = "100")
|
|
||||||
@Min(1)
|
|
||||||
@Max(500)
|
|
||||||
private Integer pageSize = 100;
|
|
||||||
|
|
||||||
@Schema(description = "同步范围列表,默认同步全部。可选:subcompany、department、jobTitle、user")
|
|
||||||
private List<String> scopes;
|
|
||||||
|
|
||||||
@Schema(description = "是否包含已失效(canceled=1)的记录", example = "false")
|
|
||||||
private Boolean includeCanceled = Boolean.FALSE;
|
|
||||||
|
|
||||||
@Schema(description = "是否允许更新已存在的本地实体", example = "false")
|
|
||||||
private Boolean allowUpdate = Boolean.FALSE;
|
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "指定同步记录的 iWork ID。传入后仅同步对应记录", example = "12345")
|
@Schema(description = "指定同步记录的 iWork ID。传入后仅同步对应记录", example = "12345")
|
||||||
@NotBlank(message = "ID不能为空")
|
@NotBlank(message = "ID不能为空")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Schema(description = "部门编码", example = "ZT001")
|
|
||||||
private String code;
|
|
||||||
|
|
||||||
@Schema(description = "部门名称", example = "ZT")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Schema(description = "部门简称", example = "技术")
|
|
||||||
private String shortName;
|
|
||||||
|
|
||||||
@Schema(description = "父部门 ID", example = "1024")
|
|
||||||
private String parentId;
|
|
||||||
|
|
||||||
@Schema(description = "负责人的用户编号", example = "2048")
|
|
||||||
private String leaderUserId;
|
|
||||||
|
|
||||||
@Schema(description = "联系电话", example = "15601691000")
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
@Schema(description = "邮箱", example = "zt@iocoder.cn")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
@Schema(description = "状态,见 CommonStatusEnum 枚举0 开启 1 关闭", example = "0")
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
private Long tenantId;
|
|
||||||
|
|
||||||
@Schema(description = "是否公司", example = "false")
|
|
||||||
private boolean isCompany;
|
|
||||||
|
|
||||||
@Schema(description = "是否集团", example = "false")
|
|
||||||
private boolean isGroup;
|
|
||||||
|
|
||||||
private boolean hasChildren;
|
|
||||||
|
|
||||||
|
|
||||||
public Set<IWorkSyncEntityTypeEnum> resolveScopes() {
|
|
||||||
EnumSet<IWorkSyncEntityTypeEnum> defaults = EnumSet.allOf(IWorkSyncEntityTypeEnum.class);
|
|
||||||
if (scopes == null || scopes.isEmpty()) {
|
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
Set<IWorkSyncEntityTypeEnum> resolved = scopes.stream()
|
|
||||||
.map(IWorkSyncEntityTypeEnum::fromCode)
|
|
||||||
.filter(java.util.Objects::nonNull)
|
|
||||||
.collect(Collectors.toCollection(() -> EnumSet.noneOf(IWorkSyncEntityTypeEnum.class)));
|
|
||||||
if (resolved.isEmpty()) {
|
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
return resolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
|
|||||||
.eq(OAuth2AccessTokenDO::getClientId, clientId));
|
.eq(OAuth2AccessTokenDO::getClientId, clientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<OAuth2AccessTokenDO> selectListByUserIdAndClientId(Long userId, String clientId) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
|
||||||
|
.eq(OAuth2AccessTokenDO::getUserId, userId)
|
||||||
|
.eq(OAuth2AccessTokenDO::getClientId, clientId));
|
||||||
|
}
|
||||||
|
|
||||||
default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
|
default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
|
||||||
return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
|
return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,4 +19,10 @@ public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshToken
|
|||||||
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default int deleteByUserIdAndClientId(Long userId, String clientId) {
|
||||||
|
return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
|
||||||
|
.eq(OAuth2RefreshTokenDO::getUserId, userId)
|
||||||
|
.eq(OAuth2RefreshTokenDO::getClientId, clientId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
|
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
|
||||||
}
|
}
|
||||||
// 创建 Token 令牌,记录登录日志
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, reqVO.getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -167,7 +167,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Token 令牌,记录登录日志
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, reqVO.getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -200,7 +200,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Token 令牌,记录登录日志
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, reqVO.getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLoginLog(Long userId, String username,
|
private void createLoginLog(Long userId, String username,
|
||||||
@@ -238,7 +238,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Token 令牌,记录登录日志
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
|
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL, reqVO.getClientId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -267,16 +267,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
return captchaService.verification(captchaVO);
|
return captchaService.verification(captchaVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
|
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType,
|
||||||
|
String clientId) {
|
||||||
// 插入登陆日志
|
// 插入登陆日志
|
||||||
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
|
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
|
||||||
// 创建访问令牌
|
// 创建访问令牌
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
||||||
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
|
resolveClientId(clientId), null);
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
|
||||||
|
return createTokenAfterLoginSuccess(userId, username, logType, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveClientId(String clientId) {
|
||||||
|
return StringUtils.isBlank(clientId) ? OAuth2ClientConstants.CLIENT_ID_DEFAULT : clientId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthLoginRespVO refreshToken(String refreshToken) {
|
public AuthLoginRespVO refreshToken(String refreshToken) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ package com.zt.plat.module.system.service.integration.iwork;
|
|||||||
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSyncByIdReqVO;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iWork 组织/人员同步服务
|
* iWork 组织/人员同步服务
|
||||||
@@ -31,8 +28,4 @@ public interface IWorkSyncService {
|
|||||||
*/
|
*/
|
||||||
IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO);
|
IWorkFullSyncRespVO fullSyncUsers(IWorkFullSyncReqVO reqVO);
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动同步
|
|
||||||
*/
|
|
||||||
IWorkFullSyncRespVO manuallySyncData(@Valid IWorkSyncByIdReqVO list);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -66,15 +65,6 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER));
|
return runFullSync(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动同步
|
|
||||||
*/
|
|
||||||
@Transactional
|
|
||||||
@Override
|
|
||||||
public IWorkFullSyncRespVO manuallySyncData(IWorkSyncByIdReqVO reqVO) {
|
|
||||||
return manuallySyncData(reqVO, EnumSet.of(IWorkSyncEntityTypeEnum.USER));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全量同步方法
|
* 全量同步方法
|
||||||
* @param reqVO 请求参数
|
* @param reqVO 请求参数
|
||||||
@@ -299,100 +289,6 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
private record BatchExecution(IWorkSyncProcessor.BatchResult result, int totalPulled) {
|
private record BatchExecution(IWorkSyncProcessor.BatchResult result, int totalPulled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据ID同步
|
|
||||||
*/
|
|
||||||
private IWorkFullSyncRespVO manuallySyncData(IWorkSyncByIdReqVO reqVO, Set<IWorkSyncEntityTypeEnum> scopes) {
|
|
||||||
|
|
||||||
//1 初始化响应对象:创建响应VO并设置分页大小和批次统计列表
|
|
||||||
IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO();
|
|
||||||
List<IWorkSyncBatchStatVO> batchStats = new ArrayList<>();
|
|
||||||
respVO.setBatches(batchStats);
|
|
||||||
//2 解析同步范围:根据传入的scopes确定需要同步的实体类型(用户、部门、分部、岗位)
|
|
||||||
boolean syncUsers = scopes.contains(IWorkSyncEntityTypeEnum.USER);
|
|
||||||
boolean syncDepartments = scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT);
|
|
||||||
boolean syncSubcompanies = scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY);
|
|
||||||
boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE);
|
|
||||||
int processedPages = 0;
|
|
||||||
IWorkSyncProcessor.SyncOptions options = manualSync(reqVO);
|
|
||||||
IWorkSyncProcessor.DeptSyncContext deptSyncContext = (syncDepartments || syncSubcompanies)
|
|
||||||
? new IWorkSyncProcessor.DeptSyncContext()
|
|
||||||
: null;
|
|
||||||
//3 按类型执行同步:依次执行分部、部门、岗位、用户的分页同步操作
|
|
||||||
if (syncSubcompanies) {
|
|
||||||
processedPages += runManualFullSyncForSubsidiaries(reqVO, options, respVO.getSubcompanyStat(), batchStats, deptSyncContext);//分部统计信息
|
|
||||||
}
|
|
||||||
if (syncDepartments) {
|
|
||||||
processedPages += departments(reqVO, options, respVO.getDepartmentStat(), batchStats, deptSyncContext);//部门统计信息
|
|
||||||
}
|
|
||||||
if (syncJobTitle) {
|
|
||||||
processedPages += syncPositionsManually(reqVO, options, respVO.getJobTitleStat(), batchStats);//岗位统计信息
|
|
||||||
}
|
|
||||||
if (syncUsers) {
|
|
||||||
processedPages += syncUsers(reqVO, options, respVO.getUserStat(), batchStats);//用户统计信息
|
|
||||||
}
|
|
||||||
//4、处理部门上下文:对部门和分部同步进行特殊处理,包括刷新待处理数据和补全部门编码
|
|
||||||
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);
|
|
||||||
return respVO;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动同步
|
|
||||||
*/
|
|
||||||
private IWorkSyncProcessor.SyncOptions manualSync(IWorkSyncByIdReqVO reqVO) {
|
|
||||||
boolean includeCanceled = Boolean.TRUE.equals(reqVO.getIncludeCanceled());
|
|
||||||
boolean allowUpdate = Boolean.TRUE.equals(reqVO.getAllowUpdate());
|
|
||||||
return IWorkSyncProcessor.SyncOptions.custom(includeCanceled, allowUpdate, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动执行子公司全量同步f
|
|
||||||
*/
|
|
||||||
private int runManualFullSyncForSubsidiaries(IWorkSyncByIdReqVO reqVO,
|
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
|
||||||
IWorkSyncEntityStatVO stat,
|
|
||||||
List<IWorkSyncBatchStatVO> batches,
|
|
||||||
IWorkSyncProcessor.DeptSyncContext context) {
|
|
||||||
return paged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> {
|
|
||||||
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
|
||||||
query.setCurpage(page);
|
|
||||||
query.setPagesize(pageSize);
|
|
||||||
applyQuery(query, reqVO);//查询条件
|
|
||||||
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
|
||||||
ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());// IWork执行成功
|
|
||||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options, context);
|
|
||||||
updateStat(stat, result, dataList.size());
|
|
||||||
return new BatchExecution(result, dataList.size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询条件
|
|
||||||
* @param query 查询条件
|
|
||||||
* @param reqVO 请求
|
|
||||||
*/
|
|
||||||
private void applyQuery(IWorkOrgBaseQueryReqVO query, IWorkSyncByIdReqVO reqVO) {
|
|
||||||
if (query == null || reqVO == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (StrUtil.isBlank(reqVO.getId())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
applyQueryId(query, reqVO.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyQueryId(IWorkOrgBaseQueryReqVO query, String id) {
|
private void applyQueryId(IWorkOrgBaseQueryReqVO query, String id) {
|
||||||
if (query == null || StrUtil.isBlank(id)) {
|
if (query == null || StrUtil.isBlank(id)) {
|
||||||
return;
|
return;
|
||||||
@@ -413,101 +309,4 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
|||||||
userQuery.setId(id);
|
userQuery.setId(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int paged(IWorkSyncByIdReqVO reqVO,
|
|
||||||
IWorkSyncEntityTypeEnum type,
|
|
||||||
List<IWorkSyncBatchStatVO> batches,
|
|
||||||
PageExecutor executor) {
|
|
||||||
int startPage = reqVO.getStartPage() == null ? 1 : reqVO.getStartPage();
|
|
||||||
int pageSize = reqVO.getPageSize() == null ? 100 : reqVO.getPageSize();
|
|
||||||
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);
|
|
||||||
batchStat.setPulled(execution.totalPulled);
|
|
||||||
batchStat.setCreated(execution.result.getCreated());
|
|
||||||
batchStat.setSkippedExisting(execution.result.getSkipped());
|
|
||||||
batchStat.setDisabled(execution.result.getDisabled());
|
|
||||||
batchStat.setFailed(execution.result.getFailed());
|
|
||||||
batches.add(batchStat);
|
|
||||||
}
|
|
||||||
return processedPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动执行部门同步
|
|
||||||
*/
|
|
||||||
private int departments(IWorkSyncByIdReqVO reqVO,
|
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
|
||||||
IWorkSyncEntityStatVO stat,
|
|
||||||
List<IWorkSyncBatchStatVO> batches,
|
|
||||||
IWorkSyncProcessor.DeptSyncContext context) {
|
|
||||||
return paged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> {
|
|
||||||
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
|
||||||
query.setCurpage(page);
|
|
||||||
query.setPagesize(pageSize);
|
|
||||||
applyQuery(query, reqVO);
|
|
||||||
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
|
||||||
ensureIWorkSuccess("拉取部门", pageResp.isSuccess(), pageResp.getMessage());
|
|
||||||
List<IWorkHrDepartmentPageRespVO.Department> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options, context);
|
|
||||||
updateStat(stat, result, dataList.size());
|
|
||||||
return new BatchExecution(result, dataList.size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动执行岗位同步
|
|
||||||
*/
|
|
||||||
private int syncPositionsManually(IWorkSyncByIdReqVO reqVO,
|
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
|
||||||
IWorkSyncEntityStatVO stat,
|
|
||||||
List<IWorkSyncBatchStatVO> batches) {
|
|
||||||
return paged(reqVO, IWorkSyncEntityTypeEnum.JOB_TITLE, batches, (page, pageSize) -> {
|
|
||||||
IWorkJobTitleQueryReqVO query = new IWorkJobTitleQueryReqVO();
|
|
||||||
query.setCurpage(page);
|
|
||||||
query.setPagesize(pageSize);
|
|
||||||
applyQuery(query, reqVO);
|
|
||||||
IWorkHrJobTitlePageRespVO pageResp = orgRestService.listJobTitles(query);
|
|
||||||
ensureIWorkSuccess("拉取岗位", pageResp.isSuccess(), pageResp.getMessage());
|
|
||||||
List<IWorkHrJobTitlePageRespVO.JobTitle> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncJobTitles(dataList, options);
|
|
||||||
updateStat(stat, result, dataList.size());
|
|
||||||
return new BatchExecution(result, dataList.size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动执行用户同步
|
|
||||||
*/
|
|
||||||
private int syncUsers(IWorkSyncByIdReqVO reqVO,
|
|
||||||
IWorkSyncProcessor.SyncOptions options,
|
|
||||||
IWorkSyncEntityStatVO stat,
|
|
||||||
List<IWorkSyncBatchStatVO> batches) {
|
|
||||||
return paged(reqVO, IWorkSyncEntityTypeEnum.USER, batches, (page, pageSize) -> {
|
|
||||||
IWorkUserQueryReqVO query = new IWorkUserQueryReqVO();
|
|
||||||
query.setCurpage(page);
|
|
||||||
query.setPagesize(pageSize);
|
|
||||||
applyQuery(query, reqVO);
|
|
||||||
IWorkHrUserPageRespVO pageResp = orgRestService.listUsers(query);
|
|
||||||
ensureIWorkSuccess("拉取人员", pageResp.isSuccess(), pageResp.getMessage());
|
|
||||||
List<IWorkHrUserPageRespVO.User> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
|
||||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncUsers(dataList, options);
|
|
||||||
updateStat(stat, result, dataList.size());
|
|
||||||
return new BatchExecution(result, dataList.size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ public interface OAuth2TokenService {
|
|||||||
*/
|
*/
|
||||||
OAuth2AccessTokenDO removeAccessToken(String accessToken);
|
OAuth2AccessTokenDO removeAccessToken(String accessToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 userId + clientId 批量移除访问令牌
|
||||||
|
* 注意:该流程中,会移除相关的刷新令牌
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param clientId 客户端编号
|
||||||
|
* @return 是否成功移除
|
||||||
|
*/
|
||||||
|
boolean removeAccessTokensByUserIdAndClientId(Long userId, String clientId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得访问令牌分页
|
* 获得访问令牌分页
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -152,6 +152,18 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
return accessTokenDO;
|
return accessTokenDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean removeAccessTokensByUserIdAndClientId(Long userId, String clientId) {
|
||||||
|
List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByUserIdAndClientId(userId, clientId);
|
||||||
|
if (CollUtil.isNotEmpty(accessTokenDOs)) {
|
||||||
|
oauth2AccessTokenMapper.deleteByIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId));
|
||||||
|
oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken));
|
||||||
|
}
|
||||||
|
int refreshRows = oauth2RefreshTokenMapper.deleteByUserIdAndClientId(userId, clientId);
|
||||||
|
return CollUtil.isNotEmpty(accessTokenDOs) || refreshRows > 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
|
public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
|
||||||
return oauth2AccessTokenMapper.selectPage(reqVO);
|
return oauth2AccessTokenMapper.selectPage(reqVO);
|
||||||
|
|||||||
Reference in New Issue
Block a user