diff --git a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java index 9bf7558..ac00ba5 100644 --- a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -54,6 +54,7 @@ public class SecurityConfig { .requestMatchers("/user/login").anonymous() .requestMatchers("/user/code").permitAll() .requestMatchers("/sms/resetPassword").permitAll() + .requestMatchers("/data/fishDraft/previewFile").permitAll() .requestMatchers("/tempFile/**").permitAll() .requestMatchers("/system/user/auditUser").permitAll() .requestMatchers("/eng/**").permitAll() diff --git a/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java b/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java index 9fac2b2..4f35d52 100644 --- a/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java +++ b/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java @@ -1,5 +1,15 @@ package com.yfd.platform.data.controller; +import java.io.File; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import cn.hutool.core.io.FileUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.databind.ObjectMapper; import com.yfd.platform.common.DataSourceRequest; @@ -21,6 +31,8 @@ import com.yfd.platform.utils.SecurityUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -57,7 +69,7 @@ public class FishDraftDataController { @PostMapping("/page") @Operation(summary = "分页查询过鱼数据(关联电站和设施)") public ResponseResult queryPageList(@RequestBody DataSourceRequest dataSourceRequest) { - Page result = fishDraftDataService.queryPageList( dataSourceRequest); + Page result = fishDraftDataService.queryPageList(dataSourceRequest); return ResponseResult.successData(result); } @@ -141,7 +153,7 @@ public class FishDraftDataController { @PostMapping("/submitDraft") @Operation(summary = "提交草稿") public ResponseResult submitDraft(@RequestParam String id, - @RequestParam String operatorId) { + @RequestParam String operatorId) { boolean result = fishDraftDataService.submitDraft(id, operatorId); return result ? ResponseResult.success("提交成功") : ResponseResult.error("提交失败"); } @@ -168,7 +180,6 @@ public class FishDraftDataController { } - @PostMapping("/lockDraft") @Operation(summary = "锁定草稿") public ResponseResult lockDraft(@RequestParam String id) { @@ -256,7 +267,7 @@ public class FishDraftDataController { result.setTaskId(taskId); String status = "VALIDATED"; if ("1".equals(result.getCode())) { - status="FAILED"; + status = "FAILED"; } importTaskService.updateStatus(taskId, status, null); importTaskService.updateProgress(taskId, result.getTotalCount(), result.getSuccessCount(), result.getFailedCount()); @@ -275,10 +286,186 @@ public class FishDraftDataController { } } + @GetMapping("/previewTempFiles") + @Operation(summary = "预览临时文件列表") + public ResponseResult previewTempFiles(@RequestParam String taskId) { + if (taskId == null || taskId.isEmpty()) { + return ResponseResult.error("任务ID不能为空"); + } + + try { + ImportTask task = importTaskService.getById(taskId); + if (task == null) { + return ResponseResult.error("任务不存在"); + } + + String resultJson = task.getResultJson(); + if (resultJson == null || resultJson.isEmpty()) { + return ResponseResult.error("任务结果为空"); + } + + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + + Map previewData = new HashMap<>(); + previewData.put("tempDir", importResult.getTempDir()); + previewData.put("excelFileName", importResult.getExcelFileName()); + previewData.put("excelFilePath", importResult.getExcelFilePath()); + + List> imageList = new ArrayList<>(); + if (importResult.getImageFiles() != null) { + for (Map.Entry entry : importResult.getImageFiles().entrySet()) { + Map fileInfo = new HashMap<>(); + fileInfo.put("originalName", entry.getKey()); + fileInfo.put("path", entry.getValue()); + fileInfo.put("type", "image"); + imageList.add(fileInfo); + } + } + previewData.put("images", imageList); + + List> videoList = new ArrayList<>(); + if (importResult.getVideoFiles() != null) { + for (Map.Entry entry : importResult.getVideoFiles().entrySet()) { + Map fileInfo = new HashMap<>(); + fileInfo.put("originalName", entry.getKey()); + fileInfo.put("path", entry.getValue()); + fileInfo.put("type", "video"); + videoList.add(fileInfo); + } + } + previewData.put("videos", videoList); + + previewData.put("totalImages", imageList.size()); + previewData.put("totalVideos", videoList.size()); + + return ResponseResult.successData(previewData); + + } catch (Exception e) { + log.error("预览临时文件失败: " + e.getMessage(), e); + return ResponseResult.error("预览失败: " + e.getMessage()); + } + } + + @GetMapping("/previewFile") + @Operation(summary = "预览临时文件内容") + public void previewFile(@RequestParam String taskId, @RequestParam String filename, @RequestParam String type, HttpServletRequest request, HttpServletResponse response) { + + ImportTask importTask = importTaskService.getById(taskId); + String resultJson = importTask.getResultJson(); + String filePath = null; + String dir = "1".equals(type) ? "images" : "videos"; + if (resultJson != null && !resultJson.isEmpty()) { + try { + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + String tempDir = importResult.getTempDir(); + filePath = tempDir + File.separator + dir + File.separator + filename; + } catch (Exception e) { + e.printStackTrace(); + // ignore parse error + } + } + if (filePath == null) { + writeErrorResponse(response, "文件路径不能为空"); + return; + } + + File file = new File(filePath); + if (!file.exists() || !file.isFile()) { + writeErrorResponse(response, "文件不存在"); + return; + } + + try { + String fileName = file.getName(); + String contentType = request.getServletContext().getMimeType(fileName); + if (contentType == null) { + contentType = "application/octet-stream"; + } + + response.setContentType(contentType); + response.setHeader("Content-Disposition", "inline; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); + response.setContentLengthLong(file.length()); + + try (FileInputStream fis = new FileInputStream(file); + OutputStream os = response.getOutputStream()) { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.flush(); + } + } catch (Exception e) { + log.error("预览文件失败: " + e.getMessage(), e); + writeErrorResponse(response, "预览失败: " + e.getMessage()); + } + } + + @GetMapping("/previewFileBase64") + @Operation(summary = "预览临时文件内容(Base64方式)") + public ResponseResult previewFileBase64(@RequestParam String filePath) { + if (filePath == null || filePath.isEmpty()) { + return ResponseResult.error("文件路径不能为空"); + } + + File file = new File(filePath); + if (!file.exists() || !file.isFile()) { + return ResponseResult.error("文件不存在"); + } + + try { + String fileName = file.getName(); + String contentType = getMimeType(fileName); + + byte[] fileContent = Files.readAllBytes(file.toPath()); + String base64Content = Base64.getEncoder().encodeToString(fileContent); + + Map result = new HashMap<>(); + result.put("fileName", fileName); + result.put("contentType", contentType); + result.put("base64Content", base64Content); + result.put("size", file.length()); + + return ResponseResult.successData(result); + } catch (Exception e) { + log.error("预览文件失败: " + e.getMessage(), e); + return ResponseResult.error("预览失败: " + e.getMessage()); + } + } + + private void writeErrorResponse(HttpServletResponse response, String message) { + try { + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"success\":false,\"message\":\"" + message + "\"}"); + } catch (Exception ignored) { + } + } + + private String getMimeType(String fileName) { + if (fileName == null) return "application/octet-stream"; + String lowerName = fileName.toLowerCase(); + if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (lowerName.endsWith(".png")) { + return "image/png"; + } else if (lowerName.endsWith(".gif")) { + return "image/gif"; + } else if (lowerName.endsWith(".mp4")) { + return "video/mp4"; + } else if (lowerName.endsWith(".webm")) { + return "video/webm"; + } else if (lowerName.endsWith(".pdf")) { + return "application/pdf"; + } else if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) { + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + } + return "application/octet-stream"; + } + @PostMapping("/cancelImport") @Operation(summary = "取消导入任务") public ResponseResult cancelImport(@RequestBody FishImportRequest fishImportRequest) { - boolean result = importTaskService.cancelTask(fishImportRequest.getTaskId(),SecurityUtils.getUserId()); + boolean result = importTaskService.cancelTask(fishImportRequest.getTaskId(), SecurityUtils.getUserId()); return result ? ResponseResult.success("取消成功") : ResponseResult.error("取消失败"); } @@ -529,34 +716,34 @@ public class FishDraftDataController { } private void validateAndNormalizeData(FishDraftData data, FishImportResult.FishImportRow importRow, - List warnings) { + List warnings) { if (data.getStcd() == null || data.getStcd().isEmpty()) { warnings.add("stcd"); } else { - String stcd = fishImportService.resolveFpssCode(data.getStcd(),data.getStnm()); + String stcd = fishImportService.resolveFpssCode(data.getStcd(), data.getStnm()); if (stcd == null) { warnings.add("stcd"); } } if (data.getRstcd() != null && !data.getRstcd().isEmpty()) { - String stationCode = fishImportService.resolveStationCode(data.getRstcd(),data.getEnnm()); + String stationCode = fishImportService.resolveStationCode(data.getRstcd(), data.getEnnm()); if (stationCode == null) { warnings.add("rstcd"); } - }else{ + } else { warnings.add("rstcd"); } if (data.getBaseId() != null && !data.getBaseId().isEmpty()) { - String baseId = fishImportService.resolveBaseCode(data.getBaseId(),data.getBaseName()); + String baseId = fishImportService.resolveBaseCode(data.getBaseId(), data.getBaseName()); if (baseId == null) { warnings.add("baseId"); } } if (data.getRvcd() != null && !data.getRvcd().isEmpty()) { - String rvcd = fishImportService.resolveRiverCode(data.getRvcd(),data.getRvcd()); + String rvcd = fishImportService.resolveRiverCode(data.getRvcd(), data.getRvcd()); if (rvcd == null) { warnings.add("rvcd"); } @@ -569,7 +756,7 @@ public class FishDraftDataController { if (data.getFtp() == null || data.getFtp().isEmpty()) { warnings.add("ftp"); } else { - String ftpCode = fishImportService.resolveFishDictCode(data.getFtp(),data.getFtpName()); + String ftpCode = fishImportService.resolveFishDictCode(data.getFtp(), data.getFtpName()); if (ftpCode == null) { warnings.add("ftp"); } diff --git a/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java b/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java index 6a1da6a..396b213 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java @@ -200,6 +200,9 @@ public class ImportTaskServiceImpl extends ServiceImpl attachmentUploadService.deleteFile(fileId)); } } + String tempDir = importResult.getTempDir(); + // del 方法会递归删除目录及其所有内容 + FileUtil.del(tempDir); } catch (Exception e) { e.printStackTrace(); // ignore parse error