- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
202 lines
6.6 KiB
Python
202 lines
6.6 KiB
Python
"""
|
||
知识点分析 API
|
||
|
||
使用 Python 原生 AI 服务实现
|
||
"""
|
||
import logging
|
||
from typing import Dict, Any
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.core.deps import get_db, get_current_user
|
||
from app.schemas.base import ResponseModel
|
||
from app.models.user import User
|
||
from app.services.course_service import course_service
|
||
from app.services.ai.knowledge_analysis_v2 import knowledge_analysis_service_v2
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
@router.post("/courses/{course_id}/materials/{material_id}/analyze", response_model=ResponseModel[Dict[str, Any]])
|
||
async def analyze_material_knowledge_points(
|
||
course_id: int,
|
||
material_id: int,
|
||
background_tasks: BackgroundTasks,
|
||
current_user: User = Depends(get_current_user),
|
||
db: AsyncSession = Depends(get_db),
|
||
):
|
||
"""
|
||
分析单个资料的知识点
|
||
|
||
- **course_id**: 课程ID
|
||
- **material_id**: 资料ID
|
||
|
||
使用 Python 原生 AI 服务:
|
||
- 本地 AI 服务调用(4sapi.com 首选,OpenRouter 备选)
|
||
- 多层 JSON 解析兜底
|
||
- 无外部平台依赖,更稳定
|
||
"""
|
||
try:
|
||
# 验证课程是否存在
|
||
course = await course_service.get_by_id(db, course_id)
|
||
if not course:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"课程 {course_id} 不存在"
|
||
)
|
||
|
||
# 获取资料信息
|
||
materials = await course_service.get_course_materials(db, course_id=course_id)
|
||
material = next((m for m in materials if m.id == material_id), None)
|
||
if not material:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"资料 {material_id} 不存在"
|
||
)
|
||
|
||
logger.info(
|
||
f"准备启动知识点分析 - course_id: {course_id}, material_id: {material_id}, "
|
||
f"file_url: {material.file_url}, user_id: {current_user.id}"
|
||
)
|
||
|
||
# 调用 Python 原生知识点分析服务
|
||
result = await knowledge_analysis_service_v2.analyze_course_material(
|
||
db=db,
|
||
course_id=course_id,
|
||
material_id=material_id,
|
||
file_url=material.file_url,
|
||
course_title=course.name,
|
||
user_id=current_user.id
|
||
)
|
||
|
||
logger.info(
|
||
f"知识点分析完成 - course_id: {course_id}, material_id: {material_id}, "
|
||
f"knowledge_points: {result.get('knowledge_points_count', 0)}, "
|
||
f"provider: {result.get('ai_provider')}"
|
||
)
|
||
|
||
# 构建响应
|
||
response_data = {
|
||
"message": "知识点分析完成",
|
||
"course_id": course_id,
|
||
"material_id": material_id,
|
||
"status": result.get("status", "completed"),
|
||
"knowledge_points_count": result.get("knowledge_points_count", 0),
|
||
"ai_provider": result.get("ai_provider"),
|
||
"ai_model": result.get("ai_model"),
|
||
"ai_tokens": result.get("ai_tokens"),
|
||
"ai_latency_ms": result.get("ai_latency_ms"),
|
||
}
|
||
|
||
return ResponseModel(
|
||
data=response_data,
|
||
message="知识点分析完成"
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(
|
||
f"知识点分析失败 - course_id: {course_id}, material_id: {material_id}, error: {e}",
|
||
exc_info=True
|
||
)
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail=f"知识点分析失败: {str(e)}"
|
||
)
|
||
|
||
|
||
@router.post("/courses/{course_id}/reanalyze", response_model=ResponseModel[Dict[str, Any]])
|
||
async def reanalyze_course_materials(
|
||
course_id: int,
|
||
background_tasks: BackgroundTasks,
|
||
current_user: User = Depends(get_current_user),
|
||
db: AsyncSession = Depends(get_db),
|
||
):
|
||
"""
|
||
重新分析课程的所有资料
|
||
|
||
- **course_id**: 课程ID
|
||
|
||
该接口会重新分析课程下的所有资料,提取知识点
|
||
"""
|
||
try:
|
||
# 验证课程是否存在
|
||
course = await course_service.get_by_id(db, course_id)
|
||
if not course:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_404_NOT_FOUND,
|
||
detail=f"课程 {course_id} 不存在"
|
||
)
|
||
|
||
# 获取课程资料信息
|
||
materials = await course_service.get_course_materials(db, course_id=course_id)
|
||
|
||
if not materials:
|
||
return ResponseModel(
|
||
data={
|
||
"message": "该课程暂无资料需要分析",
|
||
"course_id": course_id,
|
||
"status": "stopped",
|
||
"materials_count": 0
|
||
},
|
||
message="无资料需要分析"
|
||
)
|
||
|
||
# 调用 Python 原生知识点分析服务
|
||
result = await knowledge_analysis_service_v2.reanalyze_course_materials(
|
||
db=db,
|
||
course_id=course_id,
|
||
course_title=course.name,
|
||
user_id=current_user.id
|
||
)
|
||
|
||
return ResponseModel(
|
||
data={
|
||
"message": "课程资料重新分析完成",
|
||
"course_id": course_id,
|
||
"status": "completed",
|
||
"materials_count": result.get("materials_count", 0),
|
||
"success_count": result.get("success_count", 0),
|
||
"knowledge_points_count": result.get("knowledge_points_count", 0),
|
||
"analysis_results": result.get("analysis_results", [])
|
||
},
|
||
message="重新分析完成"
|
||
)
|
||
|
||
except HTTPException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(
|
||
f"启动课程资料重新分析失败 - course_id: {course_id}, error: {e}",
|
||
exc_info=True
|
||
)
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail="启动重新分析失败"
|
||
)
|
||
|
||
|
||
@router.get("/engines", response_model=ResponseModel[Dict[str, Any]])
|
||
async def list_analysis_engines():
|
||
"""
|
||
获取可用的分析引擎列表
|
||
"""
|
||
return ResponseModel(
|
||
data={
|
||
"engines": [
|
||
{
|
||
"id": "native",
|
||
"name": "Python 原生实现",
|
||
"description": "使用本地 AI 服务(4sapi.com + OpenRouter),稳定可靠",
|
||
"default": True
|
||
}
|
||
],
|
||
"default_engine": "native"
|
||
},
|
||
message="获取分析引擎列表成功"
|
||
)
|