Compare commits
2 Commits
90f483f427
...
ffaa2fb2a0
| Author | SHA1 | Date | |
|---|---|---|---|
| ffaa2fb2a0 | |||
| dc8055ce7a |
@@ -21,6 +21,7 @@ import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
|
|||||||
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
|
||||||
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -33,6 +34,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* 征信维护服务实现
|
* 征信维护服务实现
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
||||||
|
|
||||||
@@ -150,7 +152,7 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
|||||||
|
|
||||||
private void handleSingleFile(MultipartFile multipartFile, String userName, CallerContext caller) throws Exception {
|
private void handleSingleFile(MultipartFile multipartFile, String userName, CallerContext caller) throws Exception {
|
||||||
CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
|
CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
|
||||||
CreditParseInvokeResponse response = creditParseClient.parse(caller, storedHtml.remotePath());
|
CreditParseInvokeResponse response = parseAndDeleteLocalFile(caller, storedHtml);
|
||||||
CreditParsePayload payload = requireResponse(response).getPayload();
|
CreditParsePayload payload = requireResponse(response).getPayload();
|
||||||
Map<String, Object> header = requireHeader(payload);
|
Map<String, Object> header = requireHeader(payload);
|
||||||
String personId = stringValue(header.get("query_cert_no"));
|
String personId = stringValue(header.get("query_cert_no"));
|
||||||
@@ -164,6 +166,25 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
|
|||||||
replaceEmployeeCredit(personId, debts, negative, userName);
|
replaceEmployeeCredit(personId, debts, negative, userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CreditParseInvokeResponse parseAndDeleteLocalFile(
|
||||||
|
CallerContext caller, CreditHtmlStorageService.StoredCreditHtml storedHtml) throws Exception {
|
||||||
|
CreditParseInvokeResponse response;
|
||||||
|
try {
|
||||||
|
response = creditParseClient.parse(caller, storedHtml.remotePath());
|
||||||
|
} catch (Exception parseException) {
|
||||||
|
try {
|
||||||
|
creditHtmlStorageService.delete(storedHtml);
|
||||||
|
} catch (Exception deleteException) {
|
||||||
|
parseException.addSuppressed(deleteException);
|
||||||
|
log.error("征信解析失败后清理本地文件失败", deleteException);
|
||||||
|
}
|
||||||
|
throw parseException;
|
||||||
|
}
|
||||||
|
|
||||||
|
creditHtmlStorageService.delete(storedHtml);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
private void validateHtmlFile(MultipartFile file) {
|
private void validateHtmlFile(MultipartFile file) {
|
||||||
String originalFilename = file == null ? null : file.getOriginalFilename();
|
String originalFilename = file == null ? null : file.getOriginalFilename();
|
||||||
if (originalFilename == null) {
|
if (originalFilename == null) {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 征信 HTML 服务器落盘与远程访问地址生成。
|
* 征信 HTML 服务器落盘与远程访问地址生成。
|
||||||
@@ -16,6 +20,7 @@ import java.io.File;
|
|||||||
public class CreditHtmlStorageService {
|
public class CreditHtmlStorageService {
|
||||||
|
|
||||||
private static final String CREDIT_HTML_DIR = "credit-html";
|
private static final String CREDIT_HTML_DIR = "credit-html";
|
||||||
|
private static final String CREDIT_HTML_PROFILE_PREFIX = "/profile/" + CREDIT_HTML_DIR + "/";
|
||||||
private static final String[] HTML_EXTENSIONS = {"html", "htm"};
|
private static final String[] HTML_EXTENSIONS = {"html", "htm"};
|
||||||
|
|
||||||
@Value("${credit-parse.api.file-public-base-url}")
|
@Value("${credit-parse.api.file-public-base-url}")
|
||||||
@@ -26,6 +31,25 @@ public class CreditHtmlStorageService {
|
|||||||
return new StoredCreditHtml(profilePath, buildRemotePath(profilePath));
|
return new StoredCreditHtml(profilePath, buildRemotePath(profilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void delete(StoredCreditHtml storedHtml) throws IOException {
|
||||||
|
String profilePath = storedHtml == null ? null : storedHtml.profilePath();
|
||||||
|
if (StringUtils.isBlank(profilePath) || !profilePath.startsWith(CREDIT_HTML_PROFILE_PREFIX)) {
|
||||||
|
throw new IllegalArgumentException("征信HTML本地文件路径非法");
|
||||||
|
}
|
||||||
|
|
||||||
|
String relativePath = profilePath.substring(CREDIT_HTML_PROFILE_PREFIX.length());
|
||||||
|
if (StringUtils.isBlank(relativePath)) {
|
||||||
|
throw new IllegalArgumentException("征信HTML本地文件路径非法");
|
||||||
|
}
|
||||||
|
|
||||||
|
Path baseDir = Paths.get(getCreditHtmlBaseDir()).toAbsolutePath().normalize();
|
||||||
|
Path targetFile = baseDir.resolve(relativePath).normalize();
|
||||||
|
if (!targetFile.startsWith(baseDir)) {
|
||||||
|
throw new IllegalArgumentException("征信HTML本地文件路径越界");
|
||||||
|
}
|
||||||
|
Files.deleteIfExists(targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
private String getCreditHtmlBaseDir() {
|
private String getCreditHtmlBaseDir() {
|
||||||
return RuoYiConfig.getProfile() + File.separator + CREDIT_HTML_DIR;
|
return RuoYiConfig.getProfile() + File.separator + CREDIT_HTML_DIR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.ruoyi.lsfx.domain.response.CreditParseResponse;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
@@ -35,16 +36,25 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class CcdiCreditInfoServiceImplTest {
|
class CcdiCreditInfoServiceImplTest {
|
||||||
|
|
||||||
private static final CallerContext CALLER = CallerContext.of(7L, "tester");
|
private static final CallerContext CALLER = CallerContext.of(7L, "tester");
|
||||||
|
private static final CreditHtmlStorageService.StoredCreditHtml STORED_HTML =
|
||||||
|
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");
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private CcdiCreditInfoServiceImpl service;
|
private CcdiCreditInfoServiceImpl service;
|
||||||
@@ -87,8 +97,12 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
|
|
||||||
assertEquals(1, result.getSuccessCount());
|
assertEquals(1, result.getSuccessCount());
|
||||||
assertEquals(0, result.getFailureCount());
|
assertEquals(0, result.getFailureCount());
|
||||||
verify(creditParseClient).parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html");
|
InOrder processingOrder = inOrder(creditParseClient, creditHtmlStorageService, debtsInfoMapper);
|
||||||
verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
|
processingOrder.verify(creditParseClient)
|
||||||
|
.parse(CALLER, "http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html");
|
||||||
|
processingOrder.verify(creditHtmlStorageService)
|
||||||
|
.delete(any(CreditHtmlStorageService.StoredCreditHtml.class));
|
||||||
|
processingOrder.verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
|
||||||
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
|
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +123,7 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
|
|
||||||
assertEquals(0, result.getSuccessCount());
|
assertEquals(0, result.getSuccessCount());
|
||||||
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
|
||||||
|
verify(creditHtmlStorageService).delete(STORED_HTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -127,6 +142,7 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
|
|
||||||
assertEquals(0, result.getSuccessCount());
|
assertEquals(0, result.getSuccessCount());
|
||||||
assertEquals("征信解析平台状态码异常: 99999", result.getFailures().get(0).getReason());
|
assertEquals("征信解析平台状态码异常: 99999", result.getFailures().get(0).getReason());
|
||||||
|
verify(creditHtmlStorageService).delete(STORED_HTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -147,11 +163,77 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
|
|
||||||
assertEquals(0, result.getSuccessCount());
|
assertEquals(0, result.getSuccessCount());
|
||||||
assertEquals("结果解析失败", result.getFailures().get(0).getReason());
|
assertEquals("结果解析失败", result.getFailures().get(0).getReason());
|
||||||
|
verify(creditHtmlStorageService).delete(STORED_HTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadHtmlFiles_shouldDeleteBeforeDatabaseFailure() throws Exception {
|
||||||
|
MockMultipartFile file = htmlFile("a.html");
|
||||||
|
when(creditHtmlStorageService.save(any())).thenReturn(STORED_HTML);
|
||||||
|
when(creditParseClient.parse(any(CallerContext.class), anyString()))
|
||||||
|
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||||
|
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||||
|
.thenReturn(List.of(buildDebt("330101199001010011")));
|
||||||
|
when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||||
|
.thenReturn(buildNegative("330101199001010011"));
|
||||||
|
doThrow(new RuntimeException("入库失败"))
|
||||||
|
.when(debtsInfoMapper).deleteByPersonId("330101199001010011");
|
||||||
|
|
||||||
|
CreditInfoUploadResultVO result = service.upload(List.of(file), CALLER);
|
||||||
|
|
||||||
|
assertEquals(0, result.getSuccessCount());
|
||||||
|
assertEquals("入库失败", result.getFailures().get(0).getReason());
|
||||||
|
InOrder processingOrder = inOrder(creditHtmlStorageService, debtsInfoMapper);
|
||||||
|
processingOrder.verify(creditHtmlStorageService).delete(STORED_HTML);
|
||||||
|
processingOrder.verify(debtsInfoMapper).deleteByPersonId("330101199001010011");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadHtmlFiles_shouldFailBeforeValidationWhenDeleteFails() throws Exception {
|
||||||
|
MockMultipartFile file = htmlFile("a.html");
|
||||||
|
when(creditHtmlStorageService.save(any())).thenReturn(STORED_HTML);
|
||||||
|
when(creditParseClient.parse(any(CallerContext.class), anyString()))
|
||||||
|
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||||
|
doThrow(new java.io.IOException("本地文件删除失败"))
|
||||||
|
.when(creditHtmlStorageService).delete(STORED_HTML);
|
||||||
|
|
||||||
|
CreditInfoUploadResultVO result = service.upload(List.of(file), CALLER);
|
||||||
|
|
||||||
|
assertEquals(0, result.getSuccessCount());
|
||||||
|
assertEquals("本地文件删除失败", result.getFailures().get(0).getReason());
|
||||||
|
verifyNoInteractions(assembler, debtsInfoMapper, negativeInfoMapper, queryMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uploadHtmlFiles_shouldPreserveParseErrorAndContinueWhenDeleteAlsoFails() throws Exception {
|
||||||
|
MockMultipartFile failedFile = htmlFile("failed.html");
|
||||||
|
MockMultipartFile successFile = htmlFile("success.html");
|
||||||
|
CreditHtmlStorageService.StoredCreditHtml failedStoredHtml = storedHtml("failed_1.html");
|
||||||
|
CreditHtmlStorageService.StoredCreditHtml successStoredHtml = storedHtml("success_1.html");
|
||||||
|
when(creditHtmlStorageService.save(any())).thenReturn(failedStoredHtml, successStoredHtml);
|
||||||
|
when(creditParseClient.parse(any(CallerContext.class), anyString()))
|
||||||
|
.thenThrow(new RuntimeException("解析接口异常"))
|
||||||
|
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
|
||||||
|
doThrow(new java.io.IOException("本地文件删除失败"))
|
||||||
|
.doNothing()
|
||||||
|
.when(creditHtmlStorageService).delete(any(CreditHtmlStorageService.StoredCreditHtml.class));
|
||||||
|
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
|
||||||
|
.thenReturn(null);
|
||||||
|
|
||||||
|
CreditInfoUploadResultVO result = service.upload(List.of(failedFile, successFile), CALLER);
|
||||||
|
|
||||||
|
assertEquals(1, result.getSuccessCount());
|
||||||
|
assertEquals(1, result.getFailureCount());
|
||||||
|
assertEquals("解析接口异常", result.getFailures().get(0).getReason());
|
||||||
|
verify(creditHtmlStorageService).delete(failedStoredHtml);
|
||||||
|
verify(creditHtmlStorageService).delete(successStoredHtml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void creditHtmlStorage_shouldStoreHtmlUnderProfileAndBuildRemotePath(@TempDir Path profileDir) throws Exception {
|
void creditHtmlStorage_shouldStoreAndDeleteHtmlWithinProfile(@TempDir Path profileDir) throws Exception {
|
||||||
String oldProfile = RuoYiConfig.getProfile();
|
String oldProfile = RuoYiConfig.getProfile();
|
||||||
new RuoYiConfig().setProfile(profileDir.toString());
|
new RuoYiConfig().setProfile(profileDir.toString());
|
||||||
try {
|
try {
|
||||||
@@ -167,11 +249,42 @@ class CcdiCreditInfoServiceImplTest {
|
|||||||
assertEquals("http://127.0.0.1:62318" + storedHtml.profilePath(), storedHtml.remotePath());
|
assertEquals("http://127.0.0.1:62318" + storedHtml.profilePath(), storedHtml.remotePath());
|
||||||
Path savedFile = profileDir.resolve(storedHtml.profilePath().substring("/profile/".length()));
|
Path savedFile = profileDir.resolve(storedHtml.profilePath().substring("/profile/".length()));
|
||||||
assertTrue(Files.exists(savedFile));
|
assertTrue(Files.exists(savedFile));
|
||||||
|
storageService.delete(storedHtml);
|
||||||
|
assertFalse(Files.exists(savedFile));
|
||||||
|
storageService.delete(storedHtml);
|
||||||
} finally {
|
} finally {
|
||||||
new RuoYiConfig().setProfile(oldProfile);
|
new RuoYiConfig().setProfile(oldProfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void creditHtmlStorage_shouldRejectPathOutsideCreditHtmlDirectory(@TempDir Path profileDir) throws Exception {
|
||||||
|
String oldProfile = RuoYiConfig.getProfile();
|
||||||
|
new RuoYiConfig().setProfile(profileDir.toString());
|
||||||
|
try {
|
||||||
|
CreditHtmlStorageService storageService = new CreditHtmlStorageService();
|
||||||
|
Path outsideFile = Files.writeString(profileDir.resolve("outside.html"), "outside");
|
||||||
|
CreditHtmlStorageService.StoredCreditHtml storedHtml = new CreditHtmlStorageService.StoredCreditHtml(
|
||||||
|
"/profile/credit-html/../outside.html", "http://127.0.0.1/profile/credit-html/../outside.html");
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> storageService.delete(storedHtml));
|
||||||
|
assertTrue(Files.exists(outsideFile));
|
||||||
|
} finally {
|
||||||
|
new RuoYiConfig().setProfile(oldProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MockMultipartFile htmlFile(String fileName) {
|
||||||
|
return new MockMultipartFile(
|
||||||
|
"files", fileName, "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CreditHtmlStorageService.StoredCreditHtml storedHtml(String fileName) {
|
||||||
|
return new CreditHtmlStorageService.StoredCreditHtml(
|
||||||
|
"/profile/credit-html/2026/05/12/" + fileName,
|
||||||
|
"http://127.0.0.1:62318/profile/credit-html/2026/05/12/" + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
private CreditParseInvokeResponse successResponse(String personId, String personName, String reportTime) {
|
private CreditParseInvokeResponse successResponse(String personId, String personName, String reportTime) {
|
||||||
CreditParsePayload payload = new CreditParsePayload();
|
CreditParsePayload payload = new CreditParsePayload();
|
||||||
Map<String, Object> header = new HashMap<>();
|
Map<String, Object> header = new HashMap<>();
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 征信本地文件清理后端实施计划
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
征信 HTML 上传后仅作为外部解析接口的临时输入。解析调用结束后立即删除本地文件,避免成功或失败流程残留敏感征信文件。
|
||||||
|
|
||||||
|
## 实施内容
|
||||||
|
|
||||||
|
1. 在 `CreditHtmlStorageService` 增加受限删除能力,仅允许删除 `ruoyi.profile/credit-html` 目录内的文件。
|
||||||
|
2. 在征信上传单文件流程中,解析成功后先删除本地文件,再校验解析结果并入库。
|
||||||
|
3. 解析失败时仍执行删除;解析与删除同时失败时保留原始解析错误,并记录删除异常。
|
||||||
|
4. 删除失败时终止当前文件后续处理,由现有批量上传逻辑记录失败并继续处理其他文件。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- 仅修改征信维护上传的后端处理流程。
|
||||||
|
- 不修改上传接口、返回结构、数据库和前端页面。
|
||||||
|
- `/lsfx/credit/parse` 外部地址测试接口不管理本地上传文件,保持不变。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- 覆盖成功、解析失败、结果校验失败、入库失败和删除失败场景。
|
||||||
|
- 验证删除路径不能逃逸 `ruoyi.profile/credit-html`。
|
||||||
|
- 运行征信维护定向测试及 `ccdi-info-collection` 模块测试。
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# 征信本地文件清理后端实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 为征信 HTML 存储服务增加本地文件删除能力,并限制删除范围为 `ruoyi.profile/credit-html`。
|
||||||
|
- 征信上传调用外部解析接口后立即删除本地文件,再继续解析结果校验与入库。
|
||||||
|
- 解析失败时同样清理文件;清理异常不覆盖原始解析错误。
|
||||||
|
- 删除失败时当前文件不再入库,批量上传继续处理后续文件。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- 后端模块:`ccdi-info-collection`
|
||||||
|
- 前端、接口协议、数据库结构均无变更。
|
||||||
|
- 外部 `remotePath` 测试接口无变更。
|
||||||
|
|
||||||
|
## 验证情况
|
||||||
|
|
||||||
|
### 定向测试
|
||||||
|
|
||||||
|
执行命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-info-collection -am -Dtest=CcdiCreditInfoServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:通过,共执行 9 个用例,失败 0、错误 0。已覆盖解析成功、解析失败、结果校验失败、入库失败、删除失败、批量继续处理、真实文件删除、重复删除和越界路径拒绝。
|
||||||
|
|
||||||
|
### 模块测试
|
||||||
|
|
||||||
|
执行命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-info-collection -am test
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:共执行 178 个用例,本次修改相关测试全部通过;模块整体存在 1 个失败和 1 个错误:
|
||||||
|
|
||||||
|
- `CcdiBaseStaffMapperTest.mapperXml_shouldUseStableOrderForBaseStaffPagination`:员工 Mapper 排序契约断言失败。
|
||||||
|
- `CcdiBaseStaffServiceImplTest.selectBaseStaffById_shouldReturnSelfOwnedAssetInfoList`:员工详情测试返回空对象并触发空指针。
|
||||||
|
|
||||||
|
上述失败位于未修改的员工信息代码与测试中,与本次征信本地文件清理无关,本次未扩大范围处理。
|
||||||
Reference in New Issue
Block a user