AI PLAY玩法更新持续收录
← 回到更新流

2026-09-08 · 原文发布日

PDF报表对账:代码校验合计后才入账

这是一个付费模板。核心思路是不让模型自己核数:Gmail指定标签收到带PDF附件的邮件后,提取文本交给OpenAI按结构化schema读出标题、币种、打印合计和全部明细行,再用JavaScript把明细金额加总,与打印合计在设定的绝对和百分比容差内比对;对得上才把明细逐行写入Google Sheets的Line Items,对不上就进Needs Review并发Slack告警。

原文要点

原模板为n8n付费模板,作者Jessy Mariau。流程:Gmail触发器按指定标签监听含附件的新邮件并下载附件;仅保留PDF并转纯文本;OpenAI按结构化schema抽取报表标题、币种、打印合计与全部明细行;JavaScript节点把明细行金额加总,按设定的绝对容差和百分比容差与打印合计比对;对得上才把明细逐行写入Google Sheets的Line Items页,对不上或无打印合计则写入Needs Review页并向Slack频道发告警。需配置Gmail、OpenAI、Google Sheets、Slack四类OAuth2凭据,以及容差与默认币种。

这次做成什么

在n8n里搭一条“PDF报表对账”流程:模型只负责读出数字,核数交给JavaScript代码,合计对得上才入账。由于原模板需购买,以下为按原文思路独立重写的搭建方案,节点名与界面参数需在n8n实际版本中核对,本站未实测。

准备什么

  • n8n账号(云或自托管,能添加Gmail/OpenAI/Google Sheets/Slack凭据)
  • 一个Gmail标签(如reports-in)用于接收带PDF附件的报表邮件
  • 含Line Items与Needs Review两个工作表的Google Sheets文档及编辑权限
  • Slack工作区及一个接收告警的频道名
  • OpenAI API凭据,结构化输出可用的模型

具体怎么做

  1. 配置Gmail触发与PDF过滤

    新建工作流,添加Gmail Trigger,事件选收到新邮件,过滤条件填入你创建的报表标签ID;开启附件下载。后接过滤节点,仅放行附件MIME为application/pdf的邮件,其余直接结束分支。

  2. PDF转文本并结构化抽取

    用n8n的PDF提取节点把附件转成纯文本。接OpenAI节点,提示词见下方材料“结构化抽取提示词”,要求严格按JSON schema返回title、currency、statedTotal与lineItems数组,禁止模型自己改算合计。

  3. JavaScript核数与容差比对

    添加Code节点,粘贴下方“合计校验脚本”:把lineItems各项amount相加得computedSum,与statedTotal做差,绝对差≤absTolerance或相对差≤pctTolerance视为对账通过;statedTotal缺失直接判不通过。容差参数从配置节点读入。

  4. 分支写入与告警

    用IF节点按校验结果的passed字段分支:true分支把lineItems拆成单行,逐条追加到Sheets的Line Items页;false分支把标题、打印合计、计算合计、差额写入Needs Review页,再接Slack节点向告警频道发送包含差额与邮件主题的消息。

  5. 用合成PDF走通全链路

    先用下方示例的虚构报表文本粘进测试PDF发到自己邮箱,确认三张去向:能对上的进Line Items、改一个明细金额后进Needs Review并收到Slack消息、无合计的进Needs Review。

可直接复制的材料

结构化抽取提示词

本站按原文schema思路独立编写,{{ $json.pdfText }}为n8n表达式占位,需按你实际字段名替换;模型可能产生API费用。

你是报表数据抽取器。下面是一份PDF报表转出的纯文本。请只输出一个JSON对象,不要输出任何其他文字。格式:{"title": 报表标题字符串, "currency": 三字母币种代码如CNY或USD, "statedTotal": 报表上印刷的合计金额数字,找不到则为null, "lineItems": [{"description": 明细描述, "quantity": 数量数字, "unitPrice": 单价数字, "amount": 该行金额数字}]}。规则:1) 所有金额原样照抄报表上的数字,不要重新计算;2) statedTotal必须取自报表印刷的合计行,不要自己加总;3) 每一行明细都要收录,不得遗漏或合并。

报表文本:
{{ $json.pdfText }}

合计校验脚本

本站独立示例,容差默认值是按合理量级设定,非原模板数值;导入前在n8n当前版本核对Code节点写法。

// n8n Code 节点,本站独立编写的独立示例,非原模板代码
const cfg = $json.settings || {};
const absTol = cfg.absTolerance ?? 0.01;      // 绝对容差,按需要改
const pctTol = cfg.pctTolerance ?? 0.001;     // 百分比容差 0.1%
const extracted = $json.extraction;
const items = extracted.lineItems || [];
const computed = items.reduce((s, r) => s + Number(r.amount || 0), 0);
const stated = extracted.statedTotal;
let passed = false, diff = null;
if (stated !== null && stated !== undefined) {
  diff = Math.abs(computed - Number(stated));
  const pct = Number(stated) !== 0 ? diff / Math.abs(Number(stated)) : Infinity;
  passed = diff <= absTol || pct <= pctTol;
}
return [{ json: {
  title: extracted.title,
  currency: extracted.currency || cfg.defaultCurrency || 'CNY',
  statedTotal: stated,
  computedTotal: Number(computed.toFixed(2)),
  difference: diff === null ? null : Number(diff.toFixed(2)),
  passed,
  lineItems: items
} }];

流程配置与测试报表文本

本站虚构的配置与测试文本,标签名、表名、频道名均需替换为你自己的。

{
  "settings": {
    "gmailLabel": "reports-in",
    "absTolerance": 0.01,
    "pctTolerance": 0.001,
    "defaultCurrency": "CNY",
    "sheets": { "okTab": "Line Items", "reviewTab": "Needs Review" },
    "slackChannel": "#finance-alerts"
  },
  "samplePdfText": "办公用品采购对账单 2026年3月\n明细:\nA4复印纸 10箱 单价35.00 金额350.00\n黑色签字笔 5盒 单价18.00 金额90.00\n订书钉 20盒 单价2.50 金额50.00\n打印合计:490.00 CNY"
}

输入与输出示例

本站虚构演示,非原作者运行结果,未在真实n8n环境执行。

输入

PDF文本含三行明细:350.00、90.00、50.00,印刷合计490.00 CNY,容差设为绝对0.01、百分比0.1%。

对应输出

{"title":"办公用品采购对账单 2026年3月","currency":"CNY","statedTotal":490.0,"computedTotal":490.0,"difference":0.0,"passed":true},随后3行明细分别追加到Line Items页;若把某行改成55.00,则computedTotal=495.00、difference=5.00、passed=false,流程改写Needs Review并向Slack发送差额5.00元的告警。

做完检查这些

  • 对得上的测试PDF:Line Items页新增行数与明细行数完全一致,金额逐行相等
  • 改错一行金额的测试PDF:Needs Review页出现一行,差额字段与手算差额一致
  • Slack频道收到告警,消息里同时包含印刷合计、计算合计与邮件主题
  • 把statedTotal改成null后流程走不通过分支,不再写入Line Items
  • Gmail标签外或非PDF附件的邮件不触发任何写入

容易卡住的地方

  • 原模板为付费商品,本页是按公开说明独立重搭的方案,界面与参数需在n8n实际版本核对
  • 结构化抽取依赖PDF能转成纯文本,扫描件图片型PDF会得到空文本,需另接OCR
  • 金额若带千分位逗号或币种符号,脚本里的Number()会得到NaN,需在抽取提示词或脚本中先清洗
  • 容差设得过宽会让真实差错漏过,过窄会让四舍五入误差全部进复核,需按自己业务定
  • Slack与Sheets的OAuth2授权范围不足时节点会静默失败,需查看执行日志

原文与作者资料 ↗

本站借助AI整理来源事实,独立编写中文操作指南与示例。示例不代表实际运行结果;来源效果尚未由本站独立复现。付费模板与模型、服务费用分别计算。

原文标题
Extract PDF report tables into Google Sheets with Gmail, OpenAI and Slack
原作者
Jessy Mariau
原始发布日期
2026-09-08T02:04:13.508Z
来源
https://n8n.io/workflows/19346