最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot結(jié)合thymeleaf模板生成pdf文件的代碼示例

 更新時(shí)間:2026年07月07日 09:40:42   作者:愛(ài)碼猿  
Thymeleaf作為SpringBoot官方推薦的模板引擎,采用自然模板設(shè)計(jì)理念,其三層處理管道實(shí)現(xiàn)了HTML5的優(yōu)雅處理,這篇文章主要介紹了Springboot結(jié)合thymeleaf模板生成pdf文件的相關(guān)資料,需要的朋友可以參考下

1. 導(dǎo)入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
    <groupId>com.github.librepdf</groupId>
    <artifactId>openpdf</artifactId>
    <version>1.3.30</version>
</dependency>

<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf-openpdf</artifactId>
    <version>9.4.0</version>
</dependency>

2. 創(chuàng)建Html模板

在resource/template下創(chuàng)建模板文件,需要注意的是樣式必須寫在style標(biāo)簽內(nèi),不能寫在標(biāo)簽內(nèi)部,否則無(wú)效。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
<head>
    <meta charset="UTF-8"/>
    <title>處方簽</title>
    <style>
    @page {
        size: 148mm 210mm;
        margin: 0;
    }
    
    body {
        font-family: "SimSun", sans-serif;
        font-size: 13px;
        line-height: 1.2;
        margin: 0;
        padding: 5mm;
        width: 138mm;
        height: 200mm;
        -fs-pdf-font-embed: embed;
        -fs-pdf-font-encoding: Identity-H;
    }
        
        .prescription-header {
            text-align: center;
            margin-bottom: 8mm;
            font-size: 24px;
            font-family: "KaiTi", sans-serif;
            font-weight: bold;
        }
        .hospital-name {
            margin-bottom: 1mm;
        }

        .rp-header {
            font-size: 26px;
            margin: 2mm 0;
        }
        
        .medicine-table {
            width: 100%;
            border-collapse: collapse;
            margin: 2mm 0;
        }
        
        .medicine-table th,
        .medicine-table td {
            padding: 0.5mm;
            text-align: left;
        }
        
        .medicine-table th {
            font-weight: bold;
        }
        

        .signature-area {
            margin-top: 4mm;
            padding-top: 2mm;
        }
        
        .signature-table {
            width: 100%;
            border-collapse: collapse;
        }
        
        .signature-table td {
            text-align: left;
            vertical-align: top;
            padding: 1mm;
        }
        
        .signature-title {
            margin-bottom: 2mm;
        }

        .signature-image {
            width: 40px;
            height: 20px;
        }
        
        .dynamic-field {
            font-weight: normal;
        }

        .border-bottom{
            border-bottom: 1px solid #000;
            margin-bottom: 1mm;
            padding-bottom: 1mm;
        }
        .margin-top2{
            margin-top: 2mm;
        }

        .margin-left20 {
            margin-left: 20px;
        }

    .margin-left60 {
        margin-left: 60px;
    }
    .thw{
        width: 100px;
    }
    </style>
</head>
<body>
    <div class="prescription-header">
        <div class="hospital-name" th:text="${prescriptionInfo.hospitalName}">中山大學(xué)附屬第三醫(yī)院肇慶醫(yī)院</div>
        <div class="prescription-title">互聯(lián)網(wǎng)醫(yī)院處方箋</div>
    </div>
    
    <div>
        <div class="border-bottom">
            <span>費(fèi)別:</span><span th:text="${prescriptionInfo.paymentType}">自費(fèi)</span>
            <span class="margin-left20">醫(yī)??ㄌ?hào):</span><span class="txt-label-end" th:text="${prescriptionInfo.medicalInsuranceNo}">XXXXXXXXXX</span>
            <span class="margin-left20">處方編號(hào):</span><span class="txt-label-end" th:text="${prescriptionInfo.prescriptionNo}">XXXXXXXXXXXX</span>
        </div>

        <div>
            <span>姓名:</span><span class="txt-label-end" th:text="${patientInfo.name}">張三三</span>
            <span class="margin-left60">性別:</span><span class="txt-label-end" th:text="${patientInfo.gender}">男</span>
            <span class="margin-left60">年齡:</span><span class="txt-label-end" th:text="${patientInfo.age+ '歲'}">40歲</span>
            


            <div class="margin-top2">
                <span>門診號(hào):</span>
                <span class="txt-label-end" th:text="${patientInfo.medicalRecordNo}">XXXXXXXXXXXXX</span>
                <span class="margin-left60">科別:</span>
                <span class="txt-label-end" th:text="${prescriptionInfo.department}">內(nèi)科互聯(lián)網(wǎng)醫(yī)院門診</span>
            </div>

            <div class="margin-top2">
                <span>臨床診斷:</span><span th:text="${prescriptionInfo.clinicalDiagnosis}">發(fā)燒,肺炎,肺部感染</span>
            </div>


            <div class="margin-top2">
                <span>開(kāi)具日期:</span><span th:text="${prescriptionInfo.issueDate}">2026-01-12 19:11:01</span>
            </div>


            <div class="margin-top2 border-bottom">
                <span>住址/電話:</span><span th:text="${patientInfo.address + ' / ' + patientInfo.phone}">廣東省廣州市天河區(qū)鳳凰街道1號(hào) / 1300000000</span>
            </div>
        </div>
    </div>
    
    <div class="rp-header">Rp</div>
    
    <table class="medicine-table">
        <thead>
            <tr>
                <th>藥品名稱</th>
                <th>規(guī)格</th>
                <th>單次劑量</th>
                <th>用法</th>
                <th>頻次</th>
                <th>總量</th>
                <th>單價(jià)(元)</th>
                <th>總價(jià)(元)</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="item : ${medicineItems}">
                <td class="dynamic-field thw" th:text="${item.medicineName}">(基)氯硝西泮片(氯硝安定)(恩華)(精二)(甲)2mgx100片/瓶</td>
                <td class="dynamic-field" th:text="${item.specification}">228mg * 24粒</td>
                <td class="dynamic-field" th:text="${item.dosage}">2粒</td>
                <td class="dynamic-field" th:text="${item.usage}">隨餐服用</td>
                <td class="dynamic-field" th:text="${item.frequency}">每天三次</td>
                <td class="dynamic-field" th:text="${item.quantity}">4盒</td>
                <td class="dynamic-field" th:text="${item.unitPrice}">30.96</td>
                <td class="dynamic-field" th:text="${item.totalPrice}">123.84</td>
            </tr>
        </tbody>
    </table>

    
    <div class="signature-area">
        <table class="signature-table">
            <tr>
                <td>
                    <div class="signature-title">
                        <span class="doctor-label">醫(yī)   師:</span>
                        <img th:if="${medicalStaff.physician.signatureImage}" class="signature-image" th:src="'data:image/png;base64,' + ${medicalStaff.physician.signatureImage}" alt=""/>

                    </div>
                </td>
                <td class="signature-title">
                    <span style="margin-left: 60px;">藥品金額(元):</span>
                    <span class="dynamic-field" th:text="${totalAmount}">206.16</span>
                </td>
            </tr>
        </table>

        <table class="signature-table border-bottom">
            <tr>
                <td>
                    <div class="signature-title">
                        審核藥師:
                        <img th:if="${medicalStaff.physician.signatureImage}" class="signature-image" th:src="'data:image/png;base64,' + ${medicalStaff.reviewer.signatureImage}" alt=""/>
                    </div>

                </td>
                <td>
                    <div class="signature-title">
                        配藥藥師:
                        <img th:if="${medicalStaff.physician.signatureImage}" class="signature-image" th:src="'data:image/png;base64,' + ${medicalStaff.dispenser.signatureImage}" alt=""/>
                    </div>
                </td>
                <td>

                    <div class="signature-title">
                        核對(duì)、發(fā)藥藥師:
                        <img th:if="${medicalStaff.physician.signatureImage}" class="signature-image" th:src="'data:image/png;base64,' + ${medicalStaff.issuer.signatureImage}" alt=""/>
                    </div>
                </td>
            </tr>
        </table>
    </div>
    <div>
        <span>※注:處方金額以收費(fèi)時(shí)為準(zhǔn)!</span>
        

        

        <span>藥師提示:</span>
        

        <span> 1.請(qǐng)遵醫(yī)囑服藥;2.請(qǐng)繳費(fèi)后到藥房窗口取藥并點(diǎn)清藥品;3.處方當(dāng)日有效;4.發(fā)出藥品如無(wú)質(zhì)量問(wèn)題不予退換 </span>
    </div>
    
</body>
</html>

3. 渲染模板并轉(zhuǎn)為pdf

package com.chinaunicom.medical.ihm.pub.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chinaunicom.medical.ihm.baseservice.api.dict.models.Base64FileVO;
import com.chinaunicom.medical.ihm.baseservice.service.FileService;
import com.chinaunicom.medical.ihm.core.utils.DesensitizedUtils;
import com.chinaunicom.medical.ihm.obs.healthcare.repository.model.Prescription;
import com.chinaunicom.medical.ihm.pub.mapper.PubPrescriptionMapper;
import com.chinaunicom.medical.ihm.pub.model.oo.*;
import com.chinaunicom.medical.ihm.pub.utils.MetaObjectUtil;
import com.lowagie.text.DocumentException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PrescriptionPdfService {

    private final TemplateEngine templateEngine;
    private final PubPrescriptionMapper pubPrescriptionMapper;
    private final FileService fileService;


    public String generatePrescriptionPdf(@RequestParam Long prescriptionId) {
        try {
            log.info("開(kāi)始生成處方簽PDF ,處方編號(hào):{}", prescriptionId);

            PrescriptionDTO prescriptionDTO = getPrintData(prescriptionId);
            byte[] pdfBytes = generatePrescriptionPdf(prescriptionDTO);

            String pdfFileId = fileService.upload(pdfBytes, prescriptionId + ".pdf", MediaType.APPLICATION_PDF_VALUE, SpringUtil.getApplicationName(), "prescription-pdf", null);
            log.info("處方簽PDF生成成功,處方id:{} 文件id:{},文件大?。簕}字節(jié)", prescriptionId, pdfFileId, pdfBytes.length);

            //設(shè)置文件id
            pubPrescriptionMapper.update(new Prescription(), Wrappers.lambdaUpdate(Prescription.class).set(Prescription::getPdfFileId, pdfFileId).eq(Prescription::getId, prescriptionId));
            return pdfFileId;
        } catch (Exception e) {
            log.error("生成處方簽PDF失敗", e);
        }
        return "";
    }


    public PrescriptionDTO getPrintData(Long prescriptionId){
        //查詢處方
        PrescriptionSlipDTO prescriptionSlipDTO = pubPrescriptionMapper.getPrescriptionSlipInfoById(prescriptionId);
        Validator.validateTrue(Objects.nonNull(prescriptionSlipDTO), "處方不存在");
        //查詢藥品
        List<PrescriptionSlipDrugDTO> prescriptionSlipDrugInfoList = pubPrescriptionMapper.getPrescriptionSlipDrugInfoById(prescriptionId);

        //查詢頻次編碼
        List<FrequencyDict> frequencyDict = MetaObjectUtil.listByCode("Frequency_dict", FrequencyDict.class);
        Map<String, String> freqMap = CollUtil.emptyIfNull(frequencyDict).stream().collect(Collectors.toMap(FrequencyDict::getFrequencyCode, FrequencyDict::getFrequencyName, (oldValue, newValue) -> newValue));

        //查詢用法編碼
        List<DrugUsageDict> drugUsageDict = MetaObjectUtil.listByCode("Drug_usage_code", DrugUsageDict.class);
        Map<String, String> drugUsageMap = CollUtil.emptyIfNull(drugUsageDict).stream().collect(Collectors.toMap(DrugUsageDict::getDrugUsageCode, DrugUsageDict::getDrugUsageName, (oldValue, newValue) -> newValue));

        PrescriptionDTO prescriptionDTO = new PrescriptionDTO();

        //設(shè)置患者信息
        PrescriptionDTO.PatientInfo patientInfo = new PrescriptionDTO.PatientInfo();
        {
            patientInfo.setName(StrUtil.emptyIfNull(prescriptionSlipDTO.getPatientName()));
            if(Objects.nonNull(prescriptionSlipDTO.getPatientGender())){
                patientInfo.setGender(StrUtil.emptyIfNull(prescriptionSlipDTO.getPatientGender().getDesc()));
            }
            patientInfo.setAge(prescriptionSlipDTO.getPatientAge());
            patientInfo.setMedicalRecordNo(prescriptionSlipDTO.getTreatmentNo());
            patientInfo.setAddress(prescriptionSlipDTO.getPatientAddress());
            if(StrUtil.isNotBlank(prescriptionSlipDTO.getPatientPhone())){
                patientInfo.setPhone(DesensitizedUtils.decrypt(prescriptionSlipDTO.getPatientPhone()));
            }
        }

        //設(shè)置處方信息
        PrescriptionDTO.PrescriptionInfo prescriptionInfo = new PrescriptionDTO.PrescriptionInfo();
        {
            //醫(yī)院名稱
            prescriptionInfo.setHospitalName(prescriptionSlipDTO.getHospitalName());
            //費(fèi)別
            if(prescriptionSlipDTO.getTreatmentType() != null){
                prescriptionInfo.setPaymentType(StrUtil.emptyIfNull(prescriptionSlipDTO.getTreatmentType().getDesc()));
            }else {
                prescriptionInfo.setPaymentType("");
            }
            //醫(yī)保卡號(hào)
            prescriptionInfo.setMedicalInsuranceNo("");
            //處方編號(hào)
            prescriptionInfo.setPrescriptionNo(prescriptionSlipDTO.getPrescriptionNo());
            //科室名稱
            prescriptionInfo.setDepartment(prescriptionSlipDTO.getDepartmentName());
            //臨床診斷
            prescriptionInfo.setClinicalDiagnosis(prescriptionSlipDTO.getDiagnoseName());
            //開(kāi)方日期
            prescriptionInfo.setIssueDate(prescriptionSlipDTO.getCreateTime());
        }

        //設(shè)置藥品信息
        List<PrescriptionDTO.MedicineItem> medicineItems = prescriptionSlipDrugInfoList.stream().map(drug -> {
            PrescriptionDTO.MedicineItem info = new PrescriptionDTO.MedicineItem();
            info.setMedicineName(StrUtil.emptyIfNull(drug.getDrugName()));
            info.setSpecification(StrUtil.emptyIfNull(drug.getDrugSpec()));
            //單次劑量
            info.setDosage(StrUtil.join("",drug.getSingleDose(),drug.getDosageUnitCode()));
            //用法
            info.setUsage(StrUtil.emptyIfNull(drugUsageMap.get(drug.getDrugUsageCode())));
            //頻次
            info.setFrequency(StrUtil.emptyIfNull(freqMap.getOrDefault(drug.getFrequencyCode(),drug.getFrequencyCode())));
            info.setQuantity(drug.getAmount());
            info.setUnitPrice(ObjectUtil.defaultIfNull(drug.getPrice(), BigDecimal.ZERO));
            info.setTotalPrice(ObjectUtil.defaultIfNull(drug.getTotalPrice(),BigDecimal.ZERO));
            return info;
        }).toList();

        //設(shè)置患者信息
        prescriptionDTO.setPatientInfo(patientInfo);
        //設(shè)置處方信息
        prescriptionDTO.setPrescriptionInfo(prescriptionInfo);
        //總金額
        prescriptionDTO.setTotalAmount(prescriptionSlipDTO.getTotalAmount());
        prescriptionDTO.setMedicineItems(medicineItems);

        PrescriptionDTO.MedicalStaff medicalStaff = new PrescriptionDTO.MedicalStaff();

        //醫(yī)生簽名
        PrescriptionDTO.StaffInfo physician = new PrescriptionDTO.StaffInfo();
        medicalStaff.setPhysician(physician);
        try {
            Base64FileVO doctorFileBase64 = fileService.getFileBase64(prescriptionSlipDTO.getDoctorFileId(),null,null);
            if(Objects.nonNull(doctorFileBase64)){
                physician.setSignatureImage(doctorFileBase64.getBase64Str());
            }
        } catch (Exception e) {
        }


        //審核藥師簽名
        PrescriptionDTO.StaffInfo reviewer = new PrescriptionDTO.StaffInfo();
        medicalStaff.setReviewer(reviewer);
        try {
            if(StrUtil.isNotBlank(prescriptionSlipDTO.getAuditFileId())){
                Base64FileVO reViewFileBase64 = fileService.getFileBase64(prescriptionSlipDTO.getAuditFileId(),null,null);
                if(Objects.nonNull(reViewFileBase64)){
                    reviewer.setSignatureImage(reViewFileBase64.getBase64Str());
                }
            }
        } catch (Exception e) {
        }

        PrescriptionDTO.StaffInfo dispenser = new PrescriptionDTO.StaffInfo();
        dispenser.setSignatureImage("");
        medicalStaff.setDispenser(dispenser);

        PrescriptionDTO.StaffInfo issuer = new PrescriptionDTO.StaffInfo();
        issuer.setSignatureImage("");
        medicalStaff.setIssuer(issuer);

        prescriptionDTO.setMedicalStaff(medicalStaff);
        return prescriptionDTO;
    }


    /**
     * 生成處方簽PDF
     * @param prescriptionDTO 處方數(shù)據(jù)
     * @return PDF字節(jié)數(shù)組
     */
    public byte[] generatePrescriptionPdf(PrescriptionDTO prescriptionDTO) {
        try {
            // 渲染HTML模板
            String htmlContent = renderHtmlTemplate(prescriptionDTO);
            // 將HTML轉(zhuǎn)換為PDF
            return convertHtmlToPdf(htmlContent);
        } catch (Exception e) {
            log.error("生成處方簽PDF失敗", e);
            throw new RuntimeException("生成處方簽PDF失敗: " + e.getMessage(), e);
        }
    }

    /**
     * 渲染HTML模板
     * @param prescriptionDTO 處方數(shù)據(jù)
     * @return HTML內(nèi)容
     */
    private String renderHtmlTemplate(PrescriptionDTO prescriptionDTO) {
        Context context = new Context();
        
        // 設(shè)置模板變量
        context.setVariable("patientInfo", prescriptionDTO.getPatientInfo());
        context.setVariable("prescriptionInfo", prescriptionDTO.getPrescriptionInfo());
        context.setVariable("medicineItems", prescriptionDTO.getMedicineItems());
        context.setVariable("medicalStaff", prescriptionDTO.getMedicalStaff());
        context.setVariable("totalAmount", prescriptionDTO.getTotalAmount());
        
        // 渲染模板
        return templateEngine.process("prescription-template", context);
    }

    /**
     * 將HTML轉(zhuǎn)換為PDF
     * @param htmlContent HTML內(nèi)容
     * @return PDF字節(jié)數(shù)組
     */
    private byte[] convertHtmlToPdf(String htmlContent) throws DocumentException, IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        
        try {
            ITextRenderer renderer = new ITextRenderer();
            // 設(shè)置字體支持
            setFonts(renderer);
            
            // 設(shè)置頁(yè)面大小
            renderer.setDocumentFromString(htmlContent);
            renderer.layout();
            
            // 生成PDF
            renderer.createPDF(outputStream);
            
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }
        
        return outputStream.toByteArray();
    }
    
    /**
     * 設(shè)置中文字體支持
     * @param renderer PDF渲染器
     */
    private void setFonts(ITextRenderer renderer) {

        List<String> fontNames = Arrays.asList("simsun.ttc", "simkai.ttf");

        for (String fontName : fontNames) {
            try {
            // 獲取字體文件路徑
            java.net.URL fontUrl = getClass().getClassLoader().getResource("font/"+fontName);
            if (fontUrl != null) {
                //注冊(cè)字體
                log.info("加載字體: {}", fontName);
                renderer.getFontResolver().addFont(fontUrl.getPath(), com.lowagie.text.pdf.BaseFont.IDENTITY_H, com.lowagie.text.pdf.BaseFont.NOT_EMBEDDED);
            }
            } catch (Exception e) {
                log.error("加載字體失敗: {}", e.getMessage());
            }
        }
    }

}

總結(jié) 

到此這篇關(guān)于Springboot結(jié)合thymeleaf模板生成pdf文件的文章就介紹到這了,更多相關(guān)Springboot thymeleaf生成pdf文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

贵定县| 恩平市| 南阳市| 巴南区| 调兵山市| 武定县| 高阳县| 西昌市| 屯门区| 醴陵市| 潢川县| 陇南市| 昭苏县| 三都| 永城市| 贡山| 博野县| 桃源县| 广昌县| 治多县| 黑山县| 北安市| 磐安县| 武胜县| 锡林浩特市| 保山市| 南陵县| 新乡县| 南华县| 康平县| 吉水县| 高唐县| 华亭县| 南宁市| 育儿| 句容市| 彰化市| 邯郸市| 肃宁县| 晋江市| 古丈县|