Files
xiaoxia-saas/packages/adapters/in_memory/asset_repository.py
T
灵应 4fee87c5e8
CI/CD Pipeline / Deploy Staging (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Failing after 45h56m35s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 45h56m48s
feat: 素材重复上传检测 + 批量生成视频
任务1: 素材重复上传检测
- 上传接口支持 file_hash 参数,通过 MD5+素材库ID 去重
- 命中去重直接返回已有 asset_id,不重复存 OSS
- file_hash 透传: API → IngestJob → Asset 全链路
- 三条上传路径(表单/直传/分片)均支持去重
- Alembic 031: assets + ingest_jobs 加 file_hash 列+索引
- 6 个单元测试覆盖去重命中/未命中/空hash/透传

任务3: 批量生成视频
- POST /generations 支持 count 参数,一次创建多条生成任务
- 每条任务独立状态跟踪,响应返回 task_ids 列表

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:57:37 +08:00

92 lines
2.9 KiB
Python
Executable File

"""Asset InMemory Repository 实现"""
from packages.domain import Asset
class InMemoryAssetRepository:
"""素材 InMemory 仓储实现(用于同步用例和测试)"""
def __init__(self):
self._assets: dict[str, Asset] = {}
def create(self, asset: Asset) -> Asset:
self._assets[asset.id] = asset
return asset
def get(self, asset_id: str) -> Asset | None:
return self._assets.get(asset_id)
def list_by_project(self, project_id: str) -> list[Asset]:
return [asset for asset in self._assets.values() if asset.project_id == project_id]
def list_by_library(self, library_id: str) -> list[Asset]:
return [asset for asset in self._assets.values() if asset.library_id == library_id]
def find_by_library(self, library_id: str) -> list[Asset]:
"""Alias for list_by_library to match the port interface."""
return self.list_by_library(library_id)
def find_by_library_and_file_type(self, library_id: str, file_type: str) -> list[Asset]:
return [
asset
for asset in self._assets.values()
if asset.library_id == library_id and asset.mime_type and asset.mime_type.startswith(file_type)
]
def update(self, asset: Asset) -> Asset:
self._assets[asset.id] = asset
return asset
def delete(self, asset_id: str) -> bool:
if asset_id in self._assets:
del self._assets[asset_id]
return True
return False
def batch_delete(self, asset_ids: list[str]) -> int:
"""批量删除素材,返回实际删除数量。"""
count = 0
for aid in asset_ids:
if aid in self._assets:
del self._assets[aid]
count += 1
return count
def find_by_project(
self,
project_id: str,
skip: int = 0,
limit: int = 100,
) -> list[Asset]:
items = [a for a in self._assets.values() if a.project_id == project_id]
return items[skip : skip + limit]
def find_by_id(self, asset_id: str) -> Asset | None:
return self._assets.get(asset_id)
def find_by_tag_ids(
self,
tag_ids: list[str],
skip: int = 0,
limit: int = 100,
) -> list[Asset]:
"""查找包含所有指定标签的素材。"""
if not tag_ids:
return []
tag_set = set(tag_ids)
items = [a for a in self._assets.values() if tag_set.issubset(set(a.tag_ids))]
return items[skip : skip + limit]
def find_by_library_and_file_hash(
self,
library_id: str,
file_hash: str,
) -> Asset | None:
"""按素材库 + 文件哈希查找已有素材(去重检测)。"""
if not file_hash:
return None
for asset in self._assets.values():
if asset.library_id == library_id and asset.file_hash == file_hash:
return asset
return None