- 权益兑换中心ID points_shop版本 2.3.1插件制作者 cxk123售价 30 积分3749 行 / 244.1 KBHook / 路由 / 后台页用积分兑换社区权益:徽章、头衔、昵称配色、限时用户组和卡密道具。收费插件安装说明购买后点击“授权码”获取一次性授权码;在后台 > 插件广场点击在线安装或更新,并填写该授权码。已购买用户更新免费:每次在线安装或更新前,重新点击“授权码”获取新的一次性授权码即可,无需再次购买;也可直接下载新版后用后台“插件上传”更新。尊重并感谢他人劳动,请勿传播代码。app/plugins/points_shop/plugin.php插件预览6 张开发日志已有 7 条
此项目接受所有登录用户协助维护和提交更新。
主楼点赞与点评1idc投币1个19小时前
版本 1.0.0 更新:
1.0.0 首次发布:权益兑换中心用积分兑换社区权益,并把「库存 / 限购 / 发货 / 退款 / 对账 / 时效」这条闭环一次做齐。
· 五类权益:徽章、头衔、昵称配色、限时用户组、卡密道具。每件可设价格、续期价、库存、
每人限购、每日限购、上下架时间窗、有效期与可用次数、自动或人工发货。
· 库存守卫:并发抢购不会超卖,售罄自动标记并可自动下架;余额不足会撤销订单并回滚库存。
· 下单闭环:下单即锁价,自动发货的 SKU 立刻进背包,人工发货的进后台队列;超时未发货自动退款,
超时未支付自动撤单;买家确认收货即完成,管理团队可一键退款。
· 逐笔账本:每一次加减分都留一笔账(扣款 / 退款 / 核销),后台把「账目口径、订单口径、
核心实际扣款」三方摆在一起对账;账号已注销的退款记核销,不谎称已退回。
· 我的背包:穿戴 / 卸下徽章、头衔与昵称配色,查看剩余次数;到期自动回收,到期前会提醒,可花钱续期顺延。
· 用户组时效:只允许兑换不带管理权限的用户组(后台白名单 + 服务端二次校验,积分买不到管理权限);
到期自动还原成原来的用户组,期间被别人改过组则只标记冲突、绝不覆盖。
· 全站零打扰:没开商品时前台一次库都不查,其它页面零额外查询;插件不写任何其它插件的表。插件质量报告
插件:
points_shopHook 功能范围 循环 使用频率 文件读写 修改系统表 读写系统表 读写自己的表 sidebar.feature_links 首页/侧栏快捷功能 否 5 - - - 写 plugin_points_shop_items, plugin_points_shop_orders, plugin_points_shop_ledger, plugin_points_shop_bag, plugin_points_shop_codes, plugin_points_shop_log, plugin_points_shop_watch, plugin_points_shop_notes 颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。
数据字典
plugin_points_shop_items字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_orders字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_ledger字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_bag字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_codes字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_log字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_watch字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_points_shop_notes字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 版本 1.0.1 更新:
1.0.1:续期守卫(修一个真机上抓到的缺陷)· 「续期顺延」新增一道服务端守卫:只有本人的、同一件商品的、还在生效(或已到期未回收)的、非卡密的
背包行才能续期。此前如果请求里带的 bag_id 不是自己的、或与商品对不上,订单会照建、积分会照扣,
却什么也没顺延 —— 现在这类请求直接拦下,不建单、不扣分,并给出「这件权益不能续期」的点名提示。
· 顺带把守卫写进建单语句的 WHERE 里(0 条额外语句),保持「下单 POST 5 条资金语句」的预算不变。版本 2.0.0 更新:
2.0.0(第一次大更新)· 六项新能力 + 十一处缺陷修复(含 5 处资金 / 权限级)【新能力一 · 补货到货提醒(候补名单)】
· 售罄商品可以「到货提醒我」:订阅 / 取消都是标准 POST + CSRF 写路径,重复点有天然幂等闸(UNIQUE(user_id,item_id))。
· 补货 / 重置已兑后,回收计划任务把「还在等货 + 商品已经重新有货」的候补一次通知掉,每轮 50 条、靠 CAS 抢占不会重复打扰。
· 后台新增「立刻派发到货提醒」,并在面板里显示候补人数(每件商品与整站各一处)。
· 「库存 / 补货 / 售罄 / 到货 / 提醒我 / 预约」在售 414 条条目里此前 0 命中 —— 售罄一直是死路,现在有了回流入口。【新能力二 · 限时促销价】
· 每件商品可设促销价与促销窗口;窗口内下单,订单的价格快照就是促销价,账本与三账互校完全按快照走。
· 活动结束、改价、删促销价都不会动到任何历史订单与历史账本;活动没开始时按钮按原价成交并提示开抢时间。
· 货架与详情页显示划线原价 +「限时立省 N 分」;「促销 / 秒杀 / 特价 / 折扣」在售条目里此前 0 命中。【新能力三 · 售后兜底:支付中断的僵尸单自愈】
· 请求在「建单」之后、「状态推进」之前被硬中断时会留下卡死的单。现在由超时任务接管:先用账本判定钱有没有动过。
· 动过钱 → 原子退款(账号已注销则如实记「核销」,不谎称已退款)+ 回滚占用的库存 + 通知本人;没动过钱 → 只撤单,绝不盲减库存。
· 每一单都有留档;三账互校在这种异常单之后依然守恒。【新能力四 · 我的积分流水(用户侧账本)】
· 新增只读页「我的积分流水」:逐笔扣款 / 退款 / 核销 + 余额 + 原因 + 关联商品与订单号,可按类型筛选、可翻页,顶部给净支出 / 已退款 / 核销合计。
· 与后台的三账互校同一套口径(declared 是「本该动多少」、amount 是「实际动了多少」),且固定只发 3 条查询。【新能力五 · 补货即重新上架】
· 售罄被自动下架的商品,点一次「补货 / 重置已兑」就回到在售 —— 以前补了货前台仍然看不到。【新能力六 · 过期权益也能续期(并真的重新授予)】
· 背包里「已到期」的行也可以续期;续期成功顺手重置到期提醒;用户组类续期会带守卫地重新授予
(只在当前组仍是原组 / 授予组且没有冲突标记时才改,绝不顶掉管理团队的手工改组)。【本轮修的缺陷(1.0.1 → 2.0.0)】
· 退款「先给钱、后改单」:真并发两个退款会双倍入账,账本多记一笔、三账互校从此漂移 → 改成先 CAS 抢占订单,抢到才动钱。
· 发货「先入库、后改单」:真并发两次点击会重复发货(一单发两件 / 消耗两个卡密)→ 改成先抢占后入库。
· 续期单落成「已付待发货」:进人工队列后会被超时任务当未发货自动退款(用户既顺延又拿回积分)→ 续期单直接落「已发货」。
· 人工发货的「用户组时效」原组快照取在授予之后 → 到期永不还原,用户永久保留付费组 → 改为建单时快照进订单行。
· 人工发货的卡密类永远拿不到码(强制发货参数被绑定类型坑成死参数)→ 修好发货语句,并在没有卡密时拒绝发货而不是发空凭证。
· 卡密空凭证「取出」会白扣一次次数 → 改成先确认有码再扣。
· 详情页「每人限购」只数最近 3 单 → 买满后按钮还在 → 改为标量子查询统计累计有效单。
· 续期不重置到期提醒 → 续期后再也不提醒。
· 补货不恢复上架状态 / 支付中断的单不回滚库存也不退款 / 过期组权益续期不重新授予 —— 见上面新能力三 · 五 · 六。【升级与兼容】
· 覆盖升级自愈:直接替换插件文件即可,第一个请求会自动补列 / 补表 / 补索引(新增 items 促销三列与 orders 的原组快照列、
第 7 张表候补名单与 2 枚索引),不会因为老库缺列被核心停用;已落标记时这一步是纯内存判断、0 条查询。
· 铁律不变:只改 app_users 的本人积分与白名单内用户组的 group_id;app_groups 只读;扣款是带守卫的 CAS;
积分买不到管理权限。查询预算不变(首页 6 / 详情 4·5 / 订单 4 / 背包 3 / 后台 7)。【验收】
· tools/lab_verify_points_shop.sh:412 项断言 / 0 失败(21 节,含真并发退款 / 发货、覆盖升级自愈、僵尸单兜底)。
· 六道静态门禁 + 出口口径审计全绿;预览图 6 张真实截图(新能力图在前)。版本 2.1.0 更新:
2.1.0 · 修掉两条自报残留风险 + 三项新能力(订单留言 / 库存卡密预警 / 后台订单检索)【修掉的两条残留风险(第三十四轮报告里自己报出来的)】
· R1 限购的真并发守卫:以前「每人限购 / 每日限购」是在下单语句里数一遍已有订单数 —— 真并发下两个请求会同时
数到同一个旧值然后双双通过(库存有 CAS 守卫、限购没有)。现在订单多了三个占位列(user_slot / limit_day /
day_slot,可空 = 这一单不占额度),下单时在同一条语句里算出槽位号,并由两个唯一索引兜底:
并发请求会算出同一个号,其中一个必然被数据库拒绝。槽位取 MAX+1(不是 COUNT+1,避免退款释放槽位后误拒);
退款 / 撤单 / 僵尸单兜底都会把槽位置回 NULL,与「限购从订单表推导」的旧口径完全一致。
另外把「撞唯一约束」的三种可能分清楚了:同键重复提交 / 限购并发拦下 / 其它真异常(真异常照旧抛给核心,
不再被吞成一句「已达限购上限」)。
· R2 卡密人工发货的极窄窗口:拣码与建背包行本来就是同一条语句(自带「卡密类必须有码」的数据库级守卫),
现在把「抢不到码」的收尾补齐 —— 订单退回「已付待发货」(钱仍在订单口径里、三账互校不受影响)、留 ship_blocked 档;
连回退也落空时读一次真实状态、留 ship_lost_race 档并如实回报。已扣款的单永远不会悬空,也绝不发空凭证。【新能力一 · 订单留言(双向客服留言)】
· 一单一串、append-only:用户提要求(人工发货的 SKU 大多是定制类权益),管理团队在同一单里回复,
回复会给用户发站内通知。归属校验与插入是同一条语句,不是本人的订单插不进去。
· 市场扫描:「售后」0 命中;「备注 / 留言 / 客服 / 工单」命中的都是别的语义(备注用户 / 通用工单 / 举报工单)。【新能力二 · 库存与卡密预警】
· 阈值可配(默认 3,0 = 关闭):在售 SKU 的剩余件数或未发卡密数低于阈值时,回收计划任务给管理团队发站内通知;
同一个 SKU 在补货前只提醒一次(alert_at 的 CAS),补货 / 改库存会重新武装。
· 后台面板给出预警件数统计与每行的「待预警 / 已预警」角标,全部从已取回的行里算,零额外查询。
· 市场扫描:「预警 / 低库存 / 库存不足 / 缺货 / 补货提醒」全部 0 命中。【新能力三 · 后台订单检索】
· 按订单号(精确)/ 用户名 / 商品名(% 与 _ 按字面匹配,不当通配符)+ 状态筛选,结果里直接发货 / 退款 / 回复留言。
· 默认视图仍是 8 条语句,只有带检索条件时才 +1(查询预算是硬约束)。
· 市场扫描:「订单搜索 / 订单筛选」0 命中。本轮没有做批量发货:队列里的单都是要人工判断的定制类。
也没有做「导出 CSV」与「SKU 分类」—— 扫描显示「导出」8 命中 /「CSV」3 命中 /「分类」13 命中 /「排序」13 命中,
都不是缺口,凑数没有意义。【升级与兼容】
· 覆盖升级自愈:直接替换插件文件即可 —— 第一个请求会自动补 4 个列(items.alert_at、orders 的三个占位列)、
1 张表(订单留言)与 3 枚索引(含两个唯一索引),不需要停用再启用;标记已落库时这一步是纯内存判断、0 条查询。
· 铁律不变:只改 app_users 的本人积分与白名单内用户组的 group_id;app_groups 只读;扣款是带守卫的 CAS;
积分买不到管理权限;页面路径循环体内零查库;三驱动可移植 SQL。【验收】
· tools/lab_verify_points_shop.sh:508 项断言 / 0 失败(26 节)。新增 §9H 限购真并发(同秒 5 发只成 2 单、
直插同槽位被唯一索引拒绝)、§9I 四单抢一码的真并发风暴(空码背包行 0 条、无悬空单、三账守恒)、
§9J 订单留言、§9K 库存卡密预警、§9L 后台订单检索。
· 六道静态门禁 + 出口口径审计全绿;预览图 6 张真实截图(新能力图在前)。版本 2.2.0 更新:
2.2.0 · 升级路径做成常规回归 + 并发再压一轮 + 两项能力(SKU 批量编辑 / 对账单导出)【升级路径成为常规回归(每次跑套件都会走一遍)】
· 以前只有一节「假老库」自愈测试;现在套件里有两条真的老库路径:把库真的改回 1.0.1 结构
(删掉 2.0.0 与 2.1.0 的全部新增列与表)与 2.0.0 结构(只删 2.1.0 的新增物),各覆盖一次 2.2.0 文件。
· 断言:一发请求 200、结构标记落到 2.2.0、该补的列 / 表 / 索引一枚不少、插件保持 enabled,
并且订单与账本的事实列逐行 md5 一字未动、行数不变(金额 / 状态 / 时间线 / 备注一个字节都不许动);
升级后订单留言功能立刻可用。【并发再压一轮】
· 候补到货提醒的扇出去重:4 人订阅售罄 SKU → 真后台补货 → 4 发计划任务同时打 →
每个候补只被标记一次、每人恰好 1 条通知、没有残留未通知行;再跑一轮仍然不重复。
· 限时促销价到期瞬间的价格快照一致性:一件商品「原价 100 / 限时 40 / 3 秒后到期」,
窗口内同秒 3 发 + 窗口外同秒 3 发 → 每一单都满足快照价 = 该单下单时间落在的窗口判定
(窗口内必 40、窗口外必 100,到期那一秒算窗口外),账本 pay 金额 = −快照价、declared = amount,
到期竞态之后三账互校仍然守恒。【预算复核:与 2.1.0 冻结产物做 A/B】
· 把 2.1.0 的冻结产物换回站点、用同一套夹具逐页量一遍再换回 2.2.0:首页 6 / 详情 4·5 / 订单 5 /
背包 3 / 流水 3 / 后台 8 —— 2.2.0 一行查询都没多(对比后立刻恢复,另有收尾阶段的无条件恢复保险带)。【新能力一 · SKU 批量编辑】
· 勾选任意几件 SKU(或「全选本页」),一次性设置开售 / 结束时间戳、每人 / 每日限购、促销三列、排序、上下架;
留空的字段不会被改动。实现是一条UPDATE … WHERE id IN (…):没有循环、没有 N+1,
也没有「绑定参数与常数比较」那类假守卫;改过之后库存预警重新武装(下次低库存会再提醒一次)。
· 市场扫描:「批量编辑 / 批量设置 / 批量修改 / 时间窗 / 上架时间 / 定时上架 / 定时下架 / 开售时间 / 活动时间」
全部 0 命中(「批量」14 命中都是别的语义:批量导入、批量清理)。【新能力二 · 对账单导出(CSV)】
· 后台可导出订单对账单(价格快照 + 状态 + 下单 / 付款 / 发货 / 完成 / 退款 / 到期时间 + 备注)
与逐笔账本(本该动 declared / 实际动 amount / 变动后余额 / 原因);用户可导出只有自己的积分流水。
· 每个分支只有一条带上限的查询,合计行写明导出了多少条(一眼看出有没有触到 2000 行上限)。
· CSV 字段做公式注入中和(以 = + - @ Tab CR 开头的取值加单引号前缀):导出内容里有用户名与备注,
都是别人可控的文本,Excel / Sheets 打开会当公式执行。
· 顺手修掉一个自己新写出来的真缺陷:BOM 没算进 Content-Length → 下载回来的 CSV 最后一行缺换行
(Excel 照常打开、极难发现,是新加的「按行数断言」抓到的)。
· 市场扫描:「导出订单 / 订单导出 / 对账单 / 财务报表」全部 0 命中(上一轮只看泛词「导出」有 8 命中就放弃了,
这一轮细分到订单 / 对账单才确认是空白 —— 关键词要扫得够细)。【兼容与铁律】
· 2.2.0 没有新增任何表 / 列 / 索引;结构版本号仍抬到 2.2.0,让升级回归能断言「迁移跑过一次且是幂等空转」。
· 铁律不变:只改 app_users 的本人积分与白名单内用户组的 group_id;app_groups 只读;扣款是带守卫的 CAS;
积分买不到管理权限;页面路径循环体内零查库;三驱动可移植 SQL;批量编辑与导出都不增加任何页面的查询预算。【验收】
· tools/lab_verify_points_shop.sh:592 项断言 / 0 失败(32 节)。六道静态门禁 + 出口口径审计全绿(0 条待人工确认)。
· 预览图 6 张真实截图(新能力图在前:SKU 批量编辑 / 对账单导出)。版本 2.3.0 更新:
2.3.0 · 资金正确性- 修复(根因级):下单流程的「占库 → 扣款 → 账本 → 发货 → 状态推进」原来是一串独立语句,中断在扣款与账本之间会留下「积分已扣、账本无凭据、订单未推进」的僵尸单,而兜底任务以「账本有没有 pay 行」判「钱动没动」,判为没动 ⇒ 不退款 ⇒ 积分永久消失且三账互校查不出。
现在整段包进一个事务:要么整单落地、要么整单不落地;失败路径用哨兵异常触发回滚(而不是"正常返回"——那样会把占库提交掉,永久吃掉一件库存)。
- 由此「有 pay 行 ⇔ 扣款发生过 ⇔ 订单推进过」成为真不变量,僵尸单兜底不再需要猜。
- 续期路径同批收紧(顺延语句的 rowCount 计入判定)。
升级:覆盖文件即可,无数据结构变更(结构戳随版本推进,首请求幂等重算一次)。
版本 2.3.1 更新:
2.3.1 · 扣款动作补齐标准积分事件(跨插件契约修复)- 修复(跨插件契约):购买扣款与退款这两条路径原本为了与同事务内的其它写入同生共死,走了自带的原子 CAS,没有经过核心的积分变动助手 —— 于是它们不广播标准的
user.points_changed事件。后果不是本插件出错,而是下游消费者的账算不平:achievements的「积分净增」只累加事件载荷里的delta,于是「花掉的积分」这一侧被系统性漏计 ⇒ 净增高估 ⇒points_100 / points_1000 / points_5000三个成就会提前授予。 - 现在:在事务提交成功之后补发与核心同形的事件(
event_key/user_id/delta/reason/points),三驱动同口径; - 事件键取确定值(购买用
request_key、退款用order_id),因此同一笔动作重复走到补发路径也只计一次(随机键在重试语义下会重复计); - 回滚不算账:余额不足、库存被抢空、重复提交/重复退款被拒这些路径一条事件都不发;
points(动作后余额)取自账本行里同事务写下的balance_after,与amount是同一条不可变记录 —— 不会出现"读了价、又读了另一个时刻的余额"的错配。- 预算口径:下单 POST 的语句预算由 5 / 6 / 7 条变为 6 / 7 / 8 条(成功下单时多一条"取本次动作金额与余额"的读取;失败与重放路径不增加)。这条改动使页面/下单的查询数上限与实现重新一致(套件里的三条精确断言同步更新,未放宽)。
- 对本站:不开新表、不加新列、不改任何页面的可见行为;只是把"钱动过"这件事如实广播给生态。
- 修复(跨插件契约):购买扣款与退款这两条路径原本为了与同事务内的其它写入同生共死,走了自带的原子 CAS,没有经过核心的积分变动助手 —— 于是它们不广播标准的