perf(backend+frontend): 列表API响应体积优化 3.1MB→145KB (↓95.4%)

- 后端: JPQL构造器投影排除LONGTEXT大字段(uploadedImages/videoReferenceImages)
- 后端: DTO层过滤非分镜图类型的base64内联resultUrl
- 前端: 列表缩略图从video改为img loading=lazy,消除172并发请求
- 前端: download函数增加resultUrl懒加载(详情接口兜底)
- 文档: 新增性能优化报告 docs/performance-optimization-report.md
This commit is contained in:
blandarebiter
2026-04-10 18:46:37 +08:00
commit 90b5118e45
280 changed files with 92468 additions and 0 deletions

289
SORA2_INTEGRATION_TASKS.md Normal file
View File

@@ -0,0 +1,289 @@
# 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 | 5SystemSettings + 3 任务表 + UserWork | 低(各加 1 个字段) |
| 数据库 SQL | 1 | 低5 条 ALTER |
| 后端 Service | 6SystemSettingsService + RealAIService + 3 视频 Service + UserWorkService | **中**RealAIService 需根据模型分支构建请求参数) |
| 后端 Controller | 1PublicApiController | 低(公共配置中暴露 videoModel |
| 前端创建页面 | 3TextToVideoCreate + ImageToVideoCreate + StoryboardVideoCreate | **中**(动态渲染比例/时长选项) |
| 前端管理后台 | 1SystemSettings.vue | 低(加下拉框) |
| 前端作品展示 | 2MyWorks + Profile | 低(加标签) |
| 国际化 | 2 | 低(加几个 key |
| **合计** | **~21 个文件** | **整体工作量1-2 天** |
---
## 不需要改动的部分
- ❌ 前端 API 层textToVideo.js / imageToVideo.js / storyboardVideo.js— 不传 videoModel比例/时长参数名不变
- ❌ 任务 ID 生成逻辑 — 内部 ID 与模型无关
- ❌ 任务状态轮询 — `getTaskStatus()` 用 realTaskId 查询,与模型无关
- ❌ COS 存储逻辑 — 存的是视频文件,与模型无关
- ❌ 任务队列调度 — 队列只管调度,不关心模型