把“章节正文 + 图片”从 chapters 单表/JSON 结构,重构为“章节 chapter + 段落 section + 图片 memoir_images 独立表”的新数据模型,同时联动修改接口、PDF 导出、异步任务、迁移脚本、测试,以及修复 Android 端聊天列表显示问题。 (#9)
* refactor: 表结构重构,新增段落section和图片image新表 * fix: fix android app import error * refactor: 重构文件名 * fix: 优化提示词 * fix: 消息气泡显示位置异常问题 --------- Co-authored-by: yangshilin <2157598560@qq.com>
This commit is contained in:
@@ -52,3 +52,33 @@ def build_initial_image_assets(
|
||||
}
|
||||
for item in placeholders
|
||||
]
|
||||
|
||||
|
||||
def split_narrative_to_sections(narrative: str) -> list[dict[str, Any]]:
|
||||
"""
|
||||
将带 {{IMAGE:...}} 占位符的正文按占位符拆成多段。
|
||||
返回 list[dict],每项含:
|
||||
- content: 本段纯文本(不含占位符)
|
||||
- placeholder_info: 本段后的配图占位信息,或 None(最后一段无图)
|
||||
"""
|
||||
if not (narrative or narrative.strip()):
|
||||
return []
|
||||
placeholders = parse_image_placeholders(narrative, max_images=None)
|
||||
sections: list[dict[str, Any]] = []
|
||||
for i in range(len(placeholders) + 1):
|
||||
if i == 0:
|
||||
start = 0
|
||||
else:
|
||||
prev = placeholders[i - 1]
|
||||
start = prev["start_offset"] + len(prev["placeholder"])
|
||||
if i < len(placeholders):
|
||||
end = placeholders[i]["start_offset"]
|
||||
placeholder_info = placeholders[i]
|
||||
else:
|
||||
end = len(narrative)
|
||||
placeholder_info = None
|
||||
content = narrative[start:end]
|
||||
if isinstance(content, str):
|
||||
content = content.strip()
|
||||
sections.append({"content": content or "", "placeholder_info": placeholder_info})
|
||||
return sections
|
||||
|
||||
70
api/services/memoir_images/serializers.py
Normal file
70
api/services/memoir_images/serializers.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
MemoirImage 模型与 API 用 dict 的互转(与 schema.normalize_image_asset 字段一致)。
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from database.models import MemoirImage
|
||||
|
||||
|
||||
def _parse_optional_datetime(s: str | None):
|
||||
if not s:
|
||||
return None
|
||||
try:
|
||||
return datetime.fromisoformat(s.replace("Z", "+00:00"))
|
||||
except (ValueError, AttributeError):
|
||||
return None
|
||||
|
||||
|
||||
def memoir_image_to_dict(m: MemoirImage | None) -> dict[str, Any] | None:
|
||||
"""将 MemoirImage 转为前端/API 使用的单条图片 dict。"""
|
||||
if not m:
|
||||
return None
|
||||
d: dict[str, Any] = {
|
||||
"placeholder": m.placeholder or "",
|
||||
"description": m.description or "",
|
||||
"index": m.order_index,
|
||||
"status": m.status or "pending",
|
||||
"prompt": m.prompt,
|
||||
"url": m.url,
|
||||
"storage_key": m.storage_key,
|
||||
"provider": m.provider,
|
||||
"style": m.style,
|
||||
"size": m.size,
|
||||
"error": m.error,
|
||||
"retryable": m.retryable,
|
||||
"created_at": m.created_at.isoformat() if m.created_at else None,
|
||||
"updated_at": m.updated_at.isoformat() if m.updated_at else None,
|
||||
}
|
||||
return d
|
||||
|
||||
|
||||
def image_dict_to_row_kwargs(d: dict[str, Any] | None) -> dict[str, Any]:
|
||||
"""从单条图片 dict 提取可写入 MemoirImage 的字段(不含 id/chapter_id/section_id/order_index)。"""
|
||||
if not d or not isinstance(d, dict):
|
||||
return {}
|
||||
created = d.get("created_at")
|
||||
updated = d.get("updated_at")
|
||||
if isinstance(created, str):
|
||||
created = _parse_optional_datetime(created)
|
||||
elif not isinstance(created, datetime):
|
||||
created = None
|
||||
if isinstance(updated, str):
|
||||
updated = _parse_optional_datetime(updated)
|
||||
elif not isinstance(updated, datetime):
|
||||
updated = None
|
||||
return {
|
||||
"placeholder": d.get("placeholder") or None,
|
||||
"description": d.get("description") or None,
|
||||
"status": (d.get("status") or "pending").strip() or "pending",
|
||||
"prompt": d.get("prompt") or None,
|
||||
"url": d.get("url") or None,
|
||||
"storage_key": d.get("storage_key") or None,
|
||||
"provider": d.get("provider") or None,
|
||||
"style": d.get("style") or None,
|
||||
"size": d.get("size") or None,
|
||||
"error": d.get("error") or None,
|
||||
"retryable": d.get("retryable") if d.get("retryable") is not None else None,
|
||||
"created_at": created,
|
||||
"updated_at": updated,
|
||||
}
|
||||
Reference in New Issue
Block a user