Refactor project pages and update related docs
This commit is contained in:
@@ -9,6 +9,7 @@ public final class CcdiProjectStatusConstants {
|
||||
public static final String COMPLETED = "1";
|
||||
public static final String ARCHIVED = "2";
|
||||
public static final String TAGGING = "3";
|
||||
public static final String TAG_FAILED = "4";
|
||||
|
||||
private CcdiProjectStatusConstants() {
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class CcdiProject implements Serializable {
|
||||
/** 配置方式:default-全局默认,custom-自定义 */
|
||||
private String configType;
|
||||
|
||||
/** 项目状态:0-进行中,1-已完成,2-已归档,3-打标中 */
|
||||
/** 项目状态:0-进行中,1-已完成,2-已归档,3-打标中,4-打标失败 */
|
||||
private String status;
|
||||
|
||||
/** 是否归档:0-未归档,1-已归档 */
|
||||
|
||||
@@ -23,4 +23,7 @@ public class CcdiProjectStatusCountsVO {
|
||||
|
||||
/** 打标中项目数(状态3) */
|
||||
private Long status3;
|
||||
|
||||
/** 打标失败项目数(状态4) */
|
||||
private Long status4;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ public class CcdiProjectVO {
|
||||
/** 更新时间 */
|
||||
private Date updateTime;
|
||||
|
||||
/** 最近一次打标失败原因 */
|
||||
private String latestTagTaskErrorMessage;
|
||||
|
||||
/** 最近一次打标失败结束时间 */
|
||||
private Date latestTagTaskEndTime;
|
||||
|
||||
/** 创建者(用户名) */
|
||||
private String createBy;
|
||||
|
||||
|
||||
@@ -32,4 +32,12 @@ public interface CcdiBankTagTaskMapper extends BaseMapper<CcdiBankTagTask> {
|
||||
* @return 任务实体
|
||||
*/
|
||||
CcdiBankTagTask selectRunningTaskByProjectId(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询项目最近一次失败任务
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 任务实体
|
||||
*/
|
||||
CcdiBankTagTask selectLatestFailedTaskByProjectId(@Param("projectId") Long projectId);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public class CcdiBankTagServiceImpl implements ICcdiBankTagService {
|
||||
task.setUpdateBy(operator);
|
||||
task.setUpdateTime(new Date());
|
||||
updateFailedTaskSafely(task, ex);
|
||||
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.PROCESSING, operator);
|
||||
projectService.updateProjectStatus(projectId, CcdiProjectStatusConstants.TAG_FAILED, operator);
|
||||
log.error("【流水标签】任务执行失败: taskId={}, projectId={}, modelCode={}, triggerType={}, error={}",
|
||||
task.getId(), projectId, modelCode, triggerType, ex.getMessage(), ex);
|
||||
throw ex;
|
||||
|
||||
@@ -7,10 +7,12 @@ import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||
import com.ruoyi.ccdi.project.domain.event.CcdiProjectHistoryImportSubmittedEvent;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
@@ -43,6 +45,9 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
@Resource
|
||||
private CcdiProjectMapper projectMapper;
|
||||
|
||||
@Resource
|
||||
private CcdiBankTagTaskMapper bankTagTaskMapper;
|
||||
|
||||
@Resource
|
||||
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||
|
||||
@@ -77,6 +82,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
// 5. 返回VO
|
||||
CcdiProjectVO vo = new CcdiProjectVO();
|
||||
BeanUtils.copyProperties(project, vo);
|
||||
fillLatestTagFailure(project, vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -116,6 +122,7 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
}
|
||||
CcdiProjectVO vo = new CcdiProjectVO();
|
||||
BeanUtils.copyProperties(project, vo);
|
||||
fillLatestTagFailure(project, vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -183,6 +190,12 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
);
|
||||
vo.setStatus3(status3Count);
|
||||
|
||||
Long status4Count = projectMapper.selectCount(
|
||||
new LambdaQueryWrapper<CcdiProject>()
|
||||
.eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAG_FAILED)
|
||||
);
|
||||
vo.setStatus4(status4Count);
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -263,10 +276,23 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
case CcdiProjectStatusConstants.COMPLETED -> "已完成";
|
||||
case CcdiProjectStatusConstants.ARCHIVED -> "已归档";
|
||||
case CcdiProjectStatusConstants.TAGGING -> "打标中";
|
||||
case CcdiProjectStatusConstants.TAG_FAILED -> "打标失败";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
private void fillLatestTagFailure(CcdiProject project, CcdiProjectVO vo) {
|
||||
if (!CcdiProjectStatusConstants.TAG_FAILED.equals(project.getStatus())) {
|
||||
return;
|
||||
}
|
||||
CcdiBankTagTask latestFailedTask = bankTagTaskMapper.selectLatestFailedTaskByProjectId(project.getProjectId());
|
||||
if (latestFailedTask == null) {
|
||||
return;
|
||||
}
|
||||
vo.setLatestTagTaskErrorMessage(latestFailedTask.getErrorMessage());
|
||||
vo.setLatestTagTaskEndTime(latestFailedTask.getEndTime());
|
||||
}
|
||||
|
||||
private String resolveOperator(String operator) {
|
||||
return StringUtils.hasText(operator) ? operator : "system";
|
||||
}
|
||||
|
||||
@@ -65,4 +65,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectLatestFailedTaskByProjectId" resultMap="CcdiBankTagTaskResultMap">
|
||||
select id, project_id, trigger_type, model_code, status, need_rerun, success_rule_count,
|
||||
failed_rule_count, hit_count, error_message, start_time, end_time,
|
||||
create_by, create_time, update_by, update_time
|
||||
from ccdi_bank_tag_task
|
||||
where project_id = #{projectId}
|
||||
and status = 'FAILED'
|
||||
order by id desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -317,7 +317,7 @@ class CcdiBankTagServiceImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRollbackProjectStatusToProcessingWhenRebuildFails() {
|
||||
void shouldMarkProjectTagFailedWhenRebuildFails() {
|
||||
ReflectionTestUtils.setField(service, "tagRuleExecutor", (Executor) Runnable::run);
|
||||
|
||||
CcdiBankTagRule rule = buildRule("LARGE_TRANSACTION", "大额交易",
|
||||
@@ -329,7 +329,7 @@ class CcdiBankTagServiceImplTest {
|
||||
assertThrows(RuntimeException.class,
|
||||
() -> service.rebuildProject(40L, null, "tester", TriggerType.MANUAL));
|
||||
|
||||
verify(projectService).updateProjectStatus(40L, "0", "tester");
|
||||
verify(projectService).updateProjectStatus(40L, "4", "tester");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -112,7 +112,7 @@ class CcdiBankTagServiceRiskCountRefreshTest {
|
||||
|
||||
verify(taskMapper).updateTask(argThat(task -> "FAILED".equals(task.getStatus())
|
||||
&& "refresh failed".equals(task.getErrorMessage())));
|
||||
verify(projectService).updateProjectStatus(40L, "0", "tester");
|
||||
verify(projectService).updateProjectStatus(40L, "4", "tester");
|
||||
}
|
||||
|
||||
private CcdiBankTagRule buildRule() {
|
||||
|
||||
@@ -7,10 +7,12 @@ import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
|
||||
import com.ruoyi.ccdi.project.domain.event.CcdiProjectHistoryImportSubmittedEvent;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||
@@ -25,11 +27,14 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -47,6 +52,9 @@ class CcdiProjectServiceImplTest {
|
||||
@Mock
|
||||
private CcdiProjectMapper projectMapper;
|
||||
|
||||
@Mock
|
||||
private CcdiBankTagTaskMapper bankTagTaskMapper;
|
||||
|
||||
@Mock
|
||||
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||
|
||||
@@ -55,13 +63,55 @@ class CcdiProjectServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldCountTaggingProjectsSeparately() {
|
||||
when(projectMapper.selectCount(any())).thenReturn(10L, 3L, 4L, 2L, 1L);
|
||||
when(projectMapper.selectCount(any())).thenReturn(10L, 3L, 4L, 2L, 1L, 0L);
|
||||
|
||||
CcdiProjectStatusCountsVO counts = service.getStatusCounts();
|
||||
|
||||
assertEquals(1L, counts.getStatus3());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCountTagFailedProjectsSeparately() {
|
||||
when(projectMapper.selectCount(any())).thenReturn(10L, 3L, 4L, 2L, 1L, 5L);
|
||||
|
||||
CcdiProjectStatusCountsVO counts = service.getStatusCounts();
|
||||
|
||||
assertEquals(5L, counts.getStatus4());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnLatestFailedTagTaskOnFailedProjectDetail() {
|
||||
Date endTime = new Date();
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(40L);
|
||||
project.setStatus("4");
|
||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||
|
||||
CcdiBankTagTask failedTask = new CcdiBankTagTask();
|
||||
failedTask.setErrorMessage("threshold missing");
|
||||
failedTask.setEndTime(endTime);
|
||||
when(bankTagTaskMapper.selectLatestFailedTaskByProjectId(40L)).thenReturn(failedTask);
|
||||
|
||||
CcdiProjectVO result = service.getProjectById(40L);
|
||||
|
||||
assertEquals("threshold missing", result.getLatestTagTaskErrorMessage());
|
||||
assertEquals(endTime, result.getLatestTagTaskEndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotReturnLatestFailedTagTaskWhenProjectIsNotFailed() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(40L);
|
||||
project.setStatus("0");
|
||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||
|
||||
CcdiProjectVO result = service.getProjectById(40L);
|
||||
|
||||
assertNotNull(result);
|
||||
assertNull(result.getLatestTagTaskErrorMessage());
|
||||
verify(bankTagTaskMapper, never()).selectLatestFailedTaskByProjectId(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUpdatingArchivedProjectToTagging() {
|
||||
CcdiProject archived = new CcdiProject();
|
||||
@@ -84,6 +134,16 @@ class CcdiProjectServiceImplTest {
|
||||
() -> service.ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowWritingWhenProjectTagFailed() {
|
||||
CcdiProject tagFailed = new CcdiProject();
|
||||
tagFailed.setProjectId(40L);
|
||||
tagFailed.setStatus("4");
|
||||
when(projectMapper.selectById(40L)).thenReturn(tagFailed);
|
||||
|
||||
assertDoesNotThrow(() -> service.ensureProjectWritable(40L, "当前项目正在进行银行流水打标,暂不允许修改参数"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldArchiveCompletedProject() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
@@ -110,6 +170,16 @@ class CcdiProjectServiceImplTest {
|
||||
assertThrows(ServiceException.class, () -> service.archiveProject(41L, "tester"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectArchivingProjectWhenStatusIsTagFailed() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(41L);
|
||||
project.setStatus("4");
|
||||
when(projectMapper.selectById(41L)).thenReturn(project);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.archiveProject(41L, "tester"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectWritingWhenProjectIsArchived() {
|
||||
CcdiProject archived = new CcdiProject();
|
||||
|
||||
@@ -14,12 +14,25 @@ class CcdiProjectStatusSqlTest {
|
||||
void shouldContainTaggingStatusInInitAndMigrationSql() throws IOException {
|
||||
Path repoRoot = Path.of("..");
|
||||
String initSql = Files.readString(repoRoot.resolve("sql/ccdi_project.sql"));
|
||||
String prodInitSql = Files.readString(repoRoot.resolve("sql/ccdi_prod_init.sql"));
|
||||
String migrationSql = Files.readString(repoRoot.resolve("sql/migration/2026-03-18-add-project-tagging-status.sql"));
|
||||
String tagFailedMigrationSql =
|
||||
Files.readString(repoRoot.resolve("sql/migration/2026-05-27-add-project-tag-failed-status.sql"));
|
||||
|
||||
assertTrue(initSql.contains("打标中"));
|
||||
assertTrue(initSql.contains("'3'"));
|
||||
assertTrue(migrationSql.contains("ccdi_project_status"));
|
||||
assertTrue(migrationSql.contains("打标中"));
|
||||
assertTrue(migrationSql.contains("'3'"));
|
||||
|
||||
assertTrue(initSql.contains("打标失败"));
|
||||
assertTrue(initSql.contains("'4'"));
|
||||
assertTrue(prodInitSql.contains("打标失败"));
|
||||
assertTrue(prodInitSql.contains("'4','ccdi_project_status'"));
|
||||
assertTrue(tagFailedMigrationSql.contains("ccdi_project_status"));
|
||||
assertTrue(tagFailedMigrationSql.contains("打标失败"));
|
||||
assertTrue(tagFailedMigrationSql.contains("'4'"));
|
||||
assertTrue(tagFailedMigrationSql.contains("latest_task.status = 'FAILED'"));
|
||||
assertTrue(tagFailedMigrationSql.contains("project.status IN ('0', '3')"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user