| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- """export.py — POST /api/v1/export/doc"""
- from pathlib import Path
- from fastapi import APIRouter, Depends
- from sqlalchemy.ext.asyncio import AsyncSession
- from app.api.v1 import ok
- from app.config import settings
- from app.core.dependencies import get_db
- from app.core.exceptions import DocumentNotFoundError, ExportError
- from app.schemas.export import ExportDocRequest, ExportDocResponse
- from app.services.document_service import DocumentService
- from app.services.export_record_service import ExportRecordService
- from app.services.export_service import (
- build_style_map,
- load_style_file,
- markdown_to_docx_bytes,
- _make_filename,
- )
- from app.services.storage_monitor import check_quota
- router = APIRouter(tags=["Export"])
- @router.post("/export/doc", summary="导出 .doc 文件")
- async def export_document(
- body: ExportDocRequest,
- db: AsyncSession = Depends(get_db),
- ) -> dict:
- doc_svc = DocumentService(db)
- rec_svc = ExportRecordService(db)
- # 1. 读取文档
- doc = await doc_svc.get_document(body.document_id)
- user_id = doc.created_by or "default-user"
- # 2. 检查是否有最新记录可复用
- latest = await rec_svc.get_latest_record(body.document_id)
- if latest and doc.updated_at <= latest.created_at:
- # 文档未更新,直接复用已有记录
- warning = check_quota(user_id)
- return ok(ExportDocResponse(
- record_id=latest.id,
- download_url=latest.download_url,
- file_name=latest.file_name,
- style_id=latest.style_id,
- warning=warning,
- ).model_dump(by_alias=True))
- # 3. 加载样式,生成 .doc 字节流
- style_data = load_style_file(body.style_id)
- style_map = build_style_map(style_data)
- actual_style_id = body.style_id or "default"
- try:
- doc_bytes = markdown_to_docx_bytes(doc.content, style_map, style_data)
- except Exception as exc:
- raise ExportError(f"Markdown 转换失败: {exc}") from exc
- # 4. 写入文件:./tmp/{user_id}/{YYYY-MM-DD}/{name}.doc
- from datetime import date
- file_stem = _make_filename(doc.content)
- file_name = f"{file_stem}.doc"
- today = date.today().strftime("%Y-%m-%d")
- user_dir = Path(settings.temp_dir) / user_id / today
- user_dir.mkdir(parents=True, exist_ok=True)
- file_path = user_dir / file_name
- try:
- file_path.write_bytes(doc_bytes)
- except OSError as exc:
- raise ExportError(f"文件写入失败: {exc}") from exc
- file_size = file_path.stat().st_size
- # 5. 写入数据库记录(先占位 download_url,再回写)
- record = await rec_svc.create_record(
- user_id=user_id,
- file_name=file_name,
- file_path=str(file_path),
- file_size=file_size,
- download_url="",
- document_id=doc.id,
- style_id=actual_style_id,
- )
- download_url = (
- f"{settings.base_url.rstrip('/')}/api/v1/export/records/{record.id}/download"
- f"?userId={user_id}"
- )
- record.download_url = download_url
- await db.commit()
- # 6. 配额检查
- warning = check_quota(user_id)
- return ok(ExportDocResponse(
- record_id=record.id,
- download_url=download_url,
- file_name=file_name,
- style_id=actual_style_id,
- warning=warning,
- ).model_dump(by_alias=True))
|