初始化项目

This commit is contained in:
2026-04-21 16:12:04 +08:00
parent 4541af2c63
commit f9d96473da
443 changed files with 36365 additions and 19 deletions

View File

@@ -0,0 +1,31 @@
<?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-log-bus-elasticsearch</artifactId>
<!-- 依赖 -->
<dependencies>
<!-- 项目模块 begin -->
<dependency>
<groupId>org.xujun</groupId>
<artifactId>xtools-app-sys-biz</artifactId>
</dependency>
<!-- 项目模块 end -->
<!-- SpringBoot begin -->
<!-- SpringBoot-elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- SpringBoot end -->
</dependencies>
</project>

View File

@@ -0,0 +1,44 @@
package xtools.app.sys.log.bus.elasticsearch.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import xtools.app.sys.log.bus.elasticsearch.service.impl.EsSysLogServiceImpl;
import xtools.app.sys.service.SysLogService;
/**
* <p>Title : SysLogElasticsearchConfig</p>
* <p>Description : SysLogElasticsearchConfig</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/2/14 10:29
*/
@Slf4j
@Configuration
@EnableElasticsearchRepositories(basePackages = "xtools.app.sys.log.bus.elasticsearch.service.base")
public class SysLogElasticsearchConfig {
/**
* 日志类型elasticsearch
*/
private static final String TYPE_ES = "elasticsearch";
/**
* 获取ES日志服务
*
* @return 日志服务
*/
@Bean
@ConditionalOnProperty(prefix = "sys.log", name = "type", havingValue = TYPE_ES)
public SysLogService getEsSysLogService() {
log.info("系统日志存储类型:{}", TYPE_ES);
return new EsSysLogServiceImpl();
}
}

View File

@@ -0,0 +1,27 @@
package xtools.app.sys.log.bus.elasticsearch.convert;
import org.mapstruct.Mapper;
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
import xtools.app.sys.model.entity.SysLog;
/**
* <p>Title : SysLogElasticsearchConvert</p>
* <p>Description : SysLogElasticsearchConvert</p>
* <p>Company : org.xujun</p>
*
* @author : xujun
* @version : 1.0.0
* @date : 2026-02-07 20:09:03
*/
@Mapper(componentModel = "spring")
public interface SysLogElasticsearchConvert {
/**
* 实体转ES
*
* @param data 实体
* @return ES
*/
EsSysLog entityToEs(SysLog data);
}

View File

@@ -0,0 +1,143 @@
package xtools.app.sys.log.bus.elasticsearch.dto;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* <p>Title : EsSysLog</p>
* <p>Description : EsSysLog</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/2/13 19:34
*/
@Data
@Document(indexName = EsSysLog.INDEX_NAME)
public class EsSysLog {
/**
* ES索引名称
**/
public static final String INDEX_NAME = "log-sys_v16";
/**
* 主键 ID
*/
@Id
private String id;
/**
* 日志ID
*/
@Field(type = FieldType.Text)
private String logId;
/**
* 日志追踪ID
*/
@Field(type = FieldType.Text)
private String traceId;
/**
* 日志父ID
*/
@Field(type = FieldType.Text)
private String parentId;
/**
* 日志线程类型
*/
@Field(type = FieldType.Text)
private String threadType;
/**
* 日志时间
*/
@Field(type = FieldType.Long)
private Long logTime;
/**
* 日志获取索引
*/
@Field(type = FieldType.Integer)
private Integer logIndex;
/**
* 服务名称
*/
@Field(type = FieldType.Text)
private String appName;
/**
* 本机IP
*/
@Field(type = FieldType.Text)
private String localIp;
/**
* 运行端口
*/
@Field(type = FieldType.Text)
private String port;
/**
* 日志标题
*/
@Field(type = FieldType.Text)
private String title;
/**
* 日志级别
*/
@Field(type = FieldType.Integer)
private Integer logLevel;
/**
* 日志类型
*/
@Field(type = FieldType.Text)
private String logType;
/**
* 请求IP
*/
@Field(type = FieldType.Text)
private String ip;
/**
* 请求URI
*/
@Field(type = FieldType.Text)
private String uri;
/**
* 日志内容
*/
@Field(type = FieldType.Text)
private String logBody;
/**
* 堆栈信息
*/
@Field(type = FieldType.Text)
private String stackTrace;
/**
* 日志错误信息
*/
@Field(type = FieldType.Text)
private String logError;
/**
* 备注
*/
@Field(type = FieldType.Text)
private String memo;
}

View File

@@ -0,0 +1,20 @@
package xtools.app.sys.log.bus.elasticsearch.service.base;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Component;
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
/**
* <p>Title : EsSysLogRepository</p>
* <p>Description : EsSysLogRepository</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/2/13 19:42
*/
@Component
public interface EsSysLogRepository extends ElasticsearchRepository<EsSysLog, String> {
}

View File

@@ -0,0 +1,307 @@
package xtools.app.sys.log.bus.elasticsearch.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import xtools.app.sys.log.bus.elasticsearch.convert.SysLogElasticsearchConvert;
import xtools.app.sys.log.bus.elasticsearch.dto.EsSysLog;
import xtools.app.sys.log.bus.elasticsearch.service.base.EsSysLogRepository;
import xtools.app.sys.model.dto.req.SysLogPageReq;
import xtools.app.sys.model.dto.resp.SysLogResp;
import xtools.app.sys.model.entity.SysLog;
import xtools.app.sys.service.SysLogService;
import xtools.base.config.BaseParams;
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.boot.elasticsearch.utils.EsQueryUtils;
import xtools.boot.elasticsearch.utils.EsUtils;
import xtools.boot.log.LogBus;
import xtools.boot.log.enums.LogBusBaseType;
import xtools.core.CollectionUtils;
import xtools.core.enums.LogLevel;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* <p>Title : EsSysLogServiceImpl</p>
* <p>Description : EsSysLogServiceImpl</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/2/13 19:43
*/
public class EsSysLogServiceImpl implements SysLogService, BaseParams {
@Resource
private EsSysLogRepository esSysLogRepository;
@Resource
private SysLogElasticsearchConvert sysLogElasticsearchConvert;
/**
* 分页查询
*
* @param pageReq 分页请求
* @return 分页结果
*/
@Override
public Result<PageResp<SysLogResp>> page(PageReq<SysLogPageReq> pageReq) {
// 查询条件
JSONObject req = getPageReq(pageReq.getCurrentPage(), pageReq.getPageSize(), pageReq.getQuery());
// 结果
JSONObject resp = null;
try {
resp = EsUtils.exec(getDbUri() + "_search", req);
} catch (Exception e) {
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
}
return Result.ok(getPageResult(pageReq.getCurrentPage(), pageReq.getPageSize(), resp));
}
/**
* 获取分页结果
*
* @param currentPage 当前页
* @param pageSize 页大小
* @param resp 查询结果
* @return 分页结果
*/
private PageResp<SysLogResp> getPageResult(Integer currentPage, Integer pageSize, JSONObject resp) {
PageResp<SysLogResp> pageResp = new PageResp<>();
pageResp.setCurrentPage(currentPage);
pageResp.setPageSize(pageSize);
pageResp.setTotal(0L);
if (CollectionUtils.isEmpty(resp)) {
return pageResp;
}
JSONObject hits = resp.getJSONObject("hits");
if (CollectionUtils.isEmpty(hits)) {
return pageResp;
}
Long total = hits.getJSONObject("total").getLong("value");
JSONArray resultList = hits.getJSONArray("hits");
if (CollectionUtils.isEmpty(resultList)) {
return pageResp;
}
List<SysLogResp> dataList = new ArrayList<>();
for (Object obj : resultList) {
if (obj instanceof JSONObject item) {
JSONObject source = item.getJSONObject("_source");
source.remove("logBody");
source.remove("stackTrace");
source.remove("logError");
dataList.add(JSON.to(SysLogResp.class, source));
}
}
pageResp.setData(dataList);
pageResp.setTotal(total);
return pageResp;
}
/**
* 获取分页查询条件
*
* @param currentPage 当前页
* @param pageSize 页大小
* @param req 请求参数
* @return 查询条件
*/
private JSONObject getPageReq(Integer currentPage, Integer pageSize, SysLogPageReq req) {
// 查询条件处理
JSONArray must = new JSONArray();
EsQueryUtils.setTimeRange(must, "logTime", req.getLogTimeRange());
EsQueryUtils.setMatchPhrase(must, "traceId", req.getTraceId());
EsQueryUtils.setMatchPhrase(must, "logType", req.getLogType());
EsQueryUtils.setMatchPhrase(must, "logLevel", req.getLogLevel());
EsQueryUtils.setWildcard(must, "title", req.getTitle());
EsQueryUtils.setWildcard(must, "ip", req.getIp());
EsQueryUtils.setWildcard(must, "uri", req.getUri());
EsQueryUtils.setMatchPhrase(must, "threadType", req.getThreadType());
EsQueryUtils.setWildcard(must, "appName", req.getAppName());
EsQueryUtils.setWildcard(must, "localIp", req.getLocalIp());
EsQueryUtils.setWildcard(must, "port", req.getPort());
JSONObject query = JSONObject.of("bool", JSONObject.of("must", must));
// 排序条件
JSONArray sort = new JSONArray();
sort.add(JSONObject.of("logTime", JSONObject.of("order", "desc")));
sort.add(JSONObject.of("logIndex", JSONObject.of("order", "desc")));
// 完整查询器
return JSONObject.of("from", ((currentPage - 1) * pageSize), "size", pageSize, "query", query, "sort", sort);
}
/**
* 根据日志 ID 查询
*
* @param logId 日志 ID
* @return 结果
*/
@Override
public Result<SysLogResp> getByLogId(String logId) {
// 查询条件
JSONArray fields = new JSONArray();
fields.add("logId");
JSONObject query = JSONObject.of("multi_match", JSONObject.of("query", logId, "fields", fields));
// 完整查询器
JSONObject req = JSONObject.of("from", CP_NUM0, "size", CP_NUM1, "query", query);
// 结果
JSONObject resp = null;
try {
resp = EsUtils.exec(getDbUri() + "_search", req);
} catch (Exception e) {
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
}
List<SysLogResp> result = getQueryByTraceIdResult(resp);
if (CollectionUtils.isEmpty(result)) {
throw new BizError("日志ID不存在");
}
SysLogResp data = new SysLogResp();
data.setLogBody(result.getFirst().getLogBody());
return Result.ok(data);
}
/**
* 根据traceId查询
*
* @param traceId traceId
* @return 日志列表
*/
@Override
public Result<List<SysLogResp>> getByTraceId(String traceId) {
// 获取查询器
JSONObject req = getQueryByTraceIdReq(traceId);
// 结果
JSONObject resp = null;
try {
resp = EsUtils.exec(getDbUri() + "_search", req);
} catch (Exception e) {
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES查询失败").error(e).save();
}
return Result.ok(getQueryByTraceIdResult(resp));
}
/**
* 获取查询请求参数
*
* @param traceId 日志追踪ID
* @return 查询请求参数
*/
private JSONObject getQueryByTraceIdReq(String traceId) {
// 查询条件
JSONArray fields = new JSONArray();
fields.add("traceId");
JSONObject query = JSONObject.of("multi_match", JSONObject.of("query", traceId, "fields", fields));
// 排序条件
JSONArray sort = new JSONArray();
sort.add(JSONObject.of("logIndex", JSONObject.of("order", "asc")));
sort.add(JSONObject.of("logTime", JSONObject.of("order", "asc")));
// 完整查询器
return JSONObject.of("from", CP_NUM0, "size", CP_NUM10 * CP_NUM1000, "query", query, "sort", sort);
}
/**
* 获取查询结果
*
* @param resp 结果集
* @return 查询结果
*/
private List<SysLogResp> getQueryByTraceIdResult(JSONObject resp) {
if (CollectionUtils.isEmpty(resp)) {
return null;
}
JSONArray resultList = resp.getJSONObject("hits").getJSONArray("hits");
if (CollectionUtils.isEmpty(resultList)) {
return null;
}
List<SysLogResp> dataList = new ArrayList<>();
for (Object obj : resultList) {
if (obj instanceof JSONObject item) {
JSONObject source = item.getJSONObject("_source");
dataList.add(JSON.to(SysLogResp.class, source));
}
}
return dataList;
}
/**
* 保存
*
* @param sysLog 日志
* @return 是否保存成功
*/
@Override
public boolean save(SysLog sysLog) {
EsSysLog esSysLog = sysLogElasticsearchConvert.entityToEs(sysLog);
esSysLogRepository.save(esSysLog);
return true;
}
/**
* 删除
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 删除数量
*/
@Override
public long delete(Instant startTime, Instant endTime) {
if (Objects.isNull(endTime)) {
return CP_NUM0;
}
long end = endTime.toEpochMilli();
long start = CP_NUM0;
if (Objects.nonNull(startTime)) {
start = startTime.toEpochMilli();
}
JSONObject req = JSONObject.of(
"query",
JSONObject.of(
"range",
JSONObject.of(
"logTime",
JSONObject.of(
"gte", start,
"lte", end
)
)
)
);
// 结果
JSONObject resp = null;
try {
resp = EsUtils.exec(getDbUri() + "_delete_by_query", req);
} catch (Exception e) {
LogBus.init(LogLevel.ERROR, LogBusBaseType.ELASTICSEARCH).data(req).title("系统日志ES删除失败").error(e).save();
}
if (CollectionUtils.isEmpty(resp)) {
return CP_NUM0;
}
Long deleted = resp.getLong("deleted");
return Objects.nonNull(deleted) ? deleted : CP_NUM0;
}
/**
* 获取DB的请求路径
*
* @return DB的请求路径
*/
private String getDbUri() {
return CP_SLASH + EsSysLog.INDEX_NAME + CP_SLASH;
}
}