AI-video-lifeplan-demo/.qoder/repowiki/zh/content/数据模型与契约.md

378 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 数据模型与契约
<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)