Merge branch 'codex/project-upload-file-delete-backend' into dev
This commit is contained in:
@@ -163,4 +163,15 @@ public class CcdiFileUploadController extends BaseController {
|
||||
CcdiFileUploadRecord record = fileUploadService.getById(id);
|
||||
return AjaxResult.success(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除上传记录
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除上传文件", description = "按上传记录ID删除文件并清理流水")
|
||||
public AjaxResult deleteFile(@PathVariable Long id) {
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
String message = fileUploadService.deleteFileUploadRecord(id, userId);
|
||||
return AjaxResult.success(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ public class CcdiFileUploadStatisticsVO implements Serializable {
|
||||
/** 解析失败数量 */
|
||||
private Long parsedFailed;
|
||||
|
||||
/** 已删除数量 */
|
||||
private Long deleted;
|
||||
|
||||
/** 总数量 */
|
||||
private Long total;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,15 @@ public interface ICcdiFileUploadService {
|
||||
Long userId,
|
||||
String username);
|
||||
|
||||
/**
|
||||
* 删除上传记录并清理关联数据
|
||||
*
|
||||
* @param id 上传记录ID
|
||||
* @param operatorUserId 当前操作用户ID
|
||||
* @return 删除结果
|
||||
*/
|
||||
String deleteFileUploadRecord(Long id, Long operatorUserId);
|
||||
|
||||
/**
|
||||
* 查询上传记录列表
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.ruoyi.lsfx.constants.LsfxConstants;
|
||||
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
|
||||
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
|
||||
import com.ruoyi.lsfx.domain.request.GetFileUploadStatusRequest;
|
||||
import com.ruoyi.lsfx.domain.request.DeleteFilesRequest;
|
||||
import com.ruoyi.lsfx.domain.response.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
@@ -207,6 +208,30 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
return batchId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteFileUploadRecord(Long id, Long operatorUserId) {
|
||||
CcdiFileUploadRecord record = recordMapper.selectById(id);
|
||||
validateDeleteRecord(record);
|
||||
|
||||
DeleteFilesRequest request = new DeleteFilesRequest();
|
||||
request.setGroupId(record.getLsfxProjectId());
|
||||
request.setLogIds(new Integer[]{record.getLogId()});
|
||||
request.setUserId(toUploadUserId(operatorUserId));
|
||||
|
||||
DeleteFilesResponse response = lsfxClient.deleteFiles(request);
|
||||
if (response == null || Boolean.FALSE.equals(response.getSuccessResponse())) {
|
||||
throw new RuntimeException("流水分析平台删除文件失败");
|
||||
}
|
||||
|
||||
bankStatementMapper.deleteByProjectIdAndBatchId(record.getProjectId(), record.getLogId());
|
||||
|
||||
CcdiFileUploadRecord update = new CcdiFileUploadRecord();
|
||||
update.setId(record.getId());
|
||||
update.setFileStatus("deleted");
|
||||
recordMapper.updateById(update);
|
||||
return "删除成功";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<CcdiFileUploadRecord> selectPage(Page<CcdiFileUploadRecord> page,
|
||||
CcdiFileUploadQueryDTO queryDTO) {
|
||||
@@ -249,6 +274,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
vo.setParsing(0L);
|
||||
vo.setParsedSuccess(0L);
|
||||
vo.setParsedFailed(0L);
|
||||
vo.setDeleted(0L);
|
||||
|
||||
long total = 0L;
|
||||
for (Map<String, Object> item : statusCounts) {
|
||||
@@ -261,6 +287,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
case "parsing" -> vo.setParsing(count);
|
||||
case "parsed_success" -> vo.setParsedSuccess(count);
|
||||
case "parsed_failed" -> vo.setParsedFailed(count);
|
||||
case "deleted" -> vo.setDeleted(count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,6 +888,24 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
|
||||
bankStatementMapper.deleteByProjectIdAndBatchId(projectId, logId);
|
||||
}
|
||||
|
||||
private void validateDeleteRecord(CcdiFileUploadRecord record) {
|
||||
if (record == null) {
|
||||
throw new RuntimeException("上传记录不存在");
|
||||
}
|
||||
if (!"parsed_success".equals(record.getFileStatus())) {
|
||||
if ("deleted".equals(record.getFileStatus())) {
|
||||
throw new RuntimeException("文件已删除,请勿重复操作");
|
||||
}
|
||||
throw new RuntimeException("仅支持删除解析成功文件");
|
||||
}
|
||||
if (record.getLsfxProjectId() == null) {
|
||||
throw new RuntimeException("缺少流水分析项目ID");
|
||||
}
|
||||
if (record.getLogId() == null) {
|
||||
throw new RuntimeException("缺少文件logId");
|
||||
}
|
||||
}
|
||||
|
||||
private Integer toUploadUserId(Long userId) {
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("当前登录用户ID不能为空");
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -64,4 +65,19 @@ class CcdiFileUploadControllerTest {
|
||||
assertEquals(200, result.get("code"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteFile_shouldUseCurrentLoginUserId() {
|
||||
try (MockedStatic<SecurityUtils> mocked = mockStatic(SecurityUtils.class)) {
|
||||
mocked.when(SecurityUtils::getUserId).thenReturn(9527L);
|
||||
when(fileUploadService.deleteFileUploadRecord(123L, 9527L))
|
||||
.thenReturn("删除成功");
|
||||
|
||||
AjaxResult result = controller.deleteFile(123L);
|
||||
|
||||
assertEquals(200, result.get("code"));
|
||||
assertEquals("删除成功", result.get("msg"));
|
||||
verify(fileUploadService).deleteFileUploadRecord(123L, 9527L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
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.vo.CcdiFileUploadStatisticsVO;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
|
||||
@@ -12,6 +13,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
|
||||
import com.ruoyi.lsfx.domain.response.CheckParseStatusResponse;
|
||||
import com.ruoyi.lsfx.domain.response.DeleteFilesResponse;
|
||||
import com.ruoyi.lsfx.domain.response.FetchInnerFlowResponse;
|
||||
import com.ruoyi.lsfx.domain.response.GetBankStatementResponse;
|
||||
import com.ruoyi.lsfx.domain.response.GetFileUploadStatusResponse;
|
||||
@@ -37,6 +39,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -47,7 +50,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -255,6 +260,61 @@ class CcdiFileUploadServiceImplTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteFileUploadRecord_shouldDeletePlatformFileBankStatementsAndMarkDeleted() {
|
||||
CcdiFileUploadRecord record = buildRecord();
|
||||
record.setProjectId(PROJECT_ID);
|
||||
record.setLsfxProjectId(LSFX_PROJECT_ID);
|
||||
record.setLogId(LOG_ID);
|
||||
record.setFileStatus("parsed_success");
|
||||
|
||||
when(recordMapper.selectById(RECORD_ID)).thenReturn(record);
|
||||
when(lsfxClient.deleteFiles(any())).thenReturn(buildDeleteFilesResponse());
|
||||
|
||||
String result = service.deleteFileUploadRecord(RECORD_ID, 9527L);
|
||||
|
||||
assertEquals("删除成功", result);
|
||||
verify(lsfxClient).deleteFiles(argThat(request ->
|
||||
request.getGroupId().equals(LSFX_PROJECT_ID)
|
||||
&& request.getUserId().equals(9527)
|
||||
&& request.getLogIds().length == 1
|
||||
&& request.getLogIds()[0].equals(LOG_ID)
|
||||
));
|
||||
verify(bankStatementMapper).deleteByProjectIdAndBatchId(PROJECT_ID, LOG_ID);
|
||||
verify(recordMapper).updateById(org.mockito.ArgumentMatchers.<CcdiFileUploadRecord>argThat(item ->
|
||||
RECORD_ID.equals(item.getId()) && "deleted".equals(item.getFileStatus())
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteFileUploadRecord_shouldRejectNonParsedSuccessStatus() {
|
||||
CcdiFileUploadRecord record = buildRecord();
|
||||
record.setFileStatus("parsed_failed");
|
||||
when(recordMapper.selectById(RECORD_ID)).thenReturn(record);
|
||||
|
||||
RuntimeException exception = assertThrows(RuntimeException.class,
|
||||
() -> service.deleteFileUploadRecord(RECORD_ID, 9527L));
|
||||
|
||||
assertTrue(exception.getMessage().contains("仅支持删除解析成功文件"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteFileUploadRecord_shouldStopWhenLsfxDeleteFails() {
|
||||
CcdiFileUploadRecord record = buildRecord();
|
||||
record.setFileStatus("parsed_success");
|
||||
record.setLogId(LOG_ID);
|
||||
record.setLsfxProjectId(LSFX_PROJECT_ID);
|
||||
when(recordMapper.selectById(RECORD_ID)).thenReturn(record);
|
||||
when(lsfxClient.deleteFiles(any())).thenThrow(new RuntimeException("lsfx delete failed"));
|
||||
|
||||
assertThrows(RuntimeException.class, () -> service.deleteFileUploadRecord(RECORD_ID, 9527L));
|
||||
|
||||
verify(bankStatementMapper, never()).deleteByProjectIdAndBatchId(any(), any());
|
||||
verify(recordMapper, never()).updateById(org.mockito.ArgumentMatchers.<CcdiFileUploadRecord>argThat(item ->
|
||||
"deleted".equals(item.getFileStatus())
|
||||
));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// void processPullBankInfoAsync_shouldMarkParsedFailedWhenFetchInnerFlowThrows() {
|
||||
// when(lsfxClient.fetchInnerFlow(any())).thenThrow(new RuntimeException("fetch inner flow failed"));
|
||||
@@ -414,6 +474,20 @@ class CcdiFileUploadServiceImplTest {
|
||||
assertFalse(events.stream().anyMatch(event -> event.endsWith("record:parsed_success")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void countByStatus_shouldIncludeDeletedCount() {
|
||||
when(recordMapper.countByStatus(PROJECT_ID)).thenReturn(List.of(
|
||||
Map.of("status", "uploading", "count", 1),
|
||||
Map.of("status", "deleted", "count", 2)
|
||||
));
|
||||
|
||||
CcdiFileUploadStatisticsVO result = service.countByStatus(PROJECT_ID);
|
||||
|
||||
assertEquals(1L, result.getUploading());
|
||||
assertEquals(2L, result.getDeleted());
|
||||
assertEquals(3L, result.getTotal());
|
||||
}
|
||||
|
||||
private void captureRecordStatus(List<String> events, AtomicInteger sequence) {
|
||||
doAnswer(invocation -> {
|
||||
CcdiFileUploadRecord record = invocation.getArgument(0);
|
||||
@@ -493,6 +567,12 @@ class CcdiFileUploadServiceImplTest {
|
||||
return response;
|
||||
}
|
||||
|
||||
private DeleteFilesResponse buildDeleteFilesResponse() {
|
||||
DeleteFilesResponse response = new DeleteFilesResponse();
|
||||
response.setSuccessResponse(Boolean.TRUE);
|
||||
return response;
|
||||
}
|
||||
|
||||
private GetFileUploadStatusResponse buildParsedSuccessStatusResponse(String uploadFileName) {
|
||||
GetFileUploadStatusResponse response = buildParsedSuccessStatusResponse();
|
||||
response.getData().getLogs().get(0).setUploadFileName(uploadFileName);
|
||||
|
||||
Reference in New Issue
Block a user