# services/file_history_service.py """文件历史查询视图——按文件名分组的多版本上传历史。 批次 3 自 storage_integration_rustfs.py 按职责拆分(见 task_storage_service.py 头注)。 """ from typing import Optional from sqlalchemy import select, func, desc from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload from moldinsight.models import STPFile, ProcessingTask from shared.utils.logger import get_logger logger = get_logger(__name__) class FileHistoryService: """按文件名聚合的上传历史查询""" async def get_file_history_by_filename( self, session: AsyncSession, filename: str, user_id: Optional[int] = None, limit: int = 50 ) -> list: """获取同一文件名的所有上传历史记录""" query = select(STPFile).options( joinedload(STPFile.processing_tasks) ).where( STPFile.original_filename == filename ).order_by(STPFile.upload_time.desc()) if user_id: query = query.where(STPFile.user_id == user_id) query = query.limit(limit) result = await session.execute(query) files = result.unique().scalars().all() return [ { 'id': f.id, 'task_id': f.processing_tasks[0].task_id if f.processing_tasks else None, 'upload_batch': f.upload_batch, 'upload_time': f.upload_time.strftime('%Y-%m-%d %H:%M:%S') if f.upload_time else None, 'file_size': f.file_size, 'status': f.status, 'volume': f.volume, 'surface_area': f.surface_area, 'product_weight': f.product_weight, 'has_analysis': f.status == 'completed' } for f in files ] async def get_all_file_groups( self, session: AsyncSession, user_id: Optional[int] = None, limit: int = 100 ) -> list: """获取所有文件分组(按文件名分组),包含每个文件的最新分析结果""" # 子查询:获取每个文件名的最新上传 subquery = ( select( STPFile.original_filename, func.max(STPFile.upload_time).label('latest_upload') ) .group_by(STPFile.original_filename) .order_by(desc('latest_upload')) .limit(limit) ) if user_id: subquery = subquery.where(STPFile.user_id == user_id) subquery = subquery.subquery() # 主查询:获取最新记录和统计信息 query = ( select(STPFile).options( joinedload(STPFile.processing_tasks) ) .join( subquery, (STPFile.original_filename == subquery.c.original_filename) & (STPFile.upload_time == subquery.c.latest_upload) ) .order_by(STPFile.upload_time.desc()) ) result = await session.execute(query) latest_files = result.unique().scalars().all() # 一次性聚合每个文件名的上传次数(替代逐文件 count 的 N+1 查询) count_subquery = ( select(STPFile.original_filename, func.count().label("upload_count")) .group_by(STPFile.original_filename) ) if user_id: count_subquery = count_subquery.where(STPFile.user_id == user_id) count_result = await session.execute(count_subquery) upload_counts = { row.original_filename: row.upload_count for row in count_result } # 获取每个文件名的上传次数 file_groups = [] for f in latest_files: task_id = f.processing_tasks[0].task_id if f.processing_tasks else None upload_count = upload_counts.get(f.original_filename, 1) file_groups.append({ 'filename': f.original_filename, 'latest_id': f.id, 'latest_task_id': task_id, 'latest_upload_time': f.upload_time.strftime('%Y-%m-%d %H:%M:%S') if f.upload_time else None, 'latest_status': f.status, 'upload_count': upload_count, 'file_size': f.file_size, 'volume': f.volume, 'surface_area': f.surface_area, 'product_weight': f.product_weight }) return file_groups