16 KiB
财务数据建模与图表构建
**本文引用的文件** - [backend/data_builder.py](file://backend/data_builder.py) - [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts) - [src/types.ts](file://src/types.ts) - [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx) - [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx) - [src/DualChartView.tsx](file://src/DualChartView.tsx) - [src/mock-cashflow.json](file://src/mock-cashflow.json) - [src/mock-data.json](file://src/mock-data.json) - [PROJECT_README.md](file://PROJECT_README.md)目录
简介
本文件面向开发者,系统性阐述财务数据建模与图表构建模块,重点解析后端 data_builder.py 如何将原始财务输入转化为可用于 Remotion 动画渲染的结构化时序数据;详解现金流模拟算法、资产耗尽预测模型与年度快照(YearlySnapshot)生成逻辑;说明 CashFlowDataSchema 与 YearlySnapshotSchema 的设计原则及与前端 Recharts 组件的映射关系;结合代码展示数据插值、单位转换与异常值处理的关键步骤;解释 render_props.json 中 charts 字段的构造流程,并提供自定义财务假设(通胀率、投资回报率、赤字利率)的配置方法;最后给出大数据量下的分段处理与性能优化建议。
项目结构
该模块位于 Python 后端与前端 Remotion 之间,通过 render_props.json 建立强类型契约,确保前后端数据一致。后端负责财务模拟与数据转换,前端负责动画渲染与交互。
graph TB
A["后端 data_builder.py<br/>金融模拟与数据转换"] --> B["render_props.json<br/>数据契约"]
B --> C["前端 Remotion 组件<br/>CashFlowChart.tsx / LifePlanChart.tsx / DualChartView.tsx"]
C --> D["Recharts 图表<br/>动态可视化"]
图示来源
- backend/data_builder.py
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
- src/DualChartView.tsx
章节来源
核心组件
- FinancialSimulator:负责按年推进财务模拟,应用通胀、退休收入停止、事件成本、正负资产余额的不同收益/利息规则,生成 YearlySnapshot 列表。
- build_chart_data:从 PDF 提取参数,调用 FinancialSimulator,产出 assetDepletion 与 cashFlow 两套图表数据。
- YearlySnapshot 与 CashFlowData:前后端共同约定的数据结构,分别用于资产耗尽趋势与收支明细可视化。
- Recharts 组件:CashFlowChart 与 LifePlanChart 将上述数据结构映射到图表,支持动画与 HUD 信息同步。
章节来源
- backend/data_builder.py
- backend/data_builder.py
- frontend/src/render-props-types.ts
- src/types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
架构总览
后端 data_builder.py 以 FinancialSimulator 为核心,将 PDF 输入转换为两条时间线:
- 年度快照(YearlySnapshot):资产余额、收入/支出、事件标记、是否临界。
- 现金流明细(CashFlowData):按类别拆分的支出、净现金流、资产余额。
前端通过 render_props.json 接收两条时间线,CashFlowChart 与 LifePlanChart 分别消费这些数据,实现双视图联动与统一动画进度。
sequenceDiagram
participant PDF as "PDF 输入"
participant Builder as "data_builder.py"
participant Sim as "FinancialSimulator"
participant JSON as "render_props.json"
participant CF as "CashFlowChart"
participant LP as "LifePlanChart"
PDF->>Builder : 提取年龄/退休/寿命/资产/收入/支出/事件
Builder->>Sim : 传入财务参数与生命周期
Sim-->>Builder : 返回 YearlySnapshot 列表
Builder->>Builder : 构造 CashFlowData 列表
Builder->>JSON : 写入 charts.assetDepletion 与 charts.cashFlow
JSON->>CF : 传入 CashFlowData
JSON->>LP : 传入 YearlySnapshot
CF-->>CF : 动画与 HUD 更新
LP-->>LP : 动画与 HUD 更新
图示来源
- backend/data_builder.py
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
详细组件分析
1) FinancialSimulator 与现金流模拟算法
- 财务参数默认值:通胀率、投资回报率、赤字利率,均可通过配置注入。
- 生命周期推进:从当前年龄到预期寿命,每年计算:
- 收入:退休前为年收入,退休后为 0。
- 通胀:支出按年通胀指数增长。
- 事件:匹配当年事件成本累加到支出。
- 净现金流:收入减去总支出。
- 资产更新:若资产余额非负,按投资回报率复利;否则按赤字利率复利(债务复合)。
- 输出:YearlySnapshot 列表,包含年份、年龄、收入/支出/资产余额、事件标签、是否临界。
flowchart TD
Start(["开始模拟"]) --> Init["初始化资产余额与年份"]
Init --> Loop{"年龄未达寿命?"}
Loop --> |是| YearCalc["计算当年收入/通胀支出/事件成本"]
YearCalc --> Net["计算净现金流"]
Net --> Balance{"资产余额>=0?"}
Balance --> |是| Invest["按投资回报率复利"]
Balance --> |否| Deficit["按赤字利率复利"]
Invest --> Snapshot["生成 YearlySnapshot"]
Deficit --> Snapshot
Snapshot --> Loop
Loop --> |否| End(["结束"])
图示来源
章节来源
2) YearlySnapshot 与 CashFlowData 的生成与映射
- YearlySnapshot:用于资产耗尽趋势图,包含年份、代表者与配偶年龄、收入/支出/资产余额、事件与临界标记。
- CashFlowData:用于收支明细图,包含年份、年龄、收入(合计)、各类支出(生活/住房/教育/税费/事件)、总支出、净现金流、资产余额。
- 数据映射:DualChartView 将 CashFlowData 转换为 YearlySnapshot 供 LifePlanChart 使用,保持统一动画进度与 HUD。
classDiagram
class YearlySnapshot {
+number year
+object ages
+object financials
+string[] events
+boolean isCritical
}
class CashFlowData {
+number year
+number age
+object income
+object expenses
+number totalExpense
+number netCashFlow
+number assetBalance
}
class FinancialSimulator {
+simulate_timeline(...)
+build_cashflow_data(timeline)
}
class DualChartView {
+convert CashFlowData -> YearlySnapshot
}
FinancialSimulator --> YearlySnapshot : "生成"
FinancialSimulator --> CashFlowData : "生成"
DualChartView --> YearlySnapshot : "转换"
图示来源
章节来源
3) Recharts 组件与数据契约映射
- YearlySnapshotSchema:约束资产耗尽图的时间线结构,含货币单位与起始年份元数据。
- CashFlowDataSchema:约束收支明细图的时间线结构,含货币单位元数据。
- 组件职责:
- CashFlowChart:堆叠柱状图展示各类支出,折线展示收入,支持年进度插值与 HUD。
- LifePlanChart:面积图展示资产余额,支持年进度插值与渐变色(正负区间)。
- DualChartView:统一动画进度,将 CashFlowData 转 YearlySnapshot 供 LifePlanChart 使用。
sequenceDiagram
participant TS as "render-props-types.ts"
participant CF as "CashFlowChart"
participant LP as "LifePlanChart"
participant DCV as "DualChartView"
TS->>CF : YearlySnapshotSchema/CashFlowDataSchema
TS->>LP : YearlySnapshotSchema
DCV->>LP : 转换后的 YearlySnapshot[]
DCV->>CF : CashFlowData[]
CF-->>CF : 动画与 HUD
LP-->>LP : 动画与 HUD
图示来源
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
- src/DualChartView.tsx
章节来源
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
- src/DualChartView.tsx
4) 数据插值、单位转换与异常值处理
- 插值:
- LifePlanChart:在年与年之间进行线性插值,生成“当前年进度”的中间点,保证面积图平滑过渡。
- CashFlowChart:对当前年份内的支出条目按阶段顺序增长,同时生成一个“延伸点”以平滑收入线的视觉过渡。
- 单位转换:
- 元数据 currencyUnit 统一为“万円”,前端 Y 轴与 HUD 显示使用该单位。
- LifePlanChart 对大数值采用“亿”格式化显示。
- 异常值处理:
- 事件成本按年聚合,避免重复或遗漏。
- 资产余额为负时标记 isCritical,用于图表颜色与 HUD 提示。
- 当可视窗口小于固定长度时,填充空年份占位,避免坐标轴偏移。
章节来源
- src/LifePlanChart.tsx
- src/LifePlanChart.tsx
- src/CashFlowChart.tsx
- src/CashFlowChart.tsx
- backend/data_builder.py
5) render_props.json 中 charts 字段的构造过程
- 构造函数 build_chart_data 读取 PDF 参数,调用 FinancialSimulator 生成两条时间线:
- assetDepletion:timeline 为 YearlySnapshot[],meta 包含 currencyUnit 与 startYear。
- cashFlow:timeline 为 CashFlowData[],meta 包含 currencyUnit。
- 前端通过 render-props-types.ts 的 Schema 校验,确保数据一致性。
章节来源
6) 自定义财务假设配置方法
- 配置入口:FinancialSimulator 的构造函数接收 config 字典,支持以下键:
- inflation_rate:通胀率,默认 2%
- investment_return:投资回报率,默认 3%
- deficit_interest_rate:赤字利率(负资产时的复合成本),默认 15%
- 使用方式:在调用 build_chart_data 前,将上述键值放入 config 传入 FinancialSimulator。
章节来源
依赖关系分析
- 后端依赖:typing、math(用于幂运算与圆整)。
- 前端依赖:Remotion hooks(useCurrentFrame/useVideoConfig/interpolate)、Recharts(ComposedChart/AreaChart 等)、Zod(Schema 校验)。
- 类型契约:render-props-types.ts 定义 YearlySnapshotSchema 与 CashFlowDataSchema,确保前后端一致。
graph LR
DB["data_builder.py"] --> FS["FinancialSimulator"]
FS --> YS["YearlySnapshot"]
FS --> CFData["CashFlowData"]
RP["render-props-types.ts"] --> YSS["YearlySnapshotSchema"]
RP --> CFS["CashFlowDataSchema"]
CF["CashFlowChart.tsx"] --> YS
LP["LifePlanChart.tsx"] --> YS
DCV["DualChartView.tsx"] --> YS
图示来源
- backend/data_builder.py
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
- src/DualChartView.tsx
章节来源
- frontend/src/render-props-types.ts
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
- src/DualChartView.tsx
性能考量
- 大数据量分段处理策略:
- 限制每帧可见数据长度:当可视窗口较短时,使用固定域与占位年份,避免 Recharts 重绘抖动。
- 使用 useMemo 缓存转换结果(如 DualChartView 将 CashFlowData 转 YearlySnapshot),减少组件重渲染。
- 动画插值仅在必要范围内计算,避免全量数据参与每帧运算。
- 数值稳定性:
- 对资产余额与支出进行圆整,减少浮点误差累积。
- Y 轴域基于完整时间线计算,避免动画过程中轴范围跳变。
- 渲染优化:
- 关闭内部动画(isAnimationActive=false),由外部帧进度驱动,降低 Recharts 内部开销。
- 使用稳定的配置对象(如 CHART_MARGIN)避免每次渲染创建新对象。
章节来源
故障排查指南
- 数据不一致:
- 检查 render-props-types.ts 的 YearlySnapshotSchema 与 CashFlowDataSchema 是否与后端输出一致。
- 使用 validateRenderProps 或 isRenderProps 进行运行时校验。
- 图表显示异常:
- 确认 meta.currencyUnit 与前端 Y 轴单位一致。
- 若出现坐标轴跳变,检查 Y 轴域计算逻辑(完整时间线 vs 可见数据)。
- 动画不同步:
- 确保 DualChartView 使用统一的外部索引与年进度参数传递给两个子图。
- 事件/临界状态错误:
- 检查事件成本聚合与 isCritical 标记逻辑,确保资产余额为负时正确标注。
章节来源
- frontend/src/render-props-types.ts
- src/DualChartView.tsx
- src/CashFlowChart.tsx
- src/LifePlanChart.tsx
结论
本模块通过 FinancialSimulator 将 PDF 输入转化为两条高质量时序数据,借助 YearlySnapshot 与 CashFlowData 的清晰契约,实现了资产耗尽趋势与收支明细的双视图联动渲染。前后端通过 Zod Schema 严格约束数据结构,配合 Recharts 的插值与 HUD 机制,提供了流畅且可解释的财务可视化体验。通过合理的分段处理与缓存策略,可在大数据量场景下保持稳定性能。