fix: 问题修复

1. 编辑新增药品的信息同步his;
2. pdf调整(主要是压缩生成的pdf文件)
This commit is contained in:
huxuejian 2026-10-12 16:55:21 +08:00
parent f1c1812516
commit 552dc546be
2 changed files with 66 additions and 20 deletions

View File

@ -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: "更新成功" }

View File

@ -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 = {}) {
<style>
@page { size: ${size} portrait; margin: 8mm; }
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; color: #111; font-family: "Noto Sans CJK SC", "Noto Sans SC", SimSun, "Songti SC", serif; font-size: 14px; }
html, body { margin: 0; padding: 0; color: #111; background: #fff; font-family: "Noto Sans CJK SC", "Noto Sans SC", SimSun, "Songti SC", serif; font-size: 14px; }
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
.prescription { min-height: ${size === "A5" ? "194mm" : "281mm"}; display: flex; flex-direction: column; line-height: 1.28; overflow: hidden; }
.prescription { width: calc(100% - 1px); min-height: ${size === "A5" ? "194mm" : "281mm"}; display: flex; flex-direction: column; line-height: 1.28; overflow: hidden; }
.header { position: relative; border-bottom: 1px solid #111; padding: 3px 0 4px; }
.title { font-weight: 700; font-size: 17px; text-align: center; line-height: 1.4; }
.rx-type { position: absolute; top: 0; right: 0; width: 40px; border: 1px solid #333; text-align: center; color: #666; font-size: 12px; line-height: 1.3; }
.rx-type { position: absolute; top: 0; right: 1px; width: 40px; border: 1px solid #333; text-align: center; color: #666; font-size: 12px; line-height: 1.3; }
.rx-number { margin-top: 5px; }
.patient-info { padding-top: 5px; }
.info-row { display: flex; padding: 2px; }
@ -323,30 +329,66 @@ function buildPrescriptionHtml(record, paperSize = "A5", signatureImages = {}) {
</body></html>`;
}
function buildRasterPdfHtml(image, paperSize) {
const size = RASTER_PAGE_SIZES[paperSize];
return `<!doctype html><html><head><meta charset="utf-8" /><style>
@page { size: ${paperSize} portrait; margin: 8mm; }
html, body { margin: 0; padding: 0; }
img { display: block; width: ${size.contentWidth}; height: ${size.contentHeight}; }
</style></head><body><img src="${image}" alt="处方笺" /></body></html>`;
}
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();
}
}