diff --git a/src/api/routes.py b/src/api/routes.py index 2cc6552..972028c 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -15,6 +15,7 @@ from database.database import get_db_session from utils.logger import get_logger from sqlalchemy.ext.asyncio import AsyncSession from core.mold_generator import MoldCavityGenerator +from datetime import datetime logger = get_logger(__name__) @@ -244,6 +245,71 @@ async def history_page(request: Request): }) +@router.get("/api/snapshots/{stp_file_id}") +@router.post("/api/snapshots/{stp_file_id}") +async def get_analysis_snapshots(stp_file_id: int, db_session: AsyncSession = Depends(get_db_session)): + """获取指定STP文件的分析结果快照列表""" + storage_service = StorageIntegrationService() + + try: + snapshots = await storage_service.get_analysis_result_snapshots(db_session, stp_file_id) + + # 构建返回数据 + snapshot_list = [] + for snapshot in snapshots: + snapshot_list.append({ + "id": snapshot.id, + "snapshot_name": snapshot.snapshot_name, + "created_time": snapshot.created_time.isoformat() if snapshot.created_time else "", + "analysis_time": snapshot.analysis_time.isoformat() if snapshot.analysis_time else "", + "task_data": snapshot.task_data + }) + + return { + "stp_file_id": stp_file_id, + "total_snapshots": len(snapshot_list), + "snapshots": snapshot_list + } + + except Exception as e: + logger.error(f"获取分析结果快照失败: {e}") + raise HTTPException(500, f"获取快照失败: {str(e)}") + + +@router.get("/api/snapshot/{snapshot_id}") +@router.post("/api/snapshot/{snapshot_id}") +async def get_analysis_snapshot(snapshot_id: int, db_session: AsyncSession = Depends(get_db_session)): + """获取指定分析结果快照的完整数据""" + from sqlalchemy import select + from models.database import AnalysisResultSnapshot + + try: + result = await db_session.execute( + select(AnalysisResultSnapshot).where(AnalysisResultSnapshot.id == snapshot_id) + ) + snapshot = result.scalar_one_or_none() + + if not snapshot: + raise HTTPException(404, "快照不存在") + + return { + "id": snapshot.id, + "snapshot_name": snapshot.snapshot_name, + "stp_file_id": snapshot.stp_file_id, + "created_time": snapshot.created_time.isoformat() if snapshot.created_time else "", + "analysis_time": snapshot.analysis_time.isoformat() if snapshot.analysis_time else "", + "task_data": snapshot.task_data, + "geometry_data": snapshot.geometry_data, + "mold_cavity_data": snapshot.mold_cavity_data, + "key_info": snapshot.key_info, + "html_info": snapshot.html_info + } + + except Exception as e: + logger.error(f"获取分析结果快照详情失败: {e}") + raise HTTPException(500, f"获取快照详情失败: {str(e)}") + + @router.get("/result/{task_id}") @router.post("/result/{task_id}") async def result_page(request: Request, task_id: str, db_session: AsyncSession = Depends(get_db_session)): @@ -579,6 +645,39 @@ async def process_file_core( db_session, task_id, "completed", 100, "模具型腔生成完成" ) + # 构建完整的分析结果数据 + task_info = tasks[task_id] + + # 构建任务基本信息 + task_data = { + "task_id": task_info["task_id"], + "filename": task_info["filename"], + "file_size": task_info["file_size"], + "status": task_info["status"], + "upload_time": task_info["upload_time"], + "completed_at": str(datetime.now()) + } + + # 保存分析结果快照到数据库 + try: + await storage_service.save_analysis_result_snapshot( + db_session, + stp_file_id, + task_data=task_data, + geometry_data=geometry_data, + mold_cavity_data=detailed_cavity_json, + key_info=detailed_cavity_json, + html_info={ + "filename": Path(html_file_path).name, + "file_path": html_file_path, + "generated_time": str(datetime.now()) + } + ) + logger.info(f"分析结果快照保存成功: {task_id}") + except Exception as e: + logger.error(f"保存分析结果快照失败: {e}") + # 不影响主流程,继续执行 + # 更新内存任务状态 tasks[task_id]["geometry_data"] = geometry_data tasks[task_id]["analysis_result"] = analysis_result diff --git a/src/models/database.py b/src/models/database.py index 5eaceac..e83405a 100644 --- a/src/models/database.py +++ b/src/models/database.py @@ -326,3 +326,32 @@ class SystemLog(Base): def __repr__(self): return f"" + +class AnalysisResultSnapshot(Base): + """分析结果快照表 - 存储每次分析结果的完整副本""" + __tablename__ = "analysis_result_snapshots" + + id = Column(Integer, primary_key=True, index=True) + stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) + + # 快照标识信息 + snapshot_name = Column(String(255), nullable=False, index=True) # 文件名+日期时间格式 + snapshot_type = Column(String(50), default="complete") # complete, geometry_only, key_info_only + + # 完整分析结果数据 + task_data = Column(JSON, nullable=False) # 任务基本信息 + geometry_data = Column(JSON, nullable=True) # 几何数据 + mold_cavity_data = Column(JSON, nullable=True) # 模具型腔数据 + key_info = Column(JSON, nullable=True) # 关键信息 + html_info = Column(JSON, nullable=True) # HTML文件信息 + + # 时间戳 + created_time = Column(DateTime, default=func.now()) + analysis_time = Column(DateTime, nullable=False) # 原始分析时间 + + # 关联关系 + stp_file = relationship("STPFile") + + def __repr__(self): + return f"" + diff --git a/src/services/storage_integration_rustfs.py b/src/services/storage_integration_rustfs.py index e327735..9b8e554 100644 --- a/src/services/storage_integration_rustfs.py +++ b/src/services/storage_integration_rustfs.py @@ -11,7 +11,7 @@ from models.database import ( STPFile, GeometryData, MoldCavityData, HTMLFile, ProcessingTask, User, FeatureDetection, DesignRecommendation, - UserActivity, SystemLog + UserActivity, SystemLog, AnalysisResultSnapshot ) from storage.rustfs_storage import rustfs_manager from utils.logger import get_logger @@ -493,6 +493,79 @@ class StorageIntegrationService: logger.info(f"STP文件及其关联数据已删除: {stp_file_id}") + async def save_analysis_result_snapshot( + self, + session: AsyncSession, + stp_file_id: int, + task_data: Dict[str, Any], + geometry_data: Optional[Dict[str, Any]] = None, + mold_cavity_data: Optional[Dict[str, Any]] = None, + key_info: Optional[Dict[str, Any]] = None, + html_info: Optional[Dict[str, Any]] = None + ) -> AnalysisResultSnapshot: + """保存分析结果快照 - 文件名+日期时间命名方式""" + + # 获取STP文件信息 + stp_file = await session.get(STPFile, stp_file_id) + if not stp_file: + raise ValueError(f"STP文件不存在: {stp_file_id}") + + # 生成快照名称:文件名_YYYYMMDD_HHMMSS + filename = stp_file.original_filename + # 移除文件扩展名 + base_name = Path(filename).stem + # 生成时间戳 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + snapshot_name = f"{base_name}_{timestamp}" + + # 获取分析时间(从任务数据中获取或使用当前时间) + analysis_time_str = task_data.get('created_at') or task_data.get('upload_time') + if analysis_time_str: + analysis_time = datetime.fromisoformat(analysis_time_str.replace('Z', '+00:00')) + else: + analysis_time = datetime.now() + + # 创建快照记录 + snapshot = AnalysisResultSnapshot( + stp_file_id=stp_file_id, + snapshot_name=snapshot_name, + task_data=task_data, + geometry_data=geometry_data, + mold_cavity_data=mold_cavity_data, + key_info=key_info, + html_info=html_info, + analysis_time=analysis_time + ) + + session.add(snapshot) + await session.commit() + await session.refresh(snapshot) + + logger.info(f"分析结果快照保存成功: {snapshot_name} (ID: {snapshot.id})") + return snapshot + + async def get_analysis_result_snapshots( + self, + session: AsyncSession, + stp_file_id: int, + limit: int = 10 + ) -> List[AnalysisResultSnapshot]: + """获取指定STP文件的分析结果快照列表""" + + from sqlalchemy import select + from sqlalchemy.orm import selectinload + + result = await session.execute( + select(AnalysisResultSnapshot) + .where(AnalysisResultSnapshot.stp_file_id == stp_file_id) + .order_by(AnalysisResultSnapshot.created_time.desc()) + .limit(limit) + ) + + snapshots = result.scalars().all() + logger.info(f"获取到 {len(snapshots)} 个分析结果快照") + return snapshots + # 全局存储集成服务实例 storage_integration = StorageIntegrationService() diff --git a/templates/result.html b/templates/result.html index 8b9a7e0..4fa2724 100644 --- a/templates/result.html +++ b/templates/result.html @@ -159,7 +159,7 @@ -