提取PDF电子发票发票号码

SKILL效率
效率 未标注权限 许可证不明确
安装这个技能

本技能为付费技能(PAYSKILL)。调用遵循支付宝 AI 生意画像契约,由平台调度层执行 probe → pay → complete → ack 流程:

一句话选型结论

按资料字段整理

提取PDF电子发票发票号码是效率类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类效率 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_9355e276/shdskill02
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

效率
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_9355e276仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791729501
正文体量13.7 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
整理待办与日程

把零散任务收进清单,排出优先级。

批量处理重复任务

把固定动作做成模板,重复执行。

把流程写成清单

把口头流程整理成可复用的步骤。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在效率场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
提取PDF电子发票发票号码当前 未标注权限 未标注 未标注 未标注 有
Nano PDF 文档 联网访问 ClawHub 市场 424.9 万 OpenClaw 有
自动更新程序 浏览器控制 ClawHub 市场 296.4 万 OpenClaw 有
Gemini 助手 联网访问 ClawHub 市场 254.7 万 OpenClaw 有
word DOCX 文档 联网访问 ClawHub 市场 239.6 万 OpenClaw 有
Excel XLSX 前端 联网访问 ClawHub 市场 227.0 万 OpenClaw 有

SKILL.md 全文

13.7 KB
模型实际读到的说明书原文,来自技能仓库

{
"name": "扫描PDF二维码提取发票号码",
"description": "批量扫描PDF文件中的二维码,提取发票号码并以Excel格式输出。支持在Work Buddy新建任务对话框中点击左下角+号添加多个PDF文件。",
"triggers": [
"扫描PDF二维码提取发票号码",
"扫描PDF二维码",
"提取发票号码",
"批量提取发票号码",
"PDF二维码识别",
"发票号码提取",
"扫码提取发票",
"PDF扫码"
],
"window": {
"title": "扫描PDF二维码提取发票号码",
"width": 700,
"height": 480,
"resizable": true,
"modal": true
},
"ui": {
"type": "custom",
"html": "<div style='display:flex;flex-direction:column;height:100%;padding:20px 24px;font-family:'Segoe UI',system-ui,sans-serif;background:#f7f9fc;box-sizing:border-box;'>\n <div style='margin-bottom:16px;'>\n <h3 style='margin:0 0 6px 0;color:#1a1a2e;font-weight:600;font-size:18px;'>📄 扫描PDF二维码提取发票号码</h3>\n <p style='margin:0;color:#666;font-size:13px;'>点击下方「+ 添加文件」选择PDF文件(支持多选),系统将自动识别二维码中的发票号码</p>\n </div>\n\n <!-- 文件上传区域 -->\n <div style='display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;'>\n <button id='addFilesBtn' style='padding:10px 24px;border:2px dashed #0078d4;border-radius:8px;background:#fff;color:#0078d4;cursor:pointer;font-size:14px;font-weight:600;transition:all 0.2s;'>+ 添加文件</button>\n <span style='font-size:13px;color:#888;'>已添加 <span id='fileCount'>0</span> 个PDF文件</span>\n <button id='clearFilesBtn' style='padding:6px 16px;border:1px solid #ddd;border-radius:4px;background:#fff;cursor:pointer;font-size:12px;color:#999;'>清空列表</button>\n </div>\n\n <!-- 文件列表 -->\n <div style='flex:1;border:1px solid #e0e0e0;border-radius:8px;background:#fff;overflow:hidden;display:flex;flex-direction:column;min-height:0;'>\n <div style='display:flex;justify-content:space-between;padding:10px 16px;background:#f0f2f5;border-bottom:1px solid #e0e0e0;font-weight:600;font-size:13px;color:#444;'>\n <span style='flex:1;'>文件名</span>\n <span style='width:120px;text-align:center;'>状态</span>\n <span style='width:100px;text-align:center;'>发票号码</span>\n </div>\n <div id='fileListContainer' style='flex:1;overflow-y:auto;padding:4px 0;'></div>\n </div>\n\n <!-- 底部操作栏 -->\n <div style='display:flex;justify-content:space-between;align-items:center;margin-top:16px;'>\n <div style='display:flex;gap:12px;'>\n <button id='scanBtn' style='padding:10px 36px;border:none;border-radius:6px;background:#0078d4;color:#fff;cursor:pointer;font-size:14px;font-weight:600;'>开始扫描</button>\n <button id='exportBtn' style='padding:10px 28px;border:1px solid #0078d4;border-radius:6px;background:#fff;color:#0078d4;cursor:pointer;font-size:14px;font-weight:600;'>导出Excel</button>\n </div>\n <button id='closeBtn' style='padding:8px 24px;border:1px solid #ddd;border-radius:6px;background:#fff;cursor:pointer;font-size:13px;color:#888;'>关闭</button>\n </div>\n</div>"
},
"actions": {
"addFiles": {
"description": "打开文件选择对话框,添加PDF文件",
"code": "async (context) => {\n const result = await context.showOpenFilePicker({\n multiple: true,\n filters: [{ name: 'PDF文件', extensions: ['pdf'] }]\n });\n if (result && result.length > 0) {\n const currentFiles = context.state.fileList || [];\n const newFiles = result.map(file => ({\n name: file.name,\n path: file.path,\n status: '待扫描',\n invoiceNumber: ''\n }));\n const updated = [...currentFiles, ...newFiles];\n context.setState({ fileList: updated });\n context.renderFileList(updated);\n context.updateUI('fileCount', updated.length);\n }\n}"
},
"clearFiles": {
"description": "清空文件列表",
"code": "async (context) => {\n context.setState({ fileList: [] });\n context.renderFileList([]);\n context.updateUI('fileCount', 0);\n}"
},
"scanFiles": {
"description": "扫描所有PDF文件中的二维码,提取发票号码",
"code": "async (context) => {\n const fileList = context.state.fileList || [];\n if (fileList.length === 0) {\n await context.showMessage('请先添加PDF文件!', 'warning');\n return;\n }\n // 更新所有文件状态为“扫描中”\n const scanningList = fileList.map(f => ({ ...f, status: '扫描中...' }));\n context.setState({ fileList: scanningList });\n context.renderFileList(scanningList);\n let processed = 0;\n for (const item of scanningList) {\n try {\n // 调用PDF二维码识别功能(Work Buddy内置API)\n const result = await context.scanPDFQRCode(item.path);\n // 从二维码内容中提取发票号码(支持多种格式)\n const invoiceNumber = context.extractInvoiceNumber(result);\n item.status = '已完成';\n item.invoiceNumber = invoiceNumber || '未识别';\n } catch (e) {\n item.status = '失败';\n item.invoiceNumber = '识别失败';\n }\n processed++;\n context.renderFileList(scanningList);\n context.updateUI('fileCount', scanningList.length);\n // 更新进度(可选)\n await context.updateProgress(processed, scanningList.length);\n }\n const completed = scanningList.filter(f => f.status === '已完成' && f.invoiceNumber && f.invoiceNumber !== '未识别');\n await context.showMessage(扫描完成!共 ${scanningList.length} 个文件,成功识别 ${completed.length} 个发票号码。, 'info');\n}"
},
"exportExcel": {
"description": "将识别结果导出为Excel文件",
"code": "async (context) => {\n const fileList = context.state.fileList || [];\n const validResults = fileList.filter(f => f.invoiceNumber && f.invoiceNumber !== '未识别' && f.invoiceNumber !== '识别失败');\n if (validResults.length === 0) {\n await context.showMessage('没有可导出的发票号码数据,请先执行扫描!', 'warning');\n return;\n }\n // 构建Excel数据\n const excelData = [\n ['序号', '文件名', '发票号码', '状态']\n ];\n validResults.forEach((item, index) => {\n excelData.push([\n index + 1,\n item.name,\n item.invoiceNumber,\n item.status\n ]);\n });\n try {\n const filePath = await context.exportExcel(excelData, '发票号码提取结果.xlsx');\n await context.showMessage(Excel导出成功!文件保存至:${filePath}, 'info');\n } catch (e) {\n await context.showMessage(导出失败:${e.message}, 'error');\n }\n}"
},
"close": {
"description": "关闭弹窗",
"code": "async (context) => { context.close(); }"
}
},
"state": {
"fileList": []
},
"permissions": [
"readFiles",
"writeFiles",
"showDialog",
"showOpenFilePicker",
"scanQRCode",
"exportExcel"
],
"lifecycle": {
"onOpen": {
"code": "async (context) => {\n // 初始化状态\n context.setState({ fileList: [] });\n\n // 定义提取发票号码的函数\n context.extractInvoiceNumber = (qrContent) => {\n if (!qrContent) return null;\n const content = String(qrContent);\n // 常见发票号码格式:\n // 1. 纯数字发票号码(如:1234567890)\n // 2. 发票号码+发票代码组合(如:发票号码:1234567890)\n // 3. JSON格式中的发票号码字段\n let match = content.match(/发票号码[:::\s]([0-9]{8,12})/);\n if (match) return match[1];\n match = content.match(/invoice[:::\s]([0-9]{8,12})/i);\n if (match) return match[1];\n match = content.match(/number[:::\s]([0-9]{8,12})/i);\n if (match) return match[1];\n match = content.match(/\"发票号码\"\s[::]\s\"([0-9]{8,12})\"/);\n if (match) return match[1];\n match = content.match(/\"invoiceNumber\"\s[::]\s\"([0-9]{8,12})\"/i);\n if (match) return match[1];\n // 如果内容中只有一串数字且长度在8-12位之间,视为发票号码\n const numbers = content.match(/[0-9]{8,12}/g);\n if (numbers && numbers.length === 1) {\n return numbers[0];\n }\n // 尝试提取JSON对象中的发票号码\n try {\n const json = JSON.parse(content);\n if (json.invoiceNumber) return String(json.invoiceNumber);\n if (json.invoice_no) return String(json.invoice_no);\n if (json.number) return String(json.number);\n if (json.发票号码) return String(json.发票号码);\n } catch (e) { / 忽略非JSON内容 */ }\n return null;\n };\n\n // 渲染文件列表\n context.renderFileList = (fileList) => {\n const container = context.getUIElement('fileListContainer');\n if (!container) return;\n if (!fileList || fileList.length === 0) {\n container.innerHTML = '<div style=\"padding:40px 20px;text-align:center;color:#bbb;font-size:14px;\">暂无文件,请点击「+ 添加文件」添加PDF</div>';\n return;\n }\n let html = '';\n fileList.forEach((item, index) => {\n const statusColor = item.status === '已完成' ? '#28a745' :\n item.status === '失败' ? '#dc3545' :\n item.status === '扫描中...' ? '#ffc107' : '#6c757d';\n const invoiceDisplay = item.invoiceNumber || '-';\n html += \n &lt;div style=&#x27;display:flex;justify-content:space-between;padding:8px 16px;border-bottom:1px solid #f0f0f0;font-size:13px;align-items:center;&#x27;&gt;\n &lt;span style=&#x27;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#333;&#x27;&gt;${item.name}&lt;/span&gt;\n &lt;span style=&#x27;width:120px;text-align:center;color:${statusColor};font-weight:500;&#x27;&gt;${item.status}&lt;/span&gt;\n &lt;span style=&#x27;width:100px;text-align:center;color:#1a1a2e;font-weight:600;&#x27;&gt;${invoiceDisplay}&lt;/span&gt;\n &lt;/div&gt;\n;\n });\n container.innerHTML = html;\n };\n\n // 绑定UI事件\n context.bindUI('addFilesBtn', 'addFiles');\n context.bindUI('clearFilesBtn', 'clearFiles');\n context.bindUI('scanBtn', 'scanFiles');\n context.bindUI('exportBtn', 'exportExcel');\n context.bindUI('closeBtn', 'close');\n\n // 初始化空列表\n context.renderFileList([]);\n context.updateUI('fileCount', 0);\n }\n}"
}
}

支付宝付费改造契约(PAYSKILL)

本技能为付费技能(PAYSKILL)。调用遵循支付宝 AI 生意画像契约,由平台调度层执行 probe → pay → complete → ack 流程:

  1. probe(探测):平台调用前先探测是否已持有本技能的支付宝有效订阅 / 付费凭证。已付费则直接放行。
  2. 402 Payment-Needed:未付费时,脚本向标准输出返回 HTTP 状态 402 与 Payment-Needed 标记,提示用户通过支付宝完成付费(pay),平台据此唤起付费页。
  3. complete(扣费确认):用户在支付宝完成扣费后,平台下发 Payment-Proof(付费凭证)。
  4. ack(验签放行):平台携带 Payment-Proof 重试本次调用;scripts/payment_gate.py 验签该凭证,确认有效后放行主流程。

订阅持久化与幂等:

  • 付费状态缓存在本地 .pay_cache/state.json,避免每次调用重复付费(订阅持久化)。
  • 同一 Payment-Proof 在 24h 幂等窗口内仅生效一次,避免重复扣费。

现金流改造后的调用套路:

用户调用 → 平台 probe  →
   未付费:下发 402 Payment-Needed → 支付宝 pay → complete → 下发 Payment-Proof
已付费/携带 Proof:脚本 ack 验签 → 放行主流程

> 本契约由 SkillHUB 平台调度层解析,脚本本地仅处理 402 / 验签 / 持久化 三环节;付费本体交给支付宝。


支付宝 AI 按量付费(Machine Pay / A2M)改造说明

本 Skill 已按支付宝官方 A2M 协议改造为 Pay Skill,付费闸口位于 scripts/payment_gate.py,统一调用云端计费服务 https://pay.pembroli.cn/api/resource。

付费流程

  1. 未付费调用时,服务端返回 HTTP 402 + Payment-Needed 头,携带金额、商户、订单号及 RSA2 签名。
  2. 买家侧 payment_gate.py 收到 402 后引导用户完成支付,平台回传 Payment-Proof。
  3. 客户端携带 Payment-Proof 重试资源端点。

验付调用

服务端收到 Payment-Proof 后,调用支付宝验付接口(alipay.aipay.agent.payment.verify)核验凭证有效性(trade_no、out_trade_no、active 状态),验证失败返回 400,验证通过才进入资源发放。

履约确认

资源发放成功后,服务端异步调用履约确认接口(alipay.aipay.agent.fulfillment.confirm,trade_no)回执,标记本笔订单已履约;并按 out_trade_no 做幂等,重复请求直接返回已缓存结果,杜绝重复收费。

幂等与缓存

买家侧按 slug 本地缓存 Payment-Proof(24h TTL),有效期内免重复支付;服务端按订单号幂等,保证同一笔订单只履约一次。

常见问题

5 问
答案由本站依据条目字段整理
提取PDF电子发票发票号码怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_9355e276/shdskill02。安装方式为未标注。装完刷新技能列表或重开客户端。

提取PDF电子发票发票号码支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

提取PDF电子发票发票号码权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

提取PDF电子发票发票号码收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

提取PDF电子发票发票号码用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。