run_main.py 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443
  1. import settings
  2. from settings import sendSocketMessage
  3. from .detail_func import *
  4. import json
  5. from .base import *
  6. # from .match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
  7. # from module.view_control.MineQWidget import DialogShow, WorkerOneThread
  8. import threading
  9. from concurrent.futures import ThreadPoolExecutor
  10. import concurrent.futures
  11. from .data import DataModeGenerateDetail
  12. import time
  13. from PIL import Image
  14. from io import BytesIO
  15. import os, re
  16. from functools import partial
  17. from service.customer_template_service import CustomerTemplateService
  18. # from multiprocessing import Process, Queue
  19. import pickle
  20. from .base_deal import BaseDealImage
  21. from middleware import UnicornException
  22. from settings import recordDataPoint
  23. import asyncio
  24. from utils.common import message_queue
  25. class RunMain:
  26. # run_end_sign = Signal(dict)
  27. # show_dialog_sign = Signal(dict)
  28. # show_progress_detail_sign = Signal(str)
  29. # # 定义一个信号用于请求显示对话框
  30. # show_dialog_signal = Signal()
  31. # # 定义一个信号用于返回对话框的结果
  32. # dialog_result_signal = Signal(str)
  33. # dialog_result_signal = Signal(str)
  34. # 重试机制配置:最大重试次数(固定为1,避免死循环)
  35. MAX_RETRY_COUNT = 1
  36. def __init__(self, windows, token, uuid):
  37. super().__init__()
  38. self.windows = windows
  39. self.token = token
  40. self.uuid = uuid
  41. self.dialog_result = ""
  42. self.data_mode_generate_detail = DataModeGenerateDetail(token=token)
  43. # self.dialog_result_signal.connect(self.on_dialog_result)
  44. self.event = threading.Event()
  45. def set_state(self, state_value: int):
  46. # 0禁用 1进行中 2已结束
  47. if state_value not in [0, 1, 2, 99]:
  48. return
  49. # self.windows.change_state_sign.emit(state_value)
  50. # self.windows.set_state(state_value)
  51. # 抠图前先做数据规整处理;类似详情图生成逻辑
  52. def check_before_cutout(self, config_data):
  53. return_data = {
  54. "code": 99,
  55. "message": "",
  56. "data": {
  57. "all_goods_art_no_folder_data": [],
  58. "config_data": config_data,
  59. },
  60. }
  61. image_dir = config_data["image_dir"]
  62. image_order = config_data["image_order"]
  63. is_check_number = config_data["is_check_number"]
  64. is_filter = config_data["cutout_is_pass"]
  65. resize_image_view = config_data["resize_image_view"]
  66. goods_art_nos = config_data["goods_art_nos"]
  67. logo_path = config_data["logo_path"]
  68. cutout_mode = config_data["cutout_mode"] # 是否精细化抠图,默认为普通抠图
  69. special_goods_art_no_folder_line = config_data[
  70. "special_goods_art_no_folder_line"
  71. ]
  72. # 自动处理红蜻蜓货号,进行重命名
  73. # if settings.PROJECT == "红蜻蜓":
  74. # # 规整红蜻蜓货号图
  75. # all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
  76. # BaseDealImage().rename_folder_for_hqt(all_goods_art_no_folder_data=all_goods_art_no_folder_data)
  77. # 重新获取文件夹信息
  78. all_goods_art_no_folder_data = get_all_goods_art_no_folder(
  79. path=image_dir, goods_art_nos=goods_art_nos
  80. )
  81. print("all_goods_art_no_folder_data", all_goods_art_no_folder_data)
  82. f = True
  83. is_do_other = False
  84. if is_do_other:
  85. for i in all_goods_art_no_folder_data:
  86. i["label"] = "不处理"
  87. is_filter = False
  88. specified_goods_art_no_folder = special_goods_art_no_folder_line
  89. specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
  90. specified_goods_art_no_folder = specified_goods_art_no_folder.replace(
  91. ",", ","
  92. )
  93. specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(
  94. ","
  95. )
  96. specified_goods_art_no_folder_list = [
  97. x for x in specified_goods_art_no_folder_list if x
  98. ]
  99. if not specified_goods_art_no_folder_list:
  100. return_data[
  101. "message"
  102. ] += "请手动输入文件夹名称(多选),或关闭指定文件夹模式\n"
  103. else:
  104. for i in all_goods_art_no_folder_data:
  105. if i["folder_path"] in specified_goods_art_no_folder_list:
  106. i["label"] = "待处理"
  107. # 哪些数据不合规
  108. all_folder_name_list = [
  109. x["folder_name"] for x in all_goods_art_no_folder_data
  110. ]
  111. for goods_art_no_folder_name in specified_goods_art_no_folder_list:
  112. if goods_art_no_folder_name not in all_folder_name_list:
  113. return_data[
  114. "message"
  115. ] += "文件夹:{},在您选的目录下不存在\n".format(
  116. goods_art_no_folder_name
  117. )
  118. f = False
  119. if not f:
  120. # raise UnicornException(return_data["message"])
  121. self.set_state(state_value=2)
  122. return
  123. # 清空指定文件夹的已抠图文件
  124. if is_do_other:
  125. for folder_data in all_goods_art_no_folder_data:
  126. goods_art_no_folder_path = "{}/原始图_已抠图".format(
  127. folder_data["folder_path"]
  128. )
  129. if os.path.exists(goods_art_no_folder_path):
  130. remove_all_file(goods_art_no_folder_path)
  131. return_data["data"]["succeed_folder_list"] = 1
  132. # ==================检查填写的图片视角是否符合要求
  133. res = BaseDealImage(token=self.token).getImageOrder(
  134. image_order=image_order, resize_image_view=resize_image_view
  135. )
  136. if res["code"] != 0:
  137. return_data["message"] += "{}\n".format(res["msg"])
  138. # raise UnicornException(return_data["message"])
  139. return return_data
  140. else:
  141. # 图片命名顺序
  142. image_order_list = res["imageOrderList"]
  143. print("图片命名顺序", image_order_list, all_goods_art_no_folder_data)
  144. for goods_art_no_folder_data in all_goods_art_no_folder_data:
  145. if goods_art_no_folder_data["label"] != "待处理":
  146. continue
  147. goods_art_no_folder_data["image_order_list"] = image_order_list
  148. # ================是否过滤已有生成的文件夹
  149. if is_filter:
  150. for goods_art_no_folder_data in all_goods_art_no_folder_data:
  151. if goods_art_no_folder_data["label"] != "待处理":
  152. continue
  153. folder_path = goods_art_no_folder_data["folder_path"]
  154. _p = "{}/800x800".format(folder_path)
  155. if os.path.exists(_p):
  156. if len(os.listdir(_p)):
  157. goods_art_no_folder_data["label"] = "不处理"
  158. # ================检查每个货号文件夹图片数量是否符合要求
  159. all_goods_art_no_folder_data, message = BaseDealImage(
  160. token=self.token
  161. ).check_folders_image_amount(all_goods_art_no_folder_data, image_order_list)
  162. if message:
  163. return_data["message"] += "{}\n".format(message)
  164. return_data["code"] = 0
  165. return_data["data"][
  166. "all_goods_art_no_folder_data"
  167. ] = all_goods_art_no_folder_data
  168. return_data["data"]["image_dir"] = image_dir
  169. return_data["data"]["resize_image_view"] = resize_image_view
  170. return_data["data"]["cutout_mode"] = cutout_mode
  171. return_data["data"]["image_order_list"] = image_order_list
  172. return_data["data"]["logo_path"] = logo_path
  173. return return_data
  174. # 抠图校验后的回调函数处理
  175. def check_for_cutout_image_first_call_back(self, return_data):
  176. code = return_data["code"]
  177. config_data = return_data["data"]["config_data"]
  178. config_data["sign_text"] = ""
  179. if code != 0:
  180. raise UnicornException(return_data["message"])
  181. do_next = False
  182. text = ""
  183. all_goods_art_no_folder_data = return_data["data"][
  184. "all_goods_art_no_folder_data"
  185. ]
  186. button_1, button_2, button_3 = None, None, None
  187. text += return_data["message"]
  188. # 存在错误文件夹
  189. error_folder = [x for x in all_goods_art_no_folder_data if x["label"] == "错误"]
  190. todo_folder = [
  191. x for x in all_goods_art_no_folder_data if x["label"] == "待处理"
  192. ]
  193. if error_folder:
  194. button_2 = "移除错误文件"
  195. if error_folder and todo_folder:
  196. button_1 = "移除并继续"
  197. button_3 = "继续(忽略其他)"
  198. if not error_folder and todo_folder:
  199. button_1 = "继续"
  200. # do_next = True
  201. text += "\n==================\n错误数据:{}个,校验无误数据:{}个".format(
  202. len(error_folder), len(todo_folder)
  203. )
  204. self.show_progress_detail(text)
  205. if button_1 is None and button_2 is None and button_3 is None:
  206. pass
  207. elif button_1 == "继续" and button_2 is None and button_3 is None:
  208. do_next = True
  209. else:
  210. if "移除" in self.dialog_result:
  211. for error_folder_data in [
  212. x for x in all_goods_art_no_folder_data if x["label"] == "错误"
  213. ]:
  214. self.move_error_folders(
  215. one_path=error_folder_data["folder_path"],
  216. target_folder="{}/软件-处理失败".format(
  217. config_data["image_dir"]
  218. ),
  219. )
  220. if "继续" in self.dialog_result:
  221. do_next = True
  222. goods_arts = [
  223. goods_art_no_folder_data["folder_name"]
  224. for goods_art_no_folder_data in all_goods_art_no_folder_data
  225. ]
  226. if do_next:
  227. all_goods_art_no_folder_data = [
  228. x for x in all_goods_art_no_folder_data if x["label"] == "待处理"
  229. ]
  230. print("===============all_goods_art_no_folder_data===============")
  231. print(all_goods_art_no_folder_data)
  232. new_func = self.do_run_cutout_image(
  233. all_goods_art_no_folder_data=all_goods_art_no_folder_data,
  234. callback_func=self.show_progress_detail,
  235. image_order_list=return_data["data"]["image_order_list"],
  236. cutout_mode=return_data["data"]["cutout_mode"],
  237. resize_image_view=return_data["data"]["resize_image_view"],
  238. windows=None,
  239. logo_path=return_data["data"]["logo_path"],
  240. config_data=config_data,
  241. )
  242. return new_func
  243. else:
  244. print("已结束抠图处理")
  245. return True
  246. def validate_folder_integrity(self, folder_path, folder_name, expected_output_count=None):
  247. """
  248. 验证货号文件夹的完整性
  249. 检查800x800目录和阴影图处理目录中的文件数量是否等于预期数量
  250. Args:
  251. folder_path: 货号文件夹路径
  252. folder_name: 货号名称
  253. expected_output_count: 预期的输出文件数量(如果为None则根据原始图数量计算)
  254. Returns:
  255. tuple: (is_valid, original_count, processed_count, expected_count, message)
  256. """
  257. from logger import logger
  258. import settings
  259. original_dir = "{}/原始图".format(folder_path)
  260. processed_dir = "{}/800x800".format(folder_path)
  261. shadow_dir = "{}/阴影图处理".format(folder_path)
  262. # 检查目录是否存在
  263. if not os.path.exists(original_dir):
  264. logger.warning(f"[目录校验] 货号 {folder_name} - 原始图目录不存在: {original_dir}")
  265. return False, 0, 0, 0, f"原始图目录不存在"
  266. if not os.path.exists(processed_dir):
  267. logger.warning(f"[目录校验] 货号 {folder_name} - 800x800目录不存在: {processed_dir}")
  268. return False, 0, 0, 0, f"800x800目录不存在"
  269. if not os.path.exists(shadow_dir):
  270. logger.warning(f"[目录校验] 货号 {folder_name} - 阴影图处理目录不存在: {shadow_dir}")
  271. return False, 0, 0, 0, f"阴影图处理目录不存在"
  272. # 统计原始图文件数量(只统计图片文件)
  273. _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
  274. original_files = []
  275. for f in os.listdir(original_dir):
  276. _, ext = os.path.splitext(f)
  277. if ext in _Type:
  278. original_files.append(f)
  279. original_count = len(original_files)
  280. # 统计800x800目录文件数量
  281. processed_files = []
  282. for f in os.listdir(processed_dir):
  283. _, ext = os.path.splitext(f)
  284. if ext in _Type:
  285. processed_files.append(f)
  286. processed_count = len(processed_files)
  287. # 统计阴影图处理目录文件数量
  288. shadow_files = []
  289. for f in os.listdir(shadow_dir):
  290. _, ext = os.path.splitext(f)
  291. if ext in _Type:
  292. shadow_files.append(f)
  293. shadow_count = len(shadow_files)
  294. # 计算预期的输出文件数量
  295. if expected_output_count is None:
  296. # 从配置中获取主图尺寸列表
  297. try:
  298. out_pic_size_list = settings.getSysConfigs("basic_configs", "main_image_size", [1600])
  299. # 如果是字符串,尝试解析为列表
  300. if isinstance(out_pic_size_list, str):
  301. import json
  302. try:
  303. out_pic_size_list = json.loads(out_pic_size_list)
  304. except:
  305. out_pic_size_list = [1600]
  306. # 确保是列表
  307. if not isinstance(out_pic_size_list, list):
  308. out_pic_size_list = [out_pic_size_list]
  309. # 过滤空值
  310. out_pic_size_list = [x for x in out_pic_size_list if x]
  311. if not out_pic_size_list:
  312. out_pic_size_list = [1600]
  313. # 预期输出数量 = 原始图数量 * 尺寸数量
  314. expected_count = original_count * len(out_pic_size_list)
  315. logger.info(f"[目录校验] 货号 {folder_name} - 配置的输出尺寸: {out_pic_size_list}, 共{len(out_pic_size_list)}个尺寸")
  316. except Exception as e:
  317. logger.warning(f"[目录校验] 货号 {folder_name} - 获取配置失败,使用默认值: {e}")
  318. expected_count = original_count # 默认1倍
  319. else:
  320. expected_count = expected_output_count
  321. # 预期的阴影图数量 = 原始图数量 * 2(每个原图生成2个文件:阴影图和抠图)
  322. expected_shadow_count = original_count * 2
  323. logger.info(f"[目录校验] 货号 {folder_name} - 原始图: {original_count}张, 800x800: {processed_count}张, 预期: {expected_count}张, 阴影图: {shadow_count}张, 预期阴影图: {expected_shadow_count}张")
  324. # 如果原始图为空,认为无效
  325. if original_count == 0:
  326. logger.error(f"[目录校验] 货号 {folder_name} - 原始图目录为空")
  327. return False, original_count, processed_count, expected_count, "原始图目录为空"
  328. # 严格检查:800x800文件数量必须等于预期数量
  329. if processed_count != expected_count:
  330. logger.error(f"[目录校验] 货号 {folder_name} - 800x800处理失败: 预期{expected_count}张,实际{processed_count}张 (原始图{original_count}张 × {len(out_pic_size_list) if 'out_pic_size_list' in locals() else '?'}个尺寸)")
  331. return False, original_count, processed_count, expected_count, f"800x800文件数量不匹配: 预期{expected_count}张,实际{processed_count}张"
  332. # 严格检查:阴影图处理文件数量必须等于原始图的2倍
  333. if shadow_count != expected_shadow_count:
  334. logger.error(f"[目录校验] 货号 {folder_name} - 阴影图处理失败: 预期{expected_shadow_count}张(原始图{original_count}张 × 2),实际{shadow_count}张")
  335. return False, original_count, processed_count, expected_count, f"阴影图文件数量不匹配: 预期{expected_shadow_count}张,实际{shadow_count}张"
  336. logger.info(f"[目录校验] 货号 {folder_name} - 校验通过 ✓")
  337. return True, original_count, processed_count, expected_count, "校验通过"
  338. def retry_single_folder(self, goods_art_no_folder_data, image_order_list, cutout_mode, resize_image_view, logo_path, callback_func):
  339. """
  340. 重试单个货号的处理(仅执行一次,不会递归或循环)
  341. Args:
  342. goods_art_no_folder_data: 货号文件夹数据
  343. image_order_list: 图片顺序列表
  344. cutout_mode: 抠图模式
  345. resize_image_view: 缩放视角
  346. logo_path: Logo路径
  347. callback_func: 回调函数
  348. Returns:
  349. bool: 重试是否成功
  350. """
  351. from logger import logger
  352. folder_name = goods_art_no_folder_data["folder_name"]
  353. folder_path = goods_art_no_folder_data["folder_path"]
  354. logger.info(f"[重试处理] 开始重试货号: {folder_name} (仅此一次,不会重复重试)")
  355. callback_func(f"正在重试货号: {folder_name}")
  356. deal = BaseDealImage(token=self.token)
  357. try:
  358. # 只处理这一个货号 - 注意:这里直接调用处理逻辑,不会再触发校验和重试
  359. result = deal.shoes_run_one_folder_to_deal(
  360. goods_art_no_folder_data=goods_art_no_folder_data,
  361. resize_image_view=resize_image_view,
  362. logo_path=logo_path,
  363. image_order_list=image_order_list,
  364. callback_func=callback_func,
  365. windows=None,
  366. )
  367. if result:
  368. logger.info(f"[重试处理] 货号 {folder_name} 重试成功 ✓")
  369. callback_func(f"货号 {folder_name} 重试成功")
  370. return True
  371. else:
  372. logger.error(f"[重试处理] 货号 {folder_name} 重试失败 (不再重试)")
  373. callback_func(f"货号 {folder_name} 重试失败")
  374. return False
  375. except Exception as e:
  376. import traceback
  377. logger.error(f"[重试处理] 货号 {folder_name} 重试异常: {e}\n{traceback.format_exc()}")
  378. callback_func(f"货号 {folder_name} 重试异常: {e}")
  379. return False
  380. def do_run_cutout_image(
  381. self,
  382. all_goods_art_no_folder_data,
  383. callback_func,
  384. image_order_list,
  385. cutout_mode,
  386. resize_image_view,
  387. windows,
  388. logo_path,
  389. config_data,
  390. ):
  391. from logger import logger
  392. goods_arts = [
  393. goods_art_no_folder_data["folder_name"]
  394. for goods_art_no_folder_data in all_goods_art_no_folder_data
  395. ]
  396. print("BaseDealImage().run_main========>>>>")
  397. deal = BaseDealImage(token=self.token)
  398. # 重试机制:最多重试1次,避免死循环
  399. max_retry_count = self.MAX_RETRY_COUNT
  400. retry_count = 0
  401. is_success = False
  402. failed_folders_from_run_main = [] # 从 run_main 返回的失败货号列表
  403. while retry_count <= max_retry_count and not is_success:
  404. try:
  405. if retry_count > 0:
  406. logger.warning(f"[主流程重试] 第 {retry_count} 次重试,重新执行抠图处理")
  407. callback_func(f"处理失败,正在重试(第{retry_count}次)...")
  408. # 重试时只处理之前失败的货号
  409. if failed_folders_from_run_main:
  410. logger.info(f"[主流程重试] 将重试以下货号: {[f['folder_name'] for f in failed_folders_from_run_main]}")
  411. # 临时替换为只包含失败货号的数据
  412. retry_folders = failed_folders_from_run_main
  413. failed_folders_from_run_main = [] # 清空,准备接收新的失败列表
  414. else:
  415. retry_folders = all_goods_art_no_folder_data
  416. else:
  417. retry_folders = all_goods_art_no_folder_data
  418. # 执行主流程,获取返回结果
  419. result = deal.run_main(
  420. all_goods_art_no_folder_data=retry_folders,
  421. callback_func=callback_func,
  422. image_order_list=image_order_list,
  423. cutout_mode=cutout_mode,
  424. resize_image_view=resize_image_view,
  425. windows=windows,
  426. logo_path=logo_path,
  427. )
  428. # 检查返回结果
  429. if result and isinstance(result, dict):
  430. is_success = result.get('success', False)
  431. failed_folders_from_run_main = result.get('failed_folders', [])
  432. successful_num = result.get('successful_num', 0)
  433. error_num = result.get('error_num', 0)
  434. logger.info(f"[主流程完成] 成功: {successful_num}, 失败: {error_num}")
  435. if is_success:
  436. logger.info("[主流程完成] 所有货号处理成功 ✓")
  437. else:
  438. # 有失败的货号,需要重试
  439. retry_count += 1 # 增加重试计数
  440. logger.warning(f"[主流程完成] 有 {error_num} 个货号失败,准备重试")
  441. callback_func(f"有 {error_num} 个货号处理失败,准备重试...")
  442. if retry_count > max_retry_count:
  443. # 已达到最大重试次数
  444. logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
  445. callback_func(f"处理失败,已重试{max_retry_count}次仍无法完成")
  446. # 不抛出异常,继续执行后置校验
  447. is_success = True # 强制退出循环,让后置校验处理
  448. else:
  449. logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试,将只处理失败的货号")
  450. callback_func(f"准备重试失败的货号...")
  451. else:
  452. # 兼容旧版本,如果没有返回值则认为成功
  453. is_success = True
  454. except UnicornException as e:
  455. retry_count += 1
  456. logger.error(f"[主流程重试] 第 {retry_count} 次尝试失败: {e.msg}")
  457. if retry_count > max_retry_count:
  458. logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
  459. callback_func(f"处理失败,已重试{max_retry_count}次仍无法完成")
  460. raise UnicornException(e.msg)
  461. else:
  462. logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试")
  463. callback_func(f"处理异常,准备重试...")
  464. except Exception as e:
  465. import traceback
  466. retry_count += 1
  467. logger.error(f"[主流程重试] 第 {retry_count} 次尝试异常: {e}\n{traceback.format_exc()}")
  468. if retry_count > max_retry_count:
  469. logger.error(f"[主流程重试] 已达到最大重试次数 {max_retry_count},放弃重试")
  470. callback_func(f"处理异常,已重试{max_retry_count}次仍无法完成")
  471. raise UnicornException(e)
  472. else:
  473. logger.warning(f"[主流程重试] 准备第 {retry_count} 次重试")
  474. callback_func(f"处理异常,准备重试...")
  475. # ========== 后置校验:检查所有货号的目录完整性 ==========
  476. logger.info("=" * 50)
  477. logger.info("[后置校验] 开始检查所有货号的目录完整性")
  478. logger.info("=" * 50)
  479. failed_folders = []
  480. for goods_art_no_folder_data in all_goods_art_no_folder_data:
  481. if goods_art_no_folder_data["label"] != "待处理":
  482. continue
  483. folder_name = goods_art_no_folder_data["folder_name"]
  484. folder_path = goods_art_no_folder_data["folder_path"]
  485. is_valid, original_count, processed_count, expected_count, message = self.validate_folder_integrity(
  486. folder_path, folder_name
  487. )
  488. if not is_valid:
  489. logger.warning(f"[后置校验] 货号 {folder_name} 校验失败: {message}")
  490. failed_folders.append({
  491. "data": goods_art_no_folder_data,
  492. "reason": message,
  493. "original_count": original_count,
  494. "processed_count": processed_count,
  495. "expected_count": expected_count
  496. })
  497. # ========== 如果有失败的货号,进行重试(仅重试一次,避免死循环)==========
  498. if failed_folders:
  499. logger.info("=" * 50)
  500. logger.info(f"[重试机制] 发现 {len(failed_folders)} 个货号需要重试")
  501. logger.info(f"[重试机制] 重要提示:每个货号最多重试 {self.MAX_RETRY_COUNT} 次,不会无限重试")
  502. logger.info("=" * 50)
  503. retry_success_count = 0
  504. retry_failed_count = 0
  505. for failed_item in failed_folders:
  506. folder_name = failed_item["data"]["folder_name"]
  507. logger.info(f"[重试机制] 开始重试货号: {folder_name}, 原因: {failed_item['reason']}")
  508. callback_func(f"检测到 {folder_name} 处理不完整,正在重试...")
  509. # 执行单次重试(retry_single_folder 内部不会再触发校验和重试)
  510. # 注意:这里只调用一次,不会循环或递归
  511. retry_result = self.retry_single_folder(
  512. goods_art_no_folder_data=failed_item["data"],
  513. image_order_list=image_order_list,
  514. cutout_mode=cutout_mode,
  515. resize_image_view=resize_image_view,
  516. logo_path=logo_path,
  517. callback_func=callback_func
  518. )
  519. if retry_result:
  520. retry_success_count += 1
  521. else:
  522. retry_failed_count += 1
  523. logger.warning(f"[重试机制] 货号 {folder_name} 重试后仍然失败,不再继续重试(已达最大重试次数 {self.MAX_RETRY_COUNT})")
  524. logger.info("=" * 50)
  525. logger.info(f"[重试机制] 重试完成: 成功 {retry_success_count} 个, 失败 {retry_failed_count} 个")
  526. logger.info(f"[重试机制] 所有货号处理结束(包含一次重试),流程终止")
  527. logger.info("=" * 50)
  528. callback_func(f"重试完成: 成功 {retry_success_count} 个, 失败 {retry_failed_count} 个")
  529. else:
  530. logger.info("[后置校验] 所有货号校验通过,无需重试 ✓")
  531. recordDataPoint(
  532. token=self.token,
  533. uuid=self.uuid,
  534. page="抠图结束",
  535. data=goods_arts,
  536. )
  537. callback_func("已结束抠图处理")
  538. return True
  539. def do_run_cutout_image1111(
  540. self,
  541. all_goods_art_no_folder_data,
  542. callback_func,
  543. image_order_list,
  544. cutout_mode,
  545. resize_image_view,
  546. windows,
  547. logo_path,
  548. config_data,
  549. ):
  550. max_workers = 1
  551. with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
  552. futures = []
  553. futures.append(
  554. executor.submit(
  555. BaseDealImage().run_main,
  556. all_goods_art_no_folder_data=all_goods_art_no_folder_data,
  557. callback_func=callback_func,
  558. image_order_list=image_order_list,
  559. cutout_mode=cutout_mode,
  560. resize_image_view=resize_image_view,
  561. windows=windows,
  562. logo_path=logo_path,
  563. )
  564. )
  565. # 使用 wait 方法等待所有任务完成
  566. done, not_done = concurrent.futures.wait(futures, timeout=60)
  567. # 处理完成的任务
  568. for future in done:
  569. if settings.IS_TEST:
  570. result = future.result()
  571. else:
  572. try:
  573. result = future.result()
  574. except BaseException as e:
  575. print("2039 图片处理失败.{}".format(e))
  576. callback_func("2039 处理失败.{}".format(e))
  577. # ==============完成处理==============
  578. # self.set_state(state_value=2)
  579. callback_func("已结束抠图处理")
  580. config_data["sign_text"] = "已结束抠图处理"
  581. self.run_end_sign.emit(config_data)
  582. def check_before_detail(self, config_data,is_detail):
  583. # =============
  584. # 整体数据校验,返回错误内容,以及
  585. # temp_name:模板名称
  586. """
  587. 步骤:
  588. 1、 整体文件夹检查,并输出数据结构
  589. 2、数据进行对应规整(可能有excel,红蜻蜓等)
  590. 3、执行单个款数据处理
  591. """
  592. return_data = {
  593. "code": 99,
  594. "message": "",
  595. "data": {
  596. "error_folder_list": [],
  597. "goods_no_dict": {},
  598. "succeed_folder_list": [],
  599. "temp_name": config_data["temp_name"],
  600. "temp_name_list": config_data["temp_name_list"],
  601. "assigned_page_dict": config_data["assigned_page_dict"],
  602. "excel_temp_goods_no_data": {}, # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
  603. "finally_goods_no_need_temps": {}, # 每个款号需要生成的模板数据
  604. "config_data": config_data,
  605. },
  606. }
  607. temp_name = config_data["temp_name"]
  608. temp_name_list = config_data["temp_name_list"]
  609. assigned_page_dict = config_data["assigned_page_dict"]
  610. image_dir = config_data["image_dir"]
  611. is_use_excel = config_data["is_use_excel"]
  612. excel_path = config_data["excel_path"]
  613. temp_class = config_data["temp_class"]
  614. goods_art_nos = config_data["goods_art_nos"]
  615. is_check_color_is_all = config_data["is_check_color_is_all"]
  616. detail_is_pass = config_data["detail_is_pass"]
  617. error_folder_list = []
  618. # 遍历货号获取所有货号--可能为编号
  619. folder_name_list = get_all_dir_info(
  620. image_dir=image_dir, goods_art_nos=goods_art_nos
  621. )
  622. print("folder_name_list--folder_name_list===========>>>>", folder_name_list)
  623. if not folder_name_list:
  624. return_data["message"] += "不存在任何货号/编号文件夹\n"
  625. print("不存在任何货号/编号文件夹")
  626. return return_data
  627. # =========================组装数据---数据来源多种途径=========================
  628. _result = {"code": 99, "message": "无法解析到数据,请检查登录企业"}
  629. print("is_use_excel", is_use_excel)
  630. if not is_use_excel:
  631. if is_detail == 1:
  632. _result = self.data_mode_generate_detail.get_basic_goods_art_data_by_hqt_and_hlm(
  633. folder_name_list
  634. )
  635. else:
  636. _result = self.data_mode_generate_detail.makeFakeData(folder_name_list)
  637. print("fake data _result", _result)
  638. else:
  639. keys = settings.keys
  640. _result = (
  641. self.data_mode_generate_detail.get_basic_goods_art_data_form_excel(
  642. folder_name_list,
  643. excel_path,
  644. keys,
  645. )
  646. )
  647. print("打印=======>>>>>>>_result=============>", _result)
  648. if _result["code"] == 0:
  649. remote_data = _result["data"]
  650. else:
  651. print('_result["message"]====>', _result["message"])
  652. return_data["message"] += _result["message"] + "\n"
  653. return return_data
  654. # print(json.dumps(remote_data))
  655. # =========================拼接数据组合为款数据=========================
  656. """
  657. 1、获取所有文件夹基础数据内容
  658. 2、结合上述返回结果进行数据组合拼接
  659. 3、输出结果为按款为主键信息
  660. 4、注意模板ID
  661. """
  662. print("temp_class====>", temp_class)
  663. print("temp_class====>", temp_name)
  664. # 获取所有文件夹基础数据内容 检查不满足要求的文件不满足要求移动到错误文件夹
  665. # 在访问 temp_class[temp_name].need_view 前增加检查
  666. need_view_list = []
  667. if is_detail == 1:
  668. if temp_name not in temp_class or temp_class[temp_name] is None:
  669. raise UnicornException(f"模板 {temp_name} 未正确初始化或不存在")
  670. class_obj = temp_class[temp_name]
  671. class_path = class_obj.get("cls")
  672. template_type = class_obj.get("template_type")
  673. # 确保 temp_class[temp_name] 是可调用的
  674. # if template_type ==0:
  675. # if not callable(temp_class[temp_name]):
  676. # raise UnicornException(f"模板 {temp_name} 不是有效的可调用对象")
  677. try:
  678. if template_type ==0:
  679. need_view_list = class_path.need_view
  680. except KeyError as ke:
  681. raise UnicornException("未选择详情页模板,请检查")
  682. _all_dir_info_data = get_all_dir_info_and_pic_info(
  683. image_dir, folder_name_list, need_view_list
  684. )
  685. all_dir_info_data = {}
  686. for one_folder, value in _all_dir_info_data.items():
  687. if "message" in value:
  688. if value["message"]:
  689. error_folder_list.append(
  690. {
  691. "folder_name": one_folder,
  692. "folder_path": "{}/{}".format(image_dir, one_folder),
  693. }
  694. )
  695. return_data["message"] += "文件夹:{} 结构错误:{}\n".format(
  696. one_folder, value["message"]
  697. )
  698. return_data["data"]["config_data"]["success_handler"].append(
  699. {
  700. "goods_art_no": one_folder,
  701. "success": False,
  702. "info": value["message"],
  703. }
  704. )
  705. continue
  706. # 符合要求的数据处理
  707. all_dir_info_data[one_folder] = value
  708. # 结合上述返回结果进行数据组合拼接
  709. # 返回可能存在的情况:1、存在文件夹不匹配数据,2、存在货号数据缺失
  710. goods_no_dict, error_folder_name_list = merge_local_and_remote_data(
  711. all_dir_info_data=all_dir_info_data, remote_data=remote_data
  712. )
  713. # 文件在系统中没有匹配的结果
  714. if error_folder_name_list:
  715. for one_folder in error_folder_name_list:
  716. error_folder_list.append(
  717. {
  718. "folder_name": one_folder,
  719. "folder_path": "{}/{}".format(image_dir, one_folder),
  720. }
  721. )
  722. return_data["message"] += "文件夹:{} 在系统资料中找不到对应数据\n".format(
  723. one_folder
  724. )
  725. return_data["data"]["config_data"]["success_handler"].append(
  726. {
  727. "goods_art_no": one_folder,
  728. "success": False,
  729. "info": f"文件夹:{one_folder} 在系统资料中找不到对应数据",
  730. }
  731. )
  732. print("===============goods_no_dict==================")
  733. if settings.IS_TEST:
  734. _text = json.dumps(goods_no_dict)
  735. print(goods_no_dict)
  736. create_folder("qt_test")
  737. with open("qt_test/goods_no_dict.txt", "w", encoding="utf-8") as file:
  738. file.write(_text)
  739. print("===============goods_no_dict==================")
  740. # ===================================是否齐色检查=============================================
  741. # 如为红蜻蜓企业则还需要检查同款下是否齐全;数据返回结果为款号列表
  742. error_data_dict = {}
  743. if is_check_color_is_all:
  744. if not is_use_excel:
  745. if settings.PROJECT == "红蜻蜓":
  746. error_data_dict = (
  747. self.data_mode_generate_detail.check_goods_is_not_deficiency(
  748. goods_no_dict
  749. )
  750. )
  751. else:
  752. error_data_dict = self.data_mode_generate_detail.check_goods_is_not_deficiency_form_excel(
  753. goods_no_dict, excel_path
  754. )
  755. if error_data_dict:
  756. print("----error_data_dict----")
  757. print(json.dumps(error_data_dict))
  758. # 款号反向映射;因为部分key键格式为KUM9999999
  759. _x = {}
  760. for i, v in goods_no_dict.items():
  761. _x[str(v["款号"])] = i
  762. for goods_no, value in error_data_dict.items():
  763. if goods_no in _x:
  764. goods_no = _x[goods_no]
  765. if goods_no in goods_no_dict:
  766. # =====移动到错误文件夹======
  767. for _folder_name in [
  768. x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]
  769. ]:
  770. error_folder_list.append(
  771. {
  772. "folder_name": _folder_name,
  773. "folder_path": "{}\{}".format(image_dir, _folder_name),
  774. }
  775. )
  776. return_data["message"] += "文件夹:{};{}\n".format(
  777. _folder_name, value["message"]
  778. )
  779. # 从 正确 款下面进行数据剔除
  780. goods_no_dict.pop(goods_no)
  781. print("-----------------1goods_no_dict---------------")
  782. print(json.dumps(goods_no_dict, ensure_ascii=False))
  783. print("-----------------1goods_no_dict---------------")
  784. # 如果没有有效数据则进行退出
  785. if not goods_no_dict:
  786. return_data["message"] += "在系统资料中没有查询到有效数据\n"
  787. return return_data
  788. # 校验无误的文件夹数据 goods_no_dict为最终有效数据
  789. for goods_no, value in goods_no_dict.items():
  790. return_data["data"]["succeed_folder_list"].extend(
  791. [x["文件夹名称"] for x in goods_no_dict[goods_no]["货号资料"]]
  792. )
  793. # 如果是表格数据,则获取表格数据中需要生成的货号
  794. # 数据结构
  795. # {“款号1”:
  796. # {“模板名称1”:data_dict1,
  797. # “模板名称2”:data_dict2,
  798. # }}
  799. if is_use_excel:
  800. excel_temp_goods_no_data = (
  801. self.data_mode_generate_detail.get_basic_template_information(
  802. _goods_no_dict=goods_no_dict, excel_path=excel_path
  803. )
  804. )
  805. else:
  806. excel_temp_goods_no_data = {}
  807. print(
  808. "731===================excel_temp_goods_no_data,is_use_excel:{}".format(
  809. is_use_excel
  810. )
  811. )
  812. print(json.dumps(excel_temp_goods_no_data))
  813. # ===========数据组装,统计每个款需要生成哪些模板==============
  814. # 不适用表格指定模板
  815. goods_no_need_temps = {}
  816. # isUseTemplate 为false 表示使用excel表格数据
  817. if not is_use_excel:
  818. for i in goods_no_dict:
  819. goods_no_need_temps[i] = [temp_name]
  820. else:
  821. for i in goods_no_dict:
  822. # i 为款号
  823. if i in excel_temp_goods_no_data:
  824. # 获取 某个款号的所有允许生成的模板列表
  825. goods_no_need_temps[i] = list(excel_temp_goods_no_data[i].keys())
  826. # ========开始执行详情图生成===================
  827. # _goods_no_dict = goods_no_dict
  828. _goods_no_dict = {}
  829. # assigned_page_dict 如存在对应模板的,则不管是否有过滤都需要生成
  830. # 检查是否已存在模板,已存在的需要进行跳过;可能部分模板已存在,部分不存在。
  831. finally_goods_no_need_temps = {}
  832. for goods_no, value in goods_no_dict.items():
  833. if goods_no not in goods_no_need_temps:
  834. continue
  835. for __temp_name in goods_no_need_temps[goods_no]:
  836. # _path = "{}/{}/{}/{}".format(
  837. # image_dir, "软件-详情图生成", __temp_name, goods_no
  838. # )
  839. _path = "{}/{}/切片图-{}".format(
  840. image_dir, f"详情图-{goods_no}",__temp_name
  841. )
  842. if not os.path.exists(_path):
  843. print("款号详情图不存在", _path)
  844. if goods_no not in finally_goods_no_need_temps:
  845. finally_goods_no_need_temps[goods_no] = []
  846. _goods_no_dict[goods_no] = value # 需要生成的数据
  847. finally_goods_no_need_temps[goods_no].append(__temp_name)
  848. else:
  849. print("款号详情图存在", _path)
  850. # 如果在指定模板中存在,则也需要生成
  851. if __temp_name in assigned_page_dict:
  852. print("指定模板需要更新", _path)
  853. if goods_no not in finally_goods_no_need_temps:
  854. finally_goods_no_need_temps[goods_no] = []
  855. _goods_no_dict[goods_no] = value # 需要生成的数据
  856. finally_goods_no_need_temps[goods_no].append(__temp_name)
  857. else:
  858. # output/2025-04-03/软件-详情图生成/huilima-2/AQN141132
  859. return_data["data"]["config_data"]["out_put_dir"] = _path
  860. if detail_is_pass:
  861. return_data[
  862. "message"
  863. ] += "\n款号:{},模板:{} 已存在".format(
  864. goods_no, __temp_name
  865. )
  866. return_data["data"]["config_data"][
  867. "success_handler"
  868. ].append(
  869. {
  870. "goods_art_no": goods_no,
  871. "success": True,
  872. "info": "处理成功",
  873. }
  874. )
  875. else:
  876. if goods_no not in finally_goods_no_need_temps:
  877. finally_goods_no_need_temps[goods_no] = []
  878. _goods_no_dict[goods_no] = value # 需要生成的数据
  879. finally_goods_no_need_temps[goods_no].append(__temp_name)
  880. print("-----------------2goods_no_dict---------------")
  881. print(json.dumps(_goods_no_dict, ensure_ascii=False))
  882. print("-----------------2goods_no_dict---------------")
  883. return_data["data"]["error_folder_list"] = error_folder_list
  884. if len(_goods_no_dict) == 0:
  885. return_data["message"] += "\n没有任何文件夹需要执行"
  886. return_data["data"]["goods_no_dict"] = _goods_no_dict
  887. return_data["data"]["excel_temp_goods_no_data"] = excel_temp_goods_no_data
  888. return_data["data"]["finally_goods_no_need_temps"] = finally_goods_no_need_temps
  889. return_data["code"] = 0
  890. return return_data
  891. def sendAsyncMessage(self,msg="", goods_arts=[], status="",progress={}):
  892. """异步发送消息"""
  893. data = {
  894. "code": 0,
  895. "msg": msg,
  896. "status": 2,
  897. "data": {
  898. "status": status,
  899. "goods_art_nos": goods_arts,
  900. },
  901. "progress":{
  902. "msg_type":"detail_progress",
  903. "name":"详情页",
  904. "goods_art_no":progress.get("goods_art_no"),
  905. "status":progress.get("status"),
  906. "current":progress.get("current",0),
  907. "total":progress.get("total",0),
  908. "error":progress.get("error",0),
  909. "folder":progress.get("folder",None)
  910. },
  911. "msg_type": "detail_progress",
  912. }
  913. print("\033[1;32;40m 详情页消息 \033[0m",data)
  914. message_queue.put_nowait(data)
  915. def move_error_folders(self, one_path, target_folder, message=""):
  916. if os.path.exists(one_path):
  917. check_path(target_folder)
  918. move_folders(path_list=[one_path], target_folder=target_folder)
  919. '''
  920. total_progress = len(all_goods_art_no_folder_data)
  921. finish_progress = 0
  922. error_progress = 0
  923. progress = {"status":"正在处理",
  924. "current":finish_progress,
  925. "total":total_progress,
  926. "error":error_progress}
  927. sendAsyncMessage(
  928. msg="开始处理抠图", goods_arts=goods_art_nos, status="开始处理",progress=progress
  929. )
  930. '''
  931. def check_for_detail_first_call_back(self, data,request_parmas):
  932. # 首次数据校验的信息返回
  933. # self.show_message(text="22222222222222222222222")
  934. # QMessageBox.critical(self, "警告", "1111111", QMessageBox.Ok)
  935. code = data["code"]
  936. config_data = data["data"]["config_data"]
  937. target_error_folder = config_data["target_error_folder"]
  938. print("635 check_for_detail_first_call_back")
  939. print(data)
  940. if code != 0:
  941. raise UnicornException(config_data["success_handler"])
  942. do_next = False
  943. if data["message"]:
  944. button_1, button_2, button_3 = None, None, None
  945. text = data["message"]
  946. if code == 0:
  947. if data["data"]:
  948. if data["data"]["error_folder_list"]:
  949. print("22----------error_folder_list------------")
  950. print(json.dumps(data["data"]["error_folder_list"]))
  951. button_2 = "移除错误文件"
  952. if data["data"]["goods_no_dict"]:
  953. if button_2:
  954. button_1 = "移除并继续"
  955. button_3 = "继续(忽略其他)"
  956. else:
  957. button_1 = "继续"
  958. if data["data"]["succeed_folder_list"]:
  959. text += "\n==================\n校验无误数据:{}个文件夹".format(
  960. len(data["data"]["succeed_folder_list"])
  961. )
  962. self.show_progress_detail(text=data["message"])
  963. if button_1 is None and button_2 is None and button_3 is None:
  964. pass
  965. elif button_1 == "继续" and button_2 is None and button_3 is None:
  966. do_next = True
  967. else:
  968. print("runmain 642----------------")
  969. # todo 弹框修改处理等
  970. _dialog_dict = {
  971. "text": text,
  972. "button_1": button_1,
  973. "button_2": button_2,
  974. "button_3": button_3,
  975. "windows": self,
  976. }
  977. # self.show_dialog_sign.emit(_dialog_dict)
  978. # # 等待事件被设置
  979. # self.event.wait()
  980. print("self.dialog_result", self.dialog_result)
  981. # my_dialog = DialogShow(
  982. # self.windows, text=text, button_1=button_1, button_2=button_2, button_3=button_3
  983. # )
  984. print("my_dialog.flag_name", self.dialog_result)
  985. if "移除" in self.dialog_result:
  986. for error_folder_data in data["data"]["error_folder_list"]:
  987. self.move_error_folders(
  988. one_path=error_folder_data["folder_path"],
  989. target_folder=target_error_folder,
  990. )
  991. if "继续" in self.dialog_result:
  992. do_next = True
  993. if data["data"]["goods_no_dict"] and not data["data"]["error_folder_list"]:
  994. do_next = True
  995. if do_next:
  996. # self.set_state(state_value=1)
  997. getAllData = data["data"]
  998. base_temp_name = getAllData["temp_name"]
  999. # set_temp_name = getAllData.get("template_name", "")
  1000. kwargs = {
  1001. "config_data": config_data,
  1002. "_goods_no_dict": data["data"]["goods_no_dict"],
  1003. "temp_name": base_temp_name,
  1004. "temp_name_list": data["data"]["temp_name_list"],
  1005. "assigned_page_dict": data["data"]["assigned_page_dict"],
  1006. "excel_temp_goods_no_data": data["data"]["excel_temp_goods_no_data"],
  1007. # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
  1008. "finally_goods_no_need_temps": data["data"][
  1009. "finally_goods_no_need_temps"
  1010. ], # 每个款号需要生成的模板数据
  1011. }
  1012. # todo work
  1013. new_func = self.detail_run_by_thread(
  1014. config_data=kwargs["config_data"],
  1015. _goods_no_dict=kwargs["_goods_no_dict"],
  1016. temp_name=kwargs["temp_name"],
  1017. temp_name_list=kwargs["temp_name_list"],
  1018. assigned_page_dict=kwargs["assigned_page_dict"],
  1019. excel_temp_goods_no_data=kwargs["excel_temp_goods_no_data"],
  1020. finally_goods_no_need_temps=kwargs["finally_goods_no_need_temps"],
  1021. request_parmas=request_parmas,
  1022. )
  1023. # self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
  1024. # self._w_3.start()
  1025. return new_func
  1026. # threading.Thread(target=self.detail_run_by_thread, kwargs=kwargs).start()
  1027. else:
  1028. config_data["sign_text"] = "已结束详情处理"
  1029. # self.run_end_sign.emit(config_data)
  1030. return config_data
  1031. def detail_run_by_thread(
  1032. self,
  1033. config_data,
  1034. _goods_no_dict,
  1035. temp_name,
  1036. temp_name_list,
  1037. assigned_page_dict,
  1038. excel_temp_goods_no_data,
  1039. finally_goods_no_need_temps,
  1040. request_parmas
  1041. ):
  1042. """
  1043. excel_temp_goods_no_data: {}, # 表格数据可能存在多模板,数据结构为一个款号下的多个模板的数据列表
  1044. finally_goods_no_need_temps: {}, # 每个款号需要生成的模板数据
  1045. """
  1046. # 开始处理
  1047. self.n = 0
  1048. self.total_num = len(_goods_no_dict)
  1049. self.fail_num = 0
  1050. is_use_excel = config_data["is_use_excel"]
  1051. image_dir = config_data["image_dir"]
  1052. # 详情图生成结果文件夹
  1053. # out_put_dir = "{}\软件-详情图生成".format(image_dir)
  1054. out_put_dir = "{}".format(image_dir)
  1055. if settings.IS_TEST:
  1056. print("==============_goods_no_dict 打印=================")
  1057. print(json.dumps(_goods_no_dict))
  1058. print("==============_goods_no_dict 打印-end=================")
  1059. all_detail_path_list = []
  1060. out_put_dir_resp = ""
  1061. detail_total_progress = len(finally_goods_no_need_temps.items())
  1062. detail_finish_progress = 0
  1063. detail_error_progress = 0
  1064. detail_progress = {"status":"正在处理", "current":detail_finish_progress, "total":detail_total_progress, "error":detail_error_progress}
  1065. self.sendAsyncMessage(
  1066. msg="准备处理详情页",
  1067. goods_arts=[],
  1068. status="准备处理详情页",
  1069. progress=detail_progress
  1070. )
  1071. for goods_no, temp_name_list in finally_goods_no_need_temps.items():
  1072. for _temp_name in temp_name_list:
  1073. try:
  1074. detail_finish_progress+=1
  1075. detail_progress = {
  1076. "status":"正在处理",
  1077. "goods_art_no":goods_no,
  1078. "current":detail_finish_progress,
  1079. "total":detail_total_progress,
  1080. "error":detail_error_progress,
  1081. "folder":""
  1082. }
  1083. self.sendAsyncMessage(
  1084. msg="正在处理详情页",
  1085. goods_arts=[],
  1086. status="正在处理详情页",
  1087. progress=detail_progress
  1088. )
  1089. # if _temp_name != "xiaosushuoxie-4":
  1090. # continue
  1091. assigned_page_list = []
  1092. if _temp_name in assigned_page_dict:
  1093. assigned_page_list = assigned_page_dict[_temp_name]
  1094. # 如果为使用表格,则获取表格中的数据作为款号的基础数据
  1095. temp_info_data = copy.copy(_goods_no_dict[goods_no])
  1096. if is_use_excel:
  1097. # 将表格中的数据进行替换
  1098. if goods_no in excel_temp_goods_no_data:
  1099. if _temp_name in excel_temp_goods_no_data[goods_no]:
  1100. # 将表格中的特定的模板的行,替换到goods_no的data中,因为不同的模板有数据特殊性
  1101. for _key, _key_value in excel_temp_goods_no_data[
  1102. goods_no
  1103. ][_temp_name].items():
  1104. if _key in temp_info_data:
  1105. temp_info_data[_key] = _key_value
  1106. print("goods_no:{},_temp_name:{}".format(goods_no, _temp_name))
  1107. # out_put_dir_resp = "{}/详情模板{}/{}".format(
  1108. # out_put_dir, _temp_name, goods_no
  1109. # )
  1110. out_put_dir_resp = "{}/详情图-{}".format(
  1111. out_put_dir, goods_no
  1112. )
  1113. # all_detail_path_list.append(
  1114. # "{}/详情模板{}/{}".format(out_put_dir, _temp_name, goods_no)
  1115. # )
  1116. all_detail_path_list.append(out_put_dir_resp)
  1117. # continue
  1118. self.detail_deal_one_data(
  1119. goods_no=goods_no,
  1120. value=temp_info_data,
  1121. out_put_dir=out_put_dir,
  1122. temp_name=_temp_name,
  1123. assigned_page_list=assigned_page_list,
  1124. temp_class=config_data["temp_class"],
  1125. target_error_folder=config_data["target_error_folder"],
  1126. image_dir=config_data["image_dir"],
  1127. )
  1128. config_data["success_handler"].append(
  1129. {"goods_art_no": goods_no, "success": True, "info": "处理成功"}
  1130. )
  1131. detail_progress["folder"] = out_put_dir_resp
  1132. self.sendAsyncMessage(
  1133. msg="开始处理详情页",
  1134. goods_arts=[],
  1135. status="开始处理详情页",
  1136. progress=detail_progress
  1137. )
  1138. recordDataPoint(
  1139. token=self.token,
  1140. uuid=self.uuid,
  1141. page="详情图生成成功",
  1142. data={"goods_art_no": goods_no, "temp_name": _temp_name},
  1143. )
  1144. except BaseException as e:
  1145. print("详情页打印输出错误信息===>",e)
  1146. detail_error_progress+=1
  1147. detail_progress = {"status":"处理失败","goods_art_no":goods_no, "current":detail_finish_progress, "total":detail_total_progress, "error":detail_error_progress,"folder":""}
  1148. self.sendAsyncMessage(
  1149. msg="处理失败",
  1150. goods_arts=[],
  1151. status="处理失败",
  1152. progress=detail_progress
  1153. )
  1154. self.show_progress_detail(
  1155. {
  1156. "goods_art_no": goods_no,
  1157. "success": False,
  1158. "info": "款:{}生成详情异常:{}".format(goods_no, e),
  1159. }
  1160. )
  1161. # raise UnicornException("款:{}生成详情异常:{}".format(goods_no, e))
  1162. config_data["success_handler"].append(
  1163. {
  1164. "goods_art_no": goods_no,
  1165. "success": False,
  1166. "info": "款:{}生成详情异常:{}".format(goods_no, e),
  1167. }
  1168. )
  1169. recordDataPoint(
  1170. token=self.token,
  1171. uuid=self.uuid,
  1172. page="详情图生成失败",
  1173. data={
  1174. "goods_art_no": goods_no,
  1175. "temp_name": _temp_name,
  1176. "message": str(e),
  1177. },
  1178. )
  1179. # ==============完成处理==============
  1180. self.set_state(state_value=2)
  1181. if self.total_num:
  1182. if self.fail_num:
  1183. self.show_progress_detail(
  1184. "处理完成,-----处理失败数据:{}个款".format(self.fail_num)
  1185. )
  1186. else:
  1187. self.show_progress_detail("处理完成")
  1188. else:
  1189. self.show_progress_detail("没有任何数据")
  1190. config_data["sign_text"] = "已结束详情处理"
  1191. config_data["all_detail_path_list"] = all_detail_path_list
  1192. config_data["out_put_dir"] = out_put_dir_resp
  1193. print("out_put_dir_resp", out_put_dir_resp)
  1194. # 打开文件夹
  1195. # os.startfile(out_put_dir)
  1196. detail_finish_progress = self.total_num - self.fail_num
  1197. text_status = "处理完成" if detail_finish_progress > 0 else "处理失败"
  1198. detail_progress = {
  1199. "status":text_status,
  1200. "current":detail_finish_progress,
  1201. "total":self.total_num,
  1202. "error":self.fail_num,
  1203. "folder":out_put_dir_resp[0] if text_status=="处理完成" else "",
  1204. }
  1205. self.sendAsyncMessage(
  1206. msg=text_status,
  1207. goods_arts=[],
  1208. status=text_status,
  1209. progress=detail_progress
  1210. )
  1211. return config_data
  1212. def show_progress_detail(self, text):
  1213. # self.show_progress_detail_sign.emit(text)
  1214. # self.windows.show_progress_detail(text)
  1215. print("text=====>", text)
  1216. return text
  1217. def detail_deal_one_data(
  1218. self,
  1219. goods_no,
  1220. value,
  1221. out_put_dir,
  1222. temp_name,
  1223. assigned_page_list,
  1224. temp_class,
  1225. target_error_folder,
  1226. image_dir,
  1227. ):
  1228. # 模板类型,0系统模板,1自定义模板
  1229. # 自定义模板数据
  1230. # if self.windows.state == 99:
  1231. # self.show_progress_detail("用户主动取消:{}".format(goods_no))
  1232. # return
  1233. self.show_progress_detail("正在生成:{},模板:{}".format(goods_no, temp_name))
  1234. is_deal_success = False
  1235. class_obj = temp_class[temp_name]
  1236. class_path = class_obj.get("cls")
  1237. template_type = class_obj.get("template_type")
  1238. print("=================deal_one_data=====================")
  1239. print("goods_no", goods_no)
  1240. print("模板:", temp_name)
  1241. print("value:", value)
  1242. print("temp_class:", temp_class)
  1243. print("class_obj:", class_obj)
  1244. if temp_name not in temp_class or temp_class[temp_name] is None:
  1245. raise UnicornException(f"详情页模板 {temp_name} 未正确加载")
  1246. if template_type == 0:
  1247. # if not callable(class_path):
  1248. # raise UnicornException(f"详情页模板 {temp_name} 不是有效的可调用对象")
  1249. try:
  1250. # # 处理图片详情图生成
  1251. class_path(
  1252. goods_no,
  1253. value,
  1254. out_put_dir=out_put_dir,
  1255. windows=self.windows,
  1256. assigned_page_list=assigned_page_list,
  1257. )
  1258. is_deal_success = True
  1259. except BaseException as e:
  1260. self.show_progress_detail("{}处理失败".format(goods_no))
  1261. error_text = "{}".format(e)
  1262. if "Unable to allocate" in error_text:
  1263. error_text = "电脑内存不足,生成失败"
  1264. print(
  1265. f"发生错误的文件: {e.__traceback__.tb_frame.f_globals['__file__']}"
  1266. )
  1267. print(f"发生错误的行号: {e.__traceback__.tb_lineno}")
  1268. # self.show_progress_detail("失败原因:{}".format(error_text))
  1269. self.fail_num += 1
  1270. raise UnicornException(
  1271. "{}处理失败,失败原因:{}".format(goods_no, error_text)
  1272. )
  1273. else:
  1274. try:
  1275. is_deal_success = True
  1276. service = CustomerTemplateService()
  1277. save_path = f"{out_put_dir}/详情图-{goods_no}"
  1278. print("传递的class信息====>",class_path)
  1279. service.generateTemplate(value,class_path,temp_name,save_path)
  1280. except BaseException as e:
  1281. self.show_progress_detail("{}处理失败".format(goods_no))
  1282. error_text = "{}".format(e)
  1283. print("error_text",error_text)
  1284. print(f"发生错误的行号: {e.__traceback__.tb_lineno}")
  1285. print(
  1286. f"发生错误的文件: {e.__traceback__.tb_frame.f_globals['__file__']}"
  1287. )
  1288. self.n += 1
  1289. if not is_deal_success:
  1290. goods_art_no_list = value["货号资料"]
  1291. self.show_progress_detail("处理失败")
  1292. self.show_progress_detail(
  1293. "相关货号:{}".format([x["货号"] for x in goods_art_no_list])
  1294. )
  1295. # 将相关的文件夹统一移动至错误文件夹
  1296. move_folders(
  1297. path_list=[
  1298. "{}/{}".format(image_dir, x)
  1299. for x in [x["文件夹名称"] for x in goods_art_no_list]
  1300. ],
  1301. target_folder=target_error_folder,
  1302. )
  1303. pass
  1304. # 更新进度
  1305. print("更新进度============>", self.n, self.total_num)
  1306. # self.windows.progress_sign.emit(
  1307. # {
  1308. # "type": "详情图生成",
  1309. # "progress_bar_value": int(self.n / self.total_num * 100),
  1310. # }
  1311. # )
  1312. def check_serializable(self, obj): # 检查某个对象其中的属性哪些不能被序列化
  1313. for attr_name in dir(obj):
  1314. if not attr_name.startswith("__"):
  1315. try:
  1316. attr_value = getattr(obj, attr_name)
  1317. serialized = pickle.dumps(attr_value)
  1318. print(f"属性 {attr_name} 是可序列化的。")
  1319. except (TypeError, pickle.PicklingError):
  1320. print(f"属性 {attr_name} 不可序列化。")
  1321. def on_dialog_result(self, result):
  1322. """处理对话框结果"""
  1323. self.dialog_result = result
  1324. print("972 处理对话框结果:{}".format(result))
  1325. # self.quit() # 结束事件循
  1326. self.event.set()