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

pull/2/head
YunaiV 2021-01-17 20:55:14 +08:00
parent 2fccaa2b9a
commit 1768d27e11
35 changed files with 648 additions and 663 deletions

View File

@ -22,7 +22,7 @@
<maven.compiler.target>${java.version}</maven.compiler.target> <maven.compiler.target>${java.version}</maven.compiler.target>
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> <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>--> <!-- <ruoyi.version>3.3.0</ruoyi.version>-->
<!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>--> <!-- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->

View File

@ -23,28 +23,12 @@ import com.ruoyi.system.service.ISysOperLogService;
* *
* @author ruoyi * @author ruoyi
*/ */
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController { public class SysOperlogController extends BaseController {
@Autowired @Autowired
private ISysOperLogService operLogService; 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, "操作日志");
}
} }

View File

@ -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>

View File

@ -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;
}

View File

@ -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 truefalse
*/
@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;
}
}

View File

@ -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());
}
}

View File

@ -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
})
}

View File

@ -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
})
}

View File

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

View File

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

View File

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

View File

@ -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');

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package cn.iocoder.dashboard.framework.logger.operatelog.core.annotations; 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.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -32,7 +32,7 @@ public @interface OperateLog {
* *
* null * null
*/ */
SysOperateLogTypeEnum[] type() default {}; OperateLogTypeEnum[] type() default {};
// ========== 开关字段 ========== // ========== 开关字段 ==========

View File

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

View File

@ -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 cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -11,7 +11,7 @@ import lombok.Getter;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum SysOperateLogTypeEnum { public enum OperateLogTypeEnum {
/** /**
* *

View File

@ -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());
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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, "操作日志");
// }
}

View File

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

View File

@ -6,6 +6,7 @@ import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.Date; import java.util.Date;
import java.util.Map;
/** /**
* Base VO VO 使 * Base VO VO 使
@ -30,10 +31,16 @@ public class SysOperateLogBaseVO {
@NotEmpty(message = "操作名") @NotEmpty(message = "操作名")
private String name; private String name;
@ApiModelProperty(value = "操作分类", required = true, example = "操作分类", notes = "参见 SysOperateLogTypeEnum 枚举类") @ApiModelProperty(value = "操作分类", required = true, example = "1", notes = "参见 SysOperateLogTypeEnum 枚举类")
@NotNull(message = "操作分类不能为空") @NotNull(message = "操作分类不能为空")
private Integer type; 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") @ApiModelProperty(value = "请求方法名", required = true, example = "GET")
@NotEmpty(message = "请求方法名不能为空") @NotEmpty(message = "请求方法名不能为空")
private String requestMethod; private String requestMethod;

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

@ -1,9 +1,30 @@
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger; 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 cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysOperateLogDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@Mapper @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);
}
} }

View File

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

View File

@ -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.common.pojo.CommonResult;
import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; 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.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.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -62,22 +62,18 @@ public class SysOperateLogDO extends BaseDO {
/** /**
* *
* *
* {@link SysOperateLogTypeEnum} * {@link OperateLogTypeEnum}
*/ */
@TableField("operate_type") @TableField("operate_type")
private Integer type; private Integer type;
/** /**
* *
* 1 * 1
*
* TODO
*/ */
private String content; private String content;
/** /**
* *
* key "orderId"value * key "orderId"value
*
* TODO
*/ */
@TableField(typeHandler = FastjsonTypeHandler.class) @TableField(typeHandler = FastjsonTypeHandler.class)
private Map<String, Object> exts; private Map<String, Object> exts;

View File

@ -1,9 +1,21 @@
package cn.iocoder.dashboard.modules.system.service.logger; 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.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 * Service
*/ */
public interface SysOperateLogService extends OperateLogFrameworkService { public interface SysOperateLogService extends OperateLogFrameworkService {
/**
*
*
* @param reqVO
* @return
*/
PageResult<SysOperateLogDO> pageOperateLog(SysOperateLogPageReqVO reqVO);
} }

View File

@ -1,18 +1,27 @@
package cn.iocoder.dashboard.modules.system.service.logger.impl; 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.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.convert.logger.SysOperateLogConvert;
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger.SysOperateLogMapper; 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.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.logger.SysOperateLogService;
import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
import cn.iocoder.dashboard.util.string.StrUtils; import cn.iocoder.dashboard.util.string.StrUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; 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 @Service
@Slf4j @Slf4j
@ -21,6 +30,9 @@ public class SysOperateLogServiceImpl implements SysOperateLogService {
@Resource @Resource
private SysOperateLogMapper operateLogMapper; private SysOperateLogMapper operateLogMapper;
@Resource
private SysUserService userService;
@Override @Override
@Async @Async
public void createOperateLogAsync(SysOperateLogCreateReqVO reqVO) { 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);
}
} }

View File

@ -1,10 +1,15 @@
package cn.iocoder.dashboard.modules.system.service.user; 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.common.pojo.PageResult;
import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*; 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.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.List;
import java.util.Map;
/** /**
* Service * Service
@ -45,6 +50,35 @@ public interface SysUserService {
*/ */
List<SysUserDO> listUsers(SysUserExportReqVO reqVO); 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);
/** /**
* *
* *

View File

@ -83,6 +83,16 @@ public class SysUserServiceImpl implements SysUserService {
return userMapper.selectList(reqVO, this.getDeptCondition(reqVO.getDeptId())); 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);
}
/** /**
* *
* *