《互联网产品的客户端架构·交易、增长与平台化》9.3 拉新活动页的降级
活动页的失败方式比商品页多得多:配置拉不到、活动未开始、已经结束、名额耗尽、运营暂停。本节把这些情况分成「配置问题」与「业务状态」,给出缓存容忍、版本回退保护、降级目标页与降级后分享参数的规则,并附一个用假时钟与可控配置源验证的 L2 模型。
tag
活动页的失败方式比商品页多得多:配置拉不到、活动未开始、已经结束、名额耗尽、运营暂停。本节把这些情况分成「配置问题」与「业务状态」,给出缓存容忍、版本回退保护、降级目标页与降级后分享参数的规则,并附一个用假时钟与可控配置源验证的 L2 模型。
邀请关系是一本「谁欠谁奖励」的账,绑定时机早一步会被刷、晚一步会被截胡。本节比较点击、注册、首单三个绑定时机,拆开唯一性、可撤销与自己邀请自己的四类伪装,并给出一个把绑定判定、撤销与失败重试写成纯函数、可在本地用 24 条用例验证的可运行模型。
分享链接不是拼几个查询参数,而是一份要穿过渠道、安装包和归因窗口的承诺。本节拆开渠道码、活动 id、邀请人、商品与时间戳的生成与校验,说明分享卡片和落地页不一致时以谁为准、参数丢失后如何兜底,并给出一个可运行、可测试的 L1 分享参数与归因模型。
会员到期不是一瞬间的 UI 状态:权益会逐条失效,提醒要按节奏升级,已支付订单还要保住下单时的会员价。本节把降级拆成权益比较、提醒阶段与订单价格快照三层,说明为什么已支付订单永久锁价、待支付订单只保留到支付截止时间,并给出一个可测试的 TypeScript 模型。
积分不是端上可以随便加减的余额,而是一本由发放、抵扣、退回、过期四类流水组成的账。本节说明为什么余额必须从流水重放、过期批次为什么要先到期先出、抵扣失败为什么用退回流水而不是删除,以及多端账本如何按 id 合并,并给出一个可测试的 TypeScript 账本模型。
会员页最容易把等级、有效期与权益状态混成一个字段:用户看到五条权益,实际只有两条可用。本节把会员快照、权益状态与会员价拆开,说明「可领取」为什么不等于「已拥有」、会员价为什么必须由服务端给出并在结算时重算,并给出一个可测试的 TypeScript 展示模型与到期提醒口径。
划线价、券后价与「预计省 X 元」不是三个装饰数字,而是三句可以被核验的事实陈述。本节给出划线价的展示条件、券后价与优惠金额的计算口径、不可用原因的具体文案,以及失效券保留但不给动作的展示方式,避免用不存在的原价制造假优惠。价格区因此可以按固定顺序渲染。
会员价、活动、优惠券与积分同时出现时,叠加顺序、互斥组与舍入规则决定了用户最终付多少钱。本节把顺序固定为会员、活动、券、积分,用组声明处理互斥,用整数分逐级计算,并给出端上预演与服务端结果不一致时的对账与二次确认策略。所有金额保持整数分,叠加结果可逐项对账。
优惠券的「可用」不是布尔值,而是门槛、范围、有效期、领取状态与互斥关系共同裁决的结果。本节把判定顺序写成可测试的纯函数,用服务端时钟避免设备时间漂移,端上只做预筛与提示,并复盘一次因设备时间与范围并集导致的券失效事故。并给出可运行的 TypeScript 纯函数与测试。
退款不是把订单状态改成已退款,而是挂在原支付单上的一组退款单:整单退与部分退、审核中与退款中、金额分摊与到账时间都各有口径。本节用四种状态、可退额度与最大余数分摊,给出一个可测试的 TypeScript 退款模型,并说明端上展示与渠道到账时间的差异。
端上无法直接看渠道账单,只能把订单状态、支付单状态与渠道流水号三路信号按 orderId、payId、channelTxnId 对齐,再把差异分成漏单、重复、金额不符与状态不一致。本节给出一个纯函数分类器与带触发时机的待确认队列,并附真实测试输出。
重复支付通常来自重复点击、重复回跳与重复轮询三个入口,而不是用户手抖。本节用支付单号去重、显式的进行中状态与超时后先查询的策略,把同一订单的多次支付尝试收敛到一张支付单上,并给出可用假时钟验证的 TypeScript 防护模块与真实测试输出。
同一笔支付的结果会从回调、服务端推送、轮询和回跳四个方向同时到达,端上收到的顺序与事实发生的顺序无关。本节把事件写成带服务端序号的不可变记录,用去重、序号比较、来源强弱与成功吸收五条规则裁决竞争,并给出可在进程内确定性验证的 TypeScript 实现与真实测试输出。
支付结果不是成功与失败两态:超时、回跳丢失与查询未决都会落入 unknown。本节用一个五态状态机表达 pending、unknown、succeeded、failed、cancelled,规定 unknown 的进入与退出、查询结果的序号与同序号优先级、成功吸收与重试条件,并附可运行的 TypeScript 模块与真实测试输出。
支付编排不是把用户送去渠道就结束:拉起成功只说明参数被接收,回跳参数只是一句口信,真正结果必须由服务端查询确认。本节把支付单号、幂等键、回跳解析、指数退避轮询与页面可见性处理写成一个可假时钟验证的 TypeScript 模块,并复盘一次把回跳当成功导致的重复扣款。
待支付倒计时不该由本地时长递减,而要以服务端 expiresAt 为准,并用服务端时间校正本地时钟。本节讨论切后台、改系统时间、临期取消与支付回调竞争,给出一个可用假时钟验证的 TypeScript 倒计时模块、竞争裁决函数与真实测试运行输出。
服务端订单状态有八种,端上却要面对按钮、倒计时、浮层与列表刷新四类副作用。本节把状态映射写成纯函数,用未知状态的前向兼容、取消与支付的竞争分支、迁移副作用的去重,说明端上如何只做翻译而不发明状态,并附一组 L1 纯函数测试与完整真实运行输出。
下单提交超时后,端上把「未知」当成「失败」就会诱导用户重复下单。本节用进入确认页生成的幂等键、五个内部状态与三种按钮出口,把双击、重试和返回重进收敛到同一个键上,并给出一个可用假时钟验证的 TypeScript 提交控制器与真实测试运行输出。
购物车的全选、半选、失效商品与限购,最终都会落到一个数字上:结算金额。本节把勾选状态建模成可售行的三态汇总,明确失效项不可选、限购按上限截断、服务端拒绝后只回退被拒绝的行,并用可运行代码验证勾选与金额联动,附真实测试输出、一次典型复盘与边界清单。
价格明细不是五个孤立的数字,而是一份必须同版本的结算快照。本节拆开商品小计、优惠、运费、税费与实付五段,说明端上只能做展示层分摊与预演、不能产出最终金额,用最大余数法把优惠分摊到整数分,并用版本号拒绝过期明细,附可运行代码、测试输出与复盘推演。