diff --git a/ccdi-lsfx/pom.xml b/ccdi-lsfx/pom.xml
index cecec56..bf3f3ed 100644
--- a/ccdi-lsfx/pom.xml
+++ b/ccdi-lsfx/pom.xml
@@ -26,6 +26,12 @@
spring-boot-starter-web
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
org.projectlombok
diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java
index 772d5ce..ba4627a 100644
--- a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java
+++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java
@@ -3,8 +3,10 @@ package com.ruoyi.lsfx.client;
import com.ruoyi.lsfx.constants.LsfxConstants;
import com.ruoyi.lsfx.domain.request.*;
import com.ruoyi.lsfx.domain.response.*;
+import com.ruoyi.lsfx.exception.LsfxApiException;
import com.ruoyi.lsfx.util.HttpUtil;
import com.ruoyi.lsfx.util.MD5Util;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -15,6 +17,7 @@ import java.util.Map;
/**
* 流水分析平台客户端
*/
+@Slf4j
@Component
public class LsfxAnalysisClient {
@@ -52,67 +55,153 @@ public class LsfxAnalysisClient {
* 获取Token
*/
public GetTokenResponse getToken(GetTokenRequest request) {
- String secretCode = MD5Util.generateSecretCode(
- request.getProjectNo(),
- request.getEntityName(),
- appSecret
- );
- request.setAppSecretCode(secretCode);
- request.setAppId(appId);
+ log.info("【流水分析】获取Token请求: projectNo={}, entityName={}", request.getProjectNo(), request.getEntityName());
+ long startTime = System.currentTimeMillis();
- if (request.getAnalysisType() == null) {
- request.setAnalysisType(LsfxConstants.ANALYSIS_TYPE);
- }
- if (request.getRole() == null) {
- request.setRole(LsfxConstants.DEFAULT_ROLE);
- }
+ try {
+ String secretCode = MD5Util.generateSecretCode(
+ request.getProjectNo(),
+ request.getEntityName(),
+ appSecret
+ );
+ request.setAppSecretCode(secretCode);
+ request.setAppId(appId);
- String url = baseUrl + getTokenEndpoint;
- return httpUtil.postJson(url, request, null, GetTokenResponse.class);
+ if (request.getAnalysisType() == null) {
+ request.setAnalysisType(LsfxConstants.ANALYSIS_TYPE);
+ }
+ if (request.getRole() == null) {
+ request.setRole(LsfxConstants.DEFAULT_ROLE);
+ }
+
+ String url = baseUrl + getTokenEndpoint;
+ GetTokenResponse response = httpUtil.postJson(url, request, null, GetTokenResponse.class);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (response != null && response.getData() != null) {
+ log.info("【流水分析】获取Token成功: projectId={}, 耗时={}ms",
+ response.getData().getProjectId(), elapsed);
+ } else {
+ log.warn("【流水分析】获取Token响应异常: 耗时={}ms", elapsed);
+ }
+
+ return response;
+ } catch (LsfxApiException e) {
+ log.error("【流水分析】获取Token失败: projectNo={}, error={}", request.getProjectNo(), e.getMessage(), e);
+ throw e;
+ } catch (Exception e) {
+ log.error("【流水分析】获取Token未知异常: projectNo={}", request.getProjectNo(), e);
+ throw new LsfxApiException("获取Token失败: " + e.getMessage(), e);
+ }
}
/**
* 上传文件
*/
public UploadFileResponse uploadFile(Integer groupId, org.springframework.core.io.Resource file) {
- String url = baseUrl + uploadFileEndpoint;
+ log.info("【流水分析】上传文件请求: groupId={}, fileName={}", groupId, file.getFilename());
+ long startTime = System.currentTimeMillis();
- Map params = new HashMap<>();
- params.put("groupId", groupId);
- params.put("files", file);
+ try {
+ String url = baseUrl + uploadFileEndpoint;
- Map headers = new HashMap<>();
- headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+ Map params = new HashMap<>();
+ params.put("groupId", groupId);
+ params.put("files", file);
- return httpUtil.uploadFile(url, params, headers, UploadFileResponse.class);
+ Map headers = new HashMap<>();
+ headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+
+ UploadFileResponse response = httpUtil.uploadFile(url, params, headers, UploadFileResponse.class);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (response != null && response.getData() != null) {
+ log.info("【流水分析】上传文件成功: uploadStatus={}, 耗时={}ms",
+ response.getData().getUploadStatus(), elapsed);
+ } else {
+ log.warn("【流水分析】上传文件响应异常: 耗时={}ms", elapsed);
+ }
+
+ return response;
+ } catch (LsfxApiException e) {
+ log.error("【流水分析】上传文件失败: groupId={}, error={}", groupId, e.getMessage(), e);
+ throw e;
+ } catch (Exception e) {
+ log.error("【流水分析】上传文件未知异常: groupId={}", groupId, e);
+ throw new LsfxApiException("上传文件失败: " + e.getMessage(), e);
+ }
}
/**
* 拉取行内流水
*/
public FetchInnerFlowResponse fetchInnerFlow(FetchInnerFlowRequest request) {
- String url = baseUrl + fetchInnerFlowEndpoint;
+ log.info("【流水分析】拉取行内流水请求: groupId={}, customerNo={}", request.getGroupId(), request.getCustomerNo());
+ long startTime = System.currentTimeMillis();
- Map headers = new HashMap<>();
- headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+ try {
+ String url = baseUrl + fetchInnerFlowEndpoint;
- return httpUtil.postJson(url, request, headers, FetchInnerFlowResponse.class);
+ Map headers = new HashMap<>();
+ headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+
+ FetchInnerFlowResponse response = httpUtil.postJson(url, request, headers, FetchInnerFlowResponse.class);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (response != null && response.getData() != null) {
+ log.info("【流水分析】拉取行内流水完成: code={}, message={}, 耗时={}ms",
+ response.getData().getCode(), response.getData().getMessage(), elapsed);
+ } else {
+ log.warn("【流水分析】拉取行内流水响应异常: 耗时={}ms", elapsed);
+ }
+
+ return response;
+ } catch (LsfxApiException e) {
+ log.error("【流水分析】拉取行内流水失败: groupId={}, error={}", request.getGroupId(), e.getMessage(), e);
+ throw e;
+ } catch (Exception e) {
+ log.error("【流水分析】拉取行内流水未知异常: groupId={}", request.getGroupId(), e);
+ throw new LsfxApiException("拉取行内流水失败: " + e.getMessage(), e);
+ }
}
/**
* 检查文件解析状态
*/
public CheckParseStatusResponse checkParseStatus(Integer groupId, String inprogressList) {
- String url = baseUrl + checkParseStatusEndpoint;
+ log.info("【流水分析】检查文件解析状态: groupId={}, inprogressList={}", groupId, inprogressList);
+ long startTime = System.currentTimeMillis();
- Map params = new HashMap<>();
- params.put("groupId", groupId);
- params.put("inprogressList", inprogressList);
+ try {
+ String url = baseUrl + checkParseStatusEndpoint;
- Map headers = new HashMap<>();
- headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+ Map params = new HashMap<>();
+ params.put("groupId", groupId);
+ params.put("inprogressList", inprogressList);
- return httpUtil.postJson(url, params, headers, CheckParseStatusResponse.class);
+ Map headers = new HashMap<>();
+ headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+
+ CheckParseStatusResponse response = httpUtil.postJson(url, params, headers, CheckParseStatusResponse.class);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (response != null && response.getData() != null) {
+ log.info("【流水分析】检查解析状态完成: parsing={}, pendingList.size={}, 耗时={}ms",
+ response.getData().getParsing(),
+ response.getData().getPendingList() != null ? response.getData().getPendingList().size() : 0,
+ elapsed);
+ } else {
+ log.warn("【流水分析】检查解析状态响应异常: 耗时={}ms", elapsed);
+ }
+
+ return response;
+ } catch (LsfxApiException e) {
+ log.error("【流水分析】检查解析状态失败: groupId={}, error={}", groupId, e.getMessage(), e);
+ throw e;
+ } catch (Exception e) {
+ log.error("【流水分析】检查解析状态未知异常: groupId={}", groupId, e);
+ throw new LsfxApiException("检查解析状态失败: " + e.getMessage(), e);
+ }
}
/**
@@ -123,11 +212,35 @@ public class LsfxAnalysisClient {
* @return 流水明细列表
*/
public GetBankStatementResponse getBankStatement(GetBankStatementRequest request) {
- String url = baseUrl + getBankStatementEndpoint;
+ log.info("【流水分析】获取银行流水请求: groupId={}, logId={}, pageNow={}, pageSize={}",
+ request.getGroupId(), request.getLogId(), request.getPageNow(), request.getPageSize());
+ long startTime = System.currentTimeMillis();
- Map headers = new HashMap<>();
- headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+ try {
+ String url = baseUrl + getBankStatementEndpoint;
- return httpUtil.postJson(url, request, headers, GetBankStatementResponse.class);
+ Map headers = new HashMap<>();
+ headers.put(LsfxConstants.HEADER_CLIENT_ID, clientId);
+
+ GetBankStatementResponse response = httpUtil.postJson(url, request, headers, GetBankStatementResponse.class);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ if (response != null && response.getData() != null) {
+ log.info("【流水分析】获取银行流水成功: totalCount={}, 耗时={}ms",
+ response.getData().getTotalCount(), elapsed);
+ } else {
+ log.warn("【流水分析】获取银行流水响应异常: 耗时={}ms", elapsed);
+ }
+
+ return response;
+ } catch (LsfxApiException e) {
+ log.error("【流水分析】获取银行流水失败: groupId={}, logId={}, error={}",
+ request.getGroupId(), request.getLogId(), e.getMessage(), e);
+ throw e;
+ } catch (Exception e) {
+ log.error("【流水分析】获取银行流水未知异常: groupId={}, logId={}",
+ request.getGroupId(), request.getLogId(), e);
+ throw new LsfxApiException("获取银行流水失败: " + e.getMessage(), e);
+ }
}
}
diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java
index cc3d921..d02f7ae 100644
--- a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java
+++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/RestTemplateConfig.java
@@ -1,13 +1,17 @@
package com.ruoyi.lsfx.config;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
- * RestTemplate配置
+ * RestTemplate配置(使用连接池优化性能)
*/
@Configuration
public class RestTemplateConfig {
@@ -18,11 +22,29 @@ public class RestTemplateConfig {
@Value("${lsfx.api.read-timeout:60000}")
private int readTimeout;
+ @Value("${lsfx.api.pool.max-total:100}")
+ private int maxTotal;
+
+ @Value("${lsfx.api.pool.default-max-per-route:20}")
+ private int defaultMaxPerRoute;
+
@Bean
public RestTemplate restTemplate() {
- SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+ // 创建连接池管理器
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+ connectionManager.setMaxTotal(maxTotal); // 最大连接数
+ connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); // 每个路由的最大连接数
+
+ // 创建HttpClient并设置连接池
+ HttpClient httpClient = HttpClients.custom()
+ .setConnectionManager(connectionManager)
+ .build();
+
+ // 创建HttpComponentsClientHttpRequestFactory
+ HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(connectionTimeout);
- factory.setReadTimeout(readTimeout);
+ factory.setConnectionRequestTimeout(connectionTimeout);
+
return new RestTemplate(factory);
}
}
diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java
index 0e67334..cc20ca7 100644
--- a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java
+++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/LsfxTestController.java
@@ -1,6 +1,7 @@
package com.ruoyi.lsfx.controller;
import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.*;
import com.ruoyi.lsfx.domain.response.*;
@@ -26,6 +27,26 @@ public class LsfxTestController {
@Operation(summary = "获取Token", description = "创建项目并获取访问Token")
@PostMapping("/getToken")
public AjaxResult getToken(@RequestBody GetTokenRequest request) {
+ // 参数校验
+ if (StringUtils.isBlank(request.getProjectNo())) {
+ return AjaxResult.error("参数不完整:projectNo为必填");
+ }
+ if (StringUtils.isBlank(request.getEntityName())) {
+ return AjaxResult.error("参数不完整:entityName为必填");
+ }
+ if (StringUtils.isBlank(request.getUserId())) {
+ return AjaxResult.error("参数不完整:userId为必填");
+ }
+ if (StringUtils.isBlank(request.getUserName())) {
+ return AjaxResult.error("参数不完整:userName为必填");
+ }
+ if (StringUtils.isBlank(request.getOrgCode())) {
+ return AjaxResult.error("参数不完整:orgCode为必填");
+ }
+ if (StringUtils.isBlank(request.getDepartmentCode())) {
+ return AjaxResult.error("参数不完整:departmentCode为必填");
+ }
+
GetTokenResponse response = lsfxAnalysisClient.getToken(request);
return AjaxResult.success(response);
}
@@ -36,6 +57,17 @@ public class LsfxTestController {
@Parameter(description = "项目ID") @RequestParam Integer groupId,
@Parameter(description = "流水文件") @RequestParam("file") MultipartFile file
) {
+ // 参数校验
+ if (groupId == null || groupId <= 0) {
+ return AjaxResult.error("参数不完整:groupId为必填且大于0");
+ }
+ if (file == null || file.isEmpty()) {
+ return AjaxResult.error("参数不完整:文件不能为空");
+ }
+ if (file.getSize() > 10 * 1024 * 1024) { // 10MB限制
+ return AjaxResult.error("文件大小超过限制:最大10MB");
+ }
+
org.springframework.core.io.Resource fileResource = file.getResource();
UploadFileResponse response = lsfxAnalysisClient.uploadFile(groupId, fileResource);
return AjaxResult.success(response);
@@ -44,6 +76,26 @@ public class LsfxTestController {
@Operation(summary = "拉取行内流水", description = "从数仓拉取行内流水数据")
@PostMapping("/fetchInnerFlow")
public AjaxResult fetchInnerFlow(@RequestBody FetchInnerFlowRequest request) {
+ // 参数校验
+ if (request.getGroupId() == null || request.getGroupId() <= 0) {
+ return AjaxResult.error("参数不完整:groupId为必填且大于0");
+ }
+ if (StringUtils.isEmpty(request.getCustomerNo())) {
+ return AjaxResult.error("参数不完整:customerNo为必填");
+ }
+ if (request.getRequestDateId() == null) {
+ return AjaxResult.error("参数不完整:requestDateId为必填");
+ }
+ if (request.getDataStartDateId() == null) {
+ return AjaxResult.error("参数不完整:dataStartDateId为必填");
+ }
+ if (request.getDataEndDateId() == null) {
+ return AjaxResult.error("参数不完整:dataEndDateId为必填");
+ }
+ if (request.getDataStartDateId() > request.getDataEndDateId()) {
+ return AjaxResult.error("参数错误:开始日期不能大于结束日期");
+ }
+
FetchInnerFlowResponse response = lsfxAnalysisClient.fetchInnerFlow(request);
return AjaxResult.success(response);
}
@@ -54,6 +106,14 @@ public class LsfxTestController {
@Parameter(description = "项目ID") @RequestParam Integer groupId,
@Parameter(description = "文件ID列表") @RequestParam String inprogressList
) {
+ // 参数校验
+ if (groupId == null || groupId <= 0) {
+ return AjaxResult.error("参数不完整:groupId为必填且大于0");
+ }
+ if (StringUtils.isEmpty(inprogressList)) {
+ return AjaxResult.error("参数不完整:inprogressList为必填");
+ }
+
CheckParseStatusResponse response = lsfxAnalysisClient.checkParseStatus(groupId, inprogressList);
return AjaxResult.success(response);
}
diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java
index 101068a..56baeb9 100644
--- a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java
+++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java
@@ -1,9 +1,11 @@
package com.ruoyi.lsfx.util;
+import com.ruoyi.lsfx.exception.LsfxApiException;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import jakarta.annotation.Resource;
@@ -26,13 +28,27 @@ public class HttpUtil {
* @return 响应对象
*/
public T get(String url, Map headers, Class responseType) {
- HttpHeaders httpHeaders = createHeaders(headers);
- HttpEntity requestEntity = new HttpEntity<>(httpHeaders);
+ try {
+ HttpHeaders httpHeaders = createHeaders(headers);
+ HttpEntity requestEntity = new HttpEntity<>(httpHeaders);
- ResponseEntity response = restTemplate.exchange(
- url, HttpMethod.GET, requestEntity, responseType
- );
- return response.getBody();
+ ResponseEntity response = restTemplate.exchange(
+ url, HttpMethod.GET, requestEntity, responseType
+ );
+
+ if (!response.getStatusCode().is2xxSuccessful()) {
+ throw new LsfxApiException("API调用失败,HTTP状态码: " + response.getStatusCode());
+ }
+
+ T body = response.getBody();
+ if (body == null) {
+ throw new LsfxApiException("API返回数据为空");
+ }
+
+ return body;
+ } catch (RestClientException e) {
+ throw new LsfxApiException("网络请求失败: " + e.getMessage(), e);
+ }
}
/**
@@ -44,13 +60,27 @@ public class HttpUtil {
* @return 响应对象
*/
public T postJson(String url, Object request, Map headers, Class responseType) {
- HttpHeaders httpHeaders = createHeaders(headers);
- httpHeaders.setContentType(MediaType.APPLICATION_JSON);
+ try {
+ HttpHeaders httpHeaders = createHeaders(headers);
+ httpHeaders.setContentType(MediaType.APPLICATION_JSON);
- HttpEntity