mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	移除超时的在线用户&单元测试
This commit is contained in:
		| @@ -20,7 +20,7 @@ public interface SysUserSessionMapper extends BaseMapperX<SysUserSessionDO> { | |||||||
|                 .likeIfPresent("user_ip", reqVO.getUserIp())); |                 .likeIfPresent("user_ip", reqVO.getUserIp())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     default List<SysUserSessionDO> selectSessionTimeout() { |     default List<SysUserSessionDO> selectListBySessionTimoutLt() { | ||||||
|         return selectList(new QueryWrapperX<SysUserSessionDO>().lt("session_timeout",new Date())); |         return selectList(new QueryWrapperX<SysUserSessionDO>().lt("session_timeout",new Date())); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ public interface SysRedisKeyConstants { | |||||||
|  |  | ||||||
|     RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登陆用户的缓存", |     RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登陆用户的缓存", | ||||||
|             "login_user:%s", // 参数为 sessionId |             "login_user:%s", // 参数为 sessionId | ||||||
|             STRING, LoginUser.class, Duration.ofMinutes(30)); |             STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | ||||||
|  |  | ||||||
|     RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存", |     RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存", | ||||||
|             "captcha_code:%s", // 参数为 uuid |             "captcha_code:%s", // 参数为 uuid | ||||||
|   | |||||||
| @@ -1,11 +1,13 @@ | |||||||
| package cn.iocoder.dashboard.modules.system.dal.redis.auth; | package cn.iocoder.dashboard.modules.system.dal.redis.auth; | ||||||
|  |  | ||||||
| import cn.iocoder.dashboard.framework.security.core.LoginUser; | import cn.iocoder.dashboard.framework.security.core.LoginUser; | ||||||
|  | import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService; | ||||||
| import cn.iocoder.dashboard.util.json.JsonUtils; | import cn.iocoder.dashboard.util.json.JsonUtils; | ||||||
| import org.springframework.data.redis.core.StringRedisTemplate; | import org.springframework.data.redis.core.StringRedisTemplate; | ||||||
| import org.springframework.stereotype.Repository; | import org.springframework.stereotype.Repository; | ||||||
|  |  | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
|  | import java.time.Duration; | ||||||
|  |  | ||||||
| import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER; | import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER; | ||||||
|  |  | ||||||
| @@ -19,6 +21,8 @@ public class SysLoginUserRedisDAO { | |||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private StringRedisTemplate stringRedisTemplate; |     private StringRedisTemplate stringRedisTemplate; | ||||||
|  |     @Resource | ||||||
|  |     private SysUserSessionService sysUserSessionService; | ||||||
|  |  | ||||||
|     public LoginUser get(String sessionId) { |     public LoginUser get(String sessionId) { | ||||||
|         String redisKey = formatKey(sessionId); |         String redisKey = formatKey(sessionId); | ||||||
| @@ -27,7 +31,8 @@ public class SysLoginUserRedisDAO { | |||||||
|  |  | ||||||
|     public void set(String sessionId, LoginUser loginUser) { |     public void set(String sessionId, LoginUser loginUser) { | ||||||
|         String redisKey = formatKey(sessionId); |         String redisKey = formatKey(sessionId); | ||||||
|         stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), LOGIN_USER.getTimeout()); |         stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), | ||||||
|  |                 Duration.ofMillis(sysUserSessionService.getSessionTimeoutMillis())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void delete(String sessionId) { |     public void delete(String sessionId) { | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ public class SysUserSessionTimeoutJob implements JobHandler { | |||||||
|     public String execute(String param) throws Exception { |     public String execute(String param) throws Exception { | ||||||
|         log.info("[execute][执行任务:{}]", "移除超时的在线用户"); |         log.info("[execute][执行任务:{}]", "移除超时的在线用户"); | ||||||
|         long timeoutCount = sysUserSessionService.clearSessionTimeout(); |         long timeoutCount = sysUserSessionService.clearSessionTimeout(); | ||||||
|         log.info("[execute][执行任务:{}]", "移除超时的在线用户完成" + timeoutCount); |         log.info("[execute][执行任务:{}:{}]", "移除超时的在线用户完成", timeoutCount); | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -62,8 +62,8 @@ public interface SysUserSessionService { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 移除超时的在线用户 |      * 移除超时的在线用户 | ||||||
|      * @param |      * | ||||||
|      * @return {@link Long} |      * @return {@link Long } 移出的超时用户数量 | ||||||
|      * @author Lyon |      * @author Lyon | ||||||
|      * @date 2021/3/7 |      * @date 2021/3/7 | ||||||
|      **/ |      **/ | ||||||
|   | |||||||
| @@ -13,16 +13,18 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper; | |||||||
| import cn.iocoder.dashboard.modules.system.dal.redis.auth.SysLoginUserRedisDAO; | import cn.iocoder.dashboard.modules.system.dal.redis.auth.SysLoginUserRedisDAO; | ||||||
| import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService; | import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService; | ||||||
| import cn.iocoder.dashboard.modules.system.service.user.SysUserService; | import cn.iocoder.dashboard.modules.system.service.user.SysUserService; | ||||||
| import cn.iocoder.dashboard.util.date.DateUtils; | import com.google.common.collect.Lists; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
|  |  | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
|  | import java.time.Duration; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER; |  | ||||||
| import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet; | import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet; | ||||||
|  | import static cn.iocoder.dashboard.util.date.DateUtils.addTime; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 在线用户 Session Service 实现类 |  * 在线用户 Session Service 实现类 | ||||||
| @@ -53,7 +55,7 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { | |||||||
|         // 写入 DB 中 |         // 写入 DB 中 | ||||||
|         SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId) |         SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId) | ||||||
|                 .userId(loginUser.getId()).userIp(userIp).userAgent(userAgent) |                 .userId(loginUser.getId()).userIp(userIp).userAgent(userAgent) | ||||||
|                 .sessionTimeout(DateUtils.addTime(LOGIN_USER.getTimeout())) |                 .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) | ||||||
|                 .build(); |                 .build(); | ||||||
|         userSessionMapper.insert(userSession); |         userSessionMapper.insert(userSession); | ||||||
|         // 返回 Session 编号 |         // 返回 Session 编号 | ||||||
| @@ -68,7 +70,7 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { | |||||||
|         // 更新 DB 中 |         // 更新 DB 中 | ||||||
|         SysUserSessionDO updateObj = SysUserSessionDO.builder().id(sessionId).build(); |         SysUserSessionDO updateObj = SysUserSessionDO.builder().id(sessionId).build(); | ||||||
|         updateObj.setUpdateTime(new Date()); |         updateObj.setUpdateTime(new Date()); | ||||||
|         updateObj.setSessionTimeout(DateUtils.addTime(LOGIN_USER.getTimeout())); |         updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); | ||||||
|         userSessionMapper.updateById(updateObj); |         userSessionMapper.updateById(updateObj); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -106,15 +108,17 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { | |||||||
|     @Override |     @Override | ||||||
|     public long clearSessionTimeout() { |     public long clearSessionTimeout() { | ||||||
|         // 获取db里已经超时的用户列表 |         // 获取db里已经超时的用户列表 | ||||||
|         Long timeoutCount = 0L; |         List<SysUserSessionDO> sessionTimeoutDOS = userSessionMapper.selectListBySessionTimoutLt(); | ||||||
|         List<SysUserSessionDO> sessionTimeoutDOS = userSessionMapper.selectSessionTimeout(); |         List<String> timeoutIdList = sessionTimeoutDOS | ||||||
|         for (SysUserSessionDO sessionDO : sessionTimeoutDOS) { |                 .stream() | ||||||
|             // 确认已经超时,移出在线用户列表 |                 .filter(sessionDO -> loginUserRedisDAO.get(sessionDO.getId()) == null) | ||||||
|             if (loginUserRedisDAO.get(sessionDO.getId()) == null) { |                 .map(SysUserSessionDO::getId) | ||||||
|                 timeoutCount += userSessionMapper.deleteById(sessionDO.getId()); |                 .collect(Collectors.toList()); | ||||||
|             } |         // 确认已经超时,按批次移出在线用户列表 | ||||||
|  |         if (CollUtil.isNotEmpty(timeoutIdList)) { | ||||||
|  |             Lists.partition(timeoutIdList, 100).forEach(userSessionMapper::deleteBatchIds); | ||||||
|         } |         } | ||||||
|         return timeoutCount; |         return timeoutIdList.size(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -0,0 +1,54 @@ | |||||||
|  | package cn.iocoder.dashboard.modules.system.service.auth; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.date.DateUtil; | ||||||
|  | import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||||
|  | import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX; | ||||||
|  | import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO; | ||||||
|  | import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper; | ||||||
|  | import cn.iocoder.dashboard.util.RandomUtils; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | import java.util.stream.Stream; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * SysUserSessionServiceImpl Tester. | ||||||
|  |  * | ||||||
|  |  * @author Lyon | ||||||
|  |  * @version 1.0 | ||||||
|  |  * @since <pre>3月 8, 2021</pre> | ||||||
|  |  */ | ||||||
|  | public class SysUserSessionServiceImplTest extends BaseSpringBootUnitTest { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     SysUserSessionService sysUserSessionService; | ||||||
|  |     @Resource | ||||||
|  |     SysUserSessionMapper sysUserSessionMapper; | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testClearSessionTimeout_success() throws Exception { | ||||||
|  |         // 准备超时数据 120 条, 在线用户 1 条 | ||||||
|  |         int expectedTimeoutCount = 120, expectedTotal = 1; | ||||||
|  |  | ||||||
|  |         // 准备数据 | ||||||
|  |         List<SysUserSessionDO> prepareData = Stream | ||||||
|  |                 .iterate(0, i -> i) | ||||||
|  |                 .limit(expectedTimeoutCount) | ||||||
|  |                 .map(i -> RandomUtils.randomPojo(SysUserSessionDO.class, o -> o.setSessionTimeout(DateUtil.offsetSecond(new Date(), -1)))) | ||||||
|  |                 .collect(Collectors.toList()); | ||||||
|  |         prepareData.add(RandomUtils.randomPojo(SysUserSessionDO.class, o -> o.setSessionTimeout(DateUtil.offsetMinute(new Date(), 30)))); | ||||||
|  |         prepareData.forEach(sysUserSessionMapper::insert); | ||||||
|  |  | ||||||
|  |         //清空超时数据 | ||||||
|  |         long actualTimeoutCount = sysUserSessionService.clearSessionTimeout(); | ||||||
|  |         assertEquals(expectedTimeoutCount, actualTimeoutCount); | ||||||
|  |         Integer actualTotal = sysUserSessionMapper.selectCount(new QueryWrapperX<>()); | ||||||
|  |         assertEquals(expectedTotal, actualTotal); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | }  | ||||||
| @@ -8,3 +8,4 @@ DELETE FROM "sys_role"; | |||||||
| DELETE FROM "sys_role_menu"; | DELETE FROM "sys_role_menu"; | ||||||
| DELETE FROM "sys_menu"; | DELETE FROM "sys_menu"; | ||||||
| DELETE FROM "sys_dict_type"; | DELETE FROM "sys_dict_type"; | ||||||
|  | DELETE FROM "sys_user_session"; | ||||||
|   | |||||||
| @@ -114,3 +114,17 @@ CREATE TABLE "sys_dict_type" ( | |||||||
|     "deleted" bit NOT NULL DEFAULT FALSE, |     "deleted" bit NOT NULL DEFAULT FALSE, | ||||||
|     PRIMARY KEY ("id") |     PRIMARY KEY ("id") | ||||||
| ) COMMENT '字典类型表'; | ) COMMENT '字典类型表'; | ||||||
|  |  | ||||||
|  | CREATE TABLE `sys_user_session` ( | ||||||
|  |     `id` varchar(32) NOT NULL, | ||||||
|  |     `user_id` bigint DEFAULT NULL, | ||||||
|  |     `user_ip` varchar(50) DEFAULT NULL, | ||||||
|  |     `user_agent` varchar(512) DEFAULT NULL, | ||||||
|  |     `session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||||
|  |     "create_by" varchar(64) DEFAULT '', | ||||||
|  |     "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||||
|  |     `update_by` varchar(64) DEFAULT '' , | ||||||
|  |     "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||||
|  |     "deleted" bit NOT NULL DEFAULT FALSE, | ||||||
|  |     PRIMARY KEY (`id`) | ||||||
|  | ) COMMENT '用户在线 Session'; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lyon
					Lyon