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); + } + } }