Merge branch 'dev' into test
This commit is contained in:
@@ -97,30 +97,6 @@ public class IWorkIntegrationController {
|
||||
return success(orgRestService.listUsers(reqVO));
|
||||
}
|
||||
|
||||
// @PostMapping("/hr/subcompany/sync")
|
||||
// @Operation(summary = "同步分部信息至 iWork")
|
||||
// public CommonResult<IWorkHrSyncRespVO> syncSubcompanies(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||
// return success(orgRestService.syncSubcompanies(reqVO));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/hr/department/sync")
|
||||
// @Operation(summary = "同步部门信息至 iWork")
|
||||
// public CommonResult<IWorkHrSyncRespVO> syncDepartments(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||
// return success(orgRestService.syncDepartments(reqVO));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/hr/job-title/sync")
|
||||
// @Operation(summary = "同步岗位信息至 iWork")
|
||||
// public CommonResult<IWorkHrSyncRespVO> syncJobTitles(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||
// return success(orgRestService.syncJobTitles(reqVO));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/hr/user/sync")
|
||||
// @Operation(summary = "同步人员信息至 iWork")
|
||||
// public CommonResult<IWorkHrSyncRespVO> syncUsers(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||
// return success(orgRestService.syncUsers(reqVO));
|
||||
// }
|
||||
|
||||
// ----------------- 同步到本地 -----------------
|
||||
|
||||
@PostMapping("/hr/departments/full-sync")
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* iWork 人力同步响应。
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "iWork 人力同步响应")
|
||||
public class IWorkHrSyncRespVO {
|
||||
|
||||
@Schema(description = "响应码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private boolean success;
|
||||
|
||||
@Schema(description = "同步结果明细")
|
||||
private List<SyncResult> result;
|
||||
|
||||
@Data
|
||||
@Schema(description = "同步结果项")
|
||||
public static class SyncResult {
|
||||
|
||||
@Schema(description = "操作动作 add/update/delete")
|
||||
@JsonProperty("@action")
|
||||
private String action;
|
||||
|
||||
@Schema(description = "外部编码")
|
||||
@JsonProperty("code")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "执行结果 success/fail")
|
||||
@JsonProperty("result")
|
||||
private String result;
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
@JsonProperty("success")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "失败描述")
|
||||
@JsonProperty("message")
|
||||
private String message;
|
||||
|
||||
@JsonIgnore
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
@JsonAnySetter
|
||||
public void putAttribute(String key, Object value) {
|
||||
if (attributes == null) {
|
||||
attributes = new LinkedHashMap<>();
|
||||
}
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> any() {
|
||||
return attributes == null ? Collections.emptyMap() : attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 同步 iWork 人力组织信息的请求。
|
||||
*/
|
||||
@Data
|
||||
public class IWorkOrgSyncReqVO {
|
||||
|
||||
@Schema(description = "同步数据集合,将被序列化为 data 传给 iWork", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "同步数据不能为空")
|
||||
private List<Map<String, Object>> data;
|
||||
}
|
||||
@@ -1,12 +1,35 @@
|
||||
package com.zt.plat.module.system.framework.integration.iwork.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 负责加载 {@link IWorkProperties} 的自动配置类。
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@EnableConfigurationProperties(IWorkProperties.class)
|
||||
public class IWorkIntegrationConfiguration {
|
||||
|
||||
private final IWorkProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
void reportConfigurationStatus() {
|
||||
if (!properties.hasAnyConfiguredValue()) {
|
||||
log.info("[iWork] 未检测到集成配置,默认关闭 iWork 功能。需要时请在配置文件中补充 iwork.* 项。");
|
||||
return;
|
||||
}
|
||||
List<String> issues = properties.collectCriticalIssues();
|
||||
if (!issues.isEmpty()) {
|
||||
log.warn("[iWork] 配置不完整:{}。系统会继续启动,但 iWork 能力将不可用。", String.join(";", issues));
|
||||
return;
|
||||
}
|
||||
log.info("[iWork] 已检测到完整配置,iWork 集成能力已启用。");
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,8 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepa
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSyncRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
||||
|
||||
@@ -24,11 +22,4 @@ public interface IWorkOrgRestService {
|
||||
|
||||
IWorkHrUserPageRespVO listUsers(IWorkUserQueryReqVO reqVO);
|
||||
|
||||
IWorkHrSyncRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO);
|
||||
|
||||
IWorkHrSyncRespVO syncDepartments(IWorkOrgSyncReqVO reqVO);
|
||||
|
||||
IWorkHrSyncRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO);
|
||||
|
||||
IWorkHrSyncRespVO syncUsers(IWorkOrgSyncReqVO reqVO);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
|
||||
IWorkSession session = createSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
|
||||
Map<String, Object> payload = buildUserPayload(reqVO);
|
||||
String responseBody = executeJsonRequest(properties.getPaths().getUserInfo(), reqVO.getQueryParams(), appId, session, payload);
|
||||
String userInfoPath = requireConfiguredPath(properties.getPaths().getUserInfo(), "iwork.paths.user-info");
|
||||
String responseBody = executeJsonRequest(userInfoPath, reqVO.getQueryParams(), appId, session, payload);
|
||||
|
||||
return buildUserInfoResponse(responseBody);
|
||||
}
|
||||
@@ -138,7 +139,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
IWorkSession session = createSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
|
||||
|
||||
Map<String, Object> payload = buildCreatePayload(reqVO);
|
||||
String responseBody = executeFormRequest(properties.getPaths().getCreateWorkflow(), null, appId, session, payload);
|
||||
String createWorkflowPath = requireConfiguredPath(properties.getPaths().getCreateWorkflow(), "iwork.paths.create-workflow");
|
||||
String responseBody = executeFormRequest(createWorkflowPath, null, appId, session, payload);
|
||||
return buildOperationResponse(responseBody);
|
||||
}
|
||||
|
||||
@@ -154,7 +156,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
IWorkSession session = createSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
|
||||
|
||||
Map<String, Object> payload = buildVoidPayload(reqVO);
|
||||
String responseBody = executeJsonRequest(properties.getPaths().getVoidWorkflow(), null, appId, session, payload);
|
||||
String voidWorkflowPath = requireConfiguredPath(properties.getPaths().getVoidWorkflow(), "iwork.paths.void-workflow");
|
||||
String responseBody = executeJsonRequest(voidWorkflowPath, null, appId, session, payload);
|
||||
return buildOperationResponse(responseBody);
|
||||
}
|
||||
|
||||
@@ -293,6 +296,17 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
return StringUtils.trimWhitespace(value);
|
||||
}
|
||||
|
||||
private String requireConfiguredPath(String value, String propertyPath) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
throw ServiceExceptionUtil.exception(IWORK_CONFIGURATION_INVALID, propertyPath + " 未配置");
|
||||
}
|
||||
return StringUtils.trimWhitespace(value);
|
||||
}
|
||||
|
||||
private long resolveTokenTtlSeconds() {
|
||||
return Math.max(1L, properties.getToken().getTtlSeconds());
|
||||
}
|
||||
|
||||
private ClientKeyPair resolveClientKeyPair(String appId, boolean forceRefresh) {
|
||||
String configured = properties.getClientPublicKey();
|
||||
if (StringUtils.hasText(configured)) {
|
||||
@@ -333,7 +347,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
String encryptedSecret = encryptWithPublicKey(registration.secret(), registration.spk());
|
||||
String encryptedUserId = encryptWithPublicKey(operatorUserId, registration.spk());
|
||||
String token = applyToken(appId, encryptedSecret);
|
||||
Instant expiresAt = Instant.now().plusSeconds(Math.max(1L, properties.getToken().getTtlSeconds()));
|
||||
long ttlSeconds = resolveTokenTtlSeconds();
|
||||
Instant expiresAt = Instant.now().plusSeconds(ttlSeconds);
|
||||
return new IWorkSession(token, encryptedUserId, expiresAt, registration.spk());
|
||||
}
|
||||
|
||||
@@ -359,8 +374,9 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
}
|
||||
|
||||
private RegistrationState register(String appId, ClientKeyPair clientKeyPair) {
|
||||
String registerPath = requireConfiguredPath(properties.getPaths().getRegister(), "iwork.paths.register");
|
||||
Request request = new Request.Builder()
|
||||
.url(resolveUrl(properties.getPaths().getRegister()))
|
||||
.url(resolveUrl(registerPath))
|
||||
.header(properties.getHeaders().getAppId(), appId)
|
||||
.header(properties.getHeaders().getClientPublicKey(), clientKeyPair.publicKey())
|
||||
.post(RequestBody.create(null, new byte[0]))
|
||||
@@ -376,11 +392,13 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
||||
}
|
||||
|
||||
private String applyToken(String appId, String encryptedSecret) {
|
||||
String applyTokenPath = requireConfiguredPath(properties.getPaths().getApplyToken(), "iwork.paths.apply-token");
|
||||
long ttlSeconds = resolveTokenTtlSeconds();
|
||||
Request request = new Request.Builder()
|
||||
.url(resolveUrl(properties.getPaths().getApplyToken()))
|
||||
.url(resolveUrl(applyTokenPath))
|
||||
.header(properties.getHeaders().getAppId(), appId)
|
||||
.header(properties.getHeaders().getSecret(), encryptedSecret)
|
||||
.header(properties.getHeaders().getTime(), String.valueOf(properties.getToken().getTtlSeconds()))
|
||||
.header(properties.getHeaders().getTime(), String.valueOf(ttlSeconds))
|
||||
.post(RequestBody.create(null, new byte[0]))
|
||||
.build();
|
||||
String responseBody = executeRequest(request, IWORK_APPLY_TOKEN_FAILED);
|
||||
|
||||
@@ -9,11 +9,9 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepa
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSyncRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgBaseQueryReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
||||
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
||||
@@ -62,9 +60,6 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
private static final TypeReference<List<IWorkHrUserPageRespVO.User>> USER_LIST_TYPE =
|
||||
new TypeReference<>() {
|
||||
};
|
||||
private static final TypeReference<List<IWorkHrSyncRespVO.SyncResult>> SYNC_RESULT_LIST_TYPE =
|
||||
new TypeReference<>() {
|
||||
};
|
||||
private static final okhttp3.MediaType JSON_MEDIA_TYPE = okhttp3.MediaType.get("application/json; charset=UTF-8");
|
||||
|
||||
private final IWorkProperties properties;
|
||||
@@ -178,34 +173,6 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkHrSyncRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO) {
|
||||
String path = orgPaths().getSyncSubcompany();
|
||||
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||
return buildSyncResp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkHrSyncRespVO syncDepartments(IWorkOrgSyncReqVO reqVO) {
|
||||
String path = orgPaths().getSyncDepartment();
|
||||
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||
return buildSyncResp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkHrSyncRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO) {
|
||||
String path = orgPaths().getSyncJobTitle();
|
||||
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||
return buildSyncResp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWorkHrSyncRespVO syncUsers(IWorkOrgSyncReqVO reqVO) {
|
||||
String path = orgPaths().getSyncUser();
|
||||
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||
return buildSyncResp(node);
|
||||
}
|
||||
|
||||
private JsonNode invokeParamsEndpoint(String path, Map<String, Object> params) {
|
||||
Objects.requireNonNull(params, "查询参数不能为空");
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
@@ -213,13 +180,6 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
return executeJson(path, payload);
|
||||
}
|
||||
|
||||
private JsonNode invokeDataEndpoint(String path, Object data) {
|
||||
Objects.requireNonNull(data, "同步数据不能为空");
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("data", data);
|
||||
return executeJson(path, payload);
|
||||
}
|
||||
|
||||
private JsonNode executeJson(String path, Map<String, Object> payload) {
|
||||
// 统一封装请求体并发送 POST 调用
|
||||
assertOrgConfigured(path);
|
||||
@@ -369,17 +329,6 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||
return respVO;
|
||||
}
|
||||
|
||||
// 解析并封装同步结果
|
||||
private IWorkHrSyncRespVO buildSyncResp(JsonNode node) {
|
||||
ParsedEnvelope envelope = parseEnvelope(node);
|
||||
IWorkHrSyncRespVO respVO = new IWorkHrSyncRespVO();
|
||||
respVO.setCode(envelope.code());
|
||||
respVO.setMessage(envelope.message());
|
||||
respVO.setSuccess(envelope.success());
|
||||
respVO.setResult(readList(envelope.root(), "result", SYNC_RESULT_LIST_TYPE));
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private JsonNode parseJson(String body) {
|
||||
try {
|
||||
return objectMapper.readTree(body);
|
||||
|
||||
Reference in New Issue
Block a user