Files
ledgerly-docs/dev/bill-parsing.md
T

4.8 KiB
Raw Blame History

账单解析

账单导入采用「解析预览 → 确认导入」两阶段流程,解析阶段不落库,用户确认后才写入。

解析器设计

解析器采用策略模式: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」推断。
  • 计入统计默认值:解析完成后按账单来源应用「计入统计」默认值(配置于系统设置,见 动态数据源 同级的应用配置)。采用「解析器判定 && 来源默认」语义——来源默认不计入时整批置为不计入;来源默认计入时保留解析器对「不计收支/中性」行的排除。确认导入时若请求未显式提供该字段,同样以来源默认兜底。

压缩包解密

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 生成规则一致,可混用。

相关文档