Files
flow-center-service/src/main/java/com/yunhai/flowcenter/controller/api/FlowBaseController.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

93 lines
4.5 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.openapi.client.YschBaseOpenApi;
import com.yunhai.platform.utils.model.BoaModule;
import com.yunhai.platform.utils.model.BoaUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 前端壳基础端点(对齐 UserBase BaseController 的 /api/base/* 形态):GetUserInfo / GetUserMod。
* <p>GetUserMod 与其他子应用(Notice / svc-hall / 事项库 Cms)统一走平台下发:经 SDK
* {@code YschBaseOpenApi.getUserModule} 取当前用户在本应用({@code yh.open.app-key=100019})下的
* 已授权模块<b>树</b>,层级由平台契约 {@code BoaModule.children} 承载,本侧仅映射为前端 layout
* 期望的字段形状({@code page/modId/children})。
* <p>设计概览 §9.4「菜单为本地路由约定、不进 app_module」的口径自 2026-09-15 起作废,改为平台统一下发。
*/
@RestController
public class FlowBaseController {
@GetMapping("/api/base/GetUserInfo")
public BoaUser getUserInfo(BoaUser authUserWithDept) {
return authUserWithDept;
}
/**
* 当前用户已授权模块树(左侧菜单)。
* <p><b>依赖 UserBase 侧数据</b>:本应用的 {@code app_module} 行({@code path} 须逐字对上
* front 路由,见 router/routes.ts)+ 角色/组授权,二者缺一即返回空菜单。
* 这里<b>不保留本地 MENUS 兜底</b>——静默回退会把「平台未配模块/未授权」伪装成正常,
* 与其他三个子应用口径一致,配置缺失应表现为空菜单并被发现。
*/
@GetMapping("/api/base/GetUserMod")
public List<Map<String, Object>> getUserMod(BoaUser authUserWithDept) {
if (authUserWithDept == null) {
return Collections.emptyList();
}
Integer schId = authUserWithDept.getCurrentRole() != null
? authUserWithDept.getCurrentRole().getSchId() : null;
List<BoaModule> modules = YschBaseOpenApi.getUserModule(authUserWithDept.getId(), schId);
if (modules == null || modules.isEmpty()) {
return Collections.emptyList();
}
// 用户中心已按 BoaModule.children 下发模块树:本地不再重组层级,仅做前端字段改名映射
return toFrontendTree(modules);
}
/**
* BoaModule 树 → 前端 layout 期望的字段形状(递归,<b>只改名不组树</b>;Notice/svc-hall/Cms 同款口径)。<br>
* 层级与兄弟顺序均由用户中心决定(customId 升序),本侧不重排;叶子 children 为空列表。<br>
* 包级可见而非 private:供同包单测直接覆盖映射,避免为测试改写 {@code YschBaseOpenApi} 全局静态客户端。
*/
static List<Map<String, Object>> toFrontendTree(List<BoaModule> modules) {
List<Map<String, Object>> result = new ArrayList<>();
if (modules == null || modules.isEmpty()) {
return result;
}
for (BoaModule m : modules) {
Map<String, Object> node = new LinkedHashMap<>();
node.put("id", m.getId());
node.put("modId", resolveModId(m));
node.put("name", m.getName());
node.put("parentId", m.getParentId() != null ? m.getParentId() : 0);
node.put("page", m.getPath() != null ? m.getPath() : "");
node.put("level", m.getLevel() != null ? m.getLevel() : 0);
node.put("icon", m.getIcon() != null ? m.getIcon() : "");
node.put("orderNumber", m.getSortNo() != null ? m.getSortNo() : 0);
node.put("usable", true);
node.put("children", toFrontendTree(m.getChildren()));
result.add(node);
}
return result;
}
/**
* modId 取值:优先 {@code customId}(数值),以对上 front {@code router/routes.ts} 的
* {@code meta.modId}(100~105,即 app_module.custom_id)约定;customId 非数字或缺失时回退主键 id。
* 注意:不能像其他子应用那样直接取主键 id——那会让前端 modId 兜底匹配永久失配(path 匹配仍可用)。
*/
private static Object resolveModId(BoaModule m) {
String customId = m.getCustomId();
if (customId != null && customId.trim().matches("\\d+")) {
return Integer.valueOf(customId.trim());
}
return m.getId();
}
}