com.mysql
mysql-connector-j
diff --git a/src/main/java/asia/yulinling/workflow/aspectj/AopLog.java b/src/main/java/asia/yulinling/workflow/aspectj/AopLog.java
new file mode 100644
index 0000000..a4c1d36
--- /dev/null
+++ b/src/main/java/asia/yulinling/workflow/aspectj/AopLog.java
@@ -0,0 +1,185 @@
+package asia.yulinling.workflow.aspectj;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import cn.hutool.json.JSONUtil;
+import cn.hutool.log.Log;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ *
+ * 使用 aop 切面记录请求日志信息
+ *
+ *
+ * @author yulinling
+ * @since 2025/6/5
+ */
+@Aspect
+@Component
+@Slf4j
+public class AopLog {
+ /**
+ * 切入点
+ */
+ @Pointcut("execution(public * asia.yulinling.workflow.controller.*Controller.*(..))")
+ public void log() {
+
+ }
+
+ /**
+ * 环绕操作
+ *
+ * @param joinPoint 切入点
+ * @return 原方法返回值
+ * @throws Throwable 异常信息
+ */
+ @Around("log()")
+ public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
+ // 开始打印请求日志
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
+
+ // 打印请求相关参数
+ long startTime = System.currentTimeMillis();
+ Object result = joinPoint.proceed();
+ String header = request.getHeader("User-Agent");
+ UserAgent ua = UserAgentUtil.parse(header);
+ final Log l = Log.builder()
+ .threadId(Thread.currentThread().getName())
+ .threadName(Thread.currentThread().getName())
+ .ip(getIp(request))
+ .url(request.getRequestURI())
+ .httpMethod(request.getMethod())
+ .classMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName())
+ .requestParams(getNameAndValue(joinPoint))
+ .result(result)
+ .timeCost(System.currentTimeMillis() - startTime)
+ .userAgent(header)
+ .browser(ua.getBrowser().toString())
+ .os(ua.getOs().toString())
+ .build();
+
+ log.info("Request Log Info:{}", JSONUtil.toJsonStr(l));
+
+ return result;
+ }
+
+ /**
+ * 获取方法参数名和参数值
+ * @param joinPoint 切入点
+ * @return 接口参数和参数值
+ */
+ private Map getNameAndValue(ProceedingJoinPoint joinPoint) {
+ final Signature signature = joinPoint.getSignature();
+ MethodSignature methodSignature = (MethodSignature) signature;
+ final String[] names = methodSignature.getParameterNames();
+ final Object[] args = joinPoint.getArgs();
+
+ if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) {
+ return Collections.emptyMap();
+ }
+ if (names.length != args.length) {
+ log.warn("{}方法参数名和参数数量不一致", methodSignature.getName());
+ return Collections.emptyMap();
+ }
+ Map map = new HashMap<>();
+ for (int i = 0; i < names.length; i++) {
+ map.put(names[i], args[i]);
+ }
+ return map;
+ }
+
+ /**
+ * 获取ip地址
+ * @param request 请求体
+ * @return 返回ip地址
+ */
+ public static String getIp(HttpServletRequest request) {
+ String ip = request.getHeader("X-Forwarded-For");
+ if (isInvalidIp(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (isInvalidIp(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (isInvalidIp(ip)) {
+ ip = request.getHeader("HTTP_CLIENT_IP");
+ }
+ if (isInvalidIp(ip)) {
+ ip = request.getRemoteAddr();
+ }
+
+ // 处理多级代理
+ if (ip != null && ip.contains(",")) {
+ ip = ip.split(",")[0].trim();
+ }
+
+ // 本地地址处理
+ if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
+ try {
+ ip = InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ log.error("获取本地 IP 失败", e);
+ }
+ }
+
+ return ip;
+ }
+
+ private static boolean isInvalidIp(String ip) {
+ return ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip);
+ }
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ static class Log {
+ // 线程id
+ private String threadId;
+ // 线程名称
+ private String threadName;
+ // ip
+ private String ip;
+ // url
+ private String url;
+ // http方法 GET POST PUT DELETE PATCH
+ private String httpMethod;
+ // 类方法
+ private String classMethod;
+ // 请求参数
+ private Object requestParams;
+ // 返回参数
+ private Object result;
+ // 接口耗时
+ private Long timeCost;
+ // 操作系统
+ private String os;
+ // 浏览器
+ private String browser;
+ // user-agent
+ private String userAgent;
+ }
+
+}
diff --git a/src/main/java/asia/yulinling/workflow/controller/TestController.java b/src/main/java/asia/yulinling/workflow/controller/TestController.java
new file mode 100644
index 0000000..0543736
--- /dev/null
+++ b/src/main/java/asia/yulinling/workflow/controller/TestController.java
@@ -0,0 +1,46 @@
+package asia.yulinling.workflow.controller;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ *
+ * 测试aop
+ *
+ *
+ * @author yulinling
+ * @since 2025/6/5
+ */
+@Slf4j
+@RestController
+public class TestController {
+
+ /**
+ * 测试方法 GET
+ * @param who 测试参数
+ * @return {@link Dict}
+ */
+ @GetMapping("/test")
+ public Dict test(String who) {
+ return Dict.create().set("who", who);
+ }
+
+ /**
+ * 测试方法 POST
+ * @param map 请求的json参数
+ * @return {@link Dict}
+ */
+ @PostMapping("/testJson")
+ public Dict testJson(@RequestBody Map map) {
+ final String jsonStr = JSONUtil.toJsonStr(map);
+ log.info(jsonStr);
+ return Dict.create().set("json", jsonStr);
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 97ee4a3..95dfd64 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,3 +1,6 @@
+# ????
+server.port=8080
+server.servlet.context-path=/demo
# mysql??
spring.datasource.url=jdbc:mysql://122.152.201.90:9912/workflow?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8