修复流水上传身份证号回填
This commit is contained in:
@@ -46,6 +46,7 @@ import java.nio.file.StandardCopyOption;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
@@ -819,7 +820,9 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
enterpriseNamesStr, accountNosStr);
|
||||
|
||||
log.info("【文件上传】步骤7: 获取流水数据");
|
||||
FetchBankStatementResult fetchResult = fetchAndSaveBankStatements(projectId, lsfxProjectId, logId);
|
||||
String fallbackCretNo = extractIdCardFromFileName(record.getFileName());
|
||||
FetchBankStatementResult fetchResult = fetchAndSaveBankStatements(projectId, lsfxProjectId, logId,
|
||||
fallbackCretNo);
|
||||
if (!fetchResult.isSuccess()) {
|
||||
updateFailedRecord(record, fetchResult.getErrorMessage());
|
||||
return;
|
||||
@@ -892,7 +895,8 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
* @param logId 文件ID
|
||||
*/
|
||||
private FetchBankStatementResult fetchAndSaveBankStatements(Long projectId, Integer groupId,
|
||||
Integer logId) {
|
||||
Integer logId,
|
||||
String fallbackCretNo) {
|
||||
log.info("【文件上传】开始获取流水数据: projectId={}, groupId={}, logId={}",
|
||||
projectId, groupId, logId);
|
||||
|
||||
@@ -957,6 +961,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
if (statement != null) {
|
||||
statement.setBatchId(logId);
|
||||
statement.setProjectId(projectId);
|
||||
fillMissingCretNo(statement, fallbackCretNo);
|
||||
normalizeDedupFields(statement);
|
||||
batchList.add(statement);
|
||||
|
||||
@@ -1000,6 +1005,22 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
}
|
||||
}
|
||||
|
||||
private String extractIdCardFromFileName(String fileName) {
|
||||
if (!StringUtils.hasText(fileName)) {
|
||||
return null;
|
||||
}
|
||||
String mainFileName = getMainFileName(fileName);
|
||||
Matcher matcher = UPLOAD_FILE_NAME_ID_CARD_PATTERN.matcher(mainFileName);
|
||||
return matcher.find() ? matcher.group() : null;
|
||||
}
|
||||
|
||||
private void fillMissingCretNo(CcdiBankStatement statement, String fallbackCretNo) {
|
||||
if (statement == null || StringUtils.hasText(statement.getCretNo()) || !StringUtils.hasText(fallbackCretNo)) {
|
||||
return;
|
||||
}
|
||||
statement.setCretNo(fallbackCretNo);
|
||||
}
|
||||
|
||||
private void cleanupBankStatements(Long projectId, Integer logId) {
|
||||
bankStatementMapper.deleteByProjectIdAndBatchId(projectId, logId);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import ch.qos.logback.core.read.ListAppender;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiFileUploadStatisticsVO;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
|
||||
@@ -470,6 +471,44 @@ class CcdiFileUploadServiceImplTest {
|
||||
), eq("原始流水.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void processFileAsync_shouldBackfillMissingCretNoFromUploadFileName() throws IOException {
|
||||
AtomicReference<List<CcdiBankStatement>> insertedStatements = new AtomicReference<>();
|
||||
doAnswer(invocation -> {
|
||||
List<CcdiBankStatement> statements = invocation.getArgument(0);
|
||||
insertedStatements.set(new ArrayList<>(statements));
|
||||
return statements.size();
|
||||
}).when(bankStatementMapper).insertBatch(any());
|
||||
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(PROJECT_ID);
|
||||
when(projectMapper.selectById(PROJECT_ID)).thenReturn(project);
|
||||
when(bankStatementMapper.countMatchedStaffCountByProjectId(PROJECT_ID)).thenReturn(1);
|
||||
when(lsfxClient.uploadFile(eq(LSFX_PROJECT_ID), any(), eq("张三_330101199001010011_流水.xlsx")))
|
||||
.thenReturn(buildUploadResponse());
|
||||
when(lsfxClient.checkParseStatus(LSFX_PROJECT_ID, String.valueOf(LOG_ID)))
|
||||
.thenReturn(buildCheckParseStatusResponse(false));
|
||||
when(lsfxClient.getFileUploadStatus(any())).thenReturn(buildParsedSuccessStatusResponse());
|
||||
when(lsfxClient.getBankStatement(any(GetBankStatementRequest.class)))
|
||||
.thenAnswer(invocation -> {
|
||||
GetBankStatementRequest request = invocation.getArgument(0);
|
||||
if (Integer.valueOf(1).equals(request.getPageSize())) {
|
||||
return buildBankStatementCountResponse(1);
|
||||
}
|
||||
return buildBankStatementResponseWithBlankCretNo();
|
||||
});
|
||||
|
||||
CcdiFileUploadRecord record = buildRecord();
|
||||
record.setFileName("张三_330101199001010011_流水.xlsx");
|
||||
Path tempFile = createTempFile();
|
||||
|
||||
service.processFileAsync(PROJECT_ID, LSFX_PROJECT_ID, tempFile.toString(), RECORD_ID, "batch-1", record);
|
||||
|
||||
assertNotNull(insertedStatements.get());
|
||||
assertEquals(1, insertedStatements.get().size());
|
||||
assertEquals("330101199001010011", insertedStatements.get().get(0).getCretNo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processFileAsync_shouldKeepOriginalFileNameWhenStatusReturnsDifferentName() throws IOException {
|
||||
when(lsfxClient.uploadFile(eq(LSFX_PROJECT_ID), any(), org.mockito.ArgumentMatchers.anyString()))
|
||||
@@ -717,7 +756,8 @@ class CcdiFileUploadServiceImplTest {
|
||||
"fetchAndSaveBankStatements",
|
||||
PROJECT_ID,
|
||||
LSFX_PROJECT_ID,
|
||||
LOG_ID
|
||||
LOG_ID,
|
||||
null
|
||||
);
|
||||
|
||||
assertTrue(Boolean.TRUE.equals(ReflectionTestUtils.getField(result, "success")));
|
||||
@@ -929,6 +969,27 @@ class CcdiFileUploadServiceImplTest {
|
||||
return response;
|
||||
}
|
||||
|
||||
private GetBankStatementResponse buildBankStatementCountResponse(int totalCount) {
|
||||
GetBankStatementResponse.BankStatementData data = new GetBankStatementResponse.BankStatementData();
|
||||
data.setTotalCount(totalCount);
|
||||
|
||||
GetBankStatementResponse response = new GetBankStatementResponse();
|
||||
response.setData(data);
|
||||
return response;
|
||||
}
|
||||
|
||||
private GetBankStatementResponse buildBankStatementResponseWithBlankCretNo() {
|
||||
GetBankStatementResponse.BankStatementItem item = new GetBankStatementResponse.BankStatementItem();
|
||||
item.setBankStatementId(1L);
|
||||
item.setLeName("测试主体");
|
||||
item.setAccountNo("62220001");
|
||||
item.setCustomerName("交易对手");
|
||||
item.setCustomerAccountNo("62220002");
|
||||
item.setDrAmount(BigDecimal.TEN);
|
||||
item.setCretNo(null);
|
||||
return buildBankStatementResponseWithItems(1, List.of(item));
|
||||
}
|
||||
|
||||
private void invokeSubmitTasksAsync(List<String> tempFilePaths,
|
||||
List<CcdiFileUploadRecord> records,
|
||||
String batchId) throws Exception {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# 上传流水身份证号本地回填后端实施计划
|
||||
|
||||
## 目标
|
||||
|
||||
修复上传行外流水文件后,流水平台返回明细中 `cretNo` 为空导致 `ccdi_bank_statement.cret_no` 为空、结果总览和打标无法按身份证号关联的问题。
|
||||
|
||||
## 范围
|
||||
|
||||
- 仅调整后端批量上传流水文件链路。
|
||||
- 不调整拉取本行信息链路。
|
||||
- 不调整前端页面、数据库表结构、结果总览查询和打标 SQL。
|
||||
|
||||
## 实施方案
|
||||
|
||||
1. 复用现有文件名空白清理与身份证号识别规则,从归一化后的上传记录文件名中提取 18 位身份证号。
|
||||
2. 保持转传流水平台的文件名不变,继续使用当前上传记录文件名。
|
||||
3. 保存平台返回的流水明细时,若 `CcdiBankStatement.cretNo` 为空,则使用当前上传记录文件名中提取到的身份证号回填。
|
||||
4. 补充单元测试覆盖平台 `cretNo` 为空时本地回填。
|
||||
|
||||
## 验证
|
||||
|
||||
- 运行 `mvn -pl ccdi-project -am -Dtest=CcdiFileUploadServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test`。
|
||||
- 必要时运行 `mvn -pl ccdi-project -am -DskipTests compile`。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 上传流水身份证号本地回填实施记录
|
||||
|
||||
## 背景
|
||||
|
||||
上传行外流水文件时,文件名已经会校验必须包含 18 位身份证号,但流水平台返回明细中的 `cretNo` 可能为空。当前结果总览、流水打标和专项分析主要依赖 `ccdi_bank_statement.cret_no` 关联员工或关系人,空值会导致上传后有流水但结果出不来。
|
||||
|
||||
## 修改内容
|
||||
|
||||
- 仅调整后端上传流水文件链路,不修改前端、不修改拉取本行信息链路、不修改数据库结构。
|
||||
- `CcdiFileUploadServiceImpl`
|
||||
- 在上传记录文件名中复用现有身份证号识别规则提取 18 位身份证号。
|
||||
- 保存流水平台返回的流水明细前,若 `CcdiBankStatement.cretNo` 为空,则使用上传记录文件名中的身份证号回填。
|
||||
- 继续使用原有上传记录文件名转传流水平台,不新增 `身份证号-原文件名` 前缀改写。
|
||||
- `CcdiFileUploadServiceImplTest`
|
||||
- 保留“转传流水平台使用记录文件名”的测试。
|
||||
- 新增平台返回 `cretNo` 为空时,本地入库实体回填上传文件名身份证号的测试。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 对平台正常返回 `cretNo` 的流水无影响,仍优先使用平台返回值。
|
||||
- 对平台未返回 `cretNo`、但上传文件名包含身份证号的流水,入库时会补齐 `ccdi_bank_statement.cret_no`。
|
||||
- 对文件名不含身份证号的上传仍在上传前拦截,不进入落库阶段。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
mvn -pl ccdi-project -am "-Dtest=CcdiFileUploadServiceImplTest" "-Dsurefire.failIfNoSpecifiedTests=false" test
|
||||
```
|
||||
|
||||
- 结果:BUILD SUCCESS
|
||||
- 覆盖:上传链路文件名校验、上传平台文件名传递、解析完成后流水入库、`cretNo` 为空时本地回填。
|
||||
|
||||
## 备注
|
||||
|
||||
- PowerShell 下 Maven `-D` 参数需要加引号;未加引号时命令会被解析为错误生命周期阶段。
|
||||
- 本次未启动前端或后端服务,因此无测试进程需要关闭。
|
||||
Reference in New Issue
Block a user