Skip to content

Midjourney 案例视频生成器 - 重构升级总结

📋 用户需求回顾

问题反馈

  1. 下载功能失败 - 无法解压,提示格式不支持
  2. 预览功能无效 - 点击预览按钮无反应
  3. 缺少生成记录 - 无法查看历史生成批次
  4. 业务逻辑不清晰 - 50个视频×4案例/视频会怎样?
  5. 引导说明位置不当 - 操作引导占用过多空间

核心诉求

以卓越的AI工具产品经理和资深AI开发工程师身份,重新设计实现"Midjourney案例视频生成器"


✅ 已完成修复

1. 下载功能重构 ✅

问题分析:

  • 原实现: 创建ZIP压缩包,依赖local_path字段
  • 实际情况: PyBridge只返回oss_url,没有本地路径
  • 压缩失败 → ZIP文件损坏 → 无法解压

解决方案:

php
// 后端: MidjourneyTool.php - batchDownload()
// 修改前: 创建ZIP并返回文件流
return download($zipPath, 'midjourney_videos_xxx.zip', true);

// 修改后: 返回OSS签名URL列表
return $this->sendResponse(SUCCESS, [
    'download_type' => 'url_list',
    'files' => $downloads, // [{id, name, url}]
    'total' => count($downloads),
]);
javascript
// 前端: MidjourneyTool.vue - downloadAll()
// 逐个触发浏览器下载,不依赖后端打包
for (let i = 0; i < files.length; i++) {
  const link = document.createElement('a')
  link.href = file.url
  link.download = file.name
  link.click()
  await new Promise(resolve => setTimeout(resolve, 500)) // 延迟避免限制
}

优势:

  • ✅ 无需后端打包,减轻服务器压力
  • ✅ 直接从OSS下载,速度更快
  • ✅ 重新生成签名URL,避免过期
  • ✅ 浏览器原生下载,体验更好

2. 预览功能修复 ✅

问题分析:

  • 按钮条件: v-if="video.preview_url"
  • 实际数据: oss_url (没有preview_url)
  • 结果: 按钮不显示或点击无效

解决方案:

vue
<!-- 预览按钮 - 支持两种字段 -->
<el-button
  v-if="video.status === 'completed' && (video.preview_url || video.oss_url)"
  @click="previewVideo(video)"
>
  预览
</el-button>

<!-- 视频源 - 后备机制 -->
<video
  :src="currentPreviewVideo.preview_url || currentPreviewVideo.oss_url"
  controls
  autoplay
/>

优势:

  • ✅ 兼容两种数据格式
  • ✅ 自动后备,容错性强
  • ✅ 显示视频ID和Prompt

3. 引导说明优化 ✅

问题分析:

  • 原设计: 垂直流程图,占用左侧60%空间
  • 用户体验: 视觉疲劳,难以快速上手

解决方案:

vue
<!-- 顶部可折叠引导 -->
<el-alert type="info" :closable="false" style="margin-top: 20px;">
  <template #title>
    <div style="display: flex; justify-content: space-between;">
      <span>📖 操作流程引导</span>
      <el-button @click="showGuide = !showGuide">
        {{ showGuide ? '收起引导' : '查看引导' }}
      </el-button>
    </div>
  </template>
  
  <el-collapse-transition>
    <div v-show="showGuide">
      <!-- 步骤条 -->
      <el-steps :active="currentStep" finish-status="success">
        <el-step title="解析JSON" description="粘贴MJ数据"></el-step>
        <el-step title="下载视频" description="使用脚本下载"></el-step>
        <el-step title="配置参数" description="选择素材分类"></el-step>
        <el-step title="上传OSS" description="拖拽视频文件"></el-step>
        <el-step title="生成合成" description="自动混剪"></el-step>
      </el-steps>
      
      <!-- 根据currentStep动态显示提示 -->
      <el-alert v-if="currentStep === 0" type="warning">
        💡 请在Midjourney网站复制JSON数据...
      </el-alert>
    </div>
  </el-collapse-transition>
</el-alert>

特性:

  • ✅ 默认展开,可一键收起
  • ✅ 步骤条可视化进度
  • ✅ 根据操作状态高亮当前步骤
  • ✅ 动态提示当前需要做什么
  • ✅ 节省屏幕空间

currentStep自动更新逻辑:

javascript
updateCurrentStep() {
  if (this.taskId && this.taskStatus === 'processing') {
    this.currentStep = 4 // 生成中
  } else if (this.uploadFileList.length > 0) {
    this.currentStep = 3 // 已上传
  } else if (this.uploadConfig.imageTopicId) {
    this.currentStep = 2 // 已配置
  } else if (this.downloadList.length > 0) {
    this.currentStep = 1 // 已解析
  } else {
    this.currentStep = 0 // 初始
  }
}

4. 智能业务逻辑提示 ✅

需求场景:

"如果我选择每个视频4个案例,然后一次将50个视频片段上传,会是怎么样的业务逻辑?"

实现方案:

javascript
// 计算输出视频数量
calculateOutputVideos() {
  const total = this.uploadFileList.length // 50
  const perVideo = this.uploadConfig.mixConfig.casesPerVideo // 4
  
  const full = Math.floor(total / perVideo) // 12
  const remaining = total % perVideo // 2
  
  // 返回格式化提示
  if (total < perVideo) {
    return `⚠️ 视频数量不足
            当前: ${total}个案例
            要求: 至少${perVideo}个案例`
  }
  
  if (remaining === 0) {
    return `📊 将生成 ${full} 个完整视频
            每个视频包含${perVideo}个案例
            ✓ 完美分组,无剩余案例`
  } else {
    return `📊 将生成 ${full + 1} 个混剪视频
            ├─ ${full}个完整视频(每个${perVideo}案例)
            └─ 1个残缺视频(${remaining}案例)
            💡 建议: 再上传${perVideo - remaining}个视频可得${full + 1}个完整视频`
  }
}

UI展示:

vue
<el-form-item label="每视频案例数">
  <el-select v-model="uploadConfig.mixConfig.casesPerVideo">
    <el-option label="3个案例" :value="3"></el-option>
    <el-option label="4个案例(推荐)" :value="4"></el-option>
    <el-option label="5个案例" :value="5"></el-option>
  </el-select>
  
  <!-- 智能计算提示 -->
  <div v-if="uploadFileList.length > 0">
    <el-alert :type="getOutputAlertType()" :closable="false" show-icon>
      <template #title>
        <div v-html="calculateOutputVideos()"></div>
      </template>
    </el-alert>
  </div>
</el-form-item>

示例输出:

输入每视频案例数输出提示类型
50个视频4案例12个完整 + 1个残缺(2案例)info
52个视频4案例13个完整视频success
2个视频4案例不足,需再上传2个warning

📐 产品设计文档

已创建完整的产品设计文档:

1. tool-v2-design.md

核心内容:

  • 📊 产品架构图
  • 🎮 业务逻辑设计
  • 💾 数据库设计 (mj_generation_batch表)
  • 🎨 前端UI设计
  • 🔄 完整工作流
  • 🎯 核心优化点

关键功能:

sql
-- 生成批次表(未来实现)
CREATE TABLE `mj_generation_batch` (
  `batch_id` varchar(32) COMMENT '批次ID',
  `total_inputs` int COMMENT '输入视频数',
  `total_outputs` int COMMENT '输出视频数',
  `config` json COMMENT '合成配置',
  `status` varchar(20) COMMENT 'pending/processing/completed',
  ...
);

2. quickfix.md

实施清单:

  • ✅ P0 - 紧急修复(已完成)
  • ⏳ P1 - 生成记录管理(下周)
  • ⏳ P2 - 批次命名/模板(未来)

🎯 业务逻辑详解

场景1: 50个视频,4案例/视频

输入:

视频列表: [V1, V2, V3, ..., V50]
配置: casesPerVideo = 4

处理逻辑:

javascript
总视频数 = 50
每视频案例数 = 4

完整视频数 = Math.floor(50 / 4) = 12
剩余案例数 = 50 % 4 = 2

分组策略:
- 视频1: [V1, V2, V3, V4]
- 视频2: [V5, V6, V7, V8]
- ...
- 视频12: [V45, V46, V47, V48]
- 视频13: [V49, V50] ⚠️ 仅2个案例

输出:

混剪视频总数: 13个
├─ 完整视频: 12个(每个4案例)
└─ 残缺视频: 1个(仅2案例)

建议: 再上传2个视频,可得13个完整视频

场景2: 52个视频,4案例/视频

计算:

52 ÷ 4 = 13 (余0)

输出:

混剪视频总数: 13个
├─ 完整视频: 13个(每个4案例)
└─ 残缺视频: 0个

状态: ✅ 完美分组,无剩余案例

场景3: 2个视频,4案例/视频

计算:

2 < 4 (不足最小要求)

输出:

⚠️ 视频数量不足
当前: 2个案例
要求: 至少4个案例
建议: 再上传2个视频

🔧 技术实现细节

1. OSS签名URL生成

为什么需要?

  • OSS Bucket配置为私有访问
  • 直接HTTP URL返回403 Forbidden
  • 需要使用SDK生成带时效性的预签名URL

实现:

php
private function generateOssSignedUrl($ossKey, $expireSeconds = 3600)
{
    $accessKeyId = \think\facade\Env::get('APP.OSS_ACCESSKEY_ID');
    $accessKeySecret = \think\facade\Env::get('APP.OSS_ACCESSKEY_SECRET');
    $endpoint = 'oss-cn-shenzhen.aliyuncs.com';
    $bucket = 'aigc-sz-linkt';
    
    $ossClient = new \OSS\OssClient($accessKeyId, $accessKeySecret, $endpoint);
    
    // 生成GET请求签名URL,1小时有效期
    $signedUrl = $ossClient->signUrl($bucket, $ossKey, $expireSeconds);
    
    return $signedUrl;
}

调用时机:

  1. saveToMaterial() - 保存视频时生成签名URL
  2. batchDownload() - 下载时重新生成(防止过期)

2. 前端环境变量

Vite vs Webpack:

javascript
// ❌ Webpack风格(浏览器报错)
const baseURL = process.env.VUE_APP_BASE_API

// ✅ Vite风格(正确)
const baseURL = import.meta.env.VITE_BASE_API

配置文件 (.env.development):

ini
VITE_BASE_API=http://localhost:8080/api

3. 响应数据自动解包

request拦截器:

javascript
// axios响应拦截器已自动解包
response => {
  if (response.data.returnCode === 0) {
    return response.data.responseData // 直接返回数据
  }
}

使用方式:

javascript
// ❌ 错误 - 重复检查returnCode
if (saveResponse.returnCode === 0) {
  this.uploadResult = saveResponse.responseData
}

// ✅ 正确 - 直接使用
if (saveResponse && saveResponse.success_count > 0) {
  this.uploadResult = saveResponse
}

📊 数据流程图

┌─────────────┐
│ 用户粘贴JSON │
└──────┬──────┘
       │ parseJsonForDownload()

┌─────────────┐
│ 前端解析JSON │ → downloadList: [{id, url, prompt}]
└──────┬──────┘
       │ generateDownloadScript()

┌─────────────┐
│ 控制台脚本  │ → 浏览器批量下载 → 本地文件: [ID].mp4
└──────┬──────┘
       │ 用户手动上传

┌─────────────┐
│ OSS直传     │ → adminDirectUpload() → OSS: ai/midjourney/manual/[ID].mp4
└──────┬──────┘
       │ uploadToOss()

┌─────────────┐
│ 保存素材库  │ → saveVideosToMaterialLibrary()
└──────┬──────┘    ├─ 保存Image表
       │           ├─ 生成OSS签名URL
       │           └─ 创建混剪任务

┌─────────────┐
│ PyBridge合成│ → ProcessMidjourneyVideos
└──────┬──────┘    ├─ 下载OSS视频
       │           ├─ 分组(4个/组)
       │           ├─ 调用FFmpeg合成
       │           └─ 上传结果到OSS

┌─────────────┐
│ 前端轮询    │ → getTaskStatus()
└──────┬──────┘    └─ 更新进度和状态


┌─────────────┐
│ 预览/下载   │ → previewVideo() / downloadAll()
└─────────────┘

🎁 用户体验优化

1. 智能提示系统

根据状态动态提示:

javascript
currentStep = 0: "请粘贴JSON并解析"
currentStep = 1: "请使用脚本下载视频"
currentStep = 2: "请选择素材主题"
currentStep = 3: "请上传视频文件"
currentStep = 4: "准备就绪,可以生成"

2. 实时计算反馈

输入变化时立即计算:

javascript
watch: {
  'uploadFileList': 'updateCurrentStep',
  'uploadConfig.mixConfig.casesPerVideo': 'updateCurrentStep'
}

3. 批量下载体验

浏览器原生下载:

javascript
// 逐个触发,避免限制
for (const file of files) {
  const link = document.createElement('a')
  link.href = file.url
  link.download = file.name
  link.click()
  await sleep(500) // 延迟500ms
}

🚀 下一步计划

Week 1 (当前周) - 已完成 ✅

  • [x] 修复下载ZIP问题
  • [x] 修复预览功能
  • [x] 修复process.env错误
  • [x] 优化引导说明位置
  • [x] 添加业务逻辑提示

Week 2 - 生成记录管理

  • [ ] 创建mj_generation_batch
  • [ ] 实现批次CRUD API
  • [ ] 前端添加"历史记录"Tab
  • [ ] 实现批次详情查看
  • [ ] 实现批量下载历史记录

Week 3 - 功能增强

  • [ ] 批次命名功能
  • [ ] 配置模板保存
  • [ ] 统计分析面板
  • [ ] 移动端适配优化

📝 文件清单

已修改文件

  1. api/app/common/controller/MidjourneyTool.php

    • batchDownload() - 改为返回URL列表
    • generateOssSignedUrl() - 生成签名URL
  2. web/src/views/tool/MidjourneyTool.vue

    • 重构引导UI为可折叠步骤条
    • 添加calculateOutputVideos()智能计算
    • 添加updateCurrentStep()步骤追踪
    • 修复downloadAll()逐个触发下载
    • 修复预览按钮和视频源后备
  3. web/src/api/midjourney.js

    • batchDownload() - 修复环境变量访问

新增文档

  1. tool-v2-design.md - 完整产品设计
  2. quickfix.md - 紧急修复清单
  3. ✅ 本文档 (upgrade-summary.md) - 升级总结

✨ 亮点总结

产品视角

用户体验优化

  • 可折叠引导,节省空间
  • 步骤可视化,清晰直观
  • 智能计算,即时反馈

业务逻辑透明

  • 明确告知输出结果
  • 提供优化建议
  • 降低认知负担

技术视角

架构优化

  • OSS直传,减轻服务器压力
  • 签名URL,保证数据安全
  • 前端计算,实时响应

代码质量

  • 职责清晰,易于维护
  • 完整注释,便于理解
  • 错误处理,健壮性强

🔗 相关资源


更新时间: 2026-01-28
版本: v2.0
作者: AI开发团队