ref 脚手架同步为最新版本

This commit is contained in:
bootx
2024-10-04 21:55:03 +08:00
parent 8c2d860326
commit 380757016c
1516 changed files with 10762 additions and 65322 deletions

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.bootx.platform</groupId>
<artifactId>bootx-platform-starter</artifactId>
<version>3.0.0.beta1</version>
</parent>
<artifactId>starter-audit-log</artifactId>
<description>审计日志模块</description>
<dependencies>
<!-- web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 切面 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 认证模块 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>starter-auth</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- 请求头信息获取 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>common-header-holder</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- json库 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>common-jackson</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- 数据访问 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>common-mybatis-plus</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- ip库 -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${ip2region.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,21 @@
package cn.bootx.platform.starter.audit.log;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.ComponentScan;
/**
* 审计模块
*
* @author xxm
* @since 2021/11/8
*/
@ComponentScan
@ConfigurationPropertiesScan
@AutoConfiguration
@MapperScan(annotationClass = Mapper.class)
public class AuditLogAutoConfiguration {
}

View File

@@ -0,0 +1,54 @@
package cn.bootx.platform.starter.audit.log.controller;
import cn.bootx.platform.core.annotation.RequestGroup;
import cn.bootx.platform.core.annotation.RequestPath;
import cn.bootx.platform.core.rest.Res;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.core.rest.result.Result;
import cn.bootx.platform.starter.audit.log.result.LoginLogResult;
import cn.bootx.platform.starter.audit.log.param.LoginLogParam;
import cn.bootx.platform.starter.audit.log.service.log.LoginLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xxm
* @since 2021/9/7
*/
@Tag(name = "登录日志")
@RestController
@RequestMapping("/log/login")
@RequestGroup(groupCode = "loginLog", groupName = "登录日志", moduleCode = "starter", moduleName = "starter模块")
@RequiredArgsConstructor
public class LoginLogController {
private final LoginLogService loginLogService;
@RequestPath("登录日志分页")
@Operation(summary = "分页")
@GetMapping("/page")
public Result<PageResult<LoginLogResult>> page(PageParam pageParam, LoginLogParam loginLogParam) {
return Res.ok(loginLogService.page(pageParam, loginLogParam));
}
@RequestPath("获取登录日志")
@Operation(summary = "获取")
@GetMapping("/findById")
public Result<LoginLogResult> findById(Long id) {
return Res.ok(loginLogService.findById(id));
}
@RequestPath("清除指定天数之前的登录日志")
@Operation(summary = "清除指定天数之前的日志")
@PostMapping("/deleteByDay")
public Result<Void> deleteByDay(int deleteDay){
loginLogService.deleteByDay(deleteDay);
return Res.ok();
}
}

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.starter.audit.log.controller;
import cn.bootx.platform.core.annotation.RequestGroup;
import cn.bootx.platform.core.annotation.RequestPath;
import cn.bootx.platform.core.rest.Res;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.core.rest.result.Result;
import cn.bootx.platform.starter.audit.log.result.OperateLogResult;
import cn.bootx.platform.starter.audit.log.param.OperateLogParam;
import cn.bootx.platform.starter.audit.log.service.log.OperateLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 操作日志
*
* @author xxm
* @since 2021/9/8
*/
@Tag(name = "操作日志")
@RestController
@RequestMapping("/log/operate")
@RequestGroup(groupCode = "operateLog", groupName = "操作日志", moduleCode = "starter", moduleName = "starter模块")
@RequiredArgsConstructor
public class OperateLogController {
private final OperateLogService operateLogService;
@RequestPath("操作日志分页")
@Operation(summary = "分页")
@GetMapping("/page")
public Result<PageResult<OperateLogResult>> page(PageParam pageParam, OperateLogParam operateLogParam) {
return Res.ok(operateLogService.page(pageParam, operateLogParam));
}
@RequestPath("获取日志分页")
@Operation(summary = "获取")
@GetMapping("/findById")
public Result<OperateLogResult> findById(Long id) {
return Res.ok(operateLogService.findById(id));
}
@RequestPath("清除指定天数的操作日志")
@Operation(summary = "清除指定天数的日志")
@PostMapping("/deleteByDay")
public Result<Void> deleteByDay(Integer type){
operateLogService.deleteByDay(type);
return Res.ok();
}
}

View File

@@ -0,0 +1,31 @@
package cn.bootx.platform.starter.audit.log.convert;
import cn.bootx.platform.starter.audit.log.entity.LoginLogDb;
import cn.bootx.platform.starter.audit.log.entity.OperateLogDb;
import cn.bootx.platform.starter.audit.log.result.LoginLogResult;
import cn.bootx.platform.starter.audit.log.result.OperateLogResult;
import cn.bootx.platform.starter.audit.log.param.LoginLogParam;
import cn.bootx.platform.starter.audit.log.param.OperateLogParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 日志转换
*
* @author xxm
* @since 2021/8/12
*/
@Mapper
public interface LogConvert {
LogConvert CONVERT = Mappers.getMapper(LogConvert.class);
OperateLogResult convert(OperateLogDb in);
LoginLogResult convert(LoginLogDb in);
OperateLogDb convert(OperateLogParam in);
LoginLogDb convert(LoginLogParam in);
}

View File

@@ -0,0 +1,42 @@
package cn.bootx.platform.starter.audit.log.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.starter.audit.log.entity.LoginLogDb;
import cn.bootx.platform.starter.audit.log.param.LoginLogParam;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
/**
* 登录日志
*
* @author xxm
* @since 2021/8/12
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class LoginLogDbManager extends BaseManager<LoginLogDbMapper, LoginLogDb> {
public Page<LoginLogDb> page(PageParam pageParam, LoginLogParam loginLogParam) {
Page<LoginLogDb> mpPage = MpUtil.getMpPage(pageParam);
return lambdaQuery().orderByDesc(LoginLogDb::getId)
.like(StrUtil.isNotBlank(loginLogParam.getAccount()), LoginLogDb::getAccount, loginLogParam.getAccount())
.like(StrUtil.isNotBlank(loginLogParam.getClient()), LoginLogDb::getClient, loginLogParam.getClient())
.like(StrUtil.isNotBlank(loginLogParam.getLoginType()), LoginLogDb::getLoginType,
loginLogParam.getLoginType())
.page(mpPage);
}
public void deleteByOffset(LocalDateTime offset) {
lambdaUpdate()
.le(LoginLogDb::getLoginTime, offset)
.remove();
}
}

View File

@@ -0,0 +1,16 @@
package cn.bootx.platform.starter.audit.log.dao;
import cn.bootx.platform.starter.audit.log.entity.LoginLogDb;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 登录日志
*
* @author xxm
* @since 2021/8/12
*/
@Mapper
public interface LoginLogDbMapper extends MPJBaseMapper<LoginLogDb> {
}

View File

@@ -0,0 +1,50 @@
package cn.bootx.platform.starter.audit.log.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.starter.audit.log.entity.OperateLogDb;
import cn.bootx.platform.starter.audit.log.param.OperateLogParam;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 操作日志
*
* @author xxm
* @since 2021/8/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OperateLogDbManager extends BaseManager<OperateLogDbMapper, OperateLogDb> {
public Page<OperateLogDb> page(PageParam pageParam, OperateLogParam operateLogParam) {
Page<OperateLogDb> mpPage = MpUtil.getMpPage(pageParam);
return lambdaQuery()
.like(StrUtil.isNotBlank(operateLogParam.getAccount()), OperateLogDb::getAccount,
operateLogParam.getAccount())
.like(StrUtil.isNotBlank(operateLogParam.getTitle()), OperateLogDb::getTitle, operateLogParam.getTitle())
.eq(Objects.nonNull(operateLogParam.getBusinessType()), OperateLogDb::getBusinessType,
operateLogParam.getBusinessType())
.orderByDesc(OperateLogDb::getOperateTime)
.page(mpPage);
}
/**
* 删除 小于指定日期的日志
*/
public void deleteByOffset(LocalDateTime offset){
lambdaUpdate()
.le(OperateLogDb::getOperateTime, offset)
.remove();
}
}

View File

@@ -0,0 +1,10 @@
package cn.bootx.platform.starter.audit.log.dao;
import cn.bootx.platform.starter.audit.log.entity.OperateLogDb;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OperateLogDbMapper extends MPJBaseMapper<OperateLogDb> {
}

View File

@@ -0,0 +1,64 @@
package cn.bootx.platform.starter.audit.log.entity;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import cn.bootx.platform.starter.audit.log.convert.LogConvert;
import cn.bootx.platform.starter.audit.log.result.LoginLogResult;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 登录日志
*
* @author xxm
* @since 2021/8/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("starter_audit_login_log")
public class LoginLogDb extends MpIdEntity implements ToResult<LoginLogResult> {
/** 用户账号id */
private Long userId;
/** 用户名称 */
private String account;
/** 登录成功状态 */
private Boolean login;
/** 登录终端 */
private String client;
/** 登录方式 */
private String loginType;
/** 登录IP地址 */
private String ip;
/** 登录地点 */
private String loginLocation;
/** 浏览器类型 */
private String browser;
/** 操作系统 */
private String os;
/** 提示消息 */
private String msg;
/** 访问时间 */
private LocalDateTime loginTime;
@Override
public LoginLogResult toResult() {
return LogConvert.CONVERT.convert(this);
}
}

View File

@@ -0,0 +1,73 @@
package cn.bootx.platform.starter.audit.log.entity;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import cn.bootx.platform.starter.audit.log.convert.LogConvert;
import cn.bootx.platform.starter.audit.log.result.OperateLogResult;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 操作日志
*
* @author xxm
* @since 2021/8/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("starter_audit_operate_log")
public class OperateLogDb extends MpIdEntity implements ToResult<OperateLogResult> {
/** 操作模块 */
private String title;
/** 操作人员id */
private Long operateId;
/** 操作人员账号 */
private String account;
/** 业务类型 */
private String businessType;
/** 请求方法 */
private String method;
/** 请求方式 */
private String requestMethod;
/** 请求url */
private String operateUrl;
/** 操作ip */
private String operateIp;
/** 操作地点 */
private String operateLocation;
/** 请求参数 */
private String operateParam;
/** 返回参数 */
private String operateReturn;
/** 操作状态0正常 1异常 */
private Boolean success;
/** 错误消息 */
private String errorMsg;
/** 操作时间 */
private LocalDateTime operateTime;
@Override
public OperateLogResult toResult() {
return LogConvert.CONVERT.convert(this);
}
}

View File

@@ -0,0 +1,142 @@
package cn.bootx.platform.starter.audit.log.handler;
import cn.bootx.platform.common.headerholder.HeaderHolder;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.common.spring.util.AopUtil;
import cn.bootx.platform.common.spring.util.WebServletUtil;
import cn.bootx.platform.core.annotation.OperateLog;
import cn.bootx.platform.core.annotation.OperateLogs;
import cn.bootx.platform.core.code.ServletCode;
import cn.bootx.platform.core.entity.UserDetail;
import cn.bootx.platform.starter.audit.log.param.OperateLogParam;
import cn.bootx.platform.starter.audit.log.service.ip2region.IpToRegionService;
import cn.bootx.platform.starter.audit.log.service.log.OperateLogService;
import cn.bootx.platform.starter.auth.util.SecurityUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
/**
* 操作日志切面处理
*
* @author xxm
* @since 2021/8/13
*/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class OperateLogAspectHandler {
private final OperateLogService operateLogService;
private final IpToRegionService ipToRegionService;
/**
* 配置织入点
*/
@Pointcut("@annotation(cn.bootx.platform.core.annotation.OperateLog) || @annotation(cn.bootx.platform.core.annotation.OperateLogs)")
public void logPointCut() {
}
/**
* 处理完请求后执行
*/
@AfterReturning(pointcut = "logPointCut()", returning = "o")
public void doAfterReturning(JoinPoint joinPoint, Object o) {
SpringUtil.getBean(this.getClass()).handleLog(joinPoint, null, o);
}
/**
* 拦截异常操作
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
SpringUtil.getBean(this.getClass()).handleLog(joinPoint, e, null);
}
/**
* 操作log处理
*/
@Async
public void handleLog(JoinPoint joinPoint, Exception e, Object o) {
List<OperateLog> logs = getMethodAnnotation(joinPoint);
if (CollUtil.isEmpty(logs)) {
return;
}
// ip信息
String ip = "未知";
String location = "未知";
Optional<String> ipOpt = Optional.ofNullable(WebServletUtil.getRequest()).map(JakartaServletUtil::getClientIP);
if (ipOpt.isPresent()){
ip = ipOpt.get();
location = ipToRegionService.getRegionStrByIp(ip);
}
Optional<UserDetail> currentUser = SecurityUtil.getCurrentUser();
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
for (OperateLog log : logs) {
OperateLogParam operateLog = new OperateLogParam().setTitle(log.title())
.setOperateId(currentUser.map(UserDetail::getId).orElse(DesensitizedUtil.userId()))
.setAccount(currentUser.map(UserDetail::getAccount).orElse("未知"))
.setBusinessType(log.businessType().name().toLowerCase(Locale.ROOT))
.setOperateUrl(HeaderHolder.getHeader(ServletCode.REQUEST_URI))
.setMethod(className + "#" + methodName)
.setRequestMethod(HeaderHolder.getHeader(ServletCode.METHOD))
.setSuccess(true)
.setOperateIp(ip)
.setOperateLocation(location)
.setOperateTime(LocalDateTime.now());
// 异常流
if (Objects.nonNull(e)) {
operateLog.setSuccess(false).setErrorMsg(e.getMessage());
}
// 参数
if (log.saveParam()) {
Object[] args = joinPoint.getArgs();
operateLog.setOperateParam(JacksonUtil.toJson(args));
}
// 返回值
if (log.saverReturn()) {
operateLog.setOperateReturn(JacksonUtil.toJson(o));
}
operateLogService.add(operateLog);
}
}
/**
* 获取注解
*/
private List<OperateLog> getMethodAnnotation(JoinPoint joinPoint) {
List<OperateLog> operateLogs = Optional.ofNullable(AopUtil.getMethodAnnotation(joinPoint, OperateLogs.class))
.map(OperateLogs::value)
.map(ListUtil::of)
.orElse(null);
if (CollUtil.isEmpty(operateLogs)) {
operateLogs = ListUtil.of(AopUtil.getMethodAnnotation(joinPoint, OperateLog.class));
}
return operateLogs;
}
}

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.starter.audit.log.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 登录日志
*
* @author xxm
* @since 2021/8/12
*/
@Data
@Accessors(chain = true)
@Schema(description = "登录日志")
public class LoginLogParam {
@Schema(description = "用户账号id")
private Long userId;
@Schema(description = "用户名称")
private String account;
@Schema(description = "登录成功状态")
private Boolean login;
@Schema(description = "登录终端")
private String client;
@Schema(description = "登录方式")
private String loginType;
@Schema(description = "登录IP地址")
private String ip;
@Schema(description = "登录地点")
private String loginLocation;
@Schema(description = "浏览器类型")
private String browser;
@Schema(description = "操作系统")
private String os;
@Schema(description = "提示消息")
private String msg;
@Schema(description = "访问时间")
private LocalDateTime loginTime;
}

View File

@@ -0,0 +1,62 @@
package cn.bootx.platform.starter.audit.log.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 操作日志
*
* @author xxm
* @since 2021/8/12
*/
@Data
@Accessors(chain = true)
@Schema(description = "操作日志")
public class OperateLogParam {
@Schema(description = "操作模块")
private String title;
@Schema(description = "操作人员id")
private Long operateId;
@Schema(description = "操作人员账号")
private String account;
@Schema(description = "业务类型")
private String businessType;
@Schema(description = "请求方法")
private String method;
@Schema(description = "请求方式")
private String requestMethod;
@Schema(description = "请求url")
private String operateUrl;
@Schema(description = "操作ip")
private String operateIp;
@Schema(description = "操作地点")
private String operateLocation;
@Schema(description = "请求参数")
private String operateParam;
@Schema(description = "返回参数")
private String operateReturn;
@Schema(description = "操作状态0正常 1异常")
private Boolean success;
@Schema(description = "错误消息")
private String errorMsg;
@Schema(description = "操作时间")
private LocalDateTime operateTime;
}

View File

@@ -0,0 +1,63 @@
package cn.bootx.platform.starter.audit.log.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 审计日志配置
*
* @author xxm
* @since 2021/12/2
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "bootx-platform.starter.audit-log")
public class AuditLogProperties {
/**
* ip地址库配置
*/
private Ip2region ip2region = new Ip2region();
/**
* 存储方式, 默认为数据库
*/
private Store store = Store.JDBC;
/**
* Ip地址库配置
*/
@Getter
@Setter
public static class Ip2region{
/** ip2region.xdb 数据文件所在路径 */
private String filePath;
/** 查询模式, 默认为缓存 VectorIndex 索引 */
private Ip2regionSearch searchType = Ip2regionSearch.VECTOR_INDEX;
}
/**
* 存储类型
*/
public enum Store {
/** 数据库 */
JDBC,
/** MongoDB */
MONGO
}
/**
* Ip2region查询类型
*/
public enum Ip2regionSearch {
/** 完全基于文件的查询(不推荐) */
FILE,
/** 缓存 VectorIndex 索引 */
VECTOR_INDEX,
/** 缓存整个 xdb 数据 */
CACHE
}
}

View File

@@ -0,0 +1,56 @@
package cn.bootx.platform.starter.audit.log.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 登录日志
*
* @author xxm
* @since 2021/8/12
*/
@Data
@Accessors(chain = true)
@Schema(title = "登录日志")
public class LoginLogResult {
@Schema(description = "主键")
private Long id;
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "登录账号")
private String account;
@Schema(description = "登录成功状态")
private Boolean login;
@Schema(description = "登录终端")
private String client;
@Schema(description = "登录方式")
private String loginType;
@Schema(description = "登录IP地址")
private String ip;
@Schema(description = "登录地点")
private String loginLocation;
@Schema(description = "浏览器类型")
private String browser;
@Schema(description = "操作系统")
private String os;
@Schema(description = "提示消息")
private String msg;
@Schema(description = "访问时间")
private LocalDateTime loginTime;
}

View File

@@ -0,0 +1,64 @@
package cn.bootx.platform.starter.audit.log.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 操作日志
*
* @author xxm
* @since 2021/8/12
*/
@Data
@Accessors(chain = true)
public class OperateLogResult {
@Schema(description = "日志id")
private Long id;
@Schema(description = "操作模块")
private String title;
@Schema(description = "业务类型")
private String businessType;
@Schema(description = "操作人员id")
private Long operateId;
@Schema(description = "操作人员账号")
private String account;
@Schema(description = "请求方法")
private String method;
@Schema(description = "请求方式")
private String requestMethod;
@Schema(description = "请求url")
private String operateUrl;
@Schema(description = "操作ip")
private String operateIp;
@Schema(description = "操作地点")
private String operateLocation;
@Schema(description = "请求参数")
private String operateParam;
@Schema(description = "返回参数")
private String operateReturn;
@Schema(description = "操作状态0正常 1异常")
private Boolean success;
@Schema(description = "错误消息")
private String errorMsg;
@Schema(description = "操作时间")
private LocalDateTime operateTime;
}

View File

@@ -0,0 +1,76 @@
package cn.bootx.platform.starter.audit.log.service.ip2region;
import cn.hutool.core.collection.CollUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Arrays;
import java.util.List;
/**
* IP对应地址区域信息
* @author xxm
* @since 2023/4/13
*/
@Data
@Accessors(chain = true)
public class IpRegion {
private static final List<String> BIG_CHINA = List.of("香港","澳门","台湾");
private static final List<String> PROVINCE_LEVEL_CITY = List.of("北京","上海","重庆","天津");
/** 国家 */
private String country;
/** 区域 */
private String region;
/** 省份 */
private String province;
/** 城市 */
private String city;
/** ISP */
private String isp;
/** 是否内网地址 */
public boolean isInnerIp(){
return "内网IP".equals(isp);
}
/** 是否国内地址 */
public boolean isChinaIp(){
return "中国".equals(country);
}
/** 是否国内直辖市 */
public boolean isProvinceLevel(){
return "中国".equals(country)&&
PROVINCE_LEVEL_CITY.contains(province);
}
/** 是否港澳台 */
public boolean isBigChina(){
return "中国".equals(country)&&
BIG_CHINA.contains(province);
}
/**
* 国家|区域|省份|城市|ISP
*/
public static IpRegion init(List<String> ipInfo){
IpRegion ipRegion = new IpRegion();
if (CollUtil.isEmpty(ipInfo)){
return ipRegion;
}
ipRegion.country = ipInfo.get(0);
ipRegion.region = ipInfo.get(1);
ipRegion.province = ipInfo.get(2);
ipRegion.city = ipInfo.get(3);
ipRegion.isp = ipInfo.get(4);
return ipRegion;
}
}

View File

@@ -0,0 +1,157 @@
package cn.bootx.platform.starter.audit.log.service.ip2region;
import cn.bootx.platform.core.exception.BizException;
import cn.bootx.platform.starter.audit.log.properties.AuditLogProperties;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
/**
*
* @author xxm
* @since 2023/4/13
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class IpToRegionService {
/** VectorIndex 缓存 */
private static byte[] XDB_INDEX;
/** 整个 xdb 文件缓存 */
private static byte[] XDB_BUFF;
/** 参数配置 */
private final AuditLogProperties auditLogProperties;
/**
* 获取文件查询方式
*/
private Searcher getSearcherByFile(){
try {
return Searcher.newWithFileOnly(auditLogProperties.getIp2region().getFilePath());
} catch (IOException e) {
return null;
}
}
/**
* VectorIndex 索引
*/
private Searcher getSearcherByIndex(){
try {
String filePath = auditLogProperties.getIp2region()
.getFilePath();
if (Objects.isNull(XDB_INDEX)) {
XDB_INDEX = Searcher.loadVectorIndexFromFile(filePath);
}
return Searcher.newWithVectorIndex(filePath, XDB_INDEX);
} catch (IOException e) {
return null;
}
}
/**
* 缓存整个 xdb 数据
*/
private Searcher getSearcherByCache(){
try {
if (Objects.isNull(XDB_BUFF)) {
String filePath = auditLogProperties.getIp2region()
.getFilePath();
XDB_BUFF = Searcher.loadContentFromFile(filePath);
}
return Searcher.newWithBuffer(XDB_BUFF);
} catch (IOException e) {
return null;
}
}
/**
* 根据IP获得地址信息
*/
public IpRegion getRegionByIp(String ip){
// 判断IP是否合法
Matcher matcher = PatternPool.IPV4.matcher(ip);
Matcher ipV6Matcher =PatternPool.IPV6.matcher(ip);
if (!matcher.matches()) {
if (ipV6Matcher.matches()){
log.warn("IpV6地址: {}",ip);
}else {
log.warn("非法IPv4地址: {}",ip);
}
return null;
}
// 根据类型获取 Searcher 对象
Searcher searcher = switch (auditLogProperties.getIp2region()
.getSearchType()) {
case FILE -> getSearcherByFile();
case VECTOR_INDEX -> getSearcherByIndex();
case CACHE -> getSearcherByCache();
};
// 无法进行查询
if (Objects.isNull(searcher)){
log.warn("");
return null;
}
try {
// 城市Id|国家|区域|省份|城市|ISP
String search = searcher.search(ip);
List<String> ipInfo = StrUtil.split(search, "|");
return IpRegion.init(ipInfo);
} catch (Exception e) {
throw new BizException("IP查询失败");
} finally {
try {
searcher.close();
} catch (IOException e) {
// 这句不会执行, finally中try无效
log.error("关闭Ip地址库失败");
}
}
}
/**
* 获取默认格式的地址文本
*/
public String getRegionStrByIp(String ip){
String location;
// ip信息
IpRegion region = this.getRegionByIp(ip);
// 未查询到
if (Objects.isNull(region)){
return "未知";
}
// 中国 港澳台
if (region.isBigChina()){
location = StrUtil.format("{}/{}/{}",region.getCountry(),region.getProvince(),region.getIsp());
}
// 中国 直辖市
else if (region.isProvinceLevel()){
location = StrUtil.format("{}/{}",region.getProvince(),region.getIsp());
}
// 普通中国城市
else if (region.isChinaIp()){
location = StrUtil.format("{}/{}/{}",region.getProvince(),region.getCity(),region.getIsp());
}
// 内网
else if (region.isInnerIp()){
location = "内网地址";
}
// 国外
else {
location = StrUtil.format("{}/{}",region.getCountry(),region.getIsp());
}
return location.replaceAll("/0","");
}
}

View File

@@ -0,0 +1,64 @@
package cn.bootx.platform.starter.audit.log.service.log;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.exception.DataNotExistException;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.starter.audit.log.convert.LogConvert;
import cn.bootx.platform.starter.audit.log.dao.LoginLogDbManager;
import cn.bootx.platform.starter.audit.log.entity.LoginLogDb;
import cn.bootx.platform.starter.audit.log.param.LoginLogParam;
import cn.bootx.platform.starter.audit.log.result.LoginLogResult;
import cn.hutool.core.date.LocalDateTimeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* 登陆日志
*
* @author xxm
* @since 2021/12/2
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginLogService {
private final LoginLogDbManager loginLogManager;
/**
* 添加
*/
@Async
public void add(LoginLogParam loginLog) {
loginLogManager.save(LogConvert.CONVERT.convert(loginLog));
}
/**
* 获取
*/
public LoginLogResult findById(Long id) {
return loginLogManager.findById(id).map(LoginLogDb::toResult).orElseThrow(DataNotExistException::new);
}
/**
* 分页
*/
public PageResult<LoginLogResult> page(PageParam pageParam, LoginLogParam loginLogParam) {
return MpUtil.toPageResult(loginLogManager.page(pageParam, loginLogParam));
}
/**
* 删除
*/
public void deleteByDay(int deleteDay) {
LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTime.now(), -deleteDay, ChronoUnit.DAYS);
loginLogManager.deleteByOffset(offset);
}
}

View File

@@ -0,0 +1,65 @@
package cn.bootx.platform.starter.audit.log.service.log;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.exception.DataNotExistException;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.starter.audit.log.convert.LogConvert;
import cn.bootx.platform.starter.audit.log.dao.OperateLogDbManager;
import cn.bootx.platform.starter.audit.log.entity.OperateLogDb;
import cn.bootx.platform.starter.audit.log.param.OperateLogParam;
import cn.bootx.platform.starter.audit.log.result.OperateLogResult;
import cn.hutool.core.date.LocalDateTimeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
/**
* 操作日志
*
* @author xxm
* @since 2021/8/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OperateLogService {
private final OperateLogDbManager operateLogManager;
/**
* 添加
*/
@Async
public void add(OperateLogParam operateLog) {
operateLogManager.save(LogConvert.CONVERT.convert(operateLog));
}
/**
* 获取
*/
public OperateLogResult findById(Long id) {
return operateLogManager.findById(id).map(OperateLogDb::toResult).orElseThrow(DataNotExistException::new);
}
/**
* 分页
*/
public PageResult<OperateLogResult> page(PageParam pageParam, OperateLogParam operateLogParam) {
return MpUtil.toPageResult(operateLogManager.page(pageParam, operateLogParam));
}
/**
* 删除
*/
public void deleteByDay(int deleteDay) {
// 计算出来指定天数的日期
LocalDateTime offset = LocalDateTimeUtil.offset(LocalDateTime.now(), -deleteDay, ChronoUnit.DAYS);
operateLogManager.deleteByOffset(offset);
}
}