Files
X-Financial/server/src/app/services/agent_asset_timeline.py
2026-05-22 10:42:31 +08:00

133 lines
5.1 KiB
Python

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