diff --git a/hlw/drug-info/index.js b/hlw/drug-info/index.js index 083fb0e..8ae227f 100644 --- a/hlw/drug-info/index.js +++ b/hlw/drug-info/index.js @@ -286,6 +286,9 @@ async function addHlwDrugInfo(ctx) { } newDrug.createTime = Date.now(); const res = await db.collection("drug-info").insertOne(newDrug); + if (res && res.insertedId) { + updateHisDrugInfo({ id: res.insertedId.toString() }) + } return { success: true, message: "新增药品成功", id: res.insertedId } } catch (e) { return { success: false, message: e.message || '新增药品失败' } @@ -337,6 +340,7 @@ async function editHlwDrugInfo(ctx) { if (res.matchedCount === 0) { return { success: false, message: "未找到对应药品" } } + updateHisDrugInfo({ id }) return { success: true, message: "更新成功" } } return { success: true, message: "更新成功" } diff --git a/hlw/prescription-pdf/index.js b/hlw/prescription-pdf/index.js index f4a35bc..7c4ff34 100644 --- a/hlw/prescription-pdf/index.js +++ b/hlw/prescription-pdf/index.js @@ -12,9 +12,15 @@ let browserPromise; let activeJobs = 0; const waiters = []; const cachedPdfJobs = new Map(); -const PDF_TEMPLATE_VERSION = "v7"; +const PDF_TEMPLATE_VERSION = "v11"; const MAX_SIGNATURE_IMAGE_BYTES = 1024 * 1024; const SIGNATURE_DOWNLOAD_TIMEOUT_MS = 5000; +const MAX_PRESCRIPTION_PDF_BYTES = 100 * 1024; +const RASTER_PAGE_SIZES = { + // A5 (148mm) 减去左右 8mm 页边距后的实际打印宽度为 132mm。 + A5: { viewport: { width: 500, height: 733, deviceScaleFactor: 1 }, contentWidth: "132mm", contentHeight: "194mm" }, + A4: { viewport: { width: 733, height: 1062, deviceScaleFactor: 1 }, contentWidth: "194mm", contentHeight: "281mm" }, +}; const PRESCRIPTION_SEAL_IMAGE_URL = process.env.CONFIG_PRESCRIPTION_SEAL_IMAGE_URL || "https://hlwyy.hnkhhospital.com/upload-files/other/stamp.png"; @@ -96,7 +102,7 @@ async function getBrowser() { browserPromise = puppeteer.launch({ executablePath: getExecutablePath(), headless: true, - args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"], + args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu"], }); const browser = await browserPromise; browser.on("disconnected", () => { @@ -273,12 +279,12 @@ function buildPrescriptionHtml(record, paperSize = "A5", signatureImages = {}) { 处方笺`; +} + +async function generateCompactPrescriptionPdf(html, paperSize) { + // Chrome 会为中文文本嵌入字体子集;即使没有图片,单页也约 280KB。 + // 将实际打印区域以 96dpi 截图并转为 JPEG,可保留处方可读性且避免字体嵌入。 + const browser = await getBrowser(); + const size = RASTER_PAGE_SIZES[paperSize]; + const rasterPage = await browser.newPage(); + try { + await rasterPage.setViewport(size.viewport); + await rasterPage.setContent(html, { waitUntil: "load" }); + await rasterPage.evaluate(() => document.fonts && document.fonts.ready); + const screenshot = await rasterPage.screenshot({ + type: "jpeg", + quality: 72, + clip: { x: 0, y: 0, width: size.viewport.width, height: size.viewport.height }, + }); + const compactPage = await browser.newPage(); + try { + await compactPage.setContent( + buildRasterPdfHtml(`data:image/jpeg;base64,${Buffer.from(screenshot).toString("base64")}`, paperSize), + { waitUntil: "load" } + ); + const pdf = Buffer.from(await compactPage.pdf({ + format: paperSize, + preferCSSPageSize: true, + printBackground: true, + margin: { top: 0, right: 0, bottom: 0, left: 0 }, + })); + if (pdf.length > MAX_PRESCRIPTION_PDF_BYTES) { + throw new Error(`处方 PDF 压缩后仍超过 ${MAX_PRESCRIPTION_PDF_BYTES} 字节`); + } + return pdf; + } finally { + await compactPage.close().catch(() => {}); + } + } finally { + await rasterPage.close().catch(() => {}); + } +} + async function generatePrescriptionPdf(record, options = {}) { await acquireSlot(); - let page; try { - const browser = await getBrowser(); - page = await browser.newPage(); const [doctor, pharmacist, seal] = await Promise.all([ loadSignatureDataImage(record.doctorSignImg), loadSignatureDataImage(record.pharmacistSignImg), loadSignatureDataImage(PRESCRIPTION_SEAL_IMAGE_URL), ]); - await page.setContent( - buildPrescriptionHtml(record, options.paperSize, { doctor, pharmacist, seal }), - { waitUntil: "load" } - ); - await page.evaluate(() => document.fonts && document.fonts.ready); - return Buffer.from(await page.pdf({ - format: options.paperSize === "A4" ? "A4" : "A5", - preferCSSPageSize: true, - printBackground: true, - margin: { top: 0, right: 0, bottom: 0, left: 0 }, - })); + const html = buildPrescriptionHtml(record, options.paperSize, { doctor, pharmacist, seal }); + const paperSize = options.paperSize === "A4" ? "A4" : "A5"; + return generateCompactPrescriptionPdf(html, paperSize); } finally { - if (page) await page.close().catch(() => {}); releaseSlot(); } }