完善附件管理与生成任务跟踪

This commit is contained in:
zwt13703
2026-07-02 18:26:50 +08:00
parent d3530ab0b7
commit 401e8cf57b
16 changed files with 873 additions and 203 deletions
+24 -4
View File
@@ -1,6 +1,8 @@
import csv
import io
import json
import subprocess
import tempfile
from pathlib import Path
import pandas as pd
@@ -38,7 +40,10 @@ def _summarize_csv(content: bytes) -> str:
def _summarize_excel(content: bytes, suffix: str) -> str:
excel_buffer = io.BytesIO(content)
sheet_map = pd.read_excel(excel_buffer, sheet_name=None) if suffix == ".xlsx" else pd.read_excel(excel_buffer, sheet_name=None, engine="xlrd")
if suffix in {".xlsx", ".xlsm"}:
sheet_map = pd.read_excel(excel_buffer, sheet_name=None)
else:
sheet_map = pd.read_excel(excel_buffer, sheet_name=None, engine="xlrd")
parts: list[str] = []
for sheet_name, dataframe in list(sheet_map.items())[:5]:
preview = dataframe.head(10).fillna("").astype(str)
@@ -47,6 +52,21 @@ def _summarize_excel(content: bytes, suffix: str) -> str:
return "\n".join(parts)[:5000]
def _summarize_doc(content: bytes) -> str:
with tempfile.NamedTemporaryFile(suffix=".doc") as temp_file:
temp_file.write(content)
temp_file.flush()
result = subprocess.run(
["textutil", "-convert", "txt", "-stdout", temp_file.name],
capture_output=True,
check=False,
)
if result.returncode != 0:
stderr = result.stderr.decode("utf-8", errors="ignore").strip()
return f"旧版 Word 文件解析失败:{stderr or 'textutil 无法提取正文'}"
return _decode_text(result.stdout)[:4000]
def _summarize_pdf(content: bytes) -> str:
if PdfReader is None:
return "当前环境未安装 PDF 文本解析依赖,无法提取 PDF 正文。"
@@ -68,18 +88,18 @@ def summarize_file_bytes(file_name: str, content: bytes) -> str:
if suffix == ".docx":
return _summarize_docx(content)
if suffix == ".doc":
return "当前暂不支持直接解析 .doc 旧版 Word 文件正文,建议先另存为 .docx 后再上传。"
return _summarize_doc(content)
if suffix == ".pdf":
return _summarize_pdf(content)
return f"暂不支持解析该文件内容:{file_name}"
def summarize_minio_files(file_paths: list[str]) -> list[dict]:
def summarize_minio_files(file_paths: list[str], file_name_mapping: dict[str, str] | None = None) -> list[dict]:
summaries: list[dict] = []
for file_path in file_paths:
bucket, object_name = split_bucket_path(file_path)
content = download_object_bytes(bucket, object_name)
file_name = Path(object_name).name
file_name = (file_name_mapping or {}).get(file_path) or Path(object_name).name
summaries.append(
{
"file_name": file_name,
+23 -1
View File
@@ -12,6 +12,7 @@ from models.generation_log import GenerationLog
from models.paragraph import Paragraph
from models.template import Template
from services.ai_service import call_ai
from services.file_summary import summarize_minio_files
generation_progress: dict[int, dict] = {}
generation_cancel_flags: dict[int, bool] = {}
@@ -90,6 +91,13 @@ async def run_generation(document_id: int, template_id: int):
)
paragraphs = result.scalars().all()
total = len(paragraphs)
request_payload = {}
if document.request_payload_json:
try:
request_payload = json.loads(document.request_payload_json)
except Exception:
request_payload = {}
file_map = request_payload.get("file_map", {}) if isinstance(request_payload, dict) else {}
update_progress(document_id, status="generating", total=total, done=0, percent=0, message="开始生成...")
done_count = 0
@@ -114,11 +122,25 @@ async def run_generation(document_id: int, template_id: int):
model = await get_effective_model(paragraph)
model_id = model.id if model is not None else paragraph.model_id
try:
selected_file_paths = file_map.get(str(paragraph.id), [])
file_summaries = []
if selected_file_paths:
file_name_mapping = {}
for paragraph_item in request_payload.get("paragraphs", []):
for selected_file in paragraph_item.get("selected_files", []):
file_path = selected_file.get("file_path")
if file_path in selected_file_paths:
file_name_mapping[file_path] = selected_file.get("file_name")
file_summaries = await asyncio.to_thread(
summarize_minio_files,
selected_file_paths,
file_name_mapping,
)
if model is None:
content = build_mock_content(paragraph)
else:
setattr(paragraph, "enable_reasoning", bool(model.enable_reasoning))
result_data = await call_ai(paragraph, model)
result_data = await call_ai(paragraph, model, file_summaries)
content = result_data.content
status = "success"
error_message = ""
+4
View File
@@ -68,3 +68,7 @@ def upload_bytes(
len(content),
content_type=content_type,
)
def delete_object(bucket: str, object_name: str):
minio_client.remove_object(bucket, object_name)