批处理完成后保留固定值设置,新增重试和清空设置按钮
- _on_finished 不再清空产品/客户输入框及缓存选择 - 每行结果保存原始数据 _row 以支持重试 - 新增"重试"按钮:重跑所有非成功记录,原地更新结果表格 - 新增"清空设置"按钮:一键清空所有固定值字段和缓存
This commit is contained in:
@@ -208,6 +208,10 @@ class App:
|
|||||||
btn_frame, text="⏹ 停止", command=self._stop, width=10, state=DISABLED
|
btn_frame, text="⏹ 停止", command=self._stop, width=10, state=DISABLED
|
||||||
)
|
)
|
||||||
self.stop_btn.pack(side="left", padx=2)
|
self.stop_btn.pack(side="left", padx=2)
|
||||||
|
self.retry_btn = Button(
|
||||||
|
btn_frame, text="重试", command=self._retry, width=10, state=DISABLED
|
||||||
|
)
|
||||||
|
self.retry_btn.pack(side="left", padx=2)
|
||||||
|
|
||||||
# 状态
|
# 状态
|
||||||
Label(
|
Label(
|
||||||
@@ -262,6 +266,11 @@ class App:
|
|||||||
Entry(r3, textvariable=self.fv_sender_addr, width=18).pack(side="left", padx=5)
|
Entry(r3, textvariable=self.fv_sender_addr, width=18).pack(side="left", padx=5)
|
||||||
Label(r3, text="(有值即强制覆盖)", fg="gray", font=("", 8)).pack(side="left")
|
Label(r3, text="(有值即强制覆盖)", fg="gray", font=("", 8)).pack(side="left")
|
||||||
|
|
||||||
|
# 清空设置按钮
|
||||||
|
r4 = Frame(fixed_frame)
|
||||||
|
r4.pack(fill=X, pady=2)
|
||||||
|
Button(r4, text="清空设置", command=self._clear_fixed_values, width=10).pack(side="right", padx=5)
|
||||||
|
|
||||||
# ── 进度条 ──
|
# ── 进度条 ──
|
||||||
progress_frame = Frame(self.root)
|
progress_frame = Frame(self.root)
|
||||||
progress_frame.pack(fill=X, padx=10, pady=2)
|
progress_frame.pack(fill=X, padx=10, pady=2)
|
||||||
@@ -876,6 +885,7 @@ class App:
|
|||||||
break
|
break
|
||||||
|
|
||||||
result = await _engine.process_single_row(row)
|
result = await _engine.process_single_row(row)
|
||||||
|
result["_row"] = row # 保留原始行数据以便重试
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
# 更新结果表格
|
# 更新结果表格
|
||||||
@@ -914,6 +924,24 @@ class App:
|
|||||||
)
|
)
|
||||||
self.status_var.set(f"处理中: {current}/{total}")
|
self.status_var.set(f"处理中: {current}/{total}")
|
||||||
|
|
||||||
|
def _clear_fixed_values(self):
|
||||||
|
"""清空所有固定值设置和缓存的选择。"""
|
||||||
|
self.fv_product.set("")
|
||||||
|
self.fv_customer.set("")
|
||||||
|
self.fv_destination.set("")
|
||||||
|
self.fv_district_code.set("")
|
||||||
|
self.fv_sender_name.set("")
|
||||||
|
self.fv_sender_addr.set("")
|
||||||
|
self.fv_transport_type.set("")
|
||||||
|
self.fv_weight.set("")
|
||||||
|
self._product_cached_choice = None
|
||||||
|
self._product_cached_detail = None
|
||||||
|
self._customer_cached_choice = None
|
||||||
|
self._customer_cached_detail = None
|
||||||
|
self._sender_realname_cached = None
|
||||||
|
self._transmode_map = {}
|
||||||
|
self.fv_transport_combo["values"] = []
|
||||||
|
|
||||||
def _on_finished(self):
|
def _on_finished(self):
|
||||||
global _engine
|
global _engine
|
||||||
self.running = False
|
self.running = False
|
||||||
@@ -921,18 +949,18 @@ class App:
|
|||||||
_engine = None
|
_engine = None
|
||||||
self._set_controls_state("stopped")
|
self._set_controls_state("stopped")
|
||||||
self.status_var.set("就绪")
|
self.status_var.set("就绪")
|
||||||
# 清空产品/客户输入框(batch 结束后重置)
|
# 清空产品/客户输入框(batch 结束后重置)—— 已注释,保留设置以便重试
|
||||||
self.fv_product.set("")
|
# self.fv_product.set("")
|
||||||
self.fv_customer.set("")
|
# self.fv_customer.set("")
|
||||||
# 清空缓存的选择
|
# # 清空缓存的选择
|
||||||
self._product_cached_choice = None
|
# self._product_cached_choice = None
|
||||||
self._product_cached_detail = None
|
# self._product_cached_detail = None
|
||||||
self._customer_cached_choice = None
|
# self._customer_cached_choice = None
|
||||||
self._customer_cached_detail = None
|
# self._customer_cached_detail = None
|
||||||
self._sender_realname_cached = None
|
# self._sender_realname_cached = None
|
||||||
self.fv_transport_type.set("")
|
# self.fv_transport_type.set("")
|
||||||
self.fv_weight.set("")
|
# self.fv_weight.set("")
|
||||||
self._transmode_map = {}
|
# self._transmode_map = {}
|
||||||
|
|
||||||
def _pause(self):
|
def _pause(self):
|
||||||
global _engine
|
global _engine
|
||||||
@@ -962,15 +990,159 @@ class App:
|
|||||||
self.status_var.set("正在停止...")
|
self.status_var.set("正在停止...")
|
||||||
self._log("用户停止")
|
self._log("用户停止")
|
||||||
|
|
||||||
|
def _retry(self):
|
||||||
|
"""重试所有非成功的记录。"""
|
||||||
|
if self.running:
|
||||||
|
return
|
||||||
|
failed_indices = []
|
||||||
|
failed_rows = []
|
||||||
|
for i, r in enumerate(self.results):
|
||||||
|
if r.get("status") != "成功" and "_row" in r:
|
||||||
|
failed_indices.append(i)
|
||||||
|
failed_rows.append(r["_row"])
|
||||||
|
if not failed_rows:
|
||||||
|
messagebox.showinfo("提示", "没有需要重试的记录")
|
||||||
|
return
|
||||||
|
self._save_fixed_from_entries()
|
||||||
|
self.running = True
|
||||||
|
self._set_controls_state("running")
|
||||||
|
self.status_var.set(f"重试 {len(failed_rows)} 条...")
|
||||||
|
self._log(f"开始重试 {len(failed_rows)} 条失败记录...")
|
||||||
|
self.progress["maximum"] = len(failed_rows)
|
||||||
|
self.progress["value"] = 0
|
||||||
|
self.progress_label.configure(text=f"成功 0 | 失败 0 | 剩余 {len(failed_rows)}")
|
||||||
|
_run_in_thread(self._run_retry(failed_indices, failed_rows))
|
||||||
|
|
||||||
|
async def _run_retry(self, indices, rows):
|
||||||
|
"""后台重试指定的行。"""
|
||||||
|
global _engine
|
||||||
|
try:
|
||||||
|
from engine import PickupEngine
|
||||||
|
|
||||||
|
field_mapping = get_field_mapping(self.config)
|
||||||
|
defaults = get_defaults(self.config)
|
||||||
|
timing = get_timing_config(self.config)
|
||||||
|
fixed_values = get_fixed_values(self.config)
|
||||||
|
|
||||||
|
_engine = PickupEngine(
|
||||||
|
url="https://10.4.188.1/pickup-web/a/postwaybill/main",
|
||||||
|
field_mapping=field_mapping,
|
||||||
|
defaults=defaults,
|
||||||
|
timing=timing,
|
||||||
|
log_callback=self._log_async,
|
||||||
|
fixed_values=fixed_values,
|
||||||
|
choose_callback=self._choose_callback,
|
||||||
|
confirm_callback=self._confirm_callback,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self._search_api is not None:
|
||||||
|
_engine.inject_infrastructure(self._search_sess, self._search_bridge, self._search_api)
|
||||||
|
if self._product_cached_choice is not None:
|
||||||
|
_engine._product_choice = self._product_cached_choice
|
||||||
|
if self._product_cached_detail is not None:
|
||||||
|
_engine._product_detail = self._product_cached_detail
|
||||||
|
if self._customer_cached_choice is not None:
|
||||||
|
_engine._customer_choice = self._customer_cached_choice
|
||||||
|
if self._customer_cached_detail is not None:
|
||||||
|
_engine._customer_detail = self._customer_cached_detail
|
||||||
|
if self._sender_realname_cached is not None:
|
||||||
|
_engine._sender_realname = self._sender_realname_cached
|
||||||
|
transport_name = self.fv_transport_type.get()
|
||||||
|
if transport_name and self._transmode_map:
|
||||||
|
_engine._transport_type = self._transmode_map.get(transport_name, "")
|
||||||
|
|
||||||
|
self._update_status("启动中...")
|
||||||
|
try:
|
||||||
|
await _engine.start()
|
||||||
|
except Exception as e:
|
||||||
|
self._log_async(f"[error] 启动失败: {e}")
|
||||||
|
self._update_status("启动失败")
|
||||||
|
await _engine.stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not _engine._sess or not _engine._sess._http.cookies:
|
||||||
|
self._log_async("[提示] 未找到登录会话,请点击'登录'按钮完成登录后再开始处理")
|
||||||
|
self._update_status("未登录")
|
||||||
|
await _engine.stop()
|
||||||
|
return
|
||||||
|
cookie_names = {c.name for c in _engine._sess._http.cookies}
|
||||||
|
missing = [n for n in ("JSESSIONID", "jdpt.session.id") if n not in cookie_names]
|
||||||
|
if missing:
|
||||||
|
self._log_async(f"[提示] 缺少关键 cookie: {', '.join(missing)},请重新登录并手动填写")
|
||||||
|
self._update_status("未登录")
|
||||||
|
await _engine.stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
await _engine.begin_processing()
|
||||||
|
self._update_status("后台运行中")
|
||||||
|
self._log_async("HTTP API 引擎已就绪")
|
||||||
|
except Exception as e:
|
||||||
|
self._log_async(f"[error] 初始化失败: {e}")
|
||||||
|
self._update_status("启动失败")
|
||||||
|
await _engine.stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
children = self.result_tree.get_children()
|
||||||
|
success = 0
|
||||||
|
fail = 0
|
||||||
|
for j, (idx, row) in enumerate(zip(indices, rows)):
|
||||||
|
if _engine._stop_requested:
|
||||||
|
break
|
||||||
|
|
||||||
|
result = await _engine.process_single_row(row)
|
||||||
|
result["_row"] = row
|
||||||
|
self.results[idx] = result
|
||||||
|
|
||||||
|
values = (
|
||||||
|
result.get("单号", ""),
|
||||||
|
result.get("status", ""),
|
||||||
|
result.get("重量", ""),
|
||||||
|
result.get("产品", ""),
|
||||||
|
result.get("客户", ""),
|
||||||
|
result.get("运输类型", ""),
|
||||||
|
result.get("寄达地区划代码", ""),
|
||||||
|
result.get("寄件人姓名", ""),
|
||||||
|
result.get("寄件地址", ""),
|
||||||
|
)
|
||||||
|
# 更新对应 treeview 行
|
||||||
|
if idx < len(children):
|
||||||
|
self.root.after(0, lambda v=values, iid=children[idx]: self.result_tree.item(iid, values=v))
|
||||||
|
|
||||||
|
if result.get("status") == "成功":
|
||||||
|
success += 1
|
||||||
|
else:
|
||||||
|
fail += 1
|
||||||
|
|
||||||
|
self.root.after(0, lambda cur=j+1, total=len(rows), s=success, f=fail: self._update_progress(cur, total, s, f))
|
||||||
|
|
||||||
|
delay = timing.get("delay_between_rows_ms", 3000) / 1000
|
||||||
|
if j < len(rows) - 1:
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
|
||||||
|
self._log_async(f"\n重试完成!成功 {success},失败 {fail}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self._log_async(f"[error] 重试异常: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
finally:
|
||||||
|
if _engine:
|
||||||
|
await _engine.stop()
|
||||||
|
self.root.after(0, self._on_finished)
|
||||||
|
|
||||||
def _set_controls_state(self, state):
|
def _set_controls_state(self, state):
|
||||||
if state == "running":
|
if state == "running":
|
||||||
self.start_btn.configure(state=DISABLED)
|
self.start_btn.configure(state=DISABLED)
|
||||||
self.pause_btn.configure(state=NORMAL)
|
self.pause_btn.configure(state=NORMAL)
|
||||||
self.stop_btn.configure(state=NORMAL)
|
self.stop_btn.configure(state=NORMAL)
|
||||||
|
self.retry_btn.configure(state=DISABLED)
|
||||||
else:
|
else:
|
||||||
self.start_btn.configure(state=NORMAL)
|
self.start_btn.configure(state=NORMAL)
|
||||||
self.pause_btn.configure(state=DISABLED, text="⏸ 暂停")
|
self.pause_btn.configure(state=DISABLED, text="⏸ 暂停")
|
||||||
self.stop_btn.configure(state=DISABLED)
|
self.stop_btn.configure(state=DISABLED)
|
||||||
|
has_failed = any(r.get("status") != "成功" for r in self.results)
|
||||||
|
self.retry_btn.configure(state=NORMAL if has_failed else DISABLED)
|
||||||
|
|
||||||
# ─── 日志 ───────────────────────────────────────────────
|
# ─── 日志 ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user