参考大牛实现思路,实现批量查询博客质量分小项目


前言

https://blog.csdn.net/Dontla/article/details/132240693
https://blog.csdn.net/hlzdbk/article/details/130554646

主要参考以上两篇大牛博文,实现了批量查询博客文章质量分的小项目。

这两篇文章对于实现思路、步骤及代码都进行了详细介绍。这里我也记录一下自己实现的过程,学习了新知识,也对前端技术使用进行复习。


一、这个想法怎么来的?

在这里插入图片描述

在今年(2023),我打算认真整理一下自己的博客,将部分博客添加了封面,而且目录也进行了规范。一个偶然的时间,我知道了CSDN博文质量检测,并将其添加到了收藏。将近期写的文章都进行了检测,只要分数大于85我就不管了,小于85的进行优化。近百篇博文,只有近期的进行质量分查询,感觉大部分都在90分左右,于是在100篇的时候,厚着脸皮着急去认证专家。结果未通过,【您的部分博文平均质量分稍微有些低,博客专家需要近两年博文平均质量分需要达到90分左右,还请您继续创作优质博文后再来申请哦。】

然后我就将大部分的文章进行了质量分查询,发现不少分数都很低,再往前的更低。这咋整,得优化文章呀。可是得有针对性优化,于是我一篇一篇的查询质量分数,把分数低的记录下来,择期进行优化。可是百篇文章,这工程量也不小呀。

想着是否可以批量查询博客质量分呢?

二、批量查询质量分思考!

1.实现技术思考

想着是否可以批量查询博客质量分呢?于是自己动手分析了一波,发现CSDN网站的请求和响应还是很容易看懂的,很快找了到相应的请求,也可以看到具体的响应数据。有戏呀。接下里就是思考使用什么样的技术去实现了,第一想到就是爬虫,而且是Java语言的爬虫,网上搜索了一些相关技术,比如 Apache HttpClientjsoupWebMagicNutch 等等。在了解这些技术的同时我也在思考,目前的需求就是向服务器发送请求,获取响应的 JSON 数据而已,不需要爬取页面数据等,使用爬虫技术有点浪费呀,而且爬虫也比较危险。

这时候两篇大牛的博文就起到了决定作用,第一篇博客讲解了请求接口的分析,最后使用 Python 实现了,我的目标是使用熟悉的 Java 实现。但有幸在这篇博文中给出了参考第二篇文章的链接,进去一看,这才是我想要的。第二位博主同样是申请专家,但是质量分数不够,共情啊。然后他使用 Java 技术实现,就是 Spring 中的 RestTemplate 技术,只是去发送请求并接收响应数据,完全符合我的实现思路。

2.实现功能思考

其实批量查询博客质量分的功能很简单,简简单单的把主要代码敲出来,运行程序就可以查询出分数,甚至在一个 main() 方法就行,直接打印在控制台。第二篇博客是使用 POI 技术将数据导出在 Excel 中,更加方便了。

我该实现怎样的功能呢?对于批量查询的这个需求来说,简单实现即可,毕竟我们只是偶尔查看质量分,也没有必要将其保存在数据库中,而且也许哪天CSDN也可以批量查询呢(但是感觉CSDN有自己的考虑)。

想来想去,最后决定自己就做个页面吧,将文章质量分一次性查询出来,展示在页面上,同时提供下载功能,再加上分数统计。也不会将数据保存,每次想查询就输入自己的用户ID,毕竟这个分数在每次更新博客内容后要变化,而且CSDN关于质量分的算法也会变的。所以只查看当下的博客质量分就可以。

3.实现最终效果

这是最终的实现效果:

在这里插入图片描述

三、批量查询质量分具体实现!

1.RestTemplate技术

RestTemplate 技术是实现批量查询质量分的核心。它是 Spring 中的技术,是一个用于发送 HTTP 请求的工具,它提供了一组简单易用的 API 来执行各种 HTTP 请求操作,例如 GETPOSTPUTDELETE 等。RestTemplateSpring 框架的一部分,也因此在使用之前需要先导入 Spring 的相关依赖。

在这里插入图片描述

查看该类可以知道,RestTemplate 是在 org.springframework.web.client 包下,继承了 InterceptingHttpAccessor抽象类 并实现了 RestOperations 接口。官方说明:

RestTemplate 是同步客户端执行 HTTP 请求,在底层 HTTP 客户端库(如 JDK HttpURLConnectionApache HttpComponents 等)上公开一个简单的模板方法 API。RestTemplate 通过 HTTP 方法为常见场景提供了模板,此外还提供了支持不太常见情况的通用交换和执行方法。

RestTemplate 主要有以下几个特点:

  1. 统一接口:无论是 GETPOSTPUT 还是 DELETEHTTP 请求,RestTemplate 都提供了一致的接口,使得代码复用性更高。
  2. 支持不同的 HTTP 消息转换器:RestTemplate 支持多种 HTTP 消息转换器,如 StringHttpMessageConverterFormHttpMessageConverterByteArrayHttpMessageConverter 等,可以根据需要选择合适的消息转换器。
  3. 自定义请求和响应:通过 HttpEntity 可以将自定义的请求体和请求头发送给服务器,同时也可以获取服务器返回的自定义响应体和响应头。
  4. 支持不同的 HTTP 客户端:RestTemplate 默认使用 Apache HttpClient 作为 HTTP 客户端,但也可以通过修改配置使用其他 HTTP 客户端,如 OkHttpApache HttpCore 等。
  5. 异常处理:RestTemplate 提供了统一的异常处理机制,对于 HTTP 请求的各种异常都能进行合理地处理。

RestTemplate 中部分方法如下:

在这里插入图片描述

2.开发环境及技术

  • SpringBoot:2.7.15(JDK 11、Maven构建)
  • fastjson2:2.0.22
  • jquery:3.6.4
  • twitter-bootstrap:5.2.3
  • bootstrap-table:1.21.4

3.封装处理HTTP请求

在使用 RestTemplate 中 API 之前需要创建它的实例。这里使用 RestTemplateBuilder 类中的 build() 方法构建,可以对构建的 RestTemplate 实例进行其他配置,比如设置根 URL、设置拦截器等。

以下是配置类 RestTemplateConfig,主要就是获取 RestTemplate 实例。

@Configuration
public class RestTemplateConfig {

    /**
     * 获取RestTemplate实例
     *
     * @return
     */
    @Bean
    public RestTemplate getRestTemplate(){
        RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
        // 这里可以对构建的RestTemplate实例进行其他配置
        // ......
        return restTemplateBuilder.build();
    }
}

之后新建 RestTemplateArticle 类,该类封装了使用 RestTemplate 执行博文相关的 HTTP 请求的方法。整体结构如下:

@Component
public class RestTemplateArticle {
    
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 根据用户ID获取博客Tab标签页统计数据
     *
     * @param username 用户ID
     * @return LinkedHashMap<String, Object> 标签页统计数量
     */
    public LinkedHashMap<String, Object> getTabTotal(String username) {}
    
    /**
     * 获取全部博文列表数据
     *
     * @param username 用户ID
     * @param total    文章总数,注意服务器每页最高可获取100条,在这里我们每页查询50条
     * @return ArrayList<LinkedHashMap < String, Object>> 文章列表数据
     */
    public ArrayList<LinkedHashMap<String, Object>> getBusinessBlogList(String username, int total) {}
    
    /**
     * 获取文章质量分数
     *
     * @param blogList 文章列表
     * @return ArrayList<LinkedHashMap < String, Object>> 文章质量分(文章信息+质量分信息)
     */
    public ArrayList<LinkedHashMap<String, Object>> getArticleScore(ArrayList<LinkedHashMap<String, Object>> blogList) {}            
    
}

可以看到该类中共有三个方法,分别发送3个 HTTP 请求。整体思路是前获取文章总数量,然后分页查询文章列表,最后将列表中每篇文章的质量分查询出来。

第一个方法是根据用户ID获取博客Tab标签页统计数据。 就是下图这里的统计数据,这里可以拿到我们的文章数量,对下一步分页查询文章列表有用。

在这里插入图片描述

接下来看该请求,还是非常容易找到的,是一个 GET 请求。

在这里插入图片描述

查看该请求的响应结果,是 JSON 格式的,不乱,中规中矩的。

在这里插入图片描述

我们该如何实现请求,获取响应数据呢?代码很简单,发送的 url 拼接上我们的 username,通过 RestTemplategetForEntity() 方法发送请求,并接收响应。将响应结果中的 data 数据取出来强转为 LinkedHashMap 集合。我们通过 IDEA 打断点,很容易知道它的数据类型。

/**
 * 根据用户ID获取博客Tab标签页统计数据
 *
 * @param username 用户ID
 * @return LinkedHashMap<String, Object> 标签页统计数量
 */
public LinkedHashMap<String, Object> getTabTotal(String username) {

    LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();

    // 远程请求url
    String url = "https://blog.csdn.net/community/home-api/v1/get-tab-total?username=" + username;

    try {
        // 使用RestTemplate发送GET请求并返回响应对象
        ResponseEntity<JSONObject> response = restTemplate.getForEntity(url, JSONObject.class);
        // 获取响应数据:data(返回数据项有 code、message、traceId、data)
        linkedHashMap = (LinkedHashMap<String, Object>) response.getBody().get("data");

    } catch (RestClientException e) {
        e.printStackTrace();
    }

    return linkedHashMap;
}

第二个方法是根据用户ID和文章总数分页查询文章列表。 文章列表中有文章的链接地址,获取 url 后才能去查询质量分。

我们看请求的 url,也是一个 GET 请求。你会发现很多熟悉的东西:当前是第一页,每页展示20条记录,业务类型是 blog 等。

在这里插入图片描述

那么响应数据是什么样子的呢?数据还是放在 data 中,data 中的 list 里放了20篇文章的数据,数据中有文章的ID、标题、描述、文章url、发布时间等等。

在这里插入图片描述

有了这些之后,我们如何去请求呢?

/**
 * 获取全部博文列表数据
 *
 * @param username 用户ID
 * @param total    文章总数,注意服务器每页最高可获取100条,在这里我们每页查询50条
 * @return ArrayList<LinkedHashMap < String, Object>> 文章列表数据
 */
public ArrayList<LinkedHashMap<String, Object>> getBusinessBlogList(String username, int total) {

    ArrayList<LinkedHashMap<String, Object>> blogList = new ArrayList<>(total);

    // 计算页数:通过总数和每页展示数量
    int pageNum = total % 50 == 0 ? total / 50 : (total / 50) + 1;
    // 请求每页数据
    for (int i = 1; i <= pageNum; i++) {
        // 远程请求url,拼接参数:每页固定展示50条,需要拼接页数
        String url = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=" + i + "&size=50&businessType=blog&orderby=&noMore=false&year=&month=&username=" + username;

        try {
            // 使用RestTemplate发送GET请求并返回响应对象
            ResponseEntity<JSONObject> response = restTemplate.getForEntity(url, JSONObject.class);
            // 获取响应数据:data(返回数据项有 code、message、traceId、data)
            LinkedHashMap<String, Object> dataMap = (LinkedHashMap<String, Object>) response.getBody().get("data");
            // 从data中获取文章列表数据
            blogList.addAll((ArrayList<LinkedHashMap<String, Object>>) dataMap.get("list"));

        } catch (RestClientException e) {
            e.printStackTrace();
        }
    }

    return blogList;
}

方法中需要传递2个参数,一个是用户ID,一个是文章总数。文章总数主要是用来计算分页的。之前打算直接将总数带入到url中,也就是第一页就查询全部,结果发送102条数据只查询出100条,说明每页最多支持查询100条。那就自己计算呗,这里我们每页查询50条,计算一下,102条,分三页查,也就是说我们发送三次请求。并将每次查询出来的文章列表数据追加到一起。

第三个方法就是根据文章列表数据查询每篇的质量分数了。 这里只能一个链接一个链接的查询。

我们先看请求的 url,该请求是 POST 方式。

在这里插入图片描述

POST 请求携带参数是 url

在这里插入图片描述

我们得到的响应数据:文章id、分数、消息、发布时间。

在这里插入图片描述

我们的方法如下:

/**
 * 获取文章质量分数
 *
 * @param blogList 文章列表
 * @return ArrayList<LinkedHashMap < String, Object>> 文章质量分(文章信息+质量分信息)
 */
public ArrayList<LinkedHashMap<String, Object>> getArticleScore(ArrayList<LinkedHashMap<String, Object>> blogList) {

    ArrayList<LinkedHashMap<String, Object>> scoreList = new ArrayList<>(blogList.size());

    // 远程请求url
    String url = "https://bizapi.csdn.net/trends/api/v1/get-article-score";

    // 设置请求头信息,以下为必须项
    HttpHeaders headers = new HttpHeaders();
    headers.set("Accept", "application/json, text/plain, */*");
    headers.set("X-Ca-Key", "203930474");
    headers.set("X-Ca-Nonce", "45504a89-2e60-48af-af04-1612fd9cc268");
    headers.set("X-Ca-Signature", "ZA/7jL7Nh7Te2hUJLWFCNt900GNZZInhPf1UwtKdpPg=");
    headers.set("X-Ca-Signature-Headers", "x-ca-key,x-ca-nonce");
    headers.set("X-Ca-Signed-Content-Type", "multipart/form-data");
    // 设置媒体类型
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    // 循环远程请求查询所有文章质量分数
    for (LinkedHashMap<String, Object> blog : blogList) {
        // 设置请求报头和正文
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.put("url", Collections.singletonList((String) blog.get("url")));
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);

        // 创建请求URI
        URI uri = URI.create(url);

        try {
            // 使用RestTemplate发送POST请求并返回响应对象
            ResponseEntity<JSONObject> response = restTemplate.postForEntity(uri, requestEntity, JSONObject.class);
            // 获取响应数据:data(返回数据项有 code、message、traceId、data)
            LinkedHashMap<String, Object> dataMap = (LinkedHashMap<String, Object>) response.getBody().get("data");
            // 将文章数据和质量分合并
            dataMap.putAll(blog);
            // 将单篇质量分数信息添加到list集合中
            scoreList.add(dataMap);

        } catch (RestClientException e) {
            e.printStackTrace();
        }
    }
    return scoreList;
}

这里我们要设置请求头,有一些是必须项。之后要将查询到的文章列表进行遍历,查询单个文章的质量分。这里使用 post 请求方式,用到
RestTemplate.postForEntity() 方法,需求构建请求报头和正文。最后将获取的数据添加到 list 集合中。

4.服务层组合调用

之后创建了服务层,有接口和接口实现类。以下是接口实现类,主要是调用 RestTemplateArticle 中的方法,实现批量文章质量分的查询,并将结果进行整合。

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private RestTemplateArticle restTemplateArticle;

    /**
     * 获取所有文章质量分列表
     *
     * @param username 用户ID
     * @return ArrayList<LinkedHashMap<String, Object>> 文章质量分数
     */
    @Override
    public ArrayList<LinkedHashMap<String, Object>> getArticleScoreList(String username) {

        ArrayList<LinkedHashMap<String, Object>> articleScoreList = new ArrayList<>();

        // 判断传入的用户ID不为空
        if (username != null && !"".equals(username)){
            // 查询用户下文章总数
            int blogTotal = (int) restTemplateArticle.getTabTotal(username).get("blog");

            // 判断文章总数
            if (blogTotal > 0){
                // 查询用户下所有文章
                ArrayList<LinkedHashMap<String, Object>> blogList  = restTemplateArticle.getBusinessBlogList(username, blogTotal);

                // 判断获取的文章列表数量
                if (blogList.size() > 0){
                    // 查询文章质量分
                    articleScoreList = restTemplateArticle.getArticleScore(blogList);
                }
            }
        }
        return articleScoreList;
    }
}

先根据用户ID查询文章总数,之后根据文章总数查询用户下的所有文章,再根据文章列表查询出质量分,这时质量分数据中就包含了博客列表信息。

5.控制层处理

前端请求 /allArticleScore,调用业务层方法获取所有文章质量分列表,然后封装到 JSON 中响应给前端。

@RestController
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    /**
     * 查询所有文章质量分数
     *
     * @param username 用户ID
     * @return
     */
    @GetMapping("/allArticleScore")
    public JSONObject allArticleScore(String username){
        // 获取所有文章质量分列表
        ArrayList<LinkedHashMap<String, Object>> articleScoreList = articleService.getArticleScoreList(username);

        // 封装查询结果,前端分页要求:rows、total
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("rows", articleScoreList);
        jsonObject.put("total", articleScoreList.size());

        return jsonObject;
    }
}

6.前端页面展示

前端页面使用了 BootStrap 框架,表格展示使用到了 BootStrap-Table 扩展,非常方便的进行数据展示,分页展示,这里是一次性查询出全部数据,通过 BootStrap-Table 扩展假分页。同时它还提供了诸多便利的功能,比如排序、导出等。

<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>博文质量分批量查询</title>

    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.bootcdn.net/ajax/libs/bootstrap-icons/1.10.4/font/bootstrap-icons.min.css" rel="stylesheet">
    <link href="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.css" rel="stylesheet">

</head>
<body>

    <div class="container pt-5">
        <div class="mt-5">
            <form class="row justify-content-md-center">
                <div class="col-6">
                    <div class="input-group">
                        <input class="form-control form-control-lg" type="text" id="username" value="Li_Ya_Fei" placeholder="请输入用户ID">
                        <button class="btn btn-outline-secondary" type="button" onclick="searchScore()">批量查询</button>
                    </div>
                </div>
            </form>

            <div id="articleScore" class="mt-5">
                <div id="toolbar">
                    <button type="button" class="btn btn-primary">
                        100~90 <span class="badge text-bg-secondary">0</span>
                    </button>
                    <button type="button" class="btn btn-info">
                        89~80 <span class="badge text-bg-secondary">0</span>
                    </button>
                    <button type="button" class="btn btn-success">
                        79~70 <span class="badge text-bg-secondary">0</span>
                    </button>
                    <button type="button" class="btn btn-warning">
                        69~60 <span class="badge text-bg-secondary">0</span>
                    </button>
                    <button type="button" class="btn btn-danger">
                        60以下 <span class="badge text-bg-secondary">0</span>
                    </button>
                </div>
                <table id="table"
                       data-toolbar="#toolbar"
                       data-locale="zh-CN"
                       data-url="allArticleScore"
                       data-show-columns="true"
                       data-pagination="true"
                       data-search="true"
                       data-show-export="true"
                       data-show-refresh="true"
                       data-show-fullscreen="true"
                       data-show-toggle="true">
                    <thead>
                    <tr>
                        <th data-field="articleId">文章ID</th>
                        <th data-field="title" data-formatter="titleFormatter">标题</th>
                        <th data-field="postTime">发布时间</th>
                        <th data-field="viewCount" data-sortable="true">浏览量</th>
                        <th data-field="score" data-sortable="true" data-cell-style="scoreCellStyle">质量分</th>
                        <th data-field="message">提示信息</th>
                    </tr>
                    </thead>
                </table>
            </div>
        </div>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/bootstrap-table.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/locale/bootstrap-table-zh-CN.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.18.5/xlsx.core.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jspdf/2.5.1/polyfills.umd.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/TableExport/5.2.0/js/tableexport.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-table/1.21.4/extensions/export/bootstrap-table-export.min.js"></script>

    <script>
        $(document).ready(function() {
            // 初始化表格
            $("#table").bootstrapTable({
                onLoadSuccess: function (data) {
                    // 统计各分数段文章数量
                    let levelArray = [0, 0, 0, 0, 0];
                    $.each(data.rows, function(index, value) {
                        if (value.score >= 90){
                            levelArray[0]++;
                        } else if(value.score < 90 && value.score >= 80){
                            levelArray[1]++;
                        } else if(value.score < 80 && value.score >= 70 ){
                            levelArray[2]++;
                        } else if(value.score < 70 && value.score >= 60){
                            levelArray[3]++;
                        } else if (value.score < 60) {
                            levelArray[4]++;
                        }
                    });

                    // 统计数量展示
                    let levelSpanArray = $("#toolbar > button >span");
                    $.each(levelSpanArray, function (index, value) {
                        $(value).text(levelArray[index]);
                    });
                }
            });
        });

        // 查询文章质量分
        function searchScore(){
            $("#table").bootstrapTable("refresh", {url: 'allArticleScore?username=' + $("#username").val()});
        }

        // 文章标题格式化
        function titleFormatter(value, row, index){
            return `<a href="${row.url}" target="_blank">${value}</a>`;
        }

        // 单元格样式,根据分数段设置背景颜色
        function scoreCellStyle(value, row, index){
            let c = "";
            if (value >= 90){
                c = "bg-primary";
            } else if(value < 90 && value >= 80){
                c = "bg-info";
            } else if(value < 80 && value >= 70 ){
                c = "bg-success"
            } else if(value < 70 && value >= 60){
                c = "bg-warning";
            } else if (value < 60) {
                c = "bg-danger";
            }

            return {classes : c};
        }
    </script>
</body>
</html>

四、批量查询质量分页面说明

在这里插入图片描述

① 批量查询博客质量分入口,需要输入用户ID,不是昵称哦。
② 质量分数段数量统计,其实应该把平均分计算展示的。
③ 使用 bootstrap-table 扩展的搜索功能,可以进行简单数据搜索。
④ 也是扩展自带功能,可以刷新数据、切换视图、最大化,数据导出等。
⑤ 就是文章质量分列表了,展示文章ID、标题、发布时间、浏览量、质量分和提示信息。
⑥ 标题处给了链接,点击可以跳转到该博客页面。
⑦ 质量分这一列根据分数段设置了单元格的背景颜色。
⑧ 对文章列表进行了假分页。


总结

虽然在批量查询博客质量分数的小项目中,很多功能都不是必要的,毕竟实际的需求并不大。但是现在实现下来自己也是有收获的。学习或者说接触了 RestTemplate ,并进行了简单使用。还复习了前端 BootStrap 框架,比较头疼。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程火箭车

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

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

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

打赏作者

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

抵扣说明:

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

余额充值