110 lines
3.8 KiB
Python
110 lines
3.8 KiB
Python
"""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
|
|
]
|
|
}
|