From 08dcb62834156ca113e36f6b7337ae17e08afc90 Mon Sep 17 00:00:00 2001 From: bruce Date: Thu, 4 Jun 2026 02:44:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A1=A5=E9=BD=90Word=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=8A=A5=E5=91=8A=E7=BB=93=E6=9E=84=E5=8C=96=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chat/export_service.py | 83 +++++++++++++++++++++++++++++++++---- tests/test_chat.py | 5 +++ 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/apps/chat/export_service.py b/apps/chat/export_service.py index 884f2b5..f24e0ab 100644 --- a/apps/chat/export_service.py +++ b/apps/chat/export_service.py @@ -6,6 +6,7 @@ from xml.sax.saxutils import escape import zipfile from django.conf import settings +from django.utils import timezone from agent_core.governance import load_governance_config from apps.documents.services import create_export_record @@ -31,6 +32,8 @@ def generate_registration_export(*, batch, conversation, upstream_summary: dict absolute_path.parent.mkdir(parents=True, exist_ok=True) fillable_items = _build_fillable_items(batch, conversation) + filled_fields = _build_filled_fields(fillable_items) + blocked_fields = _build_blocked_fields(blocked_items) _write_minimal_docx( absolute_path, product_name=batch.product_name or conversation.product_name or "未命名资料包", @@ -59,11 +62,13 @@ def generate_registration_export(*, batch, conversation, upstream_summary: dict "formal_export_status": "completed" if can_export_formally else "blocked", "can_export_formally": can_export_formally, "fillable_items": fillable_items, + "filled_fields": filled_fields, "fillable_field_count": len(fillable_items), - "filled_field_count": len(fillable_items), + "filled_field_count": len(filled_fields), "blocked_items": blocked_items, - "blocked_field_count": len(blocked_items), - "manual_review_field_count": len(blocked_items), + "blocked_fields": blocked_fields, + "blocked_field_count": len(blocked_fields), + "manual_review_field_count": len(blocked_fields), "layout_check_status": "passed", "download_url": download_url, "output_file": { @@ -71,6 +76,8 @@ def generate_registration_export(*, batch, conversation, upstream_summary: dict "relative_path": relative_path.as_posix(), "absolute_path": str(absolute_path), "export_mode": export_mode, + "output_version": export_mode, + "generated_at": timezone.now().isoformat(), }, } create_export_record( @@ -142,11 +149,71 @@ def _can_export_formally(upstream_summary: dict, blocked_items: list[str]) -> bo def _build_fillable_items(batch, conversation) -> list[dict]: return [ - {"placeholder": "{{ product_name }}", "field_name": "产品名称", "field_value": batch.product_name}, - {"placeholder": "{{ batch_id }}", "field_name": "批次号", "field_value": batch.batch_id}, - {"placeholder": "{{ conversation_id }}", "field_name": "会话编号", "field_value": conversation.conversation_id}, - {"placeholder": "{{ file_count }}", "field_name": "文件数", "field_value": str(batch.file_count)}, - {"placeholder": "{{ page_count }}", "field_name": "页数", "field_value": str(batch.page_count)}, + { + "placeholder": "{{ product_name }}", + "field_name": "产品名称", + "field_value": batch.product_name, + "source": "资料包主信息", + "fill_status": "filled", + "required": True, + }, + { + "placeholder": "{{ batch_id }}", + "field_name": "批次号", + "field_value": batch.batch_id, + "source": "资料包主信息", + "fill_status": "filled", + "required": True, + }, + { + "placeholder": "{{ conversation_id }}", + "field_name": "会话编号", + "field_value": conversation.conversation_id, + "source": "会话主信息", + "fill_status": "filled", + "required": True, + }, + { + "placeholder": "{{ file_count }}", + "field_name": "文件数", + "field_value": str(batch.file_count), + "source": "资料包统计", + "fill_status": "filled", + "required": False, + }, + { + "placeholder": "{{ page_count }}", + "field_name": "页数", + "field_value": str(batch.page_count), + "source": "资料包统计", + "fill_status": "filled", + "required": False, + }, + ] + + +def _build_filled_fields(fillable_items: list[dict]) -> list[dict]: + return [ + { + "placeholder": item["placeholder"], + "field_name": item["field_name"], + "field_value": item["field_value"], + "source": item["source"], + "fill_status": item["fill_status"], + "required": item["required"], + } + for item in fillable_items + ] + + +def _build_blocked_fields(blocked_items: list[str]) -> list[dict]: + return [ + { + "field_name": item, + "block_reason": "待人工复核", + "risk_source": "registration_risk_report", + } + for item in blocked_items ] diff --git a/tests/test_chat.py b/tests/test_chat.py index a9cfe6f..b802785 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -497,6 +497,11 @@ def test_generate_registration_export_creates_real_docx_file(db, tmp_path, setti assert report["output_type"] == "registration_word_export_report" assert report["can_export_formally"] is False assert report["export_status"] == "draft_only" + assert report["filled_fields"][0]["field_name"] == "产品名称" + assert report["filled_fields"][0]["fill_status"] == "filled" + assert report["blocked_fields"][0]["block_reason"] == "待人工复核" + assert report["output_file"]["output_version"] == "draft" + assert report["output_file"]["generated_at"] assert report["download_url"].startswith("/media/exports/") assert export_path.exists() with zipfile.ZipFile(export_path) as archive: