【SpringBoot系列】实现数据库定期备份和定期删除(Quartz和Schedule两种方式实现)


前言

这次突然接到一个需求,要给手头的两个项目进行数据库备份。有一个项目是Spring Cloud的,一个是Spring Boot的,然后我看了一下项目的代码,发现两者实现定时的方式截然不同,前者是用Quartz实现的,后者使用Schedule实现的,因此我写下这两种方案的数据库备份实现方式,在文章中的每一个类,我会说明哪些地方是需要根据项目视情况进行修改的,以及如何进行修改,还有哪些地方是可以直接照搬,希望对看到这篇文章的你有一点点帮助。

一、Quartz实现

不废话,直接上代码。

1.1 需要用到的依赖

代码如下:

<!-- quartz 定时任务 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>        

1.2 配置文件application.properties

如果copy这段配置,需要修改数据库IP、端口号、数据库名称、用户名和密码;如果使用的是mysql8版本,还需修改driver-class-name
mysql-connector-java 5.x及之前版本中的驱动类名
driverClassName: com.mysql.jdbc.Driver
mysql-connector-java 6.x及后续版本中的驱动类名
driverClassName: com.mysql.cj.jdbc.Driver

代码如下:

spring.datasource.druid.url=jdbc:mysql://localhost:3306/campus_card?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver

1.3 获取数据库信息工具类JdbcUtils

代码如下:
这个类直接照搬即可

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.HashMap;

@Component
public class JdbcUtils {

    @Autowired
    private Environment environment;

    public HashMap<String, String> getDBInfo() {
        String url = environment.getProperty("spring.datasource.druid.url");
        String username = environment.getProperty("spring.datasource.druid.username");
        String password = environment.getProperty("spring.datasource.druid.password");
        String[] split = url.split(":");
        String host = String.format("%s:%s:%s", split[0], split[1], split[2]);
        String[] portSplit = split[3].split("/");
        String port = portSplit[0];

        String[] databaseSplit = portSplit[1].split("\\?");
        String dbName = databaseSplit[0];
        HashMap<String, String> result = new HashMap<>();
        result.put("url",url);
        result.put("host",host);
        result.put("port",port);
        result.put("dbName",dbName);
        result.put("userName",username);
        result.put("passWord",password);

        return result;
    }
}

1.4 编写Sevice接口类IQuartzService

代码如下:
这一块主要在Service类添加定时进行数据库备份定时删除过期的数据库备份两个方法

public interface IQuartzService {
    void mysqlBackupTask();

    void removeMysqlBackupTask();
}

1.5 编写Sevice接口实现类QuartzServiceImpl

需要按需修改的地方:
1、这里需要你们手动导入JdbcUtils的包,我这里不导入主要是因为我的目录跟大家不一样,我就去掉了。
2、resourcePath这里我写死了,实际上是要从配置文件获取的,配置文件的Key和Value各位要根据实际情况定了,然后再次类里定义一个变量,通过@Value获取到值就可以了。
3、然后我这里拼接的目录和文件名是resourcePath/sql/当前日期(精确到日)/项目名+当前毫秒值.sql,请大家根据实际项目情况进行修改。
4、输出的日志信息也可按需修改。
5、在删除备份的方法里,我保存的是7天内的备份,因此7天前的备份会被删除,是通过
if (betweenDay > 7)进行判断的,大家可按需修改这个天数。

代码如下:

import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.sql.Date;
import java.time.LocalDate;
import java.io.File;
import java.util.Map;

@Slf4j
@Service
public class QuartzServiceImpl implements IQuartzService {
    @Autowired
    private JdbcUtils jdbcUtils;

    @Override
    public void mysqlBackupTask() {
        String resourcePath = "C:\\lancoo\\file\\mysqlBackup";
        log.info("======执行定时器:定时备份数据库=======");
        String backUpPath = resourcePath + "/sql/" + java.sql.Date.valueOf(LocalDate.now());
        File backUpFile = new File(backUpPath);
        if (!backUpFile.exists()) {
            backUpFile.mkdirs();
        }
        File dataFile = new File(backUpPath + "/yikatong" + System.currentTimeMillis() + ".sql");
        //拼接cmd命令
        StringBuffer sb = new StringBuffer();
        Map<String, String> dbInfo = jdbcUtils.getDBInfo();
        sb.append("mysqldump");
        sb.append(" -u" + dbInfo.get("userName"));
        sb.append(" -p" + dbInfo.get("passWord"));
        sb.append(" " + dbInfo.get("dbName") + " > ");
        sb.append(dataFile);
        log.info("======数据库备份cmd命令为:" + sb.toString() + "=======");
        try {
            Process exec = Runtime.getRuntime().exec("cmd /c" + sb.toString());
            if (exec.waitFor() == 0) {
                log.info("======数据库备份成功,路径为:" + dataFile + "=======");
            }
        } catch (Exception e) {
            log.info("======数据库备份失败,异常为:" + e.getMessage() + "=======");
        }
    }

    @Override
    public void removeMysqlBackupTask() {
        log.info("======执行定时器:定时删除备份数据库文件=======");
        String resourcePath = "C:\\lancoo\\file\\mysqlBackup";
        String backUpPath = resourcePath + "/sql";
        File backUpFile = new File(backUpPath);
        if (backUpFile.exists()) {
            File[] files = backUpFile.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    Date date1 = Date.valueOf(file.getName());
                    Date date2 = Date.valueOf(LocalDate.now());
                    long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);
                    if (betweenDay > 7) {
                        File[] subFiles = file.listFiles();
                        for (File subFile : subFiles) {
                            subFile.delete();
                        }
                        file.delete();
                    }
                }
            }
        }
    }
}

1.6 编写Task类MysqlBackupTask和RemoveMysqlBackupTask继承QuartzJobBean

这两个类直接照搬就行

MysqlBackupTask

import com.lancoo.hydropower.service.IQuartzService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class MysqlBackupTask extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取JobDetail中传递的参数
        IQuartzService quartzService = (IQuartzService) jobExecutionContext.getJobDetail().getJobDataMap().get("quartzService");
        quartzService.mysqlBackupTask();
    }
}

RemoveMysqlBackupTask

import com.lancoo.hydropower.service.IQuartzService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class RemoveMysqlBackupTask extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取JobDetail中传递的参数
        IQuartzService quartzService = (IQuartzService) jobExecutionContext.getJobDetail().getJobDataMap().get("quartzService");
        quartzService.removeMysqlBackupTask();
    }
}

1.7 编写MysqlBackupQuartzConfig类,注册任务(Job)和触发器(Trigger)

1、触发器里的代码对数据库备份的时间是每日凌晨1点,这个时间可根据实际情况进行修改。
2、触发器里的代码对距离现在超过7天(这个时间是我设置的,可以根据项目实际情况进行修改,修改方法到1.5 开头提示的第5点查看)的数据库备份文件进行删除的时间是每日凌晨1点,这个时间也可根据实际情况进行修改。
3、修改定时时间的时候,可以参考下面这个表,这个表是我从其他地方copy来的。

常用示例: 
0 0 12 * * ? 每天12点触发 
0 15 10 ? * * 每天10点15分触发 
0 15 10 * * ? 每天10点15分触发 
0 15 10 * * ? * 每天10点15分触发 
0 15 10 * * ? 2005 2005年每天10点15分触发 
0 * 14 * * ? 每天下午的 2点到2点59分每分触发 
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 
每天下午的 18点到18点59分(整点开始,每隔5分触发) 
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发 
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发 (特殊情况,在一个时间设置里,执行两次或 两次以上的情况) 
0 59 2 ? * FRI    每周5凌晨2点59分触发; 
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发 
0 15 10 15 * ? 每月15号上午10点15分触发 
0 15 10 L * ? 每月最后一天的10点15分触发 
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6#3 每月的第三周的星期五开始触发 
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次 
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)

代码如下:
以下代码照搬就行

import com.lancoo.hydropower.quartz.task.MysqlBackupTask;
import com.lancoo.hydropower.quartz.task.RemoveMysqlBackupTask;
import com.lancoo.hydropower.service.IQuartzService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MysqlBackupQuartzConfig {
    private static final String MYSQL_BACKUP_JOB_GROUP_NAME = "MYSQL_BACKUP_JOB";
    private static final String MYSQL_BACKUP_TRIGGER_GROUP_NAME = "MYSQL_BACKUP_TRIGGER";

    private static final String REMOVE_MYSQL_BACKUP_JOB_GROUP_NAME = "REMOVE_MYSQL_BACKUP_JOB";
    private static final String REMOVE_MYSQL_BACKUP_TRIGGER_GROUP_NAME = "REMOVE_MYSQL_BACKUP_TRIGGER";

    @Autowired
    private IQuartzService quartzService;

    @Bean
    public JobDetail mysqlBackupJobDetail() {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("quartzService", quartzService);
        JobDetail jobDetail = JobBuilder.newJob(MysqlBackupTask.class)
                .withIdentity("mysqlBackupJobDetail", MYSQL_BACKUP_JOB_GROUP_NAME)
                .usingJobData(jobDataMap)
                .storeDurably()
                .build();
        return jobDetail;
    }

    @Bean
    public Trigger mysqlBackupTriggerQuartz() {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 0 1 * * ?");
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(mysqlBackupJobDetail()) //关联上Test述的JobDetail
                .withIdentity("mysqlBackupJobDetail", MYSQL_BACKUP_TRIGGER_GROUP_NAME) //给Trigger起个名字
                .withSchedule(cronScheduleBuilder)
                .build();
        return trigger;
    }

    @Bean
    public JobDetail removeMysqlBackupJobDetail() {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("quartzService", quartzService);
        JobDetail jobDetail = JobBuilder.newJob(RemoveMysqlBackupTask.class)
                .withIdentity("removeMysqlBackupJobDetail", REMOVE_MYSQL_BACKUP_JOB_GROUP_NAME)
                .usingJobData(jobDataMap)
                .storeDurably()
                .build();
        return jobDetail;
    }

    @Bean
    public Trigger removeMysqlBackupTriggerQuartz() {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 0 0 * * ?");
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(removeMysqlBackupJobDetail()) //关联上Test述的JobDetail
                .withIdentity("removeMysqlBackupJobDetail", REMOVE_MYSQL_BACKUP_TRIGGER_GROUP_NAME) //给Trigger起个名字
                .withSchedule(cronScheduleBuilder)
                .build();
        return trigger;
    }
}

Quartz篇总结:以上就是完整的使用Quartz实现定时数据库备份定期删除过期数据库备份两个功能的步骤,到此就完成了。


二、Schedule实现

2.1 配置类application.properties

1、如果copy这段配置,需要修改数据库IP、端口号、数据库名称、用户名和密码;如果使用的是mysql8版本,还需修改driver-class-name
mysql-connector-java 5.x及之前版本中的驱动类名
driverClassName: com.mysql.jdbc.Driver
mysql-connector-java 6.x及后续版本中的驱动类名
driverClassName: com.mysql.cj.jdbc.Driver
2、server.resource是后面数据库备份的目录,可以按需修改

spring.datasource.url=jdbc:mysql://192.168.122.124:3306/lgdb_campus_portal?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull
spring.datasource.userName=root
#spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
server.resource=D:/resource

2.2 获取数据库信息工具类JdbcUtils

代码如下:

以下代码照搬即可

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;

@Component
public class JdbcUtils {

    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    public HashMap<String, String> getDBInfo() {
        String[] split = url.split(":");
        String host = String.format("%s:%s:%s", split[0], split[1], split[2]);
        String[] portSplit = split[3].split("/");
        String port = portSplit[0];

        String[] databaseSplit = portSplit[1].split("\\?");
        String dbName = databaseSplit[0];
        HashMap<String, String> result = new HashMap<>();
        result.put("url",url);
        result.put("host",host);
        result.put("port",port);
        result.put("dbName",dbName);
        result.put("userName",username);
        result.put("passWord",password);

        return result;
    }
}

2.3 定时类Schedule

需要按需修改的地方:
1、数据库备份文件的目录resourcePath是在配置文件的server.resource处指定的,需要根据项目实际情况去配置文件进行修改。
2、我这里拼接的目录和文件名是resourcePath/sql/当前日期(精确到日)/项目名+当前毫秒值.sql,请大家根据实际项目情况进行修改。
3、输出的日志信息也可按需修改。
4、在删除备份的方法里,我保存的是7天内的备份,因此7天前的备份会被删除,是通过
if (betweenDay > 7)进行判断的,大家可按需修改这个天数。
5、这里的案例是每日凌晨1点进行数据库备份每日凌晨0点对距离现在7天(这个时间是我设置的,可以进行修改)的数据库备份删除。这两个功能的定时时间可以根据实际情况进行修改,可以参考下面这个表(这个表是我从其他地方copy来的):

常用示例: 
0 0 12 * * ? 每天12点触发 
0 15 10 ? * * 每天10点15分触发 
0 15 10 * * ? 每天10点15分触发 
0 15 10 * * ? * 每天10点15分触发 
0 15 10 * * ? 2005 2005年每天10点15分触发 
0 * 14 * * ? 每天下午的 2点到2点59分每分触发 
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 
每天下午的 18点到18点59分(整点开始,每隔5分触发) 
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发 
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发 (特殊情况,在一个时间设置里,执行两次或 两次以上的情况) 
0 59 2 ? * FRI    每周5凌晨2点59分触发; 
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发 
0 15 10 15 * ? 每月15号上午10点15分触发 
0 15 10 L * ? 每月最后一天的10点15分触发 
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6#3 每月的第三周的星期五开始触发 
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次 
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)

代码如下:

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.lancoo.campusportal.utils.JdbcUtils;
import com.lancoo.campusportal.utils.LoggerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.sql.Date;
import java.time.LocalDate;
import java.util.Map;

@Component
@EnableScheduling
public class Schedule {

   @Autowired
   private JdbcUtils jdbcUtils;

   @Value("${server.resource}")
   private String resourcePath;

   /**
    * 定时备份数据库信息
    */
   @Scheduled(cron = "0 0 1 * * ?")
   public void backUpDataBase() {
      LoggerUtil.info("======执行定时器:定时备份数据库=======");
      String backUpPath = resourcePath+"/sql/" + Date.valueOf(LocalDate.now());
      File backUpFile = new File(backUpPath);
      if (!backUpFile.exists()) {
         backUpFile.mkdirs();
      }
      File dataFile = new File(backUpPath+"/campusportal"+System.currentTimeMillis()+".sql");
      //拼接cmd命令
      StringBuffer sb = new StringBuffer();
      Map<String, String> dbInfo = jdbcUtils.getDBInfo();
      sb.append("mysqldump");
      sb.append(" -u"+dbInfo.get("userName"));
      sb.append(" -p"+dbInfo.get("passWord"));
      sb.append(" "+ dbInfo.get("dbName") +" > ");
      sb.append(dataFile);
      LoggerUtil.info("======数据库备份cmd命令为:"+sb.toString()+"=======");
      try {
         Process exec = Runtime.getRuntime().exec("cmd /c"+sb.toString());
         if (exec.waitFor() == 0){
            LoggerUtil.info("======数据库备份成功,路径为:"+dataFile+"=======");
         }
      } catch (Exception e) {
         LoggerUtil.info("======数据库备份失败,异常为:"+e.getMessage()+"=======");
      }
   }

   /**
    * 定时删除数据库备份文件,只保留最近一个星期
    */
   @Scheduled(cron = "0 0 0 * * ?")
   public void deleteBackUpDataBase() {
      LoggerUtil.info("======执行定时器:定时删除备份数据库文件=======");
      String backUpPath = resourcePath+"/sql";
      File backUpFile = new File(backUpPath);
      if (backUpFile.exists()) {
         File[] files = backUpFile.listFiles();
         for (File file : files) {
            if (file.isDirectory()) {
               Date date1 = Date.valueOf(file.getName());
               Date date2 = Date.valueOf(LocalDate.now());
               long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);
               if (betweenDay > 7) {
                  File[] subFiles = file.listFiles();
                  for (File subFile : subFiles) {
                     subFile.delete();
                  }
                  file.delete();
               }
            }
         }
      }
   }
}

Schedule篇总结:相比较Quartz来说,我觉得Schedule上手比较简单,代码量也比较少,推荐用Schedule,不过如果项目里是用Quartz做定时器的话,还是老老实实用Quartz吧,因为我在用了Quartz的项目里使用Schedule,定时器没有生效。


总结

以上就是今天要讲的内容,本文介绍了数据库定时备份和定期删除两种不同定时器的实现,如果需要真正掌握,还需要自己去动手实现一下,希望对阅读本篇的你会有所帮助。欢迎在评论区进行指正和讨论!

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Spring Boot 的定时任务功能,结合 Shell 脚本实现数据库备份。具体步骤如下: 1. 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> ``` 2. 创建一个定时任务类,实现 Job 接口,重写 execute 方法,在该方法中编写备份数据库的逻辑。 ```java @Component public class BackupJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 备份数据库的逻辑 } } ``` 3. 在 application.properties 文件中配置定时任务的触发时间。 ```properties # 每天凌晨 1 点备份数据库 backup.cron=0 0 1 * * ? ``` 4. 创建一个定时任务配置类,使用 @EnableScheduling 注解开启定时任务功能,并使用 @Scheduled 注解指定定时任务的触发时间。 ```java @Configuration @EnableScheduling public class BackupJobConfig { @Autowired private BackupJob backupJob; @Scheduled(cron = "${backup.cron}") public void backup() { JobDetail jobDetail = JobBuilder.newJob(backupJob.getClass()) .withIdentity("backupJob") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("backupTrigger") .withSchedule(CronScheduleBuilder.cronSchedule("${backup.cron}")) .build(); try { Scheduler scheduler = new StdSchedulerFactory().getScheduler(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } } ``` 5. 编写 Shell 脚本,实现数据库备份的具体逻辑。 ```shell #!/bin/bash # 备份数据库的脚本 # 数据库信息 DB_HOST="localhost" DB_PORT="3306" DB_NAME="test" DB_USER="root" DB_PASSWORD="123456" # 备份文件名 BACKUP_FILE_NAME="backup_$(date +%Y%m%d%H%M%S).sql" # 备份命令 BACKUP_COMMAND="mysqldump -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} > ${BACKUP_FILE_NAME}" # 执行备份命令 eval ${BACKUP_COMMAND} ``` 6. 在 execute 方法中调用 Shell 脚本,实现数据库备份。 ```java @Component public class BackupJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { try { // 执行 Shell 脚本备份数据库 Process process = Runtime.getRuntime().exec("/bin/bash /path/to/backup.sh"); process.waitFor(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } ``` 这样,每天凌晨 1 点就会自动备份数据库了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值