爱回收.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. import tkinter as tk
  2. from tkinter import ttk, messagebox
  3. import json
  4. import requests
  5. import threading
  6. from datetime import datetime
  7. import uuid
  8. import time
  9. import os
  10. import configparser
  11. class PhoneQueryApp:
  12. def __init__(self, root):
  13. self.root = root
  14. self.root.title("手机数据查询系统")
  15. self.root.geometry("1300x850")
  16. # 配置文件
  17. self.config_file = "config.ini"
  18. self.token = self.load_token()
  19. # 商品数据
  20. self.current_products = []
  21. self.is_loading_cycles = False
  22. self.current_page = 0
  23. self.total_pages = 0
  24. self.page_size = 30
  25. self.current_keyword = ""
  26. # 筛选选项映射
  27. self.memory_map = {
  28. "1T": 187,
  29. "512G": 192,
  30. "256G": 209,
  31. "128G": 210
  32. }
  33. self.battery_map = {
  34. "100%": 1680,
  35. "95%-99%": 5699,
  36. "90%-95%": 5700,
  37. "85%-90%": 5697,
  38. "80%-85%": 5698,
  39. "70%-80%": 6159,
  40. "70%以下": 6160,
  41. "不支持": 1627
  42. }
  43. # 状况标签映射
  44. self.condition_tags = {
  45. 10: "屏幕完美",
  46. 11: "机身无痕",
  47. 12: "功能完好",
  48. 17: "电池效率95%+",
  49. 20: "原厂在保",
  50. 19: "无维修",
  51. 15: "官方在保",
  52. 13: "原厂电池",
  53. 14: "原厂屏幕",
  54. 21: "原厂部件"
  55. }
  56. self.create_widgets()
  57. self.result_tree.bind("<Button-3>", self.show_context_menu)
  58. def load_token(self):
  59. """从配置文件加载Token"""
  60. if os.path.exists(self.config_file):
  61. try:
  62. config = configparser.ConfigParser()
  63. config.read(self.config_file)
  64. return config.get('Settings', 'token', fallback='dccf18b052ef46d78d4feb796182d5df')
  65. except:
  66. return 'dccf18b052ef46d78d4feb796182d5df'
  67. return 'dccf18b052ef46d78d4feb796182d5df'
  68. def save_token(self):
  69. """保存Token到配置文件"""
  70. try:
  71. config = configparser.ConfigParser()
  72. if os.path.exists(self.config_file):
  73. config.read(self.config_file)
  74. if not config.has_section('Settings'):
  75. config.add_section('Settings')
  76. config.set('Settings', 'token', self.token_var.get())
  77. with open(self.config_file, 'w') as f:
  78. config.write(f)
  79. self.token = self.token_var.get()
  80. messagebox.showinfo("成功", "Token已保存")
  81. return True
  82. except Exception as e:
  83. messagebox.showerror("错误", f"保存Token失败: {e}")
  84. return False
  85. def get_headers(self):
  86. """获取请求头"""
  87. return {
  88. "Connection": "keep-alive",
  89. "content-type": "application/json;charset=UTF-8",
  90. "ahs-app-version": "7.49.3",
  91. "Ahs-Timestamp": str(int(datetime.now().timestamp())),
  92. "Ahs-Token": self.token,
  93. "Ahs-Session-Id": str(uuid.uuid4()),
  94. "Ahs-App-Id": "10007",
  95. "Ahs-Device-Id": "3166e003-4ba7-4b57-8158-30859d81c7d5",
  96. "Ahs-Sign": "9ea25fcfcda37585065244ed271f1b0d",
  97. "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 26_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.74(0x18004a2c) NetType/WIFI Language/zh_CN",
  98. "Referer": "https://servicewechat.com/wx7e490492b4c23e98/1055/page-frame.html",
  99. "Accept": "*/*",
  100. "Accept-Encoding": "gzip, deflate, br"
  101. }
  102. def create_widgets(self):
  103. """创建界面组件"""
  104. main_frame = ttk.Frame(self.root, padding="10")
  105. main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  106. self.root.columnconfigure(0, weight=1)
  107. self.root.rowconfigure(0, weight=1)
  108. main_frame.columnconfigure(0, weight=1)
  109. main_frame.rowconfigure(5, weight=1)
  110. # Token设置区域
  111. token_frame = ttk.LabelFrame(main_frame, text="Token设置", padding="10")
  112. token_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
  113. token_frame.columnconfigure(1, weight=1)
  114. ttk.Label(token_frame, text="Ahs-Token:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
  115. self.token_var = tk.StringVar(value=self.token)
  116. self.token_entry = ttk.Entry(token_frame, textvariable=self.token_var, width=60, font=("Arial", 10))
  117. self.token_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
  118. self.save_token_btn = ttk.Button(token_frame, text="保存Token", command=self.save_token, width=12)
  119. self.save_token_btn.grid(row=0, column=2)
  120. # 查询区域
  121. query_frame = ttk.LabelFrame(main_frame, text="查询条件", padding="10")
  122. query_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
  123. query_frame.columnconfigure(1, weight=1)
  124. ttk.Label(query_frame, text="机型:", font=("Arial", 11)).grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
  125. self.model_entry = ttk.Entry(query_frame, width=50, font=("Arial", 11))
  126. self.model_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
  127. self.model_entry.insert(0, "iPhone 15 Pro")
  128. self.search_btn = ttk.Button(query_frame, text="搜索", command=self.search_products, width=15)
  129. self.search_btn.grid(row=0, column=2)
  130. # ==================== 筛选区域 ====================
  131. # 第一行:内存 + 电池效率(各占一半)
  132. filter_row1 = ttk.Frame(main_frame)
  133. filter_row1.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
  134. filter_row1.columnconfigure(0, weight=1)
  135. filter_row1.columnconfigure(1, weight=1)
  136. # 内存筛选
  137. memory_frame = ttk.LabelFrame(filter_row1, text="内存(多选)", padding="5")
  138. memory_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
  139. self.memory_vars = {}
  140. memory_options = ["1T", "512G", "256G", "128G"]
  141. for i, option in enumerate(memory_options):
  142. var = tk.BooleanVar()
  143. self.memory_vars[option] = var
  144. cb = ttk.Checkbutton(memory_frame, text=option, variable=var)
  145. cb.grid(row=0, column=i, sticky=tk.W, padx=15, pady=3)
  146. # 电池效率筛选
  147. battery_frame = ttk.LabelFrame(filter_row1, text="电池效率(多选)", padding="5")
  148. battery_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0))
  149. self.battery_vars = {}
  150. battery_options = ["100%", "95%-99%", "90%-95%", "85%-90%", "80%-85%", "70%-80%", "70%以下", "不支持"]
  151. for i, option in enumerate(battery_options):
  152. var = tk.BooleanVar()
  153. self.battery_vars[option] = var
  154. row = i // 4
  155. col = i % 4
  156. cb = ttk.Checkbutton(battery_frame, text=option, variable=var)
  157. cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=3)
  158. # 第二行:成色 + 状况标签(各占一半)
  159. filter_row2 = ttk.Frame(main_frame)
  160. filter_row2.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
  161. filter_row2.columnconfigure(0, weight=1)
  162. filter_row2.columnconfigure(1, weight=1)
  163. # 成色筛选
  164. fineness_frame = ttk.LabelFrame(filter_row2, text="成色(多选)", padding="5")
  165. fineness_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
  166. self.fineness_vars = {}
  167. fineness_options = ["全新仅拆封", "准新机", "99新", "95新", "9成新", "8成新", "7成新"]
  168. for i, option in enumerate(fineness_options):
  169. var = tk.BooleanVar()
  170. self.fineness_vars[option] = var
  171. cb = ttk.Checkbutton(fineness_frame, text=option, variable=var)
  172. cb.grid(row=0, column=i, sticky=tk.W, padx=10, pady=3)
  173. # 状况标签筛选
  174. condition_frame = ttk.LabelFrame(filter_row2, text="状况标签(多选)", padding="5")
  175. condition_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0))
  176. self.condition_vars = {}
  177. condition_items = list(self.condition_tags.items())
  178. for i, (tag_id, tag_name) in enumerate(condition_items):
  179. var = tk.BooleanVar()
  180. self.condition_vars[tag_id] = var
  181. row = i // 5
  182. col = i % 5
  183. cb = ttk.Checkbutton(condition_frame, text=tag_name, variable=var)
  184. cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=3)
  185. # ==================== 结果显示区域 ====================
  186. result_frame = ttk.LabelFrame(main_frame, text="查询结果(可多选商品,点击批量收藏)", padding="10")
  187. result_frame.grid(row=4, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  188. result_frame.columnconfigure(0, weight=1)
  189. result_frame.rowconfigure(0, weight=1)
  190. # 添加电池效率列
  191. columns = ("名称", "价格", "电池效率", "循环次数", "成色", "内存", "颜色", "网络制式")
  192. self.result_tree = ttk.Treeview(result_frame, columns=columns, show="headings", height=15, selectmode="extended")
  193. self.result_tree.heading("名称", text="名称")
  194. self.result_tree.heading("价格", text="价格")
  195. self.result_tree.heading("电池效率", text="电池效率")
  196. self.result_tree.heading("循环次数", text="循环次数")
  197. self.result_tree.heading("成色", text="成色")
  198. self.result_tree.heading("内存", text="内存")
  199. self.result_tree.heading("颜色", text="颜色")
  200. self.result_tree.heading("网络制式", text="网络制式")
  201. self.result_tree.column("名称", width=350)
  202. self.result_tree.column("价格", width=90)
  203. self.result_tree.column("电池效率", width=100)
  204. self.result_tree.column("循环次数", width=90)
  205. self.result_tree.column("成色", width=90)
  206. self.result_tree.column("内存", width=70)
  207. self.result_tree.column("颜色", width=90)
  208. self.result_tree.column("网络制式", width=90)
  209. scrollbar = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
  210. self.result_tree.configure(yscrollcommand=scrollbar.set)
  211. self.result_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  212. scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
  213. # ==================== 底部按钮栏 ====================
  214. bottom_frame = ttk.Frame(main_frame)
  215. bottom_frame.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=(10, 5))
  216. self.check_price_btn = ttk.Button(bottom_frame, text="获取循环次数", command=self.check_prices, width=18)
  217. self.check_price_btn.pack(side=tk.LEFT, padx=(0, 10))
  218. self.batch_collect_btn = ttk.Button(bottom_frame, text="批量收藏所选商品", command=self.batch_collect, width=20)
  219. self.batch_collect_btn.pack(side=tk.LEFT, padx=(0, 20))
  220. self.progress_var = tk.StringVar()
  221. self.progress_var.set("")
  222. ttk.Label(bottom_frame, textvariable=self.progress_var, font=("Arial", 9)).pack(side=tk.LEFT, padx=(20, 0))
  223. # ==================== 分页栏 ====================
  224. pagination_frame = ttk.Frame(main_frame)
  225. pagination_frame.grid(row=6, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
  226. self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, width=10)
  227. self.prev_btn.pack(side=tk.LEFT, padx=(0, 10))
  228. self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, width=10)
  229. self.next_btn.pack(side=tk.LEFT, padx=(0, 20))
  230. ttk.Label(pagination_frame, text="第").pack(side=tk.LEFT)
  231. self.page_entry = ttk.Entry(pagination_frame, width=8)
  232. self.page_entry.pack(side=tk.LEFT, padx=(5, 5))
  233. ttk.Label(pagination_frame, text="页").pack(side=tk.LEFT)
  234. self.jump_btn = ttk.Button(pagination_frame, text="跳转", command=self.jump_to_page, width=8)
  235. self.jump_btn.pack(side=tk.LEFT, padx=(10, 20))
  236. self.page_info_var = tk.StringVar()
  237. self.page_info_var.set("第 0 / 0 页")
  238. page_label = ttk.Label(pagination_frame, textvariable=self.page_info_var, font=("Arial", 9))
  239. page_label.pack(side=tk.LEFT)
  240. # ==================== 状态栏 ====================
  241. status_frame = ttk.Frame(main_frame)
  242. status_frame.grid(row=7, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
  243. self.status_var = tk.StringVar()
  244. self.status_var.set("就绪")
  245. status_bar = ttk.Label(status_frame, textvariable=self.status_var, relief=tk.SUNKEN)
  246. status_bar.pack(side=tk.LEFT, fill=tk.X, expand=True)
  247. self.update_pagination_buttons()
  248. def get_selected_memory(self):
  249. """获取选中的内存PPV ID列表"""
  250. selected = []
  251. for option, var in self.memory_vars.items():
  252. if var.get():
  253. selected.append(self.memory_map[option])
  254. return selected
  255. def get_selected_battery(self):
  256. """获取选中的电池效率PPV ID列表"""
  257. selected = []
  258. for option, var in self.battery_vars.items():
  259. if var.get():
  260. selected.append(self.battery_map[option])
  261. return selected
  262. def get_selected_fineness(self):
  263. fineness_map = {
  264. "全新仅拆封": 111,
  265. "准新机": 100,
  266. "99新": 99,
  267. "95新": 95,
  268. "9成新": 90,
  269. "8成新": 80,
  270. "7成新": 70
  271. }
  272. selected = []
  273. for option, var in self.fineness_vars.items():
  274. if var.get():
  275. selected.append(fineness_map[option])
  276. return selected
  277. def get_selected_condition_tags(self):
  278. selected = []
  279. for tag_id, var in self.condition_vars.items():
  280. if var.get():
  281. selected.append(tag_id)
  282. return selected
  283. def get_battery_efficiency(self, condition_labels):
  284. """从conditionLabels中提取电池效率"""
  285. if not condition_labels:
  286. return ""
  287. for label in condition_labels:
  288. if label.get('group') in ['原厂电池', '第三方电池']:
  289. return label.get('label', '')
  290. return ""
  291. def search_products(self, page=0):
  292. keyword = self.model_entry.get().strip()
  293. if not keyword:
  294. messagebox.showwarning("警告", "请输入机型")
  295. return
  296. self.current_keyword = keyword
  297. self.current_page = page
  298. for item in self.result_tree.get_children():
  299. self.result_tree.delete(item)
  300. self.current_products.clear()
  301. self.progress_var.set("")
  302. self.check_price_btn.config(state="normal")
  303. self.batch_collect_btn.config(state="normal")
  304. self.status_var.set("正在搜索...")
  305. self.search_btn.config(state="disabled")
  306. self.prev_btn.config(state="disabled")
  307. self.next_btn.config(state="disabled")
  308. self.jump_btn.config(state="disabled")
  309. thread = threading.Thread(target=self.do_search, args=(keyword, page))
  310. thread.daemon = True
  311. thread.start()
  312. def do_search(self, keyword, page):
  313. try:
  314. # 获取筛选条件
  315. memory_ids = self.get_selected_memory()
  316. battery_ids = self.get_selected_battery()
  317. fineness_ids = self.get_selected_fineness()
  318. condition_tags = self.get_selected_condition_tags()
  319. request_data = {
  320. "sortValue": "sort_composite",
  321. "scene": "search",
  322. "keyword": keyword,
  323. "sirReq": False,
  324. "cityId": 324,
  325. "pageIndex": page,
  326. "pageSize": self.page_size
  327. }
  328. # 添加内存筛选(使用 gaeaSkuPpvIds)
  329. if memory_ids:
  330. request_data["gaeaSkuPpvIds"] = {
  331. "22": memory_ids # 22是内存的ppnId
  332. }
  333. # 添加电池效率筛选(使用 gaeaPricePpvIds)
  334. if battery_ids:
  335. request_data["gaeaPricePpvIds"] = {
  336. "473": battery_ids # 473是电池效率的ppnId
  337. }
  338. # 添加成色筛选
  339. if fineness_ids:
  340. request_data["gaeaFinenessIds"] = fineness_ids
  341. # 添加状况标签筛选
  342. if condition_tags:
  343. request_data["conditionTagList"] = condition_tags
  344. url = "https://dubai-mp.aihuishou.com/ahs-yanxuan-service/products/search-goods-v2"
  345. response = requests.post(url, json=request_data, headers=self.get_headers(), timeout=30)
  346. result = response.json()
  347. print("请求参数:", json.dumps(request_data, ensure_ascii=False))
  348. self.root.after(0, self.display_results, result, page)
  349. except Exception as e:
  350. self.root.after(0, self.show_error, str(e))
  351. def parse_product_info(self, product):
  352. name = product.get('name', '')
  353. sale_goods_no = product.get('saleGoodsNo', '')
  354. product_no = product.get('productNo', '')
  355. # 提取颜色
  356. color = ""
  357. color_keywords = ['黑色', '白色', '蓝色', '原色', '金色', '银色', '石墨色', '紫色', '绿色', '粉色', '红色', '黄色', '暗紫色', '深空黑色']
  358. for keyword in color_keywords:
  359. if keyword in name:
  360. color = keyword
  361. break
  362. # 获取电池效率
  363. condition_labels = product.get('conditionLabels', [])
  364. battery_efficiency = self.get_battery_efficiency(condition_labels)
  365. # 如果conditionLabels中没有,从productTag中获取
  366. if not battery_efficiency:
  367. product_tags = product.get('productTag', [])
  368. for tag in product_tags:
  369. if '电池' in tag:
  370. battery_efficiency = tag
  371. break
  372. return {
  373. 'name': name[:50],
  374. 'price': product.get('price', 0),
  375. 'battery_efficiency': battery_efficiency,
  376. 'cycle_count': '',
  377. 'fineness': product.get('gaeaFinenessName', ''),
  378. 'memory': product.get('memoryDesc', ''),
  379. 'color': color,
  380. 'network': "全网通",
  381. 'saleGoodsNo': sale_goods_no,
  382. 'productNo': product_no
  383. }
  384. def display_results(self, result, page):
  385. self.search_btn.config(state="normal")
  386. self.jump_btn.config(state="normal")
  387. if result.get('code') != 0:
  388. self.status_var.set(f"查询失败: {result.get('resultMessage', '未知错误')}")
  389. messagebox.showerror("错误", result.get('resultMessage', '查询失败'))
  390. return
  391. data = result.get('data', [])
  392. total_count = result.get('totalCount', 0)
  393. if total_count > 0:
  394. self.total_pages = (total_count + self.page_size - 1) // self.page_size
  395. else:
  396. if len(data) < self.page_size:
  397. self.total_pages = page + 1
  398. else:
  399. self.total_pages = page + 2
  400. if not data:
  401. self.status_var.set("未找到相关产品")
  402. messagebox.showinfo("提示", "未找到相关产品")
  403. self.update_pagination_buttons()
  404. return
  405. for product in data:
  406. info = self.parse_product_info(product)
  407. item_id = self.result_tree.insert("", tk.END, values=(
  408. info['name'],
  409. f"¥{info['price']}",
  410. info['battery_efficiency'],
  411. info['cycle_count'],
  412. info['fineness'],
  413. info['memory'],
  414. info['color'],
  415. info['network']
  416. ))
  417. self.current_products.append({
  418. 'item_id': item_id,
  419. 'saleGoodsNo': info['saleGoodsNo'],
  420. 'productNo': info['productNo'],
  421. 'info': info
  422. })
  423. self.status_var.set(f"第 {page + 1} 页,共找到 {len(data)} 条结果")
  424. self.update_pagination_buttons()
  425. def update_pagination_buttons(self):
  426. self.page_info_var.set(f"第 {self.current_page + 1} / {self.total_pages if self.total_pages > 0 else 1} 页")
  427. if self.current_page <= 0:
  428. self.prev_btn.config(state="disabled")
  429. else:
  430. self.prev_btn.config(state="normal")
  431. self.next_btn.config(state="normal")
  432. if self.total_pages > 0 and self.current_page >= self.total_pages - 1:
  433. self.next_btn.config(state="disabled")
  434. else:
  435. if len(self.current_products) >= self.page_size:
  436. self.next_btn.config(state="normal")
  437. else:
  438. self.next_btn.config(state="disabled")
  439. def prev_page(self):
  440. if self.current_page > 0:
  441. self.search_products(self.current_page - 1)
  442. def next_page(self):
  443. if self.total_pages == 0 or self.current_page < self.total_pages - 1:
  444. self.search_products(self.current_page + 1)
  445. def jump_to_page(self):
  446. try:
  447. page_num = int(self.page_entry.get().strip())
  448. if page_num < 1:
  449. messagebox.showwarning("警告", "页码必须大于0")
  450. return
  451. if self.total_pages > 0 and page_num > self.total_pages:
  452. messagebox.showwarning("警告", f"页码不能超过 {self.total_pages}")
  453. return
  454. self.search_products(page_num - 1)
  455. self.page_entry.delete(0, tk.END)
  456. except ValueError:
  457. messagebox.showwarning("警告", "请输入有效的页码")
  458. def get_cycle_count(self, sale_goods_no):
  459. """获取商品的电池循环次数"""
  460. try:
  461. url = f"https://dubai-mp.aihuishou.com/ahs-yanxuan-service/products/goods-tag-param?saleGoodsNo={sale_goods_no}"
  462. response = requests.get(url, headers=self.get_headers(), timeout=10)
  463. result = response.json()
  464. if result.get('code') == 0 and 'data' in result:
  465. data = result['data']
  466. if 'machineConditionList' in data:
  467. for item in data['machineConditionList']:
  468. if item.get('name') == '充电次数':
  469. return item.get('value', '')
  470. if 'aggConditionTagParamList' in data:
  471. for group in data['aggConditionTagParamList']:
  472. if group.get('groupName') == '核心机况':
  473. for item in group.get('valueList', []):
  474. if item.get('name') == '充电次数':
  475. return item.get('value', '')
  476. return ''
  477. except Exception as e:
  478. print(f"获取循环次数失败: {e}")
  479. return ''
  480. def add_to_collection(self, item_no):
  481. try:
  482. url = "https://dubai-mp.aihuishou.com/ahs-yanxuan-service/collect/save"
  483. payload = json.dumps({"itemNo": item_no, "type": 1})
  484. headers = self.get_headers()
  485. response = requests.post(url, data=payload, headers=headers, timeout=10)
  486. result = response.json()
  487. if result.get('code') == 200 and result.get('data') == True:
  488. return True, "收藏成功"
  489. else:
  490. return False, result.get('resultMessage', '收藏失败')
  491. except Exception as e:
  492. return False, str(e)
  493. def batch_collect(self):
  494. selected_items = self.result_tree.selection()
  495. if not selected_items:
  496. messagebox.showwarning("警告", "请先选择要收藏的商品(可多选)")
  497. return
  498. result = messagebox.askyesno("确认收藏", f"确定要收藏选中的 {len(selected_items)} 个商品吗?")
  499. if not result:
  500. return
  501. thread = threading.Thread(target=self.process_batch_collect, args=(selected_items,))
  502. thread.daemon = True
  503. thread.start()
  504. def process_batch_collect(self, selected_items):
  505. self.batch_collect_btn.config(state="disabled")
  506. self.check_price_btn.config(state="disabled")
  507. success_count = 0
  508. fail_count = 0
  509. total = len(selected_items)
  510. for i, item_id in enumerate(selected_items):
  511. product = None
  512. for p in self.current_products:
  513. if p['item_id'] == item_id:
  514. product = p
  515. break
  516. if product:
  517. item_no = product['productNo'] or product['saleGoodsNo']
  518. self.root.after(0, lambda txt=f"收藏中: {i+1}/{total}": self.progress_var.set(txt))
  519. success, msg = self.add_to_collection(item_no)
  520. if success:
  521. success_count += 1
  522. else:
  523. fail_count += 1
  524. print(f"收藏失败: {product['info']['name']} - {msg}")
  525. time.sleep(0.2)
  526. self.root.after(0, lambda: self.finish_batch_collect(success_count, fail_count, total))
  527. def finish_batch_collect(self, success_count, fail_count, total):
  528. self.batch_collect_btn.config(state="normal")
  529. self.check_price_btn.config(state="normal")
  530. self.progress_var.set("")
  531. if fail_count == 0:
  532. self.status_var.set(f"批量收藏完成!成功收藏 {success_count} 个商品")
  533. messagebox.showinfo("批量收藏完成", f"成功收藏 {success_count} 个商品!")
  534. else:
  535. self.status_var.set(f"批量收藏完成:成功 {success_count},失败 {fail_count}")
  536. messagebox.showwarning("批量收藏完成", f"成功收藏 {success_count} 个商品\n失败 {fail_count} 个商品")
  537. def check_prices(self):
  538. if not self.current_products:
  539. messagebox.showwarning("警告", "请先搜索商品")
  540. return
  541. if self.is_loading_cycles:
  542. messagebox.showinfo("提示", "正在获取中,请稍候...")
  543. return
  544. thread = threading.Thread(target=self.process_check_prices)
  545. thread.daemon = True
  546. thread.start()
  547. def process_check_prices(self):
  548. self.is_loading_cycles = True
  549. self.check_price_btn.config(state="disabled")
  550. self.batch_collect_btn.config(state="disabled")
  551. total = len(self.current_products)
  552. for i, product in enumerate(self.current_products):
  553. self.root.after(0, lambda txt=f"获取中: {i+1}/{total}": self.progress_var.set(txt))
  554. cycle_count = self.get_cycle_count(product['saleGoodsNo'])
  555. if cycle_count:
  556. values = list(self.result_tree.item(product['item_id'], 'values'))
  557. values[3] = cycle_count
  558. self.root.after(0, lambda pid=product['item_id'], vals=values: self.result_tree.item(pid, values=vals))
  559. time.sleep(0.3)
  560. self.root.after(0, self.finish_check_prices)
  561. def finish_check_prices(self):
  562. self.is_loading_cycles = False
  563. self.check_price_btn.config(state="normal")
  564. self.batch_collect_btn.config(state="normal")
  565. self.progress_var.set("")
  566. self.status_var.set(f"完成!已获取 {len(self.current_products)} 条商品的循环次数")
  567. messagebox.showinfo("完成", f"已成功获取 {len(self.current_products)} 条商品的循环次数")
  568. def show_context_menu(self, event):
  569. item = self.result_tree.identify_row(event.y)
  570. if not item:
  571. return
  572. self.result_tree.selection_set(item)
  573. values = self.result_tree.item(item, 'values')
  574. product_name = values[0]
  575. battery = values[2]
  576. cycle_count = values[3]
  577. menu = tk.Menu(self.root, tearoff=0)
  578. menu.add_command(label=f"收藏: {product_name[:30]}", command=lambda: self.collect_single_item(item))
  579. if battery:
  580. menu.add_command(label=f"电池: {battery}", command=None, state="disabled")
  581. if cycle_count:
  582. menu.add_command(label=f"循环次数: {cycle_count}", command=None, state="disabled")
  583. menu.add_separator()
  584. menu.add_command(label="取消", command=None)
  585. menu.post(event.x_root, event.y_root)
  586. def collect_single_item(self, item):
  587. for product in self.current_products:
  588. if product['item_id'] == item:
  589. item_no = product['productNo'] or product['saleGoodsNo']
  590. success, msg = self.add_to_collection(item_no)
  591. if success:
  592. self.status_var.set(f"✓ 收藏成功: {product['info']['name'][:30]}")
  593. messagebox.showinfo("收藏成功", f"已成功收藏\n{product['info']['name']}")
  594. else:
  595. self.status_var.set(f"✗ 收藏失败: {msg}")
  596. messagebox.showerror("收藏失败", f"收藏失败\n{product['info']['name']}\n错误: {msg}")
  597. return
  598. def show_error(self, error_msg):
  599. self.search_btn.config(state="normal")
  600. self.jump_btn.config(state="normal")
  601. self.status_var.set(f"查询失败: {error_msg}")
  602. messagebox.showerror("错误", f"查询失败: {error_msg}")
  603. def main():
  604. root = tk.Tk()
  605. app = PhoneQueryApp(root)
  606. root.mainloop()
  607. if __name__ == "__main__":
  608. main()