Преглед на файлове

开发QueryBuilder 基于mybatics_plus的通用查询工具类
开发律师场景的通用查询

LuTong преди 1 месец
родител
ревизия
4e42c708bd

+ 531 - 0
QueryBuilder使用示例.md

@@ -0,0 +1,531 @@
+# QueryBuilder 通用查询工具类使用指南
+
+## 一、功能特点
+
+- ✅ **自动构建查询条件**:根据对象非空字段自动生成查询条件
+- ✅ **支持分页查询**:内置分页功能
+- ✅ **支持模糊查询**:可指定字段或全局设置String字段模糊查询
+- ✅ **支持批量查询**:集合类型字段自动使用IN查询
+- ✅ **支持范围查询**:识别Start/End后缀字段
+- ✅ **链式调用**:流畅的API设计
+- ✅ **类型安全**:自动处理序列化问题,避免类型转换错误
+- ✅ **字段过滤**:自动跳过静态字段、transient字段、不存在字段
+
+## 二、基本使用
+
+### 1. 简单查询
+
+```java
+// Controller中
+@GetMapping("/getList")
+public R<List<LawyerUser>> getList(@ModelAttribute LawyerUser query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/getList?name=张三&status=1
+```
+
+**说明:**
+- 所有非空字段都会作为等值查询条件(`=`)
+- String类型字段默认使用等值查询
+
+### 2. 分页查询
+
+```java
+@GetMapping("/getPage")
+public R<IPage<LawyerUser>> getPage(
+    @ModelAttribute LawyerUser query,
+    @RequestParam(defaultValue = "1") int pageNum,
+    @RequestParam(defaultValue = "10") int pageSize) {
+    
+    IPage<LawyerUser> page = QueryBuilder.of(query)
+        .page(pageNum, pageSize)  // 设置分页
+        .build()
+        .page(lawyerUserService);  // 执行分页查询
+    
+    return R.data(page);
+}
+```
+
+### 3. 模糊查询
+
+#### 方式一:指定字段模糊查询(推荐)
+
+```java
+@GetMapping("/search")
+public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .likeFields("name", "phone")  // 指定 name 和 phone 字段使用模糊查询
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/search?name=张&phone=138
+```
+
+**生成的SQL:**
+```sql
+WHERE name LIKE '%张%' AND phone LIKE '%138%'
+```
+
+#### 方式二:全局String字段模糊查询
+
+```java
+@GetMapping("/search")
+public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .stringFieldLike(true)  // 所有String类型字段都使用模糊查询
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+#### 方式三:使用Like后缀字段(自动识别)
+
+在实体类中添加Like后缀字段(仅用于查询,不需要对应数据库字段):
+
+```java
+public class LawyerUserQuery extends LawyerUser {
+    private String nameLike;  // 模糊查询姓名
+    private String phoneLike;  // 模糊查询手机号
+}
+```
+
+```java
+// Controller中
+@GetMapping("/search")
+public R<List<LawyerUser>> search(@ModelAttribute LawyerUserQuery query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/search?nameLike=张&phoneLike=138
+```
+
+### 4. 批量查询(IN查询)
+
+在实体类中添加List后缀字段:
+
+```java
+public class LawyerUserQuery extends LawyerUser {
+    private List<Integer> idList;  // 批量查询ID
+    private List<String> statusList;  // 批量查询状态
+}
+```
+
+```java
+// Controller中
+@GetMapping("/getByIds")
+public R<List<LawyerUser>> getByIds(@ModelAttribute LawyerUserQuery query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/getByIds?idList=1&idList=2&idList=3
+```
+
+**生成的SQL:**
+```sql
+WHERE id IN (1, 2, 3)
+```
+
+### 5. 范围查询
+
+在实体类中添加Start/End后缀字段:
+
+```java
+public class LawyerUserQuery extends LawyerUser {
+    private Date createdTimeStart;  // 创建时间开始
+    private Date createdTimeEnd;    // 创建时间结束
+    private Integer serviceCountStart;  // 服务次数范围开始
+    private Integer serviceCountEnd;    // 服务次数范围结束
+}
+```
+
+```java
+// Controller中
+@GetMapping("/getByTimeRange")
+public R<List<LawyerUser>> getByTimeRange(@ModelAttribute LawyerUserQuery query) {
+    List<LawyerUser> list = QueryBuilder.of(query)
+        .build()
+        .list(lawyerUserService);
+    return R.data(list);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/getByTimeRange?createdTimeStart=2025-01-01&createdTimeEnd=2025-01-31
+```
+
+**生成的SQL:**
+```sql
+WHERE created_time >= '2025-01-01' AND created_time <= '2025-01-31'
+```
+
+## 三、组合查询示例
+
+### 复杂查询场景
+
+```java
+@GetMapping("/complexQuery")
+public R<IPage<LawyerUser>> complexQuery(
+    @ModelAttribute LawyerUserQuery query,
+    @RequestParam(defaultValue = "1") int pageNum,
+    @RequestParam(defaultValue = "10") int pageSize) {
+    
+    IPage<LawyerUser> page = QueryBuilder.of(query)
+        .page(pageNum, pageSize)           // 分页
+        .likeFields("name", "phone")        // 指定字段模糊查询
+        .ignoreEmptyStr(true)               // 忽略空字符串(默认true)
+        .build()
+        .page(lawyerUserService);
+    
+    return R.data(page);
+}
+```
+
+**请求示例:**
+```
+GET /lawyer/user/complexQuery?name=张&phone=138&status=1&createdTimeStart=2025-01-01&createdTimeEnd=2025-01-31&pageNum=1&pageSize=10
+```
+
+**生成的SQL:**
+```sql
+WHERE name LIKE '%张%' 
+  AND phone LIKE '%138%'
+  AND status = 1
+  AND created_time >= '2025-01-01'
+  AND created_time <= '2025-01-31'
+LIMIT 10 OFFSET 0
+```
+
+## 四、API方法说明
+
+### QueryBuilder 方法
+
+#### 1. `of(T queryEntity)` - 创建查询构建器
+```java
+QueryBuilder.of(queryEntity)
+```
+
+#### 2. `ignoreEmptyStr(boolean ignore)` - 设置是否忽略空字符串
+```java
+.ignoreEmptyStr(true)   // 默认true,忽略空字符串
+.ignoreEmptyStr(false)  // 保留空字符串作为查询条件
+```
+
+#### 3. `stringFieldLike(boolean like)` - 设置String字段是否默认模糊查询
+```java
+.stringFieldLike(true)   // 所有String字段使用模糊查询
+.stringFieldLike(false)  // 所有String字段使用等值查询(默认)
+```
+
+#### 4. `likeFields(String... fieldNames)` - 指定字段使用模糊查询
+```java
+.likeFields("name")              // 单个字段
+.likeFields("name", "phone")     // 多个字段
+```
+
+#### 5. `page(int pageNum, int pageSize)` - 设置分页参数
+```java
+.page(1, 10)  // 第1页,每页10条
+```
+
+#### 6. `page(Page<T> page)` - 设置分页对象
+```java
+Page<LawyerUser> pageObj = new Page<>(1, 10);
+.page(pageObj)
+```
+
+#### 7. `build()` - 构建查询条件
+```java
+.build()  // 返回 QueryResult 对象
+```
+
+### QueryResult 方法
+
+#### 1. `list(IService<T> service)` - 列表查询
+```java
+List<LawyerUser> list = builder.build().list(lawyerUserService);
+```
+
+#### 2. `page(IService<T> service)` - 分页查询
+```java
+IPage<LawyerUser> page = builder.page(1, 10).build().page(lawyerUserService);
+```
+
+#### 3. `one(IService<T> service)` - 单条查询
+```java
+LawyerUser user = builder.build().one(lawyerUserService);
+```
+
+#### 4. `count(IService<T> service)` - 计数查询
+```java
+long count = builder.build().count(lawyerUserService);
+```
+
+#### 5. `getWrapper()` - 获取查询条件(用于调试)
+```java
+QueryWrapper<LawyerUser> wrapper = builder.build().getWrapper();
+System.out.println(wrapper.getTargetSql());  // 打印SQL
+```
+
+## 五、命名规则
+
+### 1. 模糊查询字段
+
+**方式一:使用 likeFields 方法指定**
+```java
+.likeFields("name", "phone")  // 在代码中指定
+```
+
+**方式二:字段名以 `Like` 结尾**
+- 字段名以 `Like` 结尾
+- 示例:`nameLike` → 查询 `name` 字段,使用 `LIKE '%value%'`
+
+### 2. 批量查询字段
+- 字段类型为 `Collection`(List、Set等)
+- 字段名以 `List` 结尾(推荐)
+- 示例:`idList` → 查询 `id` 字段,使用 `IN (1,2,3)`
+
+### 3. 范围查询字段
+- 字段名以 `Start` 或 `End` 结尾
+- 成对出现:`xxxStart` 和 `xxxEnd`
+- 示例:
+  - `createdTimeStart` → `createdTime >= value`
+  - `createdTimeEnd` → `createdTime <= value`
+
+## 六、查询优先级
+
+### 模糊查询优先级
+
+1. **最高优先级**:`likeFields()` 方法指定的字段
+2. **次优先级**:字段名以 `Like` 结尾的字段
+3. **全局设置**:`stringFieldLike(true)` 时,所有String字段使用模糊查询
+4. **默认**:等值查询(`=`)
+
+### 示例
+
+```java
+QueryBuilder.of(query)
+    .stringFieldLike(true)      // 全局:所有String字段模糊查询
+    .likeFields("name")         // 指定:name字段模糊查询(优先级更高)
+    .build()
+```
+
+**结果:**
+- `name` 字段:使用模糊查询(`likeFields` 优先级更高)
+- 其他String字段:使用模糊查询(`stringFieldLike` 生效)
+- 非String字段:使用等值查询
+
+## 七、完整示例
+
+### 实体类定义
+
+```java
+@Data
+public class LawyerUserQuery extends LawyerUser {
+    // 模糊查询字段(方式一:在代码中指定,不需要在实体类中定义)
+    // 方式二:使用Like后缀字段
+    private String nameLike;
+    private String phoneLike;
+    
+    // 批量查询字段
+    private List<Integer> idList;
+    private List<Integer> statusList;
+    
+    // 范围查询字段
+    private Date createdTimeStart;
+    private Date createdTimeEnd;
+    private Integer serviceCountStart;
+    private Integer serviceCountEnd;
+}
+```
+
+### Controller 完整示例
+
+```java
+@RestController
+@RequestMapping("/lawyer/user")
+@RequiredArgsConstructor
+public class LawyerUserController {
+    
+    private final LawyerUserService lawyerUserService;
+    
+    /**
+     * 通用查询接口(支持所有查询方式)
+     */
+    @GetMapping("/query")
+    public R<IPage<LawyerUser>> query(
+        @ModelAttribute LawyerUserQuery query,
+        @RequestParam(defaultValue = "1") int pageNum,
+        @RequestParam(defaultValue = "10") int pageSize) {
+        
+        IPage<LawyerUser> page = QueryBuilder.of(query)
+            .page(pageNum, pageSize)
+            .likeFields("name", "phone")  // 指定字段模糊查询
+            .build()
+            .page(lawyerUserService);
+        
+        return R.data(page);
+    }
+    
+    /**
+     * 简单查询(只使用等值查询)
+     */
+    @GetMapping("/simple")
+    public R<List<LawyerUser>> simple(@ModelAttribute LawyerUser query) {
+        List<LawyerUser> list = QueryBuilder.of(query)
+            .build()
+            .list(lawyerUserService);
+        return R.data(list);
+    }
+    
+    /**
+     * 全局模糊查询(所有String字段都模糊查询)
+     */
+    @GetMapping("/search")
+    public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
+        List<LawyerUser> list = QueryBuilder.of(query)
+            .stringFieldLike(true)  // 所有String字段模糊查询
+            .build()
+            .list(lawyerUserService);
+        return R.data(list);
+    }
+}
+```
+
+## 八、注意事项
+
+### 1. 字段过滤规则
+
+- **自动跳过**:静态字段(`static`)、transient字段(`transient`)
+- **自动跳过**:`@TableField(exist = false)` 标记的字段
+- **自动跳过**:空值(`null`)、空字符串(如果启用 `ignoreEmptyStr`)
+- **自动跳过**:空集合
+
+### 2. 字段名映射
+
+- **优先使用**:`@TableField` 注解的 `value` 属性
+- **默认规则**:驼峰命名转下划线命名(`userName` → `user_name`)
+
+### 3. 序列化安全
+
+- 自动过滤不可序列化的对象
+- 只支持基本类型:Number、String、Boolean、Date等
+- 集合元素会自动过滤不可序列化的项
+
+### 4. 性能考虑
+
+- 批量查询(IN)建议限制集合大小,避免SQL过长
+- 模糊查询(LIKE)在大数据量时可能较慢,建议添加索引
+
+### 5. 日期处理
+
+- 日期字段会自动处理空字符串绑定问题
+- 支持 `java.util.Date`、`java.sql.Date`、`LocalDate`、`LocalDateTime`
+
+## 九、常见问题
+
+### Q1: 如何让某个String字段使用等值查询,其他字段使用模糊查询?
+
+```java
+// 方式一:使用全局模糊查询,然后排除特定字段
+QueryBuilder.of(query)
+    .stringFieldLike(true)  // 全局模糊查询
+    // 注意:没有单独排除的方法,可以使用Like后缀字段的方式
+    
+// 方式二:只指定需要模糊查询的字段
+QueryBuilder.of(query)
+    .likeFields("name", "phone")  // 只指定这些字段模糊查询
+    .build()
+```
+
+### Q2: 如何同时使用等值查询和模糊查询?
+
+```java
+// 在实体类中定义两个字段
+public class LawyerUserQuery extends LawyerUser {
+    private String name;        // 等值查询
+    private String nameLike;    // 模糊查询
+}
+
+// 使用时
+QueryBuilder.of(query)
+    .build()  // name使用等值查询,nameLike使用模糊查询
+```
+
+### Q3: 如何查询空值?
+
+```java
+QueryBuilder.of(query)
+    .ignoreEmptyStr(false)  // 不忽略空字符串
+    .build()
+```
+
+### Q4: 如何调试生成的SQL?
+
+```java
+QueryResult<LawyerUser> result = QueryBuilder.of(query).build();
+QueryWrapper<LawyerUser> wrapper = result.getWrapper();
+System.out.println(wrapper.getTargetSql());  // 打印SQL语句
+```
+
+## 十、最佳实践
+
+### 1. 推荐使用方式
+
+```java
+// ✅ 推荐:指定字段模糊查询
+QueryBuilder.of(query)
+    .likeFields("name", "phone")
+    .build()
+    .list(service);
+
+// ✅ 推荐:使用Like后缀字段(更灵活)
+// 在实体类中定义 nameLike 字段
+QueryBuilder.of(query)
+    .build()
+    .list(service);
+```
+
+### 2. 不推荐使用方式
+
+```java
+// ❌ 不推荐:全局String字段模糊查询(可能影响不需要模糊的字段)
+QueryBuilder.of(query)
+    .stringFieldLike(true)  // 除非你真的需要所有String字段都模糊
+    .build()
+```
+
+### 3. 性能优化建议
+
+- 模糊查询字段建议添加数据库索引
+- 批量查询(IN)建议限制集合大小(如:最多1000个)
+- 复杂查询建议使用分页,避免一次性查询大量数据
+
+---
+
+**总结**:`QueryBuilder` 提供了强大而灵活的查询能力,只需要传入对象,就能自动构建各种查询条件,大大简化了开发工作。通过合理使用 `likeFields()` 和 `stringFieldLike()` 方法,可以灵活控制模糊查询行为。

+ 5 - 0
alien-entity/src/main/java/shop/alien/entity/store/LawyerLegalProblemScenario.java

@@ -34,6 +34,10 @@ public class LawyerLegalProblemScenario extends Model<LawyerLegalProblemScenario
     @TableField("name")
     private String name;
 
+    @ApiModelProperty(value = "问题场景层级")
+    @TableField("level")
+    private Integer level;
+
     @ApiModelProperty(value = "父类主键")
     @TableField("parent_id")
     private Integer parentId;
@@ -76,5 +80,6 @@ public class LawyerLegalProblemScenario extends Model<LawyerLegalProblemScenario
     @ApiModelProperty(value = "修改人ID")
     @TableField("updated_user_id")
     private Integer updatedUserId;
+
 }
 

+ 14 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerLegalProblemScenarioController.java

@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerLegalProblemScenario;
 import shop.alien.store.service.LawyerLegalProblemScenarioService;
+import shop.alien.util.LambdaQueryHelper.QueryBuilder;
 
 import java.util.List;
 
@@ -118,5 +119,18 @@ public class LawyerLegalProblemScenarioController {
         return R.fail("操作失败");
     }
 
+    @ApiOperation("通用查询")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/getList")
+    public R<List<LawyerLegalProblemScenario>> getList(@ModelAttribute LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioController.getList?lawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        // 使用 QueryBuilder 工具类进行查询,指定 name 字段使用模糊查询
+        List<LawyerLegalProblemScenario> list = QueryBuilder.of(lawyerLegalProblemScenario)
+                .likeFields("name")  // 指定 name 字段使用模糊查询,其他字段使用等值查询
+                .build()
+                .list(lawyerLegalProblemScenarioService);
+        return R.data(list);
+    }
+
 }
 

+ 1 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerLegalProblemScenarioService.java

@@ -68,5 +68,6 @@ public interface LawyerLegalProblemScenarioService extends IService<LawyerLegalP
     R<Boolean> deleteLawyerLegalProblemScenario(Integer id);
 
 
+    List<LawyerLegalProblemScenario> getListByLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario);
 }
 

+ 5 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerLegalProblemScenarioServiceImpl.java

@@ -111,5 +111,10 @@ public class LawyerLegalProblemScenarioServiceImpl extends ServiceImpl<LawyerLeg
         return R.fail("删除失败");
     }
 
+    @Override
+    public List<LawyerLegalProblemScenario> getListByLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        return lawyerLegalProblemScenarioMapper.selectList(null);
+    }
+
 }
 

+ 625 - 0
alien-util/src/main/java/shop/alien/util/LambdaQueryHelper/QueryBuilder.java

@@ -0,0 +1,625 @@
+package shop.alien.util.LambdaQueryHelper;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 通用查询构建器
+ * <p>
+ * 功能:根据对象非空字段自动构建查询条件,支持分页、模糊、批量等多种查询方式
+ * <p>
+ * 使用示例:
+ * <pre>{@code
+ * // 1. 简单查询
+ * QueryBuilder.of(query).build().list(service);
+ * 
+ * // 2. 分页查询
+ * QueryBuilder.of(query).page(1, 10).build().page(service);
+ * 
+ * // 3. 批量查询(字段名以List结尾,如:idList)
+ * QueryBuilder.of(query).build().list(service);
+ * 
+ * // 4. 范围查询(字段名以Start/End结尾,如:createdTimeStart, createdTimeEnd)
+ * QueryBuilder.of(query).build().list(service);
+ * 
+ * // 5. 模糊查询(字段名以Like结尾,如:nameLike)
+ * QueryBuilder.of(query).build().list(service);
+ * }</pre>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public class QueryBuilder<T> {
+
+    // ==================== 常量定义 ====================
+    
+    /** 特殊查询字段后缀 */
+    private static final String SUFFIX_LIST = "List";
+    private static final String SUFFIX_START = "Start";
+    private static final String SUFFIX_END = "End";
+    private static final String SUFFIX_LIKE = "Like";
+    
+    /** 模糊查询通配符 */
+    private static final String LIKE_WILDCARD = "%";
+    
+    /** 模糊查询模式枚举 */
+    public enum LikeMode {
+        /** 前后模糊:%value% */
+        BOTH,
+        /** 前模糊:%value */
+        LEFT,
+        /** 后模糊:value% */
+        RIGHT
+    }
+
+    // ==================== 成员变量 ====================
+    
+    private final T queryEntity;
+    private final QueryWrapper<T> wrapper;
+    private boolean ignoreEmptyStr = true;
+    private boolean stringFieldLike = false;  // String类型字段是否默认使用模糊查询
+    private final List<String> likeFields = new ArrayList<>();  // 指定哪些字段使用模糊查询
+    private LikeMode likeMode = LikeMode.BOTH;  // 模糊查询模式(默认前后模糊)
+    private Page<T> page;
+
+    // ==================== 构造函数 ====================
+    
+    private QueryBuilder(T queryEntity) {
+        this.queryEntity = queryEntity;
+        this.wrapper = new QueryWrapper<>();
+    }
+
+    // ==================== 公开API ====================
+    
+    /**
+     * 创建查询构建器
+     */
+    public static <T> QueryBuilder<T> of(T queryEntity) {
+        return new QueryBuilder<>(queryEntity);
+    }
+
+    /**
+     * 设置是否忽略空字符串(默认true)
+     */
+    public QueryBuilder<T> ignoreEmptyStr(boolean ignore) {
+        this.ignoreEmptyStr = ignore;
+        return this;
+    }
+
+    /**
+     * 设置String类型字段是否默认使用模糊查询(默认false,即等值查询)
+     */
+    public QueryBuilder<T> stringFieldLike(boolean like) {
+        this.stringFieldLike = like;
+        return this;
+    }
+
+    /**
+     * 指定哪些字段使用模糊查询(可指定多个字段)
+     * 例如:.likeFields("name", "code") 表示 name 和 code 字段使用模糊查询
+     */
+    public QueryBuilder<T> likeFields(String... fieldNames) {
+        if (fieldNames != null) {
+            for (String fieldName : fieldNames) {
+                if (fieldName != null && !fieldName.trim().isEmpty()) {
+                    likeFields.add(fieldName.trim());
+                }
+            }
+        }
+        return this;
+    }
+
+    /**
+     * 设置模糊查询模式(默认前后模糊:%value%)
+     * 
+     * @param mode 模糊查询模式
+     *   - BOTH: 前后模糊 %value%(默认)
+     *   - LEFT: 前模糊 %value
+     *   - RIGHT: 后模糊 value%
+     * 
+     * 使用示例:
+     * <pre>{@code
+     * QueryBuilder.of(query)
+     *     .likeFields("name")
+     *     .likeMode(LikeMode.LEFT)  // 前模糊查询
+     *     .build()
+     * }</pre>
+     */
+    public QueryBuilder<T> likeMode(LikeMode mode) {
+        if (mode != null) {
+            this.likeMode = mode;
+        }
+        return this;
+    }
+
+    /**
+     * 设置分页参数
+     */
+    public QueryBuilder<T> page(int pageNum, int pageSize) {
+        this.page = new Page<>(pageNum, pageSize);
+        return this;
+    }
+
+    /**
+     * 设置分页对象
+     */
+    public QueryBuilder<T> page(Page<T> page) {
+        this.page = page;
+        return this;
+    }
+
+    /**
+     * 构建查询条件
+     */
+    public QueryResult<T> build() {
+        buildBaseQueryConditions();
+        processSpecialQueries();
+        return new QueryResult<>(this.wrapper, this.page);
+    }
+
+    // ==================== 核心构建逻辑 ====================
+    
+    /**
+     * 构建基础查询条件(等值查询)
+     */
+    private void buildBaseQueryConditions() {
+        if (queryEntity == null) {
+            return;
+        }
+
+        List<Field> allFields = getAllFields(queryEntity.getClass());
+
+        for (Field field : allFields) {
+            if (shouldSkipField(field)) {
+                continue;
+            }
+
+            Object value = getFieldValue(field);
+            if (value == null || shouldSkipValue(value)) {
+                continue;
+            }
+
+            String columnName = getColumnName(field);
+            if (columnName == null) {
+                continue;
+            }
+
+            Object serializableValue = ensureSerializable(value);
+            if (serializableValue != null) {
+                String fieldName = field.getName();
+                
+                // 判断是否使用模糊查询
+                boolean shouldUseLike = false;
+                
+                // 1. 检查是否在指定的模糊查询字段列表中
+                if (likeFields.contains(fieldName)) {
+                    shouldUseLike = true;
+                }
+                // 2. 检查是否全局启用String字段模糊查询
+                else if (stringFieldLike && value instanceof String) {
+                    shouldUseLike = true;
+                }
+                
+                if (shouldUseLike && value instanceof String) {
+                    String likeValue = buildLikeValue(serializableValue.toString());
+                    wrapper.like(columnName, likeValue);
+                } else {
+                    wrapper.eq(columnName, serializableValue);
+                }
+            }
+        }
+    }
+
+    /**
+     * 处理特殊查询场景(批量、范围、模糊查询)
+     */
+    private void processSpecialQueries() {
+        if (queryEntity == null) {
+            return;
+        }
+
+        List<Field> allFields = getAllFields(queryEntity.getClass());
+
+        for (Field field : allFields) {
+            if (isStaticField(field)) {
+                continue;
+            }
+
+            Object value = getFieldValue(field);
+            if (value == null) {
+                continue;
+            }
+
+            String fieldName = field.getName();
+
+            // 批量查询:集合类型字段
+            if (value instanceof Collection) {
+                processBatchQuery(fieldName, (Collection<?>) value);
+                continue;
+            }
+
+            // 范围查询:Start/End 后缀
+            if (isRangeQueryField(fieldName)) {
+                processRangeQuery(fieldName, value);
+                continue;
+            }
+
+            // 模糊查询:Like 后缀
+            if (isLikeQueryField(fieldName)) {
+                processLikeQuery(fieldName, value);
+            }
+        }
+    }
+
+    // ==================== 字段过滤判断 ====================
+    
+    /**
+     * 判断是否应该跳过该字段
+     */
+    private boolean shouldSkipField(Field field) {
+        String fieldName = field.getName();
+        
+        // 跳过特殊查询字段
+        if (isSpecialQueryField(fieldName)) {
+            return true;
+        }
+        
+        // 跳过 transient 字段
+        return Modifier.isTransient(field.getModifiers());
+    }
+
+    /**
+     * 判断是否为特殊查询字段(List/Start/End/Like后缀)
+     */
+    private boolean isSpecialQueryField(String fieldName) {
+        return fieldName.endsWith(SUFFIX_LIST) ||
+               fieldName.endsWith(SUFFIX_START) ||
+               fieldName.endsWith(SUFFIX_END) ||
+               fieldName.endsWith(SUFFIX_LIKE);
+    }
+
+    /**
+     * 判断是否为范围查询字段
+     */
+    private boolean isRangeQueryField(String fieldName) {
+        return fieldName.endsWith(SUFFIX_START) || fieldName.endsWith(SUFFIX_END);
+    }
+
+    /**
+     * 判断是否为模糊查询字段
+     */
+    private boolean isLikeQueryField(String fieldName) {
+        return fieldName.endsWith(SUFFIX_LIKE);
+    }
+
+    /**
+     * 判断是否为静态字段
+     */
+    private boolean isStaticField(Field field) {
+        return Modifier.isStatic(field.getModifiers());
+    }
+
+    /**
+     * 判断是否应该跳过该值
+     */
+    private boolean shouldSkipValue(Object value) {
+        // 空字符串
+        if (ignoreEmptyStr && value instanceof String && ((String) value).trim().isEmpty()) {
+            return true;
+        }
+        
+        // 空集合
+        return value instanceof Collection && ((Collection<?>) value).isEmpty();
+    }
+
+    // ==================== 查询类型处理 ====================
+    
+    /**
+     * 处理批量查询(IN查询)
+     */
+    private void processBatchQuery(String fieldName, Collection<?> collection) {
+        String baseFieldName = removeSuffix(fieldName, SUFFIX_LIST);
+        String columnName = getColumnNameByFieldName(baseFieldName, fieldName);
+        
+        if (columnName != null) {
+            List<Object> serializableList = filterSerializableItems(collection);
+            if (!serializableList.isEmpty()) {
+                wrapper.in(columnName, serializableList);
+            }
+        }
+    }
+
+    /**
+     * 处理范围查询
+     */
+    private void processRangeQuery(String fieldName, Object value) {
+        String baseFieldName = removeSuffix(fieldName, SUFFIX_START, SUFFIX_END);
+        String columnName = getColumnNameByFieldName(baseFieldName);
+        
+        if (columnName != null) {
+            Object serializableValue = ensureSerializable(value);
+            if (serializableValue != null) {
+                if (fieldName.endsWith(SUFFIX_START)) {
+                    wrapper.ge(columnName, serializableValue);
+                } else {
+                    wrapper.le(columnName, serializableValue);
+                }
+            }
+        }
+    }
+
+    /**
+     * 处理模糊查询
+     */
+    private void processLikeQuery(String fieldName, Object value) {
+        String baseFieldName = removeSuffix(fieldName, SUFFIX_LIKE);
+        String columnName = getColumnNameByFieldName(baseFieldName);
+        
+        if (columnName != null && value != null) {
+            String likeValue = buildLikeValue(value);
+            if (likeValue != null) {
+                wrapper.like(columnName, likeValue);
+            }
+        }
+    }
+
+    // ==================== 工具方法 ====================
+    
+    /**
+     * 获取字段值
+     */
+    private Object getFieldValue(Field field) {
+        try {
+            field.setAccessible(true);
+            return field.get(queryEntity);
+        } catch (IllegalAccessException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 获取数据库字段名(通过字段对象)
+     */
+    private String getColumnName(Field field) {
+        TableField tableField = field.getAnnotation(TableField.class);
+        
+        // 检查字段是否标记为不存在
+        if (tableField != null && !tableField.exist()) {
+            return null;
+        }
+        
+        // 优先使用注解指定的字段名
+        if (tableField != null && !tableField.value().isEmpty()) {
+            return tableField.value();
+        }
+        
+        // 默认:驼峰转下划线
+        return camelToUnderscore(field.getName());
+    }
+
+    /**
+     * 获取数据库字段名(通过字段名,支持fallback)
+     */
+    private String getColumnNameByFieldName(String... fieldNames) {
+        for (String fieldName : fieldNames) {
+            try {
+                Field field = findField(queryEntity.getClass(), fieldName);
+                String columnName = getColumnName(field);
+                if (columnName != null) {
+                    return columnName;
+                }
+            } catch (Exception e) {
+                // 继续尝试下一个字段名
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 移除字段名后缀
+     */
+    private String removeSuffix(String fieldName, String... suffixes) {
+        String result = fieldName;
+        for (String suffix : suffixes) {
+            result = result.replace(suffix, "");
+        }
+        return result;
+    }
+
+    /**
+     * 构建模糊查询值(根据likeMode模式)
+     * 
+     * 模式说明:
+     * - BOTH: %value%  (前后模糊,默认)
+     * - LEFT: %value   (前模糊)
+     * - RIGHT: value%  (后模糊)
+     */
+    private String buildLikeValue(Object value) {
+        if (value == null) {
+            return null;
+        }
+        
+        String strValue = value instanceof String ? (String) value : value.toString();
+        
+        switch (likeMode) {
+            case LEFT:
+                return LIKE_WILDCARD + strValue;  // %value
+            case RIGHT:
+                return strValue + LIKE_WILDCARD;  // value%
+            case BOTH:
+            default:
+                return LIKE_WILDCARD + strValue + LIKE_WILDCARD;  // %value%
+        }
+    }
+
+    /**
+     * 过滤集合中的可序列化元素
+     */
+    private List<Object> filterSerializableItems(Collection<?> collection) {
+        List<Object> result = new ArrayList<>();
+        for (Object item : collection) {
+            if (item != null && isSerializableType(item)) {
+                result.add(item);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 确保对象是可序列化的类型
+     */
+    private Object ensureSerializable(Object value) {
+        if (value == null) {
+            return null;
+        }
+        
+        if (isSerializableType(value)) {
+            return value;
+        }
+        
+        // 尝试转换为字符串
+        try {
+            return value.toString();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 判断对象是否为可序列化的基本类型
+     */
+    private boolean isSerializableType(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        
+        Class<?> clazz = obj.getClass();
+        
+        return clazz.isPrimitive() ||
+               Number.class.isAssignableFrom(clazz) ||
+               String.class.equals(clazz) ||
+               Boolean.class.equals(clazz) ||
+               Character.class.equals(clazz) ||
+               java.util.Date.class.isAssignableFrom(clazz) ||
+               java.sql.Date.class.isAssignableFrom(clazz) ||
+               java.sql.Timestamp.class.isAssignableFrom(clazz) ||
+               java.time.LocalDate.class.isAssignableFrom(clazz) ||
+               java.time.LocalDateTime.class.isAssignableFrom(clazz);
+    }
+
+    /**
+     * 驼峰命名转下划线命名
+     */
+    private String camelToUnderscore(String camelCase) {
+        if (camelCase == null || camelCase.isEmpty()) {
+            return camelCase;
+        }
+        
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < camelCase.length(); i++) {
+            char c = camelCase.charAt(i);
+            if (Character.isUpperCase(c) && i > 0) {
+                result.append('_');
+            }
+            result.append(Character.toLowerCase(c));
+        }
+        return result.toString();
+    }
+
+    /**
+     * 查找字段(支持父类)
+     */
+    private Field findField(Class<?> clazz, String fieldName) {
+        try {
+            return clazz.getDeclaredField(fieldName);
+        } catch (NoSuchFieldException e) {
+            Class<?> superClass = clazz.getSuperclass();
+            if (superClass != null && superClass != Object.class) {
+                return findField(superClass, fieldName);
+            }
+            throw new RuntimeException("字段不存在: " + fieldName + " in " + clazz.getName(), e);
+        }
+    }
+
+    /**
+     * 获取实体类所有字段(包括父类,排除静态字段)
+     */
+    private List<Field> getAllFields(Class<?> clazz) {
+        List<Field> fieldList = new ArrayList<>();
+        while (clazz != null && clazz != Object.class) {
+            for (Field field : clazz.getDeclaredFields()) {
+                if (!isStaticField(field)) {
+                    fieldList.add(field);
+                }
+            }
+            clazz = clazz.getSuperclass();
+        }
+        return fieldList;
+    }
+
+    // ==================== 内部类 ====================
+    
+    /**
+     * 查询结果包装类
+     */
+    @Data
+    @Accessors(chain = true)
+    public static class QueryResult<T> {
+        private final QueryWrapper<T> wrapper;
+        private final Page<T> page;
+
+        public QueryResult(QueryWrapper<T> wrapper, Page<T> page) {
+            this.wrapper = wrapper;
+            this.page = page;
+        }
+
+        /**
+         * 执行列表查询
+         */
+        public List<T> list(IService<T> service) {
+            return service.list(wrapper);
+        }
+
+        /**
+         * 执行分页查询
+         */
+        public IPage<T> page(IService<T> service) {
+            if (page == null) {
+                throw new IllegalStateException("未设置分页参数,请先调用page()方法");
+            }
+            return service.page(page, wrapper);
+        }
+
+        /**
+         * 执行单条查询
+         */
+        public T one(IService<T> service) {
+            return service.getOne(wrapper);
+        }
+
+        /**
+         * 执行计数查询
+         */
+        public long count(IService<T> service) {
+            return service.count(wrapper);
+        }
+
+        /**
+         * 获取查询条件(用于调试)
+         */
+        public QueryWrapper<T> getWrapper() {
+            return wrapper;
+        }
+    }
+}