"""Review, rollback, and listing flows for experiment sync.""" from __future__ import annotations from .shared import Any, Dict, ExperimentTruthNodeSync, Optional, now_cn_naive, select class ExperimentSyncReviewMixin: """Review/rollback/list sync records.""" async def review_sync( self, sync_id: int, tenant_id: int, reviewer_id: int, action: str, review_notes: Optional[str] = None, ) -> Dict[str, Any]: record = await self._get_sync_record(sync_id, tenant_id) if not record: raise ValueError("同步记录不存在") if record.apply_status == "applied" and action == "approve": return {"sync_id": record.id, "apply_status": record.apply_status} record.reviewed_by = reviewer_id record.reviewed_at = now_cn_naive() record.review_notes = review_notes if action == "approve": truth_node = await self._get_truth_node(record.truth_node_id, tenant_id) if truth_node: await self._apply_sync_to_truth_node(record, truth_node) record.apply_status = "applied" else: record.apply_status = "rejected" await self.db.commit() await self.db.refresh(record) return { "sync_id": record.id, "apply_status": record.apply_status, "reviewed_at": record.reviewed_at.isoformat() if record.reviewed_at else None, } async def rollback_sync( self, sync_id: int, tenant_id: int, reviewer_id: int, rollback_notes: Optional[str] = None, ) -> Dict[str, Any]: record = await self._get_sync_record(sync_id, tenant_id) if not record: raise ValueError("同步记录不存在") if record.apply_status != "applied": raise ValueError("仅已应用记录支持回滚") truth_node = await self._get_truth_node(record.truth_node_id, tenant_id) if not truth_node: raise ValueError("关联宪章不存在") snapshot = (record.sync_data or {}).get("previous_truth_content") if snapshot is None: raise ValueError("缺少回滚快照,无法回滚") truth_node.content = snapshot truth_node.version = int(truth_node.version or 1) + 1 record.apply_status = "requires_review" record.reviewed_by = reviewer_id record.reviewed_at = now_cn_naive() record.review_notes = rollback_notes or "manual rollback" await self.db.commit() await self.db.refresh(record) return { "sync_id": record.id, "apply_status": record.apply_status, "rolled_back": True, } async def list_sync_records( self, tenant_id: int, truth_node_id: Optional[int] = None, limit: int = 50, ) -> Dict[str, Any]: query = select(ExperimentTruthNodeSync).where(ExperimentTruthNodeSync.tenant_id == tenant_id) if truth_node_id is not None: query = query.where(ExperimentTruthNodeSync.truth_node_id == truth_node_id) query = query.order_by(ExperimentTruthNodeSync.created_at.desc()).limit(limit) result = await self.db.execute(query) items = result.scalars().all() return { "items": [ { "id": i.id, "experiment_id": i.experiment_id, "truth_node_id": i.truth_node_id, "sync_type": i.sync_type, "confidence_score": i.confidence_score, "auto_applied": i.auto_applied, "apply_status": i.apply_status, "created_at": i.created_at.isoformat() if i.created_at else None, } for i in items ] }