模板在线编辑与导出链路重构
This commit is contained in:
+81
-13
@@ -15,6 +15,7 @@ from models.document import Document
|
||||
from models.generation_log import GenerationLog
|
||||
from models.paragraph import Paragraph
|
||||
from models.template import Template
|
||||
from models.template_block import TemplateBlock
|
||||
from services.document_export import export_document_bytes
|
||||
from services.minio_client import (
|
||||
download_object_bytes,
|
||||
@@ -26,6 +27,46 @@ from services.minio_client import (
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _block_text_content(block: TemplateBlock) -> str:
|
||||
try:
|
||||
payload = json.loads(block.content_json or "{}")
|
||||
except Exception:
|
||||
payload = {}
|
||||
return payload.get("text") or block.default_value or ""
|
||||
|
||||
|
||||
def _block_table_content(block: TemplateBlock) -> dict:
|
||||
try:
|
||||
payload = json.loads(block.content_json or "{}")
|
||||
except Exception:
|
||||
payload = {}
|
||||
return payload.get("table") or {}
|
||||
|
||||
|
||||
def _build_block_export_content(block: TemplateBlock) -> dict:
|
||||
if block.block_type == "table":
|
||||
table_data = _block_table_content(block)
|
||||
matrix = table_data.get("data") or []
|
||||
headers = matrix[0] if matrix else []
|
||||
rows = matrix[1:] if len(matrix) > 1 else []
|
||||
return {"content": [{"type": "table", "headers": headers, "rows": rows}]}
|
||||
return {"content": [{"type": "text", "text": _block_text_content(block)}]}
|
||||
|
||||
|
||||
def _resolve_block_write_modes(blocks: list[TemplateBlock]) -> list[str]:
|
||||
modes: list[str] = []
|
||||
anchor_counter: dict[str, int] = {}
|
||||
for block in blocks:
|
||||
if block.block_type == "heading":
|
||||
modes.append("replace_heading_only")
|
||||
continue
|
||||
anchor = (block.anchor_ref or block.title or "").strip()
|
||||
seen = anchor_counter.get(anchor, 0)
|
||||
modes.append("replace_section" if seen == 0 else "append_after_heading")
|
||||
anchor_counter[anchor] = seen + 1
|
||||
return modes
|
||||
|
||||
|
||||
@router.get("/{document_id}/docx")
|
||||
async def export_docx(document_id: int, db: AsyncSession = Depends(get_db)):
|
||||
document = await db.get(Document, document_id)
|
||||
@@ -39,22 +80,49 @@ async def export_docx(document_id: int, db: AsyncSession = Depends(get_db)):
|
||||
template_bucket, template_object = split_bucket_path(template.file_path)
|
||||
template_bytes = await asyncio.to_thread(download_object_bytes, template_bucket, template_object)
|
||||
|
||||
result = await db.execute(
|
||||
select(GenerationLog, Paragraph)
|
||||
.join(Paragraph, Paragraph.id == GenerationLog.paragraph_id)
|
||||
.where(GenerationLog.document_id == document_id)
|
||||
.order_by(Paragraph.sort_index.asc(), Paragraph.id.asc())
|
||||
block_result = await db.execute(
|
||||
select(TemplateBlock)
|
||||
.where(TemplateBlock.template_id == template.id)
|
||||
.order_by(TemplateBlock.sort_index.asc(), TemplateBlock.id.asc())
|
||||
)
|
||||
blocks = block_result.scalars().all()
|
||||
|
||||
log_result = await db.execute(
|
||||
select(GenerationLog).where(GenerationLog.document_id == document_id)
|
||||
)
|
||||
generation_logs = log_result.scalars().all()
|
||||
log_map = {item.paragraph_id: json.loads(item.content) if item.content else {"content": []} for item in generation_logs}
|
||||
|
||||
logs = []
|
||||
for log, paragraph in result.all():
|
||||
logs.append(
|
||||
{
|
||||
"anchor_title": paragraph.anchor_title or paragraph.title,
|
||||
"title": paragraph.title,
|
||||
"write_mode": paragraph.write_mode,
|
||||
"content": json.loads(log.content) if log.content else {"content": []},
|
||||
}
|
||||
if blocks:
|
||||
write_modes = _resolve_block_write_modes(blocks)
|
||||
for block, write_mode in zip(blocks, write_modes):
|
||||
generated_content = log_map.get(block.source_paragraph_id) if block.source_paragraph_id else None
|
||||
content = generated_content if (block.edit_mode == "ai" or block.block_type == "ai_slot") and generated_content else _build_block_export_content(block)
|
||||
logs.append(
|
||||
{
|
||||
"anchor_title": block.anchor_ref or block.title,
|
||||
"title": block.title,
|
||||
"write_mode": write_mode,
|
||||
"content": content,
|
||||
}
|
||||
)
|
||||
else:
|
||||
result = await db.execute(
|
||||
select(GenerationLog, Paragraph)
|
||||
.join(Paragraph, Paragraph.id == GenerationLog.paragraph_id)
|
||||
.where(GenerationLog.document_id == document_id)
|
||||
.order_by(Paragraph.sort_index.asc(), Paragraph.id.asc())
|
||||
)
|
||||
for log, paragraph in result.all():
|
||||
logs.append(
|
||||
{
|
||||
"anchor_title": paragraph.anchor_title or paragraph.title,
|
||||
"title": paragraph.title,
|
||||
"write_mode": paragraph.write_mode,
|
||||
"content": json.loads(log.content) if log.content else {"content": []},
|
||||
}
|
||||
)
|
||||
|
||||
exported_bytes = await asyncio.to_thread(export_document_bytes, template_bytes, logs)
|
||||
object_name = f"{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4().hex}.docx"
|
||||
|
||||
Reference in New Issue
Block a user