SpringBoot集成微信小程序 (一)【搭建】

一、背景

小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

微信小程序官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/

二、技术栈

  • SpringBoot 2.0
  • MyBatis-Plus
  • MySQL
  • Redis
  • Sa-Token
  • weixin-java-miniapp

三、实现

3.1 引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.17</version>
        <relativePath/>
    </parent>

    <groupId>com.qiangesoft</groupId>
    <artifactId>wechat-miniapp</artifactId>
    <version>1.0.0</version>
    <name>wechat-miniapp</name>
    <packaging>jar</packaging>
    <description>微信小程序</description>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- web模块 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 参数校验模块 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- 自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- sa-token -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot-starter</artifactId>
            <version>1.37.0</version>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>

        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3</version>
        </dependency>

        <!-- knife4j依赖 -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.34</version>
        </dependency>

        <!-- hutool工具包 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.8.27</version>
        </dependency>

        <!-- 开源weixin-java-miniapp -->
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>4.5.0</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>wxminiapp</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.qiangesoft.miniapp.WxMaApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.2 yml配置

server:
  port: 8087

spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/wechat_ma?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root
  # redis配置
  redis:
    host: localhost
    port: 6379
    username:
    password:
    timeout: 2000
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: 0
        min-idle: 0

# mybatis-plus配置
mybatis-plus:
  # MyBaits别名包扫描路径
  type-aliases-package: com.qiangesoft.miniapp.entity
  # Mapper所对应的XML文件位置 默认【classpath*:/mapper/**/*.xml】
  mapper-locations: classpath*:/mapper/*Mapper.xml
  configuration:
    # 日志打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 是否开启自动驼峰命名规则
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      # 全局默认主键类型
      id-type: auto
      # 逻辑删除配置
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

# Sa-Token配置
sa-token:
  # token名称(同时也是 cookie 名称)
  token-name: satoken
  # token有效期(单位:秒)默认30天
  timeout: 2592000
  # token最低活跃频率(单位:秒)
  active-timeout: -1
  # 是否允许同一账号多地同时登录
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个 token
  is-share: true
  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
  token-style: uuid
  # 是否输出操作日志
  is-log: true

# 微信小程序配置
wx:
  miniapp:
    appid: xxx
    secret: xxx
    token:
    aesKey:
    msg-data-format: JSON
    config-storage:
      type: redis
      key-prefix: wa
      http-client-type: httpclient
      http-proxy-host:
      http-proxy-username:
      http-proxy-password:
      retry-sleep-millis:
      max-retry-times:

# 接口文档配置
knife4j:
  enable: true
  openapi:
    title: 微信小程序
    description: 微信小程序
    email: xxx@qq.com
    concat: xxx
    url: https://www.baidu.com/
    version: v1.0
    license: Apache 2.0
    license-url: https://stackoverflow.com/
    terms-of-service-url: https://stackoverflow.com/
    group:
      default:
        group-name: 默认
        api-rule: package
        api-rule-resources:
          - com.qiangesoft.miniapp.controller

3.3 配置属性类

package com.qiangesoft.miniapp.properties;

import com.qiangesoft.miniapp.enums.HttpClientType;
import com.qiangesoft.miniapp.enums.StorageType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 属性配置类
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Data
@ConfigurationProperties(prefix = WxMaProperties.PREFIX)
public class WxMaProperties {

    public static final String PREFIX = "wx.miniapp";

    /**
     * 微信小程序的appid.
     */
    private String appid;

    /**
     * 微信小程序的Secret.
     */
    private String secret;

    /**
     * 微信小程序消息服务器配置的token.
     */
    private String token;

    /**
     * 微信小程序消息服务器配置的EncodingAESKey.
     */
    private String aesKey;

    /**
     * 消息格式,XML或者JSON.
     */
    private String msgDataFormat;

    /**
     * 存储策略
     */
    private final ConfigStorage configStorage = new ConfigStorage();

    /**
     * 存储配置类
     */
    @Data
    public static class ConfigStorage {

        /**
         * 存储类型.
         */
        private StorageType type = StorageType.Memory;

        /**
         * 指定key前缀.
         */
        private String keyPrefix = "wa";

        /**
         * http客户端类型.
         */
        private HttpClientType httpClientType = HttpClientType.HttpClient;

        /**
         * http代理主机.
         */
        private String httpProxyHost;

        /**
         * http代理端口.
         */
        private Integer httpProxyPort;

        /**
         * http代理用户名.
         */
        private String httpProxyUsername;

        /**
         * http代理密码.
         */
        private String httpProxyPassword;

        /**
         * http 请求重试间隔
         * <pre>
         *   {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setRetrySleepMillis(int)}
         * </pre>
         */
        private Integer retrySleepMillis = 1000;

        /**
         * http 请求最大重试次数
         * <pre>
         *   {@link cn.binarywang.wx.miniapp.api.impl.BaseWxMaServiceImpl#setMaxRetryTimes(int)}
         * </pre>
         */
        private Integer maxRetryTimes = 5;
    }

}

3.4 微信相关存储方式配置类

package com.qiangesoft.miniapp.config.ma.store;

import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.hutool.core.util.StrUtil;
import com.qiangesoft.miniapp.properties.WxMaProperties;

/**
 * 微信配置抽象
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
public abstract class AbstractWxMaStorageConfiguration {

    /**
     * 配置
     *
     * @param config
     * @param properties
     * @return
     */
    protected WxMaDefaultConfigImpl config(WxMaDefaultConfigImpl config, WxMaProperties properties) {
        // 基本配置
        config.setAppid(StrUtil.trimToNull(properties.getAppid()));
        config.setSecret(StrUtil.trimToNull(properties.getSecret()));
        config.setToken(StrUtil.trimToNull(properties.getToken()));
        config.setAesKey(StrUtil.trimToNull(properties.getAesKey()));
        config.setMsgDataFormat(StrUtil.trimToNull(properties.getMsgDataFormat()));

        // 代理配置
        WxMaProperties.ConfigStorage configStorageProperties = properties.getConfigStorage();
        config.setHttpProxyHost(configStorageProperties.getHttpProxyHost());
        config.setHttpProxyUsername(configStorageProperties.getHttpProxyUsername());
        config.setHttpProxyPassword(configStorageProperties.getHttpProxyPassword());
        if (configStorageProperties.getHttpProxyPort() != null) {
            config.setHttpProxyPort(configStorageProperties.getHttpProxyPort());
        }

        int maxRetryTimes = configStorageProperties.getMaxRetryTimes();
        if (configStorageProperties.getMaxRetryTimes() < 0) {
            maxRetryTimes = 0;
        }
        int retrySleepMillis = configStorageProperties.getRetrySleepMillis();
        if (retrySleepMillis < 0) {
            retrySleepMillis = 1000;
        }
        config.setRetrySleepMillis(retrySleepMillis);
        config.setMaxRetryTimes(maxRetryTimes);
        return config;
    }

}

package com.qiangesoft.miniapp.config.ma.store;

import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.qiangesoft.miniapp.properties.WxMaProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 内存缓存配置
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Configuration
@ConditionalOnProperty(prefix = WxMaProperties.PREFIX + ".config-storage", name = "type", matchIfMissing = true, havingValue = "memory")
@RequiredArgsConstructor
public class MemoryWxMaStorageConfiguration extends AbstractWxMaStorageConfiguration {

    private final WxMaProperties wxMaProperties;

    @Bean
    @ConditionalOnMissingBean(WxMaConfig.class)
    public WxMaConfig wxMaConfig() {
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
        return this.config(config, wxMaProperties);
    }

}

package com.qiangesoft.miniapp.config.ma.store;

import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
import com.qiangesoft.miniapp.properties.WxMaProperties;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.common.redis.WxRedisOps;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * redis缓存配置
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Configuration
@ConditionalOnProperty(prefix = WxMaProperties.PREFIX + ".config-storage", name = "type", havingValue = "redis")
@ConditionalOnClass(StringRedisTemplate.class)
@RequiredArgsConstructor
public class RedisWxMaStorageConfiguration extends AbstractWxMaStorageConfiguration {

    private final WxMaProperties properties;

    private final ApplicationContext applicationContext;

    @Bean
    @ConditionalOnMissingBean(WxMaConfig.class)
    public WxMaConfig wxMaConfig() {
        WxMaRedisBetterConfigImpl config = getWxMaInRedisTemplateConfigStorage();
        return this.config(config, properties);
    }

    private WxMaRedisBetterConfigImpl getWxMaInRedisTemplateConfigStorage() {
        StringRedisTemplate redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
        WxRedisOps redisOps = new RedisTemplateWxRedisOps(redisTemplate);
        return new WxMaRedisBetterConfigImpl(redisOps, properties.getConfigStorage().getKeyPrefix());
    }

}

package com.qiangesoft.miniapp.config.ma;

import com.qiangesoft.miniapp.config.ma.store.MemoryWxMaStorageConfiguration;
import com.qiangesoft.miniapp.config.ma.store.RedisWxMaStorageConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * 微信小程序存储策略自动配置
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Configuration
@Import({MemoryWxMaStorageConfiguration.class, RedisWxMaStorageConfiguration.class})
public class WxMaStorageAutoConfiguration {

}

3.5 平台服务配置类

package com.qiangesoft.miniapp.config.ma;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceHttpClientImpl;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceJoddHttpImpl;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceOkHttpImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import com.qiangesoft.miniapp.enums.HttpClientType;
import com.qiangesoft.miniapp.properties.WxMaProperties;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 微信小程序平台相关服务自动注册
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Configuration
@AllArgsConstructor
public class WxMaServiceAutoConfiguration {

    private final WxMaProperties wxMaProperties;

    @Bean
    @ConditionalOnMissingBean(WxMaService.class)
    public WxMaService wxMaService(WxMaConfig wxMaConfig) {
        HttpClientType httpClientType = wxMaProperties.getConfigStorage().getHttpClientType();
        WxMaService wxMaService;
        switch (httpClientType) {
            case OkHttp:
                wxMaService = new WxMaServiceOkHttpImpl();
                break;
            case JoddHttp:
                wxMaService = new WxMaServiceJoddHttpImpl();
                break;
            case HttpClient:
                wxMaService = new WxMaServiceHttpClientImpl();
                break;
            default:
                wxMaService = new WxMaServiceImpl();
                break;
        }
        wxMaService.setWxMaConfig(wxMaConfig);
        return wxMaService;
    }

}
package com.qiangesoft.miniapp.config.ma;

import com.qiangesoft.miniapp.properties.WxMaProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * 自动配置
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
@Import({WxMaStorageAutoConfiguration.class, WxMaServiceAutoConfiguration.class})
public class WxMaAutoConfiguration {

}

3.6 其他配置

package com.qiangesoft.miniapp.config;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 自定义sql字段填充器,自动填充创建修改相关字段
 *
 * @author qiangesoft
 * @date 2024-04-11
 */
@Component
public class CustomMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        setFieldValByName("createUser", StpUtil.getLoginIdAsLong(), metaObject);
        setFieldValByName("createTime", LocalDateTime.now(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("updateUser", StpUtil.getLoginIdAsLong(), metaObject);
        setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}

package com.qiangesoft.miniapp.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mybatis-plus配置
 *
 * @author qiangesoft
 * @date 2024-04-11
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

3.7 常量

package com.qiangesoft.miniapp.enums;

/**
 * httpclient类型
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
public enum HttpClientType {
    /**
     * HttpClient.
     */
    HttpClient,

    /**
     * OkHttp.
     */
    OkHttp,

    /**
     * JoddHttp.
     */
    JoddHttp,

}

package com.qiangesoft.miniapp.enums;

 /**
 * storage类型
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
public enum StorageType {
    /**
     * 内存.
     */
    Memory,

    /**
     * redis.
     */
    Redis
}

3.8 启动类

package com.qiangesoft.miniapp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 启动类
 *
 * @author qiangesoft
 * @since 2024-07-16
 */
@MapperScan("com.qiangesoft.miniapp.mapper")
@SpringBootApplication
public class WxMaApplication {

    public static void main(String[] args) {
        SpringApplication.run(WxMaApplication.class, args);
    }

}

四、至此本节结束

<think>嗯,用户想在Windows 10上正确配置Node.js的环境变量。我需要根据提供的引用资料来整理步骤。首先,引用[2]提到安装后需要新建两个文件夹,node_cache和node_global,然后在系统变量里设置NODE_HOME。引用[3]则强调检查Node.js是否安装,以及环境变量是否正确添加。用户可能遇到的问题是环境变量路径错误或者npm命令无法识别,如引用[1]和[3]所述。 我应该分步骤说明,先安装Node.js,然后配置环境变量,包括系统变量和用户变量。需要指出安装路径的确认方法,比如通过文件资源管理器找到安装目录。还要提到如何验证安装是否成功,比如使用node -v和npm -v命令。可能还需要涉及npm全局模块路径的配置,修改npm的缓存和全局路径到新建的文件夹,这涉及到编辑用户变量里的Path或者使用npm config set命令。 另外,常见问题部分需要包括路径错误、权限问题和npm命令不可用的情况,并给出解决方法,比如检查路径是否正确,以管理员权限运行命令提示符,以及重新安装Node.js。用户可能忽略的步骤是新建那两个文件夹,或者忘记在Path里添加node_global,这会导致模块无法全局使用。需要提醒用户注意这些细节。最后,确保所有步骤都清晰,避免技术术语过多,让用户容易理解。</think>### Windows 10配置Node.js环境变量分步教程 #### 、安装Node.js 1. **下载安装包** 访问[Node.js中文官网](https://nodejs.cn/)或[国际官网](https://nodejs.org/),选择LTS版本(长期支持版)下载[^2][^3]。 2. **安装步骤** - 双击安装包,默认勾选`Automatically install the necessary tools`(自动安装必要工具) - 修改安装路径(建议使用简洁路径如`D:\Nodejs`) - 完成安装后,在Node.js安装目录下新建两个文件夹:`node_cache`(缓存目录)、`node_global`(全局模块目录) #### 二、配置系统环境变量 1. **设置NODE_HOME** - 右键「此电脑」→「属性」→「高级系统设置」→「环境变量」 - 在**系统变量**中新建变量: ``` 变量名:NODE_HOME 变量值:Node.js安装路径(如D:\Nodejs) ``` 2. **修改Path变量** - 在系统变量的`Path`中添加两条记录: ``` %NODE_HOME% %NODE_HOME%\node_global ``` 3. **配置npm全局模块路径(可选)** 打开命令提示符(管理员权限),执行: ```bash npm config set prefix "D:\Nodejs\node_global" npm config set cache "D:\Nodejs\node_cache" ``` #### 三、验证配置 1. **检查安装** ```bash node -v # 显示版本号(如v18.12.1) npm -v # 显示npm版本号 ``` 2. **测试全局模块安装** ```bash npm install -g yarn # 安装成功后检查node_global文件夹是否生成文件 ``` #### 四、常见问题解决 1. **报错"npm不是内部命令"** - 检查`node_global`是否加入Path[^3] - 重启命令提示符或系统 2. **权限问题** 安装全局模块时,需以管理员身份运行命令提示符[^1] 3. **路径错误** 通过文件资源管理器手动核对Node.js安装路径,确保环境变量值与实际路径完全致[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员Meteor

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

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

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

打赏作者

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

抵扣说明:

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

余额充值