You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.8 KiB
5.8 KiB
RFC 130: Timeline Mock 数据重构
状态: 已实现
创建日期: 2026-01-24
作者: AI Assistant
背景
原有的 timeline mock 数据采用独立存储的方式,与后端 timeline-service.md 的设计理念不符。后端设计明确指出:
时间轴管理服务是分镜数据的可视化看板,提供多轨道视图展示分镜及其关联的所有资源。所有数据实时从分镜及其关联表计算,无独立数据存储。
问题
1. 数据冗余
- timeline.ts 中维护了独立的
mockTracks和mockItems数据 - 这些数据与 storyboards.ts 中的数据重复
- 需要手动维护数据同步
2. 关联性错误
- 资源轨道的 resourceId 引用不正确
- 没有真正从 storyboard.resources 获取数据
- 时间计算逻辑与分镜数据脱节
3. 扩展性差
- 添加新的分镜时需要同时更新 timeline 数据
- 容易出现数据不一致的问题
解决方案
核心设计
实现 getTimelineByProjectId() 函数,实时从 storyboards 计算时间轴数据:
export function getTimelineByProjectId(projectId: string): Timeline {
// 1. 获取项目的所有分镜(已按 orderIndex 排序)
const storyboards = getStoryboardsByProjectId(projectId);
// 2. 初始化轨道
const tracks: TimelineTrack[] = [...];
const items: TimelineItem[] = [];
// 3. 遍历分镜,累积计算时间位置
let currentTime = 0;
for (const storyboard of storyboards) {
const startTime = currentTime;
const endTime = currentTime + storyboard.duration;
// 3.1 添加分镜轨道项
items.push({ ... });
// 3.2 从 storyboard.resources 获取资源轨道项
if (storyboard.resources) {
// 角色、场景、道具、实拍
}
// 3.3 其他轨道(视频、音效、字幕、配音)
// TODO: 待实现
currentTime = endTime;
}
return { tracks, items };
}
数据流
storyboards (分镜数据)
└─ resources (关联的资源)
├─ characters
├─ scenes
├─ props
└─ footages
↓
getTimelineByProjectId() 实时计算
↓
返回 Timeline { tracks, items }
时间计算逻辑
// 按 orderIndex 排序分镜
const storyboards = getStoryboardsByProjectId(projectId);
// 累积计算每个分镜的时间位置
let currentTime = 0;
for (const storyboard of storyboards) {
const startTime = currentTime;
const endTime = currentTime + storyboard.duration;
// 添加轨道项...
currentTime = endTime; // 累加时间
}
实现细节
1. 分镜轨道
直接从 storyboard 数据生成:
items.push({
id: storyboard.id,
trackId: `track-storyboard-${projectId}`,
itemType: 'storyboard',
storyboardId: storyboard.id,
startTime,
endTime,
displayOrder: storyboard.orderIndex,
createdAt: storyboard.createdAt,
updatedAt: storyboard.updatedAt,
});
2. 资源轨道
从 storyboard.resources 字段获取:
if (storyboard.resources) {
// 角色资源
storyboard.resources.characters?.forEach((charRef, index) => {
const resource = getResourceById(charRef.resourceId);
if (resource) {
items.push({
id: `${storyboard.id}-character-${charRef.resourceId}`,
trackId: `track-resource-${projectId}`,
itemType: 'storyboard',
storyboardId: storyboard.id,
startTime,
endTime,
displayOrder: index,
// ...
});
}
});
// 场景、道具、实拍同理
}
3. 其他轨道
预留 TODO 注释,待后续实现:
// 3. 视频轨道项(如果分镜有生成的视频)
// TODO: 当实现视频生成功能后,从 storyboard.generated_video 获取
// 4. 音效轨道项
// TODO: 当实现音效功能后,从 storyboard.sound_effects 获取
// 5. 字幕轨道项(对白)
// TODO: 当实现对白功能后,从 storyboard.dialogues 获取
// 6. 配音轨道项
// TODO: 当实现配音功能后,从 storyboard.voiceovers 获取
向后兼容
为了不破坏现有代码,保留了以下导出:
// 向后兼容:使用默认项目 ID 生成 mockTimeline
export const mockTimeline = getTimelineByProjectId('018e1234-5678-7abc-8def-100000000001');
// 向后兼容:保留 UI 展示用的简化数据
export const mockTimelineTracks: TimelineTrackUI[] = [...];
优势
1. 无数据冗余
- 时间轴不存储独立数据
- 所有数据源自 storyboards
- 避免数据同步问题
2. 自动同步
- 分镜变化立即反映到时间轴
- 无需手动维护同步逻辑
3. 关联正确
- 资源轨道真正从 storyboard.resources 获取
- 通过 getResourceById() 验证资源存在性
- 时间计算基于分镜的 duration
4. 易于扩展
- 添加新分镜时,时间轴自动更新
- 后续添加视频、音效等功能时,只需从 storyboard 对应字段获取
5. 符合后端设计
- 完全遵循 timeline-service.md 的设计理念
- 实现了"实时计算"的核心原则
测试建议
- 验证时间轴数据正确生成
- 验证资源轨道项与 storyboard.resources 一致
- 验证时间位置累积计算正确
- 验证向后兼容性(mockTimeline 导出)
后续工作
- 实现视频轨道(从 storyboard.generated_video 获取)
- 实现音效轨道(从 storyboard.sound_effects 获取)
- 实现字幕轨道(从 storyboard.dialogues 获取)
- 实现配音轨道(从 storyboard.voiceovers 获取)
- 逐步迁移使用 getTimelineByProjectId() 替代 mockTimeline