xiongzhu 4 vuotta sitten
vanhempi
commit
895574849f

+ 5 - 0
pom.xml

@@ -71,6 +71,11 @@
             <artifactId>spring-boot-starter-web</artifactId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
         </dependency>
 
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
         <dependency>
         <dependency>
             <groupId>mysql</groupId>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <artifactId>mysql-connector-java</artifactId>

+ 14 - 0
src/main/java/com/izouma/jmrh/annotations/OperLog.java

@@ -0,0 +1,14 @@
+package com.izouma.jmrh.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface OperLog {
+    String value() default "";
+
+    String type() default "";
+
+    String desc() default "";
+}

+ 189 - 0
src/main/java/com/izouma/jmrh/aspect/OperLogAspect.java

@@ -0,0 +1,189 @@
+package com.izouma.jmrh.aspect;
+
+import java.lang.reflect.Method;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.alibaba.fastjson.JSON;
+import com.izouma.jmrh.annotations.OperLog;
+import com.izouma.jmrh.domain.ExceptionLog;
+import com.izouma.jmrh.domain.OperationLog;
+import com.izouma.jmrh.domain.User;
+import com.izouma.jmrh.repo.ExceptionLogRepo;
+import com.izouma.jmrh.repo.OperationLogRepo;
+import com.izouma.jmrh.utils.IPUtils;
+import com.izouma.jmrh.utils.SecurityUtils;
+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.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;
+
+@Aspect
+@Component
+public class OperLogAspect {
+
+    @Autowired
+    private OperationLogRepo operationLogRepo;
+
+    @Autowired
+    private ExceptionLogRepo exceptionLogRepo;
+
+    /**
+     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
+     */
+    @Pointcut("@annotation(com.izouma.jmrh.annotations.OperLog)")
+    public void operLogPointCut() {
+    }
+
+    /**
+     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
+     */
+    @Pointcut("execution(* com.izouma.jmrh.web..*.*(..))")
+    public void operExceptionLogPointCut() {
+    }
+
+    /**
+     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
+     *
+     * @param joinPoint 切入点
+     * @param keys      返回结果
+     */
+    @AfterReturning(value = "operLogPointCut()", returning = "keys")
+    public void saveOperLog(JoinPoint joinPoint, Object keys) {
+        // 获取RequestAttributes
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        // 从获取RequestAttributes中获取HttpServletRequest的信息
+        HttpServletRequest request = (HttpServletRequest) requestAttributes
+                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
+
+        OperationLog operationLog = new OperationLog();
+        try {
+            // 从切面织入点处通过反射机制获取织入点处的方法
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            // 获取切入点所在的方法
+            Method method = signature.getMethod();
+            // 获取操作
+            OperLog operLog = method.getAnnotation(OperLog.class);
+            if (operLog != null) {
+                operationLog.setName(operLog.value()); // 操作模块
+                operationLog.setType(operLog.type()); // 操作类型
+                operationLog.setDesc(operLog.desc()); // 操作描述
+            }
+            // 获取请求的类名
+            String className = joinPoint.getTarget().getClass().getName();
+            // 获取请求的方法名
+            String methodName = method.getName();
+            methodName = className + "." + methodName;
+
+            operationLog.setReqMethod(methodName);
+
+            // 请求的参数
+            Map<String, String> rtnMap = null;
+            String params = null;
+            if (request != null) {
+                rtnMap = convertMap(request.getParameterMap());
+                params = JSON.toJSONString(rtnMap);
+            }
+
+            operationLog.setReqParams(params);
+            operationLog.setResp(JSON.toJSONString(keys));
+
+            User user = SecurityUtils.getAuthenticatedUser();
+            if (user != null) {
+                operationLog.setUserId(String.valueOf(user.getId()));
+                operationLog.setUsername(user.getUsername());
+            }
+            operationLog.setReqIp(IPUtils.getIpAddr(request));
+            operationLog.setReqUrl(request != null ? request.getRequestURI() : null);
+            operationLog.setTime(LocalDateTime.now());
+            operationLogRepo.save(operationLog);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
+     *
+     * @param joinPoint 切入点
+     * @param e         异常信息
+     */
+    @AfterThrowing(pointcut = "operExceptionLogPointCut()", throwing = "e")
+    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
+        // 获取RequestAttributes
+        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+        // 从获取RequestAttributes中获取HttpServletRequest的信息
+        HttpServletRequest request = (HttpServletRequest) requestAttributes
+                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
+
+        ExceptionLog exceptionLog = new ExceptionLog();
+        try {
+            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+            Method method = signature.getMethod();
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = method.getName();
+            methodName = className + "." + methodName;
+            exceptionLog.setReqMethod(methodName);
+
+            Map<String, String> rtnMap = convertMap(request.getParameterMap());
+            String params = JSON.toJSONString(rtnMap);
+            exceptionLog.setReqParams(params);
+
+            exceptionLog.setName(e.getClass().getName());
+            exceptionLog.setMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e
+                    .getStackTrace()));
+            User user = SecurityUtils.getAuthenticatedUser();
+            if (user != null) {
+                exceptionLog.setUserId(String.valueOf(user.getId()));
+                exceptionLog.setUsername(user.getUsername());
+            }
+
+            exceptionLog.setReqUrl(request.getRequestURI());
+            exceptionLog.setReqIp(IPUtils.getIpAddr(request));
+            exceptionLog.setTime(LocalDateTime.now());
+
+            exceptionLogRepo.save(exceptionLog);
+
+        } catch (Exception e2) {
+            e2.printStackTrace();
+        }
+
+    }
+
+    /**
+     * 转换request 请求参数
+     *
+     * @param paramMap request获取的参数数组
+     */
+    public Map<String, String> convertMap(Map<String, String[]> paramMap) {
+        Map<String, String> rtnMap = new HashMap<>();
+        for (String key : paramMap.keySet()) {
+            rtnMap.put(key, paramMap.get(key)[0]);
+        }
+        return rtnMap;
+    }
+
+    /**
+     * 转换异常信息为字符串
+     *
+     * @param exceptionName    异常名称
+     * @param exceptionMessage 异常信息
+     * @param elements         堆栈信息
+     */
+    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
+        StringBuilder strBuff = new StringBuilder();
+        for (StackTraceElement stet : elements) {
+            strBuff.append(stet).append("\n");
+        }
+        return exceptionName + ":" + exceptionMessage + "\n\t" + strBuff;
+    }
+}

+ 71 - 0
src/main/java/com/izouma/jmrh/domain/ExceptionLog.java

@@ -0,0 +1,71 @@
+package com.izouma.jmrh.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+@ApiModel("异常日志")
+public class ExceptionLog {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("异常名称")
+    private String name;
+
+    @ApiModelProperty("操作类型")
+    private String type;
+
+    @ApiModelProperty("用户ID")
+    private String userId;
+
+    @ApiModelProperty("用户名")
+    private String username;
+
+    @ApiModelProperty("描述")
+    @Column(name = "description")
+    private String desc;
+
+    @ApiModelProperty("调用方法")
+    private String reqMethod;
+
+    @ApiModelProperty("请求地址")
+    private String reqUrl;
+
+    @ApiModelProperty("请求参数")
+    @Lob
+    private String reqParams;
+
+    @ApiModelProperty("请求ip")
+    private String reqIp;
+
+    @ApiModelProperty("返回结果")
+    @Lob
+    private String resp;
+
+    @ApiModelProperty("操作时间")
+    private LocalDateTime time;
+
+    @ApiModelProperty("异常消息")
+    @Lob
+    private String message;
+
+    @ApiModelProperty("错误追踪")
+    @Lob
+    private String stackTrace;
+}

+ 63 - 0
src/main/java/com/izouma/jmrh/domain/OperationLog.java

@@ -0,0 +1,63 @@
+package com.izouma.jmrh.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+@ApiModel("操作日志")
+public class OperationLog {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("操作名称")
+    private String name;
+
+    @ApiModelProperty("操作类型")
+    private String type;
+
+    @ApiModelProperty("用户ID")
+    private String userId;
+
+    @ApiModelProperty("用户名")
+    private String username;
+
+    @ApiModelProperty("描述")
+    @Column(name = "description")
+    private String desc;
+
+    @ApiModelProperty("调用方法")
+    private String reqMethod;
+
+    @ApiModelProperty("请求地址")
+    private String reqUrl;
+
+    @ApiModelProperty("请求参数")
+    @Lob
+    private String reqParams;
+
+    @ApiModelProperty("请求ip")
+    private String reqIp;
+
+    @ApiModelProperty("返回结果")
+    @Lob
+    private String resp;
+
+    @ApiModelProperty("操作时间")
+    private LocalDateTime time;
+}

+ 8 - 0
src/main/java/com/izouma/jmrh/repo/ExceptionLogRepo.java

@@ -0,0 +1,8 @@
+package com.izouma.jmrh.repo;
+
+import com.izouma.jmrh.domain.ExceptionLog;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface ExceptionLogRepo extends JpaRepository<ExceptionLog, Long>, JpaSpecificationExecutor<ExceptionLog> {
+}

+ 8 - 0
src/main/java/com/izouma/jmrh/repo/OperationLogRepo.java

@@ -0,0 +1,8 @@
+package com.izouma.jmrh.repo;
+
+import com.izouma.jmrh.domain.OperationLog;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface OperationLogRepo extends JpaRepository<OperationLog, Long>, JpaSpecificationExecutor<OperationLog> {
+}

+ 14 - 0
src/main/java/com/izouma/jmrh/service/ExceptionLogService.java

@@ -0,0 +1,14 @@
+package com.izouma.jmrh.service;
+
+import com.izouma.jmrh.domain.ExceptionLog;
+import com.izouma.jmrh.repo.ExceptionLogRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class ExceptionLogService {
+
+    private ExceptionLogRepo exceptionLogRepo;
+
+}

+ 14 - 0
src/main/java/com/izouma/jmrh/service/OperationLogService.java

@@ -0,0 +1,14 @@
+package com.izouma.jmrh.service;
+
+import com.izouma.jmrh.domain.OperationLog;
+import com.izouma.jmrh.repo.OperationLogRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class OperationLogService {
+
+    private OperationLogRepo operationLogRepo;
+
+}

+ 71 - 0
src/main/java/com/izouma/jmrh/utils/IPUtils.java

@@ -0,0 +1,71 @@
+package com.izouma.jmrh.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+@Slf4j
+public class IPUtils {
+    private static final String IP_UTILS_FLAG = ",";
+    private static final String UNKNOWN       = "unknown";
+    private static final String LOCALHOST_IP  = "0:0:0:0:0:0:0:1";
+    private static final String LOCALHOST_IP1 = "127.0.0.1";
+
+    /**
+     * 获取IP地址
+     * <p>
+     * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
+     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
+     */
+    public static String getIpAddr(HttpServletRequest request) {
+        String ip = null;
+        try {
+            //以下两个获取在k8s中,将真实的客户端IP,放到了x-Original-Forwarded-For。而将WAF的回源地址放到了 x-Forwarded-For了。
+            ip = request.getHeader("X-Original-Forwarded-For");
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("X-Forwarded-For");
+            }
+            //获取nginx等代理的ip
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("x-forwarded-for");
+            }
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_CLIENT_IP");
+            }
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+            }
+            //兼容k8s集群获取ip
+            if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+                if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
+                    //根据网卡取本机配置的IP
+                    InetAddress iNet = null;
+                    try {
+                        iNet = InetAddress.getLocalHost();
+                        ip = iNet.getHostAddress();
+                    } catch (UnknownHostException e) {
+                        log.error("getClientIp error", e);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("IPUtils ERROR ", e);
+        }
+        //使用代理,则获取第一个IP地址
+        if (!StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
+            ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));
+        }
+
+        return ip;
+    }
+}

+ 3 - 1
src/main/java/com/izouma/jmrh/web/ArticleController.java

@@ -1,5 +1,6 @@
 package com.izouma.jmrh.web;
 package com.izouma.jmrh.web;
 
 
+import com.izouma.jmrh.annotations.OperLog;
 import com.izouma.jmrh.domain.Article;
 import com.izouma.jmrh.domain.Article;
 import com.izouma.jmrh.dto.PageQuery;
 import com.izouma.jmrh.dto.PageQuery;
 import com.izouma.jmrh.enums.AuditStatus;
 import com.izouma.jmrh.enums.AuditStatus;
@@ -21,10 +22,11 @@ import java.util.List;
 @AllArgsConstructor
 @AllArgsConstructor
 public class ArticleController extends BaseController {
 public class ArticleController extends BaseController {
     private ArticleService articleService;
     private ArticleService articleService;
-    private ArticleRepo articleRepo;
+    private ArticleRepo    articleRepo;
 
 
     //@PreAuthorize("hasRole('ADMIN')")
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     @PostMapping("/save")
+    @OperLog(value = "发布管理-编辑", type = "编辑")
     public Article save(@RequestBody Article record) {
     public Article save(@RequestBody Article record) {
         if (record.getId() != null) {
         if (record.getId() != null) {
             Article orig = articleRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
             Article orig = articleRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));

+ 60 - 0
src/main/java/com/izouma/jmrh/web/ExceptionLogController.java

@@ -0,0 +1,60 @@
+package com.izouma.jmrh.web;
+import com.izouma.jmrh.domain.ExceptionLog;
+import com.izouma.jmrh.service.ExceptionLogService;
+import com.izouma.jmrh.dto.PageQuery;
+import com.izouma.jmrh.exception.BusinessException;
+import com.izouma.jmrh.repo.ExceptionLogRepo;
+import com.izouma.jmrh.utils.ObjUtils;
+import com.izouma.jmrh.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/exceptionLog")
+@AllArgsConstructor
+public class ExceptionLogController extends BaseController {
+    private ExceptionLogService exceptionLogService;
+    private ExceptionLogRepo exceptionLogRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public ExceptionLog save(@RequestBody ExceptionLog record) {
+        if (record.getId() != null) {
+            ExceptionLog orig = exceptionLogRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return exceptionLogRepo.save(orig);
+        }
+        return exceptionLogRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @GetMapping("/all")
+    public Page<ExceptionLog> all(PageQuery pageQuery) {
+        return exceptionLogRepo.findAll(toSpecification(pageQuery,ExceptionLog.class), toPageRequest(pageQuery));
+    }
+
+    @GetMapping("/get/{id}")
+    public ExceptionLog get(@PathVariable Long id) {
+        return exceptionLogRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        exceptionLogRepo.deleteById(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<ExceptionLog> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 60 - 0
src/main/java/com/izouma/jmrh/web/OperationLogController.java

@@ -0,0 +1,60 @@
+package com.izouma.jmrh.web;
+import com.izouma.jmrh.domain.OperationLog;
+import com.izouma.jmrh.service.OperationLogService;
+import com.izouma.jmrh.dto.PageQuery;
+import com.izouma.jmrh.exception.BusinessException;
+import com.izouma.jmrh.repo.OperationLogRepo;
+import com.izouma.jmrh.utils.ObjUtils;
+import com.izouma.jmrh.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/operationLog")
+@AllArgsConstructor
+public class OperationLogController extends BaseController {
+    private OperationLogService operationLogService;
+    private OperationLogRepo operationLogRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public OperationLog save(@RequestBody OperationLog record) {
+        if (record.getId() != null) {
+            OperationLog orig = operationLogRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return operationLogRepo.save(orig);
+        }
+        return operationLogRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @GetMapping("/all")
+    public Page<OperationLog> all(PageQuery pageQuery) {
+        return operationLogRepo.findAll(toSpecification(pageQuery,OperationLog.class), toPageRequest(pageQuery));
+    }
+
+    @GetMapping("/get/{id}")
+    public OperationLog get(@PathVariable Long id) {
+        return operationLogRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        operationLogRepo.deleteById(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<OperationLog> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
src/main/resources/genjson/ExceptionLog.json


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
src/main/resources/genjson/OperationLog.json


+ 0 - 46
src/main/vue/src/mixins/pageableTable.js

@@ -48,52 +48,6 @@ export default {
                     this.$message.error(e.error);
                     this.$message.error(e.error);
                 });
                 });
         },
         },
-        pass(data) {
-            this.$confirm('确认通过?', '提示')
-                .then(_ => {
-                    this.$http
-                        .post('/auditHistory/save', data, { body: 'json' })
-                        .then(res => {
-                            this.$message.success('成功');
-                            this.getData();
-                        })
-                        .catch(e => {
-                            console.log(e);
-                            this.saving = false;
-                            this.$message.error(e.error);
-                        });
-                })
-                .catch(_ => {});
-        },
-        deny(data) {
-            /*  { requirement: { id: row.id, state: 2 }, refuseDesc: value, state: 2 } */
-            this.$prompt('请输入拒绝原因', '提示', {
-                confirmButtonText: '确定',
-                cancelButtonText: '取消',
-                inputPattern: /.+/,
-                inputErrorMessage: '请输入拒绝原因'
-            })
-                .then(({ value }) => {
-                    data.refuseDesc = value;
-                    this.$http
-                        .post('/auditHistory/save', data, { body: 'json' })
-                        .then(res => {
-                            this.$message.success('成功');
-                            this.getData();
-                        })
-                        .catch(e => {
-                            console.log(e);
-                            this.saving = false;
-                            this.$message.error(e.error);
-                        });
-                })
-                .catch(() => {
-                    /*      this.$message({
-                    type: 'info',
-                    message: '取消输入'
-                }); */
-                });
-        },
         // getData() {
         // getData() {
         //     this.tableData = [];
         //     this.tableData = [];
         //     let urlQuery = { ...this.$route.query };
         //     let urlQuery = { ...this.$route.query };

+ 33 - 1
src/main/vue/src/router.js

@@ -164,7 +164,7 @@ const router = new Router({
                     name: 'ArticleAudit',
                     name: 'ArticleAudit',
                     component: () => import(/* webpackChunkName: "ArticleAudit" */ '@/views/ArticleAudit.vue'),
                     component: () => import(/* webpackChunkName: "ArticleAudit" */ '@/views/ArticleAudit.vue'),
                     meta: {
                     meta: {
-                        title: '公告审核'
+                        title: '发布管理'
                     }
                     }
                 },
                 },
 
 
@@ -290,6 +290,38 @@ const router = new Router({
                     meta: {
                     meta: {
                         title: '采编管理'
                         title: '采编管理'
                     }
                     }
+                },
+                {
+                    path: '/operationLogEdit',
+                    name: 'OperationLogEdit',
+                    component: () => import(/* webpackChunkName: "operationLogEdit" */ '@/views/OperationLogEdit.vue'),
+                    meta: {
+                        title: '操作日志编辑'
+                    }
+                },
+                {
+                    path: '/operationLogList',
+                    name: 'OperationLogList',
+                    component: () => import(/* webpackChunkName: "operationLogList" */ '@/views/OperationLogList.vue'),
+                    meta: {
+                        title: '操作日志'
+                    }
+                },
+                {
+                    path: '/exceptionLogEdit',
+                    name: 'ExceptionLogEdit',
+                    component: () => import(/* webpackChunkName: "exceptionLogEdit" */ '@/views/ExceptionLogEdit.vue'),
+                    meta: {
+                        title: '异常日志编辑'
+                    }
+                },
+                {
+                    path: '/exceptionLogList',
+                    name: 'ExceptionLogList',
+                    component: () => import(/* webpackChunkName: "exceptionLogList" */ '@/views/ExceptionLogList.vue'),
+                    meta: {
+                        title: '异常日志'
+                    }
                 }
                 }
                 /**INSERT_LOCATION**/
                 /**INSERT_LOCATION**/
             ]
             ]

+ 68 - 18
src/main/vue/src/views/ArticleAudit.vue

@@ -1,11 +1,19 @@
 <template>
 <template>
     <div class="list-view">
     <div class="list-view">
         <div class="filters-container">
         <div class="filters-container">
-            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
-            <el-select v-model="query.type.id" clearable filterable placeholder="公告类型" class="filter-item">
-                <el-option v-for="item in typeIdOptions" :key="item.value" :label="item.label" :value="item.value">
+            <el-input placeholder="标题" v-model="search" clearable class="filter-item"></el-input>
+            <el-select v-model="mainType" clearable filterable placeholder="类型" class="filter-item">
+                <el-option v-for="item in mainTypeOptions" :key="item.name" :label="item.name" :value="item.name">
                 </el-option>
                 </el-option>
             </el-select>
             </el-select>
+            <el-select v-model="status" clearable placeholder="状态" class="filter-item">
+                <el-option
+                    v-for="item in statusOptions"
+                    :label="item.label"
+                    :value="item.value"
+                    :key="item.value"
+                ></el-option>
+            </el-select>
             <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
             <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
             <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
             <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
             <el-button
             <el-button
@@ -26,6 +34,7 @@
             row-class-name="table-row"
             row-class-name="table-row"
             cell-class-name="table-cell"
             cell-class-name="table-cell"
             :height="tableHeight"
             :height="tableHeight"
+            v-loading="fetchingData"
         >
         >
             <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
             <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
             <el-table-column prop="id" label="ID" width="100"> </el-table-column>
             <el-table-column prop="id" label="ID" width="100"> </el-table-column>
@@ -106,7 +115,9 @@ export default {
         }
         }
     },
     },
     created() {
     created() {
-        this.getDataType({ query: { keyType: 'gg' } }, this.typeIdOptions);
+        this.$http.get('/articleType/all', { size: 10000, query: { level: 1 } }).then(res => {
+            this.mainTypeOptions = res.content;
+        });
     },
     },
     data() {
     data() {
         return {
         return {
@@ -115,19 +126,14 @@ export default {
             url: '/article/all',
             url: '/article/all',
             downloading: false,
             downloading: false,
             sortStr: 'createdAt,desc',
             sortStr: 'createdAt,desc',
-            query: {
-                type: {
-                    keyType: 'gg'
-                },
-                state: 0
-            },
             statusOptions: [
             statusOptions: [
                 { label: '待审核', value: 'PENDING' },
                 { label: '待审核', value: 'PENDING' },
                 { label: '审核通过', value: 'PASS' },
                 { label: '审核通过', value: 'PASS' },
                 { label: '审核失败', value: 'DENY' }
                 { label: '审核失败', value: 'DENY' }
             ],
             ],
-
-            typeIdOptions: []
+            status: 'PENDING',
+            mainType: null,
+            mainTypeOptions: []
         };
         };
     },
     },
     computed: {
     computed: {
@@ -144,12 +150,10 @@ export default {
             return '';
             return '';
         },
         },
         beforeGetData() {
         beforeGetData() {
-            if (this.search || this.query) {
-                return {
-                    search: this.search,
-                    query: this.query
-                };
-            }
+            return {
+                search: this.search,
+                query: { mainType: this.mainType, status: this.status }
+            };
         },
         },
         toggleMultipleMode(multipleMode) {
         toggleMultipleMode(multipleMode) {
             this.multipleMode = multipleMode;
             this.multipleMode = multipleMode;
@@ -220,6 +224,52 @@ export default {
                         this.$message.error(e.error);
                         this.$message.error(e.error);
                     }
                     }
                 });
                 });
+        },
+        pass(data) {
+            this.$confirm('确认通过?', '提示')
+                .then(_ => {
+                    this.$http
+                        .post('/auditHistory/save', data, { body: 'json' })
+                        .then(res => {
+                            this.$message.success('成功');
+                            this.getData();
+                        })
+                        .catch(e => {
+                            console.log(e);
+                            this.saving = false;
+                            this.$message.error(e.error);
+                        });
+                })
+                .catch(_ => {});
+        },
+        deny(data) {
+            /*  { requirement: { id: row.id, state: 2 }, refuseDesc: value, state: 2 } */
+            this.$prompt('请输入拒绝原因', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                inputPattern: /.+/,
+                inputErrorMessage: '请输入拒绝原因'
+            })
+                .then(({ value }) => {
+                    data.refuseDesc = value;
+                    this.$http
+                        .post('/auditHistory/save', data, { body: 'json' })
+                        .then(res => {
+                            this.$message.success('成功');
+                            this.getData();
+                        })
+                        .catch(e => {
+                            console.log(e);
+                            this.saving = false;
+                            this.$message.error(e.error);
+                        });
+                })
+                .catch(() => {
+                    /*      this.$message({
+                    type: 'info',
+                    message: '取消输入'
+                }); */
+                });
         }
         }
     }
     }
 };
 };

+ 137 - 0
src/main/vue/src/views/ExceptionLogEdit.vue

@@ -0,0 +1,137 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="96px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="id" label="id">
+                <el-input-number type="number" v-model="formData.id"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="module" label="module">
+                <el-input v-model="formData.module"></el-input>
+            </el-form-item>
+            <el-form-item prop="type" label="type">
+                <el-input v-model="formData.type"></el-input>
+            </el-form-item>
+            <el-form-item prop="desc" label="desc">
+                <el-input v-model="formData.desc"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqMethod" label="reqMethod">
+                <el-input v-model="formData.reqMethod"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqUrl" label="reqUrl">
+                <el-input v-model="formData.reqUrl"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqParams" label="reqParams">
+                <el-input v-model="formData.reqParams"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqIp" label="reqIp">
+                <el-input v-model="formData.reqIp"></el-input>
+            </el-form-item>
+            <el-form-item prop="resp" label="resp">
+                <el-input v-model="formData.resp"></el-input>
+            </el-form-item>
+            <el-form-item prop="time" label="time">
+                <el-date-picker
+                    v-model="formData.time"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="userId" label="userId">
+                <el-input v-model="formData.userId"></el-input>
+            </el-form-item>
+            <el-form-item prop="username" label="username">
+                <el-input v-model="formData.username"></el-input>
+            </el-form-item>
+            <el-form-item prop="message" label="message">
+                <el-input v-model="formData.message"></el-input>
+            </el-form-item>
+            <el-form-item prop="stackTrace" label="stackTrace">
+                <el-input v-model="formData.stackTrace"></el-input>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'ExceptionLogEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('exceptionLog/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {}
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/exceptionLog/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/exceptionLog/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 188 - 0
src/main/vue/src/views/ExceptionLogList.vue

@@ -0,0 +1,188 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="异常名称"> </el-table-column>
+            <el-table-column prop="type" label="操作类型"> </el-table-column>
+            <el-table-column prop="userId" label="用户ID"> </el-table-column>
+            <el-table-column prop="username" label="用户名"> </el-table-column>
+            <el-table-column prop="desc" label="描述"> </el-table-column>
+            <el-table-column prop="reqMethod" label="调用方法"> </el-table-column>
+            <el-table-column prop="reqUrl" label="请求地址"> </el-table-column>
+            <el-table-column prop="reqParams" label="请求参数"> </el-table-column>
+            <el-table-column prop="reqIp" label="请求IP"> </el-table-column>
+            <el-table-column prop="resp" label="返回结果"> </el-table-column>
+            <el-table-column prop="time" label="操作时间"> </el-table-column>
+            <el-table-column prop="message" label="异常消息"> </el-table-column>
+            <el-table-column prop="stackTrace" label="错误追踪"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+        <el-dialog :visible.sync="showResp" title="返回结果">
+            <pre
+                >{{ resp }}
+           </pre
+            >
+        </el-dialog>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'ExceptionLogList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/exceptionLog/all',
+            downloading: false,
+            sortStr: '',
+            resp: '',
+            showResp: false
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            if (this.search) {
+                return { search: this.search };
+            }
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/exceptionLogEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/exceptionLogEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/exceptionLog/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/exceptionLog/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        },
+        viewResp(row) {
+            try {
+                this.resp = JSON.stringify(JSON.parse(row.resp), null, 4);
+            } catch (e) {
+                console.log(e);
+                this.resp = row.resp;
+            }
+            this.showResp = true;
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 12 - 4
src/main/vue/src/views/GenCodeList.vue

@@ -18,7 +18,16 @@
                 </el-dropdown-menu>
                 </el-dropdown-menu>
             </el-dropdown> -->
             </el-dropdown> -->
         </div>
         </div>
-        <el-table :data="tableData" row-key="id" ref="table">
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            height="tableHeight"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+        >
             <el-table-column type="index" min-width="50" align="center"> </el-table-column>
             <el-table-column type="index" min-width="50" align="center"> </el-table-column>
             <el-table-column v-if="isColumnShow('tableName')" prop="tableName" label="表名" min-width="100">
             <el-table-column v-if="isColumnShow('tableName')" prop="tableName" label="表名" min-width="100">
             </el-table-column>
             </el-table-column>
@@ -39,10 +48,9 @@
 </template>
 </template>
 <script>
 <script>
 import { mapState } from 'vuex';
 import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
 export default {
 export default {
-    created() {
-        this.getData();
-    },
+    mixins: [pageableTable],
     data() {
     data() {
         return {
         return {
             search: '',
             search: '',

+ 131 - 0
src/main/vue/src/views/OperationLogEdit.vue

@@ -0,0 +1,131 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="96px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="id" label="id">
+                <el-input-number type="number" v-model="formData.id"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="module" label="module">
+                <el-input v-model="formData.module"></el-input>
+            </el-form-item>
+            <el-form-item prop="type" label="type">
+                <el-input v-model="formData.type"></el-input>
+            </el-form-item>
+            <el-form-item prop="desc" label="desc">
+                <el-input v-model="formData.desc"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqMethod" label="reqMethod">
+                <el-input v-model="formData.reqMethod"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqUrl" label="reqUrl">
+                <el-input v-model="formData.reqUrl"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqParams" label="reqParams">
+                <el-input v-model="formData.reqParams"></el-input>
+            </el-form-item>
+            <el-form-item prop="reqIp" label="reqIp">
+                <el-input v-model="formData.reqIp"></el-input>
+            </el-form-item>
+            <el-form-item prop="resp" label="resp">
+                <el-input v-model="formData.resp"></el-input>
+            </el-form-item>
+            <el-form-item prop="time" label="time">
+                <el-date-picker
+                    v-model="formData.time"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="userId" label="userId">
+                <el-input v-model="formData.userId"></el-input>
+            </el-form-item>
+            <el-form-item prop="username" label="username">
+                <el-input v-model="formData.username"></el-input>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'OperationLogEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('operationLog/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {}
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/operationLog/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/operationLog/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 131 - 0
src/main/vue/src/views/OperationLogList.vue

@@ -0,0 +1,131 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="操作名称" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="type" label="操作类型" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="userId" label="用户ID" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="username" label="用户名" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="desc" label="描述" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="reqMethod" label="调用方法" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="reqUrl" label="请求地址" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="reqParams" label="请求参数" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="reqIp" label="请求IP" show-overflow-tooltip> </el-table-column>
+            <el-table-column prop="resp" label="返回结果" align="center" width="80">
+                <template v-slot="{ row }">
+                    <el-button @click="viewResp(row)" type="text">查看</el-button>
+                </template>
+            </el-table-column>
+            <el-table-column prop="time" label="操作时间" width="150"> </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+        <el-dialog :visible.sync="showResp" title="返回结果">
+            <pre
+                >{{ resp }}
+           </pre
+            >
+        </el-dialog>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'OperationLogList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            search: '',
+            url: '/operationLog/all',
+            downloading: false,
+            sortStr: 'time,desc',
+            resp: '',
+            showResp: false
+        };
+    },
+    computed: {},
+    methods: {
+        beforeGetData() {
+            if (this.search) {
+                return { search: this.search };
+            }
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/operationLog/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        viewResp(row) {
+            try {
+                this.resp = JSON.stringify(JSON.parse(row.resp), null, 4);
+            } catch (e) {
+                console.log(e);
+                this.resp = row.resp;
+            }
+            this.showResp = true;
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä