diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java index bda8914e..a9895063 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java @@ -73,7 +73,7 @@ public class LoginHelper { /** * 获取登录用户信息 * - * @return / + * @return 登录用户信息 */ public static LoginUser getLoginUser() { LoginUser loginUser = (LoginUser)SaHolder.getStorage().get(CacheConsts.LOGIN_USER_KEY); @@ -85,6 +85,17 @@ public class LoginHelper { return loginUser; } + /** + * 根据 Token 获取登录用户信息 + * + * @param token + * 用户 Token + * @return 登录用户信息 + */ + public static LoginUser getLoginUser(String token) { + return StpUtil.getTokenSessionByToken(token).get(CacheConsts.LOGIN_USER_KEY, new LoginUser()); + } + /** * 更新登录用户信息 * diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OnlineUserQuery.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/query/OnlineUserQuery.java similarity index 96% rename from continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OnlineUserQuery.java rename to continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/query/OnlineUserQuery.java index 8fcc2998..4169c995 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OnlineUserQuery.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/query/OnlineUserQuery.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.cnadmin.monitor.model.query; +package top.charles7c.cnadmin.auth.model.query; import java.io.Serializable; import java.util.Date; diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OnlineUserVO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/OnlineUserVO.java similarity index 97% rename from continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OnlineUserVO.java rename to continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/OnlineUserVO.java index 24b92a38..c3132933 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OnlineUserVO.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/model/vo/OnlineUserVO.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.cnadmin.monitor.model.vo; +package top.charles7c.cnadmin.auth.model.vo; import java.io.Serializable; import java.time.LocalDateTime; diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/OnlineUserService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/OnlineUserService.java new file mode 100644 index 00000000..86ed9af2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/OnlineUserService.java @@ -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.charles7c.cnadmin.auth.service; + +import java.util.List; + +import top.charles7c.cnadmin.auth.model.query.OnlineUserQuery; +import top.charles7c.cnadmin.auth.model.vo.OnlineUserVO; +import top.charles7c.cnadmin.common.model.dto.LoginUser; +import top.charles7c.cnadmin.common.model.query.PageQuery; +import top.charles7c.cnadmin.common.model.vo.PageDataVO; + +/** + * 在线用户业务接口 + * + * @author Charles7c + * @since 2023/3/25 22:48 + */ +public interface OnlineUserService { + + /** + * 分页查询列表 + * + * @param query + * 查询条件 + * @param pageQuery + * 分页查询条件 + * @return 分页列表信息 + */ + PageDataVO page(OnlineUserQuery query, PageQuery pageQuery); + + /** + * 查询列表 + * + * @param query + * 查询条件 + * @return 列表信息 + */ + List list(OnlineUserQuery query); + + /** + * 根据角色 ID 清除 + * + * @param roleId + * 角色 ID + */ + void cleanByRoleId(Long roleId); +} diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/OnlineUserServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/OnlineUserServiceImpl.java new file mode 100644 index 00000000..a567606a --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/OnlineUserServiceImpl.java @@ -0,0 +1,123 @@ +/* + * 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.charles7c.cnadmin.auth.service.impl; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; + +import top.charles7c.cnadmin.auth.model.query.OnlineUserQuery; +import top.charles7c.cnadmin.auth.model.vo.OnlineUserVO; +import top.charles7c.cnadmin.auth.service.OnlineUserService; +import top.charles7c.cnadmin.common.constant.StringConsts; +import top.charles7c.cnadmin.common.model.dto.LoginUser; +import top.charles7c.cnadmin.common.model.query.PageQuery; +import top.charles7c.cnadmin.common.model.vo.PageDataVO; +import top.charles7c.cnadmin.common.util.helper.LoginHelper; + +/** + * 在线用户业务实现类 + * + * @author Charles7c + * @author Lion Li(RuoYi-Vue-Plus) + * @since 2023/3/25 22:49 + */ +@Service +@RequiredArgsConstructor +public class OnlineUserServiceImpl implements OnlineUserService { + + @Override + public PageDataVO page(OnlineUserQuery query, PageQuery pageQuery) { + List loginUserList = this.list(query); + List list = BeanUtil.copyToList(loginUserList, OnlineUserVO.class); + return PageDataVO.build(pageQuery.getPage(), pageQuery.getSize(), list); + } + + @Override + public List list(OnlineUserQuery query) { + List loginUserList = new ArrayList<>(); + // 查询所有登录用户 + List tokenKeyList = StpUtil.searchTokenValue(StringConsts.EMPTY, 0, -1, false); + for (String tokenKey : tokenKeyList) { + String token = StrUtil.subAfter(tokenKey, StringConsts.COLON, true); + // 忽略已过期或失效 Token + if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < SaTokenDao.NEVER_EXPIRE) { + continue; + } + // 检查是否符合查询条件 + LoginUser loginUser = LoginHelper.getLoginUser(token); + if (this.checkQuery(query, loginUser)) { + loginUserList.add(loginUser); + } + } + // 设置排序 + CollUtil.sort(loginUserList, Comparator.comparing(LoginUser::getLoginTime).reversed()); + return loginUserList; + } + + @Override + public void cleanByRoleId(Long roleId) { + List loginUserList = this.list(new OnlineUserQuery()); + loginUserList.parallelStream().forEach(u -> { + if (u.getRoleSet().stream().anyMatch(r -> r.getId().equals(roleId))) { + try { + StpUtil.logoutByTokenValue(u.getToken()); + } catch (NotLoginException ignored) { + } + } + }); + } + + /** + * 检查是否符合查询条件 + * + * @param query + * 查询条件 + * @param loginUser + * 登录用户信息 + * @return 是否符合查询条件 + */ + private boolean checkQuery(OnlineUserQuery query, LoginUser loginUser) { + boolean flag1 = true; + String nickname = query.getNickname(); + if (StrUtil.isNotBlank(nickname)) { + flag1 = StrUtil.contains(loginUser.getUsername(), nickname) + || StrUtil.contains(loginUser.getNickname(), nickname); + } + + boolean flag2 = true; + List loginTime = query.getLoginTime(); + if (CollUtil.isNotEmpty(loginTime)) { + flag2 = + DateUtil.isIn(DateUtil.date(loginUser.getLoginTime()).toJdkDate(), loginTime.get(0), loginTime.get(1)); + } + return flag1 && flag2; + } +} diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleDeptService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleDeptService.java index 5668e788..97350a55 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleDeptService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleDeptService.java @@ -33,8 +33,9 @@ public interface RoleDeptService { * 部门 ID 列表 * @param roleId * 角色 ID + * @return true:成功,false:无变更/失败 */ - void save(List deptIds, Long roleId); + boolean save(List deptIds, Long roleId); /** * 根据角色 ID 查询 diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleMenuService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleMenuService.java index dadec1b2..58d8a3a3 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleMenuService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleMenuService.java @@ -33,8 +33,9 @@ public interface RoleMenuService { * 菜单 ID 列表 * @param roleId * 角色 ID + * @return true:成功,false:无变更/失败 */ - void save(List menuIds, Long roleId); + boolean save(List menuIds, Long roleId); /** * 根据角色 ID 查询 diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserRoleService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserRoleService.java index 46ce389e..4b9319b9 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserRoleService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserRoleService.java @@ -33,8 +33,9 @@ public interface UserRoleService { * 角色 ID 列表 * @param userId * 用户 ID + * @return true:成功,false:无变更/失败 */ - void save(List roleIds, Long userId); + boolean save(List roleIds, Long userId); /** * 根据角色 ID 列表查询 diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleDeptServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleDeptServiceImpl.java index a4068a5a..0e14592e 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleDeptServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleDeptServiceImpl.java @@ -23,6 +23,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import cn.hutool.core.collection.CollUtil; + import top.charles7c.cnadmin.system.mapper.RoleDeptMapper; import top.charles7c.cnadmin.system.model.entity.RoleDeptDO; import top.charles7c.cnadmin.system.service.RoleDeptService; @@ -40,13 +42,19 @@ public class RoleDeptServiceImpl implements RoleDeptService { private final RoleDeptMapper roleDeptMapper; @Override - public void save(List deptIds, Long roleId) { + public boolean save(List deptIds, Long roleId) { + // 检查是否有变更 + List oldDeptIdList = roleDeptMapper.lambdaQuery().select(RoleDeptDO::getDeptId) + .eq(RoleDeptDO::getRoleId, roleId).list().stream().map(RoleDeptDO::getDeptId).collect(Collectors.toList()); + if (CollUtil.isEmpty(CollUtil.disjunction(deptIds, oldDeptIdList))) { + return false; + } // 删除原有关联 roleDeptMapper.lambdaUpdate().eq(RoleDeptDO::getRoleId, roleId).remove(); // 保存最新关联 List roleDeptList = deptIds.stream().map(deptId -> new RoleDeptDO(roleId, deptId)).collect(Collectors.toList()); - roleDeptMapper.insertBatch(roleDeptList); + return roleDeptMapper.insertBatch(roleDeptList); } @Override diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleMenuServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleMenuServiceImpl.java index ff95d99a..4d6b5e63 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleMenuServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleMenuServiceImpl.java @@ -43,13 +43,19 @@ public class RoleMenuServiceImpl implements RoleMenuService { private final RoleMenuMapper roleMenuMapper; @Override - public void save(List menuIds, Long roleId) { + public boolean save(List menuIds, Long roleId) { + // 检查是否有变更 + List oldMenuIdList = roleMenuMapper.lambdaQuery().select(RoleMenuDO::getMenuId) + .eq(RoleMenuDO::getRoleId, roleId).list().stream().map(RoleMenuDO::getMenuId).collect(Collectors.toList()); + if (CollUtil.isEmpty(CollUtil.disjunction(menuIds, oldMenuIdList))) { + return false; + } // 删除原有关联 roleMenuMapper.lambdaUpdate().eq(RoleMenuDO::getRoleId, roleId).remove(); // 保存最新关联 List roleMenuList = menuIds.stream().map(menuId -> new RoleMenuDO(roleId, menuId)).collect(Collectors.toList()); - roleMenuMapper.insertBatch(roleMenuList); + return roleMenuMapper.insertBatch(roleMenuList); } @Override diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java index 5151c9bf..2ee157d3 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java @@ -26,9 +26,12 @@ import org.springframework.transaction.annotation.Transactional; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import top.charles7c.cnadmin.auth.service.OnlineUserService; import top.charles7c.cnadmin.common.base.BaseServiceImpl; import top.charles7c.cnadmin.common.constant.SysConsts; +import top.charles7c.cnadmin.common.enums.DataScopeEnum; import top.charles7c.cnadmin.common.enums.DataTypeEnum; import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; import top.charles7c.cnadmin.common.model.dto.RoleDTO; @@ -54,9 +57,10 @@ import top.charles7c.cnadmin.system.service.*; public class RoleServiceImpl extends BaseServiceImpl implements RoleService { + private final MenuService menuService; + private final OnlineUserService onlineUserService; private final RoleMenuService roleMenuService; private final RoleDeptService roleDeptService; - private final MenuService menuService; private final UserRoleService userRoleService; @Override @@ -85,22 +89,30 @@ public class RoleServiceImpl extends BaseServiceImpl roleIds, Long userId) { + public boolean save(List roleIds, Long userId) { + // 检查是否有变更 + List oldRoleIdList = userRoleMapper.lambdaQuery().select(UserRoleDO::getRoleId) + .eq(UserRoleDO::getUserId, userId).list().stream().map(UserRoleDO::getRoleId).collect(Collectors.toList()); + if (CollUtil.isEmpty(CollUtil.disjunction(roleIds, oldRoleIdList))) { + return false; + } // 删除原有关联 userRoleMapper.lambdaUpdate().eq(UserRoleDO::getUserId, userId).remove(); // 保存最新关联 List userRoleList = roleIds.stream().map(roleId -> new UserRoleDO(userId, roleId)).collect(Collectors.toList()); - userRoleMapper.insertBatch(userRoleList); + return userRoleMapper.insertBatch(userRoleList); } @Override diff --git a/continew-admin-ui/src/views/system/role/index.vue b/continew-admin-ui/src/views/system/role/index.vue index a37cb038..02134fba 100644 --- a/continew-admin-ui/src/views/system/role/index.vue +++ b/continew-admin-ui/src/views/system/role/index.vue @@ -204,6 +204,9 @@ @cancel="handleCancel" > + + 变更角色编码、功能权限或数据权限后,关联在线用户会自动下线! +
基础信息 diff --git a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/monitor/OnlineUserController.java b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/monitor/OnlineUserController.java index 1a2d8d04..07acb570 100644 --- a/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/monitor/OnlineUserController.java +++ b/continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/monitor/OnlineUserController.java @@ -16,11 +16,6 @@ package top.charles7c.cnadmin.webapi.controller.monitor; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.List; - import lombok.RequiredArgsConstructor; import io.swagger.v3.oas.annotations.Operation; @@ -30,28 +25,19 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.dev33.satoken.dao.SaTokenDao; -import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.StpUtil; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import top.charles7c.cnadmin.common.constant.CacheConsts; -import top.charles7c.cnadmin.common.constant.StringConsts; -import top.charles7c.cnadmin.common.model.dto.LoginUser; +import top.charles7c.cnadmin.auth.model.query.OnlineUserQuery; +import top.charles7c.cnadmin.auth.model.vo.OnlineUserVO; +import top.charles7c.cnadmin.auth.service.OnlineUserService; import top.charles7c.cnadmin.common.model.query.PageQuery; import top.charles7c.cnadmin.common.model.vo.PageDataVO; import top.charles7c.cnadmin.common.model.vo.R; import top.charles7c.cnadmin.common.util.validate.CheckUtils; -import top.charles7c.cnadmin.monitor.model.query.OnlineUserQuery; -import top.charles7c.cnadmin.monitor.model.vo.*; /** * 在线用户 API * - * @author Lion Li(RuoYi-Vue-Plus) * @author Charles7c * @since 2023/1/20 21:51 */ @@ -61,59 +47,13 @@ import top.charles7c.cnadmin.monitor.model.vo.*; @RequestMapping("/monitor/online/user") public class OnlineUserController { + private final OnlineUserService onlineUserService; + @Operation(summary = "分页查询列表") @SaCheckPermission("monitor:online:user:list") @GetMapping public R> page(@Validated OnlineUserQuery query, @Validated PageQuery pageQuery) { - List loginUserList = new ArrayList<>(); - List tokenKeyList = StpUtil.searchTokenValue(StringConsts.EMPTY, 0, -1, false); - for (String tokenKey : tokenKeyList) { - String token = StrUtil.subAfter(tokenKey, StringConsts.COLON, true); - // 忽略已过期或失效 Token - if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < SaTokenDao.NEVER_EXPIRE) { - continue; - } - - // 获取 Token Session - SaSession saSession = StpUtil.getTokenSessionByToken(token); - LoginUser loginUser = saSession.get(CacheConsts.LOGIN_USER_KEY, new LoginUser()); - - // 检查是否符合查询条件 - if (Boolean.TRUE.equals(checkQuery(query, loginUser))) { - loginUserList.add(loginUser); - } - } - - // 构建分页数据 - List list = BeanUtil.copyToList(loginUserList, OnlineUserVO.class); - CollUtil.sort(list, Comparator.comparing(OnlineUserVO::getLoginTime).reversed()); - PageDataVO pageDataVO = PageDataVO.build(pageQuery.getPage(), pageQuery.getSize(), list); - return R.ok(pageDataVO); - } - - /** - * 检查是否符合查询条件 - * - * @param query - * 查询条件 - * @param loginUser - * 登录用户信息 - * @return 是否符合查询条件 - */ - private boolean checkQuery(OnlineUserQuery query, LoginUser loginUser) { - boolean flag1 = true; - String nickname = query.getNickname(); - if (StrUtil.isNotBlank(nickname)) { - flag1 = loginUser.getUsername().contains(nickname) || loginUser.getNickname().contains(nickname); - } - - boolean flag2 = true; - List loginTime = query.getLoginTime(); - if (CollUtil.isNotEmpty(loginTime)) { - flag2 = - DateUtil.isIn(DateUtil.date(loginUser.getLoginTime()).toJdkDate(), loginTime.get(0), loginTime.get(1)); - } - return flag1 && flag2; + return R.ok(onlineUserService.page(query, pageQuery)); } @Operation(summary = "强退在线用户")