feat: 厦门中医院联众人工处理

xmzyy-lianzhong-peoplehandler
wyb 5 months ago
parent 81a2672a65
commit 55d7ef547e

@ -119,7 +119,10 @@
<artifactId>imageio-tiff</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
@ -132,7 +135,6 @@
<version>2.3.4.RELEASE</version>
</plugin>
</plugins>
</build>
@ -141,6 +143,7 @@
<maven.compiler.target>8</maven.compiler.target>
<swagger.version>2.9.2</swagger.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<log4jdbc.version>1.16</log4jdbc.version>
</properties>
</project>

@ -1,6 +1,6 @@
package com.jiashi;
import com.jiashi.service.UpdateService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@ -0,0 +1,22 @@
package com.jiashi.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 所有接口
registry.addMapping("/**")
// 是否发送 Cookie
.allowCredentials(true)
// 支持域
.allowedOriginPatterns("*")
// 支持方法
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}

@ -1,36 +0,0 @@
package com.jiashi.controller;
import com.jiashi.CommonResult;
import com.jiashi.service.UpdateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author YongBin Wen
* @date 2025/2/13 19:50
*/
@RestController
@Slf4j
@Api(tags = "厦门中医院联众同步接口")
@RequestMapping("/xiamenzyy/lianzhong")
public class LianZhongSyncController {
@Autowired
private UpdateService updateService;
@ApiOperation(value = "补偿状态为4的患者状态为2原因是没找到患者目录的手动处理后添加目录并且状态改为4")
@GetMapping("/sync/dirnotexists")
public CommonResult<String> sycDirNotExists() {
try {
updateService.sycDirNotExists();
return CommonResult.success("补偿完毕!");
} catch (Exception ex) {
log.error("补偿异常 "+ex.getMessage(),ex);
return CommonResult.failed("补偿异常!");
}
}
}

@ -0,0 +1,46 @@
package com.jiashi.controller;
import com.jiashi.CommonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author YongBin Wen
* @date 2025/2/16 15:52
*/
@Api(value = "测试", tags = "测试")
@RestController()
@RequestMapping("/test")
@Slf4j
public class TestController {
@ApiOperation("厦门中医院联众同步人工处理模拟数据")
@GetMapping("/listPatPicDirNotFound")
public CommonResult listPatPicDirNotFound(){
ArrayList<Map<String, Object>> maps = new ArrayList<>();
int idinit=2015;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
for (int i = 0; i < 10; i++) {
HashMap<String, Object> map = new HashMap<>();
idinit++;
map.put("id",idinit);
map.put("patno","0002015000"+idinit);
map.put("gestno","0002015000"+idinit+"001");
map.put("outdate",format.format(new Date()));
maps.add(map);
}
return CommonResult.success(maps);
}
}

@ -0,0 +1,130 @@
package com.jiashi.controller;
import com.jiashi.dao.DataQuery;
import com.jiashi.service.CardInfo;
import com.jiashi.service.CardInfoPath;
import com.jiashi.service.Picture;
import com.jiashi.service.XiamenZhongHospConfirm;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author YongBin Wen
* @date 2025/2/16 19:27
*/
@Controller
public class WebController {
@Autowired
private DataQuery dataQuery;
@RequestMapping("/index")
public String index(Model model) {
ArrayList<XiamenZhongHospConfirm> list = new ArrayList<>();
List<CardInfo> cardInfos = dataQuery.queryNotFoundDirCardInfo();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for (CardInfo cardInfo : cardInfos) {
String flag = cardInfo.getPat_path().substring(0, 2);
List<CardInfoPath> cardInfoPaths = dataQuery.queryPath(flag, cardInfo.getPatno(), cardInfo.getGestno());
if (CollectionUtils.isEmpty(cardInfoPaths)) {
dataQuery.updateBatchState(cardInfo, 6);
continue;
}
List<String> firstPageFilePaths = new ArrayList<>();
List<String> firstPageFileNames = findFirstPageFileName(cardInfo);
for (CardInfoPath cardInfoPath : cardInfoPaths) {
for (String firstPageFileName : firstPageFileNames) {
String firstPageFilePath = cardInfoPath.getPath() + File.separator + firstPageFileName;
if (new File(firstPageFilePath).exists()) {
firstPageFilePaths.add(firstPageFilePath);
}
}
}
XiamenZhongHospConfirm hospConfirm = new XiamenZhongHospConfirm();
hospConfirm.setId(cardInfo.getId());
hospConfirm.setPatname(cardInfo.getPatname());
hospConfirm.setPatno(cardInfo.getPatno());
hospConfirm.setGestno(cardInfo.getGestno());
hospConfirm.setOutdate(sdf.format(cardInfo.getOutdate()));
hospConfirm.setPaths(firstPageFilePaths);
list.add(hospConfirm);
}
model.addAttribute("confirms", list);
return "index";
}
private List<String> findFirstPageFileName(CardInfo cardInfo) {
List<Picture> pictures = dataQuery.getPictures(cardInfo.getId());
List<Picture> firstPageFiles = pictures.stream()
.filter(pic -> "1".equals(pic.getPickind()))
.collect(Collectors.toList());
return firstPageFiles.stream().map(e-> removeFileExtension(e.getPicname())+".tif")
.collect(Collectors.toList());
}
public static String removeFileExtension(String fileName) {
// 检查文件名是否为空或没有后缀
if (fileName == null || !fileName.contains(".")) {
return fileName;
}
// 找到最后一个'.'字符的位置
int lastDotIndex = fileName.lastIndexOf('.');
// 截取不包含后缀的部分
return fileName.substring(0, lastDotIndex);
}
@GetMapping("/readfile")
public void readFile(@RequestParam("path") String path, HttpServletResponse response) {
try (ServletOutputStream outputStream = response.getOutputStream()) {
BufferedImage bufferedImage = ImageIO.read(new File(path));
Thumbnails.of(bufferedImage)
.scale(1)
.outputFormat("jpg")
.rotate(90)
.toOutputStream(outputStream);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@GetMapping("/comfirmpath")
@ResponseBody
public String confirmPath(@RequestParam("path") String path, @RequestParam("id") String id) {
String dir = new File(path).getParentFile().getPath();
CardInfo cardInfo = new CardInfo();
cardInfo.setId(id);
dataQuery.confirmPicPathAnRecollect(cardInfo, dir);
return "success";
}
@GetMapping("/notconfirmPath")
@ResponseBody
public String notconfirmPath(@RequestParam("id") String id) {
CardInfo cardInfo = new CardInfo();
cardInfo.setId(id);
dataQuery.updateBatchState(cardInfo, 6);
return "success";
}
}

@ -0,0 +1,13 @@
package com.jiashi.dao;
import com.jiashi.service.CardInfoPath;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface CardInfoPathRepository extends JpaRepository<CardInfoPath, String>, JpaSpecificationExecutor<CardInfoPath> {
}

@ -32,4 +32,9 @@ public interface CardInfoRepository extends JpaRepository<CardInfo, String>, Jpa
@Modifying
@Query("update CardInfo set findpicpath=:findpicpath where id = :id")
void updatePicPath(String id, String findpicpath);
@Transactional(rollbackFor = Exception.class)
@Modifying
@Query("update CardInfo set findpicpath = :findpicpath ,state = 4 where id = :id")
void confirmPicPathAnRecollect(String id, String findpicpath);
}

@ -1,6 +1,7 @@
package com.jiashi.dao;
import com.jiashi.service.CardInfo;
import com.jiashi.service.CardInfoPath;
import com.jiashi.service.Picture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -33,6 +34,9 @@ public class DataQuery {
@Autowired
private PictureRepository pictureRepository;
@Autowired
private CardInfoPathRepository cardInfoPathRepository;
@Value("${lz.disdate-range:}")
private String disdateRange;
@ -48,6 +52,7 @@ public class DataQuery {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String[] split = disdateRange.split(",");
try {
predicates.add(cb.between(root.<Date>get("outdate"), simpleDateFormat.parse(split[0]), simpleDateFormat.parse(split[1])));
} catch (ParseException e) {
log.error("出院时间条件错误:" + disdateRange + "," + e.getMessage(), e);
@ -75,7 +80,6 @@ public class DataQuery {
predicates.add(cb.equal(root.<String>get("state"), state));
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
List<CardInfo> all = cardInfoRepository.findAll(specification);
return all;
} finally {
@ -84,6 +88,27 @@ public class DataQuery {
}
}
public List<CardInfo> queryNotFoundDirCardInfo() {
cardInfoTableLock.lock();
try {
Specification<CardInfo> specification = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.<String>get("state"), 2));
predicates.add(cb.equal(root.<String>get("describe"), "未找到联众数据文件夹!"));
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
Sort.Order sortCreateTime = Sort.Order.asc("outdate");
Sort sort = Sort.by(sortCreateTime);
Pageable pageable = PageRequest.of(0, 20, sort);
Page<CardInfo> all = cardInfoRepository.findAll(specification,pageable);
return all.toList();
} finally {
cardInfoTableLock.unlock();
}
}
public List<CardInfo> dateQueryByInpNo(String inpNo) {
cardInfoTableLock.lock();
@ -178,4 +203,28 @@ public class DataQuery {
}
}
public void confirmPicPathAnRecollect(CardInfo cardInfo, String findpicpath) {
cardInfoTableLock.lock();
try {
cardInfoRepository.confirmPicPathAnRecollect(cardInfo.getId(), findpicpath);
} finally {
cardInfoTableLock.unlock();
}
}
public List<CardInfoPath> queryPath(String flag, String patno, String gestno) {
Specification<CardInfoPath> specification = (root, query, cb) -> {
Predicate or1 = cb.equal(root.<String>get("patnopart"), patno);
Predicate or2 = cb.equal(root.<String>get("patnopart"), gestno);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.<String>get("flag"), flag));
predicates.add(cb.or(or1,or2));
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
List<CardInfoPath> all = cardInfoPathRepository.findAll(specification);
return all;
}
}

@ -0,0 +1,30 @@
package com.jiashi.service;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "t_card_info_upload_path")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CardInfoPath {
@Id
@Column(name="path")
private String path;
@Column(name="flag")
private String flag;
@Column(name="patnopart")
private String patnopart;
@Column(name="datepart")
private String datepart;
}

@ -1,592 +0,0 @@
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.beans.factory.annotation.Value;
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.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
@Slf4j
public class UpdateService {
@Autowired
private DataQuery dataQuery;
@Value("${lz.uploadUrl:http://129.7.1.25:9511/lianzhong/batchFileUploadJpg}")
private String uploadUrl;
private Set<String> lianZhongPatPicDirs = new HashSet<>();
/**
* +10+8
*/
private Map<String, Map<String, Set<String>>> lianZhongPatPicDirMap = new HashMap<>();
public List<CardInfo> updateData() {
List<CardInfo> cardInfos = dataQuery.dateQuery();
dataQuery.updateBatchState(cardInfos, 1);
return cardInfos;
}
//state 状态标识。0 未开始, 1 正在进行, 3. 已经成功。 4. 失败。5. 不需要上传的
@PostConstruct
public void upload() {
initLianZhongPatPicDir();
ExecutorService syncExecutors = Executors.newFixedThreadPool(2);
syncExecutors.execute(this::syncNotStart);
syncExecutors.execute(this::sycDirNotExistsJob);
}
public void syncNotStart() {
log.info("联众同步数据启动>>>>>>>>>>>>>>>>>>>>");
String syncDir = FilePathUtil.currentPath() + File.separator + "lianzhong-sync";
FilePathUtil.mkdirs(syncDir);
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maximumPoolSize = corePoolSize * 2;
long keepAliveTime = 300L;
TimeUnit unit = TimeUnit.SECONDS;
LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ThreadPoolExecutor.CallerRunsPolicy handler = new ThreadPoolExecutor.CallerRunsPolicy();
ExecutorService patientExecutors = Executors.newFixedThreadPool(corePoolSize);
ThreadPoolExecutor fileExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler);
try {
while (true) {
List<CardInfo> cardInfos = this.updateData();
if (!CollectionUtils.isEmpty(cardInfos)) {
List<Future> patientFutures = new ArrayList<>();
for (CardInfo cardInfo : cardInfos) {
String picDir = syncDir + File.separator + cardInfo.getId();
FilePathUtil.mkdirs(picDir);
Future patientFuture = patientExecutors.submit(() -> {
try {
List<Picture> 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<Future> 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<LianZhongUploadInfo.FileInfo> fileInfos = new ArrayList<>();
List<String> faultFileNames = new ArrayList<>();
List<File> 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<String, LianZhongUploadInfo.FileInfo> 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<File> batch = new ArrayList<>();
List<LianZhongUploadInfo.FileInfo> 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<FormField> params = new ArrayList<>();
String s = new Gson().toJson(uploadInfo);
params.add(new FormField("uploadFileParams", s));
CommonResult commonResult = FileUploader.uploadFilesWithParams(batch, uploadUrl, params);
boolean res = commonResult.getCode() == 0;
success = success && res;
uploadInfo.setDelAllFile(0);
}
} else {
// 额外的表单字段参数
List<FormField> params = new ArrayList<>();
String s = new Gson().toJson(uploadInfo);
params.add(new FormField("uploadFileParams", s));
CommonResult commonResult = FileUploader.uploadFilesWithParams(files, uploadUrl, 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);
} finally {
patientExecutors.shutdown();
fileExecutor.shutdown();
}
}
public void sycDirNotExistsJob() {
while (true) {
try {
sycDirNotExists();
TimeUnit.SECONDS.sleep(600);
} catch (Exception ex) {
log.error("补偿异常 " + ex.getMessage(), ex);
}
}
}
public void sycDirNotExists() {
log.info("联众补偿数据启动>>>>>>>>>>>>>>>>>>>>");
String syncDir = FilePathUtil.currentPath() + File.separator + "lianzhong-makeup-sync";
FilePathUtil.mkdirs(syncDir);
List<CardInfo> cardInfos = dataQuery.dateQuery(4);
if (!CollectionUtils.isEmpty(cardInfos)) {
for (CardInfo cardInfo : cardInfos) {
String picDir = syncDir + File.separator + cardInfo.getId();
FilePathUtil.mkdirs(picDir);
List<Picture> pictures = dataQuery.getPictures(cardInfo.getId());
String lianZhongDir = cardInfo.getFindpicpath();
if (lianZhongDir == null) {
FilePathUtil.deleteDir(picDir);
continue;
}
for (Picture picture : pictures) {
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) {
log.error("补偿转换文件失败pictureId=" + picture.getPicid() + "," + e.getMessage(), e);
}
}
}
LianZhongUploadInfo.PatientInfo patientInfo = convert(cardInfo);
List<LianZhongUploadInfo.FileInfo> fileInfos = new ArrayList<>();
List<String> faultFileNames = new ArrayList<>();
List<File> 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);
continue;
}
LianZhongUploadInfo uploadInfo = new LianZhongUploadInfo();
uploadInfo.setPatientInfo(patientInfo);
uploadInfo.setFileInfos(fileInfos);
uploadInfo.setDelAllFile(1);
Map<String, LianZhongUploadInfo.FileInfo> 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<File> batch = new ArrayList<>();
List<LianZhongUploadInfo.FileInfo> 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<FormField> params = new ArrayList<>();
String s = new Gson().toJson(uploadInfo);
params.add(new FormField("uploadFileParams", s));
CommonResult commonResult = FileUploader.uploadFilesWithParams(batch, uploadUrl, params);
boolean res = commonResult.getCode() == 0;
success = success && res;
uploadInfo.setDelAllFile(0);
}
} else {
// 额外的表单字段参数
List<FormField> params = new ArrayList<>();
String s = new Gson().toJson(uploadInfo);
params.add(new FormField("uploadFileParams", s));
CommonResult commonResult = FileUploader.uploadFilesWithParams(files, uploadUrl, params);
success = commonResult.getCode() == 0;
}
if (success) {
dataQuery.updateBatchState(cardInfo, 3);
}
if (!faultFileNames.isEmpty()) {
// 不完整
dataQuery.updateBatchState(cardInfo, 2, String.join(",", faultFileNames + " 无法转换jpg图片"));
}
// 删除文件
FilePathUtil.deleteDir(picDir);
}
}
}
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<String, Set<String>> stringSetMap = updateService.lianZhongPatPicDirMap.get("75");
System.out.println(new Gson().toJson(stringSetMap));
Set<String> strings = stringSetMap.get("12345");
System.out.println(new Gson().toJson(strings));
int processors = Runtime.getRuntime().availableProcessors();
System.out.println(processors);
}
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<String> 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<String> 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<String, Set<String>> maybeMap = lianZhongPatPicDirMap.getOrDefault(flag, new HashMap<>());
Set<String> 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<String, Set<String>> maybeMap = lianZhongPatPicDirMap.get(pathPrefix);
// gestno 或者 patno 进行 识别
if (StringUtils.hasText(gestno)) {
Set<String> picsByGestno = maybeMap.get(removeLeadingZeros(gestno));
if (!CollectionUtils.isEmpty(picsByGestno) && picsByGestno.size() == 1) {
return new ArrayList<>(picsByGestno).get(0);
}
}
Set<String> 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);
}
}

@ -0,0 +1,19 @@
package com.jiashi.service;
import lombok.Data;
import java.util.List;
/**
* @author YongBin Wen
* @date 2025/2/16 22:32
*/
@Data
public class XiamenZhongHospConfirm {
private String id;
private String patname;
private String patno;
private String gestno;
private String outdate;
private List<String> paths;
}

@ -11,11 +11,16 @@ spring:
url: jdbc:sqlserver://192.169.2.170:1433;DatabaseName=u_medrecord
username: sa
password: 17931@Uni
# url: jdbc:sqlserver://10.36.116.108:1433;DatabaseName=emr_record
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=u_medrecord
# username: sa
# password: xjgs+docus911
# password: 123456
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jpa:
database-platform: org.hibernate.dialect.SQLServerDialect
#database-platform: org.hibernate.dialect.MySQL5Dialect
thymeleaf:
cache: false

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" >
<title>静态资源hello</title>
</head>
<body>
静态资源hello
</body>
</html>

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>患者信息列表</title>
</head>
<style>
/* 隐藏放大的图片容器直到它被JavaScript显示 */
.zoomed-image-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
}
.zoomed-image {
max-width: 90%;
max-height: 90%;
}
.each-pat-container {
border: 3px solid black;
margin: 5px;
}
/* 样式调整,确保图片容器是内联块元素,以便可以正确设置边距 */
.image-container {
display: inline-block;
cursor: pointer;
margin: 10px;
}
</style>
<body>
<h1 style="text-align: center">厦门中医院嘉时根据首页确认联众患者图片存储目录重新采集</h1>
<div>
<div th:each="confirm:${confirms}" th:id="${confirm.getId()}" class="each-pat-container">
<h4 style="margin:2px;text-align: center"
th:text="|姓名:${confirm.getPatname()},病案号:${confirm.getPatno()},住院号:${confirm.getGestno()},出院日期:${confirm.getOutdate()}|"></h4>
<p style="margin:10px;">
<button onclick="displayNone(this)" th:parentid="${confirm.getId()}"
style="margin-left: 20em;margin-right: 5em" width="70px" height="25px">已处理
</button>
<a target="_blank" th:href="@{/notconfirmPath(id=${confirm.getId()})}"><b>不能处理</b></a>
</p>
<div th:each="filepath:${confirm.getPaths()}" style="display: inline">
<img th:src="@{/readfile(path=${filepath})}" height="595px" onclick="openZoomedImage(this)"/>
<a target="_blank" th:href="@{/comfirmpath(path=${filepath},id=${confirm.getId()})}"><b>确认</b></a>
</div>
</div>
</div>
<!-- 放大的图片容器 -->
<div class="zoomed-image-container" id="zoomedImageContainer">
<img id="zoomedImage" class="zoomed-image" src="" alt="Zoomed Image"/>
</div>
</body>
<script>
function displayNone(button) {
var id = button.getAttribute("parentid");
// document.getElementById(id).style.display = 'none';
document.getElementById(id).remove();
}
// 打开放大图片的函数
function openZoomedImage(imgElement) {
// 获取被点击图片的src属性
var imgSrc = imgElement.src;
// 设置放大图片容器的图片的src属性
document.getElementById('zoomedImage').src = imgSrc;
// 显示放大图片容器
document.getElementById('zoomedImageContainer').style.display = 'flex';
}
// 关闭放大图片的函数
function closeZoomedImage() {
// 隐藏放大图片容器
document.getElementById('zoomedImageContainer').style.display = 'none';
}
// 为放大图片容器添加点击事件监听器,以便点击时关闭放大图片
document.getElementById('zoomedImageContainer').addEventListener('click', closeZoomedImage);
</script>
</html>
Loading…
Cancel
Save