实现项目打标状态联动并执行前后端适配

This commit is contained in:
wkc
2026-03-18 15:55:55 +08:00
parent e9394939c9
commit c0ce5ca7f9
27 changed files with 651 additions and 14 deletions

View File

@@ -0,0 +1,15 @@
package com.ruoyi.ccdi.project.constants;
/**
* 项目状态常量
*/
public final class CcdiProjectStatusConstants {
public static final String PROCESSING = "0";
public static final String COMPLETED = "1";
public static final String ARCHIVED = "2";
public static final String TAGGING = "3";
private CcdiProjectStatusConstants() {
}
}

View File

@@ -32,7 +32,7 @@ public class CcdiProject implements Serializable {
/** 配置方式default-全局默认custom-自定义 */
private String configType;
/** 项目状态0-进行中1-已完成2-已归档 */
/** 项目状态0-进行中1-已完成2-已归档3-打标中 */
private String status;
/** 是否归档0-未归档1-已归档 */

View File

@@ -20,4 +20,7 @@ public class CcdiProjectStatusCountsVO {
/** 已归档项目数(状态2) */
private Long status2;
/** 打标中项目数(状态3) */
private Long status3;
}

View File

@@ -59,4 +59,28 @@ public interface ICcdiProjectService {
* @return 状态统计
*/
CcdiProjectStatusCountsVO getStatusCounts();
/**
* 更新项目状态
*
* @param projectId 项目ID
* @param status 状态编码
* @param operator 操作人
*/
void updateProjectStatus(Long projectId, String status, String operator);
/**
* 校验项目是否允许进入打标流程
*
* @param projectId 项目ID
*/
void ensureProjectCanStartTagging(Long projectId);
/**
* 校验项目是否允许写入
*
* @param projectId 项目ID
* @param message 拒绝文案
*/
void ensureProjectWritable(Long projectId, String message);
}

View File

@@ -1,5 +1,6 @@
package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
import com.ruoyi.ccdi.project.domain.dto.CcdiBankTagRebuildDTO;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagResult;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagRule;
@@ -13,6 +14,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiBankTagService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -54,6 +56,9 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
@Resource
private BankTagRuleConfigResolver configResolver;
@Resource
private ICcdiProjectService projectService;
@Resource
@Qualifier("tagRuleExecutor")
private Executor tagRuleExecutor;
@@ -88,6 +93,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
*/
public Long rebuildProject(Long projectId, String modelCode, String operator, TriggerType triggerType) {
long taskStartTime = System.currentTimeMillis();
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.TAGGING, operator);
CcdiBankTagTask task = buildRunningTask(projectId, modelCode, operator, triggerType);
taskMapper.insertTask(task);
log.info("【流水标签】任务创建成功: taskId={}, projectId={}, modelCode={}, triggerType={}, operator={}",
@@ -128,6 +134,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
task.setUpdateBy(operator);
task.setUpdateTime(new Date());
taskMapper.updateTask(task);
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.COMPLETED, operator);
log.info("【流水标签】任务执行成功: taskId={}, projectId={}, modelCode={}, triggerType={}, ruleCount={}, hitCount={}, costMs={}",
task.getId(), projectId, modelCode, triggerType, rules.size(), allResults.size(),
System.currentTimeMillis() - taskStartTime);
@@ -140,6 +147,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
task.setUpdateBy(operator);
task.setUpdateTime(new Date());
taskMapper.updateTask(task);
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.PROCESSING, operator);
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
throw ex;

View File

@@ -15,6 +15,7 @@ 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.ICcdiFileUploadService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.constants.LsfxConstants;
import com.ruoyi.lsfx.domain.request.FetchInnerFlowRequest;
@@ -96,6 +97,9 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
@Resource
private ICcdiBankTagService bankTagService;
@Resource
private ICcdiProjectService projectService;
/**
* 获取临时文件存储目录
*/
@@ -165,6 +169,8 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
throw new IllegalArgumentException("开始日期不能晚于结束日期");
}
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
CcdiProject project = projectMapper.selectById(projectId);
if (project == null) {
throw new IllegalArgumentException("项目不存在: projectId=" + projectId);
@@ -311,6 +317,8 @@ public class CcdiFileUploadServiceImpl implements ICcdiFileUploadService {
log.info("【文件上传】开始批量上传: projectId={}, 文件数量={}, username={}",
projectId, files.length, username);
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
// 1. 生成批次ID
String batchId = UUID.randomUUID().toString().replace("-", "");

View File

@@ -17,6 +17,7 @@ import com.ruoyi.ccdi.project.domain.vo.ModelGroupVO;
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiModelParamService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,6 +47,9 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
@Resource
private CcdiProjectMapper projectMapper;
@Resource
private ICcdiProjectService projectService;
@Override
public List<ModelListVO> selectModelList(Long projectId) {
log.info("selectModelList 被调用projectId={}", projectId);
@@ -102,6 +106,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
Long projectId = saveDTO.getProjectId();
if (projectId > 0) {
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
}
@@ -178,6 +183,7 @@ public class CcdiModelParamServiceImpl implements ICcdiModelParamService {
Long projectId = saveAllDTO.getProjectId();
if (projectId > 0) {
projectService.ensureProjectWritable(projectId, "当前项目正在进行银行流水打标,暂不允许修改参数");
switchToCustomConfigIfNeeded(getRequiredProject(projectId));
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.ccdi.project.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
@@ -18,6 +19,8 @@ import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* 项目Service实现类
*
@@ -43,7 +46,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
BeanUtils.copyProperties(dto, project);
// 3. 设置默认值和流水分析平台ID
project.setStatus("0"); // 进行中
project.setStatus(CcdiProjectStatusConstants.PROCESSING);
project.setIsArchived(0); // 未归档
project.setTargetCount(0);
project.setHighRiskCount(0);
@@ -115,27 +118,70 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
// 统计进行中项目状态0
Long status0Count = projectMapper.selectCount(
new LambdaQueryWrapper<CcdiProject>()
.eq(CcdiProject::getStatus, "0")
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING)
);
vo.setStatus0(status0Count);
// 统计已完成项目状态1
Long status1Count = projectMapper.selectCount(
new LambdaQueryWrapper<CcdiProject>()
.eq(CcdiProject::getStatus, "1")
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED)
);
vo.setStatus1(status1Count);
// 统计已归档项目状态2
Long status2Count = projectMapper.selectCount(
new LambdaQueryWrapper<CcdiProject>()
.eq(CcdiProject::getStatus, "2")
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED)
);
vo.setStatus2(status2Count);
Long status3Count = projectMapper.selectCount(
new LambdaQueryWrapper<CcdiProject>()
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING)
);
vo.setStatus3(status3Count);
return vo;
}
@Override
public void updateProjectStatus(Long projectId, String status, String operator) {
CcdiProject project = getRequiredProject(projectId);
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())
&& !CcdiProjectStatusConstants.ARCHIVED.equals(status)) {
throw new ServiceException("已归档项目不允许重新进入打标流程");
}
project.setStatus(status);
project.setUpdateBy(operator);
project.setUpdateTime(new Date());
projectMapper.updateById(project);
}
@Override
public void ensureProjectCanStartTagging(Long projectId) {
CcdiProject project = getRequiredProject(projectId);
if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
throw new ServiceException("已归档项目不允许重新进入打标流程");
}
}
@Override
public void ensureProjectWritable(Long projectId, String message) {
CcdiProject project = getRequiredProject(projectId);
if (CcdiProjectStatusConstants.TAGGING.equals(project.getStatus())) {
throw new ServiceException(message);
}
}
private CcdiProject getRequiredProject(Long projectId) {
CcdiProject project = projectMapper.selectById(projectId);
if (project == null) {
throw new ServiceException("项目不存在");
}
return project;
}
/**
* 调用流水分析平台获取projectId
*

View File

@@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@@ -27,6 +28,9 @@ public class ProjectBankTagRebuildCoordinator {
@Resource
private CcdiBankTagServiceImpl bankTagService;
@Resource
private ICcdiProjectService projectService;
/**
* 提交手动重算
*
@@ -43,6 +47,7 @@ public class ProjectBankTagRebuildCoordinator {
throw new ServiceException("当前项目标签正在重算中,请稍后再试");
}
projectService.ensureProjectCanStartTagging(projectId);
executeWithLock(projectId, () -> bankTagService.rebuildProject(projectId, modelCode, operator, TriggerType.MANUAL));
}
@@ -62,6 +67,7 @@ public class ProjectBankTagRebuildCoordinator {
}
executeWithLock(projectId, () -> {
projectService.ensureProjectCanStartTagging(projectId);
boolean needRerun;
do {
Long taskId = bankTagService.rebuildProject(projectId, null, "system", triggerType);

View File

@@ -12,6 +12,7 @@ import com.ruoyi.ccdi.project.mapper.CcdiBankTagAnalysisMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagRuleMapper;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
@@ -56,6 +57,9 @@ class CcdiBankTagServiceImplTest {
@Mock
private BankTagRuleConfigResolver configResolver;
@Mock
private ICcdiProjectService projectService;
@Test
void rebuildProject_shouldDeleteOldResultsBeforeSubmittingRuleTasks() {
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
@@ -218,6 +222,42 @@ class CcdiBankTagServiceImplTest {
verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus()) && task.getFailedRuleCount() == 0));
}
@Test
void shouldMarkProjectTaggingBeforeExecutingAndCompletedAfterSuccess() {
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
"HOUSE_OR_CAR_EXPENSE", "房车消费支出交易", "STATEMENT");
BankTagRuleExecutionConfig config = buildConfig(40L, rule);
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
when(configResolver.resolve(40L, rule)).thenReturn(config);
when(analysisMapper.selectHouseOrCarExpenseStatements(40L)).thenReturn(List.of());
service.rebuildProject(40L, null, "tester", TriggerType.MANUAL);
InOrder inOrder = inOrder(projectService, taskMapper);
inOrder.verify(projectService).updateProjectStatus(40L, "3", "tester");
inOrder.verify(taskMapper).updateTask(argThat(task -> "SUCCESS".equals(task.getStatus())));
inOrder.verify(projectService).updateProjectStatus(40L, "1", "tester");
}
@Test
void shouldRollbackProjectStatusToProcessingWhenRebuildFails() {
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
"HOUSE_OR_CAR_EXPENSE", "房车消费支出交易", "STATEMENT");
when(ruleMapper.selectEnabledRules(null)).thenReturn(List.of(rule));
when(configResolver.resolve(40L, rule)).thenThrow(new RuntimeException("threshold missing"));
assertThrows(RuntimeException.class,
() -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));
verify(projectService).updateProjectStatus(40L, "0", "tester");
}
private CcdiBankTagRule buildRule(String modelCode, String modelName, String ruleCode, String ruleName, String resultType) {
CcdiBankTagRule rule = new CcdiBankTagRule();
rule.setModelCode(modelCode);

View File

@@ -12,6 +12,8 @@ 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.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.GetBankStatementRequest;
import com.ruoyi.lsfx.domain.response.CheckParseStatusResponse;
@@ -89,6 +91,9 @@ class CcdiFileUploadServiceImplTest {
@Mock
private ICcdiBankTagService bankTagService;
@Mock
private ICcdiProjectService projectService;
@TempDir
Path tempDir;
@@ -154,6 +159,38 @@ class CcdiFileUploadServiceImplTest {
}
}
@Test
void shouldRejectPullBankInfoWhenProjectIsTagging() {
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许上传或拉取数据"))
.when(projectService).ensureProjectWritable(PROJECT_ID, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
assertThrows(ServiceException.class,
() -> service.submitPullBankInfo(
PROJECT_ID,
List.of("3301"),
"2026-01-01",
"2026-01-31",
1L,
"tester"
));
}
@Test
void shouldRejectBatchUploadWhenProjectIsTagging() {
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许上传或拉取数据"))
.when(projectService).ensureProjectWritable(PROJECT_ID, "当前项目正在进行银行流水打标,暂不允许上传或拉取数据");
MockMultipartFile file = new MockMultipartFile(
"files",
"test.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"content".getBytes()
);
assertThrows(ServiceException.class,
() -> service.batchUploadFiles(PROJECT_ID, new MultipartFile[]{file}, "tester"));
}
@Test
void submitTasksAsync_shouldNotCreateLocalBatchLogFiles() throws Exception {
setField("uploadPath", tempDir.toString());

View File

@@ -2,10 +2,15 @@ package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiModelParam;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.ModelParamGroupDTO;
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveDTO;
import com.ruoyi.ccdi.project.domain.dto.ModelParamSaveAllDTO;
import com.ruoyi.ccdi.project.domain.dto.ParamValueItem;
import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO;
import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -18,6 +23,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
@@ -37,6 +43,9 @@ class CcdiModelParamServiceImplTest {
@Mock
private CcdiProjectMapper projectMapper;
@Mock
private ICcdiProjectService projectService;
@Test
void selectAllParams_shouldReadSystemDefaultsForDefaultProject() {
CcdiProject project = new CcdiProject();
@@ -95,6 +104,30 @@ class CcdiModelParamServiceImplTest {
verify(modelParamMapper).updateParamValue(123L, "LARGE_TRANSACTION", "SINGLE_TRANSACTION_AMOUNT", "2222", "admin");
}
@Test
void shouldRejectSaveAllParamsWhenProjectIsTagging() {
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许修改参数"))
.when(projectService).ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数");
assertThrows(ServiceException.class, () -> service.saveAllParams(buildSaveAllDto()));
}
@Test
void shouldRejectSaveParamsWhenProjectIsTagging() {
ModelParamSaveDTO saveDTO = new ModelParamSaveDTO();
saveDTO.setProjectId(40L);
saveDTO.setModelCode("LARGE_TRANSACTION");
ModelParamSaveDTO.ParamValueItem item = new ModelParamSaveDTO.ParamValueItem();
item.setParamCode("SINGLE_TRANSACTION_AMOUNT");
item.setParamValue("2000");
saveDTO.setParams(List.of(item));
org.mockito.Mockito.doThrow(new ServiceException("当前项目正在进行银行流水打标,暂不允许修改参数"))
.when(projectService).ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数");
assertThrows(ServiceException.class, () -> service.saveParams(saveDTO));
}
private CcdiModelParam buildParam(
Long id,
Long projectId,
@@ -114,4 +147,19 @@ class CcdiModelParamServiceImplTest {
param.setSortOrder(1);
return param;
}
private ModelParamSaveAllDTO buildSaveAllDto() {
ParamValueItem item = new ParamValueItem();
item.setParamCode("SINGLE_TRANSACTION_AMOUNT");
item.setParamValue("2000");
ModelParamGroupDTO group = new ModelParamGroupDTO();
group.setModelCode("LARGE_TRANSACTION");
group.setParams(List.of(item));
ModelParamSaveAllDTO dto = new ModelParamSaveAllDTO();
dto.setProjectId(40L);
dto.setModels(List.of(group));
return dto;
}
}

View File

@@ -0,0 +1,61 @@
package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CcdiProjectServiceImplTest {
@InjectMocks
private CcdiProjectServiceImpl service;
@Mock
private CcdiProjectMapper projectMapper;
@Mock
private LsfxAnalysisClient lsfxAnalysisClient;
@Test
void shouldCountTaggingProjectsSeparately() {
when(projectMapper.selectCount(any())).thenReturn(10L, 3L, 4L, 2L, 1L);
CcdiProjectStatusCountsVO counts = service.getStatusCounts();
assertEquals(1L, counts.getStatus3());
}
@Test
void shouldRejectUpdatingArchivedProjectToTagging() {
CcdiProject archived = new CcdiProject();
archived.setProjectId(99L);
archived.setStatus("2");
when(projectMapper.selectById(99L)).thenReturn(archived);
assertThrows(ServiceException.class,
() -> service.updateProjectStatus(99L, "3", "system"));
}
@Test
void shouldRejectWritingWhenProjectIsTagging() {
CcdiProject tagging = new CcdiProject();
tagging.setProjectId(40L);
tagging.setStatus("3");
when(projectMapper.selectById(40L)).thenReturn(tagging);
assertThrows(ServiceException.class,
() -> service.ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数"));
}
}

View File

@@ -6,6 +6,7 @@ import ch.qos.logback.core.read.ListAppender;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.enums.TriggerType;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -33,6 +34,9 @@ class ProjectBankTagRebuildCoordinatorTest {
@Mock
private CcdiBankTagServiceImpl bankTagService;
@Mock
private ICcdiProjectService projectService;
@Test
void submitManualRebuild_shouldRejectWhenProjectAlreadyRunning() {
CcdiBankTagTask runningTask = new CcdiBankTagTask();
@@ -109,4 +113,13 @@ class ProjectBankTagRebuildCoordinatorTest {
logger.detachAppender(logAppender);
}
}
@Test
void shouldRejectSubmittingRebuildForArchivedProject() {
org.mockito.Mockito.doThrow(new ServiceException("已归档项目不允许重新进入打标流程"))
.when(projectService).ensureProjectCanStartTagging(40L);
assertThrows(ServiceException.class,
() -> coordinator.submitManual(40L, null, "tester"));
}
}

View File

@@ -0,0 +1,25 @@
package com.ruoyi.ccdi.project.sql;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiProjectStatusSqlTest {
@Test
void shouldContainTaggingStatusInInitAndMigrationSql() throws IOException {
Path repoRoot = Path.of("..");
String initSql = Files.readString(repoRoot.resolve("sql/ccdi_project.sql"));
String migrationSql = Files.readString(repoRoot.resolve("sql/migration/2026-03-18-add-project-tagging-status.sql"));
assertTrue(initSql.contains("打标中"));
assertTrue(initSql.contains("'3'"));
assertTrue(migrationSql.contains("ccdi_project_status"));
assertTrue(migrationSql.contains("打标中"));
assertTrue(migrationSql.contains("'3'"));
}
}