diff --git a/system/system-biz/pom.xml b/system/system-biz/pom.xml
index 2375b2c26..6f2ee0145 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 000000000..db597757f
--- /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 000000000..a89b1f2e4
--- /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 000000000..c6a6ae5d9
--- /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 000000000..40051f3a6
--- /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 000000000..2a41ef616
--- /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 000000000..dd675d12c
--- /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 000000000..a6e348630
--- /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 000000000..159d85691
--- /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);
+}