|
@@ -644,11 +644,14 @@ public class HealthReportController {
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
String originalFilename = file.getOriginalFilename();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // 先读取文件字节:图片用于安全检测,PDF用于MD5去重(storeFile 后 temp 文件会被转移,无法再读)
|
|
|
|
|
+ byte[] fileBytes = file.getBytes();
|
|
|
|
|
+ String fileHash = computeMd5(fileBytes);
|
|
|
|
|
+
|
|
|
// 图片类型:安全检测 + 保存
|
|
// 图片类型:安全检测 + 保存
|
|
|
if ("image".equals(type) || isImageContent(contentType)) {
|
|
if ("image".equals(type) || isImageContent(contentType)) {
|
|
|
try {
|
|
try {
|
|
|
- byte[] imageBytes = file.getBytes();
|
|
|
|
|
- if (!wechatService.checkImage(imageBytes)) {
|
|
|
|
|
|
|
+ if (!wechatService.checkImage(fileBytes)) {
|
|
|
return Result.error("图片内容包含敏感信息,上传失败");
|
|
return Result.error("图片内容包含敏感信息,上传失败");
|
|
|
}
|
|
}
|
|
|
} catch (IOException e) {
|
|
} catch (IOException e) {
|
|
@@ -663,6 +666,25 @@ public class HealthReportController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 重复上传检测:同一 MD5 已入库或正在采集中则直接返回,不再重复解析
|
|
|
|
|
+ Long existingReportId = healthReportService.findReportIdByFileHash(fileHash, reportTypeOfUpload(type, familyId));
|
|
|
|
|
+ if (existingReportId != null) {
|
|
|
|
|
+ Map<String, Object> dup = new LinkedHashMap<>();
|
|
|
|
|
+ dup.put("duplicate", true);
|
|
|
|
|
+ dup.put("reportId", existingReportId);
|
|
|
|
|
+ dup.put("message", "该报告已上传过,无需重复解析");
|
|
|
|
|
+ return Result.success(dup);
|
|
|
|
|
+ }
|
|
|
|
|
+ Long collectingDraftId = healthReportService.findCollectingDraftIdByFileHash(fileHash);
|
|
|
|
|
+ if (collectingDraftId != null) {
|
|
|
|
|
+ Map<String, Object> dup = new LinkedHashMap<>();
|
|
|
|
|
+ dup.put("duplicate", true);
|
|
|
|
|
+ dup.put("draftId", collectingDraftId);
|
|
|
|
|
+ dup.put("collecting", true);
|
|
|
|
|
+ dup.put("message", "该报告正在解析中,请稍候");
|
|
|
|
|
+ return Result.success(dup);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
String fileUrl = storageService.storeFile(file, "health-reports");
|
|
String fileUrl = storageService.storeFile(file, "health-reports");
|
|
|
String reportType = familyId != null && !("pdf".equals(type) || "auto".equals(type))
|
|
String reportType = familyId != null && !("pdf".equals(type) || "auto".equals(type))
|
|
|
? "physical_exam" : "gut_flora";
|
|
? "physical_exam" : "gut_flora";
|
|
@@ -672,7 +694,7 @@ public class HealthReportController {
|
|
|
String payloadJson = objectMapper.writeValueAsString(emptyPayload);
|
|
String payloadJson = objectMapper.writeValueAsString(emptyPayload);
|
|
|
|
|
|
|
|
HealthReportDraft draft = healthReportDraftService.createDraft(
|
|
HealthReportDraft draft = healthReportDraftService.createDraft(
|
|
|
- userId, familyId, reportType, fileUrl, originalFilename, payloadJson);
|
|
|
|
|
|
|
+ userId, familyId, reportType, fileUrl, originalFilename, payloadJson, fileHash);
|
|
|
|
|
|
|
|
// 提交异步采集:指纹判定类型后走专用脚本/opencode,完成自动入库,前端轮询 parse_status
|
|
// 提交异步采集:指纹判定类型后走专用脚本/opencode,完成自动入库,前端轮询 parse_status
|
|
|
reportCollectService.submitAsyncCollect(draft.getId());
|
|
reportCollectService.submitAsyncCollect(draft.getId());
|
|
@@ -691,6 +713,32 @@ public class HealthReportController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算上传文件的MD5哈希(用于重复上传去重)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String computeMd5(byte[] data) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
|
|
|
|
|
+ byte[] digest = md.digest(data);
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ for (byte b : digest) {
|
|
|
|
|
+ sb.append(String.format("%02x", b));
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("计算文件MD5失败: {}", e.getMessage());
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据上传类型推导报告类型(与下方 reportType 计算保持一致,供去重查询使用)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String reportTypeOfUpload(String type, Long familyId) {
|
|
|
|
|
+ return familyId != null && !("pdf".equals(type) || "auto".equals(type))
|
|
|
|
|
+ ? "physical_exam" : "gut_flora";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 查询异步采集任务状态(前端轮询):返回 parse_status/parse_method/parse_error/reportId
|
|
* 查询异步采集任务状态(前端轮询):返回 parse_status/parse_method/parse_error/reportId
|
|
|
*/
|
|
*/
|