S0025136190 1 year ago
parent
commit
8101825472
23 changed files with 1154 additions and 0 deletions
  1. 10 0
      Backend/sample/pom.xml
  2. 4 0
      Backend/sample/src/main/java/com/dji/sample/common/smsp/CallSmsp.java
  3. 33 0
      Backend/sample/src/main/java/com/dji/sample/common/util/AddressUtils.java
  4. 3 0
      Backend/sample/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
  5. 4 0
      Backend/sample/src/main/java/com/dji/sample/manage/controller/LoginController.java
  6. 3 0
      Backend/sample/src/main/java/com/dji/sample/manage/controller/UserController.java
  7. 8 0
      Backend/sample/src/main/java/com/dji/sample/manage/dao/ISysOperLogMapper.java
  8. 50 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/annotation/Log.java
  9. 230 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/aspect/LogAspect.java
  10. 18 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/enums/BusinessStatus.java
  11. 58 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/enums/BusinessType.java
  12. 23 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/enums/OperatorType.java
  13. 42 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/event/LogininforEvent.java
  14. 108 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/event/OperLogEvent.java
  15. 111 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/util/JsonUtils.java
  16. 72 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/util/ReqUtils.java
  17. 67 0
      Backend/sample/src/main/java/com/dji/sample/manage/log/util/SpringUtils.java
  18. 115 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/entity/SysOperLogEntity.java
  19. 32 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/param/DeviceBoundQueryParam.java
  20. 38 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/ISysOperLogService.java
  21. 3 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/ManageDeviceLivestreamUrlServiceImpl.java
  22. 120 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/SysOperLogServiceImpl.java
  23. 2 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java

+ 10 - 0
Backend/sample/pom.xml

@@ -196,6 +196,16 @@
            <!--<classifier>linux-x86_64</classifier>-->
         </dependency>
 
+        <dependency>
+            <groupId>com.lmax</groupId>
+            <artifactId>disruptor</artifactId>
+            <version>3.4.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+            <version>2.14.5</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 4 - 0
Backend/sample/src/main/java/com/dji/sample/common/smsp/CallSmsp.java

@@ -2,6 +2,8 @@ package com.dji.sample.common.smsp;
 
 import cn.hutool.json.JSONObject;
 import com.dji.sample.configuration.CustomConfiguration;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.ManageDeviceLivestreamUrlDTO;
 import com.dji.sample.manage.model.dto.RtmpUrlDTO;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -35,6 +37,8 @@ public class CallSmsp {
 
 
 
+
+
     public static Map<String,String> getSmspToken(String username,String password,String protocal) {
         String tokenUrl = HTTPS.equals(protocal) ? CustomConfiguration.smspTokenUrl2 : CustomConfiguration.smspTokenUrl;
         String url = tokenUrl+"?username="+username+"&password="+password;

+ 33 - 0
Backend/sample/src/main/java/com/dji/sample/common/util/AddressUtils.java

@@ -0,0 +1,33 @@
+package com.dji.sample.common.util;
+
+import cn.hutool.core.net.NetUtil;
+import cn.hutool.http.HtmlUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 获取地址类
+ *
+ * @author papb
+ */
+@Slf4j
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class AddressUtils {
+
+    // 未知地址
+    public static final String UNKNOWN = "XX XX";
+
+    public static String getRealAddressByIP(String ip) {
+        if (StringUtils.isBlank(ip)) {
+            return UNKNOWN;
+        }
+        // 内网不查询
+        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
+        if (NetUtil.isInnerIP(ip)) {
+            return "内网IP";
+        }
+        return ip;
+    }
+}

+ 3 - 0
Backend/sample/src/main/java/com/dji/sample/manage/controller/LiveStreamController.java

@@ -1,6 +1,8 @@
 package com.dji.sample.manage.controller;
 
 import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.CapacityDeviceDTO;
 import com.dji.sample.manage.model.dto.LiveTypeDTO;
 import com.dji.sample.manage.service.ILiveStreamService;
@@ -54,6 +56,7 @@ public class LiveStreamController {
      * @param liveParam Live streaming parameters.
      * @return
      */
+    @Log(title = "开始直播",businessType = BusinessType.OTHER)
     @PostMapping("/streams/start")
     public HttpResultResponse liveStart(HttpServletRequest request,@RequestBody LiveTypeDTO liveParam)  {
         URL requestURL = null;

+ 4 - 0
Backend/sample/src/main/java/com/dji/sample/manage/controller/LoginController.java

@@ -1,6 +1,8 @@
 package com.dji.sample.manage.controller;
 
 import com.dji.sample.common.error.CommonErrorEnum;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.SignLoginDTO;
 import com.dji.sample.manage.model.dto.UserDTO;
 import com.dji.sample.manage.model.dto.UserLoginDTO;
@@ -36,6 +38,7 @@ public class LoginController {
     }
 
     @PostMapping("/getToken")
+    @Log(title = "流媒体获取Token",businessType = BusinessType.OTHER)
     public HttpResultResponse getToken(HttpServletRequest request,@RequestBody UserLoginDTO loginDTO) {
 
         String username = loginDTO.getUsername();
@@ -58,6 +61,7 @@ public class LoginController {
         return HttpResultResponse.success(user.get());
     }
 
+    @Log(title = "单点登录",businessType = BusinessType.OTHER)
     @PostMapping("/signLogin")
     public HttpResultResponse signLogin(@RequestBody SignLoginDTO loginDTO) {
         return userService.userLogin(loginDTO);

+ 3 - 0
Backend/sample/src/main/java/com/dji/sample/manage/controller/UserController.java

@@ -1,6 +1,8 @@
 package com.dji.sample.manage.controller;
 
 import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.UserListDTO;
 import com.dji.sample.manage.model.dto.UserLoginDTO;
 import com.dji.sample.manage.model.dto.UserPilotDTO;
@@ -69,6 +71,7 @@ public class UserController {
     }
 
     @PostMapping("/saveApiUser")
+    @Log(title = "流媒体推送API User",businessType = BusinessType.OTHER)
     public HttpResultResponse saveApiUser(HttpServletRequest request,@RequestBody UserLoginDTO loginDTO) {
         String username = loginDTO.getUsername();
         String password = loginDTO.getPassword();

+ 8 - 0
Backend/sample/src/main/java/com/dji/sample/manage/dao/ISysOperLogMapper.java

@@ -0,0 +1,8 @@
+package com.dji.sample.manage.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dji.sample.manage.model.entity.SysOperLogEntity;
+
+public interface ISysOperLogMapper extends BaseMapper<SysOperLogEntity> {
+
+}

+ 50 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/annotation/Log.java

@@ -0,0 +1,50 @@
+package com.dji.sample.manage.log.annotation;
+
+
+
+import com.dji.sample.manage.log.enums.BusinessType;
+import com.dji.sample.manage.log.enums.OperatorType;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义操作日志记录注解
+ *
+ * @author papb
+ */
+@Target({ElementType.PARAMETER, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Log {
+    /**
+     * 模块
+     */
+    String title() default "";
+
+    /**
+     * 功能
+     */
+    BusinessType businessType() default BusinessType.OTHER;
+
+    /**
+     * 操作人类别
+     */
+    OperatorType operatorType() default OperatorType.MANAGE;
+
+    /**
+     * 是否保存请求的参数
+     */
+    boolean isSaveRequestData() default true;
+
+    /**
+     * 是否保存响应的参数
+     */
+    boolean isSaveResponseData() default true;
+
+
+    /**
+     * 排除指定的请求参数
+     */
+    String[] excludeParamNames() default {};
+
+}

+ 230 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/aspect/LogAspect.java

@@ -0,0 +1,230 @@
+package com.dji.sample.manage.log.aspect;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.ttl.TransmittableThreadLocal;
+import com.dji.sample.common.util.UserRequest;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessStatus;
+import com.dji.sample.manage.log.event.OperLogEvent;
+import com.dji.sample.manage.log.util.JsonUtils;
+import com.dji.sample.manage.log.util.ReqUtils;
+import com.dji.sample.manage.log.util.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.http.HttpMethod;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+import java.util.StringJoiner;
+
+/**
+ * 操作日志记录处理
+ *
+ * @author papb
+ */
+@Slf4j
+@Aspect
+@AutoConfiguration
+public class LogAspect {
+
+    /**
+     * 排除敏感属性字段
+     */
+    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
+
+
+    /**
+     * 计算操作消耗时间
+     */
+    private static final ThreadLocal<StopWatch> TIME_THREADLOCAL = new TransmittableThreadLocal<>();
+
+    /**
+     * 处理请求前执行
+     */
+    @Before(value = "@annotation(controllerLog)")
+    public void boBefore(JoinPoint joinPoint, Log controllerLog) {
+        StopWatch stopWatch = new StopWatch();
+        TIME_THREADLOCAL.set(stopWatch);
+        stopWatch.start();
+    }
+
+    /**
+     * 处理完请求后执行
+     *
+     * @param joinPoint 切点
+     */
+    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
+    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
+        handleLog(joinPoint, controllerLog, null, jsonResult);
+    }
+
+    /**
+     * 拦截异常操作
+     *
+     * @param joinPoint 切点
+     * @param e         异常
+     */
+    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
+    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
+        handleLog(joinPoint, controllerLog, e, null);
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
+        try {
+
+            // *========数据库日志=========*//
+            OperLogEvent operLog = new OperLogEvent();
+            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
+            // 请求的地址
+            String ip = ReqUtils.getClientIp();
+            operLog.setOperIp(ip);
+            operLog.setOperUrl(StringUtils.substring(ReqUtils.getRequestURI(), 0, 255));
+            if(UserRequest.getCurrentUser() != null ){
+                operLog.setOperName(UserRequest.getCurrentUser().getUsername());
+            }
+
+            if (e != null) {
+                operLog.setStatus(BusinessStatus.FAIL.ordinal());
+                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
+            }else if(!checkCode(jsonResult)) {
+                operLog.setStatus(BusinessStatus.FAIL.ordinal());
+            }
+            // 设置方法名称
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = joinPoint.getSignature().getName();
+            operLog.setMethod(className + "." + methodName + "()");
+            // 设置请求方式
+            operLog.setRequestMethod(ReqUtils.getRequestMethod());
+            // 处理设置注解上的参数
+            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
+            // 设置消耗时间
+            StopWatch stopWatch = TIME_THREADLOCAL.get();
+            stopWatch.stop();
+            operLog.setCostTime(stopWatch.getTime());
+            // 发布事件保存数据库
+            SpringUtils.context().publishEvent(operLog);
+        } catch (Exception exp) {
+            // 记录本地异常日志
+            log.error("异常信息:{}", exp.getMessage());
+            exp.printStackTrace();
+        } finally {
+            TIME_THREADLOCAL.remove();
+        }
+    }
+
+    /**
+     * 获取注解中对方法的描述信息 用于Controller层注解
+     *
+     * @param log     日志
+     * @param operLog 操作日志
+     * @throws Exception
+     */
+    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
+        // 设置action动作
+        operLog.setBusinessType(log.businessType().ordinal());
+        // 设置标题
+        operLog.setTitle(log.title());
+        // 设置操作人类别
+        operLog.setOperatorType(log.operatorType().ordinal());
+        // 是否需要保存request,参数和值
+        if (log.isSaveRequestData()) {
+            // 获取参数的信息,传入到数据库中。
+            setRequestValue(joinPoint, operLog, log.excludeParamNames());
+        }
+        // 是否需要保存response,参数和值
+        if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
+            operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000));
+        }
+    }
+
+    /**
+     * 获取请求的参数,放到log中
+     *
+     * @param operLog 操作日志
+     * @throws Exception 异常
+     */
+    private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
+        Map<String, String[]> paramsMap = ReqUtils.getParameters();
+        String requestMethod = operLog.getRequestMethod();
+        if (MapUtil.isEmpty(paramsMap)
+                && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
+            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
+            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
+        } else {
+            MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
+            MapUtil.removeAny(paramsMap, excludeParamNames);
+            operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
+        }
+    }
+
+    /**
+     * 参数拼装
+     */
+    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
+        StringJoiner params = new StringJoiner(" ");
+        if (ArrayUtil.isEmpty(paramsArray)) {
+            return params.toString();
+        }
+        for (Object o : paramsArray) {
+            if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
+                String str = JsonUtils.toJsonString(o);
+                Dict dict = JsonUtils.parseMap(str);
+                if (MapUtil.isNotEmpty(dict)) {
+                    MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);
+                    MapUtil.removeAny(dict, excludeParamNames);
+                    str = JsonUtils.toJsonString(dict);
+                }
+                params.add(str);
+            }
+        }
+        return params.toString();
+    }
+
+    /**
+     * 判断是否需要过滤的对象。
+     *
+     * @param o 对象信息。
+     * @return 如果是需要过滤的对象,则返回true;否则返回false。
+     */
+    @SuppressWarnings("rawtypes")
+    public boolean isFilterObject(final Object o) {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray()) {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        } else if (Collection.class.isAssignableFrom(clazz)) {
+            Collection collection = (Collection) o;
+            for (Object value : collection) {
+                return value instanceof MultipartFile;
+            }
+        } else if (Map.class.isAssignableFrom(clazz)) {
+            Map map = (Map) o;
+            for (Object value : map.values()) {
+                return value instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+               || o instanceof BindingResult;
+    }
+
+    private boolean checkCode(Object json) {
+        int code = Integer.valueOf(String.valueOf(JSONUtil.parse(json).getByPath("code")));
+        boolean isSuccess = code ==0 || code==200;
+        return isSuccess;
+
+    }
+}

+ 18 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/enums/BusinessStatus.java

@@ -0,0 +1,18 @@
+package com.dji.sample.manage.log.enums;
+
+/**
+ * 操作状态
+ *
+ * @author papb
+ */
+public enum BusinessStatus {
+    /**
+     * 成功
+     */
+    SUCCESS,
+
+    /**
+     * 失败
+     */
+    FAIL,
+}

+ 58 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/enums/BusinessType.java

@@ -0,0 +1,58 @@
+package com.dji.sample.manage.log.enums;
+
+/**
+ * 业务操作类型
+ *
+ * @author papb
+ */
+public enum BusinessType {
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 新增
+     */
+    INSERT,
+
+    /**
+     * 修改
+     */
+    UPDATE,
+
+    /**
+     * 删除
+     */
+    DELETE,
+
+    /**
+     * 授权
+     */
+    GRANT,
+
+    /**
+     * 导出
+     */
+    EXPORT,
+
+    /**
+     * 导入
+     */
+    IMPORT,
+
+    /**
+     * 强退
+     */
+    FORCE,
+
+    /**
+     * 生成代码
+     */
+    GENCODE,
+
+    /**
+     * 清空数据
+     */
+    CLEAN,
+}

+ 23 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/enums/OperatorType.java

@@ -0,0 +1,23 @@
+package com.dji.sample.manage.log.enums;
+
+/**
+ * 操作人类别
+ *
+ * @author papb
+ */
+public enum OperatorType {
+    /**
+     * 其它
+     */
+    OTHER,
+
+    /**
+     * 后台用户
+     */
+    MANAGE,
+
+    /**
+     * 手机端用户
+     */
+    MOBILE
+}

+ 42 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/event/LogininforEvent.java

@@ -0,0 +1,42 @@
+package com.dji.sample.manage.log.event;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 登录事件
+ *
+ * @author papb
+ */
+
+@Data
+public class LogininforEvent implements Serializable {
+
+
+    private static final long serialVersionUID = 1L;
+
+
+
+    /**
+     * 用户账号
+     */
+    private String username;
+
+    /**
+     * 登录状态 0成功 1失败
+     */
+    private String status;
+
+    /**
+     * 提示消息
+     */
+    private String message;
+
+
+    /**
+     * 其他参数
+     */
+    private Object[] args;
+
+}

+ 108 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/event/OperLogEvent.java

@@ -0,0 +1,108 @@
+package com.dji.sample.manage.log.event;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 操作日志事件
+ *
+ * @author papb
+ */
+
+@Data
+public class OperLogEvent implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 日志主键
+     */
+    private Long operId;
+
+    /**
+     * 操作模块
+     */
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    private Integer businessType;
+
+    /**
+     * 业务类型数组
+     */
+    private Integer[] businessTypes;
+
+    /**
+     * 请求方法
+     */
+    private String method;
+
+    /**
+     * 请求方式
+     */
+    private String requestMethod;
+
+    /**
+     * 操作类别(0其它 1后台用户 2手机端用户)
+     */
+    private Integer operatorType;
+
+    /**
+     * 操作人员
+     */
+    private String operName;
+
+    /**
+     * 部门名称
+     */
+    private String deptName;
+
+    /**
+     * 请求url
+     */
+    private String operUrl;
+
+    /**
+     * 操作地址
+     */
+    private String operIp;
+
+    /**
+     * 操作地点
+     */
+    private String operLocation;
+
+    /**
+     * 请求参数
+     */
+    private String operParam;
+
+    /**
+     * 返回参数
+     */
+    private String jsonResult;
+
+    /**
+     * 操作状态(0正常 1异常)
+     */
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    private String errorMsg;
+
+    /**
+     * 操作时间
+     */
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    private Long costTime;
+}

+ 111 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/util/JsonUtils.java

@@ -0,0 +1,111 @@
+package com.dji.sample.manage.log.util;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 工具类
+ *
+ * @author 芋道源码
+ */
+public class JsonUtils {
+
+    private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
+
+    public static ObjectMapper getObjectMapper() {
+        return OBJECT_MAPPER;
+    }
+
+    public static String toJsonString(Object object) {
+        if (ObjectUtil.isNull(object)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.writeValueAsString(object);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        if (StringUtils.isEmpty(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
+        if (ArrayUtil.isEmpty(bytes)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(bytes, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, typeReference);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Dict parseMap(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
+        } catch (MismatchedInputException e) {
+            // 类型不匹配说明不是json
+            return null;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static List<Dict> parseArrayMap(String text) {
+        if (StringUtils.isBlank(text)) {
+            return null;
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
+        if (StringUtils.isEmpty(text)) {
+            return new ArrayList<>();
+        }
+        try {
+            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 72 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/util/ReqUtils.java

@@ -0,0 +1,72 @@
+package com.dji.sample.manage.log.util;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 客户端工具类
+ *
+ * @author papb
+ */
+public class ReqUtils {
+
+    /**
+     * 获取客户端IP地址
+     * @return 客户端IP地址
+     */
+    public static String getClientIp() {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        String ip = request.getHeader("X-Forwarded-For");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    /**
+     * 获取请求方法
+     * @return 请求方法
+     */
+    public static String getRequestMethod() {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        return request.getMethod();
+    }
+
+    /**
+     * 获取请求URL
+     * @return 请求方法
+     */
+    public static String getRequestURI() {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        return request.getRequestURI();
+    }
+
+    /**
+     * 获取请求参数
+     * @return 请求参数的Map
+     */
+    public static Map<String, String[]> getParameters() {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        Map<String, String[]> parameters = new HashMap<>();
+        Enumeration<String> parameterNames = request.getParameterNames();
+        while (parameterNames.hasMoreElements()) {
+            String name = parameterNames.nextElement();
+            String[] values = request.getParameterValues(name);
+            parameters.put(name, values);
+        }
+        return parameters;
+    }
+}

+ 67 - 0
Backend/sample/src/main/java/com/dji/sample/manage/log/util/SpringUtils.java

@@ -0,0 +1,67 @@
+package com.dji.sample.manage.log.util;
+
+import cn.hutool.extra.spring.SpringUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * spring工具类
+ *
+ * @author papb
+ */
+
+@Data
+@Slf4j
+public final class SpringUtils extends SpringUtil {
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     */
+    public static boolean containsBean(String name) {
+        return getBeanFactory().containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
+     * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return getBeanFactory().isSingleton(name);
+    }
+
+    /**
+     * @return Class 注册对象的类型
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return getBeanFactory().getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return getBeanFactory().getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+
+    /**
+     * 获取spring上下文
+     */
+    public static ApplicationContext context() {
+        return getApplicationContext();
+    }
+
+}

+ 115 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/entity/SysOperLogEntity.java

@@ -0,0 +1,115 @@
+package com.dji.sample.manage.model.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@TableName(value = "sys_oper_log")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class SysOperLogEntity implements Serializable {
+
+    /**
+     * 日志主键
+     */
+    @TableId(value = "oper_id")
+    private Long operId;
+
+    /**
+     * 操作模块
+     */
+    @TableField(value = "title")
+    private String title;
+
+    /**
+     * 业务类型(0其它 1新增 2修改 3删除)
+     */
+    @TableField(value = "business_type")
+    private Integer businessType;
+
+    /**
+     * 请求方法
+     */
+    @TableField(value = "method")
+    private String method;
+
+    /**
+     * 请求方式
+     */
+    @TableField(value = "request_method")
+    private String requestMethod;
+
+    /**
+     * 操作类别(0其它 1后台用户 2手机端用户)
+     */
+    @TableField(value = "operator_type")
+    private Integer operatorType;
+
+    /**
+     * 操作人员
+     */
+    @TableField(value = "oper_name")
+    private String operName;
+
+
+    /**
+     * 请求url
+     */
+    @TableField(value = "oper_url")
+    private String operUrl;
+
+    /**
+     * 操作地址
+     */
+    @TableField(value = "oper_ip")
+    private String operIp;
+
+    /**
+     * 操作地点
+     */
+    @TableField(value = "oper_location")
+    private String operLocation;
+
+    /**
+     * 请求参数
+     */
+    @TableField(value = "oper_param")
+    private String operParam;
+
+    /**
+     * 返回参数
+     */
+    @TableField(value = "json_result")
+    private String jsonResult;
+
+    /**
+     * 操作状态(0正常 1异常)
+     */
+    @TableField(value = "status")
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    @TableField(value = "error_msg")
+    private String errorMsg;
+
+    /**
+     * 操作时间
+     */
+    @TableField(value = "oper_time")
+    private Date operTime;
+
+    /**
+     * 消耗时间
+     */
+    @TableField(value = "cost_time")
+    private Long costTime;
+}

+ 32 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/param/DeviceBoundQueryParam.java

@@ -0,0 +1,32 @@
+package com.dji.sample.manage.model.param;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/8/12
+ */
+@Data
+public class DeviceBoundQueryParam {
+
+    private Long page;
+
+    @JsonProperty("page_size")
+    private Long pageSize;
+
+    @JsonProperty("begin_time")
+    private Long beginTime;
+
+    @JsonProperty("end_time")
+    private Long endTime;
+
+    @JsonProperty("device_name")
+    private String deviceName;
+
+    @JsonProperty("logs_information")
+    private String logsInformation;
+
+
+}

+ 38 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/ISysOperLogService.java

@@ -0,0 +1,38 @@
+package com.dji.sample.manage.service;
+
+
+import com.dji.sample.manage.model.entity.SysOperLogEntity;
+
+
+public interface ISysOperLogService {
+
+
+    /**
+     * 新增操作日志
+     *
+     * @param bo 操作日志对象
+     */
+    void insertOperlog(SysOperLogEntity bo);
+
+
+    /**
+     * 批量删除系统操作日志
+     *
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    int deleteOperLogByIds(Long[] operIds);
+
+    /**
+     * 查询操作日志详细
+     *
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    SysOperLogEntity selectOperLogById(Long operId);
+
+    /**
+     * 清空操作日志
+     */
+    void cleanOperLog();
+}

+ 3 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/ManageDeviceLivestreamUrlServiceImpl.java

@@ -6,6 +6,8 @@ import com.dji.sample.common.util.DesUtil;
 import com.dji.sample.configuration.CustomConfiguration;
 import com.dji.sample.manage.dao.IManageDeviceLivestreamUrlMapper;
 import com.dji.sample.manage.dao.IUserMapper;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.ManageDeviceLivestreamUrlDTO;
 import com.dji.sample.manage.model.dto.RtmpUrlDTO;
 import com.dji.sample.manage.model.entity.ManageDeviceLivestreamUrlEntity;
@@ -37,6 +39,7 @@ public class ManageDeviceLivestreamUrlServiceImpl implements IManageDeviceLivest
     private IManageDeviceLivestreamUrlMapper mapper;
 
 
+
     @Override
     public ManageDeviceLivestreamUrlDTO getUrl(ManageDeviceLivestreamUrlDTO deviceLivestreamUrlDTO,String protocal) {
 

+ 120 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/SysOperLogServiceImpl.java

@@ -0,0 +1,120 @@
+package com.dji.sample.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.dji.sample.common.util.AddressUtils;
+import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
+import com.dji.sample.manage.dao.ISysOperLogMapper;
+import com.dji.sample.manage.log.event.OperLogEvent;
+import com.dji.sample.manage.model.dto.UserDTO;
+import com.dji.sample.manage.model.entity.SysOperLogEntity;
+import com.dji.sample.manage.model.entity.UserEntity;
+import com.dji.sample.manage.service.ISysOperLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+
+
+@Service
+@Transactional
+@Slf4j
+public class SysOperLogServiceImpl implements ISysOperLogService {
+
+
+    @Autowired
+    private ISysOperLogMapper mapper;
+
+
+    /**
+     * 操作日志记录
+     *
+     * @param operLogEvent 操作日志事件
+     */
+    @Async
+    @EventListener
+    public void recordOper(OperLogEvent operLogEvent) {
+        SysOperLogEntity operLog = eventConvertToEntity(operLogEvent);
+        // 远程查询操作地点
+        operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
+        insertOperlog(operLog);
+    }
+
+
+
+    /**
+     * 新增操作日志
+     *
+     * @param operLog 操作日志对象
+     */
+    @Override
+    public void insertOperlog(SysOperLogEntity operLog) {
+        operLog.setOperTime(new Date());
+        mapper.insert(operLog);
+    }
+
+
+
+    /**
+     * 批量删除系统操作日志
+     *
+     * @param operIds 需要删除的操作日志ID
+     * @return 结果
+     */
+    @Override
+    public int deleteOperLogByIds(Long[] operIds) {
+        return mapper.deleteBatchIds(Arrays.asList(operIds));
+    }
+
+    /**
+     * 查询操作日志详细
+     *
+     * @param operId 操作ID
+     * @return 操作日志对象
+     */
+    @Override
+    public SysOperLogEntity selectOperLogById(Long operId) {
+        return mapper.selectById(operId);
+    }
+
+    /**
+     * 清空操作日志
+     */
+    @Override
+    public void cleanOperLog() {
+        mapper.delete(new LambdaQueryWrapper<>());
+    }
+
+    /**
+     * Convert database entity objects into user data transfer object.
+     * @param operLogEvent
+     * @return
+     */
+    private SysOperLogEntity eventConvertToEntity(OperLogEvent operLogEvent) {
+        if (operLogEvent == null) {
+            return null;
+        }
+        return SysOperLogEntity.builder()
+                .operId(operLogEvent.getOperId())
+                .title(operLogEvent.getTitle())
+                .businessType(operLogEvent.getBusinessType())
+                .method(operLogEvent.getMethod())
+                .requestMethod(operLogEvent.getRequestMethod())
+                .operatorType(operLogEvent.getOperatorType())
+                .operName(operLogEvent.getOperName())
+                .operUrl(operLogEvent.getOperUrl())
+                .operIp(operLogEvent.getOperIp())
+                .operLocation(operLogEvent.getOperLocation())
+                .operParam(operLogEvent.getOperParam())
+                .jsonResult(operLogEvent.getJsonResult())
+                .status(operLogEvent.getStatus())
+                .errorMsg(operLogEvent.getErrorMsg())
+                .operTime(operLogEvent.getOperTime())
+                .costTime(operLogEvent.getCostTime())
+                .build();
+    }
+
+}

+ 2 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java

@@ -17,6 +17,8 @@ import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
 import com.dji.sample.configuration.CustomConfiguration;
 import com.dji.sample.manage.dao.IUserMapper;
 import com.dji.sample.manage.dao.IWorkspaceMapper;
+import com.dji.sample.manage.log.annotation.Log;
+import com.dji.sample.manage.log.enums.BusinessType;
 import com.dji.sample.manage.model.dto.*;
 import com.dji.sample.manage.model.entity.UserEntity;
 import com.dji.sample.manage.model.entity.WorkspaceEntity;