谷粒商城p175

p175 检索添加分析

@Data
public class SearchParam {
    private String keyword;
    //三级分类id
    private Long catelog3Id;
    //排序条件 sort=skuPrice_asc/desc
    private String sort;
    //
    private Integer hasStock;
    /**
     * 价格区间
     */
    private String skuPrice;
    /**
     * 品牌id 可以多选
     */
    private List<Long> brandId;
    /**
     * 按照属性进行筛选  attrs=1_anzhuo & attrs=5_其他
     */
    private List<String> attrs;

}

p176 返回结果分析

@Data
public class SearchResult {
    /** * 查询到的所有商品信息*/
    private List<SkuEsModel> products;

    /*** 当前页码*/
    private Integer pageNum;
    /** 总记录数*/
    private Long total;
    /** * 总页码*/
    private Integer totalPages;

    /** 当前查询到的结果, 所有涉及到的品牌*/
    private List<BrandVo> brands;
    /*** 当前查询到的结果, 所有涉及到的分类*/
    private List<CatalogVo> catalogs;
    /** * 当前查询的结果 所有涉及到所有属性*/
    private List<AttrVo> attrs;



    @Data
    public static class BrandVo {

        private Long brandId;
        private String brandName;
        private String brandImg;
    }

    @Data
    public static class CatalogVo {
        private Long catalogId;
        private String catalogName;
    }

    @Data
    public static class AttrVo {

        private Long attrId;
        private String attrName;
        private List<String> attrValue;
    }
} 

p177 检索服务 检索dsl测试

#模糊匹配 过滤(按照属性 分类 品牌 价格区间 是否有库存) 排序 分页 高亮 以及最难的聚合分析!!!
get /product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "terms": {
            "brandId": [
              "1",
              "2"
            ]
          }
        },
        {
          "term": {
            "hasStock": "true"
          }
        },
        {
          "range": {
            "skuPrice": {
              "gte": 5000,
              "lte": 6000
            }
          }
        },
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "attrs.attrId": {
                        "value": "2"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attrs.attrValue": [
                        "2019",
                        "2020"
                      ]
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "skuPrice": "desc"
    }
  ],
  "from":"0",
  "size":"3",
  "highlight":{
    "fields": {"skuTitle": {}},
    "pre_tags": "<b style='color:red'>",
    "post_tags": "</b>"
  }
}

p178 检索服务 测试dsl语句 聚合部分

由于字段不符合聚合.我们新建索引库修改相关字段doc属性为true 数据迁移

字段映射类型

put /gulimall_product
{
    "mappings" : {
      "properties" : {
        "attrs" : {
          "type" : "nested",
          "properties" : {
            "attrId" : {
              "type" : "long"
            },
            "attrName" : {
              "type" : "keyword"
            },
            "attrValue" : {
              "type" : "keyword"
            }
          }
        },
        "brandId" : {
          "type" : "long"
        },
        "brandImg" : {
          "type" : "keyword"
        },
        "brandName" : {
          "type" : "keyword"
        },
        "catalogId" : {
          "type" : "long"
        },
        "catalogName" : {
          "type" : "keyword"
        },
        "hasStock" : {
          "type" : "boolean"
        },
        "hotScore" : {
          "type" : "long"
        },
        "saleCount" : {
          "type" : "long"
        },
        "skuId" : {
          "type" : "long"
        },
        "skuImg" : {
          "type" : "keyword"
        },
        "skuPrice" : {
          "type" : "keyword"
        },
        "skuTitle" : {
          "type" : "text",
          "analyzer" : "ik_smart"
        },
        "spuId" : {
          "type" : "keyword"
        }
      }
    }
} 
-- 数据迁移
post _reindex
{
  "source":{
      "index":"product"
   },
  "dest":{
      "index":"gulimall_product"
   }
}

在这里插入图片描述
聚合dsl语句

#按照 分类 品牌 属性 聚合
get /gulimall_product/_search
{
  "query": {
    "match": {
      "skuTitle": "华为"
    }
  },
  "aggs": {
    "category_agg": {
      "terms": {
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
        "category_name_agg": {
          "terms": {
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "brand_agg": {
      "terms": {
        "field": "brandId",
        "size": 10
      },
      "aggs": {
        "brand_name_agg": {
          "terms": {
            "field": "brandName",
            "size": 10
          }
        }
      }
    },
    "attr_agg": {
      "nested": {
        "path": "attrs"
      },
      "aggs": {
        "attr_id_agg": {
          "terms": {
            "field": "attrs.attrId",
            "size": 10
          },
          "aggs": {
            "attr_name_agg": {
              "terms": {
                "field": "attrs.attrName",
                "size": 10
              }
            },
            "attr_value_agg": {
              "terms": {
                "field": "attrs.attrValue",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

p179 检索服务 searchRequest构建 检索

模糊匹配 过滤(按照属性 分类 品牌 价格区间 是否有库存)

 @Override
    public SearchResult search(SearchParam searchParam) {
        //1准备检索请求
        SearchRequest searchRequest = buildSearchRequest(searchParam);
        SearchResult searchResult = null;
        try {
            //2执行检索请求
            SearchResponse searchResponse = client.search(searchRequest, GulimallEsConfig.COMMON_OPTIONS);
            //3把检索结果封转为我们想要的数据
            searchResult = buildSearchResult(searchResponse);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return searchResult;
    }

    //   #模糊匹配 过滤(按照属性 分类 品牌 价格区间 是否有库存) 排序 分页 高亮 以及最难的聚合分析!!!
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //最外层查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }
        //分类过滤
        if (searchParam.getCatelog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatelog3Id()));
        }
        //品牌过滤
        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }
        //1有库存0无库存过滤
        boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
        //价格范围过滤 0_500 _500 500_
        if (!StringUtils.isEmpty(searchParam.getSkuPrice())) {
            String skuPrice = searchParam.getSkuPrice();
            String[] split = skuPrice.split("_");
            if (split.length == 2) {
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
                rangeQueryBuilder.gte(split[0]).lte(split[1]);
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
            if (split.length == 1) {
                if (searchParam.getSkuPrice().startsWith("_")) {
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice").lte(split[0]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                }
                if (searchParam.getSkuPrice().endsWith("_")) {
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice").gte(split[0]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                }
            }
        }
        //嵌套属性过滤  attrs=1_andriod:ios & attrs=5_其他
        List<String> attrs = searchParam.getAttrs();
        if ( attrs!= null && attrs.size() > 0) {
            for (String attr : attrs) {//1_andriod:ios
                String[] split = attr.split("_");
                String attrid= split[0];
                String[] attrValues = split[1].split(":");

                BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
                boolQueryBuilder1.must(QueryBuilders.termQuery("attrs.attrId",attrid));
                boolQueryBuilder1.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //多个嵌套属性
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQueryBuilder1, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }

        searchSourceBuilder.query(boolQueryBuilder);

        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
        return searchRequest;
    }

    private SearchResult buildSearchResult(SearchResponse searchResponse) {

        return null;
    }

p180 排序 分页 高亮

//   #模糊匹配 过滤(按照属性 分类 品牌 价格区间 是否有库存) 排序 分页 高亮 以及最难的聚合分析!!!
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //最外层查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }
        //分类过滤
        if (searchParam.getCatelog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatelog3Id()));
        }
        //品牌过滤
        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }
        //1有库存0无库存过滤
        boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
        //价格范围过滤 0_500 _500 500_
        if (!StringUtils.isEmpty(searchParam.getSkuPrice())) {
            String skuPrice = searchParam.getSkuPrice();
            String[] split = skuPrice.split("_");
            if (split.length == 2) {
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
                rangeQueryBuilder.gte(split[0]).lte(split[1]);
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
            if (split.length == 1) {
                if (searchParam.getSkuPrice().startsWith("_")) {
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice").lte(split[0]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                }
                if (searchParam.getSkuPrice().endsWith("_")) {
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice").gte(split[0]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                }
            }
        }
        //嵌套属性过滤  attrs=1_andriod:ios & attrs=5_其他
        List<String> attrs = searchParam.getAttrs();
        if ( attrs!= null && attrs.size() > 0) {
            for (String attr : attrs) {//1_andriod:ios
                String[] split = attr.split("_");
                String attrid= split[0];
                String[] attrValues = split[1].split(":");

                BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
                boolQueryBuilder1.must(QueryBuilders.termQuery("attrs.attrId",attrid));
                boolQueryBuilder1.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //多个嵌套属性
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQueryBuilder1, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }
        searchSourceBuilder.query(boolQueryBuilder);

        //排序 sort=skuPrice_asc/desc
        if(!StringUtils.isEmpty(searchParam.getSort())){
            String[] split = searchParam.getSort().split("_");
            String field = split[0];
            searchSourceBuilder.sort(field,split[1].equalsIgnoreCase("asc")? SortOrder.ASC:SortOrder.DESC);
        }
        //分页
        searchSourceBuilder.from((searchParam.getPageNum()-1)*EsConstant.PRODUCT_PASIZE);
        searchSourceBuilder.size(EsConstant.PRODUCT_PASIZE);
        //高亮
        if (org.apache.commons.lang.StringUtils.isNotEmpty(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
        return searchRequest;
    }

p181 搜索服务 聚合

 //分类聚合
        TermsAggregationBuilder cateAgg = AggregationBuilders.terms("category_agg").field("catalogId").size(50);
        cateAgg.subAggregation(AggregationBuilders.terms("category_name_agg").field("catalogName").size(1));
        searchSourceBuilder.aggregation(cateAgg);
        //品牌聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        searchSourceBuilder.aggregation(brandAgg);
        //属性聚合
            //嵌套属性聚合
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attr_agg", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(50);
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));

        attrAgg.subAggregation(attrIdAgg);
        searchSourceBuilder.aggregation(attrAgg);

p182 searchResponse封装

private SearchResult buildSearchResult(SearchResponse searchResponse, SearchParam searchParam) {
        SearchResult searchResult = new SearchResult();

        SearchHits hits = searchResponse.getHits();
        long total = hits.getTotalHits().value;
        //分页结果
        searchResult.setTotal(total);
        searchResult.setPageNum(searchParam.getPageNum());
        searchResult.setTotalPages((int) (total % EsConstant.PRODUCT_PASIZE == 0 ? total / EsConstant.PRODUCT_PASIZE :
                total / EsConstant.PRODUCT_PASIZE + 1));
        //
        SearchHit[] hits1 = hits.getHits();
        //商品列表
        ArrayList<SkuEsModel> skuEsModels = new ArrayList<>();
        if (hits1 != null && hits1.length > 0) {
            for (SearchHit searchHit : hits1) {
                //拿到结果
                String sourceAsString = searchHit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
                //高亮
                if (!StringUtils.isEmpty(searchParam.getKeyword())){
                    HighlightField skuTitle = searchHit.getHighlightFields().get("skuTitle");
                    Text[] fragments = skuTitle.getFragments();
                    skuEsModel.setSkuTitle(fragments[0].string());
                }
                skuEsModels.add(skuEsModel);
            }
        }
        searchResult.setProducts(skuEsModels);
        //
        Aggregations aggregations = searchResponse.getAggregations();
        //分类聚合
        ParsedLongTerms category_agg = aggregations.get("category_agg");
        List<? extends Terms.Bucket> buckets = category_agg.getBuckets();
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            catalogVo.setCatalogId(Long.valueOf(bucket.getKeyAsString()));
            //分类名字
            ParsedStringTerms category_name_agg = bucket.getAggregations().get("category_name_agg");
            List<? extends Terms.Bucket> buckets1 = category_name_agg.getBuckets();
            for (Terms.Bucket bucket1 : buckets1) {
                catalogVo.setCatalogName(bucket1.getKeyAsString());
            }
            catalogVos.add(catalogVo);
        }
        searchResult.setCatalogs(catalogVos);
        //品牌聚合
        ParsedLongTerms brand_agg = aggregations.get("brand_agg");
        List<? extends Terms.Bucket> brand_aggBuckets = brand_agg.getBuckets();
        ArrayList<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket brand_aggBucket : brand_aggBuckets) {
            String brandId = brand_aggBucket.getKeyAsString();
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            //品牌id
            brandVo.setBrandId(Long.valueOf(brandId));
            //品牌图片
            ParsedStringTerms brand_img_agg = brand_aggBucket.getAggregations().get("brand_img_agg");
            List<? extends Terms.Bucket> brandImgAggBuckets = brand_img_agg.getBuckets();
            for (Terms.Bucket brandImgAggBucket : brandImgAggBuckets) {
                String img = brandImgAggBucket.getKeyAsString();
                brandVo.setBrandImg(img);
            }
            //品牌名称
            ParsedStringTerms brand_name_agg = brand_aggBucket.getAggregations().get("brand_name_agg");
            List<? extends Terms.Bucket> brandNameAggBuckets = brand_name_agg.getBuckets();
            for (Terms.Bucket brandNameAggBucket : brandNameAggBuckets) {
                brandVo.setBrandName(brandNameAggBucket.getKeyAsString());
            }
            brandVos.add(brandVo);
        }
        searchResult.setBrands(brandVos);
        //属性聚合
        ParsedNested attr_agg = aggregations.get("attr_agg");
        ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
        List<? extends Terms.Bucket> attrIdAggBuckets = attr_id_agg.getBuckets();
        ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket attrIdAggBucket : attrIdAggBuckets) {
            //属性id
            String attrId = attrIdAggBucket.getKeyAsString();
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(Long.valueOf(attrId));
            //属性名
            ParsedStringTerms attr_name_agg = attrIdAggBucket.getAggregations().get("attr_name_agg");
            List<? extends Terms.Bucket> attrNameAggBuckets = attr_name_agg.getBuckets();
            attrVo.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
            //属性值
            ParsedStringTerms attr_value_agg = attrIdAggBucket.getAggregations().get("attr_value_agg");
            List<? extends Terms.Bucket> attrValueAggBuckets = attr_value_agg.getBuckets();
            ArrayList<String> attrValues = new ArrayList<>();
            for (Terms.Bucket attrValueAggBucket : attrValueAggBuckets) {
                attrValues.add(attrValueAggBucket.getKeyAsString());
            }
            attrVo.setAttrValue(attrValues);
            attrVos.add(attrVo);
        }

        searchResult.setAttrs(attrVos);
        return searchResult;
    }

p183验证结果正确性

在这里插入图片描述

p184 渲染页面

主要是themeleaf语法遍历 文本展示

<div th:each="product:${result.products}">
th:each="attr:${result.attrs}"

p185 页面筛选条件渲染

在这里插入图片描述

p186-p189 搜索页面前端渲染

主要是thymeleaf的页面搜索渲染,这里采用的是前后端不分离项目,没有用ajax,不做具体笔记

p190 搜索页面面包屑导航

在这里插入图片描述

P191 192面包屑条件删除与url编码问题

 //面包屑导航 attrs=1_andriod:ios & attrs=5_其他
        if (searchParam.getAttrs()!=null&&searchParam.getAttrs().size()>0){
            List<SearchResult.NavVo> navVos = searchParam.getAttrs().stream().map(attr -> {
                String[] s = attr.split("_");
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                navVo.setNavValue(s[1]);
                R r = productFeignService.info(Long.valueOf(s[0]));
                if (r.getCode() == 0) {
                    AttrResponseVo attr1 = r.getData("attr", new TypeReference<AttrResponseVo>() {
                    });
                    navVo.setNavName(attr1.getAttrName());
                } else {
                    navVo.setNavName(s[0]);
                }
                //取消面包屑之后attrs=1_andriod:ios
                String encode = null;
                try {
                    encode = URLEncoder.encode(attr, "UTF-8");//编码
                    encode.replace("+","%20");//空格与浏览器的差异
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();

                }
                //如果是多个参数
                if (searchParam.get_queryString().contains("&")){

                    String[] split = searchParam.get_queryString().split("&");
                    String replace="";
                    if (split[0].equals("attrs="+encode)){//如果是去除第一个参数
                         replace = searchParam.get_queryString().replace("attrs=" + encode, "");
                    }else {//如果是去除 非第一个参数
                         replace = searchParam.get_queryString().replace("&attrs=" + encode, "");
                    }
                    navVo.setLink("http://search.gulimall.com/list.html?"+replace);
                }else {//如果是一个参数
                    String replace = searchParam.get_queryString().replace("attrs=" + encode, "");
                    navVo.setLink("http://search.gulimall.com/list.html"+replace);
                }
                return navVo;
            }).collect(Collectors.toList());

            searchResult.setNavs(navVos);
        }
        //品牌相关
        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() >0){
            List<SearchResult.NavVo> navs = searchResult.getNavs();
            SearchResult.NavVo navVo = new SearchResult.NavVo();
            navVo.setNavName("品牌");
            //远程查询所有品牌
            R r = productFeignService.brandInfo(searchParam.getBrandId());
            if (r.getCode() == 0){
                List<BrandVo> brand = (List<BrandVo>) r.getData("data", new TypeReference<List<BrandVo>>() {
                });
                StringBuffer buffer = new StringBuffer();
                String replace = "";
                for (BrandVo brandVo : brand) {
                    buffer.append(brandVo.getName()+";");
                    replace = replaceQueryString(searchParam, brandVo.getBrandId()+"","brandId");
                }
                navVo.setNavValue(buffer.toString());
                navVo.setLink("http://search.gulimail.com/list.html?"+replace);
            }
            navs.add(navVo);
            searchResult.setNavs(navs);

        }
        
private String replaceQueryString(SearchParam param, String value, String key) {
        String encode = null;
        try {
            encode = URLEncoder.encode(value, "UTF-8");
            encode = encode.replace("+", "%20");//浏览器和java对+号的差异化处理
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String replace = param.get_queryString().replace("&"+key+"=" + encode, "");
        return replace;
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值