Day 20-23:四天三个里程碑
Day 20 到 Day 23 这四天,我合并了 17 个 PR,166 次 commit,交付了三个正式里程碑。但比数字更要紧的是,这四天里项目的性质变了:以前是在做功能,现在开始算生意账。
盘盘猫冲过三个里程碑旗帜的马拉松赛道
一个人写 PR 是不是有病?
这四天开始引入里程碑的做法:每个里程碑都是一个完整、可交付的增量,有明确的前后对比。同时也开始认真写 PR。
一个人给自己写 PR,听着是有点蠢。但说实话,PR 实际干了三件事:记录当时的意图,留下可追溯的变更历史,还有逼自己想清楚到底要交付什么、为什么做。
每个 PR 都带结构化描述:摘要、变更表格、带复选框的测试计划。等三周后想搞明白"当时为什么改这个"的时候,这些东西实打实有用。
M1 积分经济体系,100 个文件变更
M1 干的事,是把"免费用"改成"免费试 + 付费用"。积分逻辑本身没什么技术含量,这次真正费劲的全在边上。
匿名用户合并是个状态机地狱
新用户进来就是一个 Supabase 匿名账户,零注册摩擦,能浏览、用免费积分、保存测算结果。等他决定登录,不管走 Google OAuth、邮箱还是兑换码,所有数据都得无缝合并过去:积分余额、测算记录、聊天历史、推荐关系,一样都不能丢。
兑换码流程本身就是个状态机:idle → checking → redeeming/logging_in → success/error。RedeemCodeInput 组件自动格式化 PPM-XXXX-XXXX 的输入,处理完整生命周期。匿名用户输了兑换码,后端会直接把兑换码设成这个匿名账户的密码,码本身就是下次登录的凭证。
后来 PR #103 还修了一个竞态条件:liunian 应用类型在 llm_costs_app_check 约束里缺失,导致生产环境 INSERT 失败。这种 bug 最阴,只在特定功能的特定路径上炸。
跨标签页积分不同步,用户会付两次钱
这个 bug 的路径是:用户在一个标签页买了积分,切到另一个标签页,余额显示还是旧的。他以为购买失败,又买了一次,钱就花了两遍。
方案是 Supabase 实时订阅:每个标签页监听积分变动,任何一个标签页扣了或加了积分,其他标签页一秒内同步。听着简单,但订阅生命周期得做对:挂载时订阅,卸载时取消,标签页休眠后重连。细节不少。
7 种场景化升级提示
UpgradePrompt 组件有 7 种触发类型,每种针对不同的用户心理:
- 测算中积分用完(高紧迫感,购买意愿最强)
- 每日运势浏览时余额不足(低紧迫感,意识培养)
- 测算完成后满足时刻(正面情绪,追加销售)
- 推荐提示(社交场景,"送礼"框架)
- 首次发现新功能(好奇心驱动,试用转付费)
提示有卡片和横幅两种变体,外加 24 小时的关闭冷却期,免得用户觉得被骚扰。
M2 每日运势 Hub,又是 100 个文件变更
算命这个品类天然是事件驱动的:用户带着问题来,拿到答案就走。每日运势 Hub 要解决的就是留存,给用户一个每天回来的理由。Hub 里放了五张卡片:八字日柱运势、星座运势、每日塔罗牌、黄历、占卜引导,每张都有自定义的 CSS 主题。
预生成才是核心洞察
这里有个关键决策:内容由北京时间午夜的 cron 任务提前生成好,用户打开时直接读现成的。/api/cron/daily-content 端点用 Gemini AI 生成八字和星座运势,存进 daily_content 表,自动清掉 7 天前的数据。cron 端点的密钥比较用了常量时间 XOR(安全审计提出来的),加上基于 IP 的频率限制(30 次/分钟),时区统一按 Asia/Shanghai 处理。
用户早上 8 点打开 Hub,内容已经在那了,瞬间加载,不转圈。即时加载和等 3 秒的差距,不在技术上那 3 秒,在用户心理上的"这产品靠不靠谱"。
占星模块重构
同一个 PR 里还做了占星模块的国际化重构:347 行的三语字典(zh-CN/zh-TW/en),涵盖行星、星座、相位、宫位,外加 70 多个结果页面字符串。星盘轮盘重新设计成金色配暗色的主题,带度数刻度线、中文行星/星座标签和交互式提示框,78 个测试覆盖星盘计算和布局函数。
合盘持久化
新增了 synastry_readings 表:出生信息加密存储,完整的保存/加载/聊天流程,历史记录 API 集成。出生数据加密是刚需,毕竟合盘测算里装着两个人的出生信息。
M3 数据分析,11 个文件变更
M3 是接 PostHog:用 after() 做兑换码的事件追踪,避免阻塞兑换响应,顺手清掉了被管理后台取代的旧管理路由。这是三个里程碑里规模最小的一个,但它把反馈环路闭上了。之前用户在产品里干什么,全靠猜;现在能看到了。
每天问自己的问题变了
M1 到 M3 做完,每天问自己的问题也跟着变了。以前是"下一个该做什么功能",现在是"下一步该驱动什么用户行为"。转化漏斗、留存钩子、日活参与、推荐激励,这些都是要用代码去解的生意问题。功能清单还在,只是这四天里,我第一次认真想了想留存这件事。