refactor: 使用事件解耦业务副作用并优化虚拟线程并发
- 新增登录成功事件,解耦在线用户缓存、登录日志和最近登录信息更新 - 新增在线用户清理事件,角色授权变更后异步清理受影响用户会话 - 新增 OSS 配置变更事件,统一处理配置初始化、删除和默认配置切换后的缓存刷新 - 新增工作流抄送、待办消息、结果消息事件,拆分全局监听器中的副作用逻辑 - 扩展 ThreadUtils 支持带返回值的虚拟线程批量执行 - 在线用户监控批量读取 Redis 会话信息时使用虚拟线程并发处理 - OSS 多文件查询、URL 解析和 DTO 转换使用虚拟线程并发处理 - 工作流办理人回显和用户解析按类型并发查询 - 工作流多渠道消息发送改为并发执行 - WebSocket 与 SSE 广播消息按用户并发发送
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package org.dromara.web.event;
|
||||
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
|
||||
/**
|
||||
* 用户登录成功事件。
|
||||
*
|
||||
* @param loginId 登录标识
|
||||
* @param tokenValue token 值
|
||||
* @param loginParameter 登录参数
|
||||
*/
|
||||
public record UserLoginSuccessEvent(Object loginId, String tokenValue, SaLoginParameter loginParameter) {
|
||||
}
|
||||
@@ -2,73 +2,29 @@ package org.dromara.web.listener;
|
||||
|
||||
import cn.dev33.satoken.listener.SaTokenListener;
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.CacheNames;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.common.log.event.LoginInfoEvent;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.domain.UserOnlineDTO;
|
||||
import org.dromara.web.service.SysLoginService;
|
||||
import org.dromara.web.event.UserLoginSuccessEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 用户行为监听器,用于同步在线状态和登录日志。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UserActionListener implements SaTokenListener {
|
||||
|
||||
private final SysLoginService loginService;
|
||||
|
||||
/**
|
||||
* 登录成功后记录在线信息并写入登录日志。
|
||||
*/
|
||||
@Override
|
||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
|
||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String ip = ServletUtils.getClientIP();
|
||||
UserOnlineDTO dto = new UserOnlineDTO();
|
||||
dto.setIpaddr(ip);
|
||||
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
dto.setBrowser(userAgent.getBrowser().getName());
|
||||
dto.setOs(userAgent.getOs().getName());
|
||||
dto.setLoginTime(System.currentTimeMillis());
|
||||
dto.setTokenId(tokenValue);
|
||||
String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
|
||||
dto.setUserName(username);
|
||||
dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
|
||||
dto.setDeviceType(loginParameter.getDeviceType());
|
||||
dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
|
||||
if (loginParameter.getTimeout() == -1) {
|
||||
RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
LoginInfoEvent loginInfoEvent = new LoginInfoEvent();
|
||||
loginInfoEvent.setUsername(username);
|
||||
loginInfoEvent.setStatus(Constants.LOGIN_SUCCESS);
|
||||
loginInfoEvent.setMessage(MessageUtils.message("user.login.success"));
|
||||
loginInfoEvent.setRequest(ServletUtils.getRequest());
|
||||
SpringUtils.context().publishEvent(loginInfoEvent);
|
||||
// 更新登录信息
|
||||
loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
|
||||
log.info("user doLogin, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||
SpringUtils.context().publishEvent(new UserLoginSuccessEvent(loginId, tokenValue, loginParameter));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.dromara.web.listener;
|
||||
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.CacheNames;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||
import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.domain.UserOnlineDTO;
|
||||
import org.dromara.web.event.UserLoginSuccessEvent;
|
||||
import org.dromara.web.service.SysLoginService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 用户登录成功监听器。
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UserLoginSuccessListener {
|
||||
|
||||
private final SysLoginService loginService;
|
||||
|
||||
/**
|
||||
* 登录成功后记录在线信息、登录日志与最近登录信息。
|
||||
*
|
||||
* @param event 用户登录成功事件
|
||||
*/
|
||||
@EventListener
|
||||
public void handleLoginSuccess(UserLoginSuccessEvent event) {
|
||||
SaLoginParameter loginParameter = event.loginParameter();
|
||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
String ip = ServletUtils.getClientIP();
|
||||
String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
|
||||
String tokenValue = event.tokenValue();
|
||||
|
||||
UserOnlineDTO dto = new UserOnlineDTO();
|
||||
dto.setIpaddr(ip);
|
||||
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
dto.setBrowser(userAgent.getBrowser().getName());
|
||||
dto.setOs(userAgent.getOs().getName());
|
||||
dto.setLoginTime(System.currentTimeMillis());
|
||||
dto.setTokenId(tokenValue);
|
||||
dto.setUserName(username);
|
||||
dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
|
||||
dto.setDeviceType(loginParameter.getDeviceType());
|
||||
dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
|
||||
if (loginParameter.getTimeout() == -1) {
|
||||
RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto);
|
||||
} else {
|
||||
RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
|
||||
}
|
||||
|
||||
loginService.recordLoginInfo(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||
loginService.updateLastLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
|
||||
log.info("user doLogin, userId:{}, token:***{}", event.loginId(), StringUtils.right(tokenValue, 8));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -175,7 +175,7 @@ public class SysLoginService {
|
||||
* @param userId 用户ID
|
||||
* @param ip 登录IP
|
||||
*/
|
||||
public void recordLoginInfo(Long userId, String ip) {
|
||||
public void updateLastLoginInfo(Long userId, String ip) {
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserId(userId);
|
||||
sysUser.setLoginIp(ip);
|
||||
|
||||
Reference in New Issue
Block a user