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