package cn.iocoder.yudao.module.system.service.database;
|
|
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.IoUtil;
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
|
import jakarta.servlet.http.HttpServletResponse;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.OutputStream;
|
import java.net.URLEncoder;
|
import java.nio.charset.StandardCharsets;
|
import java.util.Date;
|
import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
|
@Service
|
@Slf4j
|
public class DatabaseBackupServiceImpl implements DatabaseBackupService {
|
|
@Value("${spring.datasource.dynamic.datasource.master.url:}")
|
private String dbUrl;
|
|
@Value("${spring.datasource.dynamic.datasource.master.username:root}")
|
private String dbUsername;
|
|
@Value("${spring.datasource.dynamic.datasource.master.password:}")
|
private String dbPassword;
|
|
@Override
|
public void backupDatabase(HttpServletResponse response) throws Exception {
|
if (dbUrl == null || dbUrl.isEmpty()) {
|
throw new ServiceException(500, "未找到数据库连接配置");
|
}
|
|
// 解析 JDBC URL,提取 host, port, dbname
|
// 格式: jdbc:mysql://localhost:3306/ruoyi-vue-pro?useSSL=false
|
Pattern pattern = Pattern.compile("jdbc:mysql://([^:]+):(\\d+)/([^?]+)");
|
Matcher matcher = pattern.matcher(dbUrl);
|
String host = "127.0.0.1";
|
String port = "3306";
|
String dbName = "";
|
|
if (matcher.find()) {
|
host = matcher.group(1);
|
port = matcher.group(2);
|
dbName = matcher.group(3);
|
} else {
|
// fallback
|
log.warn("无法解析 JDBC URL: {}, 使用默认配置", dbUrl);
|
dbName = "ruoyi-vue-pro"; // 默认名,如果有的话
|
}
|
|
String timestamp = DateUtil.format(new Date(), "yyyyMMdd_HHmmss");
|
String fileName = "backup_" + timestamp + ".sql";
|
File tempFile = File.createTempFile("backup_", ".sql");
|
|
try {
|
// 构建 mysqldump 命令
|
String[] command = {
|
"mysqldump",
|
"-h" + host,
|
"-P" + port,
|
"-u" + dbUsername,
|
"-p" + dbPassword,
|
dbName,
|
"-r", tempFile.getAbsolutePath()
|
};
|
|
ProcessBuilder pb = new ProcessBuilder(command);
|
Process process = pb.start();
|
int exitCode = process.waitFor();
|
|
if (exitCode != 0) {
|
String error = IoUtil.read(process.getErrorStream(), StandardCharsets.UTF_8);
|
log.error("数据库备份失败, exitCode: {}, error: {}", exitCode, error);
|
throw new ServiceException(500, "数据库备份失败: " + error);
|
}
|
|
// 设置响应头
|
response.setContentType("application/octet-stream; charset=UTF-8");
|
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
|
response.setContentLengthLong(tempFile.length());
|
|
// 写入响应流
|
try (FileInputStream fis = new FileInputStream(tempFile);
|
OutputStream os = response.getOutputStream()) {
|
IoUtil.copy(fis, os);
|
}
|
|
} finally {
|
// 删除临时文件
|
FileUtil.del(tempFile);
|
}
|
}
|
}
|