Files
beige/backend/app/services/fission_images.py
yangqianqian 4bed7425a8 A8多套打包+M4归档+R5多图:存量功能备份
A8 多套交付包(packaging_task.py):
- 修复交付包只打第1条混乱note的bug,按ImageCandidate.strategy分A/B/C组
- 每组生独立note_0N夹(6图+文案.txt),同seq留最新去重,老数据兼容
- task74端到端验:3套各6图,独立agent7项交叉验证全过

M4 归档(tasks.py/exports.py/前端):
- list_tasks加date_from/date_to/product_id筛选+product_name批量填(防N+1)
- 新增exports.py:产品JSON导出+标杆CSV导出(UTF-8 BOM)
- 前端HistoryFilters日期/产品筛选+产品列+打回原因红banner
- response.py加raise_param_error;独立agent验A1/A2/A9通过

R5 产品多图(product_images.py/020迁移/前端):
- product_images表+5端点(上传/列/改场景/设主图/删图)
- 生图按ROLE_SCENE_PREFERENCE选对应场景图,回落primary
- 前端ProductImageManager多图画廊

R6 账号config拆页(settings/):
- 配置页按角色拆/settings(运营+组长+admin)+/config(仅admin)
- Key只显末4位不显余额(守红线)

核销表对齐真实代码状态:D1改稿框/M7裂变/E12评图分纠偏为已完成(曾漏回写)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 17:32:49 +08:00

96 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
app/services/fission_images.py — 第11环裂变 逐套生图(从 fission_pipeline 拆出)
复用单篇 generate_storyboard_imagescodeproxy edits 带产品参考图),结果存
FissionNote.images_json。拆分原因fission_pipeline.py 超 200 行红线上限。
🔴 生图引擎零改动;串行跑各套(套内 storyboard 已 asyncio.gather 并发)避免打爆中转站。
"""
import logging
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
def generate_fission_images(
db: Session, clients, ft, product: dict, image_count: int, note_ids: list[int],
) -> None:
"""逐套生图,结果存 FissionNote.images_json。
每套用该套自己的 note_json含定制 imagePlan当 note 喂生图引擎。
产品参考图沿用源产品同一张,与单篇一致。
"""
import asyncio
import json as _json
import os
from app.models.fission import FissionNote
from app.core.config import get_settings
from app.services.ai_engine.image_gen import generate_storyboard_images
from app.services.ai_engine.image_postprocessor import process_image
from app.workers.pipeline_io import _resolve_image_path
reference_images = None
_img_path = _resolve_image_path(product.get("image_path", ""))
if _img_path and os.path.isfile(_img_path):
try:
with open(_img_path, "rb") as f:
reference_images = [f.read()]
except Exception as e: # noqa: BLE001
logger.warning("裂变参考图读取失败,无参考图模式: %s", e)
# R5多图裂变同样按场景分组生图按分镜 role 选对应图
images_by_scene: dict[str, list[bytes]] = {}
for _im in (product.get("images") or []):
_p = _resolve_image_path(_im.get("path", ""))
if _p and os.path.isfile(_p):
try:
with open(_p, "rb") as _f:
images_by_scene.setdefault(_im.get("scene") or "primary", []).append(_f.read())
except Exception: # noqa: BLE001
pass
if reference_images and not images_by_scene.get("primary"):
images_by_scene.setdefault("primary", []).extend(reference_images)
upload_base = get_settings().UPLOAD_ABS_ROOT
for nid in note_ids:
fn = db.query(FissionNote).filter(FissionNote.id == nid).first()
if not fn:
continue
try:
note = _json.loads(fn.note_json) if fn.note_json else {}
except (ValueError, TypeError):
note = {}
try:
results = asyncio.run(generate_storyboard_images(
client=clients, note=note, product=product,
image_count=image_count, reference_images=reference_images,
images_by_scene=images_by_scene or None,
))
except Exception as exc: # noqa: BLE001
logger.error("裂变套 seq=%s 生图失败: %s", fn.seq, exc)
fn.status = "failed"; db.commit()
continue
img_dir = os.path.join(upload_base, str(ft.workspace_id), f"fission_{ft.id}", str(fn.seq))
os.makedirs(img_dir, exist_ok=True)
images: list[dict] = []
for i, r in enumerate(results):
if r.get("error") or not r.get("image_bytes"):
images.append({"role": r.get("role", ""), "error": str(r.get("error", "生图失败"))[:64]})
continue
try:
processed = process_image(r["image_bytes"], aspect_ratio="3:4", resample_strength=1)
except Exception: # noqa: BLE001
processed = r["image_bytes"]
fname = f"{i + 1:02d}_{r['role']}.jpg"
with open(os.path.join(img_dir, fname), "wb") as f:
f.write(processed)
images.append({
"role": r["role"], "seq": i + 1,
"url": f"/uploads/{ft.workspace_id}/fission_{ft.id}/{fn.seq}/{fname}",
})
fn.images_json = _json.dumps(images, ensure_ascii=False)
fn.status = "image_done"
db.commit()