# Wlzh Invoice Scanner

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

- Skill: `wlzh/wlzh-invoice-scanner` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add wlzh/wlzh-invoice-scanner`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wlzh/wlzh-invoice-scanner/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: wlzh (https://skillmd.com/u/wlzh)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/wlzh/wlzh-invoice-scanner

---


# 发票扫描器

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

## 用法示例

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

# Windows 路径建议加引号；JSON 配置中反斜杠需写成 \\\\，命令输入可直接使用 Windows 路径
/wlzh-invoice-scanner "C:\\Users\\Alice\\Documents\\发票"
/wlzh-invoice-scanner "C:\\Users\\Alice\\Documents\\receipts.zip"

# 招待费/客户模式：输入路径或附加提示包含「招待费」「招待」「客户」任一关键词即启用
/wlzh-invoice-scanner ./客户发票
/wlzh-invoice-scanner ./餐饮发票 招待
/wlzh-invoice-scanner "C:\\Users\\Alice\\Documents\\客户招待" 招待费
```

## 工作流程

### 1. 接收参数
- 用户会提供一个目录路径或 ZIP 文件路径
- 默认路径是当前工作目录
- 记录原始输入路径，用于后续保存报告
- 路径包含空格、括号或 Windows 盘符时，建议用引号包住，例如：`/wlzh-invoice-scanner "C:\\Users\\Alice\\Documents\\发票"`

#### 1.1 模式判定（默认统计模式 / 招待费客户模式）

**默认行为不变**：如果输入路径和附加参数都不包含触发词，继续执行原有完整发票统计逻辑（分类、发票号汇总、金额累加、Python 校验、结构化摘要卡片）。

**招待费/客户模式触发条件**：仅根据用户本次输入判断，不根据发票内容自动猜测。检查原始输入路径和路径后的附加提示，只要包含以下任一关键词即启用招待费/客户模式：
- `招待费`
- `招待`
- `客户`

示例：
```bash
/wlzh-invoice-scanner ./客户发票
/wlzh-invoice-scanner ./餐饮发票 招待
/wlzh-invoice-scanner ./receipts.zip 招待费
```

招待费/客户模式下，每张发票独立输出为一条 `招待费` 明细；**不生成发票号汇总，不进行金额累计，不输出分类统计**。

### 2. 文件扫描与预处理

#### 2.1 清理无用文件（第一步）
在开始扫描之前，先清理目录中的无用文件：
- 使用 Bash 工具查找并删除：`find "<input_dir>" -type f \( -name "*.xml" -o -name "*.ofd" \) -delete`
- 输出提示：`已清理 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 文件
     - 使用 Bash 查找：`find "<input_dir>" -name "*.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>"`
     - 输出提示：`已解压并清理 X 个 ZIP 文件`

#### 2.3 扫描发票文件
  - **每次处理前必须重新扫描目录**，不依赖任何旧报告或缓存
  - 递归扫描输入目录及所有子目录中的图片文件
  - 文件类型：`*.jpg`, `*.jpeg`, `*.png`, `*.pdf`
  - 使用 Bash 查找：`find "<input_dir>" -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.pdf" \) | sort`
  - 输出文件列表和数量

### 3. 发票识别与分类

对每个文件：
- 使用 Read 工具读取图片/PDF 内容
- 分析内容判断发票类型，分为以下五大类：

**A. 市内交通发票**
  * 🚕 打车发票（滴滴、出租车、网约车等）
  * 🚇 地铁票、公交票

**B. 长途交通发票** ⚠️ 铁路电子客票一律归此类
  * 🛫 飞机票（机票行程单、电子客票行程单）
  * 🚄 火车票（高铁、动车、普通列车、城际列车 — **所有铁路电子客票，不论距离长短**）
  * 🚌 长途汽车票

> **⚠️ 分类硬规则（v3.1 起强制执行）**：票据类型为"铁路电子客票"→ 一律归"长途交通"。市内交通**仅限**出租车/网约车发票、地铁票、公交票。即使行程是短途（如樟木头→深圳、深圳北→樟木头），铁路电子客票也归长途交通。

**C. 住宿发票**
  * 🏨 酒店住宿发票
  * 🏠 酒店结账单（仅作佐证，不重复计费）
  * 增值税专用发票（住宿服务）

**D. 退票费** ⚠️ 独立分类（v3.2 起新增）
  * 🎫 火车退票费（改签/退票产生的手续费、差额退票费）
  * ✈️ 机票退票费（机票退改签手续费）
  * 识别标志：票据上标注"退票费"、"改签费"、"退票手续费"、"差额退票"等字样
  * 注意：退票费金额通常为负数或较小的正数，与其他交通费用分开统计

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

### 4. 重复发票去重

**⚠️ 必须在识别阶段执行去重，避免重复计费**

去重规则：
1. **按发票号码去重**：如果两个文件有相同的发票号码，只计入一次
2. **行程单 vs 电子发票**：滴滴行程报销单、弘扬行程单是电子发票的辅助凭证，不单独计费
3. **结账单 vs 住宿发票**：酒店结账单是住宿发票的辅助凭证，不单独计费
4. **文件名 `_1` 后缀**：通常表示重复下载/打印的同一张发票

标记方式：
- 计入的发票标记为 `计入: 是`
- 重复/佐证文件标记为 `计入: 否`，并注明对应的正式发票号码

### 5. 信息提取

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

**A. 市内交通发票字段：**
- 分类: "市内交通"
- 具体类型: "打车" / "地铁" / "公交"
- 日期
- 时间
- 起点（如有）
- 终点（如有）
- 距离（公里，如有）
- 金额
- 平台/公司（滴滴、出租车等）
- 发票号码（如有）
- 文件路径

**B. 长途交通发票字段：**

*飞机票：*
- 分类: "长途交通"
- 具体类型: "飞机票"
- 乘客姓名
- 航班号
- 出发地（城市+机场）
- 目的地（城市+机场）
- 日期（起飞日期）
- 时间（起飞时间）
- 金额（合计，含票价+燃油+税费+民航基金）
- 票价明细（票价/燃油附加费/增值税/民航发展基金）
- 发票号码
- 电子客票号码
- 文件路径

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

**C. 住宿发票字段：**
- 分类: "住宿"
- 具体类型: "酒店发票" / "酒店结账单"
- 酒店名称
- 客人姓名（如有）
- 房间号（如有）
- 入住日期
- 离店日期
- 天数
- 金额
- 发票号码
- 发票类型（增值税专用/普通发票）
- 文件路径

**D. 退票费字段：**
- 分类: "退票费"
- 具体类型: "火车退票费" / "机票退票费"
- 乘客姓名
- 原车次/航班号（如有）
- 日期
- 金额
- 发票号码
- 退票原因/说明
- 文件路径

**E. 其他发票字段：**
- 分类: "其他"
- 具体类型: "餐饮" / "通讯" / "办公用品" / "快递" / "其他"
- 商户/服务商名称
- 日期
- 金额
- 发票号码（如有）
- 项目/服务内容描述
- 文件路径

#### 5.1 招待费/客户模式字段（输入含 `招待费` / `招待` / `客户` 时）

招待费/客户模式不再按五大分类输出，而是**每张正式发票单独一行**。多张发票必须写多行，不合并。

每张发票提取字段：
- 费用名称：固定为 `招待费`
- 发票号码
- 金额（仍使用复制友好格式，如 `¥268`、`¥520.5`）
- 餐厅/商户名称
- 开票日期（如有）
- 文件路径
- 备注（字段不确定、重复、非正式发票等）

餐厅/商户名称提取优先级：
1. 优先取 `销售方名称` / `商户名称` / `餐厅名称` / `服务商名称`
2. 如果没有明确字段，取票面上最可信的餐饮商户名称
3. 如果仍不确定，写 `待确认`，并在备注中标注 `⚠️ 餐厅/商户名称存疑，请人工复核`

招待费/客户模式仍需二次确认每张发票的发票号码、金额、餐厅/商户名称；无法确认的字段标记为 `待确认` 或添加 `⚠️` 备注。

### 6. 发票字段提取确认机制

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

#### 6.1 重点确认项

**发票号码确认：**
- 发票号码通常是一串数字（如：20位数字）
- 如果不确定，标注为"待确认"并在备注中说明

**金额确认：**
- 优先提取"价税合计"或"总金额"
- 确认数字、小数点位置完全正确
- 转换为数字后保留2位小数用于计算校验
- **展示金额时使用复制友好格式**：不加千分位逗号，去掉小数尾随 0；例如 `¥1277`、`¥44.2`、`¥400.5`、`¥0`。不要输出 `¥1,277.00`、`¥44.20`、`¥400.50`、`¥0.00`。

**日期确认：**
- 确认日期格式正确（YYYY-MM-DD）
- 对于机票/火车票，确认是出发日期而非购买日期

#### 6.2 异常处理
如果确认时发现问题：
- 重新读取发票图片进行二次提取
- 如果仍然无法确认，在发票记录中添加标记：
  ```
  备注: ⚠️ 字段提取存疑，请人工复核
  ```

### 7. 金额计算验证逻辑 ⚠️ 核心防错机制

**⚠️ 极其重要：LLM 心算经常出错（如 1837+222.58+1393.04 算成 3052 而非 3452），必须用代码验证**

> 招待费/客户模式不输出总金额、分类小计和金额累加结果，因此跳过“总额/分类合计”校验；但每张发票金额仍必须逐张二次确认，并使用复制友好金额格式展示。

#### 7.1 提取阶段
- 所有金额必须转换为浮点数（保留2位小数）
- 如果提取失败，计算值设为 0.00，并在备注中标注 "金额提取失败"；面向用户展示时写作 `¥0`

#### 7.2 默认统计模式校验阶段（必须执行，不可跳过）

**生成报告后，必须使用 Bash 工具运行 Python 进行独立算术校验：**

```bash
python3 -c "
items = [<所有发票金额列表，用逗号分隔>]
total = sum(items)
print(f'单张合计: {total:.2f}')
print(f'报告总额: <报告中的总额>')
print(f'校验: {\"✓\" if abs(total - <报告总额>) < 0.02 else \"✗ 差异!\"}')"
```

校验规则：
- `单张金额合计 === 总金额`（允许误差 ±0.02 元）
- `市内交通 + 长途交通 + 住宿 + 退票费 + 其他 === 总金额`
- 如果校验失败，**必须重新计算并修正报告**，直到校验通过
- 在报告中添加校验标记：`✓ 金额已校验`

#### 7.3 长途交通费用合并规则
- **飞机票** + **火车票** + **长途汽车票** = **长途交通合计**
- 在统计输出中必须显示：
  * 长途交通合计（飞机+火车+长途汽车的总和）
  * 每个子类型的明细（飞机票、火车票各自的数量和金额）

#### 7.4 金额展示格式（复制友好）

**所有面向用户的金额展示（报告正文、表格、统计摘要、金额验证、结构化摘要卡片）都必须使用复制友好格式：**
- 禁止千分位逗号：`¥1277`，不要写 `¥1,277.00`
- 去掉小数尾随 0：`¥44.2`，不要写 `¥44.20`；`¥400.5`，不要写 `¥400.50`
- 小数部分全为 0 时去掉小数点和两个 0：`¥230`，不要写 `¥230.00`
- 0 元显示为 `¥0`，不要写 `¥0.00`
- 计算和 Python 校验仍保留数值精度；只有最终展示做格式化

建议使用这个 Python 格式化函数生成展示金额：
```python
def fmt_yuan(x):
    s = f"{float(x):.2f}".rstrip('0').rstrip('.')
    return f"¥{s}"
```

### 8. 生成报告

生成 Markdown 报告文件到输入路径的目录：
- 如果输入是 ZIP 文件，报告保存到 ZIP 文件所在的目录
- 如果输入是目录，报告保存到该目录
- **报告文件名**：使用文件夹名称作为报告文件名，格式为 `<文件夹名称>.md`
  - 例如：目录 `/path/to/降噪客户拜访 0518-0521` → 报告 `降噪客户拜访 0518-0521.md`
  - **不再使用** `invoices.md`
- 压缩包名称格式不变，仍为 `<文件夹名称>.zip`

**<文件夹名称>.md** - 可读性报告，根据模式输出：

**默认统计模式包含：**
1. **发票号汇总行**（顶部）：将所有发票号用斜杠分隔连接成一行，格式为 "发票号1/发票号2/发票号3"，方便用户复制粘贴
2. 扫描日期和目录信息
3. **汇总统计**（带金额校验标记）
   - 总金额、总发票数
   - 五大分类的数量和金额
   - ✓ 金额已校验标记
4. 按五大分类分组的详细表格
5. 每个发票的详细信息
6. **重复发票说明**（列出哪些文件是重复/佐证，对应哪张正式发票）
7. 未识别文件列表（含原因说明）
8. **需要复核的发票**（如果有标记为⚠️的发票）

**招待费/客户模式包含：**
1. 扫描日期、目录信息、模式说明（`招待费/客户模式`）
2. 每张发票一行的招待费明细表；多张发票必须写多行
3. 明细表字段：`序号`、`费用名称`、`发票号码`、`金额`、`餐厅/商户`、`开票日期`、`文件路径`、`备注`
4. 每行 `费用名称` 固定写 `招待费`
5. 未识别文件列表（含原因说明）
6. 重复/佐证文件说明（如有）
7. 需要复核的发票（如有）

招待费/客户模式**不得输出**：发票号汇总行、总金额、金额累加、五大分类统计、分类金额校验公式、默认统计摘要卡片。

**发票号汇总行示例**：
```
📋 发票号汇总（可复制）: 1234567890/9876543210/5555666677/8888999900
```

### 9. 清理中间文件

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

- 使用 Bash 工具批量删除：`find "<report_dir>" -type f \( -name "*.xml" -o -name "*.ofd" -o -name "*.zip" \) -delete`
- 注意：先创建最终 zip（第10步），然后再清理 zip？不——清理中间文件在打包之前执行，因为打包时会创建新 zip
- **清理顺序**：先清理旧的中间文件 → 再打包最终 zip
- 输出提示：`🗑️ 已清理 X 个中间文件（.xml, .ofd, .zip）`
- 此步骤默认统计模式和招待费/客户模式都必须执行。

### 10. 打包最终文件

完成所有处理后，将报告目录中的所有文件打包成一个 ZIP 压缩包（默认统计模式和招待费/客户模式都必须执行）：

#### 10.1 确定压缩包名称
- 获取报告目录的文件夹名称（basename）
- 压缩包名称格式：`<文件夹名称>.zip`
- 例如：如果报告目录是 `/path/to/发票2024`，则压缩包名称为 `发票2024.zip`

#### 10.2 创建压缩包
- 在报告目录**内部**创建 ZIP 文件（与报告 .md 同级）
- 使用 Bash 工具执行压缩命令：
  ```bash
  cd "<report_dir>" && rm -f "<folder_name>.zip" && zip -r "<folder_name>.zip" . -x "*.DS_Store" -x "<folder_name>.zip"
  ```
- 参数说明：
  * `-r`: 递归压缩所有文件和子目录
  * `.`: 压缩当前目录的所有内容
  * `-x "*.DS_Store"`: 排除 macOS 系统文件
  * `-x "<folder_name>.zip"`: 排除 zip 文件自身，避免递归

#### 10.3 验证压缩包
- 检查压缩包是否成功创建
- 获取压缩包的文件大小

#### 10.4 输出提示
```
📦 已打包最终文件：<path_to_zip_file>
   压缩包大小：X.XX MB
```

**示例**：
```
输入目录：/path/to/发票2024
生成的报告：/path/to/发票2024/发票2024.md
生成的压缩包：/path/to/发票2024/发票2024.zip
```

## 注意事项

- 所有正式发票都应计入统计，包括电子发票、纸质发票扫描件等
- 对于无法识别的图片或非正式发票（如支付截图、转账记录），记录到"未识别文件"部分，说明原因
- **重复发票去重**：
  * 按发票号码去重，相同发票号码只计入一次
  * 行程报销单/结账单是电子发票的辅助凭证，不重复计费
  * 在报告中明确标注哪些文件是重复/佐证
- 金额提取失败时计算值标记为 0.00，展示为 `¥0`，并在备注中说明
- **发票字段确认**：
  * 每张发票提取后必须二次确认发票号和金额
  * 发现提取错误时重新读取图片
  * 无法确认的字段标记"⚠️ 待人工复核"
- **文件清理**：
  * 扫描前自动删除所有 `.xml` 和 `.ofd` 文件
  * 打包前再次清理所有 `.xml`、`.ofd` 和旧 `.zip` 文件
- **最终打包**：
  * 压缩包名称以文件夹名称命名（例如：`发票2024.zip`）
  * 压缩包保存在报告目录内部（与报告 .md 同级）
  * 排除 macOS 系统文件（.DS_Store）和 zip 文件自身
- **报告文件名**：使用文件夹名称命名（如 `降噪客户拜访 0518-0521.md`），不再使用 `invoices.md`
- **分类硬规则**：
  * 铁路电子客票（火车票/高铁/动车/城际）→ 一律归"长途交通"
  * 市内交通仅限出租车/网约车发票、地铁票、公交票
  * 住宿费增值税发票 → 归入"住宿"类
  * 退票费/改签费 → 单独归入"退票费"类（v3.2 新增）
- 完成后输出报告的完整路径和按分类的统计摘要
- 完成后输出已处理的 ZIP 文件数量、清理的文件数量和最终压缩包路径

## 错误处理

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

## 输出示例

完成后输出：
```
🗑️ 已清理 3 个无用文件（.xml, .ofd）
📦 已解压并清理 2 个 ZIP 文件

✅ 发票扫描完成

📊 统计摘要：
- 总计: 15 张发票
- 总金额: ¥8430.5
- ✓ 金额已校验（Python 独立验算通过）

📋 发票号汇总: 1234567890/9876543210/5555666677/8888999900

按分类统计：
📍 市内交通: 5 张 (¥230)
  - 打车: 4 张 (¥210)
  - 地铁: 1 张 (¥20)

✈️ 长途交通合计: 4 张 (¥3600)  ← 飞机+火车+长途汽车总和
  - 飞机票: 2 张 (¥2400)
  - 火车票: 2 张 (¥1200)

🏨 住宿: 2 张 (¥4200)

🎫 退票费: 0 张 (¥0)

📦 其他: 4 张 (¥400.5)
  - 餐饮: 3 张 (¥350.5)
  - 通讯: 1 张 (¥50)

💡 金额验证：
  单张合计: 230.00 + 3600.00 + 4200.00 + 0.00 + 400.50 = 8430.50
  市内交通 (¥230) + 长途交通 (¥3600) + 住宿 (¥4200) + 退票费 (¥0) + 其他 (¥400.5) = 总计 (¥8430.5) ✓

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

📄 报告已生成：
- /path/to/发票2024.md

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

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

### 11. 结构化摘要卡片（必须输出）

**⚠️ 处理完成后，必须在终端最后输出一个结构化摘要卡片**。默认统计模式输出分类摘要卡片；招待费/客户模式输出逐张明细卡片。

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  📂 降噪客户拜访 0518-0521
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  💰 总金额: ¥2811.55 (7张)
  📋 发票号: 26952000002075124346/26952000002055155446/26449123774000016358/26338891111036673445/26952000002037354826/26449165860002800464/26448784110004571321

  📊 分类:
     长途交通  5张  ¥1875  (飞机票×2 ¥1820 | 火车票×3 ¥55)
     住宿     3张  ¥936.55
     退票费   0张  ¥0
     市内交通 0张  ¥0
     其他     0张  ¥0
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

**默认统计模式摘要卡片要求：**
- 使用分隔线框住，视觉突出
- 文件夹名称醒目展示
- 发票号用斜杠分隔在一行内
- 分类按金额从大到小排列
- 长途交通需展开子类型（飞机票×N ¥X | 火车票×N ¥X）
- 没有发票的分类显示 `0张 ¥0`

**招待费/客户模式摘要卡片要求：**
- 使用分隔线框住，视觉突出
- 文件夹名称醒目展示，并标注 `模式：招待费/客户`
- 不输出总金额、不输出发票号汇总、不输出分类统计
- 每张发票单独一行：`招待费  发票号  金额  餐厅/商户`
- 多张发票必须输出多行

招待费/客户模式示例：
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  📂 客户餐饮发票
  模式：招待费/客户
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  招待费  1234567890  ¥268  深圳市某某餐厅
  招待费  9876543210  ¥520.5  广州市某某酒楼
  招待费  5555666677  ¥188  上海某某饭店
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

- 这是最后一步输出，确保用户一眼看到关键信息

### 12. 追加汇总到配置文件（如果配置存在）

**⚠️ 在步骤 11 输出摘要卡片之后执行**

#### 12.1 读取配置
- 读取 skill 目录下的 `config.json` 文件
- 路径：与 `SKILL.md` 同级目录下的 `config.json`
- 如果文件不存在，**跳过此步骤**，不影响正常流程
- 配置格式：
  ```json
  {
    "summary_dir": "/path/to/baoxiao/",
    "summary_file": "报销汇总.md"
  }
  ```
- Windows 路径可使用正斜杠：`"C:/Users/Alice/Documents/报销/"`
- Windows 路径如使用反斜杠，JSON 中必须双写：`"C:\\Users\\Alice\\Documents\\报销\\"`

#### 12.2 默认统计模式：追加摘要卡片
- 拼接完整路径：`summary_dir + summary_file`
- 如果文件不存在，创建新文件，先写入标题行：
  ```markdown
  # 报销汇总

  ```
- 如果文件已存在，读取现有内容
- **将当前行程的摘要卡片插入到文件正文最前面**（紧跟标题之后）
- 插入格式（每条记录以日期标题开头）：

```markdown
# 报销汇总

## YYYY-MM-DD 文件夹名称
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  💰 总金额: ¥XXX (N张)
  📋 发票号: xxx/xxx/xxx

  📊 分类:
     长途交通  N张  ¥XXX  (飞机票×N ¥XXX | 火车票×N ¥XXX)
     市内交通  N张  ¥XXX
     住宿     N张  ¥XXX
     退票费   N张  ¥XXX
     其他     N张  ¥XXX
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

（旧记录继续...）
```

#### 12.3 招待费/客户模式：追加逐张明细

当本次输入触发招待费/客户模式时，仍然写入同一个 `summary_dir + summary_file`，并插入到汇总 MD 顶部（紧跟 `# 报销汇总` 标题之后）。

- 每次扫描插入一个日期标题和一个明细表
- 多张发票必须写多行，一张发票一行
- 每行 `费用名称` 固定为 `招待费`
- 不写发票号汇总行
- 不写本次总金额
- 不写分类统计或金额累加

插入格式：

```markdown
# 报销汇总

## YYYY-MM-DD 文件夹名称 招待费

| 日期 | 费用名称 | 发票号码 | 金额 | 餐厅/商户 | 来源文件 | 备注 |
|------|----------|----------|------|-----------|----------|------|
| YYYY-MM-DD | 招待费 | 1234567890 | ¥268 | 深圳市某某餐厅 | invoice1.pdf |  |
| YYYY-MM-DD | 招待费 | 9876543210 | ¥520.5 | 广州市某某酒楼 | invoice2.jpg |  |

（旧记录继续...）
```

#### 12.4 注意事项
- 每条记录之间保留一个空行（卡片末尾 `━━━━` 或表格后加一个空行再接下一条 `##` 标题）
- `YYYY-MM-DD` 使用当前扫描日期
- `文件夹名称` 使用被扫描的文件夹 basename
- 最新的记录在最上面（按时间倒序排列）
- 如果 `summary_dir` 目录不存在，输出警告并跳过
- 操作完成后输出提示：`📝 已追加摘要到 <summary_file 路径>`

#### 12.5 配置文件说明
- `config.json`：用户的个人配置，包含真实路径，**不应提交到版本库**
- `config.json.sample`：示例配置文件，供其他用户参考
- `.gitignore`：排除 `config.json`，防止意外提交个人路径

