From 25df88d0ab9d7444ed1d162b9efa1259ac2f6f66 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 27 四月 2026 10:13:53 +0800
Subject: [PATCH] 小程序对接finish

---
 backend/src/main/java/com/sinata/appointment/audit/OpLogAuditAspect.java |  156 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 143 insertions(+), 13 deletions(-)

diff --git a/backend/src/main/java/com/sinata/appointment/audit/OpLogAuditAspect.java b/backend/src/main/java/com/sinata/appointment/audit/OpLogAuditAspect.java
index 1572151..c8ce5e1 100644
--- a/backend/src/main/java/com/sinata/appointment/audit/OpLogAuditAspect.java
+++ b/backend/src/main/java/com/sinata/appointment/audit/OpLogAuditAspect.java
@@ -1,16 +1,30 @@
 package com.sinata.appointment.audit;
 
 import cn.dev33.satoken.stp.StpUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sinata.appointment.config.MiniStpUtil;
 import com.sinata.appointment.service.OpLogApplicationService;
+import jakarta.servlet.http.HttpServletRequest;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
 import org.aspectj.lang.annotation.AfterReturning;
 import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import java.lang.reflect.Method;
 import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.util.StringUtils;
 
 /**
- * 配置类写操作审计(示例):复杂场景可在 ApplicationService 内显式记录。
+ * 统一记录控制器写操作日志。
  */
 @Slf4j
 @Aspect
@@ -19,23 +33,139 @@
 public class OpLogAuditAspect {
 
     private final OpLogApplicationService opLogApplicationService;
+    private final ObjectMapper objectMapper;
 
-    @AfterReturning(
-            pointcut =
-                    "execution(* com.sinata.appointment.service.ConfigApplicationService.save*(..))",
-            returning = "ret")
-    public void afterConfigSave(JoinPoint jp, Object ret) {
+    @Pointcut("within(com.sinata.appointment.controller..*)")
+    public void controllerWriteOperation() {}
+
+    @AfterReturning(pointcut = "controllerWriteOperation()", returning = "ret")
+    public void afterWrite(JoinPoint jp, Object ret) {
         try {
-            Long op = StpUtil.isLogin() ? StpUtil.getLoginIdAsLong() : null;
+            HttpServletRequest request = currentRequest();
+            if (request == null) {
+                return;
+            }
+            if (!isWriteMethod(request.getMethod())) {
+                return;
+            }
+            String uri = request.getRequestURI();
+            String sourceType = resolveSourceType(uri);
+            if (!StringUtils.hasText(sourceType)) {
+                return;
+            }
+            Long operatorUserId = resolveOperatorUserId(sourceType);
             opLogApplicationService.recordAsync(
-                    op,
-                    "CONFIG",
-                    jp.getSignature().getName(),
-                    jp.getArgs().length > 0 ? jp.getArgs()[0].getClass().getSimpleName() : "UNKNOWN",
-                    "",
-                    "{}");
+                    sourceType,
+                    operatorUserId,
+                    resolveModule(jp, uri),
+                    resolveAction(jp),
+                    jp.getTarget().getClass().getSimpleName(),
+                    uri,
+                    buildDetailJson(request, jp));
         } catch (Exception e) {
             log.debug("audit skip: {}", e.getMessage());
         }
     }
+
+    private boolean isWriteMethod(String method) {
+        if (!StringUtils.hasText(method)) {
+            return false;
+        }
+        return "POST".equalsIgnoreCase(method)
+                || "PUT".equalsIgnoreCase(method)
+                || "PATCH".equalsIgnoreCase(method)
+                || "DELETE".equalsIgnoreCase(method);
+    }
+
+    private HttpServletRequest currentRequest() {
+        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+        if (attributes instanceof ServletRequestAttributes servletRequestAttributes) {
+            return servletRequestAttributes.getRequest();
+        }
+        return null;
+    }
+
+    private String resolveSourceType(String uri) {
+        if (!StringUtils.hasText(uri)) {
+            return null;
+        }
+        if (uri.startsWith("/api/mini/")) {
+            return OpLogApplicationService.SOURCE_MINI_EMPLOYEE;
+        }
+        if (uri.startsWith("/api/")) {
+            return OpLogApplicationService.SOURCE_ADMIN;
+        }
+        return null;
+    }
+
+    private Long resolveOperatorUserId(String sourceType) {
+        if (OpLogApplicationService.SOURCE_MINI_EMPLOYEE.equals(sourceType)) {
+            return MiniStpUtil.isLogin() ? MiniStpUtil.getLoginIdAsLong() : null;
+        }
+        return StpUtil.isLogin() ? StpUtil.getLoginIdAsLong() : null;
+    }
+
+    private String resolveModule(JoinPoint jp, String uri) {
+        Tag tag = jp.getTarget().getClass().getAnnotation(Tag.class);
+        if (tag != null && StringUtils.hasText(tag.name())) {
+            return tag.name().trim();
+        }
+        if (!StringUtils.hasText(uri)) {
+            return "UNKNOWN";
+        }
+        String trimmed = uri.startsWith("/api/") ? uri.substring(5) : uri;
+        String[] segments = trimmed.split("/");
+        if (segments.length >= 2 && "mini".equalsIgnoreCase(segments[0])) {
+            return segments[1].toUpperCase();
+        }
+        return segments.length > 0 ? segments[0].toUpperCase() : "UNKNOWN";
+    }
+
+    private String resolveAction(JoinPoint jp) {
+        if (jp.getSignature() instanceof MethodSignature methodSignature) {
+            Method method = methodSignature.getMethod();
+            Operation operation = method.getAnnotation(Operation.class);
+            if (operation != null && StringUtils.hasText(operation.summary())) {
+                return operation.summary().trim();
+            }
+        }
+        return jp.getSignature().getName();
+    }
+
+    private String buildDetailJson(HttpServletRequest request, JoinPoint jp) {
+        Map<String, Object> payload = new LinkedHashMap<>();
+        payload.put("method", request.getMethod());
+        payload.put("uri", request.getRequestURI());
+        payload.put("handler", jp.getSignature().toShortString());
+        payload.put("args", summarizeArgs(jp.getArgs()));
+        try {
+            return objectMapper.writeValueAsString(payload);
+        } catch (Exception ex) {
+            return "{\"uri\":\"" + request.getRequestURI() + "\"}";
+        }
+    }
+
+    private Object[] summarizeArgs(Object[] args) {
+        if (args == null || args.length == 0) {
+            return new Object[0];
+        }
+        Object[] summarized = new Object[args.length];
+        for (int i = 0; i < args.length; i++) {
+            Object arg = args[i];
+            if (arg == null) {
+                summarized[i] = null;
+                continue;
+            }
+            if (arg instanceof String || arg instanceof Number || arg instanceof Boolean) {
+                String value = String.valueOf(arg);
+                summarized[i] =
+                        value.length() > 60
+                                ? value.substring(0, 60) + "..."
+                                : value;
+                continue;
+            }
+            summarized[i] = arg.getClass().getSimpleName();
+        }
+        return summarized;
+    }
 }

--
Gitblit v1.7.1