323 lines
16 KiB
Markdown
323 lines
16 KiB
Markdown
# 财务数据建模与图表构建
|
||
|
||
<cite>
|
||
**本文引用的文件**
|
||
- [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)
|
||
</cite>
|
||
|
||
## 目录
|
||
1. [简介](#简介)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心组件](#核心组件)
|
||
4. [架构总览](#架构总览)
|
||
5. [详细组件分析](#详细组件分析)
|
||
6. [依赖关系分析](#依赖关系分析)
|
||
7. [性能考量](#性能考量)
|
||
8. [故障排查指南](#故障排查指南)
|
||
9. [结论](#结论)
|
||
10. [附录](#附录)
|
||
|
||
## 简介
|
||
本文件面向开发者,系统性阐述财务数据建模与图表构建模块,重点解析后端 data_builder.py 如何将原始财务输入转化为可用于 Remotion 动画渲染的结构化时序数据;详解现金流模拟算法、资产耗尽预测模型与年度快照(YearlySnapshot)生成逻辑;说明 CashFlowDataSchema 与 YearlySnapshotSchema 的设计原则及与前端 Recharts 组件的映射关系;结合代码展示数据插值、单位转换与异常值处理的关键步骤;解释 render_props.json 中 charts 字段的构造流程,并提供自定义财务假设(通胀率、投资回报率、赤字利率)的配置方法;最后给出大数据量下的分段处理与性能优化建议。
|
||
|
||
## 项目结构
|
||
该模块位于 Python 后端与前端 Remotion 之间,通过 render_props.json 建立强类型契约,确保前后端数据一致。后端负责财务模拟与数据转换,前端负责动画渲染与交互。
|
||
|
||
```mermaid
|
||
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](file://backend/data_builder.py#L150-L200)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L135)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L1-L176)
|
||
|
||
**章节来源**
|
||
- [PROJECT_README.md](file://PROJECT_README.md#L132-L208)
|
||
|
||
## 核心组件
|
||
- FinancialSimulator:负责按年推进财务模拟,应用通胀、退休收入停止、事件成本、正负资产余额的不同收益/利息规则,生成 YearlySnapshot 列表。
|
||
- build_chart_data:从 PDF 提取参数,调用 FinancialSimulator,产出 assetDepletion 与 cashFlow 两套图表数据。
|
||
- YearlySnapshot 与 CashFlowData:前后端共同约定的数据结构,分别用于资产耗尽趋势与收支明细可视化。
|
||
- Recharts 组件:CashFlowChart 与 LifePlanChart 将上述数据结构映射到图表,支持动画与 HUD 信息同步。
|
||
|
||
**章节来源**
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L15-L115)
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L150-L200)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L68)
|
||
- [src/types.ts](file://src/types.ts#L1-L30)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
|
||
## 架构总览
|
||
后端 data_builder.py 以 FinancialSimulator 为核心,将 PDF 输入转换为两条时间线:
|
||
- 年度快照(YearlySnapshot):资产余额、收入/支出、事件标记、是否临界。
|
||
- 现金流明细(CashFlowData):按类别拆分的支出、净现金流、资产余额。
|
||
|
||
前端通过 render_props.json 接收两条时间线,CashFlowChart 与 LifePlanChart 分别消费这些数据,实现双视图联动与统一动画进度。
|
||
|
||
```mermaid
|
||
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](file://backend/data_builder.py#L150-L200)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L135)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
|
||
## 详细组件分析
|
||
|
||
### 1) FinancialSimulator 与现金流模拟算法
|
||
- 财务参数默认值:通胀率、投资回报率、赤字利率,均可通过配置注入。
|
||
- 生命周期推进:从当前年龄到预期寿命,每年计算:
|
||
- 收入:退休前为年收入,退休后为 0。
|
||
- 通胀:支出按年通胀指数增长。
|
||
- 事件:匹配当年事件成本累加到支出。
|
||
- 净现金流:收入减去总支出。
|
||
- 资产更新:若资产余额非负,按投资回报率复利;否则按赤字利率复利(债务复合)。
|
||
- 输出:YearlySnapshot 列表,包含年份、年龄、收入/支出/资产余额、事件标签、是否临界。
|
||
|
||
```mermaid
|
||
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(["结束"])
|
||
```
|
||
|
||
**图示来源**
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L24-L98)
|
||
|
||
**章节来源**
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L15-L115)
|
||
|
||
### 2) YearlySnapshot 与 CashFlowData 的生成与映射
|
||
- YearlySnapshot:用于资产耗尽趋势图,包含年份、代表者与配偶年龄、收入/支出/资产余额、事件与临界标记。
|
||
- CashFlowData:用于收支明细图,包含年份、年龄、收入(合计)、各类支出(生活/住房/教育/税费/事件)、总支出、净现金流、资产余额。
|
||
- 数据映射:DualChartView 将 CashFlowData 转换为 YearlySnapshot 供 LifePlanChart 使用,保持统一动画进度与 HUD。
|
||
|
||
```mermaid
|
||
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 : "转换"
|
||
```
|
||
|
||
**图示来源**
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L68)
|
||
- [src/types.ts](file://src/types.ts#L1-L30)
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L116-L148)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L48-L67)
|
||
|
||
**章节来源**
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L68)
|
||
- [src/types.ts](file://src/types.ts#L1-L30)
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L116-L148)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L48-L67)
|
||
|
||
### 3) Recharts 组件与数据契约映射
|
||
- YearlySnapshotSchema:约束资产耗尽图的时间线结构,含货币单位与起始年份元数据。
|
||
- CashFlowDataSchema:约束收支明细图的时间线结构,含货币单位元数据。
|
||
- 组件职责:
|
||
- CashFlowChart:堆叠柱状图展示各类支出,折线展示收入,支持年进度插值与 HUD。
|
||
- LifePlanChart:面积图展示资产余额,支持年进度插值与渐变色(正负区间)。
|
||
- DualChartView:统一动画进度,将 CashFlowData 转 YearlySnapshot 供 LifePlanChart 使用。
|
||
|
||
```mermaid
|
||
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](file://frontend/src/render-props-types.ts#L140-L231)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L1-L176)
|
||
|
||
**章节来源**
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L140-L231)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L1-L176)
|
||
|
||
### 4) 数据插值、单位转换与异常值处理
|
||
- 插值:
|
||
- LifePlanChart:在年与年之间进行线性插值,生成“当前年进度”的中间点,保证面积图平滑过渡。
|
||
- CashFlowChart:对当前年份内的支出条目按阶段顺序增长,同时生成一个“延伸点”以平滑收入线的视觉过渡。
|
||
- 单位转换:
|
||
- 元数据 currencyUnit 统一为“万円”,前端 Y 轴与 HUD 显示使用该单位。
|
||
- LifePlanChart 对大数值采用“亿”格式化显示。
|
||
- 异常值处理:
|
||
- 事件成本按年聚合,避免重复或遗漏。
|
||
- 资产余额为负时标记 isCritical,用于图表颜色与 HUD 提示。
|
||
- 当可视窗口小于固定长度时,填充空年份占位,避免坐标轴偏移。
|
||
|
||
**章节来源**
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L100-L141)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L320-L341)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L156-L210)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L211-L240)
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L100-L115)
|
||
|
||
### 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 校验,确保数据一致性。
|
||
|
||
**章节来源**
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L150-L200)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L135)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L202-L216)
|
||
|
||
### 6) 自定义财务假设配置方法
|
||
- 配置入口:FinancialSimulator 的构造函数接收 config 字典,支持以下键:
|
||
- inflation_rate:通胀率,默认 2%
|
||
- investment_return:投资回报率,默认 3%
|
||
- deficit_interest_rate:赤字利率(负资产时的复合成本),默认 15%
|
||
- 使用方式:在调用 build_chart_data 前,将上述键值放入 config 传入 FinancialSimulator。
|
||
|
||
**章节来源**
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L18-L23)
|
||
- [backend/data_builder.py](file://backend/data_builder.py#L150-L200)
|
||
|
||
## 依赖关系分析
|
||
- 后端依赖:typing、math(用于幂运算与圆整)。
|
||
- 前端依赖:Remotion hooks(useCurrentFrame/useVideoConfig/interpolate)、Recharts(ComposedChart/AreaChart 等)、Zod(Schema 校验)。
|
||
- 类型契约:render-props-types.ts 定义 YearlySnapshotSchema 与 CashFlowDataSchema,确保前后端一致。
|
||
|
||
```mermaid
|
||
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](file://backend/data_builder.py#L15-L148)
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L140-L231)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L1-L176)
|
||
|
||
**章节来源**
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L140-L231)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L1-L176)
|
||
|
||
## 性能考量
|
||
- 大数据量分段处理策略:
|
||
- 限制每帧可见数据长度:当可视窗口较短时,使用固定域与占位年份,避免 Recharts 重绘抖动。
|
||
- 使用 useMemo 缓存转换结果(如 DualChartView 将 CashFlowData 转 YearlySnapshot),减少组件重渲染。
|
||
- 动画插值仅在必要范围内计算,避免全量数据参与每帧运算。
|
||
- 数值稳定性:
|
||
- 对资产余额与支出进行圆整,减少浮点误差累积。
|
||
- Y 轴域基于完整时间线计算,避免动画过程中轴范围跳变。
|
||
- 渲染优化:
|
||
- 关闭内部动画(isAnimationActive=false),由外部帧进度驱动,降低 Recharts 内部开销。
|
||
- 使用稳定的配置对象(如 CHART_MARGIN)避免每次渲染创建新对象。
|
||
|
||
**章节来源**
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L166-L190)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L211-L240)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L48-L67)
|
||
|
||
## 故障排查指南
|
||
- 数据不一致:
|
||
- 检查 render-props-types.ts 的 YearlySnapshotSchema 与 CashFlowDataSchema 是否与后端输出一致。
|
||
- 使用 validateRenderProps 或 isRenderProps 进行运行时校验。
|
||
- 图表显示异常:
|
||
- 确认 meta.currencyUnit 与前端 Y 轴单位一致。
|
||
- 若出现坐标轴跳变,检查 Y 轴域计算逻辑(完整时间线 vs 可见数据)。
|
||
- 动画不同步:
|
||
- 确保 DualChartView 使用统一的外部索引与年进度参数传递给两个子图。
|
||
- 事件/临界状态错误:
|
||
- 检查事件成本聚合与 isCritical 标记逻辑,确保资产余额为负时正确标注。
|
||
|
||
**章节来源**
|
||
- [frontend/src/render-props-types.ts](file://frontend/src/render-props-types.ts#L237-L251)
|
||
- [src/DualChartView.tsx](file://src/DualChartView.tsx#L31-L76)
|
||
- [src/CashFlowChart.tsx](file://src/CashFlowChart.tsx#L358-L438)
|
||
- [src/LifePlanChart.tsx](file://src/LifePlanChart.tsx#L292-L343)
|
||
|
||
## 结论
|
||
本模块通过 FinancialSimulator 将 PDF 输入转化为两条高质量时序数据,借助 YearlySnapshot 与 CashFlowData 的清晰契约,实现了资产耗尽趋势与收支明细的双视图联动渲染。前后端通过 Zod Schema 严格约束数据结构,配合 Recharts 的插值与 HUD 机制,提供了流畅且可解释的财务可视化体验。通过合理的分段处理与缓存策略,可在大数据量场景下保持稳定性能。
|
||
|
||
## 附录
|
||
- 示例数据参考:
|
||
- [src/mock-cashflow.json](file://src/mock-cashflow.json#L1-L120)
|
||
- [src/mock-data.json](file://src/mock-data.json#L1-L61) |