| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |