"""因子组合 Repository 的 SQLAlchemy 实现(M7.2b)。""" from __future__ import annotations import json from datetime import datetime from sqlalchemy import select from sqlalchemy.orm import Session from app.domain.entities.composite import CompositeComponent, CompositeDefinition from app.infrastructure.persistence.sqlalchemy.models.composite import FactorCompositeModel class SqlAlchemyCompositeRepository: def __init__(self, session: Session) -> None: self._session = session def save(self, definition: CompositeDefinition) -> CompositeDefinition: if not definition.id: raise ValueError("需要 id(由调用方生成)") exists = self._session.get(FactorCompositeModel, definition.id) dup = self._session.scalar( select(FactorCompositeModel) .where(FactorCompositeModel.name == definition.name) .limit(1) ) if dup is not None and dup.id != definition.id: raise ValueError(f"组合名已存在:{definition.name}") now = definition.created_at or datetime.now() if exists is None: self._session.add( FactorCompositeModel( id=definition.id, name=definition.name, method=definition.method, description=definition.description, components_json=json.dumps( [c.model_dump() for c in definition.components], ensure_ascii=False ), created_at=now, ) ) else: exists.name = definition.name exists.method = definition.method exists.description = definition.description exists.components_json = json.dumps( [c.model_dump() for c in definition.components], ensure_ascii=False ) self._session.flush() return definition def get(self, composite_id: str) -> CompositeDefinition | None: row = self._session.get(FactorCompositeModel, composite_id) return _to_entity(row) if row else None def list(self) -> list[CompositeDefinition]: rows = self._session.scalars( select(FactorCompositeModel).order_by(FactorCompositeModel.name) ).all() return [_to_entity(r) for r in rows] def delete(self, composite_id: str) -> bool: row = self._session.get(FactorCompositeModel, composite_id) if row is None: return False self._session.delete(row) return True def _to_entity(row: FactorCompositeModel) -> CompositeDefinition: return CompositeDefinition( id=row.id, name=row.name, method=row.method, description=row.description, components=[CompositeComponent(**c) for c in json.loads(row.components_json)], created_at=row.created_at, )