为什么要做这个插件

客户用的是子比主题做资源站,主题本身带积分系统,但有两个需求满足不了:

  1. 浏览文章给积分 — 用户看文章也能攒积分,鼓励用户多逛网站
  2. 积分兑换余额 — 积分能换成站内余额,用来购买付费内容
    子比主题自带的积分主要靠签到、评论、消费获取,浏览不给;而且积分和余额是两套独立的体系,不能互转。客户想把这两个点打通。

插件都做了啥 1. 浏览积分功能

用户打开文章页面,停留一段时间就能获得积分。具体规则后台可以配:

  • 每分钟给多少积分(默认1分)
  • 多久结算一次(默认60秒)
  • 普通用户每日上限
  • VIP用户每日上限(VIP拿得多一点,鼓励充会员)
    前端有个小计时器,右下角飘着,显示倒计时和今日已获得多少积分。到点了会飘个"+X分"的动画,用户能看到自己在攒积分,挺有成就感的。

子比积分-2.jpg

子比积分-1.jpg

这里有几个细节是做的时候才想到的:

页面可见性检测 :用户切到别的标签页就暂停计时,回来继续。不然挂着页面不看也给积分,太亏了。

防刷机制 :加了请求节流,两次请求间隔不能少于10秒,防止有人刷接口。

VIP判断 :调用子比的 zib_get_user_vip_level() 函数拿等级,大于0就算VIP。做了多层 fallback,函数不存在就从 user_meta 里读,尽量兼容各种情况。

  1. 积分兑换余额

用户积分够了可以换成站内余额,兑换比例后台能设,比如100积分换1块钱。

兑换入口放在用户中心的积分页面里,点了弹个模态框,输积分数,实时显示能换多少钱,确认后直接兑换。

这个功能做的时候最折腾,主要是和子比主题的API对接:

  • 扣积分用 zibpay_update_user_points ,传负数就是扣
  • 加余额用 zibpay_update_user_balance ,类型写 "Points Exchange"
  • 两边都有日志记录,能追溯 3. 后台管理
    后台加了个设置页面,能调:
  • 浏览积分开关、每分钟积分、结算间隔、普通/VIP每日上限
  • 积分兑换开关、兑换比例
  • 前端通知开关
    还有兑换日志列表,能看谁什么时候兑换了多少积分换了多少钱,成功还是失败。出问题了方便排查。

子比积分-3.jpg

踩过的坑

坑1:子比主题的检测

最开始用 function_exists('zibpay_get_user_points') 检测主题装没装,后来发现有时候主题激活了但函数还没加载到(插件加载顺序的问题)。后来改成了双重检测:先看当前主题是不是 zibll,再检查函数存不存在,这样更靠谱一点。

坑2:兑换按钮渲染时机

最开始想通过 WordPress 的 action 钩子把按钮插到积分页面里,结果子比主题的用户中心是前端渲染的(tab切换那种),PHP 钩子根本没地方挂。

后来改成了前端 JS 注入的方式:

  • 页面加载完延迟1秒,检测当前是不是积分tab
  • 监听 tab 切换事件和 URL hash 变化
  • 检测到在积分页面就把兑换按钮 append 进去
    虽然有点"野路子",但确实能用,而且不用改主题文件,插件禁用了按钮也就没了,比较干净。

坑3:Nonce 验证失败

有段时间兑换功能时不时报安全验证失败,查了半天发现是因为页面缓存或者用户停留太久,nonce 过期了。后来加了个获取新 nonce 的 AJAX 接口,提交前如果 nonce 不对就先刷新一下,问题就解决了。

坑4:积分扣除和余额添加的原子性

最怕的就是积分扣了但余额没加上,或者反过来。处理的时候做了个简单的事务逻辑:先扣积分,再加余额;如果余额加失败了,就把积分回滚回去。虽然不是数据库级别的事务,但至少能避免大部分问题。

坑5:夜间模式兼容

子比主题有夜间模式,最开始兑换模块的背景色在夜间模式下特别刺眼。后来加了 .theme-dark 的样式覆盖,夜间模式下自动换成深色背景,就和谐多了。

技术结构

插件用的单例模式,分了几个类:

  • Fantuan_Zibll_Points — 主类,负责加载依赖和初始化
  • Fantuan_Zibll_Points_Core — 积分核心,封装子比的积分API
  • Fantuan_Zibll_Browsing_Points — 浏览积分功能
  • Fantuan_Zibll_Points_Exchange — 积分兑换功能
  • Fantuan_Zibll_Points_Frontend — 前端显示
  • Fantuan_Zibll_Points_Admin — 后台管理
    所有和子比主题交互的地方都封装在 Core 类里,以后主题API变了改一个地方就行。

总结

这个插件本身功能不复杂,但因为是深度集成到第三方主题里,很多地方得跟着主题的节奏走。最大的体会是: 做主题增强插件,一定要多留 fallback,不能假设主题的某个函数一定存在、某个钩子一定能挂得上 。