308 lines
8.5 KiB
Markdown
308 lines
8.5 KiB
Markdown
|
|
# 任务完成后作品保存流程说明
|
|||
|
|
|
|||
|
|
## 🎯 **功能概述**
|
|||
|
|
|
|||
|
|
当任务执行成功后,系统会自动将结果保存到用户的"我的作品"中,并将相关信息添加到数据库中。用户可以通过API接口查看、管理自己的作品。
|
|||
|
|
|
|||
|
|
## 📋 **完整流程**
|
|||
|
|
|
|||
|
|
### 1. **任务执行成功触发**
|
|||
|
|
```
|
|||
|
|
外部API返回结果 → TaskQueueService.updateTaskAsCompleted()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. **任务队列状态更新**
|
|||
|
|
```java
|
|||
|
|
// TaskQueueService.updateTaskAsCompleted()
|
|||
|
|
taskQueue.updateStatus(TaskQueue.QueueStatus.COMPLETED);
|
|||
|
|
taskQueueRepository.save(taskQueue);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. **积分处理**
|
|||
|
|
```java
|
|||
|
|
// 扣除冻结的积分
|
|||
|
|
userService.deductFrozenPoints(taskQueue.getTaskId());
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. **作品创建**
|
|||
|
|
```java
|
|||
|
|
// 创建用户作品
|
|||
|
|
UserWork work = userWorkService.createWorkFromTask(taskQueue.getTaskId(), resultUrl);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. **作品数据提取**
|
|||
|
|
```java
|
|||
|
|
// UserWorkService.createWorkFromTask()
|
|||
|
|
// 检查是否已存在作品(防重复)
|
|||
|
|
Optional<UserWork> existingWork = userWorkRepository.findByTaskId(taskId);
|
|||
|
|
if (existingWork.isPresent()) {
|
|||
|
|
return existingWork.get(); // 返回已存在的作品
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从原始任务中提取数据
|
|||
|
|
TextToVideoTask task = textToVideoTaskRepository.findByTaskId(taskId);
|
|||
|
|
// 或
|
|||
|
|
ImageToVideoTask task = imageToVideoTaskRepository.findByTaskId(taskId);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6. **作品信息设置**
|
|||
|
|
```java
|
|||
|
|
// UserWorkService.createTextToVideoWork() 或 createImageToVideoWork()
|
|||
|
|
UserWork work = new UserWork();
|
|||
|
|
work.setUsername(task.getUsername()); // 用户名
|
|||
|
|
work.setTaskId(task.getTaskId()); // 任务ID
|
|||
|
|
work.setWorkType(UserWork.WorkType.TEXT_TO_VIDEO); // 作品类型
|
|||
|
|
work.setTitle(generateTitle(task.getPrompt())); // 自动生成标题
|
|||
|
|
work.setDescription("文生视频作品"); // 作品描述
|
|||
|
|
work.setPrompt(task.getPrompt()); // 原始提示词
|
|||
|
|
work.setResultUrl(resultUrl); // 结果视频URL
|
|||
|
|
work.setDuration(String.valueOf(task.getDuration()) + "s"); // 视频时长
|
|||
|
|
work.setAspectRatio(task.getAspectRatio()); // 宽高比
|
|||
|
|
work.setQuality(task.isHdMode() ? "HD" : "SD"); // 画质
|
|||
|
|
work.setPointsCost(task.getCostPoints()); // 消耗积分
|
|||
|
|
work.setStatus(UserWork.WorkStatus.COMPLETED); // 作品状态
|
|||
|
|
work.setCompletedAt(LocalDateTime.now()); // 完成时间
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7. **数据库保存**
|
|||
|
|
```java
|
|||
|
|
// 保存到数据库
|
|||
|
|
work = userWorkRepository.save(work);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8. **原始任务状态更新**
|
|||
|
|
```java
|
|||
|
|
// 更新原始任务状态
|
|||
|
|
updateOriginalTaskStatus(taskQueue, "COMPLETED", resultUrl, null);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🗄️ **数据库表结构**
|
|||
|
|
|
|||
|
|
### user_works 表
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE user_works (
|
|||
|
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|||
|
|
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
|||
|
|
task_id VARCHAR(50) NOT NULL UNIQUE COMMENT '任务ID',
|
|||
|
|
work_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO') NOT NULL COMMENT '作品类型',
|
|||
|
|
title VARCHAR(200) COMMENT '作品标题',
|
|||
|
|
description TEXT COMMENT '作品描述',
|
|||
|
|
prompt TEXT COMMENT '生成提示词',
|
|||
|
|
result_url VARCHAR(500) COMMENT '结果视频URL',
|
|||
|
|
thumbnail_url VARCHAR(500) COMMENT '缩略图URL',
|
|||
|
|
duration VARCHAR(10) COMMENT '视频时长',
|
|||
|
|
aspect_ratio VARCHAR(10) COMMENT '宽高比',
|
|||
|
|
quality VARCHAR(20) COMMENT '画质',
|
|||
|
|
file_size VARCHAR(20) COMMENT '文件大小',
|
|||
|
|
points_cost INT NOT NULL DEFAULT 0 COMMENT '消耗积分',
|
|||
|
|
status ENUM('PROCESSING', 'COMPLETED', 'FAILED', 'DELETED') NOT NULL DEFAULT 'PROCESSING' COMMENT '作品状态',
|
|||
|
|
is_public BOOLEAN NOT NULL DEFAULT FALSE COMMENT '是否公开',
|
|||
|
|
view_count INT NOT NULL DEFAULT 0 COMMENT '浏览次数',
|
|||
|
|
like_count INT NOT NULL DEFAULT 0 COMMENT '点赞次数',
|
|||
|
|
download_count INT NOT NULL DEFAULT 0 COMMENT '下载次数',
|
|||
|
|
tags VARCHAR(500) COMMENT '标签',
|
|||
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
|||
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
|||
|
|
completed_at DATETIME COMMENT '完成时间'
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔌 **API接口**
|
|||
|
|
|
|||
|
|
### 获取我的作品列表
|
|||
|
|
```
|
|||
|
|
GET /api/works/my-works?page=0&size=10
|
|||
|
|
Authorization: Bearer <token>
|
|||
|
|
|
|||
|
|
Response:
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"username": "user1",
|
|||
|
|
"taskId": "txt2vid_123",
|
|||
|
|
"workType": "TEXT_TO_VIDEO",
|
|||
|
|
"title": "一只可爱的小猫...",
|
|||
|
|
"description": "文生视频作品",
|
|||
|
|
"prompt": "一只可爱的小猫在花园里玩耍",
|
|||
|
|
"resultUrl": "https://example.com/video.mp4",
|
|||
|
|
"duration": "10s",
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"quality": "HD",
|
|||
|
|
"pointsCost": 80,
|
|||
|
|
"status": "COMPLETED",
|
|||
|
|
"isPublic": false,
|
|||
|
|
"viewCount": 0,
|
|||
|
|
"likeCount": 0,
|
|||
|
|
"downloadCount": 0,
|
|||
|
|
"createdAt": "2024-01-01T10:00:00",
|
|||
|
|
"completedAt": "2024-01-01T10:05:00"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"totalElements": 1,
|
|||
|
|
"totalPages": 1,
|
|||
|
|
"currentPage": 0,
|
|||
|
|
"size": 10,
|
|||
|
|
"stats": {
|
|||
|
|
"completedCount": 1,
|
|||
|
|
"processingCount": 0,
|
|||
|
|
"failedCount": 0,
|
|||
|
|
"totalPointsCost": 80,
|
|||
|
|
"totalCount": 1,
|
|||
|
|
"publicCount": 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取作品详情
|
|||
|
|
```
|
|||
|
|
GET /api/works/{workId}
|
|||
|
|
Authorization: Bearer <token>
|
|||
|
|
|
|||
|
|
Response:
|
|||
|
|
{
|
|||
|
|
"success": true,
|
|||
|
|
"data": {
|
|||
|
|
"id": 1,
|
|||
|
|
"username": "user1",
|
|||
|
|
"taskId": "txt2vid_123",
|
|||
|
|
"workType": "TEXT_TO_VIDEO",
|
|||
|
|
"title": "一只可爱的小猫...",
|
|||
|
|
"description": "文生视频作品",
|
|||
|
|
"prompt": "一只可爱的小猫在花园里玩耍",
|
|||
|
|
"resultUrl": "https://example.com/video.mp4",
|
|||
|
|
"duration": "10s",
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"quality": "HD",
|
|||
|
|
"pointsCost": 80,
|
|||
|
|
"status": "COMPLETED",
|
|||
|
|
"isPublic": false,
|
|||
|
|
"viewCount": 1,
|
|||
|
|
"likeCount": 0,
|
|||
|
|
"downloadCount": 0,
|
|||
|
|
"createdAt": "2024-01-01T10:00:00",
|
|||
|
|
"completedAt": "2024-01-01T10:05:00"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🛡️ **安全特性**
|
|||
|
|
|
|||
|
|
### 1. **防重复创建**
|
|||
|
|
- 检查是否已存在相同任务ID的作品
|
|||
|
|
- 如果存在则返回已存在的作品,不创建新作品
|
|||
|
|
|
|||
|
|
### 2. **权限控制**
|
|||
|
|
- 只有作品所有者可以查看、编辑、删除作品
|
|||
|
|
- 通过JWT Token验证用户身份
|
|||
|
|
|
|||
|
|
### 3. **数据完整性**
|
|||
|
|
- 事务保证数据一致性
|
|||
|
|
- 作品创建失败不影响任务完成状态
|
|||
|
|
|
|||
|
|
### 4. **异常处理**
|
|||
|
|
- 完善的错误处理机制
|
|||
|
|
- 详细的日志记录
|
|||
|
|
|
|||
|
|
## 📊 **作品管理功能**
|
|||
|
|
|
|||
|
|
### 1. **作品查看**
|
|||
|
|
- 分页获取用户作品列表
|
|||
|
|
- 获取作品详细信息
|
|||
|
|
- 作品统计信息
|
|||
|
|
|
|||
|
|
### 2. **作品编辑**
|
|||
|
|
- 修改作品标题
|
|||
|
|
- 修改作品描述
|
|||
|
|
- 设置作品标签
|
|||
|
|
- 设置作品公开状态
|
|||
|
|
|
|||
|
|
### 3. **作品互动**
|
|||
|
|
- 作品点赞
|
|||
|
|
- 作品下载记录
|
|||
|
|
- 浏览次数统计
|
|||
|
|
|
|||
|
|
### 4. **作品删除**
|
|||
|
|
- 软删除作品
|
|||
|
|
- 保留数据完整性
|
|||
|
|
|
|||
|
|
## 🔄 **定时任务**
|
|||
|
|
|
|||
|
|
### 清理过期失败作品
|
|||
|
|
```java
|
|||
|
|
@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
|
|||
|
|
public void cleanupExpiredFailedWorks() {
|
|||
|
|
// 清理超过30天的失败作品
|
|||
|
|
int cleanedCount = userWorkService.cleanupExpiredFailedWorks();
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🧪 **测试验证**
|
|||
|
|
|
|||
|
|
系统包含完整的集成测试,验证:
|
|||
|
|
- 文生视频任务完成后作品创建
|
|||
|
|
- 图生视频任务完成后作品创建
|
|||
|
|
- 重复作品创建处理
|
|||
|
|
- 作品标题生成
|
|||
|
|
- 用户作品列表获取
|
|||
|
|
- 作品统计信息
|
|||
|
|
|
|||
|
|
## 📝 **使用示例**
|
|||
|
|
|
|||
|
|
### 前端调用示例
|
|||
|
|
```javascript
|
|||
|
|
// 获取我的作品列表
|
|||
|
|
const getMyWorks = async (page = 0, size = 10) => {
|
|||
|
|
const response = await fetch(`/api/works/my-works?page=${page}&size=${size}`, {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${token}`
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const data = await response.json();
|
|||
|
|
return data;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取作品详情
|
|||
|
|
const getWorkDetail = async (workId) => {
|
|||
|
|
const response = await fetch(`/api/works/${workId}`, {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${token}`
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const data = await response.json();
|
|||
|
|
return data;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 更新作品信息
|
|||
|
|
const updateWork = async (workId, updateData) => {
|
|||
|
|
const response = await fetch(`/api/works/${workId}`, {
|
|||
|
|
method: 'PUT',
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${token}`,
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(updateData)
|
|||
|
|
});
|
|||
|
|
const data = await response.json();
|
|||
|
|
return data;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ✅ **系统状态**
|
|||
|
|
|
|||
|
|
当前系统已经完全实现了任务完成后作品保存功能:
|
|||
|
|
|
|||
|
|
1. **✅ 任务完成触发作品创建**
|
|||
|
|
2. **✅ 作品信息自动提取和设置**
|
|||
|
|
3. **✅ 作品数据保存到数据库**
|
|||
|
|
4. **✅ 我的作品API接口完整**
|
|||
|
|
5. **✅ 权限控制和安全验证**
|
|||
|
|
6. **✅ 防重复创建机制**
|
|||
|
|
7. **✅ 异常处理和日志记录**
|
|||
|
|
8. **✅ 完整的测试覆盖**
|
|||
|
|
|
|||
|
|
用户现在可以在任务完成后,通过"我的作品"功能查看和管理自己生成的所有视频作品。
|
|||
|
|
|
|||
|
|
|