diff --git a/pom.xml b/pom.xml index 0c950667..843ee6e8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.youlai youlai-boot - 4.6.0 + 4.7.0 基于 Java 17 + SpringBoot 4 + Spring Security 构建的权限管理系统。 diff --git a/src/main/java/com/youlai/boot/framework/security/port/PermissionPort.java b/src/main/java/com/youlai/boot/framework/security/port/PermissionPort.java index 9417eded..0176ebf7 100644 --- a/src/main/java/com/youlai/boot/framework/security/port/PermissionPort.java +++ b/src/main/java/com/youlai/boot/framework/security/port/PermissionPort.java @@ -8,7 +8,7 @@ import java.util.Set; * 由 system 模块提供适配器实现,framework 层通过此接口获取角色权限集合, * 不直接依赖 system 模块的 {@code RoleMenuService}。 * - * @see com.youlai.boot.system.security.adapter.PermissionAdapter + * @see com.youlai.boot.system.adapter.security.PermissionAdapter */ public interface PermissionPort { diff --git a/src/main/java/com/youlai/boot/framework/security/port/UserAuthenticationPort.java b/src/main/java/com/youlai/boot/framework/security/port/UserAuthenticationPort.java index 82acea2c..b57d69cb 100644 --- a/src/main/java/com/youlai/boot/framework/security/port/UserAuthenticationPort.java +++ b/src/main/java/com/youlai/boot/framework/security/port/UserAuthenticationPort.java @@ -9,7 +9,7 @@ import com.youlai.boot.framework.security.model.SecurityUser; * 由 system 模块提供适配器实现,framework 层通过此接口获取认证数据, * 不直接依赖 system 模块的 {@code UserService} / {@code UserSocialService}。 * - * @see com.youlai.boot.system.security.adapter.UserAuthenticationAdapter + * @see com.youlai.boot.system.adapter.security.UserAuthenticationAdapter */ public interface UserAuthenticationPort { diff --git a/src/main/java/com/youlai/boot/framework/web/log/OperationLog.java b/src/main/java/com/youlai/boot/framework/web/log/OperationLog.java new file mode 100644 index 00000000..b30715bd --- /dev/null +++ b/src/main/java/com/youlai/boot/framework/web/log/OperationLog.java @@ -0,0 +1,35 @@ +package com.youlai.boot.framework.web.log; + +import com.youlai.boot.common.enums.ActionTypeEnum; +import com.youlai.boot.common.enums.LogModuleEnum; + +import java.time.LocalDateTime; + +/** + * 操作日志通用载荷 + *

由操作日志切面采集,经 {@link OperationLogPort} 交给各业务模块落库

+ * + * @author Ray.Hao + * @since 0.0.1 + */ +public record OperationLog( + LogModuleEnum module, + ActionTypeEnum actionType, + String title, + String content, + Long operatorId, + String operatorName, + String requestUri, + String requestMethod, + String ip, + String province, + String city, + String device, + String os, + String browser, + Integer status, + String errorMsg, + Integer executionTime, + LocalDateTime createTime +) { +} diff --git a/src/main/java/com/youlai/boot/common/aspect/LogAspect.java b/src/main/java/com/youlai/boot/framework/web/log/OperationLogAspect.java similarity index 70% rename from src/main/java/com/youlai/boot/common/aspect/LogAspect.java rename to src/main/java/com/youlai/boot/framework/web/log/OperationLogAspect.java index 779d8b94..f193a6d0 100644 --- a/src/main/java/com/youlai/boot/common/aspect/LogAspect.java +++ b/src/main/java/com/youlai/boot/framework/web/log/OperationLogAspect.java @@ -1,4 +1,4 @@ -package com.youlai.boot.common.aspect; +package com.youlai.boot.framework.web.log; import cn.hutool.core.util.StrUtil; import cn.hutool.http.useragent.UserAgent; @@ -8,8 +8,6 @@ import com.youlai.boot.common.enums.ActionTypeEnum; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.util.IPUtils; import com.youlai.boot.framework.security.util.SecurityUtils; -import com.youlai.boot.system.model.entity.SysLog; -import com.youlai.boot.system.service.LogService; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; @@ -27,7 +25,7 @@ import java.util.concurrent.Executor; /** * 操作日志切面 - *

@Log 标注的方法执行后异步写入 sys_log 表

+ *

@Log 标注的方法执行后异步采集操作日志,通过 {@link OperationLogPort} 落库

* * @author Ray.Hao * @since 0.0.1 @@ -35,18 +33,18 @@ import java.util.concurrent.Executor; @Aspect @Component @Slf4j -public class LogAspect { +public class OperationLogAspect { private static final int MAX_ERROR_MSG_LENGTH = 1000; - private final LogService logService; + private final OperationLogPort operationLogPort; private final Executor operationLogExecutor; - public LogAspect( - LogService logService, + public OperationLogAspect( + OperationLogPort operationLogPort, @Qualifier("operationLogExecutor") Executor operationLogExecutor ) { - this.logService = logService; + this.operationLogPort = operationLogPort; this.operationLogExecutor = operationLogExecutor; } @@ -69,13 +67,13 @@ public class LogAspect { } finally { long elapsed = System.currentTimeMillis() - start; try { - SysLog entity = buildEntity(logAnnotation, elapsed, failure, userId, username); - if (entity != null) { + OperationLog operationLog = buildOperationLog(logAnnotation, elapsed, failure, userId, username); + if (operationLog != null) { operationLogExecutor.execute(() -> { try { - logService.save(entity); + operationLogPort.record(operationLog); } catch (Exception e) { - log.error("保存操作日志失败 title={} uri={}", entity.getTitle(), entity.getRequestUri(), e); + log.error("保存操作日志失败 title={} uri={}", operationLog.title(), operationLog.requestUri(), e); } }); } @@ -85,7 +83,7 @@ public class LogAspect { } } - private SysLog buildEntity(Log ann, long elapsed, Throwable failure, Long userId, String username) { + private OperationLog buildOperationLog(Log ann, long elapsed, Throwable failure, Long userId, String username) { ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs == null) return null; @@ -96,29 +94,28 @@ public class LogAspect { LogModuleEnum module = ann.module(); ActionTypeEnum action = ann.value(); - SysLog entity = new SysLog(); - entity.setModule(module); - entity.setActionType(action); - entity.setTitle(StrUtil.blankToDefault(ann.title(), module.getLabel() + "-" + action.getLabel())); - entity.setContent(ann.content()); - entity.setOperatorId(userId); - entity.setOperatorName(username); - entity.setRequestUri(req.getRequestURI()); - entity.setRequestMethod(req.getMethod()); - entity.setIp(ip); - entity.setDevice(getDevice(ua)); - entity.setOs(getOs(ua)); - entity.setBrowser(getBrowser(ua)); - entity.setStatus(failure == null ? 1 : 0); - entity.setErrorMsg(failure == null ? null : truncate(failure.getMessage(), MAX_ERROR_MSG_LENGTH)); - entity.setExecutionTime((int) Math.min(elapsed, Integer.MAX_VALUE)); - entity.setCreateTime(LocalDateTime.now()); - IpRegion region = parseRegion(ip); - entity.setProvince(region.province); - entity.setCity(region.city); - return entity; + return new OperationLog( + module, + action, + StrUtil.blankToDefault(ann.title(), module.getLabel() + "-" + action.getLabel()), + ann.content(), + userId, + username, + req.getRequestURI(), + req.getMethod(), + ip, + region.province(), + region.city(), + getDevice(ua), + getOs(ua), + getBrowser(ua), + failure == null ? 1 : 0, + failure == null ? null : truncate(failure.getMessage(), MAX_ERROR_MSG_LENGTH), + (int) Math.min(elapsed, Integer.MAX_VALUE), + LocalDateTime.now() + ); } private IpRegion parseRegion(String ip) { diff --git a/src/main/java/com/youlai/boot/framework/web/log/OperationLogPort.java b/src/main/java/com/youlai/boot/framework/web/log/OperationLogPort.java new file mode 100644 index 00000000..780d6ae7 --- /dev/null +++ b/src/main/java/com/youlai/boot/framework/web/log/OperationLogPort.java @@ -0,0 +1,20 @@ +package com.youlai.boot.framework.web.log; + +/** + * 操作日志落库端口 + *

由各业务模块实现,framework 层通过此接口写入操作日志,不依赖具体落库实现。 + * 如 system 模块的 {@code OperationLogAdapter} 落库 sys_log 表。

+ * + * @see com.youlai.boot.system.adapter.log.OperationLogAdapter + * @author Ray.Hao + * @since 0.0.1 + */ +public interface OperationLogPort { + + /** + * 记录操作日志 + * + * @param operationLog 操作日志载荷 + */ + void record(OperationLog operationLog); +} diff --git a/src/main/java/com/youlai/boot/system/adapter/log/OperationLogAdapter.java b/src/main/java/com/youlai/boot/system/adapter/log/OperationLogAdapter.java new file mode 100644 index 00000000..6794000e --- /dev/null +++ b/src/main/java/com/youlai/boot/system/adapter/log/OperationLogAdapter.java @@ -0,0 +1,36 @@ +package com.youlai.boot.system.adapter.log; + +import com.youlai.boot.framework.web.log.OperationLog; +import com.youlai.boot.framework.web.log.OperationLogPort; +import com.youlai.boot.system.converter.LogConverter; +import com.youlai.boot.system.model.entity.SysLog; +import com.youlai.boot.system.service.LogService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 操作日志适配器 + *

实现 framework 层的 {@link OperationLogPort},将切面采集的操作日志落库 sys_log 表

+ * + * @author Ray.Hao + * @since 0.0.1 + * @see OperationLogPort + */ +@Component +@RequiredArgsConstructor +public class OperationLogAdapter implements OperationLogPort { + + private final LogService logService; + private final LogConverter logConverter; + + /** + * 记录操作日志 + *

通过 {@link LogConverter} 将操作日志载荷转换为 {@link SysLog} 后落库

+ * + * @param operationLog 操作日志载荷 + */ + @Override + public void record(OperationLog operationLog) { + logService.save(logConverter.toEntity(operationLog)); + } +} diff --git a/src/main/java/com/youlai/boot/system/security/adapter/PermissionAdapter.java b/src/main/java/com/youlai/boot/system/adapter/security/PermissionAdapter.java similarity index 93% rename from src/main/java/com/youlai/boot/system/security/adapter/PermissionAdapter.java rename to src/main/java/com/youlai/boot/system/adapter/security/PermissionAdapter.java index 386fd4e3..b8ed617b 100644 --- a/src/main/java/com/youlai/boot/system/security/adapter/PermissionAdapter.java +++ b/src/main/java/com/youlai/boot/system/adapter/security/PermissionAdapter.java @@ -1,4 +1,4 @@ -package com.youlai.boot.system.security.adapter; +package com.youlai.boot.system.adapter.security; import com.youlai.boot.framework.security.port.PermissionPort; import com.youlai.boot.system.service.RoleMenuService; diff --git a/src/main/java/com/youlai/boot/system/security/adapter/UserAuthenticationAdapter.java b/src/main/java/com/youlai/boot/system/adapter/security/UserAuthenticationAdapter.java similarity index 96% rename from src/main/java/com/youlai/boot/system/security/adapter/UserAuthenticationAdapter.java rename to src/main/java/com/youlai/boot/system/adapter/security/UserAuthenticationAdapter.java index 6726f7d1..fd7fb3cb 100644 --- a/src/main/java/com/youlai/boot/system/security/adapter/UserAuthenticationAdapter.java +++ b/src/main/java/com/youlai/boot/system/adapter/security/UserAuthenticationAdapter.java @@ -1,4 +1,4 @@ -package com.youlai.boot.system.security.adapter; +package com.youlai.boot.system.adapter.security; import com.youlai.boot.common.enums.SocialPlatformEnum; import com.youlai.boot.framework.security.model.SecurityUser; diff --git a/src/main/java/com/youlai/boot/system/converter/LogConverter.java b/src/main/java/com/youlai/boot/system/converter/LogConverter.java new file mode 100644 index 00000000..c7bec1fa --- /dev/null +++ b/src/main/java/com/youlai/boot/system/converter/LogConverter.java @@ -0,0 +1,17 @@ +package com.youlai.boot.system.converter; + +import com.youlai.boot.framework.web.log.OperationLog; +import com.youlai.boot.system.model.entity.SysLog; +import org.mapstruct.Mapper; + +/** + * 系统日志对象转换器 + * + * @author Ray.Hao + * @since 0.0.1 + */ +@Mapper(componentModel = "spring") +public interface LogConverter { + + SysLog toEntity(OperationLog operationLog); +}