24 KiB
ProjectMoneyX 系统详细设计说明书(DDS)
项目名称:ProjectMoneyX 文档类型:系统详细设计说明书 版本:v1.0 设计目标:构建面向 Firefly III 生态的本地化多源账单数据治理中间件,实现账单导入、解析、标准化、去重、链路合并、规则映射与导入编排闭环。
1. 设计目标与范围
1.1 设计目标
本系统负责将多源账单文件(优先支付宝、微信)导入后,完成以下处理链路:
- 原始文件接入与批次化管理
- 多平台账单解析与字段标准化
- 统一交易模型入库
- 严格去重、模糊去重、链路合并
- 分类/账户/标签/商户归一化规则映射
- 导出为 Firefly III / Data Importer 可消费的数据格式
- 提供预览、审计、失败重试与可回溯能力。
1.2 本期范围(V1 / MVP)
- 支持支付宝、微信账单导入
- 支持 CSV / Excel / 常见文本格式
- 支持本地 SQLite 持久化
- 支持基础规则映射(分类/账户)
- 支持严格去重 + 转账闭环链路合并
- 支持导入预览与确认
- 支持 API 推送或中间文件导出至 Firefly III / Data Importer。
1.3 非目标
- 不直接登录第三方平台抓取账单
- 不实现完整 BI 平台
- 不替代 Firefly III 本体记账能力
- 不做高频实时同步。
2. 需求分析与架构判断
2.1 核心业务意图
PRD 的真实意图不是“导入工具”,而是一个 本地账单数据治理层,核心是先统一语义与规则,再输出给 Firefly III。换言之,系统的重点不在“上传文件”,而在:
- 多源异构字段收敛
- 多因子去重与链路恢复
- 可沉淀、可解释、可迁移的本地规则体系。
2.2 关键设计约束
- 本地优先:财务数据敏感,必须本地部署,默认不上传云端。
- 插件化解析器:平台格式变化频繁,适配逻辑必须隔离。
- 统一交易模型稳定:避免下游 Firefly III 或上游平台格式污染核心域模型。
- 支付宝分类为主标准:账单说明明确支付宝交易类型最丰富,其他平台应映射到支付宝分类体系。
- 微信分类需推断:微信“交易类型”较粗,需要结合“商品”字段推断细分分类。
2.3 技术风险
- 平台格式变更导致解析失败
- 模糊去重误判造成误合并
- 内部转账闭环识别不准确
- Firefly III 字段映射不完整导致导入失败
- 用户规则不断增加后性能退化。
3. 总体架构设计
3.1 分层架构
flowchart TB
UI[Web UI / CLI]
API[GIN API Layer]
APP[Application Service Layer]
ADAPTER[Adapter Layer\n账单解析器插件]
NORMALIZE[Normalize Layer\n字段标准化]
MATCH[Match Layer\n去重/链路合并]
RULE[Rule Engine Layer\n分类/账户/标签映射]
EXPORT[Export Layer\nFirefly III / Data Importer 适配]
REPO[Repository Layer\nGORM]
DB[(SQLite)]
FF[Firefly III / Data Importer]
UI --> API
API --> APP
APP --> ADAPTER
APP --> NORMALIZE
APP --> MATCH
APP --> RULE
APP --> EXPORT
APP --> REPO
REPO --> DB
EXPORT --> FF
3.2 分层职责
- UI 层:上传文件、批次管理、预览确认、规则配置、人工确认、导入结果展示
- API 层:RESTful 接口,统一鉴权、参数校验、响应封装
- 应用服务层:编排完整业务流程,不承载具体解析规则
- Adapter 层:按平台解析原始文件,输出平台原始记录 DTO
- Normalize 层:统一字段、金额、方向、时间、分类原始值
- Match 层:严格去重、模糊去重、链路聚合、转账闭环
- Rule 层:分类、账户、对手方、标签映射
- Export 层:适配 Firefly III / Data Importer API 或 CSV/JSON
- Repository 层:隔离数据库访问,面向领域对象持久化。
4. 系统模块划分
4.1 模块清单
| 模块 | 职责 | 优先级 |
|---|---|---|
| import-center | 文件上传、批次管理、来源识别 | P0 |
| parser-engine | 平台解析器装载与执行 | P0 |
| normalize-engine | 统一模型转换 | P0 |
| dedup-engine | 严格去重与模糊去重 | P0/P1 |
| link-engine | 转账闭环与订单链路合并 | P0 |
| rule-engine | 分类/账户/标签/商户归一 | P0/P1 |
| import-orchestrator | 导入预览、执行、重试 | P0 |
| audit-center | 审计日志、处理链追溯 | P0 |
| settings-center | Firefly 配置、阈值参数 | P1 |
4.2 后端包结构建议(Go)
projectmoneyx/
├── cmd/server
├── internal/
│ ├── handler/
│ ├── service/
│ ├── domain/
│ │ ├── entity/
│ │ ├── valueobject/
│ │ ├── enum/
│ │ └── repository/
│ ├── parser/
│ │ ├── alipay/
│ │ ├── wechat/
│ │ └── registry/
│ ├── normalize/
│ ├── matcher/
│ ├── linker/
│ ├── rule/
│ ├── exporter/
│ ├── dao/
│ ├── dto/
│ ├── middleware/
│ └── config/
├── migrations/
└── web/
4.3 前端结构建议(Vue3)
src/
├── api/
├── views/
│ ├── ImportCenterView.vue
│ ├── BatchDetailView.vue
│ ├── PreviewView.vue
│ ├── DedupReviewView.vue
│ ├── RuleConfigView.vue
│ ├── ImportTaskView.vue
│ └── AuditTraceView.vue
├── components/
├── stores/
├── types/
└── router/
5. 核心业务流程设计
5.1 主流程时序图
sequenceDiagram
participant U as User
participant FE as Web UI
participant API as Gin API
participant IMP as Import Service
participant PARSER as Parser Engine
participant NORM as Normalize Engine
participant MATCH as Match/Link Engine
participant RULE as Rule Engine
participant EXP as Export Engine
participant DB as SQLite
participant FF as Firefly/Data Importer
U->>FE: 上传账单文件
FE->>API: 创建导入批次
API->>IMP: startImport(batchId)
IMP->>DB: 保存 source_files / import_batch
IMP->>PARSER: 解析文件
PARSER->>DB: 保存 raw_records
IMP->>NORM: 标准化转换
NORM->>DB: 保存 transactions(待清洗)
IMP->>MATCH: 严格去重/模糊去重/链路合并
MATCH->>DB: 保存 dedup_relations / link_relations
IMP->>RULE: 应用映射规则
RULE->>DB: 更新 category/account/tag
API-->>FE: 返回预览结果
U->>FE: 确认导入
FE->>API: 执行导入
API->>EXP: export(batchId)
EXP->>FF: 调用 API / 导出 CSV
FF-->>EXP: 返回结果
EXP->>DB: 保存 import_results
API-->>FE: 展示导入结果
5.2 批次状态机
stateDiagram-v2
[*] --> CREATED
CREATED --> UPLOADED: 文件已上传
UPLOADED --> PARSED: 解析完成
PARSED --> NORMALIZED: 标准化完成
NORMALIZED --> MATCHED: 去重/链路完成
MATCHED --> RULE_APPLIED: 规则映射完成
RULE_APPLIED --> PREVIEW_READY: 可预览
PREVIEW_READY --> IMPORTING: 用户确认导入
IMPORTING --> IMPORT_SUCCESS: 全部成功
IMPORTING --> PARTIAL_FAILED: 部分失败
IMPORTING --> IMPORT_FAILED: 全部失败
PARTIAL_FAILED --> RETRYING: 用户重试失败项
RETRYING --> IMPORT_SUCCESS
RETRYING --> PARTIAL_FAILED
IMPORT_FAILED --> RETRYING
6. 账单解析与标准化设计
6.1 解析器接口设计
type BillParser interface {
Platform() string
Detect(fileMeta FileMeta, header []string) bool
Parse(ctx context.Context, filePath string) ([]RawBillRecord, error)
}
解析器注册中心
- 启动时注册所有 Parser
- 上传后根据文件头、列名、用户指定来源进行匹配
- 匹配失败时进入“未知来源待人工选择”状态
6.2 支付宝解析规则
支付宝字段包括:
- 交易时间
- 交易分类
- 交易对方
- 对方账号
- 商品说明
- 收/支
- 金额
- 收/付款方式
- 交易状态
- 交易订单号
- 商家订单号
- 备注。
支付宝映射策略
| 原字段 | 目标字段 |
|---|---|
| 交易时间 | trade_time |
| 交易分类 | category_raw |
| 交易对方 | counterparty |
| 对方账号 | counterparty_account |
| 商品说明 | merchant_name / note |
| 收/支 | direction |
| 金额 | amount |
| 收/付款方式 | payment_method_raw |
| 交易状态 | trade_status |
| 交易订单号 | source_record_id / order_id |
| 商家订单号 | merchant_order_id / parent_order_id |
| 备注 | note |
特别说明
支付宝交易类型最丰富,系统将其作为 一级业务分类基准字典。其他平台最终都应落到这套分类枚举上。
6.3 微信解析规则
微信字段包括:
- 交易时间
- 交易类型
- 交易对方
- 商品
- 收/支
- 金额(元)
- 支付方式
- 当前状态
- 交易单号
- 商户单号
- 备注。
微信映射策略
| 原字段 | 目标字段 |
|---|---|
| 交易时间 | trade_time |
| 交易类型 | category_raw / wechat_trade_type |
| 交易对方 | counterparty |
| 商品 | merchant_name / product_desc |
| 收/支 | direction |
| 金额(元) | amount |
| 支付方式 | payment_method_raw |
| 当前状态 | trade_status |
| 交易单号 | source_record_id / order_id |
| 商户单号 | merchant_order_id / parent_order_id |
| 备注 | note |
微信分类推断规则
由于微信“交易类型”较粗(如商户消费、扫二维码付款、转账、红包等),系统必须结合“商品”字段推断更细分类。
示例推断逻辑
| 微信交易类型 | 商品关键词 | 推断分类 |
|---|---|---|
| 商户消费 | 美团/外卖/餐厅/咖啡 | 餐饮美食 |
| 商户消费 | 滴滴/打车/地铁/高铁 | 交通出行 |
| 商户消费 | 京东/超市/便利店 | 日用百货 |
| 商户消费 | 电费/水费/话费 | 充值缴费 |
| 转账 | 无 | 转账红包 / 其他 |
| 微信红包 | 无 | 转账红包 |
| xxx-退款 | 无 | 退款 |
若商品内容无法识别,先落入“其他”,并允许用户通过规则管理补充映射。
6.4 统一交易模型
classDiagram
class Transaction {
+string transaction_id
+string source_platform
+string source_record_id
+datetime trade_time
+decimal amount
+string currency
+string direction
+string counterparty
+string merchant_name
+string category_raw
+string category_mapped
+string order_id
+string parent_order_id
+string note
+json raw_payload
+string import_batch_id
+string status
}
该模型与 PRD 保持一致,作为系统核心领域对象。
标准化规则
- 时间统一存储为 Asia/Shanghai (UTC+8)
- 金额统一使用正数,方向独立用
direction表达 - 币种默认 CNY,后续可扩展多币种
- 状态初始为
PENDING_CLEAN - 原始记录完整写入
raw_payload以便审计。
7. 去重与链路合并设计
7.1 三层处理模型
PRD 明确采用:
- 基础去重(严格)
- 模糊去重(多因子)
- 链路合并(转账/订单闭环)。
flowchart LR
A[新标准化交易] --> B{严格去重}
B -- 命中 --> X[标记重复并建立关系]
B -- 未命中 --> C{模糊去重}
C -- 高置信 --> X
C -- 低置信 --> D[进入人工确认队列]
C -- 未命中 --> E{链路合并}
E -- 命中 --> F[生成合并链路]
E -- 未命中 --> G[保留独立交易]
7.2 严格去重
唯一判定键
source_platform + source_record_idsource_file_hash + row_fingerprint可信订单号(order_id)
行指纹算法建议
对以下字段标准化后做 SHA256:
- trade_time(分钟粒度)
- amount
- direction
- counterparty
- merchant_name
- order_id
这样可防止同一文件重复导入。
7.3 模糊去重
多因子评分模型
| 因子 | 分值 |
|---|---|
| 时间在 ±5 分钟内 | 30 |
| 金额一致 | 30 |
| 方向一致 | 10 |
| 订单号相同/相近 | 15 |
| 对手方相似 | 10 |
| 来源关联规则命中 | 5 |
判定阈值
>= 85:自动判定重复60 ~ 84:疑似重复,进入人工确认< 60:不判定重复
对手方相似算法
- 统一大小写/空格/符号
- 去除平台前缀后再比较
- 使用
contains + Levenshtein混合评分
7.4 链路合并(转账闭环)
典型场景
- 银行卡支出 1000
- 支付宝收入 1000
- 时间接近、金额相同、方向互补
则合并为一笔内部转账:
- from_account = 银行卡
- to_account = 支付宝
- type = transfer。
转账识别规则
- 金额一致
- 一条为支出,一条为收入
- 时间在可配置窗口内
- 来源平台不同但账户映射可闭环
- 非退款、非手续费
订单链路合并
对于“京东订单 + 微信支付”类场景:
- 保留更完整的业务记录为主交易
- 其他记录挂为关联来源
- 形成
parent_order_id聚合链路。
8. 规则引擎设计
8.1 规则模型
classDiagram
class Rule {
+string id
+string rule_type
+int priority
+string platform_scope
+json conditions
+json actions
+bool enabled
}
规则类型
- CATEGORY_MAPPING
- ACCOUNT_MAPPING
- COUNTERPARTY_NORMALIZE
- TAG_MAPPING
- FIREFLY_FIELD_MAPPING
8.2 规则匹配条件
支持:
- 平台
- 原始分类
- 交易类型
- 商品/商户关键词
- 金额范围
- 方向
- 对手方关键字
- 正则匹配
示例
{
"platform": "wechat",
"conditions": {
"trade_type": "商户消费",
"product_keywords": ["美团", "外卖"]
},
"actions": {
"category_mapped": "餐饮美食",
"merchant_name_normalized": "美团"
}
}
8.3 执行顺序
- 对手方归一化
- 商户归一化
- 分类映射
- 账户映射
- 标签映射
- Firefly 字段映射
原因
先做归一化,再做分类,可提升规则命中率与稳定性。
8.4 可解释性设计
每条交易保留:
- 命中的规则 ID
- 命中条件摘要
- 规则执行前后字段对比
用于前端“为何被分到餐饮/交通”的解释展示。
9. Firefly III / Data Importer 适配设计
9.1 导出模式
模式 A:API 推送
- 系统生成 Firefly 兼容 DTO
- 调用 Data Importer / Firefly API
- 接收导入结果并更新任务状态。
模式 B:文件导出
- 生成标准 CSV / JSON
- 用户手动导入
- 适用于 API 不可用或权限受限场景。
9.2 Firefly 交易类型映射
| 内部 direction | Firefly type |
|---|---|
| expense | withdrawal |
| income | deposit |
| transfer | transfer |
| refund | deposit / reversal(按配置) |
| fee | withdrawal |
9.3 导入前校验
- 必填字段完整
- 金额与时间合法
- 账户映射完整
- 是否已导入过
- 是否存在未确认疑似重复记录。
10. 数据库详细设计(SQLite)
10.1 ER 图
erDiagram
SOURCE_FILES ||--o{ RAW_RECORDS : contains
SOURCE_FILES ||--o{ IMPORT_BATCHES : belongs_to
IMPORT_BATCHES ||--o{ TRANSACTIONS : generates
TRANSACTIONS ||--o{ DEDUP_RELATIONS : source
TRANSACTIONS ||--o{ LINK_RELATIONS : linked
TRANSACTIONS ||--o{ RULE_HITS : matched
IMPORT_BATCHES ||--o{ IMPORT_TASKS : owns
IMPORT_TASKS ||--o{ IMPORT_RESULTS : produces
RULES ||--o{ RULE_HITS : referenced
TRANSACTIONS ||--o{ AUDIT_LOGS : traced
10.2 表结构
10.2.1 source_files
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| file_name | varchar(255) | 文件名 |
| file_hash | varchar(64) | 文件哈希 |
| source_platform | varchar(32) | 来源平台 |
| file_type | varchar(32) | csv/xlsx/txt |
| uploaded_at | datetime | 上传时间 |
| batch_id | varchar(36) | 批次 ID |
10.2.2 raw_records
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| source_file_id | varchar(36) | 来源文件 |
| row_no | int | 行号 |
| source_platform | varchar(32) | 平台 |
| source_record_id | varchar(128) | 原始流水号 |
| row_fingerprint | varchar(64) | 行指纹 |
| raw_payload | text | 原始 JSON |
| parse_status | varchar(32) | 解析状态 |
| parse_error | text | 错误信息 |
10.2.3 transactions
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| transaction_id | varchar(64) | 业务 ID |
| batch_id | varchar(36) | 导入批次 |
| source_platform | varchar(32) | 来源平台 |
| source_record_id | varchar(128) | 原始记录号 |
| trade_time | datetime | 交易时间 |
| amount | decimal(18,6) | 金额 |
| currency | varchar(16) | 币种 |
| direction | varchar(16) | 方向 |
| counterparty | varchar(255) | 对手方 |
| merchant_name | varchar(255) | 商户名 |
| category_raw | varchar(128) | 原始分类 |
| category_mapped | varchar(128) | 映射分类 |
| order_id | varchar(128) | 订单号 |
| parent_order_id | varchar(128) | 父链路号 |
| note | text | 备注 |
| status | varchar(32) | 状态 |
| imported_at | datetime | 导入时间 |
10.2.4 dedup_relations
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| src_transaction_id | varchar(64) | 原交易 |
| target_transaction_id | varchar(64) | 目标交易 |
| relation_type | varchar(32) | strict/fuzzy |
| confidence | int | 置信度 |
| reason_json | text | 判定依据 |
10.2.5 link_relations
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| parent_transaction_id | varchar(64) | 主交易 |
| child_transaction_id | varchar(64) | 子交易 |
| link_type | varchar(32) | transfer/order/refund/fee |
| reason_json | text | 关联依据 |
10.2.6 rules
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) | 主键 |
| rule_type | varchar(64) | 规则类型 |
| priority | int | 优先级 |
| platform_scope | varchar(32) | 平台范围 |
| conditions_json | text | 条件 |
| actions_json | text | 动作 |
| enabled | bool | 是否启用 |
10.2.7 import_tasks / import_results / audit_logs
按 PRD 设计分别记录导入任务、结果与操作日志。
11. API 接口设计(Gin)
11.1 导入中心
上传账单文件
POST /api/v1/import/batches
-
form-data:
- files[]
- sourcePlatform(可选)
- autoDetect(bool)
返回:
{
"code": 0,
"message": "ok",
"data": {
"batchId": "xxx",
"status": "UPLOADED"
}
}
获取批次详情
GET /api/v1/import/batches/{batchId}
触发解析与清洗
POST /api/v1/import/batches/{batchId}/process
获取预览结果
GET /api/v1/import/batches/{batchId}/preview
11.2 去重确认
获取疑似重复列表
GET /api/v1/dedup/reviews?batchId=xxx
确认合并
POST /api/v1/dedup/reviews/{reviewId}/confirm
拒绝合并
POST /api/v1/dedup/reviews/{reviewId}/reject
11.3 规则管理
GET /api/v1/rulesPOST /api/v1/rulesPUT /api/v1/rules/{id}DELETE /api/v1/rules/{id}
11.4 导入执行
确认导入到 Firefly
POST /api/v1/import/tasks
获取导入结果
GET /api/v1/import/tasks/{taskId}
重试失败项
POST /api/v1/import/tasks/{taskId}/retry
12. 前端页面设计
12.1 信息架构
PRD 已定义一级模块:
- 导入中心
- 数据清洗
- 去重处理
- 规则管理
- 导入任务
- 数据审计
- 系统设置。
12.2 页面职责
导入中心
- 拖拽上传
- 批量文件列表
- 来源自动识别结果
- 批次创建与处理按钮
清洗结果预览
- 交易列表
- 原始字段 vs 标准字段对比
- 规则命中说明
- 待人工处理标记
去重处理页
- 疑似重复列表
- 评分与命中因子展开
- 合并/拒绝操作
规则配置页
- 规则列表
- 优先级排序
- 条件编辑器
- 测试命中预览
导入结果页
- 成功/失败统计
- 失败原因
- 单条重试
审计追溯页
- 交易全处理链
- 原始文件 → 原始记录 → 标准化 → 规则 → 导入结果
13. 非功能设计
13.1 性能
目标:单次 1 万条记录在主流程内完成解析与清洗,建议 30 秒内完成主要去重流程。
优化策略
-
批量插入 raw_records / transactions
-
构建关键索引:
source_platform + source_record_idbatch_idtrade_timeorder_id
-
模糊去重按时间分桶,降低全表扫描
-
规则按平台和启用状态预筛选
13.2 安全
- API Token 加密存储
- 审计日志默认脱敏(账号、订单号局部遮罩)
- 本地部署,不上传云端。
13.3 可维护性
- 解析器插件化
- 规则条件 JSON 化
- 导入器解耦,可替换下游目标
- 统一 DTO / VO / Entity 分层。
14. 关键实现建议(面向 Go + GIN + GORM)
14.1 分层规范
handler:仅做参数绑定、返回service:负责编排domain:承载核心规则dao/repository:数据访问parser/matcher/rule/exporter:独立可测试组件
14.2 推荐关键对象
ImportBatchServiceParserRegistryTransactionNormalizeServiceDedupMatchServiceTransferLinkServiceRuleApplyServiceFireflyExportServiceAuditTraceService
14.3 事务边界
建议以下阶段分别事务化:
- 文件入库 + 原始记录入库
- 标准化结果落库
- 去重/链路关系落库
- 规则命中落库
- 导入结果落库
避免一个超长事务覆盖整个批次。
15. MVP 与后续版本演进建议
15.1 MVP(优先上线)
- 支付宝 / 微信 Parser
- 严格去重
- 转账闭环
- 基础规则映射
- 导入预览
- CSV / API 两种导出
15.2 V1.5
- 模糊去重评分
- 手动合并/拆分
- 商户别名库
- 批次管理增强
15.3 V2.0
- 京东 / 美团链路增强
- 多币种
- 可视化规则调试
- 统计报表。
16. 结论
ProjectMoneyX 的最佳实现方式,不是把逻辑堆到 Firefly III 导入器里,而是建设一个本地、分层、插件化的 账单治理中台:
- Adapter 收敛平台差异
- Normalize 建立统一交易语义
- Match/Link 解决重复与交易链碎片
- Rule Engine 沉淀可解释、可复用的分类知识
- Export 以最小耦合接入 Firefly 生态。
同时,基于账单格式说明,V1 必须把下面两点做成产品级能力:
- 支付宝分类字典作为统一分类基准
- 微信“交易类型 + 商品”联合推断分类。