Browse Source

分数支持14种类型以及card表优化

袁威 1 week ago
parent
commit
b49ea29aa1
3 changed files with 120 additions and 96 deletions
  1. 31 0
      app/api/auto_import.py
  2. 2 0
      app/crud/crud_card.py
  3. 87 96
      app/utils/card_score_calculate.py

+ 31 - 0
app/api/auto_import.py

@@ -167,6 +167,34 @@ async def _pregenerate_defect_images(card_id: int) -> None:
         logger.error("预生成缺陷裁图失败 card_id=%s error=%s", card_id, e, exc_info=True)
 
 
+async def _update_card_scores(card_id: int) -> None:
+    """
+    导入后回写 cards 表分数字段。
+    失败不影响导入主流程,避免把分数刷新异常扩大成导入失败。
+    """
+    def _run() -> None:
+        from app.core import database_loader
+        from app.crud import crud_card
+
+        if database_loader.db_connection_pool is None:
+            logger.warning("更新卡牌分数跳过:数据库连接池未初始化 card_id=%s", card_id)
+            return
+
+        db_conn = None
+        try:
+            db_conn = database_loader.db_connection_pool.get_connection()
+            crud_card.update_card_scores_and_status(db_conn, card_id)
+        finally:
+            if db_conn and db_conn.is_connected():
+                db_conn.close()
+
+    try:
+        from fastapi.concurrency import run_in_threadpool
+        await run_in_threadpool(_run)
+    except Exception as e:
+        logger.error("更新卡牌分数失败 card_id=%s error=%s", card_id, e, exc_info=True)
+
+
 def _resolve_internal_base_url(request: Request) -> str:
     """
     导入流程内部调用本服务时优先走集群内地址,避免经 ingress 再次鉴权导致 401。
@@ -545,6 +573,9 @@ async def _run_import_flow(
             "yes" if side_score_json["back"] is not None else "no",
         )
 
+        # 导入完成后回写 cards 表 detection/modified 分数字段
+        await _update_card_scores(card_id)
+
         # 导入阶段预生成缺陷裁图,保证后续查询无需实时裁图。
         # 失败不影响导入主流程,查询时会自动回退到实时裁图。
         await _pregenerate_defect_images(card_id)

+ 2 - 0
app/crud/crud_card.py

@@ -215,6 +215,8 @@ def get_card_with_details(db_conn: PooledMySQLConnection, card_id: int) -> Optio
 
         card_data.update({
             "images": final_images_list,  # 包含所有图片
+            "detection_score": score_details["detection_score"],
+            "modified_score": score_details["modified_score"],
             "detection_score_detail": score_details["detection_score_detail"],
             "modified_score_detail": score_details["modified_score_detail"]
         })

+ 87 - 96
app/utils/card_score_calculate.py

@@ -1,14 +1,16 @@
 from app.utils.scheme import ImageType, CardImageResponse
 from app.core.logger import get_logger
-from typing import List, Dict, Any
+from typing import List, Dict, Any, Optional
 
 logger = get_logger(__name__)
 
 
 def calculate_scores_from_images(images: List[CardImageResponse]) -> Dict[str, Any]:
     """
-    根据图片计算分数。
-    已更新为新的类型名称: front_coaxial, front_ring 等。
+    根据图片计算分数(兼容 14 图与历史 8 图)。
+    - 必要条件:front_ring + back_ring 存在
+    - face 分数来源:同面 stripe(1~4) / coaxial / fusion,按可用图累加
+    - 不再依赖 coaxial 必须存在
     """
     scores = {
         "detection_score": None,
@@ -28,13 +30,50 @@ def calculate_scores_from_images(images: List[CardImageResponse]) -> Dict[str, A
         "is_edited": False,
     }
 
-    # 过滤掉灰度图,只计算主4张图
-    valid_images = [img for img in images if img.image_type in [
-        ImageType.front_coaxial, ImageType.back_coaxial,
-        ImageType.front_ring, ImageType.back_ring
-    ]]
+    def _as_float(value: Any) -> float:
+        try:
+            return float(value or 0)
+        except (TypeError, ValueError):
+            return 0.0
+
+    def _get_reducts(src: Dict[str, Any], side: str) -> Dict[str, float]:
+        defect_result = src.get("result", {}).get("defect_result", {})
+        center_result = src.get("result", {}).get("center_result", {})
+        return {
+            "total": _as_float(src.get("result", {}).get("_used_compute_deduct_score", 0)),
+            "center": _as_float(center_result.get("deduct_score", 0)),
+            "corner": _as_float(defect_result.get(f"{side}_corner_deduct_score", 0)),
+            "edge": _as_float(defect_result.get(f"{side}_edge_deduct_score", 0)),
+            "face": _as_float(defect_result.get(f"{side}_face_deduct_score", 0)),
+        }
 
-    if len(valid_images) != 4:
+    front_ring: Optional[CardImageResponse] = None
+    back_ring: Optional[CardImageResponse] = None
+    front_face_imgs: List[CardImageResponse] = []
+    back_face_imgs: List[CardImageResponse] = []
+
+    for img in images:
+        image_type = img.image_type
+        if image_type == ImageType.front_ring:
+            front_ring = img
+        elif image_type == ImageType.back_ring:
+            back_ring = img
+
+        if image_type in [
+            ImageType.front_stripe1, ImageType.front_stripe2,
+            ImageType.front_stripe3, ImageType.front_stripe4,
+            ImageType.front_coaxial, ImageType.front_fusion,
+        ]:
+            front_face_imgs.append(img)
+        elif image_type in [
+            ImageType.back_stripe1, ImageType.back_stripe2,
+            ImageType.back_stripe3, ImageType.back_stripe4,
+            ImageType.back_coaxial, ImageType.back_fusion,
+        ]:
+            back_face_imgs.append(img)
+
+    # ring 是当前模型计算中心/角/边的必要数据
+    if not front_ring or not back_ring:
         return scores
 
     try:
@@ -45,53 +84,26 @@ def calculate_scores_from_images(images: List[CardImageResponse]) -> Dict[str, A
         detection_edge_score = 10.0
         detection_face_score = 10.0
 
-        for img in valid_images:
+        for ring_img, side in ((front_ring, "front"), (back_ring, "back")):
             try:
-                add_val = img.detection_json.get("result", {}).get("_used_compute_deduct_score", 0)
-                detection_score += float(add_val or 0)
-
-                if img.image_type == ImageType.front_ring:
-                    center_reduct_val = img.detection_json.get("result", {}).get("center_result", {}).get(
-                        "deduct_score", 0)
-                    corner_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "front_corner_deduct_score", 0)
-                    edge_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "front_edge_deduct_score", 0)
-                    face_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "front_face_deduct_score", 0)
-
-                    detection_center_score += float(center_reduct_val or 0)
-                    detection_corner_score += float(corner_reduct_val or 0)
-                    detection_edge_score += float(edge_reduct_val or 0)
-                    detection_face_score += float(face_reduct_val or 0)
-
-                elif img.image_type == ImageType.back_ring:
-                    center_reduct_val = img.detection_json.get("result", {}).get("center_result", {}).get(
-                        "deduct_score", 0)
-                    corner_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "back_corner_deduct_score", 0)
-                    edge_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "back_edge_deduct_score", 0)
-                    face_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "back_face_deduct_score", 0)
-
-                    detection_center_score += float(center_reduct_val or 0)
-                    detection_corner_score += float(corner_reduct_val or 0)
-                    detection_edge_score += float(edge_reduct_val or 0)
-                    detection_face_score += float(face_reduct_val or 0)
-
-                elif img.image_type == ImageType.front_coaxial:
-                    face_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "front_face_deduct_score", 0)
-                    detection_face_score += float(face_reduct_val or 0)
-
-                elif img.image_type == ImageType.back_coaxial:  # 原 back_face
-                    face_reduct_val = img.detection_json.get("result", {}).get("defect_result", {}).get(
-                        "back_face_deduct_score", 0)
-                    detection_face_score += float(face_reduct_val or 0)
-
+                reducts = _get_reducts(ring_img.detection_json or {}, side)
+                detection_score += reducts["total"]
+                detection_center_score += reducts["center"]
+                detection_corner_score += reducts["corner"]
+                detection_edge_score += reducts["edge"]
+                detection_face_score += reducts["face"]
             except Exception as e:
-                logger.warning(f"解析 detection_json 分数失败 (image_id={img.id}): {e}")
+                logger.warning(f"解析 detection_json 分数失败 (image_id={ring_img.id}): {e}")
+
+        # 同面 face 辅助图(stripe/coaxial/fusion)叠加 face 与 total
+        for side, face_imgs in (("front", front_face_imgs), ("back", back_face_imgs)):
+            for img in face_imgs:
+                try:
+                    reducts = _get_reducts(img.detection_json or {}, side)
+                    detection_score += reducts["total"]
+                    detection_face_score += reducts["face"]
+                except Exception as e:
+                    logger.warning(f"解析 detection_json 分数失败 (image_id={img.id}): {e}")
 
         scores["detection_score"] = detection_score
         scores["detection_score_detail"] = {
@@ -108,53 +120,32 @@ def calculate_scores_from_images(images: List[CardImageResponse]) -> Dict[str, A
         modified_edge_score = 10.0
         modified_face_score = 10.0
 
-        is_edited = any(img.modified_json is not None for img in valid_images)
+        all_score_images = [front_ring, back_ring] + front_face_imgs + back_face_imgs
+        is_edited = any(img.modified_json is not None for img in all_score_images if img is not None)
         scores["is_edited"] = is_edited
 
         if is_edited:
-            for img in valid_images:
-                src = img.modified_json if img.modified_json is not None else img.detection_json
+            for ring_img, side in ((front_ring, "front"), (back_ring, "back")):
+                src = ring_img.modified_json if ring_img.modified_json is not None else ring_img.detection_json
                 try:
-                    add_val = src.get("result", {}).get("_used_compute_deduct_score", 0)
-                    modified_score += float(add_val or 0)
-
-                    if img.image_type == ImageType.front_ring:
-                        center_reduct_val = src.get("result", {}).get("center_result", {}).get("deduct_score", 0)
-                        corner_reduct_val = src.get("result", {}).get("defect_result", {}).get(
-                            "front_corner_deduct_score", 0)
-                        edge_reduct_val = src.get("result", {}).get("defect_result", {}).get("front_edge_deduct_score",
-                                                                                             0)
-                        face_reduct_val = src.get("result", {}).get("defect_result", {}).get("front_face_deduct_score",
-                                                                                             0)
-
-                        modified_center_score += float(center_reduct_val or 0)
-                        modified_corner_score += float(corner_reduct_val or 0)
-                        modified_edge_score += float(edge_reduct_val or 0)
-                        modified_face_score += float(face_reduct_val or 0)
-                    elif img.image_type == ImageType.back_ring:
-                        center_reduct_val = src.get("result", {}).get("center_result", {}).get("deduct_score", 0)
-                        corner_reduct_val = src.get("result", {}).get("defect_result", {}).get(
-                            "back_corner_deduct_score", 0)
-                        edge_reduct_val = src.get("result", {}).get("defect_result", {}).get("back_edge_deduct_score",
-                                                                                             0)
-                        face_reduct_val = src.get("result", {}).get("defect_result", {}).get("back_face_deduct_score",
-                                                                                             0)
-
-                        modified_center_score += float(center_reduct_val or 0)
-                        modified_corner_score += float(corner_reduct_val or 0)
-                        modified_edge_score += float(edge_reduct_val or 0)
-                        modified_face_score += float(face_reduct_val or 0)
-                    elif img.image_type == ImageType.front_coaxial:
-                        face_reduct_val = src.get("result", {}).get("defect_result", {}).get("front_face_deduct_score",
-                                                                                             0)
-                        modified_face_score += float(face_reduct_val or 0)
-                    elif img.image_type == ImageType.back_coaxial:
-                        face_reduct_val = src.get("result", {}).get("defect_result", {}).get("back_face_deduct_score",
-                                                                                             0)
-                        modified_face_score += float(face_reduct_val or 0)
-
+                    reducts = _get_reducts(src or {}, side)
+                    modified_score += reducts["total"]
+                    modified_center_score += reducts["center"]
+                    modified_corner_score += reducts["corner"]
+                    modified_edge_score += reducts["edge"]
+                    modified_face_score += reducts["face"]
                 except Exception as e:
-                    logger.warning(f"解析 modified_json 分数失败 (image_id={img.id}): {e}")
+                    logger.warning(f"解析 modified_json 分数失败 (image_id={ring_img.id}): {e}")
+
+            for side, face_imgs in (("front", front_face_imgs), ("back", back_face_imgs)):
+                for img in face_imgs:
+                    src = img.modified_json if img.modified_json is not None else img.detection_json
+                    try:
+                        reducts = _get_reducts(src or {}, side)
+                        modified_score += reducts["total"]
+                        modified_face_score += reducts["face"]
+                    except Exception as e:
+                        logger.warning(f"解析 modified_json 分数失败 (image_id={img.id}): {e}")
 
             scores["modified_score"] = modified_score
             scores["modified_score_detail"] = {