用户把数据清理了,搞了一个P0事故,这个功能让我加班到半夜,所以我加了手工确认

一、深夜事故:一个「是」字,整站清零

故事发生在某个深夜。

一位用户翻着 VVCMS(微微CMS)后台,在「系统工具」里发现了一个新功能:测试数据生成。名字平平无奇,介绍也人畜无害——「生成测试栏目、内容和标签数据」。深夜的大脑向来好奇,他点了下去,还贴心地调大了数量。

几秒钟后,网站上凭空多出一批文章。栏目是测试栏目,内容是测试内容,标签是测试标签,整整齐齐,像一队突然占领客厅的陌生客人。

用户有点慌,来找我:这玩意儿咋删?

我说:旁边有个「清空测试数据」,点它就行。

然后,事故发生了。

用户没有逐条删除——废话,几十条测试文章逐条删,删完天都亮了。他直奔主题,点了「清空测试数据」。屏幕上弹出一个确认框,只有一句话、两个按钮。他没有读完那行字,移动鼠标,点了「是」。

刷新前台——空白。栏目页,404。文章页,404。连主题自带的单页都没了。

那一晚,我在加班中度过,凌晨才把站恢复回来。而那个「清空测试数据」按钮,看着挺老实,实际上是个不讲道理的按钮:它根本不区分测试数据和正式数据,一律全删。

互联网人的深夜三件事:改需求、修 bug,以及——亲手按下那个「是」。

痛定思痛,我们给这类操作装上了一套「二次确认」。这篇文章,就是这套防护的诞生记,加一次完整的复盘。

二、复盘一:根因不在用户,在「零成本的确认」

先把事故链路完整回放一遍。你会发现,每一步单拎出来都「合理」,合起来就是一场灾难:

  • 用户好奇,点了「生成测试数据」——功能摆在那儿,就是让人用的;
  • 发现网站冒出一堆测试内容,想清理——正常反应;
  • 旁边就有「清空测试数据」——按钮名字简直就是为这个场景准备的;
  • 弹出确认框,没细看,点了「是」——谁这辈子没点过几百个「是」;
  • 整站清零——因为后端压根不区分测试数据。

代码级的事实更直白。后台这两个按钮——「清空测试数据」和主题的「初始化数据」——后端走的是同一条底层逻辑 ClearCoreTables:直接 DELETE FROM content_tag / content / tag / column(PostgreSQL 下走 TRUNCATE)。不区分是不是测试数据,整站栏目、内容、标签及其关联关系一并清空,没有回收站,没有撤销入口。

而当时的清除提示写的是「删除测试栏目、内容、标签」——这句文案本身就是误导:它让用户以为删的只是测试数据,实际删的是全部。

结论:把「整站毁灭」做成了「一个普通按钮 + 一句话确认」,出事后却怪用户不看提示,这是甩锅。提示防不住手滑,更防不住「看了一千遍早就麻木」的确认疲劳。真正该改的是产品,不是用户。

三、复盘二:二次确认不是加个弹窗,是加一层「刹车片」

先说清楚:普通确认框为什么不够用。

「确认/取消」两个按钮,点击成本约等于零。用户对弹窗的肌肉记忆是「弹出来 → 点确定」,压根不过脑子。对不可逆操作来说,这种防护约等于没有。

所以我们的设计目标不是「再问一遍」,而是给毁灭按钮装一层刹车片。最终落地的危险确认框(confirmDanger),由四层构成:

  • 第一层,红色定调:「此操作不可撤销,也没有回收站。」红色警示置顶,先把「这事儿有多大」说清楚;
  • 第二层,后果清单:逐条写「会删掉什么」——删除全部栏目、内容与标签及其关联关系;前台所有内容页与栏目页立即变成 404,包括主题自带的单页;不进入回收站,也没有撤销入口。写具体后果,不写「请谨慎操作」这种空话;
  • 第三层,逐字输入短语:用户必须一个字不差地输入「确认清理」才能放行。这一招是把「阅读提示」变成「打字动作」——阅读可以跳过,打字跳过不了。手抖打错一个字都过不去;
  • 第四层,给出路:提示执行前先到「备份管理」创建一份数据库备份。没有回收站,就必须给备份这个真正的恢复点。

确定按钮也染成了红色(el-button--danger)——这类弹窗的「确定」必须一眼看出不是常规操作。

几个实现上的硬规矩,都是踩过坑的:

  • 短语比对忽略大小写与首尾空白,其余逐字严格相等。什么「包含即通过」「输入前两个字就算过」一律不行——宽松到那个程度,防护就没了;
  • 弹窗正文走 HTML(红色警示要靠它),所以所有动态文本必须先转义。短语和后果清单将来可能带上主题名之类的变量,不转义等于把 XSS 面铺在一个「全站毁灭」按钮旁边;
  • 用 ElMessageBox.prompt 而不是自写表单:焦点管理、回车提交、Esc 关闭这些无障碍细节,框架自带;
  • 取消、关闭、Esc,一律返回 false——「取消」不能被搞成未捕获异常。

四、真容:改造后的危险确认长什么样

光说抽象,上图。以下截图全部来自真实后台。

VVCMS 后台系统工具中的测试数据生成页面,包含刷新、清空测试数据、生成测试数据三个按钮和数量配置

图1:事故的起点——「测试数据生成」页面。注意「清空测试数据」和「生成测试数据」两个按钮肩并肩摆着,手滑成本极低。当初就是这里。

旧版普通确认弹窗,只有警告图标、一句说明和取消、确定两个按钮

图2:上一代防护——这是「生成测试数据」至今仍在用的普通确认框,也正是事故当晚「清空测试数据」的样式。一句话、两个按钮、零成本。用户不是不看提示,是这种提示看一千遍也记不住任何一个字。

清空测试数据的危险确认弹窗,红色不可撤销警示、三条后果清单、备份提示和确认短语输入框

图3:现在的「清空测试数据」。红色警示定调、三条后果写具体删什么、备份提示给出路、输入框占位符直接亮出短语「确认清理」。四个信息层,一眼就能看懂这个按钮的分量。

输入框内输入了确认二字,弹窗下方红色提示输入内容与确认短语不一致

图4:输错短语的下场。输入「确认」两个字不够,必须完整输入「确认清理」,错误提示直接告诉你差在哪。想手滑混过去?没门。

VVCMS 后台主题管理页面,主题列表中每行操作列包含详情、备份、初始化数据、导出数据等链接按钮

图5:另一个高危入口——主题管理的「初始化数据」。它只是主题列表里的一个 link 按钮,混在一排操作里,误点率比独立按钮更高。它的破坏力同样不讲道理:先清空全部栏目、内容与标签,再用主题包里的 init_data.json 重建,当前站点内容全部丢失,同时还会覆盖 init_data.json 中声明的系统配置项。

初始化主题数据的危险确认弹窗,标题带主题名,红色警示和三条后果,输入框占位为确认初始化

图6:主题「初始化数据」的同款防护,短语是「确认初始化」,标题还会带上主题名——让用户明确知道自己正在动哪个主题。同一条底层清空链路,一个都别想裸奔。

五、防护会退化,所以要有守卫

功能上线只是开始。真正让人睡不着的,是退化:哪天有人图省事,把输入确认改回「点一下确认」,或者新增一个清空接口忘了加防护——这种退化不会有任何报错,页面照常工作,直到下一次深夜事故。

所以我们加了一道静态守卫(npm run check:danger-confirm)。它扫描全部源码,守两件事:

  • 所有会清空整站内容的接口调用(目前名单:clearTestData、fetchThemesInitData),所在函数必须先出现 confirmDanger,否则直接报错退出;
  • 每一次 confirmDanger 调用必须带 phrase 和 consequences——少了短语就退化成「点一下确认」,少了后果清单就只剩一句空泛的「请谨慎操作」。

我们还实测过它的牙齿:临时往源码里插一个没有防护的清空调用,脚本立刻 exit 1;删掉,恢复绿色。新增同类接口时把函数名加进名单——漏登记的后果,是它在后台裸奔。

六、后来:连「恢复建议」都修过一次

防护上线第二天,我们又揪出一个细节:清空弹窗里的提示,原本建议用户「执行导出数据,把它写入主题的 init_data.json」作为恢复手段。

听着贴心,实则离谱——init_data.json 是主题的种子文件,不是恢复点。拿种子文件当备份,等于把抢修指南当成安全绳。我们随即将提示改为「先到备份管理创建一份数据库备份;清空无法撤销,只有备份能完整恢复」。

教训:给破坏性操作配「恢复建议」时,先确认那个建议指向的是真正的恢复点,别再造一个误导。

七、方法论沉淀:破坏性操作防护清单

这次深夜加班换来的,不只是两段代码,而是一份可以直接抄走的清单:

  1. 先分级:操作分可逆与不可逆。生成测试数据是可逆的(删掉即可),用普通确认;清空、初始化是不可逆的,必须上短语确认;
  2. 把「阅读」变成「动作」:提示可以被跳过,打字跳过不了。不可逆操作要付出打字成本,这就是刹车片;
  3. 文案写后果,不写态度:「会删掉什么、前台会怎样、能不能撤销」逐条写清楚,禁写「请谨慎操作」;
  4. 没有回收站,就要给备份出口:每一次不可逆提示,都该顺手告诉用户真正的恢复点在哪;
  5. 同链路全查:修一个点,立刻问「还有谁走同一条底层逻辑」——ClearCoreTables 上挂着两个入口,那就两个一起修;
  6. 防护要上守卫:靠人记不住的规矩,交给脚本守。退化不报错,才是防护最大的敌人。

如今,再点「清空测试数据」,你得先老老实实敲出「确认清理」四个字;点「初始化数据」,得敲「确认初始化」。多了几秒麻烦,少的是无数个可能重演的深夜。

每一个看起来「反人类」的确认框背后,都站着一个深夜加班的人。二次确认不是不信任用户——是产品替用户,记住那个不能再犯的错。

本文地址: https://www.vvcms.cn/blog/danger-confirm-story
版权所有 © VVCMS 团队 未经授权不得转载