
代码出现问题时,很多人会直接把一句"为什么重复提交"发给 AI。
这样得到的答案往往是一长串可能性:缓存、网络重试、数据库重复写入、按钮连点、接口幂等......看起来都对,但很难马上解决问题。
真正高效的方式是:
先收集事实,再让 AI 根据事实缩小排查范围。
这篇文章用一个经过脱敏的典型案例,带你走一遍完整过程。
一、问题现象:点击一次,却创建了两条订单
一个 Vue 3 订单创建页面出现了问题:
- 用户点击一次"提交订单"。
- 页面显示提交成功。
- 后台却创建了两条内容相同的订单。
第一反应很容易是"数据库写重复了"。
但这个阶段不能下结论,因为重复数据可能发生在不同位置:
- 前端调用了两次接口。
- 浏览器或网络层重试了请求。
- 后端路由被执行两次。
- 数据库写入逻辑重复执行。
- 用户快速连续点击了按钮。
先把问题拆开,才能避免一开始就查错方向。
二、第一步:收集最小证据
排查前,先记录可以确认的事实。
1. 看浏览器网络面板
打开浏览器开发者工具的 Network 面板,筛选创建订单接口:
text
POST /api/orders 201 10:12:08.421
POST /api/orders 201 10:12:08.435
两次请求间隔只有 14 毫秒,请求体也完全一致。
这时可以确认一件事:
重复请求在浏览器发出请求之前或浏览器发出请求时就已经发生了。
因此,优先检查前端事件绑定和请求函数,而不是先修改数据库。
2. 看后端日志
后端日志中也能看到两次请求:
text
POST /api/orders requestId=req_demo_001
POST /api/orders requestId=req_demo_002
这说明后端确实分别收到了两次请求,并不是同一次请求被日志重复打印。
3. 保留相关代码,不上传整个项目
此时只需要收集:
- 表单模板。
- 提交函数。
- 请求函数。
- 两条脱敏后的网络记录。
- 运行环境和复现步骤。
不需要把整个项目、数据库配置或真实订单数据交给 AI。
三、第二步:把事实交给 AI 分析
不要这样问:
text
订单为什么会重复创建?
更有效的 Prompt 是:
text
请帮我分析一个 Vue 3 页面重复提交订单的问题。
已确认事实:
1. 用户只点击了一次提交按钮。
2. 浏览器 Network 面板出现两次 POST /api/orders。
3. 两次请求体相同,时间间隔约 14 毫秒。
4. 后端收到两个不同 requestId 的请求。
5. 没有看到 3xx 跳转或网络重试标记。
相关代码:
[粘贴表单模板、提交函数和请求函数]
请输出:
1. 最可能的 3 个原因,按概率排序。
2. 每个原因对应的验证方法。
3. 下一步优先检查哪个位置,为什么。
要求:
- 不要直接修改代码。
- 不要把未确认的原因当成结论。
- 区分"已知事实"和"待验证假设"。
这个 Prompt 的重点不是让 AI 立即给答案,而是让它生成一份可验证的排查路线。
四、第三步:检查最可能的问题点
相关模板代码如下:
vue
<form @submit.prevent="submitOrder">
<input v-model="form.productId" />
<button type="submit" @click="submitOrder">提交订单</button>
</form>
提交函数:
javascript
async function submitOrder() {
await createOrder({
productId: form.productId
});
}
AI 的第一条假设通常会是:
button的click事件会调用一次submitOrder,而type="submit"又会触发表单的submit事件,再调用一次submitOrder。
这不是最终结论,但它可以立刻验证。
在函数开头临时加入一条本地日志:
javascript
async function submitOrder() {
console.count('submitOrder called');
await createOrder({
productId: form.productId
});
}
点击一次后,控制台输出:
text
submitOrder called: 1
submitOrder called: 2
至此,我们拿到了完整证据链:
text
一次点击
↓
click 事件调用 submitOrder
↓
submit 事件再次调用 submitOrder
↓
浏览器发出两次 POST 请求
↓
后端创建两条订单
注意:console.count 只用于本地定位问题,修复后要删除,不能带到正式环境。
五、第四步:做最小修复
表单提交只保留一种触发方式即可。
这里保留表单的 submit 事件,删除按钮上的 click 事件:
vue
<form @submit.prevent="submitOrder">
<input v-model="form.productId" />
<button type="submit">提交订单</button>
</form>
为什么推荐保留 submit?
- 点击按钮可以提交。
- 用户在输入框中按 Enter 也可以提交。
- 表单行为集中在一个入口,更容易维护。
修改完成后,再点击一次按钮并检查 Network 面板:
text
POST /api/orders 201 10:26:18.702
只剩下一条请求,问题得到修复。
六、修复重复调用,不等于解决所有重复订单风险
前端事件冲突解决后,仍然要考虑用户快速连续点击的情况。
可以在请求期间禁用按钮:
vue
<button type="submit" :disabled="submitting">
提交订单
</button>
javascript
async function submitOrder() {
if (submitting.value) {
return;
}
submitting.value = true;
try {
await createOrder({
productId: form.productId
});
} finally {
submitting.value = false;
}
}
这能减少重复点击,但不能替代后端保护。
对于订单、支付、扣库存等高风险接口,后端还应该根据业务设计幂等机制,例如:
- 使用客户端提交标识,重复请求返回同一个结果。
- 使用业务唯一键防止重复创建。
- 在数据库层增加合适的唯一约束。
具体方案取决于业务,不能只复制一段通用代码。
七、让 AI 帮你补充验证场景
修复后,可以继续让 AI 列出测试场景:
text
订单提交重复调用问题已经修复。
修复方式:
- 删除按钮上的 click 提交逻辑。
- 只保留 form 的 submit 事件。
- 请求期间禁用提交按钮。
请列出需要手动验证和自动化测试的场景。
要求覆盖:
1. 鼠标点击提交。
2. 在输入框按 Enter 提交。
3. 快速连续点击。
4. 请求成功。
5. 请求失败后再次提交。
6. 表单校验失败。
每个场景说明:操作、预期请求次数、预期页面结果。
我们至少要验证下面这些内容:
| 场景 | 操作 | 预期结果 |
|---|---|---|
| 鼠标提交 | 点击一次提交按钮 | 只发起一次请求 |
| 键盘提交 | 在输入框按 Enter | 只发起一次请求 |
| 连续点击 | 请求未完成时连续点击 | 只发起一次请求 |
| 请求失败 | 接口返回错误后再次提交 | 按钮恢复可用,可再次提交 |
| 校验失败 | 商品未选择就提交 | 不发起创建订单请求 |
八、这个案例中,AI 真正帮了什么
AI 没有替我们"猜中答案",它主要帮助完成了三件事:
- 根据已有事实列出排查假设。
- 把"重复订单"拆成前端、网络、后端和数据库几个层次。
- 提醒我们在修复事件冲突后,继续考虑重复点击和接口幂等。
真正定位问题的证据,仍然来自:
- Network 面板中的两次请求。
- 后端的两个请求记录。
console.count的两次函数调用。- 修复后只剩一次请求的验证结果。
这也是使用 AI 排查问题的正确方式:让 AI 帮你提出下一步,而不是替代证据。
九、一份可复用的排查模板
以后遇到报错或异常行为,可以先按下面格式整理,再发给 AI:
text
问题现象:
[用户看到了什么]
复现步骤:
1. [步骤 1]
2. [步骤 2]
已确认事实:
- [网络、日志、调用次数或返回数据]
运行环境:
- [框架、版本、浏览器或服务信息]
相关代码:
[最小相关代码]
已经尝试:
- [已经做过的检查或修改]
请输出:
1. 最可能原因,按优先级排序。
2. 每个原因的验证方法。
3. 最小修复建议。
4. 修复后需要补充的测试。
要求:先分析,不要直接重写整个项目;区分事实与假设。
总结
AI 能帮助我们更快定位问题,但前提是先给它可靠的上下文。
这个案例的排查顺序是:
text
发现重复数据
↓
查看 Network 和后端日志
↓
确认是两次独立请求
↓
让 AI 提供可验证假设
↓
用最小日志确认函数调用次数
↓
最小修改并再次验证
↓
补充防重复提交和幂等保护
记住:AI 给出的"可能原因"只是排查起点;日志、请求记录、测试结果才是最终证据。
下一篇文章将介绍:
《我的 AI 编程日常习惯:如何真正提升效率》
✍坚持原创,求关注,点赞,收藏