整个项目里,批量开票是最难啃的骨头。这个东西前前后后改了四个版本,每次都觉得"这次应该好了",然后实际一跑又出问题。
V1:最简单的做法——循环开弹窗
第一版的想法很直接:用户勾选了多少个订单,就打开多少个弹窗,每个弹窗里加载拼多多的发票页面。页面上有个自动脚本(invoiceAutomate.js)会自动点击申请按钮、填写信息、提交。
写出来之后跑了一下——能行。选了 5 个订单,5 个弹窗依次打开了,也都正常跑完了。
然后我选了 20 个订单试了一下。
屏幕瞬间堆满了 20 个弹窗,浏览器卡成幻灯片,有一半的弹窗页面根本没加载出来——空白的。最后那 20 个订单里只成功了 6 个,剩下的全是"窗口创建失败"或者"处理超时"。
很明显这个方案不行。
V2:加了过滤 + 串行处理
第二版做了两个改进。第一个是在开票之前先检查这个订单要不要跳过——待发货的跳过、已经开过票的跳过、有发票详情的跳过、已退款的跳过。这样能减少无效弹窗的数量。
第二个是改成串行——不再一次性开一堆弹窗,而是一个一个来。打开一个弹窗 → 等它处理完 → 再开下一个。还加了个进度窗口显示每个订单的处理状态(等待中、处理中、成功、失败、已跳过)。
但是,这个版本有个致命问题——还是需要等。
每个弹窗打开后等 1.8 秒再开下一个。1.8 秒看起来不多,但弹窗里那个自动脚本需要大概 8-10 秒才能处理完一个订单——意味着弹窗关了之后还要等很久。而且有的订单页面加载慢,1.8 秒之后脚本还没开始跑,弹窗就已经关了。所以实际成功率只有大概 60%。
V3:单弹窗复用
AI 提了个方案:不要每次都开关弹窗了。打开一个弹窗,处理完第一个订单之后不关闭,直接把弹窗里的页面导航到第二个订单的 URL——这样不需要重新加载整个弹窗,页面跟着变就行。弹窗只在被用户手动关闭时才重新打开。
这个思路我一开始没理解——"导航"是什么鬼?AI 解释了一下我才明白——就是把弹窗里标签页的地址直接换成下一个订单的地址。就像你在浏览器地址栏里换了一个网址那样。
这个方案的效果确实好多了。不再有弹窗堆满屏幕的问题,处理速度也快了不少。
但新问题冒出来了。
V4:找到真正的根因——消息发不出去
第三版跑起来之后,每个订单都要等 25 秒然后报"处理超时"。但我明明看到弹窗里的自动脚本已经执行完了——发票都申请成功了,订单状态也显示"已提交"了。为什么还超时?
我让 AI 帮我排查。AI 从头开始捋整个通信链路:
- 弹窗里的自动脚本处理完发票之后,会发一条消息给主面板(Dashboard),说"这个订单搞定了"
- 主面板收到消息后,把这个订单标记为"成功",然后开始处理下一个
- 问题出在哪?——主面板根本没收到消息
AI 继续排查,最后锁定到了一个细节:window.opener.postMessage() 这行代码。弹窗是用 chrome.windows.create 创建的,这种弹窗的 window.opener 是 null。也就是说,弹窗跟主面板之间根本没有通信连接——消息发出去就消失了。
我之前完全不知道这个细节。AI 说这是 Chrome 扩展开发里一个常见的坑,很多人一开始都会踩。
解决方案:不用 window.opener.postMessage,改用 chrome.runtime.sendMessage。这个 API 不依赖弹窗的创建方式,无论在哪种窗口里都能正常工作。为了让消息能正确到达主面板,中间还经过了一个"中转站"——扩展的后台 Service Worker。
改成双通道通信之后,批量开票终于丝滑了。选了 20 个订单,弹窗安静地打开、一个接一个自动处理完、关掉——全程不需要我碰一下。盯着列表里一个个"✅ 已提交"跳出来,那一刻的满足感很难形容。
这个功能的开发过程教会我一件事:不要满足于"能跑"。能跑只是第一步,性能、稳定性、异常处理——这些东西才是让功能真正好用的关键。而且这些优化大部分不需要你懂技术细节,你只需要会向 AI 描述"现在哪里不好",它就能帮你找到解决方案。