- 后端: JPQL构造器投影排除LONGTEXT大字段(uploadedImages/videoReferenceImages) - 后端: DTO层过滤非分镜图类型的base64内联resultUrl - 前端: 列表缩略图从video改为img loading=lazy,消除172并发请求 - 前端: download函数增加resultUrl懒加载(详情接口兜底) - 文档: 新增性能优化报告 docs/performance-optimization-report.md
290 lines
12 KiB
Markdown
290 lines
12 KiB
Markdown
# Sora 2 模型集成 — 任务执行清单(后台配置方案)
|
||
|
||
> **核心思路**:视频生成模型由管理员在系统后台统一指定,用户无感知。后端根据当前配置的模型名自动切换 API 请求参数格式。
|
||
|
||
---
|
||
|
||
## 一、数据库层(4 个表 + 1 个 SQL 脚本)
|
||
|
||
### 1.1 SystemSettings 新增 `video_model` 字段
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `AIGC/src/main/java/com/example/demo/model/SystemSettings.java` | 新增 `videoModel` 字段(VARCHAR(50)),默认值 `"grok-video-3"`,加 getter/setter |
|
||
|
||
### 1.2 任务表新增 `video_model` 字段(记录创建时使用的模型)
|
||
|
||
| 表名 | 文件 | 改动 |
|
||
|------|------|------|
|
||
| `text_to_video_tasks` | `AIGC/src/main/java/com/example/demo/model/TextToVideoTask.java` | 新增 `videoModel` 字段(VARCHAR(50)),默认值 `"grok-video-3"`,加 getter/setter |
|
||
| `image_to_video_tasks` | `AIGC/src/main/java/com/example/demo/model/ImageToVideoTask.java` | 同上 |
|
||
| `storyboard_video_tasks` | `AIGC/src/main/java/com/example/demo/model/StoryboardVideoTask.java` | 同上(注意:已有 `imageModel` 字段是分镜图模型,新增的 `videoModel` 是视频生成模型,不冲突) |
|
||
| `user_works` | `AIGC/src/main/java/com/example/demo/model/UserWork.java` | 新增 `videoModel` 字段(VARCHAR(50)),默认值 `"grok-video-3"` |
|
||
|
||
### 1.3 数据库迁移脚本
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `AIGC/init_database.sql` | 末尾追加 5 条 `ALTER TABLE ... ADD COLUMN IF NOT EXISTS video_model VARCHAR(50) DEFAULT 'grok-video-3'`(system_settings + 3 任务表 + user_works) |
|
||
|
||
---
|
||
|
||
## 二、后端 Service 层(5 个文件)
|
||
|
||
### 2.1 SystemSettingsService — 提供模型配置读取
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/SystemSettingsService.java` |
|
||
|------|------|
|
||
| `getOrCreate()` | 初始化默认值时设置 `videoModel = "grok-video-3"` |
|
||
|
||
### 2.2 RealAIService — 根据模型名切换 API 参数格式(核心改动)
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/RealAIService.java` |
|
||
|------|------|
|
||
| **参数构建逻辑** | 从 `SystemSettings` 读取 `videoModel`,根据模型名构建不同的请求体 |
|
||
| `submitTextToVideoTask()` | 移除外部传入的 `videoModel` 参数,改为内部读取系统设置。根据模型名选择参数格式(见下方参数对照表) |
|
||
| `submitImageToVideoTask()` | 同上 |
|
||
| `submitStoryboardVideoTask()` | 同上 |
|
||
| `selectXxxModel()` 系列 | 不再使用,模型由系统设置决定 |
|
||
|
||
#### 参数对照表
|
||
|
||
| 参数 | `grok-video-3` | `sora-2` / `sora-2-pro` |
|
||
|------|----------------|--------------------------|
|
||
| `model` | `"grok-video-3"` | `"sora-2"` 或 `"sora-2-pro"` |
|
||
| 宽高比 | `"ratio"`: `"3:2"` / `"2:3"` / `"1:1"`(`convertAspectRatioToGrokRatio()`) | `"size"`: `"1280x720"` / `"720x1280"` 等(`convertAspectRatioToSize()`) |
|
||
| 时长 | `"duration"`: 整数 5 或 10(`convertDurationToGrokDuration()`) | `"duration"`: 字符串 `"10s"` / `"15s"` / `"25s"` |
|
||
| 分辨率 | `"resolution"`: `"1080P"` / `"720P"` | 通过 `size` 参数中的像素值体现,HD 时选 sora-2-pro |
|
||
| API 端点 | `/v2/videos/generations` | `/v2/videos/generations`(相同) |
|
||
|
||
#### 模型选择逻辑
|
||
|
||
```java
|
||
// 从系统设置读取当前模型
|
||
String videoModel = systemSettingsService.getOrCreate().getVideoModel();
|
||
if (videoModel == null || videoModel.isEmpty()) {
|
||
videoModel = "grok-video-3"; // 默认值
|
||
}
|
||
|
||
// 根据模型构建请求参数
|
||
if (videoModel.startsWith("sora-2")) {
|
||
// Sora 2 参数格式
|
||
requestMap.put("model", hdMode ? "sora-2-pro" : "sora-2");
|
||
requestMap.put("size", convertAspectRatioToSize(aspectRatio, hdMode));
|
||
requestMap.put("duration", duration + "s"); // "10s", "15s"
|
||
} else {
|
||
// Grok 参数格式(默认)
|
||
requestMap.put("model", videoModel);
|
||
requestMap.put("ratio", convertAspectRatioToGrokRatio(aspectRatio));
|
||
requestMap.put("duration", convertDurationToGrokDuration(duration));
|
||
requestMap.put("resolution", hdMode ? "1080P" : "720P");
|
||
}
|
||
```
|
||
|
||
### 2.3 TextToVideoService — 记录模型到任务
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/TextToVideoService.java` |
|
||
|------|------|
|
||
| `createTask()` | 移除 `videoModel` 参数,改为从 `SystemSettings` 读取当前模型,`task.setVideoModel(videoModel)` |
|
||
| `processTaskWithRealAPI()` | 从 task 读取 `videoModel` 传给 RealAIService(保证使用创建时的模型,即使后台后来切换了) |
|
||
| `retryTask()` | 复用原任务的 `videoModel` |
|
||
|
||
### 2.4 ImageToVideoService — 记录模型到任务
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/ImageToVideoService.java` |
|
||
|------|------|
|
||
| `createTask()` | 同上逻辑 |
|
||
| `createTaskByUrl()` | 同上 |
|
||
| `processTaskWithRealAPI()` | 从 task 读取 `videoModel` 传给 RealAIService |
|
||
| `retryTask()` | 复用原任务的 `videoModel` |
|
||
|
||
### 2.5 StoryboardVideoService — 记录模型到任务
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/StoryboardVideoService.java` |
|
||
|------|------|
|
||
| 创建任务方法 | 从 `SystemSettings` 读取当前模型,写入任务 |
|
||
| 视频生成调用处 | 从 task 读取 `videoModel` 传给 RealAIService |
|
||
| `retryTask()` | 复用原任务的 `videoModel` |
|
||
|
||
### 2.6 UserWorkService — 创建作品时写入模型
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/service/UserWorkService.java` |
|
||
|------|------|
|
||
| `createProcessingTextToVideoWork()` | 从 TextToVideoTask 读取 `videoModel`,`work.setVideoModel(task.getVideoModel())` |
|
||
| `createProcessingImageToVideoWork()` | 从 ImageToVideoTask 读取 `videoModel`,同上 |
|
||
| `createProcessingStoryboardVideoWork()` | 从 StoryboardVideoTask 读取 `videoModel`,同上 |
|
||
| `createTextToVideoWork()` | 同上(完成时创建的方法) |
|
||
| `createImageToVideoWork()` | 同上 |
|
||
| `createStoryboardVideoWork()` | 同上 |
|
||
|
||
---
|
||
|
||
## 三、后端 Controller 层(1 个文件)
|
||
|
||
### 3.1 PublicApiController — 公共配置接口暴露当前模型
|
||
|
||
| 文件 | `AIGC/src/main/java/com/example/demo/controller/PublicApiController.java` |
|
||
|------|------|
|
||
| `getPublicConfig()` | 在返回的 config Map 中新增 `videoModel` 字段(从 SystemSettings 读取),前端据此动态渲染比例/时长选项 |
|
||
|
||
---
|
||
|
||
## 四、前端 — 管理后台设置页(1 个文件)
|
||
|
||
### 4.1 SystemSettings.vue — AI 模型选项卡新增视频模型配置
|
||
|
||
| 文件 | `AIGC/frontend/src/views/SystemSettings.vue` |
|
||
|------|------|
|
||
| 改动 | 在已有的"AI模型设置"选项卡中,新增视频模型选择下拉框(el-select),选项:`grok-video-3` / `sora-2`。保存时写入 SystemSettings |
|
||
|
||
---
|
||
|
||
## 五、前端 — 创建页面动态参数(3 个文件)
|
||
|
||
> **核心变化**:前端启动时从 `/api/public/config` 获取当前 `videoModel`,据此动态渲染不同的比例和时长选项。
|
||
|
||
#### 各模型前端参数对照表
|
||
|
||
| 参数 | `grok-video-3`(当前) | `sora-2` / `sora-2-pro` |
|
||
|------|------------------------|---------------------------|
|
||
| 比例选项 | `3:2`(横屏)、`2:3`(竖屏) | `16:9`(横屏)、`9:16`(竖屏) |
|
||
| 时长选项 | `5s`、`10s` | `10s`、`15s`(HD 模式下 sora-2-pro 还支持 `25s`) |
|
||
| 默认比例 | `2:3` | `16:9` |
|
||
| 默认时长 | `5` | `10` |
|
||
|
||
### 5.1 TextToVideoCreate.vue
|
||
|
||
| 文件 | `AIGC/frontend/src/views/TextToVideoCreate.vue` |
|
||
|------|------|
|
||
| 获取模型 | `onMounted` 时调用 `/api/public/config` 获取 `videoModel`,存入响应式变量 |
|
||
| 比例下拉框 | 将 `<option>` 改为动态列表,根据 `videoModel` 渲染不同选项 |
|
||
| 时长下拉框 | 同上,根据 `videoModel` 渲染不同选项 |
|
||
| 默认值 | 模型切换时重置 `aspectRatio` 和 `duration` 为对应默认值 |
|
||
|
||
### 5.2 ImageToVideoCreate.vue
|
||
|
||
| 文件 | `AIGC/frontend/src/views/ImageToVideoCreate.vue` |
|
||
|------|------|
|
||
| 改动 | 同 TextToVideoCreate.vue,动态渲染比例/时长选项 |
|
||
|
||
### 5.3 StoryboardVideoCreate.vue
|
||
|
||
| 文件 | `AIGC/frontend/src/views/StoryboardVideoCreate.vue` |
|
||
|------|------|
|
||
| 改动 | 同上,动态渲染比例/时长选项 |
|
||
|
||
#### 实现方式参考
|
||
|
||
```javascript
|
||
// 模型参数配置表
|
||
const MODEL_CONFIGS = {
|
||
'grok-video-3': {
|
||
ratios: [
|
||
{ value: '3:2', label: '3:2' },
|
||
{ value: '2:3', label: '2:3' }
|
||
],
|
||
durations: [
|
||
{ value: '5', label: '5s' },
|
||
{ value: '10', label: '10s' }
|
||
],
|
||
defaultRatio: '2:3',
|
||
defaultDuration: '5'
|
||
},
|
||
'sora-2': {
|
||
ratios: [
|
||
{ value: '16:9', label: '16:9' },
|
||
{ value: '9:16', label: '9:16' }
|
||
],
|
||
durations: [
|
||
{ value: '10', label: '10s' },
|
||
{ value: '15', label: '15s' }
|
||
],
|
||
defaultRatio: '16:9',
|
||
defaultDuration: '10'
|
||
}
|
||
}
|
||
|
||
// 从公共配置获取当前模型
|
||
const videoModel = ref('grok-video-3')
|
||
const currentConfig = computed(() => MODEL_CONFIGS[videoModel.value] || MODEL_CONFIGS['grok-video-3'])
|
||
|
||
onMounted(async () => {
|
||
const config = await fetch('/api/public/config').then(r => r.json())
|
||
videoModel.value = config.videoModel || 'grok-video-3'
|
||
// 重置为对应模型的默认值
|
||
aspectRatio.value = currentConfig.value.defaultRatio
|
||
duration.value = currentConfig.value.defaultDuration
|
||
})
|
||
```
|
||
|
||
```html
|
||
<!-- 比例选项动态渲染 -->
|
||
<select v-model="aspectRatio" class="setting-select">
|
||
<option v-for="r in currentConfig.ratios" :key="r.value" :value="r.value">{{ r.label }}</option>
|
||
</select>
|
||
|
||
<!-- 时长选项动态渲染 -->
|
||
<select v-model="duration" class="setting-select">
|
||
<option v-for="d in currentConfig.durations" :key="d.value" :value="d.value">{{ d.label }}</option>
|
||
</select>
|
||
```
|
||
|
||
---
|
||
|
||
## 六、前端 — 作品展示(2 个文件)
|
||
|
||
### 6.1 MyWorks.vue — 显示模型标签
|
||
|
||
| 文件 | `AIGC/frontend/src/views/MyWorks.vue` |
|
||
|------|------|
|
||
| 改动 | 在作品卡片上新增一个不可点击的 `el-tag`,根据 `work.videoModel` 显示模型名称。不同模型用不同颜色区分(Sora 2 紫色,Grok 蓝色) |
|
||
|
||
### 6.2 Profile.vue — 个人主页作品集显示模型标签
|
||
|
||
| 文件 | `AIGC/frontend/src/views/Profile.vue` |
|
||
|------|------|
|
||
| `transformWorkData()` | 新增 `videoModel: work.videoModel \|\| 'grok-video-3'` 映射 |
|
||
| 作品卡片模板(video-grid) | 在 `.video-thumbnail` 区域内新增不可点击的 `el-tag` 显示模型名,与 MyWorks 保持一致风格 |
|
||
| 详情弹窗(metadata-section) | 新增一行 metadata-item 显示"视频模型:Sora 2 / Grok Video 3" |
|
||
|
||
---
|
||
|
||
## 七、国际化(2 个文件)
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `AIGC/frontend/src/locales/zh.js` | 新增:`videoModel: '视频生成模型'`、`modelGrok: 'Grok Video 3'`、`modelSora2: 'Sora 2'`、`videoModelTip: '切换后新创建的任务将使用所选模型'` 等 |
|
||
| `AIGC/frontend/src/locales/en.js` | 新增对应英文翻译 |
|
||
|
||
---
|
||
|
||
## 八、积分计算
|
||
|
||
Sora 2 和 Grok 积分价格统一,不需要改动 `calculateCost()` 逻辑。
|
||
|
||
---
|
||
|
||
## 改动汇总
|
||
|
||
| 层级 | 文件数 | 复杂度 |
|
||
|------|--------|--------|
|
||
| 数据库 Model | 5(SystemSettings + 3 任务表 + UserWork) | 低(各加 1 个字段) |
|
||
| 数据库 SQL | 1 | 低(5 条 ALTER) |
|
||
| 后端 Service | 6(SystemSettingsService + RealAIService + 3 视频 Service + UserWorkService) | **中**(RealAIService 需根据模型分支构建请求参数) |
|
||
| 后端 Controller | 1(PublicApiController) | 低(公共配置中暴露 videoModel) |
|
||
| 前端创建页面 | 3(TextToVideoCreate + ImageToVideoCreate + StoryboardVideoCreate) | **中**(动态渲染比例/时长选项) |
|
||
| 前端管理后台 | 1(SystemSettings.vue) | 低(加下拉框) |
|
||
| 前端作品展示 | 2(MyWorks + Profile) | 低(加标签) |
|
||
| 国际化 | 2 | 低(加几个 key) |
|
||
| **合计** | **~21 个文件** | **整体工作量:1-2 天** |
|
||
|
||
---
|
||
|
||
## 不需要改动的部分
|
||
|
||
- ❌ 前端 API 层(textToVideo.js / imageToVideo.js / storyboardVideo.js)— 不传 videoModel,比例/时长参数名不变
|
||
- ❌ 任务 ID 生成逻辑 — 内部 ID 与模型无关
|
||
- ❌ 任务状态轮询 — `getTaskStatus()` 用 realTaskId 查询,与模型无关
|
||
- ❌ COS 存储逻辑 — 存的是视频文件,与模型无关
|
||
- ❌ 任务队列调度 — 队列只管调度,不关心模型
|