diff --git a/README.md b/README.md index 1bd4ad1..25d6bd5 100644 --- a/README.md +++ b/README.md @@ -80,3 +80,66 @@ pm2 startOrReload pm2.json - 后台管理:http://localhost:8361/admin/dashboard.html - 小程序 API:http://localhost:8361/mp/* + +## 服务器部署 Puppeteer(协议签署截图) + +协议签署功能依赖 Puppeteer 调用 Chrome 生成截图,服务器需要安装 Chrome 和中文字体。 + +### 1. 安装 Chrome + +Puppeteer 自带 Chrome 下载,但国内服务器可能下载失败,可手动安装: + +```bash +# 创建 Puppeteer 缓存目录 +mkdir -p /root/.cache/puppeteer/chrome/linux-146.0.7680.76 +cd /root/.cache/puppeteer/chrome/linux-146.0.7680.76 + +# 从镜像下载 Chrome +wget https://cdn.npmmirror.com/binaries/chrome-for-testing/146.0.7680.76/linux64/chrome-linux64.zip + +# 解压并清理 +unzip chrome-linux64.zip +rm chrome-linux64.zip +``` + +验证安装: + +```bash +ls /root/.cache/puppeteer/chrome/linux-146.0.7680.76/chrome-linux64/chrome +``` + +### 2. 检查系统依赖库 + +```bash +ldd /root/.cache/puppeteer/chrome/linux-146.0.7680.76/chrome-linux64/chrome | grep "not found" +``` + +如有缺失,安装对应依赖: + +```bash +dnf install -y nss atk at-spi2-atk cups-libs libXcomposite libXdamage libXrandr mesa-libgbm pango alsa-lib libdrm libxkbcommon +``` + +### 3. 安装中文字体 + +服务器默认无中文字体,截图中文会显示为方框。 + +```bash +dnf install -y google-noto-sans-cjk-sc-fonts +``` + +如果 dnf 源中没有,手动安装: + +```bash +mkdir -p /usr/share/fonts/chinese +cd /usr/share/fonts/chinese +wget https://github.com/googlefonts/noto-cjk/releases/download/Sans2.004/08_NotoSansCJKsc.zip +unzip 08_NotoSansCJKsc.zip +fc-cache -fv +``` + +### 4. 重启服务 + +```bash +pm2 restart cytx_api +``` diff --git a/src/controller/admin/patient.js b/src/controller/admin/patient.js index 7a82b5a..2739ee5 100644 --- a/src/controller/admin/patient.js +++ b/src/controller/admin/patient.js @@ -414,6 +414,8 @@ module.exports = class extends Base { try { const smsConfig = require('../../config/sms.js'); const templates = smsConfig.templates; + think.logger.info(`[SMS] _sendAuditSms 开始 - phone: ${phone}, type: ${type}, reason: ${reason || '(无)'}`); + think.logger.info(`[SMS] 模板配置 - auditApproved: ${templates.auditApproved || '(空)'}, auditRejected: ${templates.auditRejected || '(空)'}`); if (type === 'approved' && templates.auditApproved) { await this.sendNotifySms(phone, templates.auditApproved, [], 'audit_approved'); } else if (type === 'rejected' && templates.auditRejected) { diff --git a/src/controller/base.js b/src/controller/base.js index d2641f9..a0fe26f 100644 --- a/src/controller/base.js +++ b/src/controller/base.js @@ -244,6 +244,9 @@ module.exports = class extends think.Controller { const smsConfig = require('../config/sms.js'); const ip = this.ctx?.ip || ''; + think.logger.info(`[SMS] sendNotifySms 开始 - 手机号: ${mobile}, 模板: ${templateId}, 参数: ${JSON.stringify(templateParams)}, 业务: ${bizType}`); + think.logger.info(`[SMS] 配置状态 - enabled: ${smsConfig.enabled}, smsSdkAppId: ${smsConfig.smsSdkAppId || '(空)'}, signName: ${smsConfig.signName || '(空)'}`); + // 记录到数据库 const smsLogModel = this.model('sms_log'); const logId = await smsLogModel.add({ @@ -257,14 +260,19 @@ module.exports = class extends think.Controller { ip, create_time: think.datetime(new Date()) }); + think.logger.info(`[SMS] 日志已写入数据库 logId: ${logId}`); let sendResult = { success: true, msgId: null }; if (smsConfig.enabled && smsConfig.smsSdkAppId) { + think.logger.info(`[SMS] 调用腾讯云短信接口...`); sendResult = await this._sendTencentSms(mobile, templateId, templateParams); } else { + think.logger.info(`[SMS-DEV] 未启用真实发送 - enabled: ${smsConfig.enabled}, smsSdkAppId: ${smsConfig.smsSdkAppId || '(空)'}`); think.logger.info(`[SMS-DEV] 通知短信 手机号: ${mobile}, 模板: ${templateId}, 参数: ${JSON.stringify(templateParams)}, 业务: ${bizType}`); } + think.logger.info(`[SMS] sendNotifySms 结果 - success: ${sendResult.success}, msgId: ${sendResult.msgId || '(无)'}, message: ${sendResult.message || '(无)'}`); + await smsLogModel.where({ id: logId }).update({ status: sendResult.success ? 1 : 2, fail_reason: sendResult.success ? null : sendResult.message, @@ -295,21 +303,28 @@ module.exports = class extends think.Controller { } }); - const result = await client.SendSms({ + const reqParams = { PhoneNumberSet: [`+86${mobile}`], SmsSdkAppId: smsConfig.smsSdkAppId, SignName: smsConfig.signName, TemplateId: templateId, TemplateParamSet: templateParams - }); + }; + think.logger.info(`[SMS] _sendTencentSms 请求参数: ${JSON.stringify(reqParams)}`); + + const result = await client.SendSms(reqParams); + think.logger.info(`[SMS] _sendTencentSms 响应: ${JSON.stringify(result)}`); const sendStatus = result.SendStatusSet?.[0]; if (sendStatus?.Code === 'Ok') { + think.logger.info(`[SMS] 发送成功 SerialNo: ${sendStatus.SerialNo}`); return { success: true, msgId: sendStatus.SerialNo }; } + think.logger.error(`[SMS] 发送失败 Code: ${sendStatus?.Code}, Message: ${sendStatus?.Message}`); return { success: false, message: sendStatus?.Message || '发送失败' }; } catch (error) { - think.logger.error('[SMS] 腾讯云短信发送失败:', error); + think.logger.error('[SMS] 腾讯云短信发送异常:', error.message); + think.logger.error('[SMS] 异常堆栈:', error.stack); return { success: false, message: error.message }; } }