SpringBoot整合ElasticSearch

SpringBoot整合ElasticSearch,第1张

SpringBoot整合ElasticSearch

因为es用的是6.8版本,对应springboot2.2.x版本,创建项目后修改pom文件

 2.2.4.RELEASE

引入es依赖

  
            org.elasticsearch.client
            elasticsearch-rest-high-level-client
            6.8.18
    

在配置文件中添加

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

编写es配置类

package com.lzq.config;
​
import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
​
@Configuration   //配置类,启动就会加载
@ConfigurationProperties(prefix = "elasticsearch") //读取配置文件中带前缀的值
@Component
@Data //安装Lombok插件
public class ElasticSearchConfig {
    private String host ;
    private Integer port ;
​
    @Bean
    public RestHighLevelClient client(){
        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
        return restHighLevelClient;
    }
​
}

测试

@SpringBootTest
class DemoApplicationTests {
    @Autowired
    private RestHighLevelClient client;
    
    @Test
    public void create() throws IOException {
        //1.创建请求对象,创建索引的请求
        CreateIndexRequest request = new CreateIndexRequest("shopping1");
        //2.客户端发送请求,获取响应对象
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        //3.打印响应对象中的返回结果
        //返回index信息
        System.out.println("index:" + response.index());
        //acknowledged代表创建成功
        System.out.println("acknowledged:" + response.isAcknowledged());
        //4.关闭客户端,释放连接资源
        client.close();
    }
​
​
    //查看索引库
    @Test
    public void getIndex() throws IOException {
        //1.创建请求对象:查询索引库
        GetIndexRequest request = new GetIndexRequest("shopping1");
        //2.客户端执行请求发送,返回响应对象
        GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        System.out.println("aliases:"+response.getAliases());
        System.out.println("mappings:"+response.getMappings());
        System.out.println("settings:"+response.getSettings());
        //4.关闭客户端,释放连接资源
        client.close();
    }
​
    //删除索引库
    @Test
    public void deleteIndex() throws IOException {
        //1.创建请求对象
        DeleteIndexRequest request = new DeleteIndexRequest("shopping1");
        //2.客户端发送请求,获取响应对象
        AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        System.out.println("ack:" + response.isAcknowledged());
        //4.关闭客户端,释放连接资源
        client.close();
    }
    
}
配置映射

1.配置映射,一共2种方式:

第一种:使用XContentBuilder,构建请求

第二种:使用JSON字符串

import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
​
import java.io.IOException;
​
@SpringBootTest
class EsDemo2 {
​
    @Autowired
    private RestHighLevelClient client;
​
    
    @Test
    public void putMapping01() throws IOException {
        //1.创建请求对象:配置映射
        PutMappingRequest request = new PutMappingRequest("shopping1");
        //构建请求体
        XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
        jsonBuilder.startObject()
                .startObject("properties")
                .startObject("title")
                .field("type", "text").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("subtitle")
                .field("type", "text").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("images")
                .field("type", "keyword").field("index", false)
                .endObject()
                .startObject("price")
                .field("type", "float")
                .endObject()
                .endObject()
                .endObject();
        //设置请求体,source("请求体json构建器对象");
        request.source(jsonBuilder);
        //2.客户端发送请求,获取响应对象
        AcknowledgedResponse response = client.indices().putMapping(request, RequestOptions.DEFAULT);
        //3.打印响应结果
        System.out.println("acknowledged::" + response.isAcknowledged());
    }
​
    
    @Test
    public void putMapping02() throws IOException {
        //1.创建请求对象:配置映射
        PutMappingRequest request = new PutMappingRequest("shopping1");
        //设置请求体,source("请求体json字符串","请求体的数据类型");
        request.source("{"properties":{"title":{"type":"text","analyzer":"ik_max_word"},"subtitle":{"type":"text","analyzer":"ik_max_word"},"price":{"type":"float"},"images":{"type":"keyword","index":false}}}", XContentType.JSON);
        //2.客户端发送请求,获取响应对象
        AcknowledgedResponse response = client.indices().putMapping(request, RequestOptions.DEFAULT);
        //3.打印响应结果
        System.out.println("acknowledged::" + response.isAcknowledged());
    }
​
    
    @Test
    public void getMapping() throws IOException {
        //1.创建请求对象:查看映射
        GetMappingsRequest request = new GetMappingsRequest();
        //设置索引库name
        request.indices("shopping1");
        //2.客户端发送请求,获取响应对象
        GetMappingsResponse response = client.indices().getMapping(request, RequestOptions.DEFAULT);
        //3.打印响应结果
        System.out.println("mappings::" + response.mappings());
        System.out.println("Source::" + response.mappings().get("shopping1").getSourceAsMap());
    }
​
}

文档 *** 作

   
 //新增文档
    @Test
    public void saveDoc() throws IOException {
        //1.创建请求对象(创建索引库CreateIndexRequest),索引库名称、类型名称、主键id
        IndexRequest request = new IndexRequest().index("shopping").type("_doc").id("2");
        //方式1:写一个Product对象将对象转为json字符串
//        Product product = Product.builder().id(1L).title("小米手机").price(1999.0).build();
//        ObjectMapper objectMapper = new ObjectMapper();
//        String productJson = objectMapper.writevalueAsString(product);
//        request.source(productJson,XContentType.JSON);
​
        //方式2:直接在source中写入key-value参数
        request.source(XContentType.JSON, "id", "2","title", "小米手机",  "price", "3999");
​
        //2.客户端发送请求,获取响应对象
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        3.打印结果信息
        System.out.println("_index:" + response.getIndex());
        System.out.println("_type:" + response.getType());
        System.out.println("_id:" + response.getId());
        System.out.println("_result:" + response.getResult());
    }

修改文档*

    public void update() throws IOException {
        //1.创建请求对象(创建索引库CreateIndexRequest),索引库名称、类型名称、主键id
        UpdateRequest request = new UpdateRequest().index("shopping").type("_doc").id("2");
        //设置请求体
        request.doc(XContentType.JSON, "id", "2", "title", "小米手机", "price", "2999");
​
        //2.客户端发送请求,获取响应对象
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
        3.打印结果信息
        System.out.println("_index:" + response.getIndex());
        System.out.println("_type:" + response.getType());
        System.out.println("_id:" + response.getId());
        System.out.println("_result:" + response.getResult());
    }
​
​
    //查询文档
    @Test
    public void getDoc() throws IOException {
        //1.创建请求对象
        GetRequest request = new GetRequest().index("shopping").type("_doc").id("2");
        //2.客户端发送请求,获取响应对象
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        3.打印结果信息
        System.out.println("_index:" + response.getIndex());
        System.out.println("_type:" + response.getType());
        System.out.println("_id:" + response.getId());
        System.out.println("source:" + response.getSourceAsString());
    }
​
​
    //删除文档
    @Test
    public void deleteDoc() throws IOException {
        //创建请求对象
        DeleteRequest request = new DeleteRequest().index("shopping").type("_doc").id("2");
        //客户端发送请求,获取响应对象
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        //打印信息
        System.out.println(response.toString());
    }
​
​
    //批量新增 *** 作
    @Test
    public void bulkSave() throws IOException {
        //创建请求对象
        BulkRequest request = new BulkRequest();
        request.add(new IndexRequest().index("shopping").type("_doc").id("1").source(XContentType.JSON, "title", "小米手机"));
        request.add(new IndexRequest().index("shopping").type("_doc").id("2").source(XContentType.JSON, "title", "苹果手机"));
        request.add(new IndexRequest().index("shopping").type("_doc").id("3").source(XContentType.JSON, "title", "华为手机"));
        //客户端发送请求,获取响应对象
        BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
        //打印结果信息
        System.out.println("took:" + responses.getTook());
        System.out.println("items:" + responses.getItems());
    }
​
​
    //批量删除 *** 作
    @Test
    public void bulkDelete() throws IOException {
        //创建请求对象
        BulkRequest request = new BulkRequest();
        request.add(new DeleteRequest().index("shopping").type("_doc").id("1"));
        request.add(new DeleteRequest().index("shopping").type("_doc").id("2"));
        request.add(new DeleteRequest().index("shopping").type("_doc").id("3"));
        //客户端发送请求,获取响应对象
        BulkResponse responses = client.bulk(request, RequestOptions.DEFAULT);
        //打印结果信息
        System.out.println("took:" + responses.getTook());
        System.out.println("items:" + responses.getItems());
    }
​
}
请求体查询
@SpringBootTest
class EsDemo3 {
​
    @Autowired
    private RestHighLevelClient client;
​
    
    @Test
    public void initData() throws IOException {
        //批量新增 *** 作
        BulkRequest request = new BulkRequest();
        request.add(new IndexRequest().type("_doc").index("shopping01").source(XContentType.JSON, "title", "小米手机", "images", "http://www.gulixueyuan.com/xm.jpg", "price", 1999.0));
        request.add(new IndexRequest().type("_doc").index("shopping01").source(XContentType.JSON, "title", "小米电视", "images", "http://www.gulixueyuan.com/xmds.jpg", "price", 2999.0));
        request.add(new IndexRequest().type("_doc").index("shopping01").source(XContentType.JSON, "title", "华为手机", "images", "http://www.gulixueyuan.com/hw.jpg", "price", 4999.0, "subtitle", "小米"));
        request.add(new IndexRequest().type("_doc").index("shopping01").source(XContentType.JSON, "title", "apple手机", "images", "http://www.gulixueyuan.com/appletl.jpg", "price", 5999.00));
        request.add(new IndexRequest().type("_doc").index("shopping01").source(XContentType.JSON, "title", "apple", "images", "http://www.gulixueyuan.com/apple.jpg", "price", 3999.00));
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        System.out.println("took::" + response.getTook());
        System.out.println("Items::" + response.getItems());
    }
​
    //请求体查询-基本查询
    //1.创建请求对象
    //2.客户端发送请求,获取响应对象
    //3.打印结果信息
    @Test
    public void basicQuery() throws IOException {
        //1.创建请求对象
        SearchRequest request = new SearchRequest().indices("shopping01").types("_doc");
        //构建查询的请求体
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //查询所有
        //sourceBuilder.query(QueryBuilders.matchAllQuery());//
        //match查询,带分词器的查询
        //sourceBuilder.query(QueryBuilders.matchQuery("title","小米手机").operator(Operator.AND));
        //term查询:不带分词器,查询条件作为关键词
        sourceBuilder.query(QueryBuilders.termQuery("price", "1999"));
        //TODO ...multi_match:多个字段的match查询
        //TODO ...terms查询:多个关键词去匹配
        request.source(sourceBuilder);
        //2.客户端发送请求,获取响应对象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        printResult(response);
    }
​
    
    private void printResult(SearchResponse response) {
        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("<<========");
    }
​
​
    
    @Test
    public void fetchSourceAndSortAndByPage() throws IOException {
        //1.创建请求对象
        SearchRequest request = new SearchRequest().indices("shopping01").types("_doc");
        //构建查询的请求体
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //查询所有
        sourceBuilder.query(QueryBuilders.matchAllQuery());
​
        //分页信息
        //当前页其实索引(第一条数据的顺序号),from
        sourceBuilder.from(2);
        //每页显示多少条size
        sourceBuilder.size(2);
​
        //排序信息,参数一:排序的字段,参数二:顺序ASC升序,降序DESC
        sourceBuilder.sort("price", SortOrder.ASC);
​
        //查询字段过滤
        String[] excludes = {};
        String[] includes = {"title", "subtitle", "price"};
        sourceBuilder.fetchSource(includes, excludes);
​
        request.source(sourceBuilder);
        //2.客户端发送请求,获取响应对象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        printResult(response);
    }
​
​
    
    @Test
    public void boolAndRangeAndFuzzyQuery() throws IOException {
        //1.创建请求对象
        SearchRequest request = new SearchRequest().indices("shopping01").types("_doc");
        //构建查询的请求体
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //高级查询的三种方式:
        //-----------------------------------------------------------------------------
//        //bool查询:查询title中必须包含小米,一定不含有电视,应该含有手机的所有商品
//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        //must
//        boolQueryBuilder.must(QueryBuilders.matchQuery("title", "小米"));
//        //must not
//        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("title", "电视"));
//        //should
//        boolQueryBuilder.should(QueryBuilders.matchQuery("title", "手机"));
//        sourceBuilder.query(boolQueryBuilder);
        //-----------------------------------------------------------------------------
        //范围查询:查询价格大于3千,小于5千的所有商品
//        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
//        //#### gt 大于(greater than)
//        rangeQuery.gt("3000");
//        //#### lt 小于(less than)
//        rangeQuery.lt("5000");
//        //#### gte 大于等于(greater than equals)
//        //#### lte 小于等于(less than equals)
//        sourceBuilder.query(rangeQuery);
        //-----------------------------------------------------------------------------
        //模糊查询:查询包含apple关键词的所有商品,完成模糊查询cpple
        sourceBuilder.query(QueryBuilders.fuzzyQuery("title","cpple").fuzziness(Fuzziness.ONE));
        //-----------------------------------------------------------------------------
​
        request.source(sourceBuilder);
        //2.客户端发送请求,获取响应对象
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //3.打印结果信息
        printResult(response);
    }
}

高亮查询

@Test
public void highLighterQuery() throws IOException {
    //1.创建请求对象
    SearchRequest request = new SearchRequest().types("_doc").indices("shopping01");
    //2.创建查询请求体构建器
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    //构建查询方式:高亮查询
    TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("title","apple");
    //设置查询方式
    sourceBuilder.query(termsQueryBuilder);
    //构建高亮字段
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags("");//设置标签前缀
    highlightBuilder.postTags("");//设置标签后缀
    highlightBuilder.field("title");//设置高亮字段
    //设置高亮构建对象
    sourceBuilder.highlighter(highlightBuilder);
    //设置请求体
    request.source(sourceBuilder);
    //3.客户端发送请求,获取响应对象
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
​
    //4.打印响应结果
    SearchHits hits = response.getHits();
    System.out.println("took::"+response.getTook());
    System.out.println("time_out::"+response.isTimedOut());
    System.out.println("total::"+hits.getTotalHits());
    System.out.println("max_score::"+hits.getMaxScore());
    System.out.println("hits::::>>");
    for (SearchHit hit : hits) {
        String sourceAsString = hit.getSourceAsString();
        System.out.println(sourceAsString);
        //打印高亮结果
        Map highlightFields = hit.getHighlightFields();
        System.out.println(highlightFields);
    }
    System.out.println("<<::::");
}
SpringData整合ES

创建项目加入依赖

 
            org.projectlombok
            lombok
        
​
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        

编写配置文件

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

创建实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
 
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Product {
    private Long id;//商品唯一标识
    private String title;//商品名称
    private String category;//分类名称
    private Double price;//商品价格
    private String images;//图片地址
}

创建配置类

import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper;
import org.springframework.data.elasticsearch.core.EntityMapper;
 
@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;
}
}

dao层

import com.lzq.pojo.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
​
@Repository
public interface ProductDao extends ElasticsearchRepository {
​
}

修改实体类对应

@document(indexName = "shopping", type = "product", shards = 5, replicas = 1)
public class Product implements Serializable {
    //必须有id,这里的id是全局唯一的标识,等同于es中的"—id"
    @Id
    private Long id;//商品唯一标识
​
    
    @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;//图片地址
}

测试

//注入ElasticsearchRestTemplate
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    //创建索引并增加映射配置
    @Test
    public void createIndex(){
        //创建索引
        boolean index = elasticsearchRestTemplate.createIndex(Product.class);
        System.out.println("index = " + index);
        index = elasticsearchRestTemplate.putMapping(Product.class);
        System.out.println("index = " + index);
    }
​
    //删除索引
    @Test
    public void deleteIndex(){
        boolean index = elasticsearchRestTemplate.deleteIndex(Product.class);
        System.out.println("index = " + index);
    }
测试:文档 *** 作

注入dao层

   @Autowired
    private ProductDao productDao;
 
    @Test
    public void save(){
        Product product = new Product();
        product.setId(1L);
        product.setTitle("小米手机");
        product.setCategory("手机");
        product.setPrice(1999.0);
        product.setImages("http://www.gulixueyuan/xm.jpg");
        productDao.save(product);
    }
 
    //修改
    @Test
    public void update(){
        Product product = new Product();
        product.setId(1L);
        product.setTitle("小米2手机");
        product.setCategory("手机");
        product.setPrice(9999.0);
        product.setImages("http://www.gulixueyuan/xm.jpg");
        productDao.save(product);
    }
//根据id查询
    @Test
    public void findById(){
        Product product = productDao.findById(1L).get();
        System.out.println(product);
}
​
    //查询所有
    @Test
    public void findAll(){
        Iterable products = productDao.findAll();
        for (Product product : products) {
            System.out.println(product);
        }
    }
 
    //删除
    @Test
    public void delete(){
        Product product = new Product();
        product.setId(1L);
        productDao.delete(product);
    }
 
    //批量新增
    @Test
    public void saveAll(){
        List productList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Product product = new Product();
            product.setId(Long.valueOf(i));
            product.setTitle("["+i+"]小米手机");
            product.setCategory("手机");
            product.setPrice(1999.0+i);
            product.setImages("http://www.gulixueyuan/xm.jpg");
            productList.add(product);
        }
        productDao.saveAll(productList);
}
​
    //分页查询
    @Test
    public void findByPageable(){
        //设置排序(排序方式,正序还是倒序,排序的id)
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        int currentPage=1;//当前页,第一页从0开始,1表示第二页
        int pageSize = 20;//每页显示多少条
        //设置查询分页
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
        //分页查询
        Page productPage = productDao.findAll(pageRequest);
        System.out.println(productPage);
    }
自定义DAO查询 关键字命名规则解释示例andfindByField1AndField2根据Field1和Field2获得数据findByTitleAndPriceorfindByField1OrField2根据Field1或Field2获得数据findByTitleOrPriceisfindByField根据Field获得数据findByTitlenotfindByFieldNot根据Field获得补集数据findByTitleNotbetweenfindByFieldBetween获得指定范围的数据findByPriceBetweenlessThanEqualfindByFieldLessThan获得小于等于指定值的数据findByPriceLessThan
  
    @Test
    public void findAllByTitleAndPrice(){
        String title = "小米";
        Double price = 2050.0;
        List products = productDao.findByTitleAndPrice(title, price);
        for (Product product : products) {
            System.out.println(product);
        }
    }
    
    @Test
    public void findPriceBetween(){
        double lowPrice = 2000.0;//最低价
        double highPrice = 2005.0;//最高价
        List products = productDao.findByPriceBetween(lowPrice, highPrice);
        for (Product product : products) {
            System.out.println(product);
        }
    }
​

只需要在dao层实现就行,不用自己写逻辑

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5700220.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存