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

This commit is contained in:
chenbowen
2025-11-24 15:53:49 +08:00
2 changed files with 128 additions and 61 deletions

View File

@@ -17,10 +17,9 @@ import lombok.RequiredArgsConstructor;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.*; import okhttp3.*;
import okio.Buffer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@@ -128,9 +127,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId()); String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId());
IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
MultiValueMap<String, String> formData = buildCreateForm(reqVO); Map<String, Object> payload = buildCreatePayload(reqVO);
appendFormExtras(formData, reqVO.getFormExtras()); String responseBody = executeJsonRequest(properties.getPaths().getCreateWorkflow(), null, appId, session, payload);
String responseBody = executeFormRequest(properties.getPaths().getCreateWorkflow(), appId, session, formData);
return buildOperationResponse(responseBody); return buildOperationResponse(responseBody);
} }
@@ -145,9 +143,8 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
} }
IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken())); IWorkSession session = ensureSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
MultiValueMap<String, String> formData = buildVoidForm(reqVO); Map<String, Object> payload = buildVoidPayload(reqVO);
appendFormExtras(formData, reqVO.getFormExtras()); String responseBody = executeJsonRequest(properties.getPaths().getVoidWorkflow(), null, appId, session, payload);
String responseBody = executeFormRequest(properties.getPaths().getVoidWorkflow(), appId, session, formData);
return buildOperationResponse(responseBody); return buildOperationResponse(responseBody);
} }
@@ -327,24 +324,6 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
return executeRequest(request, IWORK_REMOTE_REQUEST_FAILED); return executeRequest(request, IWORK_REMOTE_REQUEST_FAILED);
} }
private String executeFormRequest(String path,
String appId,
IWorkSession session,
MultiValueMap<String, String> formData) {
FormBody.Builder builder = new FormBody.Builder();
formData.forEach((key, values) -> {
if (values != null) {
values.forEach(value -> builder.add(key, value));
}
});
Request request = new Request.Builder()
.url(resolveUrl(path))
.headers(authHeaders(appId, session).build())
.post(builder.build())
.build();
return executeRequest(request, IWORK_REMOTE_REQUEST_FAILED);
}
private Headers.Builder authHeaders(String appId, IWorkSession session) { private Headers.Builder authHeaders(String appId, IWorkSession session) {
return new Headers.Builder() return new Headers.Builder()
.set(properties.getHeaders().getAppId(), appId) .set(properties.getHeaders().getAppId(), appId)
@@ -361,18 +340,19 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
return payload; return payload;
} }
private MultiValueMap<String, String> buildCreateForm(IWorkWorkflowCreateReqVO reqVO) { private Map<String, Object> buildCreatePayload(IWorkWorkflowCreateReqVO reqVO) {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); Map<String, Object> payload = new LinkedHashMap<>();
formData.add("requestName", reqVO.getRequestName()); payload.put("requestName", reqVO.getRequestName());
formData.add("workflowId", String.valueOf(resolveWorkflowId(reqVO.getWorkflowId()))); payload.put("workflowId", resolveWorkflowId(reqVO.getWorkflowId()));
formData.add("mainData", toJsonString(convertFormFields(reqVO.getMainFields()))); payload.put("mainData", convertFormFields(reqVO.getMainFields()));
if (reqVO.getDetailTables() != null && !reqVO.getDetailTables().isEmpty()) { if (reqVO.getDetailTables() != null && !reqVO.getDetailTables().isEmpty()) {
formData.add("detailData", toJsonString(convertDetailTables(reqVO.getDetailTables()))); payload.put("detailData", convertDetailTables(reqVO.getDetailTables()));
} }
if (reqVO.getOtherParams() != null && !reqVO.getOtherParams().isEmpty()) { if (reqVO.getOtherParams() != null && !reqVO.getOtherParams().isEmpty()) {
formData.add("otherParams", toJsonString(reqVO.getOtherParams())); payload.put("otherParams", reqVO.getOtherParams());
} }
return formData; appendPayloadExtras(payload, reqVO.getFormExtras());
return payload;
} }
private long resolveWorkflowId(Long requestWorkflowId) { private long resolveWorkflowId(Long requestWorkflowId) {
@@ -385,29 +365,26 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
throw ServiceExceptionUtil.exception(IWORK_WORKFLOW_ID_MISSING); throw ServiceExceptionUtil.exception(IWORK_WORKFLOW_ID_MISSING);
} }
private MultiValueMap<String, String> buildVoidForm(IWorkWorkflowVoidReqVO reqVO) { private Map<String, Object> buildVoidPayload(IWorkWorkflowVoidReqVO reqVO) {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); Map<String, Object> payload = new LinkedHashMap<>();
formData.add("requestId", reqVO.getRequestId()); payload.put("requestId", reqVO.getRequestId());
if (StringUtils.hasText(reqVO.getReason())) { if (StringUtils.hasText(reqVO.getReason())) {
formData.add("remark", reqVO.getReason()); payload.put("remark", reqVO.getReason());
} }
if (reqVO.getExtraParams() != null && !reqVO.getExtraParams().isEmpty()) { if (reqVO.getExtraParams() != null && !reqVO.getExtraParams().isEmpty()) {
reqVO.getExtraParams().forEach((key, value) -> { payload.putAll(reqVO.getExtraParams());
if (value != null) {
formData.add(key, String.valueOf(value));
}
});
} }
return formData; appendPayloadExtras(payload, reqVO.getFormExtras());
return payload;
} }
private void appendFormExtras(MultiValueMap<String, String> formData, Map<String, String> extras) { private void appendPayloadExtras(Map<String, Object> payload, Map<String, String> extras) {
if (extras == null || extras.isEmpty()) { if (extras == null || extras.isEmpty()) {
return; return;
} }
extras.forEach((key, value) -> { extras.forEach((key, value) -> {
if (StringUtils.hasText(key) && value != null) { if (StringUtils.hasText(key) && value != null) {
formData.add(key, value); payload.put(key, value);
} }
}); });
} }
@@ -632,6 +609,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
} }
private String executeRequest(Request request, ErrorCode errorCode) { private String executeRequest(Request request, ErrorCode errorCode) {
logCurlCommand(request);
try (Response response = okHttpClient().newCall(request).execute()) { try (Response response = okHttpClient().newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : null; String responseBody = response.body() != null ? response.body().string() : null;
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
@@ -645,6 +623,49 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
} }
} }
private void logCurlCommand(Request request) {
if (request == null || !log.isInfoEnabled()) {
return;
}
try {
StringBuilder curl = new StringBuilder("curl");
curl.append(" -X ").append(request.method());
curl.append(" '").append(request.url()).append("'");
Headers headers = request.headers();
for (int i = 0; i < headers.size(); i++) {
curl.append(" -H '")
.append(escapeSingleQuotes(headers.name(i)))
.append(": ")
.append(escapeSingleQuotes(headers.value(i)))
.append("'");
}
RequestBody body = request.body();
if (body != null) {
Buffer buffer = new Buffer();
body.writeTo(buffer);
String bodyString = buffer.readUtf8();
if (StringUtils.hasText(bodyString)) {
curl.append(" --data '")
.append(escapeSingleQuotes(bodyString))
.append("'");
}
}
log.info("[iWork] -> {}", curl);
} catch (Exception ex) {
log.warn("[iWork] failed to build curl log for {}: {}", request.url(), ex.getMessage());
}
}
private String escapeSingleQuotes(String value) {
if (value == null) {
return "";
}
return value.replace("'", "'\"'\"'");
}
private record RegistrationState(String secret, String spk, ClientKeyPair clientKeyPair) { private record RegistrationState(String secret, String spk, ClientKeyPair clientKeyPair) {
} }

View File

@@ -15,11 +15,11 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUser
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties; import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService; import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okio.Buffer;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -46,6 +46,7 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() { private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {
}; };
private static final okhttp3.MediaType JSON_MEDIA_TYPE = okhttp3.MediaType.get("application/json; charset=UTF-8");
private final IWorkProperties properties; private final IWorkProperties properties;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@@ -179,30 +180,34 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
} }
private IWorkOrgRespVO invokeParamsEndpoint(String path, Map<String, Object> params) { private IWorkOrgRespVO invokeParamsEndpoint(String path, Map<String, Object> params) {
String payload = toJson(params); Map<String, Object> payload = new HashMap<>();
return executeForm(path, "params", payload); payload.put("params", params == null ? Collections.emptyMap() : params);
return executeJson(path, payload);
} }
private IWorkOrgRespVO invokeDataEndpoint(String path, Object data) { private IWorkOrgRespVO invokeDataEndpoint(String path, Object data) {
String payload = toJson(data); Map<String, Object> payload = new HashMap<>();
return executeForm(path, "data", payload); payload.put("data", data == null ? Collections.emptyMap() : data);
return executeJson(path, payload);
} }
private IWorkOrgRespVO executeForm(String path, String fieldName, String jsonPayload) { private IWorkOrgRespVO executeJson(String path, Map<String, Object> payload) {
assertOrgConfigured(path); assertOrgConfigured(path);
FormBody.Builder formBuilder = new FormBody.Builder(StandardCharsets.UTF_8); Map<String, Object> body = new HashMap<>();
if (StringUtils.hasText(fieldName) && StringUtils.hasText(jsonPayload)) { if (payload != null && !payload.isEmpty()) {
formBuilder.add(fieldName, jsonPayload); body.putAll(payload);
} }
formBuilder.add("token", buildTokenJson()); body.put("token", buildTokenPayload());
String jsonBody = toJson(body);
RequestBody requestBody = formBuilder.build(); RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, jsonBody);
Request request = new Request.Builder() Request request = new Request.Builder()
.url(resolveUrl(path)) .url(resolveUrl(path))
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE) .header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.post(requestBody) .post(requestBody)
.build(); .build();
logCurlCommand(request);
try (Response response = okHttpClient().newCall(request).execute()) { try (Response response = okHttpClient().newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : null; String responseBody = response.body() != null ? response.body().string() : null;
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
@@ -216,7 +221,7 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
} }
} }
private String buildTokenJson() { private Map<String, String> buildTokenPayload() {
String tokenSeed = StringUtils.trimWhitespace(orgConfig().getTokenSeed()); String tokenSeed = StringUtils.trimWhitespace(orgConfig().getTokenSeed());
if (!StringUtils.hasText(tokenSeed)) { if (!StringUtils.hasText(tokenSeed)) {
throw ServiceExceptionUtil.exception(IWORK_ORG_IDENTIFIER_MISSING); throw ServiceExceptionUtil.exception(IWORK_ORG_IDENTIFIER_MISSING);
@@ -225,11 +230,10 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
String raw = tokenSeed + ts; String raw = tokenSeed + ts;
// 通过 MD5(tokenSeed + ts) 计算 key并转为大写以符合 PDF 约定 // 通过 MD5(tokenSeed + ts) 计算 key并转为大写以符合 PDF 约定
String hashed = md5Upper(raw); String hashed = md5Upper(raw);
Map<String, String> token = Map.of( return Map.of(
"key", hashed, "key", hashed,
"ts", String.valueOf(ts) "ts", String.valueOf(ts)
); );
return toJson(token);
} }
private String md5Upper(String raw) { private String md5Upper(String raw) {
@@ -368,4 +372,46 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
} }
return baseUrl + path; return baseUrl + path;
} }
private void logCurlCommand(Request request) {
if (request == null || !log.isInfoEnabled()) {
return;
}
try {
StringBuilder curl = new StringBuilder("curl");
curl.append(" -X ").append(request.method());
curl.append(" '").append(request.url()).append("'");
for (int i = 0; i < request.headers().size(); i++) {
curl.append(" -H '")
.append(escapeSingleQuotes(request.headers().name(i)))
.append(": ")
.append(escapeSingleQuotes(request.headers().value(i)))
.append("'");
}
RequestBody body = request.body();
if (body != null) {
Buffer buffer = new Buffer();
body.writeTo(buffer);
String bodyString = buffer.readUtf8();
if (StringUtils.hasText(bodyString)) {
curl.append(" --data '")
.append(escapeSingleQuotes(bodyString))
.append("'");
}
}
log.info("[iWork-Org] -> {}", curl);
} catch (Exception ex) {
log.warn("[iWork-Org] 构建 curl 日志失败 {}: {}", request.url(), ex.getMessage());
}
}
private String escapeSingleQuotes(String value) {
if (value == null) {
return "";
}
return value.replace("'", "'\"'\"'");
}
} }