添加操作日志
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -158,6 +158,11 @@
|
|||||||
<artifactId>xtools-app-sys-scheduled</artifactId>
|
<artifactId>xtools-app-sys-scheduled</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xujun</groupId>
|
||||||
|
<artifactId>xtools-app-sys-tag</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- Sys 模块 end -->
|
<!-- Sys 模块 end -->
|
||||||
|
|
||||||
<!-- Gen 模块 begin -->
|
<!-- Gen 模块 begin -->
|
||||||
|
|||||||
@@ -90,6 +90,10 @@
|
|||||||
<groupId>org.xujun</groupId>
|
<groupId>org.xujun</groupId>
|
||||||
<artifactId>xtools-app-sys-auth</artifactId>
|
<artifactId>xtools-app-sys-auth</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xujun</groupId>
|
||||||
|
<artifactId>xtools-app-sys-tag</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- 项目模块 end -->
|
<!-- 项目模块 end -->
|
||||||
|
|
||||||
<!-- mapstruct begin -->
|
<!-- mapstruct begin -->
|
||||||
|
|||||||
@@ -367,10 +367,10 @@ import {useSettingsStore} from "@/store";
|
|||||||
import {DictItem, PageReq, PageResult} from "@/types/global";
|
import {DictItem, PageReq, PageResult} from "@/types/global";
|
||||||
import SysCommonAPI from "@/api/sys/sys-common-api";
|
import SysCommonAPI from "@/api/sys/sys-common-api";
|
||||||
#else
|
#else
|
||||||
import {PageReq} from "@/types/global";
|
import {PageReq, PageResult} from "@/types/global";
|
||||||
#end
|
#end
|
||||||
#if($exportExcel)
|
#if($exportExcel)
|
||||||
import {FileUtils, Format} from "@/utils/utils";
|
import {FileUtils, Format} from "@/utils";
|
||||||
#end
|
#end
|
||||||
import ${table.entityName}API, {
|
import ${table.entityName}API, {
|
||||||
EditForm as ${table.entityName}EditForm,
|
EditForm as ${table.entityName}EditForm,
|
||||||
|
|||||||
@@ -7,4 +7,10 @@ sys:
|
|||||||
# 日志配置
|
# 日志配置
|
||||||
log:
|
log:
|
||||||
# 存储类型(elasticsearch|mysql)
|
# 存储类型(elasticsearch|mysql)
|
||||||
type: ${SYS_LOG_TYPE:elasticsearch}
|
type: ${SYS_LOG_TYPE:elasticsearch}
|
||||||
|
# 最大保存天数
|
||||||
|
max-days: 2
|
||||||
|
# 忽略操作日志
|
||||||
|
ignore-opt-log:
|
||||||
|
- /sys/dict-item/get-by-code/*
|
||||||
|
- /**/page
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<module>xtools-app-sys-scheduled</module>
|
<module>xtools-app-sys-scheduled</module>
|
||||||
<module>xtools-app-sys-risk</module>
|
<module>xtools-app-sys-risk</module>
|
||||||
<module>xtools-app-sys-file-web</module>
|
<module>xtools-app-sys-file-web</module>
|
||||||
|
<module>xtools-app-sys-tag</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -11,6 +11,7 @@ import org.springframework.core.Ordered;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import xtools.app.common.cache.enums.AppCache;
|
import xtools.app.common.cache.enums.AppCache;
|
||||||
import xtools.app.sys.auth.model.dto.LoginUserDto;
|
import xtools.app.sys.auth.model.dto.LoginUserDto;
|
||||||
|
import xtools.app.sys.auth.model.dto.OptLogDto;
|
||||||
import xtools.app.sys.auth.utils.AuthUtils;
|
import xtools.app.sys.auth.utils.AuthUtils;
|
||||||
import xtools.app.sys.auth.utils.PremUtils;
|
import xtools.app.sys.auth.utils.PremUtils;
|
||||||
import xtools.app.sys.auth.whitelist.AuthWhitelist;
|
import xtools.app.sys.auth.whitelist.AuthWhitelist;
|
||||||
@@ -22,14 +23,18 @@ import xtools.boot.cache.redis.base.RedisService;
|
|||||||
import xtools.boot.core.holder.CommonHolder;
|
import xtools.boot.core.holder.CommonHolder;
|
||||||
import xtools.boot.core.utils.PathPatternUtils;
|
import xtools.boot.core.utils.PathPatternUtils;
|
||||||
import xtools.boot.core.utils.SpringContextUtils;
|
import xtools.boot.core.utils.SpringContextUtils;
|
||||||
|
import xtools.boot.log.LogBus;
|
||||||
|
import xtools.boot.log.enums.LogBusBaseType;
|
||||||
import xtools.boot.mask.utils.MaskIgnoreUtils;
|
import xtools.boot.mask.utils.MaskIgnoreUtils;
|
||||||
import xtools.boot.web.filter.base.BaseFilter;
|
import xtools.boot.web.filter.base.BaseFilter;
|
||||||
import xtools.core.CollectionUtils;
|
import xtools.core.CollectionUtils;
|
||||||
import xtools.core.StringUtils;
|
import xtools.core.StringUtils;
|
||||||
|
import xtools.core.enums.LogLevel;
|
||||||
import xtools.web.HeaderUtils;
|
import xtools.web.HeaderUtils;
|
||||||
import xtools.web.HttpServletUtils;
|
import xtools.web.HttpServletUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -47,7 +52,7 @@ import java.util.Set;
|
|||||||
* @date : 2026/1/31 21:18
|
* @date : 2026/1/31 21:18
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class AuthFilter extends BaseFilter implements Ordered, BaseParams {
|
public class AuthFilter extends BaseFilter implements Ordered, BootCommonConstant, BaseParams {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微服务标识
|
* 微服务标识
|
||||||
@@ -157,6 +162,10 @@ public class AuthFilter extends BaseFilter implements Ordered, BaseParams {
|
|||||||
) throws ServletException, IOException {
|
) throws ServletException, IOException {
|
||||||
// 获取访问 uri
|
// 获取访问 uri
|
||||||
String uri = HeaderUtils.getUri(request);
|
String uri = HeaderUtils.getUri(request);
|
||||||
|
String ip = HeaderUtils.getIp(request);
|
||||||
|
OptLogDto log = new OptLogDto();
|
||||||
|
log.setUri(uri);
|
||||||
|
log.setIp(ip);
|
||||||
|
|
||||||
// 忽略权限校验
|
// 忽略权限校验
|
||||||
if (checkAuthWhiteList(uri)) {
|
if (checkAuthWhiteList(uri)) {
|
||||||
@@ -184,11 +193,13 @@ public class AuthFilter extends BaseFilter implements Ordered, BaseParams {
|
|||||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.UNAUTHORIZED, null)));
|
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.UNAUTHORIZED, null)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log.setAccountId(loginUser.getId());
|
||||||
|
log.setAccount(loginUser.getAccount());
|
||||||
|
|
||||||
// 校验 uri 访问权限
|
// 校验 uri 访问权限
|
||||||
String method = request.getMethod();
|
String method = request.getMethod();
|
||||||
if (!PremUtils.checkInterfacePerm(uri, method, loginUser.getRoleIds())) {
|
if (!PremUtils.checkInterfacePerm(uri, method, loginUser.getRoleIds())) {
|
||||||
|
saveOptLog(log, "URI访问权限校验失败");
|
||||||
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.FORBIDDEN, null)));
|
HttpServletUtils.respWriter(response, JSONObject.from(new Result<>(ResultType.FORBIDDEN, null)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -199,7 +210,27 @@ public class AuthFilter extends BaseFilter implements Ordered, BaseParams {
|
|||||||
|
|
||||||
// 校验忽略掩码
|
// 校验忽略掩码
|
||||||
checkIgnoreMask();
|
checkIgnoreMask();
|
||||||
|
CommonHolder.set(GET_SWAGGER_TAG, true);
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
|
saveOptLog(log, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存操作日志
|
||||||
|
*
|
||||||
|
* @param log 日志
|
||||||
|
* @param memo 备注
|
||||||
|
*/
|
||||||
|
private void saveOptLog(OptLogDto log, String memo) {
|
||||||
|
Object tag = CommonHolder.get(SWAGGER_TAG);
|
||||||
|
if (Objects.nonNull(tag)) {
|
||||||
|
{
|
||||||
|
log.setTitle(tag.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.setMemo(memo);
|
||||||
|
log.setGmtCreate(Instant.now());
|
||||||
|
LogBus.init(LogLevel.INFO, LogBusBaseType.OPT_LOG).data(log).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package xtools.app.sys.auth.model.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : OptLogDto</p>
|
||||||
|
* <p>Description : OptLogDto</p>
|
||||||
|
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||||
|
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : XuJun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026/6/2 17:28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class OptLogDto implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志追踪id
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志追踪id")
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
@Schema(description = "标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号ID")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作URI
|
||||||
|
*/
|
||||||
|
@Schema(description = "操作URI")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间", example = "2026-01-05 10:32:00")
|
||||||
|
private Instant gmtCreate;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -157,6 +157,10 @@
|
|||||||
<groupId>org.xujun</groupId>
|
<groupId>org.xujun</groupId>
|
||||||
<artifactId>xtools-app-sys-risk</artifactId>
|
<artifactId>xtools-app-sys-risk</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xujun</groupId>
|
||||||
|
<artifactId>xtools-app-sys-tag</artifactId>
|
||||||
|
</dependency>
|
||||||
<!-- 项目模块 end -->
|
<!-- 项目模块 end -->
|
||||||
|
|
||||||
<!-- mapstruct begin -->
|
<!-- mapstruct begin -->
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import lombok.Data;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Title : SysConfig</p>
|
* <p>Title : SysConfig</p>
|
||||||
* <p>Description : SysConfig</p>
|
* <p>Description : SysConfig</p>
|
||||||
@@ -57,6 +60,11 @@ public class SysConfig {
|
|||||||
* 最大保存天数
|
* 最大保存天数
|
||||||
*/
|
*/
|
||||||
private int maxDays = 2;
|
private int maxDays = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略操作日志
|
||||||
|
*/
|
||||||
|
private List<String> ignoreOptLog = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package xtools.app.sys.controller;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import xtools.app.sys.model.dto.excel.SysOptLogExcel;
|
||||||
|
import xtools.app.sys.model.dto.req.SysOptLogPageReq;
|
||||||
|
import xtools.app.sys.model.dto.resp.SysOptLogResp;
|
||||||
|
import xtools.app.sys.service.SysOptLogService;
|
||||||
|
import xtools.boot.api.exection.BizError;
|
||||||
|
import xtools.boot.api.model.dto.Result;
|
||||||
|
import xtools.boot.api.model.dto.page.PageReq;
|
||||||
|
import xtools.boot.api.model.dto.page.PageResp;
|
||||||
|
import xtools.extend.office.FesodUtils;
|
||||||
|
import xtools.web.HttpServletUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogController</p>
|
||||||
|
* <p>Description : 系统操作日志 Controller</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "系统操作日志")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/sys/opt-log")
|
||||||
|
public class SysOptLogController {
|
||||||
|
|
||||||
|
private final SysOptLogService sysOptLogService;
|
||||||
|
|
||||||
|
@Operation(summary = "分页请求")
|
||||||
|
@PostMapping("page")
|
||||||
|
public Result<PageResp<SysOptLogResp>> page(@RequestBody @Valid PageReq<SysOptLogPageReq> pageReq) {
|
||||||
|
return sysOptLogService.page(pageReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出Excel")
|
||||||
|
@PostMapping("export")
|
||||||
|
public void exportExcel(@RequestBody @Valid SysOptLogPageReq req, HttpServletResponse response) {
|
||||||
|
String sheetName = "系统操作日志";
|
||||||
|
String filename = sheetName + ".xlsx";
|
||||||
|
List<SysOptLogExcel> dataList = sysOptLogService.exportExcel(req);
|
||||||
|
try {
|
||||||
|
FesodUtils.write(response.getOutputStream(), SysOptLogExcel.class, sheetName, dataList);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new BizError("导出Excel失败");
|
||||||
|
}
|
||||||
|
// 设置 header 和 contentType.写在最后的原因是,避免报错时,响应 contentType 已经被修改
|
||||||
|
HttpServletUtils.addDownloadHeader(response, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package xtools.app.sys.convert;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import xtools.app.sys.auth.model.dto.OptLogDto;
|
||||||
|
import xtools.app.sys.model.dto.excel.SysOptLogExcel;
|
||||||
|
import xtools.app.sys.model.dto.resp.SysOptLogResp;
|
||||||
|
import xtools.app.sys.model.entity.SysOptLog;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogConvert</p>
|
||||||
|
* <p>Description : 系统操作日志 Convert</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Mapper(componentModel = "spring")
|
||||||
|
public interface SysOptLogConvert {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体转响应
|
||||||
|
*
|
||||||
|
* @param data 实体
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
SysOptLogResp entityToResp(SysOptLog data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量实体转响应
|
||||||
|
*
|
||||||
|
* @param dataList 批量实体
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
List<SysOptLogResp> entityToRespList(List<SysOptLog> dataList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体转Excel
|
||||||
|
*
|
||||||
|
* @param data 实体
|
||||||
|
* @return Excel
|
||||||
|
*/
|
||||||
|
SysOptLogExcel entityToExcel(SysOptLog data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO转实体
|
||||||
|
*
|
||||||
|
* @param data DTO
|
||||||
|
* @return 实体
|
||||||
|
*/
|
||||||
|
SysOptLog dtoToEntity(OptLogDto data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package xtools.app.sys.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import xtools.app.sys.model.entity.SysOptLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogMapper</p>
|
||||||
|
* <p>Description : 系统操作日志 Mapper 接口</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SysOptLogMapper extends BaseMapper<SysOptLog> {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package xtools.app.sys.model.dto.excel;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||||
|
import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogExcel</p>
|
||||||
|
* <p>Description : 系统操作日志Excel对象</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SysOptLogExcel implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志追踪id
|
||||||
|
*/
|
||||||
|
@ExcelProperty("日志追踪id")
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
@ExcelProperty("标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty("账号ID")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
@ExcelProperty("账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@ExcelProperty("IP")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
@ExcelProperty("地址")
|
||||||
|
private String addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作URI
|
||||||
|
*/
|
||||||
|
@ExcelProperty("操作URI")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty("创建时间")
|
||||||
|
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date gmtCreate;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package xtools.app.sys.model.dto.req;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogPageReq</p>
|
||||||
|
* <p>Description : 系统操作日志分页请求对象</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SysOptLogPageReq implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志追踪id
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志追踪id")
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
@Schema(description = "标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号ID")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址code
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址code")
|
||||||
|
private String addrCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作URI
|
||||||
|
*/
|
||||||
|
@Schema(description = "操作URI")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间(范围)
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间(范围)", example = "['2026-01-01 00:00:00', '2026-01-01 12:00:00']")
|
||||||
|
private Instant[] gmtCreateRange;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package xtools.app.sys.model.dto.resp;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import xtools.boot.api.model.entity.BaseEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogResp</p>
|
||||||
|
* <p>Description : 系统操作日志响应对象</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SysOptLogResp extends BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志追踪id
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志追踪id")
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
@Schema(description = "标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号ID")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址code
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址code")
|
||||||
|
private String addrCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作URI
|
||||||
|
*/
|
||||||
|
@Schema(description = "操作URI")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package xtools.app.sys.model.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLog</p>
|
||||||
|
* <p>Description : 系统操作日志实体对象</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TableName("sys_opt_log")
|
||||||
|
public class SysOptLog implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "ID")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志追踪id
|
||||||
|
*/
|
||||||
|
@Schema(description = "日志追踪id")
|
||||||
|
@TableField(value = "trace_id")
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
@Schema(description = "标题")
|
||||||
|
@TableField(value = "title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号ID")
|
||||||
|
@TableField(value = "account_id")
|
||||||
|
private Long accountId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号
|
||||||
|
*/
|
||||||
|
@Schema(description = "账号")
|
||||||
|
@TableField(value = "account")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@Schema(description = "IP")
|
||||||
|
@TableField(value = "ip")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址")
|
||||||
|
@TableField(value = "addr")
|
||||||
|
private String addr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址code
|
||||||
|
*/
|
||||||
|
@Schema(description = "地址code")
|
||||||
|
@TableField(value = "addr_code")
|
||||||
|
private String addrCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作URI
|
||||||
|
*/
|
||||||
|
@Schema(description = "操作URI")
|
||||||
|
@TableField(value = "uri")
|
||||||
|
private String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@Schema(description = "备注")
|
||||||
|
@TableField(value = "memo")
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(value = "gmt_create")
|
||||||
|
@Schema(description = "创建时间", example = "2026-01-05 10:32:00")
|
||||||
|
private Instant gmtCreate;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package xtools.app.sys.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import xtools.app.sys.model.dto.excel.SysOptLogExcel;
|
||||||
|
import xtools.app.sys.model.dto.req.SysOptLogPageReq;
|
||||||
|
import xtools.app.sys.model.dto.resp.SysOptLogResp;
|
||||||
|
import xtools.boot.api.model.dto.Result;
|
||||||
|
import xtools.boot.api.model.dto.page.PageReq;
|
||||||
|
import xtools.boot.api.model.dto.page.PageResp;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogService</p>
|
||||||
|
* <p>Description : 系统操作日志 Service</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
public interface SysOptLogService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param pageReq 分页请求
|
||||||
|
* @return 分页结果
|
||||||
|
*/
|
||||||
|
Result<PageResp<SysOptLogResp>> page(PageReq<SysOptLogPageReq> pageReq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存日志
|
||||||
|
*
|
||||||
|
* @param traceId 日志追踪 ID
|
||||||
|
* @param logData 日志
|
||||||
|
*/
|
||||||
|
void save(String traceId, JSONObject logData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出 Excel
|
||||||
|
*
|
||||||
|
* @param req 请求参数
|
||||||
|
* @return Excel 数据
|
||||||
|
*/
|
||||||
|
List<SysOptLogExcel> exportExcel(SysOptLogPageReq req);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package xtools.app.sys.service.base;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import xtools.app.sys.mapper.SysOptLogMapper;
|
||||||
|
import xtools.app.sys.model.entity.SysOptLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogBaseService</p>
|
||||||
|
* <p>Description : 系统操作日志 BaseService</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SysOptLogBaseService extends ServiceImpl<SysOptLogMapper, SysOptLog> {
|
||||||
|
}
|
||||||
@@ -9,7 +9,9 @@ import org.springframework.stereotype.Service;
|
|||||||
import xtools.app.common.log.bus.service.LogBusService;
|
import xtools.app.common.log.bus.service.LogBusService;
|
||||||
import xtools.app.sys.model.entity.SysLog;
|
import xtools.app.sys.model.entity.SysLog;
|
||||||
import xtools.app.sys.service.SysLogService;
|
import xtools.app.sys.service.SysLogService;
|
||||||
|
import xtools.app.sys.service.SysOptLogService;
|
||||||
import xtools.boot.api.model.dto.log.LogTrack;
|
import xtools.boot.api.model.dto.log.LogTrack;
|
||||||
|
import xtools.boot.log.enums.LogBusBaseType;
|
||||||
import xtools.boot.log.model.dto.LogBody;
|
import xtools.boot.log.model.dto.LogBody;
|
||||||
import xtools.boot.log.model.dto.RunInfo;
|
import xtools.boot.log.model.dto.RunInfo;
|
||||||
import xtools.core.CollectionUtils;
|
import xtools.core.CollectionUtils;
|
||||||
@@ -37,6 +39,8 @@ public class LogBusServiceImpl implements LogBusService {
|
|||||||
|
|
||||||
private final SysLogService sysLogService;
|
private final SysLogService sysLogService;
|
||||||
|
|
||||||
|
private final SysOptLogService sysOptLogService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存日志
|
* 保存日志
|
||||||
*
|
*
|
||||||
@@ -44,18 +48,25 @@ public class LogBusServiceImpl implements LogBusService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void saveLog(LogBody logBody) {
|
public void saveLog(LogBody logBody) {
|
||||||
LogTrack logTrack = logBody.getLogTrack();
|
// 获取日志类型
|
||||||
RunInfo runInfo = logBody.getRunInfo();
|
String logType = logBody.getType();
|
||||||
|
|
||||||
|
LogTrack logTrack = logBody.getLogTrack();
|
||||||
JSONObject logData = logBody.getLogData();
|
JSONObject logData = logBody.getLogData();
|
||||||
if (CollectionUtils.isEmpty(logData)) {
|
if (CollectionUtils.isEmpty(logData)) {
|
||||||
logData = new JSONObject();
|
logData = new JSONObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 扩展日志处理
|
||||||
|
if (extLog(logType, logTrack.traceId(), logData)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunInfo runInfo = logBody.getRunInfo();
|
||||||
JSONArray stackTrace = logBody.getStackTrace();
|
JSONArray stackTrace = logBody.getStackTrace();
|
||||||
|
|
||||||
// 处理数据
|
// 处理数据
|
||||||
String title = logBody.getTitle();
|
String title = logBody.getTitle();
|
||||||
String logType = logBody.getType();
|
|
||||||
title = StringUtils.isBlank(title) ? logType : title;
|
title = StringUtils.isBlank(title) ? logType : title;
|
||||||
// 获取请求ip
|
// 获取请求ip
|
||||||
String ip = logData.getString("ip");
|
String ip = logData.getString("ip");
|
||||||
@@ -90,4 +101,20 @@ public class LogBusServiceImpl implements LogBusService {
|
|||||||
log.error("保存日志异常,logBody:{}", JsonUtils.toStrPretty(logBody), e);
|
log.error("保存日志异常,logBody:{}", JsonUtils.toStrPretty(logBody), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展日志
|
||||||
|
*
|
||||||
|
* @param logType 日志类型
|
||||||
|
* @param traceId 日志追踪 ID
|
||||||
|
* @param logData 日志数据
|
||||||
|
* @return 保存结果
|
||||||
|
*/
|
||||||
|
private boolean extLog(String logType, String traceId, JSONObject logData) {
|
||||||
|
if (LogBusBaseType.OPT_LOG.desc().equals(logType)) {
|
||||||
|
sysOptLogService.save(traceId, logData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package xtools.app.sys.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import xtools.app.sys.auth.model.dto.OptLogDto;
|
||||||
|
import xtools.app.sys.config.SysConfig;
|
||||||
|
import xtools.app.sys.convert.SysOptLogConvert;
|
||||||
|
import xtools.app.sys.model.dto.excel.SysOptLogExcel;
|
||||||
|
import xtools.app.sys.model.dto.req.SysOptLogPageReq;
|
||||||
|
import xtools.app.sys.model.dto.resp.SysOptLogResp;
|
||||||
|
import xtools.app.sys.model.entity.SysOptLog;
|
||||||
|
import xtools.app.sys.service.SysOptLogService;
|
||||||
|
import xtools.app.sys.service.base.SysOptLogBaseService;
|
||||||
|
import xtools.boot.api.model.dto.Result;
|
||||||
|
import xtools.boot.api.model.dto.page.PageReq;
|
||||||
|
import xtools.boot.api.model.dto.page.PageResp;
|
||||||
|
import xtools.boot.core.utils.PathPatternUtils;
|
||||||
|
import xtools.boot.db.mybatisplus.utils.QueryUtils;
|
||||||
|
import xtools.boot.ip.utils.IpUtils;
|
||||||
|
import xtools.core.StringUtils;
|
||||||
|
import xtools.extend.dto.IpAddrDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SysOptLogServiceImpl</p>
|
||||||
|
* <p>Description : 系统操作日志 ServiceImpl</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : xujun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026-06-02 16:44:47
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Primary
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SysOptLogServiceImpl implements SysOptLogService {
|
||||||
|
|
||||||
|
private final SysConfig sysConfig;
|
||||||
|
|
||||||
|
private final SysOptLogBaseService sysOptLogBaseService;
|
||||||
|
|
||||||
|
private final SysOptLogConvert sysOptLogConvert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param pageReq 分页请求
|
||||||
|
* @return 分页结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Result<PageResp<SysOptLogResp>> page(PageReq<SysOptLogPageReq> pageReq) {
|
||||||
|
// 分页查询
|
||||||
|
Page<SysOptLog> page = getPageData(pageReq.getCurrentPage(), pageReq.getPageSize(), pageReq.getQuery());
|
||||||
|
// 分装结果
|
||||||
|
PageResp<SysOptLogResp> pageResp = new PageResp<>(pageReq, page.getTotal(), sysOptLogConvert.entityToRespList(page.getRecords()));
|
||||||
|
return Result.ok(pageResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存日志
|
||||||
|
*
|
||||||
|
* @param traceId 日志追踪 ID
|
||||||
|
* @param logData 日志
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void save(String traceId, JSONObject logData) {
|
||||||
|
// 忽略操作日志
|
||||||
|
List<String> ignoreOptLog = sysConfig.getLog().getIgnoreOptLog();
|
||||||
|
|
||||||
|
OptLogDto dto = logData.toJavaObject(OptLogDto.class);
|
||||||
|
String uri = dto.getUri();
|
||||||
|
|
||||||
|
// 判断忽略操作日志
|
||||||
|
if (PathPatternUtils.match(ignoreOptLog, uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.setTraceId(traceId);
|
||||||
|
SysOptLog optLog = sysOptLogConvert.dtoToEntity(dto);
|
||||||
|
String ip = optLog.getIp();
|
||||||
|
if (StringUtils.isNotBlank(ip)) {
|
||||||
|
try {
|
||||||
|
IpAddrDto ipAddr = IpUtils.search(ip);
|
||||||
|
optLog.setAddr(IpUtils.searchAddr(ipAddr));
|
||||||
|
optLog.setAddrCode(IpUtils.getCode(ipAddr));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("获取 IP 地址信息失败,IP = {}", ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sysOptLogBaseService.save(optLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出 Excel
|
||||||
|
*
|
||||||
|
* @param req 请求参数
|
||||||
|
* @return Excel 数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<SysOptLogExcel> exportExcel(SysOptLogPageReq req) {
|
||||||
|
// 创建查询条件
|
||||||
|
LambdaQueryWrapper<SysOptLog> query = new LambdaQueryWrapper<>();
|
||||||
|
// 查询字段
|
||||||
|
query.select(
|
||||||
|
SysOptLog::getTraceId
|
||||||
|
, SysOptLog::getTitle
|
||||||
|
, SysOptLog::getAccountId
|
||||||
|
, SysOptLog::getAccount
|
||||||
|
, SysOptLog::getIp
|
||||||
|
, SysOptLog::getAddr
|
||||||
|
, SysOptLog::getUri
|
||||||
|
, SysOptLog::getMemo
|
||||||
|
, SysOptLog::getGmtCreate
|
||||||
|
);
|
||||||
|
// 设置查询条件
|
||||||
|
setQueryWrapper(query, req);
|
||||||
|
// 排序
|
||||||
|
query.orderByDesc(SysOptLog::getGmtCreate);
|
||||||
|
List<SysOptLog> dataList = sysOptLogBaseService.list(query);
|
||||||
|
return dataList.stream().map(sysOptLogConvert::entityToExcel).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分页数据
|
||||||
|
*
|
||||||
|
* @param currentPage 当前页
|
||||||
|
* @param pageSize 每页数量
|
||||||
|
* @param req 请求参数
|
||||||
|
* @return 分页数据
|
||||||
|
*/
|
||||||
|
private Page<SysOptLog> getPageData(Integer currentPage, Integer pageSize, SysOptLogPageReq req) {
|
||||||
|
// 创建查询条件
|
||||||
|
LambdaQueryWrapper<SysOptLog> query = new LambdaQueryWrapper<>();
|
||||||
|
// 查询字段
|
||||||
|
query.select(
|
||||||
|
SysOptLog::getId
|
||||||
|
, SysOptLog::getTraceId
|
||||||
|
, SysOptLog::getTitle
|
||||||
|
, SysOptLog::getAccountId
|
||||||
|
, SysOptLog::getAccount
|
||||||
|
, SysOptLog::getIp
|
||||||
|
, SysOptLog::getAddr
|
||||||
|
, SysOptLog::getAddrCode
|
||||||
|
, SysOptLog::getUri
|
||||||
|
, SysOptLog::getMemo
|
||||||
|
, SysOptLog::getGmtCreate
|
||||||
|
);
|
||||||
|
// 设置查询条件
|
||||||
|
setQueryWrapper(query, req);
|
||||||
|
// 排序
|
||||||
|
query.orderByDesc(SysOptLog::getGmtCreate);
|
||||||
|
return sysOptLogBaseService.page(QueryUtils.getPage(currentPage, pageSize), query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置查询条件
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param req 请求参数
|
||||||
|
*/
|
||||||
|
private void setQueryWrapper(LambdaQueryWrapper<SysOptLog> query, SysOptLogPageReq req) {
|
||||||
|
if (Objects.isNull(req)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 查询条件
|
||||||
|
query.eq(Objects.nonNull(req.getId()), SysOptLog::getId, req.getId());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getTraceId()), SysOptLog::getTraceId, req.getTraceId());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getTitle()), SysOptLog::getTitle, req.getTitle());
|
||||||
|
query.eq(Objects.nonNull(req.getAccountId()), SysOptLog::getAccountId, req.getAccountId());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getAccount()), SysOptLog::getAccount, req.getAccount());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getIp()), SysOptLog::getIp, req.getIp());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getAddr()), SysOptLog::getAddr, req.getAddr());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getAddrCode()), SysOptLog::getAddrCode, req.getAddrCode());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getUri()), SysOptLog::getUri, req.getUri());
|
||||||
|
query.like(StringUtils.isNotBlank(req.getMemo()), SysOptLog::getMemo, req.getMemo());
|
||||||
|
QueryUtils.addTimeRange(query, req.getGmtCreateRange(), SysOptLog::getGmtCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
xtools-app-sys/xtools-app-sys-tag/pom.xml
Normal file
42
xtools-app-sys/xtools-app-sys-tag/pom.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
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>org.xujun</groupId>
|
||||||
|
<artifactId>xtools-app-sys</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>xtools-app-sys-tag</artifactId>
|
||||||
|
|
||||||
|
<!-- 依赖 -->
|
||||||
|
<dependencies>
|
||||||
|
<!-- xtools begin -->
|
||||||
|
<!-- xtools-boot-base -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xujun</groupId>
|
||||||
|
<artifactId>xtools-boot-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- xtools end -->
|
||||||
|
|
||||||
|
<!-- spring begin -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- spring end -->
|
||||||
|
|
||||||
|
<!-- jakarta -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.annotation</groupId>
|
||||||
|
<artifactId>jakarta.annotation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package xtools.app.sys.tag.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import xtools.app.sys.tag.interceptor.SwaggerTagInterceptor;
|
||||||
|
import xtools.base.config.BaseParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : WebMvcSwaggerConfig</p>
|
||||||
|
* <p>Description : WebMvcSwaggerConfig</p>
|
||||||
|
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||||
|
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : XuJun
|
||||||
|
* @version : 1.0.0
|
||||||
|
* @date : 2026/6/2 16:44:47
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebMvcSwaggerConfig implements WebMvcConfigurer, BaseParams {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SwaggerTagInterceptor swaggerTagInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加拦截器
|
||||||
|
*
|
||||||
|
* @param registry 注册器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(swaggerTagInterceptor)
|
||||||
|
.addPathPatterns("/**")
|
||||||
|
.order(CP_NUM0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package xtools.app.sys.tag.interceptor;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
import xtools.base.config.BaseParams;
|
||||||
|
import xtools.boot.api.constant.BootCommonConstant;
|
||||||
|
import xtools.boot.core.holder.CommonHolder;
|
||||||
|
import xtools.core.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Title : SwaggerTagInterceptor</p>
|
||||||
|
* <p>Description : SwaggerTagInterceptor</p>
|
||||||
|
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||||
|
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||||
|
* <p>Company : org.xujun</p>
|
||||||
|
*
|
||||||
|
* @author : XuJun
|
||||||
|
* <p>Version : 1.0.0</p>
|
||||||
|
* @date : 2026/6/3 17:28
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SwaggerTagInterceptor implements HandlerInterceptor, BootCommonConstant, BaseParams {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入Handler方法之前执行
|
||||||
|
*
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param handler handler
|
||||||
|
* @return true(放行) or false(拦截)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(
|
||||||
|
@NonNull HttpServletRequest request,
|
||||||
|
@NonNull HttpServletResponse response,
|
||||||
|
@NonNull Object handler
|
||||||
|
) {
|
||||||
|
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Object get = CommonHolder.get(GET_SWAGGER_TAG);
|
||||||
|
if (Objects.isNull(get)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringJoiner joiner = new StringJoiner(CP_LINE);
|
||||||
|
// 获取@Tag注解
|
||||||
|
Tag tag = handlerMethod.getBeanType().getAnnotation(Tag.class);
|
||||||
|
if (Objects.nonNull(tag)) {
|
||||||
|
String name = tag.name();
|
||||||
|
if (StringUtils.isNotBlank(name)) {
|
||||||
|
joiner.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取@Operation注解
|
||||||
|
Operation operation = handlerMethod.getMethodAnnotation(Operation.class);
|
||||||
|
if (Objects.nonNull(operation)) {
|
||||||
|
String summary = operation.summary();
|
||||||
|
if (StringUtils.isNotBlank(summary)) {
|
||||||
|
joiner.add(summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommonHolder.set(SWAGGER_TAG, joiner.toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user