ソースを参照

提交630第二阶段菜单配置

chenxiaofei 4 日 前
コミット
a013a7cc3f

+ 72 - 6
sckw-auth/src/main/java/com/sckw/auth/service/impl/AuthServiceImpl.java

@@ -57,6 +57,20 @@ public class AuthServiceImpl implements IAuthService {
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteFleetService fleetService;
 
+    /**
+     * APP模块权限逻辑菜单关键字,仅用于 {@link #applyAppModulePermissionsByConfig} 控制模块显隐,
+     * 不应出现在底部导航 TabBar 中。
+     */
+    private static final String[] APP_MODULE_LOGIC_MENU_KEYWORDS = {
+            "\u5c55\u793a\u8ba2\u5355\u7edf\u8ba1", "orderStatistics", "order-statistics", "order_statistics",
+            "\u5c55\u793a\u9500\u552e\u7edf\u8ba1", "salesStatistics", "sales-statistics", "sales_statistics",
+            "\u5c55\u793a\u94b1\u5305", "wallet",
+            "\u5c55\u793a\u5730\u5740", "address",
+            "\u5c55\u793a\u5f85\u5c65\u7ea6\u4fdd\u8bc1\u91d1", "PENDING_PERFORMANCE_BALANCE", "pendingPerformanceBalance", "pending-performance-balance",
+            "\u5c55\u793a\u9884\u4ed8\u4f59\u989d", "PREPAY_BALANCE", "prepayBalance", "prepay-balance",
+            "\u5c55\u793a\u5f85\u4ed8\u8fd0\u8d39", "PENDING_FREIGHT", "pendingFreight", "pending-freight"
+    };
+
     @Override
     public HttpResult login(LoginBase loginBase) {
         if (StringUtils.isNotBlank(loginBase.getCaptcha())) {
@@ -1090,11 +1104,62 @@ public class AuthServiceImpl implements IAuthService {
         }
         return menus.stream()
                 .filter(Objects::nonNull)
-                .anyMatch(menu -> Arrays.stream(keywords)
-                        .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
-                        .anyMatch(keyword -> containsIgnoreCase(menu.getName(), keyword)
-                                || containsIgnoreCase(menu.getPerms(), keyword)
-                                || containsIgnoreCase(menu.getUrl(), keyword)));
+                .anyMatch(menu -> matchesAppModuleMenuKeywords(menu, keywords));
+    }
+
+    /**
+     * 判断菜单是否为模块权限逻辑菜单(仅控制模块显隐,不作为 TabBar 展示项)。
+     * <p>
+     * 该方法通过检查菜单的名称、权限标识或URL是否包含预定义的“逻辑菜单关键字”,
+     * 来识别该菜单是否属于仅用于控制APP端特定模块(如订单统计、钱包等)显示/隐藏的辅助菜单。
+     * 此类菜单不应出现在底部导航栏(TabBar)中。
+     * </p>
+     *
+     * @param menu APP菜单对象
+     * @return true-是逻辑菜单(不展示在TabBar),false-非逻辑菜单(可展示在TabBar)
+     */
+    boolean isAppModuleLogicMenu(AppTabBarMenuResDto menu) {
+        // 1. 空值校验:若菜单对象为空,直接返回false
+        if (Objects.isNull(menu)) {
+            return false;
+        }
+
+        // 2. 关键字匹配:检查菜单是否命中预定义的逻辑菜单关键字集合
+        boolean isLogicMenu = matchesAppModuleMenuKeywords(menu, APP_MODULE_LOGIC_MENU_KEYWORDS);
+        
+        // 3. 记录调试日志,便于排查哪些菜单被识别为逻辑菜单
+        if (isLogicMenu) {
+            log.debug("识别到APP模块逻辑菜单 - 菜单名称: {}, URL: {}", menu.getName(), menu.getUrl());
+        }
+        
+        return isLogicMenu;
+    }
+
+    /**
+     * 判断菜单是否匹配指定的关键字列表。
+     * <p>
+     * 该方法会遍历给定的关键字数组,检查菜单的名称(name)、权限标识(perms)或URL(url)
+     * 是否包含任意一个非空关键字(忽略大小写)。只要有一个字段匹配成功,即返回true。
+     * </p>
+     *
+     * @param menu     APP菜单对象
+     * @param keywords 待匹配的关键字数组
+     * @return true-匹配成功,false-未匹配
+     */
+    private boolean matchesAppModuleMenuKeywords(AppTabBarMenuResDto menu, String... keywords) {
+        // 1. 参数校验:若菜单对象或关键字数组为空,直接返回false
+        if (Objects.isNull(menu) || Objects.isNull(keywords)) {
+            return false;
+        }
+
+        // 2. 流式处理关键字进行匹配
+        return Arrays.stream(keywords)
+                // 过滤掉空白关键字,避免无效匹配
+                .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
+                // 只要有一个关键字在菜单的 name、perms 或 url 中出现(忽略大小写),即判定为匹配
+                .anyMatch(keyword -> containsIgnoreCase(menu.getName(), keyword)
+                        || containsIgnoreCase(menu.getPerms(), keyword)
+                        || containsIgnoreCase(menu.getUrl(), keyword));
     }
 
     private boolean containsIgnoreCase(String source, String keyword) {
@@ -1166,9 +1231,10 @@ public class AuthServiceImpl implements IAuthService {
             }
 
             // 5. 数据转换:将DTO列表转换为前端所需的TabBarItem对象列表
-            // 过滤掉null对象,并对每个菜单项进行字段默认值处理(防止前端展示空白
+            // 过滤掉null对象及模块权限逻辑菜单(仅用于控制模块显隐,不作为底部导航展示
             List<LoginResVo1.TabBarItem> tabBarItems = menus.stream()
                     .filter(Objects::nonNull)
+                    .filter(menu -> !isAppModuleLogicMenu(menu))
                     .map(menu -> buildTabBarItem(
                             // 菜单名称,若为空则默认为空字符串
                             org.apache.commons.lang3.StringUtils.defaultString(menu.getName()),

+ 20 - 0
sckw-auth/src/test/java/com/sckw/auth/service/impl/AuthServiceImplTest.java

@@ -94,6 +94,26 @@ public class AuthServiceImplTest {
         Assert.assertFalse(loginRes.getShowAddressModule());
     }
 
+    /**
+     * 验证模块权限逻辑菜单不会出现在底部导航 TabBar 中。
+     */
+    @Test
+    public void appTabBarShouldExcludeModuleLogicMenus() throws Exception {
+        AuthServiceImpl authService = new AuthServiceImpl();
+        AppTabBarMenuResDto homeMenu = new AppTabBarMenuResDto();
+        homeMenu.setName("首页");
+        homeMenu.setIcon("/static/icon/home-selected.png");
+        homeMenu.setNotSelectedIconPath("/static/icon/home.png");
+        homeMenu.setUrl("/pages/home/index");
+        setRemoteUserService(authService, buildRemoteUserServiceProxy(
+                homeMenu, buildOrderStatisticsMenu(), buildWalletMenu(), buildAddressMenu()));
+
+        List<LoginResVo1.TabBarItem> tabBarItems = authService.buildAppTabBarByRoleMenus(1001L);
+
+        Assert.assertEquals(1, tabBarItems.size());
+        Assert.assertEquals("首页", tabBarItems.get(0).getText());
+    }
+
     /**
      * 验证钱包、地址模块只由菜单结果控制,并按菜单子项返回钱包展示项。
      */

+ 136 - 0
sql/2026/06/2026_06_11_app_tabbar_menu_permission.sql

@@ -0,0 +1,136 @@
+-- APP TabBar 菜单初始化脚本
+-- 仅生成 kws_menu 表数据;角色授权请在菜单生成后单独维护 kws_menu_rights。
+-- 底部导航菜单已按 url + icon + not_selected_icon_path 去重。
+-- 模块权限菜单单独生成,供 AuthServiceImpl#applyAppModulePermissionsByConfig 识别。
+
+START TRANSACTION;
+
+SET @next_kws_menu_id = (SELECT COALESCE(MAX(id), 0) FROM kws_menu);
+
+INSERT INTO kws_menu (
+    id, client_type, parent_id, name, url, perms, type, links, icon, not_selected_icon_path,
+    sort, level, custom, is_display, is_main, using_roles, remark, status,
+    create_by, create_time, update_by, update_time, del_flag
+)
+SELECT
+    @next_kws_menu_id := @next_kws_menu_id + 1,
+    menu_config.client_type,
+    menu_config.parent_id,
+    menu_config.name,
+    menu_config.url,
+    menu_config.perms,
+    menu_config.type,
+    menu_config.links,
+    menu_config.icon,
+    menu_config.not_selected_icon_path,
+    menu_config.sort,
+    menu_config.level,
+    menu_config.custom,
+    menu_config.is_display,
+    menu_config.is_main,
+    menu_config.using_roles,
+    menu_config.remark,
+    menu_config.status,
+    menu_config.create_by,
+    NOW(),
+    menu_config.update_by,
+    NOW(),
+    menu_config.del_flag
+FROM (
+    SELECT 3 client_type, 0 parent_id, '商城' name, '/pages/mall/index' url,
+           'app:tabbar:mall' perms, 1 type, NULL links,
+           '/static/tabbar/mall_select.png' icon, '/static/tabbar/mall.png' not_selected_icon_path,
+           10 sort, 1 level, 0 custom, 1 is_display, 0 is_main, '' using_roles,
+           'APP底部导航:商城' remark, 0 status, 0 create_by, 0 update_by, 0 del_flag
+    UNION ALL
+    SELECT 3, 0, '贸易订单', '/pages/tradeOrder/index',
+           'app:tabbar:trade', 1, NULL,
+           '/static/tabbar/trade_select.png', '/static/tabbar/trade.png',
+           20, 1, 0, 1, 0, '', 'APP底部导航:贸易订单', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '物流订单', '/pages/logistics/order/index',
+           'app:tabbar:logOrder', 1, NULL,
+           '/static/tabbar/logOrder_select.png', '/static/tabbar/logOrder.png',
+           30, 1, 0, 1, 0, '', 'APP底部导航:物流订单', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '物流运单', '/pages/logistics/waybill/index',
+           'app:tabbar:waybill', 1, NULL,
+           '/static/tabbar/waybill_select.png', '/static/tabbar/waybill.png',
+           40, 1, 0, 1, 0, '', 'APP底部导航:物流运单', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '个人中心', '/pages/myCenter/index',
+           'app:tabbar:my', 1, NULL,
+           '/static/tabbar/my_select.png', '/static/tabbar/my.png',
+           50, 1, 0, 1, 0, '', 'APP底部导航:个人中心', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '往来车辆', '/pages/doorkeeper/index',
+           'app:tabbar:doorKeeper:car', 1, NULL,
+           '/static/tabbar/car_select.png', '/static/tabbar/car.png',
+           60, 1, 0, 1, 0, '', 'APP底部导航:门卫往来车辆', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '任务', '/pages/forklift/index',
+           'app:tabbar:forklift:task', 1, NULL,
+           '/static/tabbar/task_select.png', '/static/tabbar/task.png',
+           70, 1, 0, 1, 0, '', 'APP底部导航:叉车司机任务', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '任务', '/pages/index/index',
+           'app:tabbar:driver:task', 1, NULL,
+           '/static/tabbar/task_select.png', '/static/tabbar/task.png',
+           80, 1, 0, 1, 0, '', 'APP底部导航:司机任务', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '管理', '/pages/manage/index',
+           'app:tabbar:driver:manage', 1, NULL,
+           '/static/tabbar/manage_select.png', '/static/tabbar/manage.png',
+           90, 1, 0, 1, 0, '', 'APP底部导航:司机管理', 0, 0, 0, 0
+
+    UNION ALL
+    SELECT 3, 0, '展示订单统计', '/pages/app-permission/order-statistics',
+           'app:module:orderStatistics', 1, NULL,
+           '', '',
+           900, 1, 0, 0, 0, '', 'APP模块权限菜单:控制订单统计模块展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示销售统计', '/pages/app-permission/sales-statistics',
+           'app:module:salesStatistics', 1, NULL,
+           '', '',
+           901, 1, 0, 0, 0, '', 'APP模块权限菜单:控制销售统计模块展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示钱包', '/pages/app-permission/wallet',
+           'app:module:wallet', 1, NULL,
+           '', '',
+           902, 1, 0, 0, 0, '', 'APP模块权限菜单:控制钱包模块展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示地址', '/pages/app-permission/address',
+           'app:module:address', 1, NULL,
+           '', '',
+           903, 1, 0, 0, 0, '', 'APP模块权限菜单:控制地址模块展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示待履约保证金', '/pages/app-permission/pending-performance-balance',
+           'PENDING_PERFORMANCE_BALANCE', 1, NULL,
+           '', '',
+           904, 1, 0, 0, 0, '', 'APP模块权限菜单:控制钱包待履约保证金子项展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示预付余额', '/pages/app-permission/prepay-balance',
+           'PREPAY_BALANCE', 1, NULL,
+           '', '',
+           905, 1, 0, 0, 0, '', 'APP模块权限菜单:控制钱包预付余额子项展示', 0, 0, 0, 0
+    UNION ALL
+    SELECT 3, 0, '展示待付运费', '/pages/app-permission/pending-freight',
+           'PENDING_FREIGHT', 1, NULL,
+           '', '',
+           906, 1, 0, 0, 0, '', 'APP模块权限菜单:控制钱包待付运费子项展示', 0, 0, 0, 0
+) menu_config
+WHERE NOT EXISTS (
+    SELECT 1
+    FROM kws_menu menu
+    WHERE menu.url = menu_config.url
+      AND menu.icon = menu_config.icon
+      AND menu.not_selected_icon_path = menu_config.not_selected_icon_path
+      AND menu.del_flag = 0
+);
+
+COMMIT;
+
+
+
+
+INSERT INTO sckw_ng_system.kws_menu (id, client_type, parent_id, name, type, url, perms, icon, not_selected_icon_path, sort, level, custom, links, is_display, is_main, using_roles, remark, status, create_by, create_time, update_by, update_time, del_flag) VALUES (545295056974647296, 3, 0, '投诉', 1, '/pages/complaint/index', 'complaint:app', '/static/tabbar/complaint_select.png', '/static/tabbar/complaint.png', 150, 1, 0, null, null, 0, '', null, 0, 156383116720607232, '2026-06-10 16:03:52', 156383116720607232, '2026-06-10 16:08:02', 0);