feat(dept): 科室管理——flw_dept/flw_dept_member 组+成员模型取代单用户映射行(议题3,拍板D1选B)
build / build-push (push) Successful in 54s
build / build-push (push) Successful in 54s
- deptKey=本地自增 id;source_group_id 幂等(一键拉取=初始化/刷新语义,不重复建组);不做存量迁移 - 四处读源全部切新表:AssigneeResolver(宽容口径:deptKey 不存在/停用返回空,图示/轨迹不炸)、 enabledDeptKeys 发布校验白名单、DeptLabelSupport 唯一出口(图示科室视图+轨迹富化)、编辑器 deptList 聚合 - /admin/dept/* 全套端点(CRUD/成员/拉取预览导入);旧 /map/* 映射 CRUD 端点删除 (保留 /map/lookupUser 选人检索与 /user/teacherTree);flw_assignee_map 实体/表保留不删 - IT 种子切源(FlowTestSupport tag→deptKey 注册表,用例主体零改动);全量 191 绿;E2E 22/22 (local-debug/four-topics-e-e2e.py,含切源后果验证与生产盘点 SQL)
This commit is contained in:
@@ -3,7 +3,7 @@ package com.yunhai.flowcenter.service;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yunhai.flowcenter.controller.api.FlowAdminController;
|
||||
import com.yunhai.flowcenter.controller.api.FlowUserController;
|
||||
import com.yunhai.flowcenter.entity.FlwAssigneeMap;
|
||||
import com.yunhai.flowcenter.entity.FlwDeptMember;
|
||||
import com.yunhai.flowcenter.entity.FlwDef;
|
||||
import com.yunhai.flowcenter.entity.FlwInstance;
|
||||
import com.yunhai.flowcenter.entity.FlwTask;
|
||||
@@ -11,7 +11,6 @@ import com.yunhai.flowcenter.engine.AssigneeResolver;
|
||||
import com.yunhai.flowcenter.engine.FlowEngine;
|
||||
import com.yunhai.flowcenter.enums.FlwEnums;
|
||||
import com.yunhai.flowcenter.model.FlowModels;
|
||||
import com.yunhai.flowcenter.repository.FlwAssigneeMapRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwBranchRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwDefRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
|
||||
@@ -85,7 +84,7 @@ class FlowDiagramServiceTest {
|
||||
+ "\"edges\":[{\"from\":\"start\",\"to\":\"n2\"},{\"from\":\"n2\",\"to\":\"end\"}]}";
|
||||
|
||||
private FlwDefRepository defRepository;
|
||||
private FlwAssigneeMapRepository mapRepository;
|
||||
private com.yunhai.flowcenter.service.FlowDeptAdminService deptAdminService;
|
||||
private FlwTaskRepository taskRepository;
|
||||
private com.yunhai.flowcenter.repository.FlwCcRepository ccRepository;
|
||||
private OpenApiUserClient userClient;
|
||||
@@ -94,7 +93,7 @@ class FlowDiagramServiceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
defRepository = mock(FlwDefRepository.class);
|
||||
mapRepository = mock(FlwAssigneeMapRepository.class);
|
||||
deptAdminService = mock(com.yunhai.flowcenter.service.FlowDeptAdminService.class);
|
||||
taskRepository = mock(FlwTaskRepository.class);
|
||||
ccRepository = mock(com.yunhai.flowcenter.repository.FlwCcRepository.class);
|
||||
userClient = mock(OpenApiUserClient.class);
|
||||
@@ -107,7 +106,7 @@ class FlowDiagramServiceTest {
|
||||
taskRepository, mock(FlwBranchRepository.class), ccRepository,
|
||||
mock(AssigneeResolver.class), mock(com.yunhai.flowcenter.engine.ConditionRouteService.class),
|
||||
mock(ApplicationEventPublisher.class));
|
||||
service = new FlowDiagramService(defRepository, mapRepository,
|
||||
service = new FlowDiagramService(defRepository, deptAdminService,
|
||||
mock(com.yunhai.flowcenter.repository.FlwInstanceRepository.class),
|
||||
taskRepository, ccRepository,
|
||||
mock(com.yunhai.flowcenter.repository.FlwRouteLogRepository.class),
|
||||
@@ -127,11 +126,9 @@ class FlowDiagramServiceTest {
|
||||
return def;
|
||||
}
|
||||
|
||||
private FlwAssigneeMap mapRow(String deptName, int userId, String userName) {
|
||||
FlwAssigneeMap m = new FlwAssigneeMap();
|
||||
m.setSchId(SCH);
|
||||
m.setDeptKey("rs");
|
||||
m.setDeptName(deptName);
|
||||
private FlwDeptMember memberRow(int userId, String userName) {
|
||||
FlwDeptMember m = new FlwDeptMember();
|
||||
m.setDeptId(7L);
|
||||
m.setUserId(userId);
|
||||
m.setUserName(userName);
|
||||
m.setStatus(0);
|
||||
@@ -153,8 +150,9 @@ class FlowDiagramServiceTest {
|
||||
|
||||
@Test
|
||||
void c1_deptView_deptKeyNode_labelDeptName_subLabelCountAndDeadline() {
|
||||
when(mapRepository.findEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
mapRow("人事处", 11, "张三"), mapRow("人事处", 12, "李四")));
|
||||
when(deptAdminService.deptName(SCH, "rs")).thenReturn("人事处");
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
memberRow(11, "张三"), memberRow(12, "李四")));
|
||||
FlowModels.DiagramVo vo = service.build(def(DEPT_DEF_JSON), null, "dept");
|
||||
FlowModels.NodeVo n1 = node(vo, "n1");
|
||||
assertEquals("人事处", n1.getLabel());
|
||||
@@ -170,30 +168,32 @@ class FlowDiagramServiceTest {
|
||||
|
||||
@Test
|
||||
void c1_deptView_fallbackChain_deptKeyThenNodeName() {
|
||||
// 映射行全无名 → deptKey 兜底
|
||||
when(mapRepository.findEnabled(SCH, "rs"))
|
||||
.thenReturn(Collections.singletonList(mapRow("", 11, "张三")));
|
||||
// 科室已被删(deptName=null)→ 节点名兜底(成员解析空,人数照实)
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs"))
|
||||
.thenReturn(Collections.singletonList(memberRow(11, "张三")));
|
||||
FlowModels.NodeVo n1 = node(service.build(def(DEPT_DEF_JSON), null, "dept"), "n1");
|
||||
assertEquals("rs", n1.getLabel());
|
||||
assertEquals("科室 · 1人 · 时限48h", n1.getSubLabel());
|
||||
// 无映射行 → 节点名兜底(人数照实为 0)
|
||||
when(mapRepository.findEnabled(SCH, "rs")).thenReturn(Collections.emptyList());
|
||||
n1 = node(service.build(def(DEPT_DEF_JSON), null, "dept"), "n1");
|
||||
assertEquals("人事处审批", n1.getLabel());
|
||||
assertEquals("科室 · 1人 · 时限48h", n1.getSubLabel());
|
||||
// 科室存在但成员为空 → 科室名照常,人数 0
|
||||
when(deptAdminService.deptName(SCH, "rs")).thenReturn("人事处");
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs")).thenReturn(Collections.emptyList());
|
||||
n1 = node(service.build(def(DEPT_DEF_JSON), null, "dept"), "n1");
|
||||
assertEquals("人事处", n1.getLabel());
|
||||
assertEquals("科室 · 0人 · 时限48h", n1.getSubLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void c1_anyCountersign_appendsSuffix() {
|
||||
when(mapRepository.findEnabled(SCH, "rs"))
|
||||
.thenReturn(Collections.singletonList(mapRow("人事处", 11, "张三")));
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs"))
|
||||
.thenReturn(Collections.singletonList(memberRow(11, "张三")));
|
||||
FlowModels.NodeVo n1 = node(service.build(def(ANY_DEF_JSON), null, "dept"), "n1");
|
||||
assertEquals("科室 · 1人 · 任一人通过", n1.getSubLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void c1_fixedLabels_andEdgesCarried() {
|
||||
when(mapRepository.findEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.resolveEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.deptName(anyLong(), anyString())).thenReturn(null);
|
||||
FlowModels.DiagramVo vo = service.build(def(DEPT_DEF_JSON), null, "dept");
|
||||
assertEquals("开始", node(vo, "start").getLabel());
|
||||
assertEquals("并行分发", node(vo, "sp1").getLabel());
|
||||
@@ -215,8 +215,9 @@ class FlowDiagramServiceTest {
|
||||
|
||||
@Test
|
||||
void c1_autoView_deptKeyNode_rendersDeptStyle() {
|
||||
when(mapRepository.findEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
mapRow("人事处", 11, "张三"), mapRow("人事处", 12, "李四")));
|
||||
when(deptAdminService.deptName(SCH, "rs")).thenReturn("人事处");
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
memberRow(11, "张三"), memberRow(12, "李四")));
|
||||
FlowModels.NodeVo n1 = node(service.build(def(DEPT_DEF_JSON), null, "auto"), "n1");
|
||||
assertEquals("人事处", n1.getLabel());
|
||||
assertEquals("科室 · 2人 · 时限48h", n1.getSubLabel());
|
||||
@@ -226,8 +227,9 @@ class FlowDiagramServiceTest {
|
||||
/** 2026-09-18:mode 缺省改为人员视图(原 auto 自适应)——老深链显式带 auto 仍按自适应出图 */
|
||||
@Test
|
||||
void c1_defaultMode_isUserView() {
|
||||
when(mapRepository.findEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
mapRow("人事处", 11, "张三"), mapRow("人事处", 12, "李四")));
|
||||
when(deptAdminService.deptName(SCH, "rs")).thenReturn("人事处");
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
memberRow(11, "张三"), memberRow(12, "李四")));
|
||||
FlowModels.DiagramVo vo = service.build(def(DEPT_DEF_JSON), null, null);
|
||||
assertEquals("user", vo.getMode());
|
||||
FlowModels.NodeVo n1 = node(vo, "n1");
|
||||
@@ -288,8 +290,9 @@ class FlowDiagramServiceTest {
|
||||
|
||||
@Test
|
||||
void c3_userView_deptKeyNode_listsMemberNames_blankNameDegradesToId() {
|
||||
when(mapRepository.findEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
mapRow("人事处", 11, "张三"), mapRow("人事处", 12, null)));
|
||||
when(deptAdminService.deptName(SCH, "rs")).thenReturn("人事处");
|
||||
when(deptAdminService.resolveEnabled(SCH, "rs")).thenReturn(Arrays.asList(
|
||||
memberRow(11, "张三"), memberRow(12, null)));
|
||||
FlowModels.NodeVo n1 = node(service.build(def(DEPT_DEF_JSON), null, "user"), "n1");
|
||||
// 人员视图:label=节点名,subLabel=映射成员名(空名降级 #id)+ 会签/时限后缀
|
||||
assertEquals("人事处审批", n1.getLabel());
|
||||
@@ -304,7 +307,8 @@ class FlowDiagramServiceTest {
|
||||
when(defRepository.findFirstBySchIdAndFlowGuidAndStatusOrderByVersionDesc(
|
||||
SCH, "g1", FlwEnums.DefStatus.PUBLISHED.getCode()))
|
||||
.thenReturn(Optional.of(def));
|
||||
when(mapRepository.findEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.resolveEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.deptName(anyLong(), anyString())).thenReturn(null);
|
||||
FlowModels.DiagramVo vo = service.byGuid(SCH, "g1", null);
|
||||
assertEquals("教师入职", vo.getName());
|
||||
assertEquals(Integer.valueOf(2), vo.getVersion());
|
||||
@@ -349,7 +353,8 @@ class FlowDiagramServiceTest {
|
||||
instance.setSchId(SCH);
|
||||
instance.setDefId(12L);
|
||||
when(defRepository.findById(12L)).thenReturn(Optional.of(def(DEPT_DEF_JSON)));
|
||||
when(mapRepository.findEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.resolveEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.deptName(anyLong(), anyString())).thenReturn(null);
|
||||
FlowModels.DiagramVo vo = service.byInstance(instance, "dept");
|
||||
assertEquals(Long.valueOf(100L), vo.getInstanceId());
|
||||
assertEquals(Long.valueOf(12L), vo.getDefId());
|
||||
@@ -360,7 +365,8 @@ class FlowDiagramServiceTest {
|
||||
void c6_build_draftDef_ok() {
|
||||
FlwDef draft = def(DEPT_DEF_JSON);
|
||||
draft.setStatus(FlwEnums.DefStatus.DRAFT.getCode());
|
||||
when(mapRepository.findEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.resolveEnabled(anyLong(), anyString())).thenReturn(Collections.emptyList());
|
||||
when(deptAdminService.deptName(anyLong(), anyString())).thenReturn(null);
|
||||
FlowModels.DiagramVo vo = service.build(draft, null, "auto");
|
||||
assertEquals(Integer.valueOf(0), vo.getStatus());
|
||||
assertEquals(5, vo.getNodes().size());
|
||||
@@ -484,7 +490,8 @@ class FlowDiagramServiceTest {
|
||||
mock(FlwInstanceRepository.class), diagramService,
|
||||
mock(com.yunhai.flowcenter.service.DiagramImageService.class),
|
||||
new ObjectMapper(),
|
||||
mock(com.yunhai.flowcenter.service.FlowAppAdminService.class));
|
||||
mock(com.yunhai.flowcenter.service.FlowAppAdminService.class),
|
||||
mock(com.yunhai.flowcenter.service.FlowDeptAdminService.class));
|
||||
}
|
||||
|
||||
private BoaUser user(int id, Integer type, Integer schId, Integer... roleIds) {
|
||||
|
||||
Reference in New Issue
Block a user