[qa_bounty]悬赏问答热
- 悬赏问答ID qa_bounty版本 3.4.1插件制作者 cxk123售价 30 积分3909 行 / 248.5 KBHook / 路由 / 后台页3.4.0:**修掉资金路径的三处缺陷**(采纳发放的赏金、入账与账目现在同生共死,中断不再出现「冻结池已扣、答主没拿到、账目没有」的半成品;并发采纳时末位名额取走池中剩余的写回改为带条件写入 + 事务内不变量复核,冻结池不会再被并发击穿、积分不会凭空增发;到期结算的状态推进带上总额校验,与并发加码交错时整条悬赏顺延到下一轮按新总额重算,已结算题上的滞留赏金也纳入对账并按出资比例退回)· 并补上**答主信誉榜的匿名遮蔽**(被遮蔽的回答者此前仍会在页面里留下可反推真实账号的标记,现在彻底不输出)。收费插件安装说明购买后点击“授权码”获取一次性授权码;在后台 > 插件广场点击在线安装或更新,并填写该授权码。已购买用户更新免费:每次在线安装或更新前,重新点击“授权码”获取新的一次性授权码即可,无需再次购买;也可直接下载新版后用后台“插件上传”更新。尊重并感谢他人劳动,请勿传播代码。app/plugins/qa_bounty/plugin.php插件预览5 张开发日志最新 10 条
此项目接受所有登录用户协助维护和提交更新。
主楼 版本 1.0.0 更新:
悬赏问答 1.0.0 —— 把站内积分变成「悬赏」,让「问得值不值」有答案【它解决什么问题】
论坛里最贵的资源不是帖子,是认真回答一个问题的人。但现在问问题只能靠「有人愿意顺手帮一下」:
问的人没有任何东西可以承诺,答的人也没有任何回报预期,于是「谁懂这个」这类问题要么没人回,
要么被埋在水帖里。悬赏问答补的就是这条链路:**提问时冻结赏金 → 答案被采纳 → 赏金自动转给答主 →
到期没人被你采纳,积分原路退回**。全程每一笔都有账目,随时可查。【新增功能】
· 挂悬赏提问:发帖页勾选「悬赏」并填写赏金(1 - 10000)、截止天数(1 - 90)、最佳答案数(1 - 3),
发布时从提问者积分里冻结赏金;余额不足当场拒绝,不会出现「主题发出去了、悬赏却没挂上」的半截结果。
另有独立的发起页?a=qa_ask,不经过发帖页也能直接挂一条悬赏。
· 采纳与撤销:提问者(或管理团队)在悬赏详情页一键采纳,赏金立即转给答主并通知 TA;
最佳答案数 > 1 时按「平分、余数归先采纳者」分配。截止前可以撤销采纳(钱收回冻结池,不会重复退款)。
· 到期自动结算:过了截止时间仍未采纳的部分,由计划任务原路退回提问者并写退回账目、发通知。
一分未发 → 状态「已退回」;部分采纳 → 状态「已关闭」(已发出的不动)。
· 悬赏榜与我的悬赏:?a=qa_list未解决榜按「赏金 × 等待时长」排序(等得越久、赏金越高越靠前),
另有「已解决 / 我发起的 / 我回答的」三个页签。
· 主题页联动:挂悬赏的主题在帖子列表里带「悬赏中 · 已解决」角标,主题页顶部显示赏金与剩余时间徽标,
最佳答案会被顶到当前页最前并标上「最佳答案」。
· 逐笔账目:冻结(负)/退回(正)/发放(正,撤销时记一笔负),后台面板显示待结算条数与冻结总量,
两条恒等式随时可对账——「负数合计 = 仍冻结在这条悬赏里的积分」「结算完成后账目合计归零」。【跨插件联动(都只经由私有 Hook,对方没装时零影响)】
· 关注与动态:采纳后把「TA 的回答被采纳、赏金到账」作为一条动态投递给「关注与动态」,
由对方自行通知答主的关注者——本插件只投递事件,不直接写对方的任何表。
· 匿名区:悬赏榜与详情页的行在渲染前整页交给匿名区插件批量遮蔽,匿名主题 + 悬赏的组合下,
列表、详情、甚至连「关注者通知」都不会露出真身(匿名回答被采纳时通知由系统发出)。
· 全站零打扰:站点上一条悬赏都没有时,列表页角标零额外查询;有悬赏时也只是一次整页批查,
与行数无关(绝不逐行查库)。【性能与安全口径】
· 页面请求路径上没有任何循环体内的数据库调用;列表页 / 详情页的查询数固定,与条目数无关。
· 全部写操作 POST + CSRF 令牌;输出统一转义;搜索类查询一律带 ESCAPE 的 LIKE 参数绑定。
· 冻结是带守卫的 CAS 扣款(UPDATE ... WHERE points>=?),并发下也不会把余额扣成负数;
发放 / 退回 / 收回一律走核心user_points_change()。
· 卸载会清掉自己的 3 张表、索引、计划任务与通知,不留孤儿数据。【安装与升级】
· 单文件插件,装入后启用即用(后台 → 插件 → 本地插件)。自动建 3 张表与 6 枚索引,重复启用幂等。
· 后台可调:站点开关、赏金上下限、默认截止天数、单题最佳答案数上限、列表每页条数。
· 计划任务 settle 每 5 分钟结算一次到期悬赏,也可在后台手动触发。【兼容】PHP 8.1+ / SQLite / MySQL / PostgreSQL(时间戳全部使用 unix 秒,不使用窗口函数)。
插件质量报告
插件:
qa_bountyHook 功能范围 循环 使用频率 文件读写 修改系统表 读写系统表 读写自己的表 sidebar.feature_links 首页/侧栏快捷功能 否 5 - - - - topic.index_data.loaded 首页/版块/用户主题列表查询后 否 5 - - - 写 plugin_qa_bounty_questions, plugin_qa_bounty_answers, plugin_qa_bounty_ledger, plugin_qa_bounty_audit, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events topic.title_suffix 首页/版块主题列表逐条标题 是 5 - - - - topic.replies 主题查看页回帖集合 否 4 - - - - reply.after_save 回帖保存后 否 3 - - - 写 plugin_qa_bounty_questions, plugin_qa_bounty_answers, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events, plugin_qa_bounty_ledger, plugin_qa_bounty_audit topic.actions 主题查看页操作区(展示位置:主题操作) 否 3 - - - 写 plugin_qa_bounty_questions, plugin_qa_bounty_answers, plugin_qa_bounty_ledger, plugin_qa_bounty_audit, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events topic.after_save 主题保存后 否 3 - app_users app_users, app_topics 写 plugin_qa_bounty_questions, plugin_qa_bounty_ledger, plugin_qa_bounty_answers, plugin_qa_bounty_audit, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events topic.before_save 主题发布/编辑表单提交 否 3 - - - 写 plugin_qa_bounty_questions, plugin_qa_bounty_answers, plugin_qa_bounty_ledger, plugin_qa_bounty_audit, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events topic.form_extra 主题发布/编辑表单 否 2 - - app_topics 写 plugin_qa_bounty_questions, plugin_qa_bounty_answers, plugin_qa_bounty_ledger, plugin_qa_bounty_audit, plugin_qa_bounty_watchers, plugin_qa_bounty_watch_events diy_layout.components 非系统hook 否 1 - - - - reply.content_after 非系统hook 否 1 - - - - 颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。
数据字典
plugin_qa_bounty_questions字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_qa_bounty_answers字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_qa_bounty_ledger字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_qa_bounty_audit字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_qa_bounty_watchers字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_qa_bounty_watch_events字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 版本 2.0.0 更新:
qa_bounty 2.0.0 —— 先修 BUG,再补能力
本次是大版本更新:先修掉 6 处真实缺陷 + 1 处窗口竞态(每一处都在实验室复现过,套件里留了回归断言),再补 5 项新能力。
修掉的缺陷(现象 → 根因 → 修法)
- 后台「当前冻结总量」与台账互相矛盾:100 分 / 3 个名额的悬赏采纳 1 条后面板显示冻结 100,账目口径只有 66 —— 冻结总量按
SUM(status=0 的 reward)求和,部分采纳时把已发放的赏金也算成冻结。改为由逐笔账目推出,并新增一行「对账」把两个口径摆在一起。 - 撤销采纳后重新采纳会把余数永久留在冻结池:100 分 / 3 个名额绕一圈只发出 99,题目已是「已解决」而账目合计是 −1。末位名额改为取走池子里剩下的全部(正常路径分毫不变)。
- 同一条答案被并发采纳两次会重复发放(实测 100 分悬赏给同一条答案发了 68):答案行的 CAS 结果没人看。改为以答案行 CAS 为闸门,抢不到就回滚名额并报错;分配序号改用 CAS 之后的新值。
- 热帖(200+ 答案)打开悬赏详情页会撞
UNIQUE(question_id,reply_id),核心记 PDOException 并停用整个插件(页面全 404):补登记的「已登记集合」只从详情页LIMIT 200的展示窗口里取。改为单独查全量已登记回帖;并发撞唯一约束按幂等处理。 - 答主账号被删后赏金凭空消失(账目写着发放,全站积分一分没动):核心
user_points_change()对不存在的用户是「静默成功」。发放 / 收回前先确认账号存在,不存在就明确拒绝,钱留在冻结池。 - 提问者账号被删后到期结算写假退款:改为
void挂账核销(账目合计照样归零,但不谎称「已退回」),后台单独显示核销合计。 - 截止时刻与 settle 计划任务交错会超发(发 34 + 退 100 = 134,而悬赏只冻结 100):采纳 / 撤销的名额 CAS 补上截止时间守卫。
新能力
- 赏金加码:提问者与任何人都能给还开放的悬赏追加赏金,走同一条带守卫的 CAS 冻结扣款并落 freeze 账目;到期退款按各出资人的出资比例分摊(零头按出资先后补 1 分),分毫不差。
- 到期提醒 + 自动催办:新增
remind计划任务(每小时),两档(≤24 小时「最后催办」、≤remind_days天「到期提醒」),提醒提问者与尚未被采纳的答主;remind_stage的 CAS 保证每档每人最多一条,后台可关。 - 管理团队仲裁:暂缓结算(cron 跳过)/ 判定发放(不受截止时间限制,提问者失联也能裁决归属)/ 判定退回(按出资比例退还),全程写审计表(谁、何时、什么理由、动了哪笔钱),并通知双方。
- 答主信誉榜:
?a=qa_list&tab=answerers—— 被采纳数 / 采纳率 / 累计赏金,一条聚合查询算出,查询数与行数无关;匿名主题里的答主只显示「匿名答主」且不给主页链接。 - 悬赏统计面板:待结算笔数、暂缓笔数、两个口径的冻结余额、累计加码、平均采纳时长、答主数、仲裁笔数、四类账目合计,并直接给出对账结论。
升级与兼容
- 老库自动升级(幂等补 4 列 + 新建审计表),卸载一并清理;原有 3 张表、资金口径、状态机、Hook、页面查询预算口径不变。
- 六道静态门禁全绿;
tools/lab_verify_qa.sh扩到 366 项断言 / 0 失败。
- 后台「当前冻结总量」与台账互相矛盾:100 分 / 3 个名额的悬赏采纳 1 条后面板显示冻结 100,账目口径只有 66 —— 冻结总量按
版本 2.0.1 更新:
2.0.1 修复:后台设置保存失败时看不到真正的错误原因- 校验失败时直接 fall through 成整页 HTML,而核心前端对这张表单走 fetch +
JSON.parse→ 用户只看到「操作失败」,真正的提示被吞掉。 - 修法:AJAX 请求回
{"ok":0,"message":…}信封;无 JS 浏览器仍是原来的整页渲染(字节不变)。 - 首次补上 3 张预览图:悬赏榜(
?a=qa_list)· 悬赏详情面板 + 赏金加码 · 管理团队仲裁(暂缓结算 / 判定退回 / 判定发放 + 仲裁审计)。
- 校验失败时直接 fall through 成整页 HTML,而核心前端对这张表单走 fetch +
版本 2.0.2 更新:
补丁版本:给「页面组件 DIY」加一个原生组件(未装 layout_diy 时行为逐字节不变)
- 新增只读组件注册(私有 Hook
diy_layout.components):具名渲染回调 + 声明查询成本 + 无数据时零查询降级(靠站点标记短路)+ 拿不到数据返回空串由宿主降级成占位(不抛异常,单块坏天气不会带走整页)。 - 查询形状与行数无关:列表/映射/计数各一条批查,条数参数只改
LIMIT的绑定值。 - 组件自带参数白名单(条数 / 显示哪些字段),后台可视化编辑、越界值拒绝保存。
- 未装「页面组件 DIY」时这个 Hook 无人调用:本插件的行为、查询数、页面字节与上一版逐字节一致(用「旧版 vs 新版、两边都停用 layout_diy」的 A/B 比对守着,含查询数)。
- 新增只读组件注册(私有 Hook
这个看着不错。
版本 3.0.0 更新:
3.0.0:全站列表页零查询 · 提问者控制台 · 悬赏订阅(+ 5 处缺陷修复)
新增能力
- 列表页 / 首页角标零查询:悬赏行 → 主题 id 的映射改存
app_settings只读快照,核心在请求开头就把整张设置表读进内存 → 首页 / 版块 / 搜索 / 用户主题等所有列表页渲染「悬赏中 · 已解决」角标一次库都不用查(2.0.x 是每个列表页恒定 1 条IN批查)。快照由全部写路径就地维护、启用时重建、详情页发现漂移时自愈;悬赏超过 200 条自动回退整页批查(正确性优先)。 - 提问者控制台
?a=qa_close:提前结贴(已采纳的赏金不动,未发出的按出资比例退回给出资人,复用到期结算那条资金路径:同一套按比例退款、账号已注销走核销、同一批通知,状态转「已关闭 / 已退回」,写审计)与延期(1–30 天,总期限 ≤90 天,顺带复位催办阶段让新期限重新提醒)。只处理未结算的悬赏;管理团队「暂缓结算」期间提问者不能单方面结贴。 - 悬赏订阅 + 变更通知:详情页订阅按钮(可勾「有新回答也通知我」)+ 列表页「我订阅的」页签;被采纳 / 加码 / 待结算变动时收到站内通知。页面路径只写一行事件(新表
plugin_qa_bounty_watch_events,UNIQUE(question_id,event_key)幂等),由新增计划任务watch(600 秒)扇出 —— 通知量与订阅人数不会落到页面请求上。一条消息只进一个人的收件箱一次:被采纳者不重复发、出资人已有退款通知就不重发、动作执行者与提问者不发。加码自动关注,退订留痕(之后加码不会把你加回来);单人订阅上限后台可调。 - 后台新增「答案分页条数」(20–200,默认 100)与「单人订阅上限」(1–200,默认 50)两项设置。
修复
- 列表页 / 首页每次渲染多 1 条 SQL → 只读快照后归零(实测:本插件启用与停用时首页查询数完全相同)。
- 「加码 ∥ 采纳」交错时末位赏金按旧总额结算,题目已「已解决」而冻结池里还压着钱、且没有任何路径再发出去 → 采纳路径在置「已解决」后再对一次账(末位名额取走池中剩余),
settle计划任务另加一层对账自愈(补发给最后一条被采纳的答案,账号已注销改挂账核销,全程写审计)。 - 答案列表硬上限 200 条且无分页 → 第 201 条起既看不到也无法被采纳(热帖赏金只能白挂到到期)→ 改为答案分页 + 「只看未采纳」筛选,总数取悬赏行上已有的列,没有额外 COUNT 查询。
- 回帖已被删除的「答案」仍可被采纳(钱发给一条不存在的内容)→ 采纳前校验回帖存在,不存在就明确拒绝并提示两条出路(改采纳别的答案 / 提前结贴退回)。
- 到期退款通知对加码人误称「你的悬赏」 → 按出资身份分派措辞。
兼容
- 老库自动升级:新增 2 张表 + 3 枚索引 + 1 条计划任务 + 1 个设置键,原 4 张表一列不动、历史账目与资金口径不变。
- 卸载(连数据)会清干净新表 / 新索引 / 两个设置键;页面路径仍是循环体内零查库、三驱动可移植 SQL、写操作一律 POST + 令牌。
- 列表页 / 首页角标零查询:悬赏行 → 主题 id 的映射改存
版本 3.1.0 更新:
3.1.0:提问者管理台(批量结贴 / 延期 + 诚信分)· 真并发下的三处缺陷修复
新增能力:提问者管理台
?a=qa_manage- 一页看完「我的开放悬赏」,带呆滞标记:还没有人回答 / 有回答但快到期 / 结贴排队中 / 暂缓结算中 / 到点待结算。
- 批量延期:勾选若干条一次提交(1–30 天,总期限按 90 天上限自动夹住),催办重新计时。
- 批量结贴:勾选提交即入队(页面显示「结贴排队中」),由结算任务按出资比例把未发出的赏金退回给出资人;
已采纳的部分不动;提交后还能「取消结贴」撤回。
- 提问者诚信分:采纳率(已解决 ÷ 已结算,开放中不计入)、到期未采纳数、平均采纳时长、待结算 / 结贴排队中 ——
全部由一条聚合查询算出。
- 工程口径:批量动作对页面路径是 O(1)(延期一条 UPDATE、结贴只打标记,资金动作交给已有的结算任务),
管理台查询数与悬赏条数无关(恒定 4 条)。
修复(真并发实测抓出来的)
- 「加码 ∥ 采纳」交错后整个插件被核心自动停用:加码路径为了让事务回滚抛了未捕获异常,异常冒到核心 →
核心按插件故障处置 → 之后所有悬赏页面 404(
disabled_reason记的就是RuntimeException: 这条悬赏已经结束,加码未生效)。
现在改成「内部哨兵 + 就地捕获」:回滚照旧,用户看到「这条悬赏刚刚被采纳或结算,加码没有生效(积分未扣)」,插件不受影响。- 同类隐患一并收口:创建悬赏的事务在并发撞唯一约束或写入失败时也抛异常,而它跑在
topic.after_save钩子里
(钩子抛异常同样会停用插件)→ 现在就地消化成错误对象;
topic.after_save/reply.after_save两个钩子再加一层兜底。- 详情页上管理团队的「仲裁发放」按钮永远渲染不出来(被提问者分支吃掉,只能拿到受截止时间限制的「采纳」)→
改成按身份分派:提问者拿采纳/撤销,管理团队拿仲裁发放(不受截止时间限制,全程写审计)。
硬证据(套件第 27–29 节)
- 真并发:4 workers 站点上每轮「采纳 ∥ 加码」两条真并发请求,逐轮断言账目守恒 + 段末全站对账、零残留、
插件未被停用、前台入口仍 200(这套断言就是上面缺陷 1 的发现者)。
- 深分页:320 条答案 / 16 页 —— 第 16 页照样带采纳按钮并采纳成功、管理团队在第 14 页上仲裁发放成功、查询数与页深无关。
- 预算复核:首页列表页对悬赏角标的贡献 0 条(启用态 = 停用态)、悬赏主题页净贡献恰好 2 条、管理台查询数不随条数增长。
版本 3.2.0 更新:
3.2.0 · 覆盖升级自愈 + 对账余数- 修复(覆盖升级会砖):本插件此前只会在安装时建表/补列/建索引,而核心在覆盖文件时不重跑安装 ⇒ 老站点覆盖升级后首个查库操作就抛异常(
PDOException)并被核心自动停用整个插件。现在补上运行期结构自愈:内存副本 → 设置标记 → 才做 DDL 的三道门,稳态零查询,15 个入口全部挂上(刻意不挂读钩子,以保持「与停用状态查询数完全一致」的既有预算口径)。 - 修复(对账假账):悬赏对账自愈的「末位名额核销」分支只写账、不消解余数条件 ⇒ 同一条悬赏每轮结算都再记一笔核销,账目逐轮膨胀。现在核销同时消解余数,
Σ已发 = 赏金恒等式恢复。 - 附带:卸载补删结构标记。
升级:覆盖文件即自愈。
- 修复(覆盖升级会砖):本插件此前只会在安装时建表/补列/建索引,而核心在覆盖文件时不重跑安装 ⇒ 老站点覆盖升级后首个查库操作就抛异常(
版本 3.3.0 更新:
3.3.0 · 榜单与详情的版块可见性- 修复(越权):悬赏榜、悬赏详情、答主信誉榜与「高分榜」组件此前没有版块可见性判定 ⇒ 「仅指定用户组可见」版块里的悬赏,其主题标题会出现在公开榜单、详情与检索结果里。现在五处取数统一按核心同一口径过滤(管理团队作为该版块的合法访问者照常可见;公开版块内容不受影响)。
- 口径:站内没有受限版块时,SQL 文本逐字节不变(既有性能与指纹断言不受影响);筛选/预算增量 0。
版本 3.4.0 更新:
3.4.0 · 资金路径三连(采纳发放 / 并发金额 / 到期结算)+ 答主榜匿名遮蔽补齐- 修复(资金原子性 · 采纳与仲裁):采纳答案的三步 —— ①写
awarded扣冻结池(含名额 CAS 与「已解决」置位)、②给答主打积分、③写award账目 —— 此前是三次独立提交。①之后 ②/③ 之前任何失败(进程中断、致命错误、积分入账抛异常)都会留下「池已扣、分未到、无账目」的半成品;题目此刻已置「已解决」,结算跳过、加码被挡,没有任何对账路径能发现它。现在三步包进同一事务,失败路径一律哨兵异常回滚(本仓库纪律:tx()只在抛异常时回滚,用return会把半成品 COMMIT 掉),哨兵就地捕获、不外泄给核心(冒到核心会导致整插件被自动停用)。管理团队仲裁发放、以及同一资金链的对称路径「撤销采纳」走同一口径。 - 修复(并发多发 · 末位名额):多名额悬赏里「末位名额取走池中全部」原先基于非原子读(
SUM(awarded))且金额写回没有任何校验 ⇒ 两个并发采纳可以各读到旧余额、合计发出超过赏金总额(真实增发、冻结池被击穿),而末尾兜底在超发时算出负数被max(0,…)吞掉、不会自愈。现在金额写回收口为带 CAS 的抢占(期望值 = 本步读到的本行旧值,写完回读复核;抢不到就不再写第二遍),并在同一事务内补一道Σ已发 ≤ 赏金的不变量复核:越界部分从自己这一行收回。于是并发交错下最坏是「少付」而不是「多付」—— 少付的余数由结算对账补上,冻结池与站点积分总量都不会被击穿。 - 修复(资金滞留 + 对账缺口):到期结算的状态推进只校验
status、退款又用读来的旧总额 ⇒「加码 ∥ 结算」交错时,后加的赏金按旧总额退款,差额永久滞留在冻结池(题目已成 2/3:加码被挡、结算不再处理它),而唯一的修复入口(对账自愈)当时只覆盖status=1,够不着 2/3。现在状态推进的 CAS 一并校验reward(打不中就让这条悬赏整轮顺延,下一轮按新总额重算 —— 加码与结算抢的是同一条status=0的语句,必有一方落空且干净回滚,加码人不会被扣钱);对账自愈的选择条件从status=1扩到status IN (1,2,3):status=1照旧「末位名额取走池中全部」,status 2/3按赏金 − Σ已发 − Σ(refund/void)判真滞留并按出资比例退回(只看赏金 − Σ已发会把已经退过的钱再退一次,那是二次退款)。 - 修复(隐私 · 匿名遮蔽只做了一半):答主信誉榜此前把匿名答主的显示名与主页链接遮掉了,但那一行的
data-qa-bounty-answerer属性里仍写着真实用户 ID —— 看一次页面源码就能把「匿名答主」与真实账号对上号,与插件自身「不在榜单上把匿名答主与真实账号对上号」的设计口径直接矛盾。现在匿名行整条不输出该属性(公开行照旧输出,列表与后台排查不受影响);套件的断言也一并扩成「链接与真实 ID 都不得出现」并加了公开行同行反证。 - 口径:三条资金修复只在失败 / 并发 / 交错路径上生效;正常单线程流程的金额、账目动作名(
award/refund/void)、失败文案、通知文案与页面查询预算逐字节不变(结算任务文案只在新计数器真非零时追加一句)。新增写回全部是列与参数的比较(不写表达式 < 参数),三驱动可移植、不含窗口函数,也没有在页面路径的循环体里查库 ——「重试」语义由事务与不变量复核承担,而不是靠循环重读。匿名修复只影响被遮蔽的那一行:公开行的 HTML 逐字节不变。 - 升级:覆盖文件即可(表结构未变:无新增列 / 表 / 索引,不需要重跑安装)。
- 修复(资金原子性 · 采纳与仲裁):采纳答案的三步 —— ①写
版本 3.4.1 更新:
3.4.1 · 悬赏冻结/加码补齐标准积分事件(跨插件契约修复)- 修复(跨插件契约):提问冻结(
qa_bounty_create())与加码冻结(qa_bounty_boost_route())用的是自带守卫的原子 CAS 扣款(points=points-? WHERE ... AND points>=?),没有经过核心的积分变动助手 ⇒ 不广播user.points_changed。而赏金发放与退回是走助手的(会广播),于是同一笔悬赏在生态视角里是「进账有、出账无」—— 提问者的「积分净增」被高估整整一个赏金。 - 现在:两处都在事务提交成功之后补发与核心同形的事件(
event_key/user_id/delta/reason/points): - 冻结用
points-change:qa-bounty-freeze:<问题 id>;加码用账目行 id(points-change:qa-bounty-bump:<ledger_id>)——
特意不用问题 id:同一道悬赏可以多次加码,用问题 id 会被下游按
event_key去重吞掉第二笔;- 扣款没成功(余额不足)与竞态回滚(加码撞采纳/结算)一条事件都不发,与"钱没动过"保持一致。
- 不发第二次:发放/退回/仲裁/对账这些已经走核心助手的路径一个字未改 —— 本次只补那两处冻结,避免同一笔钱被广播两次。
- 对本站:不开新表、不加新列、不改任何页面文案与可见行为。
- 修复(跨插件契约):提问冻结(