cocoKnight vor 10 Monaten
Commit
0b63667452
36 geänderte Dateien mit 2078 neuen und 0 gelöschten Zeilen
  1. 33 0
      .gitignore
  2. 12 0
      README.md
  3. BIN
      lib/sql-jdbc-1.7.1.jar
  4. 149 0
      pom.xml
  5. 13 0
      src/main/java/com/dongzili/easysearch/EasySearchDemoApplication.java
  6. 56 0
      src/main/java/com/dongzili/easysearch/common/ElasticsearchConfig.java
  7. 205 0
      src/main/java/com/dongzili/easysearch/common/base/BaseController.java
  8. 46 0
      src/main/java/com/dongzili/easysearch/common/mybatisplus/AesTypeHandler.java
  9. 35 0
      src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusConfig.java
  10. 173 0
      src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusMapperConfig.java
  11. 59 0
      src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusUtil.java
  12. 338 0
      src/main/java/com/dongzili/easysearch/common/utils/ReflectUtils.java
  13. 11 0
      src/main/java/com/dongzili/easysearch/common/vo/IGetter.java
  14. 11 0
      src/main/java/com/dongzili/easysearch/common/vo/ISetter.java
  15. 15 0
      src/main/java/com/dongzili/easysearch/common/vo/KeyValue.java
  16. 7 0
      src/main/java/com/dongzili/easysearch/common/vo/LogicType.java
  17. 30 0
      src/main/java/com/dongzili/easysearch/common/vo/OrderType.java
  18. 15 0
      src/main/java/com/dongzili/easysearch/common/vo/OrderVo.java
  19. 49 0
      src/main/java/com/dongzili/easysearch/common/vo/PageRequest.java
  20. 27 0
      src/main/java/com/dongzili/easysearch/common/vo/QueryPeriodVo.java
  21. 118 0
      src/main/java/com/dongzili/easysearch/common/vo/R.java
  22. 69 0
      src/main/java/com/dongzili/easysearch/common/vo/ResponseCode.java
  23. 15 0
      src/main/java/com/dongzili/easysearch/common/vo/ValueLabel.java
  24. 118 0
      src/main/java/com/dongzili/easysearch/custom/EasySearchClient.java
  25. 48 0
      src/main/java/com/dongzili/easysearch/custom/controller/SiExerciseController.java
  26. 31 0
      src/main/java/com/dongzili/easysearch/custom/entity/Product.java
  27. 113 0
      src/main/java/com/dongzili/easysearch/custom/entity/SiExercise.java
  28. 11 0
      src/main/java/com/dongzili/easysearch/custom/mapper/ProductRepository.java
  29. 9 0
      src/main/java/com/dongzili/easysearch/custom/mapper/SiExerciseMapper.java
  30. 24 0
      src/main/java/com/dongzili/easysearch/custom/service/ProductService.java
  31. 48 0
      src/main/resources/application-dev.yml
  32. 8 0
      src/main/resources/application.yml
  33. 6 0
      src/main/resources/static/index.html
  34. 13 0
      src/test/java/com/dongzili/easysearch/EasySearchDemoApplicationTests.java
  35. 117 0
      src/test/java/com/dongzili/easysearch/custom/ProductServiceIntegrationTest.java
  36. 46 0
      src/test/java/com/dongzili/easysearch/custom/SiExerciseServiceTest.java

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 12 - 0
README.md

@@ -0,0 +1,12 @@
+
+
+# springboot 集成 easysearch + mybatisplus 使用jdbc连接进行sql查询操作
+
+> 介绍: INFINI Easysearch 提供一个自主可控的轻量级的 Elasticsearch 可替代版本,针对搜索业务场景优化并保持其产品的简洁与易用。
+## 1、windows环境通过安装 git-for-windows 来执行 bash 操作
+> 通过安装 git-for-windows 来执行 bash 操作   
+> https://docs.infinilabs.com/easysearch/main/docs/getting-started/install/windows/  
+> 安装完成后,可使用dbeaver来作为客户端进行查询操作,注意需要指定jdbc驱动。
+
+## 2、使用jdbc驱动连接
+> https://docs.infinilabs.com/easysearch/main/docs/references/sql/sql-jdbc/

BIN
lib/sql-jdbc-1.7.1.jar


+ 149 - 0
pom.xml

@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.dongzili</groupId>
+    <artifactId>EasySearchDemo</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>EasySearchDemo</name>
+    <description>EasySearchDemo</description>
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <spring-boot.version>2.7.6</spring-boot.version>
+        <elasticsearch.version>7.17.18</elasticsearch.version>
+
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-elasticsearch</artifactId>
+            <version>4.4.18</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>${elasticsearch.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-high-level-client</artifactId>
+            <version>${elasticsearch.version}</version>
+        </dependency>
+        <!-- JSON处理 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.13.0</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.25</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.6</version>
+        </dependency>
+        <!--        解决LocalDateTime问题-->
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-typehandlers-jsr310</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+            <version>1.15.10</version>
+        </dependency>
+        <dependency>
+            <!--groupId 随便填写 -->
+            <groupId>org.easysearch</groupId>
+            <!--artifactId 随便填写-->
+            <artifactId>sql-jdbc</artifactId>
+            <!--version 随便填写-->
+            <version>1.7.1</version>
+            <!-- scope=system表示此依赖是来自外部jar,而不是maven仓库。当scope设置为system时,systemPath属性才会生效,systemPath为一个物理文件路径,来指定依赖的jar其物理磁盘的位置。 -->
+            <scope>system</scope>
+            <!-- ${project.basedir}代表根目录 -->
+            <systemPath>${pom.basedir}/lib/sql-jdbc-1.7.1.jar</systemPath>
+        </dependency>
+
+
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <configuration>
+                    <mainClass>com.dongzili.easysearch.EasySearchDemoApplication</mainClass>
+                    <skip>true</skip>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>repackage</id>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
src/main/java/com/dongzili/easysearch/EasySearchDemoApplication.java

@@ -0,0 +1,13 @@
+package com.dongzili.easysearch;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class EasySearchDemoApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(EasySearchDemoApplication.class, args);
+    }
+
+}

+ 56 - 0
src/main/java/com/dongzili/easysearch/common/ElasticsearchConfig.java

@@ -0,0 +1,56 @@
+package com.dongzili.easysearch.common;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.ssl.SSLContexts;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
+
+import javax.net.ssl.SSLContext;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+
+@Configuration
+public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
+    @Value("${spring.elasticsearch.uris}")
+    private String elasticsearchUrl;
+
+    @Value("${spring.elasticsearch.username}")
+    private String username;
+
+    @Value("${spring.elasticsearch.password}")
+    private String password;
+
+    @Override
+    @Bean
+    public RestHighLevelClient elasticsearchClient() {
+        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(AuthScope.ANY,
+                new UsernamePasswordCredentials(username, password));
+
+        try{
+            SSLContext sslContext = SSLContexts.custom()
+                    .loadTrustMaterial(null, (x509Certificates, s) -> true)
+                    .build();
+
+            RestClientBuilder builder = RestClient.builder(HttpHost.create(elasticsearchUrl))
+                    .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
+                            .setDefaultCredentialsProvider(credentialsProvider)
+                            .setSSLContext(sslContext)
+                            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE));
+            return new RestHighLevelClient(builder);
+        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 205 - 0
src/main/java/com/dongzili/easysearch/common/base/BaseController.java

@@ -0,0 +1,205 @@
+package com.dongzili.easysearch.common.base;
+
+
+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.dongzili.easysearch.common.mybatisplus.MybatisPlusUtil;
+import com.dongzili.easysearch.common.utils.ReflectUtils;
+import com.dongzili.easysearch.common.vo.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BaseController<T> {
+
+    private final Class<T> tClass;
+//    protected final BaseMapper<T> baseMapper;
+    public BaseController(){
+        this.tClass = resolveEntityClass();
+//        DS ds = tClass.getAnnotation(DS.class);
+//        if (ds != null){
+//            DynamicDataSourceContextHolder.push(ds.value());
+//        }else{
+//            DynamicDataSourceContextHolder.push("main");
+//        }
+//        baseMapper = MybatisPlusUtil.getMapper(this.tClass);
+    }
+    /**
+     * 列表查询
+     */
+    @PostMapping("/list")
+    public R<List<T>> list(@RequestBody PageRequest<T> request) {
+        QueryWrapper<T> query = new QueryWrapper<>(request.getEntity());
+        // like 字段用 or 连接
+        Map<String, Object> fieldValueMap = ReflectUtils.getNotNullFieldValueMap(request.getLikeEntity());
+        if (!fieldValueMap.isEmpty()){
+            query.and(m -> {
+                fieldValueMap.forEach((k, v)->{
+                    if (LogicType.Or.equals(request.getLikeLogicType())){
+                        if(v instanceof String){
+                            m.or().like(k, v);
+                        }
+                        else if (v instanceof List){
+                            for (Object o : (List<?>) v) {
+                                m.or().like(k, o);
+                            }
+                        }else {
+                            m.or().eq(k, v);
+                        }
+                    }else{
+                        if(v instanceof String){
+                            m.like(k, v);
+                        }
+                        else if (v instanceof List){
+                            for (Object o : (List<?>) v) {
+                                m.like(k, o);
+                            }
+                        }else {
+                            m.eq(k, v);
+                        }
+                    }
+
+                });
+            });
+        }
+        if (request.getIn() != null && !request.getIn().isEmpty()){
+            request.getIn().forEach(query::in);
+        }
+        if (request.getNotIn() != null && !request.getNotIn().isEmpty()){
+            request.getNotIn().forEach(query::notIn);
+        }
+
+        // 时间范围过滤
+        addPeriodByCondition(request, query);
+        // 排序字段
+        addOrderByCondition(request, query);
+
+        IPage<T> pageData = MybatisPlusUtil.getMapper(this.tClass).selectPage(new Page<>(request.getCurrent(), request.getSize()), query);
+        return R.success(pageData.getRecords(), pageData.getTotal());
+    }
+
+//    /** 添加 */
+//    @PostMapping("/add")
+//    public R<Integer> add(@RequestBody T entity) {
+//
+//        Map<String, Object> fieldValueMap = new HashMap<>();
+//        if (StpUtil.isLogin()){
+//            User user = UserHelper.getCurrentUser();
+//            fieldValueMap.put("creatorId", user.getId());
+//            fieldValueMap.put("creatorName", user.getUsername());
+//            fieldValueMap.put("modifierId", user.getId());
+//            fieldValueMap.put("modifierName", user.getUsername());
+//        }else{
+//            fieldValueMap.put("creatorId", 0);
+//            fieldValueMap.put("creatorName", "");
+//            fieldValueMap.put("modifierId", 0);
+//            fieldValueMap.put("modifierName", "");
+//        }
+//        try{
+//            ReflectUtils.setValue(entity, fieldValueMap);
+//        }catch (Exception e){
+//            e.printStackTrace();
+//        }
+//        if(MybatisPlusUtil.getMapper(this.tClass).insert(entity) > 0){
+//            return R.success();
+//        }
+//        return R.error();
+//    }
+//
+//    /** 更新 */
+//    @PostMapping("/update")
+//    public R<Integer> update(@RequestBody T entity){
+//        Map<String, Object> fieldValueMap = new HashMap<>();
+//        if (StpUtil.isLogin()){
+//            User user = UserHelper.getCurrentUser();
+//            fieldValueMap.put("modifierId", user.getId());
+//            fieldValueMap.put("modifierName", user.getUsername());
+//        }else{
+//            fieldValueMap.put("modifierId", 0);
+//            fieldValueMap.put("modifierName", "");
+//        }
+//        try{
+//            ReflectUtils.setValue(entity, fieldValueMap);
+//        }catch (Exception e){
+//            e.printStackTrace();
+//        }
+//
+//        if(MybatisPlusUtil.getMapper(this.tClass).updateById(entity) > 0){
+//            return R.success();
+//        }else{
+//            if (ReflectUtils.getPrimaryValue(entity) != null){
+//                User user = UserHelper.getCurrentUser();
+//                fieldValueMap.put("creatorId", user.getId());
+//                fieldValueMap.put("creatorName", user.getUsername());
+//                try{
+//                    ReflectUtils.setValue(entity, fieldValueMap);
+//                }catch (Exception e){
+//                    e.printStackTrace();
+//                }
+//                if(MybatisPlusUtil.getMapper(this.tClass).insert(entity) > 0){
+//                    return R.success();
+//                }
+//            }
+//        }
+//
+//        return R.error();
+//    }
+
+    /** 根据id删除 */
+    @GetMapping("/delete")
+    public R<Integer> delete(@RequestParam("id") Integer id){
+        if(MybatisPlusUtil.getMapper(this.tClass).deleteById(id) > 0){
+            return R.success();
+        }
+        return R.error();
+    }
+    /** 根据id查询 */
+    @GetMapping("/get")
+        public R<T> get(@RequestParam("id") Integer id){
+        return R.success(MybatisPlusUtil.getMapper(this.tClass).selectById(id));
+    }
+
+    private Class<T> resolveEntityClass() {
+        Type superClass = getClass().getGenericSuperclass();
+        if (superClass instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) superClass;
+            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+            return (Class<T>) actualTypeArguments[0];
+        }
+        throw new IllegalArgumentException("Class is not parameterized with generic types");
+    }
+
+    /**
+     * 添加排序条件
+     */
+    private static <T> void addOrderByCondition(PageRequest<T> request, QueryWrapper<T> queryWrapper) {
+        if(request.getOrders()!=null && !request.getOrders().isEmpty()){
+            for (OrderVo orderVo : request.getOrders()){
+                if(OrderType.Desc.equals(orderVo.getOrderType())){
+                    queryWrapper.orderByDesc(orderVo.getFiledName());
+                }else{
+                    queryWrapper.orderByAsc(orderVo.getFiledName());
+                }
+            }
+        }
+    }
+
+    private static <T> void addPeriodByCondition(PageRequest<T> request, QueryWrapper<T> queryWrapper) {
+        if(request.getQueryPeriod() != null && !request.getQueryPeriod().isEmpty()){
+            for (Map.Entry<String, QueryPeriodVo> entry : request.getQueryPeriod().entrySet()){
+                if (entry.getValue().getStartTime() != null && entry.getValue().getEndTime() != null){
+                    queryWrapper.between(entry.getKey(), entry.getValue().getStartTime(), entry.getValue().getEndTime());
+                }
+            }
+        }
+    }
+
+}

+ 46 - 0
src/main/java/com/dongzili/easysearch/common/mybatisplus/AesTypeHandler.java

@@ -0,0 +1,46 @@
+//package com.dongzili.easysearch.common.mybatisplus;
+//
+//import com.kqns.chinaunicom.common.util.AesCbcUtils;
+//import org.apache.ibatis.type.BaseTypeHandler;
+//import org.apache.ibatis.type.JdbcType;
+//import org.apache.ibatis.type.MappedTypes;
+//
+//import java.sql.CallableStatement;
+//import java.sql.PreparedStatement;
+//import java.sql.ResultSet;
+//import java.sql.SQLException;
+//
+//// MyBatis类型处理器:自动处理AES加解密
+////@Component
+//@MappedTypes(String.class)
+//public class AesTypeHandler extends BaseTypeHandler<String> {
+//
+//
+//    // 插入时加密
+//    @Override
+//    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
+//        ps.setString(i, AesCbcUtils.encrypt(parameter));
+//    }
+//
+//    // 查询时解密(从字段获取)
+//    @Override
+//    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
+////        System.err.println("查询解密:columnName=" + columnName);  // 调试日志
+//        String ciphertext = rs.getString(columnName);
+//        return ciphertext != null ? AesCbcUtils.decrypt(ciphertext) : null;
+//    }
+//
+//    // 查询时解密(从索引获取)
+//    @Override
+//    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+//        String ciphertext = rs.getString(columnIndex);
+//        return ciphertext != null ? AesCbcUtils.decrypt(ciphertext) : null;
+//    }
+//
+//    @Override
+//    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+//        String ciphertext = cs.getString(columnIndex);
+//        return ciphertext != null ? AesCbcUtils.decrypt(ciphertext) : null;
+//    }
+//}
+//

+ 35 - 0
src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusConfig.java

@@ -0,0 +1,35 @@
+package com.dongzili.easysearch.common.mybatisplus;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 配置分页插件
+ *
+ */
+@Configuration
+@MapperScan("com.dongzili.easysearch.*")
+public class MybatisPlusConfig {
+
+    /**
+     * 分页插件
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
+        interceptor.addInnerInterceptor(paginationInnerInterceptor);    // 自定义分页插件,缓存分页
+        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); // 防止全表更新与删除
+        paginationInnerInterceptor.setDbType(DbType.MYSQL);
+        // 添加非法SQL拦截器
+//        interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
+        return interceptor;
+    }
+
+}
+

+ 173 - 0
src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusMapperConfig.java

@@ -0,0 +1,173 @@
+package com.dongzili.easysearch.common.mybatisplus;
+
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+import org.mybatis.spring.MyBatisSystemException;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.mapper.MapperFactoryBean;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.lang.reflect.Constructor;
+import java.util.*;
+
+
+@Configuration
+@Slf4j
+@AutoConfigureAfter(value = {SqlSessionTemplate.class, SpringUtil.class})
+public class MybatisPlusMapperConfig{
+
+    public static final Map<String, Class<?>> classMap = new HashMap<>();
+    @Resource
+    private SqlSessionTemplate sqlSessionTemplate; // 使用SqlSessionTemplate代替SqlSessionFactory
+
+    private static final List<String> basePackages = Collections.singletonList(
+            "com.dongzili.easysearch"
+    );
+
+    // 根据类名获取 bean name
+    private String getBeanName(String className) {
+        int index = className.lastIndexOf(".");
+        String simpleClassName = index != -1 ? className.substring(index + 1) : className;
+        char firstChar = simpleClassName.charAt(0);
+        if (firstChar >= 'A' && firstChar <= 'Z') {
+            firstChar -= (char) ('A' - 'a');
+        }
+        return firstChar + simpleClassName.substring(1);
+    }
+
+
+    @PostConstruct
+    public void setup() {
+        if (sqlSessionTemplate == null) {
+            log.error("SqlSessionTemplate 未正确注入,请检查配置。");
+            return;
+        }
+
+        for (String packageName : basePackages) {
+            if (!StringUtils.hasText(packageName)) {
+                continue;
+            }
+
+            // 扫描数据库实体,根据实体创建对应 mapper、service
+            Set<Class<?>> entityClassSet = ClassUtil.scanPackage(packageName);
+            if (entityClassSet.isEmpty()) {
+                continue;
+            }
+
+            for (Class<?> entityClass : entityClassSet) {
+                // 只创建带有TableName注解的实体
+                TableName tableName = entityClass.getAnnotation(TableName.class);
+                if (tableName == null) {
+                    continue;
+                }
+
+                classMap.put(tableName.value(), entityClass);
+                createMapperAndService(entityClass, packageName);
+            }
+        }
+    }
+
+    private void createMapperAndService(Class<?> entityClass, String packageName) {
+//        String mapperClassName = packageName + ".mapper" + "." + entityClass.getSimpleName() + "AutoMapper";
+//        String serviceImplClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoServiceImpl";
+//        String serviceClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoService";
+        String mapperClassName = entityClass.getName().replace(entityClass.getSimpleName(),"mapper") + "." + entityClass.getSimpleName() + "AutoMapper";
+        String serviceImplClassName = entityClass.getName().replace(entityClass.getSimpleName(),"service") + "." + entityClass.getSimpleName() + "AutoServiceImpl";
+        String serviceClassName = entityClass.getName().replace(entityClass.getSimpleName(),"service") + "." + entityClass.getSimpleName() + "AutoService";
+
+        log.debug("开始处理实体类: {}", entityClass.getName());
+
+        /* 创建 mapper */
+        try {
+            try (DynamicType.Unloaded<?> mapperType = new ByteBuddy()
+                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(BaseMapper.class, entityClass).build())
+                    .name(mapperClassName)
+                    .annotateType(AnnotationDescription.Builder.ofType(org.apache.ibatis.annotations.Mapper.class).build())
+                    .make()) {
+
+                Class<?> mapperClass = mapperType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
+                        .getLoaded();
+
+                MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
+
+                factoryBean.setSqlSessionTemplate(sqlSessionTemplate);
+                sqlSessionTemplate.getConfiguration().addMapper(mapperClass);
+
+                SpringUtil.registerBean(getBeanName(mapperClassName), factoryBean.getObject());
+                log.info("自动注册 mybatisplus mapper bean -> 名称:{}, 类名:{}", getBeanName(mapperClassName), mapperClassName);
+            }
+        } catch (Exception e) {
+            if (e instanceof MyBatisSystemException) {
+                log.error("MyBatis系统异常,注册Mapper失败: {}", e.getMessage(), e);
+            } else if (e instanceof BeanCreationException) {
+                log.error("Spring Bean创建异常,注册Mapper失败: {}", e.getMessage(), e);
+            } else {
+                log.error("自动注册 mybatisplus mapper bean 失败,原因:{}", e.getMessage(), e);
+            }
+            throw new RuntimeException("自动注册 mybatisplus mapper bean 失败!" + mapperClassName, e);
+        }
+
+        /* 创建 service */
+        try {
+            try (DynamicType.Unloaded<?> serviceType = new ByteBuddy()
+                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(IService.class, entityClass).build())
+                    .name(serviceClassName)
+                    .make()) {
+
+                Class<?> serviceClass = serviceType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
+                        .getLoaded();
+
+                List<AnnotationDescription> annotations = new ArrayList<>();
+                annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
+                annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());
+
+                try (DynamicType.Unloaded<?> serviceImplType = new ByteBuddy()
+                        .subclass(TypeDescription.Generic.Builder.parameterizedType(ServiceImpl.class,
+                                Class.forName(mapperClassName), entityClass).build())
+                        .implement(serviceClass)
+                        .name(serviceImplClassName)
+                        .annotateType(annotations)
+                        .make()) {
+
+                    Class<?> serviceImplClass = serviceImplType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
+                            .getLoaded();
+
+                    // 使用构造器创建实例,比newInstance更灵活
+                    Constructor<?> constructor = serviceImplClass.getDeclaredConstructor();
+                    constructor.setAccessible(true);
+                    SpringUtil.registerBean(getBeanName(serviceImplClassName), constructor.newInstance());
+
+                    log.info("自动注册 mybatisplus Service Bean -> 名称:{}, 类名:{}", getBeanName(serviceImplClassName), serviceImplClassName);
+                }
+            }
+        } catch (Exception e) {
+            if (e instanceof ClassNotFoundException) {
+                log.error("类未找到异常,可能是包名或类名配置错误: {}", e.getMessage(), e);
+            } else if (e instanceof NoSuchMethodException) {
+                log.error("构造器未找到异常: {}", e.getMessage(), e);
+            } else {
+                log.error("自动注册 mybatisplus Service Bean 失败,原因:{}", e.getMessage(), e);
+            }
+            throw new RuntimeException("自动注册 mybatisplus Service Bean 失败!" + serviceImplClassName, e);
+        }
+
+        log.debug("成功创建Mapper和Service: {}", entityClass.getName());
+    }
+}

+ 59 - 0
src/main/java/com/dongzili/easysearch/common/mybatisplus/MybatisPlusUtil.java

@@ -0,0 +1,59 @@
+package com.dongzili.easysearch.common.mybatisplus;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public class MybatisPlusUtil {
+    /**
+     * Service服务缓存
+     */
+    private static final ConcurrentHashMap<Class<?>, IService<?>> SERVICE_CACHE = new ConcurrentHashMap<>();
+
+    /**
+     * Mapper缓存
+     */
+    private static final ConcurrentHashMap<Class<?>, BaseMapper<?>> MAPPER_CACHE = new ConcurrentHashMap<>();
+
+    /**
+     * 获取Service(带缓存)
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> IService<T> getService(final Class<T> t) {
+        try {
+            return (IService<T>) SERVICE_CACHE.computeIfAbsent(t, k -> {
+                String beanName = getBeanName(t) + "AutoServiceImpl";
+                return SpringUtil.getBean(beanName);
+            });
+        } catch (NoSuchBeanDefinitionException e) {
+            throw new RuntimeException("未找到对应的Service实现: " + getBeanName(t) + "AutoServiceImpl", e);
+        }
+    }
+
+
+    /**
+     * 获取Mapper(带缓存)
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> BaseMapper<T> getMapper(final Class<T> t) {
+        try {
+            return (BaseMapper<T>) MAPPER_CACHE.computeIfAbsent(t, k -> {
+                String beanName = getBeanName(t) + "AutoMapper";
+                return SpringUtil.getBean(beanName);
+            });
+        } catch (NoSuchBeanDefinitionException e) {
+            throw new RuntimeException("未找到对应的Mapper: " + getBeanName(t) + "AutoMapper", e);
+        }
+    }
+    /**
+     * 获取bean名称
+     */
+    private static <T> String getBeanName(final Class<T> t) {
+        return t.getSimpleName().substring(0, 1).toLowerCase() + t.getSimpleName().substring(1);
+    }
+}

+ 338 - 0
src/main/java/com/dongzili/easysearch/common/utils/ReflectUtils.java

@@ -0,0 +1,338 @@
+package com.dongzili.easysearch.common.utils;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import org.springframework.data.annotation.Id;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 反射相关工具类
+ */
+public class ReflectUtils {
+
+    /**
+     * 获取不为空的属性的数据库字段名和值
+     * @param obj
+     * @param columns
+     * @return
+     */
+    public static Map<String, Object> getNotNullFieldValueMap(Object obj, List<String> columns) {
+        Map<String, Object> fieldValueMap = new HashMap<>();
+        Class<?> clazz = obj.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                if(columns.contains(field.getName())){
+                    continue;
+                }
+                field.setAccessible(true);
+                Object value = field.get(obj);
+                if (value != null) {
+                    String fieldName = getFieldName(field);
+                    fieldValueMap.put(fieldName, value);
+                }
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        return fieldValueMap;
+    }
+
+    /**
+     * 获取不为空的属性的数据库字段名和值
+     * @param obj
+     * @return
+     */
+    public static Map<String, Object> getNotNullFieldValueMap(Object obj) {
+        Map<String, Object> fieldValueMap = new HashMap<>();
+        if (obj == null){
+            return fieldValueMap;
+        }
+        Class<?> clazz = obj.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                TableField tableFieldAnnotation = field.getAnnotation(TableField.class);
+                if (tableFieldAnnotation != null && !tableFieldAnnotation.exist()) {
+                    continue; // Ignore if exist is false
+                }
+                Object value = field.get(obj);
+                if (value != null) {
+                    if (value instanceof List<?>){
+                        if(!((List<?>)value).isEmpty()){
+                            fieldValueMap.put(getFieldName(field), value);
+                        }
+                    }else if (value instanceof String){
+                        if(StringUtils.hasText(value.toString())){
+                            fieldValueMap.put(getFieldName(field), value);
+                        }
+                    }else{
+                        fieldValueMap.put(getFieldName(field), value);
+                    }
+
+                }
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        return fieldValueMap;
+    }
+
+    public static String getFieldName(Field field) {
+        TableField tableFieldAnnotation = field.getAnnotation(TableField.class);
+        if (tableFieldAnnotation != null && !tableFieldAnnotation.value().isEmpty()) {
+            return tableFieldAnnotation.value();
+        } else {
+            return field.getName();
+        }
+    }
+
+
+    /**
+     * 获取实体类上 @TableName 注解中定义的数据库表名
+     * @param clazz 实体类的 Class 对象
+     * @return 数据库表名,如果未找到注解或注解未设置 value 属性,则返回 null
+     */
+    public static String getTableName(Class<?> clazz) {
+        TableName tableNameAnnotation = AnnotationUtil.getAnnotation(clazz, TableName.class);
+        if (tableNameAnnotation != null) {
+            return tableNameAnnotation.value();
+        }
+        return "";
+    }
+
+    /**
+     * 获取实体字段上 @TableField 注解中定义的列名
+     * @param clazz
+     * @return
+     */
+    public static String getColumnNames(Class<?> clazz) {
+        List<String> columns = new ArrayList<>();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableField.class)) {
+                TableField tableFieldAnnotation = field.getAnnotation(TableField.class);
+                if(tableFieldAnnotation!=null && tableFieldAnnotation.exist()){
+                    columns.add(tableFieldAnnotation.value()+" as `"+field.getName()+"`");
+                }
+
+            }else if(field.isAnnotationPresent(TableId.class)){
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    columns.add(tableFieldAnnotation.value()+" as `"+field.getName()+"`");
+                }
+            }
+            else{
+                columns.add("`"+field.getName()+"`");
+            }
+        }
+        return String.join(",", columns);
+    }
+
+    /**
+     * 获取主键数据库字段名称
+     * @param clazz
+     * @return
+     */
+    public static String getPrimaryColumnKey(Class<?> clazz) {
+
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class)) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    return tableFieldAnnotation.value();
+                }
+
+            }
+        }
+        return "id";
+    }
+    public static Field getPrimaryField(Class<?> clazz) {
+
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class) || field.isAnnotationPresent(Id.class) || field.getName().equalsIgnoreCase("id")) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    return field;
+                }
+
+            }
+        }
+        return null;
+    }
+    /**
+     * 获取主键实体字段名称
+     * @param clazz
+     * @return
+     */
+    public static String getPrimaryFieldKey(Class<?> clazz) {
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class)) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    return field.getName();
+                }
+
+            }
+        }
+        return "id";
+    }
+
+
+    public static <T> Object getPrimaryValue(Class<T> clazz, T entity) throws IllegalAccessException {
+
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class)) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    return field.get(entity);
+                }
+
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取实体类的主键值
+     */
+    public static <T> Object getPrimaryValue(T entity) {
+        try{
+            Field[] fields = entity.getClass().getDeclaredFields();
+            for (Field field : fields) {
+                field.setAccessible(true);
+                if (field.isAnnotationPresent(TableId.class) || field.isAnnotationPresent(Id.class) || field.getName().equalsIgnoreCase("id")) {
+                    return field.get(entity);
+                }
+            }
+        }catch (Exception e){
+            return null;
+        }
+        return null;
+    }
+
+
+    public static <T> T setPrimaryValue(Class<T> clazz, Object value) throws IllegalAccessException, InstantiationException {
+
+        T cloneEntity = clazz.newInstance();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class)) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    field.set(cloneEntity, value);
+                    return cloneEntity;
+                }
+
+            }
+        }
+        return null;
+    }
+
+    public static <T> T setPrimaryValue(T entity, Object value) throws IllegalAccessException, InstantiationException {
+
+//        T cloneEntity = entity.getClass().newInstance();
+        Field[] fields = entity.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (field.isAnnotationPresent(TableId.class)) {
+                TableId tableFieldAnnotation = field.getAnnotation(TableId.class);
+                if(tableFieldAnnotation!=null){
+                    field.set(entity, value);
+                    return entity;
+                }
+
+            }
+        }
+        return null;
+    }
+
+    public static <T> void setValue(T entity, Map<String, Object> map) throws IllegalAccessException, InstantiationException {
+
+//        T cloneEntity = entity.getClass().newInstance();
+        Field[] fields = entity.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            if (map.containsKey(field.getName())) {
+                field.set(entity, map.get(field.getName()));
+            }
+        }
+    }
+
+    public static <T> T getObjByKeys(Class<T> clazz, T entity){
+        try{
+            Object id = getPrimaryValue(clazz, entity);
+            if(id != null){
+                return setPrimaryValue(clazz, id);
+            }
+            T cloneEntity = clazz.newInstance();
+            Field[] fields = clazz.getDeclaredFields();
+            boolean hasKey = false;
+            // 遍历所有属性
+            for (Field field : fields) {
+                // 如果属性上有此注解,则进行赋值操作
+                if (field.isAnnotationPresent(TableId.class)) {
+                    field.setAccessible(true);
+                    Object value = field.get(entity);
+                    field.set(cloneEntity, value);
+                    hasKey = true;
+                }
+            }
+            return hasKey ? cloneEntity : null;
+        }catch (Exception e){
+            return null;
+        }
+    }
+
+
+    /**
+     * 根据字段获取数据库字段名称
+     * @param clazz
+     * @param fieldName
+     * @return
+     * @param <E>
+     */
+    public static <E> String getColumnName(Class<E> clazz, String fieldName) {
+        try {
+            Field[] fields = clazz.getDeclaredFields();
+            for (Field field : fields) {
+                field.setAccessible(true);
+                if(field.getName().equals(fieldName)){
+                    if(field.isAnnotationPresent(TableField.class)){
+                        TableField tableFieldAnnotation = field.getAnnotation(TableField.class);
+                        if(tableFieldAnnotation!=null){
+                            return tableFieldAnnotation.value();
+                        }
+                    }else{
+                        return field.getName();
+                    }
+                }
+            }
+        }catch (Exception e){
+            return null;
+        }
+        return null;
+    }
+
+
+}

+ 11 - 0
src/main/java/com/dongzili/easysearch/common/vo/IGetter.java

@@ -0,0 +1,11 @@
+package com.dongzili.easysearch.common.vo;
+
+import java.io.Serializable;
+
+/**
+ * getter方法接口定义
+ */
+@FunctionalInterface
+public interface IGetter<T> extends Serializable {
+    Object apply(T source);
+}

+ 11 - 0
src/main/java/com/dongzili/easysearch/common/vo/ISetter.java

@@ -0,0 +1,11 @@
+package com.dongzili.easysearch.common.vo;
+
+import java.io.Serializable;
+
+/**
+ * setter方法接口定义
+ */
+@FunctionalInterface
+public interface ISetter<T, U> extends Serializable {
+    void accept(T t, U u);
+}

+ 15 - 0
src/main/java/com/dongzili/easysearch/common/vo/KeyValue.java

@@ -0,0 +1,15 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+public class KeyValue {
+    private Object key;
+    private Object value;
+}

+ 7 - 0
src/main/java/com/dongzili/easysearch/common/vo/LogicType.java

@@ -0,0 +1,7 @@
+package com.dongzili.easysearch.common.vo;
+
+public enum LogicType {
+
+    And,
+    Or
+}

+ 30 - 0
src/main/java/com/dongzili/easysearch/common/vo/OrderType.java

@@ -0,0 +1,30 @@
+package com.dongzili.easysearch.common.vo;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import lombok.Getter;
+
+/**
+ * 排序方式
+ */
+@Getter
+public enum OrderType {
+
+
+    /**
+     * 降序
+     */
+    Desc(1),
+    /**
+     * 升序
+     */
+    Asc(2)
+    ;
+
+    @EnumValue//标记数据库存的值
+    private final int value;
+
+    OrderType(int value) {
+        this.value = value;
+    }
+
+}

+ 15 - 0
src/main/java/com/dongzili/easysearch/common/vo/OrderVo.java

@@ -0,0 +1,15 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+public class OrderVo {
+    private String filedName;
+    private OrderType orderType;
+}

+ 49 - 0
src/main/java/com/dongzili/easysearch/common/vo/PageRequest.java

@@ -0,0 +1,49 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class PageRequest<T> {
+
+    /** 当前页数,从1开始 */
+    private Integer current;
+    /** 每页记录数 **/
+    private Integer size;
+    /** 总数 **/
+    private Integer total;
+    /**搜索关键字*/
+    private String keyword;
+    /** 请求数据实体 */
+    @Valid
+    private T entity;
+    /** 请求数据实体数组 */
+    @Valid
+    private List<T> entities;
+    /** 消息ID */
+    private String messageId;
+    /** 模糊搜索承载实体 */
+    private T likeEntity ;
+    private LogicType likeLogicType = LogicType.And;
+//    /** 排除范围查询实体 */
+//    private T notInEntity ;
+    private Map<String, List<?>> in;
+    private Map<String, List<?>> notIn;
+    /** 排序字段 */
+    private List<OrderVo> orders;
+    /** 根据指定时间区间查询字段 */
+    private Map<String, QueryPeriodVo> queryPeriod;
+
+
+    public PageRequest() {
+        current = 1;
+        size = 10;
+    }
+}

+ 27 - 0
src/main/java/com/dongzili/easysearch/common/vo/QueryPeriodVo.java

@@ -0,0 +1,27 @@
+package com.dongzili.easysearch.common.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * 查询期间
+ */
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class QueryPeriodVo {
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime startTime;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime endTime;
+
+
+}

+ 118 - 0
src/main/java/com/dongzili/easysearch/common/vo/R.java

@@ -0,0 +1,118 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.StringJoiner;
+
+@Getter
+@Setter
+@Accessors(chain = true)
+public class R<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer code;
+
+    private String message;
+
+    private String token;
+
+    private String traceId;
+
+    private String status;
+
+    private Long count;
+
+    private long times;
+
+    private T data;
+
+    public R(Integer code, String message, String status){
+        this.code = code;
+        this.message = message;
+        this.status = status;
+    }
+
+    public R(Integer code, String message, String status, T data){
+        this(code,message,status);
+        this.status = status;
+        this.data = data;
+    }
+    public R(){}
+
+    public static <T> R<T> success(){
+        return new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus());
+    }
+    public static <T> R<T> success(String message){
+        return new R<>(ResponseCode.SUCCESS.getCode(), message, ResponseCode.SUCCESS.getStatus());
+    }
+
+    public static <T> R<T> success(T data, Long count){
+        if (data instanceof List){
+            R<T> r =new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus(),data);
+            r.setCount(count);
+            return r;
+        }
+        return new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus(),data);
+    }
+
+    public static <T> R<T> success(T data){
+        if (data instanceof List){
+            R<T> r =new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus(),data);
+            r.setCount((long) ((List<?>) data).size());
+            return r;
+        }
+        return new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus(),data);
+    }
+
+    public static <T> R<T> success(T data, String message){
+        if (data instanceof List){
+            R<T> r =new R<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getMessage(), ResponseCode.SUCCESS.getStatus(),data);
+            r.setCount((long) ((List<?>) data).size());
+            return r;
+        }
+        return new R<>(ResponseCode.SUCCESS.getCode(), message, ResponseCode.SUCCESS.getStatus(),data);
+    }
+
+    public static <T> R<T> fail(){
+        return new R<>(ResponseCode.FAILURE.getCode(),ResponseCode.FAILURE.getMessage(), ResponseCode.FAILURE.getStatus());
+    }
+
+    public static <T> R<T> fail(Integer code, String message, String status){
+        return new R<>(code,message, status);
+    }
+
+    public static <T> R<T> error(){
+        return new R<>(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getMessage(), ResponseCode.ERROR.getStatus());
+    }
+
+    public static <T> R<T> error(String msg){
+        return new R<>(ResponseCode.ERROR.getCode(), msg, ResponseCode.ERROR.getStatus());
+    }
+
+    public static <T> R<T> error(String msg, T data){
+        return new R<>(ResponseCode.ERROR.getCode(), msg, ResponseCode.ERROR.getStatus(), data);
+    }
+
+    public static <T> R<T> error(ResponseCode response){
+        return new R<>(response.getCode(), response.getMessage(), response.getStatus());
+    }
+//    public static <T> ApiResponse<T> error(ResponseCode response, String msg){
+//        return new ApiResponse<>(response.getCode(), msg, response.getStatus());
+//    }
+    public static <T> R<T> error(ResponseCode response, T data){
+        return new R<>(response.getCode(), response.getMessage(), response.getStatus(), data);
+    }
+
+    @Override
+    public String toString() {
+        return (new StringJoiner(", ", R.class.getSimpleName() + "[", "]"))
+                .add("code=" + this.code)
+                .add("message='" + this.message + "'")
+                .add("data=" + this.data)
+                .toString();
+    }
+}

+ 69 - 0
src/main/java/com/dongzili/easysearch/common/vo/ResponseCode.java

@@ -0,0 +1,69 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.Getter;
+
+@Getter
+public enum ResponseCode {
+
+    SUCCESS(200,"操作成功!", "Success"),
+    FAILURE(201,"操作失败", "Failure"),
+
+    USER_NOT_LOGIN(401, "未登录", "Failure"),
+    /**系统相关的错误码:5开头**/
+    ERROR(500,"系统异常,请稍后重试", "Failure"),
+    /**参数相关的错误码:1开头**/
+    PARAM_ERROR(1000,"参数异常", "Failure"),
+    PARAM_RSA_ERROR(1001,"参数rsa解密异常", "Failure"),
+    JSON_PARSE_ERROR(1002,"json格式错误" , "Failure" ),
+    PARAM_DUPLICATE(1003, "数据约束冲突,请检查请求参数是否有误", "Failure"),
+
+
+    /**权限相关的错误码:2开头**/
+    INVALID_TOKEN(2001,"未登录或登录信息已过期,请重新登录!", "Failure"),
+    ACCESS_DENIED(2002,"授权已到期或无权访问,请联系管理员", "Failure"),
+    USERNAME_OR_PASSWORD_ERROR(2003,"用户名或密码错误", "Failure"),
+    ACCESS_DENIED_Copyright(2004,"该资源未授权,请联系管理员", "Failure"),
+    Visitors_NotAllowed(2005,"游客身份受限,请去登录...", "Failure"),
+    LOGIN_5ERROR_LIMIT(2006,"登录失败次数超过5次限制,请10分钟后再尝试..." , "Failure"),
+    LOGIN_4ERROR_LIMIT(2007,"登录失败已达4次,如果再次登陆失败该账号将被锁定10分钟,是否继续?" , "Failure"),
+    USER_DISALLOWED(2008, "该账号已禁用", "Failure"),
+
+    MULTI_AGENCY_WARN(3001,"当前IP有多个机构IP段匹配", "Failure"),
+    NONE_AGENCY_WARN(3002,"当前IP不在任何机构IP段内", "Failure"),
+    NOT_AGENCY_WARN(3002,"当前IP不在指定机构IP段内", "Failure"),
+    AGENCY_NOT_FOUND(3002,"机构不存在", "Failure"),
+    PCATEGORY_STATUS_BAN(3004,"父分类被禁用,无法新增子分类", "Failure"),
+    CATEGORY_START_GROUP_EXIST(3005,"当前等级已存在正在使用的分类组,无法新增", "Failure"),
+    NOT_PERMISSION(3006,"无权限操作", "Failure"),
+
+    CATEGORY_GROUP_START_REPEAT(3007,"当前级别已有分类组在使用", "Failure"),
+    DELETE_FAIL(3008,"删除失败", "Failure"),
+    UPLOAD_FAIL(3009,"上传失败", "Failure"),
+    DATA_UNAUTHORIZED(3010,"数据未授权", "Failure"),
+
+    NO_RECORD(3010, "未找到数据", "Failure"),
+    ACCOUNT_REQUIRE(3011, "账号不能为空", "Failure"),
+    PHONE_OR_MAIL_ERROR(3012, "手机号或邮箱错误", "Failure"),
+    ACCOUNT_NOT_REGISTERED(3013, "该账号还没有注册", "Failure"),
+    ACCOUNT_LOCKED(3014, "当前用户已被锁定,请联系管理员!", "Failure"),
+    ACCOUNT_ADJUST_REQUIRE(3015, "当前用户还未被审核,请联系管理员!", "Failure"),
+
+    LIMIT_ERROR(4000,"服务器繁忙,请稍后重试...", "Failure");
+
+    private final Integer code;
+
+    private final String message;
+
+    private final String status;
+
+//    ResponseCode(int code, String message){
+//        this.code = code;
+//        this.message = message;
+//    }
+    ResponseCode(Integer code, String message, String status){
+        this.code = code;
+        this.message = message;
+        this.status = status;
+    }
+
+}

+ 15 - 0
src/main/java/com/dongzili/easysearch/common/vo/ValueLabel.java

@@ -0,0 +1,15 @@
+package com.dongzili.easysearch.common.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+public class ValueLabel {
+    private Object label;
+    private Object value;
+}

+ 118 - 0
src/main/java/com/dongzili/easysearch/custom/EasySearchClient.java

@@ -0,0 +1,118 @@
+//package com.dongzili.easysearch.custom;
+//
+//import org.elasticsearch.action.delete.DeleteRequest;
+//import org.elasticsearch.action.delete.DeleteResponse;
+//import org.elasticsearch.action.index.IndexRequest;
+//import org.elasticsearch.action.index.IndexResponse;
+//import org.elasticsearch.action.update.UpdateRequest;
+//import org.elasticsearch.action.update.UpdateResponse;
+//import org.elasticsearch.client.Request;
+//import org.elasticsearch.client.RequestOptions;
+//import org.elasticsearch.client.RestClient;
+//import org.elasticsearch.client.RestHighLevelClient;
+//import org.elasticsearch.common.xcontent.XContentType;
+//import org.elasticsearch.rest.RestStatus;
+//import com.fasterxml.jackson.databind.ObjectMapper;
+//import java.io.IOException;
+//import java.util.HashMap;
+//import java.util.Map;
+//
+//public class EasySearchClient {
+//    private final RestHighLevelClient client;
+//    private final ObjectMapper objectMapper;
+//
+//    // 初始化客户端
+//    public EasySearchClient(String host, int port) {
+//        this.client = new RestHighLevelClient(
+//            RestClient.builder(new org.apache.http.HttpHost(host, port, "http"))
+//        );
+//        this.objectMapper = new ObjectMapper();
+//    }
+//
+//    // 新增文档
+//    public String createDocument(String index, String id, Map<String, Object> document) throws IOException {
+//        IndexRequest request = new IndexRequest(index);
+//        if (id != null) {
+//            request.id(id);
+//        }
+//        request.source(document, XContentType.JSON);
+//
+//        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
+//        return response.getId();
+//    }
+//
+//    // 更新文档
+//    public boolean updateDocument(String index, String id, Map<String, Object> updatedFields) throws IOException {
+//        UpdateRequest request = new UpdateRequest(index, id);
+//        request.doc(updatedFields, XContentType.JSON);
+//
+//        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
+//        return response.status() == RestStatus.OK;
+//    }
+//
+//    // 删除文档
+//    public boolean deleteDocument(String index, String id) throws IOException {
+//        DeleteRequest request = new DeleteRequest(index, id);
+//        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
+//        return response.status() == RestStatus.OK;
+//    }
+//
+//    // 执行SQL查询
+//    public String executeSqlQuery(String sql) throws IOException {
+//        // 构建SQL查询请求
+//        Map<String, String> params = new HashMap<>();
+//        params.put("query", sql);
+//
+//        // 发送请求并获取响应
+//        org.elasticsearch.client.Response response = client.getLowLevelClient().performRequest(
+//            Request.POST,
+//            "/_sql",
+//            new HashMap<>(),
+//            org.elasticsearch.client.Requests.DEFAULT_CONTENT_TYPE,
+//            objectMapper.writeValueAsString(params).getBytes()
+//        );
+//
+//        return org.elasticsearch.client.RestClientsUtils.toString(response.getEntity());
+//    }
+//
+//    // 关闭客户端
+//    public void close() throws IOException {
+//        client.close();
+//    }
+//
+//    // 示例用法
+//    public static void main(String[] args) {
+//        try (EasySearchClient esClient = new EasySearchClient("localhost", 9200)) {
+//            String index = "articles";
+//
+//            // 1. 创建文档
+//            Map<String, Object> article = new HashMap<>();
+//            article.put("title", "Java集成EasySearch教程");
+//            article.put("content", "这是一篇关于Java集成EasySearch的教程文章");
+//            article.put("category", "技术");
+//            article.put("publish_date", "2023-09-28");
+//            article.put("views", 100);
+//
+//            String docId = esClient.createDocument(index, null, article);
+//            System.out.println("创建文档成功,ID: " + docId);
+//
+//            // 2. 更新文档
+//            Map<String, Object> updates = new HashMap<>();
+//            updates.put("views", 150);
+//            boolean updateSuccess = esClient.updateDocument(index, docId, updates);
+//            System.out.println("更新文档成功: " + updateSuccess);
+//
+//            // 3. SQL查询
+//            String sql = "SELECT title, category, views FROM " + index + " WHERE category = '技术'";
+//            String result = esClient.executeSqlQuery(sql);
+//            System.out.println("SQL查询结果: " + result);
+//
+//            // 4. 删除文档(演示用,实际根据需要是否执行)
+//            // boolean deleteSuccess = esClient.deleteDocument(index, docId);
+//            // System.out.println("删除文档成功: " + deleteSuccess);
+//
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+//    }
+//}

+ 48 - 0
src/main/java/com/dongzili/easysearch/custom/controller/SiExerciseController.java

@@ -0,0 +1,48 @@
+package com.dongzili.easysearch.custom.controller;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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.dongzili.easysearch.common.mybatisplus.MybatisPlusUtil;
+import com.dongzili.easysearch.common.vo.R;
+import com.dongzili.easysearch.custom.entity.SiExercise;
+import com.dongzili.easysearch.custom.mapper.SiExerciseMapper;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+public class SiExerciseController {
+
+    @Resource
+    SiExerciseMapper siExerciseMapper;
+
+    @RequestMapping("/getExercise")
+    public R<?> getExercise(){
+        LambdaQueryWrapper<SiExercise> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(SiExercise::getIssue, "医学");
+
+        IPage<SiExercise> pages = siExerciseMapper.selectPage(new Page<>(1, 10), wrapper);
+        System.out.println(JSONUtil.toJsonStr(pages));
+        return R.success(pages);
+    }
+
+    @RequestMapping("/groupBy")
+    public R<?> groupBy(){
+        QueryWrapper<SiExercise> wrapper = new QueryWrapper<>();
+        wrapper.lambda().like(SiExercise::getIssue, "医学");
+        wrapper.lambda().groupBy(SiExercise::getSubType);
+        wrapper.select("subType, count(*) as count");
+        wrapper.orderByDesc("count");
+        List<Map<String,Object>> mapList = siExerciseMapper.selectMaps(wrapper);
+
+        System.out.println(JSONUtil.toJsonStr(mapList));
+        return R.success(mapList);
+    }
+
+}

+ 31 - 0
src/main/java/com/dongzili/easysearch/custom/entity/Product.java

@@ -0,0 +1,31 @@
+package com.dongzili.easysearch.custom.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.elasticsearch.annotations.Document;
+import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.FieldType;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+@Data
+@Document(indexName = "products")
+public class Product {
+    @Id
+    private String id;
+
+    @Field(type = FieldType.Text, name = "name")
+    private String name;
+
+    @Field(type = FieldType.Double, name = "price")
+    private Double price;
+
+    public Product(String s, double v) {
+        this.name = s;
+        this.price = v;
+    }
+}

+ 113 - 0
src/main/java/com/dongzili/easysearch/custom/entity/SiExercise.java

@@ -0,0 +1,113 @@
+package com.dongzili.easysearch.custom.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 练习题
+ * @TableName f_si_exercise
+ */
+@TableName(value ="f_si_exercise")
+@Data
+public class SiExercise implements Serializable {
+    /**
+     *
+     */
+    @TableId(value = "id")
+    private String id;
+
+    /**
+     *
+     */
+    @TableField(value = "sectionId")
+    private String sectionId;
+
+    /**
+     *
+     */
+    @TableField(value = "treeIds")
+    private String treeIds;
+
+    /**
+     *
+     */
+    @TableField(value = "parentId")
+    private String parentId;
+
+    /**
+     *
+     */
+    @TableField(value = "lastId")
+    private String lastId;
+
+    /**
+     *
+     */
+    @TableField(value = "subType")
+    private Integer subType;
+
+    /**
+     *
+     */
+    @TableField(value = "stem")
+    private String stem;
+
+    /**
+     *
+     */
+    @TableField(value = "issue")
+    private String issue;
+
+    /**
+     *
+     */
+    @TableField(value = "stemTail")
+    private String stemTail;
+
+    /**
+     *
+     */
+    @TableField(value = "sOption")
+    private String sOption;
+
+    /**
+     *
+     */
+    @TableField(value = "answer")
+    private String answer;
+
+    /**
+     *
+     */
+    @TableField(exist = false)
+    private String userAnswer;
+
+    /**
+     *
+     */
+    @TableField(value = "analysis")
+    private String analysis;
+
+    /**
+     *
+     */
+    @TableField(value = "number")
+    private Integer number;
+
+    /**
+     *
+     */
+    @TableField(value = "`order`")
+    private Long order;
+
+    /**
+     *
+     */
+    @TableField(value = "childre")
+    private String childre;
+
+}

+ 11 - 0
src/main/java/com/dongzili/easysearch/custom/mapper/ProductRepository.java

@@ -0,0 +1,11 @@
+package com.dongzili.easysearch.custom.mapper;
+
+import com.dongzili.easysearch.custom.entity.Product;
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
+import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
+import org.springframework.stereotype.Repository;
+
+@Repository
+@EnableElasticsearchRepositories
+public interface ProductRepository extends ElasticsearchRepository<Product, String> {
+}

+ 9 - 0
src/main/java/com/dongzili/easysearch/custom/mapper/SiExerciseMapper.java

@@ -0,0 +1,9 @@
+package com.dongzili.easysearch.custom.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dongzili.easysearch.custom.entity.SiExercise;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface SiExerciseMapper extends BaseMapper<SiExercise> {
+}

+ 24 - 0
src/main/java/com/dongzili/easysearch/custom/service/ProductService.java

@@ -0,0 +1,24 @@
+package com.dongzili.easysearch.custom.service;
+
+import com.dongzili.easysearch.custom.entity.Product;
+import com.dongzili.easysearch.custom.mapper.ProductRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ProductService {
+    private final ProductRepository productRepository;
+
+    @Autowired
+    public ProductService(ProductRepository productRepository) {
+        this.productRepository = productRepository;
+    }
+
+    public Product saveProduct(Product product) {
+        return productRepository.save(product);
+    }
+
+    public Product findProductById(String id) {
+        return productRepository.findById(id).orElse(null);
+    }
+}

+ 48 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,48 @@
+
+
+
+spring:
+  elasticsearch:
+    uris: http://localhost:9222
+    username: admin
+    password: o0zMBXvpYX7vELNJjrmDTA==
+#    ssl:
+#      verification-mode: none
+
+  datasource:
+    driver-class-name: org.easysearch.jdbc.Driver
+    url: jdbc:easysearch://localhost:9222?ssl=false
+    username: admin
+    password: o0zMBXvpYX7vELNJjrmDTA==
+    hikari:
+      maximum-pool-size: 10
+      minimum-idle: 5
+      idle-timeout: 30000
+      max-lifetime: 1800000
+      connection-timeout: 30000
+      connection-test-query: SELECT 1
+      read-only: true
+
+
+
+
+# mybatis 相关 configurations
+mybatis-plus:
+  # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
+  mapper-locations: classpath:mapper/*.xml
+  # 以下配置均有默认值,可以不设置
+  global-config:
+    db-config:
+      # 主键类型,大小写不敏感 AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID"
+      id-type: auto
+#      db-type: mysql  # 或者尝试 'mysql'
+  configuration:
+    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
+    #    mapUnderscoreToCamelCase: false
+    jdbc-type-for-null: 'NULL'
+    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
+    call-setters-on-nulls: true
+    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    map-underscore-to-camel-case: false
+

+ 8 - 0
src/main/resources/application.yml

@@ -0,0 +1,8 @@
+
+# 应用服务 WEB 访问端口
+server:
+  port: 8066
+
+spring:
+  profiles:
+    active: dev

+ 6 - 0
src/main/resources/static/index.html

@@ -0,0 +1,6 @@
+<html>
+<body>
+<h1>hello word!!!</h1>
+<p>this is a html page</p>
+</body>
+</html>

+ 13 - 0
src/test/java/com/dongzili/easysearch/EasySearchDemoApplicationTests.java

@@ -0,0 +1,13 @@
+package com.dongzili.easysearch;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class EasySearchDemoApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}

+ 117 - 0
src/test/java/com/dongzili/easysearch/custom/ProductServiceIntegrationTest.java

@@ -0,0 +1,117 @@
+package com.dongzili.easysearch.custom;
+
+import com.dongzili.easysearch.custom.entity.Product;
+import com.dongzili.easysearch.custom.service.ProductService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
+import org.springframework.data.elasticsearch.core.IndexOperations;
+import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
+import org.springframework.data.elasticsearch.core.document.Document;
+import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
+import org.springframework.data.elasticsearch.core.query.IndexQuery;
+import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
+
+import java.sql.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@SpringBootTest
+class ProductServiceIntegrationTest {
+    @Autowired
+    private ElasticsearchOperations elasticsearchOperations;
+
+    @Autowired
+    private ProductService productService;
+
+    private static final String INDEX_NAME = "products";
+
+    @BeforeEach
+    public void setUp() {
+        IndexOperations indexOperations = elasticsearchOperations.indexOps(IndexCoordinates.of(INDEX_NAME));
+        if (indexOperations.exists()) {
+            indexOperations.delete();
+        }
+
+        // 定义 mapping
+        Document mapping = Document.create()
+                .append("properties", Document.create()
+                        .append("name", Document.create()
+                                .append("type", "text")
+                                .append("analyzer", "standard"))
+                        .append("price", Document.create()
+                                .append("type", "double")));
+
+        // 创建索引并应用 mapping
+        indexOperations.create(Collections.EMPTY_MAP, mapping);
+    }
+
+    @Test
+    public void testSaveAndFindProduct() {
+         List<Product> products = Arrays.asList(
+                new Product("Test Product1", 99.99),
+                new Product("Test Product2", 199.99),
+                new Product("Test Product3", 299.99)
+        );
+
+        List<IndexQuery> queries = products.stream()
+            .map(product -> new IndexQueryBuilder()
+                .withObject(product)
+                .withIndex(INDEX_NAME)
+                .build())
+            .collect(Collectors.toList());
+
+        List<IndexedObjectInformation> indexedInfos = elasticsearchOperations.bulkIndex(
+            queries,
+            IndexCoordinates.of(INDEX_NAME)
+        );
+
+        // 验证结果
+        List<String> ids = indexedInfos.stream()
+            .map(IndexedObjectInformation::getId)
+            .collect(Collectors.toList());
+
+        assertFalse(ids.isEmpty());
+        assertEquals(products.size(), ids.size());
+    }
+
+
+    @Test
+    public void testSQL() {
+
+        testSaveAndFindProduct();
+
+        String url = "jdbc:easysearch://localhost:9222/";
+        Properties properties = new Properties();
+        properties.put("user", "admin");
+        properties.put("password", "o0zMBXvpYX7vELNJjrmDTA==");
+        properties.put("ssl", "false");
+
+        try(Connection con = DriverManager.getConnection(url, properties)){
+            Statement st = con.createStatement();
+            ResultSet rs;
+            //rs = st.executeQuery("SELECT firstname, lastname FROM accounts");
+            rs = st.executeQuery("select * from products");
+            while (rs.next()) {
+                String name = rs.getString("name");
+                double price = rs.getDouble("price");
+                String clazz = rs.getString("_class");
+                System.err.println("name: " + name + " price:" + price + " class:" + clazz);
+            }
+
+            rs.close();
+            st.close();
+
+        }catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 46 - 0
src/test/java/com/dongzili/easysearch/custom/SiExerciseServiceTest.java

@@ -0,0 +1,46 @@
+package com.dongzili.easysearch.custom;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.dongzili.easysearch.common.mybatisplus.MybatisPlusUtil;
+import com.dongzili.easysearch.custom.entity.Product;
+import com.dongzili.easysearch.custom.entity.SiExercise;
+import com.dongzili.easysearch.custom.service.ProductService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
+import org.springframework.data.elasticsearch.core.IndexOperations;
+import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
+import org.springframework.data.elasticsearch.core.document.Document;
+import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
+import org.springframework.data.elasticsearch.core.query.IndexQuery;
+import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
+
+import java.sql.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@SpringBootTest
+class SiExerciseServiceTest {
+
+
+    @Test
+    public void testSQL() {
+
+        LambdaQueryWrapper<SiExercise> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(SiExercise::getIssue, "医学");
+
+        IPage<SiExercise> pages = MybatisPlusUtil.getMapper(SiExercise.class).selectPage(new Page<>(1, 10), wrapper);
+        System.out.println(JSONUtil.toJsonStr(pages));
+    }
+}