Przeglądaj źródła

fix(cutout): 解决抠图操作中的原始图缺失和重试机制问题

- 在deal_image.py中添加路径检查功能,确保原始图目录存在
- 重构message_handler.py中的抠图处理逻辑,实现文件缺失时的自动重试机制
- 添加对FileNotFoundError的特殊处理,当检测到原始图缺失时自动重新拷贝原图
- 优化重试逻辑,支持最多1次重试以解决临时性错误
- 在run_main.py中简化异步事件循环的初始化逻辑
- 修复重试处理中的异常传播问题,确保错误能够正确传递
rambo 5 godzin temu
rodzic
commit
2c73f3d24e

+ 3 - 0
python/service/deal_image.py

@@ -13,6 +13,8 @@ import requests
 from service.pic_deal import Picture
 import xlsxwriter
 from PIL import Image
+
+from utils.utils_func import check_path
 from .base_deal import BaseDealImage
 from middleware import UnicornException
 _Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
@@ -215,6 +217,7 @@ class DealImage(BaseDealImage):
         self.goods_images_count_dict[goods_art_no] += 1
         # A9999(1).jpg
         new_file_name = "{}({})".format(goods_art_no, self.goods_images_count_dict[goods_art_no])
+        check_path("{}/原始图".format(goods_art_no_path))
         original_image_path = "{}/原始图/{}{}".format(goods_art_no_path, new_file_name, e)
         try:
             shutil.copy2(old_image_path, original_image_path)

+ 16 - 91
python/service/run_main.py

@@ -410,7 +410,7 @@ class RunMain:
                 callback_func=callback_func,
                 windows=None,
             )
-            
+
             if result:
                 logger.info(f"[重试处理] 货号 {folder_name} 重试成功 ✓")
                 callback_func(f"货号 {folder_name} 重试成功")
@@ -423,6 +423,7 @@ class RunMain:
             import traceback
             logger.error(f"[重试处理] 货号 {folder_name} 重试异常: {e}\n{traceback.format_exc()}")
             callback_func(f"货号 {folder_name} 重试异常: {e}")
+            raise
             return False
 
     def do_run_cutout_image(
@@ -437,102 +438,26 @@ class RunMain:
         config_data,
     ):
         from logger import logger
-        
+        try:
+            loop = asyncio.get_event_loop()
+        except:
+            loop = asyncio.new_event_loop()
+        executor = ThreadPoolExecutor(max_workers=10)
         goods_arts = [
             goods_art_no_folder_data["folder_name"]
             for goods_art_no_folder_data in all_goods_art_no_folder_data
         ]
         print("BaseDealImage().run_main========>>>>")
         deal = BaseDealImage(token=self.token)
-        
-        # 重试机制:最多重试1次,避免死循环
-        max_retry_count = self.MAX_RETRY_COUNT
-        retry_count = 0
-        is_success = False
-        failed_folders_from_run_main = []  # 从 run_main 返回的失败货号列表
-        
-        while retry_count <= max_retry_count and not is_success:
-            try:
-                if retry_count > 0:
-                    logger.warning(f"[主流程重试] 第 {retry_count} 次重试,重新执行抠图处理")
-                    callback_func(f"处理失败,正在重试(第{retry_count}次)...")
-                    
-                    # 重试时只处理之前失败的货号
-                    if failed_folders_from_run_main:
-                        logger.info(f"[主流程重试] 将重试以下货号: {[f['folder_name'] for f in failed_folders_from_run_main]}")
-                        # 临时替换为只包含失败货号的数据
-                        retry_folders = failed_folders_from_run_main
-                        failed_folders_from_run_main = []  # 清空,准备接收新的失败列表
-                    else:
-                        retry_folders = all_goods_art_no_folder_data
-                else:
-                    retry_folders = all_goods_art_no_folder_data
-                
-                # 执行主流程,获取返回结果
-                result = deal.run_main(
-                    all_goods_art_no_folder_data=retry_folders,
-                    callback_func=callback_func,
-                    image_order_list=image_order_list,
-                    cutout_mode=cutout_mode,
-                    resize_image_view=resize_image_view,
-                    windows=windows,
-                    logo_path=logo_path,
-                )
-                
-                # 检查返回结果
-                if result and isinstance(result, dict):
-                    is_success = result.get('success', False)
-                    failed_folders_from_run_main = result.get('failed_folders', [])
-                    successful_num = result.get('successful_num', 0)
-                    error_num = result.get('error_num', 0)
-                    
-                    logger.info(f"[主流程完成] 成功: {successful_num}, 失败: {error_num}")
-                    
-                    if is_success:
-                        logger.info("[主流程完成] 所有货号处理成功 ✓")
-                    else:
-                        # 有失败的货号,需要重试
-                        retry_count += 1  # 增加重试计数
-                        logger.warning(f"[主流程完成] 有 {error_num} 个货号失败,准备重试")
-                        callback_func(f"有 {error_num} 个货号处理失败,准备重试...")
-                        
-                        if retry_count > max_retry_count:
-                            # 已达到最大重试次数
-                            logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
-                            callback_func(f"处理失败,已重试{max_retry_count}次仍无法完成")
-                            # 不抛出异常,继续执行后置校验
-                            is_success = True  # 强制退出循环,让后置校验处理
-                        else:
-                            logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试,将只处理失败的货号")
-                            callback_func(f"准备重试失败的货号...")
-                else:
-                    # 兼容旧版本,如果没有返回值则认为成功
-                    is_success = True
-                
-            except UnicornException as e:
-                retry_count += 1
-                logger.error(f"[主流程重试] 第 {retry_count} 次尝试失败: {e.msg}")
-                
-                if retry_count > max_retry_count:
-                    logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
-                    callback_func(f"处理失败,已重试{max_retry_count}次仍无法完成")
-                    raise UnicornException(e.msg)
-                else:
-                    logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试")
-                    callback_func(f"处理异常,准备重试...")
-                    
-            except Exception as e:
-                import traceback
-                retry_count += 1
-                logger.error(f"[主流程重试] 第 {retry_count} 次尝试异常: {e}\n{traceback.format_exc()}")
-                
-                if retry_count > max_retry_count:
-                    logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
-                    callback_func(f"处理异常,已重试{max_retry_count}次仍无法完成")
-                    raise UnicornException(e)
-                else:
-                    logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试")
-                    callback_func(f"处理异常,准备重试...")
+        deal.run_main(
+            all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+            callback_func=callback_func,
+            image_order_list=image_order_list,
+            cutout_mode=cutout_mode,
+            resize_image_view=resize_image_view,
+            windows=windows,
+            logo_path=logo_path,
+        )
 
         # ========== 后置校验:检查所有货号的目录完整性 ==========
         logger.info("=" * 50)

+ 82 - 29
python/sockets/message_handler.py

@@ -23,35 +23,88 @@ executor = ThreadPoolExecutor(max_workers=4)
 async def handlerCutOut(
     manager=None, run_main=None, config_data={}, websocket=None, msg_type=""
 ):
-    try:
-        # return_data = run_main.check_before_cutout(config_data)
-        # await run_main.check_for_cutout_image_first_call_back(return_data)
-        # 将阻塞操作放到线程池中执行
-        loop = asyncio.get_event_loop()
-        return_data = await loop.run_in_executor(
-            executor, partial(run_main.check_before_cutout, config_data)
-        )
-        # await run_main.check_for_cutout_image_first_call_back(return_data)
-        await loop.run_in_executor(
-            executor,
-            partial(run_main.check_for_cutout_image_first_call_back, return_data),
-        )
-    except UnicornException as e:
-        data = manager.jsonMessage(
-            code=1,
-            msg=e.msg,
-            msg_type=msg_type,
-        )
-        await manager.send_personal_message(data, websocket)
-        return
-    except Exception as e:
-        print("error",e)
-        data = manager.jsonMessage(
-            code=1,
-            msg="抠图异常,请稍后重试~",
-            msg_type=msg_type,
-        )
-        await manager.send_personal_message(data, websocket)
+    max_retry_count = 1  # 最多重试1次
+    retry_count = 0
+    
+    while retry_count <= max_retry_count:
+        try:
+            if retry_count > 0:
+                logger.info(f"抠图操作重试第{retry_count}次")
+            
+            # return_data = run_main.check_before_cutout(config_data)
+            # await run_main.check_for_cutout_image_first_call_back(return_data)
+            # 将阻塞操作放到线程池中执行
+            loop = asyncio.get_event_loop()
+            return_data = await loop.run_in_executor(
+                executor, partial(run_main.check_before_cutout, config_data)
+            )
+            # await run_main.check_for_cutout_image_first_call_back(return_data)
+            await loop.run_in_executor(
+                executor,
+                partial(run_main.check_for_cutout_image_first_call_back, return_data),
+            )
+            # 成功则退出循环
+            break
+        except UnicornException as e:
+            logger.error(f"抠图操作发生UnicornException: {e.msg}")
+            logger.error(f"异常类型: UnicornException")
+            logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
+            
+            # data = manager.jsonMessage(
+            #     code=1,
+            #     msg=e.msg,
+            #     msg_type=msg_type,
+            # )
+            # await manager.send_personal_message(data, websocket)
+            return
+        except FileNotFoundError as e:
+            error_msg = str(e)
+            logger.error(f"抠图操作发生FileNotFoundError: {error_msg}")
+            logger.error(f"异常类型: FileNotFoundError")
+            logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
+            
+            # 检查是否是原始图缺失错误
+            if "原始图" in error_msg and retry_count < max_retry_count:
+                logger.info("检测到原始图缺失,尝试重新拷贝原图并重试...")
+                try:
+                    # 重新拷贝原图
+                    goods_art_nos = config_data.get("goods_art_nos", [])
+                    image_dir = config_data.get("image_dir", "")
+                    
+                    for goods_art_no in goods_art_nos:
+                        dealImage = DealImage(image_dir)
+                        resFlag, path = dealImage.dealMoveImageV2(
+                            goods_art_no=goods_art_no,
+                        )
+                        if not resFlag:
+                            logger.error(f"重新拷贝原图失败: {goods_art_no}")
+                            raise UnicornException(f"重新拷贝原图失败: {goods_art_no}")
+                    
+                    logger.info("原图重新拷贝成功,准备重试抠图操作")
+                    retry_count += 1
+                    continue  # 重试
+                except Exception as copy_error:
+                    logger.error(f"重新拷贝原图时发生错误: {str(copy_error)}")
+                    return
+            else:
+                logger.error(f"重新拷贝原图时发生错误: 非原始图错误或已达到最大重试次数")
+                return
+        except Exception as e:
+            import traceback
+            error_msg = str(e)
+            stack_trace = traceback.format_exc()
+            
+            logger.error(f"抠图操作发生未知异常: {error_msg}")
+            logger.error(f"异常类型: {type(e).__name__}")
+            logger.error(f"当前重试次数: {retry_count}/{max_retry_count}")
+            logger.error(f"完整堆栈跟踪:\n{stack_trace}")
+            
+            # 如果是第一次执行且未达到最大重试次数,则重试
+            if retry_count < max_retry_count:
+                logger.info("准备重试抠图操作...")
+                retry_count += 1
+                continue
+            return
 
 def handlerFolderDelete(limit_path, goods_art_no_arrays, is_write_txt_log):
     check_path(limit_path)