实现项目归档功能

This commit is contained in:
wkc
2026-03-24 21:45:55 +08:00
parent bb49d78a3a
commit 294164a504
23 changed files with 680 additions and 87 deletions

View File

@@ -14,6 +14,7 @@ import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import com.ruoyi.common.utils.SecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -53,6 +54,17 @@ public class CcdiProjectController extends BaseController {
return AjaxResult.success("项目更新成功", vo);
}
/**
* 归档项目
*/
@PostMapping("/{projectId}/archive")
@Operation(summary = "归档项目")
@PreAuthorize("@ss.hasPermi('ccdi:project:edit')")
public AjaxResult archiveProject(@PathVariable Long projectId) {
projectService.archiveProject(projectId, SecurityUtils.getUsername());
return AjaxResult.success("项目归档成功");
}
/**
* 删除项目
*/

View File

@@ -60,6 +60,14 @@ public interface ICcdiProjectService {
*/
CcdiProjectStatusCountsVO getStatusCounts();
/**
* 归档项目
*
* @param projectId 项目ID
* @param operator 操作人
*/
void archiveProject(Long projectId, String operator);
/**
* 更新项目状态
*
@@ -76,6 +84,14 @@ public interface ICcdiProjectService {
*/
void ensureProjectCanStartTagging(Long projectId);
/**
* 校验项目是否未归档
*
* @param projectId 项目ID
* @param message 拒绝文案
*/
void ensureProjectNotArchived(Long projectId, String message);
/**
* 校验项目是否允许写入
*

View File

@@ -169,6 +169,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
throw new IllegalArgumentException("开始日期不能晚于结束日期");
}
projectService.ensureProjectNotArchived(projectId, "已归档项目暂不允许上传或拉取数据");
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
CcdiProject project = projectMapper.selectById(projectId);
@@ -323,6 +324,7 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
log.info("【文件上传】开始批量上传: projectId={}, 文件数量={}, username={}",
projectId, files.length, username);
projectService.ensureProjectNotArchived(projectId, "已归档项目暂不允许上传或拉取数据");
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
// 1. 生成批次ID

View File

@@ -111,6 +111,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
Long projectId = saveDTO.getProjectId();
if (projectId > 0) {
projectService.ensureProjectNotArchived(projectId, "已归档项目暂不允许修改参数");
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
}
@@ -192,6 +193,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
Long projectId = saveAllDTO.getProjectId();
if (projectId > 0) {
projectService.ensureProjectNotArchived(projectId, "已归档项目暂不允许修改参数");
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
}

View File

@@ -152,6 +152,26 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
return vo;
}
@Override
public void archiveProject(Long projectId, String operator) {
CcdiProject project = getRequiredProject(projectId);
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
throw new ServiceException("项目已归档,无需重复操作");
}
if (!CcdiProjectStatusConstants.COMPLETED.equals(project.getStatus())) {
throw new ServiceException("仅已完成项目允许归档");
}
project.setStatus(CcdiProjectStatusConstants.ARCHIVED);
project.setIsArchived(1);
project.setUpdateBy(operator);
project.setUpdateTime(new Date());
projectMapper.updateById(project);
log.info("【项目】项目状态变更: projectId={}, projectName={}, oldStatus={}, oldStatusLabel={}, newStatus={}, newStatusLabel={}, operator={}",
project.getProjectId(), project.getProjectName(), CcdiProjectStatusConstants.COMPLETED,
resolveStatusLabel(CcdiProjectStatusConstants.COMPLETED), CcdiProjectStatusConstants.ARCHIVED,
resolveStatusLabel(CcdiProjectStatusConstants.ARCHIVED), resolveOperator(operator));
}
@Override
public void updateProjectStatus(Long projectId, String status, String operator) {
CcdiProject project = getRequiredProject(projectId);
@@ -179,6 +199,14 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
}
}
@Override
public void ensureProjectNotArchived(Long projectId, String message) {
CcdiProject project = getRequiredProject(projectId);
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
throw new ServiceException(message);
}
}
@Override
public void ensureProjectWritable(Long projectId, String message) {
CcdiProject project = getRequiredProject(projectId);

View File

@@ -0,0 +1,33 @@
package com.ruoyi.ccdi.project.controller;
import io.swagger.v3.oas.annotations.Operation;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class CcdiProjectControllerContractTest {
@Test
void shouldExposeArchiveProjectEndpointContract() throws Exception {
RequestMapping requestMapping = CcdiProjectController.class.getAnnotation(RequestMapping.class);
Method method = CcdiProjectController.class.getMethod("archiveProject", Long.class);
PostMapping postMapping = method.getAnnotation(PostMapping.class);
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(requestMapping);
assertEquals("/ccdi/project", requestMapping.value()[0]);
assertNotNull(postMapping);
assertEquals("/{projectId}/archive", postMapping.value()[0]);
assertNotNull(preAuthorize);
assertEquals("@ss.hasPermi('ccdi:project:edit')", preAuthorize.value());
assertNotNull(operation);
assertEquals("归档项目", operation.summary());
}
}

View File

@@ -0,0 +1,55 @@
package com.ruoyi.ccdi.project.controller;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class CcdiProjectControllerTest {
@InjectMocks
private CcdiProjectController controller;
@Mock
private ICcdiProjectService projectService;
@Test
void shouldArchiveProject() throws Exception {
try (MockedStatic<com.ruoyi.common.utils.SecurityUtils> mocked = mockStatic(com.ruoyi.common.utils.SecurityUtils.class)) {
mocked.when(com.ruoyi.common.utils.SecurityUtils::getUsername).thenReturn("tester");
AjaxResult result = controller.archiveProject(40L);
assertEquals(200, result.get("code"));
assertEquals("项目归档成功", result.get("msg"));
verify(projectService).archiveProject(40L, "tester");
}
Method method = CcdiProjectController.class.getMethod("archiveProject", Long.class);
PostMapping postMapping = method.getAnnotation(PostMapping.class);
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(postMapping);
assertEquals("/{projectId}/archive", postMapping.value()[0]);
assertNotNull(preAuthorize);
assertEquals("@ss.hasPermi('ccdi:project:edit')", preAuthorize.value());
assertNotNull(operation);
assertEquals("归档项目", operation.summary());
}
}

View File

@@ -154,6 +154,8 @@ class CcdiFileUploadServiceImplTest {
assertEquals("admin", inserted.get().get(0).getUploadUser());
assertEquals("uploading", inserted.get().get(0).getFileStatus());
assertEquals(1, TransactionSynchronizationManager.getSynchronizations().size());
verify(projectService).ensureProjectNotArchived(PROJECT_ID, "已归档项目暂不允许上传或拉取数据");
verify(projectService).ensureProjectWritable(PROJECT_ID, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
} finally {
TransactionSynchronizationManager.clearSynchronization();
}

View File

@@ -152,6 +152,7 @@ class CcdiModelParamServiceImplTest {
service.saveAllParams(buildSaveAllDto());
}
verify(projectService).ensureProjectNotArchived(40L, "已归档项目暂不允许修改参数");
verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_PARAM_CHANGE);
}
@@ -178,6 +179,7 @@ class CcdiModelParamServiceImplTest {
service.saveParams(saveDTO);
}
verify(projectService).ensureProjectNotArchived(40L, "已归档项目暂不允许修改参数");
verify(bankTagService).submitAutoRebuild(40L, TriggerType.AUTO_PARAM_CHANGE);
}

View File

@@ -23,6 +23,7 @@ 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.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -68,6 +69,43 @@ class CcdiProjectServiceImplTest {
() -> service.ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数"));
}
@Test
void shouldArchiveCompletedProject() {
CcdiProject project = new CcdiProject();
project.setProjectId(40L);
project.setProjectName("专案A");
project.setStatus("1");
project.setIsArchived(0);
when(projectMapper.selectById(40L)).thenReturn(project);
service.archiveProject(40L, "tester");
assertEquals("2", project.getStatus());
assertEquals(1, project.getIsArchived());
verify(projectMapper).updateById(project);
}
@Test
void shouldRejectArchivingProjectWhenStatusIsNotCompleted() {
CcdiProject project = new CcdiProject();
project.setProjectId(41L);
project.setStatus("0");
when(projectMapper.selectById(41L)).thenReturn(project);
assertThrows(ServiceException.class, () -> service.archiveProject(41L, "tester"));
}
@Test
void shouldRejectWritingWhenProjectIsArchived() {
CcdiProject archived = new CcdiProject();
archived.setProjectId(42L);
archived.setStatus("2");
when(projectMapper.selectById(42L)).thenReturn(archived);
assertThrows(ServiceException.class,
() -> service.ensureProjectNotArchived(42L, "已归档项目暂不允许修改参数"));
}
@Test
void shouldLogProjectInitialStatusWhenProjectIsCreated() {
CcdiProjectSaveDTO dto = new CcdiProjectSaveDTO();