| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- from typing import Optional, List, Dict, Any
- from mysql.connector.pooling import PooledMySQLConnection
- import json
- from datetime import datetime
- from app.core.config import settings
- from app.utils.scheme import CardImageResponse, CardType, SortBy, SortOrder
- from app.utils.card_score_calculate import calculate_scores_from_images
- def update_card_scores_and_status(db_conn: PooledMySQLConnection, card_id: int):
- """
- 根据卡牌关联的图片数量和内容,更新cards表中的分数和状态。
- - 如果图片满4张,计算并更新 detection_score, modified_score, is_edited。
- - 如果图片不足4张,将分数置为NULL,is_edited置为False。
- - 自动更新 updated_at 字段。
- """
- with db_conn.cursor(dictionary=True) as cursor:
- # 1. 获取所有关联图片
- query_images = f"SELECT * FROM {settings.DB_IMAGE_TABLE_NAME} WHERE card_id = %s"
- cursor.execute(query_images, (card_id,))
- image_records = cursor.fetchall()
- # 将数据库行转换为Pydantic模型,便于处理JSON
- images = [CardImageResponse.model_validate(row) for row in image_records]
- # 2. 计算分数和状态
- scores_data = calculate_scores_from_images(images)
- # 3. 更新 cards 表
- # 注意: updated_at 会由数据库自动更新
- query_update_card = (
- f"UPDATE {settings.DB_CARD_TABLE_NAME} SET "
- "detection_score = %s, modified_score = %s, is_edited = %s, updated_at = %s "
- "WHERE id = %s"
- )
- params = (
- scores_data["detection_score"],
- scores_data["modified_score"],
- scores_data["is_edited"],
- datetime.now(), # 手动更新时间戳以确保触发
- card_id,
- )
- cursor.execute(query_update_card, params)
- db_conn.commit()
- def get_card_with_details(db_conn: PooledMySQLConnection, card_id: int) -> Optional[Dict[str, Any]]:
- """获取单个卡牌的完整信息,包括预计算的分数和所有图片详情。"""
- with db_conn.cursor(dictionary=True) as cursor:
- # 1. 获取卡牌信息
- query_card = f"SELECT * FROM {settings.DB_CARD_TABLE_NAME} WHERE id = %s"
- cursor.execute(query_card, (card_id,))
- card_data = cursor.fetchone()
- if not card_data:
- return None
- # 2. 获取所有关联的图片信息
- query_images = f"SELECT * FROM {settings.DB_IMAGE_TABLE_NAME} WHERE card_id = %s"
- cursor.execute(query_images, (card_id,))
- image_records = cursor.fetchall()
- images = [CardImageResponse.model_validate(row) for row in image_records]
- # 3. 获取分数详情 (如果需要)
- # 只有当图片满4张时,分数详情才有意义
- score_details = calculate_scores_from_images(images)
- card_data.update({
- "images": images,
- "detection_score_detail": score_details["detection_score_detail"],
- "modified_score_detail": score_details["modified_score_detail"]
- })
- return card_data
- def get_card_list_with_images(
- db_conn: PooledMySQLConnection,
- card_name: Optional[str],
- card_type: Optional[CardType],
- sort_by: SortBy,
- sort_order: SortOrder,
- skip: int,
- limit: int
- ) -> List[Dict[str, Any]]:
- """获取带筛选和排序功能的卡牌列表,并附带其关联的图片信息。"""
- with db_conn.cursor(dictionary=True) as cursor:
- # 1. 构建动态查询
- query = f"SELECT * FROM {settings.DB_CARD_TABLE_NAME}"
- conditions = []
- params = []
- if card_name:
- conditions.append("card_name LIKE %s")
- params.append(f"%{card_name}%")
- if card_type:
- conditions.append("card_type = %s")
- params.append(card_type.value)
- if conditions:
- query += " WHERE " + " AND ".join(conditions)
- # 添加排序和分页
- query += f" ORDER BY {sort_by.value} {sort_order.value}, id DESC"
- query += " LIMIT %s OFFSET %s"
- params.extend([limit, skip])
- cursor.execute(query, tuple(params))
- cards = cursor.fetchall()
- if not cards:
- return []
- # 2. 一次性获取所有相关卡牌的图片 (避免 N+1 查询)
- card_ids = [card['id'] for card in cards]
- # 使用 IN 子句和占位符
- format_strings = ','.join(['%s'] * len(card_ids))
- image_query = (
- f"SELECT id, card_id, image_type, image_path, detection_image_path, modified_image_path "
- f"FROM {settings.DB_IMAGE_TABLE_NAME} WHERE card_id IN ({format_strings})"
- )
- cursor.execute(image_query, tuple(card_ids))
- images = cursor.fetchall()
- # 3. 将图片按 card_id 分组
- images_by_card_id = {}
- for image in images:
- cid = image['card_id']
- if cid not in images_by_card_id:
- images_by_card_id[cid] = []
- images_by_card_id[cid].append(image)
- # 4. 将图片附加到对应的卡牌上
- for card in cards:
- # 初始化新的字典字段
- card['image_path_list'] = {}
- card['detection_image_path_list'] = {}
- card['modified_image_path_list'] = {}
- # 获取与当前卡牌关联的所有图片记录
- related_images = images_by_card_id.get(card['id'], [])
- # 遍历图片记录,用 image_type 作为 key 来填充三个字典
- for image_data in related_images:
- image_type = image_data['image_type'] # e.g., 'front_face'
- if image_type: # 确保 image_type 不为空
- card['image_path_list'][image_type] = image_data.get('image_path')
- card['detection_image_path_list'][image_type] = image_data.get('detection_image_path')
- card['modified_image_path_list'][image_type] = image_data.get('modified_image_path')
- return cards
|