mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-24 07:55:06 +08:00
Merge branch 'rouyi/master' into feature/notice_test
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.dashboard.framework.mybatis.config;
|
||||
|
||||
import cn.iocoder.dashboard.framework.mybatis.core.handler.DefaultDBFieldHandler;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -24,4 +26,9 @@ public class MybatisConfiguration {
|
||||
return mybatisPlusInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MetaObjectHandler defaultMetaObjectHandler(){
|
||||
return new DefaultDBFieldHandler(); // 自动填充参数类
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.dashboard.framework.mybatis.core.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import lombok.Data;
|
||||
|
||||
@ -15,18 +17,26 @@ public class BaseDO implements Serializable {
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 创建者
|
||||
* 创建者,目前使用 SysUser 的 id 编号
|
||||
*
|
||||
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String creator;
|
||||
/**
|
||||
* 更新者
|
||||
* 更新者,目前使用 SysUser 的 id 编号
|
||||
*
|
||||
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private String updater;
|
||||
/**
|
||||
* 是否删除
|
||||
|
@ -1,11 +1,10 @@
|
||||
package cn.iocoder.dashboard.framework.mybatis.core.handle;
|
||||
package cn.iocoder.dashboard.framework.mybatis.core.handler;
|
||||
|
||||
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.dashboard.framework.security.core.LoginUser;
|
||||
import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
@ -17,7 +16,6 @@ import java.util.Objects;
|
||||
*
|
||||
* @author hexiaowu
|
||||
*/
|
||||
@Component
|
||||
public class DefaultDBFieldHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
@ -56,7 +56,7 @@ public class SecurityFrameworkUtils {
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
return (LoginUser) authentication.getPrincipal();
|
||||
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,10 @@
|
||||
package cn.iocoder.dashboard.modules.infra.controller.config;
|
||||
|
||||
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.dashboard.common.pojo.CommonResult;
|
||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||
import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.dashboard.framework.idempotent.core.annotation.Idempotent;
|
||||
import cn.iocoder.dashboard.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
|
||||
import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.dashboard.modules.infra.controller.config.vo.*;
|
||||
import cn.iocoder.dashboard.modules.infra.convert.config.InfConfigConvert;
|
||||
import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
|
||||
@ -13,50 +12,60 @@ import cn.iocoder.dashboard.modules.infra.service.config.InfConfigService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_SENSITIVE;
|
||||
|
||||
@Api(tags = "参数配置")
|
||||
@RestController
|
||||
@RequestMapping("/infra/config")
|
||||
@Validated
|
||||
public class InfConfigController {
|
||||
|
||||
@Resource
|
||||
private InfConfigService configService;
|
||||
|
||||
@ApiOperation("获取参数配置分页")
|
||||
@GetMapping("/page")
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:list')")
|
||||
public CommonResult<PageResult<InfConfigRespVO>> getConfigPage(@Validated InfConfigPageReqVO reqVO) {
|
||||
PageResult<InfConfigDO> page = configService.getConfigPage(reqVO);
|
||||
return success(InfConfigConvert.INSTANCE.convertPage(page));
|
||||
@PostMapping("/create")
|
||||
@ApiOperation("创建参数配置")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:create')")
|
||||
public CommonResult<Long> createConfig(@Valid @RequestBody InfConfigCreateReqVO reqVO) {
|
||||
return success(configService.createConfig(reqVO));
|
||||
}
|
||||
|
||||
@ApiOperation("导出参数配置")
|
||||
@GetMapping("/export")
|
||||
// @Log(title = "参数管理", businessType = BusinessType.EXPORT)
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:export')")
|
||||
public void exportSysConfig(HttpServletResponse response, @Validated InfConfigExportReqVO reqVO) throws IOException {
|
||||
List<InfConfigDO> list = configService.getConfigList(reqVO);
|
||||
// 拼接数据
|
||||
List<InfConfigExcelVO> excelDataList = InfConfigConvert.INSTANCE.convertList(list);
|
||||
// 输出
|
||||
ExcelUtils.write(response, "参数配置.xls", "配置列表",
|
||||
InfConfigExcelVO.class, excelDataList);
|
||||
@ApiOperation("修改参数配置")
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:update')")
|
||||
@Idempotent(timeout = 60)
|
||||
public CommonResult<Boolean> updateConfig(@Valid @RequestBody InfConfigUpdateReqVO reqVO) {
|
||||
configService.updateConfig(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("删除参数配置")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
|
||||
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
|
||||
configService.deleteConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/get")
|
||||
@ApiOperation("获得参数配置")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@GetMapping(value = "/get")
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:query')")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:query')")
|
||||
public CommonResult<InfConfigRespVO> getConfig(@RequestParam("id") Long id) {
|
||||
return success(InfConfigConvert.INSTANCE.convert(configService.getConfig(id)));
|
||||
}
|
||||
@ -70,38 +79,30 @@ public class InfConfigController {
|
||||
return null;
|
||||
}
|
||||
if (config.getSensitive()) {
|
||||
throw ServiceExceptionUtil.exception(CONFIG_GET_VALUE_ERROR_IF_SENSITIVE);
|
||||
throw exception(CONFIG_GET_VALUE_ERROR_IF_SENSITIVE);
|
||||
}
|
||||
return success(config.getValue());
|
||||
}
|
||||
|
||||
@ApiOperation("新增参数配置")
|
||||
@PostMapping("/create")
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:add')")
|
||||
// @Log(title = "参数管理", businessType = BusinessType.INSERT)
|
||||
@Idempotent(timeout = 60, keyResolver = ExpressionIdempotentKeyResolver.class, keyArg = "#reqVO.key")
|
||||
public CommonResult<Long> createConfig(@Validated @RequestBody InfConfigCreateReqVO reqVO) {
|
||||
return success(configService.createConfig(reqVO));
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获取参数配置分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:query')")
|
||||
public CommonResult<PageResult<InfConfigRespVO>> getConfigPage(@Valid InfConfigPageReqVO reqVO) {
|
||||
PageResult<InfConfigDO> page = configService.getConfigPage(reqVO);
|
||||
return success(InfConfigConvert.INSTANCE.convertPage(page));
|
||||
}
|
||||
|
||||
@ApiOperation("修改参数配置")
|
||||
@PutMapping("/update")
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:edit')")
|
||||
// @Log(title = "参数管理", businessType = BusinessType.UPDATE)
|
||||
@Idempotent(timeout = 60)
|
||||
public CommonResult<Boolean> updateConfig(@Validated @RequestBody InfConfigUpdateReqVO reqVO) {
|
||||
configService.updateConfig(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ApiOperation("删除参数配置")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@DeleteMapping("/delete")
|
||||
// @PreAuthorize("@ss.hasPermi('infra:config:remove')")
|
||||
// @Log(title = "参数管理", businessType = BusinessType.DELETE)
|
||||
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
|
||||
configService.deleteConfig(id);
|
||||
return success(true);
|
||||
@GetMapping("/export")
|
||||
@ApiOperation("导出参数配置")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:export')")
|
||||
@OperateLog(type = EXPORT)
|
||||
public void exportSysConfig(@Valid InfConfigExportReqVO reqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<InfConfigDO> list = configService.getConfigList(reqVO);
|
||||
// 拼接数据
|
||||
List<InfConfigExcelVO> datas = InfConfigConvert.INSTANCE.convertList(list);
|
||||
// 输出
|
||||
ExcelUtils.write(response, "参数配置.xls", "数据", InfConfigExcelVO.class, datas);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 参数配置 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface InfConfigService {
|
||||
|
||||
|
@ -10,6 +10,8 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 在线用户表
|
||||
*
|
||||
@ -36,6 +38,14 @@ public class SysUserSessionDO extends BaseDO {
|
||||
* 关联 {@link SysUserDO#getId()}
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户账号
|
||||
*
|
||||
* 冗余,因为账号可以变更
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
@ -44,5 +54,9 @@ public class SysUserSessionDO extends BaseDO {
|
||||
* 浏览器 UA
|
||||
*/
|
||||
private String userAgent;
|
||||
/**
|
||||
* 会话超时时间
|
||||
*/
|
||||
private Date sessionTimeout;
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface SysUserSessionMapper extends BaseMapperX<SysUserSessionDO> {
|
||||
@ -18,4 +20,7 @@ public interface SysUserSessionMapper extends BaseMapperX<SysUserSessionDO> {
|
||||
.likeIfPresent("user_ip", reqVO.getUserIp()));
|
||||
}
|
||||
|
||||
default List<SysUserSessionDO> selectListBySessionTimoutLt() {
|
||||
return selectList(new QueryWrapperX<SysUserSessionDO>().lt("session_timeout",new Date()));
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public interface SysRedisKeyConstants {
|
||||
|
||||
RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登陆用户的缓存",
|
||||
"login_user:%s", // 参数为 sessionId
|
||||
STRING, LoginUser.class, Duration.ofMinutes(30));
|
||||
STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
|
||||
|
||||
RedisKeyDefine CAPTCHA_CODE = new RedisKeyDefine("验证码的缓存",
|
||||
"captcha_code:%s", // 参数为 uuid
|
||||
|
@ -1,11 +1,13 @@
|
||||
package cn.iocoder.dashboard.modules.system.dal.redis.auth;
|
||||
|
||||
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 org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
|
||||
import static cn.iocoder.dashboard.modules.system.dal.redis.SysRedisKeyConstants.LOGIN_USER;
|
||||
|
||||
@ -19,6 +21,8 @@ public class SysLoginUserRedisDAO {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
@Resource
|
||||
private SysUserSessionService sysUserSessionService;
|
||||
|
||||
public LoginUser get(String sessionId) {
|
||||
String redisKey = formatKey(sessionId);
|
||||
@ -27,7 +31,8 @@ public class SysLoginUserRedisDAO {
|
||||
|
||||
public void set(String sessionId, LoginUser loginUser) {
|
||||
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) {
|
||||
|
@ -1,23 +1,30 @@
|
||||
package cn.iocoder.dashboard.modules.system.job.auth;
|
||||
|
||||
import cn.iocoder.dashboard.framework.quartz.core.handler.JobHandler;
|
||||
import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 用户 Session 超时 Job
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author 願
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SysUserSessionTimeoutJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private SysUserSessionService sysUserSessionService;
|
||||
|
||||
@Override
|
||||
public String execute(String param) throws Exception {
|
||||
// System.out.println("执行了一次任务");
|
||||
log.info("[execute][执行任务:{}]", param);
|
||||
return null;
|
||||
// 执行过期
|
||||
Long timeoutCount = sysUserSessionService.clearSessionTimeout();
|
||||
// 返回结果,记录每次的超时数量
|
||||
return String.format("移除在线会话数量为 %s 个", timeoutCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,4 +60,10 @@ public interface SysUserSessionService {
|
||||
*/
|
||||
PageResult<SysUserSessionDO> getUserSessionPage(SysUserSessionPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 移除超时的在线用户
|
||||
*
|
||||
* @return {@link Long } 移出的超时用户数量
|
||||
**/
|
||||
long clearSessionTimeout();
|
||||
}
|
||||
|
@ -6,20 +6,28 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.dashboard.framework.security.core.LoginUser;
|
||||
import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
|
||||
import cn.iocoder.dashboard.modules.system.controller.auth.vo.session.SysUserSessionPageReqVO;
|
||||
import cn.iocoder.dashboard.modules.system.dal.mysql.auth.SysUserSessionMapper;
|
||||
import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
|
||||
import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
|
||||
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
|
||||
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.enums.logger.SysLoginLogTypeEnum;
|
||||
import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
|
||||
import cn.iocoder.dashboard.modules.system.service.auth.SysUserSessionService;
|
||||
import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
|
||||
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.dashboard.util.date.DateUtils.addTime;
|
||||
|
||||
/**
|
||||
* 在线用户 Session Service 实现类
|
||||
@ -31,14 +39,14 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@Resource
|
||||
private SysLoginUserRedisDAO loginUserRedisDAO;
|
||||
@Resource
|
||||
private SysUserSessionMapper userSessionMapper;
|
||||
|
||||
@Resource
|
||||
private SysUserService userService;
|
||||
@Resource
|
||||
private SysLoginLogService loginLogService;
|
||||
|
||||
@Override
|
||||
public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
|
||||
@ -49,7 +57,9 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
|
||||
loginUserRedisDAO.set(sessionId, loginUser);
|
||||
// 写入 DB 中
|
||||
SysUserSessionDO userSession = SysUserSessionDO.builder().id(sessionId)
|
||||
.userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).build();
|
||||
.userId(loginUser.getId()).userIp(userIp).userAgent(userAgent).username(loginUser.getUsername())
|
||||
.sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())))
|
||||
.build();
|
||||
userSessionMapper.insert(userSession);
|
||||
// 返回 Session 编号
|
||||
return sessionId;
|
||||
@ -62,7 +72,9 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
|
||||
loginUserRedisDAO.set(sessionId, loginUser);
|
||||
// 更新 DB 中
|
||||
SysUserSessionDO updateObj = SysUserSessionDO.builder().id(sessionId).build();
|
||||
updateObj.setUsername(loginUser.getUsername());
|
||||
updateObj.setUpdateTime(new Date());
|
||||
updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())));
|
||||
userSessionMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@ -97,6 +109,36 @@ public class SysUserSessionServiceImpl implements SysUserSessionService {
|
||||
return userSessionMapper.selectPage(reqVO, userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long clearSessionTimeout() {
|
||||
// 获取db里已经超时的用户列表
|
||||
List<SysUserSessionDO> sessionTimeoutDOS = userSessionMapper.selectListBySessionTimoutLt();
|
||||
Map<String, SysUserSessionDO> timeoutSessionDOMap = sessionTimeoutDOS
|
||||
.stream()
|
||||
.filter(sessionDO -> loginUserRedisDAO.get(sessionDO.getId()) == null)
|
||||
.collect(Collectors.toMap(SysUserSessionDO::getId, o -> o));
|
||||
// 确认已经超时,按批次移出在线用户列表
|
||||
if (CollUtil.isNotEmpty(timeoutSessionDOMap)) {
|
||||
Lists.partition(new ArrayList<>(timeoutSessionDOMap.keySet()), 100).forEach(userSessionMapper::deleteBatchIds);
|
||||
//记录用户超时退出日志
|
||||
createTimeoutLogoutLog(timeoutSessionDOMap.values());
|
||||
}
|
||||
return timeoutSessionDOMap.size();
|
||||
}
|
||||
|
||||
private void createTimeoutLogoutLog(Collection<SysUserSessionDO> timeoutSessionDOS) {
|
||||
for (SysUserSessionDO timeoutSessionDO : timeoutSessionDOS) {
|
||||
SysLoginLogCreateReqVO reqVO = new SysLoginLogCreateReqVO();
|
||||
reqVO.setLogType(SysLoginLogTypeEnum.LOGOUT_TIMEOUT.getType());
|
||||
reqVO.setTraceId(TracerUtils.getTraceId());
|
||||
reqVO.setUsername(timeoutSessionDO.getUsername());
|
||||
reqVO.setUserAgent(timeoutSessionDO.getUserAgent());
|
||||
reqVO.setUserIp(timeoutSessionDO.getUserIp());
|
||||
reqVO.setResult(SysLoginResultEnum.SUCCESS.getResult());
|
||||
loginLogService.createLoginLog(reqVO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Session 编号,目前采用 UUID 算法
|
||||
*
|
||||
|
@ -0,0 +1,78 @@
|
||||
package cn.iocoder.dashboard.modules.system.service.auth;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.iocoder.dashboard.BaseDbAndRedisUnitTest;
|
||||
import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.dashboard.modules.system.dal.dataobject.auth.SysUserSessionDO;
|
||||
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.service.auth.impl.SysUserSessionServiceImpl;
|
||||
import cn.iocoder.dashboard.modules.system.service.dept.impl.SysDeptServiceImpl;
|
||||
import cn.iocoder.dashboard.modules.system.service.logger.impl.SysLoginLogServiceImpl;
|
||||
import cn.iocoder.dashboard.modules.system.service.user.SysUserServiceImpl;
|
||||
import cn.iocoder.dashboard.util.AssertUtils;
|
||||
import cn.iocoder.dashboard.util.RandomUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
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>
|
||||
*/
|
||||
@Import(
|
||||
SysUserSessionServiceImpl.class)
|
||||
public class SysUserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
@Resource
|
||||
SysUserSessionServiceImpl sysUserSessionService;
|
||||
@Resource
|
||||
SysUserSessionMapper sysUserSessionMapper;
|
||||
@MockBean
|
||||
SecurityProperties securityProperties;
|
||||
@MockBean
|
||||
SysDeptServiceImpl sysDeptService;
|
||||
@MockBean
|
||||
SysUserServiceImpl sysUserService;
|
||||
@MockBean
|
||||
SysLoginLogServiceImpl sysLoginLogService;
|
||||
@MockBean
|
||||
SysLoginUserRedisDAO sysLoginUserRedisDAO;
|
||||
|
||||
@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());
|
||||
SysUserSessionDO sessionDO = RandomUtils.randomPojo(SysUserSessionDO.class, o -> o.setSessionTimeout(DateUtil.offsetMinute(new Date(), 30)));
|
||||
prepareData.add(sessionDO);
|
||||
prepareData.forEach(sysUserSessionMapper::insert);
|
||||
|
||||
//清空超时数据
|
||||
long actualTimeoutCount = sysUserSessionService.clearSessionTimeout();
|
||||
//校验
|
||||
assertEquals(expectedTimeoutCount, actualTimeoutCount);
|
||||
List<SysUserSessionDO> userSessionDOS = sysUserSessionMapper.selectList();
|
||||
assertEquals(expectedTotal, userSessionDOS.size());
|
||||
AssertUtils.assertPojoEquals(sessionDO, userSessionDOS.get(0), "updateTime");
|
||||
}
|
||||
|
||||
}
|
@ -8,3 +8,4 @@ DELETE FROM "sys_role";
|
||||
DELETE FROM "sys_role_menu";
|
||||
DELETE FROM "sys_menu";
|
||||
DELETE FROM "sys_dict_type";
|
||||
DELETE FROM "sys_user_session";
|
||||
|
@ -114,3 +114,18 @@ CREATE TABLE "sys_dict_type" (
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '字典类型表';
|
||||
|
||||
CREATE TABLE `sys_user_session` (
|
||||
`id` varchar(32) NOT NULL,
|
||||
`user_id` bigint DEFAULT NULL,
|
||||
`username` varchar(50) NOT NULL DEFAULT '',
|
||||
`user_ip` varchar(50) DEFAULT NULL,
|
||||
`user_agent` varchar(512) DEFAULT NULL,
|
||||
`session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` 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