Elasticsearch基本使用初体验02

1.Java API操作ES

1.1 创建项目

创建spring Boot工程,添加相关的依赖。

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.79</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
		</dependency>	
	</dependencies>

1.2 创建配置类

创建一个配置类,专门用来获取es客户端对象;创建config包,在该包下创建EsConfig.java类。

@Configuration
public class EsConfig {

    // 该对象可以对我们的ES进行相关的操作
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("127.0.0.1",9200,"http")));
        
        return client;
    }
}

1.3 操作索引

1.3.1 创建索引
@SpringBootTest
class SpringbootEsApplicationTests {

	@Autowired
	private RestHighLevelClient client;


	@Test
	public void testCreate() throws IOException{
		// 创建索引 - 请求对象
		CreateIndexRequest request = new CreateIndexRequest("user");
		// 发送请求,获取响应
		CreateIndexResponse response = client.indices().create(request,
				RequestOptions.DEFAULT);
		boolean acknowledged = response.isAcknowledged();
		// 响应状态
		System.out.println("操作状态 = " + acknowledged);

		// 关闭客户端连接
		client.close();

	}
}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中成功添加了新的user索引。
在这里插入图片描述

1.3.2 查询索引
	@Test
	public void testSelect() throws IOException{
		// 查询索引 - 请求对象
		GetIndexRequest request = new GetIndexRequest("user");
		// 发送请求,获取响应
		GetIndexResponse response = client.indices().get(request,
				RequestOptions.DEFAULT);

		System.out.println("aliases:"+response.getAliases());
		System.out.println("mappings:"+response.getMappings());
		System.out.println("settings:"+response.getSettings());

		client.close();
	}

在这里插入图片描述

1.3.3 删除索引
	@Test
	public void testDelete() throws IOException{
		// 删除索引 - 请求对象
		DeleteIndexRequest request = new DeleteIndexRequest("user");
		// 发送请求,获取响应
		AcknowledgedResponse response = client.indices().delete(request,RequestOptions.DEFAULT);
		// 操作结果
		System.out.println("操作结果 : " + response.isAcknowledged());
		client.close();
	}

在这里插入图片描述
使用Postman,发送请求可以查看一下es中user索引已经不存在了。
在这里插入图片描述

1.4 操作文档

1.4.1 新增文档

创建User数据对象,以便后续使用。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;
    private String name;
    private Integer age;
    private String sex;
}
    @Test
	public void testInsert() throws Exception{
		// 新增文档 - 请求对象
		IndexRequest request = new IndexRequest();
		// 设置索引及唯一性标识
		request.index("user").id("1001");

		// 创建数据对象
		User user = new User();
		user.setName("picacho");
		user.setAge(18);
		user.setSex("男");

		ObjectMapper objectMapper = new ObjectMapper();
		String productJson = objectMapper.writeValueAsString(user);
		// 添加文档数据,数据格式为 JSON 格式
		request.source(productJson, XContentType.JSON);
		// 客户端发送请求,获取响应对象
		IndexResponse response = client.index(request, RequestOptions.DEFAULT);

		System.out.println("_index:" + response.getIndex());
		System.out.println("_id:" + response.getId());
		System.out.println("_result:" + response.getResult());
	}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中成功添加了新的文档内容。
在这里插入图片描述

1.4.2 查询文档
    @Test
	public void testSelectDoc() throws Exception{
		//1.创建请求对象
		GetRequest request = new GetRequest().index("user").id("1001");
		//2.客户端发送请求,获取响应对象
		GetResponse response = client.get(request, RequestOptions.DEFAULT);

		System.out.println("_index:" + response.getIndex());
		System.out.println("_type:" + response.getType());
		System.out.println("_id:" + response.getId());
		System.out.println("source:" + response.getSourceAsString());
	}

在这里插入图片描述

1.4.3 修改文档
    @Test
	public void testUpdateDoc() throws Exception{
		// 修改文档 - 请求对象
		UpdateRequest request = new UpdateRequest();
		// 配置修改参数
		request.index("user").id("1001");
		// 设置请求体,对数据进行修改
		request.doc(XContentType.JSON, "age", 19);
		// 客户端发送请求,获取响应对象
		UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
		System.out.println("_index:" + response.getIndex());
		System.out.println("_id:" + response.getId());
		System.out.println("_result:" + response.getResult());
	}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中成功修改了文档的内容。
在这里插入图片描述

1.4.5 删除文档
@Test
	public void testDeleteDoc()throws Exception{
		//创建请求对象
		DeleteRequest request = new DeleteRequest().index("user").id("1001");
		//客户端发送请求,获取响应对象
		DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
		//打印信息
		System.out.println(response.toString());
	}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中已经没有该文档的内容了。
在这里插入图片描述

1.4.5 批量新增文档
    @Test
	public void testBatchInsertDoc() throws Exception{
		//创建批量新增请求对象
		BulkRequest request = new BulkRequest();
		request.add(new
				IndexRequest().index("user").id("1001").source(XContentType.JSON, "name",
				"picacho"));
		request.add(new
				IndexRequest().index("user").id("1002").source(XContentType.JSON, "name",
				"picacho1"));
		request.add(new
				IndexRequest().index("user").id("1003").source(XContentType.JSON, "name",
				"picacho2"));
		//客户端发送请求,获取响应对象
		BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
		//打印结果信息
		System.out.println("took:" + responses.getTook());
		System.out.println("items:" + responses.getItems());
	}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中新增文档的内容了。
在这里插入图片描述

1.4.6 批量删除文档
@Test
	public void testBatchDeleteDoc() throws Exception{
		//创建批量删除请求对象
		BulkRequest request = new BulkRequest();
		request.add(new DeleteRequest().index("user").id("1001"));
		request.add(new DeleteRequest().index("user").id("1002"));
		request.add(new DeleteRequest().index("user").id("1003"));
		//客户端发送请求,获取响应对象
		BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
		//打印结果信息
		System.out.println("took:" + responses.getTook());
		System.out.println("items:" + responses.getItems());

	}

在这里插入图片描述
可以看到操作成功了,使用Postman,发送请求可以看到es中新增文档的内容已经不见了。
在这里插入图片描述

1.5 复杂文档操作

首先需要插入多条文档数据,方便后续操作展示所用。

@Test
    public void testBatchInsertDoc1() throws Exception{
		//创建批量新增请求对象
		BulkRequest request = new BulkRequest();
		request.add(new IndexRequest().index("user").id("1001").source(XContentType.JSON, "name", "picacho", "age", "18", "sex","男"));
		request.add(new IndexRequest().index("user").id("1002").source(XContentType.JSON, "name", "picacho1", "age", "19", "sex","女"));
		request.add(new IndexRequest().index("user").id("1003").source(XContentType.JSON, "name", "picacho2", "age", "20", "sex","男"));
		request.add(new IndexRequest().index("user").id("1004").source(XContentType.JSON, "name", "picacho3", "age", "20", "sex","女"));
		request.add(new IndexRequest().index("user").id("1005").source(XContentType.JSON, "name", "picacho4", "age", "22", "sex","男"));
		request.add(new IndexRequest().index("user").id("1006").source(XContentType.JSON, "name", "picacho5", "age", "27", "sex","男"));
		//客户端发送请求,获取响应对象
		BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
		//打印结果信息
		System.out.println("took:" + responses.getTook());
		System.out.println("items:" + responses.getItems());
	}
1.5.1 查询所有索引数据
@Test
	public void testQueryDoc() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		// 查询所有数据
		sourceBuilder.query(QueryBuilders.matchAllQuery());
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
	    SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
	}

在这里插入图片描述

1.5.2 条件查询
@Test
	public void testQueryDocByAge() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.query(QueryBuilders.termQuery("age", "22"));
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.3 分页查询
@Test
	public void testQueryPage() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.query(QueryBuilders.matchAllQuery());
		// 分页查询
		// 当前页其实索引(第一条数据的顺序号), from
		sourceBuilder.from(0);
		// 每页显示多少条 size
		sourceBuilder.size(2);
		
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.4 查询后排序
    @Test
	public void testQueryOrder() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");

		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.query(QueryBuilders.matchAllQuery());
		// 排序
		sourceBuilder.sort("age", SortOrder.ASC);
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.5 多条件组合查询
@Test
	public void testQuery() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
		// 必须包含
		boolQueryBuilder.must(QueryBuilders.matchQuery("age", "22"));
		// 一定不含
		boolQueryBuilder.mustNot(QueryBuilders.matchQuery("name", "picacho5"));
		// 可能包含
		boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "男"));
		sourceBuilder.query(boolQueryBuilder);
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.6 范围查询
@Test
	public void testQuery1() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
		// 大于等于
		//rangeQuery.gte("25");
		// 小于等于
		rangeQuery.lte("22");
		sourceBuilder.query(rangeQuery);
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.7 模糊查询
@Test
	public void testQuery2() throws Exception{
		// 创建搜索请求对象
		SearchRequest request = new SearchRequest();
		request.indices("user");
		// 构建查询的请求体
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.query(QueryBuilders.fuzzyQuery("name","picacho").fuzziness(Fuzziness.ONE));
		request.source(sourceBuilder);
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		// 查询匹配
		SearchHits hits = response.getHits();
		System.out.println("took:" + response.getTook());
		System.out.println("timeout:" + response.isTimedOut());
		System.out.println("total:" + hits.getTotalHits());
		System.out.println("MaxScore:" + hits.getMaxScore());
		System.out.println("hits========>>");
		for (SearchHit hit : hits) {
			//输出每条查询的结果信息
			System.out.println(hit.getSourceAsString());
		}
		System.out.println("<<========");
	}

在这里插入图片描述

1.5.8 分组查询
@Test
	public void testQuery3() throws Exception{
		SearchRequest request = new SearchRequest().indices("user");
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.aggregation(AggregationBuilders.terms("ageGroupBy").field("age"));
		//设置请求体
		request.source(sourceBuilder);
		//3.客户端发送请求,获取响应对象
		SearchResponse response = client.search(request, RequestOptions.DEFAULT);
		//4.打印响应结果
		SearchHits hits = response.getHits();
		System.out.println(response);
	}

在这里插入图片描述

2.Spring Boot项目集成ES

Spring Data Elasticsearch基于Spring Data API简化 Elasticsearch 操作,将原始操作Elasticsearch的客户端API进行封装。Spring Data为Elasticsearch项目提供集成搜索引擎。

2.1 添加主配置

# es 服务地址
elasticsearch.host=127.0.0.1
# es 服务端口
elasticsearch.port=9200
# 配置日志级别,开启 debug 日志
logging.level.com.picahco.springbootes=debug

2.2 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "shopping", shards = 3, replicas = 1)
public class Product {
    //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id
    private Long id;

    /**
     * type : 字段数据类型
     * analyzer : 分词器类型
     * index : 是否索引(默认:true)
     * Keyword : 短语,不进行分词
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;//商品名称

    @Field(type = FieldType.Keyword)
    private String category;//分类名称

    @Field(type = FieldType.Double)
    private Double price;//商品价格

    @Field(type = FieldType.Keyword, index = false)
    private String images;//图片地址
}

2.3 添加配置类

和其他spring项目中的 template类似,ElasticsearchRestTemplate是spring-data-elasticsearch项目中的一个类,用来与ES进行交互。

ElasticsearchRestTemplate基于RestHighLevelClient客户端的。需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象。

@ConfigurationProperties(prefix = "elasticsearch")
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration{

    private String host;
    private Integer port;

    //重写父类方法
    @Override
    public RestHighLevelClient elasticsearchClient() {
        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
        RestHighLevelClient restHighLevelClient = new
                RestHighLevelClient(builder);
        return restHighLevelClient;
    }
}

2.4 创建DAO数据访问对象

@Repository
public interface ProductDao extends ElasticsearchRepository<Product, Long>{

}   

2.4 测试索引

2.4.1 创建索引
@SpringBootTest
public class SpringDataESIndexTest {

    //注入 ElasticsearchRestTemplate
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Test
    public void createIndex(){
        //创建索引,系统初始化会自动创建索引
        System.out.println("创建索引");
    }
}

在这里插入图片描述
在这里插入图片描述

2.4.2 删除索引
@Test
    public void deleteIndex(){
        //创建索引,系统初始化会自动创建索引
        boolean flg = elasticsearchRestTemplate.indexOps(Product.class).delete();
        System.out.println("删除索引 = " + flg);
    }

在这里插入图片描述

2.5 测试文档

2.5.1 新增文档
@SpringBootTest
public class SpringDataESProductDaoTest {

    @Autowired
    private ProductDao productDao;
    /**
     * 新增
     */
    @Test
    public void save(){
        Product product = new Product();
        product.setId(2L);
        product.setTitle("华为手机");
        product.setCategory("手机");
        product.setPrice(2999.0);
        product.setImages("http://www.picacho/1.jpg");
        productDao.save(product);
    }
}

在这里插入图片描述

2.5.2 查询文档
    @Test
    public void findById(){
        Product product = productDao.findById(2L).get();
        System.out.println(product);
    }

在这里插入图片描述

2.5.3 修改文档
@Test
    public void update(){
        Product product = new Product();
        product.setId(2L);
        product.setTitle("小米12");
        product.setCategory("手机");
        product.setPrice(4999.0);
        product.setImages("http://www.picacho/2.jpg");
        productDao.save(product);
    }

在这里插入图片描述

2.5.4 删除文档
@Test
    public void delete(){
        Product product = new Product();
        product.setId(2L);
        productDao.delete(product);
    }

在这里插入图片描述

2.5.5 批量新增
@Test
    public void saveAll(){
        List<Product> productList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Product product = new Product();
            product.setId(Long.valueOf(i));
            product.setTitle("["+i+"]小米手机");
            product.setCategory("手机");
            product.setPrice(4999.0 + i);
            product.setImages("http://www.picacho/1.jpg");
            productList.add(product);
        }
        productDao.saveAll(productList);
    }

在这里插入图片描述

2.5.6 分页查询
@Test
    public void findByPageable(){
        //设置排序(排序方式,正序还是倒序,排序的 id)
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        int currentPage=0;//当前页,第一页从 0 开始, 1 表示第二页
        int pageSize = 5;//每页显示多少条
        //设置查询分页
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
        //分页查询
        Page<Product> productPage = productDao.findAll(pageRequest);
        for (Product Product : productPage.getContent()) {
            System.out.println(Product);
        }
    }

在这里插入图片描述
在Spring Boot项目中集成使用ES时,有一个很重要的点需要注意,不同版本的ES提供的方法可能存在差异,所以需要知道自己使用的那个版本后,再对应使用方法。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Elasticsearch 是一种开源的搜索引擎,它是基于 Lucene 搜索引擎库构建的。它提供了一个分布式、多租户的全文搜索引擎,可以用于存储、搜索和分析海量数据。 以下是 Elasticsearch基本使用: 1. 安装 Elasticsearch:可以从官方网站下载 Elasticsearch,然后按照指南进行安装。 2. 运行 Elasticsearch:启动 Elasticsearch,可以在命令行中运行 elasticsearch 命令。 3. 创建索引:在 Elasticsearch 中,数据被组织成一个或多个索引。可以使用 PUT 请求创建索引,例如:`PUT /myindex`。 4. 添加文档:将文档添加到索引中,可以使用 POST 请求,例如:`POST /myindex/_doc`。在请求体中指定文档内容。 5. 查询文档:使用 GET 请求查询文档,例如:`GET /myindex/_search?q=test`。这将返回所有包含 "test" 的文档。 6. 删除文档:使用 DELETE 请求删除文档,例如:`DELETE /myindex/_doc/1`。这将删除索引为 "myindex" 中 ID 为 "1" 的文档。 7. 更新文档:使用 POST 请求更新文档,例如:`POST /myindex/_update/1`。在请求体中指定要更新的字段和值。 8. 聚合查询:可以使用聚合查询对文档进行分组和计算。例如:`GET /myindex/_search?size=0&aggs=group_by_country:terms:field:country`,这将返回按照国家分组并计算数量的结果。 以上是 Elasticsearch基本使用,还有很多高级特性可以探索。了解 Elasticsearch 的更多功能和用法,请参阅官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

picacho_pkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值