This commit is contained in:
yuxin-pc 2026-02-12 08:58:33 +08:00
parent ca3d5fb9f3
commit 15819c7260

View File

@ -4,15 +4,10 @@ import com.jsc.dsp.service.ConfigService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -21,15 +16,19 @@ import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime; import java.nio.file.attribute.FileTime;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@Component @Component
public class AutoExportAndUpload { public class ExportAndUploadUtils {
@Resource @Resource
DatabaseConnector databaseConnector; DatabaseConnector databaseConnector;
@ -37,6 +36,9 @@ public class AutoExportAndUpload {
@Resource @Resource
FTPConnector ftpConnector; FTPConnector ftpConnector;
@Resource
SFTPConnector sftpConnector;
@Resource @Resource
ConfigService configService; ConfigService configService;
@ -46,8 +48,11 @@ public class AutoExportAndUpload {
private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
@Value("${custom.excelOutputPath}") @Value("${custom.newsExcelOutputPath}")
String excelOutputPath; String newsExcelOutputPath;
@Value("${custom.twitterExcelOutputPath}")
String twitterExcelOutputPath;
@Value("${custom.backupFilePath}") @Value("${custom.backupFilePath}")
String backupFilePath; String backupFilePath;
@ -61,7 +66,7 @@ public class AutoExportAndUpload {
/** /**
* 每周一五的早上8点执行导出数据的任务 * 每周一五的早上8点执行导出数据的任务
*/ */
public void exportDataAndUpload() { public void exportNewsDataAndUpload() {
logger.info("开始导出excel和pdf数据..."); logger.info("开始导出excel和pdf数据...");
String lastLoadTime = configService.getConfigValueByName("last_loadtime"); String lastLoadTime = configService.getConfigValueByName("last_loadtime");
String currentLoadTime = StringUtils.DateToString(new Date()); String currentLoadTime = StringUtils.DateToString(new Date());
@ -72,20 +77,21 @@ public class AutoExportAndUpload {
String zipFileName = "data_news-" + timestamp + "-001.zip"; String zipFileName = "data_news-" + timestamp + "-001.zip";
String zipFileFullName = backupFilePath + File.separator + zipFileName; String zipFileFullName = backupFilePath + File.separator + zipFileName;
String remoteZipPath = ftpUploadPath + "/" + zipFileName; String remoteZipPath = ftpUploadPath + "/" + zipFileName;
zipAndUploadDirectory(excelOutputPath, zipFileFullName, remoteZipPath); zipAndUploadDirectory(newsExcelOutputPath, zipFileFullName, remoteZipPath);
} }
public void exportTwitterDataAndUpload(String startTime) { public void exportTwitterDataAndUpload() {
logger.info("开始导出twitter excel数据..."); logger.info("开始导出twitter excel数据...");
// String twitterLastLoadTime = configService.getConfigValueByName("twitter_last_loadtime"); String twitterLastLoadTime = configService.getConfigValueByName("twitter_last_loadtime");
String currentLoadTime = StringUtils.DateToString(new Date()); String currentLoadTime = StringUtils.DateToString(new Date());
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
databaseConnector.twitterToXlsx(startTime); databaseConnector.twitterToXlsx(twitterLastLoadTime);
unzipAndMoveVideosImages(twitterLastLoadTime, currentLoadTime);
configService.setConfigValueByName("twitter_last_loadtime", currentLoadTime); configService.setConfigValueByName("twitter_last_loadtime", currentLoadTime);
String zipFileName = "data_twitter-" + timestamp + "-001.zip"; String zipFileName = "data_twitter-" + timestamp + "-001.zip";
String zipFileFullName = backupFilePath + File.separator + zipFileName; String zipFileFullName = backupFilePath + File.separator + zipFileName;
String remoteZipPath = ftpUploadPath + "/" + zipFileName; String remoteZipPath = ftpUploadPath + "/" + zipFileName;
zipAndUploadDirectory(excelOutputPath, zipFileFullName, remoteZipPath); zipAndUploadDirectory(twitterExcelOutputPath, zipFileFullName, remoteZipPath);
} }
/** /**
@ -123,16 +129,16 @@ public class AutoExportAndUpload {
} }
// 上传 ZIP 文件 // 上传 ZIP 文件
// try (InputStream zipInputStream = Files.newInputStream(localZipFile)) { try (InputStream zipInputStream = Files.newInputStream(localZipFile)) {
// boolean uploaded = ftpConnector.uploadFile(zipInputStream, remoteZipPath); boolean uploaded = sftpConnector.uploadFile(zipInputStream, remoteZipPath);
// if (uploaded) { if (uploaded) {
// logger.info("ZIP 文件上传成功 - 本地: {}, FTP: {}", localZipPath, remoteZipPath); logger.info("ZIP 文件上传成功 - 本地: {}, FTP: {}", localZipPath, remoteZipPath);
// } else { } else {
// logger.error("ZIP 文件上传失败 - FTP: {}", remoteZipPath); logger.error("ZIP 文件上传失败 - FTP: {}", remoteZipPath);
// } }
// } catch (IOException e) { } catch (IOException e) {
// logger.error("读取本地 ZIP 文件失败: {}", localZipPath, e); logger.error("读取本地 ZIP 文件失败: {}", localZipPath, e);
// } }
// 注意此处不再删除 localZipFile由调用方决定是否保留或清理 // 注意此处不再删除 localZipFile由调用方决定是否保留或清理
} }
@ -181,6 +187,155 @@ public class AutoExportAndUpload {
} }
} }
/**
* 解压存档文件并移动视频/图片目录
*
* @param startTime 业务开始时间格式yyyy-MM-dd HH:mm:ss实际未使用但保留接口兼容性
* @param endTime 业务结束时间格式yyyy-MM-dd HH:mm:ss
*/
public void unzipAndMoveVideosImages(String startTime, String endTime) {
logger.info("开始处理存档文件: startTime={}, endTime={}", startTime, endTime);
try {
// 1. 计算endTime前一日日期
LocalDate archiveDate = parseEndDate(endTime).minusDays(1);
String dateStr = archiveDate.format(DateTimeFormatter.ISO_DATE); // yyyy-MM-dd
// 2. 构建存档目录路径: D:/data/dbzq_backup/{yyyy}/{yyyy-MM}/{yyyy-MM-dd}
String year = String.valueOf(archiveDate.getYear());
String yearMonth = archiveDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
Path archiveBaseDir = Paths.get("D:/data/dbzq_backup", year, yearMonth, dateStr);
if (!Files.exists(archiveBaseDir) || !Files.isDirectory(archiveBaseDir)) {
logger.error("存档目录不存在: {}", archiveBaseDir);
throw new FileNotFoundException("存档目录不存在: " + archiveBaseDir);
}
logger.info("使用存档目录: {}", archiveBaseDir);
// 3. 确保输出目录存在
Path outputDir = Paths.get(twitterExcelOutputPath);
Files.createDirectories(outputDir);
logger.info("输出目录: {}", outputDir);
// 4. 处理视频压缩包 (image_data_plane_*.tar.gz)
processArchiveFiles(
archiveBaseDir,
"image_data_plane_",
"videos",
outputDir
);
// 5. 处理图片压缩包 (image_data_ship_*.tar.gz)
processArchiveFiles(
archiveBaseDir,
"image_data_ship_",
"images",
outputDir
);
logger.info("存档文件处理完成: {}", dateStr);
} catch (Exception e) {
logger.error("存档处理失败 [endTime={}]", endTime, e);
throw new RuntimeException("存档处理异常: " + e.getMessage(), e);
}
}
/**
* 解析结束时间字符串兼容多种常见格式
*/
private LocalDate parseEndDate(String endTime) {
// 尝试常见时间格式
String[] patterns = {
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd"
};
for (String pattern : patterns) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return LocalDate.parse(endTime.substring(0, 10), DateTimeFormatter.ISO_DATE); // 直接取日期部分
} catch (Exception ignored) {
// 尝试下一种格式
}
}
// 最终尝试完整解析
try {
return LocalDate.parse(endTime.trim().split("\\s+")[0]); // 取日期部分
} catch (DateTimeParseException e) {
throw new IllegalArgumentException("无法解析 endTime 格式: " + endTime +
",支持格式: yyyy-MM-dd[ HH:mm:ss]");
}
}
/**
* 处理指定前缀的压缩包
*
* @param archiveDir 存档目录
* @param filePrefix 文件前缀 ( "image_data_plane_")
* @param targetDirName 目标目录名 ( "videos")
* @param outputDir 输出根目录
*/
private void processArchiveFiles(Path archiveDir, String filePrefix,
String targetDirName, Path outputDir) throws IOException {
// 查找所有匹配的tar.gz文件
List<Path> tarFiles = Files.list(archiveDir)
.filter(path -> Files.isRegularFile(path)
&& path.getFileName().toString().startsWith(filePrefix)
&& path.getFileName().toString().endsWith(".tar.gz"))
.sorted() // 按文件名排序确保处理顺序
.collect(Collectors.toList());
if (tarFiles.isEmpty()) {
logger.warn("未找到 {} 开头的压缩包: {}", filePrefix, archiveDir);
return;
}
logger.info("找到 {} 个 {} 压缩包: {}", tarFiles.size(), filePrefix,
tarFiles.stream().map(Path::getFileName).collect(Collectors.toList()));
// 创建全局临时目录用于合并所有压缩包内容
Path tempMergeDir = Files.createTempDirectory("archive_merge_");
logger.debug("创建临时合并目录: {}", tempMergeDir);
try {
// 步骤1: 依次解压所有tar.gz到临时目录
int totalFiles = 0;
for (Path tarFile : tarFiles) {
logger.info("解压压缩包: {}", tarFile.getFileName());
totalFiles += FileUtils.extractTarGz(tarFile.toFile(), tempMergeDir.toFile());
}
if (totalFiles == 0) {
logger.warn("解压后未发现任何文件,跳过移动: {}", filePrefix);
return;
}
logger.info("共解压 {} 个文件到临时目录", totalFiles);
// 步骤2: 平铺移动所有文件到目标目录不保留目录结构同名覆盖
Path targetPath = outputDir.resolve(targetDirName);
Files.createDirectories(targetPath); // 确保目标目录存在
int movedCount = FileUtils.flattenAndMoveFiles(tempMergeDir, targetPath);
logger.info("成功平铺移动 {} 个文件到: {}", movedCount, targetPath);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 清理临时目录
try {
FileUtils.deleteDirectory(tempMergeDir);
logger.debug("已清理临时目录: {}", tempMergeDir);
} catch (Exception e) {
logger.warn("清理临时目录失败: {}", tempMergeDir, e);
}
}
}
public void copyPagesFiles(String startTime, String endTime) { public void copyPagesFiles(String startTime, String endTime) {
try { try {
logger.info("开始复制PDF..."); logger.info("开始复制PDF...");
@ -196,7 +351,7 @@ public class AutoExportAndUpload {
} }
// 目标目录 excelOutputPath 下创建 pdf 子目录 // 目标目录 excelOutputPath 下创建 pdf 子目录
Path targetBaseDir = Paths.get(excelOutputPath); Path targetBaseDir = Paths.get(newsExcelOutputPath);
Path targetPdfDir = targetBaseDir.resolve("pdf"); Path targetPdfDir = targetBaseDir.resolve("pdf");
// 确保目标目录存在 // 确保目标目录存在