diff --git a/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/CreditParseController.java b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/CreditParseController.java new file mode 100644 index 00000000..b40e58a0 --- /dev/null +++ b/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/CreditParseController.java @@ -0,0 +1,84 @@ +package com.ruoyi.lsfx.controller; + +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.exception.LsfxApiException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +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 +@RestController +@RequestMapping("/lsfx/credit") +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文件并调用外部解析服务") + @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 格式文件"); + } + + 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); + 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) { + // 忽略临时文件删除失败,避免影响主流程返回 + } + } + } + } +} diff --git a/ccdi-lsfx/src/test/java/com/ruoyi/lsfx/controller/CreditParseControllerTest.java b/ccdi-lsfx/src/test/java/com/ruoyi/lsfx/controller/CreditParseControllerTest.java index e1249ed5..8fe69e9a 100644 --- a/ccdi-lsfx/src/test/java/com/ruoyi/lsfx/controller/CreditParseControllerTest.java +++ b/ccdi-lsfx/src/test/java/com/ruoyi/lsfx/controller/CreditParseControllerTest.java @@ -1,16 +1,34 @@ 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.exception.LsfxApiException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockMultipartFile; +import java.io.File; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +@ExtendWith(MockitoExtension.class) class CreditParseControllerTest { - private final CreditParseController controller = new CreditParseController(); + @Mock + private CreditParseClient client; + + @InjectMocks + private CreditParseController controller; @Test void parse_shouldRejectEmptyFile() { @@ -26,4 +44,33 @@ class CreditParseControllerTest { AjaxResult result = controller.parse(file, null, null); assertEquals(500, result.get("code")); } + + @Test + void shouldUseDefaultModelAndTypeWhenMissing() { + MockMultipartFile file = new MockMultipartFile( + "file", "credit.html", "text/html", "".getBytes(StandardCharsets.UTF_8) + ); + CreditParseResponse response = new CreditParseResponse(); + response.setStatusCode("0"); + + when(client.parse(eq("LXCUSTALL"), eq("PERSON"), any(File.class))).thenReturn(response); + + AjaxResult result = controller.parse(file, null, null); + + assertEquals(200, result.get("code")); + assertSame(response, result.get("data")); + } + + @Test + void shouldReturnAjaxErrorWhenClientThrows() { + MockMultipartFile file = new MockMultipartFile( + "file", "credit.html", "text/html", "".getBytes(StandardCharsets.UTF_8) + ); + when(client.parse(anyString(), anyString(), any(File.class))) + .thenThrow(new LsfxApiException("超时")); + + AjaxResult result = controller.parse(file, null, null); + + assertEquals(500, result.get("code")); + } }