From 4914ee3a75d208387dca5e8472b5cbd166255c50 Mon Sep 17 00:00:00 2001 From: bruce Date: Thu, 4 Jun 2026 01:42:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=B6=E5=8F=A3=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E6=B2=BB=E7=90=86=E9=85=8D=E7=BD=AE=E4=B8=8E=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/platform_ui/services.py | 135 +++++++++++++++------- config/settings.py | 3 + configs/governance.yaml | 45 ++++++++ templates/platform_ui/knowledge_base.html | 37 ++++++ tests/test_platform_ui.py | 70 +++++++++++ 5 files changed, 250 insertions(+), 40 deletions(-) create mode 100644 configs/governance.yaml diff --git a/apps/platform_ui/services.py b/apps/platform_ui/services.py index 5b92352..d5daf01 100644 --- a/apps/platform_ui/services.py +++ b/apps/platform_ui/services.py @@ -1,3 +1,93 @@ +from pathlib import Path + +import yaml +from django.conf import settings + + +def _governance_defaults() -> dict: + return { + "owner_mappings": [ + { + "owner_role": "注册资料负责人", + "owner_name": "张三", + "department": "注册事务部", + "chapter_scope": "CH1", + "risk_scope": "字段冲突 / 缺失项", + "feishu_user_id": "ou_demo_1", + "feishu_open_id": "on_demo_1", + "feishu_name": "张三", + "notify_enabled": "是", + }, + { + "owner_role": "注册申报负责人", + "owner_name": "李四", + "department": "临床注册组", + "chapter_scope": "CH2-CH6", + "risk_scope": "完整性风险 / 导出阻断", + "feishu_user_id": "ou_demo_2", + "feishu_open_id": "on_demo_2", + "feishu_name": "李四", + "notify_enabled": "是", + }, + ], + "feishu_configs": [ + { + "config_name": "注册审核完成通知", + "notify_reason": "task_completed", + "channel": "群机器人", + "message_template": "审核完成摘要 + @处理人", + "status": "启用", + }, + { + "config_name": "注册审核异常通知", + "notify_reason": "task_failed", + "channel": "群机器人", + "message_template": "异常摘要 + @处理人", + "status": "启用", + }, + ], + "template_mappings": [ + { + "template_name": "注册证导出模板", + "output_type": "registration_word_export_report", + "version": "V1.0", + "placeholder_count": 18, + "status": "启用", + "field_mapping_summary": "产品名称 / 注册人 / 适用机型 / 储存条件", + }, + { + "template_name": "风险摘要导出模板", + "output_type": "registration_word_export_report", + "version": "V0.9", + "placeholder_count": 10, + "status": "待校验", + "field_mapping_summary": "风险等级 / 批次号 / 责任人 / 证据摘要", + }, + ], + } + + +def _read_governance_yaml() -> dict: + raw_path = getattr(settings, "GOVERNANCE_CONFIG_PATH", "") + if not raw_path: + return {} + config_path = Path(raw_path) + if not config_path.exists() or not config_path.is_file(): + return {} + with config_path.open("r", encoding="utf-8") as file: + return yaml.safe_load(file) or {} + + +def _load_governance_config() -> dict: + defaults = _governance_defaults() + config = _read_governance_yaml() + for key, default_value in defaults.items(): + configured_value = config.get(key) + if isinstance(default_value, list) and configured_value: + defaults[key] = configured_value + return defaults + + def get_platform_demo_context(): batch = { "name": "2026Q2-呼吸道多联检测试剂注册批次", @@ -151,46 +241,10 @@ def get_platform_demo_context(): "status": "待校订", }, ] - owner_mappings = [ - { - "owner_role": "注册资料负责人", - "owner_name": "张三", - "department": "注册事务部", - "chapter_scope": "CH1", - "risk_scope": "字段冲突 / 缺失项", - "feishu_user_id": "ou_demo_1", - "feishu_open_id": "on_demo_1", - "feishu_name": "张三", - "notify_enabled": "是", - }, - { - "owner_role": "注册申报负责人", - "owner_name": "李四", - "department": "临床注册组", - "chapter_scope": "CH2-CH6", - "risk_scope": "完整性风险 / 导出阻断", - "feishu_user_id": "ou_demo_2", - "feishu_open_id": "on_demo_2", - "feishu_name": "李四", - "notify_enabled": "是", - }, - ] - feishu_configs = [ - { - "config_name": "注册审核完成通知", - "notify_reason": "task_completed", - "channel": "群机器人", - "message_template": "审核完成摘要 + @处理人", - "status": "启用", - }, - { - "config_name": "注册审核异常通知", - "notify_reason": "task_failed", - "channel": "群机器人", - "message_template": "异常摘要 + @处理人", - "status": "启用", - }, - ] + governance_config = _load_governance_config() + owner_mappings = governance_config["owner_mappings"] + feishu_configs = governance_config["feishu_configs"] + template_mappings = governance_config["template_mappings"] mcp_connectors = [ {"name": "飞书任务通知", "kind": "协同办公", "auth": "App Token", "status": "已连接", "sync": "5 分钟前"}, {"name": "法规规则源导入", "kind": "法规服务", "auth": "文件轮询", "status": "待验证", "sync": "今天 08:50"}, @@ -420,6 +474,7 @@ def get_platform_demo_context(): "field_schemas": field_schemas, "owner_mappings": owner_mappings, "feishu_configs": feishu_configs, + "template_mappings": template_mappings, "knowledge_filters": knowledge_filters, "knowledge_form": knowledge_form, "rule_form": rule_form, diff --git a/config/settings.py b/config/settings.py index c790da0..45946ea 100644 --- a/config/settings.py +++ b/config/settings.py @@ -108,6 +108,9 @@ MEDIA_ROOT = Path(os.environ.get("UPLOAD_ROOT", BASE_DIR / "data" / "uploads")) # 配置目录和 Chroma 数据目录都允许外部覆盖,方便复试现场快速切换。 SCENARIO_CONFIG_DIR = Path(os.environ.get("SCENARIO_CONFIG_DIR", BASE_DIR / "configs")) +GOVERNANCE_CONFIG_PATH = Path( + os.environ.get("GOVERNANCE_CONFIG_PATH", BASE_DIR / "configs" / "governance.yaml") +) CHROMA_PATH = Path(os.environ.get("CHROMA_PATH", BASE_DIR / "data" / "chroma")) # LLM 与 Embedding 默认遵循“尽量少配置也能跑”的策略: diff --git a/configs/governance.yaml b/configs/governance.yaml new file mode 100644 index 0000000..c0e6c21 --- /dev/null +++ b/configs/governance.yaml @@ -0,0 +1,45 @@ +owner_mappings: + - owner_role: 注册资料负责人 + owner_name: 张三 + department: 注册事务部 + chapter_scope: CH1 + risk_scope: 字段冲突 / 缺失项 + feishu_user_id: ou_demo_1 + feishu_open_id: on_demo_1 + feishu_name: 张三 + notify_enabled: 是 + - owner_role: 注册申报负责人 + owner_name: 李四 + department: 临床注册组 + chapter_scope: CH2-CH6 + risk_scope: 完整性风险 / 导出阻断 + feishu_user_id: ou_demo_2 + feishu_open_id: on_demo_2 + feishu_name: 李四 + notify_enabled: 是 + +feishu_configs: + - config_name: 注册审核完成通知 + notify_reason: task_completed + channel: 群机器人 + message_template: 审核完成摘要 + @处理人 + status: 启用 + - config_name: 注册审核异常通知 + notify_reason: task_failed + channel: 群机器人 + message_template: 异常摘要 + @处理人 + status: 启用 + +template_mappings: + - template_name: 注册证导出模板 + output_type: registration_word_export_report + version: V1.0 + placeholder_count: 18 + status: 启用 + field_mapping_summary: 产品名称 / 注册人 / 适用机型 / 储存条件 + - template_name: 风险摘要导出模板 + output_type: registration_word_export_report + version: V0.9 + placeholder_count: 10 + status: 待校验 + field_mapping_summary: 风险等级 / 批次号 / 责任人 / 证据摘要 diff --git a/templates/platform_ui/knowledge_base.html b/templates/platform_ui/knowledge_base.html index 2c9d0c7..a6c0792 100644 --- a/templates/platform_ui/knowledge_base.html +++ b/templates/platform_ui/knowledge_base.html @@ -164,6 +164,43 @@ +
+
+
+

Word 模板与字段映射

+

维护输出模板版本、占位符数量和字段映射摘要。

+
+
+
+ + + + + + + + + + + + + {% for item in template_mappings %} + + + + + + + + + {% endfor %} + +
模板名称输出类型版本占位符数量字段映射摘要状态
{{ item.template_name }}{{ item.output_type }}{{ item.version }}{{ item.placeholder_count }}{{ item.field_mapping_summary }}{{ item.status }}
+
+
+ + +
diff --git a/tests/test_platform_ui.py b/tests/test_platform_ui.py index 934df90..2cb59a7 100644 --- a/tests/test_platform_ui.py +++ b/tests/test_platform_ui.py @@ -1,4 +1,7 @@ from django.urls import reverse +from django.test import override_settings + +from apps.platform_ui.services import get_platform_demo_context def test_knowledge_base_page_shows_governance_sections(client): @@ -30,3 +33,70 @@ def test_knowledge_base_page_shows_owner_mapping_fields_and_notify_reasons(clien assert "notify_enabled" in content assert "task_completed" in content assert "task_failed" in content + + +@override_settings(GOVERNANCE_CONFIG_PATH="") +def test_get_platform_demo_context_reads_governance_yaml(tmp_path): + config_path = tmp_path / "governance.yaml" + config_path.write_text( + """ +owner_mappings: + - owner_role: 临床注册负责人 + owner_name: 王五 + department: 临床事务部 + chapter_scope: CH3-CH6 + risk_scope: 高风险阻断 + feishu_user_id: ou_test_owner + feishu_open_id: on_test_owner + feishu_name: 王五 + notify_enabled: 是 +feishu_configs: + - config_name: Demo 完成通知 + notify_reason: task_completed + channel: 群机器人 + message_template: 审核完成摘要 + @处理人 + status: 启用 +template_mappings: + - template_name: 注册证导出模板 + output_type: registration_word_export_report + version: V1.0 + placeholder_count: 12 + status: 启用 + field_mapping_summary: 产品名称 / 注册人 / 适用机型 +""".strip(), + encoding="utf-8", + ) + + with override_settings(GOVERNANCE_CONFIG_PATH=config_path): + context = get_platform_demo_context() + + assert context["owner_mappings"][0]["owner_name"] == "王五" + assert context["feishu_configs"][0]["notify_reason"] == "task_completed" + assert context["template_mappings"][0]["template_name"] == "注册证导出模板" + assert context["template_mappings"][0]["field_mapping_summary"] == "产品名称 / 注册人 / 适用机型" + + +@override_settings(GOVERNANCE_CONFIG_PATH="") +def test_knowledge_base_page_shows_template_mappings_from_governance_config(client, tmp_path): + config_path = tmp_path / "governance.yaml" + config_path.write_text( + """ +template_mappings: + - template_name: 风险摘要导出模板 + output_type: registration_word_export_report + version: V2.0 + placeholder_count: 8 + status: 灰度中 + field_mapping_summary: 风险等级 / 产品名称 / 责任人 +""".strip(), + encoding="utf-8", + ) + + with override_settings(GOVERNANCE_CONFIG_PATH=config_path): + response = client.get(reverse("platform_ui:knowledge-base")) + + content = response.content.decode("utf-8") + assert response.status_code == 200 + assert "Word 模板与字段映射" in content + assert "风险摘要导出模板" in content + assert "风险等级 / 产品名称 / 责任人" in content