1.1.1. 磨刀工具类 —— JsonUtils

1.1.1. 磨刀工具类 —— JsonUtils

1. Maven依赖

Spring Boot项目

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

Spring Boot项目

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>

2. 代码

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import df.zhang.util.date.DatePattern;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * JSON序列化或反序列化工具,使用{@link ObjectMapper}实现。
 *
 * @author df.zhang Email: 84154025@qq.com
 * @date 2019-04-21
 * @since 1.0.0
 */
public final class JsonUtils {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // 关闭序列化时遭遇空对象(对象中无任何属性)的错误
        OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 关闭反序列化时,找不到对应属性或set方法时的错误。
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 设置值为null的属性不参与序列化
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 禁用序列化时间对象时,输出为时间戳的特性
        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 驼峰转蛇形命名法(下划线)
        OBJECT_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SnakeCaseStrategy.SNAKE_CASE);

        // 自定义java time模块
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DatePattern.DATETIME_YYYY_MM_DD_HH_MM_SS));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DatePattern.DATETIME_YYYY_MM_DD_HH_MM_SS));
        javaTimeModule.addSerializer(Date.class, new DateSerializer(false, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {

            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String dateString = jsonParser.getText().trim();
                if (StringUtils.isNotBlank(dateString)) {
                    return Date.from(LocalDateTime.parse(dateString).atZone(ZoneId.systemDefault()).toInstant());
                }
                return null;
            }
        });
        javaTimeModule.addSerializer(Instant.class, new JsonSerializer<Instant>() {

            @Override
            public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                if (instant == null) {
                    return;
                }
                jsonGenerator.writeString(DatePattern.DATETIME_YYYY_MM_DD_HH_MM_SS.format(instant.atZone(ZoneId.systemDefault())));
            }
        });
        javaTimeModule.addDeserializer(Instant.class, new JsonDeserializer<Instant>() {

            @Override
            public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                String dateString = jsonParser.getText().trim();
                if (StringUtils.isNotBlank(dateString)) {
                    return LocalDateTime.parse(dateString).atZone(ZoneId.systemDefault()).toInstant();
                }
                return null;
            }
        });

        OBJECT_MAPPER.registerModule(new ParameterNamesModule());
        // 添加Jdk8模块,支持Optional
        OBJECT_MAPPER.registerModule(new Jdk8Module());
        // 添加java time模块
        OBJECT_MAPPER.registerModule(javaTimeModule);
    }

    /**
     * 获取全局静态资源{@link ObjectMapper}对象。
     *
     * @return com.fasterxml.jackson.databind.ObjectMapper
     * @date 2019-05-04 16:11
     * @author df.zhang
     * @since 1.0.0
     */
    public static ObjectMapper getObjectMapper() {
        return OBJECT_MAPPER;
    }

    /**
     * 将任意对象序列化为JSON字符串,具体输出内容由当前类{@link ObjectMapper}的配置决定。详情参考类说明。
     *
     * @param obj 任意对象。
     * @return java.lang.String
     * @date 2019-04-21 21:59
     * @author df.zhang
     * @since 1.0.0
     */
    public static String serialize(Object obj) {
        if (Objects.isNull(obj)) {
            return "{}";
        }
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            /*
             * 如果序列化失败,判断obj的类型。 是数组或集合就输出[],否则输出{}。
             */
            Class<?> objClass = obj.getClass();
            if (objClass.isArray() || Collection.class.isAssignableFrom(objClass)) {
                return "[]";
            }
            return "{}";
        }
    }

    /**
     * 将字符串反序列化为指定类型的对象。要求第一个参数JSON字符串是对象类型字符串,即以{开头,以}结尾;第二个参数的类型不为抽象类、不为接口、有无参构造函数。
     * 若需要将[]数组类型json字符串反序列化为集合(含泛型)或更复杂的数组(集合)对象,请使用{@link JsonUtils#deserialize(String, TypeReference)}方法。
     *
     * @param json   JSON字符串
     * @param tClass 不为抽象类、不为接口、有无参构造函数的类型
     * @return java.util.Optional&lt;T&gt; 返回类型使用{@link Optional}包装,有效避免空指针异常
     * @date 2019-04-21 22:35
     * @author df.zhang
     * @since 1.0.0
     */
    public static <T> Optional<T> deserialize(String json, Class<T> tClass) {
        if (StringUtils.isBlank(json) || isDeserializeClass(tClass)) {
            return Optional.empty();
        }
        try {
            return Optional.ofNullable(OBJECT_MAPPER.readValue(json, tClass));
        } catch (IOException e) {
            return Optional.empty();
        }
    }

    /**
     * 将字符串反序列化为指定类型的对象。允许第一个参数JSON字符串任意对象类型或数组类型字符串,即以“{”开头“}”结尾或“[”开头“]”结尾均可;
     * 第二个参数的类型需要为{@link TypeReference}的实现类,该类可以接收复杂的泛型参数,用于在编译时获取到准确的泛型类型。。
     *
     * @param json          JSON字符串
     * @param typeReference 任意{@link TypeReference}的实现类,该类可以接收复杂的泛型参数,用于在编译时获取到准确的泛型类型。
     * @return java.util.Optional&lt;T&gt; 返回类型使用{@link Optional}包装,有效避免空指针异常
     * @date 2019-04-21 22:41
     * @author df.zhang
     * @since 1.0.0
     */
    public static <T> Optional<T> deserialize(String json, TypeReference<T> typeReference) {
        if (StringUtils.isBlank(json)) {
            return Optional.empty();
        }
        try {
            return Optional.ofNullable(OBJECT_MAPPER.readValue(json, typeReference));
        } catch (IOException e) {
            return Optional.empty();
        }
    }

    /**
     * 检查传入任意类型Class是否满足反序列化要求,即不为抽象类、不为接口、有无参构造函数
     *
     * @param clazz param1
     * @return boolean
     * @date 2019-04-21 22:08
     * @author df.zhang
     * @since 1.0.0
     */
    private static boolean isDeserializeClass(Class<?> clazz) {
        if (Objects.isNull(clazz)) {
            return false;
        }
        // 判断是否为抽象类或者接口
        int mod = clazz.getModifiers();
        if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) {
            return false;
        }
        // 获取无参构造函数
        try {
            Constructor<?> constructor = clazz.getConstructor();
            return constructor != null;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }


    private JsonUtils() {
        throw new UnsupportedOperationException();
    }
}

依赖工具类DatePattern.java

import java.time.format.DateTimeFormatter;

/**
 * 日期时间格式化常量类,基于Java8的Date&Time API
 *
 * @author df.zhang Email: 84154025@qq.com
 * @date 2019-04-21
 * @since 1.0.0
 */
public final class DatePattern {

    public static final DateTimeFormatter TIME_HMMSS = DateTimeFormatter.ofPattern("hmmss");
    public static final DateTimeFormatter TIME_H_MM_SS = DateTimeFormatter.ofPattern("h:mm:ss");
    public static final DateTimeFormatter TIME_AHMMSS = DateTimeFormatter.ofPattern("ahmmss");
    public static final DateTimeFormatter TIME_AH_MM_SS = DateTimeFormatter.ofPattern("ah:mm:ss");
    public static final DateTimeFormatter TIME_HHMMSS = DateTimeFormatter.ofPattern("HHmmss");
    public static final DateTimeFormatter TIME_HH_MM_SS = DateTimeFormatter.ofPattern("HH:mm:ss");

    public static final DateTimeFormatter DATE_YYMM = DateTimeFormatter.ofPattern("yyMM");
    public static final DateTimeFormatter DATE_YYYYMM = DateTimeFormatter.ofPattern("yyyyMM");
    public static final DateTimeFormatter DATE_YYYY_MM = DateTimeFormatter.ofPattern("yyyy-MM");
    public static final DateTimeFormatter DATE_YYMMDD = DateTimeFormatter.ofPattern("yyMMdd");
    public static final DateTimeFormatter DATE_YYYYMMDD = DateTimeFormatter.ofPattern("yyyyMMdd");
    public static final DateTimeFormatter DATE_YY_MM_DD = DateTimeFormatter.ofPattern("yy-MM-dd");
    public static final DateTimeFormatter DATE_YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    public static final DateTimeFormatter DATETIME_YYYYMMDDHHMMSS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    public static final DateTimeFormatter DATETIME_YYYYMMDDHHMMSSSSS = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ssZ");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS_UTC = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss'Z'");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS_SSS = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss.SSS");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS_SSS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss.SSSZ");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_H_MM_SS_SSS_UTC = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss.SSS'Z'");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_AH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd ah:mm:ss");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_AH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd ah:mm:ssZ");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_AH_MM_SS_SSS = DateTimeFormatter.ofPattern("yyyy-MM-dd ah:mm:ss.SSS");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_AH_MM_SS_SSS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd ah:mm:ss.SSSZ");
    public static final DateTimeFormatter DATETIME_YY_MM_DD_AH_MM_SS_SSS_UTC = DateTimeFormatter.ofPattern("yyyy-MM-dd ah:mm:ss.SSS'Z'");
    public static final DateTimeFormatter DATETIME_YY_MM_DDTHH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
    public static final DateTimeFormatter DATETIME_YY_MM_DDTHH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DD_HH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DDTHH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DDTHH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DDTHH_MM_SS_SSS = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DDTHH_MM_SS_SSS_Z = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    public static final DateTimeFormatter DATETIME_YYYY_MM_DDTHH_MM_SS_SSS_UTC = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    public static final DateTimeFormatter SLASH_DATE_YY_MM_DD = DateTimeFormatter.ofPattern("yy/MM/dd");
    public static final DateTimeFormatter SLASH_DATE_YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_H_MM_SS = DateTimeFormatter.ofPattern("yyyy/MM/dd h:mm:ss");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_H_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy/MM/dd h:mm:ssZ");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_AH_MM_SS = DateTimeFormatter.ofPattern("yyyy/MM/dd ah:mm:ss");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_AH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy/MM/dd ah:mm:ssZ");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_AH_MM_SS_UTC = DateTimeFormatter.ofPattern("yyyy/MM/dd ah:mm:ss'Z'");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_HH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ssZ");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DD_HH_MM_SS_UTC = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss'Z'");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DDTHH_MM_SS = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DDTHH_MM_SS_SSS = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss.SSS");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DDTHH_MM_SS_SSS_Z = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss.SSSZ");
    public static final DateTimeFormatter SLASH_DATETIME_YYYY_MM_DDTHH_MM_SS_SSS_UTC = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss.SSS'Z'");

    public static final DateTimeFormatter CHINESE_TIME_H_MM_SS = DateTimeFormatter.ofPattern("h时mm分ss秒");
    public static final DateTimeFormatter CHINESE_TIME_AH_MM_SS = DateTimeFormatter.ofPattern("ah时mm分ss秒");
    public static final DateTimeFormatter CHINESE_TIME_HH_MM_SS = DateTimeFormatter.ofPattern("HH时mm分ss秒");
    public static final DateTimeFormatter CHINESE_DATE_YY_MM_DD = DateTimeFormatter.ofPattern("yy年MM月dd日");
    public static final DateTimeFormatter CHINESE_DATE_YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDH_MM_SS = DateTimeFormatter.ofPattern("yyyy年MM月dd日h时mm分ss秒");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy年MM月dd日h时mm分ss秒Z");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDAH_MM_SS = DateTimeFormatter.ofPattern("yyyy年MM月dd日ah时mm分ss秒");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDAH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy年MM月dd日ah时mm分ss秒Z");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDHH_MM_SS = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DDHH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒Z");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
    public static final DateTimeFormatter CHINESE_DATETIME_YYYY_MM_DD_HH_MM_SS_Z = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");

    private DatePattern() {
        throw new UnsupportedOperationException();
    }
}

依赖工具类StringUtils .java,部分方法参考借鉴Spring的工具类StringUtils

import java.util.Objects;

/**
 * 字符串操作类,一些常用的字符串工具集
 *
 * @author df.zhang Email: 84154025@qq.com
 * @date 2019-04-21
 * @since 1.0.0
 */
public final class StringUtils {
    /**
     * 判断字符串是否非空。
     *
     * @param str 字符串
     * @return boolean 非空返回true,空返回false
     * @date 2019-04-21 23:20
     * @author df.zhang
     * @since 1.0.0
     */
    public static boolean isNotEmpty(String str) {
        return !Objects.isNull(str) && !str.isEmpty();
    }

    /**
     * 判断字符串是否为空。
     *
     * @param str 字符串
     * @return boolean 空返回true,非空返回false
     * @date 2019-04-21 23:21
     * @author df.zhang
     * @since 1.0.0
     */
    public static boolean isEmpty(String str) {
        return !isNotEmpty(str);
    }

    /**
     * 判断字符串是否非空且并非只包含空白符。此方法验证该字符串至少有一个非空白符的字符时才为真。
     *
     * @param str 字符串
     * @return boolean 字符串长度大于0且至少包含一个非空白符的字符时返回true,否则返回false
     * @date 2019-04-21 23:21
     * @author df.zhang
     * @since 1.0.0
     */
    public static boolean isNotBlank(CharSequence str) {
        return (!Objects.isNull(str) && str.length() > 0 && containsText(str));
    }

    /**
     * 判断字符串是否非空且并非只包含空白符。此方法验证该字符串至少有一个非空白符的字符时才为真。
     *
     * @param str 字符串
     * @return boolean 字符串长度大于0且至少包含一个非空白符的字符时返回true,否则返回false
     * @date 2019-04-21 23:21
     * @author df.zhang
     * @since 1.0.0
     */
    public static boolean isNotBlank(String str) {
        return (str != null && !str.isEmpty() && containsText(str));
    }

    /**
     * 判断字符串是否为空或者只包含空白符,即字符串长度为0或者字符串内只有空白符(一个或多个)时,都验证为真。
     *
     * @param str param1
     * @return boolean 字符串长度为0或者字符串内只有空白符(一个或多个)时,都返回true;否则返回false
     * @date 2019-04-21 23:25
     * @author df.zhang
     * @since 1.0.0
     */
    public static boolean isBlank(String str) {
        return !isNotBlank(str);
    }

    /**
     * 检查文本是否包含非空白符的字符
     *
     * @param str 字符串
     * @return boolean 包含任意非空白符字符时返回true
     * @date 2019-04-21 23:28
     * @author df.zhang
     * @since 1.0.0
     */
    private static boolean containsText(CharSequence str) {
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值