修复回忆录图片重试状态透传与前端展示

This commit is contained in:
Kevin
2026-03-11 15:20:59 +08:00
parent 4b4dea8a45
commit 305e5dcde9
18 changed files with 724 additions and 64 deletions

View File

@@ -44,12 +44,16 @@ from services.memoir_images.schema import (
normalize_image_assets,
)
from services.memoir_images.settings import MemoirImageSettings
from services.memoir_images.storage import TencentCosStorageService
from services.memoir_images.storage import TencentCosStorageService, CosUploadError
logger = logging.getLogger(__name__)
_REDIS_CLIENTS: dict[bool, redis.Redis] = {}
class PermanentImageGenerationError(RuntimeError):
"""Raised when chapter image generation hits a non-retryable failure."""
def _get_redis_client(*, decode_responses: bool = False) -> redis.Redis:
client = _REDIS_CLIENTS.get(decode_responses)
if client is None:
@@ -736,11 +740,14 @@ def generate_chapter_images(self, chapter_id: str):
len(images),
pending_count,
)
failures: list[str] = []
retryable_failures: list[str] = []
permanent_failures: list[str] = []
for index, item in enumerate(images):
if item.get("status") == IMAGE_STATUS_COMPLETED and (item.get("storage_key") or item.get("url")):
continue
if item.get("status") == IMAGE_STATUS_FAILED and item.get("retryable") is False:
continue
if item.get("status") not in {IMAGE_STATUS_PENDING, IMAGE_STATUS_FAILED}:
continue
@@ -781,6 +788,7 @@ def generate_chapter_images(self, chapter_id: str):
current_item["size"] = prompt_data["size"]
current_item["status"] = IMAGE_STATUS_COMPLETED
current_item["error"] = None
current_item["retryable"] = None
logger.info(
"章节补图成功: chapter=%s, index=%s, url=%s",
chapter_id,
@@ -790,20 +798,34 @@ def generate_chapter_images(self, chapter_id: str):
except Exception as exc:
current_item["status"] = IMAGE_STATUS_FAILED
current_item["error"] = str(exc)
failures.append(f"index={current_item.get('index')}, error={exc}")
logger.warning(f"图片生成失败: chapter={chapter_id}, index={current_item.get('index')}, error={exc}")
failure_msg = f"index={current_item.get('index')}, error={exc}"
if isinstance(exc, CosUploadError) and not exc.retryable:
current_item["retryable"] = False
permanent_failures.append(failure_msg)
logger.error("图片上传不可重试: chapter=%s, %s", chapter_id, failure_msg)
else:
current_item["retryable"] = True
retryable_failures.append(failure_msg)
logger.warning("图片生成失败(可重试): chapter=%s, %s", chapter_id, failure_msg)
current_item["updated_at"] = datetime.now(timezone.utc).isoformat()
images[index] = current_item
chapter.images = images
db.commit()
if failures:
if retryable_failures:
raise RuntimeError(
f"章节补图存在失败项: chapter={chapter_id}, failures={'; '.join(failures)}"
f"章节补图存在可重试失败项: chapter={chapter_id}, failures={'; '.join(retryable_failures)}"
)
if permanent_failures:
raise PermanentImageGenerationError(
f"章节补图存在不可重试失败项: chapter={chapter_id}, failures={'; '.join(permanent_failures)}"
)
return {"status": "success"}
except PermanentImageGenerationError as exc:
logger.error("章节补图任务失败(不重试): chapter=%s, error=%s", chapter_id, exc)
raise
except Exception as exc:
logger.error("章节补图任务失败: chapter=%s, error=%s", chapter_id, exc)
raise self.retry(exc=exc)