提要:用服务端中转 +简道云 v5 文件事务 + systemd 常驻与 journald日志,稳定实现单位/个人会员证书自动生成与回填。

搭建背景

你已有证书生成页面(PHP端点)与简道云表单审批流程,原流程需人工在网页填信息再生成证书。目标是把“审核通过后自动生成证书并回填到表单附件”,同时兼容单位会员与个人会员两类证书模板,减少人工操作与重复。

目的

  • 在简道云的“审核通过/数据修改”触发时,自动生成电子证书并将图片作为附件写回该记录。
  • 对单位/个人会员分支自动识别与处理。
  • 将服务端改为稳定常驻运行,并具备安全(签名)、日志与维护能力。

逻辑思路

  • 用服务端中转:Webhook接收审批通过,服务端读取表单字段,调用既有PHP端点生成证书(Base64),再按简道云 v5 文件接口的“事务上传”流程上传并回填附件。
  • 单位会员分支:直接把姓名、编号、有效期以 x-www-form-urlencoded 传给单位端点生成。
  • 个人会员分支:根据“会员类型=个人”,先按“编号”查询会员基本信息表补齐字段与照片,再以 multipart/form-data 传给个人端点生成。
  • 为防止平台5秒超时重试:Webhook“快速ACK”,整条链路在后台异步执行;加幂等锁与看门狗防并发与异常残留。
  • 安全与可观测:启用 x-jdy-signature 验签(SHA1),并用 systemd+journald常驻与日志管理。

总体数据流

简道云表单审核通过/数据修改Apache 443ProxyPass /webhook→Node 3000Node 服务 /webhook签名校验 + 快速ACK后台异步执行decision: "会员类型?"Danwei300.phpx-www-form-urlencoded(name, number, exp)会员基本信息表v5 data/listfields: number, gender, id_card_number,employer, title, photofilter: number eqGeren300.phpmultipart/form-data + photo证书Base64v5 get_upload_token(token, url, txid)上传文件multipart/form-data(token, file)→ keyv5 data/update同一 txid 写附件{ value: [key] }表单记录附件certificate_<number>.jpgsystemd 常驻journald 日志 POST /webhook + x-jdy-signature单位个人运行与日志

实现要点(关键节点)

  • 证书生成端点:
    • 单位:CERT_URL_DANWEI(name、number、exp)。
    • 个人:CERT_URL_GEREN(gerenname、gerennumber、gerenexp、gerensex、gerencard、gerencoltd、gerenwork、gerenphoto)。

单位会员时序

简道云ApacheNode /webhookDanwei300.phpJDY v5 文件接口 POST /webhookx-jdy-signature反向代理ACK 200name, number, expcode=0, data: base64get_upload_token(txid)token, urlupload(token, file)→ keydata/update(txid, 附件={value:[key]})OK

个人会员时序(信息补齐与数组清洗)

简道云主表ApacheNode /webhook会员基本信息表v5 data/listfilter: number eqprocessMemberDatatitle.join(', ')photo[0].urlGeren300.phpmultipart/form-dataJDY v5 文件接口 POST /webhookx-jdy-signature反向代理ACK 200fields: number, gender,id_card_number, employer,title, photo记录(含数组字段)数组字段清洗gerenname, gerennumber,... + gerenphotocode=0, base64get_upload_token(txid)token, urlupload→keydata/update(txid, 附件={value:[key]})OK
  • 简道云 v5 文件事务:

    • 先 POST 获取上传凭证与地址(token+url)与 transaction_id。
    • 用 multipart/form-data 上传文件得到 key。
    • 用同一 transaction_id 调用 data/update,将附件字段写入 { value: [key] }。
  • 字段来源:

    • 主表提供 full_name(或 name)、number、exp、member_type。
    • 个人会员需补齐:gender、id_card_number、employer、title、photo(“查询多条数据接口”按 number eq筛选,fields传别名;将 title 数组 join 为字符串、photo取第1张url)。
  • Webhook执行模型:

    • 顶部做签名校验来源IP白名单(可选)。
    • 立即返回 200(ACK),后台 setImmediate 执行“查询→生成→上传→回填”。
    • 幂等锁 processing + 看门狗(超时自动释放)。

签名校验

简道云推送x-jdy-signature原始请求体 req.rawBody服务端计算签名HMAC-SHA1(secret, rawBody)decision: "签名一致?"通过→后台执行不一致→返回说明不执行任务

异步ACK与幂等控制

收到 /webhook验签 + 白名单快速ACK 200decision: "processing.has(dataId)?"processing.add(dataId)watchdog(5min)查询/补齐→生成Base64→事务上传→回填finally:clearTimeout(watchdog)processing.delete(dataId)返回说明并终止任务 通过失败是(并发)

实现结果

  • 单位会员:审批通过或编辑修改触发一次生成并回填,表单附件字段出现 certificate_编号.jpg。
  • 个人会员:自动查询会员基本信息表补齐字段与照片,生成并回填证书图片。
  • 简道云“数据推送”不再超时重试,数据修改日志仅记录一次更新。
  • 启用 systemd 后后台常驻运行、崩溃自动重启;日志由 journald管理,带时间戳与自动轮转。

部署结构

HostApache 443SSL + ProxyPass /webhook→127.0.0.1:3000systemd vip-card-bridge.serviceExecStart=/root/.nvm/.../node server.cjsEnvironmentFile=.envRestart=alwaysNode server.cjsnvm Node 16.20.xjournald 持久化与轮转journalctl -u vip-card-bridge

注意事项

  • 事务一致性:获取上传凭证与 data/update 必须使用同一 transaction_id;有效期约1小时。
  • **字段****ID/**别名:查询多条接口 fields 与 filter.field可用你自定义的别名(number、gender等);如租户要求内部ID,需用 _widget_XXXX。
  • 个人字段数组:title/photo常为数组,提交前需处理为字符串与URL。
  • 签名校验:仅对 POST 正式推送验签;GET /webhook(连接测试)放行。验签必须对原始请求体(req.rawBody)做 HMAC-SHA1。
  • 反向代理:Apache需为 /webhook 配置 ProxyPass/ProxyPassReverse 到 127.0.0.1:3000/webhook,并在 443(HTTPS)虚拟主机同样配置。
  • CentOS 7 环境:系统 glibc 为 2.17,不支持 Node 20 的RPM包;采用 nvm 的 Node 16 直接跑 systemd(ExecStart指向绝对路径)。

维护

  • 日志:用 journald 查看(带时间戳、自动轮转)。

    • journalctl -u vip-card-bridge -f
    • journalctl -u vip-card-bridge –since today
  • 签名与安全

    • .env 里 JIANDC_WEBHOOK_SIGNATURE_KEY 是密钥,日志仅打印 hasWebhookSecret: true,不打印密钥本身。
  • Webhook****稳定性

    • 保持“快速ACK + 后台处理 + 幂等锁 + 看门狗”,避免超时与并发重复。
  • 字段与模板变更

    • 若证书版式或字段变更,同步调整PHP端点与字段映射;给模板版本与生成时间做记录更利于审计。

待优化项
- 容器化:将服务迁移到 Docker(node:20-alpine)以获得更新环境与更易移植的部署。
- 结构化日志:可引入 pino/winston 输出JSON结构化日志,便于检索与汇总。
- 队列与重试:高并发场景可加队列(Redis + BullMQ),统一控制并发与失败重试。
- 版本化证书:文件名加入时间戳/版本号(certificate_编号_YYYYMMDD.jpg),避免覆盖历史。

常用命令速查

  • Node 与依赖
- which node / type -a node
- npm install(在 /opt/vip-card-bridge)
  • systemd 服务(nvm Node 16 路径)
- 编辑 /etc/systemd/system/vip-card-bridge.service:

- ExecStart=/root/.nvm/versions/node/v16.20.2/bin/node /opt/vip-card-bridge/server.cjs
- Environment=“PATH=/root/.nvm/versions/node/v16.20.2/bin:/usr/bin:/bin”
- EnvironmentFile=/opt/vip-card-bridge/.env

- sudo systemctl daemon-reload
- sudo systemctl enable vip-card-bridge
- sudo systemctl restart vip-card-bridge
- journalctl -u vip-card-bridge -f
  • journald 持久化与上限(可选)
- sudo mkdir -p /var/log/journal
- sudo chown root:systemd-journal /var/log/journal
- sudo systemctl restart systemd-journald
- /etc/systemd/journald.conf:

- SystemMaxUse=200M
- SystemMaxFileSize=50M
  • Apache 反向代理(443虚拟主机中)
- ProxyRequests Off
- ProxyPreserveHost On
- ProxyPass “/webhook” “http://127.0.0.1:3000/webhook”
- ProxyPassReverse “/webhook” “http://127.0.0.1:3000/webhook”
  • 健康检查
- curl http://127.0.0.1:3000/health
- curl https://bjhearing.cn/health