""" app/api/v1/fission.py — 第11环裂变路由 POST /fission 触发裂变(1源任务→N套子任务) GET /fission/{id} 查裂变进度(聚合N个子任务状态) 工作台内呈现,子任务走标准 SSE 流回进度。 """ import logging from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel, Field from sqlalchemy.orm import Session from app.core.database import get_db from app.core.response import ok, raise_not_found from app.middleware.workspace_guard import CurrentUser, require_write_permission from app.models.fission import FissionTask from app.models.task import GenerationTask logger = logging.getLogger(__name__) router = APIRouter(tags=["fission"]) class FissionCreate(BaseModel): source_task_id: int reference_level: str = Field(default="mid") # low/mid/high fanout_count: int = Field(default=3, ge=1, le=5) @router.post("/fission") def create_fission_endpoint( body: FissionCreate, current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None, db: Session = Depends(get_db), ): """触发裂变:从源任务扇出 N 套完整笔记包。返回 fission_id + 子 task_ids。""" from app.services.fission_service import create_fission fission_id, task_ids = create_fission( db, current_user, body.source_task_id, body.reference_level, body.fanout_count, ) return ok({"fission_id": fission_id, "task_ids": task_ids, "reference_level": body.reference_level, "fanout_count": len(task_ids)}) @router.get("/fission/{fission_id}") def get_fission_progress( fission_id: int, current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None, db: Session = Depends(get_db), ): """查裂变进度:聚合 N 个子任务状态(x/N 完成)。""" ft = db.query(FissionTask).filter( FissionTask.id == fission_id, FissionTask.workspace_id == current_user.workspace_id, ).first() if not ft: raise_not_found("裂变任务不存在") subs = db.query(GenerationTask).filter( GenerationTask.source_fission_id == fission_id, GenerationTask.workspace_id == current_user.workspace_id, ).all() done_states = {"pending_selection", "pending_review", "approved", "archived"} done = sum(1 for s in subs if s.status in done_states) failed = sum(1 for s in subs if s.status == "failed") total = len(subs) # 聚合状态:全失败=failed,全完成=done,否则generating agg = "done" if done == total and total > 0 else ("failed" if failed == total and total > 0 else "generating") if ft.status != agg: ft.status = agg; db.commit() return ok({ "fission_id": ft.id, "reference_level": ft.reference_level, "fanout_count": ft.fanout_count, "status": agg, "progress": {"done": done, "failed": failed, "total": total}, "task_ids": [s.id for s in subs], })