SpringBoot集成ElasticSearch


前言

被逼无奈,各行各业都在卷,给自己充电学习了Elasticsearch,在学完基础知识后(其实就是CRUD😂),就去Springboot中尝试整合ES。终于抽出时间,将学习的东西整理分享在此,欢迎大家批评指正哈~


一、ElasticSearch本地环境搭建

详细安装步骤参考此资料windows 安装 Elasticsearch

二、SpringBoot整合ElasticSearch

我这里Spring Boot版本2.7.5,ElasticSearch版本7.17.3。版本一定要对应上,否则会报一堆错误问题。
SpringBoot官方推荐对应Elasticsearch版本。
在这里插入图片描述
先启动ES本地服务(双击elasticsearch.bat启动服务),然后在elasticsearch-head可视化插件目录中执行npm start run启动可视化插件。
在这里插入图片描述
在这里插入图片描述

1.pom中引入ES依赖

<!-- ElasticSearch -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>elasticsearch-java</artifactId>
                    <groupId>co.elastic.clients</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>8.0.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>jakarta.json-api</artifactId>
                    <groupId>jakarta.json</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
            <version>2.0.1</version>
        </dependency>

2.application.yaml配置elasticsearch

elasticsearch:
  port: 9200
  ip: 127.0.0.1
  username: elastic
  password: 123456

3.ElasticSearchClientConnect连接ES客户端工具类

@Configuration
public class ElasticSearchClientConnect {
    @Value("${elasticsearch.port}")
    private int port;

    @Value("${elasticsearch.ip}")
    private String ip;

    @Value("${elasticsearch.username}")
    private String username;

    @Value("${elasticsearch.password}")
    private String password;

    /**
     * 创建rest客户端
     */
    @Bean
    public ElasticSearchResult restClient(){
        RestClient restClient = RestClient.builder(
                new HttpHost(ip, port,"http")).build();
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);
        return new ElasticSearchResult(restClient,transport,client);
    }
}

4.ElasticSearchResult封装响应结果

@Data
public class ElasticSearchResult {

    private RestClient restClient;

    private ElasticsearchTransport elasticsearchTransport;

    private ElasticsearchClient elasticsearchClient;

    public ElasticSearchResult(RestClient restClient, ElasticsearchTransport elasticsearchTransport, ElasticsearchClient elasticsearchClient) {
        this.restClient = restClient;
        this.elasticsearchTransport = elasticsearchTransport;
        this.elasticsearchClient = elasticsearchClient;
    }
}

5.Person实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private String sex;
    private Integer age;
}

6.ElasticsearchController增删改查控制层

/**
 * ES增删改查
 */
@RestController
public class ElasticsearchController {

    @Autowired
    private ElasticSearchClientConnect elasticSearchClientConfig;

    /**
     * 新建my_index索引
     * @return
     * @throws IOException
     */
    @GetMapping("/createIndex")
    public Boolean createIndex() throws IOException {

        CreateIndexResponse createIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().create(c -> c.index("my_index"));
        // 打印结果
        System.out.println(createIndexResponse.acknowledged());
        // 关闭连接

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
        return createIndexResponse.acknowledged();
    }

    /**
     * 查询索引
     * @throws IOException
     */
    @GetMapping("/selectIndex")
    public void selectIndex() throws IOException {

        GetIndexResponse getIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().get(e -> e.index("jing_index"));

        // 打印结果
        System.out.println("getIndexResponse.result() = " + getIndexResponse.result());
        System.out.println("getIndexResponse.result().keySet() = " + getIndexResponse.result().keySet());

        // 关闭连接
        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 删除索引
     * @return
     * @throws IOException
     */
    @GetMapping("/deleteIndex")
    public Boolean deleteIndex() throws IOException {
        // 删除索引
        DeleteIndexResponse deleteIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().delete(e -> e.index("jing_index"));
        System.out.println("删除操作 = " + deleteIndexResponse.acknowledged());
        // 关闭连接
        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
        return deleteIndexResponse.acknowledged();
    }

}

7.ElasticsearchDocumentController文档操作控制层

/**
 * ES文档操作
 */
@RestController
public class ElasticsearchDocumentController {

    @Autowired
    private ElasticSearchClientConnect elasticSearchClientConfig;

    /**
     * 添加document
     * @throws IOException
     */
    @GetMapping("/addDocument")
    public void addDocument() throws IOException {

        // 向Person对象中添加数据
        Person Person = new Person("java客户端", "男", 18);
        // 向索引中添加数据
        CreateResponse createResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().create(e -> e.index("jing_index").id("1001").document(Person));
        System.out.println("createResponse.result() = " + createResponse.result());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 查询document
     * @throws IOException
     */
    @GetMapping("/queryDocument")
    public void queryDocument() throws IOException {
        // 构建请求
        GetResponse<Person> getResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().get(e -> e.index("jing_index").id("1001"), Person.class);
        System.out.println("getResponse.source().toString() = " + getResponse.source().toString());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 修改document
     * @throws IOException
     */
    @GetMapping("/modifyDocument")
    public void modifyDocument() throws IOException {

        // 使用map集合封装需要修改的内容
        Map<String, Object> map = new HashMap<>();
        map.put("name", "java客户端aaa");
        // 构建请求
        UpdateResponse<Person> updateResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().update(e -> e.index("jing_index").id("1001").doc(map), Person.class);
        System.out.println("updateResponse.result() = " + updateResponse.result());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 删除document
     * @throws IOException
     */
    @GetMapping("/removeDocument")
    public void removeDocument() throws  IOException {

        // 构建请求
        DeleteResponse deleteResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().delete(e -> e.index("jing_index").id("1001"));
        System.out.println("deleteResponse.result() = " + deleteResponse.result());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 批量添加document
     * @throws IOException
     */
    @GetMapping("/batchAddDocument")
    public void batchAddDocument() throws IOException {

        // 构建一个批量数据集合
        List<BulkOperation> list = new ArrayList<>();
        list.add(new BulkOperation.Builder().create(
                d -> d.document(new Person("test2", "男", 19)).id("1002").index("Person_test")).build());
        list.add(new BulkOperation.Builder().create(
                d -> d.document(new Person("test3", "男", 20)).id("1003").index("Person_test")).build());
        list.add(new BulkOperation.Builder().create(
                d -> d.document(new Person("test4", "女", 21)).id("1004").index("Person_test")).build());
        // 调用bulk方法执行批量插入操作
        BulkResponse bulkResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().bulk(e -> e.index("Person_test").operations(list));
        System.out.println("bulkResponse.items() = " + bulkResponse.items());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 批量删除document
     * @throws IOException
     */
    @GetMapping("/batchDeleteDocument")
    public void batchDeleteDocument() throws IOException {

        // 构建一个批量数据集合
        List<BulkOperation> list = new ArrayList<>();
        list.add(new BulkOperation.Builder().delete(
                d -> d.id("1002").index("Person_test")).build());
        list.add(new BulkOperation.Builder().delete(
                d -> d.id("1003").index("Person_test")).build());
        // 调用bulk方法执行批量插入操作
        BulkResponse bulkResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().bulk(e -> e.index("Person_test").operations(list));
        System.out.println("bulkResponse.items() = " + bulkResponse.items());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 全量查询document
     * @throws IOException
     */
    @GetMapping("/queryAllDocument")
    public void queryAllDocument() throws IOException {

        // 全量查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(e -> e.index("Person_test").query(q -> q.matchAll(m -> m)), Person.class);
        HitsMetadata<Person> hits = searchResponse.hits();
        for (Hit<Person> hit : hits.hits()) {
            System.out.println("Person = " + hit.source().toString());
        }
        System.out.println("searchResponse.hits().total().value() = " + searchResponse.hits().total().value());

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 分页查询document
     * @throws IOException
     */
    @GetMapping("/pagingQueryDocument")
    public void pagingQueryDocument() throws IOException {

        // 分页查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(
                s -> s.index("Person_test")
                        .query(q -> q.matchAll(m -> m))
                        .from(0)
                        .size(2)
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));
        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 排序查询document
     * @throws IOException
     */
    @GetMapping("/sortQueryDocument")
    public void sortQueryDocument() throws IOException {

        // 排序查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(
                s -> s.index("Person_test")
                        .query(q -> q.matchAll(m -> m))
                        .sort(o -> o.field(f -> f.field("age").order(SortOrder.Asc)))
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 条件查询document
     * @throws IOException
     */
    @GetMapping("/conditionQueryDocument")
    public void conditionQueryDocument() throws IOException {

        // 条件查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(
                s -> s.index("Person_test").query(q -> q.matchAll(m -> m))
                        .sort(o -> o.field(f -> f.field("age").order(SortOrder.Asc)))
                        .source(r -> r.filter(f -> f.includes("name", "age").excludes("")))
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 组合查询  must是必须满足所有条件,should只要满足一个就行
     * @throws IOException
     */
    @GetMapping("/combinationQueryDocument")
    public void combinationQueryDocument() throws IOException {

        // 组合查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(
                s -> s.index("Person_test").query(q -> q.bool(b -> b
                        .must(m -> m.match(u -> u.field("age").query(21)))
                        .must(m -> m.match(u -> u.field("sex").query("男")))
                        .mustNot(m -> m.match(u -> u.field("sex").query("女")))
                ))
                , Person.class);


        //SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(
        //                s -> s.index("Person_test").query(q -> q.bool(b -> b
        //                        .should(h -> h.match(u -> u.field("age").query(19)))
        //                        .should(h -> h.match(u -> u.field("sex").query("男")))
        //                ))
        //                , Person.class);

        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 范围查询
     * @throws IOException
     */
    @GetMapping("/scopeQueryDocument2")
    public void scopeQueryDocument2() throws IOException {

        // 范围查询,gte()表示取大于等于,gt()表示大于,lte()表示小于等于
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("Person_test").query(q -> q
                        .range(r -> r.field("age").gte(JsonData.of(20)).lt(JsonData.of(21))))
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }


    /**
     * 模糊查询
     * @throws IOException
     */
    @GetMapping("/fuzzyQueryDocument2")
    public void fuzzyQueryDocument2() throws IOException {

        // 模糊查询,fuzziness表示差几个可以查询出来
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("Person_test").query(q -> q
                        .fuzzy(f -> f.field("name").value("tst").fuzziness("2")))
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 高亮查询
     * @throws IOException
     */
    @GetMapping("/highlightQueryDocument2")
    public void highlightQueryDocument2() throws IOException {

        // 高亮查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("Person_test").query(q -> q
                        .term(t -> t.field("name").value("test4")))
                        .highlight(h -> h.fields("name", f -> f.preTags("<font color='red'>").postTags("</font>")))
                , Person.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }


    /**
     * 聚合查询
     * @throws IOException
     */
    @GetMapping("/aggregateQueryDocument2")
    public void aggregateQueryDocument2() throws IOException {

        // 聚合查询,取最大年龄
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("Person_test").aggregations("maxAge", a ->a.max(m -> m.field("age")))
                , Person.class);
        searchResponse.aggregations().entrySet().forEach(f -> System.out.println(f.getKey() + ":" + f.getValue().max().value()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }

    /**
     * 分组查询
     * @throws IOException
     */
    @GetMapping("/groupQueryDocument2")
    public void groupQueryDocument2() throws IOException {

        // 分组查询
        SearchResponse<Person> searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("Person_test")
                        .aggregations("ageGroup", a ->a.terms(t -> t.field("age")))
                , Person.class);
        searchResponse.aggregations().get("ageGroup").lterms().buckets().array().forEach(f -> System.out.println(f.key() + ":" + f.docCount()));

        elasticSearchClientConfig.restClient().getElasticsearchTransport().close();
        elasticSearchClientConfig.restClient().getRestClient().close();
    }
}

三、测试

1.创建索引,名为my_index

在这里插入图片描述

在这里插入图片描述
查询结果显示创建成功
在这里插入图片描述

2.创建文档,给名为my_index的索引创建文档

在这里插入图片描述
在这里插入图片描述
查询结果显示创建文档成功
在这里插入图片描述

四、项目结构及代码下载

在这里插入图片描述
源码下载地址,欢迎Star哦~~
springboot-cacheable


参考资料

SpringBoot官方推荐对应Elasticsearch版本
SpringBoot集成elasticsearch 7.17.3及常规应用
Elasticsearch8.x版本中RestHighLevelClient被弃用
SpringBoot 整合ElasticSearch实现模糊查询,批量CRUD,排序,分页,高亮
windows 安装 Elasticsearch

### 回答1: Elasticsearch是一个开源的分布式全文搜索和分析引擎,而Spring BootJava开发的一种快速开发框架。将两者整合可以提供一个强大的搜索和分析功能的应用程序。 使用Spring Boot整合Elasticsearch的过程如下: 1. 首先,在pom.xml文件中添加ElasticsearchSpring Boot相关的依赖项。例如,添加spring-boot-starter-data-elasticsearch依赖来使用Spring Data Elasticsearch模块。 2. 在应用程序的配置文件中,配置Elasticsearch的连接参数,如主机地址、端口号等。可以使用Spring Boot的配置注解来简化配置过程。 3. 创建一个实体类,使用注解定义其映射到Elasticsearch索引的方式。可以使用@Document注解设置索引名称、类型等属性,使用@Field注解设置字段的映射方式。 4. 创建一个Elasticsearch的Repository接口,继承自Spring Data Elasticsearch提供的ElasticsearchRepository接口。可以使用该接口提供的方法来进行索引的增删改查操作。 5. 在需要使用Elasticsearch的业务逻辑中,注入创建的Repository接口实例,通过调用其方法来实现对索引的操作。例如,可以使用save方法保存实体对象到索引中,使用deleteById方法删除索引中的记录。 通过以上步骤,就可以实现ElasticsearchSpring Boot的整合。使用Spring Boot可以极大地简化了配置和开发的过程,而Elasticsearch提供了强大的全文搜索和分析功能,可以为应用程序提供高效的搜索和查询性能。整合后的应用程序可以方便地使用Elasticsearch进行数据索引和搜索操作。 ### 回答2: Elasticsearch是一个开源的搜索引擎,可以用于处理大量数据的搜索、分析和存储。Spring Boot是一个用于快速构建应用程序的框架,可以简化开发过程并提供各种强大功能。 将ElasticsearchSpring Boot整合可以实现在应用程序中使用Elasticsearch进行数据的索引、搜索和分析。下面是一个简单的步骤来实现这个整合: 1. 添加依赖:在Spring Boot项目的pom.xml文件中,添加Elasticsearch相关的依赖。例如,可以使用spring-boot-starter-data-elasticsearch依赖来集成Elasticsearch。 2. 配置连接:在Spring Boot的配置文件中,配置Elasticsearch连接的相关信息,如主机地址、端口号、用户名和密码等。 3. 创建实体类:根据需要,创建要在Elasticsearch中索引的实体类。实体类一般使用注解来标识其在Elasticsearch中的索引和字段的映射关系。 4. 创建Repository:使用Spring Data Elasticsearch提供的@Repository注解来创建Elasticsearch的Repository接口。这个接口可以继承ElasticsearchRepository,并提供一些自定义的查询方法。 5. 编写业务逻辑:在Service层编写业务逻辑,调用自定义的Repository接口方法来对Elasticsearch进行操作。 6. 启动应用程序:使用Spring Boot的注解@SpringBootApplication来启动应用程序,其中会自动加载Elasticsearch的配置和相关的依赖。 通过以上步骤,我们就成功地将Elasticsearch整合到了Spring Boot应用程序中。可以通过访问RESTful接口来对Elasticsearch进行索引、搜索和分析等操作。此外,Spring Boot还提供了自动化配置和简化开发的特性,让整合过程更加方便快捷。 总结起来,通过整合ElasticsearchSpring Boot,我们可以利用Elasticsearch强大的搜索和存储功能来处理大量的数据,并结合Spring Boot框架的优势快速构建应用程序。 ### 回答3: Elasticsearch是一个基于Lucene的开源搜索和分析引擎,而Spring Boot是一个使用Java快速构建生产级别的应用程序的框架。将ElasticsearchSpring Boot整合可以提供强大的全文搜索和数据分析功能。 首先,我们需要在Spring Boot项目中添加Elasticsearch的依赖。可以通过在pom.xml文件中添加以下代码来实现: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> ``` 然后,我们需要创建一个Elasticsearch的配置类,用于配置Elasticsearch的连接信息。可以通过创建一个类,并添加`@Configuration`和`@EnableElasticsearchRepositories`注解来实现: ```java @Configuration @EnableElasticsearchRepositories(basePackages = "com.example.elasticsearch.repository") public class ElasticsearchConfig { @Value("${elasticsearch.host}") private String host; @Value("${elasticsearch.port}") private int port; @Bean public Client client() throws UnknownHostException { Settings settings = Settings.builder() .put("cluster.name", "elasticsearch").build(); TransportClient client = new PreBuiltTransportClient(settings); client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port)); return client; } @Bean public ElasticsearchTemplate elasticsearchTemplate() throws UnknownHostException { return new ElasticsearchTemplate(client()); } } ``` 在上述代码中,我们通过从配置文件中读取主机和端口信息创建了一个Elasticsearch的客户端连接,并创建了一个用于Elasticsearch操作的ElasticsearchTemplate对象。 接下来,我们可以创建一个持久化层的接口,用于定义Elasticsearch的操作方法。可以通过创建一个接口,并添加`@Repository`注解来实现。例如: ```java @Repository public interface UserRepository extends ElasticsearchRepository<User, String> { List<User> findByName(String name); } ``` 在上述代码中,我们定义了一个UserRepository接口,继承了ElasticsearchRepository接口,并定义了一个按照名字查询用户的方法。 最后,我们可以在业务层或者控制层使用Elasticsearch相关的方法来进行搜索和分析操作。例如,我们可以在服务类中调用UserRepository的方法来实现用户的搜索: ```java @Service public class UserService { @Autowired private UserRepository userRepository; public List<User> searchUser(String name) { return userRepository.findByName(name); } } ``` 通过以上步骤,我们就可以在Spring Boot项目中实现Elasticsearch的整合和使用了。通过配置Elasticsearch连接信息、定义操作方法和调用相关方法,可以方便地实现全文搜索和数据分析的功能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值