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。 *

GetUserMod 与其他子应用(Notice / svc-hall / 事项库 Cms)统一走平台下发:经 SDK * {@code YschBaseOpenApi.getUserModule} 取当前用户在本应用({@code yh.open.app-key=100019})下的 * 已授权模块树,层级由平台契约 {@code BoaModule.children} 承载,本侧仅映射为前端 layout * 期望的字段形状({@code page/modId/children})。 *

设计概览 §9.4「菜单为本地路由约定、不进 app_module」的口径自 2026-09-15 起作废,改为平台统一下发。 */ @RestController public class FlowBaseController { @GetMapping("/api/base/GetUserInfo") public BoaUser getUserInfo(BoaUser authUserWithDept) { return authUserWithDept; } /** * 当前用户已授权模块树(左侧菜单)。 *

依赖 UserBase 侧数据:本应用的 {@code app_module} 行({@code path} 须逐字对上 * front 路由,见 router/routes.ts)+ 角色/组授权,二者缺一即返回空菜单。 * 这里不保留本地 MENUS 兜底——静默回退会把「平台未配模块/未授权」伪装成正常, * 与其他三个子应用口径一致,配置缺失应表现为空菜单并被发现。 */ @GetMapping("/api/base/GetUserMod") public List> getUserMod(BoaUser authUserWithDept) { if (authUserWithDept == null) { return Collections.emptyList(); } Integer schId = authUserWithDept.getCurrentRole() != null ? authUserWithDept.getCurrentRole().getSchId() : null; List modules = YschBaseOpenApi.getUserModule(authUserWithDept.getId(), schId); if (modules == null || modules.isEmpty()) { return Collections.emptyList(); } // 用户中心已按 BoaModule.children 下发模块树:本地不再重组层级,仅做前端字段改名映射 return toFrontendTree(modules); } /** * BoaModule 树 → 前端 layout 期望的字段形状(递归,只改名不组树;Notice/svc-hall/Cms 同款口径)。
* 层级与兄弟顺序均由用户中心决定(customId 升序),本侧不重排;叶子 children 为空列表。
* 包级可见而非 private:供同包单测直接覆盖映射,避免为测试改写 {@code YschBaseOpenApi} 全局静态客户端。 */ static List> toFrontendTree(List modules) { List> result = new ArrayList<>(); if (modules == null || modules.isEmpty()) { return result; } for (BoaModule m : modules) { Map 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(); } }