[audit] P6.1 F11-4 台账路由挂起可观测:pageAdmin 增 hung 过滤 + 行级 routeHung 红标(口径=max attempt 路由行 outlet 仍 NULL,防误报)
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
package com.yunhai.flowcenter.controller.api;
|
||||
|
||||
import com.yunhai.flowcenter.entity.FlwInstance;
|
||||
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwRouteLogRepository;
|
||||
import com.yunhai.flowcenter.service.FlowAdminGuard;
|
||||
import com.yunhai.platform.utils.model.BasePageResult;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* F11-4 台账「路由挂起」可观测单测:hung 筛选透传 + 行级 routeHung 红标三态
|
||||
* (在途+挂起 / 在途未挂 / 已终态)。挂起判定 JPQL 口径(最大 attempt 行 outlet 仍 NULL)
|
||||
* 在 {@link FlwRouteLogRepository#findHungInstanceIds} 与 pageAdmin 的 exists 子查询里,
|
||||
* 语义断言由 160 人工验收(V 过滤)与真库回归兜住;本测锁定控制器编排与红标填充。
|
||||
*/
|
||||
class FlowAdminInstanceHungTest {
|
||||
|
||||
private final FlowAdminGuard adminGuard = mock(FlowAdminGuard.class);
|
||||
private final FlwInstanceRepository instanceRepository = mock(FlwInstanceRepository.class);
|
||||
private final FlwRouteLogRepository routeLogRepository = mock(FlwRouteLogRepository.class);
|
||||
|
||||
private final FlowAdminController controller = new FlowAdminController(
|
||||
adminGuard,
|
||||
mock(com.yunhai.flowcenter.service.FlowDefAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowMapAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowCallbackAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowRouterAdminService.class),
|
||||
instanceRepository,
|
||||
mock(com.yunhai.flowcenter.service.FlowDiagramService.class),
|
||||
mock(com.yunhai.flowcenter.service.DiagramImageService.class),
|
||||
mock(com.fasterxml.jackson.databind.ObjectMapper.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowAppAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowDeptAdminService.class),
|
||||
routeLogRepository);
|
||||
|
||||
private static FlwInstance instance(long id, int status) {
|
||||
FlwInstance i = new FlwInstance();
|
||||
i.setId(id);
|
||||
i.setStatus(status);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Test
|
||||
void hungFilterPassedThroughToRepository() {
|
||||
when(adminGuard.requireAdmin(any(), any())).thenReturn(990001L);
|
||||
when(instanceRepository.pageAdmin(eq(990001L), any(), any(), eq(Boolean.TRUE), any()))
|
||||
.thenReturn(new PageImpl<>(Arrays.asList(instance(1, 10))));
|
||||
when(routeLogRepository.findHungInstanceIds(any())).thenReturn(Arrays.asList());
|
||||
|
||||
controller.instancePage(new BoaUser(), null, null, Boolean.TRUE, 1, 10);
|
||||
|
||||
ArgumentCaptor<Boolean> hung = ArgumentCaptor.forClass(Boolean.class);
|
||||
verify(instanceRepository).pageAdmin(eq(990001L), isNull(), isNull(), hung.capture(), any());
|
||||
assertEquals(Boolean.TRUE, hung.getValue(), "hung=true 必须原样透传到分页查询");
|
||||
}
|
||||
|
||||
@Test
|
||||
void routeHungBadgeThreeStates() {
|
||||
when(adminGuard.requireAdmin(any(), any())).thenReturn(990001L);
|
||||
// 三行:1=在途且在挂起集合;2=在途但不在集合;3=已终态(集合必然不含)
|
||||
when(instanceRepository.pageAdmin(eq(990001L), any(), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(Arrays.asList(instance(1, 10), instance(2, 10), instance(3, 20))));
|
||||
when(routeLogRepository.findHungInstanceIds(any()))
|
||||
.thenReturn(Arrays.asList(1L));
|
||||
|
||||
BasePageResult<FlwInstance> result = controller.instancePage(new BoaUser(), null, null, null, 1, 10);
|
||||
|
||||
List<FlwInstance> rows = result.getContent();
|
||||
assertEquals(3, rows.size());
|
||||
assertTrue(rows.get(0).getRouteHung(), "在途且最新 attempt 路由行无出口 → 红标 true");
|
||||
assertFalse(rows.get(1).getRouteHung(), "在途但无挂起路由行 → false");
|
||||
assertFalse(rows.get(2).getRouteHung(), "已终态办件不参与挂起判定 → false");
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyPageSkipsHungLookup() {
|
||||
when(adminGuard.requireAdmin(any(), any())).thenReturn(990001L);
|
||||
when(instanceRepository.pageAdmin(eq(990001L), any(), any(), any(), any()))
|
||||
.thenReturn(new PageImpl<>(Arrays.asList()));
|
||||
|
||||
controller.instancePage(new BoaUser(), 10, null, null, 1, 10);
|
||||
|
||||
verify(routeLogRepository, never()).findHungInstanceIds(any());
|
||||
}
|
||||
}
|
||||
@@ -493,7 +493,8 @@ class FlowDiagramServiceTest {
|
||||
mock(com.yunhai.flowcenter.service.DiagramImageService.class),
|
||||
new ObjectMapper(),
|
||||
mock(com.yunhai.flowcenter.service.FlowAppAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowDeptAdminService.class));
|
||||
mock(com.yunhai.flowcenter.service.FlowDeptAdminService.class),
|
||||
mock(com.yunhai.flowcenter.repository.FlwRouteLogRepository.class));
|
||||
}
|
||||
|
||||
private BoaUser user(int id, Integer type, Integer schId, Integer... roleIds) {
|
||||
|
||||
Reference in New Issue
Block a user