feat: 用户操作日志.
This commit is contained in:
@@ -6,7 +6,7 @@ import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.ops.module.infra.define.operator.AuthenticationOperatorType;
|
||||
import com.orion.ops.module.infra.entity.request.user.OfflineUserSessionRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
|
||||
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||
@@ -79,12 +79,11 @@ public class MineController {
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@PutMapping("/offline-session")
|
||||
@Operation(summary = "下线当前用户会话")
|
||||
public HttpWrapper<?> offlineCurrentUserSession(@Validated @RequestBody OfflineUserSessionRequest request) {
|
||||
public HttpWrapper<?> offlineCurrentUserSession(@Validated @RequestBody UserSessionOfflineRequest request) {
|
||||
mineService.offlineCurrentUserSession(request);
|
||||
return HttpWrapper.ok();
|
||||
}
|
||||
|
||||
// fixme 全部用户接口进行 设置缓存
|
||||
// fixme 操作日志
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ public class SystemRoleController {
|
||||
|
||||
@GetMapping("/get-menu-id")
|
||||
@Operation(summary = "获取角色菜单id")
|
||||
@Parameter(name = "roleId", description = "roleId", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:system-role:query')")
|
||||
public List<Long> getRoleMenuIdList(@RequestParam("roleId") Long roleId) {
|
||||
return systemRoleMenuService.getRoleMenuIdList(roleId);
|
||||
|
||||
@@ -110,7 +110,7 @@ public class SystemUserController {
|
||||
@Operation(summary = "查询所有用户")
|
||||
@PreAuthorize("@ss.hasPermission('infra:system-user:query')")
|
||||
public List<SystemUserVO> getSystemUserList() {
|
||||
return systemUserService.getSystemUserByIdList();
|
||||
return systemUserService.getSystemUserList();
|
||||
}
|
||||
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@@ -150,7 +150,7 @@ public class SystemUserController {
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@PutMapping("/offline-session")
|
||||
@Operation(summary = "下线用户会话")
|
||||
public HttpWrapper<?> offlineUserSession(@Validated @RequestBody OfflineUserSessionRequest request) {
|
||||
public HttpWrapper<?> offlineUserSession(@Validated @RequestBody UserSessionOfflineRequest request) {
|
||||
systemUserService.offlineUserSession(request);
|
||||
return HttpWrapper.ok();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.orion.ops.module.infra.convert;
|
||||
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.ops.module.infra.entity.dto.UserInfoDTO;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserCreateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserQueryRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
|
||||
@@ -35,10 +36,14 @@ public interface SystemUserConvert {
|
||||
|
||||
SystemUserVO to(SystemUserDO domain);
|
||||
|
||||
SystemUserVO to(UserInfoDTO user);
|
||||
|
||||
List<SystemUserVO> to(List<SystemUserDO> list);
|
||||
|
||||
LoginUser toLoginUser(SystemUserDO domain);
|
||||
|
||||
UserInfoDTO toUserInfo(SystemUserDO domain);
|
||||
|
||||
UserCollectInfoVO toCollectInfo(LoginUser user);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.lang.define.cache.CacheKeyBuilder;
|
||||
import com.orion.lang.define.cache.CacheKeyDefine;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.ops.module.infra.entity.dto.UserInfoDTO;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -22,9 +23,17 @@ public interface UserCacheKeyDefine {
|
||||
.type(LoginUser.class)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine USER_LIST = new CacheKeyBuilder()
|
||||
.key("user:list:{}")
|
||||
.desc("用户列表")
|
||||
.type(UserInfoDTO.class)
|
||||
.timeout(1, TimeUnit.DAYS)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
|
||||
.key("user:failed:{}")
|
||||
.desc("用户登录失败次数 ${username}")
|
||||
.type(Integer.class)
|
||||
.timeout(3, TimeUnit.DAYS)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.orion.ops.module.infra.entity.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户信息 缓存对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-7-13 18:42
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "UserInfoDTO", description = "用户信息 缓存对象")
|
||||
public class UserInfoDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "花名")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "头像地址")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户状态 0停用 1启用 2锁定")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -6,15 +6,15 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 用户下线请求
|
||||
* 用户会话下线请求
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/17 12:19
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "OfflineUserSessionRequest", description = "用户下线请求")
|
||||
public class OfflineUserSessionRequest {
|
||||
@Schema(name = "UserSessionOfflineRequest", description = "用户会话下线请求")
|
||||
public class UserSessionOfflineRequest {
|
||||
|
||||
@Schema(description = "userId")
|
||||
private Long userId;
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.orion.ops.module.infra.service;
|
||||
|
||||
import com.orion.ops.module.infra.entity.request.user.OfflineUserSessionRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
|
||||
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
|
||||
@@ -59,6 +59,6 @@ public interface MineService {
|
||||
*
|
||||
* @param request request
|
||||
*/
|
||||
void offlineCurrentUserSession(OfflineUserSessionRequest request);
|
||||
void offlineCurrentUserSession(UserSessionOfflineRequest request);
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public interface SystemUserService {
|
||||
*
|
||||
* @return rows
|
||||
*/
|
||||
List<SystemUserVO> getSystemUserByIdList();
|
||||
List<SystemUserVO> getSystemUserList();
|
||||
|
||||
/**
|
||||
* 分页查询用户
|
||||
@@ -74,9 +74,10 @@ public interface SystemUserService {
|
||||
/**
|
||||
* 删除 id 删除用户拓展信息
|
||||
*
|
||||
* @param id id
|
||||
* @param id id
|
||||
* @param username username
|
||||
*/
|
||||
void deleteSystemUserRel(Long id);
|
||||
void deleteSystemUserRel(Long id, String username);
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
@@ -98,6 +99,6 @@ public interface SystemUserService {
|
||||
*
|
||||
* @param request request
|
||||
*/
|
||||
void offlineUserSession(OfflineUserSessionRequest request);
|
||||
void offlineUserSession(UserSessionOfflineRequest request);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.ops.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.ops.module.infra.entity.request.user.OfflineUserSessionRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserResetPasswordRequest;
|
||||
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
|
||||
@@ -81,7 +81,7 @@ public class MineServiceImpl implements MineService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offlineCurrentUserSession(OfflineUserSessionRequest request) {
|
||||
public void offlineCurrentUserSession(UserSessionOfflineRequest request) {
|
||||
request.setUserId(SecurityUtils.getLoginUserId());
|
||||
systemUserService.offlineUserSession(request);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import com.orion.lang.define.wrapper.DataGrid;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.crypto.Signatures;
|
||||
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.ErrorCode;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisMaps;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisStrings;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
@@ -21,9 +23,11 @@ import com.orion.ops.module.infra.define.cache.TipsCacheKeyDefine;
|
||||
import com.orion.ops.module.infra.define.cache.UserCacheKeyDefine;
|
||||
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.ops.module.infra.entity.dto.UserInfoDTO;
|
||||
import com.orion.ops.module.infra.entity.request.user.*;
|
||||
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
|
||||
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
|
||||
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.ops.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.ops.module.infra.service.AuthenticationService;
|
||||
import com.orion.ops.module.infra.service.FavoriteService;
|
||||
@@ -31,7 +35,6 @@ import com.orion.ops.module.infra.service.PreferenceService;
|
||||
import com.orion.ops.module.infra.service.SystemUserService;
|
||||
import com.orion.spring.SpringHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -68,9 +71,6 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
@Resource
|
||||
private PreferenceService preferenceService;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
@Override
|
||||
public Long createSystemUser(SystemUserCreateRequest request) {
|
||||
// 转换
|
||||
@@ -82,6 +82,8 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
// 插入
|
||||
int effect = systemUserDAO.insert(record);
|
||||
log.info("SystemUserService-createSystemUser effect: {}, record: {}", effect, JSON.toJSONString(record));
|
||||
// 删除用户列表缓存
|
||||
RedisUtils.delete(UserCacheKeyDefine.USER_LIST);
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@@ -104,6 +106,8 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
RedisStrings.<LoginUser>processSetJson(UserCacheKeyDefine.USER_INFO, s -> {
|
||||
s.setNickname(request.getNickname());
|
||||
}, id);
|
||||
// 删除用户列表缓存
|
||||
RedisUtils.delete(UserCacheKeyDefine.USER_LIST);
|
||||
return effect;
|
||||
}
|
||||
|
||||
@@ -131,12 +135,14 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
log.info("SystemUserService-updateUserStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||
// 如果之前是锁定则删除登录失败次数缓存
|
||||
if (UserStatusEnum.LOCKED.getStatus().equals(record.getStatus())) {
|
||||
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||
RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||
}
|
||||
// 更新缓存中的status
|
||||
// 更新用户缓存中的 status
|
||||
RedisStrings.<LoginUser>processSetJson(UserCacheKeyDefine.USER_INFO, s -> {
|
||||
s.setStatus(request.getStatus());
|
||||
}, id);
|
||||
// 删除用户列表缓存
|
||||
RedisUtils.delete(UserCacheKeyDefine.USER_LIST);
|
||||
return effect;
|
||||
}
|
||||
|
||||
@@ -150,14 +156,28 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemUserVO> getSystemUserByIdList() {
|
||||
// 查询
|
||||
List<SystemUserDO> records = systemUserDAO.selectList(null);
|
||||
if (records.isEmpty()) {
|
||||
return Lists.empty();
|
||||
public List<SystemUserVO> getSystemUserList() {
|
||||
// fixme test
|
||||
// 查询用户列表
|
||||
List<UserInfoDTO> list = RedisMaps.valuesJson(UserCacheKeyDefine.USER_LIST);
|
||||
if (list.isEmpty()) {
|
||||
// 查询数据库
|
||||
list = systemUserDAO.of().list(SystemUserConvert.MAPPER::toUserInfo);
|
||||
// 添加默认值 防止穿透
|
||||
if (list.isEmpty()) {
|
||||
list.add(UserInfoDTO.builder()
|
||||
.id(Const.NONE_ID)
|
||||
.build());
|
||||
}
|
||||
// 设置缓存
|
||||
RedisMaps.putAllJson(UserCacheKeyDefine.USER_LIST.getKey(), s -> s.getId().toString(), list);
|
||||
RedisMaps.setExpire(UserCacheKeyDefine.USER_LIST);
|
||||
}
|
||||
// 转换
|
||||
return SystemUserConvert.MAPPER.to(records);
|
||||
// 删除默认值
|
||||
return list.stream()
|
||||
.filter(s -> !s.getId().equals(Const.NONE_ID))
|
||||
.map(SystemUserConvert.MAPPER::to)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,21 +210,26 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
int effect = systemUserDAO.deleteById(id);
|
||||
log.info("SystemUserService-deleteSystemUserById id: {}, effect: {}", id, effect);
|
||||
// 异步删除额外信息
|
||||
SpringHolder.getBean(SystemUserService.class).deleteSystemUserRel(id);
|
||||
SpringHolder.getBean(SystemUserService.class).deleteSystemUserRel(id, record.getUsername());
|
||||
return effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async("asyncExecutor")
|
||||
public void deleteSystemUserRel(Long id) {
|
||||
public void deleteSystemUserRel(Long id, String username) {
|
||||
log.info("SystemUserService-deleteSystemUserRel id: {}", id);
|
||||
// 删除用户列表缓存
|
||||
// FIXME test
|
||||
RedisMaps.delete(UserCacheKeyDefine.USER_LIST, id);
|
||||
// 删除用户缓存 需要扫描的 key 让其自动过期
|
||||
redisTemplate.delete(Lists.of(
|
||||
RedisUtils.delete(
|
||||
// 用户缓存
|
||||
UserCacheKeyDefine.USER_INFO.format(id),
|
||||
// 登录失败次数
|
||||
UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(username),
|
||||
// 用户提示
|
||||
TipsCacheKeyDefine.TIPS.format(id)
|
||||
));
|
||||
);
|
||||
// 删除角色关联
|
||||
systemUserRoleDAO.deleteByUserId(id);
|
||||
// 删除操作日志
|
||||
@@ -230,19 +255,19 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
int effect = systemUserDAO.updateById(update);
|
||||
log.info("SystemUserService-resetPassword record: {}, effect: {}", JSON.toJSONString(update), effect);
|
||||
// 删除登录失败次数缓存
|
||||
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||
RedisUtils.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(record.getUsername()));
|
||||
// 删除登录缓存
|
||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
||||
Set<String> loginKeyList = RedisUtils.scanKeys(loginKey);
|
||||
if (!loginKeyList.isEmpty()) {
|
||||
redisTemplate.delete(loginKeyList);
|
||||
RedisUtils.delete(loginKeyList);
|
||||
}
|
||||
// 删除续签信息
|
||||
if (AuthenticationService.allowRefresh) {
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*");
|
||||
Set<String> refreshKeyList = RedisUtils.scanKeys(refreshKey);
|
||||
if (!refreshKeyList.isEmpty()) {
|
||||
redisTemplate.delete(refreshKeyList);
|
||||
RedisUtils.delete(refreshKeyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,22 +284,25 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
if (Lists.isEmpty(tokens)) {
|
||||
return Lists.empty();
|
||||
}
|
||||
final boolean isCurrentUser = userId.equals(SecurityUtils.getLoginUserId());
|
||||
// 返回
|
||||
return tokens.stream()
|
||||
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getStatus()))
|
||||
.map(LoginTokenDTO::getOrigin)
|
||||
.map(s -> UserSessionVO.builder()
|
||||
.current(s.getLoginTime().equals(SecurityUtils.getLoginTimestamp()))
|
||||
.current(isCurrentUser && s.getLoginTime().equals(SecurityUtils.getLoginTimestamp()))
|
||||
.address(s.getAddress())
|
||||
.location(s.getLocation())
|
||||
.userAgent(s.getUserAgent())
|
||||
.loginTime(new Date(s.getLoginTime()))
|
||||
.build())
|
||||
.sorted(Comparator.comparing(UserSessionVO::getLoginTime).reversed())
|
||||
.sorted(Comparator.comparing(UserSessionVO::getCurrent).reversed()
|
||||
.thenComparing(Comparator.comparing(UserSessionVO::getLoginTime).reversed()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offlineUserSession(OfflineUserSessionRequest request) {
|
||||
public void offlineUserSession(UserSessionOfflineRequest request) {
|
||||
Long userId = Valid.notNull(request.getUserId());
|
||||
Long timestamp = request.getTimestamp();
|
||||
RedisStrings.delete(
|
||||
|
||||
Reference in New Issue
Block a user