- 新增审核卡片和确认卡片模型,支持Agent推送审核任务和用户确认 - 实现审核卡片API服务,支持创建、更新、批准、驳回等操作 - 扩展审核维度配置,新增角色一致性、剧情连贯性等维度 - 优化前端审核配置页面,修复API路径错误和状态枚举问题 - 改进剧集创作平台布局,新增左侧边栏用于剧集管理和上下文查看 - 增强Skill管理,支持从审核系统跳转创建/编辑Skill - 修复episodes.json数据问题,清理聊天历史记录 - 更新Agent提示词,明确Skill引用加载流程 - 统一前端主题配置,优化整体UI体验
192 lines
6.3 KiB
Python
192 lines
6.3 KiB
Python
"""
|
|
Confirm Card Repository
|
|
|
|
确认卡片持久化仓储 - JSON文件存储
|
|
"""
|
|
from pathlib import Path
|
|
from typing import List, Optional
|
|
import json
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from app.models.confirm_card import (
|
|
ConfirmCard, ConfirmCardStatus, ConfirmCardOption
|
|
)
|
|
from app.utils.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
# 数据文件路径
|
|
DATA_DIR = Path(__file__).parent.parent.parent / "data"
|
|
CONFIRM_CARDS_FILE = DATA_DIR / "confirm_cards.json"
|
|
|
|
|
|
class ConfirmCardRepository:
|
|
"""确认卡片仓储"""
|
|
|
|
def __init__(self):
|
|
self.file_path = CONFIRM_CARDS_FILE
|
|
self._cards: Dict[str, ConfirmCard] = {}
|
|
self._ensure_data_dir()
|
|
self._load()
|
|
|
|
def _ensure_data_dir(self):
|
|
"""确保数据目录存在"""
|
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
def _load(self):
|
|
"""从文件加载"""
|
|
if self.file_path.exists():
|
|
try:
|
|
content = self.file_path.read_text(encoding="utf-8")
|
|
if content.strip():
|
|
data = json.loads(content)
|
|
for card_id, card_data in data.items():
|
|
# 转换选项列表
|
|
if "options" in card_data and isinstance(card_data["options"], list):
|
|
card_data["options"] = [
|
|
ConfirmCardOption(**opt) for opt in card_data["options"]
|
|
]
|
|
# 确保datetime正确转换
|
|
if isinstance(card_data.get("created_at"), str):
|
|
card_data["created_at"] = datetime.fromisoformat(
|
|
card_data["created_at"].replace('Z', '+00:00')
|
|
)
|
|
if isinstance(card_data.get("confirmed_at"), str):
|
|
card_data["confirmed_at"] = datetime.fromisoformat(
|
|
card_data["confirmed_at"].replace('Z', '+00:00')
|
|
)
|
|
if isinstance(card_data.get("expires_at"), str):
|
|
card_data["expires_at"] = datetime.fromisoformat(
|
|
card_data["expires_at"].replace('Z', '+00:00')
|
|
)
|
|
self._cards[card_id] = ConfirmCard(**card_data)
|
|
except Exception as e:
|
|
logger.error(f"加载确认卡片失败: {e}")
|
|
|
|
def _save(self):
|
|
"""保存到文件"""
|
|
try:
|
|
data = {}
|
|
for card_id, card in self._cards.items():
|
|
card_dict = card.dict()
|
|
# 确保datetime正确序列化
|
|
if isinstance(card_dict.get("created_at"), datetime):
|
|
card_dict["created_at"] = card_dict["created_at"].isoformat()
|
|
if isinstance(card_dict.get("confirmed_at"), datetime):
|
|
card_dict["confirmed_at"] = card_dict["confirmed_at"].isoformat()
|
|
if isinstance(card_dict.get("expires_at"), datetime):
|
|
card_dict["expires_at"] = card_dict["expires_at"].isoformat()
|
|
data[card_id] = card_dict
|
|
|
|
self.file_path.write_text(
|
|
json.dumps(data, ensure_ascii=False, indent=2),
|
|
encoding="utf-8"
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"保存确认卡片失败: {e}")
|
|
raise
|
|
|
|
async def create(self, card: ConfirmCard) -> ConfirmCard:
|
|
"""创建确认卡片"""
|
|
if not card.id:
|
|
card.id = str(uuid.uuid4())
|
|
|
|
self._cards[card.id] = card
|
|
self._save()
|
|
logger.info(f"创建确认卡片: {card.id}")
|
|
return card
|
|
|
|
async def get(self, card_id: str) -> Optional[ConfirmCard]:
|
|
"""获取单个卡片"""
|
|
return self._cards.get(card_id)
|
|
|
|
async def list_by_project(
|
|
self,
|
|
project_id: str,
|
|
status: Optional[ConfirmCardStatus] = None,
|
|
include_expired: bool = False
|
|
) -> List[ConfirmCard]:
|
|
"""列出项目的确认卡片"""
|
|
cards = [
|
|
card for card in self._cards.values()
|
|
if card.project_id == project_id
|
|
]
|
|
|
|
if status:
|
|
cards = [c for c in cards if c.status == status]
|
|
|
|
if not include_expired:
|
|
now = datetime.now()
|
|
cards = [c for c in cards if not c.expires_at or c.expires_at > now]
|
|
|
|
# 按创建时间倒序
|
|
cards = sorted(cards, key=lambda c: c.created_at, reverse=True)
|
|
return cards
|
|
|
|
async def update_status(
|
|
self,
|
|
card_id: str,
|
|
status: ConfirmCardStatus,
|
|
selected_option_id: Optional[str] = None,
|
|
custom_response: Optional[str] = None,
|
|
user_notes: Optional[str] = None
|
|
) -> Optional[ConfirmCard]:
|
|
"""更新确认卡片状态"""
|
|
card = self._cards.get(card_id)
|
|
if not card:
|
|
return None
|
|
|
|
card.status = status
|
|
card.selected_option_id = selected_option_id
|
|
card.custom_response = custom_response
|
|
card.user_notes = user_notes
|
|
card.confirmed_at = datetime.now()
|
|
|
|
self._cards[card_id] = card
|
|
self._save()
|
|
|
|
logger.info(f"更新确认卡片状态: {card_id} -> {status}")
|
|
return card
|
|
|
|
async def delete(self, card_id: str) -> bool:
|
|
"""删除确认卡片"""
|
|
if card_id in self._cards:
|
|
del self._cards[card_id]
|
|
self._save()
|
|
return True
|
|
return False
|
|
|
|
async def cleanup_expired(self) -> int:
|
|
"""清理过期的确认卡片"""
|
|
now = datetime.now()
|
|
expired_ids = [
|
|
card_id for card_id, card in self._cards.items()
|
|
if card.expires_at and card.expires_at < now
|
|
]
|
|
|
|
for card_id in expired_ids:
|
|
self._cards[card_id].status = ConfirmCardStatus.EXPIRED
|
|
|
|
if expired_ids:
|
|
self._save()
|
|
logger.info(f"标记 {len(expired_ids)} 个确认卡片为过期")
|
|
|
|
return len(expired_ids)
|
|
|
|
|
|
# 全局单例
|
|
_confirm_card_repo = None
|
|
|
|
|
|
def get_confirm_card_repo() -> ConfirmCardRepository:
|
|
"""获取确认卡片仓储单例"""
|
|
global _confirm_card_repo
|
|
if _confirm_card_repo is None:
|
|
_confirm_card_repo = ConfirmCardRepository()
|
|
return _confirm_card_repo
|
|
|
|
|
|
# 导入Dict类型
|
|
from typing import Dict
|