Sfoglia il codice sorgente

数据库增加一列json, 原来的作为原生数据

AnlaAnla 2 mesi fa
parent
commit
2b183e1ccf

+ 7 - 0
.idea/deployment.xml

@@ -3,6 +3,13 @@
   <component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
   <component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
     <serverData>
     <serverData>
       <paths name="192.168.31.243">
       <paths name="192.168.31.243">
+        <serverdata>
+          <mappings>
+            <mapping deploy="/media/martin/DATA/_ML/RemoteProject/CardScoreDataServer" local="$PROJECT_DIR$" web="/" />
+          </mappings>
+        </serverdata>
+      </paths>
+      <paths name="martin@192.168.77.66:22 password">
         <serverdata>
         <serverdata>
           <mappings>
           <mappings>
             <mapping local="$PROJECT_DIR$" web="/" />
             <mapping local="$PROJECT_DIR$" web="/" />

+ 1 - 0
.idea/encodings.xml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
 <project version="4">
   <component name="Encoding">
   <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/Test/test01.py" charset="GBK" />
     <file url="PROJECT" charset="GBK" />
     <file url="PROJECT" charset="GBK" />
   </component>
   </component>
 </project>
 </project>

+ 27 - 0
Test/test01.py

@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+import requests
+
+url = 'http://192.168.31.243:7745/api/image_data/data_list'
+params = {
+    'start_id': 5,
+    'end_id': 9,
+    'skip': 0,
+    'limit': 100
+}
+headers = {
+    'accept': 'application/json'
+}
+
+
+
+try:
+    response = requests.get(url, params=params, headers=headers)
+    response.raise_for_status()
+
+    # 打印响应内容
+    print("Status Code:", response.status_code)
+    print("Response JSON:", response.json()) # 如果响应是JSON格式
+    # print("Response Text:", response.text) # 如果响应是纯文本
+
+except requests.exceptions.RequestException as e:
+    print(f"An error occurred: {e}")

+ 13 - 0
Test/数据库测试.py

@@ -0,0 +1,13 @@
+from app.core.config import settings
+import mysql.connector
+
+if __name__ == '__main__':
+    cnx = mysql.connector.connect(**settings.DATABASE_CONFIG_WITH_DB)
+    cursor = cnx.cursor()
+
+
+    query = f"SELECT IFNULL(img_name, img_result_json) AS img_result_json FROM {settings.DB_TABLE_NAME} WHERE img_id = %s"
+    cursor.execute(query, (3,))
+    result = cursor.fetchone()
+
+    print(result)

+ 15 - 43
app/api/image_data.py

@@ -6,11 +6,11 @@ from typing import Optional, Dict, Any, List
 
 
 from fastapi import APIRouter, File, UploadFile, Depends, HTTPException, Form, Query
 from fastapi import APIRouter, File, UploadFile, Depends, HTTPException, Form, Query
 from fastapi.responses import JSONResponse, FileResponse
 from fastapi.responses import JSONResponse, FileResponse
-from pydantic import BaseModel, field_validator
 from mysql.connector.pooling import PooledMySQLConnection
 from mysql.connector.pooling import PooledMySQLConnection
 
 
 from app.core.config import settings
 from app.core.config import settings
 from app.core.logger import get_logger
 from app.core.logger import get_logger
+from app.utils.scheme import ImageRecordResponse, map_row_to_model
 from app.core.database_loader import get_db_connection
 from app.core.database_loader import get_db_connection
 
 
 # --- 初始化 ---
 # --- 初始化 ---
@@ -21,40 +21,6 @@ router = APIRouter()
 db_dependency = Depends(get_db_connection)
 db_dependency = Depends(get_db_connection)
 
 
 
 
-# --- Pydantic 数据模型 ---
-class ImageRecordResponse(BaseModel):
-    """用于API响应的数据模型,确保数据结构一致"""
-    img_id: int
-    img_name: Optional[str] = None
-    img_path: str
-    img_result_json: Dict[str, Any]
-    created_at: datetime
-
-    @field_validator('img_result_json', mode='before')
-    @classmethod
-    def parse_json_string(cls, v):
-        """
-        这个验证器会在Pydantic进行类型检查之前运行 (因为 pre=True)。
-        它负责将从数据库取出的JSON字符串转换为Python字典。
-        """
-        if isinstance(v, str):
-            try:
-                return json.loads(v)
-            except json.JSONDecodeError:
-                # 如果数据库中的JSON格式错误,则抛出异常
-                raise ValueError("Invalid JSON string in database")
-        return v
-    # --- FIX ENDS HERE ---
-
-
-# --- 辅助函数 ---
-def map_row_to_model(row: tuple, columns: List[str]) -> ImageRecordResponse:
-    """将数据库查询出的一行数据映射到Pydantic模型"""
-    row_dict = dict(zip(columns, row))
-    # 现在当 ImageRecordResponse 被调用时,上面的验证器会先生效
-    return ImageRecordResponse(**row_dict)
-
-
 # --- API 接口实现 ---
 # --- API 接口实现 ---
 
 
 #  1: 存储图片和JSON数据
 #  1: 存储图片和JSON数据
@@ -217,8 +183,6 @@ def get_records_by_name(img_name: str, db_conn: PooledMySQLConnection = db_depen
             cursor.close()
             cursor.close()
 
 
 
 
-# ... (其他接口代码保持不变) ...
-
 #  5: 修改JSON数据
 #  5: 修改JSON数据
 @router.put("/update/json/{img_id}", status_code=200, summary="5. 修改指定ID记录的JSON数据")
 @router.put("/update/json/{img_id}", status_code=200, summary="5. 修改指定ID记录的JSON数据")
 def update_record_json(
 def update_record_json(
@@ -231,7 +195,7 @@ def update_record_json(
     try:
     try:
         cursor = db_conn.cursor()
         cursor = db_conn.cursor()
         new_json_str = json.dumps(new_json_data, ensure_ascii=False)
         new_json_str = json.dumps(new_json_data, ensure_ascii=False)
-        query = f"UPDATE {settings.DB_TABLE_NAME} SET img_result_json = %s WHERE img_id = %s"
+        query = f"UPDATE {settings.DB_TABLE_NAME} SET img_result_json_new = %s WHERE img_id = %s"
         cursor.execute(query, (new_json_str, img_id))
         cursor.execute(query, (new_json_str, img_id))
 
 
         if cursor.rowcount == 0:
         if cursor.rowcount == 0:
@@ -282,18 +246,26 @@ def get_image_file(img_id: int, db_conn: PooledMySQLConnection = db_dependency):
 #  7: 获取JSON数据
 #  7: 获取JSON数据
 @router.get("/json/{img_id}", summary="7. 获取指定ID的JSON数据")
 @router.get("/json/{img_id}", summary="7. 获取指定ID的JSON数据")
 def get_record_json(img_id: int, db_conn: PooledMySQLConnection = db_dependency):
 def get_record_json(img_id: int, db_conn: PooledMySQLConnection = db_dependency):
-    """根据img_id查找记录,并仅返回其JSON数据部分。"""
+    """
+    根据img_id查找记录,并仅返回其JSON数据部分。
+    优先返回 img_result_json_new,如果为 NULL 则返回 img_result_json。
+    """
     cursor = None
     cursor = None
     try:
     try:
         cursor = db_conn.cursor()
         cursor = db_conn.cursor()
-        query = f"SELECT img_result_json FROM {settings.DB_TABLE_NAME} WHERE img_id = %s"
+        query = (
+            f"SELECT IFNULL({settings.DB_TABLE_NAME}.img_result_json_new, {settings.DB_TABLE_NAME}.img_result_json) AS img_result_json "
+            f"FROM {settings.DB_TABLE_NAME} WHERE img_id = %s"
+        )
         cursor.execute(query, (img_id,))
         cursor.execute(query, (img_id,))
-        result = cursor.fetchone()
+        result = cursor.fetchone()  # result will be a tuple like ('{"key": "value"}',)
         if not result:
         if not result:
             raise HTTPException(status_code=404, detail=f"ID为 {img_id} 的记录未找到。")
             raise HTTPException(status_code=404, detail=f"ID为 {img_id} 的记录未找到。")
 
 
-        json_data = json.loads(result[0])
-        return JSONResponse(content=json_data)
+        # result[0] 已经是经过 IFNULL 处理后的目标 JSON 字符串
+        json_to_return = json.loads(result[0])
+
+        return JSONResponse(content=json_to_return)
     except Exception as e:
     except Exception as e:
         logger.error(f"获取JSON失败 ({img_id}): {e}")
         logger.error(f"获取JSON失败 ({img_id}): {e}")
         if isinstance(e, HTTPException): raise e
         if isinstance(e, HTTPException): raise e

+ 1 - 0
app/core/database_loader.py

@@ -34,6 +34,7 @@ def init_database():
             "  `img_name` VARCHAR(255) NULL,"
             "  `img_name` VARCHAR(255) NULL,"
             "  `img_path` VARCHAR(512) NOT NULL,"
             "  `img_path` VARCHAR(512) NOT NULL,"
             "  `img_result_json` JSON NOT NULL,"
             "  `img_result_json` JSON NOT NULL,"
+            "  `img_result_json_new` JSON NULL,"
             "  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
             "  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
             ") ENGINE=InnoDB"
             ") ENGINE=InnoDB"
         )
         )

+ 49 - 0
app/utils/scheme.py

@@ -0,0 +1,49 @@
+import os
+import uuid
+import json
+from datetime import date, datetime
+from typing import Optional, Dict, Any, List
+from pydantic import BaseModel, field_validator
+
+
+# --- Pydantic 数据模型 ---
+class ImageRecordResponse(BaseModel):
+    """用于API响应的数据模型,确保数据结构一致"""
+    img_id: int
+    img_name: Optional[str] = None
+    img_path: str
+    img_result_json: Dict[str, Any] # 这个字段在响应中代表的是 img_result_json_new 或 img_result_json 的实际值
+    created_at: datetime
+
+    @field_validator('img_result_json', mode='before')
+    @classmethod
+    def parse_json_string(cls, v):
+        """
+        这个验证器会在Pydantic进行类型检查之前运行 (因为 pre=True)。
+        它负责将从数据库取出的JSON字符串转换为Python字典。
+        """
+        if isinstance(v, str):
+            try:
+                return json.loads(v)
+            except json.JSONDecodeError:
+                # 如果数据库中的JSON格式错误,则抛出异常
+                raise ValueError("Invalid JSON string in database")
+        return v
+
+
+def map_row_to_model(row: tuple, columns: List[str]) -> ImageRecordResponse:
+    """
+    将数据库查询出的一行数据映射到Pydantic模型。
+    优先使用 img_result_json_new,如果它为 NULL,则使用 img_result_json。
+    """
+    row_dict = dict(zip(columns, row))
+
+    # Pydantic 字段名为 img_result_json
+    if 'img_result_json_new' in row_dict and row_dict['img_result_json_new'] is not None:
+        row_dict['img_result_json'] = row_dict['img_result_json_new']
+
+    # 移除 img_result_json_new 字段,因为它不属于 ImageRecordResponse 模型
+    if 'img_result_json_new' in row_dict:
+        del row_dict['img_result_json_new']
+
+    return ImageRecordResponse(**row_dict)