From f4ea2d44d671cee14a026dd7dfeb63053cb482b1 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Mon, 16 Jan 2023 00:18:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=E6=96=B0=E5=A2=9E=E6=9F=A5=E8=AF=A2=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=A5=E5=BF=97=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=97=A5=E5=BF=97=E8=A1=A8=E7=BB=93=E6=9E=84=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E6=94=AF=E6=8C=81=E5=85=B3=E9=97=AD=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E5=86=85=E7=BD=91=20IP=20=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +- .../cnadmin/common/annotation/Query.java | 113 +++++++++ .../handler/GlobalExceptionHandler.java | 10 +- .../{OperationLog.java => LogContext.java} | 5 +- .../cnadmin/common/model/query/PageQuery.java | 127 ++++++++++ .../cnadmin/common/model/vo/PageInfo.java | 96 ++++++++ .../charles7c/cnadmin/common/model/vo/R.java | 23 +- .../cnadmin/common/util/ReflectUtils.java | 66 ++++++ .../common/util/helper/QueryHelper.java | 216 ++++++++++++++++++ .../common/util/holder/LogContextHolder.java | 24 +- continew-admin-monitor/pom.xml | 4 +- .../cnadmin/monitor/annotation/Log.java | 4 +- .../config/properties/LogProperties.java | 23 +- .../{LogLevelEnum.java => LogResultEnum.java} | 16 +- .../monitor/interceptor/LogInterceptor.java | 68 +++--- .../cnadmin/monitor/mapper/LogMapper.java | 2 +- .../cnadmin/monitor/model/entity/SysLog.java | 20 +- .../model/query/OperationLogQuery.java | 50 ++++ .../monitor/model/vo/OperationLogVO.java | 95 ++++++++ .../cnadmin/monitor/service/LogService.java | 2 +- .../monitor/service/OperationLogService.java | 42 ++++ .../monitor/service/impl/LogServiceImpl.java | 2 +- .../service/impl/OperationLogServiceImpl.java | 90 ++++++++ .../src/api/monitor/operation-log.ts | 34 +++ .../src/assets/style/global.less | 3 + .../src/store/modules/login/index.ts | 2 +- .../src/store/modules/login/types.ts | 2 +- .../src/views/login/components/login-form.vue | 2 +- .../user/center/components/operation-log.vue | 142 ++++++++++++ .../src/views/system/user/center/index.vue | 4 + .../views/system/user/center/locale/en-US.ts | 1 + .../views/system/user/center/locale/zh-CN.ts | 1 + continew-admin-webapi/pom.xml | 6 - .../monitor/OperationLogController.java | 58 +++++ .../src/main/resources/application.yml | 12 +- .../changelog/v0.0.1/continew-admin_table.sql | 12 +- 36 files changed, 1272 insertions(+), 119 deletions(-) create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/Query.java rename continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/{OperationLog.java => LogContext.java} (95%) create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/query/PageQuery.java create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/PageInfo.java create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ReflectUtils.java create mode 100644 continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/QueryHelper.java rename continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/{LogLevelEnum.java => LogResultEnum.java} (77%) create mode 100644 continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OperationLogQuery.java create mode 100644 continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OperationLogVO.java create mode 100644 continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/OperationLogService.java create mode 100644 continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/OperationLogServiceImpl.java create mode 100644 continew-admin-ui/src/api/monitor/operation-log.ts create mode 100644 continew-admin-ui/src/views/system/user/center/components/operation-log.vue create mode 100644 continew-admin-webapi/src/main/java/top/charles7c/cnadmin/webapi/controller/monitor/OperationLogController.java diff --git a/README.md b/README.md index 292de20e..12196a0b 100644 --- a/README.md +++ b/README.md @@ -134,9 +134,10 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ │ └─ cnadmin │ │ ├─ webapi │ │ │ └─ controller - │ │ │ ├─ auth # 认证相关 API - │ │ │ ├─ common # 公共相关 API(例如:验证码 API 等) - │ │ │ └─ system # 系统管理相关 API + │ │ │ ├─ auth # 认证相关 API + │ │ │ ├─ common # 公共相关 API(例如:验证码 API 等) + │ │ │ ├─ monitor # 系统监控相关 API + │ │ │ └─ system # 系统管理相关 API │ │ └─ ContinewAdminApplication.java # 启动入口 │ └─ resources # 工程配置目录 │ ├─ db.changelog # 数据库脚本文件 @@ -159,7 +160,9 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ │ ├─ interceptor # 系统监控相关拦截器 │ │ ├─ mapper # 系统监控相关 Mapper │ │ ├─ model # 系统监控相关模型 - │ │ │ └─ entity # 系统监控相关实体对象 + │ │ │ ├─ entity # 系统监控相关实体对象 + │ │ │ ├─ query # 系统监控相关查询条件 + │ │ │ └─ vo # 系统监控相关 VO(View Object) │ │ └─ service # 系统监控相关业务接口及实现类 │ │ └─ impl # 系统监控相关业务实现类 │ └─ resources # 工程配置目录 @@ -197,6 +200,7 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ └─ charles7c │ └─ cnadmin │ └─ common + │ ├─ annotation # 公共注解 │ ├─ config # 公共配置 │ │ ├─ jackson # Jackson 配置 │ │ ├─ mybatis # MyBatis Plus 配置 @@ -209,6 +213,7 @@ continew-admin # 全局通用项目配置及依赖版本管理 │ ├─ model # 公共模型 │ │ ├─ dto # 公共 DTO(Data Transfer Object) │ │ ├─ entity # 公共实体对象 + │ │ ├─ query # 公共查询条件 │ │ └─ vo # 公共 VO(View Object) │ └─ util # 公共工具类 │ ├─ helper # 公共 Helper(助手) @@ -226,6 +231,7 @@ continew-admin │ ├─ api # 请求接口 │ │ ├─ auth # 认证模块 │ │ ├─ common # 公共模块 + │ │ ├─ monitor # 系统监控模块 │ │ └─ system # 系统管理模块 │ ├─ assets # 静态资源 │ │ ├─ images # 图片资源 diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/Query.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/Query.java new file mode 100644 index 00000000..5d439e5f --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/annotation/Query.java @@ -0,0 +1,113 @@ +/* + * 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.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 查询注解 + * + * @author Charles7c + * @since 2023/1/15 18:01 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Query { + + /** + * 属性名(默认和使用该注解的属性的名称一致) + */ + String property() default ""; + + /** + * 查询类型(等值查询、模糊查询、范围查询等) + */ + Type type() default Type.EQUAL; + + /** + * 多属性模糊查询,仅支持 String 类型属性,多个属性之间用逗号分隔 + *

+ * 例如:@Query(blurry = "username,email") 表示根据用户名和邮箱模糊查询 + *

+ */ + String blurry() default ""; + + /** + * 查询类型 + */ + enum Type { + /** + * 等值查询,例如:WHERE `age` = 18 + */ + EQUAL, + /** + * 非等值查询,例如:WHERE `age` != 18 + */ + NOT_EQUAL, + /** + * 大于查询,例如:WHERE `age` > 18 + */ + GREATER_THAN, + /** + * 小于查询,例如:WHERE `age` < 18 + */ + LESS_THAN, + /** + * 大于等于查询,例如:WHERE `age` >= 18 + */ + GREATER_THAN_OR_EQUAL, + /** + * 小于等于查询,例如:WHERE `age` <= 18 + */ + LESS_THAN_OR_EQUAL, + /** + * 范围查询,例如:WHERE `age` BETWEEN 10 AND 18 + */ + BETWEEN, + /** + * 左模糊查询,例如:WHERE `nickname` LIKE '%张' + */ + LEFT_LIKE, + /** + * 中模糊查询,例如:WHERE `nickname` LIKE '%雪%' + */ + INNER_LIKE, + /** + * 右模糊查询,例如:WHERE `nickname` LIKE '雪%' + */ + RIGHT_LIKE, + /** + * 包含查询,例如:WHERE `age` IN (10, 20, 30) + */ + IN, + /** + * 不包含查询,例如:WHERE `age` NOT IN (20, 30) + */ + NOT_IN, + /** + * 空查询,例如:WHERE `email` IS NULL + */ + IS_NULL, + /** + * 非空查询,例如:WHERE `email` IS NOT NULL + */ + NOT_NULL,; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java index b2b7a103..5517c008 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/handler/GlobalExceptionHandler.java @@ -41,7 +41,7 @@ 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.OperationLog; +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; @@ -179,15 +179,15 @@ public class GlobalExceptionHandler { } /** - * 操作日志保存异常信息 + * 在系统日志上下文中保存异常信息 * * @param e * 异常信息 */ private void setException(Exception e) { - OperationLog operationLog = LogContextHolder.get(); - if (operationLog != null) { - operationLog.setException(e); + LogContext logContext = LogContextHolder.get(); + if (logContext != null) { + logContext.setException(e); } } } \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/OperationLog.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/LogContext.java similarity index 95% rename from continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/OperationLog.java rename to continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/LogContext.java index 1348024d..934d753e 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/OperationLog.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/dto/LogContext.java @@ -21,13 +21,13 @@ import java.time.LocalDateTime; import lombok.Data; /** - * 操作日志 + * 系统日志上下文 * * @author Charles7c * @since 2022/12/25 8:59 */ @Data -public class OperationLog { +public class LogContext { /** * 操作人 @@ -43,5 +43,4 @@ public class OperationLog { * 异常 */ private Exception exception; - } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/query/PageQuery.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/query/PageQuery.java new file mode 100644 index 00000000..61fed926 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/query/PageQuery.java @@ -0,0 +1,127 @@ +/* + * 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.common.model.query; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.data.domain.Sort; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; + +/** + * 分页查询条件 + * + * @author Charles7c + * @since 2023/1/15 10:59 + */ +@Data +@ParameterObject +@Schema(description = "分页查询条件") +public class PageQuery implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 页码 + */ + @Schema(description = "页码") + private int page; + + /** + * 每页记录数 + */ + @Schema(description = "每页记录数") + private int size; + + /** + * 排序条件 + */ + @Schema(description = "排序条件", example = "sort=published,desc&sort=title,asc") + private String[] sort; + + /** 默认页码:1 */ + private static final int DEFAULT_PAGE = 1; + + /** 默认每页记录数:int 最大值 */ + private static final int DEFAULT_SIZE = Integer.MAX_VALUE; + private static final String DELIMITER = ","; + + public PageQuery() { + this.page = DEFAULT_PAGE; + this.size = DEFAULT_SIZE; + } + + /** + * 解析排序条件为 Spring 分页排序实体 + * + * @return Spring 分页排序实体 + */ + public Sort getSort() { + if (ArrayUtil.isEmpty(sort)) { + return Sort.unsorted(); + } + + List orders = new ArrayList<>(sort.length); + if (sort[0].contains(DELIMITER)) { + // e.g "sort=published,desc&sort=title,asc" + for (String s : sort) { + String[] sortArr = s.split(DELIMITER); + Sort.Order order = new Sort.Order(Sort.Direction.valueOf(sortArr[1].toUpperCase()), sortArr[0]); + orders.add(order); + } + } else { + // e.g "sort=published,desc" + Sort.Order order = new Sort.Order(Sort.Direction.valueOf(sort[1].toUpperCase()), sort[0]); + orders.add(order); + } + return Sort.by(orders); + } + + /** + * 基于分页查询条件转换为 MyBatis Plus 分页条件 + * + * @param + * 列表数据类型 + * @return MyBatis Plus 分页条件 + */ + public IPage toPage() { + Page mybatisPage = new Page<>(this.getPage(), this.getSize()); + Sort pageSort = this.getSort(); + if (CollUtil.isNotEmpty(pageSort)) { + for (Sort.Order order : pageSort) { + OrderItem orderItem = new OrderItem(); + orderItem.setAsc(order.isAscending()); + orderItem.setColumn(StrUtil.toUnderlineCase(order.getProperty())); + mybatisPage.addOrder(orderItem); + } + } + return mybatisPage; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/PageInfo.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/PageInfo.java new file mode 100644 index 00000000..b8ae4f58 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/PageInfo.java @@ -0,0 +1,96 @@ +/* + * 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.common.model.vo; + +import java.util.List; + +import lombok.Data; +import lombok.experimental.Accessors; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.baomidou.mybatisplus.core.metadata.IPage; + +import cn.hutool.core.bean.BeanUtil; + +/** + * 分页信息 + * + * @param + * 列表数据类型 + * @author Charles7c + * @since 2023/1/14 23:40 + */ +@Data +@Accessors(chain = true) +@Schema(description = "分页信息") +public class PageInfo { + + /** + * 列表数据 + */ + @Schema(description = "列表数据") + private List list; + + /** + * 总记录数 + */ + @Schema(description = "总记录数") + private long total; + + /** + * 基于 MyBatis Plus 分页数据构建分页信息,并将源数据转换为指定类型数据 + * + * @param page + * MyBatis Plus 分页数据 + * @param targetClass + * 目标类型 Class 对象 + * @param + * 源列表数据类型 + * @param + * 目标列表数据类型 + * @return 分页信息 + */ + public static PageInfo build(IPage page, Class targetClass) { + if (page == null) { + return null; + } + PageInfo pageInfo = new PageInfo<>(); + pageInfo.setList(BeanUtil.copyToList(page.getRecords(), targetClass)); + pageInfo.setTotal(page.getTotal()); + return pageInfo; + } + + /** + * 基于 MyBatis Plus 分页数据构建分页信息 + * + * @param page + * MyBatis Plus 分页数据 + * @param + * 列表数据类型 + * @return 分页信息 + */ + public static PageInfo build(IPage page) { + if (page == null) { + return null; + } + PageInfo pageInfo = new PageInfo<>(); + pageInfo.setList(page.getRecords()); + pageInfo.setTotal(pageInfo.getTotal()); + return pageInfo; + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/R.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/R.java index 17199de5..d9287192 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/R.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/model/vo/R.java @@ -16,7 +16,6 @@ package top.charles7c.cnadmin.common.model.vo; -import java.io.Serializable; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -36,9 +35,7 @@ import org.springframework.http.HttpStatus; @Data @NoArgsConstructor(access = AccessLevel.PRIVATE) @Schema(description = "响应信息") -public class R implements Serializable { - - private static final long serialVersionUID = 1L; +public class R { /** 是否成功 */ @Schema(description = "是否成功") @@ -72,39 +69,39 @@ public class R implements Serializable { this.data = data; } - public static R ok() { + public static R ok() { return new R<>(true, SUCCESS_CODE, "操作成功", null); } - public static R ok(V data) { + public static R ok(V data) { return new R<>(true, SUCCESS_CODE, "操作成功", data); } - public static R ok(String msg) { + public static R ok(String msg) { return new R<>(true, SUCCESS_CODE, msg, null); } - public static R ok(String msg, V data) { + public static R ok(String msg, V data) { return new R<>(true, SUCCESS_CODE, msg, data); } - public static R fail() { + public static R fail() { return new R<>(false, FAIL_CODE, "操作失败", null); } - public static R fail(String msg) { + public static R fail(String msg) { return new R<>(false, FAIL_CODE, msg, null); } - public static R fail(V data) { + public static R fail(V data) { return new R<>(false, FAIL_CODE, "操作失败", data); } - public static R fail(String msg, V data) { + public static R fail(String msg, V data) { return new R<>(false, FAIL_CODE, msg, data); } - public static R fail(int code, String msg) { + public static R fail(int code, String msg) { return new R<>(false, code, msg, null); } } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ReflectUtils.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ReflectUtils.java new file mode 100644 index 00000000..285ddefc --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/ReflectUtils.java @@ -0,0 +1,66 @@ +/* + * 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.common.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import cn.hutool.core.util.ReflectUtil; + +/** + * 反射工具类 + * + * @author Charles7c + * @since 2023/1/15 22:05 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectUtils { + + /** + * 获得一个类中所有非静态字段名列表,包括其父类中的字段
+ * 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。 + * + * @param beanClass + * 类 + * @return 非静态字段名列表 + * @throws SecurityException + * 安全检查异常 + */ + public static String[] getNonStaticFieldsName(Class beanClass) throws SecurityException { + Field[] nonStaticFields = getNonStaticFields(beanClass); + return Arrays.stream(nonStaticFields).map(Field::getName).toArray(String[]::new); + } + + /** + * 获得一个类中所有非静态字段列表,包括其父类中的字段
+ * 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。 + * + * @param beanClass + * 类 + * @return 非静态字段列表 + * @throws SecurityException + * 安全检查异常 + */ + public static Field[] getNonStaticFields(Class beanClass) throws SecurityException { + Field[] fields = ReflectUtil.getFields(beanClass); + return Arrays.stream(fields).filter(f -> !Modifier.isStatic(f.getModifiers())).toArray(Field[]::new); + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/QueryHelper.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/QueryHelper.java new file mode 100644 index 00000000..16bde786 --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/QueryHelper.java @@ -0,0 +1,216 @@ +/* + * 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.common.util.helper; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; + +import top.charles7c.cnadmin.common.annotation.Query; + +/** + * 查询助手 + * + * @author Charles7c + * @since 2023/1/15 18:17 + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class QueryHelper { + + /** + * 根据查询条件构建 MyBatis Plus 查询条件封装对象 + * + * @param query + * 查询条件 + * @param + * 查询条件数据类型 + * @param + * 查询数据类型 + * @return MyBatis Plus 查询条件封装对象 + */ + public static QueryWrapper build(Q query) { + QueryWrapper queryWrapper = Wrappers.query(); + // 没有查询条件,直接返回 + if (query == null) { + return queryWrapper; + } + + // 获取查询条件中所有的属性(包括私有的和父类的) + List fieldList = getFieldList(query.getClass(), new ArrayList<>()); + fieldList.forEach(field -> buildQuery(query, field, queryWrapper)); + return queryWrapper; + } + + /** + * 获取指定类的所有属性(包括私有的和父类的) + * + * @param clazz + * 指定类 + * @param fieldList + * 属性列表 + * @param + * 查询条件数据类型 + * @return 属性列表(包括私有的和父类的) + */ + public static List getFieldList(Class clazz, List fieldList) { + if (clazz != null) { + fieldList.addAll(Arrays.asList(clazz.getDeclaredFields())); + getFieldList(clazz.getSuperclass(), fieldList); + } + return fieldList; + } + + /** + * 构建 MyBatis Plus 查询条件封装对象 + * + * @param query + * 查询条件 + * @param field + * 属性 + * @param queryWrapper + * MyBatis Plus 查询条件封装对象 + * @param + * 查询条件数据类型 + * @param + * 查询数据类型 + */ + private static void buildQuery(Q query, Field field, QueryWrapper queryWrapper) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + // 没有 @Query,直接返回 + Query queryAnnotation = field.getAnnotation(Query.class); + if (queryAnnotation == null) { + return; + } + + // 如果属性值为空,直接返回 + Object fieldValue = field.get(query); + if (ObjectUtil.isEmpty(fieldValue)) { + return; + } + + // 解析查询条件 + parse(queryAnnotation, field.getName(), fieldValue, queryWrapper); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + field.setAccessible(accessible); + } + } + + /** + * 解析查询条件 + * + * @param queryAnnotation + * 查询注解 + * @param fieldName + * 属性名 + * @param fieldValue + * 属性值 + * @param queryWrapper + * MyBatis Plus 查询条件封装对象 + * @param + * 查询数据类型 + */ + private static void parse(Query queryAnnotation, String fieldName, Object fieldValue, + QueryWrapper queryWrapper) { + // 解析多属性模糊查询 + // 如果设置了多属性模糊查询,分割属性进行条件拼接 + String blurry = queryAnnotation.blurry(); + if (StrUtil.isNotBlank(blurry)) { + String[] propertyArr = blurry.split(","); + queryWrapper.and(wrapper -> { + for (String property : propertyArr) { + wrapper.or().like(StrUtil.toUnderlineCase(property), fieldValue); + } + }); + return; + } + + // 解析单个属性查询 + // 如果没有单独指定属性名,就和使用该注解的属性的名称一致 + // 注意:数据库规范中列采用下划线连接法命名,程序规范中变量采用驼峰法命名 + String property = queryAnnotation.property(); + fieldName = StrUtil.isNotBlank(property) ? property : fieldName; + String columnName = StrUtil.toUnderlineCase(fieldName); + switch (queryAnnotation.type()) { + case EQUAL: + queryWrapper.eq(columnName, fieldValue); + break; + case NOT_EQUAL: + queryWrapper.ne(columnName, fieldValue); + break; + case GREATER_THAN: + queryWrapper.gt(columnName, fieldValue); + break; + case LESS_THAN: + queryWrapper.lt(columnName, fieldValue); + break; + case GREATER_THAN_OR_EQUAL: + queryWrapper.ge(columnName, fieldValue); + break; + case LESS_THAN_OR_EQUAL: + queryWrapper.le(columnName, fieldValue); + break; + case BETWEEN: + List between = new ArrayList<>((List)fieldValue); + queryWrapper.between(columnName, between.get(0), between.get(1)); + break; + case LEFT_LIKE: + queryWrapper.likeLeft(columnName, fieldValue); + break; + case INNER_LIKE: + queryWrapper.like(columnName, fieldValue); + break; + case RIGHT_LIKE: + queryWrapper.likeRight(columnName, fieldValue); + break; + case IN: + if (CollUtil.isNotEmpty((List)fieldValue)) { + queryWrapper.in(columnName, (List)fieldValue); + } + break; + case NOT_IN: + if (CollUtil.isNotEmpty((List)fieldValue)) { + queryWrapper.notIn(columnName, (List)fieldValue); + } + break; + case IS_NULL: + queryWrapper.isNull(columnName); + break; + case NOT_NULL: + queryWrapper.isNotNull(columnName); + break; + default: + break; + } + } +} diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/holder/LogContextHolder.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/holder/LogContextHolder.java index 78ae5960..6a13d012 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/holder/LogContextHolder.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/holder/LogContextHolder.java @@ -19,10 +19,10 @@ package top.charles7c.cnadmin.common.util.holder; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import top.charles7c.cnadmin.common.model.dto.OperationLog; +import top.charles7c.cnadmin.common.model.dto.LogContext; /** - * 操作日志上下文持有者 + * 系统日志上下文持有者 * * @author Charles7c * @since 2022/12/25 8:55 @@ -30,29 +30,29 @@ import top.charles7c.cnadmin.common.model.dto.OperationLog; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class LogContextHolder { - private static final ThreadLocal LOG_THREAD_LOCAL = new ThreadLocal<>(); + private static final ThreadLocal LOG_THREAD_LOCAL = new ThreadLocal<>(); /** - * 存储操作日志 + * 存储系统日志上下文 * - * @param operationLog - * 操作日志信息 + * @param logContext + * 系统日志上下文信息 */ - public static void set(OperationLog operationLog) { - LOG_THREAD_LOCAL.set(operationLog); + public static void set(LogContext logContext) { + LOG_THREAD_LOCAL.set(logContext); } /** - * 获取操作日志 + * 获取系统日志上下文 * - * @return 操作日志信息 + * @return 系统日志上下文信息 */ - public static OperationLog get() { + public static LogContext get() { return LOG_THREAD_LOCAL.get(); } /** - * 移除操作日志 + * 移除系统日志上下文 */ public static void remove() { LOG_THREAD_LOCAL.remove(); diff --git a/continew-admin-monitor/pom.xml b/continew-admin-monitor/pom.xml index 59cadaf8..7998c349 100644 --- a/continew-admin-monitor/pom.xml +++ b/continew-admin-monitor/pom.xml @@ -32,10 +32,10 @@ limitations under the License. 系统监控模块(存放系统监控模块相关功能,例如:日志管理、服务监控等) - + top.charles7c - continew-admin-common + continew-admin-system \ No newline at end of file diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/annotation/Log.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/annotation/Log.java index 0da9e8de..2f408bb8 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/annotation/Log.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/annotation/Log.java @@ -19,7 +19,7 @@ package top.charles7c.cnadmin.monitor.annotation; import java.lang.annotation.*; /** - * 操作日志注解(用于接口方法或类上) + * 系统日志注解(用于接口方法或类上) * * @author Charles7c * @since 2022/12/23 20:00 @@ -30,7 +30,7 @@ import java.lang.annotation.*; public @interface Log { /** - * 操作日志描述 + * 日志描述 */ String value() default ""; diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/config/properties/LogProperties.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/config/properties/LogProperties.java index c0221cc7..422a0ee9 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/config/properties/LogProperties.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/config/properties/LogProperties.java @@ -25,28 +25,33 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** - * 操作日志配置属性 + * 系统日志配置属性 * * @author Charles7c * @since 2022/12/24 23:04 */ @Data @Component -@ConfigurationProperties(prefix = "logging.operation") +@ConfigurationProperties(prefix = "logging.system") public class LogProperties { /** - * 是否启用操作日志 + * 是否启用系统日志 */ - private Boolean enabled = false; + private Boolean enabled; + + /** + * 是否记录内网 IP 操作 + */ + private Boolean includeInnerIp; + + /** + * 哪些请求方式不记录系统日志 + */ + private List excludeMethods = new ArrayList<>(); /** * 脱敏字段 */ private List desensitize = new ArrayList<>(); - - /** - * 不记录操作日志的请求方式 - */ - private List excludeMethods = new ArrayList<>(); } diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogLevelEnum.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogResultEnum.java similarity index 77% rename from continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogLevelEnum.java rename to continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogResultEnum.java index 0efbdc86..05c16221 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogLevelEnum.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/enums/LogResultEnum.java @@ -19,22 +19,24 @@ package top.charles7c.cnadmin.monitor.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; +import com.baomidou.mybatisplus.annotation.IEnum; + /** - * 操作日志级别枚举 + * 操作结果枚举 * * @author Charles7c * @since 2022/12/25 9:09 */ @Getter @RequiredArgsConstructor -public enum LogLevelEnum { +public enum LogResultEnum implements IEnum { - /** 普通 */ - INFO("普通"), + /** 成功 */ + SUCCESS(1, "成功"), - /** 错误 */ - ERROR("错误"),; + /** 失败 */ + FAILURE(2, "失败"),; - /** 描述 */ + private final Integer value; private final String description; } diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java index d777f27b..24845bce 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java @@ -42,20 +42,21 @@ import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONUtil; -import top.charles7c.cnadmin.common.model.dto.OperationLog; +import top.charles7c.cnadmin.common.model.dto.LogContext; import top.charles7c.cnadmin.common.util.IpUtils; import top.charles7c.cnadmin.common.util.ServletUtils; import top.charles7c.cnadmin.common.util.helper.LoginHelper; import top.charles7c.cnadmin.common.util.holder.LogContextHolder; import top.charles7c.cnadmin.monitor.annotation.Log; import top.charles7c.cnadmin.monitor.config.properties.LogProperties; -import top.charles7c.cnadmin.monitor.enums.LogLevelEnum; +import top.charles7c.cnadmin.monitor.enums.LogResultEnum; import top.charles7c.cnadmin.monitor.model.entity.SysLog; /** - * 操作日志拦截器 + * 系统日志拦截器 * * @author Charles7c * @since 2022/12/24 21:14 @@ -87,14 +88,14 @@ public class LogInterceptor implements HandlerInterceptor { return; } - // 记录描述 + // 记录日志描述 this.logDescription(sysLog, handler); // 记录请求信息 this.logRequest(sysLog, request); // 记录响应信息 this.logResponse(sysLog, response); - // 保存操作日志 + // 保存系统日志 SpringUtil.getApplicationContext().publishEvent(sysLog); } @@ -102,31 +103,31 @@ public class LogInterceptor implements HandlerInterceptor { * 记录操作时间 */ private void logCreateTime() { - OperationLog operationLog = new OperationLog(); - operationLog.setCreateUser(LoginHelper.getUserId()); - operationLog.setCreateTime(LocalDateTime.now()); - LogContextHolder.set(operationLog); + LogContext logContext = new LogContext(); + logContext.setCreateUser(LoginHelper.getUserId()); + logContext.setCreateTime(LocalDateTime.now()); + LogContextHolder.set(logContext); } /** * 记录请求耗时及异常信息 * - * @return 日志信息 + * @return 系统日志信息 */ private SysLog logElapsedTimeAndException() { - OperationLog operationLog = LogContextHolder.get(); - if (operationLog != null) { + LogContext logContext = LogContextHolder.get(); + if (logContext != null) { LogContextHolder.remove(); SysLog sysLog = new SysLog(); - sysLog.setCreateTime(operationLog.getCreateTime()); + sysLog.setCreateTime(logContext.getCreateTime()); sysLog.setElapsedTime(System.currentTimeMillis() - LocalDateTimeUtil.toEpochMilli(sysLog.getCreateTime())); - sysLog.setLogLevel(LogLevelEnum.INFO); + sysLog.setResult(LogResultEnum.SUCCESS); // 记录异常信息 - Exception exception = operationLog.getException(); + Exception exception = logContext.getException(); if (exception != null) { - sysLog.setLogLevel(LogLevelEnum.ERROR); - sysLog.setException(ExceptionUtil.stacktraceToString(operationLog.getException(), -1)); + sysLog.setResult(LogResultEnum.FAILURE); + sysLog.setException(ExceptionUtil.stacktraceToString(exception, -1)); } return sysLog; } @@ -137,7 +138,7 @@ public class LogInterceptor implements HandlerInterceptor { * 记录日志描述 * * @param sysLog - * 日志信息 + * 系统日志信息 * @param handler * 处理器 */ @@ -148,7 +149,7 @@ public class LogInterceptor implements HandlerInterceptor { if (methodOperation != null) { sysLog.setDescription( - StrUtil.isNotBlank(methodOperation.summary()) ? methodOperation.summary() : "请在该接口方法上指定操作日志描述"); + StrUtil.isNotBlank(methodOperation.summary()) ? methodOperation.summary() : "请在该接口方法上指定日志描述"); } // 例如:@Log("获取验证码") -> 获取验证码 if (methodLog != null && StrUtil.isNotBlank(methodLog.value())) { @@ -160,7 +161,7 @@ public class LogInterceptor implements HandlerInterceptor { * 记录请求信息 * * @param sysLog - * 日志信息 + * 系统日志信息 * @param request * 请求对象 */ @@ -184,18 +185,21 @@ public class LogInterceptor implements HandlerInterceptor { * 记录响应信息 * * @param sysLog - * 日志信息 + * 系统日志信息 * @param response * 响应对象 */ private void logResponse(SysLog sysLog, HttpServletResponse response) { - sysLog.setStatusCode(response.getStatus()); + int status = response.getStatus(); + sysLog.setStatusCode(status); sysLog.setResponseHeader(this.desensitize(ServletUtil.getHeadersMap(response))); // 响应体(不记录非 JSON 响应数据) String responseBody = this.getResponseBody(response); if (StrUtil.isNotBlank(responseBody) && JSONUtil.isTypeJSON(responseBody)) { sysLog.setResponseBody(responseBody); } + // 操作失败:>= 400 + sysLog.setResult(status >= HttpStatus.HTTP_BAD_REQUEST ? LogResultEnum.FAILURE : sysLog.getResult()); } /** @@ -258,7 +262,7 @@ public class LogInterceptor implements HandlerInterceptor { } /** - * 检查是否要记录操作日志 + * 检查是否要记录系统日志 * * @param handler * / @@ -267,28 +271,34 @@ public class LogInterceptor implements HandlerInterceptor { * @return true 需要记录,false 不需要记录 */ private boolean checkIsNeedRecord(Object handler, HttpServletRequest request) { - // 1、未启用时,不需要记录操作日志 + // 1、未启用时,不需要记录系统日志 if (!(handler instanceof HandlerMethod) || Boolean.FALSE.equals(operationLogProperties.getEnabled())) { return false; } - // 2、排除不需要记录日志的接口 + // 2、检查是否需要记录内网 IP 操作 + boolean isInnerIp = IpUtils.isInnerIP(ServletUtil.getClientIP(request)); + if (isInnerIp && Boolean.FALSE.equals(operationLogProperties.getIncludeInnerIp())) { + return false; + } + + // 3、排除不需要记录系统日志的接口 HandlerMethod handlerMethod = (HandlerMethod)handler; Log methodLog = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Log.class); - // 2.1 请求方式不要求记录且请求上没有 @Log 注解,则不记录操作日志 + // 3.1 请求方式不要求记录且请求上没有 @Log 注解,则不记录系统日志 if (operationLogProperties.getExcludeMethods().contains(request.getMethod()) && methodLog == null) { return false; } - // 2.2 如果接口上既没有 @Log 注解,也没有 @Operation 注解,则不记录操作日志 + // 3.2 如果接口上既没有 @Log 注解,也没有 @Operation 注解,则不记录系统日志 Operation methodOperation = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Operation.class); if (methodLog == null && methodOperation == null) { return false; } - // 2.3 如果接口被隐藏,不记录操作日志 + // 3.3 如果接口被隐藏,不记录系统日志 if (methodOperation != null && methodOperation.hidden()) { return false; } - // 2.4 如果接口上有 @Log 注解,但是要求忽略该接口,则不记录操作日志 + // 3.4 如果接口上有 @Log 注解,但是要求忽略该接口,则不记录系统日志 return methodLog == null || !methodLog.ignore(); } } diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/mapper/LogMapper.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/mapper/LogMapper.java index da1db18a..520fe464 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/mapper/LogMapper.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/mapper/LogMapper.java @@ -21,7 +21,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import top.charles7c.cnadmin.monitor.model.entity.SysLog; /** - * 操作日志 Mapper + * 系统日志 Mapper * * @author Charles7c * @since 2022/12/22 21:47 diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/entity/SysLog.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/entity/SysLog.java index 24945bd3..8bd0538f 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/entity/SysLog.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/entity/SysLog.java @@ -24,10 +24,10 @@ import lombok.Data; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import top.charles7c.cnadmin.monitor.enums.LogLevelEnum; +import top.charles7c.cnadmin.monitor.enums.LogResultEnum; /** - * 操作日志实体 + * 系统日志实体 * * @author Charles7c * @since 2022/12/25 9:11 @@ -44,18 +44,13 @@ public class SysLog implements Serializable { @TableId private Long logId; - /** - * 日志级别 - */ - private LogLevelEnum logLevel; - /** * 日志描述 */ private String description; /** - * 请求 URL + * 请求URL */ private String requestUrl; @@ -95,12 +90,17 @@ public class SysLog implements Serializable { private Long elapsedTime; /** - * 请求IP + * 操作结果(1成功 2失败) + */ + private LogResultEnum result; + + /** + * 操作IP */ private String requestIp; /** - * 操作地址 + * 操作地点 */ private String location; diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OperationLogQuery.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OperationLogQuery.java new file mode 100644 index 00000000..984849d9 --- /dev/null +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/query/OperationLogQuery.java @@ -0,0 +1,50 @@ +/* + * 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 static top.charles7c.cnadmin.common.annotation.Query.Type; + +import java.io.Serializable; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import org.springdoc.api.annotations.ParameterObject; + +import top.charles7c.cnadmin.common.annotation.Query; + +/** + * 操作日志查询条件 + * + * @author Charles7c + * @since 2023/1/15 11:43 + */ +@Data +@ParameterObject +@Schema(description = "操作日志查询条件") +public class OperationLogQuery implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 操作人 + */ + @Schema(description = "操作人") + @Query(property = "createUser", type = Type.EQUAL) + private Long uid; +} diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OperationLogVO.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OperationLogVO.java new file mode 100644 index 00000000..2eb6f847 --- /dev/null +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/model/vo/OperationLogVO.java @@ -0,0 +1,95 @@ +/* + * 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 com.fasterxml.jackson.annotation.JsonIgnore; + +import top.charles7c.cnadmin.monitor.enums.LogResultEnum; + +/** + * 操作日志信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@Schema(description = "操作日志信息") +public class OperationLogVO 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 LogResultEnum result; + + /** + * 操作IP + */ + @Schema(description = "操作IP") + private String requestIp; + + /** + * 操作地点 + */ + @Schema(description = "操作地点") + private String location; + + /** + * 浏览器 + */ + @Schema(description = "浏览器") + private String browser; + + /** + * 操作人 + */ + @JsonIgnore + private Long createUser; + + /** + * 操作人 + */ + @Schema(description = "操作人") + private String createUserString; + + /** + * 操作时间 + */ + @Schema(description = "操作时间") + private LocalDateTime createTime; +} diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/LogService.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/LogService.java index 5c522c8d..834508b7 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/LogService.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/LogService.java @@ -17,7 +17,7 @@ package top.charles7c.cnadmin.monitor.service; /** - * 操作日志业务接口 + * 系统日志业务接口 * * @author Charles7c * @since 2022/12/23 20:12 diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/OperationLogService.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/OperationLogService.java new file mode 100644 index 00000000..222a096f --- /dev/null +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/OperationLogService.java @@ -0,0 +1,42 @@ +/* + * 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; + +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; + +/** + * 操作日志业务接口 + * + * @author Charles7c + * @since 2023/1/15 21:05 + */ +public interface OperationLogService { + + /** + * 分页查询列表 + * + * @param query + * 查询条件 + * @param pageQuery + * 分页查询条件 + * @return 分页信息 + */ + PageInfo list(OperationLogQuery query, PageQuery pageQuery); +} diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/LogServiceImpl.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/LogServiceImpl.java index fabb8220..059aa4c2 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/LogServiceImpl.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/LogServiceImpl.java @@ -28,7 +28,7 @@ import top.charles7c.cnadmin.monitor.model.entity.SysLog; import top.charles7c.cnadmin.monitor.service.LogService; /** - * 操作日志业务实现类 + * 系统日志业务实现类 * * @author Charles7c * @since 2022/12/23 20:12 diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/OperationLogServiceImpl.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/OperationLogServiceImpl.java new file mode 100644 index 00000000..5bd02c91 --- /dev/null +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/service/impl/OperationLogServiceImpl.java @@ -0,0 +1,90 @@ +/* + * 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 list(OperationLogQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryHelper.build(query); + + // 限定查询信息 + String[] fieldsName = ReflectUtils.getNonStaticFieldsName(OperationLogVO.class); + List columns = Arrays.stream(fieldsName).map(StrUtil::toUnderlineCase) + .filter(n -> !n.endsWith("string")).collect(Collectors.toList()); + queryWrapper.select(columns); + + // 分页查询 + IPage page = logMapper.selectPage(pageQuery.toPage(), queryWrapper); + PageInfo pageInfo = PageInfo.build(page, OperationLogVO.class); + 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.getNickname()); + } +} diff --git a/continew-admin-ui/src/api/monitor/operation-log.ts b/continew-admin-ui/src/api/monitor/operation-log.ts new file mode 100644 index 00000000..bbfc31a9 --- /dev/null +++ b/continew-admin-ui/src/api/monitor/operation-log.ts @@ -0,0 +1,34 @@ +import axios from 'axios'; +import qs from 'query-string'; + +export interface OperationLogRecord { + logId: string; + description: string; + result: number, + requestIp: string, + location: string, + browser: string, + createUserString: string; + createTime: string; +} + +export interface OperationLogParams extends Partial { + page: number; + size: number; + sort: Array; + uid: string; +} + +export interface OperationLogListRes { + list: OperationLogRecord[]; + total: number; +} + +export function queryOperationLogList(params: OperationLogParams) { + return axios.get('/monitor/log/operation', { + params, + paramsSerializer: (obj) => { + return qs.stringify(obj); + }, + }); +} \ No newline at end of file diff --git a/continew-admin-ui/src/assets/style/global.less b/continew-admin-ui/src/assets/style/global.less index f90b206f..b6b80fff 100644 --- a/continew-admin-ui/src/assets/style/global.less +++ b/continew-admin-ui/src/assets/style/global.less @@ -90,5 +90,8 @@ body { &.pass { background-color: rgb(var(--green-6)); } + &.fail { + background-color: rgb(var(--red-6)); + } } } diff --git a/continew-admin-ui/src/store/modules/login/index.ts b/continew-admin-ui/src/store/modules/login/index.ts index 41c77d0c..9de81a76 100644 --- a/continew-admin-ui/src/store/modules/login/index.ts +++ b/continew-admin-ui/src/store/modules/login/index.ts @@ -13,7 +13,7 @@ import useAppStore from '../app'; const useLoginStore = defineStore('user', { state: (): UserState => ({ - userId: 1, + userId: '', username: '', nickname: '', gender: 0, diff --git a/continew-admin-ui/src/store/modules/login/types.ts b/continew-admin-ui/src/store/modules/login/types.ts index 69fb4bbf..2320d167 100644 --- a/continew-admin-ui/src/store/modules/login/types.ts +++ b/continew-admin-ui/src/store/modules/login/types.ts @@ -1,6 +1,6 @@ export type RoleType = '' | '*' | 'admin' | 'user'; export interface UserState { - userId: number; + userId: string; username: string; nickname: string; gender: number; diff --git a/continew-admin-ui/src/views/login/components/login-form.vue b/continew-admin-ui/src/views/login/components/login-form.vue index ab1899be..0150e589 100644 --- a/continew-admin-ui/src/views/login/components/login-form.vue +++ b/continew-admin-ui/src/views/login/components/login-form.vue @@ -133,7 +133,7 @@ captchaImgBase64.value = data.img } onMounted(() => { - getCaptcha() + getCaptcha(); }) // 记住我 diff --git a/continew-admin-ui/src/views/system/user/center/components/operation-log.vue b/continew-admin-ui/src/views/system/user/center/components/operation-log.vue new file mode 100644 index 00000000..61e4c8eb --- /dev/null +++ b/continew-admin-ui/src/views/system/user/center/components/operation-log.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/continew-admin-ui/src/views/system/user/center/index.vue b/continew-admin-ui/src/views/system/user/center/index.vue index e2b39b43..d560ddb4 100644 --- a/continew-admin-ui/src/views/system/user/center/index.vue +++ b/continew-admin-ui/src/views/system/user/center/index.vue @@ -15,6 +15,9 @@ + + + @@ -25,6 +28,7 @@ import UserPanel from './components/user-panel.vue'; import BasicInfo from './components/basic-info.vue'; import SecuritySettings from './components/security-settings.vue'; + import OperationLog from './components/operation-log.vue';