10 KiB
幸运礼包:48 小时期限与重新激活
当前方案:后端负责活动有效期,奖励数量与实际发放由客户端负责。商品 ID 仍为 starter_pack。支付、查单、转单和领取接口恢复 a1d3bfe 之前的行为,不对新手礼包新增订单版本、奖励快照、价格固定或下单资格校验。
期限规则
limitedTimeEvent的首次save在内部关卡levelAmount >= 15时保存服务器时间 + 48 小时,read不主动开启未触发的礼包。- 旧礼包按访问时迁移:只延长未购买且在迁移时仍有效的旧记录,在旧截止时间上加 24 小时,即原触发时间 + 48 小时。
- 已购买记录不重新开启;已过期记录只有通过下述重新激活动作才会重开。原 read/save 不重开过期礼包。重复或并发请求不会重复延长,也不会覆盖购买状态。
- 用户字段
starter_pack是截止时间戳(毫秒),starter_packState=1表示已购买;starterPackVersion=2仅标记期限已按 48 小时处理。缺失/1 表示旧期限,不再代表订单奖励。 - 保留
login和wucaiMigration对用户期限标记的保存,避免用户迁移丢失标记而重复加时。 limitedTimeEvent返回serverTime供客户端校正倒计时。48 小时计算和迁移函数均内置在limitedTimeEvent.ts,不依赖额外的starterPackConfig云函数;仍使用项目已有的Utils校验 token。
重新激活(2026-09-10)
新增 limitedTimeEvent 动作(event 仍为 starter_pack):
shown:客户端在实际展示礼包时传入当前截止时间expiry(毫秒)。只有该期限与当前有效、未购买的礼包一致时,才记录服务器时间starterPackLastShownAt;旧周期或已过期的展示通知不能推迟当前周期。reactivate:礼包未购买、已触发且到期,且服务器当前时间距最近展示严格超过 48 小时,才重新保存当前时间 + 48 小时。保持原等级门槛。返回数据增加reactivated和starterPackLastShownAt。
reason 的可选值与判断:low_coin(数据库 coinAmount < 500)、shop(客户端已进入商城)、level_purchase(客户端点击局内购买入口)、four_failures(客户端上报 failureCount >= 4 的整数且 level 与数据库 levelAmount 一致)。前端负责行为上报与连续失败计数,计数按账号和关卡保存在设备上,通关清零,单次挑战内重复失败不重复累计。后台没有独立的关卡失败结算,因此此处不是服务器权威验证失败次数。
老记录缺少展示时间时保守使用其原截止时间,需要从到期后再经过 48 小时。客户端附带 lastShownAt 用于补充尚未同步的本地展示时间,只能延后重开,且最多按服务器当前时间处理。数据库更新同时比对原截止时间、购买状态、期限版本、展示时间;金币触发额外比对金币,避免并发覆盖购买或在金币已变化时误触发。
首次重新激活和每次后续重新激活都沿用同一规则,不设置次数上限。提醒仍沿用每日首次进首页;不增加每次 login 弹窗,也暂不增加商城宣传标签。已经支付的旧订单继续原查单与领取流程。
本次后端只需发布完整 limitedTimeEvent.ts,无新增模块依赖;与新客户端配套发布。没有部署、修改线上数据或改动支付回调分流。
奖励与支付约定
前端发起 300 分、数量 1 的购买;所有未发放新手礼包订单在新版客户端统一发放 8000 金币和锤子/冻结/魔法棒各 8 个,不再区分新旧订单。不自动给已经发放的旧订单补差额。
普通支付查询的 pay_state=2 表示已支付待领取,pay_state=1 且 code=1 表示已领取。原 getOrderReward 返回 data="ok",客户端自行携带订单号去重。旧 iOS 客服查单成功会直接完成订单,客户端收到成功即发奖;登录补单继续沿用原行为。
到期禁购由客户端检查服务器截止时间并禁用入口实现;恢复原后端下单接口后,不再保证到期后无法直接创建订单。到期前已有有效订单可以继续支付。服务端不直接增加金币和道具,前端发奖仍有跨设备及中断恢复限制;本次没有引入新的服务端发奖事务。
发布范围
- 活动期限修复只需发布最新
limitedTimeEvent.ts,并保留用户迁移时的期限标记。不再需要新建或发布starterPackConfig;原独立模块文件已移除。 - 如果已经部署
a1d3bfe:还需重新发布恢复后的wx/orderPaySig、wx/iosorderPaySig、wx/KeFuInfo、wx/checkIos、wx/getOrderReward、wx/getPayInfo、wx/iosgetPayInfo和login,配套使用本次客户端。 - 已存入历史订单的版本/快照字段无需批量清理,新版客户端不再使用这些字段。
若遇到 function starterPackConfig not found,表示云端旧版 limitedTimeEvent 引用了未发布的模块。用本次完整文件替换并发布后可消除该依赖。limitedTimeEvent 的错误日志是活动接口日志,不是 wx/payCallBack 发货回调日志;它本身不能说明 state=0 订单的支付回调是否已到达。
本次没有发布云函数、修改线上用户数据或调用真实支付。
验证
node --test laf-cloud/tests/starter-pack.test.mjs laf-cloud/tests/monthly-card-renewal.test.mjs
期限测试覆盖首次 48 小时、重复/并发请求、旧期限迁移、已购买/已过期、关卡和身份检查,以及购买状态与迁移竞争。月卡测试作为原支付流程的回归检查。原先针对订单版本和奖励快照的测试已移除,客户端另有原支付协议的发奖回归测试。
2026-09-10 回归:新手礼包 15 项、月卡 7 项通过。额外支付分流 9 项在提供 TEST_WX_PAY_NOTIFY_URL=https://sor779u2w8.sealoshzh.site/wx/payCallBack 后全部通过(网络、数据库与签名均由测试模拟);不提供该现有环境变量时,分流测试中地址断言会失败。本次没有修改支付分流实现或测试。
购买成功埋点 rookie_gift(2026-09-10)
仅在后端确认商品 starter_pack 支付成功后发送,前端发奖和弹窗不直接发送此事件。奖励、支付环境分流、下单资格与发奖协议沿用当前行为。
| 属性 | 类型与口径 |
|---|---|
| buy_round | 整数;订单所属的 48 小时活动周期。首次开启为 1,每次成功重新激活加 1;每日提醒、手动打开、read/save 和 24→48 小时迁移不递增。 |
| time_left | 非负整数秒;max(0, floor((订单快照截止时间-支付成功时间)/1000))。到期后的有效订单完成支付记为 0。 |
| order_id | 订单号,辅助核对与去重。 |
用户字段 starterPackRound 在活动更新时与期限一起写入,并加入并发更新条件。login、wucaiMigration 保留轮次。旧用户缺失轮次时,已有周期视为第 1 轮;部署前累计重开次数无法还原,下一次重开从第 2 轮继续。
原生 Android/iOS 下单和旧 iOS 客服下单保存 starterPackBuyRound、starterPackExpiresAt(仅埋点快照,不是奖励快照);checkIos 转单原样保留。订单跨周期支付时使用原订单快照,不读取新周期替换它。
时间来源:wx/payCallBack 使用后端首次确认成功时的服务器毫秒时间(当前原生回调没有接入明确的支付完成时间字段,因此微信回调延迟会影响结果);旧 wx/iosgetPayInfo 优先使用微信查单响应 success_time,无效则回退本次服务器确认时间。不是点击购买时间,也不是前端领取奖励时间。首次上报前在订单条件写入 rookieGiftPaidAt、rookieGiftBuyRound、rookieGiftTimeLeft,重试读取固定值。
部署前创建的待支付订单没有快照:创建时间落在用户当前活动周期内时使用当前期限和轮次,否则保守使用第 1 轮、剩余 0 秒。这类历史订单无法恢复准确的旧周期倒计时。
上报沿用现有 payment 事件的数数项目选择(isDebug 字符串 true 使用测试项目),accountId 为订单 openid,存在 distinctId 时一并发送。使用 trackFirst,以订单号作为 firstCheckId;重复回调、并发查单由数数按同一事件和订单号去重。数数首次事件默认约延迟 1 小时入库,验收不要只看实时事件列表:官方 Node.js 首次事件说明。部署的 thinkingdata-node 必须支持 trackFirst。
上报错误记录 rookie_gift 上报失败,不阻断支付确认或发奖。原生回调重试、带有已保存埋点快照的原生已支付查单、旧 iOS 成功查单会再次尝试发送。SDK 沿用现有 BatchConsumer;没有增加定时补报任务或持久消息队列,长期服务故障后如无后续回调/查单,仍需要人工补报。埋点快照写入失败时也只记录错误,需要后续成功回调重试。
发布顺序与范围:
- 先发布完整 limitedTimeEvent(新增轮次与共享埋点方法,仍只依赖既有 Utils)。
- 发布 wx/orderPaySig、wx/iosorderPaySig、wx/KeFuInfo、wx/checkIos、wx/payCallBack、wx/getPayInfo、wx/iosgetPayInfo。
- 发布 login 和 wucaiMigration,保留账号迁移后的轮次。
不用新建云函数,也无需修改前端或 UI。生产、测试环境分别更新各自的函数,继续保留现有支付回调分流配置。本地没有部署或触发真实数数事件。
新增模拟测试 rookie-gift.test.mjs 覆盖下单快照、跨周期支付、秒数取整/到期归零、重复与并发去重键、SDK/数据库异常、旧 iOS 转单及支付成功时间;starter-pack.test.mjs 增加轮次与多次实际曝光的区分。
本次验证:rookie-gift、starter-pack、payment-routing、monthly-card-renewal、login-wucai-state、wucai-migration 共 72 项,71 项通过。唯一失败为已有 pristine classification requires explicit unpaid state and no activity(wucai-migration.test.mjs:71),已使用修改前 HEAD 文件复现同一失败;本次不调整该迁移判定。10 个修改的 TypeScript 文件转译无语法诊断,git diff --check 通过。支付、数数、数据库均使用模拟,未做线上联调。