diff --git a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java index c33784e9f..38637c5ea 100644 --- a/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java +++ b/common/common-framework/src/main/java/cn/iocoder/common/framework/util/MallUtil.java @@ -25,10 +25,12 @@ public class MallUtil { request.setAttribute(MallConstants.REQUEST_ATTR_USER_TYPE_KEY, userType); } + @Deprecated public static CommonResult getCommonResult(ServletRequest request) { return (CommonResult) request.getAttribute(MallConstants.REQUEST_ATTR_COMMON_RESULT); } + @Deprecated public static void setCommonResult(ServletRequest request, CommonResult result) { request.setAttribute(MallConstants.REQUEST_ATTR_COMMON_RESULT, result); } diff --git a/common/mall-spring-boot-starter-swagger/pom.xml b/common/mall-spring-boot-starter-swagger/pom.xml index 7631d474d..ef0181466 100644 --- a/common/mall-spring-boot-starter-swagger/pom.xml +++ b/common/mall-spring-boot-starter-swagger/pom.xml @@ -12,8 +12,6 @@ mall-spring-boot-starter-swagger - - org.springframework.boot diff --git a/common/mall-spring-boot-starter-web/pom.xml b/common/mall-spring-boot-starter-web/pom.xml index 80fd32f80..e90b22b63 100644 --- a/common/mall-spring-boot-starter-web/pom.xml +++ b/common/mall-spring-boot-starter-web/pom.xml @@ -17,6 +17,7 @@ cn.iocoder.mall system-rpc-api 1.0-SNAPSHOT + true diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/config/CommonWebAutoConfiguration.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/config/CommonWebAutoConfiguration.java index 8a02892fa..a2aee725d 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/config/CommonWebAutoConfiguration.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/config/CommonWebAutoConfiguration.java @@ -1,23 +1,53 @@ package cn.iocoder.mall.web.config; +import cn.iocoder.mall.web.constant.CommonMallConstants; +import cn.iocoder.mall.web.handler.GlobalResponseBodyHandler; import cn.iocoder.mall.web.interceptor.AccessLogInterceptor; -import org.apache.dubbo.config.annotation.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class CommonWebAutoConfiguration implements WebMvcConfigurer { - @Reference(validation = "true", version = "${dubbo.consumer.AdminAccessLogService.version:1.0.0}") -// @Reference(validation = "true", version = "1.0.0") -// private SystemLogRPC systemLogRPC; + private Logger logger = LoggerFactory.getLogger(getClass()); + + // ========== 全局处理器 ========== @Bean + @ConditionalOnMissingBean(GlobalResponseBodyHandler.class) + public GlobalResponseBodyHandler globalResponseBodyHandler() { + return new GlobalResponseBodyHandler(); + } + + // ========== 拦截器相关 ========== + + @Bean + @ConditionalOnClass(name = "cn.iocoder.mall.system.rpc.api.SystemLogRPC") + @ConditionalOnMissingBean(AccessLogInterceptor.class) public AccessLogInterceptor accessLogInterceptor() { return new AccessLogInterceptor(); } + @Override + public void addInterceptors(InterceptorRegistry registry) { + try { + AccessLogInterceptor accessLogInterceptor = this.accessLogInterceptor(); + if (accessLogInterceptor != null) { + registry.addInterceptor(accessLogInterceptor) + .addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**", CommonMallConstants.ROOT_PATH_USER + "/**"); + } + } catch (NoSuchBeanDefinitionException e) { + logger.warn("[addInterceptors][无法获取 AccessLogInterceptor 拦截器,因此不启动 AccessLog 的记录]"); + } + } + } diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/constant/CommonMallConstants.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/constant/CommonMallConstants.java new file mode 100644 index 000000000..1f9f79313 --- /dev/null +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/constant/CommonMallConstants.java @@ -0,0 +1,30 @@ +package cn.iocoder.mall.web.constant; + +public interface CommonMallConstants { + + // 全局请求路径枚举类,用于定义不同用户类型的根请求路径 + /** + * 根路径 - 用户 + */ + String ROOT_PATH_USER = "/users"; + /** + * 根路径 - 管理员 + */ + String ROOT_PATH_ADMIN = "/admins"; + + // HTTP Request Attr + /** + * HTTP Request Attr - 账号编号 + */ + String REQUEST_ATTR_USER_ID_KEY = "mall_account_id"; + /** + * HTTP Request Attr - Controller 执行返回 + */ + String REQUEST_ATTR_COMMON_RESULT = "mall_common_result"; + /** + * HTTP Request Attr - 访问开始时间 + */ + String REQUEST_ATTR_ACCESS_START_TIME = "mall_access_start_time"; + + +} diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/handler/GlobalResponseBodyHandler.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/handler/GlobalResponseBodyHandler.java index f86aed51b..1226d8597 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/handler/GlobalResponseBodyHandler.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/handler/GlobalResponseBodyHandler.java @@ -1,21 +1,45 @@ package cn.iocoder.mall.web.handler; -//@ControllerAdvice -//public class GlobalResponseBodyHandler implements ResponseBodyAdvice { -// -// @Override -// public boolean supports(MethodParameter returnType, Class converterType) { -// if (returnType.getMethod() == null) { -// return false; -// } -// return returnType.getMethod().getReturnType() == CommonResult.class; -// } -// -// @Override -// public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, -// ServerHttpRequest request, ServerHttpResponse response) { -// MallUtil.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult) body); -// return body; -// } -// -//} +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.web.util.CommonWebUtil; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * 全局响应结果(ResponseBody)处理器 + * + * 不同于在网上看到的很多文章,会选择自动将 Controller 返回结果包上 {@link CommonResult}, + * 在 onemall 中,是 Controller 在返回时,主动自己包上 {@link CommonResult}。 + * 原因是,GlobalResponseBodyHandler 本质上是 AOP,它不应该改变 Controller 返回的数据结构 + * + * 目前,GlobalResponseBodyHandler 的主要作用是,记录 Controller 的返回结果, + * 方便 {@link cn.iocoder.mall.web.interceptor.AccessLogInterceptor} 记录访问日志 + */ +@ControllerAdvice +public class GlobalResponseBodyHandler implements ResponseBodyAdvice { + + @Override + @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 + public boolean supports(MethodParameter returnType, Class converterType) { + if (returnType.getMethod() == null) { + return false; + } + // 只拦截返回结果为 CommonResult 类型 + return returnType.getMethod().getReturnType() == CommonResult.class; + } + + @Override + @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, + ServerHttpRequest request, ServerHttpResponse response) { + // 记录 Controller 结果 + CommonWebUtil.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult) body); + return body; + } + +} diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/interceptor/AccessLogInterceptor.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/interceptor/AccessLogInterceptor.java index c095333ff..29c9c909f 100644 --- a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/interceptor/AccessLogInterceptor.java +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/interceptor/AccessLogInterceptor.java @@ -1,102 +1,86 @@ package cn.iocoder.mall.web.interceptor; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; +import cn.iocoder.common.framework.util.HttpUtil; +import cn.iocoder.common.framework.util.MallUtil; +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.system.rpc.api.SystemLogRPC; +import cn.iocoder.mall.system.rpc.request.system.AccessLogAddRequest; +import cn.iocoder.mall.web.util.CommonWebUtil; +import com.alibaba.fastjson.JSON; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.dubbo.config.annotation.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.util.Assert; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Date; + /** * 访问日志拦截器 */ -public class AccessLogInterceptor extends HandlerInterceptorAdapter implements InitializingBean, ApplicationContextAware { +public class AccessLogInterceptor extends HandlerInterceptorAdapter { - private ApplicationContext context; + private Logger logger = LoggerFactory.getLogger(getClass()); -// -// private Logger logger = LoggerFactory.getLogger(getClass()); -// -// /** -// * 开始时间 -// */ -// private static final ThreadLocal START_TIME = new ThreadLocal<>(); -// -// @Reference(validation = "true", version = "${dubbo.consumer.AdminAccessLogService.version:1.0.0}") -// private SystemLogService systemAccessLogService; -// -// @Value("${spring.application.name}") -// private String applicationName; -// -// @Override -// public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { -// // 记录当前时间 -// START_TIME.set(new Date()); -// return true; -// } -// -// @Override -// public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { -// AccessLogAddDTO accessLog = new AccessLogAddDTO(); -// try { -// // 初始化 accessLog -// initAccessLog(accessLog, request); -// // 执行插入 accessLog -// addAccessLog(accessLog); -// // TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。 -// } catch (Throwable th) { -// logger.error("[afterCompletion][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); -// } finally { -// clear(); -// } -// } -// -// private void initAccessLog(AccessLogAddDTO accessLog, HttpServletRequest request) { -// // 设置用户编号 -// accessLog.setUserId(MallUtil.getUserId(request)); -// if (accessLog.getUserId() == null) { -// accessLog.setUserId(AccessLogAddDTO.USER_ID_NULL); -// } -// accessLog.setUserType(MallUtil.getUserType(request)); -// // 设置访问结果 -// CommonResult result = MallUtil.getCommonResult(request); -// Assert.isTrue(result != null, "result 必须非空"); -// accessLog.setErrorCode(result.getCode()) -// .setErrorMessage(result.getMessage()); -// // 设置其它字段 -// accessLog.setTraceId(MallUtil.getTraceId()) -// .setApplicationName(applicationName) -// .setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。 -// .setQueryString(HttpUtil.buildQueryString(request)) -// .setMethod(request.getMethod()) -// .setUserAgent(HttpUtil.getUserAgent(request)) -// .setIp(HttpUtil.getIp(request)) -// .setStartTime(START_TIME.get()) -// .setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime())); // 默认响应时间设为 0 -// } -// -// @Async // 异步入库 -// public void addAccessLog(AccessLogAddDTO accessLog) { -// try { -// systemAccessLogService.addAccessLog(accessLog); -// } catch (Throwable th) { -// logger.error("[addAccessLog][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); -// } -// } -// -// private static void clear() { -// START_TIME.remove(); -// } -// + @Reference(validation = "true", version = "${dubbo.consumer.SystemLogRPC.version}") + private SystemLogRPC systemLogRPC; + + @Value("${spring.application.name}") + private String applicationName; @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 记录当前时间 + CommonWebUtil.setAccessStartTime(request, new Date()); + return true; } @Override - public void afterPropertiesSet() throws Exception { - System.out.println(); + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + AccessLogAddRequest accessLog = new AccessLogAddRequest(); + try { + // 初始化 accessLog + initAccessLog(accessLog, request); + // 执行插入 accessLog + addAccessLog(accessLog); + // TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。 + } catch (Throwable th) { + logger.error("[afterCompletion][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); + } } + private void initAccessLog(AccessLogAddRequest accessLog, HttpServletRequest request) { + // 设置用户编号 + accessLog.setAccountId(CommonWebUtil.getAccountId(request)); + // 设置访问结果 + CommonResult result = CommonWebUtil.getCommonResult(request); + Assert.isTrue(result != null, "result 必须非空"); + accessLog.setErrorCode(result.getCode()) + .setErrorMessage(result.getMessage()); + // 设置其它字段 + accessLog.setTraceId(MallUtil.getTraceId()) + .setApplicationName(applicationName) + .setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。 + .setQueryString(HttpUtil.buildQueryString(request)) + .setMethod(request.getMethod()) + .setUserAgent(HttpUtil.getUserAgent(request)) + .setIp(HttpUtil.getIp(request)) + .setStartTime(CommonWebUtil.getAccessStartTime(request)) + .setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime())); // 默认响应时间设为 0 + } + + @Async // 异步入库 + public void addAccessLog(AccessLogAddRequest accessLog) { + try { + systemLogRPC.addAccessLog(accessLog); + } catch (Throwable th) { + logger.error("[addAccessLog][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); + } + } } diff --git a/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/util/CommonWebUtil.java b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/util/CommonWebUtil.java new file mode 100644 index 000000000..d755fa663 --- /dev/null +++ b/common/mall-spring-boot-starter-web/src/main/java/cn/iocoder/mall/web/util/CommonWebUtil.java @@ -0,0 +1,35 @@ +package cn.iocoder.mall.web.util; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.web.constant.CommonMallConstants; + +import javax.servlet.ServletRequest; +import java.util.Date; + +public class CommonWebUtil { + + public static Integer getAccountId(ServletRequest request) { + return (Integer) request.getAttribute(CommonMallConstants.REQUEST_ATTR_USER_ID_KEY); + } + + public static void setAccountId(ServletRequest request, Integer userId) { + request.setAttribute(CommonMallConstants.REQUEST_ATTR_USER_ID_KEY, userId); + } + + public static CommonResult getCommonResult(ServletRequest request) { + return (CommonResult) request.getAttribute(CommonMallConstants.REQUEST_ATTR_COMMON_RESULT); + } + + public static void setCommonResult(ServletRequest request, CommonResult result) { + request.setAttribute(CommonMallConstants.REQUEST_ATTR_COMMON_RESULT, result); + } + + public static void setAccessStartTime(ServletRequest request, Date startTime) { + request.setAttribute(CommonMallConstants.REQUEST_ATTR_ACCESS_START_TIME, startTime); + } + + public static Date getAccessStartTime(ServletRequest request) { + return (Date) request.getAttribute(CommonMallConstants.REQUEST_ATTR_ACCESS_START_TIME); + } + +} diff --git a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerAutoConfiguration.java b/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerAutoConfiguration.java deleted file mode 100644 index fff0db615..000000000 --- a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerAutoConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.mall.spring.boot.swagger; - -import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -/** - * 简单的 Swagger2 自动配置类 - * - * 较为完善的,可以了解 https://mvnrepository.com/artifact/com.spring4all/spring-boot-starter-swagger - */ -@Configuration -@EnableSwagger2 -@EnableSwaggerBootstrapUI -@ConditionalOnClass({Docket.class, ApiInfoBuilder.class}) -@ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true) // 允许使用 swagger.enable=false 禁用 Swagger -@EnableConfigurationProperties(SwaggerProperties.class) -@Deprecated -public class SwaggerAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - public SwaggerProperties swaggerProperties() { - return new SwaggerProperties(); - } - - @Bean - public Docket createRestApi() { - SwaggerProperties properties = swaggerProperties(); - // 创建 Docket 对象 - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo(properties)) - .select() - .apis(RequestHandlerSelectors.basePackage(properties.getBasePackage())) - .paths(PathSelectors.any()) - .build(); - } - - private ApiInfo apiInfo(SwaggerProperties properties) { - return new ApiInfoBuilder() - .title(properties.getTitle()) - .description(properties.getDescription()) - .version(properties.getVersion()) - .build(); - } - -} diff --git a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerProperties.java b/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerProperties.java deleted file mode 100644 index a04e224a7..000000000 --- a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/swagger/SwaggerProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.mall.spring.boot.swagger; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -@Data -@ConfigurationProperties("swagger") -@Deprecated -public class SwaggerProperties { - - private String title; - private String description; - private String version; - private String basePackage; - -} diff --git a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/handler/GlobalResponseBodyHandler.java b/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/handler/GlobalResponseBodyHandler.java deleted file mode 100644 index c10b72ca7..000000000 --- a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/handler/GlobalResponseBodyHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.mall.spring.boot.web.handler; - -import cn.iocoder.common.framework.util.MallUtil; -import cn.iocoder.common.framework.vo.CommonResult; -import org.springframework.core.MethodParameter; -import org.springframework.http.MediaType; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.http.server.ServletServerHttpRequest; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; - -@ControllerAdvice -public class GlobalResponseBodyHandler implements ResponseBodyAdvice { - - @Override - public boolean supports(MethodParameter returnType, Class converterType) { - if (returnType.getMethod() == null) { - return false; - } - return returnType.getMethod().getReturnType() == CommonResult.class; - } - - @Override - public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, - ServerHttpRequest request, ServerHttpResponse response) { - MallUtil.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult) body); - return body; - } - -} diff --git a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/interceptor/AccessLogInterceptor.java b/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/interceptor/AccessLogInterceptor.java deleted file mode 100644 index a0e951a2c..000000000 --- a/common/mall-spring-boot/src/main/java/cn/iocoder/mall/spring/boot/web/interceptor/AccessLogInterceptor.java +++ /dev/null @@ -1,102 +0,0 @@ -package cn.iocoder.mall.spring.boot.web.interceptor; - -import cn.iocoder.common.framework.util.HttpUtil; -import cn.iocoder.common.framework.util.MallUtil; -import cn.iocoder.common.framework.vo.CommonResult; -import cn.iocoder.mall.system.api.SystemLogService; -import cn.iocoder.mall.system.api.dto.systemlog.AccessLogAddDTO; -import com.alibaba.fastjson.JSON; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.dubbo.config.annotation.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Date; - -/** - * 访问日志拦截器 - */ -@Component -public class AccessLogInterceptor extends HandlerInterceptorAdapter { - - private Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * 开始时间 - */ - private static final ThreadLocal START_TIME = new ThreadLocal<>(); - - @Reference(validation = "true", version = "${dubbo.consumer.AdminAccessLogService.version:1.0.0}") - private SystemLogService systemAccessLogService; - - @Value("${spring.application.name}") - private String applicationName; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // 记录当前时间 - START_TIME.set(new Date()); - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { - AccessLogAddDTO accessLog = new AccessLogAddDTO(); - try { - // 初始化 accessLog - initAccessLog(accessLog, request); - // 执行插入 accessLog - addAccessLog(accessLog); - // TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。 - } catch (Throwable th) { - logger.error("[afterCompletion][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); - } finally { - clear(); - } - } - - private void initAccessLog(AccessLogAddDTO accessLog, HttpServletRequest request) { - // 设置用户编号 - accessLog.setUserId(MallUtil.getUserId(request)); - if (accessLog.getUserId() == null) { - accessLog.setUserId(AccessLogAddDTO.USER_ID_NULL); - } - accessLog.setUserType(MallUtil.getUserType(request)); - // 设置访问结果 - CommonResult result = MallUtil.getCommonResult(request); - Assert.isTrue(result != null, "result 必须非空"); - accessLog.setErrorCode(result.getCode()) - .setErrorMessage(result.getMessage()); - // 设置其它字段 - accessLog.setTraceId(MallUtil.getTraceId()) - .setApplicationName(applicationName) - .setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。 - .setQueryString(HttpUtil.buildQueryString(request)) - .setMethod(request.getMethod()) - .setUserAgent(HttpUtil.getUserAgent(request)) - .setIp(HttpUtil.getIp(request)) - .setStartTime(START_TIME.get()) - .setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime())); // 默认响应时间设为 0 - } - - @Async // 异步入库 - public void addAccessLog(AccessLogAddDTO accessLog) { - try { - systemAccessLogService.addAccessLog(accessLog); - } catch (Throwable th) { - logger.error("[addAccessLog][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th)); - } - } - - private static void clear() { - START_TIME.remove(); - } - -} diff --git a/common/mall-spring-boot/src/main/resources/META-INF/spring.factories b/common/mall-spring-boot/src/main/resources/META-INF/spring.factories index 8d7f6f54c..2c1124717 100644 --- a/common/mall-spring-boot/src/main/resources/META-INF/spring.factories +++ b/common/mall-spring-boot/src/main/resources/META-INF/spring.factories @@ -1,5 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.iocoder.mall.spring.boot.web.AdminMVCAutoConfiguration, \ cn.iocoder.mall.spring.boot.web.UserMVCAutoConfiguration, \ - cn.iocoder.mall.spring.boot.swagger.SwaggerAutoConfiguration, \ cn.iocoder.mall.spring.boot.metrics.MetricsAutoConfiguration diff --git a/system/system-application/src/main/resources/application.yaml b/system/system-application/src/main/resources/application.yaml index 412190a61..0c9dbffc4 100644 --- a/system/system-application/src/main/resources/application.yaml +++ b/system/system-application/src/main/resources/application.yaml @@ -1,7 +1,7 @@ spring: # Application 的配置项 application: - name: admin-application + name: system-application # Profile 的配置项 profiles: active: local diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dto/system/AccessLogAddDTO.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dto/system/AccessLogAddDTO.java index 44582399c..9855238d9 100644 --- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dto/system/AccessLogAddDTO.java +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/dto/system/AccessLogAddDTO.java @@ -1,11 +1,16 @@ package cn.iocoder.mall.system.biz.dto.system; +import lombok.Data; +import lombok.experimental.Accessors; + import javax.validation.constraints.NotNull; import java.util.Date; /** * 访问日志添加 DTO */ +@Data +@Accessors(chain = true) public class AccessLogAddDTO { /** diff --git a/system/system-rpc-api/pom.xml b/system/system-rpc-api/pom.xml index f3068d633..61568b1cd 100644 --- a/system/system-rpc-api/pom.xml +++ b/system/system-rpc-api/pom.xml @@ -19,11 +19,15 @@ 1.0-SNAPSHOT - + javax.validation validation-api + + org.projectlombok + lombok + diff --git a/system/system-rpc-api/src/main/java/cn/iocoder/mall/system/rpc/request/system/AccessLogAddRequest.java b/system/system-rpc-api/src/main/java/cn/iocoder/mall/system/rpc/request/system/AccessLogAddRequest.java index 1c3613388..a755043ed 100644 --- a/system/system-rpc-api/src/main/java/cn/iocoder/mall/system/rpc/request/system/AccessLogAddRequest.java +++ b/system/system-rpc-api/src/main/java/cn/iocoder/mall/system/rpc/request/system/AccessLogAddRequest.java @@ -1,8 +1,16 @@ package cn.iocoder.mall.system.rpc.request.system; +import lombok.Data; +import lombok.experimental.Accessors; + import javax.validation.constraints.NotNull; import java.util.Date; +/** + * 访问日志添加请求 + */ +@Data +@Accessors(chain = true) public class AccessLogAddRequest { /** @@ -10,7 +18,9 @@ public class AccessLogAddRequest { */ public static final Integer ACCOUNT_ID_NULL = 0; - @NotNull(message = "链路追踪编号不能为空") + /** + * 链路追踪编号 + */ private String traceId; /** * 账号编号 diff --git a/system/system-rpc/src/main/java/cn/iocoder/mall/system/rpc/rpc/SystemLogRPCImpl.java b/system/system-rpc/src/main/java/cn/iocoder/mall/system/rpc/rpc/SystemLogRPCImpl.java index f572fbfcc..13965a0d6 100644 --- a/system/system-rpc/src/main/java/cn/iocoder/mall/system/rpc/rpc/SystemLogRPCImpl.java +++ b/system/system-rpc/src/main/java/cn/iocoder/mall/system/rpc/rpc/SystemLogRPCImpl.java @@ -9,7 +9,7 @@ import cn.iocoder.mall.system.rpc.request.system.AccessLogAddRequest; import org.apache.dubbo.config.annotation.Service; import org.springframework.beans.factory.annotation.Autowired; -@Service(version = "dubbo.provider.SystemLogRPC.version", validation = "true") +@Service(version = "${dubbo.provider.SystemLogRPC.version}", validation = "true") public class SystemLogRPCImpl implements SystemLogRPC { @Autowired diff --git a/system/system-rpc/src/main/resources/rpc.yaml b/system/system-rpc/src/main/resources/rpc.yaml index 79c1f0deb..5660fa89d 100644 --- a/system/system-rpc/src/main/resources/rpc.yaml +++ b/system/system-rpc/src/main/resources/rpc.yaml @@ -15,3 +15,7 @@ dubbo: filter: -exception SystemLogRPC: version: 1.0.0 + # Dubbo 服务消费者的配置 + consumer: + SystemLogRPC: # 用于 AccessLogInterceptor 等拦截器,记录 HTTP API 请求的访问日志 + version: 1.0.0 diff --git a/user/user-rest/pom.xml b/user/user-rest/pom.xml index 133475a28..e5fd87907 100644 --- a/user/user-rest/pom.xml +++ b/user/user-rest/pom.xml @@ -15,10 +15,17 @@ - + + + + cn.iocoder.mall + common-framework + 1.0-SNAPSHOT + + cn.iocoder.mall diff --git a/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/TestController.java b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/TestController.java new file mode 100644 index 000000000..e2f74c320 --- /dev/null +++ b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/TestController.java @@ -0,0 +1,18 @@ +package cn.iocoder.mall.user.rest.controller; + +import cn.iocoder.common.framework.vo.CommonResult; +import cn.iocoder.mall.web.constant.CommonMallConstants; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(CommonMallConstants.ROOT_PATH_USER + "/test") +public class TestController { + + @GetMapping("/echo") + public CommonResult echo() { + return CommonResult.success(true); + } + +} diff --git a/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/package-info.java b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/package-info.java new file mode 100644 index 000000000..6064a5d49 --- /dev/null +++ b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/controller/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.user.rest.controller; diff --git a/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/package-info.java b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/package-info.java new file mode 100644 index 000000000..89de46672 --- /dev/null +++ b/user/user-rest/src/main/java/cn/iocoder/mall/user/rest/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.mall.user.rest; diff --git a/user/user-rpc/pom.xml b/user/user-rpc/pom.xml index e6761a4be..9fb14f0fb 100644 --- a/user/user-rpc/pom.xml +++ b/user/user-rpc/pom.xml @@ -13,11 +13,11 @@ - - cn.iocoder.mall - system-rpc-api - 1.0-SNAPSHOT - + + + + + cn.iocoder.mall user-rpc-api