68 lines
4.8 KiB
Markdown
68 lines
4.8 KiB
Markdown
# 账单解析
|
||
|
||
账单导入采用「**解析预览 → 确认导入**」两阶段流程,解析阶段不落库,用户确认后才写入。
|
||
|
||
## 解析器设计
|
||
|
||
解析器采用策略模式:`BillParser` 接口声明 `getBillType()` 与 `parse(byte[])`,各实现类对应一种账单类型。服务层构造时用 `EnumMap<BillType, BillParser>` 注册所有实现,按类型分发,便于扩展新来源。
|
||
|
||
| 解析器 | 格式 | 表头锚点 | 去重键来源 |
|
||
|---|---|---|---|
|
||
| `AlipayBillParser` | CSV(UTF-8/GBK) | 「交易号」 | 交易号 |
|
||
| `WechatBillParser` | xlsx(POI) | 「交易时间」 | 交易单号 |
|
||
| `MeituanBillParser` | CSV | 「交易创建时间」 | 交易单号 |
|
||
| `TaobaoBillParser` | xlsx(POI) | 「订单号」 | 订单号(按订单号聚合多商品行) |
|
||
| `JdBillParser` | CSV(UTF-8/GBK) | 「交易时间」 | 交易订单号 |
|
||
| `CmbBillParser` | CSV | 「交易日期」 | 生成 `CMB_日期_类型_金额_余额` |
|
||
| `CmbPdfParser` | PDF(PDFBox) | 「记账日期」 | 同上(与 CSV 互通) |
|
||
| `BocomBillParser` | xls(POI) | 「记账日期」 | 生成 `BOCOM_日期_时分秒_类型_金额` |
|
||
|
||
各解析器统一输出 `ParsedBillRow`:行号、`externalNo`、交易时间、类型、金额、交易对方、商品、交易状态、原始收/支方向、是否计入统计、是否可导入、是否重复、命中规则与建议分类/标签、`rawData`(保留原始列的 JSON)、错误信息。
|
||
|
||
### 解析要点
|
||
|
||
- **编码识别**:CSV 自动识别 UTF-8 / UTF-8 BOM / GBK。
|
||
- **表头定位**:以特定表头文字定位数据区,忽略元信息与统计行。
|
||
- **银行 PDF**:按字符坐标切列、按纵坐标合并折行还原表格;用带符号金额判断收支;按「联机余额」做连续性校验(误差 0.005),不连续的行标记为不可导入。
|
||
- **时间兼容**:微信账单兼容 Excel 日期序列号与文本时间;支付宝在付款时间缺失时回退交易创建时间;淘宝订单同样兼容文本时间与 Excel 日期序列号。
|
||
- **订单聚合**:淘宝订单数据中多商品订单的订单级列(订单号、提交时间、状态、店铺、实付金额、运费)以合并单元格呈现,仅首个商品行有值;解析器按订单号分组聚合,每个订单输出一条记录,金额优先取订单级实付金额,为空时回退「Σ(商品金额×数量)+运费」。
|
||
- **不计收支处理**:京东流水的「不计收支」行(白条还款、小金库转入、全额退款等)标记为可导入但**不计入统计且默认不勾选**,类型按「交易说明含『退款』→ INCOME,否则 EXPENSE」推断。
|
||
- **计入统计默认值**:解析完成后按**账单来源**应用「计入统计」默认值(配置于系统设置,见 [动态数据源](/dev/database) 同级的应用配置)。采用「解析器判定 && 来源默认」语义——来源默认不计入时整批置为不计入;来源默认计入时保留解析器对「不计收支/中性」行的排除。确认导入时若请求未显式提供该字段,同样以来源默认兜底。
|
||
|
||
## 压缩包解密
|
||
|
||
`BillArchiveExtractor` 处理 zip 压缩包:
|
||
|
||
- 按魔数 `PK\x03\x04` 判断是否为 zip,并区分「xlsx 本身是 zip」与「账单压缩包」。
|
||
- 支持 ZipCrypto 与 AES 加密;未提供密码返回错误码 `4001`,密码错误返回 `4002`。
|
||
- 限制条目数与总大小,防止 zip bomb;解析完成后清理临时文件。
|
||
|
||
## 两阶段流程
|
||
|
||
### parse(不落库)
|
||
|
||
1. 按账单类型分发到对应解析器;若为 zip 则先解密提取。
|
||
2. 批量查询已存在的 `externalNo`,标记重复行并取消勾选。
|
||
3. 调用规则引擎批量匹配,生成建议分类与标签(批量查询分类与标签,避免 N+1)。
|
||
|
||
### confirm(落库,事务)
|
||
|
||
1. 校验默认分类归属与类型,过滤默认标签。
|
||
2. 再次批量运行规则匹配。
|
||
3. 逐条校验类型、时间、金额;按 `externalNo` 去重(含本批次内去重)。
|
||
4. **规则建议分类优先于面板默认分类**;标签取「默认标签 ∪ 规则标签」并去重。
|
||
5. 批量插入(每 500 条一批),批次失败时回退逐条插入;标签关联在批量插入未回填主键时按 `externalNo` 回查 ID。
|
||
6. 返回成功 / 跳过 / 失败统计。
|
||
|
||
## 去重策略
|
||
|
||
- 统一使用「用户 + `externalNo`」唯一约束 `uk_user_external` 去重,`external_no` 为 NULL 的手动记录不受影响。
|
||
- 有交易号的账单直接使用交易号。
|
||
- 银行流水无交易号,则用「日期 + 类型 + 金额 + 余额(或时分秒)」生成稳定键,保证同一账单重复导入不会重复;招商银行 CSV 与 PDF 生成规则一致,可混用。
|
||
|
||
## 相关文档
|
||
|
||
- [账单类型与获取](/guide/bills/overview)
|
||
- [账单导入(使用说明)](/guide/transactions)
|
||
- [规则引擎](/dev/rule-engine)
|