接通历史项目导入后端闭环

This commit is contained in:
wkc
2026-03-29 09:56:01 +08:00
parent 46d190aa74
commit d6457491e8
5 changed files with 75 additions and 0 deletions

View File

@@ -16,6 +16,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.ccdi.project.service.ICcdiFileUploadService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.constants.LsfxConstants;
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
@@ -964,6 +965,9 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
if (record == null) {
throw new RuntimeException("上传记录不存在");
}
if ("HISTORY_IMPORT".equals(record.getSourceType())) {
throw new ServiceException("历史导入文件不支持删除");
}
if (!"parsed_success".equals(record.getFileStatus())) {
if ("deleted".equals(record.getFileStatus())) {
throw new RuntimeException("文件已删除,请勿重复操作");

View File

@@ -2,11 +2,13 @@ package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.ccdi.project.service.ICcdiProjectHistoryImportService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -47,6 +49,9 @@ public class CcdiProjectHistoryImportServiceImpl implements ICcdiProjectHistoryI
@Qualifier("fileUploadExecutor")
private Executor fileUploadExecutor;
@Resource
private ICcdiBankTagService bankTagService;
@Override
public void submitImport(Long targetProjectId, Integer targetLsfxProjectId,
CcdiProjectImportHistoryDTO dto, String operator) {
@@ -97,6 +102,10 @@ public class CcdiProjectHistoryImportServiceImpl implements ICcdiProjectHistoryI
if (!recordsToInsert.isEmpty()) {
recordMapper.insertBatch(recordsToInsert);
}
if (!statementsToInsert.isEmpty()) {
refreshProjectTargetCount(targetProjectId);
bankTagService.submitAutoRebuild(targetProjectId, TriggerType.AUTO_BATCH_UPLOAD);
}
}
private CcdiBankStatement copyStatement(CcdiBankStatement sourceStatement, Long targetProjectId,
@@ -136,6 +145,17 @@ public class CcdiProjectHistoryImportServiceImpl implements ICcdiProjectHistoryI
return sourceProject == null ? null : sourceProject.getProjectName();
}
private void refreshProjectTargetCount(Long targetProjectId) {
CcdiProject targetProject = projectMapper.selectById(targetProjectId);
if (targetProject == null) {
log.warn("【项目历史导入】刷新目标人数时项目不存在: projectId={}", targetProjectId);
return;
}
int targetCount = bankStatementMapper.countMatchedStaffCountByProjectId(targetProjectId);
targetProject.setTargetCount(targetCount);
projectMapper.updateById(targetProject);
}
private void normalizeDedupFields(CcdiBankStatement statement) {
statement.setLeAccountNo(trimToNull(statement.getLeAccountNo()));
}

View File

@@ -411,6 +411,20 @@ class CcdiFileUploadServiceImplTest {
assertTrue(exception.getMessage().contains("仅支持删除解析成功文件"));
}
@Test
void deleteFileUploadRecord_shouldRejectHistoryImportRecord() {
CcdiFileUploadRecord record = buildRecord();
record.setFileStatus("parsed_success");
record.setSourceType("HISTORY_IMPORT");
when(recordMapper.selectById(RECORD_ID)).thenReturn(record);
ServiceException exception = assertThrows(ServiceException.class,
() -> service.deleteFileUploadRecord(RECORD_ID, 9527L));
assertTrue(exception.getMessage().contains("历史导入文件不支持删除"));
verify(lsfxClient, never()).deleteFiles(any());
}
@Test
void deleteFileUploadRecord_shouldStopWhenLsfxDeleteFails() {
CcdiFileUploadRecord record = buildRecord();

View File

@@ -2,11 +2,13 @@ package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
@@ -23,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -46,6 +49,9 @@ class CcdiProjectHistoryImportServiceImplTest {
@Mock
private Executor fileUploadExecutor;
@Mock
private ICcdiBankTagService bankTagService;
@Test
void shouldFilterStatementsByTrxDateAndDeduplicateAcrossSourceProjects() {
CcdiProjectImportHistoryDTO dto = buildImportDto();
@@ -126,6 +132,30 @@ class CcdiProjectHistoryImportServiceImplTest {
assertNotEquals(sourceRecord.getLogId(), insertedRecords.get().get(0).getLogId());
}
@Test
void shouldRefreshTargetCountAndSubmitAutoRebuildAfterImport() {
CcdiProjectImportHistoryDTO dto = buildImportDto();
when(recordMapper.selectSuccessfulRecordsByProjectIds(dto.getSourceProjectIds()))
.thenReturn(List.of(buildSourceRecord(11L, 101, "批次A")));
when(bankStatementMapper.selectStatementsForHistoryImport(11L, 101, "2026-01-01", "2026-01-31"))
.thenReturn(List.of(buildStatement("2026-01-12", "6444", "300.00", "备注C")));
doAnswer(invocation -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return null;
}).when(fileUploadExecutor).execute(any(Runnable.class));
when(projectMapper.selectById(11L)).thenReturn(buildProject(11L, "历史项目A"));
when(projectMapper.selectById(90L)).thenReturn(buildProject(90L, "新项目"));
when(bankStatementMapper.countMatchedStaffCountByProjectId(90L)).thenReturn(3);
service.submitImport(90L, 3001, dto, "tester");
verify(projectMapper).updateById(org.mockito.ArgumentMatchers.<CcdiProject>argThat(project ->
Long.valueOf(90L).equals(project.getProjectId()) && Integer.valueOf(3).equals(project.getTargetCount())
));
verify(bankTagService).submitAutoRebuild(90L, TriggerType.AUTO_BATCH_UPLOAD);
}
private CcdiProjectImportHistoryDTO buildImportDto() {
CcdiProjectImportHistoryDTO dto = new CcdiProjectImportHistoryDTO();
dto.setProjectName("新项目");

View File

@@ -50,3 +50,10 @@
-`CcdiProjectHistoryImportServiceImpl` 扩展为真实异步复制链路:读取成功批次、按新批次号重建目标流水、按来源字段生成历史导入文件记录,并在导入前做内存去重
- 新增 `CcdiProjectHistoryImportServiceImplTest` 与 XML 断言,验证日期范围传递、跨来源批次去重、新批次号生成及来源标识写入
- 验证命令:`mvn -pl ccdi-project -am -Dtest=CcdiProjectHistoryImportServiceImplTest,CcdiFileUploadServiceImplTest,CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`
### 2026-03-29 Task 5 后端自动打标与删除拦截收口
-`CcdiProjectHistoryImportServiceImpl` 中补齐目标人数刷新与 `bankTagService.submitAutoRebuild(targetProjectId, TriggerType.AUTO_BATCH_UPLOAD)` 调用
-`CcdiFileUploadServiceImpl#validateDeleteRecord` 中优先拦截 `sourceType = HISTORY_IMPORT` 的记录,返回“历史导入文件不支持删除”
- 扩展历史导入服务测试与文件删除测试,锁定自动重算与禁删行为
- 完成后端回归验证:`mvn -pl ccdi-project -am -Dtest=CcdiProjectControllerTest,CcdiProjectControllerContractTest,CcdiProjectServiceImplTest,CcdiProjectHistoryImportServiceImplTest,CcdiFileUploadServiceImplTest,CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`