瀏覽代碼

完善新操作日志的页面修改

YunaiV 4 年之前
父節點
當前提交
1768d27e11
共有 35 個文件被更改,包括 649 次插入664 次删除
  1. 1 1
      pom.xml
  2. 2 18
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
  3. 0 35
      ruoyi-common/pom.xml
  4. 0 41
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java
  5. 0 160
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
  6. 0 29
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
  7. 0 35
      ruoyi-ui/src/api/monitor/operlog.js
  8. 19 0
      ruoyi-ui/src/api/system/operatelog.js
  9. 4 0
      ruoyi-ui/src/main.js
  10. 1 0
      ruoyi-ui/src/utils/dict.js
  11. 275 318
      ruoyi-ui/src/views/system/operatelog/index.vue
  12. 29 0
      sql/ruoyi-vue-pro-data.sql
  13. 4 0
      src/main/java/cn/iocoder/dashboard/common/pojo/PageResult.java
  14. 1 1
      src/main/java/cn/iocoder/dashboard/framework/async/config/AsyncConfiguration.java
  15. 2 2
      src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java
  16. 8 8
      src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java
  17. 2 2
      src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/enums/OperateLogTypeEnum.java
  18. 24 0
      src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java
  19. 0 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java
  20. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.http
  21. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java
  22. 3 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysOperateLogController.http
  23. 86 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysOperateLogController.java
  24. 0 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/package-info.java
  25. 8 1
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogBaseVO.java
  26. 39 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogPageReqVO.java
  27. 21 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogRespVO.java
  28. 6 0
      src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysOperateLogConvert.java
  29. 23 2
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysOperateLogMapper.java
  30. 4 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/user/SysUserMapper.java
  31. 2 6
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/logger/SysOperateLogDO.java
  32. 12 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysOperateLogService.java
  33. 27 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysOperateLogServiceImpl.java
  34. 34 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
  35. 10 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java

+ 1 - 1
pom.xml

@@ -22,7 +22,7 @@
         <maven.compiler.target>${java.version}</maven.compiler.target>
         <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
         <!-- 统一依赖管理 -->
-        <spring.boot.version>2.4.1</spring.boot.version>
+        <spring.boot.version>2.4.2</spring.boot.version>
 
 <!--        <ruoyi.version>3.3.0</ruoyi.version>-->
 <!--        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->

+ 2 - 18
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java

@@ -23,28 +23,12 @@ import com.ruoyi.system.service.ISysOperLogService;
  *
  * @author ruoyi
  */
-@RestController
-@RequestMapping("/monitor/operlog")
+
 public class SysOperlogController extends BaseController {
 
     @Autowired
     private ISysOperLogService operLogService;
 
-    @PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(SysOperLog operLog) {
-        startPage();
-        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
-        return getDataTable(list);
-    }
-
-    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
-    @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
-    @GetMapping("/export")
-    public AjaxResult export(SysOperLog operLog) {
-        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
-        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
-        return util.exportExcel(list, "操作日志");
-    }
+
 
 }

+ 0 - 35
ruoyi-common/pom.xml

@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>ruoyi</artifactId>
-        <groupId>com.ruoyi</groupId>
-        <version>3.3.0</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>ruoyi-common</artifactId>
-
-    <description>
-        common通用工具
-    </description>
-
-    <dependencies>
-
-        <!-- yml解析器 -->
-        <dependency>
-            <groupId>org.yaml</groupId>
-            <artifactId>snakeyaml</artifactId>
-        </dependency>
-
-        <!-- 解析客户端操作系统、浏览器等 -->
-        <dependency>
-            <groupId>eu.bitwalker</groupId>
-            <artifactId>UserAgentUtils</artifactId>
-        </dependency>
-
-
-    </dependencies>
-
-</project>

+ 0 - 41
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java

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

+ 0 - 160
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java

@@ -1,160 +0,0 @@
-package com.ruoyi.framework.aspectj;
-
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.Signature;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.HandlerMapping;
-import com.alibaba.fastjson.JSON;
-import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.enums.BusinessStatus;
-import com.ruoyi.common.enums.HttpMethod;
-import com.ruoyi.common.utils.ServletUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.ip.IpUtils;
-import com.ruoyi.common.utils.spring.SpringUtils;
-import com.ruoyi.framework.manager.AsyncManager;
-import com.ruoyi.framework.manager.factory.AsyncFactory;
-import com.ruoyi.framework.web.service.TokenService;
-import com.ruoyi.system.domain.SysOperLog;
-
-/**
- * 操作日志记录处理
- *
- * @author ruoyi
- */
-@Aspect
-@Component
-public class LogAspect {
-    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
-
-    // 配置织入点
-    @Pointcut("@annotation(com.ruoyi.common.annotation.Log)")
-    public void logPointCut() {
-    }
-
-    /**
-     * 处理完请求后执行
-     *
-     * @param joinPoint 切点
-     */
-    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
-    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
-        handleLog(joinPoint, null, jsonResult);
-    }
-
-    /**
-     * 拦截异常操作
-     *
-     * @param joinPoint 切点
-     * @param e         异常
-     */
-    @AfterThrowing(value = "logPointCut()", throwing = "e")
-    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
-        handleLog(joinPoint, e, null);
-    }
-
-    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
-        try {
-            // 获得注解
-            Log controllerLog = getAnnotationLog(joinPoint);
-            if (controllerLog == null) {
-                return;
-            }
-
-            // 获取当前的用户
-            LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
-
-            // *========数据库日志=========*//
-            SysOperLog operLog = new SysOperLog();
-            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
-            // 请求的地址
-            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
-            operLog.setOperIp(ip);
-            // 返回参数
-            operLog.setJsonResult(JSON.toJSONString(jsonResult));
-
-            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
-            if (loginUser != null) {
-                operLog.setOperName(loginUser.getUsername());
-            }
-
-            if (e != null) {
-                operLog.setStatus(BusinessStatus.FAIL.ordinal());
-                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
-            }
-            // 设置方法名称
-            String className = joinPoint.getTarget().getClass().getName();
-            String methodName = joinPoint.getSignature().getName();
-            operLog.setMethod(className + "." + methodName + "()");
-            // 设置请求方式
-            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
-            // 处理设置注解上的参数
-            getControllerMethodDescription(joinPoint, controllerLog, operLog);
-            // 保存数据库
-            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
-        } catch (Exception exp) {
-            // 记录本地异常日志
-            log.error("==前置通知异常==");
-            log.error("异常信息:{}", exp.getMessage());
-            exp.printStackTrace();
-        }
-    }
-
-    /**
-     * 参数拼装
-     */
-    private String argsArrayToString(Object[] paramsArray) {
-        String params = "";
-        if (paramsArray != null && paramsArray.length > 0) {
-            for (int i = 0; i < paramsArray.length; i++) {
-                if (!isFilterObject(paramsArray[i])) {
-                    Object jsonObj = JSON.toJSON(paramsArray[i]);
-                    params += jsonObj.toString() + " ";
-                }
-            }
-        }
-        return params.trim();
-    }
-
-    /**
-     * 判断是否需要过滤的对象。
-     *
-     * @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 (Iterator iter = collection.iterator(); iter.hasNext(); ) {
-                return iter.next() instanceof MultipartFile;
-            }
-        } else if (Map.class.isAssignableFrom(clazz)) {
-            Map map = (Map) o;
-            for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
-                Map.Entry entry = (Map.Entry) iter.next();
-                return entry.getValue() instanceof MultipartFile;
-            }
-        }
-        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
-    }
-}

+ 0 - 29
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java

@@ -1,29 +0,0 @@
-package com.ruoyi.framework.config;
-
-import java.util.TimeZone;
-
-import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.EnableAspectJAutoProxy;
-
-/**
- * 程序注解配置
- *
- * @author ruoyi
- */
-@Configuration
-// 表示通过aop框架暴露该代理对象,AopContext能够访问
-@EnableAspectJAutoProxy(exposeProxy = true)
-// 指定要扫描的Mapper类的包的路径
-@MapperScan("com.ruoyi.**.mapper")
-public class ApplicationConfig {
-    /**
-     * 时区配置
-     */
-    @Bean
-    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
-        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
-    }
-}

+ 0 - 35
ruoyi-ui/src/api/monitor/operlog.js

@@ -1,35 +0,0 @@
-import request from '@/utils/request'
-
-// 查询操作日志列表
-export function list(query) {
-  return request({
-    url: '/monitor/operlog/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 删除操作日志
-export function delOperlog(operId) {
-  return request({
-    url: '/monitor/operlog/' + operId,
-    method: 'delete'
-  })
-}
-
-// 清空操作日志
-export function cleanOperlog() {
-  return request({
-    url: '/monitor/operlog/clean',
-    method: 'delete'
-  })
-}
-
-// 导出操作日志
-export function exportOperlog(query) {
-  return request({
-    url: '/monitor/operlog/export',
-    method: 'get',
-    params: query
-  })
-}

+ 19 - 0
ruoyi-ui/src/api/system/operatelog.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+// 查询操作日志列表
+export function listOperateLog(query) {
+  return request({
+    url: '/system/operate-log/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出操作日志
+export function exportOperateLog(query) {
+  return request({
+    url: '/system/operate-log/export',
+    method: 'get',
+    params: query
+  })
+}

+ 4 - 0
ruoyi-ui/src/main.js

@@ -23,6 +23,7 @@ import RightToolbar from "@/components/RightToolbar"
 // 代码高亮插件
 import hljs from 'highlight.js'
 import 'highlight.js/styles/github-gist.css'
+import {DICT_TYPE, getDictDataLabel, getDictDatas} from "@/utils/dict";
 
 // 全局方法挂载
 Vue.prototype.getDicts = getDicts
@@ -32,6 +33,9 @@ Vue.prototype.resetForm = resetForm
 Vue.prototype.addDateRange = addDateRange
 Vue.prototype.selectDictLabel = selectDictLabel
 Vue.prototype.selectDictLabels = selectDictLabels
+Vue.prototype.getDictDatas = getDictDatas
+Vue.prototype.getDictDataLabel = getDictDataLabel
+Vue.prototype.DICT_TYPE = DICT_TYPE
 Vue.prototype.download = download
 Vue.prototype.downloadExcel = downloadExcel
 Vue.prototype.handleTree = handleTree

+ 1 - 0
ruoyi-ui/src/utils/dict.js

@@ -12,6 +12,7 @@ export const DICT_TYPE = {
   SYS_DATA_SCOPE: 'sys_data_scope',
   SYS_USER_SEX: 'sys_user_sex',
   SYS_NOTICE_TYPE: 'sys_notice_type',
+  SYS_OPERATE_TYPE: 'sys_operate_type'
 }
 
 /**

+ 275 - 318
ruoyi-ui/src/views/monitor/operlog/index.vue → ruoyi-ui/src/views/system/operatelog/index.vue

@@ -1,318 +1,275 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="系统模块" prop="title">
-        <el-input
-          v-model="queryParams.title"
-          placeholder="请输入系统模块"
-          clearable
-          style="width: 240px;"
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="操作人员" prop="operName">
-        <el-input
-          v-model="queryParams.operName"
-          placeholder="请输入操作人员"
-          clearable
-          style="width: 240px;"
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="类型" prop="businessType">
-        <el-select
-          v-model="queryParams.businessType"
-          placeholder="操作类型"
-          clearable
-          size="small"
-          style="width: 240px"
-        >
-          <el-option
-            v-for="dict in typeOptions"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="操作状态"
-          clearable
-          size="small"
-          style="width: 240px"
-        >
-          <el-option
-            v-for="dict in statusOptions"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="操作时间">
-        <el-date-picker
-          v-model="dateRange"
-          size="small"
-          style="width: 240px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['monitor:operlog:remove']"
-        >删除</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          icon="el-icon-delete"
-          size="mini"
-          @click="handleClean"
-          v-hasPermi="['monitor:operlog:remove']"
-        >清空</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          icon="el-icon-download"
-          size="mini"
-          @click="handleExport"
-          v-hasPermi="['system:config:export']"
-        >导出</el-button>
-      </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="日志编号" align="center" prop="operId" />
-      <el-table-column label="系统模块" align="center" prop="title" />
-      <el-table-column label="操作类型" align="center" prop="businessType" :formatter="typeFormat" />
-      <el-table-column label="请求方式" align="center" prop="requestMethod" />
-      <el-table-column label="操作人员" align="center" prop="operName" />
-      <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
-      <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
-      <el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" />
-      <el-table-column label="操作日期" align="center" prop="operTime" width="180">
-        <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.operTime) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-view"
-            @click="handleView(scope.row,scope.index)"
-            v-hasPermi="['monitor:operlog:query']"
-          >详细</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total>0"
-      :total="total"
-      :page.sync="queryParams.pageNum"
-      :limit.sync="queryParams.pageSize"
-      @pagination="getList"
-    />
-
-    <!-- 操作日志详细 -->
-    <el-dialog title="操作日志详细" :visible.sync="open" width="700px" append-to-body>
-      <el-form ref="form" :model="form" label-width="100px" size="mini">
-        <el-row>
-          <el-col :span="12">
-            <el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
-            <el-form-item
-              label="登录信息:"
-            >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item>
-            <el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="操作状态:">
-              <div v-if="form.status === 0">正常</div>
-              <div v-else-if="form.status === 1">失败</div>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="异常信息:" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="open = false">关 闭</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import { list, delOperlog, cleanOperlog, exportOperlog } from "@/api/monitor/operlog";
-
-export default {
-  name: "Operlog",
-  data() {
-    return {
-      // 遮罩层
-      loading: true,
-      // 选中数组
-      ids: [],
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 表格数据
-      list: [],
-      // 是否显示弹出层
-      open: false,
-      // 类型数据字典
-      typeOptions: [],
-      // 类型数据字典
-      statusOptions: [],
-      // 日期范围
-      dateRange: [],
-      // 表单参数
-      form: {},
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        title: undefined,
-        operName: undefined,
-        businessType: undefined,
-        status: undefined
-      }
-    };
-  },
-  created() {
-    this.getList();
-    this.getDicts("sys_oper_type").then(response => {
-      this.typeOptions = response.data;
-    });
-    this.getDicts("sys_common_status").then(response => {
-      this.statusOptions = response.data;
-    });
-  },
-  methods: {
-    /** 查询登录日志 */
-    getList() {
-      this.loading = true;
-      list(this.addDateRange(this.queryParams, this.dateRange)).then( response => {
-          this.list = response.rows;
-          this.total = response.total;
-          this.loading = false;
-        }
-      );
-    },
-    // 操作日志状态字典翻译
-    statusFormat(row, column) {
-      return this.selectDictLabel(this.statusOptions, row.status);
-    },
-    // 操作日志类型字典翻译
-    typeFormat(row, column) {
-      return this.selectDictLabel(this.typeOptions, row.businessType);
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.dateRange = [];
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.operId)
-      this.multiple = !selection.length
-    },
-    /** 详细按钮操作 */
-    handleView(row) {
-      this.open = true;
-      this.form = row;
-    },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const operIds = row.operId || this.ids;
-      this.$confirm('是否确认删除日志编号为"' + operIds + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delOperlog(operIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        })
-    },
-    /** 清空按钮操作 */
-    handleClean() {
-        this.$confirm('是否确认清空所有操作日志数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return cleanOperlog();
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("清空成功");
-        })
-    },
-    /** 导出按钮操作 */
-    handleExport() {
-      const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return exportOperlog(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-        })
-    }
-  }
-};
-</script>
-
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="系统模块" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入系统模块"
+          clearable
+          style="width: 240px;"
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="操作人员" prop="operName">
+        <el-input
+          v-model="queryParams.operName"
+          placeholder="请输入操作人员"
+          clearable
+          style="width: 240px;"
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="类型" prop="type">
+        <el-select
+          v-model="queryParams.type"
+          placeholder="操作类型"
+          clearable
+          size="small"
+          style="width: 240px"
+        >
+          <el-option
+            v-for="dict in this.getDictDatas(DICT_TYPE.SYS_OPERATE_TYPE)"
+            :key="parseInt(dict.value)"
+            :label="dict.label"
+            :value="parseInt(dict.value)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.success"
+          placeholder="操作状态"
+          clearable
+          size="small"
+          style="width: 240px"
+        >
+          <el-option
+              :key="true"
+              label="成功"
+              :value="true"
+          />
+          <el-option
+              :key="false"
+              label="失败"
+              :value="false"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="操作时间">
+        <el-date-picker
+          v-model="dateRange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:config:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="日志编号" align="center" prop="id" />
+      <el-table-column label="操作模块" align="center" prop="module" />
+      <el-table-column label="操作名" align="center" prop="name" width="180" />
+      <el-table-column label="操作类型" align="center" prop="type">
+        <template slot-scope="scope">
+          <span>{{ getDictDataLabel(DICT_TYPE.SYS_OPERATE_TYPE, scope.row.type) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作人" align="center" prop="userNickname" />
+      <el-table-column label="操作结果" align="center" prop="status">
+        <template slot-scope="scope">
+          <span>{{ scope.row.resultCode === 0 ? '成功' : '失败' }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作日期" align="center" prop="startTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.startTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="执行时长" align="center" prop="startTime">
+        <template slot-scope="scope">
+          <span>{{ scope.row.duration }}  ms</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row,scope.index)"
+            v-hasPermi="['system:operate-log:query']"
+          >详细</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNo"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 操作日志详细 -->
+    <el-dialog title="操作日志详细" :visible.sync="open" width="700px" append-to-body>
+      <el-form ref="form" :model="form" label-width="100px" size="mini">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
+            <el-form-item
+              label="登录信息:"
+            >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item>
+            <el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="操作状态:">
+              <div v-if="form.status === 0">正常</div>
+              <div v-else-if="form.status === 1">失败</div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="操作时间:">{{ parseTime(form.operTime) }}</el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="异常信息:" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="open = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listOperateLog, exportOperateLog } from "@/api/system/operatelog";
+
+export default {
+  name: "Operlog",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 表格数据
+      list: [],
+      // 是否显示弹出层
+      open: false,
+      // 类型数据字典
+      typeOptions: [],
+      // 类型数据字典
+      statusOptions: [],
+      // 日期范围
+      dateRange: [],
+      // 表单参数
+      form: {},
+      // 查询参数
+      queryParams: {
+        pageNo: 1,
+        pageSize: 10,
+        title: undefined,
+        operName: undefined,
+        businessType: undefined,
+        status: undefined
+      },
+
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询登录日志 */
+    getList() {
+      this.loading = true;
+      listOperateLog(this.addDateRange(this.queryParams, [
+        this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined,
+        this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined,
+      ])).then( response => {
+          this.list = response.data.list;
+          this.total = response.data.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 操作日志状态字典翻译
+    statusFormat(row, column) {
+      return this.selectDictLabel(this.statusOptions, row.status);
+    },
+    // 操作日志类型字典翻译
+    typeFormat(row, column) {
+      return this.selectDictLabel(this.typeOptions, row.businessType);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNo = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 详细按钮操作 */
+    handleView(row) {
+      this.open = true;
+      this.form = row;
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportOperateLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    }
+  }
+};
+</script>
+

+ 29 - 0
sql/ruoyi-vue-pro-data.sql

@@ -0,0 +1,29 @@
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (1, 1, '男', '1', 'sys_user_sex', 0, '性别男', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 05:48:53', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (2, 2, '女', '2', 'sys_user_sex', 0, '性别女', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 05:48:55', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (8, 1, '正常', '0', 'sys_job_status', 0, '正常状态', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (9, 2, '暂停', '1', 'sys_job_status', 0, '停用状态', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (10, 1, '默认', 'DEFAULT', 'sys_job_group', 0, '默认分组', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (11, 2, '系统', 'SYSTEM', 'sys_job_group', 0, '系统分组', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (12, 1, '是', 'Y', 'sys_yes_no', 0, '系统默认是', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (13, 2, '否', 'N', 'sys_yes_no', 0, '系统默认否', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (14, 1, '通知', '1', 'sys_notice_type', 0, '通知', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (15, 2, '公告', '2', 'sys_notice_type', 0, '公告', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 00:02:28', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (16, 0, '其它', '0', 'sys_operate_type', 0, '其它操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:51:12', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (17, 1, '查询', '1', 'sys_operate_type', 0, '查询操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:51:10', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (18, 2, '新增', '2', 'sys_operate_type', 0, '新增操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:51:17', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (19, 3, '修改', '3', 'sys_operate_type', 0, '修改操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:51:20', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (20, 4, '删除', '4', 'sys_operate_type', 0, '删除操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:51:24', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (22, 5, '导出', '5', 'sys_operate_type', 0, '导出操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:49:20', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (23, 6, '导入', '6', 'sys_operate_type', 0, '导入操作', 'admin', '2021-01-05 17:03:48', '', '2021-01-16 13:49:24', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (27, 1, '开启', '0', 'sys_common_status', 0, '开启状态', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 02:57:12', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (28, 2, '关闭', '1', 'sys_common_status', 0, '关闭状态', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 05:48:32', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (29, 1, '目录', '1', 'sys_menu_type', 0, '目录', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 13:33:30', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (30, 2, '菜单', '2', 'sys_menu_type', 0, '菜单', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 13:33:35', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (31, 3, '按钮', '3', 'sys_menu_type', 0, '按钮', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 13:33:38', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (32, 1, '内置', '1', 'sys_role_type', 0, '内置角色', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 13:34:22', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (33, 2, '自定义', '2', 'sys_role_type', 0, '自定义角色', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 13:34:26', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (34, 1, '全部数据权限', '1', 'sys_data_scope', 0, '全部数据权限', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 19:38:02', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (35, 2, '指定部门数据权限', '2', 'sys_data_scope', 0, '指定部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 19:38:20', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (36, 3, '本部门数据权限', '3', 'sys_data_scope', 0, '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 19:38:29', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (37, 4, '本部门及以下数据权限', '4', 'sys_data_scope', 0, '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 19:38:32', b'0');
+INSERT INTO `ruoyi-vue-pro`.`sys_dict_data`(`id`, `sort`, `label`, `value`, `dict_type`, `status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`) VALUES (38, 5, '仅本人数据权限', '5', 'sys_data_scope', 0, '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2021-01-06 19:38:38', b'0');

+ 4 - 0
src/main/java/cn/iocoder/dashboard/common/pojo/PageResult.java

@@ -31,4 +31,8 @@ public final class PageResult<T> implements Serializable {
         this.total = total;
     }
 
+    public static <T> PageResult<T> empty() {
+        return new PageResult<>(0L);
+    }
+
 }

+ 1 - 1
src/main/java/cn/iocoder/dashboard/framework/async/config/AsyncConfiguration.java

@@ -4,6 +4,6 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.EnableAsync;
 
 @Configuration
-@EnableAsync(proxyTargetClass = true)
+@EnableAsync
 public class AsyncConfiguration {
 }

+ 2 - 2
src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/annotations/OperateLog.java

@@ -1,6 +1,6 @@
 package cn.iocoder.dashboard.framework.logger.operatelog.core.annotations;
 
-import cn.iocoder.dashboard.modules.system.enums.logger.SysOperateLogTypeEnum;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateLogTypeEnum;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 
@@ -32,7 +32,7 @@ public @interface OperateLog {
      *
      * 实际并不是数组,因为枚举不能设置 null 作为默认值
      */
-    SysOperateLogTypeEnum[] type() default {};
+    OperateLogTypeEnum[] type() default {};
 
     // ========== 开关字段 ==========
 

+ 8 - 8
src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/aop/OperateLogAspect.java

@@ -6,11 +6,11 @@ import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.servlet.ServletUtil;
 import cn.iocoder.dashboard.common.pojo.CommonResult;
 import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateLogTypeEnum;
 import cn.iocoder.dashboard.framework.logger.operatelog.core.service.OperateLogFrameworkService;
 import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils;
 import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogCreateReqVO;
-import cn.iocoder.dashboard.modules.system.enums.logger.SysOperateLogTypeEnum;
 import cn.iocoder.dashboard.util.servlet.ServletUtils;
 import com.alibaba.fastjson.JSON;
 import com.google.common.collect.Maps;
@@ -180,7 +180,7 @@ public class OperateLogAspect {
         }
         if (operateLogVO.getType() == null) {
             RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
-            SysOperateLogTypeEnum operateLogType = convertOperateLogType(requestMethod);
+            OperateLogTypeEnum operateLogType = convertOperateLogType(requestMethod);
             operateLogVO.setType(operateLogType != null ? operateLogType.getType() : null);
         }
         // content 和 exts 属性
@@ -275,21 +275,21 @@ public class OperateLogAspect {
         return requestMethods[0];
     }
 
-    private static SysOperateLogTypeEnum convertOperateLogType(RequestMethod requestMethod) {
+    private static OperateLogTypeEnum convertOperateLogType(RequestMethod requestMethod) {
         if (requestMethod == null) {
             return null;
         }
         switch (requestMethod) {
             case GET:
-                return SysOperateLogTypeEnum.GET;
+                return OperateLogTypeEnum.GET;
             case POST:
-                return SysOperateLogTypeEnum.CREATE;
+                return OperateLogTypeEnum.CREATE;
             case PUT:
-                return SysOperateLogTypeEnum.UPDATE;
+                return OperateLogTypeEnum.UPDATE;
             case DELETE:
-                return SysOperateLogTypeEnum.DELETE;
+                return OperateLogTypeEnum.DELETE;
             default:
-                return SysOperateLogTypeEnum.OTHER;
+                return OperateLogTypeEnum.OTHER;
         }
     }
 

+ 2 - 2
src/main/java/cn/iocoder/dashboard/modules/system/enums/logger/SysOperateLogTypeEnum.java → src/main/java/cn/iocoder/dashboard/framework/logger/operatelog/core/enums/OperateLogTypeEnum.java

@@ -1,4 +1,4 @@
-package cn.iocoder.dashboard.modules.system.enums.logger;
+package cn.iocoder.dashboard.framework.logger.operatelog.core.enums;
 
 import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
 import lombok.AllArgsConstructor;
@@ -11,7 +11,7 @@ import lombok.Getter;
  */
 @Getter
 @AllArgsConstructor
-public enum SysOperateLogTypeEnum {
+public enum OperateLogTypeEnum {
 
     /**
      * 查询

+ 24 - 0
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java

@@ -0,0 +1,24 @@
+package cn.iocoder.dashboard.framework.mybatis.core.mapper;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
+ */
+public interface BaseMapperX<T> extends BaseMapper<T> {
+
+    default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
+        // MyBatis Plus 查询
+        IPage<T> mpPage = MyBatisUtils.buildPage(pageParam);
+        selectPage(mpPage, queryWrapper);
+        // 转换返回
+        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
+    }
+
+}

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java

@@ -54,7 +54,6 @@ public class SysAuthController {
 
     @ApiOperation("获取登陆用户的权限信息")
     @GetMapping("/get-permission-info")
-    @OperateLog
     public CommonResult<SysAuthPermissionInfoRespVO> getPermissionInfo() {
         // 获得用户信息
         SysUserDO user = userService.getUser(getLoginUserId());

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.http

@@ -1,2 +1,2 @@
 ### 请求 /captcha/get-image 接口 => 成功
-GET {{baseUrl}}/captcha/get-image
+GET {{baseUrl}}/system/captcha/get-image

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java

@@ -23,7 +23,7 @@ public class SysCaptchaController {
 
     @ApiOperation("生成图片验证码")
     @GetMapping("/get-image")
-    private CommonResult<SysCaptchaImageRespVO> getCaptchaImage() {
+    public CommonResult<SysCaptchaImageRespVO> getCaptchaImage() {
         return success(captchaService.getCaptchaImage());
     }
 

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysOperateLogController.http

@@ -0,0 +1,3 @@
+### 请求 /system/operate-log/demo 接口 => 成功
+GET {{baseUrl}}/system/operate-log/demo
+Authorization: Bearer {{token}}

+ 86 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysOperateLogController.java

@@ -0,0 +1,86 @@
+package cn.iocoder.dashboard.modules.system.controller.logger;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateLogTypeEnum;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.util.OperateLogUtils;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogPageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogRespVO;
+import cn.iocoder.dashboard.modules.system.convert.logger.SysOperateLogConvert;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.service.logger.SysOperateLogService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
+import cn.iocoder.dashboard.util.collection.MapUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+
+@Api(tags = "操作日志 API")
+@RestController
+@RequestMapping("/system/operate-log")
+public class SysOperateLogController {
+
+    @Resource
+    private SysOperateLogService operateLogService;
+
+    @Resource
+    private SysUserService userService;
+
+    @ApiOperation("示例")
+    @OperateLog(type = OperateLogTypeEnum.OTHER)
+    @GetMapping("/demo")
+    public CommonResult<Boolean> demo() {
+        // 这里可以调用业务逻辑
+
+        // 补全操作日志的明细
+        OperateLogUtils.setContent("将编号 1 的数据,xxx 字段修改成了 yyyy");
+        OperateLogUtils.addExt("orderId", 1);
+
+        // 响应
+        return success(true);
+    }
+
+    @ApiOperation("查看操作日志分页列表")
+    @GetMapping("/page")
+//    @PreAuthorize("@ss.hasPermi('system:operate-log:query')")
+    public CommonResult<PageResult<SysOperateLogRespVO>> pageOperateLog(@Validated SysOperateLogPageReqVO reqVO) {
+        PageResult<SysOperateLogDO> pageResult = operateLogService.pageOperateLog(reqVO);
+
+        // 获得拼接需要的数据
+        Collection<Long> userIds = CollectionUtils.convertList(pageResult.getList(), SysOperateLogDO::getUserId);
+        Map<Long, SysUserDO> userMap = userService.getUserMap(userIds);
+        // 拼接数据
+        List<SysOperateLogRespVO> list = new ArrayList<>(pageResult.getList().size());
+        pageResult.getList().forEach(operateLog -> {
+            SysOperateLogRespVO respVO = SysOperateLogConvert.INSTANCE.convert(operateLog);
+            list.add(respVO);
+            // 拼接用户信息
+            MapUtils.findAndThen(userMap, operateLog.getUserId(), user -> respVO.setUserNickname(user.getNickname()));
+        });
+        return success(new PageResult<>(list, pageResult.getTotal()));
+    }
+
+//    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+//    @PreAuthorize("@ss.hasPermi('system:operate-log:export')")
+//    @GetMapping("/export")
+//    public AjaxResult export(SysOperLog operLog) {
+//        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
+//        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
+//        return util.exportExcel(list, "操作日志");
+//    }
+
+}

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.dashboard.modules.system.controller.logger;

+ 8 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogBaseVO.java

@@ -6,6 +6,7 @@ import lombok.Data;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.Map;
 
 /**
  * 操作日志 Base VO,提供给添加、修改、详细的子 VO 使用
@@ -30,10 +31,16 @@ public class SysOperateLogBaseVO {
     @NotEmpty(message = "操作名")
     private String name;
 
-    @ApiModelProperty(value = "操作分类", required = true, example = "操作分类", notes = "参见 SysOperateLogTypeEnum 枚举类")
+    @ApiModelProperty(value = "操作分类", required = true, example = "1", notes = "参见 SysOperateLogTypeEnum 枚举类")
     @NotNull(message = "操作分类不能为空")
     private Integer type;
 
+    @ApiModelProperty(value = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。")
+    private String content;
+
+    @ApiModelProperty(value = "拓展字段", example = "{'orderId': 1}")
+    private Map<String, Object> exts;
+
     @ApiModelProperty(value = "请求方法名", required = true, example = "GET")
     @NotEmpty(message = "请求方法名不能为空")
     private String requestMethod;

+ 39 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogPageReqVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.dashboard.modules.system.controller.logger.vo;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("操作日志分页列表 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysOperateLogPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "操作模块", example = "订单", notes = "模拟匹配")
+    private String module;
+
+    @ApiModelProperty(value = "用户昵称", example = "芋道", notes = "模拟匹配")
+    private String userNickname;
+
+    @ApiModelProperty(value = "操作分类", example = "1", notes = "参见 SysOperateLogTypeEnum 枚举类")
+    private Integer type;
+
+    @ApiModelProperty(value = "操作状态", example = "true")
+    private Boolean success;
+
+    @ApiModelProperty(value = "开始时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date beginTime;
+
+    @ApiModelProperty(value = "结束时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date endTime;
+
+}

+ 21 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/SysOperateLogRespVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.dashboard.modules.system.controller.logger.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@ApiModel("操作日志 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysOperateLogRespVO extends SysOperateLogBaseVO {
+
+    @ApiModelProperty(value = "日志编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
+    private String userNickname;
+
+}

+ 6 - 0
src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysOperateLogConvert.java

@@ -1,6 +1,8 @@
 package cn.iocoder.dashboard.modules.system.convert.logger;
 
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogRespVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
@@ -12,4 +14,8 @@ public interface SysOperateLogConvert {
 
     SysOperateLogDO convert(SysOperateLogCreateReqVO bean);
 
+    PageResult<SysOperateLogRespVO> convertPage(PageResult<SysOperateLogDO> page);
+
+    SysOperateLogRespVO convert(SysOperateLogDO bean);
+
 }

+ 23 - 2
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysOperateLogMapper.java

@@ -1,9 +1,30 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger;
 
+import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogPageReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Collection;
+
 @Mapper
-public interface SysOperateLogMapper extends BaseMapper<SysOperateLogDO> {
+public interface SysOperateLogMapper extends BaseMapperX<SysOperateLogDO> {
+
+    default PageResult<SysOperateLogDO> selectPage(SysOperateLogPageReqVO reqVO, Collection<Long> userIds) {
+        QueryWrapperX<SysOperateLogDO> query = new QueryWrapperX<SysOperateLogDO>()
+                .likeIfPresent("module", reqVO.getModule())
+                .inIfPresent("user_id", userIds)
+                .eqIfPresent("operate_type", reqVO.getType())
+                .betweenIfPresent("start_time", reqVO.getBeginTime(), reqVO.getEndTime());
+        if (Boolean.TRUE.equals(reqVO.getSuccess())) {
+            query.eq("result_code", GlobalErrorCodeConstants.SUCCESS.getCode());
+        } else if (Boolean.FALSE.equals(reqVO.getSuccess())) {
+            query.gt("result_code", GlobalErrorCodeConstants.SUCCESS.getCode());
+        }
+        return selectPage(reqVO, query);
+    }
+
 }

+ 4 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/user/SysUserMapper.java

@@ -45,5 +45,9 @@ public interface SysUserMapper extends BaseMapper<SysUserDO> {
                 .inIfPresent("dept_id", deptIds));
     }
 
+    default List<SysUserDO> selectListByNickname(String nickname) {
+        return selectList(new QueryWrapperX<SysUserDO>().like("nickname", nickname));
+    }
+
 }
 

+ 2 - 6
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/logger/SysOperateLogDO.java

@@ -3,7 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger;
 import cn.iocoder.dashboard.common.pojo.CommonResult;
 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
-import cn.iocoder.dashboard.modules.system.enums.logger.SysOperateLogTypeEnum;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateLogTypeEnum;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -62,22 +62,18 @@ public class SysOperateLogDO extends BaseDO {
     /**
      * 操作分类
      *
-     * 枚举 {@link SysOperateLogTypeEnum}
+     * 枚举 {@link OperateLogTypeEnum}
      */
     @TableField("operate_type")
     private Integer type;
     /**
      * 操作内容,记录整个操作的明细
      * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
-     *
-     * TODO 预留字段
      */
     private String content;
     /**
      * 拓展字段,有些复杂的业务,需要记录一些字段
      * 例如说,记录订单编号,则可以添加 key 为 "orderId",value 为订单编号
-     *
-     * TODO 预留字段
      */
     @TableField(typeHandler = FastjsonTypeHandler.class)
     private Map<String, Object> exts;

+ 12 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysOperateLogService.java

@@ -1,9 +1,21 @@
 package cn.iocoder.dashboard.modules.system.service.logger;
 
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.framework.logger.operatelog.core.service.OperateLogFrameworkService;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogPageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
 
 /**
  * 操作日志 Service 接口
  */
 public interface SysOperateLogService extends OperateLogFrameworkService {
+
+    /**
+     * 获得操作日志分页列表
+     *
+     * @param reqVO 分页条件
+     * @return 操作日志分页列表
+     */
+    PageResult<SysOperateLogDO> pageOperateLog(SysOperateLogPageReqVO reqVO);
+
 }

+ 27 - 1
src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysOperateLogServiceImpl.java

@@ -1,18 +1,27 @@
 package cn.iocoder.dashboard.modules.system.service.logger.impl;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.SysOperateLogPageReqVO;
 import cn.iocoder.dashboard.modules.system.convert.logger.SysOperateLogConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger.SysOperateLogMapper;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.service.logger.SysOperateLogService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
 import cn.iocoder.dashboard.util.string.StrUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.util.Collection;
 
-import static cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO.*;
+import static cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH;
+import static cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO.RESULT_MAX_LENGTH;
+import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
 
 @Service
 @Slf4j
@@ -21,6 +30,9 @@ public class SysOperateLogServiceImpl implements SysOperateLogService {
     @Resource
     private SysOperateLogMapper operateLogMapper;
 
+    @Resource
+    private SysUserService userService;
+
     @Override
     @Async
     public void createOperateLogAsync(SysOperateLogCreateReqVO reqVO) {
@@ -35,4 +47,18 @@ public class SysOperateLogServiceImpl implements SysOperateLogService {
         }
     }
 
+    @Override
+    public PageResult<SysOperateLogDO> pageOperateLog(SysOperateLogPageReqVO reqVO) {
+        // 处理基于用户昵称的查询
+        Collection<Long> userIds = null;
+        if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
+            userIds = convertSet(userService.listUsersByNickname(reqVO.getUserNickname()), SysUserDO::getId);
+            if (CollUtil.isEmpty(userIds)) {
+                return PageResult.empty();
+            }
+        }
+        // 查询分页
+        return operateLogMapper.selectPage(reqVO, userIds);
+    }
+
 }

+ 34 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java

@@ -1,10 +1,15 @@
 package cn.iocoder.dashboard.modules.system.service.user;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
 
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 用户 Service 接口
@@ -45,6 +50,35 @@ public interface SysUserService {
      */
     List<SysUserDO> listUsers(SysUserExportReqVO reqVO);
 
+    /**
+     * 获得用户列表
+     *
+     * @param ids 用户编号数组
+     * @return 用户列表
+     */
+    List<SysUserDO> listUsers(Collection<Long> ids);
+
+    /**
+     * 获得用户 Map
+     *
+     * @param ids 用户编号数组
+     * @return 用户 Map
+     */
+    default Map<Long, SysUserDO> getUserMap(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return new HashMap<>();
+        }
+        return CollectionUtils.convertMap(listUsers(ids), SysUserDO::getId);
+    }
+
+    /**
+     * 获得用户列表,基于昵称模糊匹配
+     *
+     * @param nickname 昵称
+     * @return 用户列表
+     */
+    List<SysUserDO> listUsersByNickname(String nickname);
+
     /**
      * 创建用户
      *

+ 10 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java

@@ -83,6 +83,16 @@ public class SysUserServiceImpl implements SysUserService {
         return userMapper.selectList(reqVO, this.getDeptCondition(reqVO.getDeptId()));
     }
 
+    @Override
+    public List<SysUserDO> listUsers(Collection<Long> ids) {
+        return userMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public List<SysUserDO> listUsersByNickname(String nickname) {
+        return userMapper.selectListByNickname(nickname);
+    }
+
     /**
      * 获得部门条件:查询指定部门的子部门编号们,包括自身
      *