From c7adeb1f4c48dfea357e824d69496fcaf1768d52 Mon Sep 17 00:00:00 2001
From: xieb <vip_xiaobin810@163.com>
Date: Tue, 17 Oct 2023 17:39:52 +0800
Subject: [PATCH] 新增用户操作日志

---
 src/main/java/com/dji/sample/log/service/ISysLogService.java              |   14 ++
 src/main/java/com/dji/sample/log/service/impl/SysLogServiceImpl.java      |   26 +++++
 src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java |    7 +
 src/main/java/com/dji/sample/log/dao/ISysLogMapper.java                   |   13 ++
 src/main/java/com/dji/sample/log/aspect/LogAspect.java                    |  145 +++++++++++++++++++++++++++++
 src/main/java/com/dji/sample/log/aspect/SysLogAnnotation.java             |   21 ++++
 src/main/java/com/dji/sample/log/model/entity/SysLogEntity.java           |   57 +++++++++++
 src/main/java/com/dji/sample/CloudApiSampleApplication.java               |    3 
 pom.xml                                                                   |    7 +
 9 files changed, 293 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index 070d3ea..c1e34d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -185,6 +185,13 @@
             <version>1.2.33</version>
         </dependency>
 
+
+        <!-- aop依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/src/main/java/com/dji/sample/CloudApiSampleApplication.java b/src/main/java/com/dji/sample/CloudApiSampleApplication.java
index 6eb857b..5a667dc 100644
--- a/src/main/java/com/dji/sample/CloudApiSampleApplication.java
+++ b/src/main/java/com/dji/sample/CloudApiSampleApplication.java
@@ -5,6 +5,8 @@
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.web.client.RestTemplate;
 
@@ -12,6 +14,7 @@
 import java.util.TimeZone;
 
 @MapperScan("com.dji.sample.*.dao")
+@EnableAspectJAutoProxy(proxyTargetClass = true)
 @SpringBootApplication
 @EnableScheduling
 //@EnableConfigurationProperties(OssConfiguration.class)
diff --git a/src/main/java/com/dji/sample/log/aspect/LogAspect.java b/src/main/java/com/dji/sample/log/aspect/LogAspect.java
new file mode 100644
index 0000000..b88d050
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/aspect/LogAspect.java
@@ -0,0 +1,145 @@
+package com.dji.sample.log.aspect;
+
+import com.alibaba.druid.support.json.JSONUtils;
+import com.alibaba.fastjson.JSON;
+import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.common.model.ResponseResult;
+import com.dji.sample.log.model.entity.SysLogEntity;
+import com.dji.sample.log.service.ISysLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION: 操作日志切面处理类
+ * @USER: aix
+ * @DATE: 2023/10/17 16:02
+ */
+
+@Aspect
+@Component
+@Slf4j
+public class LogAspect {
+
+    @Autowired
+    private ISysLogService sysLogService;
+
+    /**
+     * 设置操作日志切入点   在注解的位置切入代码
+     */
+    @Pointcut("@annotation(com.dji.sample.log.aspect.SysLogAnnotation)")
+    public void operLogPoinCut() { }
+
+    @AfterReturning(returning = "result", value = "operLogPoinCut()")
+    public void saveOperLog(JoinPoint joinPoint, ResponseResult result) {
+
+        // 获取RequestAttributes
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        // 从获取RequestAttributes中获取HttpServletRequest的信息
+        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
+        try {
+
+            SysLogEntity sysLog = new SysLogEntity();
+
+            // 从切面织入点处通过反射机制获取织入点处的方法
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            //获取切入点所在的方法
+            Method method = signature.getMethod();
+            //获取操作
+            SysLogAnnotation annotation = method.getAnnotation(SysLogAnnotation.class);
+            if (annotation != null) {
+                sysLog.setModel(annotation.operModul());
+                sysLog.setType(annotation.operType());
+                sysLog.setDescription(annotation.operDesc());
+            }
+
+            // 获取请求的类名
+            String className = joinPoint.getTarget().getClass().getName();
+            // 获取请求的方法名
+            String methodName = method.getName();
+            methodName = className + "." + methodName;
+            sysLog.setMethod(methodName); // 类名.请求方法
+
+            //操作用户
+            CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
+            sysLog.setUsername(customClaim.getUsername());
+
+            String ipAddress = request.getHeader("X-Forwarded-For");
+            if (ipAddress == null) {
+                ipAddress = request.getRemoteAddr();
+            }
+            sysLog.setIp(ipAddress); //操作IP
+            sysLog.setUrl(request.getRequestURI()); // 请求URI
+
+            // 方法请求的参数
+            Map<String, String> rtnMap = converMap(request.getParameterMap());
+            // 将参数所在的数组转换成json
+            String params = JSONUtils.toJSONString(rtnMap);
+            //获取json的请求参数
+            if (rtnMap == null || rtnMap.size() == 0) {
+                params = getJsonStrByRequest(request);
+            }
+            sysLog.setParams(params); // 请求参数
+
+            sysLog.setResult(JSON.toJSONString(result));
+
+            sysLogService.saveSysLog(sysLog);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("日志记录异常,请检查返回值是否统一");
+        }
+    }
+
+    /**
+     * 转换request 请求参数
+     *
+     * @param paramMap request获取的参数数组
+     */
+    public Map<String, String> converMap(Map<String, String[]> paramMap) {
+        Map<String, String> rtnMap = new HashMap<>();
+        for (String key : paramMap.keySet()) {
+            rtnMap.put(key, paramMap.get(key)[0]);
+        }
+        return rtnMap;
+    }
+
+    /**
+     * 获取json格式 请求参数
+     */
+    public String getJsonStrByRequest(HttpServletRequest request) {
+        String param = null;
+        try {
+            BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
+            StringBuilder responseStrBuilder = new StringBuilder();
+            String inputStr;
+            while ((inputStr = streamReader.readLine()) != null) {
+                responseStrBuilder.append(inputStr);
+            }
+
+            param = JSONUtils.toJSONString(responseStrBuilder);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return param;
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/log/aspect/SysLogAnnotation.java b/src/main/java/com/dji/sample/log/aspect/SysLogAnnotation.java
new file mode 100644
index 0000000..6e4ecca
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/aspect/SysLogAnnotation.java
@@ -0,0 +1,21 @@
+package com.dji.sample.log.aspect;
+
+import java.lang.annotation.*;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION: 自定义操作日志注解
+ * @USER: aix
+ * @DATE: 2023/10/17 16:08
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SysLogAnnotation {
+
+    String operModul() default ""; // 操作模块
+
+    String operType() default "";  // 操作类型
+
+    String operDesc() default "";  // 操作说明
+}
diff --git a/src/main/java/com/dji/sample/log/dao/ISysLogMapper.java b/src/main/java/com/dji/sample/log/dao/ISysLogMapper.java
new file mode 100644
index 0000000..3e0e883
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/dao/ISysLogMapper.java
@@ -0,0 +1,13 @@
+package com.dji.sample.log.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dji.sample.log.model.entity.SysLogEntity;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION: 日志dao
+ * @USER: aix
+ * @DATE: 2023/10/17 15:58
+ */
+public interface ISysLogMapper extends BaseMapper<SysLogEntity> {
+}
diff --git a/src/main/java/com/dji/sample/log/model/entity/SysLogEntity.java b/src/main/java/com/dji/sample/log/model/entity/SysLogEntity.java
new file mode 100644
index 0000000..03b4fca
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/model/entity/SysLogEntity.java
@@ -0,0 +1,57 @@
+package com.dji.sample.log.model.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION: 日志实体类
+ * @USER: aix
+ * @DATE: 2023/10/17 15:42
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName(value = "sys_log")
+public class SysLogEntity implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @TableField(value = "username")
+    private String username;
+
+    @TableField(value = "method")
+    private String method; //方法名
+
+    @TableField(value = "params")
+    private String params; //参数
+
+    @TableField(value = "ip")
+    private String ip; //ip地址
+
+    @TableField(value = "url")
+    private String url; //请求url
+
+    @TableField(value = "type")
+    private String type; //操作类型 :新增、删除等等
+
+    @TableField(value = "model")
+    private String model; //模块
+
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private Long createTime;
+
+    @TableField(value = "result")
+    private String result; //操作结果
+
+    @TableField(value = "description")
+    private String description;//描述
+
+}
diff --git a/src/main/java/com/dji/sample/log/service/ISysLogService.java b/src/main/java/com/dji/sample/log/service/ISysLogService.java
new file mode 100644
index 0000000..e2712c2
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/service/ISysLogService.java
@@ -0,0 +1,14 @@
+package com.dji.sample.log.service;
+
+import com.dji.sample.log.model.entity.SysLogEntity;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION: 保存用户日志接口
+ * @USER: aix
+ * @DATE: 2023/10/17 15:56
+ */
+public interface ISysLogService {
+
+    void saveSysLog(SysLogEntity sysLog);
+}
diff --git a/src/main/java/com/dji/sample/log/service/impl/SysLogServiceImpl.java b/src/main/java/com/dji/sample/log/service/impl/SysLogServiceImpl.java
new file mode 100644
index 0000000..35ab04f
--- /dev/null
+++ b/src/main/java/com/dji/sample/log/service/impl/SysLogServiceImpl.java
@@ -0,0 +1,26 @@
+package com.dji.sample.log.service.impl;
+
+import com.dji.sample.log.dao.ISysLogMapper;
+import com.dji.sample.log.model.entity.SysLogEntity;
+import com.dji.sample.log.service.ISysLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @PROJECT_NAME: iot_drone_api
+ * @DESCRIPTION:
+ * @USER: aix
+ * @DATE: 2023/10/17 15:59
+ */
+@Service
+public class SysLogServiceImpl implements ISysLogService {
+
+    @Autowired
+    private ISysLogMapper mapper;
+
+    @Override
+    public void saveSysLog(SysLogEntity sysLog) {
+        mapper.insert(sysLog);
+    }
+
+}
diff --git a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
index ef9ef82..788dee3 100644
--- a/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
+++ b/src/main/java/com/dji/sample/wayline/controller/WaylineJobController.java
@@ -3,6 +3,7 @@
 import com.dji.sample.common.model.CustomClaim;
 import com.dji.sample.common.model.PaginationData;
 import com.dji.sample.common.model.ResponseResult;
+import com.dji.sample.log.aspect.SysLogAnnotation;
 import com.dji.sample.wayline.model.dto.WaylineJobDTO;
 import com.dji.sample.wayline.model.param.CreateJobParam;
 import com.dji.sample.wayline.model.param.UpdateJobParam;
@@ -39,6 +40,7 @@
      * @throws SQLException
      */
     @PostMapping("/{workspace_id}/flight-tasks")
+    @SysLogAnnotation(operModul = "计划库", operType = "新增", operDesc = "创建计划")
     public ResponseResult createJob(HttpServletRequest request, @Valid @RequestBody CreateJobParam param,
                                     @PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
         CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
@@ -57,6 +59,7 @@
      * @throws SQLException
      */
     @PostMapping("/{workspace_id}/flight-tasks-condition")
+    @SysLogAnnotation(operModul = "计划库", operType = "新增", operDesc = "创建航路任务(重复定时和连续执行)")
     public ResponseResult createJobCondition(HttpServletRequest request, @Valid @RequestBody CreateJobParam param,
                                     @PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
         CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
@@ -73,6 +76,7 @@
      * @return
      */
     @GetMapping("/{workspace_id}/jobs")
+    @SysLogAnnotation(operModul = "计划库", operType = "查询", operDesc = "分页查询")
     public ResponseResult<PaginationData<WaylineJobDTO>> getJobs(@RequestParam(defaultValue = "1") Long page,
                                                                  @RequestParam(name = "page_size", defaultValue = "10") Long pageSize,
                                                                  @PathVariable(name = "workspace_id") String workspaceId, WaylineJobQueryParam waylineJobQueryParam){
@@ -88,6 +92,7 @@
      * @throws SQLException
      */
     @DeleteMapping("/{workspace_id}/jobs")
+    @SysLogAnnotation(operModul = "计划库", operType = "删除", operDesc = "发送命令取消作业")
     public ResponseResult publishCancelJob(@RequestParam(name = "job_id") Set<String> jobIds,
                                      @PathVariable(name = "workspace_id") String workspaceId) throws SQLException {
         waylineJobService.cancelFlightTask(workspaceId, jobIds);
@@ -101,6 +106,7 @@
      * @return
      */
     @PostMapping("/{workspace_id}/jobs/{job_id}/media-highest")
+    @SysLogAnnotation(operModul = "计划库", operType = "修改", operDesc = "将此作业的媒体文件设置为立即上传")
     public ResponseResult uploadMediaHighestPriority(@PathVariable(name = "workspace_id") String workspaceId,
                                              @PathVariable(name = "job_id") String jobId) {
         waylineJobService.uploadMediaHighestPriority(workspaceId, jobId);
@@ -108,6 +114,7 @@
     }
 
     @PutMapping("/{workspace_id}/jobs/{job_id}")
+    @SysLogAnnotation(operModul = "计划库", operType = "修改", operDesc = "修改")
     public ResponseResult updateJobStatus(@PathVariable(name = "workspace_id") String workspaceId,
                                           @PathVariable(name = "job_id") String jobId,
                                           @Valid @RequestBody UpdateJobParam param) {

--
Gitblit v1.9.3