feat: 用户请求日志记录

This commit is contained in:
Bunny 2024-12-29 13:05:24 +08:00
parent cd35da82a0
commit 615f6323c5
14 changed files with 419 additions and 10 deletions

View File

@ -28,6 +28,11 @@
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<!-- mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>

View File

@ -0,0 +1,31 @@
package cn.bunny.dao.dto.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "UserLogin对象", title = "用户登录日志", description = "用户登录日志")
public class UserRequestLogDto {
@Schema(name = "url", title = "请求地址")
private String url;
@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 = "executeTime", title = "执行时间")
private String executeTime;
}

View File

@ -0,0 +1,63 @@
package cn.bunny.dao.entity.log;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;
/**
* <p>
* 用户请求日志
* </p>
*
* @author Bunny
* @since 2024-12-27
*/
@Getter
@Setter
@Document(collection = "logs_request")
@Schema(name = "UserLogin对象", title = "用户登录日志", description = "用户登录日志")
public class UserRequestLog {
@Id
@MongoId(targetType = FieldType.STRING)
private ObjectId id;
@Schema(name = "url", title = "请求地址")
private String url;
@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 = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
private String xRequestedWith;
@Schema(name = "arg", title = "传入参数")
private String arg;
@Schema(name = "result", title = "执行结果")
private String result;
@Schema(name = "executeTime", title = "执行时间")
private String executeTime;
}

View File

@ -0,0 +1,58 @@
package cn.bunny.dao.vo.log;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 用户请求日志
* </p>
*
* @author Bunny
* @since 2024-12-27
*/
@Getter
@Setter
@Schema(name = "UserRequestLogVo对象", title = "用户请求日志", description = "用户请求日志")
public class UserRequestLogVo {
@Schema(name = "id", title = "id")
private String id;
@Schema(name = "url", title = "请求地址")
private String url;
@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 = "xRequestedWith", title = "标识客户端是否是通过Ajax发送请求的")
@JsonProperty("xRequestedWith")
private String xRequestedWith;
@Schema(name = "arg", title = "传入参数")
private String arg;
@Schema(name = "result", title = "执行结果")
private String result;
@Schema(name = "executeTime", title = "执行时间")
private String executeTime;
}

View File

@ -37,11 +37,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- admin 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -24,7 +24,7 @@ import java.util.Map;
@Component
public class JobExecuteAop {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS);
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS);
@Autowired
private ScheduleExecuteLogMapper scheduleExecuteLogMapper;
@ -41,7 +41,7 @@ public class JobExecuteAop {
// 格式化时间
LocalDateTime startLocalDateTime = LocalDateTime.now();
String startExecuteTIme = startLocalDateTime.format(dateTimeFormatter);
String startExecuteTime = startLocalDateTime.format(dateTimeFormatter);
// 获取上下文map集合
Map<String, Object> jobDataMap = context.getJobDetail().getJobDataMap().getWrappedMap();
@ -62,7 +62,7 @@ public class JobExecuteAop {
executeLogJson.setResult(JobEnums.UNFINISHED.getType());
executeLogJson.setStatus(JobEnums.RUNNING.getType());
executeLogJson.setMessage(JobEnums.RUNNING.getSummary());
executeLogJson.setOperationTime(startExecuteTIme);
executeLogJson.setOperationTime(startExecuteTime);
executeLogJson.setExecuteParams(jobDataMap);
executeLog.setExecuteResult(JSON.toJSONString(executeLogJson));
scheduleExecuteLogMapper.insert(executeLog);

View File

@ -0,0 +1,84 @@
package cn.bunny.services.aop;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.utils.ip.IpUtil;
import cn.bunny.dao.entity.log.UserRequestLog;
import cn.bunny.dao.pojo.constant.LocalDateTimeConstant;
import cn.bunny.dao.vo.system.user.LoginVo;
import cn.bunny.services.repository.UserRequestLogRepository;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Aspect
@Slf4j
@Component
public class UserRequestLogAop {
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS);
@Autowired
private UserRequestLogRepository userRequestLogRepository;
@Around(value = "execution(* cn.bunny.services.controller.*.*.*(..)) && !@annotation(cn.bunny.services.aop.annotation.ExcludeRequestLog)")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
LoginVo loginVo = BaseContext.getLoginVo();
Object[] args = joinPoint.getArgs();
// 执行时间
LocalDateTime localDateTime = LocalDateTime.now();
String executeTime = localDateTime.format(dateTimeFormatter);
// 获取IP地址并更新用户登录信息
String ipAddr = IpUtil.getCurrentUserIpAddress().getIpAddr();
String ipRegion = IpUtil.getCurrentUserIpAddress().getIpRegion();
// 日期内容
UserRequestLog userRequestLog = new UserRequestLog();
userRequestLog.setUserId(BaseContext.getUserId());
userRequestLog.setUsername(BaseContext.getUsername());
userRequestLog.setToken(loginVo != null ? loginVo.getToken() : "");
userRequestLog.setIpAddress(ipAddr);
userRequestLog.setIpRegion(ipRegion);
userRequestLog.setExecuteTime(executeTime);
userRequestLog.setArg(JSON.toJSONString(args));
// 当前请求request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request;
if (requestAttributes != null) {
request = requestAttributes.getRequest();
// 获取请求URL
userRequestLog.setUrl(request.getRequestURI());
// 获取User-Agent
String userAgent = request.getHeader("User-Agent");
userRequestLog.setUserAgent(userAgent);
// 获取X-Requested-With
String xRequestedWith = request.getHeader("X-Requested-With");
userRequestLog.setXRequestedWith(xRequestedWith);
}
Object proceed = joinPoint.proceed();
// 执行结果
String result = JSON.toJSONString(proceed);
userRequestLog.setResult(result);
userRequestLogRepository.insert(userRequestLog);
return proceed;
}
}

View File

@ -0,0 +1,11 @@
package cn.bunny.services.aop.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ExcludeRequestLog {
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.controller;
package cn.bunny.services.controller.system;
import cn.bunny.dao.dto.financial.HomeDto;
import cn.bunny.dao.pojo.result.Result;

View File

@ -0,0 +1,56 @@
package cn.bunny.services.controller.system;
import cn.bunny.dao.dto.log.UserRequestLogDto;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.pojo.result.Result;
import cn.bunny.dao.vo.log.UserRequestLogVo;
import cn.bunny.services.aop.annotation.ExcludeRequestLog;
import cn.bunny.services.service.system.UserRequestLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* <p>
* 用户请求日志表 前端控制器
* </p>
*
* @author Bunny
* @since 2024-10-19 01:01:01
*/
@Tag(name = "用户登录日志", description = "用户登录日志相关接口")
@RestController
@RequestMapping("admin/userRequestLog")
public class UserRequestLogController {
@Autowired
private UserRequestLogService userRequestLogService;
@Operation(summary = "分页查询用户请求日志", description = "分页查询用户请求日志")
@ExcludeRequestLog
@GetMapping("getRequestLogList/{page}/{limit}")
public Mono<Result<PageResult<UserRequestLogVo>>> getRequestLogList(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit,
UserRequestLogDto dto) {
Pageable pageable = PageRequest.of(page, limit);
PageResult<UserRequestLogVo> pageResult = userRequestLogService.getRequestLogList(pageable, dto);
return Mono.just(Result.success(pageResult));
}
@Operation(summary = "删除用户请求日志", description = "删除用户请求日志")
@DeleteMapping("deletedRequestLogByIds")
public Mono<Result<String>> deletedRequestLogByIds(@RequestBody List<String> ids) {
userRequestLogService.deletedRequestLogByIds(ids);
return Mono.just(Result.success());
}
}

View File

@ -0,0 +1,9 @@
package cn.bunny.services.repository;
import cn.bunny.dao.entity.log.UserRequestLog;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRequestLogRepository extends MongoRepository<UserRequestLog, ObjectId> {
}

View File

@ -0,0 +1,27 @@
package cn.bunny.services.service.system;
import cn.bunny.dao.dto.log.UserRequestLogDto;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.vo.log.UserRequestLogVo;
import org.springframework.data.domain.Pageable;
import java.util.List;
public interface UserRequestLogService {
/**
* 分页查询用户请求日志
*
* @param pageable 分页
* @param dto 表单查询
* @return 分页结果
*/
PageResult<UserRequestLogVo> getRequestLogList(Pageable pageable, UserRequestLogDto dto);
/**
* 删除用户请求日志
*
* @param ids 删除的Id
*/
void deletedRequestLogByIds(List<String> ids);
}

View File

@ -0,0 +1,69 @@
package cn.bunny.services.service.system.impl;
import cn.bunny.dao.dto.log.UserRequestLogDto;
import cn.bunny.dao.entity.log.UserRequestLog;
import cn.bunny.dao.pojo.result.PageResult;
import cn.bunny.dao.vo.log.UserRequestLogVo;
import cn.bunny.services.repository.UserRequestLogRepository;
import cn.bunny.services.service.system.UserRequestLogService;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserRequestLogServiceImpl implements UserRequestLogService {
@Autowired
private UserRequestLogRepository userRequestLogRepository;
/**
* 分页查询用户请求日志
*
* @param pageable 分页
* @param dto 表单查询
* @return 分页结果
*/
@Override
public PageResult<UserRequestLogVo> getRequestLogList(Pageable pageable, UserRequestLogDto dto) {
// 封装条件
UserRequestLog userRequestLog = new UserRequestLog();
BeanUtils.copyProperties(dto, userRequestLog);
// 构建条件
Example<UserRequestLog> example = Example.of(userRequestLog);
Page<UserRequestLog> page = userRequestLogRepository.findAll(example, pageable);
List<UserRequestLogVo> content = page.getContent().stream().map(requestLog -> {
UserRequestLogVo vo = new UserRequestLogVo();
BeanUtils.copyProperties(requestLog, vo);
String hexString = requestLog.getId().toHexString();
vo.setId(hexString);
return vo;
}).toList();
return PageResult.<UserRequestLogVo>builder()
.list(content)
.pageNo((long) page.getNumber())
.pageSize((long) page.getSize())
.total(page.getTotalElements())
.build();
}
/**
* 删除用户请求日志
*
* @param ids 删除的Id
*/
@Override
public void deletedRequestLogByIds(List<String> ids) {
List<ObjectId> idList = ids.stream().map(ObjectId::new).toList();
userRequestLogRepository.deleteAllById(idList);
}
}

View File

@ -26,7 +26,8 @@ spring:
password: ${bunny.mongodb.password}
authentication-database: ${bunny.mongodb.authentication-database}
additional-hosts: ${bunny.mongodb.additional-hosts}
field-naming-strategy: org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy
redis:
host: ${bunny.redis.host}
port: ${bunny.redis.port}