|
|
@@ -1,28 +1,42 @@
|
|
|
package com.fs.hisStore.facade.impl;
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.io.resource.ClassPathResource;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.json.JSONArray;
|
|
|
import cn.hutool.json.JSONObject;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.fs.hisStore.facade.FsStore580FacadeService;
|
|
|
import com.fs.hospital580.entity.Hospital580PrescriptionAnswerScrmEntity;
|
|
|
import com.fs.hospital580.entity.Hospital580PrescriptionChatScrmEntity;
|
|
|
import com.fs.hospital580.entity.Hospital580PrescriptionMedicineScrmEntity;
|
|
|
import com.fs.hospital580.entity.Hospital580PrescriptionScrmEntity;
|
|
|
+import com.fs.hospital580.param.PrescriptionPdfParam;
|
|
|
import com.fs.hospital580.service.Hospital580PrescriptionAnswerScrmService;
|
|
|
import com.fs.hospital580.service.Hospital580PrescriptionChatScrmService;
|
|
|
import com.fs.hospital580.service.Hospital580PrescriptionMedicineScrmService;
|
|
|
import com.fs.hospital580.service.Hospital580PrescriptionScrmService;
|
|
|
import com.fs.hospital580.vo.*;
|
|
|
+import com.itextpdf.text.*;
|
|
|
+import com.itextpdf.text.pdf.BaseFont;
|
|
|
+import com.itextpdf.text.pdf.PdfPCell;
|
|
|
+import com.itextpdf.text.pdf.PdfPTable;
|
|
|
+import com.itextpdf.text.pdf.PdfWriter;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
-import java.util.Collections;
|
|
|
+import javax.net.ssl.HttpsURLConnection;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.net.*;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
import java.util.List;
|
|
|
-import java.util.Objects;
|
|
|
-import java.util.Optional;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Service("fsStore580FacadeService")
|
|
|
@@ -37,6 +51,8 @@ public class FsStore580FacadeServiceImpl implements FsStore580FacadeService {
|
|
|
@Autowired
|
|
|
private Hospital580PrescriptionMedicineScrmService medicineScrmService;
|
|
|
|
|
|
+ private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
|
|
+ private static final ClassPathResource FONT_RESOURCE = new ClassPathResource("fonts/SimHei.ttf");
|
|
|
|
|
|
@Override
|
|
|
public List<Hospital580PrescriptionScrmEntity> list(PrescriptionAdminQueryVo vo) {
|
|
|
@@ -88,4 +104,322 @@ public class FsStore580FacadeServiceImpl implements FsStore580FacadeService {
|
|
|
.collect(Collectors.toList()))
|
|
|
.orElse(Collections.emptyList());
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ByteArrayOutputStream generatePdf(PrescriptionPdfParam param) {
|
|
|
+ //获取需要下载咨询的列表信息
|
|
|
+ List<Hospital580PrescriptionScrmEntity> hospital580PrescriptionScrmEntityList=prescriptionScrmService.list(new LambdaQueryWrapper<Hospital580PrescriptionScrmEntity>().in(Hospital580PrescriptionScrmEntity::getPreId,param.getIds()));
|
|
|
+ if(!hospital580PrescriptionScrmEntityList.isEmpty()){
|
|
|
+ //获取对应聊天记录信息
|
|
|
+ List<Hospital580PrescriptionChatScrmEntity> chatScrmEntityList = chatScrmService.list(new LambdaQueryWrapper<Hospital580PrescriptionChatScrmEntity>().select(Hospital580PrescriptionChatScrmEntity::getContent,Hospital580PrescriptionChatScrmEntity::getPreId).in(Hospital580PrescriptionChatScrmEntity::getPreId,param.getIds()));
|
|
|
+ Map<Long,List<ChatMessageVo>> messageMap = null;
|
|
|
+ if(!chatScrmEntityList.isEmpty()){
|
|
|
+ //转换消息
|
|
|
+ List<ChatMessageVo> chatMessageVoList = chatScrmEntityList.stream()
|
|
|
+ .flatMap(c -> {
|
|
|
+ Long preId = c.getPreId();
|
|
|
+ return JSON.parseArray(c.getContent(), ChatMessageVo.class)
|
|
|
+ .stream()
|
|
|
+ .peek(vo -> vo.setPreId(preId));
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ messageMap = chatMessageVoList.stream()
|
|
|
+ .collect(Collectors.groupingBy(ChatMessageVo::getPreId));
|
|
|
+ }
|
|
|
+ return combinedPdf(hospital580PrescriptionScrmEntityList,messageMap);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ //组合pdf格式
|
|
|
+ public ByteArrayOutputStream combinedPdf(List<Hospital580PrescriptionScrmEntity> entityList, Map<Long, List<ChatMessageVo>> messageMap) {
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
+ Document document = new Document(PageSize.A4, 40, 40, 50, 50);
|
|
|
+ try {
|
|
|
+ PdfWriter.getInstance(document, outputStream);
|
|
|
+ document.open();
|
|
|
+ int pageNum=0;
|
|
|
+ for (Hospital580PrescriptionScrmEntity h : entityList) {
|
|
|
+ List<ChatMessageVo> messages = messageMap.getOrDefault(h.getPreId(), Collections.emptyList());
|
|
|
+ generateToDocument(h, document, messages,pageNum);
|
|
|
+ pageNum++;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ throw new RuntimeException("PDF合并失败", e);
|
|
|
+ } finally {
|
|
|
+ if (document.isOpen()) {
|
|
|
+ document.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return outputStream;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重构:仅向现有文档添加内容,不返回document,不关闭任何资源
|
|
|
+ */
|
|
|
+ private void generateToDocument(Hospital580PrescriptionScrmEntity entity, Document document, List<ChatMessageVo> messageList,Integer pageNum) throws DocumentException, IOException {
|
|
|
+ if(pageNum > 0){
|
|
|
+ document.newPage();
|
|
|
+ }
|
|
|
+ BaseFont baseFont = BaseFont.createFont(FONT_RESOURCE.getPath(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
|
|
|
+ Font titleFont = new Font(baseFont, 18, Font.BOLD, BaseColor.BLACK);
|
|
|
+ Font tableKeyFont = new Font(baseFont, 11, Font.BOLD, BaseColor.DARK_GRAY);
|
|
|
+ Font tableValueFont = new Font(baseFont, 11, Font.NORMAL, BaseColor.BLACK);
|
|
|
+ Font subTitleFont = new Font(baseFont, 14, Font.BOLD, BaseColor.DARK_GRAY);
|
|
|
+ Font msgFont = new Font(baseFont, 10, Font.NORMAL, BaseColor.BLACK);
|
|
|
+
|
|
|
+ //------------每条处方首页-----
|
|
|
+ Paragraph title = new Paragraph("问诊处方信息表", titleFont);
|
|
|
+ title.setAlignment(Element.ALIGN_CENTER);
|
|
|
+ title.setSpacingAfter(30);
|
|
|
+ document.add(title);
|
|
|
+
|
|
|
+ Map<String, String> dataMap = convertEntityData(entity);
|
|
|
+ PdfPTable table = new PdfPTable(2);
|
|
|
+ table.setWidthPercentage(90);
|
|
|
+ table.setHorizontalAlignment(Element.ALIGN_CENTER);
|
|
|
+ table.setSpacingBefore(10);
|
|
|
+ table.setSpacingAfter(20);
|
|
|
+ table.setWidths(new float[]{1f, 2f});
|
|
|
+
|
|
|
+ for (Map.Entry<String, String> entry : dataMap.entrySet()) {
|
|
|
+ PdfPCell keyCell = new PdfPCell(new Phrase(entry.getKey(), tableKeyFont));
|
|
|
+ keyCell.setPadding(8);
|
|
|
+ keyCell.setBackgroundColor(new BaseColor(240, 240, 240));
|
|
|
+ keyCell.setBorder(Rectangle.BOX);
|
|
|
+
|
|
|
+ PdfPCell valueCell = new PdfPCell(new Phrase(entry.getValue(), tableValueFont));
|
|
|
+ valueCell.setPadding(8);
|
|
|
+ valueCell.setBorder(Rectangle.BOX);
|
|
|
+
|
|
|
+ table.addCell(keyCell);
|
|
|
+ table.addCell(valueCell);
|
|
|
+ }
|
|
|
+ document.add(table);
|
|
|
+
|
|
|
+
|
|
|
+ //-----------生成图片------------------
|
|
|
+ if (entity.getDstFilePath() != null) {
|
|
|
+ try {
|
|
|
+ document.newPage();
|
|
|
+ Paragraph pageBreakPlaceholder = new Paragraph(" ", tableValueFont);
|
|
|
+ pageBreakPlaceholder.setSpacingAfter(10);
|
|
|
+ document.add(pageBreakPlaceholder);
|
|
|
+ Image image = loadImageFromUrl(entity.getDstFilePath());
|
|
|
+ if (image != null) {
|
|
|
+ scaleImageToFitPage(image, document);
|
|
|
+ image.setAlignment(Element.ALIGN_CENTER);
|
|
|
+ document.add(image);
|
|
|
+ } else {
|
|
|
+ Paragraph errorMsg = new Paragraph("无法加载处方图片", msgFont);
|
|
|
+ errorMsg.setAlignment(Element.ALIGN_CENTER);
|
|
|
+ document.add(errorMsg);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ Paragraph errorMsg = new Paragraph("图片处理失败: " + e.getMessage(), msgFont);
|
|
|
+ errorMsg.setAlignment(Element.ALIGN_CENTER);
|
|
|
+ document.add(errorMsg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //------------聊天页-----------------
|
|
|
+ document.newPage();
|
|
|
+
|
|
|
+ Paragraph msgTitle = new Paragraph("沟通记录", subTitleFont);
|
|
|
+ msgTitle.setAlignment(Element.ALIGN_CENTER);
|
|
|
+ msgTitle.setSpacingAfter(20);
|
|
|
+ document.add(msgTitle);
|
|
|
+
|
|
|
+ // 对话框表格
|
|
|
+ PdfPTable chatTable = new PdfPTable(1);
|
|
|
+ chatTable.setWidthPercentage(90);
|
|
|
+ chatTable.setHorizontalAlignment(Element.ALIGN_CENTER);
|
|
|
+ chatTable.setSpacingAfter(20);
|
|
|
+
|
|
|
+ if (messageList.isEmpty()) {
|
|
|
+ PdfPCell emptyCell = new PdfPCell(new Phrase("无沟通记录", msgFont));
|
|
|
+ emptyCell.setBorder(Rectangle.NO_BORDER);
|
|
|
+ emptyCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
|
|
+ emptyCell.setPadding(10);
|
|
|
+ chatTable.addCell(emptyCell);
|
|
|
+ } else {
|
|
|
+ for (ChatMessageVo msg : messageList) {
|
|
|
+ PdfPCell msgCell = new PdfPCell();
|
|
|
+ msgCell.setBorder(Rectangle.NO_BORDER);
|
|
|
+ msgCell.setPadding(5);
|
|
|
+
|
|
|
+ Paragraph content = new Paragraph();
|
|
|
+ String sender = msg.getTarget() == 2 ? "医生" : "患者";
|
|
|
+ Chunk senderChunk = new Chunk("【" + sender + "】", new Font(baseFont, 10, Font.BOLD, BaseColor.DARK_GRAY));
|
|
|
+ content.add(senderChunk);
|
|
|
+
|
|
|
+ Chunk timeChunk = new Chunk(":" + timestampToDate(msg.getCreatedTime()) + "\n\n", new Font(baseFont, 10, Font.NORMAL, new BaseColor(100, 100, 100)));
|
|
|
+ content.add(timeChunk);
|
|
|
+
|
|
|
+ //type=3的药品信息
|
|
|
+ String msgContent;
|
|
|
+ if (msg.getType() == 3) {
|
|
|
+ msgContent = parseMedicineContent(msg.getContent());
|
|
|
+ } else {
|
|
|
+ msgContent = msg.getContent().replaceAll("<br />", "\n").replaceAll("<br>", "\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ Chunk contentChunk = new Chunk(msgContent, msgFont);
|
|
|
+ content.add(contentChunk);
|
|
|
+
|
|
|
+ if (msg.getTarget() == 2) {
|
|
|
+ msgCell.setHorizontalAlignment(Element.ALIGN_LEFT);
|
|
|
+ msgCell.setBackgroundColor(new BaseColor(245, 245, 245));
|
|
|
+ } else {
|
|
|
+ msgCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
|
|
|
+ msgCell.setBackgroundColor(new BaseColor(220, 235, 255));
|
|
|
+ }
|
|
|
+
|
|
|
+ msgCell.setPadding(10);
|
|
|
+ msgCell.setBorderWidth(1);
|
|
|
+ msgCell.setBorderColor(BaseColor.LIGHT_GRAY);
|
|
|
+ msgCell.setPhrase(content);
|
|
|
+ chatTable.addCell(msgCell);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ document.add(chatTable);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, String> convertEntityData(Hospital580PrescriptionScrmEntity entity) {
|
|
|
+ Map<String, String> dataMap = new HashMap<>(20);
|
|
|
+ dataMap.put("处方编号", StringUtils.defaultString(entity.getSerialNo(), "无"));
|
|
|
+ dataMap.put("用户ID", entity.getUserId() == null ? "无" : entity.getUserId().toString());
|
|
|
+ dataMap.put("创建时间", entity.getCreateTime() == null ? "无" : entity.getCreateTime().format(DATE_FORMAT));
|
|
|
+ dataMap.put("就诊人姓名", StringUtils.defaultString(entity.getUserFamilyName(), "无"));
|
|
|
+ dataMap.put("性别", convertGender(entity.getUserFamilyGender()));
|
|
|
+ dataMap.put("年龄", entity.getUserFamilyAge() == null ? "无" : entity.getUserFamilyAge().toString());
|
|
|
+ dataMap.put("身份证号", StringUtils.defaultString(entity.getUserFamilyIdCard(), "无"));
|
|
|
+ dataMap.put("联系电话", StringUtils.defaultString(entity.getUserFamilyPhone(), "无"));
|
|
|
+ dataMap.put("家庭住址", StringUtils.defaultString(entity.getUserFamilyAddr(), "无"));
|
|
|
+ dataMap.put("医生姓名", StringUtils.defaultString(entity.getDoctorName(), "无"));
|
|
|
+ dataMap.put("所属科室", StringUtils.defaultString(entity.getDoctorOffice(), "无"));
|
|
|
+ dataMap.put("开方时间", entity.getCreatedTime() == null ? "无" : entity.getCreatedTime().format(DATE_FORMAT));
|
|
|
+ dataMap.put("服务类型", convertServiceType(entity.getServiceType()));
|
|
|
+ dataMap.put("处方状态", convertStatus(entity.getStatus()));
|
|
|
+ dataMap.put("审核状态", convertAuditStatus(entity.getAuditStatus()));
|
|
|
+ dataMap.put("审方药师", StringUtils.defaultString(entity.getAuditApothecaryName(), "无"));
|
|
|
+ dataMap.put("医院名称", StringUtils.defaultString(entity.getHospitalName(), "无"));
|
|
|
+ dataMap.put("药店名称", StringUtils.defaultString(entity.getStoreName(), "无"));
|
|
|
+ dataMap.put("诊断标签", StringUtils.defaultString(entity.getTags(), "无"));
|
|
|
+ return dataMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String convertGender(Byte gender) {
|
|
|
+ return gender == null ? "无" : (gender == 1 ? "男" : "女");
|
|
|
+ }
|
|
|
+
|
|
|
+ private String convertServiceType(Byte type) {
|
|
|
+ return type == null ? "无" : (type == 0 ? "图文" : "视频");
|
|
|
+ }
|
|
|
+
|
|
|
+ private String convertStatus(Byte status) {
|
|
|
+ return status == null ? "无" : (status == 1 ? "正常" : "已作废");
|
|
|
+ }
|
|
|
+
|
|
|
+ private String convertAuditStatus(Integer status) {
|
|
|
+ if (status == null) return "无";
|
|
|
+ switch (status) {
|
|
|
+ case 1: return "待审核";
|
|
|
+ case 2: return "审核通过";
|
|
|
+ case 3: return "审核不通过";
|
|
|
+ default: return "未知";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String timestampToDate(Long timestamp) {
|
|
|
+ Date date = new Date(timestamp);
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
|
|
+ sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
|
|
|
+ return sdf.format(date);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析type=3的药品信息JSON字符串,返回格式化文本
|
|
|
+ */
|
|
|
+ private String parseMedicineContent(String content) {
|
|
|
+ try {
|
|
|
+ cn.hutool.json.JSONObject jsonObj = JSONUtil.parseObj(content);
|
|
|
+ cn.hutool.json.JSONArray medicineList = jsonObj.getJSONArray("list");
|
|
|
+
|
|
|
+ if (medicineList.isEmpty()) {
|
|
|
+ return "无具体药品信息";
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuilder sb = new StringBuilder("处方药品清单:\n");
|
|
|
+ for (int i = 0; i < medicineList.size(); i++) {
|
|
|
+ cn.hutool.json.JSONObject medicine = medicineList.getJSONObject(i);
|
|
|
+ String productName = medicine.getStr("productName", "未知药品");
|
|
|
+ String spec = medicine.getStr("spec", "无");
|
|
|
+ int number = medicine.getInt("number", 0);
|
|
|
+ String usageHowto = medicine.getStr("usageHowto", "无");
|
|
|
+ String frequency = medicine.getStr("frequency", "无");
|
|
|
+ String packingSpec= medicine.getStr("packingSpec", "无");
|
|
|
+ String perDosageNumber = medicine.getStr("perDosageNumber", "无");
|
|
|
+ String perDosageUnit = medicine.getStr("perDosageUnit", "无");
|
|
|
+
|
|
|
+ sb.append(String.format("%d. %s\n", i + 1, productName + "(" +packingSpec+")"));
|
|
|
+ sb.append(String.format(" 规格用法:%s\n", spec));
|
|
|
+ sb.append(String.format(" 服用方式:%s,%s\n", usageHowto, frequency));
|
|
|
+ sb.append(String.format(" 数量:%d盒\n", number));
|
|
|
+ sb.append(String.format(" 每次:%s%s\n", perDosageNumber, perDosageUnit));
|
|
|
+ sb.append("\n");
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return "药品信息解析失败";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //图片调整
|
|
|
+ private void scaleImageToFitPage(Image image, Document document) {
|
|
|
+ float pageWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin();
|
|
|
+ float pageHeight = document.getPageSize().getHeight() - document.topMargin() - document.bottomMargin() - 50; // 预留50pt空白,避免贴边
|
|
|
+ float imageWidth = image.getWidth();
|
|
|
+ float imageHeight = image.getHeight();
|
|
|
+
|
|
|
+ float widthRatio = pageWidth / imageWidth;
|
|
|
+ float heightRatio = pageHeight / imageHeight;
|
|
|
+ float scaleRatio = Math.min(widthRatio, heightRatio);
|
|
|
+
|
|
|
+ if (scaleRatio < 1.0) {
|
|
|
+ image.scalePercent(scaleRatio * 100);
|
|
|
+ }
|
|
|
+ image.setAlignment(Image.ALIGN_CENTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ *URL加载图片
|
|
|
+ */
|
|
|
+ private Image loadImageFromUrl(String imageUrl) throws IOException, BadElementException {
|
|
|
+ try {
|
|
|
+ URL url = new URL(imageUrl);
|
|
|
+ URLConnection connection = url.openConnection();
|
|
|
+ connection.setConnectTimeout(10000);
|
|
|
+ connection.setReadTimeout(30000);
|
|
|
+
|
|
|
+ try (InputStream inputStream = connection.getInputStream()) {
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
+ byte[] buffer = new byte[4096];
|
|
|
+ int bytesRead;
|
|
|
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
|
+ outputStream.write(buffer, 0, bytesRead);
|
|
|
+ }
|
|
|
+ byte[] imageBytes = outputStream.toByteArray();
|
|
|
+ return Image.getInstance(imageBytes);
|
|
|
+ }
|
|
|
+ } catch (MalformedURLException e) {
|
|
|
+ throw new IOException("图片URL格式错误: " + imageUrl, e);
|
|
|
+ } catch (SocketTimeoutException e) {
|
|
|
+ throw new IOException("图片下载超时: " + imageUrl, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new IOException("无法加载图片: " + imageUrl, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|