基于SpringBoot的外卖项目

一、数据库环境

在这里插入图片描述

二、公共字段自动填充

只要有表使用了这些字段,就能在被使用时自动填充值。
update_time自动填充出问题01
update_time自动填充出问题02

实体类中:

    @TableField(fill = FieldFill.INSERT)    //插入时填充字段
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新时填充字段
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

实现MetaObjectHandler接口:

package com.hisi.pex.common.mpConfig;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * MyBatisPlus提供的对公共字段的操作
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 插入数据时的操作
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        //查看是否有setter方法,来判定该字段是否存在(即是否要填充该字段)
        if (metaObject.hasSetter("createTime")) {
            metaObject.setValue("createTime", LocalDateTime.now());
        }
        if (metaObject.hasSetter("updateTime")) {
            metaObject.setValue("updateTime", LocalDateTime.now());
        }

    }

    /**
     * 更新数据时的操作
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        if (metaObject.hasSetter("updateTime")) {
//            metaObject.setValue("updateTime", LocalDateTime.now());
            this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
        }
    }
}

ThreadLocal

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
工具类BaseContext:

/**
 * 基于ThreadLocal封装工具类,用于设置和获取当前登录用户的ID
 */
public class BaseContext {
    //new一个ThreadLocal,作用于Long id,所以T->Long
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    private BaseContext() {
    }

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }
}

在Filter的doFilter()方法中使用工具类设置当前用户id(或者另两个方法里)

        //3. 判断登录状态,如果已登录,直接放行
        if (request.getSession().getAttribute("employee") != null) {
            log.info("用户已登录,id为{}", request.getSession().getAttribute("employee"));

            //使用工具类,存储当前用户id到ThreadLocal
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);
            
            filterChain.doFilter(request, response);
            return;
        }

创建元数据对象处理器:

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 插入数据时的操作
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("插入数据时操作:...");
        log.info(metaObject.toString());

        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }

    /**
     * 更新数据时的操作
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("更新数据时操作:...");
        log.info(metaObject.getOriginalObject().toString());

        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

三、文件上传与下载

(一)文件上传

在这里插入图片描述

代码实现(将文件上传到了指定位置,在yml配置了基础文件路径):

    @Value("${reggie.path}")
    private String basePath;

    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result<String> upLoad(MultipartFile file) {
        //file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除


        //1. 获取原始文件名,以及文件后缀
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //2. 使用UUID生成文件名,防止文件名重复导致的文件被覆盖
        String fileName = UUID.randomUUID().toString() + suffix;

        //3. 创建目录对象,判断该目录是否存在
        File dir = new File(basePath);
        if (!dir.exists()) {
            //目录不存在就创建
            dir.mkdirs();
        }

        //4. 将临时文件转存到指定位置
        try {
            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回文件名,方便图片通过文件名存入数据库
        return Result.success(fileName);
    }

(二)文件下载

在这里插入图片描述
在这里插入图片描述

代码实现:

    /**
     * 文件下载,通过response输出流传给前端展示
     *
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void downLocal(String name, HttpServletResponse response) {
        try {
            //1. 通过name读取本地文件
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(basePath + name));

            //2. 获取输出流
            ServletOutputStream outputStream = response.getOutputStream();
            //设置返回类型为图片
            response.setContentType("image/*");

            //3. 通过IOUtils工具实现输入输出流对拷
            IOUtils.copy(bis, outputStream);

            outputStream.close();
            bis.close();
        } catch (IOException e) {
//            e.printStackTrace();
            throw new BusinessException("文件下载失败,请稍后重试");
        }
    }

四、DTO

  • DTO(Data Transfer Object),数据传输对象,一般用于展示层和服务层之间的数据传输。
  • 一般DTO都有自己单独的包,不放在domain里。

五、规范

  1. 不能在service层注入其他mapper,service层只允许调用自己的DAO层,这是规范,想要在自己的service层调用别的DAO层要靠相应的service层去调。

六、开发工具

  1. 对象拷贝工具
        //对象拷贝(因为Dish中没有封装categoryName,所以需要查询多个表)
        //通过spring提供的工具,复制属性方法,进行对象拷贝,参数:(原始对象,目标对象,"忽略的属性名")
        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");

七、Linux相关配置

(一)配置网络

网络配置文件目录:cd /etc/sysconfig/network-scripts/
找到对应文件vi,大致内容如下

TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens33"
UUID="11527653-4284-4ad0-8c74-51b153b5473f"
DEVICE="ens33"
ONBOOT="yes"
IPADDR="192.168.32.10"
PREFIX="24"
GATEWAY="192.168.32.2"
DNS1="114.114.114.114"
IPV6_PRIVACY="no"

(1)配置防火墙等

  1. 关闭防火墙
    systemctl stop firewalld.service
  2. 关闭防火墙自启动
    chkconfig firewalld off
  3. 关闭Selinux
    vim /etc/sysconfig/selinux
    (修改如下内容)
    SELINUX=disabled
  4. 使SElinux立即生效,参数0是宽容模式(Prmissive),参数1是强制模式(Enforcing)
    setenforce 0
    查看Selinux状态,
    getenforce

(二)配置yum源

(1)本地yum源

  1. mkdir /mnt/centos
    将镜像文件传入根目录中进行挂载
    mount /CentOS-7-x86_64-DVD-1511.iso /mnt/centos/
  2. 将自带的.repo文件移动到其他地方,并自行创建一个repo
    mkdir /opt/repo
    mv /etc/yum.repos.d/* /opt/repo/
    vi /etc/yum.repos.d/local.repo
  3. 内容如下
[local]
name=local
baseurl=file:///mnt/centos/
gpgcheck=0
enabled=1
  1. yum clean all清除缓存
    yum makecache生成缓存
    yum list查看列表
  2. 安装vim和自动补全,然后重新登录以生效
    yum install vim bash-completion -y

(2)网络yum源

  1. 配置阿里yum源
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
  2. 清除缓存并重新生成

(3)yum安装软件

在这里插入图片描述

(三)常用命令复习

  1. cat [-n] fileName查看显示文件内容,-n表示从1开始对行数编号。
  2. more fileName以分页形式显示内容
    回车:向下滚动一行
    空格:向下滚动一屏
    b:返回上一屏
    q或ctrl+c:退出
  3. tail [-20] [-f] fileName显示文件末尾内容
    默认显示末尾10行
    -20表示显示末尾20行
    -f:动态读取末尾内容,主要用于日志监控
  4. mkdir [-p] dirName创建目录
    -p表示多级目录创建,如果父目录不存在,直接创建一个
  5. rm [-rf] fileName删除文件或目录
    -r:将目录和其中子目录、文件一同删除
    -f:无需确认,直接删除
  6. cp [-r] 源文件位置 目标文件位置
    -r:如果复制的是目录,则必须用此选项
  7. mv命令包含了改名的功能
  8. tar [-zcxvf] fileName [files]对文件进行打包、解包、压缩、解压
    .tar表示只打包,没压缩;.tar.gz表示打包并压缩
    -z:gzip,通过gzip命令处理文件,gzip可以进行压缩或解压
    -c:create,创建新的包文件
    -x:extract,从包文件中还原文件
    -v:verbose,显示命令执行过程
    -f:file,指定包文件名称
    [files]:不指定,默认将所在目录下的所有文件打包;指定文件名,将目标文件打包
    一般打包时,需要指定包文件名称-f和目标文件filestar -cv test.tar test/
    解压并解包时tar -zxvf test.tar.gz,解压完之后就有test目录
  9. echo "hhh" >> test/java.txt
  10. find dirName -option fileName指定目录查找文件;一般-option为-name,用文件名称来查找
    eg:find . -name "*.java"查找当前目录后缀为.java的文件
  11. grep wordName fileName指定文件查找文本内容
    eg:grep Hello Test.txt在Test文件中查找文本Hello
    grep Hello *.txt在所有后缀为txt中查找文本Hello

(1)Vim

三种模式:

  • 命令模式
  • 插入模式
  • 底行模式:可以通过命令对文件内容进行查找、显示行号、退出vim等,[:或/]进入该模式,/进入查找模式,如/name,n查找下一个,N查找上一个;set nu显示行号;

(四)常用软件安装

(1)Java

二进制安装:

  1. 上传.tar.gz到Linux。java历史版本
  2. tar命令解压tar -zxvf jdk-xxxx.tar.gz -C /usr/local
  3. 配置环境变量,/etc/profile末尾加入如下
JAVA_HOME=/usr/local/jdkxxxx
PATH=$JAVA_HOME/bin:$PATH
  1. 重载profile文件,使更改立即生效sourcce /etc/profile
  2. java -version

(2)TomCat

二进制安装:

  1. 上传.tar.gz到Linux。tomcat历史版本
  2. 解压到/usr/local
  3. 进入tomcat的bin目录启动服务sh startup.sh./startup.sh
  4. 验证是否启动成功ps -ef | grep tomcat
    ps -ef查看当前运行的所有进程的详细信息
  5. 停止服务命令sh shutdown.sh./shutdown.sh
    或者结束tomcat进程,通过ps -ef找到进程号,然后kill -9 进程号

(3)MySQL

rpm安装:

  1. 首先查看系统是否安装了MySQL和mariadb(mariadb和mysql有冲突)
    rpm -qa | grep mysql
    rpm -qa | grep mariadb
  2. 卸载冲突软件
    rpm -e --nodeps mariadb-libs...(刚找到的软件名称)
  3. 将.tar.gz上传到Linux。mysql历史版本(linux选RPM Bundle)
  4. mkdir /usr/local/mysql新建一个文件夹,因为这个mysql包解压后会有很多个文件
  5. 解压tar -zxvf 包名 -C /usr/local/mysql(解压后有多个rpm包)
  6. rpm包安装顺序,必须按照该顺序安装:
    rpm -ivh 以下包名依次安装
mysql-community-common-5.7.25-1.el7.x86_64.rpm
mysql-community-libs-5.7.25-1.el7.x86_64.rpm
mysql-community-devel-5.7.25-1.el7.x86_64.rpm
mysql-community-libs-compat-5.7.25-1.el7.x86_64.rpm
mysql-community-client-5.7.25-1.el7.x86_64.rpm
yum install net-tools
mysql-community-server-5.7.25-1.el7.x86_64.rpm
  1. 启动mysqlsystemctl status mysqld
    自启动systemctl enable mysqld
    netstat -tunlp查看已启动服务(还有端口等信息yum install net-tools提供)
  2. 登录Mysql,查看临时密码
    cat /var/log/mysqld.log | grep password
    mysql -uroot -p然后输入临时密码
  3. 修改配置密码
# 密码长度最低位数
set global validate_password_length=4;
# 密码安全等级
set global validate_password_policy=LOW;
# 密码
set password = password("0000");
# 开启访问权限(外部也能访问mysql)
grant all on *.* to 'root'@'%' identified by 'root';
flush privileges;

(4)lrzsz

该软件用于在Linux中文件上传和下载的软件(比如宿主机和虚拟机的文件互传)。
yum安装:

  1. 搜索是否有该安装包yum list lrzsz
  2. 安装yum install lrzsz.x86_64 -y
  3. rz命令就可以打开文件互传界面

八、项目部署

(一)手动部署

  1. 将项目打为jar包(packge)
  2. java -jar xxx.jar运行该项目
  3. 改为后台运行该项目,并将日志输出到日志文件
    nohup command [Arg...] [&]不挂断运行指定命令,退出终端不会影响程序执行
    command:要执行的命令
    Arg:一些参数,可以指定输出文件
    &:让命令在后台运行
    eg:nohup java -jar xxx.jar &> hello.log &后台运行java -jar命令,并将日志输出到指定log文件
  4. nohup启动的命令如何停止
    ps -ef | grep 'java -jar',找到进程号,kill -9 进程号

(二)Shell脚本部署

步骤:

  1. Linux中安装Git(本地编好的代码push到仓库,在Linux中直接pull代码)
  2. Linux安装Maven
  3. 编写Shell脚本(拉取代码、编译、打包、启动)
  4. 授予Shell脚本执行权限
  5. 执行Shell脚本

(1)安装Git

  1. yum install git -y
    安装完成后,直接输入git命令就可以执行
  2. 克隆项目到本地
    git clone 地址

(2)安装Maven

  1. 上传.tar.gz到Linux
  2. 解压tar -zxvf /apache-maven-3.5.4-bin.tar.gz -C /usr/local
  3. vim /etc/profile
export MAVEN_HOME=/usr/local/apache-maven-3.5.4
export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH
  1. source /etc/profile
  2. 查看是否成功mvn -version
  3. vim /usr/local/apache-maven-3.5.4/conf/settings.xml 修改配置文件如下,该目录作为本地仓库目录(先mkdir /usr/local/repo)
    <localRepository>/usr/local/repo</localRepository>

(3)编写Shell脚本

(4)Shell脚本授权

  1. ll查看该文件权限情况
    在这里插入图片描述
  2. 授予权限chmod 777 文件名
    如何计算该数字?
    权限共有三组,rwx对应2^22^12^0,加起来最大权限就是7。

九、项目优化

(一)Redis缓存优化

(1)环境搭建

  1. 导入Reids的maven坐标
  2. 配置文件.yum,添加Redis的内容
  3. 配置类(非必须,但可以加一个)

(2)缓存登录验证码

之前的验证码是在userController里设置放在Session里的(默认保存30分钟),需要优化到缓存里(实际只需要用redis保存5分钟就够了)
在这里插入图片描述

(3)缓存菜品数据

在这里插入图片描述

(4)Spring Cache

注意!!!!!!!!!!!!!!!!!!!!!!!!!
使用注解方式实现缓存,其对象必须实现序列化(implements Serializable)

简介
Spring提供的用于整合其他缓存产品的框架,可以通过注解的方式来加缓存,简化代码开发。
默认使用Map作为缓存,Map跟随内存,如果服务被重启,则之前缓存的数据会消失。
可以更改底层缓存为对应的缓存产品,比如Redis。
在这里插入图片描述

常用注解
在这里插入图片描述
@CachePut(),通常用在新增中(不是一定用在增,根据业务逻辑的不同来)
在这里插入图片描述
@CacheEvicat(),通常用在删改中,有时也用于增
在这里插入图片描述
@Cacheable(),通常用在查询中
在这里插入图片描述
在这里插入图片描述

更改底层缓存技术为Redis
在这里插入图片描述

(5)缓存套餐数据

在这里插入图片描述
注意:Result类要实现Serializable

(二)MySQL读写分离

(1)主从复制

介绍
是一个异步复制过程,底层基于MySQL自带的二进制日志功能,从库通过复制解析该二进制日志来保证数据一致。
在这里插入图片描述

配置
主库Master配置:

  1. 修改Mysql配置文件etc/my.cnf/
[mysqld]
log-bin=mysql-bin	#启用二进制日志
server-id=100	# 服务器唯一ID,自定义
  1. 重启Mysqlsystemctl restart mysqld
  2. 创建一个新Mysql用户,用于主库和从库之间的通信(slave必须被mater授予必要的权限才能从主库复制)
    注意:密码复杂度要求大写+小写+特殊符+数字
    GRANT REPLICATION SLAVE ON *.* to 'slaveuser'@'%' identified by 'Aa!000000';
  3. 数据库中查看Master状态,并记录下File(日志文件名)和Position(日志文件位置)值;查看时不要执行任何操作,因为其他操作会导致File和Position有变动。
    show master status;

从库Slave配置:
(如果是vmware克隆,用uuidgen重新生成uuid并更改,然后再重新生成mysql的uuid):

  1. 修改配置文件/etc/my.cnf
[mysqld]
server-id=101 # 服务器唯一ID,自定义
  1. 重启Mysql
  2. 登录mysql,执行SQL
    change master to master_host='192.168.32.10',master_user='slaveuser',master_password='Aa!000000',master_log_file='mysql-bin.000001',master_log_pos=442;
  3. 启动从库start slave;
  4. 查看从库状态show slave status\G
要保证下面两个都为yes
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

测试
主库新建数据库,看从库是否也存在。

(2)读写分离案例

只需要导入Maven,并修改yml配置文件就够了
背景
在这里插入图片描述

Sharding-JDBC介绍
在这里插入图片描述

入门案例

  1. 导入Maven坐标。
  2. 配置文件.yml中配置读写分离规则
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=UTC&userUnicode=true&charsetEncoding=utf-8&useServerPrepStmts=true
    username: root
    password: "000000"
    type: com.alibaba.druid.pool.DruidDataSource

  shardingsphere:
    datasource:
      names: master,slave
      # 主数据源
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jsbc:mysql://192.168.32.10:3306/db1?characterEncoding=utf-8
        username: root
        password: 000000
        # 从数据源
      slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jsbc:mysql://192.168.32.20:3306/db1?characterEncoding=utf-8
        username: root
        password: 000000
    masterslave:
      # 读写分离配置(某一个策略,目前策略为轮询)
      load-balance-algorithm-type: round_robin
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: master
      # 从库数据源名称列表,多个用逗号分割
      slave-data-source-names: slave
    props:
      sql:
        # 开启sql显示
        show: true
  main:
    # 允许Bean定义覆盖,当前有shardingsphere和Druid的dataSource,启动时会报错,所以要允许后创建的覆盖先创建的Bean
    allow-bean-definition-overriding: true
  1. 配置文件.yml中配置运行bean定义覆盖配置项(上面已经配置了,main.allow-bean…)

功能测试

(3)项目实现读写分离

将上面的配置文件,复制到yml文件,覆盖datasource的内容就够了。

(三)Nginx反向代理

(四)前后端分离

前后端分离开发后,前后端代码不再混合在同一个Maven工程中,而是分为前端工程和后端工程。
在这里插入图片描述
在这里插入图片描述

(1)Yapi

主要用于定义接口(定制接口)
在这里插入图片描述
视频讲解

(2)Swagger

前后端分离开发时,后端常用技术,帮助生成接口文档
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
5.网页查看,localhost:8080/doc.html

Swagger常用注解
通过使用Swagger注解,可以在查看文档时更清晰的看见项目描述
在这里插入图片描述

(3)项目部署

部署架构
在这里插入图片描述

部署环境说明
在这里插入图片描述

部署前端项目

部署后端项目

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个完整的外卖系统,包括手机端,后台管理,api 基于spring boot和vue的前后端分离的外卖系统 包含完整的手机端,后台管理功能 技术选型 核心框架:Spring Boot 数据库层:Spring data jpa/Spring data mongodb 数据库连接池:Druid 缓存:Ehcache 前端:Vue.js 数据库:mysql5.5以上,Mongodb4.0(不要使用最新版4.2) 模块 flash-waimai-mobile 手机端站点 flash-waimai-manage后台管理系统 flash-waimai-api java接口服务 flash-waimai-core 底层核心模块 flash-waimai-generate 代码生成模块 快速开始 数据存储采用了mysql和mongodb,其中基础管理配置功能数据使用mysql,业务数据使用mongodb存储。 创建mysql数据库 CREATE DATABASE IF NOT EXISTS waimai DEFAULT CHARSET utf8 COLLATE utf8_general_ci; CREATE USER 'waimai'@'%' IDENTIFIED BY 'waimai123'; GRANT ALL privileges ON waimai.* TO 'waimai'@'%'; flush privileges; mysql数据库创建好了之后,启动flash-waimai-api服务,会自动初始化数据,无需开发人员自己手动初始化数据 安装mongodb并创建数据库:flash-waimai 使用mongorestore命令 导入mongodb数据,由于测试数据量较大,打包放在了百度云盘:链接:https://pan.baidu.com/s/1mfO7yckFL7lMb_O0BPsviw 提取码:apgd 下载后将文件解压到d:\elm,如下命令导入数据: mongorestore.exe -d flash-waimai d:\\elm 下载项目测试数据的图片(商家和食品图片):链接:https://pan.baidu.com/s/1rvZDspoapWa6rEq2D_5kzw 提取码:urzw ,将图片存放到t_sys_cfg表中system.file.upload.path配置的目录下 启动管理平台:进入flash-waimai-manage目录:运行 npm install --registry=https://registry.npm.taobao.org运行npm run dev启动成功后访问 http://localhost:9528 ,登录,用户名密码:admin/admin 启动手机端:进入flash-waimai-mobile目录:运行 npm install --registry=https://registry.npm.taobao.org运行npm run local启动成功后访问 http://localhost:8000

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值