from __future__ import annotations from app.core.agent_enums import AgentReviewStatus from app.schemas.agent_asset import ( AgentAssetSpreadsheetChangeRecordRead, AgentAssetSpreadsheetDiffCellRead, AgentAssetSpreadsheetDiffSheetRead, AgentAssetVersionTimelineItemRead, ) class AgentAssetTimelineMixin: def list_version_timeline(self, asset_id: str) -> list[AgentAssetVersionTimelineItemRead]: self._ensure_ready() asset = self.repository.get(asset_id) if asset is None: raise LookupError("Asset not found") events: list[AgentAssetVersionTimelineItemRead] = [] versions = self.repository.list_versions(asset_id) for version in versions: source_version = self._extract_restore_source_version(version.change_note) events.append( AgentAssetVersionTimelineItemRead( event_type="restored" if source_version else "created", version=version.version, actor=version.created_by, event_time=version.created_at, title="恢复生成工作稿" if source_version else "创建工作版本", description=version.change_note or "生成新版本", note=version.change_note, source_version=source_version, ) ) for review in self.repository.list_reviews(asset_id): event_type = { AgentReviewStatus.PENDING.value: "submitted", AgentReviewStatus.APPROVED.value: "approved", AgentReviewStatus.REJECTED.value: "rejected", }.get(review.review_status, "reviewed") title = { "submitted": "提交审核", "approved": "审核通过", "rejected": "审核驳回", }.get(event_type, "审核处理") events.append( AgentAssetVersionTimelineItemRead( event_type=event_type, version=review.version, actor=review.reviewer, event_time=review.reviewed_at or review.created_at, title=title, description=review.review_note or "", note=review.review_note, ) ) audit_logs = self.audit_service.repository.list( resource_type=asset.asset_type, resource_id=asset.id, limit=200, ) for log in audit_logs: if log.action != "activate_agent_asset": continue after_json = log.after_json or {} version = str( after_json.get("published_version") or after_json.get("current_version") or "" ).strip() if not version: continue events.append( AgentAssetVersionTimelineItemRead( event_type="published", version=version, actor=log.actor, event_time=log.created_at, title="正式上线", description="该版本已切换为线上正式版本。", ) ) return sorted(events, key=lambda item: item.event_time) def list_spreadsheet_change_records( self, asset_id: str, *, limit: int = 30, ) -> list[AgentAssetSpreadsheetChangeRecordRead]: self._ensure_ready() asset = self._require_spreadsheet_rule(asset_id) logs = self.audit_service.repository.list( resource_type=asset.asset_type, resource_id=asset.id, action="edit_rule_spreadsheet", limit=min(max(limit, 1), 30), ) return [ AgentAssetSpreadsheetChangeRecordRead( id=log.id, actor=log.actor, changed_at=log.created_at, summary=str((log.after_json or {}).get("summary") or "表格内容已保存。"), sheet_changes=[ AgentAssetSpreadsheetDiffSheetRead.model_validate(item) for item in ((log.after_json or {}).get("sheet_changes") or []) ], cell_changes=[ AgentAssetSpreadsheetDiffCellRead.model_validate(item) for item in ((log.after_json or {}).get("cell_changes") or []) ], changed_sheet_count=int((log.after_json or {}).get("changed_sheet_count") or 0), changed_cell_count=int((log.after_json or {}).get("changed_cell_count") or 0), ) for log in logs ] @staticmethod def _extract_restore_source_version(change_note: str | None) -> str | None: normalized = str(change_note or "").strip() prefix = "基于历史版本 " suffix = " 恢复生成工作稿" if not normalized.startswith(prefix) or suffix not in normalized: return None return normalized.removeprefix(prefix).split(suffix, 1)[0].strip() or None