Files
sionrui/openspec/changes/refactor-mix-scene编排/specs/scene-candidates/spec.md
2025-12-22 00:15:02 +08:00

16 KiB
Raw Blame History

场景多候选功能规格文档

ADDED Requirements

Requirement: 场景候选数据结构

MUST: 每个场景必须支持存储多个候选视频素材,替代原有的单一素材模式。 优先级:版本: v1.0

数据结构:

interface Scene {
  index: number;           // 场景序号
  candidates: Material[];  // 候选素材列表
  duration: number;        // 单场景时长
}

interface Material {
  fileId: number;          // 素材文件ID
  fileUrl: string;         // 素材文件URL
  fileDuration?: number;   // 素材实际时长(可选)
}

验证规则:

  • 每个场景至少包含 1 个候选素材
  • 每个场景最多包含 10 个候选素材
  • 同一场景内的候选素材不能重复(基于 fileId 判断)
  • 候选素材必须为视频类型

Scenario: 创建新场景

用户调整总时长和单切片时长后,系统自动创建对应数量的空场景。每个场景初始化时包含一个空的候选列表,等待用户添加素材。

Scenario: 添加候选

用户点击场景格子,打开候选选择弹窗,从素材库中选择多个视频素材添加到场景的候选列表中。

Scenario: 验证重复

当用户尝试添加已在候选列表中的素材时系统自动检查并阻止添加同时在UI上显示提示信息。

Scenario: 限制数量

当场景的候选数量达到上限10个系统禁用添加按钮并提示用户已达到最大候选数量。


Requirement: 场景候选管理操作

MUST: 用户必须能够对场景的候选素材进行增删改查操作。

操作类型:

  1. 添加候选:从素材库选择视频添加到场景候选
  2. 移除候选:从场景候选中移除指定的素材
  3. 清空场景:移除场景的所有候选素材
  4. 查看候选:以弹窗或侧边栏形式展示所有候选

交互规则:

  • 点击场景格子打开候选选择弹窗
  • 弹窗中显示当前场景已选候选数量
  • 素材库中已选候选显示"已选择"状态
  • 支持批量选择多个候选后一次性确认

Scenario: 添加单个候选

用户在场景格子上的弹窗中选择一个素材,确认后该素材被添加到场景的候选列表中。

Scenario: 批量添加候选

用户在素材库中选择多个素材,然后点击"批量添加"按钮,一次性将所有选中的素材添加到场景候选列表中。

Scenario: 移除候选

用户在场景格子或弹窗中点击候选素材上的移除按钮,系统将该候选从场景候选列表中删除。

Scenario: 查看候选详情

用户点击场景格子,系统以弹窗形式展示该场景的所有候选素材,包括缩略图、文件名和时长信息。


Requirement: 一键填充功能优化

MUST: 系统必须优化一键填充功能,自动为每个场景添加多个候选素材。

填充策略:

  1. 随机分配:从素材库中随机选择素材分配给每个场景
  2. 防重复:确保同一场景内的候选不重复
  3. 尽量均匀:尽可能平均分配素材到各个场景
  4. 数量控制:每个场景填充 3-5 个候选(根据素材库数量动态调整)

算法逻辑:

For each scene in scenes:
  If scene.candidates.isEmpty():
    randomly select 3-5 materials from groupFiles
    ensure no duplicate within scene
    add to scene.candidates

Scenario: 自动填充空场景

用户点击"一键填充"按钮,系统只填充空的场景,已有候选的场景保持不变。

Scenario: 补充候选数量

如果场景的候选数量不足默认数量3个系统自动补充候选素材到默认数量。

Scenario: 全量填充

用户选择"全量填充"选项,系统为所有场景(包括已有候选的场景)重新填充候选素材。

Scenario: 智能跳过

系统自动检测已填满的场景并跳过,只处理需要填充的场景。


Requirement: 场景候选可视化展示

MUST: 系统必须在前端界面中清晰展示每个场景的候选数量和候选列表。

UI 展示元素:

  1. 候选数量标签:在场景格子上显示"候选数量/X"
  2. 候选列表预览:以缩略图或标签形式展示候选
  3. 使用状态标识:标识哪些候选已被使用
  4. 悬停提示:鼠标悬停显示候选详细信息

样式规范:

  • 候选数量使用徽标组件badge展示
  • 候选列表使用小缩略图或文件图标
  • 已使用候选使用不同颜色或图标标识
  • 悬停提示显示候选文件名和时长

Scenario: 查看候选概览

用户在主界面上可以直观地看到每个场景显示的候选数量,快速了解整体配置情况。

Scenario: 预览候选内容

用户将鼠标悬停在场景格子上,系统显示该场景所有候选的缩略图预览。

Scenario: 识别使用状态

用户可以通过不同的视觉标识(如颜色、图标)快速识别哪些候选素材已被使用。

Scenario: 快速定位

用户通过可视化展示快速定位需要编辑的场景,提高操作效率。


Requirement: 场景候选防重复机制

MUST: 系统必须确保同一场景内的候选素材不重复,保证素材多样性。

验证机制:

  1. 前端验证:在选择素材时实时检查并提示
  2. 后端验证:在提交时进行最终验证
  3. UI 反馈:已选择的素材显示禁用或选中状态

重复判断规则:

  • 基于 fileId 进行唯一性判断
  • fileId 相同视为重复素材
  • 允许同一素材在不同场景中出现

Scenario: 阻止重复添加

用户在选择素材时,如果该素材已在候选列表中,系统立即提示"该素材已在候选列表中",并阻止添加。

Scenario: 视觉反馈

已选择的素材在素材库中显示为禁用状态,用户可以直观地看到哪些素材已被选择。

Scenario: 批量去重

一键填充功能自动去除重复候选,确保每个场景内的候选都是唯一的。

Scenario: 手动去重

用户可以在场景候选列表中手动移除重复的候选素材,系统保持列表的唯一性。


MODIFIED Requirements

Requirement: 混剪任务提交数据结构

MUST: 系统必须修改混剪任务提交数据结构以支持场景多候选模式。

修改前: MUST: ```json { "title": "视频标题", "materials": [ {"fileId": 1, "fileUrl": "url1", "duration": 3}, {"fileId": 2, "fileUrl": "url2", "duration": 3} ], "produceCount": 3 }


**系统必须修改为以下结构:**
```json
{
  "title": "视频标题",
  "scenes": [
    {
      "duration": 3,
      "candidates": [
        {"fileId": 1, "fileUrl": "url1", "fileDuration": 60},
        {"fileId": 2, "fileUrl": "url2", "fileDuration": 45}
      ]
    },
    {
      "duration": 3,
      "candidates": [
        {"fileId": 3, "fileUrl": "url3", "fileDuration": 50},
        {"fileId": 4, "fileUrl": "url4", "fileDuration": 55}
      ]
    }
  ],
  "produceCount": 3
}

向后兼容:

  • 支持旧的 materials 字段格式
  • 当接收到 materials 时,自动转换为新的 scenes 格式
  • 保持现有 API 端点不变

Scenario: 提交新格式

前端使用新的 scenes 格式提交混剪任务,包含每个场景的候选素材列表。

Scenario: 兼容旧格式

后端接收到包含 materials 字段的旧格式数据时,自动将其转换为 scenes 格式(每个场景包含一个候选)。

Scenario: 数据转换

系统将旧格式的 materials 数组转换为新格式的 scenes 数组,每个场景包含一个候选素材。

Scenario: 版本协商

前后端协商确定使用的数据格式,优先使用新格式,向后兼容旧格式。


Requirement: 批量混剪随机选择逻辑

MUST: 系统必须实现两层随机选择逻辑以最大化视频内容差异。

修改前: MUST: 使用相同的素材列表,通过随机起点实现差异化:

// 每个视频使用相同的素材,不同的截取起点
List<MaterialItem> materials = createReqVO.getMaterials();
for (int videoIndex = 0; videoIndex < produceCount; videoIndex++) {
  produceSingleVideoWithOffset(materials, videoIndex, userId, cropMode);
}

系统必须修改为以下结构: 系统必须从每个场景的候选中随机选择一个素材,然后仍然使用随机起点:

// 从每个场景的候选中随机选择素材,然后通过随机起点实现差异化
List<SceneConfig> scenes = createReqVO.getScenes();
for (int videoIndex = 0; videoIndex < produceCount; videoIndex++) {
  List<MaterialItem> selectedMaterials = new ArrayList<>();
  for (SceneConfig scene : scenes) {
    // 从场景的候选中随机选择一个素材
    MaterialItem selected = selectRandomCandidate(scene.getCandidates(), videoIndex, scene.getIndex());
    selectedMaterials.add(selected);
  }
  // 对选中的素材使用随机起点生成视频
  produceSingleVideoWithOffset(selectedMaterials, videoIndex, userId, cropMode);
}

随机选择算法:

  • 第一层随机:基于 videoIndexsceneIndex 和候选素材 fileId 从每个场景的候选中随机选择一个素材
  • 第二层随机:对选中的素材仍然使用随机起点实现进一步差异化
  • 确保同一 videoIndex 在不同时间运行结果一致
  • 保证不同 videoIndex 选择的素材不同(尽可能)

示例:

  • 场景1有候选[A, B, C]场景2有候选[D, E, F]场景3有候选[G, H, I]
  • 视频1可能选择 A + D + G然后从A的随机起点1、B的随机起点2...生成
  • 视频2可能选择 B + E + H然后从B的随机起点2、E的随机起点3...生成
  • 视频3可能选择 C + F + I然后从C的随机起点3、F的随机起点1...生成

Scenario: 生成第一个视频

从每个场景的候选中使用第一层随机选择一个素材,然后对每个素材应用随机起点生成视频。

Scenario: 生成第二个视频

从每个场景的候选中再次随机选择(尽量与第一个视频不同),然后应用不同的随机起点。

Scenario: 生成第N个视频

每个视频都经历两层随机选择:第一层从场景候选中选择,第二层对选中素材应用随机起点。

Scenario: 保证差异化

通过两层随机性(候选选择 + 随机起点),最大化每个生成视频的内容差异。


Requirement: 场景配置验证规则

MUST: 系统必须更新场景配置验证规则以支持多候选场景。

修改前: MUST: 验证素材列表:

// 验证素材列表不为空
if (req.getMaterials() == null || req.getMaterials().isEmpty()) {
  throw new IllegalArgumentException("素材列表不能为空");
}

// 验证总时长
int totalDuration = req.getMaterials().stream()
    .mapToInt(MixTaskSaveReqVO.MaterialItem::getDuration)
    .sum();

系统必须修改为以下结构: 系统必须验证场景配置:

// 验证场景列表不为空
if (req.getScenes() == null || req.getScenes().isEmpty()) {
  throw new IllegalArgumentException("场景列表不能为空");
}

// 验证每个场景至少有一个候选
for (SceneConfig scene : req.getScenes()) {
  if (scene.getCandidates() == null || scene.getCandidates().isEmpty()) {
    throw new IllegalArgumentException("场景" + scene.getIndex() + "没有候选素材");
  }
  if (scene.getCandidates().size() > MAX_CANDIDATES_PER_SCENE) {
    throw new IllegalArgumentException("场景候选数量不能超过" + MAX_CANDIDATES_PER_SCENE);
  }
}

// 验证总时长
int totalDuration = req.getScenes().stream()
    .mapToInt(scene -> scene.getDuration() * scene.getCandidates().size())
    .sum();

Scenario: 验证场景完整性

检查所有场景都必须包含至少一个候选素材,缺少候选的场景抛出异常。

Scenario: 验证候选数量

检查每个场景的候选数量在允许范围内1-10个超过上限抛出异常。

Scenario: 验证总时长

根据场景数量和候选数量计算总时长,验证是否在 15-30 秒范围内。

Scenario: 验证素材有效性

检查所有候选素材的文件ID和URL有效性无效素材导致验证失败。


性能要求

Requirement: 场景加载性能

目标: 场景数据加载时间 < 2 秒 测量: 从用户选择素材分组到场景渲染完成的时间 场景: 50 个候选素材5 个场景

Scenario: 正常加载

MUST: 在50个候选素材、5个场景的情况下场景数据加载时间不超过2秒。

Scenario: 大量素材加载

测试100个候选素材、10个场景的加载性能确保仍在可接受范围内。

Scenario: 网络延迟场景

在网络延迟300ms的情况下场景加载时间仍在用户可接受范围内。

Scenario: 缓存优化

利用前端缓存机制,提升重复访问时的场景加载速度。


Requirement: 批量混剪性能

目标: 混剪任务创建时间与现有实现持平(< 3 秒) 测量: 从用户点击"开始混剪"到任务创建成功的时间 场景: 5 个场景,每个场景 3-5 个候选,生成 5 个视频

Scenario: 标准场景混剪

MUST: 在标准配置下5个场景每个场景3-5个候选混剪任务创建时间不超过3秒。

Scenario: 大量候选混剪

测试每个场景10个候选的极限情况性能仍在可接受范围内。

Scenario: 批量生成性能

生成5个视频的批量混剪性能与现有实现持平。

Scenario: 并发场景

测试多个用户同时创建混剪任务的性能表现。


Requirement: 内存使用

目标: 前端内存使用增长 < 20% 测量: 场景候选功能开启前后的内存使用对比 场景: 长时间使用混剪功能,累积创建多个任务

Scenario: 正常使用内存

MUST: 用户正常操作混剪功能内存使用增长不超过20%。

Scenario: 长时间使用

用户连续使用混剪功能1小时内存无明显泄漏。

Scenario: 大量数据处理

处理大量候选素材时,内存使用保持在合理范围内。

Scenario: 内存回收

页面切换或刷新后,前端内存能够正确释放。


兼容性要求

Requirement: 向后兼容

要求: 支持现有的 materials 格式 实现: 自动转换旧格式为新格式 测试: 使用旧格式创建混剪任务

Scenario: 旧格式请求

MUST: 后端接收到包含materials字段的请求时自动转换为scenes格式。

Scenario: 新格式请求

前端优先使用新的scenes格式提交请求。

Scenario: 格式检测

系统能够自动检测请求使用的格式并进行相应处理。

Scenario: 错误处理

当格式转换失败时,提供清晰的错误信息。


Requirement: 渐进式迁移

要求: 用户可以选择使用新模式或旧模式 实现: 通过功能开关控制 场景: 新用户使用新模式,老用户可以选择继续使用旧模式

Scenario: 功能开关

MUST: 提供开关让用户选择使用新模式或旧模式。

Scenario: 用户偏好保存

用户的选择偏好能够持久化保存,下次访问时保持上次选择。

Scenario: 模式切换

用户可以在新旧模式之间自由切换。

Scenario: 默认模式

新用户默认使用新模式,老用户默认使用旧模式。


安全要求

Requirement: 输入验证

要求: 严格验证所有用户输入 范围: 文件ID、URL、候选数量等 场景: 防止恶意用户提交非法数据

Scenario: 文件ID验证

MUST: 验证所有文件ID必须是有效的数字且对应的文件存在。

Scenario: URL验证

验证所有URL必须是有效的OSS地址防止XSS攻击。

Scenario: 候选数量限制

限制候选数量在合理范围内防止DDoS攻击。

Scenario: SQL注入防护

使用参数化查询防止SQL注入攻击。


Requirement: 权限控制

要求: 候选素材必须属于当前用户或有权限访问 实现: 后端验证素材所有权 场景: 用户尝试添加他人素材到候选列表

Scenario: 素材所有权验证

MUST: 后端验证候选素材是否属于当前用户或用户有权限访问。

Scenario: 权限检查

对每个候选素材进行权限检查,无权限的素材拒绝添加。

Scenario: 越权防护

防止用户访问或操作其他用户的素材。

Scenario: 审计日志

记录所有素材访问和操作日志,便于安全审计。