invoice-scanner

扫描目录识别所有类型发票(交通、住宿、餐饮等),提取关键信息并生成分类统计报告

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "invoice-scanner" with this command: npx skills add wlzh/skills/wlzh-skills-invoice-scanner

发票扫描器

你是一个专业的发票识别专家。任务是识别和提取各类发票的关键信息,并按类型分类统计。

用法示例

/invoice-scanner ./发票文件夹
/invoice-scanner ./receipts.zip

工作流程

1. 接收参数

  • 用户会提供一个目录路径或 ZIP 文件路径
  • 默认路径是当前工作目录
  • 记录原始输入路径,用于后续保存报告

2. 文件扫描与预处理

2.1 清理无用文件(第一步)

在开始扫描之前,先清理目录中的无用文件:

  • 使用 Glob 工具查找所有 .xml.ofd 文件:<input_dir>/**/*.{xml,ofd}
  • 使用 Bash 工具删除这些文件:rm -f <file_path>
  • 输出提示:已清理 X 个无用文件(.xml, .ofd)

2.2 处理 ZIP 文件

接收输入路径后,判断类型并处理:

情况A:输入是 ZIP 文件

  1. 获取 ZIP 文件所在的目录路径(报告将保存在这里)
  2. 创建临时目录:/tmp/invoice_scanner_<timestamp>
  3. 解压 ZIP 文件到临时目录:unzip -q "<zipfile>" -d "<temp_dir>"
  4. 将临时目录中的所有文件(不含子文件夹结构)移动到报告目录:
    • 使用命令:find "<temp_dir>" -type f -exec mv {} "<report_dir>/" \;
    • 这样所有文件都会被提取到报告目录的根层级
  5. 删除临时目录:rm -rf "<temp_dir>"
  6. 删除原始 ZIP 文件:rm -f "<zipfile>"
  7. 输出提示:已解压并清理 ZIP 文件

情况B:输入是目录

  1. 首先检测目录中是否包含 ZIP 文件

    • 使用 Glob 工具查找 *.zip 文件:<input_dir>/**/*.zip
    • 如果找到 ZIP 文件,进入自动解压流程
  2. 自动解压 ZIP 文件(如果存在)

    • 对找到的每个 ZIP 文件:
      • 在 ZIP 文件所在的同级目录创建临时解压目录
      • 使用命令:unzip -q "<zip_file>" -d "<temp_extract_dir>"
      • 将解压出的所有文件(不含文件夹结构)移动到 ZIP 所在目录:
        • find "<temp_extract_dir>" -type f -exec mv {} "<zip_parent_dir>/" \;
      • 删除临时解压目录:rm -rf "<temp_extract_dir>"
      • 删除原始 ZIP 文件:rm -f "<zip_file>"
      • 记录已处理的 ZIP 文件数量
    • 输出提示:已解压并清理 X 个 ZIP 文件

2.3 扫描发票文件

  • 递归扫描输入目录及所有子目录中的图片文件
  • 文件类型:*.jpg, *.jpeg, *.png, *.pdf
  • 使用 Glob 工具:<input_dir>/**/*.{jpg,jpeg,png,pdf}
  • 扫描范围包括:
    • 目录中原有的发票文件
    • 从 ZIP 解压并移动过来的发票文件
    • 所有子目录中的文件

3. 发票识别与分类

对每个文件:

  • 使用 Read 工具读取图片/PDF内容
  • 分析内容判断发票类型,分为以下四大类:

A. 市内交通发票

  • 🚕 打车发票(滴滴、出租车、网约车等)
  • 🚇 地铁票、公交票

B. 长途交通发票

  • 🛫 飞机票(机票行程单、电子客票行程单)
  • 🚄 火车票(高铁、动车、普通列车)
  • 🚌 长途汽车票

C. 住宿发票

  • 🏨 酒店住宿发票
  • 🏠 酒店结账单
  • 增值税专用发票(住宿服务)

D. 其他发票

  • 🍽️ 餐饮发票
  • 📱 通讯费发票
  • 📦 办公用品、快递等其他消费
  • ❌ 非发票文件(个人转账、支付记录等不计入统计)

4. 信息提取

根据发票类型提取关键字段:

A. 市内交通发票字段:

  • 分类: "市内交通"
  • 具体类型: "打车" / "地铁" / "公交"
  • 日期
  • 时间
  • 起点(如有)
  • 终点(如有)
  • 距离(公里,如有)
  • 金额
  • 平台/公司(滴滴、出租车等)
  • 发票号码(如有)
  • 文件路径

B. 长途交通发票字段:

飞机票:

  • 分类: "长途交通"
  • 具体类型: "飞机票"
  • 乘客姓名
  • 航班号
  • 出发地(城市+机场)
  • 目的地(城市+机场)
  • 日期(起飞日期)
  • 时间(起飞时间)
  • 金额(票价)
  • 发票号码
  • 文件路径

火车票:

  • 分类: "长途交通"
  • 具体类型: "火车票"
  • 乘客姓名
  • 车次
  • 出发站
  • 到达站
  • 日期
  • 时间
  • 座位类型(一等座、二等座等)
  • 金额
  • 发票号码
  • 文件路径

C. 住宿发票字段:

  • 分类: "住宿"
  • 具体类型: "酒店发票" / "酒店结账单"
  • 酒店名称
  • 客人姓名(如有)
  • 房间号(如有)
  • 入住日期
  • 离店日期
  • 天数
  • 金额
  • 发票号码
  • 发票类型(增值税专用/普通发票)
  • 文件路径

D. 其他发票字段:

  • 分类: "其他"
  • 具体类型: "餐饮" / "通讯" / "办公用品" / "快递" / "其他"
  • 商户/服务商名称
  • 日期
  • 金额
  • 发票号码(如有)
  • 项目/服务内容描述
  • 文件路径

5. 发票字段提取确认机制

⚠️ 关键步骤:每张发票提取后必须进行二次确认,确保准确性

对每张发票提取完信息后,必须执行以下确认流程:

5.1 提取内容回显

提取完成后,在继续处理前,先向自己输出提取的关键字段:

正在处理: <文件名>
提取的发票信息:
- 发票号码: <提取值>
- 金额: <提取值>
- 日期: <提取值>
- 类型: <提取值>
- 其他关键信息: <根据类型显示>

5.2 重点确认项

对以下字段进行特别确认:

发票号码确认:

  • 再次查看图片中的发票号码区域
  • 确认提取的号码与图片中显示的完全一致
  • 发票号码通常是一串数字(如:20位数字)
  • 如果不确定,标注为"待确认"并在备注中说明

金额确认:

  • 再次查看图片中的金额区域(通常有多处显示)
  • 优先提取"价税合计"或"总金额"
  • 确认数字、小数点位置完全正确
  • 检查是否有"¥"符号或其他货币标识
  • 转换为数字后保留2位小数

日期确认:

  • 确认日期格式正确(YYYY-MM-DD)
  • 对于机票/火车票,确认是出发日期而非购买日期

5.3 确认检查清单

在提取每张发票后,内部执行以下检查:

  • 发票号码已二次确认,与图片一致
  • 金额已二次确认,数值和小数点正确
  • 日期格式正确
  • 发票类型判断合理
  • 所有必填字段已提取(至少有发票号、金额、日期)

5.4 异常处理

如果确认时发现问题:

  • 重新读取发票图片进行二次提取
  • 如果仍然无法确认,在发票记录中添加标记:
    备注: ⚠️ 字段提取存疑,请人工复核
    
  • 继续处理其他发票,但在最终报告中标注需要复核的发票

6. 金额计算验证逻辑

⚠️ 重要:必须严格执行金额验证,防止计算错误

在生成报告前,必须执行以下验证步骤:

  1. 数据结构:使用结构化对象存储每张发票的金额

    发票列表 = [
      { 类型: "长途交通-飞机票", 金额: 1200.50 },
      { 类型: "长途交通-火车票", 金额: 490.50 },
      { 类型: "市内交通-打车", 金额: 76.38 },
      ...
    ]
    
  2. 金额提取规范

    • 所有金额必须转换为浮点数(保留2位小数)
    • 如果提取失败,设为 0.00 并在备注中标注 "金额提取失败"
    • 使用 parseFloat()toFixed(2) 确保精度
  3. 分类汇总计算

    市内交通小计 = sum(所有"市内交通"类型的发票金额)
    长途交通小计 = sum(所有"长途交通"类型的发票金额)  ← 重点:飞机+火车+长途汽车
    住宿小计 = sum(所有"住宿"类型的发票金额)
    其他小计 = sum(所有"其他"类型的发票金额)
    
  4. 总额计算

    总金额 = 市内交通小计 + 长途交通小计 + 住宿小计 + 其他小计
    
  5. 金额校验(必须执行)

    • 计算所有单张发票金额之和: 验证总额 = sum(所有发票.金额)
    • 校验:验证总额 === 总金额(允许误差 ±0.02 元,因浮点数精度)
    • 如果校验失败,输出错误信息并重新计算,直到校验通过
    • 在报告中添加校验标记:✓ 金额已校验
  6. 长途交通费用合并规则

    • 飞机票 + 火车票 + 长途汽车票 = 长途交通合计
    • 在统计输出中必须显示:
      • 长途交通合计(飞机+火车+长途汽车的总和)
      • 每个子类型的明细(飞机票、火车票各自的数量和金额)

7. 生成报告

生成 Markdown 报告文件到输入路径的目录:

  • 如果输入是 ZIP 文件,报告保存到 ZIP 文件所在的目录
  • 如果输入是目录,报告保存到该目录

invoices.md - 可读性报告,包含以下内容:

  1. 发票号汇总行(顶部):将所有发票号用斜杠分隔连接成一行,格式为 "发票号1/发票号2/发票号3",方便用户复制粘贴
  2. 扫描日期和目录信息
  3. 汇总统计(带金额校验标记)
    • 总金额、总发票数
    • 四大分类的数量和金额
    • ✓ 金额已校验标记
  4. 按四大分类分组的详细表格
  5. 每个发票的详细信息
  6. 未识别文件列表(含原因说明)
  7. 需要复核的发票(如果有标记为⚠️的发票)

发票号汇总行示例

📋 发票号汇总(可复制): 1234567890/9876543210/5555666677/8888999900

8. 清理中间文件

生成报告后,清理报告目录中的所有中间文件:

8.1 查找需要清理的文件

  • 使用 Glob 工具查找报告目录中的所有中间文件:<report_dir>/**/*.{xml,ofd,zip}
  • 这些文件包括:
    • .xml 文件(电子发票元数据)
    • .ofd 文件(电子发票格式)
    • .zip 文件(可能残留的压缩包)

8.2 删除文件

  • 使用 Bash 工具批量删除:find "<report_dir>" -type f \( -name "*.xml" -o -name "*.ofd" -o -name "*.zip" \) -delete
  • 或者逐个删除每个找到的文件
  • 统计删除的文件数量

8.3 输出提示

🗑️ 已清理 X 个中间文件(.xml, .ofd, .zip)

9. 打包最终文件

完成所有处理后,将报告目录中的所有文件打包成一个 ZIP 压缩包:

9.1 确定压缩包名称

  • 获取报告目录的文件夹名称(basename)
  • 压缩包名称格式:<文件夹名称>.zip
  • 例如:如果报告目录是 /Users/m/Documents/发票2024,则压缩包名称为 发票2024.zip

9.2 创建压缩包

  • 在报告目录内部创建 ZIP 文件(与invoices.md同级)
  • 使用 Bash 工具执行压缩命令:
    cd "<report_dir>" && zip -r "<folder_name>.zip" . -x "*.DS_Store" -x "<folder_name>.zip"
    
  • 参数说明:
    • -r: 递归压缩所有文件和子目录
    • .: 压缩当前目录的所有内容
    • -x "*.DS_Store": 排除 macOS 系统文件
    • -x "<folder_name>.zip": 排除zip文件自身,避免递归
    • 压缩包内容为目录中的所有文件(不含目录本身作为根文件夹)

9.3 验证压缩包

  • 检查压缩包是否成功创建
  • 获取压缩包的文件大小(可选)

9.4 输出提示

📦 已打包最终文件:<path_to_zip_file>
   压缩包大小:X.XX MB

示例

输入目录:/Users/m/Documents/发票2024
生成的压缩包:/Users/m/Documents/发票2024/发票2024.zip
生成的报告:/Users/m/Documents/发票2024/invoices.md

注意事项

  • 使用 TodoWrite 工具跟踪处理进度
  • 所有正式发票都应计入统计,包括电子发票、纸质发票扫描件等
  • 对于无法识别的图片或非正式发票(如支付截图、转账记录),记录到"未识别文件"部分,说明原因
  • 重复发票(如行程单+对应电子发票)需要识别并避免重复计算金额
  • 金额提取失败时标记为 0.00 并在备注中说明
  • 发票字段确认
    • 每张发票提取后必须二次确认发票号和金额
    • 发现提取错误时重新读取图片
    • 无法确认的字段标记"⚠️ 待人工复核"
  • ZIP 文件处理
    • 解压后将所有文件移动到报告目录(扁平化,不保留文件夹结构)
    • 删除原始 ZIP 文件和临时解压目录
  • 文件清理
    • 扫描前自动删除所有 .xml.ofd 文件
    • 生成报告后再次清理所有 .xml.ofd.zip 文件
    • 这些文件通常是电子发票的元数据文件和临时压缩包,不需要保留
  • 最终打包
    • 完成所有处理后,将报告目录中的所有文件打包成一个 ZIP 压缩包
    • 压缩包名称以文件夹名称命名(例如:发票2024.zip
    • 压缩包保存在报告目录内部(与invoices.md同级)
    • 排除 macOS 系统文件(.DS_Store)和zip文件自身
  • 报告文件名固定为 invoices.md
  • 完成后输出报告的完整路径和按分类的统计摘要
  • 完成后输出已处理的 ZIP 文件数量、清理的文件数量和最终压缩包路径
  • 发票分类的优先级:按实际内容判断,如"住宿费增值税发票"应归入"住宿"类

错误处理

  • 如果路径不存在,提示用户
  • 如果没有找到任何发票,生成空报告
  • 如果图片损坏无法读取,记录错误并继续处理其他文件

输出示例

完成后输出:

🗑️ 已清理 3 个无用文件(.xml, .ofd)
📦 已解压并清理 2 个 ZIP 文件

✅ 发票扫描完成

📊 统计摘要:
- 总计: 15 张发票
- 总金额: ¥8,430.50
- ✓ 金额已校验

按分类统计:
📍 市内交通: 5 张 (¥230.00)
  - 打车: 4 张 (¥210.00)
  - 地铁: 1 张 (¥20.00)

✈️ 长途交通合计: 4 张 (¥3,600.00)  ← 飞机+火车+长途汽车总和
  - 飞机票: 2 张 (¥2,400.00)
  - 火车票: 2 张 (¥1,200.00)

🏨 住宿: 2 张 (¥4,200.00)

📦 其他: 4 张 (¥400.50)
  - 餐饮: 3 张 (¥350.50)
  - 通讯: 1 张 (¥50.00)

💡 金额验证:
  市内交通 (¥230.00) + 长途交通 (¥3,600.00) + 住宿 (¥4,200.00) + 其他 (¥400.50) = 总计 (¥8,430.50) ✓

⚠️ 1 张发票需要人工复核(发票字段提取存疑)

📄 报告已生成:
- /path/to/invoices.md

🗑️ 已清理 5 个中间文件(.xml, .ofd, .zip)

📦 已打包最终文件:/path/to/发票2024.zip
   压缩包大小:12.45 MB
   位置:报告目录内部

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

text-to-speech

No summary provided by upstream source.

Repository SourceNeeds Review
34-wlzh
General

youtube-to-blog-post

No summary provided by upstream source.

Repository SourceNeeds Review
34-wlzh
General

image-generator

No summary provided by upstream source.

Repository SourceNeeds Review
27-wlzh
General

x-fetcher

No summary provided by upstream source.

Repository SourceNeeds Review
27-wlzh