"""前端组件目录:运行时只读启动时从 MongoDB 载入的进程内存缓存,没有文件回退""" import hashlib import json from pathlib import Path from app.schemas.component_resource import ComponentCatalog # 进程内共享的组件目录,由启动流程载入;agent 与前端接口读取同一份 _cached_catalog: ComponentCatalog | None = None def set_catalog_cache(catalog: ComponentCatalog | None) -> None: global _cached_catalog _cached_catalog = catalog def get_catalog_cache() -> ComponentCatalog | None: return _cached_catalog # 手写的物料定义目录,只供导入脚本读取后写入 MongoDB,运行时不读取 MATERIALS_DIR = Path(__file__).resolve().parent / "materials" FIELD_GROUPS_FILE = "field_groups.json" EVENT_GROUPS_FILE = "event_groups.json" def load_material_definitions( directory: str | Path = MATERIALS_DIR, ) -> ComponentCatalog: """读取物料定义目录:共享字段分组、共享事件组各一个文件,每个物料一个文件;不合法直接报错 version 为全部文件内容的摘要,定义有任何改动都会得到新版本 """ target = Path(directory) if not target.is_absolute(): target = Path(__file__).resolve().parents[2] / target files = sorted(target.glob("*.json")) if not any(path.name == FIELD_GROUPS_FILE for path in files): raise FileNotFoundError(f"物料定义目录缺少 {FIELD_GROUPS_FILE}: {target}") digest = hashlib.sha256() components, field_groups, event_groups = [], [], [] for path in files: # utf-8-sig 同时兼容带 BOM 和不带 BOM 的 UTF-8 文件 text = path.read_text(encoding="utf-8-sig") digest.update(path.name.encode() + b"\0" + text.encode()) if path.name == FIELD_GROUPS_FILE: field_groups = json.loads(text) elif path.name == EVENT_GROUPS_FILE: event_groups = json.loads(text) else: components.append(json.loads(text)) # 文件名排序与展示顺序无关,物料按定义中的 order 排列,未写时排在最后 components.sort(key=lambda item: item.get("order", len(files))) return ComponentCatalog.model_validate( { "version": f"sha256:{digest.hexdigest()}", "components": [ {key: value for key, value in item.items() if key != "order"} for item in components ], "field_groups": field_groups, "event_groups": event_groups, } )