|
@@ -2,6 +2,8 @@ from fastapi import APIRouter, HTTPException, Depends, Query
|
|
|
from mysql.connector.pooling import PooledMySQLConnection
|
|
from mysql.connector.pooling import PooledMySQLConnection
|
|
|
|
|
|
|
|
import io
|
|
import io
|
|
|
|
|
+import json
|
|
|
|
|
+from datetime import datetime
|
|
|
from app.core.minio_client import minio_client
|
|
from app.core.minio_client import minio_client
|
|
|
from typing import List, Dict, Any, Optional
|
|
from typing import List, Dict, Any, Optional
|
|
|
from PIL import Image
|
|
from PIL import Image
|
|
@@ -16,6 +18,51 @@ logger = get_logger(__name__)
|
|
|
router = APIRouter()
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _parse_json_value(raw_value: Any) -> Dict[str, Any]:
|
|
|
|
|
+ if raw_value is None:
|
|
|
|
|
+ return {}
|
|
|
|
|
+ if isinstance(raw_value, dict):
|
|
|
|
|
+ return raw_value
|
|
|
|
|
+ if isinstance(raw_value, str):
|
|
|
|
|
+ try:
|
|
|
|
|
+ return json.loads(raw_value)
|
|
|
|
|
+ except json.JSONDecodeError:
|
|
|
|
|
+ return {}
|
|
|
|
|
+ return {}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _format_datetime(dt: Any) -> str:
|
|
|
|
|
+ if isinstance(dt, datetime):
|
|
|
|
|
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
+ return ""
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _save_rating_report_history(
|
|
|
|
|
+ db_conn: PooledMySQLConnection,
|
|
|
|
|
+ card_id: int,
|
|
|
|
|
+ card_no: str,
|
|
|
|
|
+ report_name: str,
|
|
|
|
|
+ report_json: Dict[str, Any]
|
|
|
|
|
+) -> int:
|
|
|
|
|
+ try:
|
|
|
|
|
+ with db_conn.cursor() as cursor:
|
|
|
|
|
+ insert_sql = (
|
|
|
|
|
+ f"INSERT INTO {settings.RATING_REPORT_HISTORY_TABLE_NAME} "
|
|
|
|
|
+ "(card_id, cardNo, report_name, report_json) "
|
|
|
|
|
+ "VALUES (%s, %s, %s, %s)"
|
|
|
|
|
+ )
|
|
|
|
|
+ cursor.execute(
|
|
|
|
|
+ insert_sql,
|
|
|
|
|
+ (card_id, card_no, report_name, json.dumps(report_json, ensure_ascii=False))
|
|
|
|
|
+ )
|
|
|
|
|
+ db_conn.commit()
|
|
|
|
|
+ return cursor.lastrowid
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ db_conn.rollback()
|
|
|
|
|
+ logger.error(f"保存评级报告历史失败: {e}")
|
|
|
|
|
+ raise HTTPException(status_code=500, detail="保存评级报告历史失败。")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _get_active_json(image_data: Any) -> Optional[Dict]:
|
|
def _get_active_json(image_data: Any) -> Optional[Dict]:
|
|
|
"""获取有效的json数据,优先 modified_json"""
|
|
"""获取有效的json数据,优先 modified_json"""
|
|
|
if not image_data:
|
|
if not image_data:
|
|
@@ -117,8 +164,9 @@ def generate_rating_report(
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
card_id = row[0]
|
|
card_id = row[0]
|
|
|
|
|
+ rating_time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
|
|
|
|
|
|
- top_n_defects = 3
|
|
|
|
|
|
|
+ # top_n_defects = 3
|
|
|
"""
|
|
"""
|
|
|
根据 Card ID 生成评级报告 JSON
|
|
根据 Card ID 生成评级报告 JSON
|
|
|
"""
|
|
"""
|
|
@@ -237,7 +285,8 @@ def generate_rating_report(
|
|
|
# 按实际面积从大到小排序
|
|
# 按实际面积从大到小排序
|
|
|
all_defects_collected.sort(key=lambda x: x["area"], reverse=True)
|
|
all_defects_collected.sort(key=lambda x: x["area"], reverse=True)
|
|
|
|
|
|
|
|
- top_defects = all_defects_collected[:top_n_defects]
|
|
|
|
|
|
|
+ # top_defects = all_defects_collected[:top_n_defects]
|
|
|
|
|
+ top_defects = all_defects_collected
|
|
|
|
|
|
|
|
final_defect_list = []
|
|
final_defect_list = []
|
|
|
for idx, item in enumerate(top_defects, start=1):
|
|
for idx, item in enumerate(top_defects, start=1):
|
|
@@ -249,7 +298,7 @@ def generate_rating_report(
|
|
|
d_id = idx # 1, 2, 3
|
|
d_id = idx # 1, 2, 3
|
|
|
|
|
|
|
|
# 构造文件名: {card_id}_{seq_id}.jpg
|
|
# 构造文件名: {card_id}_{seq_id}.jpg
|
|
|
- filename = f"{card_id}_{d_id}.jpg"
|
|
|
|
|
|
|
+ filename = f"{card_id}_{d_id}_{rating_time_now}.jpg"
|
|
|
|
|
|
|
|
# 执行切图
|
|
# 执行切图
|
|
|
min_rect = defect.get("min_rect")
|
|
min_rect = defect.get("min_rect")
|
|
@@ -285,5 +334,84 @@ def generate_rating_report(
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
response_data["defectDetailList"] = final_defect_list
|
|
response_data["defectDetailList"] = final_defect_list
|
|
|
|
|
+ report_name = f"{cardNo}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
|
|
|
+ rating_id = _save_rating_report_history(db_conn, card_id, cardNo, report_name, response_data)
|
|
|
|
|
+ response_data["ratingId"] = rating_id
|
|
|
|
|
+ response_data["reportName"] = report_name
|
|
|
|
|
|
|
|
return response_data
|
|
return response_data
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@router.get("/history", status_code=200, summary="根据 cardNo 查询评级报告历史列表")
|
|
|
|
|
+def get_rating_report_history_list(
|
|
|
|
|
+ cardNo: str,
|
|
|
|
|
+ limit: int = Query(100, ge=1, le=1000),
|
|
|
|
|
+ db_conn: PooledMySQLConnection = Depends(get_db_connection)
|
|
|
|
|
+):
|
|
|
|
|
+ if not cardNo or not cardNo.strip():
|
|
|
|
|
+ raise HTTPException(status_code=400, detail="cardNo 不能为空")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ with db_conn.cursor(dictionary=True) as cursor:
|
|
|
|
|
+ query_sql = (
|
|
|
|
|
+ f"SELECT rating_id, card_id, cardNo, report_name, created_at "
|
|
|
|
|
+ f"FROM {settings.RATING_REPORT_HISTORY_TABLE_NAME} "
|
|
|
|
|
+ "WHERE cardNo = %s "
|
|
|
|
|
+ "ORDER BY rating_id DESC "
|
|
|
|
|
+ "LIMIT %s"
|
|
|
|
|
+ )
|
|
|
|
|
+ cursor.execute(query_sql, (cardNo, limit))
|
|
|
|
|
+ rows = cursor.fetchall()
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"查询评级报告历史列表失败: {e}")
|
|
|
|
|
+ raise HTTPException(status_code=500, detail="查询评级报告历史列表失败。")
|
|
|
|
|
+
|
|
|
|
|
+ history_list = []
|
|
|
|
|
+ for row in rows:
|
|
|
|
|
+ history_list.append({
|
|
|
|
|
+ "ratingId": row.get("rating_id"),
|
|
|
|
|
+ "cardId": row.get("card_id"),
|
|
|
|
|
+ "cardNo": row.get("cardNo"),
|
|
|
|
|
+ "reportName": row.get("report_name"),
|
|
|
|
|
+ "createdAt": _format_datetime(row.get("created_at"))
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "cardNo": cardNo,
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "total": len(history_list),
|
|
|
|
|
+ "list": history_list
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+@router.get("/history/{rating_id}", status_code=200, summary="根据 rating_id 查询单个评级报告历史")
|
|
|
|
|
+def get_rating_report_history_detail(
|
|
|
|
|
+ rating_id: int,
|
|
|
|
|
+ db_conn: PooledMySQLConnection = Depends(get_db_connection)
|
|
|
|
|
+):
|
|
|
|
|
+ try:
|
|
|
|
|
+ with db_conn.cursor(dictionary=True) as cursor:
|
|
|
|
|
+ query_sql = (
|
|
|
|
|
+ f"SELECT rating_id, card_id, cardNo, report_name, report_json, created_at, updated_at "
|
|
|
|
|
+ f"FROM {settings.RATING_REPORT_HISTORY_TABLE_NAME} "
|
|
|
|
|
+ "WHERE rating_id = %s LIMIT 1"
|
|
|
|
|
+ )
|
|
|
|
|
+ cursor.execute(query_sql, (rating_id,))
|
|
|
|
|
+ row = cursor.fetchone()
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logger.error(f"查询评级报告历史详情失败: {e}")
|
|
|
|
|
+ raise HTTPException(status_code=500, detail="查询评级报告历史详情失败。")
|
|
|
|
|
+
|
|
|
|
|
+ if not row:
|
|
|
|
|
+ raise HTTPException(status_code=404, detail=f"未找到 rating_id={rating_id} 的历史记录")
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "ratingId": row.get("rating_id"),
|
|
|
|
|
+ "cardId": row.get("card_id"),
|
|
|
|
|
+ "cardNo": row.get("cardNo"),
|
|
|
|
|
+ "reportName": row.get("report_name"),
|
|
|
|
|
+ "createdAt": _format_datetime(row.get("created_at")),
|
|
|
|
|
+ "updatedAt": _format_datetime(row.get("updated_at")),
|
|
|
|
|
+ "reportData": _parse_json_value(row.get("report_json"))
|
|
|
|
|
+ }
|