fix(diagram): CONDITION 多出口分支分列——不等长/空分支不再被拓扑分层压成单列直穿(用户实测 def-32 或签流程报错)。spreadConditionBranches 后处理:各出口分支按列对称展开(链形分支逐节点对列中心,内嵌并行回退整块平移),隐式汇合点(各分支可达集交集 BFS 最早节点)吸附回条件节点中线;等长同层分支为恒等变换不回归(golden 8/8)
build / build-push (push) Successful in 52s
build / build-push (push) Successful in 52s
This commit is contained in:
@@ -183,6 +183,60 @@ class DiagramLayoutServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void conditionUnequalBranches_forkIntoColumns_joinStaysOnAxis() {
|
||||
// def-32 同款(2026-10-12 用户报错场景):校长室线=抄送→科室审批(两层),其他线=直达结束;
|
||||
// 修复前各层退化单列、跳层出口边直穿中间节点。修复后:分支成员独占一列,隐式汇合点(end)
|
||||
// 留在条件节点中线上,cond→end 直落边在自己的车道里(不与分支列水平相交)。
|
||||
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
|
||||
node("start", FlowDefModel.TYPE_START),
|
||||
node("n1", FlowDefModel.TYPE_APPROVAL),
|
||||
node("cond", FlowDefModel.TYPE_CONDITION),
|
||||
node("cc1", FlowDefModel.TYPE_NOTIFY),
|
||||
node("n3", FlowDefModel.TYPE_APPROVAL),
|
||||
node("end", FlowDefModel.TYPE_END)));
|
||||
Map<String, int[]> box = layout(nodes,
|
||||
edge("start", "n1"), edge("n1", "cond"),
|
||||
edge("cond", "cc1"), edge("cond", "end"),
|
||||
edge("cc1", "n3"), edge("n3", "end"));
|
||||
assertEquals(cx(box.get("cc1")), cx(box.get("n3")), 1, "校长室线成员应同列");
|
||||
assertEquals(cx(box.get("cond")), cx(box.get("end")), 1, "隐式汇合点应留在条件节点中线");
|
||||
assertEquals((DiagramLayoutService.SLOT_W + DiagramLayoutService.GAP_X) / 2,
|
||||
Math.abs(cx(box.get("cond")) - cx(box.get("cc1"))), 1, "空分支时非空分支占 ±列距/2 位(与等长分支对称口径一致)");
|
||||
// 直落边车道无遮挡:cc1/n3 的盒体不得横跨 cond→end 垂直线(x=条件节点中线)
|
||||
int axis = cx(box.get("cond"));
|
||||
for (String m : new String[]{"cc1", "n3"}) {
|
||||
int[] b = box.get(m);
|
||||
assertTrue(b[0] + b[2] <= axis || b[0] >= axis, m + " 盒体跨中线会被直落边穿过");
|
||||
}
|
||||
for (int[] b : box.values()) {
|
||||
assertTrue(b[0] >= 0 && b[1] >= 0, "归零后不得有负坐标");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void conditionBothBranchesNonEmpty_multiLevelSideBySide() {
|
||||
// 两分支均多层且长度不等:A 线两层、B 线一层——各自成列、汇合点回中线
|
||||
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
|
||||
node("start", FlowDefModel.TYPE_START),
|
||||
node("cond", FlowDefModel.TYPE_CONDITION),
|
||||
node("a1", FlowDefModel.TYPE_APPROVAL),
|
||||
node("a2", FlowDefModel.TYPE_APPROVAL),
|
||||
node("b1", FlowDefModel.TYPE_APPROVAL),
|
||||
node("end", FlowDefModel.TYPE_END)));
|
||||
Map<String, int[]> box = layout(nodes,
|
||||
edge("start", "cond"),
|
||||
edge("cond", "a1"), edge("cond", "b1"),
|
||||
edge("a1", "a2"), edge("a2", "end"), edge("b1", "end"));
|
||||
assertEquals(cx(box.get("a1")), cx(box.get("a2")), 1, "A 线成员同列");
|
||||
assertEquals(cx(box.get("cond")), cx(box.get("end")), 1, "汇合点在中线");
|
||||
// A/B 两列分居中线两侧(±列距/2 对称)
|
||||
assertTrue((cx(box.get("a1")) < cx(box.get("cond"))) != (cx(box.get("b1")) < cx(box.get("cond"))),
|
||||
"两分支应分居中线两侧");
|
||||
assertEquals(DiagramLayoutService.SLOT_W + DiagramLayoutService.GAP_X,
|
||||
Math.abs(cx(box.get("b1")) - cx(box.get("a1"))), 1, "两分支列距");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cycle_defensive_fallbackLevelStillPositionsAllNodes() {
|
||||
List<FlowModels.NodeVo> nodes = new ArrayList<>(Arrays.asList(
|
||||
|
||||
Reference in New Issue
Block a user