package com.jiashi.service; import com.google.gson.Gson; import com.jiashi.CommonResult; import com.jiashi.FilePathUtil; import com.jiashi.FileUploader; import com.jiashi.dao.DataQuery; import lombok.extern.slf4j.Slf4j; import net.coobird.thumbnailator.Thumbnails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @Component @Slf4j public class UpdateService { @Autowired private DataQuery dataQuery; private Set lianZhongPatPicDirs = new HashSet<>(); /** * 最终保存文件的目录两位标识+10位病案号相关+8位日期,将标识作为第一级检索,病案号相关位第二层检索 */ private Map>> lianZhongPatPicDirMap = new HashMap<>(); public List updateData() { List cardInfos = dataQuery.dateQuery(); dataQuery.updateBatchState(cardInfos, 1); return cardInfos; } //state 状态标识。0 未开始, 1, 正在进行, 3. 已经成功。 4. 失败。5. 不需要上传的 @PostConstruct public void upload() { initLianZhongPatPicDir(); log.info("联众同步数据启动>>>>>>>>>>>>>>>>>>>>"); String syncDir = FilePathUtil.currentPath() + File.separator + "lianzhong-sync"; FilePathUtil.mkdirs(syncDir); ExecutorService patientExecutors = Executors.newFixedThreadPool(10); ExecutorService fileExecutor = Executors.newFixedThreadPool(90); try { while (true) { List cardInfos = this.updateData(); if (!CollectionUtils.isEmpty(cardInfos)) { List patientFutures = new ArrayList<>(); for (CardInfo cardInfo : cardInfos) { String picDir = syncDir + File.separator + cardInfo.getId(); FilePathUtil.mkdirs(picDir); Future patientFuture = patientExecutors.submit(() -> { try { List pictures = dataQuery.getPictures(cardInfo.getId()); if (CollectionUtils.isEmpty(pictures)) { //如果是空的则不同步 dataQuery.updateBatchState(cardInfo, 5); FilePathUtil.deleteDir(picDir); return; } String lianZhongDir = findLianZhongDir(cardInfo); if (lianZhongDir == null) { //失败,说明原因 dataQuery.updateBatchState(cardInfo, 2, "未找到联众数据文件夹!"); FilePathUtil.deleteDir(picDir); return; } dataQuery.updatePicPath(cardInfo, lianZhongDir); List fileFutures = new ArrayList<>(); for (Picture picture : pictures) { Future fileFuture = fileExecutor.submit(() -> { String tifFilePath = lianZhongDir + File.separator + removeFileExtension(picture.getPicname()) + ".tif"; File tifFile = new File(tifFilePath); if (tifFile.exists()) { try { BufferedImage read = ImageIO.read(tifFile); Thumbnails.of(read) .scale(1) .outputFormat("jpg") .rotate(picture.getRotatedegree()) .toFile(picDir + File.separator + removeFileExtension(picture.getPicname()) + ".jpg"); read.flush(); } catch (IOException e) { throw new RuntimeException(e); } } }); fileFutures.add(fileFuture); } for (Future future : fileFutures) { try { future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } LianZhongUploadInfo.PatientInfo patientInfo = convert(cardInfo); List fileInfos = new ArrayList<>(); List faultFileNames = new ArrayList<>(); List files = new ArrayList<>(); for (Picture picture : pictures) { File pictureFile = new File(picDir + File.separator + removeFileExtension(picture.getPicname()) + ".jpg"); if (!pictureFile.exists()) { faultFileNames.add(picture.getPicname()); continue; } files.add(pictureFile); LianZhongUploadInfo.FileInfo fileInfo = new LianZhongUploadInfo.FileInfo(); fileInfo.setFileTitle(picture.getPicname()); fileInfo.setUploadFileName(pictureFile.getName()); fileInfo.setAssortId(picture.getPickind()); fileInfo.setSort(picture.getPicno()); fileInfos.add(fileInfo); } if (files.isEmpty()) { dataQuery.updateBatchState(cardInfo, 2, "未获取到图片!"); // 删除文件 FilePathUtil.deleteDir(picDir); return; } LianZhongUploadInfo uploadInfo = new LianZhongUploadInfo(); uploadInfo.setPatientInfo(patientInfo); uploadInfo.setFileInfos(fileInfos); uploadInfo.setDelAllFile(1); try { Map fileInfoMap = fileInfos.stream() .collect(Collectors.toMap(LianZhongUploadInfo.FileInfo::getUploadFileName, Function.identity())); boolean success = true; // 上传 int totalSize = files.size(); int batchSize = 500; if (totalSize > batchSize) { for (int i = 0; i < totalSize; i += batchSize) { ArrayList batch = new ArrayList<>(); List uploadFileInfoList = new ArrayList<>(); // 计算当前批次的结束索引 int end = Math.min(i + batchSize, totalSize); for (int j = i; j < end; j++) { batch.add(files.get(j)); uploadFileInfoList.add(fileInfoMap.get(files.get(j).getName())); } uploadInfo.setFileInfos(uploadFileInfoList); // 额外的表单字段参数 List params = new ArrayList<>(); String s = new Gson().toJson(uploadInfo); params.add(new FormField("uploadFileParams", s)); CommonResult commonResult = FileUploader.uploadFilesWithParams(batch, "http://129.7.1.25:9511/lianzhong/batchFileUploadJpg", params); boolean res = commonResult.getCode() == 0; success = success && res; uploadInfo.setDelAllFile(0); } } else { // 额外的表单字段参数 List params = new ArrayList<>(); String s = new Gson().toJson(uploadInfo); params.add(new FormField("uploadFileParams", s)); CommonResult commonResult = FileUploader.uploadFilesWithParams(files, "http://129.7.1.25:9511/lianzhong/batchFileUploadJpg", params); success = commonResult.getCode() == 0; } if (success) { dataQuery.updateBatchState(cardInfo, 3); } else { dataQuery.updateBatchState(cardInfo, 2, "上传服务端出现异常!"); } } catch (Exception e) { dataQuery.updateBatchState(cardInfo, 2, e.getMessage()); log.error(e.getMessage(), e); } if (!faultFileNames.isEmpty()) { // 不完整 dataQuery.updateBatchState(cardInfo, 2, String.join(",", faultFileNames + " 无法转换jpg图片!")); } // 删除文件 FilePathUtil.deleteDir(picDir); } catch (Exception ex) { log.error(ex.getMessage(), ex); FilePathUtil.deleteDir(picDir); dataQuery.updateBatchState(cardInfo, 2, ex.getMessage()); } }); patientFutures.add(patientFuture); } for (Future future : patientFutures) { future.get(); } continue; } TimeUnit.SECONDS.sleep(300); } } catch (Exception ex) { log.error("联众同步数据异常:" + ex.getMessage(), ex); } } public static void main(String[] args) { UpdateService updateService = new UpdateService(); System.out.println(new Gson().toJson(updateService.lianZhongPatPicDirs)); System.out.println(new Gson().toJson(updateService.lianZhongPatPicDirMap)); updateService.initLianZhongPatPicDir(); System.out.println(new Gson().toJson(updateService.lianZhongPatPicDirs)); System.out.println(new Gson().toJson(updateService.lianZhongPatPicDirMap)); Map> stringSetMap = updateService.lianZhongPatPicDirMap.get("75"); System.out.println(new Gson().toJson(stringSetMap)); Set strings = stringSetMap.get("12345"); System.out.println(new Gson().toJson(strings)); } private void initLianZhongPatPicDir() { log.info(">>>>>>>>>>>>初始化联众患者文件目录"); String readFilePath = FilePathUtil.currentPath() + File.separator + "lianzhong-patpic-dir.txt"; File readFile = new File(readFilePath); if (readFile.exists()) { try (BufferedReader bufferedReader = new BufferedReader(new FileReader(readFile))) { String line; while ((line = bufferedReader.readLine()) != null) { lianZhongPatPicDirs.add(line); } initLianZhongPatPicDirsMap(lianZhongPatPicDirs); return; } catch (Exception ex) { log.error(">>>>>>>>>>>> 初始化联众患者文件目录读取存储文件错误!"); } } List rootDirs = Arrays.asList("D:\\UnionNet\\ServerD", "D:\\UnionNet\\ServerD_ny", "D:\\UnionNet\\ServerDTemp", "G:\\UnionNet\\ServerD", "G:\\UnionNet\\ServerD_ny", "G:\\UnionNet\\ServerDTemp"); for (String rootDir : rootDirs) { File rootDirFile = new File(rootDir); if (!rootDirFile.exists()) { continue; } // 第一层 年 ,如果有,后面基本都会有 File[] level1 = rootDirFile.listFiles(); if (level1 == null || level1.length <= 0) { continue; } for (File level1File : level1) { // 第二层 年月 File[] level2 = level1File.listFiles(); if (level2 == null || level2.length <= 0) { continue; } for (File level2File : level2) { if (level2File.isDirectory()) { // 第三层 年月日 File[] level3 = level2File.listFiles(); if (level3 == null || level3.length <= 0) { continue; } for (File level3File : level3) { if (level3File.isDirectory()) { // 第四层,患者存放文件的最后一级目录 File[] level4 = level3File.listFiles(); if (level4 == null || level4.length <= 0) { continue; } for (File level4File : level4) { if (level4File.isDirectory()) { lianZhongPatPicDirs.add(level4File.getPath()); } } } } } } } } if (!lianZhongPatPicDirs.isEmpty()) { initLianZhongPatPicDirsMap(lianZhongPatPicDirs); try (BufferedWriter writer = new BufferedWriter(new FileWriter(readFile));) { for (String picDir : lianZhongPatPicDirs) { writer.write(picDir); writer.newLine(); } } catch (Exception ex) { log.error("持久化联众患者文件目录错误:" + ex.getMessage(), ex); if (readFile.exists()) { readFile.delete(); } } } } /** * 最终保存文件的目录两位标识+10位病案号相关+8位日期,将标识作为第一级检索,病案号相关位第二层检索 */ private void initLianZhongPatPicDirsMap(Set lianZhongPatPicDirs) { for (String lianZhongPatPicDir : lianZhongPatPicDirs) { File patPicDirFile = new File(lianZhongPatPicDir); String name = patPicDirFile.getName(); String flag = name.substring(0, 2); String maybe = name.substring(2).substring(0, 10); maybe = removeLeadingZeros(maybe); Map> maybeMap = lianZhongPatPicDirMap.getOrDefault(flag, new HashMap<>()); Set maybeVal = maybeMap.getOrDefault(maybe, new HashSet<>()); maybeVal.add(lianZhongPatPicDir); maybeMap.put(maybe, maybeVal); lianZhongPatPicDirMap.put(flag, maybeMap); } } private LianZhongUploadInfo.PatientInfo convert(CardInfo cardInfo) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); LianZhongUploadInfo.PatientInfo patientInfo = new LianZhongUploadInfo.PatientInfo(); patientInfo.setInpatientNo(cardInfo.getPatno()); String gestno = cardInfo.getGestno(); // 123步设置住院次数 Integer admissTimes = cardInfo.getPatnum(); if (admissTimes == null && StringUtils.hasText(gestno)) { try { String admissTimesStr = removeLeadingZeros(gestno.substring(gestno.length() - 3)); admissTimes = Integer.valueOf(admissTimesStr); } catch (Exception ex) { log.error("无法解析gestno:{} 当中的住院次数!", gestno); } } // 没办法获取那就直接用出院的日期当住院次数 if (admissTimes == null) { SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd"); admissTimes = Integer.valueOf(sdf2.format(cardInfo.getOutdate())); } patientInfo.setAdmissTimes(admissTimes); patientInfo.setAge(cardInfo.getPatage()); patientInfo.setName(cardInfo.getPatname()); patientInfo.setSex("1".equals(cardInfo.getPatsex()) ? "男" : "女"); patientInfo.setSexName(""); patientInfo.setAdmissDate(cardInfo.getIndate() == null ? null : sdf.format(cardInfo.getIndate())); patientInfo.setDisDate(cardInfo.getOutdate() == null ? null : sdf.format(cardInfo.getOutdate())); patientInfo.setAdmissDeptName(cardInfo.getIndeptname()); patientInfo.setDisDeptName(cardInfo.getOutdeptname()); patientInfo.setIdCard(cardInfo.getPatciticard()); patientInfo.setMainDiagCode(null); patientInfo.setMainDiagName(null); patientInfo.setMainOperateCode(null); patientInfo.setMainOperateName(null); return patientInfo; } private String findLianZhongDir(CardInfo cardInfo) { String gestno = cardInfo.getGestno(); String patno = cardInfo.getPatno(); // Date outdate = cardInfo.getOutdate(); String pathPrefix = cardInfo.getPat_path().substring(0, 2); /// 取消出院日期判断,日期可能不是出院日期 //SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); // String outDateFormat = sdf.format(outdate); Map> maybeMap = lianZhongPatPicDirMap.get(pathPrefix); // gestno 或者 patno 进行 识别 if (StringUtils.hasText(gestno)) { Set picsByGestno = maybeMap.get(removeLeadingZeros(gestno)); if(!CollectionUtils.isEmpty(picsByGestno) && picsByGestno.size() == 1){ return new ArrayList<>(picsByGestno).get(0); } } Set picsByPatno = maybeMap.get(removeLeadingZeros(patno)); if(!CollectionUtils.isEmpty(picsByPatno) && picsByPatno.size() == 1){ return new ArrayList<>(picsByPatno).get(0); } return null; } public static String removeLeadingZeros(String str) { if (str == null || str.isEmpty()) { return str; // 如果字符串为空或null,直接返回 } int index = 0; // 循环检查每个字符,直到找到不是'0'的字符 while (index < str.length() && str.charAt(index) == '0') { index++; } // 返回从第一个非'0'字符开始到字符串末尾的子字符串 return str.substring(index); } public static String removeFileExtension(String fileName) { // 检查文件名是否为空或没有后缀 if (fileName == null || !fileName.contains(".")) { return fileName; } // 找到最后一个'.'字符的位置 int lastDotIndex = fileName.lastIndexOf('.'); // 截取不包含后缀的部分 return fileName.substring(0, lastDotIndex); } }