Appearance
账单解析
账单导入采用「解析预览 → 确认导入」两阶段流程,解析阶段不落库,用户确认后才写入。
解析器设计
解析器采用策略模式:BillParser 接口声明 getBillType() 与 parse(byte[]),各实现类对应一种账单类型。服务层构造时用 EnumMap<BillType, BillParser> 注册所有实现,按类型分发,便于扩展新来源。
| 解析器 | 格式 | 表头锚点 | 去重键来源 |
|---|---|---|---|
AlipayBillParser | CSV(UTF-8/GBK) | 「交易号」 | 交易号 |
WechatBillParser | xlsx(POI) | 「交易时间」 | 交易单号 |
MeituanBillParser | CSV | 「交易创建时间」 | 交易单号 |
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 日期序列号与文本时间;支付宝在付款时间缺失时回退交易创建时间。
压缩包解密
BillArchiveExtractor 处理 zip 压缩包:
- 按魔数
PK\x03\x04判断是否为 zip,并区分「xlsx 本身是 zip」与「账单压缩包」。 - 支持 ZipCrypto 与 AES 加密;未提供密码返回错误码
4001,密码错误返回4002。 - 限制条目数与总大小,防止 zip bomb;解析完成后清理临时文件。
两阶段流程
parse(不落库)
- 按账单类型分发到对应解析器;若为 zip 则先解密提取。
- 批量查询已存在的
externalNo,标记重复行并取消勾选。 - 调用规则引擎批量匹配,生成建议分类与标签(批量查询分类与标签,避免 N+1)。
confirm(落库,事务)
- 校验默认分类归属与类型,过滤默认标签。
- 再次批量运行规则匹配。
- 逐条校验类型、时间、金额;按
externalNo去重(含本批次内去重)。 - 规则建议分类优先于面板默认分类;标签取「默认标签 ∪ 规则标签」并去重。
- 批量插入(每 500 条一批),批次失败时回退逐条插入;标签关联在批量插入未回填主键时按
externalNo回查 ID。 - 返回成功 / 跳过 / 失败统计。
去重策略
- 统一使用「用户 +
externalNo」唯一约束uk_user_external去重,external_no为 NULL 的手动记录不受影响。 - 有交易号的账单直接使用交易号。
- 银行流水无交易号,则用「日期 + 类型 + 金额 + 余额(或时分秒)」生成稳定键,保证同一账单重复导入不会重复;招商银行 CSV 与 PDF 生成规则一致,可混用。