From 821bec35b569044a736021b36181b50c0b9606ac Mon Sep 17 00:00:00 2001 From: luke Date: Mon, 19 May 2025 23:52:15 +0800 Subject: [PATCH] init --- .gitignore | 37 +++ Readme.md | 3 + pom.xml | 295 ++++++++++++++++++ .../com/doc/parser/DocParserApplication.java | 16 + .../parser/application/anno/OperationLog.java | 14 + .../aspect/OperationLogAspect.java | 42 +++ .../application/enums/ErrorCodeEnum.java | 23 ++ .../application/enums/ExceptionTypeEnum.java | 20 ++ .../parser/application/enums/MDCKeyEnum.java | 20 ++ .../application/exceptions/BizException.java | 24 ++ .../parser/application/filter/LogFilter.java | 98 ++++++ .../filter/ServletTraceIdFilter.java | 47 +++ .../infrastructure/config/ConstantConfig.java | 9 + .../infrastructure/config/DynamicConfig.java | 21 ++ .../infrastructure/north/dto/ApiResponse.java | 111 +++++++ .../infrastructure/util/LocalCacheUtil.java | 103 ++++++ src/main/resources/application-dev.properties | 22 ++ .../resources/application-prod.properties | 2 + .../resources/application-test.properties | 0 src/main/resources/application.properties | 12 + src/main/resources/log/log4j.xml | 67 ++++ 21 files changed, 986 insertions(+) create mode 100644 .gitignore create mode 100644 Readme.md create mode 100644 pom.xml create mode 100644 src/main/java/com/doc/parser/DocParserApplication.java create mode 100644 src/main/java/com/doc/parser/application/anno/OperationLog.java create mode 100644 src/main/java/com/doc/parser/application/aspect/OperationLogAspect.java create mode 100644 src/main/java/com/doc/parser/application/enums/ErrorCodeEnum.java create mode 100644 src/main/java/com/doc/parser/application/enums/ExceptionTypeEnum.java create mode 100644 src/main/java/com/doc/parser/application/enums/MDCKeyEnum.java create mode 100644 src/main/java/com/doc/parser/application/exceptions/BizException.java create mode 100644 src/main/java/com/doc/parser/application/filter/LogFilter.java create mode 100644 src/main/java/com/doc/parser/application/filter/ServletTraceIdFilter.java create mode 100644 src/main/java/com/doc/parser/infrastructure/config/ConstantConfig.java create mode 100644 src/main/java/com/doc/parser/infrastructure/config/DynamicConfig.java create mode 100644 src/main/java/com/doc/parser/infrastructure/north/dto/ApiResponse.java create mode 100644 src/main/java/com/doc/parser/infrastructure/util/LocalCacheUtil.java create mode 100644 src/main/resources/application-dev.properties create mode 100644 src/main/resources/application-prod.properties create mode 100644 src/main/resources/application-test.properties create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/log/log4j.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f251485 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +/arthas-output/ +/doc-parser-server-log/ + +### Mac OS ### +.DS_Store diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..0b9d2bf --- /dev/null +++ b/Readme.md @@ -0,0 +1,3 @@ +#TODO + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..dfeddb3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,295 @@ + + + 4.0.0 + + luke.micro.saas + doc-parser-server + 0.0.1-SNAPSHOT + doc-parser-server + 文档资源解析服务 + + + 11 + UTF-8 + UTF-8 + 2.7.6 + 3.6.7 + + 1.7.36 + 2.17.1 + + 1.9.9.1 + + + + 3.1.5 + 2021.0.5.0 + 3.1.5 + 2.13.4.2 + 2.13.4 + + 1.18.24 + 3.12.0 + 5.7.20 + 31.0.1-jre + + + + + com.taobao.arthas + arthas-spring-boot-starter + ${arthas-spring-boot-starter.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-test + test + ${spring-boot.version} + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.core.version} + + + + org.apache.logging.log4j + log4j-api + ${log4j.core.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.core.version} + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + org.aspectj + aspectjrt + ${aspectj.version} + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${spring-cloud-starter-openfeign.version} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${spring-cloud-starter-alibaba-nacos.version} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${spring-cloud-starter-alibaba-nacos.version} + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + ${spring-cloud-starter-loadbalancer.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-core.version} + + + + org.projectlombok + lombok + true + ${lombok.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + cn.hutool + hutool-all + ${hutool-all.version} + + + com.google.guava + guava + ${guava.version} + + + + + + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + org.aspectj + aspectjweaver + + + org.aspectj + aspectjrt + + + + com.taobao.arthas + arthas-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + logback-classic + ch.qos.logback + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + + + + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + + + org.projectlombok + lombok + true + + + org.apache.commons + commons-lang3 + + + cn.hutool + hutool-all + + + com.google.guava + guava + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + UTF-8 + + + org.apache.logging.log4j + log4j-core + 2.17.1 + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.doc.parser.DocParserApplication + + + + repackage + + repackage + + + + + + + + diff --git a/src/main/java/com/doc/parser/DocParserApplication.java b/src/main/java/com/doc/parser/DocParserApplication.java new file mode 100644 index 0000000..9daca8f --- /dev/null +++ b/src/main/java/com/doc/parser/DocParserApplication.java @@ -0,0 +1,16 @@ +package com.doc.parser; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Luke.ye + */ +@SpringBootApplication +public class DocParserApplication { + + public static void main(String[] args) { + SpringApplication.run(DocParserApplication.class, args); + } + +} diff --git a/src/main/java/com/doc/parser/application/anno/OperationLog.java b/src/main/java/com/doc/parser/application/anno/OperationLog.java new file mode 100644 index 0000000..aa576db --- /dev/null +++ b/src/main/java/com/doc/parser/application/anno/OperationLog.java @@ -0,0 +1,14 @@ +package com.doc.parser.application.anno; + +import java.lang.annotation.*; + +/** + * @author Luke.ye + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@Inherited +@Documented +public @interface OperationLog { + +} diff --git a/src/main/java/com/doc/parser/application/aspect/OperationLogAspect.java b/src/main/java/com/doc/parser/application/aspect/OperationLogAspect.java new file mode 100644 index 0000000..712a746 --- /dev/null +++ b/src/main/java/com/doc/parser/application/aspect/OperationLogAspect.java @@ -0,0 +1,42 @@ +package com.doc.parser.application.aspect; + +import cn.hutool.json.JSONUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + + +/** + * @author Luke.ye + * @date 2025/5/7 15:01 + */ +@Component +@Aspect +public class OperationLogAspect { + private static final Logger LOGGER = LoggerFactory.getLogger(OperationLogAspect.class); + + @Around("@annotation(com.doc.parser.application.anno.OperationLog)") + public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Object result = null; + try { + result = joinPoint.proceed(); + LOGGER.info("方法名 [{}] , 输入参数: {}, 返回结果: {}", method.getName() + , JSONUtil.toJsonStr(joinPoint.getArgs()), JSONUtil.toJsonStr(result)); + } catch (Throwable ex) { + LOGGER.error("方法名 [{}] 抛出异常: {}", method.getName(), ex.getMessage(), ex); + throw ex; + } + + return result; + } + +} diff --git a/src/main/java/com/doc/parser/application/enums/ErrorCodeEnum.java b/src/main/java/com/doc/parser/application/enums/ErrorCodeEnum.java new file mode 100644 index 0000000..527ea59 --- /dev/null +++ b/src/main/java/com/doc/parser/application/enums/ErrorCodeEnum.java @@ -0,0 +1,23 @@ +package com.doc.parser.application.enums; + +/** + * @Author: luke ye + * @Date:05/06/2025 15:19 + * @Description: + */ +public enum ErrorCodeEnum { + + INVALID_PARAMS(1, "参数错误"), + + INNER_SERVER_ERROR(99, "服务端处理异常") + ; + + public int code; + + public String desc; + + ErrorCodeEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/src/main/java/com/doc/parser/application/enums/ExceptionTypeEnum.java b/src/main/java/com/doc/parser/application/enums/ExceptionTypeEnum.java new file mode 100644 index 0000000..5093e20 --- /dev/null +++ b/src/main/java/com/doc/parser/application/enums/ExceptionTypeEnum.java @@ -0,0 +1,20 @@ +package com.doc.parser.application.enums; + +/** + * @author Luke.ye + * @date 2025/5/6 08:49 + */ +public enum ExceptionTypeEnum { + INVALID_PARAMS(1, "请求参数校验失败!"), + + ; + + public int code; + + public String desc; + + ExceptionTypeEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/src/main/java/com/doc/parser/application/enums/MDCKeyEnum.java b/src/main/java/com/doc/parser/application/enums/MDCKeyEnum.java new file mode 100644 index 0000000..a4b2849 --- /dev/null +++ b/src/main/java/com/doc/parser/application/enums/MDCKeyEnum.java @@ -0,0 +1,20 @@ +package com.doc.parser.application.enums; + +/** + * @author Luke.ye + * @date 2024/11/6 21:21 + */ +public enum MDCKeyEnum { + + TRACE_ID("traceId", "日志跟踪号"), + HOST_NAME("hostName", "主机名") + ; + public String name; + public String desc; + + MDCKeyEnum(String name, String desc) { + this.name = name; + this.desc = desc; + } + +} diff --git a/src/main/java/com/doc/parser/application/exceptions/BizException.java b/src/main/java/com/doc/parser/application/exceptions/BizException.java new file mode 100644 index 0000000..fa3f471 --- /dev/null +++ b/src/main/java/com/doc/parser/application/exceptions/BizException.java @@ -0,0 +1,24 @@ +package com.doc.parser.application.exceptions; + +/** + * @author Luke.ye + * @date 2025/5/6 08:47 + */ +public class BizException extends RuntimeException { + + private int code; + + public BizException(int code, String message) { + super(message); + this.code = code; + } + + public BizException(int code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/com/doc/parser/application/filter/LogFilter.java b/src/main/java/com/doc/parser/application/filter/LogFilter.java new file mode 100644 index 0000000..c23190b --- /dev/null +++ b/src/main/java/com/doc/parser/application/filter/LogFilter.java @@ -0,0 +1,98 @@ +package com.doc.parser.application.filter; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.doc.parser.application.enums.MDCKeyEnum; +import com.doc.parser.infrastructure.config.DynamicConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.InetAddress; +import java.util.Optional; +import java.util.UUID; + +/** + * Description: 交易日志记录 + */ +@Component +public class LogFilter extends OncePerRequestFilter { + private final static Logger LOGGER = LoggerFactory.getLogger(LogFilter.class); + + @Autowired + private DynamicConfig dynamicConfig; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + ContentCachingRequestWrapper contentCachingRequestWrapper = new ContentCachingRequestWrapper(request); + ContentCachingResponseWrapper contentCachingResponseWrapper = new ContentCachingResponseWrapper(response); + if (!"Y".equals(dynamicConfig.getRecordMsgBody())) { + // 不记录请求和响应信息 + filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper); + contentCachingResponseWrapper.copyBodyToResponse(); + return; + } + + StopWatch stopWatch = new StopWatch(); + try { + stopWatch.start(); + //获取http header 中的traceid,没有默认生成一个,放入到mdc中,进行日志打印 + String traceId = Optional.ofNullable(request.getHeader("traceId")) + .orElse(UUID.randomUUID().toString().replace("-", "")); + InetAddress addr = InetAddress.getLocalHost(); + MDC.put(MDCKeyEnum.HOST_NAME.name, addr.getHostName()); + MDC.put(MDCKeyEnum.TRACE_ID.name, traceId); + filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper); + + } catch (Exception e) { + LOGGER.warn("交易跟踪号[{}]记录交易报文信息异常:{}", MDC.get(MDCKeyEnum.TRACE_ID.name), e.getMessage()); + + } finally { + byte[] requestBody = contentCachingRequestWrapper.getContentAsByteArray(); + byte[] responseBody = contentCachingResponseWrapper.getContentAsByteArray(); + try { + //获取请求路径 + String url = contentCachingRequestWrapper.getRequestURL().toString(); + //获取报文头交易码 + String tranCode = Optional.ofNullable(contentCachingRequestWrapper.getHeader("TRANCODE")).orElse("UNKOWN"); + //获取IP地址 + String remoteIp = getRemortIP(contentCachingRequestWrapper); + //获取IP端口 + int remotePort = contentCachingRequestWrapper.getRemotePort(); + stopWatch.stop(); + + LOGGER.info("交易跟踪号[{}]\n请求路径[{}]\n交易码[{}]\n远程地址[{}]\n远程端口[{}]\n请求内容:[{}]\n响应内容:[{}]\n响应时间:[{}]毫秒", + MDC.get(MDCKeyEnum.TRACE_ID.name), + url, + tranCode, + remoteIp, + remotePort, + JSONUtil.toJsonStr(new String(requestBody)), + JSONUtil.toJsonStr(new String(responseBody)), + stopWatch.getTotalTimeMillis()); + } catch (Exception e) { + LOGGER.warn("登记交易信息异常:" + e.getMessage()); + } + MDC.remove(MDCKeyEnum.HOST_NAME.name); + MDC.remove(MDCKeyEnum.TRACE_ID.name); + contentCachingResponseWrapper.copyBodyToResponse(); + } + } + + private String getRemortIP(HttpServletRequest request) { + return StrUtil.isBlank(request.getHeader("x-forwarded-for")) ? + request.getRemoteAddr() : + request.getHeader("x-forwarded-for"); + } +} diff --git a/src/main/java/com/doc/parser/application/filter/ServletTraceIdFilter.java b/src/main/java/com/doc/parser/application/filter/ServletTraceIdFilter.java new file mode 100644 index 0000000..7165dc7 --- /dev/null +++ b/src/main/java/com/doc/parser/application/filter/ServletTraceIdFilter.java @@ -0,0 +1,47 @@ +package com.doc.parser.application.filter; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +import cn.hutool.core.util.IdUtil; +import com.doc.parser.application.enums.MDCKeyEnum; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.ThreadContext; + + +/** + * #TODO 使用log4j的traceId无法解决异步记录日志传递traceId的问题,暂时先使用servlet的filter来实现 + * + * @author Luke.ye + * @date 2025/5/6 14:21 + */ +//@Component +public class ServletTraceIdFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException {} + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + if (StringUtils.isBlank(ThreadContext.get(MDCKeyEnum.TRACE_ID.name))) { + // 如果没有 traceId,则生成并添加一个新的 traceId + String traceId = IdUtil.randomUUID(); + ThreadContext.put(MDCKeyEnum.TRACE_ID.name, traceId); + } + try { + chain.doFilter(request, response); // 执行业务逻辑 + } finally { + ThreadContext.clearMap(); // 清理上下文 + } + } + + @Override + public void destroy() {} + +} + diff --git a/src/main/java/com/doc/parser/infrastructure/config/ConstantConfig.java b/src/main/java/com/doc/parser/infrastructure/config/ConstantConfig.java new file mode 100644 index 0000000..1f6fc6f --- /dev/null +++ b/src/main/java/com/doc/parser/infrastructure/config/ConstantConfig.java @@ -0,0 +1,9 @@ +package com.doc.parser.infrastructure.config; + +/** + * @author Luke.ye + * @date 2025/5/19 11:18 + */ +public class ConstantConfig { + +} diff --git a/src/main/java/com/doc/parser/infrastructure/config/DynamicConfig.java b/src/main/java/com/doc/parser/infrastructure/config/DynamicConfig.java new file mode 100644 index 0000000..46488f3 --- /dev/null +++ b/src/main/java/com/doc/parser/infrastructure/config/DynamicConfig.java @@ -0,0 +1,21 @@ +package com.doc.parser.infrastructure.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +/** + * @author Luke.ye + * @date 2025/5/8 09:59 + */ +@Component +@RefreshScope +public class DynamicConfig { + //是否记录请求和响应信息 Y-记录 N-不记录 默认记录 + @Value("${micro.saas.doc.parser.recordMsgBody:Y}") + private String recordMsgBody; + + public String getRecordMsgBody() { + return recordMsgBody; + } +} diff --git a/src/main/java/com/doc/parser/infrastructure/north/dto/ApiResponse.java b/src/main/java/com/doc/parser/infrastructure/north/dto/ApiResponse.java new file mode 100644 index 0000000..0feb5f1 --- /dev/null +++ b/src/main/java/com/doc/parser/infrastructure/north/dto/ApiResponse.java @@ -0,0 +1,111 @@ +package com.doc.parser.infrastructure.north.dto; + +import cn.hutool.core.bean.BeanUtil; +import com.doc.parser.application.enums.ErrorCodeEnum; + +import java.io.Serializable; +import java.util.Map; + +/** + * @Author: luke ye + * @Date:05/06/2025 16:54 + * @Description: + */ +public class ApiResponse implements Serializable { + private static final long serialVersionUID = -7006468489245806612L; + + private boolean status; + + /** + * {@link ErrorCodeEnum} + */ + private int errorCode; + private String errorMessage; + private T data; + + public ApiResponse() { + } + + public ApiResponse(boolean status) { + this.status = status; + } + + public static ApiResponse toSuccess(T result) { + ApiResponse response = new ApiResponse<>(); + response.setStatus(true); + response.setData(result); + return response; + } + + + public static ApiResponse toFalse() { + ApiResponse apiResponse = new ApiResponse<>(); + apiResponse.setStatus(false); + return apiResponse; + } + + public static ApiResponse toFailure(String errorMessage) { + ApiResponse response = new ApiResponse<>(); + response.setStatus(false); + response.setErrorMessage(errorMessage); + return response; + } + + public static ApiResponse toFailure(int errorCode) { + ApiResponse response = new ApiResponse<>(); + response.setStatus(false); + response.setErrorCode(errorCode); + return response; + } + + public static ApiResponse toFailure(int errorCode, String errorMessage) { + return toFailure(errorCode, errorMessage, null); + } + + public static ApiResponse toFailure(int errorCode, String errorMessage, T result) { + ApiResponse response = new ApiResponse<>(); + response.setStatus(false); + response.setErrorCode(errorCode); + response.setErrorMessage(errorMessage); + if (result != null) { + response.setData(result); + } + return response; + } + + public Map toMap() { + return BeanUtil.beanToMap(this); + } + + public boolean getStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} \ No newline at end of file diff --git a/src/main/java/com/doc/parser/infrastructure/util/LocalCacheUtil.java b/src/main/java/com/doc/parser/infrastructure/util/LocalCacheUtil.java new file mode 100644 index 0000000..14091b0 --- /dev/null +++ b/src/main/java/com/doc/parser/infrastructure/util/LocalCacheUtil.java @@ -0,0 +1,103 @@ +package com.doc.parser.infrastructure.util; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +/** + * 使用 Guava 实现的本地缓存工具类 + * + * @author Luke.ye + * @date 2025/5/7 08:46 + */ +public class LocalCacheUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(LocalCacheUtil.class); + + // 默认缓存配置 + private static final int DEFAULT_MAX_CACHE_SIZE = 1000; // 最大缓存条目数 + private static final int DEFAULT_INITIAL_CAPACITY = 400; // 缓存初始容量 + private static final int DEFAULT_EXPIRATION_DAYS = 7; // 缓存过期时间(单位:天) + + private static Cache cache; + + static { + // 初始化缓存(使用默认配置) + initializeCache(DEFAULT_MAX_CACHE_SIZE, DEFAULT_INITIAL_CAPACITY, DEFAULT_EXPIRATION_DAYS); + } + + /** + * 初始化缓存。 + * + * @param maxSize 缓存的最大容量。 + * @param initialCapacity 缓存的初始容量。 + * @param expirationDays 缓存条目的过期时间(单位:天)。 + */ + private static void initializeCache(int maxSize, int initialCapacity, int expirationDays) { + cache = CacheBuilder.newBuilder() + .expireAfterAccess(expirationDays, TimeUnit.DAYS) // 设置访问过期时间 + .initialCapacity(initialCapacity) // 设置初始容量 + .maximumSize(maxSize) // 设置最大缓存条目数 + .build(); + } + + /** + * 根据 key 获取缓存中的值。 + * + * @param key 缓存的键。 + * @return 返回对应的值,如果键不存在则返回 null。 + */ + public static Object get(String key) { + return cache.getIfPresent(key); + } + + /** + * 向缓存中存储一个键值对。 + * 如果缓存容量接近上限,记录警告日志。 + * + * @param key 缓存的键。 + * @param value 缓存的值。 + */ + public static void put(String key, Object value) { + long size = cache.size(); + if (size >= cache.stats().loadCount() && size >= DEFAULT_MAX_CACHE_SIZE >> 1) { + LOGGER.warn("缓存接近最大容量 {},当前大小为 {},建议增加缓存容量。", DEFAULT_MAX_CACHE_SIZE, size); + } + cache.put(key, value); + } + + /** + * 根据 key 移除缓存中的条目。 + * + * @param key 要移除的键。 + */ + public static void remove(String key) { + cache.invalidate(key); + } + + /** + * 清空缓存中的所有条目。 + */ + public static void clearAll() { + cache.invalidateAll(); + } + + /** + * 查询缓存中的所有键值对。 + * + * @return 返回包含缓存内容的并发映射。 + */ + public static ConcurrentMap getAll() { + return cache.asMap(); + } + + /** + * 打印当前缓存统计信息(如命中率、加载次数等)。 + */ + public static void logCacheStats() { + LOGGER.info("缓存最大容量 {},当前大小为 {}, 缓存统计信息: {}", DEFAULT_MAX_CACHE_SIZE, cache.size(), cache.stats().toString()); + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..3918672 --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,22 @@ +# 日志 +logging.config=classpath:log/log4j.xml + +# nacos相关 +## nacos公用 +spring.cloud.nacos.config.data-id-without-suffix=nacos +spring.cloud.nacos.config.file-extension=properties +spring.config.import=nacos:${spring.cloud.nacos.config.data-id-without-suffix}.${spring.cloud.nacos.config.file-extension}?refresh=true +## Nacos配置中心 +spring.cloud.nacos.config.username=nacos +spring.cloud.nacos.config.password=lukeye +spring.cloud.nacos.config.contextPath=/nacos +spring.cloud.nacos.config.server-addr=http://101.132.255.39:8848 +spring.cloud.nacos.config.namespace=lukeye +spring.cloud.nacos.config.refreshEnabled=true + +# Arthas配置 +arthas.telnetPort=-1 +arthas.httpPort=-1 +arthas.ip=127.0.0.1 +arthas.appName=${spring.application.name} +arthas.tunnel-server=ws://101.132.255.39:7777/ws \ No newline at end of file diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..b6dbda7 --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,2 @@ +# TODO + diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..a528f65 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,12 @@ + +spring.application.name=doc-parser-server +application.author=Luke.Ye +server.port=18080 + +# 日志相关 +logging.config=classpath:log/log4j.xml +log4j2.enable.threadlocals=true +logging.level.com.doc.parser=info +logging.level.root=${logging.level.com.doc.parser} +logging.org.springframework.web=${logging.level.com.doc.parser} +logging.org.springframework.context=${logging.level.com.doc.parser} \ No newline at end of file diff --git a/src/main/resources/log/log4j.xml b/src/main/resources/log/log4j.xml new file mode 100644 index 0000000..5142e35 --- /dev/null +++ b/src/main/resources/log/log4j.xml @@ -0,0 +1,67 @@ + + + + + doc-parser-server-log + doc-parser-server + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file