溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot整合ElasticSearch實現(xiàn)多版本的兼容

發(fā)布時間:2020-06-14 13:32:31 來源:網(wǎng)絡(luò) 閱讀:1958 作者:沙漏半杯 欄目:編程語言

前言

本篇主要是介紹和使用目前最火的搜索引擎ElastiSearch,并和SpringBoot進(jìn)行結(jié)合使用。

ElasticSearch介紹

ElasticSearch是一個基于Lucene的搜索服務(wù)器,其實就是對Lucene進(jìn)行封裝,提供了 REST API 的操作接口?ElasticSearch作為一個高度可拓展的開源全文搜索和分析引擎,可用于快速地對大數(shù)據(jù)進(jìn)行存儲,搜索和分析。
ElasticSearch主要特點:分布式、高可用、異步寫入、多API、面向文檔 。
ElasticSearch核心概念:近實時,集群,節(jié)點(保存數(shù)據(jù)),索引,分片(將索引分片),副本(分片可設(shè)置多個副本) 。它可以快速地儲存、搜索和分析海量數(shù)據(jù)。
ElasticSearch使用案例:維基百科、Stack Overflow、Github 等等。

SpringBoot整合Elasticsearch

在使用SpringBoot整合Elasticsearch?之前,我們應(yīng)該了解下它們之間對應(yīng)版本的關(guān)系。

Spring Boot Version (x)Spring Data Elasticsearch Version (y)Elasticsearch Version (z)
x <= 1.3.5y <= 1.3.4z <= 1.7.2*
x >= 1.4.x2.0.0 <=y < 5.0.0**2.0.0 <= z < 5.0.0**

這里我們使用的SpringBoot的版本是1.5.9,Elasticsearch的版本是2.3.5。

使用SpringBoot整合Elasticsearch,一般都是使用?SpringData?進(jìn)行封裝的,然后再dao層接口繼承ElasticsearchRepository?類,該類實現(xiàn)了很多的方法,比如常用的CRUD方法。

SpringData的使用

首先,在使用之前,先做好相關(guān)的準(zhǔn)備。

Maven的配置如下:
<dependency>
????????????<groupId>org.springframework.boot</groupId>
????????????<artifactId>spring-boot-starter-web</artifactId>
?????????????<version>1.5.9.RELEASE</version>
????????</dependency>
??<dependency>
????????????<groupId>org.springframework.boot</groupId>
????????????<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
?????????????<version>1.5.9.RELEASE</version>
????????</dependency>
application.properties的配置
spring.data.elasticsearch.repositories.enabled?=?truespring.data.elasticsearch.cluster-nodes?=127.0.0.1\:9300

注: 9300 是 Java 客戶端的端口。9200 是支持 Restful HTTP 的接口。

更多的配置:

spring.data.elasticsearch.cluster-name?Elasticsearch?集群名。(默認(rèn)值:?elasticsearch)spring.data.elasticsearch.cluster-nodes?集群節(jié)點地址列表,用逗號分隔。如果沒有指定,就啟動一個客戶端節(jié)點。spring.data.elasticsearch.propertie?用來配置客戶端的額外屬性。spring.data.elasticsearch.repositories.enabled?開啟?Elasticsearch?倉庫。(默認(rèn)值:true。)
代碼編寫

實體類

@Document(indexName?=?"userindex",?type?=?"user")
public?class?User?implements?Serializable{?????/**?????*??????*/
????private?static?final?long?serialVersionUID?=?1L;????/**?編號?*/
?????private?Long?id;?????/**?姓名?*/
?????private?String?name;?????
?????/**?年齡?*/
?????private?Integer?age;?????
?????/**?描述?*/??
?????private?String?description;?????
?????/**?創(chuàng)建時間?*/
?????private?String?createtm;????//?getter和setter?略}

使用SpringData的時候,它需要在實體類中設(shè)置indexName?和type?,如果和傳統(tǒng)型數(shù)據(jù)庫比較的話,就相當(dāng)于。需要注意的是indexNametype都必須是小寫!!!

dao層

public?interface?UserDao?extends?ElasticsearchRepository<User,?Long>{}

dao層這里就比較簡單了,只需繼承ElasticsearchRepository該類就行了。其中主要的方法就是 save、delete和search。其中save方法相當(dāng)如insert和update,沒有就新增,有就覆蓋。delete方法主要就是刪除數(shù)據(jù)以及索引庫。至于search就是查詢了,包括一些常用的查詢,如分頁、權(quán)重之類的。

Service層

@Servicepublic?class?UserServiceImpl?implements?UserService?{????@Autowired
????private?UserDao?userDao;????@Override
????public?boolean?insert(User?user)?{????????boolean?falg=false;????????try{
????????????userDao.save(user);
????????????falg=true;
????????}catch(Exception?e){
????????????e.printStackTrace();
????????}????????return?falg;
????}????@Override
????public?List<User>?search(String?searchContent)?{
??????????QueryStringQueryBuilder?builder?=?new?QueryStringQueryBuilder(searchContent);
??????????System.out.println("查詢的語句:"+builder);
??????????Iterable<User>?searchResult?=?userDao.search(builder);
??????????Iterator<User>?iterator?=?searchResult.iterator();
??????????List<User>?list=new?ArrayList<User>();??????????while?(iterator.hasNext())?{
????????????list.add(iterator.next());
??????????}???????return?list;
????}????
????
????
????@Override
????public?List<User>?searchUser(Integer?pageNumber,?Integer?pageSize,String?searchContent)?{?????????//?分頁參數(shù)
????????Pageable?pageable?=?new?PageRequest(pageNumber,?pageSize);
????????QueryStringQueryBuilder?builder?=?new?QueryStringQueryBuilder(searchContent);
????????SearchQuery?searchQuery?=?new?NativeSearchQueryBuilder().withPageable(pageable).withQuery(builder).build();
????????System.out.println("查詢的語句:"?+?searchQuery.getQuery().toString());
????????Page<User>?searchPageResults?=?userDao.search(searchQuery);????????return?searchPageResults.getContent();
????}????

????@Override
????public?List<User>?searchUserByWeight(String?searchContent)?{?????//?根據(jù)權(quán)重進(jìn)行查詢
????????FunctionScoreQueryBuilder?functionScoreQueryBuilder?=?QueryBuilders.functionScoreQuery()
????????????????.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name",?searchContent)),
????????????????????ScoreFunctionBuilders.weightFactorFunction(10))
????????????????.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("description",?searchContent)),
????????????????????????ScoreFunctionBuilders.weightFactorFunction(100)).setMinScore(2);
????????System.out.println("查詢的語句:"?+?functionScoreQueryBuilder.toString());
????????Iterable<User>?searchResult?=?userDao.search(functionScoreQueryBuilder);
????????Iterator<User>?iterator?=?searchResult.iterator();
????????List<User>?list=new?ArrayList<User>();????????while?(iterator.hasNext())?{
????????????list.add(iterator.next());
????????}????????return?list;
????}
}

這里我就簡單的寫了幾個方法,其中主要的方法是查詢。查詢包括全文搜索,分頁查詢和權(quán)重查詢。其中需要說明的是權(quán)重查詢這塊,權(quán)重的分值越高,查詢的結(jié)果也越靠前,如果沒有對其它的數(shù)據(jù)設(shè)置分值,它們默認(rèn)的分值就是1,如果不想查詢這些語句,只需使用setMinScore將其設(shè)為大于1即可。

代碼測試

調(diào)用接口進(jìn)行添加數(shù)據(jù)

新增數(shù)據(jù):

POST?http://localhost:8086/api/user{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","createtm":"2018-4-25?11:07:42"}
{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}
{"id":3,"name":"王五","age":25,"description":"王五是個運維工程師","createtm":"2016-8-21?06:11:32"}

進(jìn)行全文查詢
請求

http://localhost:8086/api/user?searchContent=工程師

返回

[{"id":2,"name":"李四","age":14,"description":"李四是個測試工程師","createtm":?"1980-2-15?19:01:32"},
{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師",?"createtm":?"2018-4-25?11:07:42"},
{"id":3,"name":"王五","age":25,"description":"王五是個運維工程師","createtm":?"2016-8-21?06:11:32"}]

進(jìn)行分頁查詢
請求

http://localhost:8086/api/user?pageNumber=0&pageSize=2&searchContent=工程師

返回

[{"id":2,"name":"李四","age":14,"description":"李四是個測試工程師"},{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師"}]

進(jìn)行權(quán)重查詢
請求

http://localhost:8086/api/user2?searchContent=李四

返回

[{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}]

權(quán)重查詢打印的語句:

查詢的語句:{{??"function_score"?:?{????"functions"?:?[?{??????"filter"?:?{????????"bool"?:?{??????????"should"?:?{????????????"match"?:?{??????????????"name"?:?{????????????????"query"?:?"李四",????????????????"type"?:?"boolean"
??????????????}
????????????}
??????????}
????????}
??????},??????"weight"?:?10.0
????},?{??????"filter"?:?{????????"bool"?:?{??????????"should"?:?{????????????"match"?:?{??????????????"description"?:?{????????????????"query"?:?"李四",????????????????"type"?:?"boolean"
??????????????}
????????????}
??????????}
????????}
??????},??????"weight"?:?100.0
????}?],????"min_score"?:?2.0
??}
}

注:測試中,因為設(shè)置了setMinScore最小權(quán)重分為2的,所以無關(guān)的數(shù)據(jù)是不會顯示出來的。如果想顯示的話,在代碼中去掉即可。

新增完數(shù)據(jù)之后,可以在瀏覽器輸入:http://localhost:9200/_plugin/head/
然后點擊基本查詢,便可以查看添加的數(shù)據(jù)。如果想用語句查詢,可以將程序中控制臺打印的查詢語句粘貼到查詢界面上進(jìn)行查詢!
SpringBoot整合ElasticSearch實現(xiàn)多版本的兼容

注:這里的ElasticSearch是我在windows上安裝的,并安裝了ES插件head,具體安裝步驟在文章末尾。

除了SpringData之外,其實還有其它的方法操作ElasticSearch的。
比如使用原生ElasticSearch的Api,使用TransportClient類實現(xiàn)。
或者使用由Spring封裝,只需在Service層,進(jìn)行注入Bean即可。
示例:

@Autowired
?ElasticsearchTemplate?elasticsearchTemplate;

但是,上述方法中都有其局限性,也就是隨著ElasticSearch的版本變更,相關(guān)的Java API也在做不斷的調(diào)整,就是ElasticSearch服務(wù)端版本進(jìn)行更改之后,客戶端的代碼可能需要重新編寫。
因此介紹一個相當(dāng)好用的第三方工具JestClient,它對ElasticSearch進(jìn)行封裝,填補了?ElasticSearch?HttpRest接口 客戶端的空白,它適用于ElasticSearch2.x以上的版本,無需因為ElasticSearch服務(wù)端版本更改而對代碼進(jìn)行更改!

JestClient

首先在Maven中添加如下依賴:

????<dependency>
????????<groupId>io.searchbox</groupId>?
?????????<artifactId>jest</artifactId>
????????<version>5.3.3</version>
????</dependency>

然后編寫相關(guān)的測試代碼。
代碼中的注釋應(yīng)該很完整,所以這里就不再對代碼過多的講述了。

import?java.util.ArrayList;import?java.util.List;import?org.elasticsearch.index.query.QueryBuilders;import?org.elasticsearch.search.builder.SearchSourceBuilder;import?com.pancm.pojo.User;import?io.searchbox.client.JestClient;import?io.searchbox.client.JestClientFactory;import?io.searchbox.client.JestResult;import?io.searchbox.client.config.HttpClientConfig;import?io.searchbox.core.Bulk;import?io.searchbox.core.BulkResult;import?io.searchbox.core.Delete;import?io.searchbox.core.DocumentResult;import?io.searchbox.core.Index;import?io.searchbox.core.Search;import?io.searchbox.indices.CreateIndex;import?io.searchbox.indices.DeleteIndex;import?io.searchbox.indices.mapping.GetMapping;import?io.searchbox.indices.mapping.PutMapping;public?class?JestTest?{??
????????private?static?JestClient?jestClient;??
????????private?static?String?indexName?=?"userindex";??
//??????private?static?String?indexName?=?"userindex2";??
????????private?static?String?typeName?=?"user";??
????????private?static?String?elasticIps="http://192.169.2.98:9200";//??????private?static?String?elasticIps="http://127.0.0.1:9200";
????????
????????
????????public?static?void?main(String[]?args)?throws?Exception?{
????????????jestClient?=?getJestClient();??
????????????insertBatch();
????????????serach2();
????????????serach3();
????????????serach4();
????????????jestClient.close();??
????????????
????????}????????
????????private?static??JestClient?getJestClient()?{??
????????????JestClientFactory?factory?=?new?JestClientFactory();??
????????????factory.setHttpClientConfig(new?HttpClientConfig.Builder(elasticIps).connTimeout(60000).readTimeout(60000).multiThreaded(true).build());??
????????????return?factory.getObject();??
????????}??
????????
????????public?static?void?insertBatch()?{
????????????List<Object>?objs?=?new?ArrayList<Object>();
????????????objs.add(new?User(1L,?"張三",?20,?"張三是個Java開發(fā)工程師","2018-4-25?11:07:42"));
????????????objs.add(new?User(2L,?"李四",?24,?"李四是個測試工程師","1980-2-15?19:01:32"));
????????????objs.add(new?User(3L,?"王五",?25,?"王五是個運維工程師","2016-8-21?06:11:32"));????????????boolean?result?=?false;????????????try?{
????????????????result?=?insertBatch(jestClient,indexName,?typeName,objs);
????????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????????}
????????????System.out.println("批量新增:"+result);
????????}????????
????????
????????/**
?????????*?全文搜索
?????????*/
????????public?static?void?serach2()?{
????????????String?query?="工程師";????????????try?{
????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();?
?????????????????searchSourceBuilder.query(QueryBuilders.queryStringQuery(query));?
?????????????????//分頁設(shè)置
?????????????????searchSourceBuilder.from(0).size(2);?
????????????????System.out.println("全文搜索查詢語句:"+searchSourceBuilder.toString());
????????????????System.out.println("全文搜索返回結(jié)果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString()));
????????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????????}
????????}????????
????????/**
?????????*?精確搜索
?????????*/
????????public?static?void?serach3()?{????????????try?{
????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();?
????????????????searchSourceBuilder.query(QueryBuilders.termQuery("age",?24));?
????????????????System.out.println("精確搜索查詢語句:"+searchSourceBuilder.toString());
????????????????System.out.println("精確搜索返回結(jié)果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString()));
????????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????????}
????????}????????
????????
????????/**
?????????*?區(qū)間搜索
?????????*/
????????public?static?void?serach4()?{
????????????String?createtm="createtm";
????????????String?from="2016-8-21?06:11:32";
????????????String?to="2018-8-21?06:11:32";????????????
????????????try?{
????????????????SearchSourceBuilder?searchSourceBuilder?=?new?SearchSourceBuilder();?
????????????????searchSourceBuilder.query(QueryBuilders.rangeQuery(createtm).gte(from).lte(to));?
????????????????System.out.println("區(qū)間搜索語句:"+searchSourceBuilder.toString());
????????????????System.out.println("區(qū)間搜索返回結(jié)果:"+search(jestClient,indexName,?typeName,?searchSourceBuilder.toString()));
????????????}?catch?(Exception?e)?{
????????????????e.printStackTrace();
????????????}
????????}????????
????????
????????/**
?????????*?創(chuàng)建索引
?????????*?@param?indexName
?????????*?@return
?????????*?@throws?Exception
?????????*/
????????public?boolean?createIndex(JestClient?jestClient,String?indexName)?throws?Exception?{??
????????????JestResult?jr?=?jestClient.execute(new?CreateIndex.Builder(indexName).build());??
????????????return?jr.isSucceeded();??
????????}??
??????????
????????/**
?????????*?新增數(shù)據(jù)
?????????*?@param?indexName
?????????*?@param?typeName
?????????*?@param?source
?????????*?@return
?????????*?@throws?Exception
?????????*/
????????public?boolean?insert(JestClient?jestClient,String?indexName,?String?typeName,?String?source)?throws?Exception?{??
????????????PutMapping?putMapping?=?new?PutMapping.Builder(indexName,?typeName,?source).build();??
????????????JestResult?jr?=?jestClient.execute(putMapping);??
????????????return?jr.isSucceeded();??
????????}??
??????????
????????
?????????/**
??????????*?查詢數(shù)據(jù)
??????????*?@param?indexName
??????????*?@param?typeName
??????????*?@return
??????????*?@throws?Exception
??????????*/
????????public?static?String?getIndexMapping(JestClient?jestClient,String?indexName,?String?typeName)?throws?Exception?{??
????????????GetMapping?getMapping?=?new?GetMapping.Builder().addIndex(indexName).addType(typeName).build();??
????????????JestResult?jr?=jestClient.execute(getMapping);??
????????????return?jr.getJsonString();??
?????????}??
??????????
????????
????????
???????/**
????????*?批量新增數(shù)據(jù)
????????*?@param?indexName
????????*?@param?typeName
????????*?@param?objs
????????*?@return
????????*?@throws?Exception
????????*/
????????public?static?boolean?insertBatch(JestClient?jestClient,String?indexName,?String?typeName,?List<Object>?objs)?throws?Exception?{??
????????????Bulk.Builder?bulk?=?new?Bulk.Builder().defaultIndex(indexName).defaultType(typeName);??
????????????for?(Object?obj?:?objs)?{??
????????????????Index?index?=?new?Index.Builder(obj).build();??
?????????????????bulk.addAction(index);??
????????????}??
????????????BulkResult?br?=?jestClient.execute(bulk.build());??
????????????return?br.isSucceeded();??
???????????}??
??????????
????????/**
?????????*?全文搜索
?????????*?@param?indexName
?????????*?@param?typeName
?????????*?@param?query
?????????*?@return
?????????*?@throws?Exception
?????????*/
????????public?static?String?search(JestClient?jestClient,String?indexName,?String?typeName,?String?query)?throws?Exception?{??
?????????????Search?search?=?new?Search.Builder(query)
?????????????.addIndex(indexName)
?????????????.addType(typeName)??
?????????????.build();?
????????????JestResult?jr?=?jestClient.execute(search);??
//??????????System.out.println("--"+jr.getJsonString());//??????????System.out.println("--"+jr.getSourceAsObject(User.class));
????????????return?jr.getSourceAsString();??
?????????}??
??????????
??????????
????????
???????
??????????
???????/**
????????*?刪除索引
????????*?@param?indexName
????????*?@return
????????*?@throws?Exception
????????*/
????????public?boolean?delete(JestClient?jestClient,String?indexName)?throws?Exception?{??
????????????JestResult?jr?=?jestClient.execute(new?DeleteIndex.Builder(indexName).build());??
????????????return?jr.isSucceeded();??
????????}??
??????????
???????/**
????????*?刪除數(shù)據(jù)
????????*?@param?indexName
????????*?@param?typeName
????????*?@param?id
????????*?@return
????????*?@throws?Exception
????????*/
????????public?boolean?delete(JestClient?jestClient,String?indexName,?String?typeName,?String?id)?throws?Exception?{??
????????????DocumentResult?dr?=?jestClient.execute(new?Delete.Builder(id).index(indexName).type(typeName).build());??
????????????return?dr.isSucceeded();??
????????}

注:測試之前先說明下,本地windows系統(tǒng)安裝的是ElasticSearch版本是2.3.5,linux服務(wù)器上安裝的ElasticSearch版本是6.2。

測試結(jié)果

全文搜索

全文搜索查詢語句:{??"from"?:?0,??"size"?:?2,??"query"?:?{????"query_string"?:?{??????"query"?:?"工程師"
????}
??}
}

全文搜索返回結(jié)果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","createtm":"2018-4-25?11:07:42"},{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}

匹配搜索

精確搜索查詢語句:{??"query"?:?{????"term"?:?{??????"age"?:?24
????}
??}
}

精確搜索返回結(jié)果:{"id":2,"name":"李四","age":24,"description":"李四是個測試工程師","createtm":"1980-2-15?19:01:32"}

時間區(qū)間搜索

區(qū)間搜索語句:{??"query"?:?{????"range"?:?{??????"createtm"?:?{????????"from"?:?"2016-8-21?06:11:32",????????"to"?:?"2018-8-21?06:11:32",????????"include_lower"?:?true,????????"include_upper"?:?true
??????}
????}
??}
}
區(qū)間搜索返回結(jié)果:{"id":1,"name":"張三","age":20,"description":"張三是個Java開發(fā)工程師","createtm":"2018-4-25?11:07:42"}

新增完數(shù)據(jù)之后,我們可以上linux的?Kibana中進(jìn)行相關(guān)的查詢,查詢結(jié)果如下:

SpringBoot整合ElasticSearch實現(xiàn)多版本的兼容

注:Kibana 是屬于ELK中一個開源軟件。Kibana可以為 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以幫助匯總、分析和搜索重要數(shù)據(jù)日志。

上述代碼中測試返回的結(jié)果符合我們的預(yù)期。其中關(guān)于JestClient只是用到了很少的一部分,更多的使用可以查看JestClient的官方文檔。

Windows安裝ElasticSearch

1,文件準(zhǔn)備
下載地址:
https://www.elastic.co/downloads
選擇ElasticSearch相關(guān)版本, 然后選擇后綴名為ZIP文件進(jìn)行下載,下載之后進(jìn)行解壓。

2,啟動Elasticsearch
進(jìn)入bin目錄下,運行 elasticsearch.bat
然后在瀏覽上輸入: localhost:9200
成功顯示一下界面表示成功!
SpringBoot整合ElasticSearch實現(xiàn)多版本的兼容

3,安裝ES插件
web管理界面head 安裝
進(jìn)入bin目錄下,打開cmd,進(jìn)入dos界面
輸入:plugin install mobz/elasticsearch-head
進(jìn)行下載
成功下載之后,在瀏覽器輸入:http://localhost:9200/_plugin/head/
若顯示一下界面,則安裝成功!
SpringBoot整合ElasticSearch實現(xiàn)多版本的兼容

4,注冊服務(wù)
進(jìn)入bin目錄下,打開cmd,進(jìn)入dos界面
依次輸入:
service.bat install
service.bat start
成功之后,再輸入
services.msc
跳轉(zhuǎn)到Service服務(wù)界面,可以直接查看es的運行狀態(tài)!

歡迎工作一到五年的Java工程師朋友們加入Java技術(shù)交流:659270626
群內(nèi)提供免費的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構(gòu)資料)合理利用自己每一分每一秒的時間來學(xué)習(xí)提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI