diff --git a/src/config/router.js b/src/config/router.js index cd4fdf2..b9c0252 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -74,6 +74,7 @@ module.exports = [ ['/api/mp/messageDetail', 'mp/messageDetail'], ['/api/mp/unreadCount', 'mp/unreadCount'], ['/api/mp/subscribeConfig', 'mp/subscribeConfig'], + ['/api/mp/regenerateSign', 'mp/regenerateSign'], // 瘤种管理 ['/admin/tag', 'admin/tag/index'], diff --git a/src/controller/mp.js b/src/controller/mp.js index f580932..22fc96c 100644 --- a/src/controller/mp.js +++ b/src/controller/mp.js @@ -631,6 +631,56 @@ module.exports = class extends Base { } } + /** + * 批量重新生成声明与承诺签署图并更新数据库 + * GET /api/mp/regenerateSign + */ + async regenerateSignAction() { + return false; + try { + const patients = await this.model('patient') + .field('id, name, sign_promise') + .where({ is_deleted: 0, sign_promise: ['!=', ''] }) + .select(); + + if (!patients.length) return this.json({ code: 1, msg: '没有需要处理的患者' }); + + const doc = await this.model('content').getByKey('sign_promise'); + if (think.isEmpty(doc)) return this.json({ code: 1, msg: '协议内容未配置' }); + + const screenshotService = this.service('screenshot'); + const results = []; + let successCount = 0; + let failCount = 0; + + for (const patient of patients) { + try { + const newUrl = await screenshotService.regenerate({ + originalImageUrl: patient.sign_promise, + title: doc.title, + content: doc.content + }); + await this.model('patient').where({ id: patient.id }).update({ sign_promise: newUrl }); + successCount++; + results.push({ id: patient.id, name: patient.name, status: 'ok' }); + } catch (e) { + failCount++; + results.push({ id: patient.id, name: patient.name, status: 'fail', error: e.message }); + think.logger.error(`regenerateSign patient ${patient.id} error:`, e); + } + } + + return this.json({ + code: 0, + data: { total: patients.length, success: successCount, fail: failCount, results }, + msg: `处理完成:成功${successCount},失败${failCount}` + }); + } catch (error) { + think.logger.error('regenerateSign error:', error); + return this.json({ code: 1, msg: '执行失败: ' + error.message }); + } + } + // @private async _verifyIdCard(name, idCard) { const faceidConfig = require('../config/faceid.js'); diff --git a/src/service/screenshot.js b/src/service/screenshot.js index 21b99c5..03ead83 100644 --- a/src/service/screenshot.js +++ b/src/service/screenshot.js @@ -155,6 +155,99 @@ module.exports = class extends think.Service { `; } + /** + * 重新生成签署图(保留原图签名区域,替换上方内容) + * 原理:原图做背景 bottom 对齐,白色 overlay 覆盖上方内容区, + * placeholder 撑出签名区高度让背景图底部签名露出 + * @param {Object} params + * @param {string} params.originalImageUrl - 原合成图URL + * @param {string} params.title - 新协议标题 + * @param {string} params.content - 新协议富文本内容 + * @returns {string} 新合成图COS URL + */ + async regenerate({ originalImageUrl, title, content }) { + const puppeteer = require('puppeteer'); + + const html = ` + + + + + + +
+
${title}
+
${content}
+
+
+ +`; + + let browser; + try { + browser = await puppeteer.launch({ + headless: 'new', + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-gpu', + '--disable-dev-shm-usage', + '--font-render-hinting=none' + ] + }); + + const page = await browser.newPage(); + await page.setViewport({ width: 750, height: 1000, deviceScaleFactor: 2 }); + await page.setContent(html, { waitUntil: 'networkidle0', timeout: 15000 }); + + const screenshot = await page.screenshot({ fullPage: true, type: 'png' }); + await browser.close(); + browser = null; + + const url = await this._uploadToCos(screenshot); + return url; + } catch (error) { + if (browser) await browser.close().catch(() => {}); + throw error; + } + } + /** * 上传截图到 COS */