Files
msh-system/.cursor/plans/打卡视频生成与展示_12778af3.plan.md

460 lines
15 KiB
Markdown
Raw Normal View History

---
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表正确更新 → 前端轮询立即获取到结果