diff --git a/dao/pom.xml b/dao/pom.xml index 4b9e9aa..60d43fd 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -28,6 +28,11 @@ com.baomidou mybatis-plus-spring-boot3-starter + + + org.springframework.boot + spring-boot-starter-data-mongodb + com.github.xiaoymin diff --git a/dao/src/main/java/cn/bunny/dao/dto/log/UserRequestLogDto.java b/dao/src/main/java/cn/bunny/dao/dto/log/UserRequestLogDto.java new file mode 100644 index 0000000..89ad2ca --- /dev/null +++ b/dao/src/main/java/cn/bunny/dao/dto/log/UserRequestLogDto.java @@ -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; + +} diff --git a/dao/src/main/java/cn/bunny/dao/entity/log/UserRequestLog.java b/dao/src/main/java/cn/bunny/dao/entity/log/UserRequestLog.java new file mode 100644 index 0000000..0f3c81b --- /dev/null +++ b/dao/src/main/java/cn/bunny/dao/entity/log/UserRequestLog.java @@ -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; + +/** + *

+ * 用户请求日志 + *

+ * + * @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; + +} diff --git a/dao/src/main/java/cn/bunny/dao/vo/log/UserRequestLogVo.java b/dao/src/main/java/cn/bunny/dao/vo/log/UserRequestLogVo.java new file mode 100644 index 0000000..2ef032c --- /dev/null +++ b/dao/src/main/java/cn/bunny/dao/vo/log/UserRequestLogVo.java @@ -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; + +/** + *

+ * 用户请求日志 + *

+ * + * @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; + +} diff --git a/service/pom.xml b/service/pom.xml index 6e7f9fb..2e3bc49 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -37,11 +37,6 @@ org.springframework.boot spring-boot-starter-web
- - - org.springframework.boot - spring-boot-starter-data-mongodb - org.springframework.boot diff --git a/service/src/main/java/cn/bunny/services/aop/JobExecuteAop.java b/service/src/main/java/cn/bunny/services/aop/JobExecuteAop.java index 65e9da3..30c94af 100644 --- a/service/src/main/java/cn/bunny/services/aop/JobExecuteAop.java +++ b/service/src/main/java/cn/bunny/services/aop/JobExecuteAop.java @@ -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 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); diff --git a/service/src/main/java/cn/bunny/services/aop/UserRequestLogAop.java b/service/src/main/java/cn/bunny/services/aop/UserRequestLogAop.java new file mode 100644 index 0000000..b2787e9 --- /dev/null +++ b/service/src/main/java/cn/bunny/services/aop/UserRequestLogAop.java @@ -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; + } +} diff --git a/service/src/main/java/cn/bunny/services/aop/annotation/ExcludeRequestLog.java b/service/src/main/java/cn/bunny/services/aop/annotation/ExcludeRequestLog.java new file mode 100644 index 0000000..573dd9f --- /dev/null +++ b/service/src/main/java/cn/bunny/services/aop/annotation/ExcludeRequestLog.java @@ -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 { +} diff --git a/service/src/main/java/cn/bunny/services/controller/IndexController.java b/service/src/main/java/cn/bunny/services/controller/system/IndexController.java similarity index 97% rename from service/src/main/java/cn/bunny/services/controller/IndexController.java rename to service/src/main/java/cn/bunny/services/controller/system/IndexController.java index 3efd415..4498ea5 100644 --- a/service/src/main/java/cn/bunny/services/controller/IndexController.java +++ b/service/src/main/java/cn/bunny/services/controller/system/IndexController.java @@ -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; diff --git a/service/src/main/java/cn/bunny/services/controller/system/UserRequestLogController.java b/service/src/main/java/cn/bunny/services/controller/system/UserRequestLogController.java new file mode 100644 index 0000000..0bb9b15 --- /dev/null +++ b/service/src/main/java/cn/bunny/services/controller/system/UserRequestLogController.java @@ -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; + +/** + *

+ * 用户请求日志表 前端控制器 + *

+ * + * @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>> 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 pageResult = userRequestLogService.getRequestLogList(pageable, dto); + return Mono.just(Result.success(pageResult)); + } + + @Operation(summary = "删除用户请求日志", description = "删除用户请求日志") + @DeleteMapping("deletedRequestLogByIds") + public Mono> deletedRequestLogByIds(@RequestBody List ids) { + userRequestLogService.deletedRequestLogByIds(ids); + return Mono.just(Result.success()); + } +} diff --git a/service/src/main/java/cn/bunny/services/repository/UserRequestLogRepository.java b/service/src/main/java/cn/bunny/services/repository/UserRequestLogRepository.java new file mode 100644 index 0000000..e9a7363 --- /dev/null +++ b/service/src/main/java/cn/bunny/services/repository/UserRequestLogRepository.java @@ -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 { + +} diff --git a/service/src/main/java/cn/bunny/services/service/system/UserRequestLogService.java b/service/src/main/java/cn/bunny/services/service/system/UserRequestLogService.java new file mode 100644 index 0000000..c831173 --- /dev/null +++ b/service/src/main/java/cn/bunny/services/service/system/UserRequestLogService.java @@ -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 getRequestLogList(Pageable pageable, UserRequestLogDto dto); + + /** + * 删除用户请求日志 + * + * @param ids 删除的Id + */ + void deletedRequestLogByIds(List ids); +} diff --git a/service/src/main/java/cn/bunny/services/service/system/impl/UserRequestLogServiceImpl.java b/service/src/main/java/cn/bunny/services/service/system/impl/UserRequestLogServiceImpl.java new file mode 100644 index 0000000..1689824 --- /dev/null +++ b/service/src/main/java/cn/bunny/services/service/system/impl/UserRequestLogServiceImpl.java @@ -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 getRequestLogList(Pageable pageable, UserRequestLogDto dto) { + // 封装条件 + UserRequestLog userRequestLog = new UserRequestLog(); + BeanUtils.copyProperties(dto, userRequestLog); + + // 构建条件 + Example example = Example.of(userRequestLog); + Page page = userRequestLogRepository.findAll(example, pageable); + List 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.builder() + .list(content) + .pageNo((long) page.getNumber()) + .pageSize((long) page.getSize()) + .total(page.getTotalElements()) + .build(); + } + + /** + * 删除用户请求日志 + * + * @param ids 删除的Id + */ + @Override + public void deletedRequestLogByIds(List ids) { + List idList = ids.stream().map(ObjectId::new).toList(); + userRequestLogRepository.deleteAllById(idList); + } +} + diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml index 10cd2b8..66625b2 100644 --- a/service/src/main/resources/application.yml +++ b/service/src/main/resources/application.yml @@ -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}