115 lines
7.7 KiB
Python
115 lines
7.7 KiB
Python
from pathlib import Path
|
|
import os
|
|
|
|
artifact = Path(r'C:\kefu\deploy\review-visibility-20260916')
|
|
review = (artifact / 'review_state.py').read_text(encoding='utf-8')
|
|
review = review.replace("delivery_uncertain = send_state == 'uncertain' or stage == 'uncertain'", "delivery_uncertain = send_state in {'uncertain', 'sent_uncommitted', 'unknown'} or stage in {'uncertain', 'receipt_check', 'unknown'}")
|
|
review = review.replace("protected = send_state in {'sending', 'uncertain', 'sent_uncommitted', 'failed'} or stage in {'sending', 'receipt_check'}", "protected = delivery_uncertain or send_state in {'sending', 'failed'} or stage == 'sending'")
|
|
review = review.replace("and not approved\n", "and not approved and not protected\n")
|
|
review = review.replace(" if delivery_uncertain:\n", " if send_state == 'sent_uncommitted':\n reason = '回复已发送但归档尚未完成。请先在企业微信核对发送结果,再转交人工;保留发送记录,不会自动重发。' + ('\\n' + reason if reason else '')\n elif delivery_uncertain:\n")
|
|
(artifact / 'review_state.py').write_text(review, encoding='utf-8')
|
|
|
|
method = ''' def _handoff_queue_tasks(self, keys) -> None:
|
|
requested = list(dict.fromkeys(str(key or "").strip() for key in (keys or []) if str(key or "").strip()))
|
|
if not requested:
|
|
return
|
|
|
|
def confirm_receipt():
|
|
return QMessageBox.question(
|
|
self, "核对发送结果后转人工",
|
|
"这条回复可能已经发出。请先在企业微信的原会话核对发送结果。\\n\\n"
|
|
"继续后将从自动队列移出,由人工跟进;保留发送记录,不会自动重发,也不会标记为已确认发送。\\n\\n"
|
|
"已核对,确认转人工处理?",
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
QMessageBox.StandardButton.No,
|
|
) == QMessageBox.StandardButton.Yes
|
|
|
|
def handoff(targets, confirmed):
|
|
thread = getattr(self, "_thread", None)
|
|
if thread is not None and thread.is_alive():
|
|
return thread.handoff_pending_tasks(targets, confirm_uncertain=confirmed)
|
|
from gui_runtime import handoff_pending_reply_file
|
|
path = OFFLINE_QUEUE_PATH
|
|
return handoff_pending_reply_file(targets, str(path), confirm_uncertain=confirmed)
|
|
|
|
pending = getattr(self.queue_page, "pending_states", {}) or {}
|
|
confirmed = any(task_review_state(pending.get(key))["deliveryUncertain"] for key in requested)
|
|
if confirmed and not confirm_receipt():
|
|
return
|
|
try:
|
|
result = handoff(requested, confirmed)
|
|
# The worker may have started receipt checking after the UI snapshot.
|
|
# Re-check at the backend and never treat a stale UI as confirmation.
|
|
needs_confirmation = list(result.get("requires_confirmation") or [])
|
|
if needs_confirmation:
|
|
result["protected"] = [key for key in result.get("protected", []) if key not in needs_confirmation]
|
|
if confirm_receipt():
|
|
followup = handoff(needs_confirmation, True)
|
|
for field in ("handed_off", "deleted", "scheduled", "protected"):
|
|
result[field] = sorted(set(result.get(field) or []) | set(followup.get(field) or []))
|
|
if followup.get("error"):
|
|
result["error"] = followup["error"]
|
|
except Exception as exc:
|
|
QMessageBox.warning(self, "转人工失败", str(exc))
|
|
return
|
|
if result.get("error"):
|
|
QMessageBox.warning(self, "转人工失败", str(result["error"]))
|
|
accepted = set(result.get("handed_off") or result.get("deleted") or [])
|
|
scheduled = set(result.get("scheduled") or [])
|
|
if accepted:
|
|
self.append_log(f"已将 {len(accepted)} 条任务转交人工,保留发送记录,不再自动重发。", "notify")
|
|
if scheduled:
|
|
self.append_log(f"已登记 {len(scheduled)} 条人工接管操作,监听线程就绪后执行。", "notify")
|
|
if result.get("protected"):
|
|
QMessageBox.information(self, "暂不能转人工", "任务正在发送或属于其他账号,请等待发送结束并在对应账号下处理。")
|
|
self.queue_page.refresh_data()
|
|
self._schedule_console_push(urgent=True)
|
|
|
|
'''
|
|
|
|
for root in (Path(r'C:\kefu\wechat_rpa'), Path(r'C:\wechat_rpa')):
|
|
def save(name, content):
|
|
path = root / name
|
|
tmp = path.with_suffix('.audit-tmp')
|
|
tmp.write_text(content, encoding='utf-8')
|
|
os.replace(tmp, path)
|
|
save('review_state.py', review)
|
|
path = root / 'wechat_gui_qt.py'
|
|
text = path.read_text(encoding='utf-8')
|
|
start = text.index(' def _handoff_queue_tasks(self, keys) -> None:')
|
|
end = text.index(' def _approve_queue_tasks(self, keys) -> None:', start)
|
|
queue = 'queue_path_for_transport(self.runtime_settings.get("reply_transport", VISUAL), root=SCRIPT_DIR)' if root == Path(r'C:\wechat_rpa') else 'SCRIPT_DIR / "pending_replies.json"'
|
|
text = text[:start] + method.replace('OFFLINE_QUEUE_PATH', queue) + text[end:]
|
|
text = text.replace('if send_state == "sending":\n return "正在发送"\n if review["deliveryUncertain"]:', 'if send_state == "sending" or state.get("stage") == "sending":\n return "正在发送"\n if send_state == "sent_uncommitted":\n return "已发送,待归档(需人工核对)"\n if review["deliveryUncertain"]:')
|
|
text = text.replace(' if send_state == "sent_uncommitted":\n return "已发送,待归档"\n', '')
|
|
save('wechat_gui_qt.py', text)
|
|
for name in ('test_desktop_review_visibility.py', 'test_qt_console_bridge.py'):
|
|
text = (root / name).read_text(encoding='utf-8')
|
|
text = text.replace("thread.handoff_pending_tasks.assert_called_once_with(['uncertain'])", "thread.handoff_pending_tasks.assert_called_once_with(['uncertain'], confirm_uncertain=True)")
|
|
text = text.replace('thread.handoff_pending_tasks.assert_called_once_with(["done", "protected"])', 'thread.handoff_pending_tasks.assert_called_once_with(["done", "protected"], confirm_uncertain=False)')
|
|
if name == 'test_desktop_review_visibility.py':
|
|
text += '''
|
|
def test_stale_snapshot_requires_explicit_confirmation_before_backend_handoff(self):
|
|
self.seed()
|
|
thread=SimpleNamespace(is_alive=lambda:True, handoff_pending_tasks=mock.Mock(side_effect=[
|
|
{'handed_off':[], 'protected':['ordinary'], 'requires_confirmation':['ordinary']},
|
|
{'handed_off':['ordinary'], 'protected':[]},
|
|
]))
|
|
self.host._thread=thread
|
|
with mock.patch.object(QMessageBox,'question',return_value=QMessageBox.StandardButton.Yes) as question:
|
|
qt.MainWindow._handoff_queue_tasks(self.host,['ordinary'])
|
|
self.assertEqual(thread.handoff_pending_tasks.call_args_list,[
|
|
mock.call(['ordinary'],confirm_uncertain=False), mock.call(['ordinary'],confirm_uncertain=True)])
|
|
question.assert_called_once()
|
|
|
|
def test_legacy_receipt_states_are_visible_but_not_sendable(self):
|
|
for state in ({'stage':'receipt_check'}, {'send_state':'unknown'}, {'send_state':'sent_uncommitted'}):
|
|
with self.subTest(state=state):
|
|
flags=task_review_state(state)
|
|
self.assertTrue(flags['deliveryUncertain']);self.assertTrue(flags['needsHuman'])
|
|
self.assertFalse(flags['canApprove']);self.assertFalse(flags['canRetry'])
|
|
self.assertEqual(self.page._display_task_status(self.page._state_text(state)),'需人工')
|
|
'''
|
|
save(name, text)
|
|
print(str(root) + ': receipt confirmation strengthened')
|