feat: 完善 MVP-2 演示和进度体验
This commit is contained in:
@@ -8,7 +8,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from app.config import get_settings
|
||||
from app.db import SessionLocal
|
||||
from app.models import Comment, ContentItem, Hotspot, Task
|
||||
from app.models import Comment, ContentItem, Hotspot, Report, Task, utc_now
|
||||
from app.platforms.base import PlatformAPIError, TikHubClient
|
||||
from app.platforms.douyin import DouyinPlatform
|
||||
from app.platforms.xiaohongshu import XiaohongshuPlatform
|
||||
@@ -22,6 +22,18 @@ RESTART_ERROR_MESSAGE = "系统重启,任务被中断"
|
||||
task_executor = ThreadPoolExecutor(max_workers=1)
|
||||
|
||||
|
||||
STAGE_LABELS = {
|
||||
"queued": "等待启动",
|
||||
"crawl_hotspots": "获取热点中",
|
||||
"search_items": "搜索内容中",
|
||||
"crawl_comments": "抓取评论中",
|
||||
"ai_analysis": "AI 分析中",
|
||||
"generate_reports": "生成报告中",
|
||||
"success": "已完成",
|
||||
"failed": "失败",
|
||||
}
|
||||
|
||||
|
||||
def has_running_task(session: Session) -> bool:
|
||||
return session.scalar(select(Task.id).where(Task.status == "running").limit(1)) is not None
|
||||
|
||||
@@ -110,6 +122,8 @@ def create_task(session: Session, request: CreateTaskRequest, *, submit_backgrou
|
||||
failed_items_count=0,
|
||||
analysis_success_rate=0.0,
|
||||
analysis_status="normal",
|
||||
current_stage="queued",
|
||||
last_progress_at=utc_now(),
|
||||
)
|
||||
session.add(task)
|
||||
session.commit()
|
||||
@@ -120,11 +134,38 @@ def create_task(session: Session, request: CreateTaskRequest, *, submit_backgrou
|
||||
|
||||
|
||||
def list_tasks(session: Session) -> list[Task]:
|
||||
return list(session.scalars(select(Task).order_by(Task.created_at.desc())))
|
||||
tasks = list(session.scalars(select(Task).order_by(Task.created_at.desc())))
|
||||
for task in tasks:
|
||||
hydrate_task_progress(session, task)
|
||||
return tasks
|
||||
|
||||
|
||||
def get_task(session: Session, task_id: str) -> Task | None:
|
||||
return session.get(Task, task_id)
|
||||
task = session.get(Task, task_id)
|
||||
if task is not None:
|
||||
hydrate_task_progress(session, task)
|
||||
return task
|
||||
|
||||
|
||||
def hydrate_task_progress(session: Session, task: Task) -> Task:
|
||||
task.comments_count = session.scalar(select(func.count(Comment.id)).where(Comment.task_id == task.id)) or 0
|
||||
task.reports_count = session.scalar(select(func.count(Report.id)).where(Report.task_id == task.id)) or 0
|
||||
task.is_demo = task.id.startswith("demo-")
|
||||
task.last_progress_at = task.last_progress_at or task.created_at
|
||||
stage_code = task.current_stage
|
||||
if not stage_code and task.status == "success":
|
||||
stage_code = "success"
|
||||
elif not stage_code and task.status == "failed":
|
||||
stage_code = "failed"
|
||||
elif not stage_code and task.status == "running":
|
||||
stage_code = "queued"
|
||||
task.current_stage_label = STAGE_LABELS.get(stage_code or "", stage_code or "等待启动")
|
||||
return task
|
||||
|
||||
|
||||
def update_task_stage(task: Task, stage: str) -> None:
|
||||
task.current_stage = stage
|
||||
task.last_progress_at = utc_now()
|
||||
|
||||
|
||||
def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionmaker = SessionLocal) -> None:
|
||||
@@ -139,6 +180,8 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
ai_requester, report_summary_provider = build_ai_dependencies()
|
||||
|
||||
try:
|
||||
update_task_stage(task, "crawl_hotspots")
|
||||
session.commit()
|
||||
hotspots = platform.fetch_hotspots(limit=task.hotspot_limit)
|
||||
except PlatformAPIError as exc:
|
||||
_mark_task_failed(task, "crawl_hotspots", exc.error_type, str(exc))
|
||||
@@ -163,6 +206,8 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
session.flush()
|
||||
|
||||
try:
|
||||
update_task_stage(task, "search_items")
|
||||
session.commit()
|
||||
items = platform.search_items_by_hotspot(hotspot.title, limit=task.item_limit_per_hotspot)
|
||||
except Exception as exc:
|
||||
task.failed_items_count += task.item_limit_per_hotspot
|
||||
@@ -193,6 +238,8 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
session.add(item)
|
||||
session.flush()
|
||||
try:
|
||||
update_task_stage(task, "crawl_comments")
|
||||
session.commit()
|
||||
comments = platform.fetch_comments(item.source_item_id, limit=task.comment_limit_per_item)
|
||||
for comment_data in comments:
|
||||
session.add(
|
||||
@@ -211,6 +258,8 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
)
|
||||
session.flush()
|
||||
item_comments = list(session.scalars(select(Comment).where(Comment.content_item_id == item.id)))
|
||||
update_task_stage(task, "ai_analysis")
|
||||
session.commit()
|
||||
ai_results = analyze_comments_with_retry(
|
||||
[{"comment_id": comment.id, "content": comment.content} for comment in item_comments],
|
||||
requester=ai_requester,
|
||||
@@ -231,6 +280,7 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
comment.ai_analysis_status = result.ai_analysis_status
|
||||
item.status = "success"
|
||||
task.successful_items_count += 1
|
||||
update_task_stage(task, "generate_reports")
|
||||
generate_item_report(session, item.id, summary_provider=report_summary_provider)
|
||||
except Exception as exc:
|
||||
item.status = "failed"
|
||||
@@ -246,6 +296,7 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
session.commit()
|
||||
|
||||
if task.successful_items_count > 0:
|
||||
update_task_stage(task, "generate_reports")
|
||||
total_comments = session.scalar(select(func.count(Comment.id)).where(Comment.task_id == task.id)) or 0
|
||||
success_comments = sum(1 for comment in task.comments if comment.ai_analysis_status == "success")
|
||||
task.analysis_success_rate, task.analysis_status = calculate_analysis_status(
|
||||
@@ -255,6 +306,7 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
for hotspot in task.hotspots:
|
||||
generate_hotspot_report(session, hotspot.id, summary_provider=report_summary_provider)
|
||||
task.status = "success"
|
||||
update_task_stage(task, "success")
|
||||
else:
|
||||
_mark_task_failed(task, task.error_stage or "crawl_items", task.error_type or "no_successful_items", task.error_message or "没有任何内容条目成功")
|
||||
session.commit()
|
||||
@@ -268,6 +320,7 @@ def run_task(task_id: str, *, session_factory: Callable[[], Session] | sessionma
|
||||
|
||||
def _mark_task_failed(task: Task, stage: str, error_type: str, message: str) -> None:
|
||||
task.status = "failed"
|
||||
update_task_stage(task, "failed")
|
||||
task.error_stage = stage
|
||||
task.error_type = error_type
|
||||
task.error_message = message
|
||||
|
||||
Reference in New Issue
Block a user