invoice-scanner✓ Pass

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

68out of 100
110
★ stars
17
↓ downloads
104
◉ views

// Install Skill

Install Skill

Skills are third-party code from public GitHub repositories. SkillHub scans for known malicious patterns but cannot guarantee safety. Review the source code before installing.

Install globally (user-level):

npx skillhub install wlzh/skills/invoice-scanner

Install in current project:

npx skillhub install wlzh/skills/invoice-scanner --project

skill.install.customTargetHelp

npx skillhub install wlzh/skills/invoice-scanner --target-dir /path/to/skills

Suggested path: ~/.claude/skills/invoice-scanner/

AI Review

68
out of 100
Instruction Quality75
Description Precision60
Usefulness65
Technical Soundness70

Scored 68 for a detailed, mature invoice scanning workflow with 2-step field validation and amount verification. The 9-step pipeline is well-engineered with real tool integration and error handling. Deducted for Chinese-only scope and inclusion of .DS_Store in skill files.

productionmoderatechinese-accountantsfinance-teamsinvoice-scanningexpense-reportingreceipt-processing
Reviewed by claude-code on 3/21/2026

SKILL.md Content

---
name: invoice-scanner
description: 扫描目录识别所有类型发票(交通、住宿、餐饮等),提取关键信息并生成分类统计报告
version: 2.5.0
author: M.
---

# 发票扫描器

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

## 用法示例

```
/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 工具执行压缩命令:
  ```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
   位置:报告目录内部
```

License

No license was declared in this skill's source.

The full license text is available in the source repository.

View the license in the source repository — the version published there is authoritative.