feat: 用户登录日志完成

This commit is contained in:
Bunny 2024-10-19 02:37:30 +08:00
parent 3e34dea4f5
commit 4462de2530
13 changed files with 266 additions and 97 deletions

View File

@ -3,7 +3,11 @@ package cn.bunny.common.generator.generator;
import cn.bunny.common.generator.entity.BaseField;
import cn.bunny.common.generator.entity.BaseResultMap;
import cn.bunny.common.generator.utils.GeneratorCodeUtils;
import cn.bunny.dao.dto.log.UserLoginLogAddDto;
import cn.bunny.dao.dto.log.UserLoginLogDto;
import cn.bunny.dao.dto.log.UserLoginLogUpdateDto;
import cn.bunny.dao.entity.log.UserLoginLog;
import cn.bunny.dao.vo.log.UserLoginLogVo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.base.CaseFormat;
import io.swagger.v3.oas.annotations.media.Schema;
@ -48,10 +52,10 @@ public class WebGeneratorCode {
public static void main(String[] args) throws Exception {
Class<?> originalClass = UserLoginLog.class;
Class<?> dtoClass = UserLoginLog.class;
Class<?> addDtoClass = UserLoginLog.class;
Class<?> updateDtoClass = UserLoginLog.class;
Class<?> voClass = UserLoginLog.class;
Class<?> dtoClass = UserLoginLogDto.class;
Class<?> addDtoClass = UserLoginLogAddDto.class;
Class<?> updateDtoClass = UserLoginLogUpdateDto.class;
Class<?> voClass = UserLoginLogVo.class;
// 设置velocity资源加载器
Properties prop = new Properties();

View File

@ -0,0 +1,86 @@
package cn.bunny.dao.dto.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "UserLoginLogDto对象", title = "用户登录日志分页查询", description = "用户登录日志分页查询")
public class UserLoginLogAddDto {
@Schema(name = "userId", title = "用户Id")
private Long userId;
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "token", title = "登录token")
private String token;
@Schema(name = "ipAddress", title = "登录Ip")
private String ipAddress;
@Schema(name = "ipRegion", title = "登录Ip归属地")
private String ipRegion;
@Schema(name = "userAgent", title = "登录时代理")
private String userAgent;
@Schema(name = "type", title = "操作类型")
private String type;
@Schema(name = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
private String xRequestedWith;
@Schema(name = "secChUa", title = "用户代理的品牌和版本")
private String secChUa;
@Schema(name = "secChUaArch", title = "用户代理的底层平台架构")
private String secChUaArch;
@Schema(name = "secChUaBitness", title = "用户代理的底层CPU架构位数")
private String secChUaBitness;
@Schema(name = "secChUaMobile", title = "用户代理是否在手机设备上运行")
private String secChUaMobile;
@Schema(name = "secChUaModel", title = "用户代理的设备模型")
private String secChUaModel;
@Schema(name = "secChUaPlatform", title = "用户代理的底层操作系统/平台")
private String secChUaPlatform;
@Schema(name = "secChUaPlatformVersion", title = "用户代理的底层操作系统版本")
private String secChUaPlatformVersion;
@Schema(name = "contentDpr", title = "客户端设备像素比")
private String contentDpr;
@Schema(name = "deviceMemory", title = "客户端RAM内存的近似值")
private String deviceMemory;
@Schema(name = "dpr", title = "客户端设备像素比")
private String dpr;
@Schema(name = "viewportWidth", title = "布局视口宽度")
private String viewportWidth;
@Schema(name = "width", title = "所需资源宽度")
private String width;
@Schema(name = "downlink", title = "客户端连接到服务器的近似带宽")
private String downlink;
@Schema(name = "ect", title = "有效连接类型")
private String ect;
@Schema(name = "rtt", title = "应用层往返时间")
private String rtt;
}

View File

@ -22,10 +22,10 @@ public class UserLoginLogDto {
@Schema(name = "token", title = "登录token")
private String token;
@Schema(name = "ipAddress", title = "登录Ip地点")
@Schema(name = "ipAddress", title = "登录Ip")
private String ipAddress;
@Schema(name = "ipRegion", title = "登录Ip")
@Schema(name = "ipRegion", title = "登录Ip归属地")
private String ipRegion;
@Schema(name = "userAgent", title = "登录时代理")
@ -34,5 +34,17 @@ public class UserLoginLogDto {
@Schema(name = "type", title = "操作类型")
private String type;
@Schema(name = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
private String xRequestedWith;
@Schema(name = "secChUa", title = "用户代理的品牌和版本")
private String secChUa;
@Schema(name = "secChUaMobile", title = "用户代理是否在手机设备上运行")
private String secChUaMobile;
@Schema(name = "secChUaPlatform", title = "用户代理的底层操作系统/平台")
private String secChUaPlatform;
}

View File

@ -22,10 +22,10 @@ public class UserLoginLogUpdateDto {
@Schema(name = "token", title = "登录token")
private String token;
@Schema(name = "ipAddress", title = "登录Ip地点")
@Schema(name = "ipAddress", title = "登录Ip")
private String ipAddress;
@Schema(name = "ipRegion", title = "登录Ip")
@Schema(name = "ipRegion", title = "登录Ip归属地")
private String ipRegion;
@Schema(name = "userAgent", title = "登录时代理")
@ -34,4 +34,16 @@ public class UserLoginLogUpdateDto {
@Schema(name = "type", title = "操作类型")
private String type;
@Schema(name = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
private String xRequestedWith;
@Schema(name = "secChUa", title = "用户代理的品牌和版本")
private String secChUa;
@Schema(name = "secChUaMobile", title = "用户代理是否在手机设备上运行")
private String secChUaMobile;
@Schema(name = "secChUaPlatform", title = "用户代理的底层操作系统/平台")
private String secChUaPlatform;
}

View File

@ -13,7 +13,7 @@ import lombok.experimental.Accessors;
* </p>
*
* @author Bunny
* @since 2024-10-18
* @since 2024-10-19
*/
@Getter
@Setter
@ -43,4 +43,16 @@ public class UserLoginLog extends BaseEntity {
@Schema(name = "type", title = "操作类型")
private String type;
@Schema(name = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
private String xRequestedWith;
@Schema(name = "secChUa", title = "用户代理的品牌和版本")
private String secChUa;
@Schema(name = "secChUaMobile", title = "用户代理是否在手机设备上运行")
private String secChUaMobile;
@Schema(name = "secChUaPlatform", title = "用户代理的底层操作系统/平台")
private String secChUaPlatform;
}

View File

@ -1,6 +1,7 @@
package cn.bunny.dao.vo.log;
import cn.bunny.dao.vo.common.BaseVo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
@ -21,10 +22,10 @@ public class UserLoginLogVo extends BaseVo {
@Schema(name = "token", title = "登录token")
private String token;
@Schema(name = "ipAddress", title = "登录Ip地点")
@Schema(name = "ipAddress", title = "登录Ip")
private String ipAddress;
@Schema(name = "ipRegion", title = "登录Ip")
@Schema(name = "ipRegion", title = "登录Ip归属地")
private String ipRegion;
@Schema(name = "userAgent", title = "登录时代理")
@ -33,4 +34,17 @@ public class UserLoginLogVo extends BaseVo {
@Schema(name = "type", title = "操作类型")
private String type;
@Schema(name = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
@JsonProperty("xRequestedWith")
private String xRequestedWith;
@Schema(name = "secChUa", title = "用户代理的品牌和版本")
private String secChUa;
@Schema(name = "secChUaMobile", title = "用户代理是否在手机设备上运行")
private String secChUaMobile;
@Schema(name = "secChUaPlatform", title = "用户代理的底层操作系统/平台")
private String secChUaPlatform;
}

View File

@ -1,7 +1,6 @@
package cn.bunny.services.controller;
import cn.bunny.dao.dto.log.UserLoginLogDto;
import cn.bunny.dao.dto.log.UserLoginLogUpdateDto;
import cn.bunny.dao.entity.log.UserLoginLog;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.Result;
@ -12,7 +11,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
@ -25,7 +23,7 @@ import java.util.List;
* </p>
*
* @author Bunny
* @since 2024-10-18 22:36:07
* @since 2024-10-19 01:01:01
*/
@Tag(name = "用户登录日志", description = "用户登录日志相关接口")
@RestController
@ -48,13 +46,6 @@ public class UserLoginLogController {
return Mono.just(Result.success(pageResult));
}
@Operation(summary = "更新用户登录日志", description = "更新用户登录日志")
@PutMapping("updateUserLoginLog")
public Mono<Result<String>> updateUserLoginLog(@Valid @RequestBody UserLoginLogUpdateDto dto) {
userLoginLogService.updateUserLoginLog(dto);
return Mono.just(Result.success(ResultCodeEnum.UPDATE_SUCCESS));
}
@Operation(summary = "删除用户登录日志", description = "删除用户登录日志")
@DeleteMapping("deleteUserLoginLog")
public Mono<Result<String>> deleteUserLoginLog(@RequestBody List<Long> ids) {

View File

@ -53,37 +53,53 @@ public class UserFactory {
@Autowired
private MinioUtil minioUtil;
public LoginVo buildUserVo(AdminUser user, long readMeDay) {
// 创建token
Long userId = user.getId();
String email = user.getEmail();
String token = JwtHelper.createToken(userId, email, (int) readMeDay);
String avatar = user.getAvatar();
// 获取IP地址
String ipAddr = IpUtil.getCurrentUserIpAddress().getIpAddr();
String ipRegion = IpUtil.getCurrentUserIpAddress().getIpRegion();
// 当前请求request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// 更新用户登录信息
setUpdateUser(userId, ipAddr, ipRegion);
// 设置用户返回信息
LoginVo loginVo = setLoginVo(user, token, readMeDay, ipAddr, ipRegion);
// 将用户登录保存在用户登录日志表中
UserLoginLog userLoginLog = new UserLoginLog();
userLoginLog.setUsername(user.getUsername());
userLoginLog.setUserId(userId);
userLoginLog.setIpAddress(ipAddr);
userLoginLog.setIpRegion(ipRegion);
userLoginLog.setToken(token);
userLoginLog.setType("login");
setUserLoginLog(userLoginLog);
userLoginLogMapper.insert(userLoginLog);
// 将信息保存在Redis中
redisTemplate.opsForValue().set(RedisUserConstant.getAdminLoginInfoPrefix(email), loginVo, readMeDay, TimeUnit.DAYS);
// 将Redis中验证码删除
redisTemplate.delete(RedisUserConstant.getAdminUserEmailCodePrefix(email));
return loginVo;
}
/**
* * 设置更新用户设置内容
*/
public LoginVo setLoginVo(AdminUser user, String token, long readMeDay, String ipAddr, String ipRegion) {
Long userId = user.getId();
// 判断用户是否有头像如果没有头像设置默认头像
String avatar = user.getAvatar();
avatar = StringUtils.hasText(avatar) ? minioUtil.getObjectNameFullPath(avatar) : UserConstant.USER_AVATAR;
// 设置用户IP地址并更新用户信息
AdminUser updateUser = new AdminUser();
updateUser.setId(userId);
updateUser.setLastLoginIp(ipAddr);
updateUser.setLastLoginIpAddress(ipRegion);
userMapper.updateById(updateUser);
// 计算过期时间并格式化返回
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime plusDay = localDateTime.plusDays(readMeDay);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS_SLASH);
String expires = plusDay.format(dateTimeFormatter);
// 查找用户橘色
List<String> roles = new ArrayList<>(roleMapper.selectListByUserId(userId).stream().map(Role::getRoleCode).toList());
List<String> permissions = new ArrayList<>();
@ -94,6 +110,12 @@ public class UserFactory {
permissions = powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).toList();
}
// 计算过期时间并格式化返回
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime plusDay = localDateTime.plusDays(readMeDay);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS_SLASH);
String expires = plusDay.format(dateTimeFormatter);
// 构建返回对象设置用户需要内容
LoginVo loginVo = new LoginVo();
BeanUtils.copyProperties(user, loginVo);
@ -108,27 +130,53 @@ public class UserFactory {
loginVo.setUpdateUser(userId);
loginVo.setExpires(expires);
// 将用户登录保存在用户登录日志表中
UserLoginLog userLoginLog = new UserLoginLog();
BeanUtils.copyProperties(user, userLoginLog);
userLoginLog.setUserId(userId);
userLoginLog.setIpAddress(ipAddr);
userLoginLog.setIpRegion(ipRegion);
userLoginLog.setToken(token);
userLoginLog.setType("login");
if (requestAttributes != null) {
HttpServletRequest request = requestAttributes.getRequest();
String userAgent = request.getHeader("User-Agent");
userLoginLog.setUserAgent(userAgent);
}
userLoginLogMapper.insert(userLoginLog);
// 将信息保存在Redis中
redisTemplate.opsForValue().set(RedisUserConstant.getAdminLoginInfoPrefix(email), loginVo, readMeDay, TimeUnit.DAYS);
// 将Redis中验证码删除
redisTemplate.delete(RedisUserConstant.getAdminUserEmailCodePrefix(email));
return loginVo;
}
/**
* * 设置更新用户设置内容
*
* @param userId 用户ID
*/
public void setUpdateUser(Long userId, String ipAddr, String ipRegion) {
// 设置用户IP地址并更新用户信息
AdminUser updateUser = new AdminUser();
updateUser.setId(userId);
updateUser.setLastLoginIp(ipAddr);
updateUser.setLastLoginIpAddress(ipRegion);
userMapper.updateById(updateUser);
}
/**
* * 设置用户登录日志内容
*
* @param userLoginLog 用户登录日志
*/
public void setUserLoginLog(UserLoginLog userLoginLog) {
// 当前请求request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) return;
HttpServletRequest request = requestAttributes.getRequest();
// 获取User-Agent
String userAgent = request.getHeader("User-Agent");
userLoginLog.setUserAgent(userAgent);
// 获取X-Requested-With
String xRequestedWith = request.getHeader("X-Requested-With");
userLoginLog.setXRequestedWith(xRequestedWith);
// 获取Sec-CH-UA
String secCHUA = request.getHeader("sec-ch-ua");
userLoginLog.setSecChUa(secCHUA);
// 获取Sec-CH-UA-Mobile
String secCHUAMobile = request.getHeader("Sec-CH-UA-Mobile");
userLoginLog.setSecChUaMobile(secCHUAMobile);
// 获取Sec-CH-UA-Platform
String secCHUAPlatform = request.getHeader("Sec-CH-UA-Platform");
userLoginLog.setSecChUaPlatform(secCHUAPlatform);
}
}

View File

@ -16,7 +16,7 @@ import java.util.List;
* </p>
*
* @author Bunny
* @since 2024-10-18 22:36:07
* @since 2024-10-19 01:01:01
*/
@Mapper
public interface UserLoginLogMapper extends BaseMapper<UserLoginLog> {

View File

@ -1,13 +1,11 @@
package cn.bunny.services.service;
import cn.bunny.dao.dto.log.UserLoginLogDto;
import cn.bunny.dao.dto.log.UserLoginLogUpdateDto;
import cn.bunny.dao.entity.log.UserLoginLog;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.vo.log.UserLoginLogVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.Valid;
import java.util.List;
@ -17,7 +15,7 @@ import java.util.List;
* </p>
*
* @author Bunny
* @since 2024-10-18 22:36:07
* @since 2024-10-19 01:01:01
*/
public interface UserLoginLogService extends IService<UserLoginLog> {
@ -28,13 +26,6 @@ public interface UserLoginLogService extends IService<UserLoginLog> {
*/
PageResult<UserLoginLogVo> getUserLoginLogList(Page<UserLoginLog> pageParams, UserLoginLogDto dto);
/**
* * 更新用户登录日志
*
* @param dto 更新表单
*/
void updateUserLoginLog(@Valid UserLoginLogUpdateDto dto);
/**
* * 删除|批量删除用户登录日志类型
*

View File

@ -1,7 +1,6 @@
package cn.bunny.services.service.impl;
import cn.bunny.dao.dto.log.UserLoginLogDto;
import cn.bunny.dao.dto.log.UserLoginLogUpdateDto;
import cn.bunny.dao.entity.log.UserLoginLog;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.vo.log.UserLoginLogVo;
@ -10,7 +9,6 @@ import cn.bunny.services.service.UserLoginLogService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.validation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@ -22,7 +20,7 @@ import java.util.List;
* </p>
*
* @author Bunny
* @since 2024-10-18 22:36:07
* @since 2024-10-19 01:01:01
*/
@Service
public class UserLoginLogServiceImpl extends ServiceImpl<UserLoginLogMapper, UserLoginLog> implements UserLoginLogService {
@ -53,19 +51,6 @@ public class UserLoginLogServiceImpl extends ServiceImpl<UserLoginLogMapper, Use
.build();
}
/**
* 更新用户登录日志
*
* @param dto 用户登录日志更新
*/
@Override
public void updateUserLoginLog(@Valid UserLoginLogUpdateDto dto) {
// 更新内容
UserLoginLog userLoginLog = new UserLoginLog();
BeanUtils.copyProperties(dto, userLoginLog);
updateById(userLoginLog);
}
/**
* 删除|批量删除用户登录日志
*

View File

@ -41,8 +41,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
@ -211,9 +209,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
*/
@Override
public void forcedOffline(Long id) {
// 当前请求request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (id == null) throw new BunnyException(ResultCodeEnum.REQUEST_IS_EMPTY);
// 根据id查询用户登录前缀

View File

@ -13,14 +13,19 @@
<id column="user_id" property="userId"/>
<id column="username" property="username"/>
<id column="token" property="token"/>
<id column="ip_region" property="ipRegion"/>
<id column="ip_address" property="ipAddress"/>
<id column="ip_region" property="ipRegion"/>
<id column="user_agent" property="userAgent"/>
<id column="type" property="type"/>
<id column="x_requested_with" property="xRequestedWith"/>
<id column="sec_ch_ua" property="secChUa"/>
<id column="sec_ch_ua_mobile" property="secChUaMobile"/>
<id column="sec_ch_ua_platform" property="secChUaPlatform"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, create_time, update_time, create_user, update_user, is_deleted, user_id, username, token, ip_region, ip_address, user_agent
id, create_time, update_time, create_user, update_user, is_deleted, user_id, username, token, ip_address, ip_region, user_agent, type, x_requested_with, sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform
</sql>
<!-- 分页查询用户登录日志内容 -->
@ -38,17 +43,31 @@
<if test="dto.token != null and dto.token != ''">
and token like CONCAT('%',#{dto.token},'%')
</if>
<if test="dto.ipRegion != null and dto.ipRegion != ''">
and ip_region like CONCAT('%',#{dto.ipRegion},'%')
</if>
<if test="dto.ipAddress != null and dto.ipAddress != ''">
and ip_address like CONCAT('%',#{dto.ipAddress},'%')
</if>
<if test="dto.ipRegion != null and dto.ipRegion != ''">
and ip_region like CONCAT('%',#{dto.ipRegion},'%')
</if>
<if test="dto.userAgent != null and dto.userAgent != ''">
and user_agent like CONCAT('%',#{dto.userAgent},'%')
</if>
<if test="dto.type != null and dto.type != ''">
and type like CONCAT('%',#{dto.type},'%')
</if>
<if test="dto.xRequestedWith != null and dto.xRequestedWith != ''">
and x_requested_with like CONCAT('%',#{dto.xRequestedWith},'%')
</if>
<if test="dto.secChUa != null and dto.secChUa != ''">
and sec_ch_ua like CONCAT('%',#{dto.secChUa},'%')
</if>
<if test="dto.secChUaMobile != null and dto.secChUaMobile != ''">
and sec_ch_ua_mobile like CONCAT('%',#{dto.secChUaMobile},'%')
</if>
<if test="dto.secChUaPlatform != null and dto.secChUaPlatform != ''">
and sec_ch_ua_platform like CONCAT('%',#{dto.secChUaPlatform},'%')
</if>
</where>
order by update_time desc
</select>
<!-- 物理删除用户登录日志 -->