295 lines
7.4 KiB
Markdown
295 lines
7.4 KiB
Markdown
# 铁路系统发票批量处理使用说明
|
||
|
||
## 原则
|
||
|
||
- 只使用铁路 12306 官方页面/App 完成开票。
|
||
- 本工具不保存 12306 账号密码,不绕过验证码或身份核验。
|
||
- 公司抬头、税号、接收邮箱从本地配置读取;不要写入 `.env`。
|
||
- `config.local.json` 已被 `.gitignore` 忽略,不要把真实公司信息写进 `config.example.json`。
|
||
|
||
## 初始化
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py init
|
||
```
|
||
|
||
先复制一份本地配置:
|
||
|
||
```bash
|
||
cp config.example.json config.local.json
|
||
```
|
||
|
||
然后把 `config.local.json` 里的三项改成真实值:
|
||
|
||
```json
|
||
{
|
||
"company_title": "公司全称",
|
||
"tax_id": "统一社会信用代码",
|
||
"receiver_email": "统一收票邮箱"
|
||
}
|
||
```
|
||
|
||
## 导入开票码
|
||
|
||
把旅行社给的二维码图片或 PDF 放到:
|
||
|
||
```text
|
||
data/input/开票码批次/
|
||
```
|
||
|
||
执行:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py import-codes
|
||
```
|
||
|
||
输出台账:
|
||
|
||
```text
|
||
work/invoice_tasks.csv
|
||
```
|
||
|
||
如果本机没有二维码解码器,任务会进入 `待补码`。人工扫码后,把链接或码写入:
|
||
|
||
```text
|
||
data/input/开票码批次/manual_codes.csv
|
||
```
|
||
|
||
再执行一次 `import-codes`。
|
||
|
||
如果这是铁路“扫码开票单”截图,二维码通常只能用 12306 App 扫。可以在
|
||
`manual_codes.csv` 里用 `APP_SCAN` 补录开票单号和代办人:
|
||
|
||
```csv
|
||
source_file,passenger,ticket_ref,code
|
||
data/input/开票码批次/ScreenShot_xxx.png,张*木,EKP37846902581249178,APP_SCAN
|
||
```
|
||
|
||
## 真实手工 POC
|
||
|
||
第一次必须用 1-3 张真实开票码在 Mac 上跑通:
|
||
|
||
1. 把真实二维码图片/PDF 放进 `data/input/开票码批次/`。
|
||
2. 执行 `python3 scripts/invoice_tool.py import-codes`。
|
||
3. 打开 `work/invoice_tasks.csv` 看状态:
|
||
- `待网页开票`:说明二维码可在 Mac 网页端继续,后续可做更强的网页自动化。
|
||
- `待人工扫码` / `待补码`:说明大概率要 12306 App 扫码,Mac 端只能做台账、收票和归档自动化。
|
||
4. 手动登录 12306,完成身份核验。
|
||
5. 执行 `python3 scripts/invoice_tool.py rpa-plan`,打开 `output/rpa_helper.html`。
|
||
6. 用辅助页逐条打开官方页面,填写统一公司抬头、税号、邮箱并提交。
|
||
7. 收到邮件后,把 `.eml` 放入 `data/mail/`,或把附件放入 `data/attachments/`。
|
||
8. 执行归集和汇总命令,检查 `output/发票汇总.xlsx`。
|
||
|
||
## Android 自动填写身份证后 8 位
|
||
|
||
适用场景:12306 App 扫码后,页面显示脱敏姓名(如 `张*岩`),要求输入身份证后 8 位验证码。
|
||
|
||
### 本地 Web UI
|
||
|
||
启动本地页面:
|
||
|
||
```bash
|
||
python3 scripts/web_ui.py
|
||
```
|
||
|
||
打开:
|
||
|
||
```text
|
||
http://127.0.0.1:8765
|
||
```
|
||
|
||
页面支持上传 `.xlsx/.xlsm/.csv` 名单、查看成功/失败/处理中/未操作统计、显示手机连接/当前 App 状态、启动手机 12306、抓取当前页队列、启动/停止批处理,并持续读取:
|
||
|
||
```text
|
||
work/android_invoice_progress.csv
|
||
```
|
||
|
||
这个 CSV 是逐人处理结果表,页面里的“下载进度表”会直接下载它。
|
||
|
||
### 1. 导入 Excel 名单
|
||
|
||
名单里至少要有“姓名”和“身份证/证件号码”两类列名。支持 `.xlsx/.xlsm/.csv`:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py import-roster --input /path/to/名单.xlsx
|
||
```
|
||
|
||
输出:
|
||
|
||
```text
|
||
work/passenger_roster.csv
|
||
```
|
||
|
||
先离线验证一个脱敏姓名:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py match-roster --masked-name '张*岩'
|
||
```
|
||
|
||
如果出现 `ambiguous`,说明同名脱敏后匹配到多人,必须人工确认。
|
||
|
||
### 2. 连接 Android 手机
|
||
|
||
手机开启 USB 调试后连接 Mac:
|
||
|
||
```bash
|
||
/opt/homebrew/share/android-commandlinetools/platform-tools/adb devices
|
||
```
|
||
|
||
先手工在 12306 App 扫码,进入要求填写身份证后 8 位的页面。
|
||
|
||
读取当前页面文字:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-dump --output work/android_page.txt
|
||
```
|
||
|
||
如果能看到 `张*岩` 这类文字,就可以自动匹配并输入身份证后 8 位:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-type-id --tap 500,1200
|
||
```
|
||
|
||
`--tap` 是输入框坐标,需要按手机页面实际位置改。也可以用按钮/输入框可访问文字:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-type-id --click-text '证件号码后8位' --contains
|
||
```
|
||
|
||
### 3. 后续点击流程
|
||
|
||
复制并修改模板:
|
||
|
||
```bash
|
||
cp templates/android_flow.example.json templates/android_flow.local.json
|
||
python3 scripts/invoice_tool.py android-run-flow --flow templates/android_flow.local.json
|
||
```
|
||
|
||
模板支持这些动作:
|
||
|
||
| action | 用途 |
|
||
|---|---|
|
||
| `click_text` | 按完整文字点击 |
|
||
| `click_contains` | 按包含文字点击 |
|
||
| `tap` | 按坐标点击 |
|
||
| `input_id8_for_visible_masked_name` | 从当前页面识别 `张*岩` 并输入身份证后 8 位 |
|
||
| `input_config` | 输入 `config.local.json` 中的公司抬头、税号、邮箱 |
|
||
| `input_text` | 输入固定文本 |
|
||
| `sleep` | 等待 |
|
||
| `keyevent` | 发送 Android keyevent |
|
||
|
||
真实 12306 App 页面可能不暴露全部控件文字。遇到找不到控件时,用 `android-dump` 看文字;如果没有文字,就改用 `tap` 坐标。
|
||
|
||
### 4. 单步状态机
|
||
|
||
当前页面已校准后,优先用单步命令。它每次只做一步动作,默认等待 `10 秒`,未出现目标状态再等 `20 秒`,仍不成功就暂停:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-invoice-step
|
||
```
|
||
|
||
如果当前在“发票开具成功”页,默认会点 `继续开票` 回列表。需要先发邮件时才加:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-invoice-step --send-email
|
||
```
|
||
|
||
### 5. 扫码列表页批处理队列
|
||
|
||
扫码开票单列表页不要用固定坐标。先抓当前可见的一页,按脱敏姓名和名单生成队列:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-build-page-queue
|
||
```
|
||
|
||
队列文件:
|
||
|
||
```text
|
||
work/android_page_queue.csv
|
||
```
|
||
|
||
然后反复跑:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-page-step
|
||
```
|
||
|
||
行为:
|
||
|
||
- 当前在扫码列表页:按本页队列点击下一个可匹配人员的 `开具`。
|
||
- 当前在核验/发票管理/发票信息/成功页:执行普通单步状态机。
|
||
- 本页没有可处理人员:输出 `page_done`,不自动乱滑。
|
||
|
||
本页处理完后上滑到下一页并重建队列:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-page-next --rebuild
|
||
```
|
||
|
||
也可以让 `android-page-step` 在本页完成后自动上滑:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py android-page-step --auto-next
|
||
```
|
||
|
||
## 网页辅助开票
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py rpa-plan
|
||
```
|
||
|
||
打开:
|
||
|
||
```text
|
||
output/rpa_helper.html
|
||
```
|
||
|
||
先手动登录 12306 并完成核验,再逐条打开官方页面,复制公司抬头、税号和邮箱提交。
|
||
|
||
## 邮件和附件归集
|
||
|
||
如果有 `.eml` 邮件文件:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py collect-mail
|
||
```
|
||
|
||
如果已经把 PDF/OFD/XML 附件下载到 `data/attachments/`:
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py collect-attachments
|
||
```
|
||
|
||
归集后的发票在:
|
||
|
||
```text
|
||
output/invoices/
|
||
```
|
||
|
||
## 汇总和查漏
|
||
|
||
```bash
|
||
python3 scripts/invoice_tool.py export-summary
|
||
```
|
||
|
||
输出:
|
||
|
||
```text
|
||
output/发票汇总.xlsx
|
||
```
|
||
|
||
工作簿包含:
|
||
|
||
- `发票汇总`
|
||
- `缺票清单`
|
||
- `合规检查`
|
||
|
||
## 状态含义
|
||
|
||
| 状态 | 含义 |
|
||
|---|---|
|
||
| `待补码` | 未能自动识别二维码,需要人工扫码补录 |
|
||
| `待人工扫码` | 码不是网页链接,通常需要 12306 App 扫码 |
|
||
| `待网页开票` | 可以从辅助页打开官方页面处理 |
|
||
| `已收票` | 已匹配到发票附件 |
|