新增检验报告打印接口
This commit is contained in:
parent
25597dcfc9
commit
97aa3efef6
23
pom.xml
23
pom.xml
|
@ -17,7 +17,7 @@
|
|||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<java.version>11</java.version>
|
||||
<hutool.version>5.8.36</hutool.version>
|
||||
<jna.version>5.17.0</jna.version>
|
||||
<pdfbox.version>3.0.2</pdfbox.version>
|
||||
|
@ -159,6 +159,27 @@
|
|||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
</dependency>
|
||||
<!-- 报告打印转PDF -->
|
||||
<!-- iText 核心 -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- XML Worker (HTML 转 PDF) -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf.tool</groupId>
|
||||
<artifactId>xmlworker</artifactId>
|
||||
<version>5.5.13.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 中文字体支持 -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext-asian</artifactId>
|
||||
<version>5.2.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@ import com.dpkj.common.dto.LexMarkResultDTO;
|
|||
import com.dpkj.common.vo.Result;
|
||||
import com.dpkj.modules.print.request.MS439Request;
|
||||
import com.dpkj.modules.print.service.MS439PrintService;
|
||||
import com.dpkj.modules.print.vo.PrinterStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* MS439控制层,利盟打印机
|
||||
|
@ -24,12 +27,28 @@ public class MS439Controller {
|
|||
private MS439PrintService ms439PrintService;
|
||||
|
||||
@PostMapping("/print")
|
||||
public Result<LexMarkResultDTO<?>> print(@Validated @RequestBody MS439Request request){
|
||||
public Result<LexMarkResultDTO<?>> print(@Validated @RequestBody MS439Request request) {
|
||||
return Result.ok(ms439PrintService.printImage(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验报告打印
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/printJY")
|
||||
public Result<LexMarkResultDTO<?>> printJY() {
|
||||
MS439Request ms439Request = new MS439Request();
|
||||
// 获取检验报告PDF
|
||||
String path = ms439PrintService.getJYPDFPath();
|
||||
ms439Request.setPagesource("A5");
|
||||
ms439Request.setFileDir(path);
|
||||
LexMarkResultDTO<?> result = ms439PrintService.printImage(ms439Request);
|
||||
return Result.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping("/getStatus")
|
||||
public Result<String> print(@RequestParam(defaultValue = "A4") String papersource){
|
||||
public Result<String> print(@RequestParam(defaultValue = "A4") String papersource) {
|
||||
this.ms439PrintService.getStatus(papersource);
|
||||
return Result.ok();
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ package com.dpkj.modules.print.service;
|
|||
import com.dpkj.common.dto.LexMarkResultDTO;
|
||||
import com.dpkj.modules.print.request.MS439Request;
|
||||
import com.dpkj.modules.print.vo.PrinterStatus;
|
||||
import javassist.compiler.Lex;
|
||||
|
||||
public interface MS439PrintService {
|
||||
|
||||
LexMarkResultDTO<?> printImage(MS439Request request);
|
||||
|
||||
LexMarkResultDTO<PrinterStatus> getStatus(String papersource);
|
||||
|
||||
String getJYPDFPath();
|
||||
}
|
||||
|
|
|
@ -6,17 +6,48 @@ import com.dpkj.common.dto.LexMarkDTO;
|
|||
import com.dpkj.common.dto.LexMarkResultDTO;
|
||||
import com.dpkj.common.exception.RRException;
|
||||
import com.dpkj.common.utils.ThirdService;
|
||||
import com.dpkj.modules.print.enums.*;
|
||||
import com.dpkj.modules.print.enums.MS439DeviceStatusEnum;
|
||||
import com.dpkj.modules.print.enums.MS439InkStatusEnum;
|
||||
import com.dpkj.modules.print.enums.MS439MediaStatusEnum;
|
||||
import com.dpkj.modules.print.enums.MS439PaperStatusEnum;
|
||||
import com.dpkj.modules.print.enums.MS439StampStatusEnum;
|
||||
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.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.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 利盟MS439打印机服务
|
||||
|
@ -49,7 +80,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
PrinterStatus ms439 = status.getData();
|
||||
|
||||
// 如果是要盖章,校验盖章机器是否正常
|
||||
if ( request.getStamp() == 1) {
|
||||
if (request.getStamp() == 1) {
|
||||
String deviceExStatus = ms439.getStDeviceExStatus();
|
||||
if (deviceExStatus == null || deviceExStatus.equals("")) {
|
||||
throw new RRException("获取打印机设备状态出问题");
|
||||
|
@ -60,7 +91,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
}
|
||||
String device = devices[1];
|
||||
// 校验盖章机是否正常,除了HEALTHY都抛异常
|
||||
if (!device.equals(MS439StampStatusEnum.HEALTHY.getPrintCode()) ) {
|
||||
if (!device.equals(MS439StampStatusEnum.HEALTHY.getPrintCode())) {
|
||||
throw new RRException(MS439StampStatusEnum.getPCode(device),
|
||||
MS439StampStatusEnum.getMessage(device));
|
||||
}
|
||||
|
@ -72,7 +103,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
lexMarkDTO.setDevName("HtmPrinter");
|
||||
lexMarkDTO.setPluginMethod("exec");
|
||||
|
||||
JSONObject param = new JSONObject();
|
||||
JSONObject param = new JSONObject();
|
||||
|
||||
// printType 选择箱子
|
||||
// param.put("prtData", String.format("PrintType=%d;pagesource=%s;copies=%d;file[0]=%s;stamp=%d;duplex=%d;color=%d;direction=%d",
|
||||
|
@ -104,7 +135,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
lexMarkDTO.setParam(param.toString());
|
||||
LexMarkResultDTO<LexMarkResultDTO.Param> paramLexMarkResultDTO = thirdService.callDevice(lexMarkDTO, LexMarkResultDTO.Param.class);
|
||||
|
||||
if ( paramLexMarkResultDTO.getData().getResult() != 0){
|
||||
if (paramLexMarkResultDTO.getData().getResult() != 0) {
|
||||
throw new RRException(500, paramLexMarkResultDTO.getData().getResult() + "");
|
||||
}
|
||||
|
||||
|
@ -132,7 +163,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
lexMarkDTO.setPluginMethod("exec");
|
||||
|
||||
LexMarkResultDTO<PrinterStatus> status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
|
||||
if ( status.getResult() != 0){
|
||||
if (status.getResult() != 0) {
|
||||
thirdService.open("HtmPrinter", 0);
|
||||
status = thirdService.callDevice(lexMarkDTO, PrinterStatus.class);
|
||||
}
|
||||
|
@ -142,7 +173,7 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
throw new RRException("获取打印机纸张状态出问题");
|
||||
}
|
||||
String[] papers = stPaperEx.split("\\|");
|
||||
if ( papers.length < 2) {
|
||||
if (papers.length < 2) {
|
||||
throw new RRException("打印机纸盒数量不对");
|
||||
}
|
||||
String paperStatus = "";
|
||||
|
@ -154,13 +185,13 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
if (papersource.equals("A5")) {
|
||||
paperStatus = printerConfig.getLevelOne().equals("A5") ? papers[0] : papers[1];
|
||||
}
|
||||
if ( !(paperStatus.equals(MS439PaperStatusEnum.PAPERFULL.getPrintCode()) || paperStatus.equals(MS439PaperStatusEnum.PAPERLOW.getPrintCode()))){
|
||||
if (!(paperStatus.equals(MS439PaperStatusEnum.PAPERFULL.getPrintCode()) || paperStatus.equals(MS439PaperStatusEnum.PAPERLOW.getPrintCode()))) {
|
||||
throw new RRException(500, paperStatus);
|
||||
}
|
||||
|
||||
PrinterStatus ms439 = status.getData();
|
||||
// 校验打印机是否正常,除了HEALTHY都抛异常
|
||||
if (!ms439.getStDeviceStatus().equals(MS439DeviceStatusEnum.HEALTHY.getPrintCode()) ) {
|
||||
if (!ms439.getStDeviceStatus().equals(MS439DeviceStatusEnum.HEALTHY.getPrintCode())) {
|
||||
throw new RRException(500, ms439.getStDeviceStatus());
|
||||
}
|
||||
|
||||
|
@ -170,19 +201,131 @@ public class MS439PrintServiceImpl implements MS439PrintService {
|
|||
}
|
||||
|
||||
// 校验磁带 满或者少才放行
|
||||
if ( !(ms439.getStToner().equals(MS439TonerStatusEnum.TONERFULL.getPrintCode()) || ms439.getStToner().equals(MS439TonerStatusEnum.TONERLOW.getPrintCode()))){
|
||||
if (!(ms439.getStToner().equals(MS439TonerStatusEnum.TONERFULL.getPrintCode()) || ms439.getStToner().equals(MS439TonerStatusEnum.TONERLOW.getPrintCode()))) {
|
||||
throw new RRException(500, ms439.getStToner());
|
||||
}
|
||||
|
||||
// 校验墨盒 满或者少才放行,当前获取为未知
|
||||
if ( false && !(ms439.getStInk().equals(MS439InkStatusEnum.INKFULL.getPrintCode()) || ms439.getStInk().equals(MS439InkStatusEnum.INKLOW.getPrintCode())
|
||||
|| ms439.getStInk().equals(MS439InkStatusEnum.INKUNKNOWN.getPrintCode()))){
|
||||
if (false && !(ms439.getStInk().equals(MS439InkStatusEnum.INKFULL.getPrintCode()) || ms439.getStInk().equals(MS439InkStatusEnum.INKLOW.getPrintCode())
|
||||
|| ms439.getStInk().equals(MS439InkStatusEnum.INKUNKNOWN.getPrintCode()))) {
|
||||
throw new RRException(500, ms439.getStInk());
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJYPDFPath() {
|
||||
// 1. 准备输出路径
|
||||
String dirPath = "D:/TempJYPDF/";
|
||||
File dir = new File(dirPath);// 确保目录存在
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
String fileName = "report_jy_" + System.currentTimeMillis() + ".pdf";
|
||||
String pdfPath = dirPath + fileName;
|
||||
|
||||
try {
|
||||
// 2. 准备模板数据
|
||||
Map<String, Object> data = prepareReportData();
|
||||
// 3. 渲染Thymeleaf模板
|
||||
String htmlContent = renderThymeleafTemplate(data);
|
||||
// 4. 生成PDF文件
|
||||
createPdfFromHtml(htmlContent, pdfPath);
|
||||
|
||||
return pdfPath;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(); // 打印完整异常堆栈
|
||||
throw new RuntimeException("PDF生成失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void createPdfFromHtml(String html, String outputPath)
|
||||
throws IOException, DocumentException {
|
||||
|
||||
// 大小为A5纸
|
||||
Document document = new Document(PageSize.A5);
|
||||
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() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
// 患者信息
|
||||
Map<String, String> patient = new HashMap<>();
|
||||
patient.put("name", "余文财");
|
||||
patient.put("gender", "男");
|
||||
patient.put("age", "25岁");
|
||||
patient.put("medicalRecord", "20240624001");
|
||||
patient.put("department", "内科");
|
||||
patient.put("bedNumber", "A301");
|
||||
data.put("patient", patient);
|
||||
|
||||
// 检验项目数据
|
||||
List<Map<String, Object>> items = new ArrayList<>();
|
||||
addItem(items, "白细胞计数", "7.26", "3.5-9.5", "10^9/L", false);
|
||||
addItem(items, "血红蛋白", "165", "115-150", "g/L", true);
|
||||
// ...其他检验项
|
||||
data.put("items", items);
|
||||
|
||||
// 系统信息
|
||||
data.put("reportTime", new Date());
|
||||
data.put("doctor", "李医师");
|
||||
data.put("reviewer", "王主任");
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void addItem(List<Map<String, Object>> items, String name,
|
||||
String value, String range, String unit, boolean abnormal) {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("name", name);
|
||||
item.put("result", value);
|
||||
item.put("referenceRange", range);
|
||||
item.put("unit", unit);
|
||||
item.put("isAbnormal", abnormal);
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package com.dpkj.modules.print.utils;
|
||||
|
||||
import com.itextpdf.text.pdf.BaseFont;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author 余文财
|
||||
* @description 字体加载
|
||||
* @date 2025.6.24
|
||||
*/
|
||||
public class FontLoader {
|
||||
public static BaseFont loadFont(String resourcePath) {
|
||||
try (InputStream fontStream = FontLoader.class.getResourceAsStream(resourcePath)) {
|
||||
if (fontStream == null) {
|
||||
throw new IllegalArgumentException("字体文件未找到: " + resourcePath);
|
||||
}
|
||||
// 创建支持中文的 BaseFont
|
||||
return BaseFont.createFont(
|
||||
resourcePath,
|
||||
BaseFont.IDENTITY_H,
|
||||
BaseFont.EMBEDDED,
|
||||
false,
|
||||
fontStream.readAllBytes(),
|
||||
null
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("字体加载失败: " + resourcePath, e);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>检验报告单</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<!-- 标题区域 -->
|
||||
<div style="text-align:center; margin-bottom:20px;">
|
||||
<h2 style="color:#003366;">澜沧拉祜族自治县中医医院血常规检验报告单</h2>
|
||||
<p>报告时间: <span th:text="${#dates.format(reportTime, 'yyyy-MM-dd HH:mm')}"></span></p>
|
||||
</div>
|
||||
|
||||
<!-- 患者信息表格 -->
|
||||
<div>
|
||||
<table style="width:100%; border-collapse:collapse;">
|
||||
<tr>
|
||||
<td style="width:15%; padding:5px; border:1px solid #ddd;">姓名</td>
|
||||
<td style="width:35%; padding:5px; border:1px solid #ddd;" th:text="${patient.name}"></td>
|
||||
<td style="width:15%; padding:5px; border:1px solid #ddd;">性别</td>
|
||||
<td style="width:35%; padding:5px; border:1px solid #ddd;" th:text="${patient.gender}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px; border:1px solid #ddd;">年龄</td>
|
||||
<td style="padding:5px; border:1px solid #ddd;" th:text="${patient.age}"></td>
|
||||
<td style="padding:5px; border:1px solid #ddd;">病历号</td>
|
||||
<td style="padding:5px; border:1px solid #ddd;" th:text="${patient.medicalRecord}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:5px; border:1px solid #ddd;">科室</td>
|
||||
<td style="padding:5px; border:1px solid #ddd;" th:text="${patient.department}"></td>
|
||||
<td style="padding:5px; border:1px solid #ddd;">床号</td>
|
||||
<td style="padding:5px; border:1px solid #ddd;" th:text="${patient.bedNumber}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 检测结果表格 -->
|
||||
<table style="width:100%; border-collapse:collapse; margin-top:20px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="border:1px solid #ddd; padding:8px; text-align:center; background-color:#f2f2f2;">检测项目</th>
|
||||
<th style="border:1px solid #ddd; padding:8px; text-align:center; background-color:#f2f2f2;">结果</th>
|
||||
<th style="border:1px solid #ddd; padding:8px; text-align:center; background-color:#f2f2f2;">参考范围</th>
|
||||
<th style="border:1px solid #ddd; padding:8px; text-align:center; background-color:#f2f2f2;">单位</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 动态行渲染+异常值红色高亮 -->
|
||||
<tr th:each="item : ${items}"
|
||||
th:style="${item.isAbnormal} ? 'color:red;' : ''">
|
||||
<td style="border:1px solid #ddd; padding:8px; text-align:center;" th:text="${item.name}"></td>
|
||||
<td style="border:1px solid #ddd; padding:8px; text-align:center;" th:text="${item.result}"></td>
|
||||
<td style="border:1px solid #ddd; padding:8px; text-align:center;" th:text="${item.referenceRange}"></td>
|
||||
<td style="border:1px solid #ddd; padding:8px; text-align:center;" th:text="${item.unit}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 页脚声明 -->
|
||||
<div style="margin-top:30px; text-align:right;">
|
||||
<div>检验医生: <span th:text="${doctor}"></span></div>
|
||||
<div>审核医生: <span th:text="${reviewer}"></span></div>
|
||||
<div>声明: 本报告仅对此标本负责,如有疑问请3日内联系检验科</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue