新增:新增系统监控/登录日志功能,优化日志表结构,并新增记录错误信息(非未知异常不记录异常详情,只记录错误信息)
This commit is contained in:
parent
aa726fc6b6
commit
c57383abad
@ -41,7 +41,6 @@ import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.exception.BadRequestException;
|
||||
import top.charles7c.cnadmin.common.exception.ServiceException;
|
||||
import top.charles7c.cnadmin.common.model.dto.LogContext;
|
||||
import top.charles7c.cnadmin.common.model.vo.R;
|
||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||
import top.charles7c.cnadmin.common.util.StreamUtils;
|
||||
@ -63,8 +62,8 @@ public class GlobalExceptionHandler {
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(Exception.class)
|
||||
public R handleException(Exception e, HttpServletRequest request) {
|
||||
this.setException(e);
|
||||
log.error("请求地址'{}',发生未知异常", request.getRequestURI(), e);
|
||||
LogContextHolder.setException(e);
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
|
||||
@ -74,8 +73,8 @@ public class GlobalExceptionHandler {
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
|
||||
this.setException(e);
|
||||
log.error("请求地址'{}',发生系统异常", request.getRequestURI(), e);
|
||||
LogContextHolder.setException(e);
|
||||
return R.fail(e.getMessage());
|
||||
}
|
||||
|
||||
@ -85,8 +84,8 @@ public class GlobalExceptionHandler {
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
public R handleServiceException(ServiceException e, HttpServletRequest request) {
|
||||
this.setException(e);
|
||||
log.error("请求地址'{}',发生业务异常", request.getRequestURI(), e);
|
||||
LogContextHolder.setErrorMsg(e.getMessage());
|
||||
return R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
|
||||
}
|
||||
|
||||
@ -97,6 +96,7 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(BadRequestException.class)
|
||||
public R handleBadRequestException(BadRequestException e, HttpServletRequest request) {
|
||||
log.error("请求地址'{}',自定义验证失败", request.getRequestURI(), e);
|
||||
LogContextHolder.setErrorMsg(e.getMessage());
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
|
||||
}
|
||||
|
||||
@ -107,8 +107,9 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(BindException.class)
|
||||
public R handleBindException(BindException e, HttpServletRequest request) {
|
||||
log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
|
||||
String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ",");
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), message);
|
||||
String errorMsg = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ",");
|
||||
LogContextHolder.setErrorMsg(errorMsg);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,8 +119,9 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public R constraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
|
||||
log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
|
||||
String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ",");
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), message);
|
||||
String errorMsg = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ",");
|
||||
LogContextHolder.setErrorMsg(errorMsg);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,8 +131,10 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
|
||||
log.error("请求地址'{}',参数验证失败", request.getRequestURI(), e);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), ExceptionUtils
|
||||
.exToNull(() -> Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()));
|
||||
String errorMsg = ExceptionUtils
|
||||
.exToNull(() -> Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
|
||||
LogContextHolder.setErrorMsg(errorMsg);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,6 +147,7 @@ public class GlobalExceptionHandler {
|
||||
String subMsg = StrUtil.format("参数名:'{}',期望参数类型:'{}'", e.getName(), e.getParameter().getParameterType());
|
||||
log.error("请求地址'{}',参数转换失败。方法:'{}',{}", request.getRequestURI(),
|
||||
Objects.requireNonNull(e.getParameter().getMethod()).getName(), subMsg, e);
|
||||
LogContextHolder.setErrorMsg(subMsg);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), subMsg);
|
||||
}
|
||||
|
||||
@ -152,6 +157,7 @@ public class GlobalExceptionHandler {
|
||||
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public R handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
|
||||
LogContextHolder.setErrorMsg(e.getMessage());
|
||||
log.error("请求地址'{}',不支持'{}'请求", request.getRequestURI(), e.getMethod());
|
||||
return R.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getMessage());
|
||||
}
|
||||
@ -163,7 +169,9 @@ public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public R handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
log.error("请求地址'{}',认证失败,无法访问系统资源", request.getRequestURI(), e);
|
||||
return R.fail(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录");
|
||||
String errorMsg = "登录状态已过期,请重新登录";
|
||||
LogContextHolder.setErrorMsg(errorMsg);
|
||||
return R.fail(HttpStatus.UNAUTHORIZED.value(), errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,22 +180,10 @@ public class GlobalExceptionHandler {
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public R handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e, HttpServletRequest request) {
|
||||
String sizeLimit = StrUtil.subBetween(e.getMessage(), "The maximum size ", " for");
|
||||
log.error("请求地址'{}',上传文件失败,文件大小超过限制", request.getRequestURI(), e);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(),
|
||||
String.format("请上传小于 %s MB 的文件", NumberUtil.parseLong(sizeLimit) / 1024 / 1024));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在系统日志上下文中保存异常信息
|
||||
*
|
||||
* @param e
|
||||
* 异常信息
|
||||
*/
|
||||
private void setException(Exception e) {
|
||||
LogContext logContext = LogContextHolder.get();
|
||||
if (logContext != null) {
|
||||
logContext.setException(e);
|
||||
}
|
||||
String sizeLimit = StrUtil.subBetween(e.getMessage(), "The maximum size ", " for");
|
||||
String errorMsg = String.format("请上传小于 %s MB 的文件", NumberUtil.parseLong(sizeLimit) / 1024 / 1024);
|
||||
LogContextHolder.setErrorMsg(errorMsg);
|
||||
return R.fail(HttpStatus.BAD_REQUEST.value(), errorMsg);
|
||||
}
|
||||
}
|
@ -40,7 +40,12 @@ public class LogContext {
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 异常
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private Exception exception;
|
||||
}
|
||||
|
@ -57,4 +57,31 @@ public class LogContextHolder {
|
||||
public static void remove() {
|
||||
LOG_THREAD_LOCAL.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在系统日志上下文中保存异常信息
|
||||
*
|
||||
* @param e
|
||||
* 异常信息
|
||||
*/
|
||||
public static void setException(Exception e) {
|
||||
LogContext logContext = get();
|
||||
if (logContext != null) {
|
||||
logContext.setErrorMsg(e.getMessage());
|
||||
logContext.setException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在系统日志上下文中保存错误信息(非未知异常不记录异常信息,只记录错误信息)
|
||||
*
|
||||
* @param errorMsg
|
||||
* 错误信息
|
||||
*/
|
||||
public static void setErrorMsg(String errorMsg) {
|
||||
LogContext logContext = get();
|
||||
if (logContext != null) {
|
||||
logContext.setErrorMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录请求耗时及异常信息
|
||||
* 记录请求耗时及异常详情
|
||||
*
|
||||
* @return 系统日志信息
|
||||
*/
|
||||
@ -123,11 +123,17 @@ public class LogInterceptor implements HandlerInterceptor {
|
||||
sysLog.setElapsedTime(System.currentTimeMillis() - LocalDateTimeUtil.toEpochMilli(sysLog.getCreateTime()));
|
||||
sysLog.setStatus(LogStatusEnum.SUCCESS);
|
||||
|
||||
// 记录异常信息
|
||||
// 记录错误信息(非未知异常不记录异常详情,只记录错误信息)
|
||||
String errorMsg = logContext.getErrorMsg();
|
||||
if (StrUtil.isNotBlank(errorMsg)) {
|
||||
sysLog.setStatus(LogStatusEnum.FAILURE);
|
||||
sysLog.setErrorMsg(errorMsg);
|
||||
}
|
||||
// 记录异常详情
|
||||
Exception exception = logContext.getException();
|
||||
if (exception != null) {
|
||||
sysLog.setStatus(LogStatusEnum.FAILURE);
|
||||
sysLog.setException(ExceptionUtil.stacktraceToString(exception, -1));
|
||||
sysLog.setExceptionDetail(ExceptionUtil.stacktraceToString(exception, -1));
|
||||
}
|
||||
return sysLog;
|
||||
}
|
||||
|
@ -110,9 +110,14 @@ public class SysLog implements Serializable {
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 异常
|
||||
* 错误信息
|
||||
*/
|
||||
private String exception;
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 异常详情
|
||||
*/
|
||||
private String exceptionDetail;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.monitor.model.query;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.springdoc.api.annotations.ParameterObject;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import top.charles7c.cnadmin.common.annotation.Query;
|
||||
|
||||
/**
|
||||
* 登录日志查询条件
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/16 23:25
|
||||
*/
|
||||
@Data
|
||||
@ParameterObject
|
||||
@Schema(description = "登录日志查询条件")
|
||||
public class LoginLogQuery implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 登录状态(1成功 2失败)
|
||||
*/
|
||||
@Schema(description = "登录状态(1成功 2失败)")
|
||||
@Query(type = Query.Type.EQUAL)
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
@Schema(description = "登录时间")
|
||||
@Query(type = Query.Type.BETWEEN)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private List<Date> createTime;
|
||||
}
|
@ -14,29 +14,32 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.monitor.service;
|
||||
package top.charles7c.cnadmin.monitor.model.vo;
|
||||
|
||||
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* 操作日志业务接口
|
||||
* 基础日志信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/15 21:05
|
||||
* @since 2023/1/17 21:43
|
||||
*/
|
||||
public interface OperationLogService {
|
||||
@Data
|
||||
public class LogVO {
|
||||
|
||||
/**
|
||||
* 分页查询列表
|
||||
*
|
||||
* @param query
|
||||
* 查询条件
|
||||
* @param pageQuery
|
||||
* 分页查询条件
|
||||
* @return 分页信息
|
||||
* 创建人
|
||||
*/
|
||||
PageInfo<OperationLogVO> list(OperationLogQuery query, PageQuery pageQuery);
|
||||
@JsonIgnore
|
||||
private Long createUser;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@Schema(description = "创建人")
|
||||
private String createUserString;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.monitor.model.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.charles7c.cnadmin.monitor.enums.LogStatusEnum;
|
||||
|
||||
/**
|
||||
* 登录日志信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/16 23:19
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "登录日志信息")
|
||||
public class LoginLogVO extends LogVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 日志ID
|
||||
*/
|
||||
@Schema(description = "日志ID")
|
||||
private Long logId;
|
||||
|
||||
/**
|
||||
* 日志描述
|
||||
*/
|
||||
@Schema(description = "日志描述")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 操作状态(1成功 2失败)
|
||||
*/
|
||||
@Schema(description = "操作状态(1成功 2失败)", type = "Integer", allowableValues = {"1", "2"})
|
||||
private LogStatusEnum status;
|
||||
|
||||
/**
|
||||
* 登录IP
|
||||
*/
|
||||
@Schema(description = "登录IP")
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 登录地点
|
||||
*/
|
||||
@Schema(description = "登录地点")
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 浏览器
|
||||
*/
|
||||
@Schema(description = "浏览器")
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
@Schema(description = "登录时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
@ -23,8 +23,6 @@ import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import top.charles7c.cnadmin.monitor.enums.LogStatusEnum;
|
||||
|
||||
/**
|
||||
@ -35,7 +33,7 @@ import top.charles7c.cnadmin.monitor.enums.LogStatusEnum;
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "操作日志信息")
|
||||
public class OperationLogVO implements Serializable {
|
||||
public class OperationLogVO extends LogVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -76,16 +74,10 @@ public class OperationLogVO implements Serializable {
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 操作人
|
||||
* 错误信息
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Long createUser;
|
||||
|
||||
/**
|
||||
* 操作人
|
||||
*/
|
||||
@Schema(description = "操作人")
|
||||
private String createUserString;
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
|
@ -16,6 +16,13 @@
|
||||
|
||||
package top.charles7c.cnadmin.monitor.service;
|
||||
|
||||
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||
|
||||
/**
|
||||
* 系统日志业务接口
|
||||
*
|
||||
@ -24,4 +31,25 @@ package top.charles7c.cnadmin.monitor.service;
|
||||
*/
|
||||
public interface LogService {
|
||||
|
||||
/**
|
||||
* 分页查询操作日志列表
|
||||
*
|
||||
* @param query
|
||||
* 查询条件
|
||||
* @param pageQuery
|
||||
* 分页查询条件
|
||||
* @return 操作日志分页信息
|
||||
*/
|
||||
PageInfo<OperationLogVO> list(OperationLogQuery query, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 分页查询登录日志列表
|
||||
*
|
||||
* @param query
|
||||
* 查询条件
|
||||
* @param pageQuery
|
||||
* 分页查询条件
|
||||
* @return 登录日志分页信息
|
||||
*/
|
||||
PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery);
|
||||
}
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
package top.charles7c.cnadmin.monitor.service.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -23,9 +27,25 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
|
||||
import top.charles7c.cnadmin.common.util.ReflectUtils;
|
||||
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
|
||||
import top.charles7c.cnadmin.monitor.mapper.LogMapper;
|
||||
import top.charles7c.cnadmin.monitor.model.entity.SysLog;
|
||||
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.LogVO;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||
import top.charles7c.cnadmin.system.service.UserService;
|
||||
|
||||
/**
|
||||
* 系统日志业务实现类
|
||||
@ -39,10 +59,69 @@ import top.charles7c.cnadmin.monitor.service.LogService;
|
||||
public class LogServiceImpl implements LogService {
|
||||
|
||||
private final LogMapper logMapper;
|
||||
private final UserService userService;
|
||||
|
||||
@Async
|
||||
@EventListener
|
||||
public void save(SysLog sysLog) {
|
||||
logMapper.insert(sysLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<OperationLogVO> list(OperationLogQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
||||
|
||||
// 限定查询信息
|
||||
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(OperationLogVO.class);
|
||||
List<String> columns = Arrays.stream(fieldsName).map(StrUtil::toUnderlineCase)
|
||||
.filter(n -> !n.endsWith("string")).collect(Collectors.toList());
|
||||
queryWrapper.select(columns);
|
||||
|
||||
// 分页查询
|
||||
IPage<SysLog> page = logMapper.selectPage(pageQuery.toPage(), queryWrapper);
|
||||
PageInfo<OperationLogVO> pageInfo = PageInfo.build(page, OperationLogVO.class);
|
||||
|
||||
// 填充数据(如果是查询个人操作日志,只查询一次用户信息即可)
|
||||
if (query.getUid() != null) {
|
||||
String nickname = ExceptionUtils.exToNull(() -> userService.getById(query.getUid()).getNickname());
|
||||
pageInfo.getList().forEach(o -> o.setCreateUserString(nickname));
|
||||
} else {
|
||||
pageInfo.getList().forEach(this::fill);
|
||||
}
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<LoginLogVO> list(LoginLogQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
||||
queryWrapper.and(qw -> qw.like("request_url", "/auth/login").or().like("request_url", "/auth/logout"));
|
||||
|
||||
// 限定查询信息
|
||||
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(LoginLogVO.class);
|
||||
List<String> columns = Arrays.stream(fieldsName).map(StrUtil::toUnderlineCase)
|
||||
.filter(n -> !n.endsWith("string")).collect(Collectors.toList());
|
||||
queryWrapper.select(columns);
|
||||
|
||||
// 分页查询
|
||||
IPage<SysLog> page = logMapper.selectPage(pageQuery.toPage(), queryWrapper);
|
||||
PageInfo<LoginLogVO> pageInfo = PageInfo.build(page, LoginLogVO.class);
|
||||
|
||||
// 填充数据
|
||||
pageInfo.getList().forEach(this::fill);
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
*
|
||||
* @param vo
|
||||
* VO
|
||||
*/
|
||||
private void fill(LogVO vo) {
|
||||
Long createUser = vo.getCreateUser();
|
||||
if (createUser == null) {
|
||||
return;
|
||||
}
|
||||
vo.setCreateUserString(ExceptionUtils.exToNull(() -> userService.getById(vo.getCreateUser())).getNickname());
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.monitor.service.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.common.util.ReflectUtils;
|
||||
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
|
||||
import top.charles7c.cnadmin.monitor.mapper.LogMapper;
|
||||
import top.charles7c.cnadmin.monitor.model.entity.SysLog;
|
||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||
import top.charles7c.cnadmin.monitor.service.OperationLogService;
|
||||
import top.charles7c.cnadmin.system.mapper.UserMapper;
|
||||
import top.charles7c.cnadmin.system.model.entity.SysUser;
|
||||
|
||||
/**
|
||||
* 操作日志业务实现类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/15 21:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OperationLogServiceImpl implements OperationLogService {
|
||||
|
||||
private final LogMapper logMapper;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public PageInfo<OperationLogVO> list(OperationLogQuery query, PageQuery pageQuery) {
|
||||
QueryWrapper<SysLog> queryWrapper = QueryHelper.build(query);
|
||||
|
||||
// 限定查询信息
|
||||
String[] fieldsName = ReflectUtils.getNonStaticFieldsName(OperationLogVO.class);
|
||||
List<String> columns = Arrays.stream(fieldsName).map(StrUtil::toUnderlineCase)
|
||||
.filter(n -> !n.endsWith("string")).collect(Collectors.toList());
|
||||
queryWrapper.select(columns);
|
||||
|
||||
// 分页查询
|
||||
IPage<SysLog> page = logMapper.selectPage(pageQuery.toPage(), queryWrapper);
|
||||
PageInfo<OperationLogVO> pageInfo = PageInfo.build(page, OperationLogVO.class);
|
||||
|
||||
// 填充数据(如果是查询个人操作日志,只查询一次用户信息即可)
|
||||
if (query.getUid() != null) {
|
||||
SysUser sysUser = userMapper.selectById(query.getUid());
|
||||
pageInfo.getList().forEach(o -> o.setCreateUserString(sysUser.getUsername()));
|
||||
} else {
|
||||
pageInfo.getList().forEach(this::fill);
|
||||
}
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
*
|
||||
* @param vo
|
||||
* VO
|
||||
*/
|
||||
private void fill(OperationLogVO vo) {
|
||||
Long createUser = vo.getCreateUser();
|
||||
if (createUser == null) {
|
||||
return;
|
||||
}
|
||||
SysUser sysUser = userMapper.selectById(createUser);
|
||||
vo.setCreateUserString(sysUser.getUsername());
|
||||
}
|
||||
}
|
@ -79,4 +79,13 @@ public interface UserService {
|
||||
* 用户ID
|
||||
*/
|
||||
void updateEmail(String newEmail, String currentPassword, Long userId);
|
||||
|
||||
/**
|
||||
* 根据 ID 查询
|
||||
*
|
||||
* @param userId
|
||||
* 用户ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
SysUser getById(Long userId);
|
||||
}
|
||||
|
@ -141,14 +141,8 @@ public class UserServiceImpl implements UserService {
|
||||
LoginHelper.updateLoginUser(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 查询
|
||||
*
|
||||
* @param userId
|
||||
* 用户 ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
private SysUser getById(Long userId) {
|
||||
@Override
|
||||
public SysUser getById(Long userId) {
|
||||
ValidationUtils.exIfNull(userId, "用户不存在");
|
||||
SysUser sysUser = userMapper.selectById(userId);
|
||||
ValidationUtils.exIfNull(sysUser, String.format("ID为 [%s] 的用户已不存在", userId));
|
||||
|
33
continew-admin-ui/src/api/monitor/login-log.ts
Normal file
33
continew-admin-ui/src/api/monitor/login-log.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
|
||||
export interface LoginLogRecord {
|
||||
logId: string;
|
||||
status: number;
|
||||
clientIp: string;
|
||||
location: string;
|
||||
browser: string;
|
||||
errorMsg: string;
|
||||
createUserString: string;
|
||||
createTime: string;
|
||||
}
|
||||
|
||||
export interface LoginLogParams extends Partial<LoginLogRecord> {
|
||||
page: number;
|
||||
size: number;
|
||||
sort: Array<string>;
|
||||
}
|
||||
|
||||
export interface LoginLogListRes {
|
||||
list: LoginLogRecord[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export function queryLoginLogList(params: LoginLogParams) {
|
||||
return axios.get<LoginLogListRes>('/monitor/log/login', {
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
return qs.stringify(obj);
|
||||
},
|
||||
});
|
||||
}
|
@ -4,10 +4,11 @@ import qs from 'query-string';
|
||||
export interface OperationLogRecord {
|
||||
logId: string;
|
||||
description: string;
|
||||
status: number,
|
||||
clientIp: string,
|
||||
location: string,
|
||||
browser: string,
|
||||
status: number;
|
||||
clientIp: string;
|
||||
location: string;
|
||||
browser: string;
|
||||
errorMsg: string;
|
||||
createUserString: string;
|
||||
createTime: string;
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import localeMonitor from '@/views/dashboard/monitor/locale/en-US';
|
||||
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/en-US';
|
||||
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/en-US';
|
||||
|
||||
import localeLog from '@/views/monitor/log/operation/locale/en-US';
|
||||
import localeOperationLog from '@/views/monitor/log/operation/locale/en-US';
|
||||
import localeLoginLog from '@/views/monitor/log/login/locale/en-US';
|
||||
|
||||
import localeSearchTable from '@/views/list/search-table/locale/en-US';
|
||||
import localeCardList from '@/views/list/card/locale/en-US';
|
||||
@ -55,7 +56,8 @@ export default {
|
||||
...localeDataAnalysis,
|
||||
...localeMultiDAnalysis,
|
||||
|
||||
...localeLog,
|
||||
...localeOperationLog,
|
||||
...localeLoginLog,
|
||||
|
||||
...localeSearchTable,
|
||||
...localeCardList,
|
||||
|
@ -8,7 +8,8 @@ import localeMonitor from '@/views/dashboard/monitor/locale/zh-CN';
|
||||
import localeDataAnalysis from '@/views/visualization/data-analysis/locale/zh-CN';
|
||||
import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/zh-CN';
|
||||
|
||||
import localeLog from '@/views/monitor/log/operation/locale/zh-CN';
|
||||
import localeOperationLog from '@/views/monitor/log/operation/locale/zh-CN';
|
||||
import localeLoginLog from '@/views/monitor/log/login/locale/zh-CN';
|
||||
|
||||
import localeSearchTable from '@/views/list/search-table/locale/zh-CN';
|
||||
import localeCardList from '@/views/list/card/locale/zh-CN';
|
||||
@ -55,7 +56,8 @@ export default {
|
||||
...localeDataAnalysis,
|
||||
...localeMultiDAnalysis,
|
||||
|
||||
...localeLog,
|
||||
...localeOperationLog,
|
||||
...localeLoginLog,
|
||||
|
||||
...localeSearchTable,
|
||||
...localeCardList,
|
||||
|
@ -22,6 +22,16 @@ const Monitor: AppRouteRecordRaw = {
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'log/login',
|
||||
name: 'LoginLog',
|
||||
component: () => import('@/views/monitor/log/login/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.log.login.list',
|
||||
requiresAuth: true,
|
||||
roles: ['*'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { DEFAULT_LAYOUT } from '../base';
|
||||
import { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const USER: AppRouteRecordRaw = {
|
||||
path: '/system/user',
|
||||
path: '/login/user',
|
||||
name: 'user',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
|
214
continew-admin-ui/src/views/monitor/log/login/index.vue
Normal file
214
continew-admin-ui/src/views/monitor/log/login/index.vue
Normal file
@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['menu.monitor', 'menu.log.login.list']" />
|
||||
<a-card class="general-card" :title="$t('menu.log.login.list')">
|
||||
<a-row style="margin-bottom: 15px">
|
||||
<a-col :span="24">
|
||||
<a-form
|
||||
ref="queryFormRef"
|
||||
:model="queryFormData"
|
||||
layout="inline"
|
||||
>
|
||||
<a-form-item
|
||||
field="status"
|
||||
hide-label
|
||||
>
|
||||
<a-select
|
||||
v-model="queryFormData.status"
|
||||
:options="statusOptions"
|
||||
placeholder="登录状态搜索"
|
||||
allow-clear
|
||||
style="width: 150px;"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="createTime"
|
||||
hide-label
|
||||
>
|
||||
<a-range-picker
|
||||
v-model="queryFormData.createTime"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
show-time
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-button type="primary" @click="toQuery">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button @click="resetQuery">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-table
|
||||
row-key="logId"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:columns="columns"
|
||||
:data="renderData"
|
||||
:bordered="false"
|
||||
:stripe="true"
|
||||
size="large"
|
||||
@page-change="onPageChange"
|
||||
>
|
||||
<template #index="{ rowIndex }">
|
||||
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-space v-if="record.status === 1">
|
||||
<a-tag color="green">
|
||||
<span class="circle pass"></span>
|
||||
成功
|
||||
</a-tag>
|
||||
</a-space>
|
||||
<a-space v-else>
|
||||
<a-tooltip :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail"></span>
|
||||
失败
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #operations>
|
||||
<a-button v-permission="['admin']" type="text" size="small">详情</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, reactive } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { queryLoginLogList, LoginLogRecord, LoginLogParams } from '@/api/monitor/login-log';
|
||||
import { Pagination } from '@/types/global';
|
||||
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
|
||||
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
const renderData = ref<LoginLogRecord[]>([]);
|
||||
|
||||
const queryFormData = ref({
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
});
|
||||
const statusOptions = computed<SelectOptionData[]>(() => [
|
||||
{
|
||||
label: '成功',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '失败',
|
||||
value: 2,
|
||||
},
|
||||
]);
|
||||
|
||||
const basePagination: Pagination = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
const pagination = reactive({
|
||||
...basePagination,
|
||||
});
|
||||
const columns = computed<TableColumnData[]>(() => [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'index',
|
||||
slotName: 'index',
|
||||
},
|
||||
{
|
||||
title: '用户昵称',
|
||||
dataIndex: 'createUserString',
|
||||
},
|
||||
{
|
||||
title: '登录行为',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
{
|
||||
title: '登录状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '登录IP',
|
||||
dataIndex: 'clientIp',
|
||||
},
|
||||
{
|
||||
title: '登录地点',
|
||||
dataIndex: 'location',
|
||||
},
|
||||
{
|
||||
title: '浏览器',
|
||||
dataIndex: 'browser',
|
||||
},
|
||||
{
|
||||
title: '登录时间',
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
]);
|
||||
const fetchData = async (
|
||||
params: LoginLogParams = { page: 1, size: 10, sort: ['createTime,desc'] }
|
||||
) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data } = await queryLoginLogList(params);
|
||||
renderData.value = data.list;
|
||||
pagination.current = params.page;
|
||||
pagination.total = data.total;
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onPageChange = (current: number) => {
|
||||
fetchData({ page: current, size: pagination.pageSize, sort: ['createTime,desc'] });
|
||||
};
|
||||
|
||||
// 查询
|
||||
const toQuery = () => {
|
||||
fetchData({
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
sort: ['createTime,desc'],
|
||||
...queryFormData.value,
|
||||
} as unknown as LoginLogParams);
|
||||
};
|
||||
|
||||
// 重置
|
||||
const resetQuery = async () => {
|
||||
await queryFormRef.value?.resetFields();
|
||||
await fetchData();
|
||||
};
|
||||
fetchData();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'LoginLog',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
&:last-child {
|
||||
.arco-table-th-item-title {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
'menu.log.login.list': 'Login log',
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
'menu.log.login.list': '登录日志',
|
||||
};
|
@ -81,10 +81,12 @@
|
||||
</a-tag>
|
||||
</a-space>
|
||||
<a-space v-else>
|
||||
<a-tag color="red">
|
||||
<span class="circle fail"></span>
|
||||
失败
|
||||
</a-tag>
|
||||
<a-tooltip :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail"></span>
|
||||
失败
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
@ -22,10 +22,12 @@
|
||||
</a-tag>
|
||||
</a-space>
|
||||
<a-space v-else>
|
||||
<a-tag color="red">
|
||||
<span class="circle fail"></span>
|
||||
失败
|
||||
</a-tag>
|
||||
<a-tooltip :content="record.errorMsg">
|
||||
<a-tag color="red" style="cursor: pointer">
|
||||
<span class="circle fail"></span>
|
||||
失败
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #pagination-left>
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.charles7c.cnadmin.webapi.controller.monitor;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
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 top.charles7c.cnadmin.common.model.query.PageQuery;
|
||||
import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.common.model.vo.R;
|
||||
import top.charles7c.cnadmin.monitor.model.query.LoginLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.LoginLogVO;
|
||||
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||
|
||||
/**
|
||||
* 登录日志 API
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/1/16 23:17
|
||||
*/
|
||||
@Tag(name = "登录日志 API")
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping(value = "/monitor/log/login", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class LoginLogController {
|
||||
|
||||
private final LogService logService;
|
||||
|
||||
@Operation(summary = "分页查询登录日志列表")
|
||||
@GetMapping
|
||||
public R<PageInfo<LoginLogVO>> list(@Validated LoginLogQuery query, @Validated PageQuery pageQuery) {
|
||||
PageInfo<LoginLogVO> pageInfo = logService.list(query, pageQuery);
|
||||
return R.ok(pageInfo);
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ import top.charles7c.cnadmin.common.model.vo.PageInfo;
|
||||
import top.charles7c.cnadmin.common.model.vo.R;
|
||||
import top.charles7c.cnadmin.monitor.model.query.OperationLogQuery;
|
||||
import top.charles7c.cnadmin.monitor.model.vo.OperationLogVO;
|
||||
import top.charles7c.cnadmin.monitor.service.OperationLogService;
|
||||
import top.charles7c.cnadmin.monitor.service.LogService;
|
||||
|
||||
/**
|
||||
* 操作日志 API
|
||||
@ -47,12 +47,12 @@ import top.charles7c.cnadmin.monitor.service.OperationLogService;
|
||||
@RequestMapping(value = "/monitor/log/operation", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class OperationLogController {
|
||||
|
||||
private final OperationLogService operationLogService;
|
||||
private final LogService logService;
|
||||
|
||||
@Operation(summary = "分页查询操作日志列表")
|
||||
@GetMapping
|
||||
public R<PageInfo<OperationLogVO>> list(@Validated OperationLogQuery query, @Validated PageQuery pageQuery) {
|
||||
PageInfo<OperationLogVO> pageInfo = operationLogService.list(query, pageQuery);
|
||||
PageInfo<OperationLogVO> pageInfo = logService.list(query, pageQuery);
|
||||
return R.ok(pageInfo);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ CREATE TABLE IF NOT EXISTS `sys_log` (
|
||||
`client_ip` varchar(255) DEFAULT NULL COMMENT '客户端IP',
|
||||
`location` varchar(512) DEFAULT NULL COMMENT 'IP归属地',
|
||||
`browser` varchar(255) DEFAULT NULL COMMENT '浏览器',
|
||||
`exception` mediumtext DEFAULT NULL COMMENT '异常',
|
||||
`error_msg` text DEFAULT NULL COMMENT '错误信息',
|
||||
`exception_detail` mediumtext DEFAULT NULL COMMENT '异常详情',
|
||||
`create_user` bigint(20) unsigned DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`log_id`) USING BTREE,
|
||||
|
Loading…
Reference in New Issue
Block a user