- fission_service: 1源任务扇出N套子任务(回填source_fission_id,复用主管道),MAX_FANOUT=5红线 - fission_prompt: 三档参考度(high/mid/low)占位规则,真实措辞待北哥定义 - tasks.py: worker识别裂变子任务→注入对应档位规则进文案上下文(关键缺口修复) - api/v1/fission: POST触发 + GET进度聚合, main.py注册路由 - 实测: fission_id=2扇出task70/71,worker注入日志level=high,进度端点聚合正确 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
85 lines
3.1 KiB
Python
85 lines
3.1 KiB
Python
"""
|
|
app/services/fission_service.py — 第11环裂变 扇出 service
|
|
|
|
1爆款 → N套完整笔记包。每套=一个独立 GenerationTask(回填 source_fission_id),
|
|
复用主管道 run_generation_pipeline 生成文案+配图。
|
|
🔴 占位档位规则见 fission_prompt.py,真实措辞待北哥定义(倩倩姐2026-06-16先打通)。
|
|
"""
|
|
import json
|
|
import logging
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.response import raise_business
|
|
from app.middleware.workspace_guard import CurrentUser
|
|
from app.models.fission import FissionTask
|
|
from app.models.task import GenerationTask
|
|
from app.services.ai_engine.fission_prompt import valid_level
|
|
from app.services.task_service import _check_user_has_key, enqueue_generation
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MAX_FANOUT = 5 # 每用户并发上限5(红线),裂变扇出数受同等约束
|
|
|
|
|
|
def create_fission(
|
|
db: Session, current_user: CurrentUser,
|
|
source_task_id: int, reference_level: str, fanout_count: int,
|
|
) -> tuple[int, list[int]]:
|
|
"""
|
|
从一个源任务裂变出 N 套子任务。
|
|
返回 (fission_id, [子task_id,...])。
|
|
"""
|
|
_check_user_has_key(db, current_user.user_id, current_user.workspace_id)
|
|
|
|
level = valid_level(reference_level)
|
|
n = max(1, min(fanout_count or 3, MAX_FANOUT))
|
|
|
|
# 读源任务(取产品+主题+已选文案作为爆款源)
|
|
src = db.query(GenerationTask).filter(
|
|
GenerationTask.id == source_task_id,
|
|
GenerationTask.workspace_id == current_user.workspace_id,
|
|
).first()
|
|
if not src:
|
|
raise_business("裂变源任务不存在")
|
|
|
|
source_note = {"title": src.theme or "", "content": _extract_source_copy(db, src)}
|
|
|
|
# 建 fission_task 记录
|
|
ft = FissionTask(
|
|
workspace_id=current_user.workspace_id,
|
|
source_note=json.dumps(source_note, ensure_ascii=False),
|
|
reference_level=level, fanout_count=n, status="generating",
|
|
)
|
|
db.add(ft); db.commit(); db.refresh(ft)
|
|
|
|
# 扇出 N 个子任务(回填 source_fission_id),复用主管道
|
|
task_ids: list[int] = []
|
|
for _ in range(n):
|
|
sub = GenerationTask(
|
|
workspace_id=current_user.workspace_id,
|
|
product_id=src.product_id, operator_id=current_user.user_id,
|
|
theme=src.theme, text_count=src.text_count, image_count=src.image_count,
|
|
track="ai", need_product_image=src.need_product_image,
|
|
status="pending", source_fission_id=ft.id,
|
|
)
|
|
db.add(sub); db.commit(); db.refresh(sub)
|
|
enqueue_generation(sub.id)
|
|
task_ids.append(sub.id)
|
|
|
|
logger.info("fission created id=%s level=%s fanout=%s tasks=%s",
|
|
ft.id, level, n, task_ids)
|
|
return ft.id, task_ids
|
|
|
|
|
|
def _extract_source_copy(db: Session, src: GenerationTask) -> str:
|
|
"""取源任务已选/最高分文案当爆款正文;无则用主题兜底。"""
|
|
from app.models.task import TextCandidate
|
|
c = (db.query(TextCandidate)
|
|
.filter(TextCandidate.task_id == src.id)
|
|
.order_by(TextCandidate.is_selected.desc(), TextCandidate.id.asc())
|
|
.first())
|
|
if c and c.content:
|
|
return c.content
|
|
return src.theme or ""
|