""" 播课功能 API 接口 """ import logging from datetime import datetime from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db, get_current_user, require_admin_or_manager from app.schemas.base import ResponseModel from app.models.course import Course from app.models.user import User from app.services.coze_broadcast_service import broadcast_service logger = logging.getLogger(__name__) router = APIRouter() # Schema 定义 class GenerateBroadcastResponse(BaseModel): """生成播课响应""" message: str = 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="生成时间") @router.post("/courses/{course_id}/generate-broadcast", response_model=ResponseModel[GenerateBroadcastResponse]) async def generate_broadcast( course_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_admin_or_manager) ): """ 触发播课音频生成(立即返回,Coze工作流会直接写数据库) 权限:manager、admin Args: course_id: 课程ID db: 数据库会话 current_user: 当前用户 Returns: 启动提示信息 Raises: HTTPException 404: 课程不存在 """ logger.info( f"请求生成播课", extra={"course_id": course_id, "user_id": current_user.id} ) # 查询课程 result = await db.execute( select(Course) .where(Course.id == course_id) .where(Course.is_deleted == False) ) course = result.scalar_one_or_none() if not course: logger.warning(f"课程不存在", extra={"course_id": course_id}) raise HTTPException(status_code=404, detail="课程不存在") # 调用 Coze 工作流(不等待结果,工作流会直接写数据库) try: await broadcast_service.trigger_workflow(course_id) logger.info( f"播课生成工作流已触发", extra={"course_id": course_id, "user_id": current_user.id} ) return ResponseModel( code=200, message="播课生成已启动", data=GenerateBroadcastResponse( message="播课生成工作流已启动,生成完成后将自动更新" ) ) except Exception as e: logger.error( f"触发播课生成失败", extra={"course_id": course_id, "error": str(e)} ) raise HTTPException(status_code=500, detail=f"触发播课生成失败: {str(e)}") @router.get("/courses/{course_id}/broadcast", response_model=ResponseModel[BroadcastInfo]) async def get_broadcast_info( course_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 获取播课信息 权限:所有登录用户 Args: course_id: 课程ID db: 数据库会话 current_user: 当前用户 Returns: 播课信息 Raises: HTTPException 404: 课程不存在 """ # 查询课程 result = await db.execute( select(Course) .where(Course.id == course_id) .where(Course.is_deleted == False) ) course = result.scalar_one_or_none() if not course: raise HTTPException(status_code=404, detail="课程不存在") # 构建播课信息 has_broadcast = bool(course.broadcast_audio_url) return ResponseModel( code=200, message="success", data=BroadcastInfo( has_broadcast=has_broadcast, mp3_url=course.broadcast_audio_url if has_broadcast else None, generated_at=course.broadcast_generated_at if has_broadcast else None ) )