460 lines
15 KiB
Markdown
460 lines
15 KiB
Markdown
---
|
|
name: 打卡视频生成与展示
|
|
overview: 实现打卡勾选生成视频后,在饮食记录页面定时查询视频任务状态并显示,在打卡详情支持视频播放,勾选生成视频的打卡帖子在社区最新tab显示
|
|
todos:
|
|
- id: backend-checkin-article
|
|
content: 修改打卡提交逻辑,勾选视频时创建eb_article记录存储taskId
|
|
status: completed
|
|
- id: backend-list-api
|
|
content: 更新打卡列表/详情接口,关联article表返回videoUrl和videoStatus
|
|
status: completed
|
|
- id: backend-callback
|
|
content: 完善KieAI回调处理,更新article的video_url和status_task
|
|
status: completed
|
|
- id: backend-community
|
|
content: 更新社区列表接口,关联打卡记录的视频信息
|
|
status: completed
|
|
- id: frontend-dietary-records
|
|
content: 饮食记录页面增加视频状态显示和定时轮询逻辑
|
|
status: completed
|
|
- id: frontend-detail-video
|
|
content: 打卡详情页增加视频播放器组件
|
|
status: completed
|
|
- id: frontend-community-badge
|
|
content: 社区页面卡片增加视频标记
|
|
status: completed
|
|
- id: frontend-api
|
|
content: 新增getVideoTaskStatus API方法
|
|
status: completed
|
|
isProject: false
|
|
---
|
|
|
|
# 打卡视频生成与展示功能实现计划
|
|
|
|
## 背景分析
|
|
|
|
根据代码探索,当前系统已具备:
|
|
|
|
- **打卡发布**: `[checkin-publish.vue](msh_single_uniapp/pages/tool/checkin-publish.vue)` 支持勾选"生成打卡视频分享到社区",会调用KieAI创建视频任务并返回 `taskId`
|
|
- **数据存储**: `eb_user_sign` 表存储 `task_id` 和 `enable_ai_video` 字段,`eb_article` 表存储视频URL和任务状态
|
|
- **视频生成**: 通过 `[ToolSora2Service](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolSora2ServiceImpl.java)` 调用KieAI API
|
|
- **状态查询接口**: `GET /api/front/kieai/video/task/{taskId}` 已存在
|
|
|
|
**缺失功能**:
|
|
|
|
1. 打卡时未创建 `eb_article` 记录来存储视频
|
|
2. 饮食记录页面不显示视频任务状态
|
|
3. 打卡详情页不支持视频播放
|
|
4. 社区帖子未关联视频信息
|
|
|
|
## 实现方案
|
|
|
|
### 数据库层面
|
|
|
|
#### 1. 修改表结构
|
|
|
|
**eb_user_sign表**: 已有 `task_id`, `enable_ai_video` 字段,无需修改
|
|
|
|
**v2_community_posts表**: 已有 `video_url` 字段(从SQL看到),需确认Java实体类是否包含
|
|
|
|
**eb_article表**: 已有 `video_url`, `task_id`, `status_task`, `check_in_record_id` 字段
|
|
|
|
#### 2. 数据流设计
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[用户勾选生成视频并发布打卡] --> B[前端:调用KieAI创建视频任务]
|
|
B --> C[前端:获得taskId]
|
|
C --> D[前端:提交打卡 带taskId+enableAIVideo]
|
|
D --> E[后端:保存eb_user_sign记录]
|
|
E --> F[后端:创建eb_article记录]
|
|
F --> G[eb_article: taskId, statusTask=0, checkInRecordId]
|
|
|
|
H[饮食记录页面] --> I[加载打卡列表API]
|
|
I --> J[返回 taskId, enableAIVideo, videoUrl]
|
|
J --> K[前端:检测有taskId且无videoUrl]
|
|
K --> L[启动定时轮询 /kieai/video/task/taskId]
|
|
L --> M{任务状态?}
|
|
M -->|进行中| N[显示生成中状态]
|
|
M -->|完成| O[更新videoUrl到article]
|
|
M -->|失败| P[显示失败状态]
|
|
O --> Q[页面刷新后显示视频]
|
|
|
|
R[打卡详情页] --> S[查询详情API]
|
|
S --> T[返回taskId+videoUrl]
|
|
T --> U{有videoUrl?}
|
|
U -->|是| V[显示video标签播放]
|
|
U -->|否+有taskId| W[显示生成中状态]
|
|
|
|
X[社区最新tab] --> Y[查询v2_community_posts]
|
|
Y --> Z[关联eb_user_sign.enableAIVideo]
|
|
Z --> AA[显示有视频标记的帖子]
|
|
```
|
|
|
|
|
|
|
|
### 后端改动
|
|
|
|
#### 1. 更新打卡提交逻辑
|
|
|
|
`[ToolCheckinServiceImpl.submit()](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCheckinServiceImpl.java)`
|
|
|
|
- 在打卡成功后,如果 `enableAIVideo=1` 且有 `taskId`,创建 `eb_article` 记录:
|
|
- `type = 2` (视频类型)
|
|
- `task_id = taskId`
|
|
- `status_task = 0` (已创建)
|
|
- `check_in_record_id = userSign.id`
|
|
- `title` = 打卡备注或默认标题
|
|
- 其他字段按现有逻辑填充
|
|
|
|
#### 2. 更新打卡列表接口
|
|
|
|
`[ToolCheckinServiceImpl.getList()](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCheckinServiceImpl.java)`
|
|
|
|
- 查询时关联 `eb_article` 表(通过 `eb_user_sign.task_id = eb_article.task_id`)
|
|
- 返回字段增加:
|
|
- `videoUrl`: 从 `eb_article.video_url` 获取
|
|
- `videoStatus`: 从 `eb_article.status_task` 获取 (0=生成中, 1=完成, 2=失败)
|
|
- `taskId`: 已有
|
|
- `enableAIVideo`: 已有
|
|
|
|
#### 3. 更新打卡详情接口
|
|
|
|
`[ToolCheckinServiceImpl.getDetail()](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCheckinServiceImpl.java)`
|
|
|
|
- 增加返回 `videoUrl` 和 `videoStatus` 字段
|
|
|
|
#### 4. 完善KieAI回调处理
|
|
|
|
`[KieAIController.handleCallback()](msh_crmeb_22/crmeb-front/src/main/java/com/zbkj/front/controller/KieAIController.java)`
|
|
|
|
- 当前只记录日志,需改为:
|
|
- 解析回调参数获取 `taskId`, `state`, `result_urls`
|
|
- 根据 `taskId` 更新 `eb_article` 表:
|
|
- `status_task = 1` (成功) 或 `2` (失败)
|
|
- `video_url = result_urls[0]`
|
|
|
|
参考 `models-integration` 项目的实现:
|
|
|
|
```java
|
|
articleMapper.updateVideoUrlAndTaskStatusByTaskId(taskId, videoUrl, statusTask);
|
|
```
|
|
|
|
#### 5. 更新社区列表接口
|
|
|
|
`[ToolCommunityServiceImpl.getList()](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java)`
|
|
|
|
- 当前已关联 `eb_user_sign` 获取 `mealType`
|
|
- 同样方式关联获取 `enable_ai_video` 和 `task_id`
|
|
- 通过 `task_id` 关联 `eb_article` 获取 `video_url`
|
|
- 返回字段增加:
|
|
- `hasVideo`: boolean (是否有视频)
|
|
- `videoUrl`: 视频地址
|
|
- `enableAIVideo`: 是否启用了视频生成
|
|
|
|
#### 6. 更新V2CommunityPost实体类
|
|
|
|
`[V2CommunityPost.java](msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2CommunityPost.java)`
|
|
|
|
- 确认是否已有 `videoUrl` 字段,若无则添加:
|
|
|
|
```java
|
|
@ApiModelProperty(value = "视频地址")
|
|
private String videoUrl;
|
|
```
|
|
|
|
### 前端改动
|
|
|
|
#### 1. 饮食记录页面增加视频状态显示
|
|
|
|
`[dietary-records.vue](msh_single_uniapp/pages/tool/dietary-records.vue)`
|
|
|
|
**显示逻辑**:
|
|
|
|
- 列表项增加视频状态角标:
|
|
- 有 `videoUrl`: 显示"📹视频"标记
|
|
- 有 `taskId` 但无 `videoUrl`: 显示"⏳生成中"或进度条
|
|
- `videoStatus = 2`: 显示"❌生成失败"
|
|
|
|
**轮询逻辑**:
|
|
|
|
```javascript
|
|
data() {
|
|
return {
|
|
pollingTasks: [], // 需要轮询的任务列表
|
|
pollingTimer: null
|
|
}
|
|
},
|
|
methods: {
|
|
// 启动轮询
|
|
startPolling() {
|
|
if (this.pollingTimer) return;
|
|
this.pollingTimer = setInterval(() => {
|
|
this.pollVideoTasks();
|
|
}, 5000); // 每5秒查询一次
|
|
},
|
|
|
|
// 轮询视频任务状态
|
|
async pollVideoTasks() {
|
|
const tasksToCheck = this.recordList.filter(item =>
|
|
item.enableAIVideo && item.taskId && !item.videoUrl
|
|
);
|
|
|
|
if (tasksToCheck.length === 0) {
|
|
this.stopPolling();
|
|
return;
|
|
}
|
|
|
|
for (const task of tasksToCheck) {
|
|
try {
|
|
const res = await getVideoTaskStatus(task.taskId);
|
|
if (res.code === 200 && res.data) {
|
|
const status = res.data.state;
|
|
if (status === 'success') {
|
|
// 刷新列表以获取最新视频URL
|
|
this.loadRecordList();
|
|
break;
|
|
} else if (status === 'failed') {
|
|
// 标记失败
|
|
task.videoStatus = 2;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('查询视频任务失败:', error);
|
|
}
|
|
}
|
|
},
|
|
|
|
// 停止轮询
|
|
stopPolling() {
|
|
if (this.pollingTimer) {
|
|
clearInterval(this.pollingTimer);
|
|
this.pollingTimer = null;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**生命周期**:
|
|
|
|
```javascript
|
|
onLoad() {
|
|
this.loadRecordList();
|
|
this.startPolling();
|
|
},
|
|
onUnload() {
|
|
this.stopPolling();
|
|
},
|
|
onShow() {
|
|
// 从其他页面返回时重新检查
|
|
this.loadRecordList();
|
|
this.startPolling();
|
|
}
|
|
```
|
|
|
|
**模板增加视频状态标记**:
|
|
|
|
```vue
|
|
<view class="video-status-badge" v-if="item.enableAIVideo">
|
|
<text v-if="item.videoUrl" class="status-success">📹 有视频</text>
|
|
<text v-else-if="item.videoStatus === 2" class="status-failed">❌ 生成失败</text>
|
|
<text v-else class="status-pending">⏳ 生成中</text>
|
|
</view>
|
|
```
|
|
|
|
#### 2. 打卡详情页增加视频播放
|
|
|
|
`[checkin-detail.vue](msh_single_uniapp/pages/tool/checkin-detail.vue)`
|
|
|
|
**数据结构更新**:
|
|
|
|
```javascript
|
|
data() {
|
|
return {
|
|
checkinData: {
|
|
// ...现有字段
|
|
videoUrl: '',
|
|
taskId: '',
|
|
enableAIVideo: false,
|
|
videoStatus: 0
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**模板增加视频播放器**:
|
|
|
|
```vue
|
|
<!-- 在图片轮播区域下方或上方增加 -->
|
|
<view class="video-section" v-if="checkinData.videoUrl">
|
|
<view class="video-header">
|
|
<text class="video-icon">🎬</text>
|
|
<text class="video-title">打卡视频</text>
|
|
</view>
|
|
<video
|
|
class="checkin-video"
|
|
:src="checkinData.videoUrl"
|
|
controls
|
|
:show-center-play-btn="true"
|
|
:enable-progress-gesture="true"
|
|
object-fit="contain"
|
|
></video>
|
|
</view>
|
|
|
|
<!-- 视频生成中状态 -->
|
|
<view class="video-generating" v-else-if="checkinData.enableAIVideo && checkinData.taskId">
|
|
<text class="generating-icon">⏳</text>
|
|
<text class="generating-text">视频生成中,请稍后...</text>
|
|
</view>
|
|
```
|
|
|
|
**formatCheckinData更新**:
|
|
|
|
```javascript
|
|
formatCheckinData(item) {
|
|
// ...现有逻辑
|
|
this.checkinData = {
|
|
// ...现有字段
|
|
videoUrl: item.videoUrl || '',
|
|
taskId: item.taskId || '',
|
|
enableAIVideo: item.enableAIVideo || false,
|
|
videoStatus: item.videoStatus || 0
|
|
};
|
|
}
|
|
```
|
|
|
|
#### 3. 社区页面显示视频标记
|
|
|
|
`[community.vue](msh_single_uniapp/pages/tool_main/community.vue)`
|
|
|
|
**formatPostList更新**:
|
|
|
|
```javascript
|
|
formatPostList(list) {
|
|
return list.map(item => {
|
|
// ...现有逻辑
|
|
return {
|
|
// ...现有字段
|
|
hasVideo: item.hasVideo || false,
|
|
videoUrl: item.videoUrl || '',
|
|
enableAIVideo: item.enableAIVideo || false
|
|
};
|
|
});
|
|
}
|
|
```
|
|
|
|
**卡片增加视频标记**:
|
|
|
|
```vue
|
|
<view class="post-card">
|
|
<!-- 现有内容 -->
|
|
|
|
<!-- 视频标记 -->
|
|
<view class="video-badge" v-if="item.hasVideo || item.videoUrl">
|
|
<text class="badge-icon">🎬</text>
|
|
<text class="badge-text">视频</text>
|
|
</view>
|
|
</view>
|
|
```
|
|
|
|
**样式**:
|
|
|
|
```scss
|
|
.video-badge {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 8px;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
border-radius: 12px;
|
|
padding: 4px 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
|
|
.badge-icon {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.badge-text {
|
|
font-size: 12px;
|
|
color: #fff;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4. 新增API方法
|
|
|
|
`[tool.js](msh_single_uniapp/api/tool.js)`
|
|
|
|
```javascript
|
|
/**
|
|
* 查询视频任务状态
|
|
* @param {String} taskId - 任务ID
|
|
*/
|
|
export function getVideoTaskStatus(taskId) {
|
|
return request.get(`kieai/video/task/${taskId}`, {
|
|
apiKey: 'your-api-key' // 从配置获取
|
|
});
|
|
}
|
|
```
|
|
|
|
### 关键技术细节
|
|
|
|
#### 1. 轮询策略
|
|
|
|
- **触发条件**: 列表中存在 `enableAIVideo=true` 且 `taskId` 不为空但 `videoUrl` 为空的记录
|
|
- **轮询间隔**: 5秒
|
|
- **停止条件**:
|
|
- 所有任务都有 `videoUrl` 或 `videoStatus=2`
|
|
- 页面销毁(onUnload)
|
|
- 超过最大轮询次数(如60次,即5分钟)
|
|
- **优化**: 只轮询当前可见列表中的任务,避免全量查询
|
|
|
|
#### 2. 视频播放兼容性
|
|
|
|
- 使用uni-app的 `<video>` 组件,支持小程序、H5、APP
|
|
- 视频格式要求: mp4 (KieAI返回格式)
|
|
- 封面图: 可使用打卡的第一张照片作为视频封面
|
|
|
|
#### 3. 错误处理
|
|
|
|
- **视频生成失败**: 显示失败状态,允许用户重新生成或查看原因
|
|
- **网络错误**: 轮询失败时静默处理,不影响用户浏览
|
|
- **回调失败**: 前端轮询作为兜底方案
|
|
|
|
## 文件清单
|
|
|
|
### 后端Java文件
|
|
|
|
|
|
| 文件 | 改动类型 | 说明 |
|
|
| -------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------------------- |
|
|
| `[ToolCheckinServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCheckinServiceImpl.java)` | 修改 | submit增加article创建,getList/getDetail增加video字段 |
|
|
| `[KieAIController.java](msh_crmeb_22/crmeb-front/src/main/java/com/zbkj/front/controller/KieAIController.java)` | 修改 | 完善回调处理更新article表 |
|
|
| `[ToolKieAIServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolKieAIServiceImpl.java)` | 修改 | handleTaskCallback增加更新article逻辑 |
|
|
| `[ToolCommunityServiceImpl.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCommunityServiceImpl.java)` | 修改 | getList关联video信息 |
|
|
| `[V2CommunityPost.java](msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2CommunityPost.java)` | 检查/修改 | 确认有videoUrl字段 |
|
|
| `[ArticleDao.java](msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/dao/ArticleDao.java)` | 新增方法 | 增加updateVideoUrlByTaskId方法 |
|
|
|
|
|
|
### 前端Vue文件
|
|
|
|
|
|
| 文件 | 改动类型 | 说明 |
|
|
| ------------------------------------------------------------------------- | ---- | ------------------------ |
|
|
| `[dietary-records.vue](msh_single_uniapp/pages/tool/dietary-records.vue)` | 修改 | 增加视频状态显示和轮询逻辑 |
|
|
| `[checkin-detail.vue](msh_single_uniapp/pages/tool/checkin-detail.vue)` | 修改 | 增加视频播放器 |
|
|
| `[community.vue](msh_single_uniapp/pages/tool_main/community.vue)` | 修改 | 增加视频标记 |
|
|
| `[tool.js](msh_single_uniapp/api/tool.js)` | 新增方法 | 增加getVideoTaskStatus API |
|
|
|
|
|
|
## 测试要点
|
|
|
|
1. **打卡流程**: 勾选生成视频 → 发布 → 验证taskId保存 → 验证article记录创建
|
|
2. **状态轮询**: 饮食记录页面显示"生成中" → 等待KieAI完成 → 自动更新显示"有视频"
|
|
3. **视频播放**: 打卡详情查看视频 → 播放正常 → 控制条功能正常
|
|
4. **社区显示**: 最新tab显示有视频标记的帖子 → 点击查看带视频
|
|
5. **异常处理**: 视频生成失败 → 显示失败状态 → 不影响打卡记录查看
|
|
6. **回调机制**: KieAI回调成功 → article表正确更新 → 前端轮询立即获取到结果
|
|
|