378 lines
15 KiB
Markdown
378 lines
15 KiB
Markdown
# 数据模型与契约
|
||
|
||
<cite>
|
||
**本文引用的文件**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts)
|
||
- [types.ts](file://src/types.ts)
|
||
- [data_builder.py](file://backend/video_processor/data_builder.py)
|
||
- [main.py](file://main.py)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx)
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx)
|
||
- [mock-data.json](file://src/mock-data.json)
|
||
- [mock-cashflow.json](file://src/mock-cashflow.json)
|
||
</cite>
|
||
|
||
## 目录
|
||
1. [引言](#引言)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心组件](#核心组件)
|
||
4. [架构总览](#架构总览)
|
||
5. [详细组件分析](#详细组件分析)
|
||
6. [依赖关系分析](#依赖关系分析)
|
||
7. [性能考量](#性能考量)
|
||
8. [故障排查指南](#故障排查指南)
|
||
9. [结论](#结论)
|
||
10. [附录](#附录)
|
||
|
||
## 引言
|
||
本文件聚焦于“核心数据模型与系统间数据契约”,围绕 render-props-types.ts 中定义的 RenderPropsSchema 设计进行深入解析。该契约是 Python 后端与 Remotion 前端之间的唯一通信桥梁:后端生成 render_props.json,前端通过 Zod 运行时校验确保数据完整性,再驱动可视化组件渲染视频内容。本文将逐层拆解 YearlySnapshot、CashFlowData、ScriptSegment、AssetCollection 等关键数据结构的字段语义、类型约束与业务逻辑;阐明 Zod 如何在运行时保障数据一致性;并通过示例文件展示 render_props.json 的实际形态,帮助初学者理解数据契约的重要性与最佳实践。
|
||
|
||
## 项目结构
|
||
本项目采用前后端分离的模块化组织:
|
||
- 前端(Remotion)位于 src 与 frontend/src,负责渲染与可视化;
|
||
- 后端(Python)位于 backend,负责数据模拟与 render_props.json 生成;
|
||
- 契约文件 render-props-types.ts 定义了 TypeScript 接口与 Zod Schema,确保前后端对同一份数据结构达成一致。
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "后端"
|
||
PY_MAIN["main.py<br/>构建与保存 render_props.json"]
|
||
PY_DB["backend/video_processor/data_builder.py<br/>财务模拟与图表数据生成"]
|
||
end
|
||
subgraph "前端"
|
||
TS_TYPES["frontend/src/render-props-types.ts<br/>RenderPropsSchema 定义与运行时校验"]
|
||
REACT_LPC["src/LifePlanChart.tsx<br/>资产枯竭曲线图"]
|
||
REACT_CFC["src/CashFlowChart.tsx<br/>现金流分解图"]
|
||
TYPES_TS["src/types.ts<br/>YearlySnapshot 等类型"]
|
||
end
|
||
PY_MAIN --> PY_DB
|
||
PY_DB --> PY_MAIN
|
||
PY_MAIN --> TS_TYPES
|
||
TS_TYPES --> REACT_LPC
|
||
TS_TYPES --> REACT_CFC
|
||
TYPES_TS --> REACT_LPC
|
||
```
|
||
|
||
**图表来源**
|
||
- [main.py](file://main.py#L145-L199)
|
||
- [data_builder.py](file://backend/video_processor/data_builder.py#L150-L200)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L231)
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [types.ts](file://src/types.ts#L1-L30)
|
||
|
||
**章节来源**
|
||
- [main.py](file://main.py#L145-L199)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L231)
|
||
|
||
## 核心组件
|
||
本节从“数据契约”的角度,系统梳理 RenderProps 及其子结构,明确字段含义、类型与业务约束,并说明 Zod Schema 的作用。
|
||
|
||
- RenderProps(顶层契约)
|
||
- meta:视频元信息,包含标题、货币单位、总时长、可选客户名与生成时间戳
|
||
- charts:两类图表数据
|
||
- assetDepletion:资产枯竭曲线的时间序列与元信息(货币单位、起始年份)
|
||
- cashFlow:现金流分解的时间序列与元信息(货币单位)
|
||
- script:脚本段落数组,描述旁白、图表介绍/高亮、视频片段、转场等
|
||
- assets:媒体资源集合,包含 B-Roll 映射、视频片段引用、可选 TTS 音频路径
|
||
|
||
- YearlySnapshot(年度快照)
|
||
- year:年份
|
||
- ages:代表者年龄与可选配偶年龄
|
||
- financials:收入、支出、资产余额
|
||
- events:当年事件标签列表
|
||
- isCritical:资产余额是否为负(标记风险临界点)
|
||
|
||
- CashFlowData(年度现金流)
|
||
- year、age:年份与年龄
|
||
- income.total:年度总收入
|
||
- expenses:生活、住房、教育、税金、事件等细项
|
||
- totalExpense、netCashFlow、assetBalance:派生或补充字段(由后端计算)
|
||
|
||
- ScriptSegment(脚本段落)
|
||
- id、text、duration:段落标识、日文旁白文本、时长(秒)
|
||
- type:段落类型枚举(旁白、图表介绍、图表高亮、视频片段、转场)
|
||
- assetUrl:B-Roll 或视频片段 URL/路径
|
||
- chartSync:可选的图表同步参数(图表 ID、高亮年份、动画起始帧)
|
||
|
||
- AssetCollection(媒体资源集合)
|
||
- broll:关键词到视频 URL/路径的映射
|
||
- videoClips:视频片段引用数组(id、源路径、起止时间、输出路径)
|
||
- ttsAudio:可选的 TTS 语音文件路径
|
||
|
||
- Zod Schema(运行时校验)
|
||
- YearlySnapshotSchema、CashFlowDataSchema、ScriptSegmentSchema、RenderPropsSchema
|
||
- 提供 parse/safeParse 两种校验方式,前者失败抛错,后者返回布尔判断
|
||
- validateRenderProps/isRenderProps 两个辅助函数,分别用于强制校验与类型守卫
|
||
|
||
**章节来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L231)
|
||
- [types.ts](file://src/types.ts#L1-L30)
|
||
|
||
## 架构总览
|
||
RenderPropsSchema 作为 Python 后端与 Remotion 前端之间的“数据契约”,贯穿以下流程:
|
||
- 后端根据 PDF 输入与财务模拟器生成 render_props.json
|
||
- 前端读取 render_props.json,使用 Zod Schema 进行运行时校验
|
||
- 校验通过后,前端组件(如 LifePlanChart、CashFlowChart)消费数据并渲染视频
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Backend as "Python 后端"
|
||
participant Builder as "data_builder.py"
|
||
participant Main as "main.py"
|
||
participant Frontend as "Remotion 前端"
|
||
participant Schema as "render-props-types.ts"
|
||
participant Charts as "LifePlanChart/CashFlowChart"
|
||
Backend->>Builder : "输入PDF数据"
|
||
Builder->>Builder : "财务模拟与图表数据生成"
|
||
Builder-->>Main : "返回 charts 财务数据"
|
||
Main->>Main : "拼装 meta/script/assets"
|
||
Main-->>Frontend : "render_props.json"
|
||
Frontend->>Schema : "validateRenderProps/isRenderProps"
|
||
Schema-->>Frontend : "校验结果"
|
||
Frontend->>Charts : "传入 RenderProps 渲染"
|
||
Charts-->>Frontend : "输出视频帧"
|
||
```
|
||
|
||
**图表来源**
|
||
- [data_builder.py](file://backend/video_processor/data_builder.py#L150-L200)
|
||
- [main.py](file://main.py#L145-L199)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L194-L250)
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
|
||
## 详细组件分析
|
||
|
||
### RenderPropsSchema 设计与运行时校验
|
||
- 设计要点
|
||
- 严格嵌套结构:meta、charts、script、assets 四大块,每块内部字段一一对应
|
||
- 类型精确:数值、字符串、枚举、可选字段均有明确约束
|
||
- 元信息与数据一致性:charts.meta 携带货币单位与起始年份,保证图表显示一致
|
||
- 运行时校验
|
||
- parse:失败即抛异常,适合强约束场景
|
||
- safeParse:返回成功/失败,便于条件分支处理
|
||
- 辅助函数:validateRenderProps 与 isRenderProps 提供统一入口
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start(["开始"]) --> Load["读取 render_props.json"]
|
||
Load --> Parse["调用 RenderPropsSchema.parse/safeParse"]
|
||
Parse --> Valid{"校验通过?"}
|
||
Valid --> |否| Throw["抛出错误/返回失败"]
|
||
Valid --> |是| Use["前端组件消费数据"]
|
||
Use --> End(["结束"])
|
||
Throw --> End
|
||
```
|
||
|
||
**图表来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L237-L250)
|
||
|
||
**章节来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L194-L250)
|
||
|
||
### YearlySnapshot 与 CashFlowData 的字段语义与业务逻辑
|
||
- YearlySnapshot
|
||
- 年度视角的财务快照,支持代表者与配偶双人年龄,便于双人财务规划可视化
|
||
- financials.income/expenditure/assetBalance 三元组构成资产枯竭曲线的核心
|
||
- events 与 isCritical 用于标注重大事件与风险节点
|
||
- CashFlowData
|
||
- 将 YearlySnapshot 转换为更利于“收入-支出”分解展示的数据形式
|
||
- expenses 细分五类,便于堆叠柱状图呈现
|
||
- totalExpense、netCashFlow、assetBalance 为派生字段,便于 HUD 展示与动画控制
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class YearlySnapshot {
|
||
+number year
|
||
+ages
|
||
+financials
|
||
+string[] events?
|
||
+boolean isCritical?
|
||
}
|
||
class CashFlowData {
|
||
+number year
|
||
+number age
|
||
+income
|
||
+expenses
|
||
+number totalExpense?
|
||
+number netCashFlow?
|
||
+number assetBalance?
|
||
}
|
||
class incomes {
|
||
+number total
|
||
}
|
||
class expenses {
|
||
+number living
|
||
+number housing
|
||
+number education
|
||
+number tax
|
||
+number event
|
||
}
|
||
YearlySnapshot --> incomes : "financials.income"
|
||
YearlySnapshot --> expenses : "financials.expenditure"
|
||
CashFlowData --> incomes : "income"
|
||
CashFlowData --> expenses : "expenses"
|
||
```
|
||
|
||
**图表来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L49)
|
||
- [types.ts](file://src/types.ts#L1-L18)
|
||
|
||
**章节来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L18-L49)
|
||
- [types.ts](file://src/types.ts#L1-L18)
|
||
|
||
### ScriptSegment 与 AssetCollection 的脚本与媒体资源
|
||
- ScriptSegment
|
||
- 类型枚举覆盖旁白、图表介绍/高亮、视频片段、转场等场景
|
||
- chartSync 支持与图表联动,实现高亮年份与动画起始帧的同步
|
||
- AssetCollection
|
||
- broll 关键词到视频资源的映射,便于按主题检索
|
||
- videoClips 记录剪辑片段的来源与裁剪参数,便于复用与导出
|
||
- ttsAudio 为可选的人工合成语音路径
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class ScriptSegment {
|
||
+number id
|
||
+string text
|
||
+number duration
|
||
+string type
|
||
+string assetUrl?
|
||
+chartSync?
|
||
}
|
||
class AssetCollection {
|
||
+Record~string,string~ broll
|
||
+VideoClipReference[] videoClips
|
||
+string ttsAudio?
|
||
}
|
||
class VideoClipReference {
|
||
+string id
|
||
+string sourcePath
|
||
+number startTime
|
||
+number duration
|
||
+string outputPath?
|
||
}
|
||
ScriptSegment --> AssetCollection : "引用媒体资源"
|
||
AssetCollection --> VideoClipReference : "包含"
|
||
```
|
||
|
||
**图表来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L74-L111)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L99-L111)
|
||
|
||
**章节来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L74-L111)
|
||
|
||
### 前端消费与可视化组件
|
||
- LifePlanChart(资产枯竭曲线)
|
||
- 使用 YearlySnapshot 渲染面积图,支持插值与渐变色(正负资产区域)
|
||
- HUD 展示当前年份、资产余额、收支差额
|
||
- CashFlowChart(现金流分解)
|
||
- 使用 CashFlowData 渲染堆叠柱状图与收入线,支持分段顺序增长动画
|
||
- HUD 展示当前年份、收入、总支出、净现金流
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Frontend as "Remotion 前端"
|
||
participant Schema as "RenderPropsSchema"
|
||
participant LPC as "LifePlanChart"
|
||
participant CFC as "CashFlowChart"
|
||
Frontend->>Schema : "校验 render_props.json"
|
||
Schema-->>Frontend : "校验通过"
|
||
Frontend->>LPC : "传入 assetDepletion 数据"
|
||
Frontend->>CFC : "传入 cashFlow 数据"
|
||
LPC-->>Frontend : "渲染资产曲线帧"
|
||
CFC-->>Frontend : "渲染现金流帧"
|
||
```
|
||
|
||
**图表来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L194-L231)
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
|
||
**章节来源**
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
|
||
## 依赖关系分析
|
||
- 后端到前端的数据流
|
||
- main.py 负责组装 render_props.json 并保存
|
||
- data_builder.py 生成 charts 数据(资产枯竭与现金流)
|
||
- 前端 render-props-types.ts 以 Zod Schema 对齐数据结构
|
||
- 前端组件对数据的依赖
|
||
- LifePlanChart 依赖 YearlySnapshot 类型
|
||
- CashFlowChart 依赖 CashFlowData 类型
|
||
- 两者均依赖 RenderPropsSchema 的校验结果
|
||
|
||
```mermaid
|
||
graph LR
|
||
PY["main.py"] --> DB["data_builder.py"]
|
||
DB --> JSON["render_props.json"]
|
||
JSON --> TS["render-props-types.ts"]
|
||
TS --> LPC["LifePlanChart.tsx"]
|
||
TS --> CFC["CashFlowChart.tsx"]
|
||
TYPES["types.ts"] --> LPC
|
||
```
|
||
|
||
**图表来源**
|
||
- [main.py](file://main.py#L145-L199)
|
||
- [data_builder.py](file://backend/video_processor/data_builder.py#L150-L200)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L231)
|
||
- [LifePlanChart.tsx](file://src/LifePlanChart.tsx#L1-L349)
|
||
- [CashFlowChart.tsx](file://src/CashFlowChart.tsx#L1-L441)
|
||
- [types.ts](file://src/types.ts#L1-L30)
|
||
|
||
**章节来源**
|
||
- [main.py](file://main.py#L145-L199)
|
||
- [data_builder.py](file://backend/video_processor/data_builder.py#L150-L200)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L231)
|
||
|
||
## 性能考量
|
||
- 数据规模与渲染开销
|
||
- 大时间序列(如多代人、长周期)会增加 Recharts 渲染压力
|
||
- 建议在后端进行数据采样或聚合,减少前端渲染负担
|
||
- 动画与插值
|
||
- 插值与顺序增长动画需谨慎使用,避免在长序列上产生过多帧级计算
|
||
- 校验成本
|
||
- Zod 校验在开发阶段建议开启,生产环境可考虑缓存校验结果或仅在首帧执行
|
||
|
||
[本节为通用指导,不直接分析具体文件]
|
||
|
||
## 故障排查指南
|
||
- 校验失败
|
||
- 现象:validateRenderProps 抛错;isRenderProps 返回 false
|
||
- 排查:检查字段类型(数字/字符串)、可选字段是否遗漏、枚举值是否正确
|
||
- 参考:Zod Schema 定义与辅助函数
|
||
- 数据不一致
|
||
- 现象:图表显示货币单位不一致、起始年份错位
|
||
- 排查:确认 charts.meta.currencyUnit 与 charts.meta.startYear 是否与 timeline 对齐
|
||
- 脚本与媒体资源不匹配
|
||
- 现象:ScriptSegment.chartSync 未生效或 assetUrl 无法加载
|
||
- 排查:确认 chartId、highlightYear、animationStart 与 assets.broll/videoClips 的键值一致
|
||
|
||
**章节来源**
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L194-L250)
|
||
|
||
## 结论
|
||
RenderPropsSchema 通过严格的类型与运行时校验,将 Python 后端的 render_props.json 与 Remotion 前端的可视化组件紧密耦合,形成稳定、可演进的数据契约。YearlySnapshot 与 CashFlowData 的清晰边界,配合 ScriptSegment 与 AssetCollection 的脚本与媒体资源管理,使得系统在保持前后端松耦合的同时,具备良好的扩展性与可维护性。建议在后续迭代中持续对齐契约版本,确保前后端同步更新。
|
||
|
||
[本节为总结性内容,不直接分析具体文件]
|
||
|
||
## 附录
|
||
|
||
### render_props.json 实例与字段对照
|
||
- 示例文件
|
||
- mock-data.json:资产枯竭曲线的 timeline 与 meta
|
||
- mock-cashflow.json:现金流分解的 timeline 与 meta
|
||
- 字段对照
|
||
- meta.title、meta.currencyUnit、meta.duration、meta.clientName、meta.generatedAt
|
||
- charts.assetDepletion.timeline、charts.assetDepletion.meta.currencyUnit、charts.assetDepletion.meta.startYear
|
||
- charts.cashFlow.timeline、charts.cashFlow.meta.currencyUnit
|
||
- script[].id、script[].text、script[].duration、script[].type、script[].assetUrl、script[].chartSync
|
||
- assets.broll、assets.videoClips[]、assets.ttsAudio
|
||
|
||
**章节来源**
|
||
- [mock-data.json](file://src/mock-data.json#L1-L61)
|
||
- [mock-cashflow.json](file://src/mock-cashflow.json#L1-L800)
|
||
- [render-props-types.ts](file://frontend/src/render-props-types.ts#L117-L231) |