156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
import asyncio
|
|
import json
|
|
import os
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from fastapi.responses import PlainTextResponse, RedirectResponse
|
|
from sqlalchemy import 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 models.template_block import TemplateBlock
|
|
from services.document_export import export_document_bytes, export_document_bytes_from_blocks
|
|
from services.minio_client import (
|
|
download_object_bytes,
|
|
get_presigned_url,
|
|
split_bucket_path,
|
|
upload_bytes,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/{document_id}/docx")
|
|
async def export_docx(document_id: int, db: AsyncSession = Depends(get_db)):
|
|
document = await db.get(Document, document_id)
|
|
if document is None:
|
|
raise HTTPException(status_code=404, detail="生成记录不存在")
|
|
|
|
template = await db.get(Template, document.template_id)
|
|
if template is None:
|
|
raise HTTPException(status_code=404, detail="模板不存在")
|
|
|
|
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())
|
|
)
|
|
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": []},
|
|
}
|
|
)
|
|
|
|
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"
|
|
await asyncio.to_thread(
|
|
upload_bytes,
|
|
settings.MINIO_BUCKET_OUTPUTS,
|
|
object_name,
|
|
exported_bytes,
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
)
|
|
|
|
document.file_path = f"{settings.MINIO_BUCKET_OUTPUTS}/{object_name}"
|
|
await db.commit()
|
|
return RedirectResponse(
|
|
url=get_presigned_url(settings.MINIO_BUCKET_OUTPUTS, object_name),
|
|
status_code=307,
|
|
)
|
|
|
|
|
|
@router.get("/{document_id}/docx-v2")
|
|
async def export_docx_v2(document_id: int, db: AsyncSession = Depends(get_db)):
|
|
"""基于块结构的 Word 导出。"""
|
|
document = await db.get(Document, document_id)
|
|
if document is None:
|
|
raise HTTPException(status_code=404, detail="生成记录不存在")
|
|
|
|
template = await db.get(Template, document.template_id)
|
|
if template is None:
|
|
raise HTTPException(status_code=404, detail="模板不存在")
|
|
|
|
template_bucket, template_object = split_bucket_path(template.file_path)
|
|
template_bytes = await asyncio.to_thread(download_object_bytes, template_bucket, template_object)
|
|
|
|
# 查询块和生成日志
|
|
blocks_result = await db.execute(
|
|
select(TemplateBlock)
|
|
.where(TemplateBlock.template_id == document.template_id)
|
|
.order_by(TemplateBlock.sort_index.asc(), TemplateBlock.id.asc())
|
|
)
|
|
blocks = blocks_result.scalars().all()
|
|
|
|
logs_result = await db.execute(
|
|
select(GenerationLog)
|
|
.where(GenerationLog.document_id == document_id)
|
|
)
|
|
logs = logs_result.scalars().all()
|
|
|
|
block_dicts = [
|
|
{
|
|
"id": b.id,
|
|
"sort_index": b.sort_index,
|
|
"anchor_start_index": b.anchor_start_index,
|
|
"anchor_end_index": b.anchor_end_index,
|
|
"edit_mode": b.edit_mode,
|
|
"block_type": b.block_type,
|
|
"title": b.title,
|
|
}
|
|
for b in blocks
|
|
]
|
|
|
|
log_dicts = [
|
|
{
|
|
"id": log.id,
|
|
"block_id": log.block_id,
|
|
"status": log.status,
|
|
"content": json.loads(log.content) if log.content else {"content": []},
|
|
}
|
|
for log in logs
|
|
]
|
|
|
|
exported_bytes = await asyncio.to_thread(
|
|
export_document_bytes_from_blocks, template_bytes, block_dicts, log_dicts
|
|
)
|
|
|
|
object_name = f"{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4().hex}.docx"
|
|
await asyncio.to_thread(
|
|
upload_bytes,
|
|
settings.MINIO_BUCKET_OUTPUTS,
|
|
object_name,
|
|
exported_bytes,
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
)
|
|
|
|
document.file_path = f"{settings.MINIO_BUCKET_OUTPUTS}/{object_name}"
|
|
await db.commit()
|
|
return RedirectResponse(
|
|
url=get_presigned_url(settings.MINIO_BUCKET_OUTPUTS, object_name),
|
|
status_code=307,
|
|
)
|
|
|
|
|
|
@router.get("/{document_id}/pdf")
|
|
async def export_pdf(document_id: int):
|
|
return PlainTextResponse(
|
|
f"文档 {document_id} 的 PDF 导出功能正在开发中,当前版本请先使用预览页查看结果。",
|
|
media_type="text/plain; charset=utf-8",
|
|
)
|