python的flask项目中实现多个和单个数据库的备份和还原

作者:lizhonglin
github: https://github.com/Leezhonglin/
blog: https://leezhonglin.github.io/

python的flask项目中实现多个和单个数据库的备份和还原
1.分析

我们在项目中经常会遇到要备份数据库和还原数据库的需求。遇到这样的问题我们该如何解决呢???

其实不难想到mysql 的mysqldumps命令。我们可以借助这个来实现数据库的备份和恢复。理论上是可以实现的,现实呢其实也是可以实现的,不过有些前提条件。

  1. 项目代码部署在和mysql数据库服务器服务器上。
  2. 如果是windows和(macos、Linux)部分代码有些许区别

先看最终实现的效果。在说怎么做的吧。

屏幕快照 2019-05-24 22.44.44

2.具体做法
2.1. 创建数据库备份的脚本
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# -*- desc:自动备份压缩mysql数据库 -*-
# 配置文件库
import configparser
import datetime
import os, os.path, time
# 解压和压缩文件的库
import zipfile

# 日志处理 (需要自己实现)
from app.main.utils.log_helper import mylog
# 配置文件读取(需要自己实现)
from config import db_and_ip_config

# 项目有多个数据库的的操作
databases = []

databases.append(db_and_ip_config.dbname1)
databases.append(db_and_ip_config.dbname2)
databases.append(db_and_ip_config.dbname3)
databases.append(db_and_ip_config.dbname4)
databases.append(db_and_ip_config.dbname5)
databases.append(db_and_ip_config.dbname6)

host = db_and_ip_config.host
user = db_and_ip_config.user
pwd = db_and_ip_config.pwd
port = db_and_ip_config.port
dump = db_and_ip_config.mysql_bin

# 组装备份的命令名称
mysql_dump = 'mysqldump'
# 组装还原的程序命令mysql
mysql_bak = 'mysql'

backDay = time.strftime("%Y%m%d%H%M%S")
# 当前系统运行的目录
# path = os.path.abspath(os.path.dirname(os.getcwd()))

##########################
"""
print '***获取当前目录***'
print os.getcwd()
print os.path.abspath(os.path.dirname(__file__))

print '***获取上级目录***'
print os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
print os.path.abspath(os.path.dirname(os.getcwd()))
print os.path.abspath(os.path.join(os.getcwd(), ".."))

print '***获取上上级目录***'
print os.path.abspath(os.path.join(os.getcwd(), "../.."))
"""
##########################

path = os.path.abspath(os.path.join(os.getcwd(), ".."))
# 路径有些区别
#(macos/linux)下面使用这个
backDir = path + '/TempMysql/'  # 临时备份sql存放的路径
zip_path = path + '/AutoBackupZip/'    # 自动备份存放的路径
manual_path = path + '/ManualBackupZip/' # 手动备份存放的路径
# windows下面 使用这个
backDir = path + '\\TempMysql\\'  # 临时备份sql存放的路径
zip_path = path + '\\AutoBackupZip\\'
manual_path = path + '\\ManualBackupZip\\'


# deleteDay = 30  # 删除多久之前的备份数据,单位(天)
# # 删除旧数据
# def clear():
#     if not os.path.isdir(backDir):
#         return
#     files = os.listdir(backDir)
#     oldTime = time.time() - deleteDay * 57600
#     for f in files:
#         filePath = backDir + f
#         if os.path.isfile(filePath):
#             ctime = os.path.getctime(filePath)
#             if (ctime < oldTime):
#                 try:
#                     os.remove(filePath)
#                 except Exception:
#                     log("删除旧的备份数据异常")



def zip_dir(dirname, zipfilename):
    """
    添加压缩文件
    :param dirname: 被压缩文件的路径
    :param zipfilename: 压缩后文件的文件路径
    :return:
    """
    filelist = []
    if os.path.isfile(dirname):
        filelist.append(dirname)
    else:
        for root, dirs, files in os.walk(dirname):
            for name in files:
                filelist.append(os.path.join(root, name))
    zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED)
    for tar in filelist:
        arcname = tar[len(dirname):]
        # print arcname
        zf.write(tar, arcname)
    zf.close()
    return 0


def unzip_file(zipfilename, unziptodir):
    """
    解压文件
    :param zipfilename: 被解压的文件路径
    :param unziptodir:  解压后文件路径
    :return:
    """
    if not os.path.exists(unziptodir): os.mkdir(unziptodir)
    zfobj = zipfile.ZipFile(zipfilename)
    for name in zfobj.namelist():
        name = name.replace('\\', '/')
        if name.endswith('/'):
            os.mkdir(os.path.join(unziptodir, name))
        else:
            ext_filename = os.path.join(unziptodir, name)
            ext_dir = os.path.dirname(ext_filename)
            if not os.path.exists(ext_dir): os.mkdir(ext_dir)
            outfile = open(ext_filename, 'wb')
            outfile.write(zfobj.read(name))
            outfile.close()


# 数据库备份
def dbDump():
   	# 创建备份目录
    if not os.path.isdir(backDir):
        try:
            os.makedirs(backDir)
        except Exception:
            log("创建目录失败")
    # 创建压缩目录
    if not os.path.isdir(zip_path):
        try:
            os.makedirs(zip_path)
        except Exception:
            log("创建目录失败")
    # 获取备份时间
    backDay = time.strftime("%Y%m%d%H%M%S")
    # 获取当前项目路径
    start_path = os.getcwd()
    # 切换到配置文件配置的mysql的bin目录 做这步操作的目的是防止mysql安装服务了但是没有添加到环境变量中,而导致备份失败。
    os.chdir(dump)
    for database in databases:
      	# 定义备份的名称
        backName = backDir + database + '.sql'
        b_name = '"' + backName + '"'
        # 定义备份的命令
        sql_command = "%s -u%s -p%s -h%s -P%s %s -B > %s" % (mysql_dump, user, pwd, host, port, database, b_name)
        # 执行备份操作
        os.system(sql_command)
    # 切换到项目的路径  这步必须操作。如果步做的话会导致自己的项目报错,不能运行    
    os.chdir(start_path)
    try:
        # 定义压缩文件的文件名
        zipName = zip_path + 'DB' + '_' + backDay + '.zip'
        # 执行压缩操作并且判断是否压缩成功
        if (zip_dir(backDir, zipName) == 0):
            files = os.listdir(backDir)
            for fi in files:
                print(backDir + fi)
                # 删除临时文件夹的文件 以便下次备份使用
                os.remove(backDir + fi)
            message = 'DB' + "备份压缩成功!"
            log(message)
        else:
            message = 'DB' + "备份压缩失败!"
            log(message)
    except Exception:
        message = 'DB' + "备份异常!!"
        log(message)


def manualDbDump():
    """
    手动备份
    :return:
    """
    if not os.path.isdir(backDir):
        try:
            os.makedirs(backDir)
        except Exception:
            log("创建目录失败")

    if not os.path.isdir(manual_path):
        try:
            os.makedirs(manual_path)
        except Exception:
            log("创建目录失败")

    backDay = time.strftime("%Y%m%d%H%M%S")
    start_path = os.getcwd()
    os.chdir(dump)
    for database in databases:
        backName = backDir + database + '.sql'
        b_name = '"' + backName + '"'
        sql_command = "%s -u %s -p%s -h %s -P %s %s -B > %s" % (mysql_dump, user, pwd, host, port, database, b_name)
        os.system(sql_command)
    os.chdir(start_path)
    try:
        zipName = manual_path + 'DB' + '_' + backDay + '.zip'

        if (zip_dir(backDir, zipName) == 0):
            files = os.listdir(backDir)
            for fi in files:
                print(backDir + fi)
                os.remove(backDir + fi)
            message = 'DB' + "手动备份压缩成功!"
            log(message)
        else:
            message = 'DB' + "手动备份压缩失败!"
            log(message)
        return True
    except Exception:
        message = 'DB' + "备份异常!!"
        log(message)
        return False


def restore_db(unzip_to_path):
    """
    还原数据到数据库
    :return:
    """
    # 读取被还原的文件并解压
    files = os.listdir(unzip_to_path)
    try:
        start_path = os.getcwd()
        # 切换目录
        os.chdir(dump)
        for file in files:
          	# 处理文件名以便下面 创建恢复数据库的命令使用
            database = file[:-4]
            out_path = unzip_to_path + file
            ou = '"' + out_path + '"'
            # 组合还原数据的命令
            sql_command = '%s -u %s -p%s -h%s %s < %s' % (mysql_bak, user, pwd, host, database, ou)
						# 执行还原的操作
            os.system(sql_command)
            mylog.error(sql_command)
            os.remove(out_path)
        os.chdir(start_path)
        return 0
    except Exception as e:
        mylog.error(e)
        return 1


# 记录日志函数
def log(msg):
    logfile = open(path + "\\mysqldump.log", "a")
    date_now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    log_str = '%s : %s \n' % (date_now, msg)
    logfile.write(log_str)


def get_backup_db_config(config_path):
    '''
    获取BACKUP_DB中的值
    :return:
    '''
    cf = configparser.ConfigParser()
    cf.read(config_path)
    bak_date = cf.get('BACKUP_DB', 'bakDate')
    bak_time = cf.get('BACKUP_DB', 'bakTime')
    cycle = cf.get('BACKUP_DB', 'cycle')
    hour = cf.get('BACKUP_DB', 'hour')
    minute = cf.get('BACKUP_DB', 'minute')
    return bak_date, bak_time, cycle, hour, minute


def sum_time(day):
    """
    计算备份周期的秒数
    :param day:  天
    :param hour:  小时
    :param minute: 分钟
    :return:
    """
    # h = hour * 60 * 60
    # s = minute * 60
    d = day * 24 * 60 * 60
    return d


def count_start_time(bak_date, bak_time):
    """
    计算备份的时间的时间戳
    :param bak_date:  日期 格式'1555257600000'
    :param bak_time:  时间 '154627980000'
    :return:  首次备份时间的时间戳
    """
    b_k_t = int(bak_time) / 1000
    hour = time.localtime(b_k_t).tm_hour
    minute = time.localtime(b_k_t).tm_min
    h = hour * 60 * 60
    m = minute * 60
    b_k_d = int(bak_date) / 1000
    return b_k_d + h + m


def get_bak_time_hour_and_minute(bak_time):
    """
    获取备份时间的小时和分钟
    :param bak_time:
    :return:
    """
    b_k_t = int(bak_time) / 1000
    hour = time.localtime(b_k_t).tm_hour
    minute = time.localtime(b_k_t).tm_min
    return hour, minute


def count_local_time(bak_time):
    """
    获取当前日期备份时间点的时间戳
    :return:
    """
    d_time = datetime.date.today()
    # 当前日期的时间戳
    un_time = int(time.mktime(d_time.timetuple()))
    b_k_t = int(bak_time) / 1000
    hour = time.localtime(b_k_t).tm_hour
    minute = time.localtime(b_k_t).tm_min
    h = hour * 60 * 60
    m = minute * 60
    return un_time + h + m


def get_current_time():
    """
    获取当前时间的时间戳
    :return:
    """
    local_time = datetime.datetime.now()
    result = int(time.mktime(local_time.timetuple()))
    return result

2.2 后端代码的实现

有了上面的备份脚本这才实现了四分之一的内容。下面我们要考虑前端、后端如何实现。 还有我们需要的备份策略如何实现。

flask后端的代码

"""
此模块为数据库备份的模块

"""
import configparser
import datetime
import os
import time

from apscheduler.triggers.interval import IntervalTrigger
from flask import Blueprint, Response, request
from flask.json import jsonify

from app.main.utils.cs_http_helper import  restore_db_msg
from app.main.utils.list_dict_helper import list_sort_inverted
from app.main.utils.log_helper import mylog
from app.main.views.db_bak.db_script import zip_path, get_backup_db_config, count_start_time, \
    get_bak_time_hour_and_minute, dbDump, unzip_file, restore_db, manual_path, manualDbDump
from app.main.views.views import login_required
from app.main.utils.backup_timer import scheduler
from config import db_and_ip_config

db_views_blueprint = Blueprint('db_views_blueprint', __name__, url_prefix='/db/backup/')


@db_views_blueprint.route('/getList', methods=['GET'])
@login_required
def db_backup_all():
    """
    获取备份全部的数据库
    :return:
    """
    # 判断是否有自动备份的文件夹
    if not os.path.isdir(zip_path):
        try:
            os.makedirs(zip_path)
        except Exception:
            pass
    # 判断是否有手动备份的文件夹
    if not os.path.isdir(manual_path):
        try:
            os.makedirs(manual_path)
        except Exception:
            pass
    # 读取配置文件
    cf = configparser.ConfigParser()
    cf.read(db_and_ip_config.config_path)
    bak_date = cf.get('BACKUP_DB', 'bak_date_ui')
    bak_time = cf.get('BACKUP_DB', 'bak_time_ui')
    cycle = cf.get('BACKUP_DB', 'cycle')
    # 获取自动备份文件的路径
    files = os.listdir(zip_path)
    # 读取手动备份文件的路径
    manual_files = os.listdir(manual_path)
    file_list = []
    for zip in files:
        if zip[-4:] == '.zip':
            bak_name = zip
            bak_url = zip_path + zip
            create_time = time.localtime(os.stat(bak_url).st_ctime)
            c_time = int(time.mktime(create_time))
            url = '/db/backup/down/' + zip
            restore_url = '/db/backup/restore_data/' + zip
            # 组装 备份文件名称 备份日期 备份类型 还原的url 备份的url等信息给前端使用 typeValue=0表示自动,为1时表示手动
            file_list.append(
                {'bakName': bak_name, 'bakUrl': url, 'create_time': c_time, 'restoreUrl': restore_url, 'typeValue': 0})
    # 遍历手动备份的文件
    for manual_zip in manual_files:
        if manual_zip[-4:] == '.zip':
            bak_name = manual_zip
            bak_url = manual_path + manual_zip
            # 获取文件的创建时间
            create_time = time.localtime(os.stat(bak_url).st_ctime)
            c_time = int(time.mktime(create_time))
            url = '/db/backup/down/' + manual_zip
            restore_url = '/db/backup/restore_data/' + manual_zip
            file_list.append(
                {'bakName': bak_name, 'bakUrl': url, 'create_time': c_time, 'restoreUrl': restore_url, 'typeValue': 1})
    # 按照备份时间的顺序进行排序
    file_list_list = list_sort_inverted(file_list, 'create_time')

    for files in file_list_list:
        file_s = files.get('create_time')
        a = time.localtime(file_s)
        files['create_time'] = time.strftime('%Y-%m-%d %H:%M:%S', a)

    return jsonify(code=200, data=file_list_list, bak_date=bak_date, bak_time=bak_time, cycle=cycle)

# 制作请求匹配的动态url
@db_views_blueprint.route('/down/<int:type>/<string:file_name>', methods=['GET'])
def down(type, file_name):
    """
    下载备份数据文件
    :param file_name: 被下载的文件的文件名称 例如:DB_20190415100100.zip
    :return:
    """
    resp = None
    if type == 0:
        files = os.listdir(zip_path)
        for zip in files:
            if zip == file_name:
                with open(zip_path + file_name, "rb") as f:
                    zip_file = f.read()
                # 读取压缩文件并且返回要下载的文件 mimetype这个很重要
                resp = Response(zip_file, mimetype="application/zip")
    else:
        manual_files = os.listdir(manual_path)
        for manual_zip in manual_files:
            if manual_zip == file_name:
                with open(manual_path + file_name, "rb") as f:
                    zip_file = f.read()
                resp = Response(zip_file, mimetype="application/zip")
    return resp


@db_views_blueprint.route('/update_config', methods=['POST'])
@login_required
def update_config():
    """
    修改配置文件 并且修改定时任务
    :return:
    """
    conf_path = db_and_ip_config.config_path
    # 获取上一次配置文件中的数据备份信息
    old_bak_date, old_bak_time, old_cycle, old_hour, old_minute = get_backup_db_config(conf_path)
    # 备份时间
    bak_time = request.form.get('bakTime')
    # 备份周期
    bak_date = request.form.get('bakDate')
    res_bak_date = int(count_start_time(int(bak_date), int(bak_time)))
    time_array = time.localtime(res_bak_date)
    bak_str = time.strftime("%Y-%m-%d %H:%M:%S", time_array)
    hour, minute = get_bak_time_hour_and_minute(bak_time)
    cycle = request.form.get('cycle')
    cf = configparser.ConfigParser()
    cf.read(conf_path)
    cf.set('BACKUP_DB', 'bakDate', bak_str)
    cf.set('BACKUP_DB', 'hour', str(hour))
    cf.set('BACKUP_DB', 'minute', str(minute))
    cf.set('BACKUP_DB', 'bakTime', bak_time)
    cf.set('BACKUP_DB', 'cycle', cycle)
    cf.set('BACKUP_DB', 'bak_time_ui', bak_time)
    cf.set('BACKUP_DB', 'bak_date_ui', bak_date)
    cf.write(open(conf_path, 'w'))

    new_bak_date, new_bak_time, new_cycle, new_hour, new_minute = get_backup_db_config(conf_path)
		# 判断配置文件是否与原来有变化
    if old_bak_date != new_bak_date or old_bak_time != old_bak_time or old_cycle != new_cycle:
        # 判断配置文件变化 更新定时任务
        if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
          # 这个是定时任务的操作
            interval = IntervalTrigger(days=int(new_cycle),
                                       start_date=datetime.datetime.strptime(new_bak_date, '%Y-%m-%d %H:%M:%S'),
                                       end_date=datetime.datetime.strptime('2099-02-10 12:11:00', '%Y-%m-%d %H:%M:%S'),
                                       timezone='Asia/Shanghai', )
            scheduler.add_job(func=dbDump,
                              trigger=interval,
                              id='bak_one', replace_existing=True)
    return jsonify(code='200', msg='配置修改成功')


@db_views_blueprint.route('/restore_data/', methods=['GET'])
@login_required
def restore_data():
    """
    还原数据文件
    :param file_name: 还原的数据的文件名称 例如:DB_20190415100100.zip
    :return:
    """
    type_value = request.args.get('typeValue')
    file_name = request.args.get('fileName')
    if type_value == '0':
        unzip_path = zip_path + file_name
    else:
        unzip_path = manual_path + file_name
    # 获取当前工作目录
    # path = os.getcwd()
    # 获取当前工作目录的上上级目录
    path = os.path.abspath(os.path.join(os.getcwd(), ".."))
    unzip_to_path = path + '\\unzipFile\\'
    if not os.path.isdir(unzip_to_path):
        try:
            os.makedirs(unzip_to_path)
        except Exception:
            pass
    try:
        unzip_file(unzip_path, unzip_to_path)
        result = restore_db(unzip_to_path)
        if result == 0:
            return jsonify(code=200, msg='数据还原成功')
        else:
            return jsonify(code=2000, msg='数据还原失败,需要手动还原!!')
    except FileNotFoundError as e:
        mylog.error(e)
        return jsonify(code=2000, msg='数据还原失败,需要手动还原!!')


@db_views_blueprint.route('/manual_db', methods=['GET'])
@login_required
def manual_db():
    """
    手动备份
    :return:
    """
    manual = manualDbDump()
    if manual == True:
        return jsonify(code=200, msg='手动备份成功')
    else:
        return jsonify(code=20001, msg='手动备份失败')

完成到这里我们已经完成了一半的工作了,还有前端代码没有实现。我们的配置文件到的是什么样的。继续加油

2.3 后端的配置文件
[IPCONFIG]
host = 0.0.0.0
port = 8008
debug = True

[DB]
user = root
pwd = 123456
host = 127.0.0.1
port = 3306
dbname1 = jobs
dbname2 = news
dbname3 = photo_data
dbname4 = music_data
dbname5 = user_data
dbname6 = video_data

[BACKUP_DB]
mysql_bin = /usr/local/mysql/bin/
bakdate = 2019-05-24 17:23:00
baktime = 1558689792000
hour = 17
minute = 23
cycle = 1
bak_time_ui = 1558689792000
bak_date_ui = 1558689792000
2.4 前端实现

接下来就是我们的最后一步前端的实现,我这边是用的vue实现的。具体内容见下面

<template>
  <div id="dbBackupAll">
    <div class="top-setting" style="margin-bottom: 6px">
      <span>备份策略:</span>
      <el-select v-model="strategyType" placeholder="请选择" style="width: 200px" @change="showType">
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </el-option>
      </el-select>
      <div class="top-save" @click="backupSubmit()" v-if="isShowTwo">
        <el-button class="button" type="primary">保存</el-button>
      </div>
    </div>

    <div v-if="isShow" class="top-setting-content">

      <img src="../../../static/img/beifen.png" alt="" @click="manualDbDump()">
      <span class="backup-copy">备份</span>

    </div>

    <div v-if="isShowTwo" class="top-setting-content">
      <el-form>

        <div class="block">
          <div class="block-contain">
            <span class="demonstration">首次备份日期:</span>
            <el-date-picker
              v-model="bakDate"
              type="date"
              placeholder="选择日期"
              format="yyyy-MM-dd"
              value-format="timestamp"
            >
            </el-date-picker>
          </div>

          <div class="block-contain">
            <span class="demonstration">备份时间:</span>
            <el-time-picker
              v-model="bakTime"
              :picker-options="{
              selectableRange: '00:00:00 - 23:59:59'
              }"
              placeholder="选择时间"
              format="HH:mm"
              value-format="timestamp">
            </el-time-picker>
          </div>
          <div class="block-contain">
            <span>备份周期:</span>
            <el-input-number
              v-model="cycle"
              size="mini"
              :min="1"
              :max="31"
            >
            </el-input-number>
            <span>(天)</span>
          </div>

        </div>

      </el-form>
    </div>

    <div class="top-setting" style="border-bottom: none">
      <p class="top-title">历史备份数据下载:</p>
      <div class="top-refresh" @click="refresh()">
        <el-button type="primary" class="button">刷新</el-button>
      </div>
    </div>
    <div class="table">
      <el-table
        :data="tableData"
        :header-cell-style="rowClass"
        stripe
        border
        @row-click="onRowClick"
        max-height="600"
        style="width: auto;margin: 0 10px">
        <el-table-column
          fixed
          prop="bakName"
          label="备份文件名称"
          width="auto">
        </el-table-column>
        <el-table-column
          prop="create_time"
          label="备份日期"
          sortable
          width="auto">
        </el-table-column>

        <el-table-column
          prop="typeValueStr"
          label="备份类型"
          sortable
          width="auto">
        </el-table-column>

        <el-table-column
          fixed="right"
          label="下载操作"
          width="auto">
          <template slot-scope="scope">
            <button @click="download(scope.row.bakUrl,scope.row.typeValue)" class="download"></button>
            <button @click="restore" class="restore"></button>

          </template>
        </el-table-column>

      </el-table>
    </div>

    <el-dialog
      title="警告"
      :visible.sync="dialogVisible"
      width="30%"
      :show-close="false"
      >
      <div style="display: flex;align-items: center">
        <img src="../../../static/img/jingaoxinxi.png" alt="" style="margin-left: 20px;">
        <div style="display: inline-block;margin-left: 17px">
          <span class="jin-gao">确定放弃当前数据,还原到选定备份数据库?<br>还原过程中,请暂停软件使用......</span>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="dialogVisible = false;restoreSubmit()" style="color: white">
      <span style="color: white">确 定</span>
    </el-button>
  </span>
    </el-dialog>
  </div>
</template>

<script>
  import {axiosBeg} from '../../utils/axiosBeg'

  export default {
    name: "dbBackupAll",
    data() {
      return {
        dialogVisible: false,
        input: '',
        //控制手动
        isShow: 0,
        // 控制自动
        isShowTwo: 1,
        //行数据
        rowData: {},

        cycle: 1,
        bakDate: '',
        bakTime: '',
        tableData: [],

        options: [{
          value: 0,
          label: '自动'
        }, {
          value: 1,
          label: '手动'
        }],
        // 手自动策略选择的值0自动1手动
        strategyType: 0,
      }
    },
    mounted: function () {
      this.init()
    },

    methods: {
      //设置表头样式
      rowClass({row, rowIndex}) {
        return 'background:rgba(243,243,243,1)'
      },
      // 初始化页面
      async init() {
        let result = await axiosBeg.get('/db/backup/getList');
        if (result.code == 200) {
          let res = result.data;
          res.forEach((item, index) => {
            if (item.typeValue == 0) {
              item.typeValueStr = '自动'
            } else {
              item.typeValueStr = '手动'
            }
          });
          this.tableData = res;
          this.bakDate = result.bak_date;
          this.bakTime = result.bak_time;
          this.cycle = result.cycle;
        }
      },
      // 是选择手动自动策略
      showType: function () {
        if (this.strategyType == 0) {
          this.isShow = 0;
          this.isShowTwo = 1;
        } else {
          this.isShow = 1;
          this.isShowTwo = 0;
        }
      },

      //下载备份文件的函数
      download(url, type) {
        // 获取请求服务的地址
        let serverUrl = window._config.serverUrl;
        let a = url.split('DB');
        let api = a[0];
        let file = a.pop();
        // 下在备份文件
        window.location.href = serverUrl + api + type + '/' + 'DB' + file;
      },

      //获取table的选中行
      onRowClick(row, event, column) {
        this.rowData = row;
      },

      //修改自动备份策略
      async backupSubmit() {

        if (this.bakDate == '') {
          this.$message.error("请选首次备份日期")
        }
        else if (this.bakTime == '') {
          this.$message.error("请选备份时间")
        } else {
          let data = {
            cycle: this.cycle,
            bakTime: this.bakTime,
            bakDate: this.bakDate,
          };
          let result = await axiosBeg.post('/db/backup/update_config', data);
          if (result.code == 200) {
            this.$message.success("配置保存成功");
            this.init()
          }
        }
      },

      // 还原数据的弹窗
      restore: function () {
        this.dialogVisible = true
      },
      // 还原数据提交函数
      async restoreSubmit() {
        let data = {
          typeValue: this.rowData.typeValue,
          fileName: this.rowData.bakName
        };
        this.restoreStart();

        let result = await axiosBeg.get('/db/backup/restore_data/', data);
        if (result.code == 200) {
          this.restoreEnd();
          setTimeout(() => {
            this.$message.success("还原成功");
            this.init();
          }, 1000);
        }else if(result.code == 2000){
          this.restoreEnd();
          setTimeout(() => {
            this.$message.error("数据还原失败");
            this.init();
          }, 1000);
        }else{
          this.restoreEnd();
          setTimeout(() => {
            this.$message.error("数据还原失败");
            this.init();
          }, 1000);
        }
      },
      // 还原蒙版层开始
      restoreStart() {
        const loading = this.$loading({
          lock: true,
          text: '数据还原中,请等待...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)',
          target: document.querySelector('.div1')
        });
      },
      // 还原蒙版层结束
      restoreEnd() {
        const loading = this.$loading({
          lock: true,
          text: '数据还原中,请等待...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)',
          target: document.querySelector('.div1')
        });
        setTimeout(() => {
          loading.close();
        }, 1000);
      },


      // 手动备份蒙版层开始
      manualBakStart() {
        const loading = this.$loading({
          lock: true,
          text: '手动备份中,请等待...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)',
          target: document.querySelector('.div1')
        });
      },
      //手动备份蒙版层结束
      manualBakEnd() {
        const loading = this.$loading({
          lock: true,
          text: '手动备份中,请等待...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)',
          target: document.querySelector('.div1')
        });
        setTimeout(() => {
          loading.close();
        }, 1000);
      },
      // 手动备份数据函数
      async manualDbDump() {
        this.manualBakStart();
        let res = await axiosBeg.get('/db/backup/manual_db');
        if (res.code == 200) {
          this.manualBakEnd();
          setTimeout(() => {
            this.$message.success("手动备份成功");
            this.init()
          }, 1000);
        }else  if(res.code == 20001){
          this.manualBakEnd();
          setTimeout(() => {
            this.$message.success("手动备份失败");
            this.init()
          }, 1000);
        }
      },

      refresh: function () {
        this.init()
      }

    }
  }
</script>

<style scoped>
  #dbBackupAll {
    width: calc(100% - 20px);
    height: calc(100% - 70px);
    background: white;
    margin: 10px;
    position: relative;
    overflow-x: hidden;
    box-sizing: border-box;
  }

  .top-setting {
    margin: 20px 20px 10px 16px;
  }

  .top-setting-content {
    margin: 0 8px;
    padding: 17px 0 19px 14px;
    background: rgba(245, 245, 245, 1);
    border: 1px solid rgba(226, 226, 226, 1);
  }

  .top-setting .top-title {
    margin-top: 10px;
    display: inline-block;
    font-size: 12px;
    font-family: MicrosoftYaHei-Bold;
    font-weight: bold;
    color: rgba(111, 126, 149, 1);
  }

  .top-setting .top-save {
    display: inline-block;
    float: right;
  }

  .top-setting .top-save span {
    font-size: 12px;
    font-family: MicrosoftYaHei;
    font-weight: 400;
    color: rgba(111, 126, 149, 1);
    vertical-align: middle;
    margin-left: 3px;
  }

  .top-setting .top-refresh {
    display: inline-block;
    float: right;
    margin-top: 7px;
  }

  .top-setting .top-refresh span {
    vertical-align: middle;
    margin-left: 3px;
  }

  .top-setting .top-save span {
    vertical-align: middle;
    margin-left: 3px;
  }

  .block {
    display: flex;
    flex-direction: row;
    padding: 5px 0 15px;
  }

  .block-contain:not(:first-child) {
    margin-left: 80px;
  }

  .block-contain span {
    font-size: 12px;
    font-family: MicrosoftYaHei;
    font-weight: 400;
    color: rgba(111, 126, 149, 1);
  }

  .button {
    width: 68px;
    height: 22px;
    line-height: 22px;
    background: linear-gradient(24deg, rgba(5, 182, 232, 1), rgba(0, 221, 218, 1));
    border-radius: 2px;
  }

  .button span {
    font-size: 11px;
    font-family: MicrosoftYaHei;
    font-weight: 400;
    color: rgba(255, 255, 255, 1);
  }

  .backup-copy {
    font-size: 14px;
    font-family: MicrosoftYaHei-Bold;
    font-weight: bold;
    color: rgba(114, 114, 114, 1);
  }

  .download {
    outline: 0;
    display: inline-block;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 50%;
    background: url('../../../static/img/xiazai.png') no-repeat center rgba(224, 224, 224, 1)
  }

  .restore {
    outline: 0;
    display: inline-block;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 50%;
    background: url('../../../static/img/huanyuan.png') no-repeat center rgba(224, 224, 224, 1)
  }

  .download:hover {
    background: url('../../../static/img/xiazaihover.png') no-repeat center rgba(5, 182, 232, 1);
  }

  .download:active, .download:focus {
    border: none;
    border-radius: 50%;
    background: url('../../../static/img/xiazaihover.png') no-repeat center rgba(5, 182, 232, 1);
  }

  .restore:active, .restore:focus {
    border: none;
    border-radius: 50%;
    background: url('../../../static/img/huanyuanhover.png') no-repeat center rgba(5, 182, 232, 1);
  }

  .restore:hover {
    background: url('../../../static/img/huanyuanhover.png') no-repeat center rgba(5, 182, 232, 1);
  }
  .jin-gao {
    margin-top: 10px;
    font-size: 12px;
    font-family: MicrosoftYaHei;
    font-weight: bold;
    color: rgba(111, 126, 149, 1);
  }
</style>

到这里 这个功能就已经完成了。 上面有提到一个创建定时任务的问题可以参考我的另外一篇文章 Flask-APScheduler使用教程
有详细的定时任务的介绍。

Li-boss原创分享,转载注明出处

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

kujirashark

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

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

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

打赏作者

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

抵扣说明:

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

余额充值