Groovy 学习

1. 导包

1.1 Http

// Http模块
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method

1.2 Json

// Json模块
import groovy.json.*

// jackson模块
import com.fasterxml.jackson.databind.ObjectMapper

1.3 kafka

// kafka模块
import org.apache.kafka.clients.producer.KafkaProducer
import org.apache.kafka.clients.producer.ProducerRecord
import org.apache.kafka.common.serialization.Serializer
import org.apache.kafka.common.serialization.StringSerializer

2. 变量

2.1 常用

// 常用
def str = 'hello'

2.2 数组

// 数组,{...} 语句块是留给闭包(Closure)使用的
int[] array = [1,2,3]

2.3 集合

// 集合
def numbers = [1, 2, 3]        

2.4 Map

// Map
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']

2.5 Pojo

// Pojo 
// Groovy 默认会隐式的创建getter、setter方法,并且会提供带参的构造器
class Person {
    String name
}
def person = new Person(name: '张三')
assert '张三' == person.name
person.name = '李四'
// person.setName('李四') 
assert '李四' == person.getName()

3. 字符串

3.1 单引号 (不可以插值)

// 单引号 (不可以插值)
def str = 'groovy'

3.2 三引号 支持多行

// 三引号 支持多行
def str = '''groovy'''
assert str.class == java.lang.String

//需要注意的是,这个字符串每一次换行都包含了一个换行转义符:
//可以通过使用反斜杠换行符剥离该字符
assert str.contains("\n") == true
assert str.size() == 14
def str = '''\
one\
two\
three'''
assert str.contains("\n") == false

3.3 双引号包裹(可以插值)

// 双引号包裹(可以插值)
def name = 'Groovy'
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Groovy'

//而且任何Groovy表达式是合法的,正如我们在示例中使用算数表达式所见一样:
def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'

//除了${}占位符以外,也可以使用$作为表达式前缀:
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'

//空${}占位符等于null
assert "demo${}" == "demonull"

//如果在GString中你需要转义$或${}占位符,使它们不出现插值,那么你只需要使用反斜杠字符转义美元符号:
assert '${name}' == "\${name}"

3.4 常用API

//String center(Number numberOfChar)  返回一个长度为numberOfChar,其左边和右边均使用空格的填充的新字符串。
def str = "demo"
assert str.center(6) == " demo "
assert str.center(6).size() == 6
assert str.center(5) ==  "demo "    //如果numberOfChar比字符串长度大1.那么在原字符串后添加一个空格。

//String center(Number numberOfChar,String padding)  返回一个长度为numberOfChar,其左边和右边均使用padding的填充的新字符串,与上一个一致:如果第一个参数比字符串长度大1,在元字符串后添加padding
assert str.center(5,"a") == "demoa"
assert str.center(7,"ab") == "ademoab"      //先在元字符串后添加padding的前一个字符,再在原字符串前添加padding的前一个字符。然后在新字符串后添加padding的第二个字符,以此类推。

//int compareToIgnoreCase(String str) 按字典大小比较两个字符串,忽略大小写,返回他们的顺序差值
def str = "a"
assert str.compareToIgnoreCase("a") == 0    //相同返回0
assert str.compareToIgnoreCase("A") == 0    //忽略大小写
assert str.compareToIgnoreCase("c") == -2   //返回差值

//Boolean equalsIgnoreCase(String str) 判断两个字符串是否相等,忽略大小写

//String getAt(int index)   字符串的下标运算符
assert "abcdefg".getAt(2) == "c"
assert "abcdefg"[2] == "c"
assert "abcdefg".getAt(1..2) == "bc"    //String getAt(Range range)
assert "abcdefg"[1..2] == "bc"

//Int indexOf(String str) 返回给定子字符串在当前字符串中首次出现的索引值
assert "abcdefg".indexOf("b") == 1
assert "abcd".indexOf("g") == -1    //如果原字符串中不存在给定子字符串就返回-1

//StringBuffer leftShift(Object value)  重载<< 使两个字符串相加,返回一个新的字符串
assert "ab".leftShift("cd").class ==  java.lang.StringBuffer
assert "ab".leftShift("cd").toString() == "abcd"

//int length() / int size()   返回字符串的长度
assert "abcd".length() == 4
assert "abcd".size() == 4

//String concat(String str) 在字符串后添加str字符串
assert "ab".concat("12") == "ab12"

//Boolean endsWith(String suffix) 测试字符串是否以给定的后缀结尾
assert "demo1".endsWith("1") == true

//String minus(Object value) 删除字符串中value部分
assert "abcd".minus("bc") == "ad"

//String next()  按字典顺序增加给定字符串末尾字符顺序
assert "abcd".next() == "abce"
assert "abc1".next() == "abc2"

//String previous() 按字典顺序增加给定字符串末尾字符顺序
assert "abcd".previous() == "abcc"
assert "abc1".previous() == "abc0"


//String padLeft(Number numberOfCharacters) 与center()用法类似,在元字符串左边填充空格字符
assert "abcd".padLeft(5) == " abcd" 
//String padLeft(Number numberOfCharacters,String padding) 与center()用法类似,在元字符串左边填充padding字符
assert "abcd".padLeft(5,"12") == "1abcd"
//String padRight(Number numberOfCharacters) 与padLeft()用法类似,在元字符串左边填充空格字符
assert "abcd".padRight(5) == "abcd " 
//String padRight(Number numberOfCharacters,String padding) 与padLeft()用法类似,在元字符串左边填充padding字符
assert "abcd".padRight(5,"12") == "abcd1"

//String plus(Object valus) 字符串相加
assert "abcd".plus("123") == "abcd123"

//String reverse() 创建当前字符串的逆序字符串
assert "abcd".reverse() == "dcba"

//String substring(int beginIndex) 返回一个当前字符串的指定索引开始的子字符串
assert "abcd".substring(1) == "bcd"
//String substring(int beginIndex,int endIndex) 返回一个当前字符串的指定索引开始的子字符串
assert "abcd".substring(1,2) == "bc"
//Character toCharacter()
//Double toDouble()
//Float toFloat()
//Integer toInteger()
//Long toLong() 字符串类型转换
//List toList() 将指定的字符串转换成一个由单个字符组成的字符串列表
assert "abcd".toList() == ["a","b","c","d"]

//String toUpperCase() 将当前字符串对象的所有字符转换为大写
assert "abcd".toUpperCase() == "ABCD"
//String toLowerCase() 将当前字符串对象的所有字符转换为小写 
assert "ABCD".toUpperCase() == "abcd"
//List tokenize()  使用空格作为字符串的分隔符
//List tokenize(String token) 使用指定的token参数作为字符串的分隔符
//String[] split(String regex) 使用与给定的正则表达式相匹配的子字符串将字符串分隔为多个字符串
// Boolean matches(String regex) 测试字符串是否匹配给定子字符串

3.5 tokenize() vs split()

1.split()返回string[], tokenize()返回list
2.tokenize()忽略空字符串

String testString = 'hello brother'
assert testString.split() instanceof String[]
assert ['hello','brother']==testString.split() //split with no arguments
assert['he','','o brother']==testString.split('l')

assert testString.tokenize() instanceof List
assert ['hello','brother']==testString.tokenize() //tokenize with no arguments
assert ['he','o brother']==testString.tokenize('l')

3.tokenize()使用字符串内的所有字符

String  testString1='hello world'
assert ['hel',' world']==testString1.split('lo')
assert ['he',' w','r','d']==testString1.tokenize('lo')

4.split()可以使用正则表达式

String testString2='hello world 123 herload'
assert['hello world ',' herload']==testString2.split(/\d{3}/)

3.6 matches()

正则表达式的简单规则
\b :单词的开始或者结尾
\w :匹配字母或者数字或者下划线或汉字
\d :匹配数字
{n}:表示匹配前一个规则,重复n次
. :匹配除了换行符以外的任意字符

  • :*前边的内容可以连续重复使用任意次以使整个表达式得到匹配
  • :与*相似,匹配一次或更多次数。
    \s :匹配任意的空白符
    ^ :匹配字符串的开始
    :匹配字符串的结束和:匹配字符串的结束和的意义就变成了匹配行的开始处和结束处。
assert 'hi'.matches('hi')
assert '111'.matches('\\d{3}')       //'\d':表示匹配一位数字
assert '11as11'.matches('\\d{2}.*\\d{2}')

4. 数组

因为 Groovy中使用[ ] 表示就是一个 List 集合,如果要定义 Array ,那么就必须要强制指定为一个数组类型。

使用强类型定义。
使用 as 关键字定义数组

//使用强类型定义
String[] arr1 = ["Java", "Groovy", "Android"]
assert arr1 instanceof String[]

//使用 as 关键字定义数组
def arr2 = ["Java", "Groovy", "Android"] as String[]
assert arr2 instanceof String[]


//定义多维数组
def arr3 = new int[3][4]

//println arr3.class
assert arr3.length == 3
assert arr3.size() == 3

API操作同Java一致,更建议使用集合

5. 集合

5.1 声明

list = [1,2,3]
//range转换为list
list = (1..3).toList()  

assert list == [1,2,3]
assert list[1] == 2
assert list instanceof java.util.List
list[1] = 12
assert list == [1,12,3]
longList = (0..100)
assert longList[22] == 22

5.2 List 下标操作


/** 
    GDK使用range 和 collection作为参数重载了getAt()方法,这样就可以使用范围和索引的集合访问list
*/

list = [1,2,3,4]

//使用range访问list
assert list[0..2] == [1,2,3]    
//使用集合访问list
assert list[0,2] ==[1,3]    

list[0..2] = ['x','y','z']
assert list == ['x','y','z',4]

//当范围作为下标时,数量不一定要相等,当指定的列表值小于或者为空时,列表收缩
//当指定的列表值更大时,列表增长

list = [1,2,3,4]
list[1..2] = []
assert list == [3,4] 

list = [1,2,3,4]
list[1..1] = [1,2]
assert list == [1,1,2,3,4]

//list可以通过负数进行索引
//-1表示最后一个。-2表示倒数第二个,以此类推

list = (0..3).toList()
assert list[-1] == 3

 //当范围反向时,结果列表也是反向的
assert list[3..0] == [3,2,1,0]    

//正负数可以一起使用 下式可以去掉开头和最后的元素
assert list[1..-2] == [1,2]

5.3 增加删除List元素

//+ plus方法
list = []
list += 1        //plus(Object)
list += [2,3]        //plus(Collection)
assert list == [1,2,3]
list += (4..5) 
assert list == [1,2,3,4,5]

//<< leftShift()方法
list << 6 <<7        //leftShift(Object)
assert list == [1,2,3,4,5,6,7]
list << [8,9]
assert list == [1,2,3,4,5,6,7,[8,9]]
list = [1,2,3]
list << (4..5)  //4..5 字符串
assert list == [1,2,3,4..5]

//- minus()方法
list = [1,2,3,4,5,6]
assert list -= [5,6] == [1,2,3,4]  
assert list -=3..4== [1,2]
assert list*2 == [1,2,1,2]         //multiply(Object)

5.4 List方法

//add()类似于<<,但返回布尔值
list = [1,2,3]
list.add(4)
assert list == [1,2,3,4]
list = [1,2,3]
list.add(1,4)
assert list == [1,4,2,3]    //在给定索引处插入新值
list = [1,2,3]
list.add([4,5])
assert list == [1,2,3,[4,5]]
list = [1,2,3]
list.add((1..2))
assert list == [1,2,3,1..2]

//addAll()类似于+,返回布尔值。
list = [1,2,3]
list.addAll([4,5])
assert list == [1,2,3,4,5]
list = [1,2,3]
list.addAll((4..5))
assert list == [1,2,3,4,5]

//get()  getAt()
assert [1,2,3].get(1) == 2
assert [1,2,3].getAt([1,2]) == [2,3]    //返回指定索引段的新列表
assert [1,2,3].getAt((0..1)) == [1,2]

assert  [].isEmpty()    //判断列表是否为空
assert [1,2,3].intersect([2,3]) == [2,3]    //取交集,参数只能为列表
assert [1,2,3,4].disjoint([1])    //判断是否有交集 有则返回false;
assert [1,2,[3,4]].flatten() == [1,2,3,4]   //打开嵌套的列表
assert [1,2,1,3].unique() == [1,2,3]    //去重
assert new HashSet([1,2,3,1,1,1]).toList() == [1,2,3]    //去重
assert [1,2,3,4].reverse() == [4,3,2,1]    //反转
assert [1,5,3,7].sort() == [1,3,5,7]    //升序
//对map组成的list进行排序
def list = [[a:11],[a:3],[a:5]]
assert list.sort{a,b-> return (a.a).compareTo(b.a)} == [[a:3], [a:5], [a:11]]
assert [1,5,3,7].sort{a,b -> b<=>a} == [7,5,3,1]   //降序
assert [1,2,3,1,1].count(1) == 3    //计算包含元素的个数
assert list.join('-') == '1-2-3'    //把所有的元素用给定字符串连接
assert list.join('') == '123'

[1,2,3,1,1,1].sum()    //求和
[1,2,3,1,1,1].max()    //求最大值
[1,2,3,1,1,1].min()    //求最小值

//包含 contains()
assert [1,2].contains(1)

//every() 所有的element都满足条件才返回true,否则返回false
assert list.every{it -> it<5}

//any() 只要存在一个满足条件的element就返回true,否则返回false
assert list.any{it -> it>2}

//像堆栈(stack)一样使用 push操作由<<代替 返回被删除的值
assert [1,2,3].pop()
assert [1,2,3].push(4)  //返回布尔值

//通过下标删除元素 返回被删除的值
assert [1,2,3,4].remove(2) == [1,2,4] 

//通过值删除符合条件的一个元素   返回布尔值
assert [1,'2',3,4].remove('2')   
assert [1,'2',3,1,1,1].removeAll(['2',3])    //删除所有符合条件的元素

//grep() 符合条件的element会被提取出来,形成一个list
def list = ['a', 'ab', 'abc']
assert list.grep { elem -> elem.length()<3} == ['a','ab']   //条件以闭包形式传入
assert ["ab"] == list.grep(~/../)  //条件以regex形式传入
assert ["a","ab"] == list.grep(['a', 'cde', 'ab'])  //条件以列表形式传入

//each() 遍历list
store = '' 
[1,2,3].each{it -> store += it}
assert store == '123'

//eachWithIndex() 带index的each
def list = []
['a', 'b', 'c'].eachWithIndex { elem, i ->list << "${elem}${i}"}
assert list == ["a0","b1","c2"]

//find()  findAll()返回符合条件的所有元素,
def list = [1, 2, 3, 4]
assert list.find{it -> it%2 ==0} ==2 //返回符合条件的一个元素
assert list.findAll{it -> it%2 ==0} == [2,4]
assert [1,2,3,4].findAll{it>2?it*2:null} == [3,4]   //但不能改变元素

//collect() 对集合每个元素进行运算后,得到一个新集合
def list = [1, 2, 3, 4]
assert [2,4,6,8] == list.collect{it*2}
assert [1,2,3].collect{if(it > 1){"a"}} ==   [null, 'a','a']    //返回每个元素,即使没有指定值.
assert [1,2,2,3].collect(new HashSet()){it*2}  == [2,4,6] as Set    //指定集合的类型

//findResults() 使用提供的闭包迭代Iterable变换项,并收集任何非空结果
 assert  [1,2,3].findResults { it > 1 ? "Found $it" : null } == ["Found 2", "Found 3"]

//collectEntries() 对list的每一个元素进行一定格式的转换,返回map.可以指定初始值.
assert [1,10,100,1000].collectEntries{[it,it.toString().size()]}  ==  [1:1, 10:2, 100:3, 1000:4] 
assert [1,10,100,1000].collectEntries{if(it>11){[it,it.toString().size()]}else{[it.toString().size(),it] }} == [1:1, 2:10, 100:3, 1000:4]   //必须返回每一个元素
 assert  [1,10,100,1000].collectEntries([11:1]){[it,it.toString().size()]}   ==   [11:1, 1:1, 10:2, 100:3, 1000:4] //可以指定初始值.

//groupBy() 对collection中的element按给定条件进行分组
def list = ['a', 'b', 'abc', 'ab', 'c', 'bc']
assert [1:["a","b","c"],3:["abc"],2:["ab","bc"]] == list.groupBy { elem ->
    elem.length()
}

//inject()方法遍历集合,第一次将传递的值和集合元素传给闭包,将处理结果作为传递的值,和下一个集合元素传给闭包,依此类推
result = list.inject(5){a,b -> a+=b}
assert result == 5 + 1+2+3

//transpose()方法实际上就是数学中矩阵的转置,简单的来说就是行和列的交换。如果List的长度不一,则取最短的长度
def list41 = [1, 1, 1]  
def list51 = [2, 2]  
assert [[1, 2], [1, 2]] == [list41, list51].transpose()

5.5 tokenize() vs split()

//1.split()返回string[], tokenize()返回list
//2.tokenize()忽略空字符串
String testString = 'hello brother'
assert testString.split() instanceof String[]
assert ['hello','brother']==testString.split() 
assert['he','','o brother']==testString.split('l')

assert testString.tokenize() instanceof List
assert ['hello','brother']==testString.tokenize() 
assert ['he','o brother']==testString.tokenize('l')

//3.tokenize()使用字符串内的所有字符
String  testString1='hello world'
assert ['hel',' world']==testString1.split('lo')
assert ['he',' w','r','d']==testString1.tokenize('lo')

//4.split()可以使用正则表达式
String testString2='hello world 123 herload'
assert['hello world ',' herload']==testString2.split(/\d{3}/)

5.6 集合应用

//    根据list内的map数组元素的元素有条件的去重,对name进行去重,age尽量不为空

def list =  [['name':"zhangsan",'age':null],['name':"zhangsan",'age':23],
['name':"lisi",'age':null],['name':"lisi",'age':20],
['name':"lisi",'age':null],['name':"wangwu",'age':null]]
def ref = [];

list.each{
    if(it.name in ref['name']){
       name =it.name;
       age  = it.age;
       ref.each{
            if(it.name == name && it.age == null){
                ref -= ["name":it.name,"age":it.age]
                ref += ["name":name,"age":age]           
            }
        }
    }else{
        ref += ["name":it.name,"age":it.age]
    }
}
assert ref == [[name:zhangsan, age:23], [name:lisi, age:20], [name:wangwu, age:null]]

6. Map

6.1 声明Map

通过[:]来声明一个空的map,map缺省的类型是java.util.HashMap,也可以通过调用构造方法进行显示声明,这样map仍然可以使用下标操作符进行操作。

def map = [a:1,b:2,c:3]

assert map instanceof HashMap
assert map.size() == 3
assert map['a'] == 1

emptyMap = [:]
assert map.size() == 0

explicitMap = new TreeMap()
explicitMap.putAll(map)
assert explicitMap['a'] == 1

6.2 Map操作符

Groovy中有三种从map中获取对象的方法:

  • 使用下表操作符,因为map实现了getAt方法;
  • 使用点语法像使用属性那样来获取对象;
  • 使用get()方法,可以传递一个缺省值,在map中没有相应的key时,允许返回缺省值,如果没有缺省值放回null,如果get(key,default)被调用时,key没有找到,缺省值返回,那么key:defualt对将被增加到map中。
def map == [a:1,b:2,c:3]

assert map['a']         == 1
assert map.a            == 1
assert map.get('a')     == 1
assert map.get('a',0)   == 1

assert map['d']         == null
assert map.d            == null
assert map.get('d')     == null

assert map.get('d',0) == 0
assert map.d          == 0

map['d']        = 1         
assert map.d    == 1
map.d       = 2
assert map.d    == 2

// 将值分配给map时可以使用下标操作符或者点语法,在使用点语法时,如果key包含了特殊字符,需要使用字符串符号括起来。
map = ["a.b" : 1]
assert map.'a.b'    == 1   

6.3 Map方法

map   = [a:1,b:2,c:3]
other = [c:3,a:1,b:1]

assert map == other 

assert map.isEmpty() == false   //是否为空
assert map.size() == 3
assert map.containKey('a')  //判断map中是否包含给定的key
assert map.containValue(1)  //判断map中是否包含给定的value
//keySet()返回一个key的set集合,不包含重复的key,没有固定的顺序
assert map.keySet() == toSet(['a','b','c'])
assert toSet(map.values()) == toSet([1,2,3])
assert map.entrySet() instanceof Collection

assert map.any{entry -> entry.value > 2}    //与list中同名方法类似,
assert map.every{entry -> entry.key < 'd'}

def toSet(list){
    new java.util.HashSet(list)
}

6.4 遍历Map


//each()  
//each()接受两种形式的闭包:传递一个参数,那么这个参数代表map的一个entry;传递两个参数,那么就是key和value
def map = [a:1,b:2,c:3]

def store = ''
map.each{entry -> store += entry.key + entry.value}
assert store == "a1b2c3"

def store = ''
map.each{key,value -> store += key  store += value}
assert store == "a1b2c3"


6.5 修改Map

def map = [a:1,b:2,c:3]
map.clear()
assert map.isEmpty()

def map = [a:1,b:2,c:3]
map.remove('a')
assert map.size() == 2

def map = [a:1,b:2,c:3]
assert [a:1,b:2] == map.subMap(['a','b']) 
assert [a:1,b:2] == map.findAll{entry -> entry.value < 3}

neWMap = map.find{entry -> entry.value < 3} //返回符合条件一个entry

//collect() 返回闭包结果组成的list(是否返回可选)
assert [2,4,6] == map.collect{entry -> entry.value*= 2}
//collect()可以接受一个list
assert [1,2,2,4,6] == map.collect([1,2]){entry -> entry.value*= 2}

7. JSON

7.1 JSON转Object

def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText('{ "name": "John", "ID" : "1"}')

7.2 JSON转String

//  对象转换为字符串需要自己在前后增加双引号,否则提交时解析失败!
def deviceProperty = '"'+new JsonBuilder(deviceProperty_json).toString()+'"'

7.3 JSON转String

// 解决中文显示unicode编码问题
ObjectMapper mapper = new ObjectMapper();
String json_text_jackson = mapper.writeValueAsString(jjqxsjsbVar_json);

7.4 Object 转 String

def message=[
        deviceID: deviceID,
        alertDesc: alertDesc,
        moreinfo: moreinfo,
        reasons: reasons,
        suggest: suggest,
        fixComment: fixComment
]
// 将对象变为字符串
def message_str=JsonOutput.toJson(message)

8. HTTP

8.1 Get

// GET 无参数
def employee = new URL('http://dcom-http:8080/bj/employee/getCascaderForDisplay').text
def supply = 'http://dcom-http:8080/bj/supplier/getCascaderForFlow'.toURL().text

// GET 有参数 ( 如果要使用get带参数,最好将参数内容通过urlencode编码,而且使用new url方式调用)
def sendcondLines_text = new URL('http://dcom-http:8080/bj/post/getSecondLineListByProfessionId?professionId='+URLEncoder.encode(professionId,'UTF-8')).text

8.2 Post

// POST
// 参数
def params = [
        // 告警点位
        "dcimDevice": execution.getVariable("devicePoint")
]
// URL
def path = 'http://dcom-http:8080/bj/eventLevel/getDeviceAndLevel'
// 发起请求
def http = new HTTPBuilder(path)
http.request(Method.POST) {
    requestContentType = ContentType.JSON
    body = params
    // 失败
    response.failure = { resp ->
        // 打印错误日志
        org.slf4j.LoggerFactory.getLogger("--->from groovy ").warn("get device and level failure ${resp.status}")
    }
    // 成功
    response.success = { resp, json ->
        if (json != null) {
            if (json.deviceName != null && json.deviceName != "") {
            	...
            }
        }
    }
}

10. 日志

org.slf4j.LoggerFactory.getLogger("--->from groovy ").warn( "Request failed with status ${resp.status}")
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值