From 2fb3b0271cfd789d6940a5c133ec986920984266 Mon Sep 17 00:00:00 2001
From: chenjw28 <792430652@qq.com>
Date: Wed, 6 May 2026 17:20:54 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=86=E6=A8=A1=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E4=B8=BA=E6=B3=A8=E5=A1=91=E6=A8=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/aluminum_price_routes.py | 21 ++++
src/core/ai_parting_detector.py | 2 +-
src/core/aluminum_foam_mold.py | 2 +-
src/core/base_mold_generator.py | 140 +++++++++++++-----------
src/core/cad_exporter.py | 2 +-
src/core/mold_generator.py | 4 +-
src/core/side_action_designer.py | 4 +-
src/main.py | 2 +
src/services/aluminum_price_service.py | 93 ++++++++++++++++
src/services/verification_service.py | 8 +-
static/index.html | 3 +-
static/style.css | 143 ++++++++++++++++++++++++
static/vue-app.js | 144 ++++++++++++++++++++++++-
13 files changed, 492 insertions(+), 76 deletions(-)
create mode 100644 src/api/aluminum_price_routes.py
create mode 100644 src/services/aluminum_price_service.py
diff --git a/src/api/aluminum_price_routes.py b/src/api/aluminum_price_routes.py
new file mode 100644
index 0000000..bca365e
--- /dev/null
+++ b/src/api/aluminum_price_routes.py
@@ -0,0 +1,21 @@
+"""
+铝金属价格API路由
+
+提供铝金属价格的当前报价和历史走势数据。
+路由前缀: /api/aluminum-price
+不需要认证,公开访问。
+"""
+from fastapi import APIRouter, Query
+from services.aluminum_price_service import get_aluminum_current_price, get_aluminum_price_history
+
+router = APIRouter(prefix="/aluminum-price", tags=["铝金属价格"])
+
+
+@router.get("/current")
+async def aluminum_current_price():
+ return get_aluminum_current_price()
+
+
+@router.get("/history")
+async def aluminum_price_history(days: int = Query(default=30, ge=7, le=365)):
+ return get_aluminum_price_history(days=days)
diff --git a/src/core/ai_parting_detector.py b/src/core/ai_parting_detector.py
index e8e6eec..76854cb 100644
--- a/src/core/ai_parting_detector.py
+++ b/src/core/ai_parting_detector.py
@@ -68,7 +68,7 @@ class ShapeGraphBuilder:
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop
from OCC.Core.Bnd import Bnd_Box
- from OCC.Core.BRepBndLib import brepbndlib_Add
+ from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.TopTools import TopTools_IndexedDataMapOfShapeListOfShape
from OCC.Core.TopExp import topexp_MapShapesAndAncestors
from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Edge, topods
diff --git a/src/core/aluminum_foam_mold.py b/src/core/aluminum_foam_mold.py
index 501d0f1..2604fcb 100644
--- a/src/core/aluminum_foam_mold.py
+++ b/src/core/aluminum_foam_mold.py
@@ -21,7 +21,7 @@ from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE
from OCC.Core.Bnd import Bnd_Box
-from OCC.Core.BRepBndLib import brepbndlib_Add
+from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop
diff --git a/src/core/base_mold_generator.py b/src/core/base_mold_generator.py
index c40b5dc..02c0a50 100644
--- a/src/core/base_mold_generator.py
+++ b/src/core/base_mold_generator.py
@@ -226,13 +226,14 @@ class BaseMoldGenerator:
def _split_cavity_core(self, shape: Any, parting_surface: Any, margin: int = 20) -> Tuple[Any, Any]:
"""
- 分离型腔和型芯
+ 分离型腔和型芯 — 完全嵌入 + 突出贴合方式
- 正确流程:
- 1. 创建模具块(产品边界框 + 余量)
- 2. 用分型面将模具块切分为 A板(上模)和 B板(下模)
- 3. A板减去产品 → 型腔(凹模)
- 4. B板减去产品 → 型芯(凸模)
+ 流程:
+ 1. 创建完整模具块(产品包围盒 + 全方向余量)
+ 2. 型腔(凹模)= 模具块 - 产品 → 产品完全嵌入型腔块中
+ 3. 型芯(凸模)= 产品形状突出体 → 从芯块面突出贴合
+
+ 不再使用分型面中间切开产品的方式。
"""
try:
bbox = Bnd_Box()
@@ -246,38 +247,69 @@ class BaseMoldGenerator:
mold_ymax = ymax + margin
mold_zmax = zmax + margin
- mold_block = BRepPrimAPI_MakeBox(
+ cavity_block = BRepPrimAPI_MakeBox(
gp_Pnt(mold_xmin, mold_ymin, mold_zmin),
gp_Pnt(mold_xmax, mold_ymax, mold_zmax)
).Shape()
- parting_plane = self._get_parting_plane(parting_surface, shape)
- if parting_plane is None:
- logger.warning("无法获取分型面平面,使用Z中面作为分型面")
- center_z = (zmin + zmax) / 2
- parting_plane = gp_Pln(gp_Pnt(0, 0, center_z), gp_Dir(0, 0, 1))
-
- a_plate, b_plate = self._split_mold_block_by_plane(mold_block, parting_plane)
-
- if a_plate is None or b_plate is None:
- logger.warning("A/B板分离失败,回退到简化方法")
- return self._split_cavity_core_fallback(shape, mold_block)
-
- cavity = self._subtract_product_from_plate(a_plate, shape, "A板")
- core = self._subtract_product_from_plate(b_plate, shape, "B板")
-
+ cavity = self._subtract_product_from_plate(cavity_block, shape, "型腔")
if cavity is None:
- cavity = a_plate
- if core is None:
- core = b_plate
+ logger.warning("型腔布尔减运算失败,使用原始模具块")
+ cavity = cavity_block
- logger.info("型腔/型芯分离完成(基于分型面A/B板切分)")
+ core = self._build_protruding_core(
+ shape, cavity_block,
+ mold_xmin, mold_ymin, mold_zmin,
+ mold_xmax, mold_ymax, mold_zmax,
+ xmin, ymin, zmin, xmax, ymax, zmax
+ )
+
+ logger.info("型腔/型芯分离完成(完全嵌入+突出贴合)")
return cavity, core
except Exception as e:
logger.error(f"型腔分离失败: {e}")
return self._split_cavity_core_fallback(shape, None)
+ def _build_protruding_core(
+ self,
+ shape: Any,
+ cavity_block: Any,
+ mold_xmin: float, mold_ymin: float, mold_zmin: float,
+ mold_xmax: float, mold_ymax: float, mold_zmax: float,
+ xmin: float, ymin: float, zmin: float,
+ xmax: float, ymax: float, zmax: float,
+ ) -> Any:
+ """
+ 构建突出贴合式型芯。
+
+ 型芯 = 模具块 ∩ 产品形状 → 产品突出体。
+ 从视觉上:型芯面突出产品形状,与型腔的凹入形状完美贴合。
+ """
+ try:
+ common_op = BRepAlgoAPI_Common(cavity_block, shape)
+ if common_op.IsDone():
+ core = common_op.Shape()
+ logger.info("型芯突出体构建成功(布尔交)")
+ return core
+ except Exception as e:
+ logger.warning(f"布尔交构建型芯失败: {e}")
+
+ try:
+ core_plate = BRepPrimAPI_MakeBox(
+ gp_Pnt(mold_xmin, mold_ymin, zmin - 5.0),
+ gp_Pnt(mold_xmax, mold_ymax, zmin)
+ ).Shape()
+ cut_op = BRepAlgoAPI_Cut(cavity_block, shape)
+ if cut_op.IsDone():
+ logger.info("型芯突出体构建成功(布尔减回退)")
+ return cut_op.Shape()
+ except Exception:
+ pass
+
+ logger.info("型芯回退为产品形状")
+ return shape
+
def _get_parting_plane(self, parting_surface: Any, shape: Any) -> Optional[gp_Pln]:
"""从分型面提取平面方程"""
try:
@@ -370,68 +402,52 @@ class BaseMoldGenerator:
def _split_cavity_core_fallback(self, shape: Any,
mold_block: Optional[Any] = None) -> Tuple[Any, Any]:
"""
- 分模回退方案:当分型面切分失败时使用
+ 分模回退方案:完全嵌入+突出贴合,不切分模具块。
- 使用Z中面将模具块简单切分为上下两半
+ 1. 创建完整模具块 → 型腔 = 模具块 - 产品(产品完全嵌入)
+ 2. 型芯 = 产品形状突出体(突出贴合)
"""
- logger.warning("使用分模回退方案(Z中面切分)")
+ logger.warning("使用分模回退方案(完全嵌入+突出贴合)")
try:
bbox = Bnd_Box()
brepbndlib.Add(shape, bbox)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
+ margin = 20
if mold_block is None:
- margin = 20
mold_block = BRepPrimAPI_MakeBox(
gp_Pnt(xmin - margin, ymin - margin, zmin - margin),
gp_Pnt(xmax + margin, ymax + margin, zmax + margin)
).Shape()
- center_z = (zmin + zmax) / 2
- parting_plane = gp_Pln(gp_Pnt(0, 0, center_z), gp_Dir(0, 0, 1))
+ cavity = self._subtract_product_from_plate(mold_block, shape, "型腔(回退)")
+ if cavity is None:
+ cavity = mold_block
- a_plate, b_plate = self._split_mold_block_by_plane(mold_block, parting_plane)
+ core = self._build_protruding_core(
+ shape, mold_block,
+ xmin - margin, ymin - margin, zmin - margin,
+ xmax + margin, ymax + margin, zmax + margin,
+ xmin, ymin, zmin, xmax, ymax, zmax
+ )
- if a_plate is not None and b_plate is not None:
- cavity = self._subtract_product_from_plate(a_plate, shape, "A板(回退)")
- core = self._subtract_product_from_plate(b_plate, shape, "B板(回退)")
- return cavity or a_plate, core or b_plate
-
- logger.warning("回退方案也失败,使用最简A/B板切分(避免返回产品本体)")
- center_z = (zmin + zmax) / 2
- margin = 20
- a_plate = BRepPrimAPI_MakeBox(
- gp_Pnt(xmin - margin, ymin - margin, center_z),
- gp_Pnt(xmax + margin, ymax + margin, zmax + margin)
- ).Shape()
- b_plate = BRepPrimAPI_MakeBox(
- gp_Pnt(xmin - margin, ymin - margin, zmin - margin),
- gp_Pnt(xmax + margin, ymax + margin, center_z)
- ).Shape()
-
- cavity = self._subtract_product_from_plate(a_plate, shape, "A板(最简)")
- core = self._subtract_product_from_plate(b_plate, shape, "B板(最简)")
- return cavity or a_plate, core or b_plate
+ logger.info("回退方案型腔/型芯分离完成")
+ return cavity or mold_block, core
except Exception as e:
logger.error(f"分模回退方案失败: {e}")
- # 最后兜底也返回模具半板,而不是产品本体,避免预览出现“三份产品”
try:
bbox = Bnd_Box()
brepbndlib.Add(shape, bbox)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
- center_z = (zmin + zmax) / 2
margin = 20
- a_plate = BRepPrimAPI_MakeBox(
- gp_Pnt(xmin - margin, ymin - margin, center_z),
+ cavity_block = BRepPrimAPI_MakeBox(
+ gp_Pnt(xmin - margin, ymin - margin, zmin - margin),
gp_Pnt(xmax + margin, ymax + margin, zmax + margin)
).Shape()
- b_plate = BRepPrimAPI_MakeBox(
- gp_Pnt(xmin - margin, ymin - margin, zmin - margin),
- gp_Pnt(xmax + margin, ymax + margin, center_z)
- ).Shape()
- return a_plate, b_plate
+ cavity = self._subtract_product_from_plate(cavity_block, shape, "型腔(兜底)")
+ return cavity or cavity_block, shape
except Exception:
return shape, shape
diff --git a/src/core/cad_exporter.py b/src/core/cad_exporter.py
index 3430c0e..ddb3523 100644
--- a/src/core/cad_exporter.py
+++ b/src/core/cad_exporter.py
@@ -26,7 +26,7 @@ FreeCAD 导入建议:
"""
import os
-from typing import Dict, List, Any, Optional
+from typing import Dict, List, Any, Optional, Tuple
from pathlib import Path
from utils.logger import get_logger
diff --git a/src/core/mold_generator.py b/src/core/mold_generator.py
index 6b70bd4..e13b93b 100644
--- a/src/core/mold_generator.py
+++ b/src/core/mold_generator.py
@@ -7,7 +7,7 @@ from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE
from OCC.Core.Bnd import Bnd_Box
-from OCC.Core.BRepBndLib import brepbndlib_Add
+from OCC.Core.BRepBndLib import brepbndlib
from models.schemas import create_mold_cavity_data, create_mold_key_info
from utils.logger import get_logger
@@ -304,7 +304,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
normal = surface.Plane().Position().Direction()
else:
bbox = Bnd_Box()
- brepbndlib_Add(face, bbox)
+ brepbndlib.Add(face, bbox)
normal = gp_Dir(0, 0, 1)
face_normals.append(normal)
diff --git a/src/core/side_action_designer.py b/src/core/side_action_designer.py
index f6c19d9..2fe7343 100644
--- a/src/core/side_action_designer.py
+++ b/src/core/side_action_designer.py
@@ -58,7 +58,7 @@ class UndercutDetector:
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop
from OCC.Core.Bnd import Bnd_Box
- from OCC.Core.BRepBndLib import brepbndlib_Add
+ from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.gp import gp_Dir
dir_vec = np.array(parting_direction, dtype=np.float64)
@@ -118,7 +118,7 @@ class UndercutDetector:
center = face_props.CentreOfMass()
bbox = Bnd_Box()
- brepbndlib_Add(face, bbox)
+ brepbndlib.Add(face, bbox)
try:
fxmin, fymin, fzmin, fxmax, fymax, fzmax = bbox.Get()
except Exception:
diff --git a/src/main.py b/src/main.py
index 5a57476..54ebd29 100644
--- a/src/main.py
+++ b/src/main.py
@@ -44,6 +44,7 @@ import time
from api.auth_routes import router as auth_router
from api.inventory import inventory_router
+from api.aluminum_price_routes import router as aluminum_price_router
from utils.logger import setup_logging, get_logger
from database.init_db import init_database
@@ -156,6 +157,7 @@ app.mount("/html", StaticFiles(directory=html_output_dir), name="html")
app.include_router(auth_router)
app.include_router(inventory_router)
+app.include_router(aluminum_price_router, prefix="/api")
try:
from api.v1 import router as moldinsight_router
except Exception as e:
diff --git a/src/services/aluminum_price_service.py b/src/services/aluminum_price_service.py
new file mode 100644
index 0000000..8ee6a8f
--- /dev/null
+++ b/src/services/aluminum_price_service.py
@@ -0,0 +1,93 @@
+"""
+铝金属价格数据服务
+
+提供铝金属的当前价格和历史价格走势数据。
+数据来源优先级:
+1. 外部API(预留接口)
+2. 模拟真实走势数据(当前使用)
+
+数据基于上海期货交易所(SHFE)铝期货价格走势特征生成。
+"""
+import random
+import hashlib
+from datetime import datetime, timedelta
+from typing import List, Dict, Optional
+
+
+BASE_PRICE = 18950.0
+PRICE_VOLATILITY = 120.0
+TREND_DRIFT = 0.3
+
+
+def _daily_seed(date_str: str) -> float:
+ h = hashlib.md5(date_str.encode()).hexdigest()
+ seed = int(h[:8], 16) / (16 ** 8)
+ return seed
+
+
+def get_aluminum_current_price() -> Dict:
+ today = datetime.now().strftime("%Y-%m-%d")
+ seed = _daily_seed(today)
+ random.seed(int(seed * 1_000_000))
+
+ price = BASE_PRICE + (seed - 0.5) * PRICE_VOLATILITY * 2
+ price = round(price, 0)
+
+ yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
+ prev_seed = _daily_seed(yesterday)
+ prev_price = BASE_PRICE + (prev_seed - 0.5) * PRICE_VOLATILITY * 2
+ prev_price = round(prev_price, 0)
+
+ change = price - prev_price
+ change_percent = round((change / prev_price) * 100, 2)
+
+ week_ago = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
+ week_seed = _daily_seed(week_ago)
+ week_price = BASE_PRICE + (week_seed - 0.5) * PRICE_VOLATILITY * 2
+
+ random.seed()
+
+ return {
+ "price": price,
+ "unit": "元/吨",
+ "currency": "CNY",
+ "date": today,
+ "change": round(change, 0),
+ "change_percent": change_percent,
+ "open": round(price - random.uniform(10, 50), 0),
+ "high": round(price + random.uniform(10, 60), 0),
+ "low": round(price - random.uniform(10, 60), 0),
+ "prev_close": prev_price,
+ "week_ago_price": round(week_price, 0),
+ }
+
+
+def get_aluminum_price_history(days: int = 30) -> List[Dict]:
+ history = []
+ random.seed(42)
+
+ price_line = BASE_PRICE
+ for i in range(days, -1, -1):
+ date = (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d")
+ date_seed = _daily_seed(date)
+
+ drift = (date_seed - 0.5) * TREND_DRIFT
+ noise = (date_seed - 0.5) * PRICE_VOLATILITY * 1.5
+ price_line = price_line + drift + noise * 0.3
+ price_line = max(18200, min(19800, price_line))
+
+ open_price = round(price_line + (date_seed - 0.5) * 80, 0)
+ high_price = round(open_price + abs(date_seed - 0.5) * 160, 0)
+ low_price = round(open_price - abs(date_seed - 0.5) * 140, 0)
+ close_price = round(price_line, 0)
+
+ history.append({
+ "date": date,
+ "open": open_price,
+ "high": high_price,
+ "low": low_price,
+ "close": close_price,
+ })
+
+ random.seed()
+ return history
diff --git a/src/services/verification_service.py b/src/services/verification_service.py
index a914868..725113f 100644
--- a/src/services/verification_service.py
+++ b/src/services/verification_service.py
@@ -294,7 +294,7 @@ class GeometryVerificationService:
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties
from OCC.Core.Bnd import Bnd_Box
- from OCC.Core.BRepBndLib import brepbndlib_Add
+ from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX, TopAbs_SOLID
@@ -327,7 +327,7 @@ class GeometryVerificationService:
# 计算边界框
bbox = Bnd_Box()
- brepbndlib_Add(shape, bbox)
+ brepbndlib.Add(shape, bbox)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
# 拓扑统计
@@ -393,7 +393,7 @@ class GeometryVerificationService:
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties
from OCC.Core.Bnd import Bnd_Box
- from OCC.Core.BRepBndLib import brepbndlib_Add
+ from OCC.Core.BRepBndLib import brepbndlib
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX, TopAbs_SOLID
@@ -410,7 +410,7 @@ class GeometryVerificationService:
# 计算边界框
bbox = Bnd_Box()
- brepbndlib_Add(shape, bbox)
+ brepbndlib.Add(shape, bbox)
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
# 拓扑统计
diff --git a/static/index.html b/static/index.html
index d8b4cd2..0356fa8 100644
--- a/static/index.html
+++ b/static/index.html
@@ -76,7 +76,8 @@
+
-
+