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