系统新增备份功能(一次新增多张表)linux 和windows通用

枚举类

Java 复制代码
import lombok.Getter;

import java.util.List;

@Getter
public enum BackupTypeEnum {

    ALARM_RECORD("ALARM_RECORD", List.of("fix_alarm_record"), "alarm_record", "报警记录"),
    DEVICE_RECORD("DEVICE_RECORD", List.of("fix_device_record"), "device_record", "设备记录"),
    FAULT_RECORD("FAULT_RECORD", List.of("fix_fault", "fix_fault_dict", "fix_fault_repair"), "", "故障记录"),
    AUTO_RECORD("AUTO_RECORD", List.of("fix_record_base_data", "fix_record_co2_data", "fix_record_detail"), "auto", "多参数据");

    private final String type;
    private final List<String> tableNames;

    private final String folder;
    private final String desc;

    BackupTypeEnum(String type, List<String> tableNames, String folder, String desc) {
        this.type = type;
        this.tableNames = tableNames;
        this.folder = folder;
        this.desc = desc;
    }
}

类方法

Java 复制代码
 /**
     * 手动备份入口
     */
    @Override
    public Boolean backup(String type) {

        BackupTypeEnum backupType = BackupTypeEnum.valueOf(type);

        String tenantId = LoginHelper.getTenantId();

        File sqlFile = null;

        try {

            // 1. 导出SQL
            sqlFile = dumpTables(backupType);

            // 2. 读取文件
            byte[] bytes = Files.readAllBytes(sqlFile.toPath());

            // 3. 文件名
            String fileName =
                backupType.getType() + "_" +
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) +
                    ".sql";

            // 4. 上传OSS
            Long ossId = ossService.uploadFile(bytes, fileName, "application/sql");

            // 5. 保存OSS记录
            GisOss oss = new GisOss();
            oss.setSysOssId(ossId);
            oss.setKeyId(Long.parseLong(tenantId));
            oss.setFileTable(String.join(",", backupType.getTableNames()));
            oss.setFileType(type);
            oss.setFileDetailType(backupType.getFolder());

            gisOssMapper.insert(oss);

            // 6. 保存备份记录
            FixDataBackupRecord record = new FixDataBackupRecord();

            record.setBackupName(backupType.getDesc());
            record.setBackupType(backupType.getType());
            record.setBackupMode("MANUAL");
            record.setBackupTime(new Date());
            record.setOssId(ossId);
            record.setBackupNickName(
                LoginHelper.getLoginUser().getNickname()
            );

            backupRecordMapper.insert(record);

            return true;

        } catch (Exception e) {

            log.error("备份失败 type={}", type, e);

            throw new ServiceException("数据库备份失败:" + e.getMessage());

        } finally {

            // 删除临时文件
            if (sqlFile != null && sqlFile.exists()) {
                if (!sqlFile.delete()) {
                    log.warn("临时文件删除失败:{}", sqlFile.getAbsolutePath());
                }
            }
        }
    }


    /**
     * 多表导出
     */
    private File dumpTables(BackupTypeEnum backupType)
        throws IOException, InterruptedException {

        // 1. 读取主库配置
        DynamicDataSourceProperties.DataSourceConfig master =
            dsProps.getDatasource().get("master");

        if (master == null) {
            throw new ServiceException("找不到 master 数据源配置");
        }

        JdbcInfo jdbc = JdbcInfo.parse(master.getUrl());

        boolean isWindows =
            System.getProperty("os.name").toLowerCase().contains("win");

        // 2. 创建临时文件
        String tmpDir = System.getProperty("java.io.tmpdir");

        String fileName =
            backupType.getType() + "_" +
                System.currentTimeMillis() + ".sql";

        File outputFile = new File(tmpDir, fileName);

        log.info("开始备份:{} -> {}",
            backupType.getTableNames(),
            outputFile.getAbsolutePath()
        );

        if (isWindows) {

            dumpForWindows(master, jdbc, backupType, outputFile);

        } else {

            dumpForLinux(master, jdbc, backupType, outputFile);
        }

        // 3. 校验
        if (!outputFile.exists() || outputFile.length() == 0) {
            throw new ServiceException("备份文件为空");
        }

        log.info("备份成功,大小={}KB",
            outputFile.length() / 1024
        );

        return outputFile;
    }


    /**
     * Windows导出
     */
    private void dumpForWindows(
        DynamicDataSourceProperties.DataSourceConfig master,
        JdbcInfo jdbc,
        BackupTypeEnum backupType,
        File outputFile
    ) throws IOException, InterruptedException {

        String tables =
            String.join(" ", backupType.getTableNames());

        String cmd =
            "mysqldump " +
                "-u" + master.getUsername() + " " +
                "-p" + master.getPassword() + " " +
                jdbc.getDbName() + " " +
                tables +
                " -r \"" + outputFile.getAbsolutePath() + "\"";

        Process process = Runtime.getRuntime().exec(cmd);

        String err = readProcessOutput(process.getErrorStream());

        int code = process.waitFor();

        if (code != 0) {
            throw new ServiceException(
                "mysqldump失败(Windows): " + err
            );
        }
    }


    /**
     * Linux导出(推荐方式)
     */
    private void dumpForLinux(
        DynamicDataSourceProperties.DataSourceConfig master,
        JdbcInfo jdbc,
        BackupTypeEnum backupType,
        File outputFile
    ) throws IOException, InterruptedException {

        ProcessBuilder pb = new ProcessBuilder();

        // 密码走环境变量(安全)
        pb.environment().put(
            "MYSQL_PWD",
            master.getPassword()
        );

        List<String> cmd = new ArrayList<>();

        cmd.add("mysqldump");
        cmd.add("-h" + jdbc.getHost());
        cmd.add("-P" + jdbc.getPort());
        cmd.add("-u" + master.getUsername());

        cmd.add("--single-transaction");
        cmd.add("--skip-lock-tables");
        cmd.add("--skip-comments");
        cmd.add("--complete-insert");

        // 数据库
        cmd.add(jdbc.getDbName());

        // 多表
        cmd.addAll(backupType.getTableNames());

        pb.command(cmd);

        pb.redirectOutput(outputFile);
        pb.redirectErrorStream(true);

        Process process = pb.start();

        int code = process.waitFor();

        if (code != 0) {

            String msg =
                readProcessOutput(process.getInputStream());

            throw new ServiceException(
                "mysqldump失败(Linux): " + msg
            );
        }
    }


    /**
     * 读取进程输出
     */
    private String readProcessOutput(
        java.io.InputStream inputStream
    ) throws IOException {

        StringBuilder sb = new StringBuilder();

        try (BufferedReader reader =
                 new BufferedReader(
                     new InputStreamReader(inputStream))) {

            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
        }

        return sb.toString();
    }

}
相关推荐
cooldream20094 小时前
Vim 报错 E325:swap 文件冲突的原理、处理流程与彻底避免方案
linux·编辑器·vim
i建模4 小时前
在 Rocky Linux 上安装轻量级的 XFCE 桌面
linux·运维·服务器
Data_Journal4 小时前
Scrapy vs. Crawlee —— 哪个更好?!
运维·人工智能·爬虫·媒体·社媒营销
若风的雨5 小时前
WC (Write-Combining) 内存类型优化原理
linux
YMWM_5 小时前
不同局域网下登录ubuntu主机
linux·运维·ubuntu
zmjjdank1ng5 小时前
restart与reload的区别
linux·运维
哼?~5 小时前
进程替换与自主Shell
linux
Suchadar5 小时前
Docker常用命令
运维·docker·容器
玖釉-5 小时前
深入浅出:渲染管线中的抗锯齿技术全景解析
c++·windows·图形渲染
你才是臭弟弟5 小时前
MinIo开发环境配置方案(Docker版本)
运维·docker·容器