fix(ccdi-project): truncate upload error messages
This commit is contained in:
@@ -50,6 +50,8 @@ import java.util.concurrent.RejectedExecutionException;
|
|||||||
@Service
|
@Service
|
||||||
public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||||
|
|
||||||
|
private static final int MAX_ERROR_MESSAGE_LENGTH = 2000;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
private static class FetchBankStatementResult {
|
private static class FetchBankStatementResult {
|
||||||
private boolean success;
|
private boolean success;
|
||||||
@@ -330,10 +332,26 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
CcdiFileUploadRecord record = new CcdiFileUploadRecord();
|
CcdiFileUploadRecord record = new CcdiFileUploadRecord();
|
||||||
record.setId(recordId);
|
record.setId(recordId);
|
||||||
record.setFileStatus(status);
|
record.setFileStatus(status);
|
||||||
record.setErrorMessage(errorMessage);
|
record.setErrorMessage(normalizeErrorMessage(errorMessage));
|
||||||
recordMapper.updateById(record);
|
recordMapper.updateById(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFailedRecord(CcdiFileUploadRecord record, String errorMessage) {
|
||||||
|
record.setFileStatus("parsed_failed");
|
||||||
|
record.setErrorMessage(normalizeErrorMessage(errorMessage));
|
||||||
|
recordMapper.updateById(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeErrorMessage(String errorMessage) {
|
||||||
|
if (!StringUtils.hasText(errorMessage)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (errorMessage.length() <= MAX_ERROR_MESSAGE_LENGTH) {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
return errorMessage.substring(0, MAX_ERROR_MESSAGE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步处理单个文件的完整流程
|
* 异步处理单个文件的完整流程
|
||||||
* 包含:上传 → 轮询解析状态 → 获取结果 → 保存流水数据
|
* 包含:上传 → 轮询解析状态 → 获取结果 → 保存流水数据
|
||||||
@@ -444,9 +462,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
FetchBankStatementResult fetchResult =
|
FetchBankStatementResult fetchResult =
|
||||||
fetchAndSaveBankStatements(projectId, lsfxProjectId, logId);
|
fetchAndSaveBankStatements(projectId, lsfxProjectId, logId);
|
||||||
if (!fetchResult.isSuccess()) {
|
if (!fetchResult.isSuccess()) {
|
||||||
record.setFileStatus("parsed_failed");
|
updateFailedRecord(record, fetchResult.getErrorMessage());
|
||||||
record.setErrorMessage(fetchResult.getErrorMessage());
|
|
||||||
recordMapper.updateById(record);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,9 +475,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
|||||||
} else {
|
} else {
|
||||||
// 解析失败
|
// 解析失败
|
||||||
log.warn("【文件上传】步骤6: 解析失败: status={}, desc={}", status, uploadStatusDesc);
|
log.warn("【文件上传】步骤6: 解析失败: status={}, desc={}", status, uploadStatusDesc);
|
||||||
record.setFileStatus("parsed_failed");
|
updateFailedRecord(record, "解析失败: " + uploadStatusDesc);
|
||||||
record.setErrorMessage("解析失败: " + uploadStatusDesc);
|
|
||||||
recordMapper.updateById(record);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("【文件上传】处理完成: fileName={}", record.getFileName());
|
log.info("【文件上传】处理完成: fileName={}", record.getFileName());
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
private static final Integer LSFX_PROJECT_ID = 200;
|
private static final Integer LSFX_PROJECT_ID = 200;
|
||||||
private static final Long RECORD_ID = 300L;
|
private static final Long RECORD_ID = 300L;
|
||||||
private static final Integer LOG_ID = 400;
|
private static final Integer LOG_ID = 400;
|
||||||
|
private static final int MAX_ERROR_MESSAGE_LENGTH = 2000;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private CcdiFileUploadServiceImpl service;
|
private CcdiFileUploadServiceImpl service;
|
||||||
@@ -148,6 +149,44 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
verify(bankStatementMapper).deleteByProjectIdAndBatchId(PROJECT_ID, LOG_ID);
|
verify(bankStatementMapper).deleteByProjectIdAndBatchId(PROJECT_ID, LOG_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processFileAsync_shouldTruncateLongErrorMessageWhenBankStatementFetchFails() throws IOException {
|
||||||
|
List<CcdiFileUploadRecord> updates = new ArrayList<>();
|
||||||
|
captureUpdatedRecords(updates);
|
||||||
|
|
||||||
|
when(lsfxClient.uploadFile(eq(LSFX_PROJECT_ID), any())).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)))
|
||||||
|
.thenThrow(new RuntimeException("bank statement fetch failed:" + "x".repeat(3000)));
|
||||||
|
|
||||||
|
CcdiFileUploadRecord record = buildRecord();
|
||||||
|
Path tempFile = createTempFile();
|
||||||
|
|
||||||
|
service.processFileAsync(PROJECT_ID, LSFX_PROJECT_ID, tempFile.toString(), RECORD_ID, "batch-1", record);
|
||||||
|
|
||||||
|
CcdiFileUploadRecord failedRecord = findLastUpdatedRecordByStatus(updates, "parsed_failed");
|
||||||
|
assertTrue(failedRecord.getErrorMessage().length() <= MAX_ERROR_MESSAGE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processFileAsync_shouldTruncateLongErrorMessageWhenUnexpectedFailureOccurs() throws IOException {
|
||||||
|
List<CcdiFileUploadRecord> updates = new ArrayList<>();
|
||||||
|
captureUpdatedRecords(updates);
|
||||||
|
|
||||||
|
when(lsfxClient.uploadFile(eq(LSFX_PROJECT_ID), any()))
|
||||||
|
.thenThrow(new RuntimeException("upload failed:" + "x".repeat(3000)));
|
||||||
|
|
||||||
|
CcdiFileUploadRecord record = buildRecord();
|
||||||
|
Path tempFile = createTempFile();
|
||||||
|
|
||||||
|
service.processFileAsync(PROJECT_ID, LSFX_PROJECT_ID, tempFile.toString(), RECORD_ID, "batch-1", record);
|
||||||
|
|
||||||
|
CcdiFileUploadRecord failedRecord = findLastUpdatedRecordByStatus(updates, "parsed_failed");
|
||||||
|
assertTrue(failedRecord.getErrorMessage().length() <= MAX_ERROR_MESSAGE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
private void captureRecordStatus(List<String> events, AtomicInteger sequence) {
|
private void captureRecordStatus(List<String> events, AtomicInteger sequence) {
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
CcdiFileUploadRecord record = invocation.getArgument(0);
|
CcdiFileUploadRecord record = invocation.getArgument(0);
|
||||||
@@ -156,6 +195,18 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
}).when(recordMapper).updateById(any(CcdiFileUploadRecord.class));
|
}).when(recordMapper).updateById(any(CcdiFileUploadRecord.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void captureUpdatedRecords(List<CcdiFileUploadRecord> updates) {
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
CcdiFileUploadRecord record = invocation.getArgument(0);
|
||||||
|
CcdiFileUploadRecord snapshot = new CcdiFileUploadRecord();
|
||||||
|
snapshot.setId(record.getId());
|
||||||
|
snapshot.setFileStatus(record.getFileStatus());
|
||||||
|
snapshot.setErrorMessage(record.getErrorMessage());
|
||||||
|
updates.add(snapshot);
|
||||||
|
return 1;
|
||||||
|
}).when(recordMapper).updateById(any(CcdiFileUploadRecord.class));
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiFileUploadRecord buildRecord() {
|
private CcdiFileUploadRecord buildRecord() {
|
||||||
CcdiFileUploadRecord record = new CcdiFileUploadRecord();
|
CcdiFileUploadRecord record = new CcdiFileUploadRecord();
|
||||||
record.setId(RECORD_ID);
|
record.setId(RECORD_ID);
|
||||||
@@ -236,4 +287,14 @@ class CcdiFileUploadServiceImplTest {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CcdiFileUploadRecord findLastUpdatedRecordByStatus(List<CcdiFileUploadRecord> updates,
|
||||||
|
String status) {
|
||||||
|
for (int i = updates.size() - 1; i >= 0; i--) {
|
||||||
|
if (status.equals(updates.get(i).getFileStatus())) {
|
||||||
|
return updates.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AssertionError("未找到状态为 " + status + " 的更新记录");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
USE ccdi;
|
||||||
|
|
||||||
|
ALTER TABLE ccdi_file_upload_record
|
||||||
|
MODIFY COLUMN error_message TEXT COMMENT '错误信息(解析失败时记录)';
|
||||||
Reference in New Issue
Block a user