Explorar o código

提交630第二阶段app登录角色修改为配置

chenxiaofei hai 1 semana
pai
achega
574749dcfb

+ 7 - 1
sckw-auth/pom.xml

@@ -76,6 +76,12 @@
             <version>4.5.0</version>
         </dependency>
 
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
 
     </dependencies>
     <build>
@@ -102,4 +108,4 @@
             </plugin>
         </plugins>
     </build>
-</project>
+</project>

+ 306 - 11
sckw-auth/src/main/java/com/sckw/auth/service/impl/AuthServiceImpl.java

@@ -10,6 +10,7 @@ import com.sckw.auth.model.vo.res.LoginResVo1;
 import com.sckw.auth.service.IAuthService;
 import com.sckw.auth.util.AsyncFactory;
 import com.sckw.core.common.enums.enums.DictEnum;
+import com.sckw.core.common.enums.enums.DictTypeEnum;
 import com.sckw.core.exception.SystemException;
 import com.sckw.core.model.constant.Global;
 import com.sckw.core.model.constant.NumberConstant;
@@ -168,11 +169,11 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setClientType(loginBase.getClientType());
         loginRes.setSystemType(loginBase.getSystemType());
         loginRes.setToken(token);
-        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBar(loginRes, loginBase, null, 1, loginRes.getEntTypes());
+        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBarByConfig(loginRes, loginBase, null, 1, loginRes.getEntTypes());
         if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(tabBar)) {
             loginRes.setTabBar(tabBar);
         }
-        applyAppModulePermissions(loginRes, loginBase, null);
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         loginRes.setRefreshToken(refreshToken);
         return HttpResult.ok(loginRes);
     }
@@ -262,11 +263,11 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setDriverId(user.getDriverId());
         loginRes.setRoleName(user.getRoleName());
         loginRes.setRoleList(user.getRoleInfoDto());
-        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBar(loginRes, loginBase, user.getRoleName(), 0, loginRes.getEntTypes());
+        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBarByConfig(loginRes, loginBase, user.getRoleName(), 0, loginRes.getEntTypes());
         if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(tabBar)) {
             loginRes.setTabBar(tabBar);
         }
-        applyAppModulePermissions(loginRes, loginBase, user.getRoleName());
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         if (user.getSystemType().equals(SystemTypeEnum.MANAGE.getCode())) {
             loginRes.setValid(true);
         } else {
@@ -352,12 +353,12 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setRoleId(user.getRoleId());
         loginRes.setRoleName(user.getRoleName());
         loginRes.setRoleList(user.getRoleInfoDto());
-        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBar(loginRes, loginBase, user.getRoleName(), 0, loginRes.getEntTypes());
+        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBarByConfig(loginRes, loginBase, user.getRoleName(), 0, loginRes.getEntTypes());
         if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(tabBar)) {
             loginRes.setTabBar(tabBar);
 
         }
-        applyAppModulePermissions(loginRes, loginBase, user.getRoleName());
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         if (user.getSystemType().equals(SystemTypeEnum.MANAGE.getCode())) {
             loginRes.setValid(true);
         } else {
@@ -523,7 +524,7 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setRoleId(user.getRoleId());
         LoginBase loginBase = new LoginBase();
         loginBase.setClientType(clientType);
-        applyAppModulePermissions(loginRes, loginBase, user.getRoleName());
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         if (user.getSystemType().equals(SystemTypeEnum.MANAGE.getCode())) {
             loginRes.setValid(true);
         } else {
@@ -859,11 +860,11 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setToken(newToken);
         loginRes.setRefreshToken(newRefreshToken);
         loginRes.setDriverId(driver.getId());
-        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBar(loginRes, loginBase, null, 1, loginRes.getEntTypes());
+        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBarByConfig(loginRes, loginBase, null, 1, loginRes.getEntTypes());
         if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(tabBar)) {
             loginRes.setTabBar(tabBar);
         }
-        applyAppModulePermissions(loginRes, loginBase, null);
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         return loginRes;
     }
 
@@ -897,11 +898,11 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setDriverId(user.getDriverId());
         loginRes.setRoleName(user.getRoleName());
         loginRes.setRoleList(user.getRoleInfoDto());
-        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBar(loginRes, loginBase, user.getRoleName(), 1, loginRes.getEntTypes());
+        List<LoginResVo1.TabBarItem> tabBar = buildAppTabBarByConfig(loginRes, loginBase, user.getRoleName(), 1, loginRes.getEntTypes());
         if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(tabBar)) {
             loginRes.setTabBar(tabBar);
         }
-        applyAppModulePermissions(loginRes, loginBase, user.getRoleName());
+        applyAppModulePermissionsByConfig(loginRes, loginBase);
         if (user.getSystemType().equals(SystemTypeEnum.MANAGE.getCode())) {
             loginRes.setValid(true);
         } else {
@@ -917,6 +918,300 @@ public class AuthServiceImpl implements IAuthService {
                 || Objects.equals(clientType, ClientTypeEnum.android.getValue())
                 || Objects.equals(clientType, ClientTypeEnum.mobile.getValue());
     }
+    
+    /**
+     * 根据角色ID字典配置控制APP模块权限。
+     * <p>
+     * 该方法主要用于在登录或刷新Token时,根据当前用户的角色配置,动态设置APP端需要展示的模块(如订单统计、销售统计、钱包、地址管理等)。
+     * 仅在APP端登录时生效,PC/H5端不展示这些特定模块配置。
+     *
+     * @param loginRes  登录返回信息对象,用于设置模块显示状态及钱包子项
+     * @param loginBase 登录请求基础信息,用于判断客户端类型
+     */
+    private void applyAppModulePermissionsByConfig(LoginResVo1 loginRes, LoginBase loginBase) {
+        // 1. 初始化默认值:隐藏所有特定模块,清空钱包子项
+        // 确保在未匹配到任何角色或配置异常时,前端不会错误展示敏感或无关模块
+        loginRes.setShowOrderStatisticsModule(Boolean.FALSE);
+        loginRes.setShowSalesStatisticsModule(Boolean.FALSE);
+        loginRes.setShowWalletModule(Boolean.FALSE);
+        loginRes.setShowAddressModule(Boolean.FALSE);
+        loginRes.setWalletModuleItems(Collections.emptyList());
+
+        // 2. 校验客户端类型:仅对APP端(iOS/Android/Mobile)进行模块权限控制
+        if (!isAppLogin(loginBase)) {
+            log.debug("非APP端登录,跳过APP模块权限配置。clientType: {}", loginBase.getClientType());
+            return;
+        }
+
+        // 3. 加载当前角色的APP权限配置
+        // 从字典表 app_role_permission 中读取角色ID对应的权限标识
+        Long roleId = loginRes.getRoleId();
+        AppRoleConfig appRoleConfig = loadAppRoleConfig(roleId);
+        log.info("开始配置APP模块权限 - userId: {}, roleId: {}, config: {}", loginRes.getId(), roleId, appRoleConfig.roleTypes);
+
+        // 4. 匹配角色类型并设置相应的模块显示状态
+        boolean isSeller = appRoleConfig.match(AppRoleType.SELLER);   // 销售/供应商
+        boolean isFinance = appRoleConfig.match(AppRoleType.FINANCE); // 财务
+        boolean isPurchase = appRoleConfig.match(AppRoleType.PURCHASE); // 采购/买家
+
+        // 4.1 销售/供应商角色权限
+        // 展示:订单统计、销售统计
+        if (isSeller) {
+            log.info("用户角色匹配[SELLER],开启订单统计与销售统计模块");
+            loginRes.setShowOrderStatisticsModule(Boolean.TRUE);
+            loginRes.setShowSalesStatisticsModule(Boolean.TRUE);
+            return;
+        }
+
+        // 4.2 财务角色权限
+        // 展示:订单统计、钱包模块
+        // 钱包子项:待履约保证金(PENDING_PERFORMANCE_BALANCE)、待付运费(PENDING_FREIGHT)
+        if (isFinance) {
+            log.info("用户角色匹配[FINANCE],开启订单统计与钱包模块(待履约保证金、待付运费)");
+            loginRes.setShowOrderStatisticsModule(Boolean.TRUE);
+            loginRes.setShowWalletModule(Boolean.TRUE);
+            loginRes.setWalletModuleItems(Arrays.asList("PENDING_PERFORMANCE_BALANCE", "PENDING_FREIGHT"));
+            return;
+        }
+
+        // 4.3 采购/买家角色权限
+        // 展示:订单统计、钱包模块、地址管理模块
+        // 钱包子项:预付余额(PREPAY_BALANCE)、待付运费(PENDING_FREIGHT)
+        if (isPurchase) {
+            log.info("用户角色匹配[PURCHASE],开启订单统计、钱包模块(预付余额、待付运费)及地址管理模块");
+            loginRes.setShowOrderStatisticsModule(Boolean.TRUE);
+            loginRes.setShowWalletModule(Boolean.TRUE);
+            loginRes.setShowAddressModule(Boolean.TRUE);
+            loginRes.setWalletModuleItems(Arrays.asList("PREPAY_BALANCE", "PENDING_FREIGHT"));
+        } else {
+            // 未匹配到上述特定角色,保持默认隐藏状态
+            log.debug("用户角色未匹配SELLER/FINANCE/PURCHASE,保持模块默认隐藏状态");
+        }
+    }
+
+
+    /**
+     * 根据角色ID字典配置构建APP底部导航。
+     *
+     * @param loginRes     登录返回信息,用于设置角色名称等上下文信息
+     * @param loginBase    登录请求信息,用于判断客户端类型
+     * @param roleName     当前角色名称,仅作为未配置字典时的兼容兜底(本方法主要依赖 roleId)
+     * @param flag         预留参数,兼容原方法签名,暂未使用
+     * @param entTypeNames 企业类型标识(1:供应商, 2:采购商, 3:4PL物流),用于区分特定管理员角色
+     * @return APP底部导航配置列表,若非APP端登录或无匹配角色则返回空列表
+     */
+    private List<LoginResVo1.TabBarItem> buildAppTabBarByConfig(LoginResVo1 loginRes, LoginBase loginBase,
+                                                                String roleName, int flag, String entTypeNames) {
+        // 优先使用登录结果中的角色名称,若为空则使用传入的参数
+        roleName = org.apache.commons.lang3.StringUtils.defaultIfBlank(loginRes.getRoleName(), roleName);
+        
+        // 记录关键上下文信息,便于排查角色匹配问题
+        log.info("构建APP TabBar - 角色ID:{},角色名称:{},企业类型:{}", loginRes.getRoleId(), roleName, entTypeNames);
+
+        // 非APP端登录(如PC、H5等)不展示底部导航
+        if (!isAppLogin(loginBase)) {
+            return List.of();
+        }
+
+        // 加载基于字典配置的角色权限映射
+        AppRoleConfig appRoleConfig = loadAppRoleConfig(loginRes.getRoleId());
+
+        // 1. 采购员角色:直接返回买家导航
+        if (appRoleConfig.match(AppRoleType.PURCHASE)) {
+            return buildBuyerTabBar();
+        }
+
+        // 2. 解析具体角色标识
+        boolean isDoorKeeper = appRoleConfig.match(AppRoleType.DOOR_KEEPER);      // 门卫
+        boolean isForkliftDriver = appRoleConfig.match(AppRoleType.FORKLIFT_DRIVER); // 叉车司机
+        boolean isBuyer = appRoleConfig.match(AppRoleType.BUYER);                 // 买家
+        boolean isSeller = appRoleConfig.match(AppRoleType.SELLER);               // 销售/供应商
+        boolean isDriver = appRoleConfig.match(AppRoleType.DRIVER);               // 司机
+        boolean isLogistics = appRoleConfig.match(AppRoleType.LOGISTICS);         // 物流人员
+        boolean isFinance = appRoleConfig.match(AppRoleType.FINANCE);             // 财务
+
+        // 3. 解析特定企业管理员角色(需同时满足角色标识和企业类型)
+        // 供应商管理员:角色匹配且企业类型为"1"
+        boolean isSupplierAdmin = appRoleConfig.match(AppRoleType.SUPPLIER_ADMIN)
+                && org.apache.commons.lang3.StringUtils.equals(entTypeNames, "1");
+        // 物流商管理员:角色匹配且企业类型为"3"
+        boolean isLogisticsAdmin = appRoleConfig.match(AppRoleType.LOGISTICS_ADMIN)
+                && org.apache.commons.lang3.StringUtils.equals(entTypeNames, "3");
+        // 采购商管理员:角色匹配且企业类型为"2"
+        boolean isPurchaseAdmin = appRoleConfig.match(AppRoleType.PURCHASE_ADMIN)
+                && org.apache.commons.lang3.StringUtils.equals(entTypeNames, "2");
+
+        // 4. 按优先级匹配并返回对应的TabBar配置
+        if (isDoorKeeper) {
+            return buildDoorKeeperTabBar();
+        }
+        if (isForkliftDriver) {
+            return buildForkliftDriverTabBar();
+        }
+        if (isBuyer) {
+            return buildBuyerTabBar();
+        }
+        // 销售或财务共用卖家导航
+        if (isSeller || isFinance) {
+            return buildSellerTabBar();
+        }
+        if (isDriver) {
+            return buildDefaultDriverTabBar();
+        }
+        if (isLogistics) {
+            return buildLogisticsTabBar();
+        }
+        
+        // 5. 处理特定管理员角色,并修正显示的角色名称
+        if (isSupplierAdmin) {
+            loginRes.setRoleName("供应商管理员");
+            return buildSupplierAdminTabBar();
+        }
+        if (isLogisticsAdmin) {
+            loginRes.setRoleName("物流商管理员");
+            return buildLogisticsAdminTabBar();
+        }
+        if (isPurchaseAdmin) {
+            log.info("用户角色配置为采购管理员且企业属性为采购商,返回采购管理员TabBar");
+            loginRes.setRoleName("采购商管理员");
+            return buildPurchaseAdminTabBar();
+        }
+
+        // 未匹配到任何已知角色,返回空列表
+        return List.of();
+    }
+
+
+    /**
+     * 加载APP角色权限配置。
+     * <p>
+     * 该方法从系统字典服务中查询类型为 {@link DictTypeEnum#APP_ROLE_PERMISSION} 的字典项,
+     * 并将其转换为 {@link AppRoleConfig} 对象,用于后续判断当前用户角色对应的APP端权限(如TabBar显示、模块可见性等)。
+     * </p>
+     *
+     * @param roleId 当前登录用户的角色ID
+     * @return {@link AppRoleConfig} 包含该角色匹配的APP角色类型集合;若roleId为空或查询异常,则返回空配置
+     */
+    private AppRoleConfig loadAppRoleConfig(Long roleId) {
+        // 1. 参数校验:若角色ID为空,直接返回空配置,避免无效查询
+        if (Objects.isNull(roleId)) {
+            log.debug("角色ID为空,返回空APP角色配置");
+            return AppRoleConfig.empty();
+        }
+
+        try {
+            // 2. 记录开始加载日志,便于追踪性能和问题
+            log.debug("开始加载APP角色权限配置,roleId: {}", roleId);
+
+            // 3. 远程调用系统服务,获取APP角色权限相关的字典数据
+            // 字典类型由 DictTypeEnum.APP_ROLE_PERMISSION 定义,通常包含 value(角色类型枚举名) 和 label(关联的角色ID列表)
+            List<SysDictResDto> dictList = systemService.queryDictDbByType(DictTypeEnum.APP_ROLE_PERMISSION.getType());
+
+            // 4. 数据预处理与转换
+            // 过滤掉 null 对象及 value 为空的无效字典项
+            // 将 List 转换为 Map,Key 为字典项的 value (即 AppRoleType 枚举名),Value 为字典对象本身
+            // 注意:若存在重复 Key,保留第一个出现的值 (first, second) -> first
+            Map<String, SysDictResDto> dictMap = dictList.stream()
+                    .filter(Objects::nonNull)
+                    .filter(item -> org.apache.commons.lang3.StringUtils.isNotBlank(item.getValue()))
+                    .collect(Collectors.toMap(SysDictResDto::getValue, item -> item, (first, second) -> first));
+
+            // 5. 构建并返回角色配置对象
+            AppRoleConfig config = AppRoleConfig.fromDict(roleId, dictMap);
+            log.debug("APP角色权限配置加载完成,roleId: {}, 匹配到的角色类型: {}", roleId, config.roleTypes);
+            return config;
+
+        } catch (Exception e) {
+            // 6. 异常处理:记录警告日志并返回空配置,确保登录流程不因配置加载失败而中断
+            // 使用 warn 级别是因为这属于非核心业务异常,但需要运维关注
+            log.warn("读取APP角色权限字典配置失败,roleId: {},错误信息: {}", roleId, e.getMessage(), e);
+            return AppRoleConfig.empty();
+        }
+    }
+
+    /**
+     * APP端角色类型枚举。
+     * 用于定义APP底部导航栏(TabBar)及模块权限控制的角色标识,
+     * 对应字典表 app_role_permission 中的 value 值。
+     */
+    enum AppRoleType {
+        /** 销售/供应商 */
+        SELLER,
+        /** 财务 */
+        FINANCE,
+        /** 采购 */
+        PURCHASE,
+        /** 门卫 */
+        DOOR_KEEPER,
+        /** 叉车司机 */
+        FORKLIFT_DRIVER,
+        /** 买家 */
+        BUYER,
+        /** 司机 */
+        DRIVER,
+        /** 物流人员 */
+        LOGISTICS,
+        /** 供应商管理员 */
+        SUPPLIER_ADMIN,
+        /** 物流商管理员 */
+        LOGISTICS_ADMIN,
+        /** 采购商管理员 */
+        PURCHASE_ADMIN
+    }
+
+    static class AppRoleConfig {
+        private final Set<AppRoleType> roleTypes;
+
+        private AppRoleConfig(Set<AppRoleType> roleTypes) {
+            this.roleTypes = roleTypes;
+        }
+
+        static AppRoleConfig empty() {
+            return new AppRoleConfig(Collections.emptySet());
+        }
+
+        static AppRoleConfig fromDict(Long roleId, Map<String, SysDictResDto> dictMap) {
+            if (Objects.isNull(roleId) || dictMap == null || dictMap.isEmpty()) {
+                return empty();
+            }
+            String roleIdValue = String.valueOf(roleId);
+            Set<AppRoleType> roleTypes = dictMap.values().stream()
+                    .filter(Objects::nonNull)
+                    .filter(item -> matchRoleId(roleIdValue, item.getLabel()))
+                    .map(SysDictResDto::getValue)
+                    .map(AppRoleConfig::parseRoleType)
+                    .filter(Optional::isPresent)
+                    .map(Optional::get)
+                    .collect(Collectors.toSet());
+            return new AppRoleConfig(roleTypes);
+        }
+
+        boolean match(AppRoleType roleType) {
+            return roleTypes.contains(roleType);
+        }
+
+        private static boolean matchRoleId(String roleId, String roleIds) {
+            if (org.apache.commons.lang3.StringUtils.isBlank(roleId) || org.apache.commons.lang3.StringUtils.isBlank(roleIds)) {
+                return false;
+            }
+            return Arrays.stream(roleIds.split(","))
+                    .map(String::trim)
+                    .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
+                    .anyMatch(roleId::equals);
+        }
+
+        private static Optional<AppRoleType> parseRoleType(String roleType) {
+            if (org.apache.commons.lang3.StringUtils.isBlank(roleType)) {
+                return Optional.empty();
+            }
+            try {
+                return Optional.of(AppRoleType.valueOf(roleType.trim().toUpperCase(Locale.ROOT)));
+            } catch (IllegalArgumentException e) {
+                log.warn("APP角色权限字典value配置错误,value:{}", roleType);
+                return Optional.empty();
+            }
+        }
+    }
 
     private void applyAppModulePermissions(LoginResVo1 loginRes, LoginBase loginBase, String roleName) {
         roleName = loginRes.getRoleName();

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

@@ -0,0 +1,49 @@
+package com.sckw.auth.service.impl;
+
+import com.sckw.system.api.model.dto.res.SysDictResDto;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AuthServiceImpl单元测试。
+ */
+public class AuthServiceImplTest {
+
+    /**
+     * 验证APP角色权限可通过字典中的角色ID匹配,不依赖角色名称。
+     */
+    @Test
+    public void appRoleConfigShouldMatchRoleIdFromDict() {
+        Map<String, SysDictResDto> dictMap = new HashMap<>();
+        dictMap.put("SELLER", buildDict("SELLER", "1001,1002"));
+        dictMap.put("FINANCE", buildDict("FINANCE", "2001"));
+
+        AuthServiceImpl.AppRoleConfig appRoleConfig = AuthServiceImpl.AppRoleConfig.fromDict(1002L, dictMap);
+
+        Assert.assertTrue(appRoleConfig.match(AuthServiceImpl.AppRoleType.SELLER));
+        Assert.assertFalse(appRoleConfig.match(AuthServiceImpl.AppRoleType.FINANCE));
+    }
+
+    /**
+     * 验证未配置当前角色ID时不命中任何APP角色类型。
+     */
+    @Test
+    public void appRoleConfigShouldNotMatchWhenRoleIdMissing() {
+        Map<String, SysDictResDto> dictMap = new HashMap<>();
+        dictMap.put("PURCHASE", buildDict("PURCHASE", "3001"));
+
+        AuthServiceImpl.AppRoleConfig appRoleConfig = AuthServiceImpl.AppRoleConfig.fromDict(9999L, dictMap);
+
+        Assert.assertFalse(appRoleConfig.match(AuthServiceImpl.AppRoleType.PURCHASE));
+    }
+
+    private SysDictResDto buildDict(String value, String label) {
+        SysDictResDto dict = new SysDictResDto();
+        dict.setValue(value);
+        dict.setLabel(label);
+        return dict;
+    }
+}

+ 1 - 0
sckw-common/sckw-common-core/src/main/java/com/sckw/core/common/enums/enums/DictTypeEnum.java

@@ -11,6 +11,7 @@ import lombok.Getter;
 @Getter
 @AllArgsConstructor
 public enum DictTypeEnum {
+    APP_ROLE_PERMISSION("app_role_permission", "APP角色权限配置"),
     MSG_CATEGORY("msg_category", "消息分类"),
     MSG_STATUS("msg_status", "消息状态"),
     SEND_SMS_TYPE("send_sms_type", "发送短信类型"),

+ 7 - 0
sckw-modules-api/sckw-system-api/src/main/java/com/sckw/system/api/RemoteSystemService.java

@@ -43,6 +43,13 @@ public interface RemoteSystemService {
      */
     List<SysDictResDto> queryDictByType(String type);
 
+    /**
+     * @param type 字典类型
+     * @return SysDictResDto
+     * @desc 根据字典类型直接查询数据库,不读取Redis缓存,适用于运行期频繁调整的配置
+     */
+    List<SysDictResDto> queryDictDbByType(String type);
+
     /**
      * @param type 类型
      * @return SysDictResDto

+ 18 - 0
sckw-modules/sckw-system/src/main/java/com/sckw/system/dubbo/RemoteSystemServiceImpl.java

@@ -138,6 +138,24 @@ public class RemoteSystemServiceImpl implements RemoteSystemService {
         return JSONObject.parseArray(dictCache, SysDictResDto.class);
     }
 
+    /**
+     * 根据字典类型直接查询数据库,不使用Redis缓存。
+     *
+     * @param type 字典类型
+     * @return 字典列表
+     */
+    @Override
+    public List<SysDictResDto> queryDictDbByType(String type) {
+        if (StringUtils.isBlank(type)) {
+            return Collections.emptyList();
+        }
+        List<SysDict> sysDictList = sysDictDao.queryByType(type);
+        if (CollectionUtils.isEmpty(sysDictList)) {
+            return Collections.emptyList();
+        }
+        return BeanUtils.copyToList(sysDictList, SysDictResDto.class);
+    }
+
     @Override
     public Map<String, Map<String, String>> queryDictByType(List<String> list) {
         if (CollectionUtils.isEmpty(list)) {

+ 110 - 0
sql/2026/06/2026_06_07_fleet_truck_max_transport_distance.sql

@@ -1,2 +1,112 @@
 ALTER TABLE `kwf_truck`
     ADD COLUMN `max_transport_distance` decimal(12, 2) NULL COMMENT '最大运输距离,单位:公里' AFTER `position_device`;
+
+
+
+
+-- 1. 插入数据(手动生成19位随机数字ID,和你现有ID格式完全一致)
+INSERT INTO sys_dict_type (
+    id, name, type, remark, status, create_by, create_time, update_by, update_time, del_flag
+)
+SELECT
+    LPAD(FLOOR(RAND() * 10000000000000000000), 19, '0'), -- 生成19位数字字符串ID
+    'APP角色权限配置',
+    'app_role_permission',
+    'APP登录按角色ID控制底部导航和模块权限',
+    0,
+    0,
+    NOW(),
+    0,
+    NOW(),
+    0
+WHERE NOT EXISTS (
+    SELECT 1 FROM sys_dict_type
+    WHERE type = 'app_role_permission' AND del_flag = 0
+);
+
+-- 2. 获取刚插入数据的ID,赋值给变量
+SET @app_role_permission_dict_id = (
+    SELECT id FROM sys_dict_type
+    WHERE type = 'app_role_permission' AND del_flag = 0
+    LIMIT 1
+);
+
+-- label配置逗号分隔角色ID,例如 '1001,1002'
+
+-- ===================== sys_dict 插入(零冲突版)=====================
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'SELLER', '替换为销售角色ID,多个用逗号分隔', 'app_role_permission', '销售角色:显示订单统计、销售统计;底部导航走销售配置', 10, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'SELLER' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'FINANCE', '替换为财务角色ID', 'app_role_permission', '财务角色:显示订单统计、钱包模块;底部导航走销售配置', 20, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'FINANCE' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'PURCHASE', '替换为采购角色ID', 'app_role_permission', '采购角色:显示订单统计、钱包、地址;底部导航走采购配置', 30, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'PURCHASE' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'DOOR_KEEPER', '替换为门卫角色ID', 'app_role_permission', '门卫角色:底部导航走门卫配置', 40, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'DOOR_KEEPER' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'FORKLIFT_DRIVER', '替换为铲车司机角色ID', 'app_role_permission', '铲车司机角色:底部导航走铲车司机配置', 50, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'FORKLIFT_DRIVER' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'BUYER', '替换为买家角色ID', 'app_role_permission', '买家角色:底部导航走买家配置', 60, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'BUYER' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'DRIVER', '替换为司机角色ID', 'app_role_permission', '司机角色:底部导航走默认司机配置', 70, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'DRIVER' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'LOGISTICS', '替换为物流角色ID', 'app_role_permission', '物流角色:底部导航走物流配置', 80, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'LOGISTICS' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'SUPPLIER_ADMIN', '替换为供应商管理员角色ID', 'app_role_permission', '供应商管理员:企业类型为1时生效', 90, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'SUPPLIER_ADMIN' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'LOGISTICS_ADMIN', '替换为物流商管理员角色ID', 'app_role_permission', '物流商管理员:企业类型为3时生效', 100, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'LOGISTICS_ADMIN' AND del_flag = 0);
+
+INSERT INTO sys_dict (
+    id, dict_id, value, label, type, description, sort, parent_id, remark,
+    status, create_by, create_time, update_by, update_time, del_flag, url
+)
+SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM sys_dict), @app_role_permission_dict_id, 'PURCHASE_ADMIN', '替换为采购商管理员角色ID', 'app_role_permission', '采购商管理员:企业类型为2时生效', 110, '0', NULL, 0, 0, NOW(), 0, NOW(), 0, NULL
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict WHERE type = 'app_role_permission' AND value = 'PURCHASE_ADMIN' AND del_flag = 0);