fix: 保存草稿优化

This commit is contained in:
tangwei 2026-05-08 11:06:47 +08:00
parent 03f2e1c669
commit 28e53119a1
8 changed files with 212 additions and 57 deletions

View File

@ -2,6 +2,7 @@ package com.yfd.platform.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.yfd.platform.utils.SecurityUtils; import com.yfd.platform.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -12,6 +13,7 @@ import java.util.Date;
* 用于处理 @TableField(fill = FieldFill.INSERT/UPDATE) 注解 * 用于处理 @TableField(fill = FieldFill.INSERT/UPDATE) 注解
*/ */
@Component @Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler { public class MyMetaObjectHandler implements MetaObjectHandler {
@ -35,7 +37,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
// 自动填充更新时间 // 自动填充更新时间
this.strictInsertFill(metaObject, "updatedBy", String.class, userId); this.strictInsertFill(metaObject, "updatedBy", String.class, userId);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.info("message{}",e.getMessage());
} }
} }
@ -52,7 +54,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
// 自动填充更新人 // 自动填充更新人
this.strictInsertFill(metaObject, "updatedBy", String.class, userId); this.strictInsertFill(metaObject, "updatedBy", String.class, userId);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.info("message{}",e.getMessage());
} }
} }

View File

@ -33,6 +33,8 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -137,19 +139,15 @@ public class FishDraftDataController {
ImportTask importTask = importTaskService.getById(taskId); ImportTask importTask = importTaskService.getById(taskId);
String resultJson = importTask.getResultJson(); String resultJson = importTask.getResultJson();
FishImportResult importResult = null; FishImportResult importResult = null;
log.info("==============批量保存草稿================: {}", taskId); Map<String, String> imageFiles = null;
Map<String, String> videoFiles = null;
if (resultJson != null && !resultJson.isEmpty()) { if (resultJson != null && !resultJson.isEmpty()) {
try { try {
importResult = objectMapper.readValue(resultJson, FishImportResult.class); importResult = objectMapper.readValue(resultJson, FishImportResult.class);
ZipFileUtil.ZipContent content = new ZipFileUtil.ZipContent(); imageFiles = importResult.getImageFiles();
content.images = importResult.getImageFiles(); videoFiles = importResult.getVideoFiles();
content.videos = importResult.getVideoFiles();
log.info("==============processAttachments前================: {}", taskId);
fishImportService.processAttachments(importResult, content);
log.info("==============processAttachments后================: {}", taskId);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
// ignore parse error
} }
} }
@ -169,6 +167,7 @@ public class FishDraftDataController {
fishDraftDataList.add(data); fishDraftDataList.add(data);
} }
boolean result = fishDraftDataService.saveBatch(fishDraftDataList); boolean result = fishDraftDataService.saveBatch(fishDraftDataList);
fishImportService.processAttachmentsAsync(fishDraftDataList, imageFiles, videoFiles,importTask.getTempDir());
importTaskService.markSuccess(taskId); importTaskService.markSuccess(taskId);
return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败"); return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败");
} }
@ -392,9 +391,10 @@ public class FishDraftDataController {
task.setTempDir(tempDirPath.toString()); task.setTempDir(tempDirPath.toString());
importTaskService.save(task); importTaskService.save(task);
log.info("导入任务已创建: {}", taskId); log.info("导入任务已创建: {}", taskId);
SecurityContext securityContext = SecurityContextHolder.getContext();
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try { try {
SecurityContextHolder.setContext(securityContext);
log.info("异步开始解析ZIP文件, taskId: {}", taskId); log.info("异步开始解析ZIP文件, taskId: {}", taskId);
FishImportResult result = fishImportService.parseAndMapZipFromFile( FishImportResult result = fishImportService.parseAndMapZipFromFile(
savedZipFile, tempDirPath.toString(), uploadUserId); savedZipFile, tempDirPath.toString(), uploadUserId);
@ -413,6 +413,7 @@ public class FishDraftDataController {
log.error("异步解析ZIP失败, taskId: {}", taskId, e); log.error("异步解析ZIP失败, taskId: {}", taskId, e);
importTaskService.markFailed(taskId, "导入失败: " + e.getMessage()); importTaskService.markFailed(taskId, "导入失败: " + e.getMessage());
} finally { } finally {
SecurityContextHolder.clearContext();
if (savedZipFile.exists()) { if (savedZipFile.exists()) {
savedZipFile.delete(); savedZipFile.delete();
} }

View File

@ -38,6 +38,10 @@ public class AttachmentUploadService {
@Value("${attachment.video-url}") @Value("${attachment.video-url}")
private String videoUrl; private String videoUrl;
@Value("${attachment.delete-url}")
private String deleteUrl;
// private static final String DELETE_URL = "https://211.99.26.225:12125/FileDelete";
private static final ExecutorService UPLOAD_EXECUTOR = new ThreadPoolExecutor( private static final ExecutorService UPLOAD_EXECUTOR = new ThreadPoolExecutor(
5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy() new ThreadPoolExecutor.CallerRunsPolicy()
@ -313,7 +317,55 @@ public class AttachmentUploadService {
} }
} }
private static final String DELETE_URL = "https://211.99.26.225:12125/FileDelete";
// public boolean deleteFile(String attachmentId) {
// if (attachmentId == null || attachmentId.isEmpty()) {
// log.warn("附件ID为空");
// return false;
// }
//
// try {
// TrustManager[] trustAllCerts = new TrustManager[]{
// new X509TrustManager() {
// public void checkClientTrusted(X509Certificate[] chain, String authType) {}
// public void checkServerTrusted(X509Certificate[] chain, String authType) {}
// public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
// }
// };
//
// SSLContext sc = SSLContext.getInstance("TLS");
// sc.init(null, trustAllCerts, new SecureRandom());
//
// HttpClient secureClient = HttpClient.newBuilder()
// .sslContext(sc)
// .build();
//
// String formData = "fileId=" + attachmentId;
//
// HttpRequest request = HttpRequest.newBuilder()
// .uri(URI.create(deleteUrl))
// .header("Content-Type", "application/x-www-form-urlencoded")
// .header("token", token)
// .POST(HttpRequest.BodyPublishers.ofString(formData))
// .build();
//
// HttpResponse<String> response = secureClient.send(request, HttpResponse.BodyHandlers.ofString());
//
// if (response.statusCode() == 200) {
// String responseBody = response.body();
// return parseDeleteResult(responseBody);
// } else {
// log.error("删除附件失败: {}, 状态码: {}", attachmentId, response.statusCode());
// return false;
// }
// } catch (Exception e) {
// log.error("删除附件过程中发生异常: {}", attachmentId, e);
// return false;
// }
// }
public boolean deleteFile(String attachmentId) { public boolean deleteFile(String attachmentId) {
if (attachmentId == null || attachmentId.isEmpty()) { if (attachmentId == null || attachmentId.isEmpty()) {
@ -322,31 +374,16 @@ public class AttachmentUploadService {
} }
try { try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpClient secureClient = HttpClient.newBuilder()
.sslContext(sc)
.build();
String formData = "fileId=" + attachmentId; String formData = "fileId=" + attachmentId;
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(DELETE_URL)) .uri(URI.create(deleteUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.header("token", token) .header("token", token)
.POST(HttpRequest.BodyPublishers.ofString(formData)) .POST(HttpRequest.BodyPublishers.ofString(formData))
.build(); .build();
HttpResponse<String> response = secureClient.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
String responseBody = response.body(); String responseBody = response.body();
@ -361,6 +398,7 @@ public class AttachmentUploadService {
} }
} }
private boolean parseDeleteResult(String jsonResponse) { private boolean parseDeleteResult(String jsonResponse) {
if (jsonResponse == null || jsonResponse.isEmpty()) { if (jsonResponse == null || jsonResponse.isEmpty()) {
return false; return false;

View File

@ -51,4 +51,9 @@ public interface IFishImportService {
boolean validateFpssBelongsToStation(String fpssCode, String stationCode); boolean validateFpssBelongsToStation(String fpssCode, String stationCode);
void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent); void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent);
void processAttachmentsAsync(java.util.List<com.yfd.platform.data.domain.FishDraftData> dataList,
java.util.Map<String, String> imageFiles,
java.util.Map<String, String> videoFiles,
String tempDir);
} }

View File

@ -1,5 +1,6 @@
package com.yfd.platform.data.service.impl; package com.yfd.platform.data.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yfd.platform.data.domain.FishDraftData; import com.yfd.platform.data.domain.FishDraftData;
@ -8,6 +9,7 @@ import com.yfd.platform.data.domain.FishImportResult;
import com.yfd.platform.data.domain.SysUserDataScope; import com.yfd.platform.data.domain.SysUserDataScope;
import com.yfd.platform.data.mapper.SysUserDataScopeMapper; import com.yfd.platform.data.mapper.SysUserDataScopeMapper;
import com.yfd.platform.data.service.AttachmentUploadService; import com.yfd.platform.data.service.AttachmentUploadService;
import com.yfd.platform.data.service.IFishDraftDataService;
import com.yfd.platform.data.service.IFishImportService; import com.yfd.platform.data.service.IFishImportService;
import com.yfd.platform.data.utils.ZipFileUtil; import com.yfd.platform.data.utils.ZipFileUtil;
import com.yfd.platform.env.domain.*; import com.yfd.platform.env.domain.*;
@ -19,6 +21,8 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -31,6 +35,7 @@ import java.math.BigDecimal;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
@Service @Service
@Slf4j @Slf4j
@ -63,6 +68,9 @@ public class FishImportServiceImpl implements IFishImportService {
@Resource @Resource
private AttachmentUploadService attachmentUploadService; private AttachmentUploadService attachmentUploadService;
@Resource
private IFishDraftDataService fishDraftDataService;
@Resource @Resource
private SysUserDataScopeMapper userDataScopeMapper; private SysUserDataScopeMapper userDataScopeMapper;
@ -1283,6 +1291,118 @@ public class FishImportServiceImpl implements IFishImportService {
} }
} }
@Override
public void processAttachmentsAsync(List<FishDraftData> dataList,
Map<String, String> imageFiles,
Map<String, String> videoFiles,
String tempDir) {
if (dataList == null || dataList.isEmpty()) {
return;
}
List<CompletableFuture<Void>> futures = new ArrayList<>();
SecurityContext securityContext = SecurityContextHolder.getContext();
for (FishDraftData data : dataList) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
SecurityContextHolder.setContext(securityContext);
String vdpth = data.getVdpth();
String picpth = data.getPicpth();
if(StrUtil.isBlank(vdpth)||StrUtil.isBlank(picpth)){
log.error("数据不完整, 忽略处理");
return;
}
if (StringUtils.hasText(vdpth) && videoFiles != null && !videoFiles.isEmpty()) {
String uploadedVdpth = uploadVideoFilesAsync(vdpth, videoFiles);
if (uploadedVdpth != null) {
data.setVdpth(uploadedVdpth);
}
}
if (StringUtils.hasText(picpth) && imageFiles != null && !imageFiles.isEmpty()) {
String uploadedPicpth = uploadImageFilesAsync(picpth, imageFiles);
if (uploadedPicpth != null) {
data.setPicpth(uploadedPicpth);
}
}
fishDraftDataService.updateById(data);
log.info("异步上传附件完成, dataId: {},{},{}", data.getId(), data.getPicpth(), data.getVdpth());
} catch (Exception e) {
log.error("异步上传附件失败, dataId: {}", data.getId(), e);
} finally {
SecurityContextHolder.clearContext();
}
});
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
try {
// del 方法会递归删除目录及其所有内容
FileUtil.del(tempDir);
} catch (Exception e) {
log.error("删除临时目录失败{}", e.getMessage());
}
})
.exceptionally(ex -> {
log.error("等待异步任务完成时发生异常", ex);
return null;
});
}
private String uploadVideoFilesAsync(String videoNames, Map<String, String> videoMap) {
String[] fileNames = videoNames.split(";");
List<String> attachmentIds = new ArrayList<>();
for (String fileName : fileNames) {
fileName = fileName.trim();
if (fileName.isEmpty()) {
continue;
}
String actualPath = findFilePath(fileName, videoMap);
if (actualPath != null) {
try {
String attachmentId = attachmentUploadService.uploadFileAndGetId(actualPath);
if (attachmentId != null) {
attachmentIds.add(attachmentId);
}
} catch (Exception e) {
log.error("上传视频失败: {}", fileName, e);
}
}
}
return attachmentIds.isEmpty() ? videoNames : String.join(",", attachmentIds);
}
private String uploadImageFilesAsync(String imageNames, Map<String, String> imageMap) {
String[] fileNames = imageNames.split(";");
List<String> attachmentIds = new ArrayList<>();
for (String fileName : fileNames) {
fileName = fileName.trim();
if (fileName.isEmpty()) {
continue;
}
String actualPath = findFilePath(fileName, imageMap);
if (actualPath != null) {
try {
String attachmentId = attachmentUploadService.uploadFileAndGetId(actualPath);
if (attachmentId != null) {
attachmentIds.add(attachmentId);
}
} catch (Exception e) {
log.error("上传图片失败: {}", fileName, e);
}
}
}
return attachmentIds.isEmpty() ? imageNames : String.join(",", attachmentIds);
}
private String processVideoAttachments(FishImportResult.FishImportRow importRow, String videoNames, Map<String, String> videoMap) { private String processVideoAttachments(FishImportResult.FishImportRow importRow, String videoNames, Map<String, String> videoMap) {
if (videoNames == null || videoNames.isEmpty() || videoMap == null || videoMap.isEmpty()) { if (videoNames == null || videoNames.isEmpty() || videoMap == null || videoMap.isEmpty()) {
return videoNames; return videoNames;

View File

@ -140,19 +140,6 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
importTask.setStatus("CONFIRMED"); importTask.setStatus("CONFIRMED");
importTask.setUpdatedAt(new Date()); importTask.setUpdatedAt(new Date());
boolean b = this.updateById(importTask); boolean b = this.updateById(importTask);
// 删除本地临时目录数据
String resultJson = importTask.getResultJson();
if (resultJson != null && !resultJson.isEmpty()) {
try {
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
String tempDir = importResult.getTempDir();
// del 方法会递归删除目录及其所有内容
FileUtil.del(tempDir);
} catch (Exception e) {
e.printStackTrace();
// ignore parse error
}
}
return b; return b;
} }

View File

@ -122,5 +122,6 @@ attachment:
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH} token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload} upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload} video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/delete}
# upload-url: ${ATTACHMENT_UPLOAD_URL:https://211.99.26.225:12125/upload} # upload-url: ${ATTACHMENT_UPLOAD_URL:https://211.99.26.225:12125/upload}
# video-url: ${ATTACHMENT_VIDEO_URL:https://211.99.26.225:12125/upload} # video-url: ${ATTACHMENT_VIDEO_URL:https://211.99.26.225:12125/upload}

View File

@ -122,3 +122,4 @@ attachment:
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH} token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload} upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload} video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/delete}