先说结论:那个按钮当时点下去显示"成功",但一个请求都没发出去。
问题不在按钮。在我自己三个月前写的推送函数里,它开头有这么一行:
if ( $last && ( time() - $last ) < 6 * HOUR_IN_SECONDS ) {
return; // 6 小时内推成功过,直接返回
}
我加它是有道理的。WordPress 后台编辑文章会反复触发 save_post,每存一次推一次,百度那边的日配额当天就烧光。防抖必须做。
但它防抖防的是"自动推送"。我拿它去接"人主动点"的按钮,就出事了。
昨天张工说想自己点推送、能看见状态、能改 API token。听起来就是加个按钮。我图省事,直接复用了发布流程那条路:按钮 → 调 zz_baidu_push($id) → 显示结果。
界面一切正常。绿字,写着"已推送百度"。
我盯着那行绿字,脑子里冒出一个很蠢的问题:如果它根本没发请求,我拿什么证明它发了?
一个函数,两种语义
回去翻那段代码,我才发现,我把两种完全不同的动作塞进了同一个函数。
- 自动推送:系统自己触发,一秒内可能被叫醒十次,必须去重
- 手动推送:人点了按钮,一次点击就是一次意图,必须真发
zz_baidu_push() 是给第一种写的。它甚至不区分"发过但失败了"和"六小时内发成功过"—— 只要六小时内成功过,一律 return,连日志都不记。
这就是假成功。界面告诉你成功了,底层一个字节都没出去。它比报错难查得多,因为没有任何东西是红的。
走独立通道,共写一张账
改法很简单:手动通道不复用那个带短路的封装,直接调到再下一层。
// baidu_push.py 里对应的 PHP 侧逻辑
if ( $mode === 'manual' ) {
$r = zz_baidu_post( $urls ); // 发请求 + 拿真实响应
zz_baidu_record( $post_id, $r ); // 落 postmeta,和自动通道共写一张账
}
这里有个刻意保留的设计:两个通道写同一份 postmeta。不然手动推过的文章,列表里的状态列还是"未推送",人会再点一次,白白多烧一次配额。
怎么验证它真的发了?光看界面没用,得看百度服务端报的数。我挑了一篇 9 小时前已经推成功的文章 —— 它百分百会撞上短路 —— 手动点了一次,然后盯配额:
推送前:{"remain": 8, "success": 0}
推送后:{"remain": 7, "success": 1}
remain 从 8 掉到 7。这是百度那边扣的,不是我本地记的,做不了假。

顺带说个更要紧的事
按钮做完当天,张工发来截图问我:"接口调用地址里没有填百度 API 的地方啊。"
他要填的那个地址是 http://data.zz.baidu.com/urls?site=https://www.budingwz.cn&token=xxx。这个 site 参数本身就是错的,带了协议会直接 400。
| site 写法 | 实测结果 |
|---|---|
site=https://www.budingwz.cn |
400 {"error":400,"message":"site init fail"} |
site=www.budingwz.cn |
200 {"remain":6,"success":1} |
好消息是本站不用改。线上插件里的 zz_baidu_host() 会自己剥协议,渲染时剥一层、运行时再剥一层。但这个坑我已经踩过一次了,配置错了半年,一篇都没推出去 —— 那次为什么一直失败,写在另一篇里。
所以这次干脆把对照做进设置面板:正确写法绿色、错误写法红色加删除线,并排摆着。配置这种东西,光写文档没用,得让错的那个当场显形。

按钮之外
后面还补了三件事。入口从「设置」改名叫「百度设置」——"设置"这词太泛,没人知道里面装的是百度 API。界面上加一列推送状态,直接读 postmeta。再做一条结果横幅:发布或提交后页面顶部弹一整条,四种状态(推送成功 / 推送失败 / 未推送 / 存草稿),一屏看完不用翻日志。

顺手记个小东西,不确定算不算最佳实践:验证状态我直接查了库,没走 WP 的对象缓存。
mysql -e "SELECT post_id, meta_value FROM wp_postmeta WHERE meta_key='_zz_baidu_last'"
真花时间的不是按钮。是把"看得见"这件事做到底:能点、能改、点完能立刻看见结果 —— 而且那个结果是真的。
上次也踩过同类的坑。脚本发文章绕过了 WordPress 钩子,连发 6 篇百度一篇没收到,脚本还老老实实报成功。任何"我封一层"的动作,先问一句:里面那层会不会悄悄把活跳过。