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

15 KiB

name, overview, todos, isProject
name overview todos isProject
打卡视频生成与展示 实现打卡勾选生成视频后,在饮食记录页面定时查询视频任务状态并显示,在打卡详情支持视频播放,勾选生成视频的打卡帖子在社区最新tab显示
id content status
backend-checkin-article 修改打卡提交逻辑,勾选视频时创建eb_article记录存储taskId completed
id content status
backend-list-api 更新打卡列表/详情接口,关联article表返回videoUrl和videoStatus completed
id content status
backend-callback 完善KieAI回调处理,更新article的video_url和status_task completed
id content status
backend-community 更新社区列表接口,关联打卡记录的视频信息 completed
id content status
frontend-dietary-records 饮食记录页面增加视频状态显示和定时轮询逻辑 completed
id content status
frontend-detail-video 打卡详情页增加视频播放器组件 completed
id content status
frontend-community-badge 社区页面卡片增加视频标记 completed
id content status
frontend-api 新增getVideoTaskStatus API方法 completed
false

打卡视频生成与展示功能实现计划

背景分析

根据代码探索,当前系统已具备:

  • 打卡发布: [checkin-publish.vue](msh_single_uniapp/pages/tool/checkin-publish.vue) 支持勾选"生成打卡视频分享到社区",会调用KieAI创建视频任务并返回 taskId
  • 数据存储: eb_user_sign 表存储 task_idenable_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. 数据流设计

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)

  • 增加返回 videoUrlvideoStatus 字段

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 项目的实现:

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_videotask_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 字段,若无则添加:
@ApiModelProperty(value = "视频地址")
private String videoUrl;

前端改动

1. 饮食记录页面增加视频状态显示

[dietary-records.vue](msh_single_uniapp/pages/tool/dietary-records.vue)

显示逻辑:

  • 列表项增加视频状态角标:
    • videoUrl: 显示"📹视频"标记
    • taskId 但无 videoUrl: 显示"生成中"或进度条
    • videoStatus = 2: 显示"生成失败"

轮询逻辑:

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;
    }
  }
}

生命周期:

onLoad() {
  this.loadRecordList();
  this.startPolling();
},
onUnload() {
  this.stopPolling();
},
onShow() {
  // 从其他页面返回时重新检查
  this.loadRecordList();
  this.startPolling();
}

模板增加视频状态标记:

<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)

数据结构更新:

data() {
  return {
    checkinData: {
      // ...现有字段
      videoUrl: '',
      taskId: '',
      enableAIVideo: false,
      videoStatus: 0
    }
  }
}

模板增加视频播放器:

<!-- 在图片轮播区域下方或上方增加 -->
<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更新:

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更新:

formatPostList(list) {
  return list.map(item => {
    // ...现有逻辑
    return {
      // ...现有字段
      hasVideo: item.hasVideo || false,
      videoUrl: item.videoUrl || '',
      enableAIVideo: item.enableAIVideo || false
    };
  });
}

卡片增加视频标记:

<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>

样式:

.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)

/**
 * 查询视频任务状态
 * @param {String} taskId - 任务ID
 */
export function getVideoTaskStatus(taskId) {
  return request.get(`kieai/video/task/${taskId}`, { 
    apiKey: 'your-api-key' // 从配置获取
  });
}

关键技术细节

1. 轮询策略

  • 触发条件: 列表中存在 enableAIVideo=truetaskId 不为空但 videoUrl 为空的记录
  • 轮询间隔: 5秒
  • 停止条件:
    • 所有任务都有 videoUrlvideoStatus=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表正确更新 → 前端轮询立即获取到结果