From 13390cc99d5b83793f4dc2cae6d4b40566b67f8b Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sat, 11 Jul 2026 12:24:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(iam):=20=E8=8F=9C=E5=8D=95=E5=AD=90?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=88=86=E7=BB=84=E4=B8=8E=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E5=8F=98=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持 subpage_group 父子约束与统一处理;更新菜单时禁止修改 menuType,并补充错误与枚举 i18n。 --- _config/sql/iam_perm_menu.sql | 5 +- _config/sql/update-datas.sql | 85 ++++++++++++++++-- .../resources/i18n/en-US/enum/menu_type.json | 1 + .../resources/i18n/en-US/error/iam/menu.json | 5 ++ .../resources/i18n/zh-CN/enum/menu_type.json | 1 + .../resources/i18n/zh-CN/error/iam/menu.json | 5 ++ .../core/enums/perm/MenuTypeEnum.java | 4 +- .../permission/resource/PermMenuResult.java | 2 +- .../permission/resource/PermMenuService.java | 90 +++++++++++++++---- 9 files changed, 169 insertions(+), 29 deletions(-) diff --git a/_config/sql/iam_perm_menu.sql b/_config/sql/iam_perm_menu.sql index 663e7b193..ed4027e7b 100644 --- a/_config/sql/iam_perm_menu.sql +++ b/_config/sql/iam_perm_menu.sql @@ -12,7 +12,7 @@ Target Server Version : 160014 (160014) File Encoding : 65001 - Date: 11/07/2026 10:27:03 + Date: 11/07/2026 11:04:59 */ @@ -76,7 +76,7 @@ COMMENT ON COLUMN "public"."iam_perm_menu"."creator" IS '创建者ID'; COMMENT ON COLUMN "public"."iam_perm_menu"."last_modifier" IS '最后修改ID'; COMMENT ON COLUMN "public"."iam_perm_menu"."version" IS '版本号'; COMMENT ON COLUMN "public"."iam_perm_menu"."deleted" IS '删除标志'; -COMMENT ON COLUMN "public"."iam_perm_menu"."menu_type" IS '菜单类型: catalog-目录, menu-菜单, embedded-内嵌, link-外链'; +COMMENT ON COLUMN "public"."iam_perm_menu"."menu_type" IS '菜单类型: catalog-目录, menu-菜单, subpage-子页面, subpage_group-子页面分组, embedded-内嵌, link-外链'; COMMENT ON COLUMN "public"."iam_perm_menu"."active_icon" IS '激活状态图标'; COMMENT ON COLUMN "public"."iam_perm_menu"."badge" IS '徽章显示文本'; COMMENT ON COLUMN "public"."iam_perm_menu"."badge_type" IS '徽章类型: dot-圆点, normal-文本'; @@ -160,6 +160,7 @@ INSERT INTO "public"."iam_perm_menu" VALUES (803, 8, 'develop:auth', 'admin', 'C INSERT INTO "public"."iam_perm_menu" VALUES (310, 308, 'system:notify:wechat-config', 'admin', 'WechatNotify', '微信消息通知', 'Wechat Message Notification', 'menu.system.notify.wechatConfig', 'lucide:message-circle', 'f', 'f', '/system/notify/wechat/index', '/system/notify/wechat', NULL, 30, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-09 00:00:00+00', '2026-07-09 00:00:00+00'); INSERT INTO "public"."iam_perm_menu" VALUES (40504, 304, 'system:config:mobile_app', 'admin', 'MobileAppConfig', '移动端应用管理', 'Mobile App Management', 'menu.system.config.mobileApp', 'lucide:smartphone', 'f', 'f', '/system/config/mobileApp/MobileAppConfig', '/system/config/mobile-app', NULL, 6, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-05 00:00:00+00', '2026-07-10 08:54:32.213917+00'); INSERT INTO "public"."iam_perm_menu" VALUES (40505, 40504, 'system:config:mobile_app_detail', 'admin', 'MobileAppDetail', '移动端应用详情', 'Mobile App Detail', 'menu.system.config.mobileAppDetail', NULL, 't', 'f', '/system/config/mobileApp/detail/MobileAppDetail', '/system/config/mobile-app/detail/:appType', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-05 00:00:00+00', '2026-07-10 08:54:32.222476+00'); +INSERT INTO "public"."iam_perm_menu" VALUES (4040121, 40401, 'merchant:gateway-aggregate', 'admin', 'AggregateScanConfig', '聚合扫码', 'Aggregate QR Pay', 'menu.payment.merchant.aggregateScan', NULL, 't', 'f', '/payment/merchant/aggregate/AggregateScanConfig', '/payment/merchant/aggregate', NULL, 3, 'f', 't', 'f', 1, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-11 03:00:13.374496+00', '2026-07-11 03:00:13.374496+00'); -- ---------------------------- -- Primary Key structure for table iam_perm_menu diff --git a/_config/sql/update-datas.sql b/_config/sql/update-datas.sql index 2ab426691..ec2c85f7c 100644 --- a/_config/sql/update-datas.sql +++ b/_config/sql/update-datas.sql @@ -57,13 +57,82 @@ WHERE "id" = 9 AND "menu_code" = 'device'; -- ---------------------------- -- 聚合扫码配置菜单(应用级子页面) -- 对接后端 GatewayAggregateConfigAdminController, 三级配置深度(AUTO/METHOD/DIRECT) +-- 注意: id 不能用 4040120, 该 ID 已被"微信域名验证"(merchant:wx_verify)占用 -- ---------------------------- -INSERT INTO "public"."iam_perm_menu" -SELECT 4040120, 40401, 'merchant:gateway-aggregate', 'admin', 'AggregateScanConfig', '聚合扫码', 'Aggregate QR Pay', - 'menu.payment.merchant.aggregateScan', NULL, 't', 'f', - '/payment/merchant/aggregate/AggregateScanConfig', '/payment/merchant/aggregate', NULL, 3, 'f', 't', 'f', 1, 1, 1, 'f', 'subpage', - NULL, NULL, NULL, NULL, NULL, NULL, - CURRENT_TIMESTAMP, CURRENT_TIMESTAMP -WHERE NOT EXISTS ( - SELECT 1 FROM "public"."iam_perm_menu" WHERE "id" = 4040120 OR "menu_code" = 'merchant:gateway-aggregate' +INSERT INTO "public"."iam_perm_menu" VALUES ( + 4040121, 40401, 'merchant:gateway-aggregate', 'admin', 'AggregateScanConfig', + '聚合扫码', 'Aggregate QR Pay', 'menu.payment.merchant.aggregateScan', + NULL, 't', 'f', + '/payment/merchant/aggregate/AggregateScanConfig', '/payment/merchant/aggregate', + NULL, 3, 'f', 't', 'f', 1, 1, 1, 'f', 'subpage', + NULL, NULL, NULL, NULL, NULL, NULL, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ); + +-- ---------------------------- +-- 2026-07-11 菜单子页面分组(subpage_group)升级 +-- 新增 subpage_group 类型用于组织 menu 下大量平铺的 subpage, 不在侧边栏显示 +-- 详见方案: catalog > menu > subpage_group(隐藏) > subpage, 面包屑显示完整四级 +-- ---------------------------- + +-- 更新 menu_type 字段注释 +COMMENT ON COLUMN "public"."iam_perm_menu"."menu_type" IS '菜单类型: catalog-目录, menu-菜单, subpage-子页面, subpage_group-子页面分组, embedded-内嵌, link-外链'; + +-- 新建 4 个子页面分组节点(纯容器: 无 path/component/menu_code, 强制隐藏) +-- 40401 商户列表下 3 个分组 +INSERT INTO "public"."iam_perm_menu" + (id, pid, menu_code, client_code, name, title_cn, title_en, i18n_key, icon, + hidden, hide_children_menu, component, path, redirect, sort_no, root, + keep_alive, affix_tab, creator, last_modifier, version, deleted, menu_type, + active_icon, badge, badge_type, badge_variants, iframe_src, link, + create_time, last_modified_time) +VALUES + (4040130, 40401, NULL, 'admin', 'MchManageGroup', '商户管理', 'Merchant Management', + 'menu.payment.merchant.group.manage', 'lucide:settings-2', + true, false, NULL, NULL, NULL, 1, false, + false, false, 1, 1, 0, false, 'subpage_group', + NULL, NULL, NULL, NULL, NULL, NULL, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (4040131, 40401, NULL, 'admin', 'ChannelMerchantGroup', '通道商户', 'Channel Merchant', + 'menu.payment.merchant.group.channelMerchant', 'lucide:repeat', + true, false, NULL, NULL, NULL, 2, false, + false, false, 1, 1, 0, false, 'subpage_group', + NULL, NULL, NULL, NULL, NULL, NULL, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (4040132, 40401, NULL, 'admin', 'ChannelAppGroup', '渠道应用', 'Channel App', + 'menu.payment.merchant.group.channelApp', 'lucide:layout-grid', + true, false, NULL, NULL, NULL, 3, false, + false, false, 1, 1, 0, false, 'subpage_group', + NULL, NULL, NULL, NULL, NULL, NULL, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + -- 40105 支付产品管理下 1 个分组 + (40508, 40105, NULL, 'admin', 'ChannelIsvConfigGroup', '渠道服务商配置', 'Channel ISV Config', + 'menu.payment.config.group.channelIsv', 'lucide:server', + true, false, NULL, NULL, NULL, 1, false, + false, false, 1, 1, 0, false, 'subpage_group', + NULL, NULL, NULL, NULL, NULL, NULL, + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +ON CONFLICT (id) DO NOTHING; + +-- 将子页面 pid 迁移到对应分组 +-- 4040130 商户管理组(9个) +UPDATE "public"."iam_perm_menu" +SET "pid" = 4040130, "last_modified_time" = CURRENT_TIMESTAMP +WHERE "id" IN (4040101, 4040103, 4040102, 4040110, 4040111, 4040108, 4040117, 4040121, 4040120); + +-- 4040131 通道商户组(3个) +UPDATE "public"."iam_perm_menu" +SET "pid" = 4040131, "last_modified_time" = CURRENT_TIMESTAMP +WHERE "id" IN (4040106, 4040109, 4040112); + +-- 4040132 渠道应用组(4个) +UPDATE "public"."iam_perm_menu" +SET "pid" = 4040132, "last_modified_time" = CURRENT_TIMESTAMP +WHERE "id" IN (4040113, 4040114, 4040115, 4040119); + +-- 40508 渠道服务商配置组(3个) +UPDATE "public"."iam_perm_menu" +SET "pid" = 40508, "last_modified_time" = CURRENT_TIMESTAMP +WHERE "id" IN (40502, 40503, 40506); + +-- 注: 40501(产品配置详情)、40505(移动端应用详情) 仅1个子页面, 保留直挂 menu 不强制分组 diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/menu_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/menu_type.json index 8faef3113..1abfcdf6e 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/menu_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/enum/menu_type.json @@ -2,6 +2,7 @@ "catalog": "Catalog", "menu": "Menu", "subpage": "Subpage", + "subpage_group": "Subpage Group", "embedded": "Embedded Page", "link": "External Link" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iam/menu.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iam/menu.json index 4ea2339d8..efd211058 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iam/menu.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/en-US/error/iam/menu.json @@ -4,9 +4,14 @@ "subpageNoChildren": "Subpage cannot have child menus", "hasChildrenCannotDelete": "Cannot delete menu with child menus", "invalidType": "Invalid menu type", + "typeImmutable": "Menu type cannot be changed after creation", "subpageNeedParent": "Subpage must select a parent menu", "parentNotExist": "Parent menu not found", "subpageParentMustBeMenu": "Subpage parent must be a menu type", + "subpageParentInvalid": "Subpage parent must be a menu or subpage group", + "subpageGroupNeedParent": "Subpage group must select a parent menu", + "subpageGroupParentMustBeMenu": "Subpage group parent must be a menu type", + "subpageGroupChildMustBeSubpage": "Subpage group can only contain subpages", "needParentCatalog": "Menu, embedded page, or external link must select a parent directory", "parentMustBeCatalog": "Parent of menu, embedded page, or external link must be a directory type", "notExist": "Menu not found" diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/menu_type.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/menu_type.json index a01708d8b..fd6ca6c50 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/menu_type.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/enum/menu_type.json @@ -2,6 +2,7 @@ "catalog": "目录", "menu": "菜单", "subpage": "子页面", + "subpage_group": "子页面分组", "embedded": "内嵌页面", "link": "外链" } diff --git a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iam/menu.json b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iam/menu.json index 291ced58e..473a86623 100644 --- a/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iam/menu.json +++ b/daxpay-platform/daxpay-platform-common/common-i18n/src/main/resources/i18n/zh-CN/error/iam/menu.json @@ -4,9 +4,14 @@ "subpageNoChildren": "子页面不能有下级菜单", "hasChildrenCannotDelete": "有子菜单不可以删除", "invalidType": "无效的菜单类型", + "typeImmutable": "菜单类型创建后不可修改", "subpageNeedParent": "子页面必须选择上级菜单", "parentNotExist": "上级菜单不存在", "subpageParentMustBeMenu": "子页面的上级菜单必须是菜单类型", + "subpageParentInvalid": "子页面的上级必须是菜单或子页面分组", + "subpageGroupNeedParent": "子页面分组必须选择上级菜单", + "subpageGroupParentMustBeMenu": "子页面分组的上级必须是菜单类型", + "subpageGroupChildMustBeSubpage": "子页面分组下只能包含子页面", "needParentCatalog": "菜单、内嵌页面、外链必须选择上级目录", "parentMustBeCatalog": "菜单、内嵌页面、外链的上级菜单必须是目录类型", "notExist": "菜单不存在" diff --git a/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/perm/MenuTypeEnum.java b/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/perm/MenuTypeEnum.java index 9f35ef369..4b7c3cca1 100644 --- a/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/perm/MenuTypeEnum.java +++ b/daxpay-platform/daxpay-platform-core/src/main/java/cn/daxpay/open/platform/core/enums/perm/MenuTypeEnum.java @@ -16,6 +16,8 @@ public enum MenuTypeEnum implements I18nSupport { MENU("menu"), /// 子页面 SUBPAGE("subpage"), + /// 子页面分组 + SUBPAGE_GROUP("subpage_group"), /// 内嵌页面 EMBEDDED("embedded"), /// 外链 @@ -46,7 +48,7 @@ public enum MenuTypeEnum implements I18nSupport { } public boolean canAddChild() { - return this == CATALOG || this == MENU; + return this == CATALOG || this == MENU || this == SUBPAGE_GROUP; } public boolean needParent() { diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/permission/resource/PermMenuResult.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/permission/resource/PermMenuResult.java index ceefcc09c..92866f8a8 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/permission/resource/PermMenuResult.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/result/permission/resource/PermMenuResult.java @@ -61,7 +61,7 @@ public class PermMenuResult { @Schema(description = "是否固定标签页") private boolean affixTab; - @Schema(description = "菜单类型: catalog-目录, menu-菜单, embedded-内嵌, link-外链") + @Schema(description = "菜单类型: catalog-目录, menu-菜单, subpage-子页面, subpage_group-子页面分组, embedded-内嵌, link-外链") private String menuType; @Schema(description = "徽章显示文本") diff --git a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/permission/resource/PermMenuService.java b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/permission/resource/PermMenuService.java index 675afa315..ca5eac3ce 100644 --- a/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/permission/resource/PermMenuService.java +++ b/daxpay-platform/daxpay-platform-service/service-iam/src/main/java/cn/daxpay/open/platform/iam/service/permission/resource/PermMenuService.java @@ -47,9 +47,8 @@ public class PermMenuService { throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.codeExists"); } } - if (MenuTypeEnum.SUBPAGE.equalsCode(param.getMenuType())) { - param.setHidden(true); - } + // 子页面类菜单(subpage/subpage_group)统一处理 + applySubpageLikeRules(param, false); PermMenu permMenu = PermMenu.init(param); permMenuManager.save(permMenu); } @@ -61,6 +60,10 @@ public class PermMenuService { // 权限: 菜单不存在 .orElseThrow(() -> new DataNotExistException("error.iam.menu.notExist")); + // 菜单类型创建后不可修改 + if (!Objects.equals(permMenu.getMenuType(), param.getMenuType())) { + throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.typeImmutable"); + } // 校验父级菜单类型 validateParentType(param.getPid(), param.getMenuType()); // 检查上级菜单是否出现了循环依赖 @@ -79,9 +82,8 @@ public class PermMenuService { // 权限: 子页面不能有下级菜单 throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageNoChildren"); } - if (MenuTypeEnum.SUBPAGE.equalsCode(param.getMenuType())) { - param.setHidden(true); - } + // 子页面类菜单统一处理(含分组下级类型校验) + applySubpageLikeRules(param, true); PermMenuConvert.CONVERT.copy(param, permMenu); permMenuManager.updateById(permMenu); } @@ -168,7 +170,8 @@ public class PermMenuService { /// 校验父级菜单类型 /// - 目录类型可以选择目录作为父级,也可以不选(作为根目录) /// - 菜单、内嵌页面、外链的上级必须是目录类型 - /// - 子页面的上级必须是菜单类型 + /// - 子页面的上级必须是菜单或子页面分组 + /// - 子页面分组的上级必须是菜单(一级限制,不可嵌套) /// /// @param pid 父级菜单ID /// @param menuType 当前菜单类型 @@ -182,18 +185,21 @@ public class PermMenuService { return; } if (MenuTypeEnum.SUBPAGE.equals(typeEnum)) { - if (pid == null) { - // 权限: 子页面必须选择上级菜单 - throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageNeedParent"); - } - PermMenu parent = permMenuManager.findById(pid).orElse(null); - if (parent == null) { - // 权限: 上级菜单不存在 - throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.parentNotExist"); + PermMenu parent = validateAndGetParent(pid, "error.iam.menu.subpageNeedParent"); + String parentType = parent.getMenuType(); + // 子页面的上级可以是菜单或子页面分组 + if (!MenuTypeEnum.MENU.equalsCode(parentType) && !MenuTypeEnum.SUBPAGE_GROUP.equalsCode(parentType)) { + // 权限: 子页面的上级必须是菜单或子页面分组 + throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageParentInvalid"); } + return; + } + if (MenuTypeEnum.SUBPAGE_GROUP.equals(typeEnum)) { + PermMenu parent = validateAndGetParent(pid, "error.iam.menu.subpageGroupNeedParent"); + // 子页面分组的上级必须是菜单(不可嵌套分组) if (!MenuTypeEnum.MENU.equalsCode(parent.getMenuType())) { - // 权限: 子页面的上级菜单必须是菜单类型 - throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageParentMustBeMenu"); + // 权限: 子页面分组的上级必须是菜单类型 + throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageGroupParentMustBeMenu"); } return; } @@ -212,6 +218,56 @@ public class PermMenuService { } } + /// 校验并获取父级菜单(子页面/子页面分组共用) + /// + /// @param pid 父级菜单ID + /// @param needParentKey pid 为空时抛出的错误消息 key + /// @return 父级菜单实体 + private PermMenu validateAndGetParent(Long pid, String needParentKey) { + if (pid == null) { + throw new BizException(CommonCode.FAIL_CODE, needParentKey); + } + PermMenu parent = permMenuManager.findById(pid).orElse(null); + if (parent == null) { + // 权限: 上级菜单不存在 + throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.parentNotExist"); + } + return parent; + } + + /// 子页面类菜单(subpage/subpage_group)的统一处理 + /// - 强制隐藏(不在侧边栏显示) + /// - 子页面分组额外校验下级只能是子页面,并清空路由/组件/编码(纯容器) + /// + /// @param param 菜单参数 + /// @param checkChildren 是否校验下级类型(编辑时为 true) + private void applySubpageLikeRules(PermMenuParam param, boolean checkChildren) { + String menuType = param.getMenuType(); + boolean isSubpage = MenuTypeEnum.SUBPAGE.equalsCode(menuType); + boolean isGroup = MenuTypeEnum.SUBPAGE_GROUP.equalsCode(menuType); + if (!isSubpage && !isGroup) { + return; + } + // 强制隐藏 + param.setHidden(true); + if (isGroup) { + // 子页面分组: 编辑时校验下级只能是子页面 + if (checkChildren && param.getId() != null) { + List children = permMenuManager.findAllByPid(param.getId()); + for (PermMenu child : children) { + if (!MenuTypeEnum.SUBPAGE.equalsCode(child.getMenuType())) { + // 权限: 子页面分组下只能包含子页面 + throw new BizException(CommonCode.FAIL_CODE, "error.iam.menu.subpageGroupChildMustBeSubpage"); + } + } + } + // 清空路由/组件/编码(纯容器节点) + param.setMenuCode(null); + param.setComponent(null); + param.setPath(null); + } + } + /// 检查菜单编码是否存在(同一终端下) /// @param menuCode 菜单编码 /// @param clientCode 终端编码