diff --git a/apps/chat/views.py b/apps/chat/views.py index 6b55f97..9b0e29d 100644 --- a/apps/chat/views.py +++ b/apps/chat/views.py @@ -85,6 +85,7 @@ def detail(request, conversation_id: str): active_node = "risk" conversation.refresh_from_db() workspace_summary = _build_workspace_summary(conversation, batch) + export_card = _build_export_card(result, conversation) return render( request, @@ -103,6 +104,7 @@ def detail(request, conversation_id: str): "active_node": active_node, "workspace_summary": workspace_summary, "upload_form": upload_form, + "export_card": export_card, }, ) @@ -240,6 +242,29 @@ def _build_workspace_summary(conversation: Conversation, batch: SubmissionBatch } +def _build_export_card(result: AgentResult | None, conversation: Conversation) -> dict: + """ + 统一组装 Word 导出能力卡上下文。 + + 优先使用本次执行结果;若本次未执行,则回退到会话最新摘要。 + """ + structured_output = {} + if result and result.structured_output: + structured_output = result.structured_output + else: + structured_output = (conversation.latest_summary or {}).get("structured_output") or {} + if structured_output.get("output_type") != "registration_word_export_report": + return {} + return { + "template_name": structured_output.get("template_name", ""), + "template_version": structured_output.get("template_version", ""), + "export_status": structured_output.get("export_status", ""), + "filled_fields": structured_output.get("filled_fields") or [], + "blocked_fields": structured_output.get("blocked_fields") or [], + "download_url": structured_output.get("download_url", ""), + } + + def _apply_agent_result_to_conversation(conversation: Conversation, result: AgentResult) -> None: conversation.task_status = result.status if result.node_results: diff --git a/templates/chat/index.html b/templates/chat/index.html index a4ae363..d898d43 100644 --- a/templates/chat/index.html +++ b/templates/chat/index.html @@ -88,7 +88,68 @@

节点式结果

- {% if result and result.structured_output %} + {% if export_card %} +
+
+ Word 导出能力卡 +
模板:{{ export_card.template_name }} / {{ export_card.template_version|default:"-" }}
+
当前导出状态:{{ export_card.export_status|default:"-" }}
+
+ +
+

回填字段表

+
+ + + + + + + + + + + + + {% for item in export_card.filled_fields %} + + + + + + + + + {% empty %} + + {% endfor %} + +
占位符字段名字段值来源回填状态是否必填
{{ item.placeholder }}{{ item.field_name }}{{ item.field_value }}{{ item.source }}{{ item.fill_status }}{{ item.required|yesno:"是,否" }}
当前暂无回填字段。
+
+
+ +
+

拦截项区

+
    + {% for item in export_card.blocked_fields %} +
  • + {{ item.field_name }} +
    拦截原因:{{ item.block_reason }}
    +
    来源:{{ item.risk_source }}
    +
  • + {% empty %} +
  • 当前无拦截项。
  • + {% endfor %} +
+
+ + +
+ {% elif result and result.structured_output %} {% for key, value in result.structured_output.items %} diff --git a/tests/test_chat.py b/tests/test_chat.py index b802785..61888c9 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -433,6 +433,60 @@ def test_chat_page_uses_structured_formal_export_flag_when_node_status_is_comple assert "/downloads/review-only.docx" in content +def test_chat_page_shows_word_export_field_table_and_governance_entries(client, db): + batch, conversation = _create_conversation_with_batch() + conversation.node_results = [ + {"label": "资料包导入", "status": "已完成"}, + {"label": "目录汇总", "status": "已完成"}, + {"label": "法规完整性检查", "status": "已完成"}, + {"label": "字段抽取", "status": "已完成"}, + {"label": "一致性核查", "status": "已完成"}, + {"label": "风险预警", "status": "已阻断"}, + {"label": "Word 回填导出", "status": "待复核"}, + {"label": "飞书通知", "status": "待处理"}, + ] + conversation.latest_summary = { + "structured_output": { + "output_type": "registration_word_export_report", + "template_name": "注册证导出模板", + "template_version": "V1.0", + "export_status": "draft_only", + "filled_fields": [ + { + "placeholder": "{{ product_name }}", + "field_name": "产品名称", + "field_value": "新型冠状病毒 2019-nCoV 核酸检测试剂盒", + "source": "资料包主信息", + "fill_status": "filled", + "required": True, + } + ], + "blocked_fields": [ + { + "field_name": "产品名称跨文档不一致", + "block_reason": "待人工复核", + "risk_source": "registration_risk_report", + } + ], + "download_url": "/media/exports/20260604/SUB-20260604-001-draft.docx", + } + } + conversation.save(update_fields=["node_results", "latest_summary", "updated_at"]) + + response = client.get(reverse("chat:detail", args=[conversation.conversation_id])) + + content = response.content.decode("utf-8") + assert response.status_code == 200 + assert "Word 导出能力卡" in content + assert "回填字段表" in content + assert "产品名称" in content + assert "资料包主信息" in content + assert "拦截项区" in content + assert "产品名称跨文档不一致" in content + assert "维护 Word 模板" in content + assert "维护字段映射" in content + + def test_chat_upload_keeps_existing_conversation_binding_and_adds_documents(client, db): batch, conversation = _create_conversation_with_batch() existing_document = UploadedDocument.objects.create(