This commit is contained in:
cjw
2026-02-16 18:33:17 +08:00
parent d111721ed9
commit 1525f6fc0b
5 changed files with 25 additions and 849 deletions
+22 -291
View File
@@ -15,7 +15,6 @@ 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__)
@@ -117,56 +116,14 @@ async def upload_stp(
@router.get("/status/{task_id}")
@router.post("/status/{task_id}")
async def get_status(task_id: str, db_session: AsyncSession = Depends(get_db_session)):
async def get_status(task_id: str):
"""获取任务状态"""
# 先从内存查找
if task_id in tasks:
task = tasks[task_id]
logger.info(f"返回任务状态(内存): {task_id} - {task['status']}")
return task
# 内存中没有,从数据库查找
from sqlalchemy import select
from models.database import ProcessingTask, STPFile
result = await db_session.execute(
select(ProcessingTask, STPFile)
.join(STPFile, ProcessingTask.stp_file_id == STPFile.id)
.where(ProcessingTask.task_id == task_id)
)
task_record = result.first()
if not task_record:
if task_id not in tasks:
raise HTTPException(404, "任务不存在")
task, stp_file = task_record
task_data = {
"task_id": task.task_id,
"filename": stp_file.original_filename if stp_file else "",
"file_size": stp_file.file_size if stp_file else 0,
"status": task.status,
"progress": task.progress,
"current_step": task.current_step,
"upload_time": task.created_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.created_time else "",
"completed_at": task.completed_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.completed_time else "",
"error": task.error_message if task.error_message else ""
}
# 尝试获取快照中的 key_info
from models.database import AnalysisResultSnapshot
snapshot_result = await db_session.execute(
select(AnalysisResultSnapshot)
.where(AnalysisResultSnapshot.stp_file_id == stp_file.id)
.order_by(AnalysisResultSnapshot.created_time.desc())
.limit(1)
)
snapshot = snapshot_result.scalar_one_or_none()
if snapshot and snapshot.key_info:
task_data["key_info"] = snapshot.key_info
logger.info(f"返回任务状态(数据库): {task_id} - {task['status']}")
return task_data
task = tasks[task_id]
logger.info(f"返回任务状态: {task_id} - {task['status']}")
return task
@router.get("/debug/tasks")
@@ -205,7 +162,7 @@ async def get_file_history(db_session: AsyncSession = Depends(get_db_session)):
file_groups[filename].append({
"task_id": task.task_id,
"filename": filename,
"upload_time": task.created_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.created_time else "",
"upload_time": task.created_time.isoformat() if task.created_time else "",
"status": task.status,
"file_size": stp_file.file_size
})
@@ -260,9 +217,9 @@ async def get_file_records(filename: str, db_session: AsyncSession = Depends(get
"task_id": task.task_id,
"filename": stp_file.original_filename,
"file_size": stp_file.file_size,
"upload_time": task.created_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.created_time else "",
"upload_time": task.created_time.isoformat() if task.created_time else "",
"status": task.status,
"completed_at": task.completed_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.completed_time else ""
"completed_at": task.completed_time.isoformat() if task.completed_time else ""
})
# 按上传时间排序(最新的在前)
@@ -287,92 +244,28 @@ 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)):
"""结果详情页面"""
from sqlalchemy import select
from models.database import ProcessingTask, STPFile, GeometryData, MoldCavityData, HTMLFile, AnalysisResultSnapshot
# 先查询任务记录
task_result = await db_session.execute(
from models.database import ProcessingTask, STPFile, GeometryData, MoldCavityData, HTMLFile
# 从数据库查询任务详情
result = await db_session.execute(
select(ProcessingTask, STPFile)
.join(STPFile, ProcessingTask.stp_file_id == STPFile.id)
.where(ProcessingTask.task_id == task_id)
)
task_record = task_result.first()
task_record = result.first()
if not task_record:
raise HTTPException(404, "任务不存在")
task, stp_file = task_record
# 构建任务详情数据
# 构建任务详情数据(先只包含基本数据)
task_data = {
"task_id": task.task_id,
"filename": stp_file.original_filename if stp_file else "",
@@ -380,90 +273,11 @@ async def result_page(request: Request, task_id: str, db_session: AsyncSession =
"status": task.status,
"progress": task.progress,
"current_step": task.current_step,
"upload_time": task.created_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.created_time else "",
"completed_at": task.completed_time.astimezone().strftime('%Y-%m-%d %H:%M:%S') if task.completed_time else "",
"created_at": task.created_time.isoformat() if task.created_time else "",
"completed_at": task.completed_time.isoformat() if task.completed_time else "",
"error": task.error_message if task.error_message else ""
}
# 尝试从分析结果快照获取数据
snapshot_result = await db_session.execute(
select(AnalysisResultSnapshot)
.where(AnalysisResultSnapshot.stp_file_id == stp_file.id)
.order_by(AnalysisResultSnapshot.created_time.desc())
.limit(1)
)
snapshot = snapshot_result.scalar_one_or_none()
if snapshot:
# 从快照获取完整的数据
task_data["geometry_data"] = snapshot.geometry_data if snapshot.geometry_data else None
task_data["key_info"] = snapshot.key_info if snapshot.key_info else None
task_data["mold_cavity_data"] = snapshot.mold_cavity_data if snapshot.mold_cavity_data else None
logger.info(f"从快照加载任务数据: {task_id}")
else:
# 如果没有快照,从各个表分别获取
result = await db_session.execute(
select(GeometryData, MoldCavityData, HTMLFile)
.outerjoin(MoldCavityData, GeometryData.stp_file_id == MoldCavityData.stp_file_id)
.outerjoin(HTMLFile, GeometryData.stp_file_id == HTMLFile.stp_file_id)
.where(GeometryData.stp_file_id == stp_file.id)
)
other_data = result.first()
if other_data:
geometry_data, mold_cavity_data, html_file = other_data
# 如果有几何数据,添加到返回结果
if geometry_data:
task_data["geometry_data"] = {
"volume": geometry_data.volume,
"surface_area": geometry_data.surface_area,
"bounding_box": {
"min": geometry_data.bounding_box_min,
"max": geometry_data.bounding_box_max,
"dimensions": [
geometry_data.bounding_box_max[0] - geometry_data.bounding_box_min[0] if geometry_data.bounding_box_max and geometry_data.bounding_box_min else 0,
geometry_data.bounding_box_max[1] - geometry_data.bounding_box_min[1] if geometry_data.bounding_box_max and geometry_data.bounding_box_min else 0,
geometry_data.bounding_box_max[2] - geometry_data.bounding_box_min[2] if geometry_data.bounding_box_max and geometry_data.bounding_box_min else 0
]
},
"topology": {
"faces": geometry_data.topology_faces,
"edges": geometry_data.topology_edges,
"vertices": geometry_data.topology_vertices
},
"center_of_mass": geometry_data.center_of_mass
}
# 如果有模具型腔数据,添加到返回结果
if mold_cavity_data:
task_data["key_info"] = {
"metadata": {
"shrinkage_rate": mold_cavity_data.shrinkage_rate,
"draft_angle": mold_cavity_data.draft_angle
},
"manufacturing_info": {
"mold_material": mold_cavity_data.mold_material,
"estimated_clamping_force": mold_cavity_data.estimated_clamping_force,
"parting_line_length": mold_cavity_data.parting_line_length,
"estimated_cycle_time": mold_cavity_data.estimated_cycle_time,
"manufacturing_tolerance": mold_cavity_data.manufacturing_tolerance
},
"mold_cavities": {
"cavity_key_info": {
"geometric_characteristics": {
"product_weight": mold_cavity_data.product_weight,
"product_volume": mold_cavity_data.product_volume,
"wall_thickness_range": mold_cavity_data.wall_thickness_range,
"complexity_score": mold_cavity_data.complexity_score
}
}
}
}
logger.info(f"从各个表加载任务数据: {task_id}")
from fastapi.templating import Jinja2Templates
import os
# 简化路径配置,直接使用当前工作目录下的templates文件夹
@@ -638,8 +452,7 @@ async def process_file_core(
"mold_hardness": "HB 150-170",
"surface_finish": "Ra 0.8 μm",
"parting_line_length": f"{parting_line_length:.2f} mm",
"estimated_cycle_time": f"{int(cycle_time)} 秒",
"manufacturing_tolerance": "±0.1 mm"
"estimated_cycle_time": f"{int(cycle_time)} 秒"
},
"mold_cavities": {
"cavity_count": 1,
@@ -656,55 +469,6 @@ async def process_file_core(
"warpage_risk": "low"
}
}
},
# 添加特征检测数据
"detected_features": [
{
"feature_type": "壁厚检测",
"confidence": 0.85,
"location": "整体结构",
"description": f"平均壁厚: {avg_thickness_mm:.2f} mm"
},
{
"feature_type": "拔模角检测",
"confidence": 0.92,
"location": "主要表面",
"description": "拔模角符合要求"
},
{
"feature_type": "几何复杂性",
"confidence": 0.78,
"location": "整体结构",
"description": f"复杂度评分: {complexity_score:.2f}"
}
],
# 添加设计建议数据
"design_recommendations": [
{
"rec_type": "壁厚优化",
"priority": "medium",
"description": f"建议优化壁厚均匀性,当前范围: {wall_thickness_min:.2f} - {wall_thickness_max:.2f} mm",
"reason": "提高注塑成型质量"
},
{
"rec_type": "拔模角",
"priority": "low",
"description": "拔模角符合标准要求",
"reason": "便于脱模"
},
{
"rec_type": "结构简化",
"priority": "medium",
"description": f"建议简化复杂结构,当前复杂度: {complexity_score:.2f}",
"reason": "降低制造成本"
}
],
# 添加质量指标数据
"quality_metrics": {
"volume_utilization": round(complexity_score * 100, 2),
"wall_thickness_uniformity": round((1 - (wall_thickness_max - wall_thickness_min) / avg_thickness_mm) * 100, 2),
"geometric_complexity": round(complexity_score * 10, 2),
"moldability_score": round((complexity_score + 1) * 50, 2)
}
}
@@ -758,39 +522,6 @@ 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
-31
View File
@@ -199,8 +199,6 @@ class MoldCavityData(Base):
product_volume = Column(Float, nullable=True)
wall_thickness_range = Column(String(50), nullable=True)
complexity_score = Column(Float, nullable=True)
estimated_cycle_time = Column(String(50), nullable=True)
manufacturing_tolerance = Column(String(50), nullable=True)
# 质量评估
weld_line_risk = Column(String(50), nullable=True) # 熔接痕风险
@@ -328,32 +326,3 @@ class SystemLog(Base):
def __repr__(self):
return f"<SystemLog(id={self.id}, level='{self.level}', module='{self.module}')>"
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"<AnalysisResultSnapshot(id={self.id}, snapshot_name='{self.snapshot_name}', stp_file_id={self.stp_file_id})>"
+2 -75
View File
@@ -3,7 +3,7 @@
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update
from pathlib import Path
from typing import Optional, Dict, Any, List
from typing import Optional, Dict, Any
import json
from datetime import datetime
@@ -11,7 +11,7 @@ from models.database import (
STPFile, GeometryData, MoldCavityData,
HTMLFile, ProcessingTask, User,
FeatureDetection, DesignRecommendation,
UserActivity, SystemLog, AnalysisResultSnapshot
UserActivity, SystemLog
)
from storage.rustfs_storage import rustfs_manager
from utils.logger import get_logger
@@ -493,79 +493,6 @@ 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()