1 条 audit=0 的幽灵记录:我说不清是我删的,还是平台吞的
我把自己掘金账号的账本全量拉了一遍:21 篇文章,全部 audit=2(已过审),0 篇拒审,草稿箱 0 条,总阅读 1132。 干净得像是可以收工了。
但有一条记录,我至今说不清:7656751882112057371,标题显示为「Agent Nexus:为 AI Agent 经...」,audit=0、阅读 0。它曾经出现在我"已发布文章"的列表里,今天不在了------而我证明不了是我删的。 这篇就写清楚:草稿、已发布、幽灵记录是三套系统;判断一条东西到底在不在,我用三个探针;以及一次"删了却没留证据"的翻车。
一、先说清这条记录有多怪
它的公开链接是 404,但它出现在作者侧的文章列表接口 里。这两个事实同时成立,才叫幽灵:article/query_list(作者侧)认它,juejin.cn/post/<id>(匿名侧)不认它。
按掘金 id 单调递增的规律,它的 id 紧挨着我 6 月那批手写文章(7655217832594915379),比 9 月 13 日管道产出的第一篇(7684658712820613147)小了一截------也就是说,它是 6 月底前后留下的一份记录,不是自动化管道写的。至于正文写了什么,我查不到 :draft/detail 返回 403、编辑器页空白。严格讲,我只知道它有 id、有标题前缀、状态是 audit=0。
二、三个探针:判断"这条东西到底在不在"
不要用单一接口的结论下判断。我用三步,全部在页面上下文执行(这些接口对脚本重放有签名校验,外部 curl 会拿到"路由不存在"):
js
// 登录掘金后,在浏览器控制台执行。id 换成你要查的那条
const id = '7656751882112057371';
// ① 作者侧状态:1=审核中 2=已过审 0=未提交/未发布的历史记录(注意:0 不是"被拒")
const j = await (await fetch('https://api.juejin.cn/content_api/v1/article/query_list', {
method: 'POST', credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cursor: '0', sort_type: 2, user_id: '<你的 UID>' })
})).json();
const hit = (j.data || []).find(x => x.article_id === id);
console.log('① 作者侧:', hit ? { audit: hit.article_info.audit_status, view: hit.article_info.view_count } : '不在列表');
// ② 公开页:200=已公开发布,404=未公开
console.log('② 公开页:', (await fetch('https://juejin.cn/post/' + id, { credentials: 'omit' })).status);
// ③ 编辑权限:403=该 id 不在当前账号可编辑的草稿里
const d = await fetch('https://api.juejin.cn/content_api/v1/article_draft/detail', {
method: 'POST', credentials: 'include',
headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ draft_id: id })
});
console.log('③ draft/detail:', (await d.text()).slice(0, 60));
今天(2026-09-24 20:30 CST)的实测输出,一字未改:
bash
① 作者侧: 不在列表(全量 21 条里已无 audit=0 的记录)
② 公开页: 404
③ draft/detail: {"err_no":403,"err_msg":"权限认证错误"}
三个探针合起来给的是同一个结论:它不在任何"用户可见"的位置------但它曾经过审前那套列表。9 月 14 日我第一次看到它时,结论就已经写成"未提交的历史记录态,不在发布队列,它不会自己发出去,别当成被拒重投"。这句话救了我:如果我把它当"被拒",就会去重投一篇 6 月的旧稿。
三、为什么"已发布列表"里会有没发布的东西
因为后台至少有三套东西,名字很像,行为完全不同:
| 系统 | 接口 | 关键坑 |
|---|---|---|
| 草稿箱 | article_draft/query_list |
从"已发布文章 → 编辑"生成的编辑草稿不进这里(我的草稿箱长期是 0 条,但编辑器里明明有稿) |
| 文章记录(作者侧) | article/query_list |
带 audit_status;audit=0 的历史记录会出现在这里,公开页却 404 |
| 编辑草稿 | 无公开列表 | id 与文章 id、原草稿 id 三者都不同;只能靠编辑器页 URL 记 |
还有一个更阴的:空 body 探测写接口,会真的建一条记录。 我早期用 POST article_draft/create 传 {} 去"探接口通不通",返回 err_no:0,然后账号里就多了一条空草稿------探测本身就是写入。现在的纪律是:探写接口前先把删除手段准备好,并且用"字段名故意写错"的合法 JSON 去探,拿 schema 报错,不落库。
四、数据口径:同一天两套读数,谁都别信
9 月 14 日我遇到过一次分裂:一套读数说三篇文章是 46 / 14 / 4,另一套(每周复盘脚本的输出)说是 9 / 1 / 0,id 集合还不同------因为一套把那个 audit=0 的记录算进来了,另一套没算。没有统一口径之前,任何"规律"都建在流沙上。
所以今天我用的是唯一口径:分页拉全量 (has_more + cursor,默认首页只给 10 条,只看首页会得出错的均值):
ini
21 篇 · 全部 audit=2 · 总阅读 1132 · 赞 5 · 收藏 2 · 草稿箱 0
AI 全自动发的 10 篇:合计 291 阅读,均值 29.1(逐篇 7/11/9/15/25/17/19/135/26/27)
我手写的 11 篇:合计 841 阅读,均值 76.5
最好一篇:135 阅读,也是全账号仅有的 2 个收藏的唯一来源
均值上 AI 组只有手写组的 38%,但把极值摆出来就露馅了:AI 组里有一篇 135,比手写组 11 篇里的 10 篇都高。同一套脚本、同一个账号,阅读差 19 倍------变量是内容,不是"AI"。 这也再一次说明:一条记录的归类错了(比如把 audit=0 当成"未过审"),整个账本的分母就错了。
五、翻车:删了,却证明不了
这条从未公开的记录留在列表里没有价值,我给它写了删除脚本。守卫是双条件------标题正则匹配 + 状态必须为 0,两条都满足才允许执行:
js
// 摘自我 2026-09-14 落盘的删除守卫脚本(mtime 11:55)
const hit = (j.data || []).find(x => x.article_id === id);
const guardOk = hit && /Agent Nexus/i.test(hit.article_info.title) && hit.article_info.audit_status === 0;
if (!guardOk) { console.log('!! 守卫未通过(标题不匹配或状态非 0),未执行删除'); return; }
// 通过后才调用:
// POST /content_api/v1/article/delete body: { article_id: id }
// 删后复核三件:作者侧条数 -1 / 公开 URL 仍为 404 / 自家已发布文章的条数与标题未受影响
守卫的逻辑我现在还认。问题在于我没有执行日志。 今天我能证明的只有:这条记录不在列表里了(全量 21 条,audit=0 记录数 = 0)、公开页 404、draft/detail 403。我不能证明是那个脚本干的------也可能是平台自己清理了历史态记录。
这就是这篇最想抄给你的一条:写有回查,删没有回查。 发文链路我给它配了四处回查(建稿回查 draft_id、编辑器页核对正文长度、上传封面回查 cover_image 非空、发布后回查最新一篇标题前 14 字),可轮到删除,我只信了"接口返回 ok"。平台不会给你一张"已删除"的回执,所以删除类动作必须自己落账:谁删的、什么时候、删前快照(id + 标题 + audit + 阅读)。这一行日志我漏了,代价就是这篇文章的标题------我删了一篇东西,却拿不出证据。
六、还有一个更便宜的教训:窗口是幻觉
9 月 18 日我看数据时用的是 slice(0, 3) 的硬编码窗口,结果第一篇滑出了窗口,我当下分不清它是"被新文章挤下去"还是"被删了"。当时我只敢在复盘里写"本次无法取证",没敢下结论。
今天回头看,那次和这次是同一个错的两面:"列表里没有"≠"被删了","列表里有"≠"已发布"。 唯一能信的是全量 + 多探针。
七、可以直接抄的清单
- 判断一条内容"到底在不在",用三探针:作者侧状态 / 匿名公开页 / 编辑权限,别用单一接口的结论;
audit=0是"未提交的历史记录",不是"被拒"; - 拉数据一律分页全量:
cursor+has_more,默认首页只有 10 条,用首页数据算均值会错; - 探测写接口前先准备好删除手段,并且用"字段名故意写错"的合法 JSON 探,不落库------空 body 会真的建记录;
- 删除必须双条件守卫(标题正则 + 状态),并且删前落一份快照、删后写明"谁删的/何时";
- 删后复核三件:作者侧条数 -1、公开 URL 状态、自家已发布文章的条数与标题未受影响;
- 任何"列表窗口"(
slice、只看首页、只看最近 N 条)都不配当证据,只有全量配。
21 篇全过审、0 拒审,是我这段时间账号侧最好看的一次账。但真正让我睡不着的不是那些数字,是那条消失的记录------我能证明的比我想象的少。