系统新增备份功能(一次新增多张表)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();
    }

}
相关推荐
游戏开发爱好者85 分钟前
App Store 上传 IPA 自动化,.p8 密钥认证与 CI/CD 接入实战
android·运维·ci/cd·小程序·uni-app·自动化·iphone
天疆说13 分钟前
04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参
linux·运维·缓存
微硬创新14 分钟前
大点数模拟量采集落地:耐达讯自动化32路0-20mA适配PROFINET总线实践解析
运维·人工智能·网络协议·自动化·信息与通信
星卯教育tony15 分钟前
NOI Linux 2.0 服务器多用户网页桌面部署方案(腾讯云轻量Ubuntu20.04专属版 CSP复赛比赛环境搭建免安装虚拟机 )
linux·服务器·腾讯云
GIS数据转换器1 小时前
村镇无人机物流配送与跨域监测一体化平台
大数据·运维·人工智能·科技·无人机
Forever Nore1 小时前
LeetCode 14 最长公共前缀 - 纵向扫描
linux·服务器·leetcode
拂拉氏1 小时前
【知识讲解】 Linux权限相关知识讲解
linux·权限
倔强的石头1061 小时前
飞牛OS部署Mtab:Docker搭建自托管书签导航并实现远程访问
运维·docker·容器
Shadow(⊙o⊙)1 小时前
Linux网络部分——TCP协议服务端客户端交互接口,入门级硬核解析1.0
linux·网络·tcp/ip
Chief_fly2 小时前
【Windows 下载 ARM64 deb 包 麒麟 ARM 离线安装】
arm开发·windows