diff --git a/system/system-biz/pom.xml b/system/system-biz/pom.xml index 2375b2c2..6f2ee014 100644 --- a/system/system-biz/pom.xml +++ b/system/system-biz/pom.xml @@ -85,6 +85,22 @@ fastjson + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework + spring-webmvc + compile + + + javax.servlet + servlet-api + compile + + diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/annotation/OperationLogging.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/annotation/OperationLogging.java new file mode 100644 index 00000000..db597757 --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/annotation/OperationLogging.java @@ -0,0 +1,20 @@ +package cn.iocoder.mall.system.biz.log.operation.annotation; + +import java.lang.annotation.*; + +/** + * @author Hccake + * @version 1.0 + * @date 2019/10/15 18:09 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OperationLogging { + + /** + * 日志信息 + * @return + */ + String value(); +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java new file mode 100644 index 00000000..a89b1f2e --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/aspect/OperationLogAspect.java @@ -0,0 +1,118 @@ +package cn.iocoder.mall.system.biz.log.operation.aspect; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.json.JSONUtil; +import cn.iocoder.common.framework.util.HttpUtil; +import cn.iocoder.common.framework.util.MallUtil; +import cn.iocoder.mall.system.biz.log.operation.annotation.OperationLogging; +import cn.iocoder.mall.system.biz.log.operation.enums.LogStatus; +import cn.iocoder.mall.system.biz.log.operation.event.OperationLogEvent; +import cn.iocoder.mall.system.biz.log.operation.model.dto.OperationLogDTO; +import lombok.RequiredArgsConstructor; +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.reflect.MethodSignature; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.annotation.Order; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author Hccake + * @version 1.0 + * @date 2019/10/15 18:16 + */ +@Slf4j +@Aspect +@Order(0) +@RequiredArgsConstructor +public class OperationLogAspect { + private final ApplicationEventPublisher publisher; + + @Around("@annotation(operationLogging)") + public Object around(ProceedingJoinPoint joinPoint, OperationLogging operationLogging) throws Throwable { + Signature signature = joinPoint.getSignature(); + String strClassName = joinPoint.getTarget().getClass().getName(); + String strMethodName = signature.getName(); + log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName); + + // 获取日志 + OperationLogDTO operationLogDTO = prodOperationLog(); + operationLogDTO.setMsg(operationLogging.value()); + // 记录参数 + MethodSignature methodSignature = (MethodSignature) signature; + operationLogDTO.setParams(getParams(joinPoint, methodSignature)); + // 开始时间 + long startTime = System.currentTimeMillis(); + Object result; + try { + result = joinPoint.proceed(); + } catch (Throwable throwable) { + operationLogDTO.setStatus(LogStatus.FAIL.getValue()); + throw throwable; + } + // 结束时间 + operationLogDTO.setResponseTime((int) (System.currentTimeMillis() - startTime)); + // 发布事件 + publisher.publishEvent(new OperationLogEvent(operationLogDTO)); + + return result; + } + + + /** + * 获取方法参数 + * @param joinPoint joinPoint + * @param methodSignature 方法签名 + * @return 方法参数的Json字符串形式 + */ + private String getParams(ProceedingJoinPoint joinPoint, MethodSignature methodSignature) { + String[] parameterNames = methodSignature.getParameterNames(); + Object[] args = joinPoint.getArgs(); + if(ArrayUtil.isEmpty(parameterNames)){ + return null; + } + Map paramsMap = new HashMap<>(); + for (int i = 0; i < parameterNames.length; i++) { + paramsMap.put(parameterNames[i], args[i]); + } + return JSONUtil.toJsonStr(paramsMap); + } + + + /** + * 根据请求生成操作日志 + * @return 操作日志DTO + */ + private OperationLogDTO prodOperationLog() { + HttpServletRequest request = ((ServletRequestAttributes) Objects + .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + + return new OperationLogDTO() + .setTraceId(MallUtil.getTraceId()) + .setUri(URLUtil.getPath(request.getRequestURI())) + .setUserAgent(HttpUtil.getUserAgent(request)) + .setIp(HttpUtil.getIp(request)) + .setMethod(request.getMethod()) + // TODO 获取管理员用户名 或者 用户ID + // .setOperator(Objects.requireNonNull(LogUtils.getUsername())) + .setStatus(LogStatus.SUCCESS.getValue()); + } + + + + + + + + +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/enums/LogStatus.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/enums/LogStatus.java new file mode 100644 index 00000000..c6a6ae5d --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/enums/LogStatus.java @@ -0,0 +1,27 @@ +package cn.iocoder.mall.system.biz.log.operation.enums; + +/** + * @author Hccake + * @version 1.0 + * @date 2020/5/15 14:47 + */ +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 操作状态 + */ +@Getter +@AllArgsConstructor +public enum LogStatus { + /** + * 成功 + */ + SUCCESS(1), + /** + * 失败 + */ + FAIL(0); + + private final int value; +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogEvent.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogEvent.java new file mode 100644 index 00000000..40051f3a --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogEvent.java @@ -0,0 +1,15 @@ +package cn.iocoder.mall.system.biz.log.operation.event; + +import cn.iocoder.mall.system.biz.log.operation.model.dto.OperationLogDTO; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author + * 系统日志事件 + */ +@Getter +@AllArgsConstructor +public class OperationLogEvent { + private final OperationLogDTO operationLogDTO; +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogListener.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogListener.java new file mode 100644 index 00000000..2a41ef61 --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/event/OperationLogListener.java @@ -0,0 +1,26 @@ +package cn.iocoder.mall.system.biz.log.operation.event; + +import cn.iocoder.mall.system.biz.log.operation.service.OperationLogSaveService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.Async; + +/** + * @author + * 异步监听日志事件 + */ +@Slf4j +public class OperationLogListener { + + @Autowired + private OperationLogSaveService operationLogSaveService; + + @Async + @Order + @EventListener(OperationLogEvent.class) + public void saveSysLog(OperationLogEvent event) { + operationLogSaveService.saveLog(event.getOperationLogDTO()); + } +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/dto/OperationLogDTO.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/dto/OperationLogDTO.java new file mode 100644 index 00000000..dd675d12 --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/dto/OperationLogDTO.java @@ -0,0 +1,87 @@ +package cn.iocoder.mall.system.biz.log.operation.model.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 操作日志 + * + * @author hccake + * @date 2020-05-15 15:12:53 + */ +@Data +@Accessors(chain = true) +@ApiModel(value = "操作日志") +public class OperationLogDTO{ + private static final long serialVersionUID = 1L; + + /** + * 链路追踪编号 + */ + @ApiModelProperty(value = "链路追踪编号") + private String traceId; + /** + * 账号编号 + */ + @ApiModelProperty(value = "账号编号") + private Integer accountId; + /** + * 应用名 + */ + @ApiModelProperty(value = "应用名") + private String applicationName; + /** + * 访问地址 + */ + @ApiModelProperty(value = "访问地址") + private String uri; + /** + * 参数 + */ + @ApiModelProperty(value = "参数") + private String params; + /** + * http 方法 + */ + @ApiModelProperty(value = "http 方法") + private String method; + /** + * userAgent + */ + @ApiModelProperty(value = "userAgent") + private String userAgent; + /** + * ip + */ + @ApiModelProperty(value = "ip") + private String ip; + /** + * 请求时间 + */ + @ApiModelProperty(value = "请求时间") + private LocalDateTime startTime; + /** + * 响应时长 -- 毫秒级 + */ + @ApiModelProperty(value = "响应时长 -- 毫秒级") + private Integer responseTime; + /** + * 日志消息 + */ + @ApiModelProperty(value = "日志消息") + private String msg; + /** + * 操作状态 + */ + @ApiModelProperty(value = "操作状态") + private Integer status; + /** + * 创建者 + */ + @ApiModelProperty(value = "创建者") + private String operator; +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/po/OperationLogPO.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/po/OperationLogPO.java new file mode 100644 index 00000000..a6e34863 --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/model/po/OperationLogPO.java @@ -0,0 +1,89 @@ +package cn.iocoder.mall.system.biz.log.operation.model.po; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + * 操作日志 + * + * @author hccake + * @date 2020-05-15 15:12:53 + */ +@Data +@TableName("operation_log") +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "操作日志") +public class OperationLogPO extends Model { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @TableId + private Integer id; + /** + * 链路追踪编号 + */ + private String traceId; + /** + * 账号编号 + */ + private Integer accountId; + /** + * 应用名 + */ + private String applicationName; + /** + * 访问地址 + */ + private String uri; + /** + * 参数 + */ + private String params; + /** + * http 方法 + */ + private String method; + /** + * userAgent + */ + private String userAgent; + /** + * ip + */ + private String ip; + /** + * 请求时间 + */ + private LocalDateTime startTime; + /** + * 响应时长 -- 毫秒级 + */ + private Integer responseTime; + /** + * 日志消息 + */ + private String msg; + /** + * 操作状态 + */ + private Integer status; + /** + * 创建者 + */ + private String operator; + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/service/OperationLogSaveService.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/service/OperationLogSaveService.java new file mode 100644 index 00000000..159d8569 --- /dev/null +++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/log/operation/service/OperationLogSaveService.java @@ -0,0 +1,20 @@ +package cn.iocoder.mall.system.biz.log.operation.service; + + +import cn.iocoder.mall.system.biz.log.operation.model.dto.OperationLogDTO; + +/** + * 操作日志业务类 + * @author Hccake + * @version 1.0 + * @date 2019/10/15 19:57 + */ +public interface OperationLogSaveService { + + /** + * 保存操作日志 + * @param operationLogDTO + * @return true/false + */ + boolean saveLog(OperationLogDTO operationLogDTO); +}