132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
|
|
# 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
|