Refactor credit parse to use remote HTML paths
This commit is contained in:
@@ -14,8 +14,10 @@ import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||
import com.ruoyi.info.collection.service.ICcdiCreditInfoService;
|
||||
import com.ruoyi.info.collection.service.support.CreditHtmlStorageService;
|
||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -23,8 +25,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -39,6 +39,9 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||
@Resource
|
||||
private CreditParseClient creditParseClient;
|
||||
|
||||
@Resource
|
||||
private CreditHtmlStorageService creditHtmlStorageService;
|
||||
|
||||
@Resource
|
||||
private CreditInfoPayloadAssembler assembler;
|
||||
|
||||
@@ -141,37 +144,20 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSingleFile(MultipartFile multipartFile, String userName) throws IOException {
|
||||
File tempFile = createTempFile(multipartFile);
|
||||
try {
|
||||
CreditParseResponse response = creditParseClient.parse("LXCUSTALL", "PERSON", tempFile);
|
||||
CreditParsePayload payload = requireResponse(response).getPayload();
|
||||
Map<String, Object> header = requireHeader(payload);
|
||||
String personId = stringValue(header.get("query_cert_no"));
|
||||
String personName = stringValue(header.get("query_cust_name"));
|
||||
LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
|
||||
ensurePersonIdPresent(personId);
|
||||
ensureLatestQueryDate(personId, queryDate);
|
||||
private void handleSingleFile(MultipartFile multipartFile, String userName) throws Exception {
|
||||
CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
|
||||
CreditParseInvokeResponse response = creditParseClient.parse(storedHtml.remotePath());
|
||||
CreditParsePayload payload = requireResponse(response).getPayload();
|
||||
Map<String, Object> header = requireHeader(payload);
|
||||
String personId = stringValue(header.get("query_cert_no"));
|
||||
String personName = stringValue(header.get("query_cust_name"));
|
||||
LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
|
||||
ensurePersonIdPresent(personId);
|
||||
ensureLatestQueryDate(personId, queryDate);
|
||||
|
||||
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
|
||||
CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
|
||||
replaceEmployeeCredit(personId, debts, negative, userName);
|
||||
} finally {
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File createTempFile(MultipartFile multipartFile) throws IOException {
|
||||
String originalFilename = multipartFile.getOriginalFilename();
|
||||
String suffix = ".html";
|
||||
if (originalFilename != null && originalFilename.contains(".")) {
|
||||
suffix = originalFilename.substring(originalFilename.lastIndexOf('.'));
|
||||
}
|
||||
File tempFile = File.createTempFile("credit-info-", suffix);
|
||||
multipartFile.transferTo(tempFile);
|
||||
return tempFile;
|
||||
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
|
||||
CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
|
||||
replaceEmployeeCredit(personId, debts, negative, userName);
|
||||
}
|
||||
|
||||
private void validateHtmlFile(MultipartFile file) {
|
||||
@@ -185,14 +171,21 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||
}
|
||||
}
|
||||
|
||||
private CreditParseResponse requireResponse(CreditParseResponse response) {
|
||||
if (response == null || response.getPayload() == null) {
|
||||
private CreditParseResponse requireResponse(CreditParseInvokeResponse response) {
|
||||
if (response == null || response.getData() == null || response.getData().getMappingOutputFields() == null) {
|
||||
throw new RuntimeException("征信解析结果为空");
|
||||
}
|
||||
if (!"0".equals(response.getStatusCode())) {
|
||||
throw new RuntimeException(stringValue(response.getMessage(), "征信解析失败"));
|
||||
CreditParseResponse mappingOutputFields = response.getData().getMappingOutputFields();
|
||||
if (!Boolean.TRUE.equals(response.getSuccess()) || response.getCode() == null || response.getCode() != 1000) {
|
||||
throw new RuntimeException(stringValue(mappingOutputFields.getMessage(), "征信解析平台调用失败"));
|
||||
}
|
||||
return response;
|
||||
if (!"0".equals(mappingOutputFields.getStatusCode())) {
|
||||
throw new RuntimeException(stringValue(mappingOutputFields.getMessage(), "征信解析失败"));
|
||||
}
|
||||
if (mappingOutputFields.getPayload() == null) {
|
||||
throw new RuntimeException("征信解析结果为空");
|
||||
}
|
||||
return mappingOutputFields;
|
||||
}
|
||||
|
||||
private Map<String, Object> requireHeader(CreditParsePayload payload) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ruoyi.info.collection.service.support;
|
||||
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 征信 HTML 服务器落盘与远程访问地址生成。
|
||||
*/
|
||||
@Component
|
||||
public class CreditHtmlStorageService {
|
||||
|
||||
private static final String CREDIT_HTML_DIR = "credit-html";
|
||||
private static final String[] HTML_EXTENSIONS = {"html", "htm"};
|
||||
|
||||
@Value("${credit-parse.api.file-public-base-url}")
|
||||
private String filePublicBaseUrl;
|
||||
|
||||
public StoredCreditHtml save(MultipartFile file) throws Exception {
|
||||
String profilePath = FileUploadUtils.upload(getCreditHtmlBaseDir(), file, HTML_EXTENSIONS);
|
||||
return new StoredCreditHtml(profilePath, buildRemotePath(profilePath));
|
||||
}
|
||||
|
||||
private String getCreditHtmlBaseDir() {
|
||||
return RuoYiConfig.getProfile() + File.separator + CREDIT_HTML_DIR;
|
||||
}
|
||||
|
||||
private String buildRemotePath(String profilePath) {
|
||||
if (StringUtils.isBlank(filePublicBaseUrl)) {
|
||||
throw new IllegalStateException("征信HTML公开访问地址未配置");
|
||||
}
|
||||
String normalizedBaseUrl = StringUtils.stripEnd(filePublicBaseUrl.trim(), "/");
|
||||
String normalizedProfilePath = profilePath.startsWith("/") ? profilePath : "/" + profilePath;
|
||||
return normalizedBaseUrl + normalizedProfilePath;
|
||||
}
|
||||
|
||||
public record StoredCreditHtml(String profilePath, String remotePath) {
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ruoyi.info.collection.service;
|
||||
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
|
||||
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
|
||||
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
|
||||
@@ -7,26 +8,33 @@ import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
|
||||
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
|
||||
import com.ruoyi.info.collection.service.impl.CcdiCreditInfoServiceImpl;
|
||||
import com.ruoyi.info.collection.service.support.CreditHtmlStorageService;
|
||||
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeData;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -41,6 +49,9 @@ class CcdiCreditInfoServiceImplTest {
|
||||
@Mock
|
||||
private CreditParseClient creditParseClient;
|
||||
|
||||
@Mock
|
||||
private CreditHtmlStorageService creditHtmlStorageService;
|
||||
|
||||
@Mock
|
||||
private CreditInfoPayloadAssembler assembler;
|
||||
|
||||
@@ -54,11 +65,15 @@ class CcdiCreditInfoServiceImplTest {
|
||||
private CcdiCreditInfoQueryMapper queryMapper;
|
||||
|
||||
@Test
|
||||
void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() {
|
||||
void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() throws Exception {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||
when(creditHtmlStorageService.save(any()))
|
||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
||||
"/profile/credit-html/2026/05/12/family_1.html",
|
||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html"));
|
||||
when(creditParseClient.parse(anyString()))
|
||||
.thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
|
||||
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||
.thenReturn(List.of(buildDebt("330101199202020022")));
|
||||
@@ -69,15 +84,20 @@ class CcdiCreditInfoServiceImplTest {
|
||||
|
||||
assertEquals(1, result.getSuccessCount());
|
||||
assertEquals(0, result.getFailureCount());
|
||||
verify(creditParseClient).parse("http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html");
|
||||
verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
|
||||
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uploadHtmlFiles_shouldRejectOlderReportDate() {
|
||||
void uploadHtmlFiles_shouldRejectOlderReportDate() throws Exception {
|
||||
MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
|
||||
when(creditHtmlStorageService.save(any()))
|
||||
.thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
|
||||
"/profile/credit-html/2026/05/12/a_1.html",
|
||||
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
|
||||
when(creditParseClient.parse(anyString()))
|
||||
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||
when(queryMapper.selectLatestQueryDate("330101199001010011"))
|
||||
.thenReturn(LocalDate.parse("2026-03-05"));
|
||||
@@ -88,7 +108,29 @@ class CcdiCreditInfoServiceImplTest {
|
||||
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
||||
}
|
||||
|
||||
private CreditParseResponse successResponse(String personId, String personName, String reportTime) {
|
||||
@Test
|
||||
void creditHtmlStorage_shouldStoreHtmlUnderProfileAndBuildRemotePath(@TempDir Path profileDir) throws Exception {
|
||||
String oldProfile = RuoYiConfig.getProfile();
|
||||
new RuoYiConfig().setProfile(profileDir.toString());
|
||||
try {
|
||||
CreditHtmlStorageService storageService = new CreditHtmlStorageService();
|
||||
ReflectionTestUtils.setField(storageService, "filePublicBaseUrl", "http://127.0.0.1:62318/");
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"files", "credit.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
CreditHtmlStorageService.StoredCreditHtml storedHtml = storageService.save(file);
|
||||
|
||||
assertTrue(storedHtml.profilePath().startsWith("/profile/credit-html/"));
|
||||
assertTrue(storedHtml.profilePath().endsWith(".html"));
|
||||
assertEquals("http://127.0.0.1:62318" + storedHtml.profilePath(), storedHtml.remotePath());
|
||||
Path savedFile = profileDir.resolve(storedHtml.profilePath().substring("/profile/".length()));
|
||||
assertTrue(Files.exists(savedFile));
|
||||
} finally {
|
||||
new RuoYiConfig().setProfile(oldProfile);
|
||||
}
|
||||
}
|
||||
|
||||
private CreditParseInvokeResponse successResponse(String personId, String personName, String reportTime) {
|
||||
CreditParsePayload payload = new CreditParsePayload();
|
||||
Map<String, Object> header = new HashMap<>();
|
||||
header.put("query_cert_no", personId);
|
||||
@@ -99,9 +141,18 @@ class CcdiCreditInfoServiceImplTest {
|
||||
payload.setLxPublictype(Map.of("civil_cnt", 1));
|
||||
|
||||
CreditParseResponse response = new CreditParseResponse();
|
||||
response.setMessage("成功");
|
||||
response.setStatusCode("0");
|
||||
response.setPayload(payload);
|
||||
return response;
|
||||
|
||||
CreditParseInvokeData data = new CreditParseInvokeData();
|
||||
data.setMappingOutputFields(response);
|
||||
|
||||
CreditParseInvokeResponse invokeResponse = new CreditParseInvokeResponse();
|
||||
invokeResponse.setSuccess(true);
|
||||
invokeResponse.setCode(1000);
|
||||
invokeResponse.setData(data);
|
||||
return invokeResponse;
|
||||
}
|
||||
|
||||
private CcdiDebtsInfo buildDebt(String personId) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ruoyi.lsfx.client;
|
||||
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||
import com.ruoyi.lsfx.util.HttpUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -8,7 +10,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -22,26 +23,51 @@ public class CreditParseClient {
|
||||
@Value("${credit-parse.api.url}")
|
||||
private String creditParseUrl;
|
||||
|
||||
public CreditParseResponse parse(String model, String hType, File file) {
|
||||
@Value("${credit-parse.api.org-code:902000}")
|
||||
private String orgCode;
|
||||
|
||||
@Value("${credit-parse.api.run-type:1}")
|
||||
private String runType;
|
||||
|
||||
@Value("${credit-parse.api.model:LXCUSTALL}")
|
||||
private String defaultModel;
|
||||
|
||||
public CreditParseInvokeResponse parse(String remotePath) {
|
||||
return parse(defaultModel, remotePath);
|
||||
}
|
||||
|
||||
public CreditParseInvokeResponse parse(String model, String remotePath) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("【征信解析】开始调用: fileName={}, model={}, hType={}", file.getName(), model, hType);
|
||||
String actualModel = StringUtils.isBlank(model) ? defaultModel : model;
|
||||
log.info("【征信解析】开始调用: model={}, remotePath={}", actualModel, remotePath);
|
||||
|
||||
try {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("model", model);
|
||||
params.put("hType", hType);
|
||||
params.put("file", file);
|
||||
params.put("serialNum", buildSerialNum());
|
||||
params.put("orgCode", orgCode);
|
||||
params.put("runType", runType);
|
||||
params.put("remotePath", remotePath);
|
||||
params.put("model", actualModel);
|
||||
|
||||
CreditParseResponse response = httpUtil.uploadFile(creditParseUrl, params, null, CreditParseResponse.class);
|
||||
CreditParseInvokeResponse response = httpUtil.postUrlEncodedForm(
|
||||
creditParseUrl, params, null, CreditParseInvokeResponse.class);
|
||||
|
||||
long elapsed = System.currentTimeMillis() - startTime;
|
||||
log.info("【征信解析】调用完成: statusCode={}, cost={}ms",
|
||||
response != null ? response.getStatusCode() : null, elapsed);
|
||||
log.info("【征信解析】调用完成: success={}, code={}, businessStatusCode={}, cost={}ms",
|
||||
response != null ? response.getSuccess() : null,
|
||||
response != null ? response.getCode() : null,
|
||||
response != null && response.getData() != null && response.getData().getMappingOutputFields() != null
|
||||
? response.getData().getMappingOutputFields().getStatusCode() : null,
|
||||
elapsed);
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
log.error("【征信解析】调用失败: fileName={}, model={}, hType={}, error={}",
|
||||
file.getName(), model, hType, e.getMessage(), e);
|
||||
log.error("【征信解析】调用失败: model={}, remotePath={}, error={}",
|
||||
actualModel, remotePath, e.getMessage(), e);
|
||||
throw new LsfxApiException("征信解析调用失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildSerialNum() {
|
||||
return "CCDI_CREDIT_" + System.currentTimeMillis() + "_" + IdUtils.fastSimpleUUID();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -14,13 +14,6 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
@Tag(name = "征信解析接口测试", description = "用于测试征信解析接口")
|
||||
@Anonymous
|
||||
@@ -29,56 +22,27 @@ import java.nio.file.StandardCopyOption;
|
||||
public class CreditParseController {
|
||||
|
||||
private static final String DEFAULT_MODEL = "LXCUSTALL";
|
||||
private static final String DEFAULT_HTYPE = "PERSON";
|
||||
|
||||
@Resource
|
||||
private CreditParseClient creditParseClient;
|
||||
|
||||
@Operation(summary = "解析征信HTML", description = "上传征信HTML文件并调用外部解析服务")
|
||||
@Operation(summary = "解析征信HTML", description = "传入征信HTML远程地址并调用外部解析服务")
|
||||
@PostMapping("/parse")
|
||||
public AjaxResult parse(@Parameter(description = "征信HTML文件") @RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "解析模型,默认LXCUSTALL") @RequestParam(required = false) String model,
|
||||
@Parameter(description = "主体类型,默认PERSON") @RequestParam(required = false) String hType) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return AjaxResult.error("征信HTML文件不能为空");
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (StringUtils.isBlank(originalFilename)) {
|
||||
return AjaxResult.error("文件名不能为空");
|
||||
}
|
||||
|
||||
String lowerCaseName = originalFilename.toLowerCase();
|
||||
if (!lowerCaseName.endsWith(".html") && !lowerCaseName.endsWith(".htm")) {
|
||||
return AjaxResult.error("仅支持 HTML 格式文件");
|
||||
public AjaxResult parse(@Parameter(description = "征信HTML远程访问地址") @RequestParam("remotePath") String remotePath,
|
||||
@Parameter(description = "模型编码,默认LXCUSTALL") @RequestParam(required = false) String model) {
|
||||
if (StringUtils.isBlank(remotePath)) {
|
||||
return AjaxResult.error("征信HTML远程地址不能为空");
|
||||
}
|
||||
|
||||
String actualModel = StringUtils.isBlank(model) ? DEFAULT_MODEL : model;
|
||||
String actualHType = StringUtils.isBlank(hType) ? DEFAULT_HTYPE : hType;
|
||||
|
||||
Path tempFile = null;
|
||||
try {
|
||||
String suffix = lowerCaseName.endsWith(".htm") ? ".htm" : ".html";
|
||||
tempFile = Files.createTempFile("credit_parse_", suffix);
|
||||
Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
File convertedFile = tempFile.toFile();
|
||||
CreditParseResponse response = creditParseClient.parse(actualModel, actualHType, convertedFile);
|
||||
CreditParseInvokeResponse response = creditParseClient.parse(actualModel, remotePath);
|
||||
return AjaxResult.success(response);
|
||||
} catch (LsfxApiException e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
return AjaxResult.error("文件转换失败:" + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("征信解析失败:" + e.getMessage());
|
||||
} finally {
|
||||
if (tempFile != null) {
|
||||
try {
|
||||
Files.deleteIfExists(tempFile);
|
||||
} catch (IOException ignored) {
|
||||
// 忽略临时文件删除失败,避免影响主流程返回
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ruoyi.lsfx.domain.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreditParseInvokeData {
|
||||
|
||||
private CreditParseResponse mappingOutputFields;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ruoyi.lsfx.domain.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CreditParseInvokeResponse {
|
||||
|
||||
private Boolean success;
|
||||
|
||||
private Integer code;
|
||||
|
||||
private CreditParseInvokeData data;
|
||||
}
|
||||
@@ -206,6 +206,47 @@ public class HttpUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送POST请求(application/x-www-form-urlencoded格式,带请求头)
|
||||
* @param url 请求URL
|
||||
* @param params 表单参数
|
||||
* @param headers 请求头
|
||||
* @param responseType 响应类型
|
||||
* @return 响应对象
|
||||
*/
|
||||
public <T> T postUrlEncodedForm(String url, Map<String, Object> params, Map<String, String> headers, Class<T> responseType) {
|
||||
try {
|
||||
HttpHeaders httpHeaders = createHeaders(headers);
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
||||
if (params != null) {
|
||||
params.forEach((key, value) -> {
|
||||
if (value != null) {
|
||||
body.add(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, httpHeaders);
|
||||
|
||||
ResponseEntity<T> response = restTemplate.postForEntity(url, requestEntity, responseType);
|
||||
|
||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
||||
throw new LsfxApiException("API调用失败,HTTP状态码: " + response.getStatusCode());
|
||||
}
|
||||
|
||||
T responseBody = response.getBody();
|
||||
if (responseBody == null) {
|
||||
throw new LsfxApiException("API返回数据为空");
|
||||
}
|
||||
|
||||
return responseBody;
|
||||
} catch (RestClientException e) {
|
||||
throw new LsfxApiException("网络请求失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件(Multipart格式)
|
||||
* @param url 请求URL
|
||||
|
||||
@@ -2,23 +2,28 @@ package com.ruoyi.lsfx.controller;
|
||||
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.lsfx.client.CreditParseClient;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
||||
import com.ruoyi.lsfx.exception.LsfxApiException;
|
||||
import com.ruoyi.lsfx.util.HttpUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@@ -31,31 +36,21 @@ class CreditParseControllerTest {
|
||||
private CreditParseController controller;
|
||||
|
||||
@Test
|
||||
void parse_shouldRejectEmptyFile() {
|
||||
AjaxResult result = controller.parse(null, null, null);
|
||||
void parse_shouldRejectBlankRemotePath() {
|
||||
AjaxResult result = controller.parse(null, null);
|
||||
assertEquals(500, result.get("code"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_shouldRejectNonHtmlFile() {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "credit.pdf", "application/pdf", "x".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
AjaxResult result = controller.parse(file, null, null);
|
||||
assertEquals(500, result.get("code"));
|
||||
}
|
||||
void shouldUseDefaultModelWhenMissing() {
|
||||
CreditParseInvokeResponse response = new CreditParseInvokeResponse();
|
||||
response.setSuccess(true);
|
||||
response.setCode(1000);
|
||||
|
||||
@Test
|
||||
void shouldUseDefaultModelAndTypeWhenMissing() {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "credit.html", "text/html", "<html/>".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
CreditParseResponse response = new CreditParseResponse();
|
||||
response.setStatusCode("0");
|
||||
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
|
||||
when(client.parse(eq("LXCUSTALL"), eq(remotePath))).thenReturn(response);
|
||||
|
||||
when(client.parse(eq("LXCUSTALL"), eq("PERSON"), any(File.class))).thenReturn(response);
|
||||
|
||||
AjaxResult result = controller.parse(file, null, null);
|
||||
AjaxResult result = controller.parse(remotePath, null);
|
||||
|
||||
assertEquals(200, result.get("code"));
|
||||
assertSame(response, result.get("data"));
|
||||
@@ -63,14 +58,53 @@ class CreditParseControllerTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnAjaxErrorWhenClientThrows() {
|
||||
MockMultipartFile file = new MockMultipartFile(
|
||||
"file", "credit.html", "text/html", "<html/>".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
when(client.parse(anyString(), anyString(), any(File.class)))
|
||||
when(client.parse(anyString(), anyString()))
|
||||
.thenThrow(new LsfxApiException("超时"));
|
||||
|
||||
AjaxResult result = controller.parse(file, null, null);
|
||||
AjaxResult result = controller.parse("http://127.0.0.1:62318/profile/credit-html/a.html", null);
|
||||
|
||||
assertEquals(500, result.get("code"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
void creditParseClient_shouldPostUrlEncodedRemotePathParameters() {
|
||||
HttpUtil httpUtil = mock(HttpUtil.class);
|
||||
CreditParseClient parseClient = new CreditParseClient();
|
||||
ReflectionTestUtils.setField(parseClient, "httpUtil", httpUtil);
|
||||
ReflectionTestUtils.setField(parseClient, "creditParseUrl", "http://tz/api/service/interface/invokeService/xfeature");
|
||||
ReflectionTestUtils.setField(parseClient, "orgCode", "902000");
|
||||
ReflectionTestUtils.setField(parseClient, "runType", "1");
|
||||
ReflectionTestUtils.setField(parseClient, "defaultModel", "LXCUSTALL");
|
||||
|
||||
CreditParseInvokeResponse response = new CreditParseInvokeResponse();
|
||||
response.setSuccess(true);
|
||||
response.setCode(1000);
|
||||
when(httpUtil.postUrlEncodedForm(
|
||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
||||
org.mockito.ArgumentMatchers.<Map<String, Object>>any(),
|
||||
isNull(),
|
||||
eq(CreditParseInvokeResponse.class)
|
||||
)).thenReturn(response);
|
||||
|
||||
String remotePath = "http://127.0.0.1:62318/profile/credit-html/a.html";
|
||||
CreditParseInvokeResponse actual = parseClient.parse(remotePath);
|
||||
|
||||
assertSame(response, actual);
|
||||
ArgumentCaptor<Map<String, Object>> paramsCaptor = ArgumentCaptor.forClass((Class) Map.class);
|
||||
verify(httpUtil).postUrlEncodedForm(
|
||||
eq("http://tz/api/service/interface/invokeService/xfeature"),
|
||||
paramsCaptor.capture(),
|
||||
isNull(),
|
||||
eq(CreditParseInvokeResponse.class)
|
||||
);
|
||||
|
||||
Map<String, Object> params = paramsCaptor.getValue();
|
||||
assertNotNull(params.get("serialNum"));
|
||||
assertTrue(params.get("serialNum").toString().startsWith("CCDI_CREDIT_"));
|
||||
assertEquals("902000", params.get("orgCode"));
|
||||
assertEquals("1", params.get("runType"));
|
||||
assertEquals(remotePath, params.get("remotePath"));
|
||||
assertEquals("LXCUSTALL", params.get("model"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# 征信解析远程路径调用改造实施记录
|
||||
|
||||
## 背景
|
||||
|
||||
天座征信解析接口调用方式变更:不再直接 multipart 上传 HTML 文件,改为先将 HTML 保存到业务服务器可访问目录,再将完整远程访问地址作为 `remotePath` 以 `application/x-www-form-urlencoded` 表单参数提交到新接口。
|
||||
|
||||
本次按新接口文档确认输出外层结构为 `success/code/data/mappingOutputFields`,其中 `mappingOutputFields.payload` 内部仍沿用原有 `lx_header/lx_debt/lx_publictype` 结构。
|
||||
|
||||
## 修改内容
|
||||
|
||||
1. 后端上传处理
|
||||
- 上传入口 `/ccdi/creditInfo/upload` 保持不变。
|
||||
- 新增 `CreditHtmlStorageService`,校验通过后的 HTML 保存到 `ruoyi.profile/credit-html/...`。
|
||||
- 根据 `credit-parse.api.file-public-base-url` 与 `/profile/credit-html/...` 拼接生成 `remotePath`。
|
||||
- 征信维护落库逻辑改为调用 `CreditParseClient.parse(remotePath)`,后续日期校验、落库、失败记录逻辑保持原样。
|
||||
|
||||
2. 天座接口客户端
|
||||
- `CreditParseClient` 改为提交 `serialNum/orgCode/runType/remotePath/model` 表单参数。
|
||||
- `HttpUtil` 增加 `postUrlEncodedForm` 方法,统一提交 `application/x-www-form-urlencoded`。
|
||||
- 新增 `CreditParseInvokeResponse`、`CreditParseInvokeData`,承载新外层响应结构。
|
||||
- 业务解析只读取 `data.mappingOutputFields` 下的 `message/status_code/payload`。
|
||||
|
||||
3. 配置
|
||||
- `application-dev.yml`、`application-pro.yml`、`application-nas.yml`、`application-uat.yml` 同步调整 `credit-parse.api.url` 到 `/api/service/interface/invokeService/xfeature`。
|
||||
- 新增 `credit-parse.api.file-public-base-url`、`org-code=902000`、`run-type=1`、`model=LXCUSTALL`。
|
||||
|
||||
4. 本地 mock
|
||||
- `lsfx-mock-server` 新增支持 `/api/service/interface/invokeService/xfeature`。
|
||||
- mock 接收新表单参数并通过 `remotePath` 读取 HTML。
|
||||
- mock 返回新外层结构,payload 仍按旧结构生成。
|
||||
|
||||
5. 测试覆盖
|
||||
- `CreditParseControllerTest` 覆盖新调试入口、默认模型、客户端异常、表单参数完整性。
|
||||
- `CcdiCreditInfoServiceImplTest` 覆盖 HTML 保存路径与 `remotePath` 拼接、旧日期拦截、成功落库、从新外层结构读取旧 payload。
|
||||
|
||||
## 验证结果
|
||||
|
||||
1. 接口文档核对
|
||||
- 已读取 `天座征信解析接口文档.xlsx`,确认调用方式为 `POST application/x-www-form-urlencoded`。
|
||||
- 已确认输入参数为 `serialNum/orgCode/runType/remotePath/model`。
|
||||
- 已确认输出结构为 `success/code/data/mappingOutputFields`。
|
||||
|
||||
2. 单元测试与编译
|
||||
- `mvn -pl ccdi-lsfx -Dtest=CreditParseControllerTest test`:通过,4 个用例成功。
|
||||
- `mvn -pl ccdi-info-collection -am -Dtest=CcdiCreditInfoServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test`:通过,3 个用例成功。
|
||||
- `mvn -pl ccdi-lsfx,ccdi-info-collection -am compile`:通过。
|
||||
|
||||
3. 联调验证
|
||||
- 通过 `bin/restart_java_backend.sh restart` 启动后端。
|
||||
- 启动本地 mock 后,通过 `/ccdi/creditInfo/upload` 上传临时 HTML。
|
||||
- 后端日志确认调用参数包含:
|
||||
- `model=LXCUSTALL`
|
||||
- `remotePath=http://127.0.0.1:62318/profile/credit-html/2026/05/12/credit_remote_path_test_20260512190228A001.html`
|
||||
- mock 日志确认收到 `POST /api/service/interface/invokeService/xfeature` 并返回 200。
|
||||
- 上传结果:`successCount=1`、`failureCount=0`。
|
||||
- 列表与详情均可查询到解析后的征信负面信息和债务信息。
|
||||
- 联调测试身份证号 `330781199001019999` 对应数据已通过删除接口清理,复查列表 `total=0`。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 影响征信维护上传入口、征信解析客户端、本地 mock、相关环境配置。
|
||||
- 前端上传入口和页面接口路径不变。
|
||||
- 不保留旧 multipart 外部接口兼容逻辑。
|
||||
|
||||
@@ -110,22 +110,31 @@ response = requests.post(
|
||||
### 征信解析 Mock
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8000/xfeature-mngs/conversation/htmlEval \
|
||||
-F model=LXCUSTALL \
|
||||
-F hType=PERSON \
|
||||
-F file=@./sample-credit.html
|
||||
curl -s -X POST http://localhost:8000/api/service/interface/invokeService/xfeature \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
-d serialNum=CCDI_CREDIT_1 \
|
||||
-d orgCode=902000 \
|
||||
-d runType=1 \
|
||||
-d remotePath=http://127.0.0.1:62318/profile/credit-html/sample-credit.html \
|
||||
-d model=LXCUSTALL
|
||||
```
|
||||
|
||||
成功时返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "成功",
|
||||
"status_code": "0",
|
||||
"payload": {
|
||||
"lx_header": {},
|
||||
"lx_debt": {},
|
||||
"lx_publictype": {}
|
||||
"success": true,
|
||||
"code": 1000,
|
||||
"data": {
|
||||
"mappingOutputFields": {
|
||||
"message": "成功",
|
||||
"status_code": "0",
|
||||
"payload": {
|
||||
"lx_header": {},
|
||||
"lx_debt": {},
|
||||
"lx_publictype": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -133,10 +142,13 @@ curl -s -X POST http://localhost:8000/xfeature-mngs/conversation/htmlEval \
|
||||
调试错误码时,可在 `model` 中追加错误标记:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8000/xfeature-mngs/conversation/htmlEval \
|
||||
-F model=error_ERR_10001 \
|
||||
-F hType=PERSON \
|
||||
-F file=@./sample-credit.html
|
||||
curl -s -X POST http://localhost:8000/api/service/interface/invokeService/xfeature \
|
||||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||||
-d serialNum=CCDI_CREDIT_1 \
|
||||
-d orgCode=902000 \
|
||||
-d runType=1 \
|
||||
-d remotePath=http://127.0.0.1:62318/profile/credit-html/sample-credit.html \
|
||||
-d model=error_ERR_10001
|
||||
```
|
||||
|
||||
健康检查:
|
||||
@@ -258,7 +270,7 @@ pytest tests/ -v --cov=. --cov-report=html
|
||||
| 4 | POST | `/watson/api/project/upload/getpendings` | 检查解析状态 |
|
||||
| 5 | POST | `/watson/api/project/batchDeleteUploadFile` | 删除文件 |
|
||||
| 6 | POST | `/watson/api/project/getBSByLogId` | 获取银行流水 |
|
||||
| 7 | POST | `/xfeature-mngs/conversation/htmlEval` | 征信解析 Mock |
|
||||
| 7 | POST | `/api/service/interface/invokeService/xfeature` | 征信解析 Mock |
|
||||
| 8 | GET | `/credit/health` | 征信解析健康检查 |
|
||||
|
||||
## ⚠️ 错误码列表
|
||||
|
||||
@@ -26,7 +26,7 @@ app = FastAPI(
|
||||
- **解析状态** - 轮询检查文件解析状态
|
||||
- **文件删除** - 批量删除上传的文件
|
||||
- **流水查询** - 分页获取银行流水数据
|
||||
- **征信解析** - 上传 HTML 并返回结构化征信 payload
|
||||
- **征信解析** - 读取 HTML 远程地址并返回结构化征信 payload
|
||||
|
||||
### 错误模拟
|
||||
|
||||
@@ -40,7 +40,7 @@ app = FastAPI(
|
||||
2. 上传文件: POST /watson/api/project/remoteUploadSplitFile
|
||||
3. 轮询解析状态: POST /watson/api/project/upload/getpendings
|
||||
4. 获取流水: POST /watson/api/project/getBSByLogId
|
||||
5. 征信解析: POST /xfeature-mngs/conversation/htmlEval
|
||||
5. 征信解析: POST /api/service/interface/invokeService/xfeature
|
||||
""",
|
||||
version=settings.APP_VERSION,
|
||||
docs_url="/docs",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import urlopen
|
||||
|
||||
from fastapi import APIRouter, File, Form, UploadFile
|
||||
from fastapi import APIRouter, Form
|
||||
|
||||
from services.credit_debug_service import CreditDebugService
|
||||
from services.credit_html_identity_service import CreditHtmlIdentityService
|
||||
@@ -12,26 +14,30 @@ debug_service = CreditDebugService("config/credit_response_examples.json")
|
||||
identity_service = CreditHtmlIdentityService()
|
||||
|
||||
|
||||
@router.post("/xfeature-mngs/conversation/htmlEval")
|
||||
@router.post("/api/service/interface/invokeService/xfeature")
|
||||
async def html_eval(
|
||||
serialNum: Optional[str] = Form(None),
|
||||
orgCode: Optional[str] = Form(None),
|
||||
runType: Optional[str] = Form(None),
|
||||
remotePath: Optional[str] = Form(None),
|
||||
model: Optional[str] = Form(None),
|
||||
hType: Optional[str] = Form(None),
|
||||
file: Optional[UploadFile] = File(None),
|
||||
):
|
||||
error_response = debug_service.validate_request(
|
||||
serial_num=serialNum,
|
||||
org_code=orgCode,
|
||||
run_type=runType,
|
||||
remote_path=remotePath,
|
||||
model=model,
|
||||
h_type=hType,
|
||||
file_present=file is not None,
|
||||
)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
html_content = await file.read()
|
||||
html_content = fetch_remote_html(remotePath)
|
||||
subject_identity = identity_service.extract_identity(html_content)
|
||||
payload = payload_service.generate_payload(
|
||||
model=model,
|
||||
h_type=hType,
|
||||
filename=file.filename or "credit.html",
|
||||
h_type="PERSON",
|
||||
filename=remote_filename(remotePath),
|
||||
subject_identity=subject_identity,
|
||||
)
|
||||
return debug_service.build_success_response(payload)
|
||||
@@ -40,3 +46,14 @@ async def html_eval(
|
||||
@router.get("/credit/health")
|
||||
async def credit_health():
|
||||
return {"status": "healthy", "service": "credit-mock"}
|
||||
|
||||
|
||||
def fetch_remote_html(remote_path: str) -> bytes:
|
||||
with urlopen(remote_path, timeout=5) as response:
|
||||
return response.read()
|
||||
|
||||
|
||||
def remote_filename(remote_path: str) -> str:
|
||||
path = urlparse(remote_path).path
|
||||
filename = path.rsplit("/", 1)[-1]
|
||||
return filename or "credit.html"
|
||||
|
||||
@@ -9,19 +9,29 @@ class CreditDebugService:
|
||||
"""处理征信解析接口的调试标记、参数校验与响应封装。"""
|
||||
|
||||
VALID_MODEL = "LXCUSTALL"
|
||||
VALID_HTYPES = {"PERSON", "ENTERPRISE"}
|
||||
|
||||
def __init__(self, template_path: str):
|
||||
self.template_path = template_path
|
||||
self.templates = self._load_templates()
|
||||
|
||||
def validate_request(self, model: Optional[str], h_type: Optional[str], file_present: bool):
|
||||
def validate_request(
|
||||
self,
|
||||
serial_num: Optional[str],
|
||||
org_code: Optional[str],
|
||||
run_type: Optional[str],
|
||||
remote_path: Optional[str],
|
||||
model: Optional[str],
|
||||
):
|
||||
if not serial_num:
|
||||
return self.build_missing_param_response("serialNum")
|
||||
if not org_code:
|
||||
return self.build_missing_param_response("orgCode")
|
||||
if not run_type:
|
||||
return self.build_missing_param_response("runType")
|
||||
if not remote_path:
|
||||
return self.build_missing_param_response("remotePath")
|
||||
if not model:
|
||||
return self.build_missing_param_response("model")
|
||||
if not file_present:
|
||||
return self.build_missing_param_response("file")
|
||||
if not h_type:
|
||||
return self.build_missing_param_response("hType")
|
||||
|
||||
error_code = self.detect_error_marker(model)
|
||||
if error_code:
|
||||
@@ -29,22 +39,21 @@ class CreditDebugService:
|
||||
|
||||
if model != self.VALID_MODEL:
|
||||
return self.build_error_response("ERR_10002")
|
||||
if h_type not in self.VALID_HTYPES:
|
||||
return self.build_error_response("ERR_10003")
|
||||
return None
|
||||
|
||||
def build_success_response(self, payload: dict) -> dict:
|
||||
response = copy.deepcopy(self.templates["success"])
|
||||
response["payload"] = payload
|
||||
return response
|
||||
return self.wrap_mapping_response(response)
|
||||
|
||||
def build_missing_param_response(self, param_name: str) -> dict:
|
||||
response = self.build_error_response("ERR_99999")
|
||||
response["message"] = response["message"].replace("XX", param_name)
|
||||
mapping_output_fields = response["data"]["mappingOutputFields"]
|
||||
mapping_output_fields["message"] = mapping_output_fields["message"].replace("XX", param_name)
|
||||
return response
|
||||
|
||||
def build_error_response(self, error_code: str) -> dict:
|
||||
return copy.deepcopy(self.templates["errors"][error_code])
|
||||
return self.wrap_mapping_response(copy.deepcopy(self.templates["errors"][error_code]))
|
||||
|
||||
def detect_error_marker(self, model: str) -> Optional[str]:
|
||||
matched = re.search(r"error_(ERR_\d+)", model)
|
||||
@@ -55,6 +64,15 @@ class CreditDebugService:
|
||||
return error_code
|
||||
return None
|
||||
|
||||
def wrap_mapping_response(self, mapping_output_fields: dict) -> dict:
|
||||
return {
|
||||
"success": True,
|
||||
"code": 1000,
|
||||
"data": {
|
||||
"mappingOutputFields": mapping_output_fields,
|
||||
},
|
||||
}
|
||||
|
||||
def _load_templates(self) -> dict:
|
||||
template_file = Path(self.template_path)
|
||||
if not template_file.is_absolute():
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 开发环境配置
|
||||
ruoyi:
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
profile: D:/ruoyi/uploadPath
|
||||
profile: tmp/uploadPath
|
||||
|
||||
ccdi:
|
||||
report:
|
||||
@@ -149,4 +149,8 @@ lsfx:
|
||||
|
||||
credit-parse:
|
||||
api:
|
||||
url: http://localhost:8000/xfeature-mngs/conversation/htmlEval
|
||||
url: http://localhost:8000/api/service/interface/invokeService/xfeature
|
||||
file-public-base-url: http://127.0.0.1:62318
|
||||
org-code: 902000
|
||||
run-type: 1
|
||||
model: LXCUSTALL
|
||||
|
||||
@@ -149,4 +149,8 @@ lsfx:
|
||||
|
||||
credit-parse:
|
||||
api:
|
||||
url: http://192.168.0.111:62320/xfeature-mngs/conversation/htmlEval
|
||||
url: http://192.168.0.111:62320/api/service/interface/invokeService/xfeature
|
||||
file-public-base-url: http://192.168.0.111:62318
|
||||
org-code: 902000
|
||||
run-type: 1
|
||||
model: LXCUSTALL
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 开发环境配置
|
||||
ruoyi:
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
profile: backend/uploadPath
|
||||
profile: /webapps/ccdi/uploadPath
|
||||
|
||||
ccdi:
|
||||
report:
|
||||
@@ -144,4 +144,8 @@ lsfx:
|
||||
|
||||
credit-parse:
|
||||
api:
|
||||
url: http://64.202.94.120:8081/xfeature-mngs/conversation/htmlEval
|
||||
url: http://64.202.32.40:8083/api/service/interface/invokeService/xfeature
|
||||
file-public-base-url: http://64.116.19.153
|
||||
org-code: 999000
|
||||
run-type: 1
|
||||
model: LXCUSTALL
|
||||
|
||||
@@ -147,4 +147,8 @@ lsfx:
|
||||
|
||||
credit-parse:
|
||||
api:
|
||||
url: http://192.168.0.111:62320/xfeature-mngs/conversation/htmlEval
|
||||
url: http://192.168.0.111:62320/api/service/interface/invokeService/xfeature
|
||||
file-public-base-url: http://158.234.199.250:62318
|
||||
org-code: 902000
|
||||
run-type: 1
|
||||
model: LXCUSTALL
|
||||
|
||||
Reference in New Issue
Block a user