mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 00:57:13 +08:00 
			
		
		
		
	refactor: 重构获取登录用户信息方式(线程级存储)
This commit is contained in:
		| @@ -17,8 +17,7 @@ | ||||
| package top.continew.admin.common.config.mybatis; | ||||
|  | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.starter.extension.datapermission.enums.DataScope; | ||||
| import top.continew.starter.extension.datapermission.filter.DataPermissionUserContextProvider; | ||||
| import top.continew.starter.extension.datapermission.model.RoleContext; | ||||
| @@ -36,17 +35,16 @@ public class DefaultDataPermissionUserContextProvider implements DataPermissionU | ||||
|  | ||||
|     @Override | ||||
|     public boolean isFilter() { | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|         return !loginUser.isAdmin(); | ||||
|         return !UserContextHolder.isAdmin(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public UserContext getUserContext() { | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|         top.continew.admin.common.context.UserContext context = UserContextHolder.getContext(); | ||||
|         UserContext userContext = new UserContext(); | ||||
|         userContext.setUserId(Convert.toStr(loginUser.getId())); | ||||
|         userContext.setDeptId(Convert.toStr(loginUser.getDeptId())); | ||||
|         userContext.setRoles(loginUser.getRoles() | ||||
|         userContext.setUserId(Convert.toStr(context.getId())); | ||||
|         userContext.setDeptId(Convert.toStr(context.getDeptId())); | ||||
|         userContext.setRoles(context.getRoles() | ||||
|             .stream() | ||||
|             .map(r -> new RoleContext(Convert.toStr(r.getId()), DataScope.valueOf(r.getDataScope().name()))) | ||||
|             .collect(Collectors.toSet())); | ||||
|   | ||||
| @@ -19,9 +19,8 @@ package top.continew.admin.common.config.mybatis; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; | ||||
| import org.apache.ibatis.reflection.MetaObject; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.starter.core.exception.BusinessException; | ||||
| import top.continew.starter.core.util.ExceptionUtils; | ||||
| import top.continew.starter.extension.crud.model.entity.BaseDO; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| @@ -62,7 +61,7 @@ public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { | ||||
|             if (null == metaObject) { | ||||
|                 return; | ||||
|             } | ||||
|             Long createUser = ExceptionUtils.exToNull(LoginHelper::getUserId); | ||||
|             Long createUser = UserContextHolder.getUserId(); | ||||
|             LocalDateTime createTime = LocalDateTime.now(); | ||||
|             if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { | ||||
|                 // 继承了 BaseDO 的类,填充创建信息字段 | ||||
| @@ -89,8 +88,7 @@ public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { | ||||
|             if (null == metaObject) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             Long updateUser = LoginHelper.getUserId(); | ||||
|             Long updateUser = UserContextHolder.getUserId(); | ||||
|             LocalDateTime updateTime = LocalDateTime.now(); | ||||
|             if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { | ||||
|                 // 继承了 BaseDO 的类,填充修改信息 | ||||
|   | ||||
| @@ -16,11 +16,11 @@ | ||||
|  | ||||
| package top.continew.admin.common.config.websocket; | ||||
|  | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import org.springframework.http.server.ServletServerHttpRequest; | ||||
| import org.springframework.stereotype.Component; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.starter.core.exception.BusinessException; | ||||
| import top.continew.starter.messaging.websocket.core.WebSocketClientService; | ||||
|  | ||||
| /** | ||||
| @@ -36,7 +36,9 @@ public class WebSocketClientServiceImpl implements WebSocketClientService { | ||||
|     public String getClientId(ServletServerHttpRequest request) { | ||||
|         HttpServletRequest servletRequest = request.getServletRequest(); | ||||
|         String token = servletRequest.getParameter("token"); | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(token); | ||||
|         return loginUser.getToken(); | ||||
|         if (null == StpUtil.getLoginIdByToken(token)) { | ||||
|             throw new BusinessException("登录已过期,请重新登录"); | ||||
|         } | ||||
|         return token; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,11 +31,6 @@ public class CacheConstants { | ||||
|      */ | ||||
|     public static final String DELIMITER = StringConstants.COLON; | ||||
|  | ||||
|     /** | ||||
|      * 登录用户键 | ||||
|      */ | ||||
|     public static final String LOGIN_USER_KEY = "LOGIN_USER"; | ||||
|  | ||||
|     /** | ||||
|      * 验证码键前缀 | ||||
|      */ | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package top.continew.admin.common.model.dto; | ||||
| package top.continew.admin.common.context; | ||||
| 
 | ||||
| import lombok.Data; | ||||
| import top.continew.admin.common.enums.DataScopeEnum; | ||||
| @@ -23,13 +23,13 @@ import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| 
 | ||||
| /** | ||||
|  * 角色信息 | ||||
|  * 角色上下文 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/3/7 22:08 | ||||
|  */ | ||||
| @Data | ||||
| public class RoleDTO implements Serializable { | ||||
| public class RoleContext implements Serializable { | ||||
| 
 | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
| @@ -14,7 +14,7 @@ | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package top.continew.admin.common.model.dto; | ||||
| package top.continew.admin.common.context; | ||||
| 
 | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import lombok.Data; | ||||
| @@ -28,14 +28,14 @@ import java.util.Set; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| /** | ||||
|  * 登录用户信息 | ||||
|  * 用户上下文 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/24 13:01 | ||||
|  * @since 2024/10/9 20:29 | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class LoginUser implements Serializable { | ||||
| public class UserContext implements Serializable { | ||||
| 
 | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
| @@ -55,6 +55,16 @@ public class LoginUser implements Serializable { | ||||
|      */ | ||||
|     private Long deptId; | ||||
| 
 | ||||
|     /** | ||||
|      * 最后一次修改密码时间 | ||||
|      */ | ||||
|     private LocalDateTime pwdResetTime; | ||||
| 
 | ||||
|     /** | ||||
|      * 登录时系统设置的密码过期天数 | ||||
|      */ | ||||
|     private Integer passwordExpirationDays; | ||||
| 
 | ||||
|     /** | ||||
|      * 权限码集合 | ||||
|      */ | ||||
| @@ -68,57 +78,17 @@ public class LoginUser implements Serializable { | ||||
|     /** | ||||
|      * 角色集合 | ||||
|      */ | ||||
|     private Set<RoleDTO> roles; | ||||
|     private Set<RoleContext> roles; | ||||
| 
 | ||||
|     /** | ||||
|      * 令牌 | ||||
|      */ | ||||
|     private String token; | ||||
| 
 | ||||
|     /** | ||||
|      * IP | ||||
|      */ | ||||
|     private String ip; | ||||
| 
 | ||||
|     /** | ||||
|      * IP 归属地 | ||||
|      */ | ||||
|     private String address; | ||||
| 
 | ||||
|     /** | ||||
|      * 浏览器 | ||||
|      */ | ||||
|     private String browser; | ||||
| 
 | ||||
|     /** | ||||
|      * 操作系统 | ||||
|      */ | ||||
|     private String os; | ||||
| 
 | ||||
|     /** | ||||
|      * 登录时间 | ||||
|      */ | ||||
|     private LocalDateTime loginTime; | ||||
| 
 | ||||
|     /** | ||||
|      * 最后一次修改密码时间 | ||||
|      */ | ||||
|     private LocalDateTime pwdResetTime; | ||||
| 
 | ||||
|     /** | ||||
|      * 登录时系统设置的密码过期天数 | ||||
|      */ | ||||
|     private Integer passwordExpirationDays; | ||||
| 
 | ||||
|     public LoginUser(Set<String> permissions, Set<RoleDTO> roles, Integer passwordExpirationDays) { | ||||
|     public UserContext(Set<String> permissions, Set<RoleContext> roles, Integer passwordExpirationDays) { | ||||
|         this.permissions = permissions; | ||||
|         this.setRoles(roles); | ||||
|         this.passwordExpirationDays = passwordExpirationDays; | ||||
|     } | ||||
| 
 | ||||
|     public void setRoles(Set<RoleDTO> roles) { | ||||
|     public void setRoles(Set<RoleContext> roles) { | ||||
|         this.roles = roles; | ||||
|         this.roleCodes = roles.stream().map(RoleDTO::getCode).collect(Collectors.toSet()); | ||||
|         this.roleCodes = roles.stream().map(RoleContext::getCode).collect(Collectors.toSet()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -0,0 +1,185 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.continew.admin.common.context; | ||||
|  | ||||
| import cn.dev33.satoken.session.SaSession; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import top.continew.starter.core.util.ExceptionUtils; | ||||
| import top.continew.starter.extension.crud.service.CommonUserService; | ||||
|  | ||||
| import java.util.Optional; | ||||
|  | ||||
| /** | ||||
|  * 用户上下文 Holder | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2022/12/24 12:58 | ||||
|  */ | ||||
| public class UserContextHolder { | ||||
|  | ||||
|     private static final ThreadLocal<UserContext> CONTEXT_HOLDER = new ThreadLocal<>(); | ||||
|     private static final ThreadLocal<UserExtraContext> EXTRA_CONTEXT_HOLDER = new ThreadLocal<>(); | ||||
|  | ||||
|     private UserContextHolder() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 设置上下文 | ||||
|      * | ||||
|      * @param context 上下文 | ||||
|      */ | ||||
|     public static void setContext(UserContext context) { | ||||
|         setContext(context, true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 设置上下文 | ||||
|      * | ||||
|      * @param context  上下文 | ||||
|      * @param isUpdate 是否更新 | ||||
|      */ | ||||
|     public static void setContext(UserContext context, boolean isUpdate) { | ||||
|         CONTEXT_HOLDER.set(context); | ||||
|         if (isUpdate) { | ||||
|             StpUtil.getSessionByLoginId(context.getId()).set(SaSession.USER, context); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取上下文 | ||||
|      * | ||||
|      * @return 上下文 | ||||
|      */ | ||||
|     public static UserContext getContext() { | ||||
|         UserContext context = CONTEXT_HOLDER.get(); | ||||
|         if (null == context) { | ||||
|             context = StpUtil.getSession().getModel(SaSession.USER, UserContext.class); | ||||
|             CONTEXT_HOLDER.set(context); | ||||
|         } | ||||
|         return context; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取指定用户的上下文 | ||||
|      * | ||||
|      * @param userId 用户 ID | ||||
|      * @return 上下文 | ||||
|      */ | ||||
|     public static UserContext getContext(Long userId) { | ||||
|         SaSession session = StpUtil.getSessionByLoginId(userId, false); | ||||
|         if (null == session) { | ||||
|             return null; | ||||
|         } | ||||
|         return session.getModel(SaSession.USER, UserContext.class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 设置额外上下文 | ||||
|      * | ||||
|      * @param context 额外上下文 | ||||
|      */ | ||||
|     public static void setExtraContext(UserExtraContext context) { | ||||
|         EXTRA_CONTEXT_HOLDER.set(context); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取额外上下文 | ||||
|      * | ||||
|      * @return 额外上下文 | ||||
|      */ | ||||
|     public static UserExtraContext getExtraContext() { | ||||
|         UserExtraContext context = EXTRA_CONTEXT_HOLDER.get(); | ||||
|         if (null == context) { | ||||
|             context = getExtraContext(StpUtil.getTokenValue()); | ||||
|             EXTRA_CONTEXT_HOLDER.set(context); | ||||
|         } | ||||
|         return context; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取额外上下文 | ||||
|      * | ||||
|      * @param token 令牌 | ||||
|      * @return 额外上下文 | ||||
|      */ | ||||
|     public static UserExtraContext getExtraContext(String token) { | ||||
|         UserExtraContext context = new UserExtraContext(); | ||||
|         context.setIp(Convert.toStr(StpUtil.getExtra(token, "ip"))); | ||||
|         context.setAddress(Convert.toStr(StpUtil.getExtra(token, "address"))); | ||||
|         context.setBrowser(Convert.toStr(StpUtil.getExtra(token, "browser"))); | ||||
|         context.setOs(Convert.toStr(StpUtil.getExtra(token, "os"))); | ||||
|         context.setLoginTime(Convert.toLocalDateTime(StpUtil.getExtra(token, "loginTime"))); | ||||
|         return context; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 清除上下文 | ||||
|      */ | ||||
|     public static void clearContext() { | ||||
|         CONTEXT_HOLDER.remove(); | ||||
|         EXTRA_CONTEXT_HOLDER.remove(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取用户 ID | ||||
|      * | ||||
|      * @return 用户 ID | ||||
|      */ | ||||
|     public static Long getUserId() { | ||||
|         return Optional.ofNullable(getContext()).map(UserContext::getId).orElse(null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取用户名 | ||||
|      * | ||||
|      * @return 用户名 | ||||
|      */ | ||||
|     public static String getUsername() { | ||||
|         return Optional.ofNullable(getContext()).map(UserContext::getUsername).orElse(null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取用户昵称 | ||||
|      * | ||||
|      * @return 用户昵称 | ||||
|      */ | ||||
|     public static String getNickname() { | ||||
|         return getNickname(getUserId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取用户昵称 | ||||
|      * | ||||
|      * @param userId 登录用户 ID | ||||
|      * @return 用户昵称 | ||||
|      */ | ||||
|     public static String getNickname(Long userId) { | ||||
|         return ExceptionUtils.exToNull(() -> SpringUtil.getBean(CommonUserService.class).getNicknameById(userId)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 是否为管理员 | ||||
|      * | ||||
|      * @return 是否为管理员 | ||||
|      */ | ||||
|     public static Boolean isAdmin() { | ||||
|         StpUtil.checkLogin(); | ||||
|         return getContext().isAdmin(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,77 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.continew.admin.common.context; | ||||
|  | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.extra.servlet.JakartaServletUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import top.continew.starter.core.util.ExceptionUtils; | ||||
| import top.continew.starter.core.util.IpUtils; | ||||
| import top.continew.starter.web.util.ServletUtils; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * 用户额外上下文 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2024/10/9 20:29 | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class UserExtraContext implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * IP | ||||
|      */ | ||||
|     private String ip; | ||||
|  | ||||
|     /** | ||||
|      * IP 归属地 | ||||
|      */ | ||||
|     private String address; | ||||
|  | ||||
|     /** | ||||
|      * 浏览器 | ||||
|      */ | ||||
|     private String browser; | ||||
|  | ||||
|     /** | ||||
|      * 操作系统 | ||||
|      */ | ||||
|     private String os; | ||||
|  | ||||
|     /** | ||||
|      * 登录时间 | ||||
|      */ | ||||
|     private LocalDateTime loginTime; | ||||
|  | ||||
|     public UserExtraContext(HttpServletRequest request) { | ||||
|         this.ip = JakartaServletUtil.getClientIP(request); | ||||
|         this.address = ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(this.ip)); | ||||
|         this.setBrowser(ServletUtils.getBrowser(request)); | ||||
|         this.setLoginTime(LocalDateTime.now()); | ||||
|         this.setOs(StrUtil.subBefore(ServletUtils.getOs(request), " or", false)); | ||||
|     } | ||||
| } | ||||
| @@ -1,152 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.continew.admin.common.util.helper; | ||||
|  | ||||
| import cn.dev33.satoken.context.SaHolder; | ||||
| import cn.dev33.satoken.exception.NotLoginException; | ||||
| import cn.dev33.satoken.session.SaSession; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.extra.servlet.JakartaServletUtil; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.starter.core.util.ExceptionUtils; | ||||
| import top.continew.starter.core.util.IpUtils; | ||||
| import top.continew.starter.extension.crud.service.CommonUserService; | ||||
| import top.continew.starter.web.util.ServletUtils; | ||||
| import top.continew.starter.web.util.SpringWebUtils; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * 登录助手 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @author Lion Li(<a href="https://gitee.com/dromara/RuoYi-Vue-Plus">RuoYi-Vue-Plus</a>) | ||||
|  * @since 2022/12/24 12:58 | ||||
|  */ | ||||
| public class LoginHelper { | ||||
|  | ||||
|     private LoginHelper() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 用户登录并缓存用户信息 | ||||
|      * | ||||
|      * @param loginUser 登录用户信息 | ||||
|      * @return 令牌 | ||||
|      */ | ||||
|     public static String login(LoginUser loginUser) { | ||||
|         // 记录登录信息 | ||||
|         HttpServletRequest request = SpringWebUtils.getRequest(); | ||||
|         loginUser.setIp(JakartaServletUtil.getClientIP(request)); | ||||
|         loginUser.setAddress(ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(loginUser.getIp()))); | ||||
|         loginUser.setBrowser(ServletUtils.getBrowser(request)); | ||||
|         loginUser.setLoginTime(LocalDateTime.now()); | ||||
|         loginUser.setOs(StrUtil.subBefore(ServletUtils.getOs(request), " or", false)); | ||||
|         // 登录并缓存用户信息 | ||||
|         StpUtil.login(loginUser.getId()); | ||||
|         SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         String tokenValue = StpUtil.getTokenValue(); | ||||
|         loginUser.setToken(tokenValue); | ||||
|         StpUtil.getTokenSession().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         return tokenValue; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 更新登录用户信息 | ||||
|      * | ||||
|      * @param loginUser | ||||
|      *                  登录用户信息 | ||||
|      * @param token     令牌 | ||||
|      */ | ||||
|     public static void updateLoginUser(LoginUser loginUser, String token) { | ||||
|         SaHolder.getStorage().delete(CacheConstants.LOGIN_USER_KEY); | ||||
|         StpUtil.getTokenSessionByToken(token).set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户信息 | ||||
|      * | ||||
|      * @return 登录用户信息 | ||||
|      * @throws NotLoginException 未登录异常 | ||||
|      */ | ||||
|     public static LoginUser getLoginUser() throws NotLoginException { | ||||
|         StpUtil.checkLogin(); | ||||
|         LoginUser loginUser = (LoginUser)SaHolder.getStorage().get(CacheConstants.LOGIN_USER_KEY); | ||||
|         if (null != loginUser) { | ||||
|             return loginUser; | ||||
|         } | ||||
|         SaSession tokenSession = StpUtil.getTokenSession(); | ||||
|         loginUser = (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); | ||||
|         SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); | ||||
|         return loginUser; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 根据 Token 获取登录用户信息 | ||||
|      * | ||||
|      * @param token 用户 Token | ||||
|      * @return 登录用户信息 | ||||
|      */ | ||||
|     public static LoginUser getLoginUser(String token) { | ||||
|         SaSession tokenSession = StpUtil.getStpLogic().getTokenSessionByToken(token, false); | ||||
|         if (null == tokenSession) { | ||||
|             return null; | ||||
|         } | ||||
|         return (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户 ID | ||||
|      * | ||||
|      * @return 登录用户 ID | ||||
|      */ | ||||
|     public static Long getUserId() { | ||||
|         return getLoginUser().getId(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户名 | ||||
|      * | ||||
|      * @return 登录用户名 | ||||
|      */ | ||||
|     public static String getUsername() { | ||||
|         return getLoginUser().getUsername(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户昵称 | ||||
|      * | ||||
|      * @return 登录用户昵称 | ||||
|      */ | ||||
|     public static String getNickname() { | ||||
|         return getNickname(getUserId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取登录用户昵称 | ||||
|      * | ||||
|      * @param userId 登录用户 ID | ||||
|      * @return 登录用户昵称 | ||||
|      */ | ||||
|     public static String getNickname(Long userId) { | ||||
|         return ExceptionUtils.exToNull(() -> SpringUtil.getBean(CommonUserService.class).getNicknameById(userId)); | ||||
|     } | ||||
| } | ||||
| @@ -18,7 +18,6 @@ package top.continew.admin.auth.service; | ||||
|  | ||||
| import top.continew.admin.auth.model.query.OnlineUserQuery; | ||||
| import top.continew.admin.auth.model.resp.OnlineUserResp; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.starter.extension.crud.model.query.PageQuery; | ||||
| import top.continew.starter.extension.crud.model.resp.PageResp; | ||||
|  | ||||
| @@ -48,7 +47,7 @@ public interface OnlineUserService { | ||||
|      * @param query 查询条件 | ||||
|      * @return 列表信息 | ||||
|      */ | ||||
|     List<LoginUser> list(OnlineUserQuery query); | ||||
|     List<OnlineUserResp> list(OnlineUserQuery query); | ||||
|  | ||||
|     /** | ||||
|      * 查询 Token 最后活跃时间 | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| package top.continew.admin.auth.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.stp.SaLoginConfig; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.bean.BeanUtil; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| @@ -39,11 +40,12 @@ import top.continew.admin.auth.service.LoginService; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.constant.RegexConstants; | ||||
| import top.continew.admin.common.constant.SysConstants; | ||||
| import top.continew.admin.common.context.RoleContext; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.context.UserExtraContext; | ||||
| import top.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.continew.admin.common.enums.GenderEnum; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.model.dto.RoleDTO; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.enums.MenuTypeEnum; | ||||
| import top.continew.admin.system.enums.MessageTemplateEnum; | ||||
| import top.continew.admin.system.enums.MessageTypeEnum; | ||||
| @@ -61,6 +63,7 @@ import top.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.continew.starter.extension.crud.annotation.TreeField; | ||||
| import top.continew.starter.extension.crud.util.TreeUtils; | ||||
| import top.continew.starter.messaging.websocket.util.WebSocketUtils; | ||||
| import top.continew.starter.web.util.SpringWebUtils; | ||||
|  | ||||
| import java.time.Duration; | ||||
| import java.time.LocalDateTime; | ||||
| @@ -205,15 +208,19 @@ public class LoginServiceImpl implements LoginService { | ||||
|         Long userId = user.getId(); | ||||
|         CompletableFuture<Set<String>> permissionFuture = CompletableFuture.supplyAsync(() -> roleService | ||||
|             .listPermissionByUserId(userId), threadPoolTaskExecutor); | ||||
|         CompletableFuture<Set<RoleDTO>> roleFuture = CompletableFuture.supplyAsync(() -> roleService | ||||
|         CompletableFuture<Set<RoleContext>> roleFuture = CompletableFuture.supplyAsync(() -> roleService | ||||
|             .listByUserId(userId), threadPoolTaskExecutor); | ||||
|         CompletableFuture<Integer> passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService | ||||
|             .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); | ||||
|         CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture); | ||||
|         LoginUser loginUser = new LoginUser(permissionFuture.join(), roleFuture.join(), passwordExpirationDaysFuture | ||||
|             .join()); | ||||
|         BeanUtil.copyProperties(user, loginUser); | ||||
|         return LoginHelper.login(loginUser); | ||||
|         UserContext userContext = new UserContext(permissionFuture.join(), roleFuture | ||||
|             .join(), passwordExpirationDaysFuture.join()); | ||||
|         BeanUtil.copyProperties(user, userContext); | ||||
|         // 登录并缓存用户信息 | ||||
|         StpUtil.login(userContext.getId(), SaLoginConfig.setExtraData(BeanUtil | ||||
|             .beanToMap(new UserExtraContext(SpringWebUtils.getRequest())))); | ||||
|         UserContextHolder.setContext(userContext); | ||||
|         return StpUtil.getTokenValue(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import cn.dev33.satoken.dao.SaTokenDao; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.bean.BeanUtil; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| @@ -28,17 +29,16 @@ import org.springframework.stereotype.Service; | ||||
| import top.continew.admin.auth.model.query.OnlineUserQuery; | ||||
| import top.continew.admin.auth.model.resp.OnlineUserResp; | ||||
| import top.continew.admin.auth.service.OnlineUserService; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.context.UserExtraContext; | ||||
| import top.continew.starter.core.constant.StringConstants; | ||||
| import top.continew.starter.extension.crud.model.query.PageQuery; | ||||
| import top.continew.starter.extension.crud.model.resp.PageResp; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Comparator; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.*; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| /** | ||||
|  * 在线用户业务实现 | ||||
| @@ -53,31 +53,43 @@ public class OnlineUserServiceImpl implements OnlineUserService { | ||||
|     @Override | ||||
|     @AutoOperate(type = OnlineUserResp.class, on = "list") | ||||
|     public PageResp<OnlineUserResp> page(OnlineUserQuery query, PageQuery pageQuery) { | ||||
|         List<LoginUser> loginUserList = this.list(query); | ||||
|         List<OnlineUserResp> list = BeanUtil.copyToList(loginUserList, OnlineUserResp.class); | ||||
|         List<OnlineUserResp> list = this.list(query); | ||||
|         return PageResp.build(pageQuery.getPage(), pageQuery.getSize(), list); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<LoginUser> list(OnlineUserQuery query) { | ||||
|         List<LoginUser> loginUserList = new ArrayList<>(); | ||||
|         // 查询所有登录用户 | ||||
|     public List<OnlineUserResp> list(OnlineUserQuery query) { | ||||
|         List<OnlineUserResp> list = new ArrayList<>(); | ||||
|         // 查询所有在线 Token | ||||
|         List<String> tokenKeyList = StpUtil.searchTokenValue(StringConstants.EMPTY, 0, -1, false); | ||||
|         tokenKeyList.parallelStream().forEach(tokenKey -> { | ||||
|         Map<Long, List<String>> tokenMap = tokenKeyList.stream().filter(tokenKey -> { | ||||
|             String token = StrUtil.subAfter(tokenKey, StringConstants.COLON, true); | ||||
|             // 忽略已过期或失效 Token | ||||
|             if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < SaTokenDao.NEVER_EXPIRE) { | ||||
|                 return; | ||||
|             return StpUtil.getStpLogic().getTokenActiveTimeoutByToken(token) >= SaTokenDao.NEVER_EXPIRE; | ||||
|         }) | ||||
|             .map(tokenKey -> StrUtil.subAfter(tokenKey, StringConstants.COLON, true)) | ||||
|             .collect(Collectors.groupingBy(token -> Convert.toLong(StpUtil.getLoginIdByToken(token)))); | ||||
|         // 过滤 Token | ||||
|         for (Map.Entry<Long, List<String>> entry : tokenMap.entrySet()) { | ||||
|             Long userId = entry.getKey(); | ||||
|             UserContext userContext = UserContextHolder.getContext(userId); | ||||
|             if (null == userContext || !this.isMatchNickname(query.getNickname(), userContext)) { | ||||
|                 continue; | ||||
|             } | ||||
|             // 检查是否符合查询条件 | ||||
|             LoginUser loginUser = LoginHelper.getLoginUser(token); | ||||
|             if (this.isMatchQuery(query, loginUser)) { | ||||
|                 loginUserList.add(loginUser); | ||||
|             List<Date> loginTimeList = query.getLoginTime(); | ||||
|             entry.getValue().parallelStream().forEach(token -> { | ||||
|                 UserExtraContext extraContext = UserContextHolder.getExtraContext(token); | ||||
|                 if (this.isMatchLoginTime(loginTimeList, extraContext.getLoginTime())) { | ||||
|                     OnlineUserResp resp = BeanUtil.copyProperties(userContext, OnlineUserResp.class); | ||||
|                     BeanUtil.copyProperties(extraContext, resp); | ||||
|                     resp.setToken(token); | ||||
|                     list.add(resp); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         // 设置排序 | ||||
|         CollUtil.sort(loginUserList, Comparator.comparing(LoginUser::getLoginTime).reversed()); | ||||
|         return loginUserList; | ||||
|         CollUtil.sort(list, Comparator.comparing(OnlineUserResp::getLoginTime).reversed()); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -95,35 +107,31 @@ public class OnlineUserServiceImpl implements OnlineUserService { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 是否符合查询条件 | ||||
|      * 是否匹配昵称 | ||||
|      * | ||||
|      * @param query     查询条件 | ||||
|      * @param loginUser 登录用户信息 | ||||
|      * @return 是否符合查询条件 | ||||
|      * @param nickname    昵称 | ||||
|      * @param userContext 用户上下文信息 | ||||
|      * @return 是否匹配昵称 | ||||
|      */ | ||||
|     private boolean isMatchQuery(OnlineUserQuery query, LoginUser loginUser) { | ||||
|         boolean flag1 = true; | ||||
|         String nickname = query.getNickname(); | ||||
|         if (StrUtil.isNotBlank(nickname)) { | ||||
|             flag1 = StrUtil.contains(loginUser.getUsername(), nickname) || StrUtil.contains(LoginHelper | ||||
|                 .getNickname(loginUser.getId()), nickname); | ||||
|     private boolean isMatchNickname(String nickname, UserContext userContext) { | ||||
|         if (StrUtil.isBlank(nickname)) { | ||||
|             return true; | ||||
|         } | ||||
|         boolean flag2 = true; | ||||
|         List<Date> loginTime = query.getLoginTime(); | ||||
|         if (CollUtil.isNotEmpty(loginTime)) { | ||||
|             flag2 = DateUtil.isIn(DateUtil.date(loginUser.getLoginTime()).toJdkDate(), loginTime.get(0), loginTime | ||||
|                 .get(1)); | ||||
|         return StrUtil.contains(userContext.getUsername(), nickname) || StrUtil.contains(UserContextHolder | ||||
|             .getNickname(userContext.getId()), nickname); | ||||
|     } | ||||
|         boolean flag3 = true; | ||||
|         Long userId = query.getUserId(); | ||||
|         if (null != userId) { | ||||
|             flag3 = userId.equals(loginUser.getId()); | ||||
|  | ||||
|     /** | ||||
|      * 是否匹配登录时间 | ||||
|      * | ||||
|      * @param loginTimeList 登录时间列表 | ||||
|      * @param loginTime     登录时间 | ||||
|      * @return 是否匹配登录时间 | ||||
|      */ | ||||
|     private boolean isMatchLoginTime(List<Date> loginTimeList, LocalDateTime loginTime) { | ||||
|         if (CollUtil.isEmpty(loginTimeList)) { | ||||
|             return true; | ||||
|         } | ||||
|         boolean flag4 = true; | ||||
|         Long roleId = query.getRoleId(); | ||||
|         if (null != roleId) { | ||||
|             flag4 = loginUser.getRoles().stream().anyMatch(r -> r.getId().equals(roleId)); | ||||
|         } | ||||
|         return flag1 && flag2 && flag3 && flag4; | ||||
|         return DateUtil.isIn(DateUtil.date(loginTime).toJdkDate(), loginTimeList.get(0), loginTimeList.get(1)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,7 @@ import org.dromara.x.file.storage.core.FileInfo; | ||||
| import org.dromara.x.file.storage.core.recorder.FileRecorder; | ||||
| import org.dromara.x.file.storage.core.upload.FilePartInfo; | ||||
| import org.springframework.stereotype.Component; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.system.enums.FileTypeEnum; | ||||
| import top.continew.admin.system.mapper.FileMapper; | ||||
| import top.continew.admin.system.mapper.StorageMapper; | ||||
| @@ -66,7 +66,7 @@ public class FileRecorderImpl implements FileRecorder { | ||||
|         StorageDO storage = (StorageDO)fileInfo.getAttr().get(ClassUtil.getClassName(StorageDO.class, false)); | ||||
|         file.setStorageId(storage.getId()); | ||||
|         file.setCreateTime(DateUtil.toLocalDateTime(fileInfo.getCreateTime())); | ||||
|         file.setUpdateUser(LoginHelper.getUserId()); | ||||
|         file.setUpdateUser(UserContextHolder.getUserId()); | ||||
|         file.setUpdateTime(file.getCreateTime()); | ||||
|         fileMapper.insert(file); | ||||
|         return true; | ||||
|   | ||||
| @@ -16,13 +16,9 @@ | ||||
|  | ||||
| package top.continew.admin.system.mapper; | ||||
|  | ||||
| import org.apache.ibatis.annotations.Param; | ||||
| import org.apache.ibatis.annotations.Select; | ||||
| import top.continew.admin.system.model.entity.UserRoleDO; | ||||
| import top.continew.starter.data.mp.base.BaseMapper; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * 用户和角色 Mapper | ||||
|  * | ||||
| @@ -30,13 +26,4 @@ import java.util.List; | ||||
|  * @since 2023/2/13 23:13 | ||||
|  */ | ||||
| public interface UserRoleMapper extends BaseMapper<UserRoleDO> { | ||||
|  | ||||
|     /** | ||||
|      * 根据用户 ID 查询 | ||||
|      * | ||||
|      * @param userId 用户 ID | ||||
|      * @return 角色 ID 列表 | ||||
|      */ | ||||
|     @Select("SELECT role_id FROM sys_user_role WHERE user_id = #{userId}") | ||||
|     List<Long> selectRoleIdByUserId(@Param("userId") Long userId); | ||||
| } | ||||
|   | ||||
| @@ -27,12 +27,12 @@ import com.alibaba.excel.annotation.ExcelProperty; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import top.continew.admin.common.constant.ContainerConstants; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.continew.admin.common.enums.GenderEnum; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.service.DeptService; | ||||
| import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; | ||||
| import top.continew.starter.extension.crud.model.resp.BaseDetailResp; | ||||
| import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; | ||||
| import top.continew.starter.file.excel.converter.ExcelListConverter; | ||||
| import top.continew.starter.security.crypto.annotation.FieldEncrypt; | ||||
|  | ||||
| @@ -160,6 +160,6 @@ public class UserDetailResp extends BaseDetailResp { | ||||
|  | ||||
|     @Override | ||||
|     public Boolean getDisabled() { | ||||
|         return this.getIsSystem() || Objects.equals(this.getId(), LoginHelper.getUserId()); | ||||
|         return this.getIsSystem() || Objects.equals(this.getId(), UserContextHolder.getUserId()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,9 +21,9 @@ import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import top.continew.admin.common.constant.ContainerConstants; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.continew.admin.common.enums.GenderEnum; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.starter.extension.crud.model.resp.BaseDetailResp; | ||||
| import top.continew.starter.security.mask.annotation.JsonMask; | ||||
| import top.continew.starter.security.mask.enums.MaskType; | ||||
| @@ -129,6 +129,6 @@ public class UserResp extends BaseDetailResp { | ||||
|  | ||||
|     @Override | ||||
|     public Boolean getDisabled() { | ||||
|         return this.getIsSystem() || Objects.equals(this.getId(), LoginHelper.getUserId()); | ||||
|         return this.getIsSystem() || Objects.equals(this.getId(), UserContextHolder.getUserId()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| package top.continew.admin.system.service; | ||||
|  | ||||
| import top.continew.admin.common.model.dto.RoleDTO; | ||||
| import top.continew.admin.common.context.RoleContext; | ||||
| import top.continew.admin.system.model.entity.RoleDO; | ||||
| import top.continew.admin.system.model.query.RoleQuery; | ||||
| import top.continew.admin.system.model.req.RoleReq; | ||||
| @@ -66,7 +66,7 @@ public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQ | ||||
|      * @param userId 用户 ID | ||||
|      * @return 角色集合 | ||||
|      */ | ||||
|     Set<RoleDTO> listByUserId(Long userId); | ||||
|     Set<RoleContext> listByUserId(Long userId); | ||||
|  | ||||
|     /** | ||||
|      * 根据角色编码查询 | ||||
|   | ||||
| @@ -59,6 +59,14 @@ public interface UserRoleService { | ||||
|      */ | ||||
|     List<Long> listRoleIdByUserId(Long userId); | ||||
|  | ||||
|     /** | ||||
|      * 根据角色 ID 查询 | ||||
|      * | ||||
|      * @param roleId 角色 ID | ||||
|      * @return 用户 ID 列表 | ||||
|      */ | ||||
|     List<Long> listUserIdByRoleId(Long roleId); | ||||
|  | ||||
|     /** | ||||
|      * 根据角色 ID 判断是否已被用户关联 | ||||
|      * | ||||
|   | ||||
| @@ -26,15 +26,14 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import top.continew.admin.auth.model.query.OnlineUserQuery; | ||||
| import top.continew.admin.auth.service.OnlineUserService; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.constant.ContainerConstants; | ||||
| import top.continew.admin.common.constant.SysConstants; | ||||
| import top.continew.admin.common.context.RoleContext; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.enums.DataScopeEnum; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.model.dto.RoleDTO; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.mapper.RoleMapper; | ||||
| import top.continew.admin.system.model.entity.RoleDO; | ||||
| import top.continew.admin.system.model.query.RoleQuery; | ||||
| @@ -104,13 +103,14 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes | ||||
|         boolean isSaveDeptSuccess = roleDeptService.add(req.getDeptIds(), id); | ||||
|         // 如果功能权限或数据权限有变更,则更新在线用户权限信息 | ||||
|         if (isSaveMenuSuccess || isSaveDeptSuccess || ObjectUtil.notEqual(req.getDataScope(), oldDataScope)) { | ||||
|             OnlineUserQuery query = new OnlineUserQuery(); | ||||
|             query.setRoleId(id); | ||||
|             List<LoginUser> loginUserList = onlineUserService.list(query); | ||||
|             loginUserList.forEach(loginUser -> { | ||||
|                 loginUser.setRoles(this.listByUserId(loginUser.getId())); | ||||
|                 loginUser.setPermissions(this.listPermissionByUserId(loginUser.getId())); | ||||
|                 LoginHelper.updateLoginUser(loginUser, loginUser.getToken()); | ||||
|             List<Long> userIdList = userRoleService.listUserIdByRoleId(id); | ||||
|             userIdList.parallelStream().forEach(userId -> { | ||||
|                 UserContext userContext = UserContextHolder.getContext(userId); | ||||
|                 if (null != userContext) { | ||||
|                     userContext.setRoles(this.listByUserId(userId)); | ||||
|                     userContext.setPermissions(this.listPermissionByUserId(userId)); | ||||
|                     UserContextHolder.setContext(userContext); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -171,10 +171,10 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Set<RoleDTO> listByUserId(Long userId) { | ||||
|     public Set<RoleContext> listByUserId(Long userId) { | ||||
|         List<Long> roleIdList = userRoleService.listRoleIdByUserId(userId); | ||||
|         List<RoleDO> roleList = baseMapper.lambdaQuery().in(RoleDO::getId, roleIdList).list(); | ||||
|         return new HashSet<>(BeanUtil.copyToList(roleList, RoleDTO.class)); | ||||
|         return new HashSet<>(BeanUtil.copyToList(roleList, RoleContext.class)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -75,7 +75,24 @@ public class UserRoleServiceImpl implements UserRoleService { | ||||
|     @Override | ||||
|     @ContainerMethod(namespace = ContainerConstants.USER_ROLE_ID_LIST, type = MappingType.ORDER_OF_KEYS) | ||||
|     public List<Long> listRoleIdByUserId(Long userId) { | ||||
|         return baseMapper.selectRoleIdByUserId(userId); | ||||
|         return baseMapper.lambdaQuery() | ||||
|             .select(UserRoleDO::getRoleId) | ||||
|             .eq(UserRoleDO::getUserId, userId) | ||||
|             .list() | ||||
|             .stream() | ||||
|             .map(UserRoleDO::getRoleId) | ||||
|             .toList(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<Long> listUserIdByRoleId(Long roleId) { | ||||
|         return baseMapper.lambdaQuery() | ||||
|             .select(UserRoleDO::getUserId) | ||||
|             .eq(UserRoleDO::getRoleId, roleId) | ||||
|             .list() | ||||
|             .stream() | ||||
|             .map(UserRoleDO::getUserId) | ||||
|             .toList(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -52,15 +52,14 @@ import org.springframework.security.crypto.password.PasswordEncoder; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
| import top.continew.admin.auth.model.query.OnlineUserQuery; | ||||
| import top.continew.admin.auth.service.OnlineUserService; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.constant.SysConstants; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.continew.admin.common.enums.GenderEnum; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.SecureUtils; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.mapper.UserMapper; | ||||
| import top.continew.admin.system.model.entity.DeptDO; | ||||
| import top.continew.admin.system.model.entity.RoleDO; | ||||
| @@ -310,7 +309,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes | ||||
|         String phone = req.getPhone(); | ||||
|         CheckUtils.throwIf(StrUtil.isNotBlank(phone) && this.isPhoneExists(phone, id), errorMsgTemplate, phone); | ||||
|         DisEnableStatusEnum newStatus = req.getStatus(); | ||||
|         CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(newStatus) && ObjectUtil.equal(id, LoginHelper | ||||
|         CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(newStatus) && ObjectUtil.equal(id, UserContextHolder | ||||
|             .getUserId()), "不允许禁用当前用户"); | ||||
|         UserDO oldUser = super.getById(id); | ||||
|         if (Boolean.TRUE.equals(oldUser.getIsSystem())) { | ||||
| @@ -333,14 +332,12 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes | ||||
|         } | ||||
|         // 如果角色有变更,则更新在线用户权限信息 | ||||
|         if (isSaveUserRoleSuccess) { | ||||
|             OnlineUserQuery query = new OnlineUserQuery(); | ||||
|             query.setUserId(id); | ||||
|             List<LoginUser> loginUserList = onlineUserService.list(query); | ||||
|             loginUserList.forEach(loginUser -> { | ||||
|                 loginUser.setRoles(roleService.listByUserId(loginUser.getId())); | ||||
|                 loginUser.setPermissions(roleService.listPermissionByUserId(loginUser.getId())); | ||||
|                 LoginHelper.updateLoginUser(loginUser, loginUser.getToken()); | ||||
|             }); | ||||
|             UserContext userContext = UserContextHolder.getContext(id); | ||||
|             if (null != userContext) { | ||||
|                 userContext.setRoles(roleService.listByUserId(id)); | ||||
|                 userContext.setPermissions(roleService.listPermissionByUserId(id)); | ||||
|                 UserContextHolder.setContext(userContext); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -348,7 +345,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     @CacheInvalidate(key = "#ids", name = CacheConstants.USER_KEY_PREFIX, multi = true) | ||||
|     public void delete(List<Long> ids) { | ||||
|         CheckUtils.throwIf(CollUtil.contains(ids, LoginHelper.getUserId()), "不允许删除当前用户"); | ||||
|         CheckUtils.throwIf(CollUtil.contains(ids, UserContextHolder.getUserId()), "不允许删除当前用户"); | ||||
|         List<UserDO> list = baseMapper.lambdaQuery() | ||||
|             .select(UserDO::getNickname, UserDO::getIsSystem) | ||||
|             .in(UserDO::getId, ids) | ||||
|   | ||||
| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.continew.admin.config.satoken; | ||||
|  | ||||
| import cn.dev33.satoken.fun.SaParamFunction; | ||||
| import cn.dev33.satoken.interceptor.SaInterceptor; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.springframework.lang.Nullable; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
|  | ||||
| /** | ||||
|  * Sa-Token 扩展拦截器 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2024/10/10 20:25 | ||||
|  */ | ||||
| public class SaExtensionInterceptor extends SaInterceptor { | ||||
|  | ||||
|     public SaExtensionInterceptor(SaParamFunction<Object> auth) { | ||||
|         super(auth); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean preHandle(HttpServletRequest request, | ||||
|                              HttpServletResponse response, | ||||
|                              Object handler) throws Exception { | ||||
|         boolean flag = super.preHandle(request, response, handler); | ||||
|         if (flag && StpUtil.isLogin()) { | ||||
|             UserContextHolder.getContext(); | ||||
|             UserContextHolder.getExtraContext(); | ||||
|         } | ||||
|         return flag; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void afterCompletion(HttpServletRequest request, | ||||
|                                 HttpServletResponse response, | ||||
|                                 Object handler, | ||||
|                                 @Nullable Exception e) throws Exception { | ||||
|         try { | ||||
|             super.afterCompletion(request, response, handler, e); | ||||
|         } finally { | ||||
|             UserContextHolder.clearContext(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -19,11 +19,12 @@ package top.continew.admin.config.satoken; | ||||
| import cn.dev33.satoken.interceptor.SaInterceptor; | ||||
| import cn.dev33.satoken.router.SaRouter; | ||||
| import cn.dev33.satoken.stp.StpInterface; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties; | ||||
| import top.continew.starter.core.constant.StringConstants; | ||||
| import top.continew.starter.core.util.validate.CheckUtils; | ||||
| @@ -54,14 +55,15 @@ public class SaTokenConfiguration { | ||||
|      */ | ||||
|     @Bean | ||||
|     public SaInterceptor saInterceptor() { | ||||
|         return new SaInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) | ||||
|         return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) | ||||
|             .notMatch(properties.getSecurity().getExcludes()) | ||||
|             .check(r -> { | ||||
|                 LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|                 StpUtil.checkLogin(); | ||||
|                 if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) { | ||||
|                     return; | ||||
|                 } | ||||
|                 CheckUtils.throwIf(loginUser.isPasswordExpired(), "密码已过期,请修改密码"); | ||||
|                 UserContext userContext = UserContextHolder.getContext(); | ||||
|                 CheckUtils.throwIf(userContext.isPasswordExpired(), "密码已过期,请修改密码"); | ||||
|             })); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,8 +17,8 @@ | ||||
| package top.continew.admin.config.satoken; | ||||
|  | ||||
| import cn.dev33.satoken.stp.StpInterface; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @@ -33,13 +33,13 @@ public class SaTokenPermissionImpl implements StpInterface { | ||||
|  | ||||
|     @Override | ||||
|     public List<String> getPermissionList(Object loginId, String loginType) { | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|         return new ArrayList<>(loginUser.getPermissions()); | ||||
|         UserContext userContext = UserContextHolder.getContext(); | ||||
|         return new ArrayList<>(userContext.getPermissions()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<String> getRoleList(Object loginId, String loginType) { | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|         return new ArrayList<>(loginUser.getRoleCodes()); | ||||
|         UserContext userContext = UserContextHolder.getContext(); | ||||
|         return new ArrayList<>(userContext.getRoleCodes()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,9 +35,9 @@ import top.continew.admin.auth.model.resp.RouteResp; | ||||
| import top.continew.admin.auth.model.resp.UserInfoResp; | ||||
| import top.continew.admin.auth.service.LoginService; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.model.dto.LoginUser; | ||||
| import top.continew.admin.common.context.UserContext; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.util.SecureUtils; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.model.resp.UserDetailResp; | ||||
| import top.continew.admin.system.service.UserService; | ||||
| import top.continew.starter.cache.redisson.util.RedisUtils; | ||||
| @@ -122,12 +122,12 @@ public class AuthController { | ||||
|     @Operation(summary = "获取用户信息", description = "获取登录用户信息") | ||||
|     @GetMapping("/user/info") | ||||
|     public UserInfoResp getUserInfo() { | ||||
|         LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|         UserDetailResp userDetailResp = userService.get(loginUser.getId()); | ||||
|         UserContext userContext = UserContextHolder.getContext(); | ||||
|         UserDetailResp userDetailResp = userService.get(userContext.getId()); | ||||
|         UserInfoResp userInfoResp = BeanUtil.copyProperties(userDetailResp, UserInfoResp.class); | ||||
|         userInfoResp.setPermissions(loginUser.getPermissions()); | ||||
|         userInfoResp.setRoles(loginUser.getRoleCodes()); | ||||
|         userInfoResp.setPwdExpired(loginUser.isPasswordExpired()); | ||||
|         userInfoResp.setPermissions(userContext.getPermissions()); | ||||
|         userInfoResp.setRoles(userContext.getRoleCodes()); | ||||
|         userInfoResp.setPwdExpired(userContext.isPasswordExpired()); | ||||
|         return userInfoResp; | ||||
|     } | ||||
|  | ||||
| @@ -135,6 +135,6 @@ public class AuthController { | ||||
|     @Operation(summary = "获取路由信息", description = "获取登录用户的路由信息") | ||||
|     @GetMapping("/route") | ||||
|     public List<RouteResp> listRoute() { | ||||
|         return loginService.buildRouteTree(LoginHelper.getUserId()); | ||||
|         return loginService.buildRouteTree(UserContextHolder.getUserId()); | ||||
|     } | ||||
| } | ||||
| @@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.system.model.query.MessageQuery; | ||||
| import top.continew.admin.system.model.resp.MessageResp; | ||||
| import top.continew.admin.system.model.resp.MessageUnreadResp; | ||||
| @@ -53,7 +53,7 @@ public class MessageController { | ||||
|     @Operation(summary = "分页查询列表", description = "分页查询列表") | ||||
|     @GetMapping | ||||
|     public PageResp<MessageResp> page(MessageQuery query, @Validated PageQuery pageQuery) { | ||||
|         query.setUserId(LoginHelper.getUserId()); | ||||
|         query.setUserId(UserContextHolder.getUserId()); | ||||
|         return baseService.page(query, pageQuery); | ||||
|     } | ||||
|  | ||||
| @@ -76,6 +76,6 @@ public class MessageController { | ||||
|     @Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY) | ||||
|     @GetMapping("/unread") | ||||
|     public MessageUnreadResp countUnreadMessage(@RequestParam(required = false) Boolean detail) { | ||||
|         return messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail); | ||||
|         return messageUserService.countUnreadMessageByUserId(UserContextHolder.getUserId(), detail); | ||||
|     } | ||||
| } | ||||
| @@ -31,8 +31,8 @@ import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
| import top.continew.admin.common.constant.CacheConstants; | ||||
| import top.continew.admin.common.context.UserContextHolder; | ||||
| import top.continew.admin.common.util.SecureUtils; | ||||
| import top.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.continew.admin.system.enums.SocialSourceEnum; | ||||
| import top.continew.admin.system.model.entity.UserSocialDO; | ||||
| import top.continew.admin.system.model.req.UserBasicInfoUpdateReq; | ||||
| @@ -73,14 +73,14 @@ public class UserCenterController { | ||||
|     @PostMapping("/avatar") | ||||
|     public AvatarResp updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException { | ||||
|         ValidationUtils.throwIf(avatarFile::isEmpty, "头像不能为空"); | ||||
|         String newAvatar = userService.updateAvatar(avatarFile, LoginHelper.getUserId()); | ||||
|         String newAvatar = userService.updateAvatar(avatarFile, UserContextHolder.getUserId()); | ||||
|         return AvatarResp.builder().avatar(newAvatar).build(); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "修改基础信息", description = "修改用户基础信息") | ||||
|     @PatchMapping("/basic/info") | ||||
|     public void updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) { | ||||
|         userService.updateBasicInfo(req, LoginHelper.getUserId()); | ||||
|         userService.updateBasicInfo(req, UserContextHolder.getUserId()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "修改密码", description = "修改用户登录密码") | ||||
| @@ -92,7 +92,7 @@ public class UserCenterController { | ||||
|         String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq | ||||
|             .getNewPassword())); | ||||
|         ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败"); | ||||
|         userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId()); | ||||
|         userService.updatePassword(rawOldPassword, rawNewPassword, UserContextHolder.getUserId()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "修改手机号", description = "修改手机号") | ||||
| @@ -106,7 +106,7 @@ public class UserCenterController { | ||||
|         ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); | ||||
|         ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误"); | ||||
|         RedisUtils.delete(captchaKey); | ||||
|         userService.updatePhone(updateReq.getPhone(), rawOldPassword, LoginHelper.getUserId()); | ||||
|         userService.updatePhone(updateReq.getPhone(), rawOldPassword, UserContextHolder.getUserId()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "修改邮箱", description = "修改用户邮箱") | ||||
| @@ -120,13 +120,13 @@ public class UserCenterController { | ||||
|         ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); | ||||
|         ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误"); | ||||
|         RedisUtils.delete(captchaKey); | ||||
|         userService.updateEmail(updateReq.getEmail(), rawOldPassword, LoginHelper.getUserId()); | ||||
|         userService.updateEmail(updateReq.getEmail(), rawOldPassword, UserContextHolder.getUserId()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "查询绑定的三方账号", description = "查询绑定的三方账号") | ||||
|     @GetMapping("/social") | ||||
|     public List<UserSocialBindResp> listSocialBind() { | ||||
|         List<UserSocialDO> userSocialList = userSocialService.listByUserId(LoginHelper.getUserId()); | ||||
|         List<UserSocialDO> userSocialList = userSocialService.listByUserId(UserContextHolder.getUserId()); | ||||
|         return userSocialList.stream().map(userSocial -> { | ||||
|             String source = userSocial.getSource(); | ||||
|             UserSocialBindResp userSocialBind = new UserSocialBindResp(); | ||||
| @@ -144,13 +144,13 @@ public class UserCenterController { | ||||
|         AuthResponse<AuthUser> response = authRequest.login(callback); | ||||
|         ValidationUtils.throwIf(!response.ok(), response.getMsg()); | ||||
|         AuthUser authUser = response.getData(); | ||||
|         userSocialService.bind(authUser, LoginHelper.getUserId()); | ||||
|         userSocialService.bind(authUser, UserContextHolder.getUserId()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "解绑三方账号", description = "解绑三方账号") | ||||
|     @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH) | ||||
|     @DeleteMapping("/social/{source}") | ||||
|     public void unbindSocial(@PathVariable String source) { | ||||
|         userSocialService.deleteBySourceAndUserId(source, LoginHelper.getUserId()); | ||||
|         userSocialService.deleteBySourceAndUserId(source, UserContextHolder.getUserId()); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user