Files
flow-center-service/src/test/java/com/yunhai/flowcenter/controller/api/FlowBaseControllerTest.java
T
chenxing f116786846
build / build-push (push) Successful in 1m59s
feat(base): GetUserMod 改为消费 UserBase 下发的模块树,移除硬编码菜单
与其他子应用(Notice / svc-hall / 事项库)统一:GetUserMod 不再返回本应用硬编码 MENUS,
改经平台 SDK YschBaseOpenApi.getUserModule(userId, schId) 取当前用户在本应用
(yh.open.app-key=100019)下的已授权模块树,层级由平台契约 BoaModule.children 承载,
本侧仅做前端 layout 契约的字段改名映射(toFrontendTree),不再本地组树。
设计概览 §9.4「菜单为本地路由约定、不进 app_module」的口径自此作废。

两处刻意的差异与取舍:

1) modId 取 custom_id 而非主键 id:front router/routes.ts 的 meta.modId 约定是 100~105,
   等于 app_module.custom_id,旧硬编码菜单发的也是这些值;若照抄其他子应用「modId=主键id」,
   前端 modId 兜底匹配会永久失配(仅剩 path 匹配可用)。故 custom_id 可解析为数字时取之,
   否则回退主键 id。
2) 不保留本地 MENUS 兜底:静默回退会把「UserBase 未配 app_module / 未授权」伪装成正常。
   配置缺失应表现为空菜单并被立刻发现。发布顺序必须先是 flow-center接入UserBase菜单.sql
   (含 app_module 写入与角色/组授权),再发本应用。

新增 FlowBaseControllerTest 4 例:层级原样保留与字段改名、children 恒为数组(含上游未展开
的 null 归零)、custom_id 非数字回退主键、path 缺失给空串且 null/空入参不抛 NPE。
映射方法放宽为包级可见以免为测试改写 YschBaseOpenApi 全局静态客户端、污染共享 JVM 内其它用例。
2026-09-15 18:44:16 +08:00

90 lines
4.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.yunhai.flowcenter.controller.api;
import com.yunhai.platform.utils.model.BoaModule;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* GetUserMod 映射单测:flow-center 已改为直接消费 UserBase 下发的模块树({@code BoaModule.children}),
* 本侧只做前端 layout 契约的字段改名,不再本地组树。<br>
* 另锁定两个易错点:modId 必须取 custom_id(对上 routes.ts 的 meta.modId 100~105),
* 以及 children 恒为数组(叶子为空列表、上游未展开的 null 也要归零),否则前端递归会炸。
*/
class FlowBaseControllerTest {
@Test
void toFrontendTree_shouldKeepServerHierarchyAndRenameFields() {
BoaModule parent = module(501, "100", "/workbench", 1);
BoaModule child = module(502, "101", "/my", 2);
parent.setChildren(new ArrayList<>(Collections.singletonList(child)));
BoaModule otherRoot = module(503, "105", "/admin/callbacks", 1);
List<Map<String, Object>> tree = FlowBaseController.toFrontendTree(Arrays.asList(parent, otherRoot));
assertEquals(2, tree.size(), "顶层只应有两个根,子节点不得浮到顶层");
Map<String, Object> first = tree.get(0);
assertEquals("/workbench", first.get("page"), "path → page(前端按 page 匹配菜单高亮)");
assertEquals(100, first.get("modId"), "modId 必须取 custom_id 而非主键 id(routes.ts meta.modId 约定)");
assertEquals(501, first.get("id"), "原始主键仍经 id 字段透出");
@SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) first.get("children");
assertEquals(1, children.size(), "层级应原样保留");
assertEquals(101, children.get(0).get("modId"));
assertEquals("/my", children.get(0).get("page"));
}
@Test
void toFrontendTree_shouldAlwaysEmitChildrenAsArray() {
BoaModule configuredLeaf = module(601, "102", "/admin/instances", 1);
configuredLeaf.setChildren(new ArrayList<>());
BoaModule notExpanded = module(602, "103", "/admin/defs", 1); // 上游未展开 → children 为 null
List<Map<String, Object>> tree = FlowBaseController.toFrontendTree(Arrays.asList(configuredLeaf, notExpanded));
for (Map<String, Object> node : tree) {
assertNotNull(node.get("children"), "children 不得为 null,前端要无判空递归");
assertTrue(((List<?>) node.get("children")).isEmpty(), "无子节点时应为空列表");
}
}
@Test
void toFrontendTree_shouldFallBackToPrimaryKeyWhenCustomIdNotNumeric() {
BoaModule m = module(701, "workbench-home", "/workbench", 1);
List<Map<String, Object>> tree = FlowBaseController.toFrontendTree(Collections.singletonList(m));
assertEquals(701, tree.get(0).get("modId"), "custom_id 非数字时回退模块主键 id");
}
@Test
void toFrontendTree_shouldTolerateNullPathAndEmptyInput() {
BoaModule noPath = module(801, "104", null, 1);
assertEquals("", FlowBaseController.toFrontendTree(Collections.singletonList(noPath)).get(0).get("page"),
"path 缺失应给空串而非 null(前端字符串比较不判空)");
assertTrue(FlowBaseController.toFrontendTree(Collections.emptyList()).isEmpty());
assertTrue(FlowBaseController.toFrontendTree(null).isEmpty(), "上游返回 null 不得抛 NPE");
}
private static BoaModule module(Integer id, String customId, String path, Integer level) {
BoaModule m = new BoaModule();
m.setId(id);
m.setCustomId(customId);
m.setName("模块" + customId);
m.setPath(path);
m.setLevel(level);
m.setSortNo(level * 10);
return m;
}
}