Jelajahi Sumber

feat(api): 添加通过货号获取动作名称列表接口

- 新增 /get_action_names_by_goods 接口实现货号到动作名称的查询功能
- 实现按自然排序获取照片记录的 action_id 顺序
- 集成 DeviceConfig 查询获取对应的动作名称列表
- 添加参数验证和错误处理机制
- 移除废弃的自动添加缺失字段功能代码
rambo 1 Minggu lalu
induk
melakukan
c3164bd00f
1 mengubah file dengan 67 tambahan dan 120 penghapusan
  1. 67 120
      python/api.py

+ 67 - 120
python/api.py

@@ -1232,6 +1232,73 @@ def get_photo_record_detail(goods_art_no: str = None):
     }
 
 
+@app.get("/get_action_names_by_goods", description="通过货号获取动作名称列表")
+def get_action_names_by_goods(goods_art_no: str = None):
+    """
+    通过货号查询PhotoRecord表,按自然排序获取action_id,
+    再查询DeviceConfig获取对应的action_name,按顺序返回action_name数组
+    
+    Args:
+        goods_art_no: 货号
+        
+    Returns:
+        如果货号不存在: {"code": 1, "msg": "货号不存在", "data": None}
+        如果货号存在: {"code": 0, "msg": "", "data": [action_name1, action_name2, ...]}
+    """
+    if goods_art_no is None or goods_art_no.strip() == "":
+        return {"code": 1, "msg": "参数错误", "data": None}
+    
+    session = SqlQuery()
+    try:
+        # 1. 查询该货号的所有记录
+        photos = CRUD(PhotoRecord)
+        photo_records = photos.read_all(
+            session, 
+            conditions={"goods_art_no": goods_art_no, "delete_time": None}
+        )
+        
+        # 2. 如果货号不存在,返回错误
+        if not photo_records or len(photo_records) == 0:
+            return {"code": 1, "msg": "货号不存在", "data": None}
+        
+        # 3. 按自然排序对记录进行排序(基于image_index或其他字段)
+        # 使用natsort对image_index进行自然排序
+        from natsort import natsorted, ns
+        
+        # 将记录转换为可排序的列表,按image_index排序
+        sorted_records = natsorted(
+            photo_records, 
+            key=lambda x: str(x.image_index) if x.image_index is not None else "",
+            alg=ns.PATH | ns.IGNORECASE
+        )
+        
+        # 4. 提取排序后的action_id列表
+        action_ids = [record.action_id for record in sorted_records if record.action_id is not None]
+        
+        if not action_ids:
+            return {"code": 0, "msg": "", "data": []}
+        
+        # 5. 根据action_id查询DeviceConfig获取action_name
+        device_config_crud = CRUD(DeviceConfig)
+        action_names = []
+        
+        for action_id in action_ids:
+            device_config = device_config_crud.read(
+                session, 
+                conditions={"id": action_id,'take_picture':True}
+            )
+            if device_config and device_config.action_name:
+                action_names.append(device_config.action_name)
+        
+        return {"code": 0, "msg": "", "data": action_names}
+        
+    except Exception as e:
+        logger.error(f"查询货号 {goods_art_no} 的动作名称失败: {str(e)}")
+        return {"code": 1, "msg": f"查询失败: {str(e)}", "data": None}
+    finally:
+        session.close()
+
+
 @app.post("/delect_goods_arts", description="通过货号删除记录")
 def delect_goods_arts(params: PhotoRecordDelete):
     limit_path = "{}/{}".format(settings.OUTPUT_DIR,
@@ -2077,123 +2144,3 @@ async def import_images_from_dir(params:ImportDirs):
         logger.error(f"API 调用异常: {str(e)}")
         raise UnicornException(f"{str(e)}")
 
-
-def auto_add_missing_columns():
-    """
-    自动检测并添加缺失的数据库字段(打包兼容版本)
-    - 使用原生 SQLite,避免 SQLAlchemy 在打包环境中的问题
-    - 简化字段类型处理,避免复杂类型导致的问题
-    - 只添加新字段,不会修改或删除现有字段
-    - 不会丢失任何数据
-    - 可以安全地重复执行(幂等性)
-    """
-    try:
-        import sqlite3
-
-        # 获取数据库文件路径
-        db_path = sqlite_file_name.replace("sqlite:///", "")
-
-        # 直接连接 SQLite 数据库
-        conn = sqlite3.connect(db_path)
-        cursor = conn.cursor()
-
-        # 定义模型和表名的映射
-        models_tables = [
-            (DeviceConfig, "device_config"),
-            (PhotoRecord, "photo_record"),
-            (DeviceConfigTabs, "device_config_tabs"),
-            (SysConfigs, "sys_configs"),
-        ]
-
-        for model_class, table_name in models_tables:
-            try:
-                # 检查表是否存在
-                cursor.execute(
-                    f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}'")
-                if not cursor.fetchone():
-                    print(f"⚠️ 表 {table_name} 不存在,跳过")
-                    continue
-
-                # 获取现有列
-                cursor.execute(f"PRAGMA table_info({table_name})")
-                existing_columns = {row[1] for row in cursor.fetchall()}
-                print(f"📋 表 {table_name} 现有字段: {existing_columns}")
-
-                # 从模型中获取所有字段
-                model_columns = model_class.__table__.columns
-
-                for column_name, column_obj in model_columns.items():
-                    # 如果列已存在,跳过
-                    if column_name in existing_columns:
-                        continue
-
-                    try:
-                        # 简化类型映射 - 使用 SQLite 原生类型
-                        column_type_str = str(column_obj.type).upper()
-
-                        # 映射 SQLAlchemy 类型到 SQLite 类型
-                        if 'INTEGER' in column_type_str or 'INT' in column_type_str:
-                            sqlite_type = 'INTEGER'
-                        elif 'FLOAT' in column_type_str or 'DOUBLE' in column_type_str or 'DECIMAL' in column_type_str or 'REAL' in column_type_str:
-                            sqlite_type = 'REAL'
-                        elif 'BOOLEAN' in column_type_str or 'BOOL' in column_type_str:
-                            sqlite_type = 'INTEGER'  # SQLite 用 0/1 表示布尔
-                        elif 'DATETIME' in column_type_str or 'TIMESTAMP' in column_type_str:
-                            sqlite_type = 'TEXT'  # SQLite 用 TEXT 存储时间
-                        elif 'VARCHAR' in column_type_str or 'CHAR' in column_type_str or 'STRING' in column_type_str or 'TEXT' in column_type_str:
-                            sqlite_type = 'TEXT'
-                        else:
-                            sqlite_type = 'TEXT'  # 默认使用 TEXT
-
-                        # 处理 NULL/NOT NULL
-                        nullable_str = "" if column_obj.nullable else "NOT NULL"
-
-                        # 简化默认值处理
-                        default_str = ""
-                        if column_obj.default is not None:
-                            default_value = column_obj.default.arg
-                            if callable(default_value):
-                                # 跳过函数默认值
-                                default_str = ""
-                            elif isinstance(default_value, bool):
-                                default_str = f"DEFAULT {int(default_value)}"
-                            elif isinstance(default_value, (int, float)):
-                                default_str = f"DEFAULT {default_value}"
-                            elif isinstance(default_value, str):
-                                default_str = f"DEFAULT '{default_value}'"
-
-                        # 构建 ALTER TABLE 语句
-                        parts = [
-                            f"ALTER TABLE {table_name}",
-                            f"ADD COLUMN {column_name}",
-                            sqlite_type,
-                        ]
-                        if nullable_str:
-                            parts.append(nullable_str)
-                        if default_str:
-                            parts.append(default_str)
-
-                        sql = " ".join(parts)
-
-                        cursor.execute(sql)
-                        print(
-                            f"✅ 成功添加字段: {table_name}.{column_name} ({sqlite_type})")
-
-                    except Exception as e:
-                        print(f"⚠️ 添加字段失败 {table_name}.{column_name}: {e}")
-                        # 继续处理下一个字段
-
-            except Exception as e:
-                print(f"⚠️ 处理表 {table_name} 时出错: {e}")
-                import traceback
-                traceback.print_exc()
-
-        conn.commit()
-        conn.close()
-        print("🎉 数据库迁移检查完成")
-
-    except Exception as e:
-        print(f"⚠️ 数据库迁移整体失败: {e}")
-        import traceback
-        traceback.print_exc()
-        # 不抛出异常,允许程序继续运行