From 409dc0aa7b5a2e01ce17a46e54b069ce2b2df36b Mon Sep 17 00:00:00 2001
From: linjj <850658129@qq.com>
Date: Thu, 19 Mar 2026 14:35:20 +0800
Subject: [PATCH] =?UTF-8?q?=E7=9C=81=E5=8F=A3=E8=85=94=E5=90=8C=E6=AD=A5?=
=?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 4 +-
.../record/RecordSystemApplication.java | 2 +-
.../client/MedicalRecordHttpClient.java | 38 +--
.../controller/MedicalRecordController.java | 149 +++++----
.../medical/record/dao/CommomTableMapper.java | 46 +++
.../medical/record/entity/CommomTableVo.java | 35 +++
.../medical/record/entity/TscanAssort.java | 22 ++
src/main/resources/application.yml | 26 +-
.../resources/mapper/CommomTableMapper.xml | 32 ++
.../record/RecordSystemApplicationTests.java | 283 ++++++++++++++++++
10 files changed, 538 insertions(+), 99 deletions(-)
create mode 100644 src/main/java/com/medical/record/dao/CommomTableMapper.java
create mode 100644 src/main/java/com/medical/record/entity/CommomTableVo.java
create mode 100644 src/main/java/com/medical/record/entity/TscanAssort.java
create mode 100644 src/main/resources/mapper/CommomTableMapper.xml
diff --git a/pom.xml b/pom.xml
index dba8955..ed17ea0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,8 @@
spring-boot-starter-web
+
+
org.springdoc
springdoc-openapi-ui
@@ -42,7 +44,7 @@
com.microsoft.sqlserver
mssql-jdbc
- 9.4.1.jre8
+ 6.4.0.jre8
diff --git a/src/main/java/com/medical/record/RecordSystemApplication.java b/src/main/java/com/medical/record/RecordSystemApplication.java
index ea7fa07..029a871 100644
--- a/src/main/java/com/medical/record/RecordSystemApplication.java
+++ b/src/main/java/com/medical/record/RecordSystemApplication.java
@@ -5,7 +5,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
-@MapperScan("com.medical.record.mapper") // 扫描Mapper接口
+@MapperScan("com.medical.record.dao") // 扫描Mapper接口
public class RecordSystemApplication {
public static void main(String[] args) {
SpringApplication.run(RecordSystemApplication.class, args);
diff --git a/src/main/java/com/medical/record/client/MedicalRecordHttpClient.java b/src/main/java/com/medical/record/client/MedicalRecordHttpClient.java
index 8ede346..da078d0 100644
--- a/src/main/java/com/medical/record/client/MedicalRecordHttpClient.java
+++ b/src/main/java/com/medical/record/client/MedicalRecordHttpClient.java
@@ -109,17 +109,6 @@ public class MedicalRecordHttpClient {
/* 把 XML 对象序列化成字符串 */
- String xmlContent = xmlMapper.writeValueAsString(medicalRecordXml);
-
- /* 保存到本地(可选,调试时打开) */
- Path xmlPath = Paths.get("medicalRecord.xml"); // 可改成绝对路径
- Files.write(xmlPath,
- xmlContent.getBytes(StandardCharsets.UTF_8),
- StandardOpenOption.CREATE,
- StandardOpenOption.TRUNCATE_EXISTING);
-
-
- //把 XML 对象序列化成字节数组,准备当“文件”传 */
byte[] xmlBytes = xmlMapper.writeValueAsBytes(medicalRecordXml);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
@@ -131,32 +120,7 @@ public class MedicalRecordHttpClient {
ContentType.create("application/zip"),
zipFile.getFileName().toString());
-
-
-
- // 1. 先 build 实体
- HttpEntity rawEntity = builder.build();
-
- // 2. 包成可重复读实体,这样后面真正发请求时还能再用
- BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(rawEntity);
-
- // 3. 输出到内存缓冲区
- ByteArrayOutputStream buf = new ByteArrayOutputStream();
- bufferedEntity.writeTo(buf); // 不会把流弄空
- String multipartTxt = buf.toString(String.valueOf(StandardCharsets.UTF_8));
-
- // 4. 用 log 打印(slf4j 的 info 级别)
- log.info("---------- multipart 原文本 ----------\n{}", multipartTxt);
-
- // 5. 再放回请求,正常发
- httpPost.setEntity(bufferedEntity);
-
-
-// httpPost.setEntity(builder.build());
-
-
-
-
+ httpPost.setEntity(builder.build());
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
diff --git a/src/main/java/com/medical/record/controller/MedicalRecordController.java b/src/main/java/com/medical/record/controller/MedicalRecordController.java
index 837d6f5..c98fcac 100644
--- a/src/main/java/com/medical/record/controller/MedicalRecordController.java
+++ b/src/main/java/com/medical/record/controller/MedicalRecordController.java
@@ -1,10 +1,13 @@
package com.medical.record.controller;
import com.medical.record.client.MedicalRecordHttpClient;
+import com.medical.record.dao.CommomTableMapper;
import com.medical.record.dto.MedicalAttachmentFile;
import com.medical.record.dto.MedicalRecordData;
import com.medical.record.dto.MedicalRecordException;
import com.medical.record.dto.MedicalRecordXml;
+import com.medical.record.entity.CommomTableVo;
+import com.medical.record.entity.TscanAssort;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
@@ -18,6 +21,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.text.SimpleDateFormat;
@RestController
@RequestMapping("/api/record")
@@ -26,6 +30,11 @@ public class MedicalRecordController {
// 创建SLF4J Logger实例
private static final Logger log = LoggerFactory.getLogger(MedicalRecordController.class);
+ @Autowired
+ private CommomTableMapper commomTableMapper;
+
+ // 定义格式化器
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 同步病案数据到第三方系统、
@@ -34,84 +43,122 @@ public class MedicalRecordController {
@PostMapping("/sync")
@Operation(summary = "获取影像记录", description = "获取影像记录")
public String syncMedicalRecord(@RequestParam String accessKey) {
- Path zipFile = null;
+
try {
// 1. 初始化HTTP客户端
MedicalRecordHttpClient httpClient = new MedicalRecordHttpClient("http://172.16.1.133/emras");
- // 2. 获取SID
- String sid = httpClient.getSid(accessKey);
- log.info("sid:::::::::::::::::"+sid);
- // 3. 准备图像文件(实际业务中动态获取)
+
+ // 移除初始num查询,直接初始化分页参数
+ int batchSize = 100;
+ int offset = 0;
+ int batch = 0;
+ // 无限循环,直到查询结果为空
+ while (true) {
+ List commomTableVos = commomTableMapper.getPatientList(offset, batchSize);
+ // 无数据则终止循环
+ if (commomTableVos == null || commomTableVos.isEmpty()) {
+ log.info("无更多未同步患者数据,同步结束");
+ break;
+ }
+ batch++;
+ // 遍历处理当前批次数据
+ for (CommomTableVo vo : commomTableVos) {
+ try {
+ syncPatient(vo, httpClient, accessKey);
+ commomTableMapper.updateStaicByPatientId(vo.getPatientId(),"1");
+ } catch (Exception e) {
+ log.error("同步患者失败,patientId: {}", vo.getPatientId(), e);
+ commomTableMapper.updateStaicByPatientId(vo.getPatientId(),"2");
+ }
+ }
+ log.info("第 {} 批处理完成,本批次 {} 条", batch, commomTableVos.size());
+ // 偏移量自增
+ offset += batchSize;
+ }
+ return "同步完成";
+ }catch (Exception e) {
+ log.error("未知异常: {}", e.getMessage(), e);
+ return "同步失败(未知异常): " + e.getMessage();
+ }
+ }
+
+
+ //同步患者逻辑
+ public void syncPatient(CommomTableVo CommomTableVo, MedicalRecordHttpClient httpClient,String accessKey) throws Exception {
+ Path zipFile = null;
+ // 2. 获取SID
+ String sid = httpClient.getSid(accessKey);
+ try {
+ // 查询患者文件信息
+ List tscanAssorts = commomTableMapper.getTscanAssort(CommomTableVo.getPatientId());
List imagePaths = new ArrayList<>();
- imagePaths.add(Paths.get("D:/test/image1.jpg"));
- imagePaths.add(Paths.get("D:/test/image2.jpg"));
- // 4. 打包成ZIP
+
+ // 添加图像路径到图像路径集合中
+ for (TscanAssort tscanAssort : tscanAssorts) {
+ imagePaths.add(Paths.get(CommomTableVo.getFilePath() + "\\" + tscanAssort.getScanPage()));
+ }
+
+ // 打包成ZIP
zipFile = Paths.get("D:/temp/upload_" + System.currentTimeMillis() + ".zip");
MedicalRecordHttpClient.packageImagesToZip(imagePaths, zipFile);
- // 5. 构建附件信息
+ // 构建附件信息
List fileList = new ArrayList<>();
- for (Path img : imagePaths) {
+ for (int i = 0; i < imagePaths.size(); i++) {
MedicalAttachmentFile file = new MedicalAttachmentFile();
- file.setFileName(img.getFileName().toString());
- file.setFileSize(Files.size(img));
- file.setFileSuffix(getExtension(img));
- file.setMd5(MedicalRecordHttpClient.calculateMD5(img));
- file.setCatalogue("病历首页");
- file.setCatalogueId("C6A8308D842B450FB9FA0D21762237AB");
+ file.setFileName(imagePaths.get(i).getFileName().toString());
+ file.setFileSize(Files.size(imagePaths.get(i)));
+ file.setFileSuffix(getExtension(imagePaths.get(i)));
+ file.setMd5(MedicalRecordHttpClient.calculateMD5(imagePaths.get(i)));
+ file.setCatalogue(tscanAssorts.get(i).getAssortName());
+ file.setCatalogueId(tscanAssorts.get(i).getAssortId());
file.setCode("CODE_" + System.currentTimeMillis());
file.setTimeStapPrice(String.valueOf(System.currentTimeMillis()));
fileList.add(file);
}
- // 6. 构建病人信息
+ // 构建病人信息
MedicalRecordData data = new MedicalRecordData();
- data.setNumber("BA" + System.currentTimeMillis());
- data.setSickName("测试患者");
- data.setSerialNumber("SN" + System.currentTimeMillis());
- data.setInHospitalNumber(1);
- data.setInSection("影像科");
- data.setInSectionId(" ");
- data.setOutSection("影像科");
- data.setOutSectionId(" ");
+ data.setNumber(CommomTableVo.getInpatientNo());
+ data.setSickName(CommomTableVo.getName());
+ data.setSerialNumber(CommomTableVo.getPatientId());
+ data.setInHospitalNumber(CommomTableVo.getAdmissTimes());
+ data.setInSection("口腔颌面外科病房");
+ data.setInSectionId("668279670cda953360c1af33");
+ data.setOutSection("口腔颌面外科病房");
+ data.setOutSectionId("668279670cda953360c1af33");
data.setAttendingPhysician(" ");
data.setAttendingPhysicianId(" ");
data.setIdentityCard(" ");
- data.setInHospitalDate("2024-01-20 08:00:00");
- data.setOutHospitalDate("2024-01-20 10:30:00");
-
+ if (CommomTableVo.getAdmissDate() == null || CommomTableVo.getAdmissDate().equals("")){
+ data.setInHospitalDate(" ");
+ } else {
+ // 如果是 Date 类型
+ data.setInHospitalDate(DATE_FORMAT.format(CommomTableVo.getAdmissDate()));
+ }
- // 7. 构建XML对象
- MedicalRecordXml xml =
- new MedicalRecordXml(fileList, data);
+ if (CommomTableVo.getDisDate() == null || CommomTableVo.getDisDate().equals("")){
+ data.setOutHospitalDate(" ");
+ } else {
+ data.setOutHospitalDate(DATE_FORMAT.format(CommomTableVo.getDisDate()));
+ }
- // 8. 上传第三方系统
- String recordId = httpClient.uploadMedicalRecord(sid, xml, zipFile);
+ // 构建XML对象
+ MedicalRecordXml xml = new MedicalRecordXml(fileList, data);
- // 9 清理临时文件
+ // 上传第三方系统
+ String recordId = httpClient.uploadMedicalRecord(sid, xml, zipFile);
Files.deleteIfExists(zipFile);
-
- return "同步成功!病案ID: " + recordId;
-
- } catch (MedicalRecordException e) {
- log.error("业务异常: {}", e.getMessage(), e);
- return "同步失败(业务异常): " + e.getMessage();
- } catch (IOException e) {
- log.error("IO异常: {}", e.getMessage(), e);
- return "同步失败(IO异常): " + e.getMessage();
- } catch (Exception e) {
- log.error("未知异常: {}", e.getMessage(), e);
- return "同步失败(未知异常): " + e.getMessage();
- }
- finally {
+ } finally {
+ // 确保临时文件被清理(无论是否发生异常)
if (zipFile != null) {
try {
Files.deleteIfExists(zipFile);
- log.debug("临时文件在finally中已清理");
- } catch (IOException e) {
- log.warn("清理临时文件失败: {}", e.getMessage());
+ log.debug("临时文件已清理: {}", zipFile);
+ } catch (Exception e) {
+ log.warn("清理临时文件失败: {}", zipFile, e);
}
}
}
diff --git a/src/main/java/com/medical/record/dao/CommomTableMapper.java b/src/main/java/com/medical/record/dao/CommomTableMapper.java
new file mode 100644
index 0000000..5567a63
--- /dev/null
+++ b/src/main/java/com/medical/record/dao/CommomTableMapper.java
@@ -0,0 +1,46 @@
+package com.medical.record.dao;
+
+import com.medical.record.entity.CommomTableVo;
+import com.medical.record.entity.TscanAssort;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @ClassName CommomTableMapper
+ * @Description 患者基础数据
+ * @Author linjj
+ * @Date 2026/2/4 15:16
+ * @Version 1.0
+ */
+@Mapper
+public interface CommomTableMapper {
+
+ /**
+ * @Author: linjj
+ * @Description: 查询患者基础数量
+ * @DateTime: 2026/2/4 15:18
+ * @Return Integer
+ */
+ Integer getNum();
+
+ /**
+ * @Author: linjj
+ * @Description: 查询患者基础信息
+ * @DateTime: 2026/2/4 15:38
+ * @Params: [offset, limit]
+ * @Return java.util.List
+ */
+ List getPatientList(@Param("offset") int offset, @Param("limit") int limit);
+ /**
+ * @Author: linjj
+ * @Description: 查询患者图像信息
+ * @DateTime: 2026/2/4 15:39
+ * @Params: [PatientId]
+ * @Return java.util.List
+ */
+ List getTscanAssort(String PatientId);
+
+ boolean updateStaicByPatientId(@Param("PatientId") String PatientId,@Param("flag") String flag);
+}
diff --git a/src/main/java/com/medical/record/entity/CommomTableVo.java b/src/main/java/com/medical/record/entity/CommomTableVo.java
new file mode 100644
index 0000000..044a4d0
--- /dev/null
+++ b/src/main/java/com/medical/record/entity/CommomTableVo.java
@@ -0,0 +1,35 @@
+package com.medical.record.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @ClassName commomtable
+ * @Description 患者基础数据
+ * @Author linjj
+ * @Date 2026/2/4 15:03
+ * @Version 1.0
+ */
+@Data
+@TableName("commontable")
+public class CommomTableVo {
+
+ //流水号
+ private String patientId;
+ //住院号
+ private String inpatientNo;
+ //患者姓名
+ private String name;
+ //住院次数
+ private Integer admissTimes;
+ //出院时间
+ private Date disDate;
+ //入院时间
+ private Date admissDate;
+ //路径
+ private String filePath;
+
+
+}
diff --git a/src/main/java/com/medical/record/entity/TscanAssort.java b/src/main/java/com/medical/record/entity/TscanAssort.java
new file mode 100644
index 0000000..22e84fe
--- /dev/null
+++ b/src/main/java/com/medical/record/entity/TscanAssort.java
@@ -0,0 +1,22 @@
+package com.medical.record.entity;
+
+import lombok.Data;
+
+/**
+ * @ClassName TscanAssort
+ * @Description 文件信息
+ * @Author linjj
+ * @Date 2026/2/4 15:37
+ * @Version 1.0
+ */
+@Data
+public class TscanAssort {
+
+ //文件名称
+ private String scanPage;
+ //分段id
+ private String assortId;
+ //分段名称
+ private String assortName;
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 5c6d5dc..95a41c3 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,19 +1,27 @@
server:
port: 8891
+
spring:
datasource:
+ # 2008 R2 驱动类(6.4.0驱动还是这个,无需改)
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
- url: jdbc:sqlserver://localhost:1433;databaseName=medical_record_db;encrypt=true;trustServerCertificate=true;
- username: SA
- password: YourStrong@Passw0rd
+ # 上面的最简URL,替换IP/库名
+ url: jdbc:sqlserver://127.0.0.1:1433;databaseName=record;loginTimeout=30;sendStringParametersAsUnicode=false
+ username: sa
+ password: docus@702
hikari:
- minimum-idle: 5
- maximum-pool-size: 15
- auto-commit: true
- connection-timeout: 30000
- idle-timeout: 300000
- max-lifetime: 1200000
+ pool-name: HikariPool-medical
+ minimum-idle: 3 # 2008 R2不建议设太高,3-5即可,避免过多空闲连接
+ maximum-pool-size: 10 # 最大连接数,2008 R2性能有限,建议10-20,别超过30
+ idle-timeout: 300000 # 空闲超时5分钟(核心!2008 R2默认空闲10分钟断开,这里设5分钟,提前剔除)
+ max-lifetime: 1200000 # 连接最大生命周期20分钟(必须小于idle-timeout,且小于数据库超时)
+ connection-timeout: 30000 # 获取连接超时30秒
+ validation-timeout: 5000 # 连接校验超时5秒
+ test-on-borrow: true # 获取连接时校验(2008 R2必须开,兜底)
+ test-while-idle: true # 空闲后台校验(核心,自动清失效连接)
+ time-between-eviction-runs-millis: 60000 # 后台校验间隔1分钟,高频检查
+ connection-test-query: SELECT 1 # 2008 R2支持的最简校验语句,高效无副作用
# MyBatis-Plus配置
mybatis-plus:
diff --git a/src/main/resources/mapper/CommomTableMapper.xml b/src/main/resources/mapper/CommomTableMapper.xml
new file mode 100644
index 0000000..030a521
--- /dev/null
+++ b/src/main/resources/mapper/CommomTableMapper.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ UPDATE commomtable set static=#{flag} WHERE patient_id=#{PatientId}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/com/medical/record/RecordSystemApplicationTests.java b/src/test/java/com/medical/record/RecordSystemApplicationTests.java
index f4c43f6..da13ade 100644
--- a/src/test/java/com/medical/record/RecordSystemApplicationTests.java
+++ b/src/test/java/com/medical/record/RecordSystemApplicationTests.java
@@ -1,13 +1,296 @@
package com.medical.record;
+import org.apache.tomcat.jni.FileInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+
@SpringBootTest
class RecordSystemApplicationTests {
+
+ // 图片文件扩展名
+ private static final Set IMAGE_EXTENSIONS = new HashSet<>(Arrays.asList(
+ "jpg", "jpeg", "tif", "tiff"
+ ));
+
+ // 存储结果
+ private int jpgCount = 0;
+ private int tifCount = 0;
+ private int otherCount = 0;
+ private List jpgFiles = new ArrayList<>();
+ private List tifFiles = new ArrayList<>();
+ private List otherFiles = new ArrayList<>();
+
+
+ // 文件信息类
+ static class FileInfo {
+ String path;
+ String detectedFormat;
+ String extension;
+ long size;
+
+ FileInfo(String path, String detectedFormat, String extension, long size) {
+ this.path = path;
+ this.detectedFormat = detectedFormat;
+ this.extension = extension;
+ this.size = size;
+ }
+ }
+
+
+ /**
+ * 检查目录下的所有图片
+ */
+ public void checkDirectory(String directoryPath) throws IOException {
+ Path startPath = Paths.get(directoryPath);
+
+ if (!Files.exists(startPath)) {
+ throw new IOException("目录不存在: " + directoryPath);
+ }
+
+ if (!Files.isDirectory(startPath)) {
+ throw new IOException("指定路径不是目录: " + directoryPath);
+ }
+
+ System.out.println("开始扫描目录: " + startPath.toAbsolutePath());
+ System.out.println("==========================================");
+
+ // 遍历目录
+ Files.walkFileTree(startPath, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ String fileName = file.getFileName().toString().toLowerCase();
+
+ // 检查是否是图片文件(根据扩展名初步筛选)
+ String extension = getExtension(fileName);
+ if (IMAGE_EXTENSIONS.contains(extension)) {
+ analyzeImage(file.toFile(), extension);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+
+ printSummary();
+ }
+
+
+ /**
+ * 获取文件扩展名
+ */
+ private String getExtension(String fileName) {
+ int lastDot = fileName.lastIndexOf('.');
+ return (lastDot > 0) ? fileName.substring(lastDot + 1) : "";
+ }
+
+ /**
+ * 分析图片文件的真实格式
+ */
+ private void analyzeImage(File file, String extension) {
+ String detectedFormat = detectRealFormat(file);
+ long size = file.length();
+ String path = file.getAbsolutePath();
+
+ FileInfo info = new FileInfo(path, detectedFormat, extension, size);
+
+ // 根据检测到的真实格式分类
+ switch (detectedFormat.toUpperCase()) {
+ case "JPEG":
+ case "JPG":
+ jpgFiles.add(info);
+ jpgCount++;
+ break;
+ case "TIFF":
+ case "TIF":
+ tifFiles.add(info);
+ tifCount++;
+ break;
+ default:
+ otherFiles.add(info);
+ otherCount++;
+ break;
+ }
+ }
+
+ /**
+ * 通过文件头(Magic Number)检测真实图片格式
+ */
+ private String detectRealFormat(File file) {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ byte[] header = new byte[8];
+ int read = fis.read(header);
+
+ if (read < 4) return "UNKNOWN";
+
+ // 检查 JPEG: FF D8 FF
+ if ((header[0] & 0xFF) == 0xFF &&
+ (header[1] & 0xFF) == 0xD8 &&
+ (header[2] & 0xFF) == 0xFF) {
+ return "JPEG";
+ }
+
+ // 检查 TIFF: 49 49 2A 00 (小端) 或 4D 4D 00 2A (大端)
+ if (((header[0] & 0xFF) == 0x49 && (header[1] & 0xFF) == 0x49 &&
+ (header[2] & 0xFF) == 0x2A && (header[3] & 0xFF) == 0x00) ||
+ ((header[0] & 0xFF) == 0x4D && (header[1] & 0xFF) == 0x4D &&
+ (header[2] & 0xFF) == 0x00 && (header[3] & 0xFF) == 0x2A)) {
+ return "TIFF";
+ }
+
+ // 检查 PNG: 89 50 4E 47
+ if ((header[0] & 0xFF) == 0x89 &&
+ (header[1] & 0xFF) == 0x50 &&
+ (header[2] & 0xFF) == 0x4E &&
+ (header[3] & 0xFF) == 0x47) {
+ return "PNG";
+ }
+
+ // 检查 GIF: 47 49 46 38
+ if ((header[0] & 0xFF) == 0x47 &&
+ (header[1] & 0xFF) == 0x49 &&
+ (header[2] & 0xFF) == 0x46 &&
+ (header[3] & 0xFF) == 0x38) {
+ return "GIF";
+ }
+
+ // 检查 BMP: 42 4D
+ if ((header[0] & 0xFF) == 0x42 &&
+ (header[1] & 0xFF) == 0x4D) {
+ return "BMP";
+ }
+
+ // 检查 WebP: 52 49 46 46 ... 57 45 42 50
+ if ((header[0] & 0xFF) == 0x52 &&
+ (header[1] & 0xFF) == 0x49 &&
+ (header[2] & 0xFF) == 0x46 &&
+ (header[3] & 0xFF) == 0x46) {
+ byte[] extra = new byte[4];
+ if (fis.skip(4) == 4 && fis.read(extra) == 4) {
+ if (extra[0] == 0x57 && extra[1] == 0x45 &&
+ extra[2] == 0x42 && extra[3] == 0x50) {
+ return "WEBP";
+ }
+ }
+ return "RIFF";
+ }
+
+ return "UNKNOWN";
+
+ } catch (IOException e) {
+ return "ERROR: " + e.getMessage();
+ }
+ }
+
+ /**
+ * 打印汇总报告
+ */
+ private void printSummary() {
+ System.out.println("\n========== 扫描结果汇总 ==========");
+ System.out.printf("JPEG/JPG 文件: %d 个%n", jpgCount);
+ System.out.printf("TIFF/TIF 文件: %d 个%n", tifCount);
+ System.out.printf("其他格式文件: %d 个%n", otherCount);
+ System.out.printf("总计: %d 个文件%n", jpgCount + tifCount + otherCount);
+ System.out.println("==================================\n");
+
+ // 打印详细信息
+ if (!jpgFiles.isEmpty()) {
+ System.out.println("【JPEG/JPG 文件列表】");
+ printFileList(jpgFiles);
+ }
+
+ if (!tifFiles.isEmpty()) {
+ System.out.println("【TIFF/TIF 文件列表】");
+ printFileList(tifFiles);
+ }
+
+ if (!otherFiles.isEmpty()) {
+ System.out.println("【其他格式/异常文件列表】");
+ printFileList(otherFiles);
+ }
+
+ // 检查扩展名与实际格式不匹配的情况
+ checkMismatch();
+ }
+
+ /**
+ * 打印文件列表
+ */
+ private void printFileList(List files) {
+ for (FileInfo info : files) {
+ System.out.printf(" %s%n", info.path);
+ System.out.printf(" 实际格式: %s | 扩展名: .%s | 大小: %s%n",
+ info.detectedFormat, info.extension, formatFileSize(info.size));
+ }
+ System.out.println();
+ }
+
+ /**
+ * 检查扩展名与实际格式不匹配的情况
+ */
+ private void checkMismatch() {
+ System.out.println("【扩展名与实际格式不匹配警告】");
+ boolean hasMismatch = false;
+
+ List allFiles = new ArrayList<>();
+ allFiles.addAll(jpgFiles);
+ allFiles.addAll(tifFiles);
+ allFiles.addAll(otherFiles);
+
+ for (FileInfo info : allFiles) {
+ String ext = info.extension.toUpperCase();
+ String real = info.detectedFormat.toUpperCase();
+
+ boolean mismatch = false;
+ if (ext.equals("JPG") || ext.equals("JPEG")) {
+ if (!real.equals("JPEG") && !real.equals("JPG")) mismatch = true;
+ } else if (ext.equals("TIF") || ext.equals("TIFF")) {
+ if (!real.equals("TIFF") && !real.equals("TIF")) mismatch = true;
+ }
+
+ if (mismatch) {
+ System.out.printf(" ⚠️ %s%n", info.path);
+ System.out.printf(" 扩展名: .%s, 实际格式: %s%n", info.extension, info.detectedFormat);
+ hasMismatch = true;
+ }
+ }
+
+ if (!hasMismatch) {
+ System.out.println(" ✓ 未发现不匹配情况");
+ }
+ System.out.println();
+ }
+
+ /**
+ * 格式化文件大小
+ */
+ private String formatFileSize(long size) {
+ if (size < 1024) return size + " B";
+ if (size < 1024 * 1024) return String.format("%.2f KB", size / 1024.0);
+ if (size < 1024 * 1024 * 1024) return String.format("%.2f MB", size / (1024.0 * 1024));
+ return String.format("%.2f GB", size / (1024.0 * 1024 * 1024));
+ }
+
+
+
+
+
@Test
void contextLoads() {
+
+
+ try {
+ checkDirectory("E:\\tmp");
+ } catch (IOException e) {
+ System.err.println("错误: " + e.getMessage());
+ System.exit(1);
+ }
+
}
}