# 播课功能技术方案

## 一、需求背景

课程管理员需要为课程生成播课音频，学员可以在课程卡片上点击"播课"按钮收听课程内容，支持音频播放控制。

## 二、核心设计

### 2.1 触发方式

- **管理员手动触发**：在课程编辑页的"学习资料与知识点"选项卡，点击"生成播课"按钮
- **生成范围**：一个课程生成一个综合播课音频（多个资料合并生成）
- **存储方式**：只存储 mp3 URL，不存储在本地服务器

### 2.2 技术选型

| 技术项 | 选型 | 说明 |
|--------|------|------|
| AI 工作流 | Coze 工作流 | workflow_id: 7561161554420482088 |
| SDK | cozepy 0.2.0 | 已安装，与陪练功能共用 |
| 认证方式 | Personal Access Token | 与陪练功能共用 token |
| 数据库字段 | `broadcast_audio_url` | 存储 mp3 URL |
| 前端播放器 | HTML5 audio | 原生支持，无需额外依赖 |

### 2.3 数据流程

```
管理员点击"生成播课" 
  → 前端调用后端 API 
  → 后端调用 Coze 工作流 
  → Coze 生成 mp3 音频
  → 返回 mp3 URL
  → 后端保存到数据库
  → 前端显示成功提示

学员点击"播课" 
  → 前端查询播课信息
  → 跳转到播放页面
  → 加载并播放音频
```

## 三、数据库设计

### 3.1 courses 表新增字段

```sql
ALTER TABLE courses 
ADD COLUMN broadcast_audio_url VARCHAR(500) NULL COMMENT '播课音频URL',
ADD COLUMN broadcast_generated_at DATETIME NULL COMMENT '播课生成时间';
```

**字段说明**：
- `broadcast_audio_url`: 存储 Coze 工作流返回的 mp3 文件 URL
- `broadcast_generated_at`: 记录生成时间，用于判断是否需要重新生成

**索引**: 无需额外索引（查询通过主键 course_id）

## 四、后端架构

### 4.1 配置层

**文件**: `app/core/config.py`

```python
class Settings(BaseSettings):
    # 播课工作流配置
    COZE_BROADCAST_WORKFLOW_ID: str = Field(default="7561161554420482088")
    COZE_BROADCAST_SPACE_ID: str = Field(default="7474971491470688296")
```

### 4.2 服务层

**文件**: `app/services/coze_broadcast_service.py`

**职责**：
- 封装 Coze 工作流调用逻辑
- 解析返回的 mp3 URL
- 错误处理和日志记录

**关键方法**：

```python
class CozeBroadcastService:
    async def generate_broadcast(self, course_id: int) -> str:
        """
        调用 Coze 工作流生成播课音频
        
        Args:
            course_id: 课程ID
            
        Returns:
            mp3 音频文件 URL
            
        Raises:
            CozeError: Coze API 调用失败
            ValueError: 返回结果解析失败
        """
```

### 4.3 API 层

**文件**: `app/api/v1/broadcast.py`

**接口1**: 生成播课
- 路径: `POST /api/v1/courses/{course_id}/generate-broadcast`
- 权限: manager, admin
- 超时: 180秒
- 功能: 调用工作流生成音频，更新数据库

**接口2**: 获取播课信息
- 路径: `GET /api/v1/courses/{course_id}/broadcast`
- 权限: trainee, manager, admin
- 功能: 查询课程是否有播课以及播课 URL

### 4.4 Schema 定义

```python
class GenerateBroadcastResponse(BaseModel):
    """生成播课响应"""
    mp3_url: str = Field(..., description="播课音频URL")
    generated_at: datetime = Field(..., description="生成时间")

class BroadcastInfo(BaseModel):
    """播课信息"""
    has_broadcast: bool = Field(..., description="是否有播课")
    mp3_url: Optional[str] = Field(None, description="播课音频URL")
    generated_at: Optional[datetime] = Field(None, description="生成时间")
```

## 五、前端架构

### 5.1 类型定义

**文件**: `src/types/broadcast.ts`

```typescript
export interface BroadcastInfo {
  has_broadcast: boolean
  mp3_url?: string
  generated_at?: string
}

export interface GenerateBroadcastResponse {
  mp3_url: string
  generated_at: string
}
```

### 5.2 API 封装

**文件**: `src/api/broadcast.ts`

```typescript
export const broadcastApi = {
  generate(courseId: number): Promise<ResponseData<GenerateBroadcastResponse>>
  getInfo(courseId: number): Promise<ResponseData<BroadcastInfo>>
}
```

### 5.3 页面改造

**1. 课程编辑页** (`src/views/manager/edit-course.vue`)

新增功能：
- 在"学习资料与知识点"选项卡添加"生成播课"按钮
- 查询并显示播课信息（是否已生成、生成时间）
- 生成中显示 Loading 状态
- 生成成功后显示"重新生成"按钮

**2. 播放页面** (`src/views/trainee/broadcast-course.vue`)

播放器功能：
- 播放/暂停控制
- 进度条拖动
- 当前时间/总时长显示
- 播放速度调节（1.0x, 1.25x, 1.5x, 2.0x）

**3. 课程卡片** (`src/views/trainee/course-center.vue`)

修改播课按钮点击事件：
- 查询播课信息
- 如无播课，提示"该课程暂无播课内容"
- 如有播课，跳转到播放页面

## 六、交互流程

### 6.1 生成播课流程

```
1. 管理员进入课程编辑页
2. 切换到"学习资料与知识点"选项卡
3. 点击"生成播课"按钮
4. 前端调用 POST /api/v1/courses/{id}/generate-broadcast
5. 后端调用 Coze 工作流（约10分钟）
6. Coze 返回 mp3 URL
7. 后端更新数据库 broadcast_audio_url 和 broadcast_generated_at
8. 前端显示"生成成功"提示
9. 按钮文本变为"重新生成"，显示生成时间
```

### 6.2 播放流程

```
1. 学员在课程中心查看课程卡片
2. 点击"播课"按钮
3. 前端调用 GET /api/v1/courses/{id}/broadcast
4. 如 has_broadcast=false，显示"暂无播课内容"
5. 如 has_broadcast=true，跳转到播放页面
6. 播放页面加载 mp3 音频
7. 学员使用播放器控制播放
```

## 七、性能与安全

### 7.1 性能优化

- **超时配置**: 前后端统一设置 180 秒超时
- **缓存策略**: 音频 URL 从数据库读取，无需额外缓存
- **CDN 加速**: mp3 文件由 Coze 托管，自带 CDN 加速

### 7.2 错误处理

| 场景 | 处理方式 |
|------|---------|
| 工作流调用超时 | 提示"生成超时，请稍后重试" |
| 工作流执行失败 | 记录 logid，提示"生成失败，请联系管理员" |
| mp3 URL 解析失败 | 记录错误日志，提示"结果解析失败" |
| 播放加载失败 | 提示"音频加载失败，请检查网络" |

### 7.3 日志记录

```python
# 生成开始
logger.info(f"开始生成播课: course_id={course_id}, user_id={user_id}")

# 生成成功
logger.info(f"播课生成成功: course_id={course_id}, mp3_url={mp3_url}, duration={duration}")

# 生成失败
logger.error(
    f"播课生成失败: course_id={course_id}",
    extra={"logid": logid, "error": str(error)}
)
```

## 八、测试策略

### 8.1 单元测试

- CozeBroadcastService 工作流调用测试
- broadcast API 接口测试
- 前端 broadcastApi 封装测试

### 8.2 集成测试

- 管理员生成播课端到端测试
- 学员播放端到端测试
- 错误场景测试（无播课、生成失败、播放失败）

### 8.3 性能测试

- 工作流调用耗时（预期 10 分钟左右）
- 前端音频加载时间
- 并发生成请求测试

## 九、部署与监控

### 9.1 配置检查

部署前确认以下配置：
- ✅ COZE_API_TOKEN 正确配置
- ✅ COZE_BROADCAST_WORKFLOW_ID = 7561161554420482088
- ✅ COZE_BROADCAST_SPACE_ID = 7474971491470688296
- ✅ 工作流已在 Coze 平台发布

### 9.2 监控指标

- 生成成功率（目标 >95%）
- 平均生成时长（预期 10 分钟）
- 播放失败率（目标 <5%）
- Coze API 调用错误率

## 十、后续优化方向

1. **自动生成**：资料上传后自动触发生成（需评估资源消耗）
2. **进度提示**：显示生成进度（需 Coze 工作流支持进度回调）
3. **音频预览**：生成后在编辑页预览播放
4. **历史版本**：保存多个版本的播课音频
5. **字幕支持**：工作流返回字幕文件，前端同步显示

---

**文档版本**: v1.0  
**创建日期**: 2025-10-14  
**维护人**: 考培练系统开发团队

