支持段落删除与移动排序,修复导出残留与外键约束

- 前端:段落列表/配置区/手动编辑区新增上移、下移、删除按钮,hover 显示
- 前端:移动和删除操作后自动保存到后端,修正 canDeleteBlock 判定逻辑
- 后端:保存段落时级联删除关联的 generation_logs 再删段落
- 后端:导出时清理未被引用的标题段落及其内容,避免已删段落残留在 Word 中
- 后端:删除模板时级联清理 generation_logs/documents/paragraphs
This commit is contained in:
zwt13703
2026-07-03 17:13:02 +08:00
parent a796301ae8
commit 1369d87afb
3 changed files with 208 additions and 7 deletions
+26 -2
View File
@@ -6,11 +6,13 @@ from datetime import datetime
from io import BytesIO
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from sqlalchemy import func, select
from sqlalchemy import delete, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from config import settings
from database import get_db
from models.document import Document
from models.generation_log import GenerationLog
from models.paragraph import Paragraph
from models.template import Template
from schemas.schemas import Response, TemplateSave
@@ -194,8 +196,14 @@ async def save_template_paragraphs(
paragraph_map = {item.id: item for item in existing_paragraphs}
incoming_ids = {config.id for config in body.paragraphs if config.id}
print(f"[SAVE] template_id={template_id}, incoming_ids={incoming_ids}, existing_ids={[p.id for p in existing_paragraphs]}")
for paragraph in existing_paragraphs:
if paragraph.id not in incoming_ids:
print(f"[SAVE] Deleting paragraph id={paragraph.id} title={paragraph.title}")
await db.execute(
delete(GenerationLog).where(GenerationLog.paragraph_id == paragraph.id)
)
await db.delete(paragraph)
for index, config in enumerate(body.paragraphs, start=1):
@@ -229,7 +237,23 @@ async def delete_template(template_id: int, db: AsyncSession = Depends(get_db)):
raise HTTPException(status_code=404, detail="模板不存在")
result = await db.execute(select(Paragraph).where(Paragraph.template_id == template_id))
for paragraph in result.scalars().all():
paragraphs_to_delete = result.scalars().all()
paragraph_ids = [p.id for p in paragraphs_to_delete]
doc_result = await db.execute(select(Document).where(Document.template_id == template_id))
documents_to_delete = doc_result.scalars().all()
if paragraph_ids:
await db.execute(
delete(GenerationLog).where(GenerationLog.paragraph_id.in_(paragraph_ids))
)
for document in documents_to_delete:
await db.execute(
delete(GenerationLog).where(GenerationLog.document_id == document.id)
)
await db.delete(document)
for paragraph in paragraphs_to_delete:
await db.delete(paragraph)
file_path = template.file_path or ""