Sfoglia il codice sorgente

提交登录接口开发接口

chenxiaofei 1 giorno fa
parent
commit
658f9c19fb

+ 28 - 0
sckw-auth/src/main/java/com/sckw/auth/controller/AuthController.java

@@ -44,9 +44,37 @@ public class AuthController {
         loginBase.setSystemType(systemType);
         loginBase.setClientType(clientType);
         loginBase.setLoginMethod(LoginMethodEnum.ORDINARY.getValue());
+        loginBase.setRememberMe(reqVo.getRememberMe());
+        loginBase.setDeviceId(reqVo.getDeviceId());
         return authService.login(loginBase);
     }
 
+    /**
+     * @param reqVo 刷新Token入参
+     * @return HttpResult
+     * @desc: 刷新Token(记住密码时使用,支持多设备)
+     * @date: 2026/1/14
+     */
+    @PostMapping("/refresh")
+    public HttpResult refreshToken(@Valid @RequestBody RefreshTokenReqVo reqVo, HttpServletRequest request) {
+        int systemType = request.getIntHeader("System-Type");
+        String clientType = request.getHeader("Client-Type");
+        return authService.refreshToken(reqVo.getRefreshToken(), clientType, systemType, reqVo.getDeviceId());
+    }
+
+    /**
+     * @param reqVo 切换账号入参
+     * @return HttpResult
+     * @desc: 切换账号(使用目标账号的refreshToken快速登录,无需重新输入密码)
+     * @date: 2026/1/14
+     */
+    @PostMapping("/switchAccount")
+    public HttpResult switchAccount(@Valid @RequestBody SwitchAccountReqVo reqVo, HttpServletRequest request) {
+        int systemType = request.getIntHeader("System-Type");
+        String clientType = request.getHeader("Client-Type");
+        return authService.switchAccount(reqVo.getTargetRefreshToken(), clientType, systemType, reqVo.getDeviceId());
+    }
+
     /**
      * 切换角色
      *

+ 10 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/LoginBase.java

@@ -44,4 +44,14 @@ public class LoginBase {
      * 登录方式1 账号密码登录/2账号短信登录/3单账号登录
      */
     private int loginMethod;
+
+    /**
+     * 是否记住密码(用于生成refreshToken)
+     */
+    private Boolean rememberMe = false;
+
+    /**
+     * 设备唯一标识(用于多设备记住密码)
+     */
+    private String deviceId;
 }

+ 10 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/LoginReqVo.java

@@ -47,4 +47,14 @@ public class LoginReqVo implements Serializable {
      */
     private String captcha;
 
+    /**
+     * 是否记住密码
+     */
+    private Boolean rememberMe = false;
+
+    /**
+     * 设备唯一标识(用于多设备记住密码)
+     */
+    private String deviceId;
+
 }

+ 39 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/RefreshTokenReqVo.java

@@ -0,0 +1,39 @@
+package com.sckw.auth.model.vo.req;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @desc 刷新Token请求入参
+ * @date 2026/1/14
+ */
+@Data
+public class RefreshTokenReqVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 刷新令牌
+     */
+    @NotBlank(message = "刷新令牌不能为空")
+    private String refreshToken;
+
+    /**
+     * 设备类型
+     */
+    private String clientType;
+
+    /**
+     * 系统类型
+     */
+    private Integer systemType;
+
+    /**
+     * 设备唯一标识
+     */
+    private String deviceId;
+}

+ 39 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/SwitchAccountReqVo.java

@@ -0,0 +1,39 @@
+package com.sckw.auth.model.vo.req;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @desc 切换账号请求入参
+ * @date 2026/1/14
+ */
+@Data
+public class SwitchAccountReqVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 目标账号的刷新令牌
+     */
+    @NotBlank(message = "目标账号的刷新令牌不能为空")
+    private String targetRefreshToken;
+
+    /**
+     * 设备类型
+     */
+    private String clientType;
+
+    /**
+     * 系统类型
+     */
+    private Integer systemType;
+
+    /**
+     * 设备唯一标识
+     */
+    private String deviceId;
+}

+ 6 - 1
sckw-auth/src/main/java/com/sckw/auth/model/vo/res/LoginResVo1.java

@@ -114,10 +114,15 @@ public class LoginResVo1 implements Serializable {
     private Integer systemType;
 
     /**
-     * 设备类型
+     * 访问令牌
      */
     private String token;
 
+    /**
+     * 刷新令牌(记住密码时返回)
+     */
+    private String refreshToken;
+
     /**
      * 机构id
      */

+ 22 - 0
sckw-auth/src/main/java/com/sckw/auth/service/IAuthService.java

@@ -53,4 +53,26 @@ public interface IAuthService {
     LoginResVo1 getLoginResByToken(String clientType, String token);
 
     HttpResult changeRole(RoleChangeVo reqVo);
+
+    /**
+     * @param refreshToken 刷新令牌
+     * @param clientType   客户端类型
+     * @param systemType   系统类型
+     * @param deviceId     设备标识
+     * @return HttpResult
+     * @desc: 刷新Token(记住密码时使用,支持多设备)
+     * @date: 2026/1/14
+     */
+    HttpResult refreshToken(String refreshToken, String clientType, Integer systemType, String deviceId);
+
+    /**
+     * @param targetRefreshToken 目标账号的刷新令牌
+     * @param clientType         客户端类型
+     * @param systemType         系统类型
+     * @param deviceId           设备标识
+     * @return HttpResult
+     * @desc: 切换账号(使用目标账号的refreshToken快速登录)
+     * @date: 2026/1/14
+     */
+    HttpResult switchAccount(String targetRefreshToken, String clientType, Integer systemType, String deviceId);
 }

+ 332 - 1
sckw-auth/src/main/java/com/sckw/auth/service/impl/AuthServiceImpl.java

@@ -142,6 +142,12 @@ public class AuthServiceImpl implements IAuthService {
         /*缓存信息**/
         AsyncFactory.execute(new AsyncProcess(loginBase, null, driver, enterprise, remoteUserService));
 
+        /*生成refreshToken(记住密码时)**/
+        String refreshToken = null;
+        if (Boolean.TRUE.equals(loginBase.getRememberMe())) {
+            refreshToken = generateRefreshToken(loginBase, driver.getId());
+        }
+
         /*数据组装**/
         LoginResVo1 loginRes = new LoginResVo1();
         loginRes.setId(driver.getId());
@@ -156,6 +162,7 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setClientType(loginBase.getClientType());
         loginRes.setSystemType(loginBase.getSystemType());
         loginRes.setToken(token);
+        loginRes.setRefreshToken(refreshToken);
         return HttpResult.ok(loginRes);
     }
 
@@ -211,8 +218,14 @@ public class AuthServiceImpl implements IAuthService {
         /*缓存信息**/
 //        AsyncFactory.execute(new AsyncProcess(loginBase, user, null, enterprise, remoteUserService));
         new AsyncProcess(loginBase, user, null, enterprise, remoteUserService).run();
-        /*数据组装**/
 
+        /*生成refreshToken(记住密码时)**/
+        String refreshToken = null;
+        if (Boolean.TRUE.equals(loginBase.getRememberMe())) {
+            refreshToken = generateRefreshToken(loginBase, user.getId());
+        }
+
+        /*数据组装**/
         LoginResVo1 loginRes = new LoginResVo1();
         loginRes.setId(user.getId());
         loginRes.setName(user.getName());
@@ -231,6 +244,7 @@ public class AuthServiceImpl implements IAuthService {
         loginRes.setClientType(loginBase.getClientType());
         loginRes.setSystemType(user.getSystemType());
         loginRes.setToken(token);
+        loginRes.setRefreshToken(refreshToken);
         loginRes.setDeptId(user.getDeptId());
         loginRes.setRoleId(user.getRoleId());
         loginRes.setRoleName(user.getRoleName());
@@ -459,6 +473,323 @@ public class AuthServiceImpl implements IAuthService {
         return token;
     }
 
+    /**
+     * @param loginBase 登录参数
+     * @param userId    用户ID
+     * @desc 生成refreshToken(记住密码时使用,支持多设备)
+     * @author assistant
+     * @date 2026/1/14
+     **/
+    private String generateRefreshToken(LoginBase loginBase, Long userId) {
+        String account = loginBase.getAccount();
+        String clientType = loginBase.getClientType();
+        Integer systemType = loginBase.getSystemType();
+        String deviceId = loginBase.getDeviceId();
+        Long timestamp = System.currentTimeMillis();
+        Map<String, Object> info = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        info.put("userId", userId);
+        info.put("account", account);
+        info.put("clientType", clientType);
+        info.put("systemType", systemType);
+        info.put("deviceId", deviceId);
+        info.put("timestamp", timestamp);
+        info.put("type", "refresh");
+        String key = Global.getFullRefreshTokenKey(clientType, userId, deviceId);
+        String refreshToken = EncryUtil.encryV1(Global.PRI_KEY, JSON.toJSONString(info));
+        RedissonUtils.putString(key, refreshToken, Global.REFRESH_TOKEN_EXPIRE);
+        return refreshToken;
+    }
+
+    @Override
+    public HttpResult refreshToken(String refreshToken, String clientType, Integer systemType, String deviceId) {
+        log.info("刷新token,refreshToken:{},clientType:{},systemType:{},deviceId:{}", refreshToken, clientType, systemType, deviceId);
+        
+        // 验证refreshToken参数
+        HttpResult validateResult = validateRefreshTokenParam(refreshToken);
+        if (validateResult != null) {
+            return validateResult;
+        }
+        
+        // 解析refreshToken
+        Map<String, Object> tokenInfoMap = parseRefreshToken(refreshToken);
+        if (tokenInfoMap == null) {
+            return HttpResult.error(HttpStatus.TOKEN_INVALID_CODE, "刷新令牌无效!");
+        }
+        
+        // 验证refreshToken是否有效
+        HttpResult verifyResult = verifyRefreshToken(tokenInfoMap, refreshToken);
+        if (verifyResult != null) {
+            return verifyResult;
+        }
+        
+        // 构建LoginBase对象
+        LoginBase loginBase = buildLoginBaseFromTokenInfo(tokenInfoMap);
+        
+        // 根据系统类型处理刷新逻辑
+        Integer tokenSystemType = Integer.valueOf(tokenInfoMap.get("systemType").toString());
+        if (tokenSystemType == SystemTypeEnum.DRIVER.getCode()) {
+            return handleDriverRefresh(loginBase, tokenInfoMap);
+        } else {
+            return handleCommonRefresh(loginBase, tokenInfoMap);
+        }
+    }
+    
+    @Override
+    public HttpResult switchAccount(String targetRefreshToken, String clientType, Integer systemType, String deviceId) {
+        log.info("切换账号,targetRefreshToken:{},clientType:{},systemType:{},deviceId:{}", targetRefreshToken, clientType, systemType, deviceId);
+        
+        // 验证targetRefreshToken参数
+        HttpResult validateResult = validateRefreshTokenParam(targetRefreshToken);
+        if (validateResult != null) {
+            return validateResult;
+        }
+        
+        // 解析目标账号的refreshToken
+        Map<String, Object> targetTokenInfoMap = parseRefreshToken(targetRefreshToken);
+        if (targetTokenInfoMap == null) {
+            return HttpResult.error(HttpStatus.TOKEN_INVALID_CODE, "目标账号的刷新令牌无效!");
+        }
+        
+        // 验证目标账号的refreshToken是否有效
+        HttpResult verifyResult = verifyRefreshToken(targetTokenInfoMap, targetRefreshToken);
+        if (verifyResult != null) {
+            return HttpResult.error(HttpStatus.TOKEN_INVALID_CODE, "目标账号的刷新令牌已过期或无效!");
+        }
+        
+        // 清除当前登录用户的token缓存(如果存在)
+        clearCurrentUserToken(clientType, deviceId);
+        
+        // 构建LoginBase对象(使用新的deviceId)
+        LoginBase loginBase = buildLoginBaseFromTokenInfo(targetTokenInfoMap);
+        // 如果传入了新的deviceId,则使用新的deviceId
+        if (StringUtils.isNotBlank(deviceId)) {
+            loginBase.setDeviceId(deviceId);
+        }
+        
+        // 根据系统类型处理切换逻辑
+        Integer targetSystemType = Integer.valueOf(targetTokenInfoMap.get("systemType").toString());
+        if (Objects.equals(SystemTypeEnum.DRIVER.getCode(), targetSystemType)) {
+            return handleDriverRefresh(loginBase, targetTokenInfoMap);
+        } else {
+            return handleCommonRefresh(loginBase, targetTokenInfoMap);
+        }
+    }
+    
+    /**
+     * 清除当前登录用户的token缓存
+     */
+    private void clearCurrentUserToken(String clientType, String deviceId) {
+        try {
+            LoginUserInfo currentUser = LoginUserHolder.get();
+            if (currentUser != null && currentUser.getId() != null) {
+                // 清除当前用户的token
+                String tokenKey = Global.getFullUserTokenKey(clientType, currentUser.getId());
+                RedissonUtils.delete(tokenKey);
+                // 清除当前用户的refreshToken
+                String refreshTokenKey = Global.getFullRefreshTokenKey(clientType, currentUser.getId(), deviceId);
+                RedissonUtils.delete(refreshTokenKey);
+                
+                // 清除当前用户的登录信息缓存
+                String loginKey = Global.getFullUserLoginKey(currentUser.getSystemType(), currentUser.getId());
+                RedissonUtils.delete(loginKey);
+                
+                log.info("已清除当前用户token缓存,userId:{}", currentUser.getId());
+            }
+        } catch (Exception e) {
+            log.warn("清除当前用户token缓存失败", e);
+        }
+    }
+    
+    /**
+     * 验证refreshToken参数
+     */
+    private HttpResult validateRefreshTokenParam(String refreshToken) {
+        if (StringUtils.isBlank(refreshToken)) {
+            return HttpResult.error(HttpStatus.PARAMETERS_MISSING_CODE, "刷新令牌不能为空!");
+        }
+        return null;
+    }
+    
+    /**
+     * 解析refreshToken
+     */
+    private Map<String, Object> parseRefreshToken(String refreshToken) {
+        String tokenInfo;
+        try {
+            tokenInfo = EncryUtil.descry(Global.PRI_KEY, refreshToken);
+            return JSON.parseObject(tokenInfo, Map.class);
+        } catch (Exception e) {
+            log.error("解析refreshToken失败", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 验证refreshToken是否有效
+     */
+    private HttpResult verifyRefreshToken(Map<String, Object> tokenInfoMap, String refreshToken) {
+        Long userId = Long.valueOf(tokenInfoMap.get("userId").toString());
+        String tokenClientType = tokenInfoMap.get("clientType").toString();
+        String tokenDeviceId = tokenInfoMap.get("deviceId") != null ? tokenInfoMap.get("deviceId").toString() : null;
+        
+        String key = Global.getFullRefreshTokenKey(tokenClientType, userId, tokenDeviceId);
+        String storedRefreshToken = RedissonUtils.getString(key);
+        if (StringUtils.isBlank(storedRefreshToken) || !storedRefreshToken.equals(refreshToken)) {
+            return HttpResult.error(HttpStatus.TOKEN_INVALID_CODE, "刷新令牌已过期或无效,请重新登录!");
+        }
+        return null;
+    }
+    
+    /**
+     * 从token信息构建LoginBase对象
+     */
+    private LoginBase buildLoginBaseFromTokenInfo(Map<String, Object> tokenInfoMap) {
+        String account = tokenInfoMap.get("account").toString();
+        String tokenClientType = tokenInfoMap.get("clientType").toString();
+        Integer tokenSystemType = Integer.valueOf(tokenInfoMap.get("systemType").toString());
+        String tokenDeviceId = tokenInfoMap.get("deviceId") != null ? tokenInfoMap.get("deviceId").toString() : null;
+        
+        LoginBase loginBase = new LoginBase();
+        loginBase.setAccount(account);
+        loginBase.setClientType(tokenClientType);
+        loginBase.setSystemType(tokenSystemType);
+        loginBase.setDeviceId(tokenDeviceId);
+        loginBase.setRememberMe(true);
+        
+        return loginBase;
+    }
+    
+    /**
+     * 处理司机端token刷新
+     */
+    private HttpResult handleDriverRefresh(LoginBase loginBase, Map<String, Object> tokenInfoMap) {
+        String account = tokenInfoMap.get("account").toString();
+        String tokenClientType = tokenInfoMap.get("clientType").toString();
+        Integer tokenSystemType = Integer.valueOf(tokenInfoMap.get("systemType").toString());
+        
+        // 查询司机信息
+        RDriverDetailVo driver = fleetService.findDriverDetai(account);
+        if (driver == null) {
+            return HttpResult.error(HttpStatus.QUERY_FAIL_CODE, "账号不存在!");
+        }
+        if (driver.getStatus() == Global.YES) {
+            return HttpResult.error(HttpStatus.CODE_10301, "您的账号已冻结!");
+        }
+
+        EntCacheResDto enterprise = systemService.queryEntDetails(driver.getEntId());
+
+        // 生成新token
+        String newToken = generateToken(loginBase, driver.getId());
+        // 生成新的refreshToken
+        String newRefreshToken = generateRefreshToken(loginBase, driver.getId());
+
+        // 更新缓存
+        new AsyncProcess(loginBase, null, driver, enterprise, remoteUserService).run();
+
+        // 构建返回结果
+        LoginResVo1 loginRes = buildDriverLoginRes(driver, enterprise, tokenClientType, tokenSystemType, newToken, newRefreshToken);
+        return HttpResult.ok(loginRes);
+    }
+    
+    /**
+     * 处理运营端/企业端token刷新
+     */
+    private HttpResult handleCommonRefresh(LoginBase loginBase, Map<String, Object> tokenInfoMap) {
+        String account = tokenInfoMap.get("account").toString();
+        String tokenClientType = tokenInfoMap.get("clientType").toString();
+        Integer tokenSystemType = Integer.valueOf(tokenInfoMap.get("systemType").toString());
+        
+        // 查询用户信息
+        KwsUserResDto user = systemService.queryUserDetails(account, tokenSystemType);
+        if (user == null) {
+            return HttpResult.error(HttpStatus.QUERY_FAIL_CODE, "账号不存在!");
+        }
+        if (user.getStatus() == Global.YES) {
+            return HttpResult.error(HttpStatus.CODE_10301, "您的账号已冻结!");
+        }
+
+        EntCacheResDto enterprise = systemService.queryEntDetails(user.getEntId());
+        if (tokenSystemType == SystemTypeEnum.COMPANY.getCode()) {
+            if (enterprise == null) {
+                return HttpResult.error(HttpStatus.QUERY_FAIL_CODE, "账号没有归属企业!");
+            }
+        }
+        if (enterprise != null && enterprise.getStatus() == Global.YES) {
+            return HttpResult.error(HttpStatus.QUERY_FAIL_CODE, "企业已冻结!");
+        }
+
+        // 生成新token
+        String newToken = generateToken(loginBase, user.getId());
+        String newRefreshToken = generateRefreshToken(loginBase, user.getId());
+
+        // 更新缓存
+        new AsyncProcess(loginBase, user, null, enterprise, remoteUserService).run();
+
+        // 构建返回结果
+        LoginResVo1 loginRes = buildCommonLoginRes(user, enterprise, tokenClientType, tokenSystemType, newToken, newRefreshToken);
+        return HttpResult.ok(loginRes);
+    }
+    
+    /**
+     * 构建司机端登录返回结果
+     */
+    private LoginResVo1 buildDriverLoginRes(RDriverDetailVo driver, EntCacheResDto enterprise, 
+                                            String tokenClientType, Integer tokenSystemType, 
+                                            String newToken, String newRefreshToken) {
+        LoginResVo1 loginRes = new LoginResVo1();
+        loginRes.setId(driver.getId());
+        loginRes.setName(driver.getName());
+        loginRes.setAccount(driver.getPhone());
+        loginRes.setPhone(driver.getPhone());
+        loginRes.setStatus(driver.getStatus());
+        loginRes.setEntId(driver.getEntId());
+        loginRes.setFirmName(enterprise != null ? enterprise.getFirmName() : null);
+        loginRes.setApproval(enterprise != null ? enterprise.getApproval() : null);
+        loginRes.setEntTypeNames(enterprise != null ? enterprise.getEntTypeNames() : null);
+        loginRes.setClientType(tokenClientType);
+        loginRes.setSystemType(tokenSystemType);
+        loginRes.setToken(newToken);
+        loginRes.setRefreshToken(newRefreshToken);
+        return loginRes;
+    }
+    
+    /**
+     * 构建运营端/企业端登录返回结果
+     */
+    private LoginResVo1 buildCommonLoginRes(KwsUserResDto user, EntCacheResDto enterprise, 
+                                           String tokenClientType, Integer tokenSystemType, 
+                                           String newToken, String newRefreshToken) {
+        LoginResVo1 loginRes = new LoginResVo1();
+        loginRes.setId(user.getId());
+        loginRes.setName(user.getName());
+        loginRes.setAccount(user.getAccount());
+        loginRes.setPhone(user.getPhone());
+        loginRes.setPhoto(user.getPhoto());
+        loginRes.setIsMain(user.getIsMain());
+        loginRes.setStatus(user.getStatus());
+        loginRes.setDeptName(user.getDeptName());
+        loginRes.setClientId(user.getClientId());
+        loginRes.setEntId(user.getEntId());
+        loginRes.setFirmName(enterprise != null ? enterprise.getFirmName() : null);
+        loginRes.setApproval(enterprise != null ? enterprise.getApproval() : null);
+        loginRes.setEntTypes(enterprise != null ? enterprise.getEntTypes() : null);
+        loginRes.setEntTypeNames(enterprise != null ? enterprise.getEntTypeNames() : null);
+        loginRes.setClientType(tokenClientType);
+        loginRes.setSystemType(user.getSystemType());
+        loginRes.setToken(newToken);
+        loginRes.setRefreshToken(newRefreshToken);
+        loginRes.setDeptId(user.getDeptId());
+        loginRes.setRoleId(user.getRoleId());
+        loginRes.setRoleName(user.getRoleName());
+        loginRes.setRoleList(user.getRoleInfoDto());
+        if (user.getSystemType().equals(SystemTypeEnum.MANAGE.getCode())) {
+            loginRes.setValid(true);
+        } else {
+            loginRes.setValid(!Objects.isNull(enterprise) && enterprise.getValid());
+        }
+        return loginRes;
+    }
+
     static class AsyncProcess implements Runnable {
         private final LoginBase loginBase;
 

+ 23 - 0
sckw-common/sckw-common-core/src/main/java/com/sckw/core/model/constant/Global.java

@@ -147,9 +147,15 @@ public class Global {
     public static final String REDIS_USER_LOGIN_PREFIX = "userLoginInfo:";
     public static final String REDIS_USER_PREFIX = "userInfo:";
     public static final String REDIS_USER_TOKEN_PREFIX = "userToken:";
+    public static final String REDIS_USER_REFRESH_TOKEN_PREFIX = "userRefreshToken:";
     //客户经理redisKey
     public static final String REDIS_CUSTOMER_MANAGER_USER_LOGIN_PREFIX = "customerManagerUserLoginInfo:";
 
+    /**
+     * refresh token有效期为30天
+     */
+    public static final int REFRESH_TOKEN_EXPIRE = 30 * 24 * 60 * 60;
+
     /**
      * redis企业信息前缀
      */
@@ -452,6 +458,23 @@ public class Global {
         return builder;
     }
 
+    /**
+     * 完整的用户刷新tokenkey(支持多设备)
+     */
+    public static String getFullRefreshTokenKey(String clientType, Long userId, String deviceId) {
+        StringBuilder builder = new StringBuilder(REDIS_USER_REFRESH_TOKEN_PREFIX);
+        if (StringUtils.isNotNull(clientType)) {
+            builder.append(clientType).append(COLON);
+        }
+        if (Objects.nonNull(userId)) {
+            builder.append(userId);
+        }
+        if (StringUtils.isNotNull(deviceId)) {
+            builder.append(COLON).append(deviceId);
+        }
+        return builder.toString();
+    }
+
     /**
      * 完整的用户企业信息key
      */