检验报告改A5

This commit is contained in:
2025-11-07 17:37:20 +08:00
parent 7c2df09c72
commit 32142d948d
3 changed files with 303 additions and 83 deletions

View File

@@ -17,35 +17,15 @@ import com.dpkj.modules.print.enums.MS439TonerStatusEnum;
import com.dpkj.modules.print.request.MS439Request;
import com.dpkj.modules.print.service.MS439PrintService;
import com.dpkj.modules.print.utils.FolderUtils;
import com.dpkj.modules.print.utils.FontLoader;
import com.dpkj.modules.print.utils.PDFUtils;
import com.dpkj.modules.print.utils.TemplateUtil;
import com.dpkj.modules.print.vo.PrinterParam;
import com.dpkj.modules.print.vo.PrinterStatus;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -244,9 +224,9 @@ public class MS439PrintServiceImpl implements MS439PrintService {
// 2. 准备模板数据
Map<String, Object> data = prepareReportData(params);
// 3. 渲染Thymeleaf模板
String htmlContent = renderThymeleafTemplate(data);
String htmlContent = TemplateUtil.renderThymeleafTemplate(data, "reportJY");
// 4. 生成PDF文件
createPdfFromHtml(htmlContent, pdfPath);
TemplateUtil.createPdfFromHtml(htmlContent, pdfPath);
return pdfPath;
} catch (Exception e) {
@@ -275,74 +255,61 @@ public class MS439PrintServiceImpl implements MS439PrintService {
}
}
private void createPdfFromHtml(String html, String outputPath)
throws IOException, DocumentException {
// 大小为A4纸
Document document = new Document(PageSize.A4);
try (OutputStream os = Files.newOutputStream(Paths.get(outputPath))) {
PdfWriter writer = PdfWriter.getInstance(document, os);
document.open();
// 加载自定义字体
BaseFont simfangFont = FontLoader.loadFont("/font/simfang.ttf");
// 创建自定义字体提供器
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider() {
@Override
public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
return new Font(simfangFont, size, style, color);
}
};
// 解析 HTML 并生成 PDF
XMLWorkerHelper.getInstance().parseXHtml(
writer, document,
new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8)),
null, StandardCharsets.UTF_8,
fontProvider // 注入字体提供器
);
document.close(); // 这里关闭,确保 OutputStream 还没被关闭
}
}
// 模板渲染方法(保持不变)
private String renderThymeleafTemplate(Map<String, Object> data) {
// 设置模板数据
Context context = new Context();
context.setVariables(data);
// 填充数据到html
TemplateEngine templateEngine = new TemplateEngine();
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(true);
resolver.setApplicationContext(new AnnotationConfigReactiveWebApplicationContext());
templateEngine.setTemplateResolver(resolver);
return templateEngine.process("reportJY", context);
}
// 报告数据准备(根据实际业务实现)
private Map<String, Object> prepareReportData(JSONObject params) {
public static Map<String, Object> prepareReportData(JSONObject params) {
Map<String, Object> data = new HashMap<>();
// 基本信息
Map<String, String> baseInfo = new HashMap<>();
baseInfo.put("reportId", params.getString("reportId"));// 报告id(标本编号)
baseInfo.put("lspcmName", params.getString("lspcmName"));// 标本名称
// 报告信息
JSONObject printInfo = params.getJSONObject("print_info");
baseInfo.put("reportId", printInfo.getString("reportId"));// 报告id(标本编号)
baseInfo.put("lspcmName", printInfo.getString("lspcmName"));// 标本名称
baseInfo.put("citemContent", printInfo.getString("citemContent"));// 申请内容
baseInfo.put("examTime", printInfo.getString("examTime")); // 检验时间
baseInfo.put("applyTime", printInfo.getString("applyTime"));// 申请时间
baseInfo.put("spcmClctTime", printInfo.getString("spcmClctTime")); // 采集时间
baseInfo.put("reportTime", printInfo.getString("reportTime")); // 报告时间
baseInfo.put("chkTime", printInfo.getString("reportTime")); // 报告审核时间
// 患者信息
JSONObject patInfo = params.getJSONObject("pat_info");
baseInfo.put("patName", patInfo.getString("pat_name"));// 姓名
baseInfo.put("patSex", patInfo.getString("pat_sex"));// 性别
baseInfo.put("patAge", patInfo.getString("pat_age"));// 年龄
baseInfo.put("patHomePhno", patInfo.getString("pat_home_phno"));// 电话
baseInfo.put("idno", patInfo.getString("idno"));// 身份证号
baseInfo.put("outpno", patInfo.getString("outpno"));// 门诊号
baseInfo.put("inpno", patInfo.getString("inpno"));// 住院号
baseInfo.put("contactsPhno", patInfo.getString("contacts_phno"));// 电话
// 就诊信息
JSONObject pv1Info = params.getJSONObject("pv1_info");
baseInfo.put("deptName", pv1Info.getString("dept_name"));// 科室
baseInfo.put("inpWardName", pv1Info.getString("inp_ward_name"));// 病区名称
baseInfo.put("inpBedNo", pv1Info.getString("inp_bed_no"));// 床号
baseInfo.put("rgstNo", pv1Info.getString("rgst_no"));// 挂号单号
String patVisitType = pv1Info.getString("pat_visit_type");
if (StrUtil.isNotEmpty(patVisitType)) {
if (StrUtil.equals("1", patVisitType)) {
patVisitType = "门诊";
} else if (StrUtil.equals("2", patVisitType)) {
patVisitType = "住院";
} else if (StrUtil.equals("4", patVisitType)) {
patVisitType = "体检";
}
}
baseInfo.put("patVisitType", patVisitType);// 就诊来源
JSONObject applyInfoFirst = params.getJSONArray("apply_info").getJSONObject(0);
baseInfo.put("placerName", applyInfoFirst.getString("placer_name"));// 申请医生
baseInfo.put("placerName", applyInfoFirst.getString("placer_name"));// 开单医生
baseInfo.put("spcmClctor", applyInfoFirst.getString("spcm_clctor"));// 标本采集人
baseInfo.put("citemContent", params.getString("citemContent"));// 申请项目
// baseInfo.put("applyTime", params.getString("apply_time"));// 开单时间
// baseInfo.put("citemContent", params.getString("citemContent"));// 申请项目
data.put("baseInfo", baseInfo);
// 检验项目数据
@@ -369,8 +336,13 @@ public class MS439PrintServiceImpl implements MS439PrintService {
return data;
}
private void addItem(List<Map<String, Object>> items, String name,
String value, String range, String unit, String method, String oaflag) {
public static void addItem(List<Map<String, Object>> items,
String name,
String value,
String range,
String unit,
String method,
String oaflag) {
Map<String, Object> item = new HashMap<>();
item.put("loitemCname", name);// 项目名称
item.put("orderRptResult", value);// 结果

View File

@@ -0,0 +1,100 @@
package com.dpkj.modules.print.utils;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
/**
* @Auther: 萧道子
* @Date: 2025/11/7 14:50
* @Description:
*/
public class TemplateUtil {
/**
* 渲染Thymeleaf模板
*
* @param data : 数据
* @param fileName : 模板文件名
* @return java.lang.String
* @author 萧道子 2025/11/7
*/
public static String renderThymeleafTemplate(Map<String, Object> data, String fileName) {
// 设置模板数据
Context context = new Context();
context.setVariables(data);
// 填充数据到html
TemplateEngine templateEngine = new TemplateEngine();
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(true);
resolver.setApplicationContext(new AnnotationConfigReactiveWebApplicationContext());
templateEngine.setTemplateResolver(resolver);
return templateEngine.process(fileName, context);
}
public static void createPdfFromHtml(String html, String outputPath) throws IOException, DocumentException {
createPdfFromHtml(html, outputPath, PageSize.A4);
}
/**
* 将Html转为图片
*
* @param html :
* @param outputPath :
* @return void
* @author 萧道子 2025/11/7
*/
public static void createPdfFromHtml(String html, String outputPath, Rectangle size) throws IOException, DocumentException {
// 大小为A4纸
Document document = new Document(size);
try (OutputStream os = Files.newOutputStream(Paths.get(outputPath))) {
PdfWriter writer = PdfWriter.getInstance(document, os);
document.open();
// 加载自定义字体
BaseFont simfangFont = FontLoader.loadFont("/font/simfang.ttf");
// 创建自定义字体提供器
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider() {
@Override
public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color) {
return new Font(simfangFont, size, style, color);
}
};
// 解析 HTML 并生成 PDF
XMLWorkerHelper.getInstance().parseXHtml(
writer, document,
new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8)),
null, StandardCharsets.UTF_8,
fontProvider // 注入字体提供器
);
document.close(); // 这里关闭,确保 OutputStream 还没被关闭
}
}
}

View File

@@ -0,0 +1,148 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>检验报告单</title>
</head>
<body>
<div style="font-size: 12px;">
<!-- 医院标题 -->
<div style="text-align: center;">
<div style="font-size: 14px; font-weight: bold;">
澜沧拉祜族自治县中医医院<span th:text="${baseInfo.lspcmName}"></span>检验单
</div>
</div>
<!--分隔-->
<div style="height: 10px;"></div>
<div style="background-color: #000;height: 1px;"></div>
<div style="height: 5px;"></div>
<!-- 患者基本信息区域 - 使用表格布局以确保兼容 -->
<table style="width: 100%;font-size: 12px;">
<!-- 第1行2个字段 -->
<tr>
<td style="width: 33%;font-weight: bold;">
<span>患者姓名:</span>
<span th:text="${baseInfo.patName}"></span>
</td>
<td style="width: 33%;font-weight: bold;">
<span>患者性别:</span>
<span th:text="${baseInfo.patSex}"></span>
</td>
<td style="width: 33%;font-weight: bold;">
<span>患者年龄:</span>
<span th:text="${baseInfo.patAge}"></span>
</td>
</tr>
<!-- 第2行3个字段 -->
<tr>
<td style="width: 33%;">
<span>患者电话:</span>
<span th:text="${baseInfo.contactsPhno}"></span>
</td>
<td style="width: 66%;"
colspan="3">
<span>身份证号:</span>
<span th:text="${baseInfo.idno}"></span>
</td>
</tr>
<tr>
<td style="width: 33%;">
<span>就诊来源:</span>
<span th:text="${baseInfo.patVisitType}"></span>
</td>
<td style="width: 33%;">
<span>就诊科室:</span>
<span th:text="${baseInfo.deptName}"></span>
</td>
<td style="width: 33%;">
<span>挂号单号:</span>
<span th:text="${baseInfo.rgstNo}"></span>
</td>
</tr>
<tr>
<td style="width: 33%;">
<span>门诊编号:</span>
<span th:text="${baseInfo.outpno}"></span>
</td>
<td style="width: 33%;">
<span>住院编号:</span>
<span th:text="${baseInfo.inpno}"></span>
</td>
<td style="width: 33%;">
<span>住院床号:</span>
<span th:text="${baseInfo.inpBedNo}"></span>
</td>
</tr>
<tr>
<td style="width: 33%;">
<div>申请时间:</div>
<div style="padding-top: 3px;"
th:text="${baseInfo.applyTime}"></div>
</td>
<td style="width: 33%;">
<div>检验时间:</div>
<div style="padding-top: 3px;"
th:text="${baseInfo.examTime}"></div>
</td>
<td style="width: 33%; ">
<span>申请项目:</span>
<span th:text="${baseInfo.citemContent}"></span>
</td>
</tr>
</table>
<!--分隔-->
<div style="height: 10px;"></div>
<!-- 检测结果表格 -->
<table style="border-collapse:collapse;width: 100%;border:1px solid gray;font-size: 12px;">
<thead style="text-align: center;">
<tr style="background-color: #f0f0f0;height:30px;">
<th style="padding: 8px 5px; width: 25%;">项目名称</th>
<th style="padding: 8px 5px; width: 20%;">结果</th>
<th style="padding: 8px 5px; width: 15%;">单位</th>
<th style="padding: 8px 5px; width: 20%;">参考区间</th>
<th style="padding: 8px 5px; width: 20%;">测试方法</th>
</tr>
</thead>
<tbody style="text-align: center;">
<!-- 动态行渲染+异常值红色高亮 -->
<tr th:each="item, stat : ${items}"
th:style="${stat.index % 2 == 0} ? '' : 'background-color: #f0f0f0;'">
<td style="padding: 8px 5px;"
th:text="${item.loitemCname}"></td>
<td style="padding: 8px 5px;">
<span th:text="${item.orderRptResult}"></span>
<span th:if="${item.oaflagLow}"></span>
<span th:if="${item.oaflagHigh}"></span>
</td>
<td style="padding: 8px 5px;"
th:text="${item.loitemUnit}"></td>
<td style="padding: 8px 5px;"
th:text="${item.loitemRv}"></td>
<td style="padding: 8px 5px;"
th:text="${item.inspectionMethod}"></td>
</tr>
<!-- 补齐空行 -->
<!-- <tr th:each="i : ${#numbers.sequence(1, 10 - items.size())}" th:if="${items.size()} < 10">
<td style="padding: 8px 5px;">&nbsp;</td>
<td style="padding: 8px 5px;">&nbsp;</td>
<td style="padding: 8px 5px;">&nbsp;</td>
<td style="padding: 8px 5px;">&nbsp;</td>
<td style="padding: 8px 5px;">&nbsp;</td>
</tr>-->
</tbody>
</table>
<!-- 建议与解释 -->
<!--<div>
<div style="padding-top: 10px;font-size: 12px;">
建议与解释:
</div>
</div>-->
</div>
</body>
</html>