【Golang语言之旅3】Create and use Module!~完整七步

创建一个Go模块

接下来,将创建两个模块。第一个是旨在由其他库或应用导入的库。第二个是将使用第一个的调用应用。

本教程包括七个简短的主题,每个主题都说明了语言的不同部分。

  1. 创建一个模块——用你可以从另一个模块调用的函数编写一个小模块。
  2. 从另一个模块调用您的代码——导入并使用您的新模块。
  3. 返回并处理一个错误——添​​加简单的错误处理。
  4. 返回一个随机的问候——处理切片中的数据(Go 的动态大小的数组)。
  5. 为多人返回问候——将键/值对存储在映射中。
  6. 添加测试——使用 Go 的内置单元测试功能来测试您的代码。
  7. 编译并安装应用程序——在本地编译并安装您的代码。

预准备

有一定的编程经验。 这里的代码非常简单,但它有助于了解有关函数、循环和数组的一些知识。

一种编辑代码的工具。 您拥有的任何文本编辑器都可以正常工作。大多数文本编辑器都对 Go 有很好的支持。最受欢迎的是 VSCode(免费)、GoLand(付费)和 Vim(免费)。

命令终端。 Go 适用于 Linux 和 Mac 上的任何终端,以及 Windows 中的 PowerShell 或 cmd。

1.创建一个他人可以使用的module

由创建一个go模块开始。在一个模块中,你收集一个或多个相关具体功能的一系列函数包。例如,你可能创造一个有金融分析函数包的模块,他人就可以用它来写金融应用。更多关于开发modules的,可以参考Developing and publishing modules.

Go 代码被分组到包中,包被分组到模块中。您的模块指定运行代码所需的依赖项,包括 Go 版本及其所需的其他模块集。

在模块中添加或改进功能时,您发布模块的新版本。编写调用模块中函数的代码的开发人员可以导入模块的更新包并在将其投入生产使用之前使用新版本进行测试。

1.1. 打开命令行终端,cd到home目录

在Linux 或 Mac:

cd

在Windows上:

cd %HOMEPATH%

1.2. 给你的Go module源代码创建一个greetings目录

mkdir greetings
cd greetings

1.3. 使用go mod init <module路径>命令开始你的module

如果您发布一个模块,这必须是 Go 工具可以从中下载您的模块的路径。那将是您代码的存储库。

$ go mod init example.com/greetings
go: creating new go.mod: module example.com/greetings

go mod init命令会创建一个go.mod文件来追踪你的代码依赖项。目前,文件里仅包含你的module名字和Go版本。但是,随着你添加依赖项,go.mod文件会列出你代码所依赖的各版本。这保证了重现性,并直接控制module版本的使用。
在这里插入图片描述

1.4.在你的文本编辑器中,新建一个你可以写代码的文件,并命名为greetings.go.

在这里插入图片描述

1.5.复制一下代码进greetings.go文件中,并保存文件。

package greetings

import "fmt"

// Hello returns a greeting for the named person.
func Hello(name string) string {
    // Return a greeting that embeds the name in a message.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message
}

这是你的module的第一段代码。它向调用返回问候语。您将在下一步中编写调用此函数的代码。

在这个代码中,你将:

  1. 声明一个greetings package来收集相关函数。
  2. 实现一个 Hello 函数来返回问候语。
    该函数接受一个类型为字符串name参数。该函数还返回一个字符串。在 Go 中,名称以大写字母开头的函数可以被不在同一个包中的函数调用。这在 Go 中称为导出名称(exported name)。有关导出名称的更多信息,请参阅 Go tour 中的exported name。
    在这里插入图片描述
  3. 声明一个message变量来保存您的问候语。在go中, := 操作符是在一行中声明和初始化变量的快捷方式(Go 使用右侧的值来确定变量的类型)。您也可以将其写为:
var message string
message = fmt.Sprintf("Hi, %v. Welcome!", name)`。
  1. 使用 fmt 包的Sprintf 函数创建问候消息。第一个参数是字符串类型,然后用%v替代了name参数。
  2. 对调用者返回问候语。

2. 下一步中,你将从另一个module中调用这个函数。

在第一步,创建了一个greetings模块。第二部,你将在你写的模块中调用Hello函数。你将写代码,作为一个可执行的应用,来调用Hello函数。

2.1 给你的Go module源代码创建一个hello目录(与greetings目录是同级的),这是写你的调用器的地方。

cd ..
mkdir hello
cd hello

在这里插入图片描述

2.2 为你将要写的代码添加可追踪依赖项。(即 go mod init命令)

$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

2.3 在文本编辑器中,新建一个hello.go文件来写代码

2.4 将以下代码复制到hello.go中,调用Hello函数,并打印函数的返回值。

package main

import (
	"fmt"

	"example.com/greetings"
)

func main() {
	// Get a greeting message and print it.
	message := greetings.Hello("Gladys")
	fmt.Println(message)
}

在此代码中,您:

1.声明一个main package。在 Go 中。作为应用程序执行的代码必须在main package中。
2.导入两个包:example.com/greetingsfmt 包。这使您的代码可以访问这些包中的函数。导入 example.com/greetings(包含在您之前创建的模块中的包)可以让您访问 Hello 函数。导入 fmt,它具有处理输入和输出文本的功能(例如将文本打印到控制台)。
3.通过调用 greetings 包的 Hello 函数来获取问候语。

2.5 编辑example.com/hello模块来使用你当地的example.com/greetings模块。

为了生产使用,您最好从存储库中发布 example.com/greetings 模块(带有反映其发布位置的模块路径),这样Go 工具可以在其中找到并下载它。目前,由于您尚未发布该模块,因此您需要调整 example.com/hello 模块,以便它可以在您的本地文件系统上找到 example.com/greetings 代码。

为此,请使用 go mod edit 命令编辑 example.com/hello 模块,将 Go 工具从其模块路径(模块所在的位置)重定向到本地目录(所在的位置)。

2.5.1 在hello目录下,终端输入以下命令:
$ go mod edit -replace example.com/greetings=../greetings

这个命令将example.com/greetings替换成了…/greetings,是为了本地依赖项的目的。运行后,hello目录下的go.mod文件就包含了一个替换行:
在这里插入图片描述

2.5.2 在hello目录下,运行 go mod tidy命令行。同步 example.com/hello 模块的依赖项,添加代码所需但尚未在模块中跟踪的依赖项。
$ go mod tidy
go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000

命令完成后,example.com/hello模块的go.mod会变成:
在这里插入图片描述
这个命令,找到了greetings目录下的当地代码,并添加了需要的依赖项,从而使得example.com/hello需要example.com/greetings.当你在hello.go中导入greetings包的时候,就创造了这个依赖项。

模块路径后的数字是伪版本号——生成的数字用来代替语义版本号(模块还没有)。

要标记引用已发布的模块,go.mod 文件通常会省略 replace 指令并使用末尾带有标记版本号的 require 指令。

require example.com/greetings v1.1.0

更多有关版本号的,详见 Module version numbering

2.6 在hello目录下,运行你的代码。

$ go run .
Hi, Gladys. Welcome!

祝贺!到现在为止,你已经写好了两个函数模块。
下一节,你将添加一些错误处理。

3.返回并处理一个错误

处理错误是可靠代码的基本特征。在本节中,您将添加一些代码来从 greetings 模块返回错误,然后在调用器中处理它。

3.1 在greetings/greetings.go,添加一下代码。

如果您不知道该向谁打招呼,则发送问候是没有意义的。如果名称为空,则向调用者返回错误。将以下代码复制到 greetings.go 并保存文件。

package greetings

import (
    "errors"
    "fmt"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return "", errors.New("empty name")
    }

    // If a name was received, return a value that embeds the name
    // in a greeting message.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}

在这段代码,你:

1.更改函数,使其返回两个值:一个字符串和一个错误。您的调用者将检查第二个值以查看是否发生错误。 (任何 Go 函数都可以返回多个值。有关更多信息,请参阅 Effective Go。)
2.导入 Go 标准库errors包,以便您可以使用它的 errors.New 函数
3.添加 if 语句以检查无效请求(名称应为空字符串)并在请求无效时返回错误。 errors.New 函数返回一个错误,其中包含您的消息。
4.添加 nil(意味着没有错误)作为成功返回的第二个值。这样,调用者就可以看到函数成功了。

3.2 在你的hello/hello.go文件,处理现在由 Hello 函数返回的错误以及非错误值。

将以下代码粘贴到 hello.go 中。

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Set properties of the predefined Logger, including
    // the log entry prefix and a flag to disable printing
    // the time, source file, and line number.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // Request a greeting message.
    message, err := greetings.Hello("")
    // If an error was returned, print it to the console and
    // exit the program.
    if err != nil {
        log.Fatal(err)
    }

    // If no error was returned, print the returned message
    // to the console.
    fmt.Println(message)
}

在此代码中,您:

1.将log package配置为在其log messages的开头打印命令名称(“greetings:”),不带时间戳或源文件信息。
2.将两个 Hello 返回值(包括错误)分配给变量。
3.将 Hello 参数从 Gladys 的名称更改为空字符串,以便您可以尝试错误处理代码。
4.寻找非零错误值(non-nil error value)。在这种情况下继续下去是没有意义的。
5.使用标准库的log package中的函数输出错误信息。如果出现错误,则使用log包的 Fatal 函数打印错误并停止程序。

3.3 在hello目录,终端运行hello. go看代码是否work。

$ go run .
greetings: empty name
exit status 1

这是 Go 中常见的错误处理:将错误作为值返回,以便调用者可以检查它。
接下来,您将使用 Go 切片返回随机选择的问候语。

4. 返回一个随机的问候

在本节中,您将更改代码,不是每次都返回同一个问候语,而是返回多个预定义的问候语消息中的之一。

切片类似于数组,不同之处在于它的大小会随着您添加和删除项目而动态变化。 slice 是 Go 最有用的类型之一。

您将添加一小部分来包含三个greeting消息,然后让您的代码随机返回其中一个消息。有关切片的更多信息,请参阅 Go 博客中的 Go 切片

4.1. 在greetings/greetings.go,更改代码。

package greetings

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Create a message using a random format.
    message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// init sets initial values for variables used in the function.
func init() {
    rand.Seed(time.Now().UnixNano())
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
    // A slice of message formats.
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

    // Return a randomly selected message format by specifying
    // a random index for the slice of formats.
    return formats[rand.Intn(len(formats))]
}

在此代码中,您:

1.添加一个 randomFormat 函数,该函数为greeting message返回随机选择的格式。请注意, randomFormat 以小写字母开头,使其只能由其自己的包中的代码访问(换句话说,它不会被导出)。
2.在 randomFormat 中,声明具有三种消息格式的格式切片。声明切片时,您可以在括号中省略其大小,就像:[]string。这告诉 Go 切片底层数组的大小可以动态更改。
3.使用 math/rand 包生成一个随机数,用于从切片中选择一个项目。
4.添加一个** init 函数**以使用当前时间为 rand 包做种子。 Go 在程序启动时自动执行 init 函数,在全局变量初始化之后。有关 init 函数的更多信息,请参阅 Effective Go
5.在 Hello 中,调用 randomFormat 函数获取您将返回的消息的格式,然后将格式和名称值一起使用来创建消息。
6.像以前一样返回消息(或错误)…

4.2. 在hello/hello.go中,更改代码。

您只是将 Gladys 的名字(或不同的名字,如果您愿意)作为参数添加到 hello.go 中的 Hello 函数调用中。

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Set properties of the predefined Logger, including
    // the log entry prefix and a flag to disable printing
    // the time, source file, and line number.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // Request a greeting message.
    message, err := greetings.Hello("Gladys")
    // If an error was returned, print it to the console and
    // exit the program.
    if err != nil {
        log.Fatal(err)
    }

    // If no error was returned, print the returned message
    // to the console.
    fmt.Println(message)
}

4.3. 在hello目录的命令行,运行hello.go。多跑几次。

在这里插入图片描述
下一节,您将使用切片来问候多个人。

5. 为多个人返回greetings。

在您对模块代码所做的最后更改中,您将添加对在一个请求中为多人获取问候的支持。换句话说,您将处理多值输入,然后将该输入中的值与多值输出配对。为此,您需要将一组名称传递给一个可以为每个名称返回问候语的函数。

但有一个障碍。将 Hello 函数的参数从单个名称更改为一组名称会更改函数的签名。如果您已经发布了 example.com/greetings 模块并且用户已经编写了调用 Hello 的代码,那么该更改会破坏他们的程序。

在这种情况下,更好的选择是编写一个具有不同名称的新函数。新函数将采用多个参数。这保留了旧功能以实现向后兼容性。

5.1. 在 greetings/greetings.go 中,更改代码。

package greetings

import (
    "errors"
    "fmt"
    "math/rand"
    "time"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Create a message using a random format.
    message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
    // A map to associate names with messages.
    messages := make(map[string]string)
    // Loop through the received slice of names, calling
    // the Hello function to get a message for each name.
    for _, name := range names {
        message, err := Hello(name)
        if err != nil {
            return nil, err
        }
        // In the map, associate the retrieved message with
        // the name.
        messages[name] = message
    }
    return messages, nil
}

// Init sets initial values for variables used in the function.
func init() {
    rand.Seed(time.Now().UnixNano())
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
    // A slice of message formats.
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

    // Return one of the message formats selected at random.
    return formats[rand.Intn(len(formats))]
}

在此代码中,您:

  1. 添加一个 Hellos 函数,其参数是一个名称片段而不是单个名称。此外,您将其返回类型之一从字符串更改为映射,以便您可以返回映射到问候消息的名称。
    2.让新的 Hellos 函数调用现有的 Hello 函数。这有助于减少重复,同时保留两个功能。
    3.创建消息映射以将每个接收到的名称(作为键)与生成的消息(作为值)相关联。在 Go 中,您使用以下语法初始化地图:make(map[key-type]value-type)。您让 Hellos 函数将此映射返回给调用者。有关地图的更多信息,请参阅 Go 博客上的 Go 地图实战。
    4.循环遍历您的函数收到的名称,检查每个名称是否具有非空值,然后将消息与每个名称关联。在这个 for 循环中,range 返回两个值:循环中当前项目的索引和项目值的副本。您不需要索引,因此您使用 Go 空白标识符(下划线)来忽略它。有关更多信息,请参阅 Effective Go 中的空白标识符。

5.2. 在hello/hello.go中调用代码,然后打印您返回的名称/消息映射的内容。在 hello.go 中,更改代码如下。

package main

import (
    "fmt"
    "log"

    "example.com/greetings"
)

func main() {
    // Set properties of the predefined Logger, including
    // the log entry prefix and a flag to disable printing
    // the time, source file, and line number.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // A slice of names.
    names := []string{"Gladys", "Samantha", "Darrin"}

    // Request greeting messages for the names.
    messages, err := greetings.Hellos(names)
    if err != nil {
        log.Fatal(err)
    }
    // If no error was returned, print the returned map of
    // messages to the console.
    fmt.Println(messages)
}

通过这些更改,您:

1.创建一个 names 变量作为包含三个名称的切片类型。
2.将 names 变量作为参数传递给 Hellos 函数。

5.3.在hello目录下,命令行输入运行。

输出应该是将名称与消息相关联的映射的字符串表示形式,如下所示:

$ go run .
map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!]

这一节介绍了用于表示名称/值对的映射。它还引入了通过为模块中的新功能或更改功能实现新功能来保持向后兼容性的想法。有关向后兼容性的更多信息,请参阅Keeping your modules compatible

接下来,您将使用内置的 Go 功能为您的代码创建单元测试。

6. 添加一个测试

既然您已经将代码放到了一个稳定的位置(顺便说一句,做得很好),请添加一个测试。在开发期间测试您的代码可以暴露在您进行更改时发现的错误。在本主题中,您将为 Hello 函数添加一个测试。

Go 对单元测试的内置支持使您可以更轻松地进行测试。具体来说,使用命名约定、Go 的testing package和 go test命令,您可以快速编写和执行测试。

6.1. 在greetings目录,创造一个文件叫greetings_test.go.

以 _test.go 结尾的文件名告诉 go test命令该文件包含测试函数.

6.2 在greetings_test.go里面复制代码。

package greetings

import (
    "testing"
    "regexp"
)

// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
    name := "Gladys"
    want := regexp.MustCompile(`\b`+name+`\b`)
    msg, err := Hello("Gladys")
    if !want.MatchString(msg) || err != nil {
        t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
    }
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
    msg, err := Hello("")
    if msg != "" || err == nil {
        t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
    }
}

在此代码中,您:

1.在与您正在测试的代码相同的包中实现测试功能。
2.创建两个测试函数来测试 greetings.Hello 函数。测试函数名称的形式为 TestName,其中 Name 表示特定测试的一些内容。此外,测试函数将指向testing package的 testing.T type的指针作为参数。您可以使用此参数的方法来报告和记录您的测试。
3.实现两个测试:
TestHelloName 调用 Hello 函数,传递一个name值,该函数应该能够使用该值返回有效的响应消息。如果调用返回一个错误或一个意外的响应消息(不包含您传入的名称的消息),则使用 t 参数的 Fatalf method将消息打印到控制台并结束执行。
TestHelloEmpty 使用空字符串调用 Hello 函数。此测试旨在确认您的错误处理工作正常。如果调用返回非空字符串或没有错误,则使用 t 参数的 Fatalf 方法将消息打印到控制台并结束执行。

5.3 在greetings目录的命令行,运行go test命令来执行测试。

go test命令在测试文件(名称以 _test.go 结尾的文件)中执行测试函数(名称以 Test 开头)。您可以添加 -v 标志以获得列出所有测试及其结果的详细输出。

测试应该通过。
在这里插入图片描述

5.4 中断 greetings.Hello 函数以查看失败的测试。

TestHelloName 测试函数检查您指定为 Hello 函数参数的名称的返回值。要查看失败的测试结果,请更改 greetings.Hello 函数,使其不再包含名称。

在 greetings/greetings.go 中,粘贴以下代码代替 Hello 函数。请注意,突出显示的行会更改函数返回的值,就好像 name 参数已被意外删除一样。

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Create a message using a random format.
    // message := fmt.Sprintf(randomFormat(), name)
    message := fmt.Sprint(randomFormat())
    return message, nil
}

5.5 在greetings目录下,运行go test执行测试。

这一次,不带 -v 标志运行 go test。输出将仅包含失败的测试的结果,这在您进行大量测试时非常有用。 TestHelloName 测试应该失败——TestHelloEmpty 仍然通过。
在这里插入图片描述
在下一个(也是最后一个)主题中,您将看到如何编译和安装代码以在本地运行它。

6.编译和安装应用

在最后一个主题中,您将学习几个新的 go 命令。虽然go run命令是在您进行频繁更改时编译和运行程序的有用快捷方式,但它不会生成二进制可执行文件。

本主题介绍了两个用于构建代码的附加命令:

  1. go build 命令编译包及其依赖项,但不安装结果。
  2. go install 命令编译并安装软件包。

6.1 在hello目录,运行go build命令,代码编译为可执行文件。

go build

输入后,会在hello目录下生成一个hello.exe文件。

6.2 在hello目录下,运行新的hello可执行文件。

请注意,根据您在测试后是否更改了 greetings.go 代码,您的结果可能会有所不同。(5.4那测试后我又改回去了)

On Linux or Mac:

$ ./hello
map[Darrin:Great to see you, Darrin! Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well met!]

On Windows:

$ hello.exe
map[Darrin:Great to see you, Darrin! Gladys:Hail, Gladys! Well met! Samantha:Hail, Samantha! Well m

您已将应用程序编译为可执行文件,以便可以运行它。但是要当前运行它,您的提示需要位于可执行文件的目录中,或者指定可执行文件的路径。

接下来,您将安装可执行文件,以便无需指定其路径即可运行它。

6.3 发现 Go 安装路径,go 命令将在其中安装当前包。

您可以通过运行 go list命令来发现安装路径,如下例所示:

 $ go list -f '{{.Target}}' 

在这里插入图片描述
命令的输出是C:\Users\11381\go\bin\hello.exe,这意味着二进制文件安装到 C:\Users\11381\go\bin。您将在下一步中需要此安装目录。

6.4 添加Go的安装目录到你的系统shell路径。

这样,您就可以运行程序的可执行文件,而无需指定可执行文件的位置。
在Linux or Mac运行:

$ export PATH=$PATH:/path/to/your/install/directory

在windows上运行:

$ set PATH=%PATH%;C:\path\to\your\install\directory

我在windows的cmd上输入:
在这里插入图片描述
或者,如果你的 shell 路径中已经有一个像 $HOME/bin 这样的目录,并且你想在那里安装你的 Go 程序,你可以通过使用 go env 命令设置 GOBIN 变量来更改安装目标:

$ go env -w GOBIN=/path/to/your/bin

or

$ go env -w GOBIN=C:\path\to\your\bin

6.5 一旦你更新了shell路径,运行go install命令来编译和安装package

$ go install

6.6 通过简单输入应用的名字就可运行。

$ hello
map[Darrin:Hail, Darrin! Well met! Gladys:Great to see you, Gladys! Samantha:Hail, Samantha! Well met!]

在这里插入图片描述
为了使这个有趣,打开一个新的命令提示符并在其他目录中运行 hello 可执行文件名称。
在这里插入图片描述

7.总结

在本教程中,您编写了打包成两个模块的函数:一个具有发送问候的逻辑;另一个作为第一个消费者。

有关在代码中管理依赖项的更多信息,请参阅Managing dependencies

有关开发供他人使用的模块的更多信息,请参阅Developing and publishing modules

有关 Go 语言的更多功能,请查看 Go 语言之旅

本 Go 教程到此结束!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值