feat:物料存入数据库
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""物料目录的 MongoDB 存储:每个组件一条记录,目录版本和来源放在元数据记录中"""
|
||||
|
||||
from loguru import logger
|
||||
from pymongo.asynchronous.database import AsyncDatabase
|
||||
|
||||
from app.resources.component import load_catalog_file, set_catalog_cache
|
||||
from app.schemas.component_resource import ComponentCatalog
|
||||
|
||||
# 元数据记录的 _id,组件记录的 _id 为组件 key
|
||||
META_ID = "__catalog__"
|
||||
|
||||
|
||||
class MongoMaterialRepository:
|
||||
def __init__(self, db: AsyncDatabase, prefix: str = ""):
|
||||
# 集合名前缀用于测试隔离
|
||||
self._materials = db.get_collection(f"{prefix}materials")
|
||||
|
||||
async def load_all(self) -> ComponentCatalog | None:
|
||||
# 没有元数据记录视为尚未导入,由调用方决定回退方式
|
||||
meta = await self._materials.find_one({"_id": META_ID})
|
||||
if meta is None:
|
||||
return None
|
||||
documents = await self._materials.find({"_id": {"$ne": META_ID}}).to_list()
|
||||
components = [
|
||||
{key: value for key, value in item.items() if key not in ("_id", "order")}
|
||||
for item in sorted(documents, key=lambda item: item.get("order", 0))
|
||||
]
|
||||
return ComponentCatalog.model_validate(
|
||||
{
|
||||
"version": meta["version"],
|
||||
"source": meta["source"],
|
||||
"components": components,
|
||||
}
|
||||
)
|
||||
|
||||
async def replace_all(self, catalog: ComponentCatalog) -> None:
|
||||
# 整体替换目录,供导入脚本使用;order 保持导出文件里的组件顺序
|
||||
documents = [
|
||||
{
|
||||
**component.model_dump(mode="json", by_alias=True),
|
||||
"_id": component.key,
|
||||
"order": index,
|
||||
}
|
||||
for index, component in enumerate(catalog.components)
|
||||
]
|
||||
await self._materials.delete_many({})
|
||||
if documents:
|
||||
await self._materials.insert_many(documents)
|
||||
await self._materials.insert_one(
|
||||
{"_id": META_ID, "version": catalog.version, "source": catalog.source}
|
||||
)
|
||||
|
||||
|
||||
async def warm_material_cache(db: AsyncDatabase | None) -> ComponentCatalog | None:
|
||||
"""启动时把物料目录载入进程缓存;Mongo 未启用或尚未导入时回退读本地 JSON"""
|
||||
catalog = await MongoMaterialRepository(db).load_all() if db is not None else None
|
||||
if catalog is None:
|
||||
catalog = load_catalog_file()
|
||||
logger.warning("物料未从 MongoDB 加载,回退使用本地目录文件")
|
||||
else:
|
||||
logger.info("已从 MongoDB 加载物料 {} 个", len(catalog.components))
|
||||
set_catalog_cache(catalog)
|
||||
return catalog
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
from loguru import logger
|
||||
@@ -16,6 +17,8 @@ class Persistence:
|
||||
# 为空时由图使用默认的内存检查点
|
||||
checkpointer: BaseCheckpointSaver | None
|
||||
close: Callable[[], Awaitable[None]]
|
||||
# 启用 MongoDB 时的数据库,供物料等其他集合复用同一连接
|
||||
db: Any = None
|
||||
|
||||
|
||||
async def _close_nothing() -> None:
|
||||
@@ -67,4 +70,4 @@ async def open_persistence() -> Persistence:
|
||||
raise
|
||||
|
||||
logger.info("已启用 MongoDB 持久化,库名 {}", settings.mongodb_db)
|
||||
return Persistence(store, checkpointer, close)
|
||||
return Persistence(store, checkpointer, close, async_client[settings.mongodb_db])
|
||||
|
||||
Reference in New Issue
Block a user