封闭邀请制上线完整清单
把一个带付费内容的小站做成封闭邀请制上线,和公开发布的检查点很不一样。这篇是完整的执行清单,每一项都对应一个真实的失败场景。
邀请码这一关
注册接口在没有邀请码时必须返回明确的错误码(这里是 403 INVITE_REQUIRED),并且不创建任何用户记录。模糊的错误提示会让排查变得很痛苦,前端也拿不到可处理的分支。
邀请码的状态机要覆盖所有拒绝路径。过期、作废、重复使用、无效格式,一条都不能漏。最容易出问题的地方在并发,两个请求同时拿同一个码注册,数据库层面必须只有一个赢。靠应用层先查后写挡不住,唯一索引加原子状态转换才行。
还有一类失败是注册流程中途崩溃。邀请码在”认领”和”完成注册”之间有一个中间态,用户关页面或者服务重启,这个码不能永久卡死。方案是给认领设过期时间,过期的认领自动回收,码重新可用。
内容保护
付费文章的正文和附件有两个要求。不能出现在静态构建产物里,也不能靠猜 URL 拿到。
构建产物检查很简单,发布前扫一遍 dist/client,确认付费正文不在里面。猜 URL 的防护靠受保护的资源接口,每个请求都过一遍用户权限,匿名用户只拿到试看部分,买过的用户和管理员拿全文和附件。
试看比例在 frontmatter 里配置,默认取站点配置的值。注意正文里的资源链接要在渲染时改写成受保护接口地址,原始静态路径一条都不能留在付费正文的 HTML 里。
账务
第一版用兑换码换额度,没有在线支付,但账务的严肃程度不能降。
兑换、扣减、购买必须在一个事务里完成,重复购买要幂等(返回已有记录而不是再扣一次钱)。购买时把当时的价格记进购买记录,之后文章调价不影响已购用户,这个快照字段在恢复数据时也要一起验证。
每个用户的余额要有流水支撑,余额字段和最新一条流水的 balance_after 必须时刻一致。这条约束写进恢复检查脚本里,备份恢复后自动验证。
运维底线
上线前要跑通至少一次完整演练。备份恢复到临时库,恢复出来的用户能登录,购买权限还在。重启服务器之后,session、邀请码、额度、购买记录都还在原地。
健康检查接口要真的连数据库,只检查进程活着没有意义。请求日志从第一天就开,出问题时没有日志的站点只能靠猜。
限流在单实例上用内存计数器就够,挡住暴力试邀请码和撞库。等真到了多实例那天再换分布式方案,那是另一个计划的事。