|
@@ -1,6 +1,7 @@
|
|
|
package com.kingdee.eas.custom.beisen.utils;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import okhttp3.*;
|
|
|
import org.apache.log4j.Logger;
|
|
@@ -14,6 +15,7 @@ import java.util.concurrent.TimeUnit;
|
|
|
public class BeisenApiClient {
|
|
|
private static final Logger logger = Logger.getLogger(BeisenApiClient.class);
|
|
|
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
|
|
|
+ private static final int MAX_RETRIES = 3;
|
|
|
|
|
|
// 单例实例
|
|
|
private static volatile BeisenApiClient instance;
|
|
@@ -41,131 +43,148 @@ public class BeisenApiClient {
|
|
|
return instance;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
- * 调用北森API (POST)
|
|
|
- * @param apiUrl API地址
|
|
|
- * @param requestData 请求数据(JSON格式)
|
|
|
- * @return API响应内容
|
|
|
- * @throws IOException 当API调用失败时抛出
|
|
|
+ * api调用qun
|
|
|
+ * @param apiUrl
|
|
|
+ * @param requestData
|
|
|
+ * @return
|
|
|
+ * @throws IOException
|
|
|
*/
|
|
|
public JSONObject callApi(String apiUrl, JSONObject requestData) throws IOException {
|
|
|
- return callApi(apiUrl, requestData, true);
|
|
|
+ return callApiInternal(apiUrl, requestData, true, "POST");
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * 调用北森API (POST,支持重试)
|
|
|
- * @param apiUrl API地址
|
|
|
- * @param requestData 请求数据(JSON格式)
|
|
|
- * @param retryOnTokenExpired 是否在token过期时自动重试
|
|
|
- * @return API响应内容
|
|
|
- * @throws IOException 当API调用失败时抛出
|
|
|
- */
|
|
|
- public JSONObject callApi(String apiUrl, JSONObject requestData, boolean retryOnTokenExpired) throws IOException {
|
|
|
- return callApi(apiUrl, requestData, retryOnTokenExpired, "POST");
|
|
|
+
|
|
|
+ public JSONObject callApi(String apiUrl, JSONArray dataArray) throws IOException {
|
|
|
+ return callApiInternal(apiUrl, dataArray, true, "POST");
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * 调用北森API (PUT)
|
|
|
- * @param apiUrl API地址
|
|
|
- * @param requestData 请求数据(JSON格式)
|
|
|
- * @return API响应内容
|
|
|
- * @throws IOException 当API调用失败时抛出
|
|
|
- */
|
|
|
+
|
|
|
public JSONObject callPutApi(String apiUrl, JSONObject requestData) throws IOException {
|
|
|
- return callApi(apiUrl, requestData, true, "PUT");
|
|
|
+ return callApiInternal(apiUrl, requestData, true, "PUT");
|
|
|
}
|
|
|
|
|
|
+ // 核心API调用逻辑----------------------------------------------------------
|
|
|
+
|
|
|
/**
|
|
|
* 通用API调用方法
|
|
|
- * @param apiUrl API地址
|
|
|
- * @param requestData 请求数据
|
|
|
- * @param retryOnTokenExpired 是否重试token过期
|
|
|
- * @param method HTTP方法 (POST/PUT)
|
|
|
- * @return API响应
|
|
|
- * @throws IOException
|
|
|
+ * @param requestBody 支持JSONObject/JSONArray
|
|
|
*/
|
|
|
- private JSONObject callApi(String apiUrl, JSONObject requestData, boolean retryOnTokenExpired, String method)
|
|
|
+ private JSONObject callApiInternal(String apiUrl, Object requestBody, boolean retryOnTokenExpired, String method)
|
|
|
throws IOException {
|
|
|
int retryCount = 0;
|
|
|
- final int maxRetries = 3;
|
|
|
-
|
|
|
- while (retryCount <= maxRetries) {
|
|
|
+ JSONObject handleResponse = new JSONObject();
|
|
|
+ while (retryCount <= MAX_RETRIES) {
|
|
|
try {
|
|
|
String accessToken = tokenManager.getAccessToken();
|
|
|
- Request request = buildRequest(apiUrl, requestData, accessToken, method);
|
|
|
+ Request request = buildRequest(apiUrl, requestBody, accessToken, method);
|
|
|
|
|
|
try (Response response = httpClient.newCall(request).execute()) {
|
|
|
- if (!response.isSuccessful()) {
|
|
|
- String errorBody = response.body() != null ? response.body().string() : "null";
|
|
|
- logger.error("API request failed. Code: " + response.code() +
|
|
|
- ", URL: " + apiUrl + ", Method: " + method +
|
|
|
- ", Body: " + errorBody);
|
|
|
- throw new IOException("API request failed with code: " + response.code());
|
|
|
- }
|
|
|
-
|
|
|
- String responseBody = response.body().string();
|
|
|
- JSONObject jsonResponse = JSON.parseObject(responseBody);
|
|
|
-
|
|
|
- // 检查API返回的业务状态码
|
|
|
- int apiCode = jsonResponse.getIntValue("code");
|
|
|
- if (apiCode != 200) {
|
|
|
- String apiMessage = jsonResponse.getString("message");
|
|
|
- logger.error("API business error. Code: " + apiCode +
|
|
|
- ", Message: " + apiMessage + ", URL: " + apiUrl);
|
|
|
-// throw new IOException("API business error: " + apiMessage +
|
|
|
-// " (code: " + apiCode + ")");
|
|
|
- }
|
|
|
- return jsonResponse;
|
|
|
+ handleResponse = handleResponse(response, apiUrl, method);
|
|
|
+ return handleResponse;
|
|
|
}
|
|
|
} catch (IOException e) {
|
|
|
- // 如果是token过期错误且允许重试,则尝试刷新token后重试
|
|
|
- if (retryOnTokenExpired && isTokenExpiredError(e) && retryCount < maxRetries) {
|
|
|
+ if (shouldRetry(retryOnTokenExpired, retryCount, e)) {
|
|
|
retryCount++;
|
|
|
- logger.warn("Token may have expired, refreshing and retrying... (" +
|
|
|
- retryCount + "/" + maxRetries + ")");
|
|
|
- try {
|
|
|
- tokenManager.refreshToken();
|
|
|
- } catch (IOException ex) {
|
|
|
- logger.error("Failed to refresh token during retry", ex);
|
|
|
- }
|
|
|
+ handleTokenRefresh(retryCount);
|
|
|
continue;
|
|
|
}
|
|
|
- throw e;
|
|
|
}
|
|
|
}
|
|
|
- throw new IOException("API request failed after " + maxRetries + " attempts");
|
|
|
+ return handleResponse;
|
|
|
+ //不抛出,使用调用的地方存储msg
|
|
|
+// throw new IOException("API request failed after " + MAX_RETRIES + " attempts");
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 构建请求对象
|
|
|
- * @param apiUrl API地址
|
|
|
- * @param requestData 请求数据
|
|
|
- * @param accessToken 访问令牌
|
|
|
- * @param method HTTP方法
|
|
|
- * @return Request对象
|
|
|
- */
|
|
|
- private Request buildRequest(String apiUrl, JSONObject requestData, String accessToken, String method) {
|
|
|
- RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, requestData.toJSONString());
|
|
|
- Request.Builder requestBuilder = new Request.Builder()
|
|
|
- .url(apiUrl)
|
|
|
- .addHeader("Authorization", "Bearer " + accessToken)
|
|
|
- .addHeader("Content-Type", "application/json");
|
|
|
-
|
|
|
- // 根据方法设置请求类型
|
|
|
- if ("PUT".equalsIgnoreCase(method)) {
|
|
|
- requestBuilder.put(body);
|
|
|
- } else {
|
|
|
- requestBuilder.post(body); // 默认为POST
|
|
|
+ // 响应处理逻辑-------------------------------------------------------------
|
|
|
+
|
|
|
+ private JSONObject handleResponse(Response response, String apiUrl, String method) throws IOException {
|
|
|
+ if (!response.isSuccessful()) {
|
|
|
+ handleHttpError(response, apiUrl, method);
|
|
|
+ }
|
|
|
+
|
|
|
+ String responseBody = response.body().string();
|
|
|
+ JSONObject jsonResponse = parseResponse(responseBody, apiUrl);
|
|
|
+
|
|
|
+ validateBusinessStatus(jsonResponse, apiUrl);
|
|
|
+ return jsonResponse;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleHttpError(Response response, String apiUrl, String method) throws IOException {
|
|
|
+ String errorBody = response.body() != null ? response.body().string() : "null";
|
|
|
+ logger.error("API request failed. Code: " + response.code() +
|
|
|
+ ", URL: " + apiUrl + ", Method: " + method +
|
|
|
+ ", Body: " + errorBody);
|
|
|
+// throw new IOException("HTTP error: " + response.code());
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject parseResponse(String responseBody, String apiUrl) throws IOException {
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(responseBody);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("Invalid JSON response from API. URL: " + apiUrl + ", Response: " + responseBody);
|
|
|
+ throw new IOException("Failed to parse API response", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void validateBusinessStatus(JSONObject jsonResponse, String apiUrl) throws IOException {
|
|
|
+ int apiCode = jsonResponse.getIntValue("code");
|
|
|
+ if (apiCode != 200) {
|
|
|
+ String apiMessage = jsonResponse.getString("message");
|
|
|
+ logger.error("API business error. Code: " + apiCode +
|
|
|
+ ", Message: " + apiMessage + ", URL: " + apiUrl);
|
|
|
+// throw new IOException("Business error: " + apiMessage + " (code: " + apiCode + ")");
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- return requestBuilder.build();
|
|
|
+ // Token重试逻辑-----------------------------------------------------------
|
|
|
+
|
|
|
+ private boolean shouldRetry(boolean retryOnTokenExpired, int retryCount, Exception e) {
|
|
|
+ return retryOnTokenExpired &&
|
|
|
+ isTokenExpiredError(e) &&
|
|
|
+ retryCount < MAX_RETRIES;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void handleTokenRefresh(int retryCount) {
|
|
|
+ logger.warn("Token may have expired, refreshing and retrying... (" +
|
|
|
+ retryCount + "/" + MAX_RETRIES + ")");
|
|
|
+ try {
|
|
|
+ tokenManager.refreshToken();
|
|
|
+ } catch (IOException ex) {
|
|
|
+ logger.error("Failed to refresh token during retry", ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 请求构建----------------------------------------------------------------
|
|
|
+
|
|
|
+ private Request buildRequest(String apiUrl, Object requestBody, String accessToken, String method) {
|
|
|
+ String jsonString = convertToJsonString(requestBody);
|
|
|
+ RequestBody body = RequestBody.create(JSON_MEDIA_TYPE, jsonString);
|
|
|
+
|
|
|
+ return new Request.Builder()
|
|
|
+ .url(apiUrl)
|
|
|
+ .addHeader("Authorization", "Bearer " + accessToken)
|
|
|
+ .addHeader("Content-Type", "application/json")
|
|
|
+ .method(method, body)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String convertToJsonString(Object data) {
|
|
|
+ if (data instanceof JSONObject) {
|
|
|
+ return ((JSONObject) data).toJSONString();
|
|
|
+ } else if (data instanceof JSONArray) {
|
|
|
+ return ((JSONArray) data).toJSONString();
|
|
|
+ }
|
|
|
+ throw new IllegalArgumentException("Unsupported request data type: " + data.getClass());
|
|
|
}
|
|
|
|
|
|
+ // 辅助方法---------------------------------------------------------------
|
|
|
+
|
|
|
private boolean isTokenExpiredError(Exception e) {
|
|
|
- return e.getMessage() != null &&
|
|
|
- (e.getMessage().contains("401") ||
|
|
|
- e.getMessage().contains("Unauthorized") ||
|
|
|
- e.getMessage().contains("token"));
|
|
|
+ String msg = e.getMessage();
|
|
|
+ return msg != null && (msg.contains("401") ||
|
|
|
+ msg.contains("Unauthorized") ||
|
|
|
+ msg.contains("token"));
|
|
|
}
|
|
|
|
|
|
private OkHttpClient buildHttpClient() {
|