主要更新: - 调整并发配置为50人(数据库连接池30,Tomcat线程150,异步线程池5/20) - 实现无界阻塞队列(LinkedBlockingQueue)任务处理 - 实现分镜视频保存功能(保存到uploads目录) - 统一管理页面导航栏和右上角样式 - 添加日活用户统计功能 - 优化视频拼接和保存逻辑 - 添加部署文档和快速部署指南 - 更新.gitignore排除敏感配置文件
190 lines
5.0 KiB
JavaScript
190 lines
5.0 KiB
JavaScript
import request from './request'
|
||
|
||
/**
|
||
* 图生视频API服务
|
||
*/
|
||
export const imageToVideoApi = {
|
||
/**
|
||
* 创建图生视频任务
|
||
* @param {Object} params - 任务参数
|
||
* @param {File} params.firstFrame - 首帧图片
|
||
* @param {File} params.lastFrame - 尾帧图片(可选)
|
||
* @param {string} params.prompt - 描述文字
|
||
* @param {string} params.aspectRatio - 视频比例
|
||
* @param {number} params.duration - 视频时长
|
||
* @param {boolean} params.hdMode - 是否高清模式
|
||
* @returns {Promise} API响应
|
||
*/
|
||
createTask(params) {
|
||
// 参数验证
|
||
if (!params) {
|
||
throw new Error('参数不能为空')
|
||
}
|
||
if (!params.firstFrame) {
|
||
throw new Error('首帧图片不能为空')
|
||
}
|
||
if (!params.prompt || params.prompt.trim() === '') {
|
||
throw new Error('描述文字不能为空')
|
||
}
|
||
if (!params.aspectRatio) {
|
||
throw new Error('视频比例不能为空')
|
||
}
|
||
if (!params.duration || params.duration < 1 || params.duration > 60) {
|
||
throw new Error('视频时长必须在1-60秒之间')
|
||
}
|
||
|
||
const formData = new FormData()
|
||
|
||
// 添加必填参数
|
||
formData.append('firstFrame', params.firstFrame)
|
||
formData.append('prompt', params.prompt.trim())
|
||
formData.append('aspectRatio', params.aspectRatio)
|
||
formData.append('duration', params.duration.toString())
|
||
formData.append('hdMode', params.hdMode.toString())
|
||
|
||
// 添加可选参数
|
||
if (params.lastFrame) {
|
||
formData.append('lastFrame', params.lastFrame)
|
||
}
|
||
|
||
return request({
|
||
url: '/image-to-video/create',
|
||
method: 'POST',
|
||
data: formData,
|
||
headers: {
|
||
'Content-Type': 'multipart/form-data'
|
||
}
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 获取用户任务列表
|
||
* @param {number} page - 页码
|
||
* @param {number} size - 每页数量
|
||
* @returns {Promise} API响应
|
||
*/
|
||
getTasks(page = 0, size = 10) {
|
||
return request({
|
||
url: '/image-to-video/tasks',
|
||
method: 'GET',
|
||
params: { page, size }
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 获取任务详情
|
||
* @param {string} taskId - 任务ID
|
||
* @returns {Promise} API响应
|
||
*/
|
||
getTaskDetail(taskId) {
|
||
return request({
|
||
url: `/image-to-video/tasks/${taskId}`,
|
||
method: 'GET'
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 获取任务状态
|
||
* @param {string} taskId - 任务ID
|
||
* @returns {Promise} API响应
|
||
*/
|
||
getTaskStatus(taskId) {
|
||
return request({
|
||
url: `/image-to-video/tasks/${taskId}/status`,
|
||
method: 'GET'
|
||
})
|
||
},
|
||
|
||
|
||
/**
|
||
* 轮询任务状态
|
||
* @param {string} taskId - 任务ID
|
||
* @param {Function} onProgress - 进度回调
|
||
* @param {Function} onComplete - 完成回调
|
||
* @param {Function} onError - 错误回调
|
||
* @returns {Function} 停止轮询的函数
|
||
*/
|
||
pollTaskStatus(taskId, onProgress, onComplete, onError) {
|
||
let isPolling = true
|
||
let pollCount = 0
|
||
const maxPolls = 30 // 最大轮询次数(1小时,每2分钟一次)
|
||
|
||
const poll = async () => {
|
||
if (!isPolling || pollCount >= maxPolls) {
|
||
if (pollCount >= maxPolls) {
|
||
onError && onError(new Error('任务超时'))
|
||
}
|
||
return
|
||
}
|
||
|
||
try {
|
||
const response = await request({
|
||
url: `/image-to-video/tasks/${taskId}/status`,
|
||
method: 'GET'
|
||
})
|
||
|
||
// 检查响应是否有效
|
||
if (!response || !response.data || !response.data.success) {
|
||
onError && onError(new Error('获取任务状态失败'))
|
||
isPolling = false
|
||
return
|
||
}
|
||
|
||
const taskData = response.data.data
|
||
|
||
// 检查taskData是否有效
|
||
if (!taskData || !taskData.status) {
|
||
onError && onError(new Error('无效的任务数据'))
|
||
isPolling = false
|
||
return
|
||
}
|
||
|
||
if (taskData.status === 'COMPLETED') {
|
||
onComplete && onComplete(taskData)
|
||
isPolling = false
|
||
return
|
||
}
|
||
|
||
if (taskData.status === 'FAILED' || taskData.status === 'CANCELLED') {
|
||
console.error('任务失败:', {
|
||
taskId: taskId,
|
||
status: taskData.status,
|
||
errorMessage: taskData.errorMessage,
|
||
pollCount: pollCount
|
||
})
|
||
onError && onError(new Error(taskData.errorMessage || '任务失败'))
|
||
isPolling = false
|
||
return
|
||
}
|
||
|
||
// 调用进度回调
|
||
onProgress && onProgress({
|
||
status: taskData.status,
|
||
progress: taskData.progress || 0,
|
||
resultUrl: taskData.resultUrl
|
||
})
|
||
|
||
pollCount++
|
||
|
||
// 继续轮询
|
||
setTimeout(poll, 120000) // 每2分钟轮询一次
|
||
|
||
} catch (error) {
|
||
console.error('轮询任务状态失败:', error)
|
||
onError && onError(error)
|
||
isPolling = false
|
||
}
|
||
}
|
||
|
||
// 开始轮询
|
||
poll()
|
||
|
||
// 返回停止轮询的函数
|
||
return () => {
|
||
isPolling = false
|
||
}
|
||
}
|
||
}
|
||
|
||
export default imageToVideoApi
|