87 lines
5.4 KiB
Python
87 lines
5.4 KiB
Python
from pathlib import Path
|
||
import hashlib
|
||
import json
|
||
|
||
folder = Path(__file__).resolve().parent
|
||
names = [
|
||
'wechat_gui_qt.py', 'gui_runtime.py', 'wechat_bot.py', 'review_state.py',
|
||
'assets/ui/console-app.js', 'assets/ui/zhenyang-ai-console.html', 'assets/ui/console-review.css',
|
||
'test_desktop_review_visibility.py', 'test_visual_review_visibility.py', 'test_visual_handoff_visibility.py',
|
||
'test_review_handoff.py', 'test_handoff_safety.py', 'test_handoff_evidence.py', 'test_queued_handoff_report.py',
|
||
'test_qt_console_bridge.py', 'test_queue_log.py',
|
||
]
|
||
manifest = []
|
||
for root in (Path(r'C:\kefu\wechat_rpa'), Path(r'C:\wechat_rpa')):
|
||
files = names + (['protocol_engine.py', 'test_protocol_review_visibility.py'] if root.name == 'wechat_rpa' and root.parent == Path('C:/') else [])
|
||
for name in files:
|
||
path = root / name
|
||
if path.exists():
|
||
data = path.read_bytes()
|
||
if path.suffix == '.py':
|
||
compile(data, str(path), 'exec')
|
||
manifest.append({'path': str(path), 'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()})
|
||
(folder / 'final-file-manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
|
||
rows = []
|
||
for label, name in (
|
||
('原版桌面、队列、运行器', 'desktop-source-final.json'),
|
||
('协议版桌面、队列、运行器、协议流水', 'desktop-target-final.json'),
|
||
('原版人工接管保护及证据', 'handoff-source-final.json'),
|
||
('协议版人工接管保护及证据', 'handoff-target-final.json'),
|
||
('原版视觉与回复安全', 'visual-source-final.json'),
|
||
('协议目录视觉与回复安全', 'visual-target-final.json'),
|
||
):
|
||
report = json.loads((folder / name).read_text(encoding='utf-8'))
|
||
assert not report['failures'] and not report['errors'], name
|
||
assert not report['blocked_external_actions'], name
|
||
passed = report['tests_run'] - report['skipped']
|
||
rows.append(f"| {label} | {passed} | {report['skipped']} | 0 | [{name}]({name}) |")
|
||
frontend = json.loads((folder / 'qa/frontend-results.json').read_text(encoding='utf-8'))
|
||
assert all(item['passed'] for item in frontend['results'])
|
||
assert not frontend['errors']
|
||
rows.append(f"| 两目录浏览器交互与窄屏 | {len(frontend['results'])} | 0 | 0 | [结果](qa/frontend-results.json) |")
|
||
content = '''# 桌面审核与人工处理可见性修复
|
||
|
||
日期:2026-09-16
|
||
|
||
已更新 C:\\kefu\\wechat_rpa 与 C:\\wechat_rpa 的本地源码。未重新打包安装程序,也未重启正在运行的客户端。
|
||
|
||
## 问题及结果
|
||
|
||
- 协议 `awaiting_review` 原来未映射为待审核,队列缺少独立审核筛选,详情未完整显示草稿及审核原因;现在提供“待审核”“需人工”入口和计数。
|
||
- JS 更新判据遗漏审核标记/原因,任务仅改变审核状态时可能不刷新;现在审核状态变化会更新界面。
|
||
- 视觉队列保存/恢复时遗漏审核原因,部分旧任务被当作过期或旧标记清理;现在保留审核依据,未知旧原因继续等待人工核对。
|
||
- 实际发现的协议任务处于 `receipt_check + uncertain`,是发送结果待核对。现在明确显示原因,仅提供“核对后转人工”;不显示再次审批、重试或普通取消。
|
||
- 人工移交会在后台重新核对发送状态,发送中继续受保护;未知回执须显式确认。监听停止时也能处理。
|
||
- 接管保存原任务快照及操作时间,保留去重/发送证据,不把移交动作标记为送达确认;保存失败不宣称成功。
|
||
- 协议环境异步启动期间保留用户已经点击的审核请求,环境就绪后再核对会话处理。
|
||
|
||
## 使用方式
|
||
|
||
1. 从更新后的目录重新启动客户端,进入“任务队列”。源码更新不会自动替换已安装的旧 EXE。
|
||
2. “待审核”:查看来源消息、审核原因和草稿,确认后点击“通过并发送”。
|
||
3. “需人工”:查看具体原因;发送待核对任务先去企业微信原会话检查,再点击“核对后转人工”。不要把人工接管当作重新发送。
|
||
|
||
视觉模式下,转人工的原消息不会因重启再次发送;只有识别到可靠的后续客户消息才恢复自动处理。无法证明出现新消息时,界面进度及运行日志提示继续人工跟进。
|
||
|
||
## 验证
|
||
|
||
均使用临时数据、模拟模型和发送器;网络、桌面输入与真实发送受到隔离。包含真实 Qt 内嵌页面读取队列状态后的显示验证,以及 Playwright 浏览器交互验证。
|
||
|
||
| 检查范围 | 通过 | 跳过 | 失败 | 结果 |
|
||
|---|---:|---:|---:|---|
|
||
''' + '\n'.join(rows) + '''
|
||
|
||
跳过项是原版目录不具备的协议功能;不是失败。早期中间报告已被上述 `*-final.json` 替代。
|
||
|
||
截图:[待审核详情](qa/protocol-review-queue.png)、[发送待核对](qa/protocol-uncertain-delivery.png)。
|
||
|
||
没有批准、移交或删除真实客户任务,也没有向真实客户发送测试消息。真实模型问答联调的先前登录限制不属于本次隔离验证结果。
|
||
|
||
## 回滚与文件
|
||
|
||
原文件备份:两个项目目录下 `backups/review-visibility-20260916/`,Qt 与前端备份位于对应子目录。当前文件与 SHA256 见 [清单](final-file-manifest.json)。只替换本次对应源码,勿还原真实队列或登录配置。
|
||
'''
|
||
(folder / 'TEST_REPORT.md').write_text(content, encoding='utf-8')
|
||
print(json.dumps({'report': str(folder/'TEST_REPORT.md'), 'manifest_files': len(manifest)}, ensure_ascii=False))
|