国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

springboot實(shí)現(xiàn)ES多種分頁(yè)方式

這篇具有很好參考價(jià)值的文章主要介紹了springboot實(shí)現(xiàn)ES多種分頁(yè)方式。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

es有兩種分頁(yè)方式

  • from+size 淺分頁(yè)
  • scroll 深分頁(yè)

在這里就不展開(kāi)介紹了,網(wǎng)絡(luò)上有很多相關(guān)介紹。
我們還可以通過(guò)維護(hù)自增主鍵來(lái)實(shí)現(xiàn)分頁(yè)
下面結(jié)合Springboot,通過(guò)代碼展現(xiàn)實(shí)現(xiàn)es分頁(yè)的三種實(shí)現(xiàn)方式

需要引入依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <version>2.2.12.RELEASE</version>
        </dependency>

代碼:

ESBase.java

@Service
@Slf4j
public class ESBase<T, U extends UserIdxESReq> {

//    @Autowired
//    ElasticsearchRepository<T, String> elasticsearchRepository;

    @Autowired
    ElasticsearchTemplate elasticsearchTemplate;

    @Value("${scroll.timeout.ms:60000}")
    private long scrollTimeoutMs;

    public NativeSearchQueryBuilder getNativeSearchQueryBuilder(U esReq) {
        /*NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(taskItemAddReq.getSettleNo())) {
            TermsQueryBuilder deptNoTermsQb = QueryBuilders.termsQuery(CommonConstant.SETTLE_NO, portBillDetailFactoringFreightReq.getSettleNo());
            boolQueryBuilder.must(deptNoTermsQb);
        }
        searchQueryBuilder.withQuery(boolQueryBuilder);*/
        return new NativeSearchQueryBuilder();
    }

    protected NativeSearchQuery getNativeSearchQuery(U esReq) {
        String reqStr = JSONUtil.toJsonStr(esReq);
        String reqMd5 = SecureUtil.md5(reqStr);
        NativeSearchQuery searchQuery = getNativeSearchQueryBuilder(esReq)
//                .withSort(SortBuilders.fieldSort("pin").order(SortOrder.ASC))
                .withPageable(PageRequest.of(esReq.getPageNo(),
                        esReq.getPageSize())).build();
        searchQuery.setPreference(reqMd5);
        return searchQuery;
    }

    /**
     * 通過(guò)自增主鍵來(lái)實(shí)現(xiàn)分頁(yè)
     * @param start
     * @param end
     * @param clazz
     * @return
     */
    public List<T> autoIncrementKeyPage(long start, long end, Class<T> clazz) {
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("key").gte(start).lte(end);
        boolQueryBuilder.must(rangeQueryBuilder);

        searchQueryBuilder.withQuery(boolQueryBuilder);
        NativeSearchQuery searchQuery = searchQueryBuilder
                .withSort(SortBuilders.fieldSort("key").order(SortOrder.ASC))
                .build();
        List<T> list = elasticsearchTemplate.queryForList(searchQuery, clazz);
        return list;
    }

    /**
     * from+size 淺分頁(yè)
     * @param esReq
     * @return
     */
    public EsQueryPojo<List<T>> fromSizePage(U esReq, Class<T> clazz) {
        Page<T> page = elasticsearchTemplate.queryForPage(getNativeSearchQuery(esReq), clazz);
//        Page<T> page = elasticsearchRepository.search(getNativeSearchQuery(esReq));
        EsQueryPojo<List<T>> listEsQueryPojo = new EsQueryPojo<>();
        listEsQueryPojo.setRows(page.getContent());//當(dāng)前分頁(yè)返回的結(jié)果
        listEsQueryPojo.setPage(page.getNumber());//當(dāng)前頁(yè)碼
        listEsQueryPojo.setTotal(page.getTotalPages());//分頁(yè)總數(shù)
        listEsQueryPojo.setRecords(page.getTotalElements());//元素總數(shù)
        listEsQueryPojo.setSize(page.getSize());//當(dāng)前頁(yè)返回的數(shù)量
        return listEsQueryPojo;
    }

    /**
     * scroll 深分頁(yè),另一種方式是通過(guò)維護(hù)自增主鍵
     * @param esReq
     * @return
     */
    public EsScrollQueryPojo<List<T>> scrollPage(U esReq, Class<T> clazz) {
        EsScrollQueryPojo<List<T>> listEsQueryPojo = new EsScrollQueryPojo<>();
        ScrolledPage<T> scrolledPage;
        try {
            if (StringUtils.isEmpty(esReq.getScrollId())) {
                //第一次查詢
                scrolledPage = elasticsearchTemplate.startScroll(scrollTimeoutMs, getNativeSearchQuery(esReq), clazz);//游標(biāo)的過(guò)期時(shí)間是指兩個(gè)查詢之間的間隔,而不是整個(gè)查詢過(guò)程
            } else {
                //帶scrollId查詢
                scrolledPage = elasticsearchTemplate.continueScroll(esReq.getScrollId(), scrollTimeoutMs, clazz);
            }

            listEsQueryPojo.setRows(scrolledPage.getContent());//當(dāng)前分頁(yè)返回的結(jié)果
            listEsQueryPojo.setPage(scrolledPage.getNumber());//當(dāng)前頁(yè)碼,在深度分頁(yè)不生效
            listEsQueryPojo.setTotal(scrolledPage.getTotalPages());//分頁(yè)總數(shù)
            listEsQueryPojo.setRecords(scrolledPage.getTotalElements());//元素總數(shù)
            listEsQueryPojo.setSize(scrolledPage.getSize());//當(dāng)前返回的數(shù)量
            listEsQueryPojo.setScrollId(scrolledPage.getScrollId());
        } catch (Exception e) {
            log.error(ExceptionEnum.ES_SCROLL_PAGE_EXCEPTION.getMsg() + e.getMessage());
            throw e;
        }
        return listEsQueryPojo;
    }

    public void clearScroll(String scrollId) {
        if (scrollId != null) {
            elasticsearchTemplate.clearScroll(scrollId);
        }
    }

    public void deleteIndexData(Class<T> clazz) {
        if (elasticsearchTemplate.indexExists(clazz)) {
            DeleteQuery deleteQuery = new DeleteQuery();
            deleteQuery.setQuery(QueryBuilders.matchAllQuery());
            elasticsearchTemplate.delete(deleteQuery, clazz);//刪除語(yǔ)句
            log.info("clearing index data");
        } else {
            log.warn("index not exsit");
        }
    }

    public long countIndex(Class<T> clazz) {
        if (elasticsearchTemplate.indexExists(clazz)) {
            NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
            searchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
            return elasticsearchTemplate.count(searchQueryBuilder.build(), clazz);
        } else {
            log.warn("index not exsit");
            return 0L;
        }
    }
}

UserIdxES.java

@Service
@Slf4j
public class UserIdxES extends ESBase<UserIdx, UserIdxESReq> {

    @Override
    protected NativeSearchQuery getNativeSearchQuery(UserIdxESReq esReq) {
        String reqStr = JSONUtil.toJsonStr(esReq);
        String reqMd5 = SecureUtil.md5(reqStr);
        NativeSearchQuery searchQuery = getNativeSearchQueryBuilder(esReq)
//                .withSort(SortBuilders.fieldSort("pin").order(SortOrder.ASC))
                .withPageable(PageRequest.of(esReq.getPageNo(),
                        esReq.getPageSize())).build();
        searchQuery.setPreference(reqMd5);
        return searchQuery;
    }
}

UserIdx.java

@AllArgsConstructor
@NoArgsConstructor
@Data
@Document(indexName = "user_idx", type = "user_type")
public class UserIdx {

    @Id
    private String id;

    @Field(name = "pin", type = FieldType.Keyword)
    private String pin;

    @Field(name = "name", type = FieldType.Keyword)
    private String name;

    @Field(name = "sex", type = FieldType.Keyword)
    private String sex;

    @Field(name = "age", type = FieldType.Keyword)
    private String age;

    @Field(name = "score", type = FieldType.Keyword)
    private String score;
}

UserIdxESReq.java

@NoArgsConstructor
@AllArgsConstructor
@Data
@ApiModel(value = "UserIdxESReq", description = "UserIdxESReq request bean")
public class UserIdxESReq {

    @ApiModelProperty(example = "0")
    private int pageNo;

    @ApiModelProperty(example = "10")
    private int pageSize;

    @ApiModelProperty(value = "分頁(yè)ID", name = "scrollId")
    private String scrollId;
}

測(cè)試
ESBaseTest.java

@SpringBootTest
@RunWith(SpringRunner.class)
//TODO
public class ESBaseTest {
    @Autowired
    ESBase<UserIdx, UserIdxESReq> esBase;

    @Autowired
    ESBase<UserWithKeyIdx, UserIdxESReq> esBase1;
    @Test
    public void fromSizePageTest() {
        UserIdxESReq UserIdxEsReq = new UserIdxESReq();
        UserIdxEsReq.setPageNo(0);
        UserIdxEsReq.setPageSize(10);
        esBase.fromSizePage(UserIdxEsReq, UserIdx.class);
    }

    @Test
    public void scrollPageTest() {
        UserIdxESReq UserIdxEsReq = new UserIdxESReq();
        UserIdxEsReq.setPageNo(0);
        UserIdxEsReq.setPageSize(10);
        esBase.scrollPage(UserIdxEsReq, UserIdx.class);
    }

    @Test
    public void autoIncrementIdPageTest() {
        List<UserIdx> list = esBase.autoIncrementKeyPage(0, 1000, UserIdx.class);
        Assert.assertNotNull(list);
    }

    @Test
    public void deleteIndexDataTest() {
        esBase.deleteIndexData(UserIdx.class);
    }

    @Test
    public void countIndexTest() {
        long cnt = esBase1.countIndex(UserWithKeyIdx.class);
        Assert.assertEquals(cnt,100);
    }
}

UserIdxESBaseTest.java

@SpringBootTest
@RunWith(SpringRunner.class)
public class UserIdxESBaseTest {

    @Autowired
    UserIdxES esService;

    @Test
    public void fromSizePageTest() {
        UserIdxESReq esReq = new UserIdxESReq();
//        esReq.setAge(123);
//        esReq.setPageNo(0);
        esReq.setPageSize(40);
        int pages = 0;
        int currentPage = 0;
        do {
            esReq.setPageNo(currentPage);
            EsQueryPojo<List<UserIdx>> esScrollQueryPojo = esService.fromSizePage(esReq,UserIdx.class);
            pages = esScrollQueryPojo.getTotal();
            currentPage = esScrollQueryPojo.getPage();
            List<UserIdx> list = esScrollQueryPojo.getRows();
//            currentPage++;
        } while (currentPage++ < pages);
    }

    @Test
    public void scrollPageTest() {
        UserIdxESReq esReq = new UserIdxESReq();
        esReq.setPageNo(0);
        esReq.setPageSize(10);
//        esService.scrollPage(esReq, TaskItemAddIndex.class);
        EsScrollQueryPojo<List<UserIdx>> esScrollQueryPojo = esService.scrollPage(esReq, UserIdx.class);
        List<UserIdx> taskItemAddDtoList = (List<UserIdx>) esScrollQueryPojo.getRows();
        Assert.assertNotNull(taskItemAddDtoList);
    }
}

附錄:
2. http 測(cè)試筆記

測(cè)試es:
spring.data.elasticsearch.cluster-name=docker-cluster
spring.data.elasticsearch.cluster-nodes=x.x.x.x:9300

查看:
    curl -X GET http://x.x.x.x:9200/_cat
創(chuàng)建索引:
    curl -X PUT http://x.x.x.x:9200/user_with_key_idx
刪除索引:
    curl -XDELETE http://x.x.x.x:9200/user_with_key_idx
查看mapping
    curl -XGET http://x.x.x.x:9200/user_with_key_idx?pretty
    curl -XGET http://x.x.x.x:9200/user_with_key_idx?pretty
查詢數(shù)據(jù):
    curl -XGET http://x.x.x.x:9200/user_with_key_idx/user_with_key_type/_search?pretty


深度分頁(yè)scroll游標(biāo)使用
    curl -XGET http://x.x.x.x:9200/user_with_key_idx/_search?scroll=1m \
    -H "Content-Type: application/json" \
    -d '
    {
        "query": { "match_all": {}},
        "sort" : ["_doc"],
        "size":  1
    }'

curl -XGET http://x.x.x.x:9200/_search/scroll?pretty \
-H "Content-Type: application/json" \
-d '{
    "scroll": "1m",
    "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAJFFlNsSXFUcS1qUzFlYWFFWGpWUkE5RncAAAAAAAACRxZTbElxVHEtalMxZWFhRVhqVlJBOUZ3AAAAAAAAAkYWU2xJcVRxLWpTMWVhYUVYalZSQTlGdwAAAAAAAAJIFlNsSXFUcS1qUzFlYWFFWGpWUkE5RncAAAAAAAACSRZTbElxVHEtalMxZWFhRVhqVlJBOUZ3"
}'

創(chuàng)建mapping
curl -XPOST 'http://x.x.x.x:9200/user_with_key_idx/user_with_key_type/_mapping?pretty' -H "Content-Type: application/json"  \
-d '
{
    "user_with_key_type": {
        "properties": {
            "pin": {
                "type": "keyword"
            },
            "name": {
                "type": "keyword"
            },
            "sex": {
                "type": "keyword"
            },
            "age": {
                "type": "keyword"
            },
            "score": {
                "type": "keyword"
            },
            "key": {
                "type": "long"
            }
        }
    }
}'

user_with_key_idx/user_with_key_type/_mapping

PUT user_with_key_idx
{
    "mappings": {
        "user_with_key_type": {
                "properties": {
                    "pin": {
                        "type": "keyword"
                    },
                    "name": {
                        "type": "keyword"
                    },
                    "sex": {
                        "type": "keyword"
                    },
                    "age": {
                        "type": "keyword"
                    },
                    "score": {
                        "type": "keyword"
                    },
                     "key": {
                         "type": "long"
                     }
                }
        }
    }
}

插入數(shù)據(jù):
    curl -XPOST http://x.x.x.x:9200/user_with_key_idx/user_with_key_type?pretty -H "Content-Type: application/json" \
    -d '{
        "pin": "pin_001",
        "phone": "1234567890",
        "name": "lisi",
        "bizData": "",
        "sex": 1,
        "age": 18
        }'

    curl -XPOST http://x.x.x.x:9200/user_with_key_idx/user_with_key_type?pretty -H "Content-Type: application/json" \
    -d '{
        "pin": "pin_004",
        "phone": "1234567890",
        "name": "lisi02",
        "bizData": "",
        "sex": 1,
        "age": 18
        }'


查詢數(shù)據(jù):
    curl -XGET http://x.x.x.x:9200/user_with_key_idx/user_with_key_type/_search?pretty


  1. 深分頁(yè)和淺分頁(yè)的使用區(qū)別
  • scroll 深分頁(yè)
curl -XGET http://11.51.197.4:9200/user_idx/user_type/_search?pretty&scroll=3m \
-d '{
  "query":{
    "match_all": {}
  },
  "from": 0,
  "size": 3,
  "sort": [
    {
      "id": {
        "order": "asc"
      }
    }
  ]
}'

curl -XGET http://11.51.197.4:9200/_search/scroll \
-d '{
  "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAABvFlNsSXFUcS1qUzFlYWFFWGpWUkE5RncAAAAAAAAAcRZTbElxVHEtalMxZWFhRVhqVlJBOUZ3AAAAAAAAAHAWU2xJcVRxLWpTMWVhYUVYalZSQTlGdwAAAAAAAAByFlNsSXFUcS1qUzFlYWFFWGpWUkE5RncAAAAAAAAAcxZTbElxVHEtalMxZWFhRVhqVlJBOUZ3",
  "scroll": "3m"
}'

-淺分頁(yè),調(diào)整索引的配置項(xiàng)index.max_result_window文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-498065.html

PUT index_name/_settings
{
"index.max_result_window":10000
}

到了這里,關(guān)于springboot實(shí)現(xiàn)ES多種分頁(yè)方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)文件下載的多種方式

    1.1 下載文件

    2024年02月06日
    瀏覽(22)
  • SpringBoot實(shí)現(xiàn)分頁(yè)的三種方式

    一 自己封裝Page對(duì)象實(shí)現(xiàn) 博客鏈接 二 使用sql實(shí)現(xiàn)分頁(yè) 2.1 場(chǎng)景分析 前段傳遞給給后臺(tái)什么參數(shù)? 當(dāng)前頁(yè)碼 currentPage 每頁(yè)顯示條數(shù) pageSize 后臺(tái)給前端返回什么數(shù)據(jù)? 當(dāng)前頁(yè)數(shù)據(jù) List 總記錄數(shù) totalCount 2.2 前段代碼 2.3 后端代碼 PageBean mapper service impl controller 三 使用PageHelper插件

    2024年02月10日
    瀏覽(29)
  • springboot整合微信(公眾號(hào))實(shí)現(xiàn)掃碼登錄(兩種方式,兩種實(shí)現(xiàn))

    springboot整合微信(公眾號(hào))實(shí)現(xiàn)掃碼登錄(兩種方式,兩種實(shí)現(xiàn))

    首先說(shuō)一下這個(gè)微信掃碼登錄它的方式有兩種,一種是基于網(wǎng)頁(yè)的redirect實(shí)現(xiàn),一種是基于公眾號(hào)推送消息實(shí)現(xiàn), 二者實(shí)現(xiàn)的效果是不一樣的 貼一個(gè)官方文檔 需要有自己的域名 (這里你可以使用內(nèi)網(wǎng)穿透,會(huì)生成一個(gè)自己的域名,網(wǎng)上一大堆,自己奧利給吧) 需要申請(qǐng)微信認(rèn)

    2024年01月18日
    瀏覽(42)
  • 【工作記錄】基于springboot3+springsecurity6實(shí)現(xiàn)多種登錄方式(一)

    【工作記錄】基于springboot3+springsecurity6實(shí)現(xiàn)多種登錄方式(一)

    springboot3已經(jīng)推出有一段時(shí)間了,近期公司里面的小項(xiàng)目使用的都是springboot3版本的,安全框架還是以springsecurity為主,畢竟親生的。 本文針對(duì)基于springboot3和springsecurity實(shí)現(xiàn)用戶登錄認(rèn)證訪問(wèn)以及異常處理做個(gè)記錄總結(jié),也希望能幫助到需要的朋友。 需要提供登錄接口,支持

    2024年03月24日
    瀏覽(21)
  • 【工作記錄】基于springboot3+springsecurity實(shí)現(xiàn)多種方式登錄及鑒權(quán)(二)

    上篇文章介紹了基于springboot3+springsecurity實(shí)現(xiàn)的基于模擬數(shù)據(jù)的用戶多種方式登錄及鑒權(quán)的流程和代碼實(shí)現(xiàn),本文我們繼續(xù)完善。 主要通過(guò)如下幾個(gè)點(diǎn)來(lái)完成優(yōu)化和完善: 用戶信息獲取通過(guò)查詢mysql數(shù)據(jù)庫(kù)實(shí)現(xiàn) token生成方式使用jwt 用戶信息存儲(chǔ)及讀取使用redis 完善過(guò)濾器用戶

    2024年01月19日
    瀏覽(14)
  • ElasticSearch系列 - SpringBoot整合ES:實(shí)現(xiàn)分頁(yè)搜索 from+size、search after、scroll

    01. 數(shù)據(jù)準(zhǔn)備 ElasticSearch 向 my_index 索引中索引了 12 條文檔: 02. ElasticSearch 如何查詢所有文檔? ElasticSearch 查詢所有文檔 根據(jù)查詢結(jié)果可以看出,集群中總共有12個(gè)文檔,hits.total.value=12, 但是在 hits 數(shù)組中只有 10 個(gè)文檔。如何才能看到其他的文檔? 03. ElasticSearch 如何指定搜

    2023年04月08日
    瀏覽(29)
  • SpringBoot調(diào)用第三方WebService接口的兩種實(shí)現(xiàn)方式

    SpringBoot調(diào)用第三方WebService接口的兩種實(shí)現(xiàn)方式

    WebService接口的發(fā)布通常一般都是使用WSDL(web service descriptive language)文件的樣式來(lái)發(fā)布的,該文檔包含了請(qǐng)求的參數(shù)信息,返回的結(jié)果信息,我們需要根據(jù)WSDL文檔的信息來(lái)編寫相關(guān)的代碼進(jìn)行調(diào)用WebService接口。接下來(lái)我將采用常見(jiàn)的兩種方式調(diào)用WebService接口。 目前我需要

    2024年02月12日
    瀏覽(19)
  • Java連接ES的多種方式

    前言 本篇文章主要介紹:使用Basic、Transport、Kerberos三種方式連接ES Bisic方式 Transport方式 Kerberos方式 正常訪問(wèn) 綜合測(cè)試 EsConnectDemo 配置文件 kerberos.properties 文中提到的工具類 PropertiesUtil JaasKrbUtil SpnegoHttpClientConfigCallbackHandler

    2024年02月11日
    瀏覽(18)
  • 53、springboot對(duì)websocket的支持有兩種方式-------1、基于注解開(kāi)發(fā) WebSocket ,簡(jiǎn)潔實(shí)現(xiàn)多人聊天界面

    53、springboot對(duì)websocket的支持有兩種方式-------1、基于注解開(kāi)發(fā) WebSocket ,簡(jiǎn)潔實(shí)現(xiàn)多人聊天界面

    –注解就是: @OnOpen、 @OnClose 、 @OnMessage 、@OnError這些 ▲ Spring Boot為WebSocket提供了兩種開(kāi)發(fā)方式: 基于spring-boot-starter-websocket.jar開(kāi)發(fā)WebSocket 基于Spring WebFlux開(kāi)發(fā)WebSocket 兩種方式對(duì)比: springboot API Socket:套接字。 插座。 在通信的兩端分別建立虛擬的Socket(插座),網(wǎng)絡(luò)協(xié)議

    2024年02月09日
    瀏覽(27)
  • Spring Boot整合ES的兩種方式

    Spring Boot整合ES的兩種方式

    在Spring Boot中整合Elasticsearch的方式之一是使用Elasticsearch的官方Spring Data Elasticsearch Starter。該Starter提供了對(duì)Elasticsearch的高級(jí)集成,簡(jiǎn)化了配置和管理Elasticsearch客戶端。 下面是使用Spring Data Elasticsearch Starter的詳細(xì)介紹以及如何管理客戶端對(duì)象的初始化和關(guān)閉操作: 添加依賴坐

    2024年02月13日
    瀏覽(29)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包