MatchMaster/server/laf-cloud/七日签到与新手礼包生产上线文档.md
2026-09-10 19:55:12 +08:00

324 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 七日签到与新手礼包后端生产上线文档
编写日期:2026-09-10。本文是发布操作说明,未执行云函数发布、生产数据修改或真实支付。
> 最新发布范围:仅七日签到、新手礼包、战令,每日任务不进行上线操作。请优先使用同目录 `三模块上线范围与检查.md` 的明确发布清单和检查结论;本文旧范围不直接作为执行清单。
> 2026-09-10 合并更新:main 已整合 local 并保留主线战令实现。下文候选版本和部分 login 依赖描述属于合并前记录;本次合并的兼容性问题与验证结果请先阅读同目录 `main合并local后兼容性复查.md`,不能直接按旧清单覆盖生产。
## 1. 发布基线与当前结论
| 项目 | 核对结果 |
| --- | --- |
| 后端代码来源 | 独立仓库 `C:/Users/NK/Desktop/workspace/server`,目录 `laf-cloud/functions` |
| 后端候选版本 | `366e96b8b5765d164910c0bd97db25a5edaf01a9` |
| 配套客户端参考 | `C:/Users/NK/Desktop/workspace/MatchMaster`,版本 `85d3dd6c50129a0c25f09be8b933336c11855ce0` |
| 正式应用 | 仓库记录为 `q6rvwvtnga`;发布人员须在控制台核对实际应用 |
| 测试应用 | 仓库记录为 `sor779u2w8` |
| 生产当前版本 | **尚未取得**,不能将本地提交差异直接等同于生产待发布差异 |
| 本次验证 | Node.js `v24.11.1`,102 项模拟测试,95 通过、7 失败;未做云端或真机验收 |
| 发布判断 | **目前不能直接标记“可上线”**:签到客户端收据衔接问题、礼包奖励口径及共享链路失败项需先完成下述检查 |
不要使用 MatchMaster 仓库内的 `server/` 副本作为本次发布源。本文按当前最终实现整理,不要求按历史提交逐个发布,也不建议整仓覆盖正式应用。
### 上线前必须处理的事项
1. **签到客户端存在漏奖路径。** 当前 `assets/seven_day_gift/SevenDayGiftRuntime.ts` 的 `claimReward` 只在 `canClaim=true`、`todayClaimed=false` 时发奖,并用 `[activityId,startAt,rewardDay]` 生成本地 ID;没有消费服务端 `todayClaim.claimId/rewards`。服务端首次查询已记领取,若响应超时丢弃、分包加载失败或发奖前退出,再查会返回 `canClaim=false`,现有客户端无法凭当天收据恢复。第 7 天再查还会变成 `completed`,主页可见性判断也会隐藏活动。上线前应接入服务端收据及未处理收据恢复,完成第 7 节的断线验收。变更防重键时还要兼容已有本地记录,避免旧记录失效造成二次发奖。本次仅编写文档,未修改客户端。
2. **礼包奖励以实际发布客户端为准。** 后端 `starterPack.README.md` 中“8000 金币、道具各 8”已与当前客户端不同。当前 `assets/Script/module/Config/StarterPack.ts` 实际为 **3000 金币、锤子/冻结/魔法棒各 3、1800 秒无限体力**,价格约定仍为 300 分、数量 1。发布人员需确认这就是本次产品口径,并核对购买展示、正常发奖、登录补单均一致。后端不会替客户端决定奖励数量。
3. **共享链路回归未全绿。** 五彩迁移 1 项、Jungle 6 项失败,详见第 8 节。既有 README 已记录同类历史失败,本次复跑仍失败;不能因此宣称共享支付链路全部通过。发布前完成故障归因与相关业务验收,并记录处理结果。
4. **确认生产基线和依赖。** 先导出正式服涉及函数的现行代码、路由及环境变量名清单,逐文件比对候选版本,保留线上独有且仍需保留的修复。本文涉及的 `login` 和支付函数同时包含每日任务、五彩迁移、Jungle、支付分流等逻辑,整文件覆盖会一并带入差异。
## 2. 后端代码变更清单
以下路径均相对独立 server 仓库;发布时使用候选版本的完整文件。13 个核心函数/模块需要核对更新,另有 2 个按生产历史版本决定的兼容文件。
### 2.1 七日签到:3 个核心文件
| 文件 | 最终职责与变更 | 发布要求 |
| --- | --- | --- |
| `laf-cloud/functions/SignInActivity.ts` | 配置校验、按 `levelAmount` 开启活动、UTC+8 日期边界、状态序列化、领取记录、SHA-256 领取 ID、完成/过期判定 | 先发布公共模块;名称大小写必须准确。仓库没有对应 YAML,不能假设批量工具会自动创建模块 |
| `laf-cloud/functions/signInClaim.ts` | 校验用户/token、每天最多一次、按累计次数选择 Day 1~7 奖励、写领取记录,第 7 次标记完成 | 在活动入口之前发布;供 `signInActivityInfo` 内部引用,不能因为不再由客户端直接调用而漏发 |
| `laf-cloud/functions/signInActivityInfo.ts` | 未达门槛返回 `data:false`;首次达标查询开启活动;自动领取;返回领取前展示快照和领取后的 `todayClaim` | 最后发布对外入口;配套 `signInActivityInfo.yaml` |
相关历史:`4b761d2` 初版;`c6df809` 移除在 `setUserLevel` 中开启签到的逻辑;`b7fb1da` 调整等级判断;`ca1e901` 状态改为 JSON 字符串并增加测试工具;`bc9200b` 查询自动领取;`eced022` 返回领取前展示快照。
`login` 和 `setUserLevel` 当前均不负责开启签到。若正式服仍部署初版 `setUserLevel` 中的 `activateSignInActivity` 调用,必须补发当前 `laf-cloud/functions/setUserLevel.ts`;否则它不是本次签到必须更新的文件。同一历史提交中的头像等改动不属于签到运行依赖,不按提交捆绑发布。
### 2.2 新手礼包:10 个核心文件
业务文案也称“幸运礼包”,商品 ID 保持 `starter_pack`。
| 文件 | 最终职责与变更 |
| --- | --- |
| `laf-cloud/functions/limitedTimeEvent.ts` | 首次 48 小时、旧 24 小时迁移、曝光 `shown`、重开 `reactivate`、活动轮次;导出 `starterPackOrderSnapshot`、`reportRookieGift` 供支付函数调用 |
| `laf-cloud/functions/wx/orderPaySig.ts` | Android 直购下单保存轮次/截止时间快照;当前文件同时包含正式/测试订单分流和签名环境变量 |
| `laf-cloud/functions/wx/iosorderPaySig.ts` | 原生 iOS 直购下单保存同样快照;当前代码的支付 `platform` 仍为 `android`、`env=0`,不要在发布过程中擅自修改协议 |
| `laf-cloud/functions/wx/KeFuInfo.ts` | 旧 iOS 客服下单,在 `iosOrder` 保存活动快照;商户信息与回调地址读取环境变量 |
| `laf-cloud/functions/wx/checkIos.ts` | 从 `iosOrder` 转入 `order` 时保留 `starterPackBuyRound`、`starterPackExpiresAt` |
| `laf-cloud/functions/wx/payCallBack.ts` | 支付确认、用户购买标记后上报 `rookie_gift`;保留现有测试订单验签/转发及其他商品逻辑 |
| `laf-cloud/functions/wx/getPayInfo.ts` | 对已经保存埋点快照的已支付/已领取订单重试上报;继续原查单协议 |
| `laf-cloud/functions/wx/iosgetPayInfo.ts` | 旧 iOS 查询支付成功后上报,优先使用微信 `success_time`;继续原成功查单/完成订单协议 |
| `laf-cloud/functions/wucaiMigration.ts` | 迁移数据时保留 `starterPackVersion` 和 `starterPackRound` |
| `laf-cloud/functions/login.ts` | 新建/迁移账号时保存期限版本和轮次;保留既有登录补单逻辑 |
版本演变:`a1d3bfe` 曾增加订单奖励版本/快照与 48 小时;`ce02e44` 撤回礼包订单奖励版本、价格固定和下单资格校验;`adb7c99` 把期限逻辑内置到 `limitedTimeEvent`;`2fef1fd` 增加曝光冷却与重新激活;`366e96b` 增加轮次与购买埋点。
**条件更新:** 如果正式服曾发布 `a1d3bfe` 的奖励版本方案,还需发布当前 `laf-cloud/functions/wx/getOrderReward.ts`,恢复 `code:1,data:"ok"` 协议,并确保上表支付函数都已恢复最终实现。最新埋点中的订单快照只用于统计,不是曾被撤回的奖励快照。
**不要发布或恢复 `starterPackConfig`。** 最终代码已删除此模块。遇到 `function starterPackConfig not found` 应更新完整 `limitedTimeEvent`,不是补上已经废弃的旧模块。
### 2.3 依赖和路由核对
| 项目 | 检查内容 |
| --- | --- |
| `Utils` | 签到和活动接口复用的 token 校验模块,先确认云端存在 |
| `jungleConfig` | `login`、直购下单、回调、旧 iOS 查单的既有共享依赖;核对导出方法匹配,勿为本次发布盲目覆盖其他活动配置 |
| `dailyTaskService` → `dailyTaskConfig` | 当前 `login` 的既有依赖;生产缺失时不能直接上线该 `login` |
| `thinkingdata-node`、`ip2region` | 支付/埋点、登录的既有运行依赖;确认部署中的数数 SDK 支持 `trackFirst` |
| `wx/iosorderPaySig` | 当前仓库没有对应 YAML;核对云端函数和 POST 路由确实存在,批量导入不能只按 YAML 列表判断发布完成 |
| `signInActivityInfo` | 当前 YAML 开放 GET/POST,但处理参数读取 `ctx.body`;验收使用 POST,不能用无业务参数的 GET 证明功能可用 |
| `signInClaim` | 当前 YAML 开放 POST。若正式客户端已统一使用自动领取,生产建议设 `methods: []`,仅保留内部引用;这属于待执行路由配置,不是仓库现状。若仍需兼容旧客户端,先验证协议再决定,不能直接关闭其依赖接口 |
| `SignInActivity` | 公共模块,不需要对外 HTTP 路由;在发布工具/控制台显式配置 |
| 测试接口 | **不发布 `signInTestAdmin.ts/.yaml` 到生产,不配置 `SIGN_IN_TEST_ADMIN_TOKEN`**。它能清空签到记录和改写等级;代码没有自动判断正式环境,只依赖密钥。若线上已存在,关闭对外路由并移除该测试密钥 |
## 3. 功能口径
### 3.1 七日签到
- 仅主游戏 `users`;请求 `gameName=iaa` 被拒绝。用户存在 token 时校验 token。
- 首次调用 `signInActivityInfo` 且数据库 `users.levelAmount >= 23` 时开启;门槛来自配置,不使用 `userLevel` 或客户端显示关卡。
- 从开启当天 **北京时间 00:00** 起算 14 个自然日,到 `endAt` 即结束;不是开启时刻再加完整 14×24 小时。期间累计签到 7 次,允许断签,每个自然日最多一次。
- 服务端只记领取并返回奖励,**不直接增加金币、道具、无限体力或猫皮肤**。客户端按服务端 `claimId` 防重和同步资产。
- 自动领取首次响应的 `claimedCount`、`todayClaimed`、`canClaim`、`status`、`rewards[].claimed` 是领取前展示快照;`todayClaim` 是领取后的凭据。同一天再查返回同一凭据,但展示字段变成已领取。
- 第 7 次首次响应为 `active`,再次查询为 `completed`。不能只依赖 `canClaim` 或活动可见性决定是否恢复未处理的收据。
- `todayClaim` 只包含当天记录;当前后端没有跨日漏奖补领协议,也没有资产发放事务。不要把领取记录存在等同于资产已到账。
- 已有有效活动状态的账号不会因新配置启用而自动重开。将配置 `enabled` 改为 `false` 只阻止新激活,已参与用户仍按 `activityId` 读取原配置继续签到。
### 3.2 新手礼包期限与重开
| 动作 | 规则 |
| --- | --- |
| `save` | 内部 `levelAmount >= 15`、未买且未触发时,保存服务器当前时间 + 48 小时,版本 2、轮次 1 |
| `read` | 返回状态和 `serverTime`;不首次开启、不重开过期礼包,但可触发旧有效期限迁移,因此也不完全是只读操作 |
| 旧记录迁移 | 仅未购买、尚未过期、版本缺失/小于 2 的记录,在旧截止时间加 24 小时,标记版本 2;不是从访问时重新计 48 小时 |
| `shown` | 实际展示时提交当前 `expiry`;仅当前有效未购周期匹配时记录服务器曝光时间,过期/旧周期通知不能推迟当前周期 |
| `reactivate` | 未购、已触发且过期、等级达标,距最近曝光严格超过 48 小时,再满足下列一个原因,重开当前时间 + 48 小时并增加轮次 |
重开原因:`low_coin`(数据库金币 < 500)、`shop`(进入商城)、`level_purchase`(局内购买入口)、`four_failures`(整数 `failureCount >= 4`,上报 `level` 等于数据库 `levelAmount`)。行为和失败次数由客户端上报,后端没有独立失败结算验证。
缺失曝光时间的老账号用旧截止时间作为起点,故须到期后再超过 48 小时。客户端传入 `lastShownAt` 只能延后重开。购买、曝光、版本、期限、轮次等参与条件更新,避免并发延长或覆盖购买标记。不限制重开次数;重复读取或每日展示不增加轮次。
`starter_packState=1` 后不再开启。到期禁购和价格/数量约束由客户端承担,当前后端礼包下单没有新增到期拒绝、奖励版本或固定价格校验。到期前已创建的有效订单可继续支付;当前代码也不保证绕过客户端后无法在到期后创建订单。
### 3.3 支付和埋点
| 数据 | 口径 |
| --- | --- |
| 普通礼包订单 `state` | 0 未确认支付;1 已支付待领取;2 已确认领取 |
| `wx/getPayInfo` | `code=1,pay_state=2` 为待领取;`code=1,pay_state=1` 为已领取;未支付是 `code=0,pay_state=1`,必须同时看 `code` |
| `wx/getOrderReward` | 返回 `code=1,data:"ok"`,客户端用原始订单号去重;此响应不表示后端已加资产 |
| 旧 iOS 客服支付 | `wx/iosgetPayInfo` 成功时普通礼包直接完成订单并返回成功,由客户端发奖,不再调用一次领取确认 |
| `rookie_gift` | 仅后端确认 `starter_pack` 支付成功后发送,前端弹窗/发奖不发送 |
| `buy_round` | 订单所属周期;初次为 1,每次成功重开 +1,迁移 24→48 小时不增加 |
| `time_left` | `max(0,floor((订单周期截止时间-支付确认时间)/1000))`,非负整数秒 |
| `order_id` | 原订单号;`trackFirst.firstCheckId` 使用相同订单号作去重键 |
原生回调取后端首次确认时间;旧 iOS 查单优先取有效的微信 `success_time`。回调延迟会影响原生订单的统计值。跨周期支付仍用原订单快照,不用用户新周期替换。历史无快照待支付单仅在创建时间属于当前周期时取当前周期,否则退回轮次 1、剩余 0 秒;无法还原历史精确轮次。
埋点失败记录 `rookie_gift 上报失败`,不阻断支付确认。已经保存统计快照的普通已支付查单可重试;没有快照时普通查单不会主动补齐,需后续成功回调。没有定时补报任务或持久队列,长期故障后可能需人工核对补报。数数项目仍由用户 `isDebug === "true"` 选择,与订单分流环境不是同一个开关。验收需核对 `trackFirst` 的去重及入库,不仅查看实时列表。
## 4. 数据库准备与迁移
### 4.1 七日签到配置
创建/核对 `sign_in_activity_config` 和 `sign_in_activity_claims` 集合。新活动配置使用仓库的 **`laf-cloud/sign-in-activity-config.json`**,不要复制旧 README 示例中不同的 `activityId`。
| 配置项 | 本次 JSON 值 |
| --- | --- |
| `activityId` | `seven_day_sign_in_v1` |
| `enabled` | `true`(建议准备时先置 false,兼容性验收完成再启用) |
| `triggerLevel` | 23 |
| `durationDays` | 14 |
| `timezone` | `UTC+8`;代码本身按 UTC+8 固定计算,改此文本不会改变代码时区 |
| 奖励日 | 奖励 |
| --- | --- |
| Day 1 | 无限体力 900 秒 |
| Day 2 | 锤子 ×1 |
| Day 3 | 无限体力 1800 秒 |
| Day 4 | 冻结 ×1、魔法棒 ×1 |
| Day 5 | 金币 ×600 |
| Day 6 | 锤子 ×1、冻结 ×1、魔法棒 ×1 |
| Day 7 | 无限体力 3600 秒、猫皮肤 `itemId=12` ×1 |
必须且只能存在一条有效的 `enabled:true` 配置;`activityId` 应避免重复。已有参加者引用的旧配置保留,不能直接删除或改 ID;若线上已使用 `sign_in_v1` 等不同 ID,先按用户 `signInActivity.activityId` 对照,不能强制替换用户状态。更改同 ID 的奖励会影响后续读取,当前配置并非按用户冻结的完整奖励快照。
代码依赖领取记录 `_id` 唯一性(`SHA-256(uid:activityId:dateKey)`);确认集合保留默认唯一 `_id` 索引。可按现有数据量评估增加 `{uid:1,activityId:1}` 普通查询索引,它不是新的幂等前提。集合写入通过云函数完成,沿用现有权限规则,不向客户端开放任意修改领取记录的权限。
### 4.2 字段清单
| 集合 | 字段 | 含义/迁移方式 |
| --- | --- | --- |
| `users` | `signInActivity` | **JSON 字符串**:`activityId,startAt,endAt,activatedAt,completedAt`;读兼容旧对象/null,激活或完成时写字符串 |
| `sign_in_activity_claims` | `_id,claimId,uid,activityId,dateKey,rewardDay,rewards,claimedAt` | 每日领取凭据;`dateKey` 是 UTC+8 日序数字符串,不是 `YYYY-MM-DD` |
| `users` | `starter_pack,starter_packState` | 截止毫秒时间、购买标记,沿用旧字段 |
| `users` | `starterPackVersion` | 期限迁移标记:缺失/1 为旧,2 为 48 小时,不代表奖励版本 |
| `users` | `starterPackLastShownAt` | 当前实际曝光毫秒时间;缺失按规则保守回退 |
| `users` | `starterPackRound` | 活动轮次;老有效周期缺失时按第 1 轮理解 |
| `iosOrder`、`order` | `starterPackBuyRound,starterPackExpiresAt` | 下单时冻结统计周期,旧 iOS 转单保留 |
| `order` | `rookieGiftPaidAt,rookieGiftBuyRound,rookieGiftTimeLeft` | 首次支付成功埋点持久快照;重试不改变数值 |
| `order` | `paymentAppEnv` | 直购订单的服务端环境标记,`production/test` |
没有 SQL/批量迁移脚本需要执行。期限升级和轮次补齐按访问/更新发生,不要全量延长已过期或已购买用户,不要重置订单状态或清空签到记录。历史订单奖励版本/快照字段无需批量删除。
当前五彩迁移只新增保留期限版本和轮次,**没有保留 `starterPackLastShownAt`**;迁移后缺失曝光时间按截止时间保守回退,不能承诺精确沿用迁移前曝光时间。
## 5. 环境变量与外部配置
只核对变量名、是否配置及所属应用,不把真实密钥写进文档或提交。
| 配置 | 正式服要求 | 测试服/补充说明 |
| --- | --- | --- |
| `PAYMENT_APP_ENV` | 显式设 `production`(代码缺失时默认 production) | 测试服必须为 `test`,非法值拒绝支付 |
| `WX_MIDAS_PAY_SIGN_KEY` | 配置小游戏正式虚拟支付 AppKey;生产下单同样实际依赖此值,不能因只对测试做缺失预检就漏配 | 两服对应同一小游戏正式 AppKey;不是 AppSecret 或商户 API v3 密钥 |
| `TEST_WX_PAY_NOTIFY_URL` | 如保留测试直购分流,配置为经核对的测试应用 `/wx/payCallBack` HTTPS 地址;仓库测试断言使用 `https://sor779u2w8.sealoshzh.site/wx/payCallBack` | 当前代码读取环境变量,旧 README “固定地址”描述不能代替配置;缺失会导致生产转发失败 |
| `WX_MCH_ID` | 旧 iOS 客服支付及登录补单使用的商户 ID | 与证书匹配 |
| `WX_MCH_CERT_SERIAL_NO` | 旧 iOS 客服支付及登录补单的证书序列号 | 与实际私钥匹配 |
| `WX_MINIGAME_APP_ID` | `wx/KeFuInfo` 使用的 App ID | 核对对应小游戏/支付主体 |
| `WX_PAY_NOTIFY_URL` | 旧 iOS 商户支付的已验证生产通知地址 | 与原生虚拟支付回调/测试转发地址分开核对,不直接照抄 `TEST_WX_PAY_NOTIFY_URL` |
| `SIGN_IN_TEST_ADMIN_TOKEN` | **不配置** | 仅测试应用需要时设置 |
保留现有私钥读取依赖:`j3k323ol-colorblock-oss` 桶中的 `apiclient_key.pem` 及访问权限。本文不读取、不导出私钥。注意 `wx/iosgetPayInfo.ts` 目前仍硬编码旧商户号/证书号,查询 URL 也含固定商户号;不能认为它已完全受上述环境变量控制,发布前核对与正式商户一致。
微信原生虚拟支付通知地址按仓库记录为 `https://q6rvwvtnga.sealoshzh.site/wx/payCallBack`,须到实际微信后台确认。生产订单前缀 `wcx_`,测试新订单前缀 `wct_`。生产对测试单先验签再转发,测试服再次验签、比对订单;转发最长 3 秒、不跟随重定向,只有 HTTP 200 且 `ErrCode=0` 才确认成功。
`env=0` 仍是真实支付。开发版/体验版礼包的客户端免费发货不能证明生产支付成功;若需要真实测试订单,先准备测试服和生产转发链路。此前测试库中的 `wcx_` 旧单不会自动按新规则分流,单独核账,不直接改成已支付。
## 6. 发布执行顺序
在第 1 节问题处理完成后执行;本仓库未提供已验证的一键生产部署配置,以下按 Laf 控制台/现有发布工具逐项操作,不提供未经验证的整仓 deploy 命令。
1. **冻结发布清单与备份。** 记录正式当前版本、候选版本、客户端版本、发布人/时间;导出涉及云函数现版及路由、依赖版本。按现有备份流程备份相关集合,保存配置和必要用户/订单核对快照。对比共享函数,处理线上独有差异。
2. **准备测试环境。** 按第 4、5 节配置,先在测试服完成第 7 节边界/支付流程测试;测试数据准备接口只部署到测试服。确认 SDK 的 `trackFirst` 可调用以及缺失 YAML 的函数都能发布。
3. **准备生产配置和依赖。** 确认 `Utils`、`jungleConfig`、`dailyTaskService/dailyTaskConfig` 等已有依赖;新增签到配置初始保持不启用(已有活动则保留其正常配置与记录)。支付环境变量在替换下单函数前就绪。
4. **先发布公共实现。** 发布 `SignInActivity`、完整 `limitedTimeEvent`、`wucaiMigration`。此阶段不让未配套客户端开始调用自动签到入口;必要时用现有网关/入口控制发布窗口。活动接口一旦更新,被已有客户端访问就可能执行期限迁移,需记下实际生效时间。
5. **发布签到链路。** 先 `signInClaim`,再 `signInActivityInfo`,设置并核对第 2.3 节路由;若生产还含初版签到触发逻辑,更新 `setUserLevel`。禁止随批量操作发布 `signInTestAdmin`。
6. **发布礼包支付和登录链路。** 在完整 `limitedTimeEvent` 已可导出共享方法后,更新 `wx/orderPaySig`、`wx/iosorderPaySig`、`wx/KeFuInfo`、`wx/checkIos`、`wx/payCallBack`、`wx/getPayInfo`、`wx/iosgetPayInfo`,再更新 `login`;如需撤回历史奖励版本协议,同步更新 `wx/getOrderReward`。发布工具若能按依赖成组发布,采用同一候选版本;逐函数发布时安排短窗口,避免新下单快照和旧回调混用造成埋点缺失。
7. **配套客户端并启用签到。** 确认正式包使用正确生产域名、正确礼包奖励、服务端签到收据协议、猫 12 资源。客户端就绪后使签到活动恰有一条有效启用配置。若旧客户端已在调用同名签到接口,启用配置不能隔离老客户端,必须先完成兼容处理或现有版本准入控制。
8. **生产冒烟与观察。** 使用专用验收账号执行最小领取和真实支付流程,记录订单、领取凭据、资产前后值、回调和埋点结果。生产不通过改真实玩家等级/签到日期来做 Day 1~7 场景。
9. **登记完成。** 逐函数核对线上代码/路由版本,记录未解决事项、验收证据、观察时间及回滚版本。未满足放行条件就保持未完成状态。
## 7. 验收清单
### 7.1 签到
请求入口为 `POST /signInActivityInfo`,请求体示例:`{"uid":"<验收账号>","token":"<有效token>"}`。它会写领取记录,不能用真实玩家做无意的“只查一下”。
| 场景 | 预期结果 | 验收环境 |
| --- | --- | --- |
| 等级 22/23、状态为空/null | 22 返回 `code=1,data=false`;23 建活动字符串并记 Day 1,返回当天凭据 | 测试;生产专用账号可冒烟 |
| 同日重复/并发查询 | 同账号/活动/日仅一条领取记录;相同 `claimId`;资产仅发一次 | 测试,生产核对重复请求 |
| 首次响应丢失、分包失败、发奖前退出后同日重进 | 返回相同 `todayClaim`,即使 `canClaim=false` 也恢复未处理奖励,不重发已完成奖励;当前参考客户端需先修正 | 测试必过 |
| Day 7 首次领取与恢复 | 首次展示 active,重查 completed;仍能恢复未处理收据和猫 12 发放 | 测试必过 |
| 断签与日期边界 | 非连续登录累计推进;UTC+8 次日可下一档,`now>=endAt` 不再新增记录 | 测试 |
| 配置缺失/无效/多条有效启用 | 返回配置错误,不新建活动/领取记录 | 测试 |
| token 不正确、`gameName=iaa` | 拒绝且无写入 | 测试 |
| 客户端资产 | 奖励数量、无限体力秒数、猫 12 解锁正确;前后台切换和同步失败不重复到账 | 测试、生产最小冒烟 |
| 测试接口与领取路由 | 生产 `signInTestAdmin` 不可调用;`signInClaim` 按最终兼容策略配置且内部引用可用 | 生产 |
可用测试资料:`laf-cloud/signInActivity.TESTING.md`、`laf-cloud/postman/sign-in.postman_collection.json`、`laf-cloud/postman/sign-in.README.md`。运行 Postman 前核对 baseUrl;测试管理场景不能切到生产执行。
### 7.2 新手礼包
活动请求体:`{"uid":"<验收账号>","token":"<有效token>","event":"starter_pack","action":"read"}`。首次用 `save`;展示用 `shown` 加 `expiry`;重开用 `reactivate` 加 `reason`,失败原因加整数 `failureCount` 和 `level`。
| 场景 | 预期结果 |
| --- | --- |
| 等级门槛与首次开启 | 低于 15 不开;首次 save 截止约为服务器 +48 小时、版本 2、轮次 1;重复请求不续期 |
| 老记录迁移 | 未购且仍有效的旧期限只加 24 小时一次;过期/已购买记录不自动延长 |
| 曝光与冷却 | 只有当前有效周期 `shown` 更新;恰好 48 小时不重开,严格超过才可;read 不推迟曝光 |
| 四种重开原因 | 各自满足时成功;金币 500 不满足;失败数非整数/不足 4/关卡不符不满足;旧周期通知无效 |
| 并发与轮次 | 并发只重开一轮,重复展示不增轮;支付购买标记不能被活动更新清除 |
| Android/原生 iOS 购买 | 正式订单 `wcx_`,金额 300 分、数量 1;写统计快照;真实回调后 state 1,领取确认后 state 2,用户购买标记为 1 |
| 查单/登录补单/旧订单 | 同一订单正常发奖与补单不重复;旧奖励版本字段不拦截;当前客户端发放 3000/各3/1800秒(以最终确认版本为准) |
| 旧 iOS 客服路径 | 若仍开放该入口,`iosOrder→order` 保留快照;查单成功直接发奖;重复成功不重复发奖 |
| 到期和跨周期支付 | 已有有效订单继续确认,使用原下单周期;到期后 `time_left=0`;重试不改变持久快照 |
| `rookie_gift` | 同一订单同一去重键;正式账号进入正式项目,轮次/秒数正确;模拟 SDK 失败不阻断支付 |
| 测试分流 | 测试单经生产转发到测试库,不写生产订单/玩家;无效签名或转发失败不确认成功;不能以客户端免费发货替代验证 |
| 共享业务 | 普通登录、五彩迁移、月卡、Jungle 下单/回调/领取正常;对第 8 节失败项提供明确验收结论 |
### 7.3 观察项
按发布前相同口径观察:活动接口错误量、配置无效/模块缺失日志、订单 state=0 停留及待领取积压、回调非零返回、`rookie_gift 上报失败`、用户漏奖/重复到账反馈。按同一订单号串联下单、回调、查单和统计快照;按 `uid + activityId + claimId` 核对签到。观察窗口和异常阈值由现有生产基线填写,不把本文模拟测试数据当成线上指标。
## 8. 本次实际验证记录
2026-09-10 在后端候选版本执行,数据库、网络、微信签名及数数 SDK 由现有测试模拟,未触发真实扣款或真实埋点。
```powershell
# 在独立 server 仓库运行;仅给当前测试进程提供模拟转发地址。
$env:TEST_WX_PAY_NOTIFY_URL = 'https://sor779u2w8.sealoshzh.site/wx/payCallBack'
node --test --test-reporter=spec laf-cloud/tests/sign-in-activity.test.mjs laf-cloud/tests/starter-pack.test.mjs laf-cloud/tests/rookie-gift.test.mjs laf-cloud/tests/payment-routing.test.mjs laf-cloud/tests/monthly-card-renewal.test.mjs laf-cloud/tests/login-wucai-state.test.mjs laf-cloud/tests/wucai-migration.test.mjs laf-cloud/tests/jungle-treasure.test.mjs
```
| 测试文件 | 通过/总数 |
| --- | --- |
| `sign-in-activity.test.mjs` | 17/17 |
| `starter-pack.test.mjs` | 17/17 |
| `rookie-gift.test.mjs` | 12/12 |
| `payment-routing.test.mjs` | 9/9 |
| `monthly-card-renewal.test.mjs` | 7/7 |
| `login-wucai-state.test.mjs` | 22/22 |
| `wucai-migration.test.mjs` | 4/5 |
| `jungle-treasure.test.mjs` | 7/13 |
| **合计** | **95/102;命令退出码 1** |
失败明细(行号对应候选版本):
| 文件与失败位置 | 失败内容 |
| --- | --- |
| `wucai-migration.test.mjs:71` | `pristine classification requires explicit unpaid state and no activity`:缺少明确未付费字段时,实际返回 true,期望 false |
| `jungle-treasure.test.mjs:162` | 51 档奖励配置与测试规划表不同 |
| `jungle-treasure.test.mjs:252` | 不按顺序领取的测试预期成功,实际 code=0 |
| `jungle-treasure.test.mjs:266` | 锁定付费档位预期“尚未解锁”,实际“当前没有可处理的 Jungle 活动” |
| `jungle-treasure.test.mjs:311` | 微信回调解锁订单状态实际 2、期望 1 |
| `jungle-treasure.test.mjs:334` | Jungle 服务端价格下单测试实际 code=0、期望 1 |
| `jungle-treasure.test.mjs:389` | 旧 usersAd 路由断言不符 |
历史 `starterPack.README.md`、`paymentRouting.README.md` 已记录相同类型失败及当时的旧版本复现结论。本次只复跑当前候选版本,未重新做历史版本对照,也未修复业务代码。上述测试通过不能证明签到客户端恢复链路、真实支付签名配置或真实数数入库已通过。
## 9. 回滚与故障处置
### 9.1 签到
1. 先阻止继续产生异常领取:用现有客户端入口/网关控制暂停活动调用;如有旧客户端直调 `signInClaim`,同时控制该入口。仅 `enabled=false` **不能停止已有参与者签到**,只阻止新激活。
2. 使用发布前导出的兼容版本恢复 `signInActivityInfo`、`signInClaim`、`SignInActivity` 及对应路由;先回退入口/调用方,再按依赖回退模块。若恢复旧版本不支持字符串状态,先处理兼容,不能直接切回最早版本。
3. 保留 `users.signInActivity`、全部领取记录和旧活动配置,不通过删领取记录让用户“再领一次”。按记录与客户端资产核对是否漏发,另行补偿;跨日漏奖不依赖现有 `todayClaim` 自动恢复。
### 9.2 新手礼包/支付
1. 若发奖或下单异常,先关闭客户端新购买/重开入口,并按现有服务端入口控制阻止新下单;继续处理已付款订单的确认和必要补单,不能把停回调当成回滚。
2. 先回滚调用方(下单、查单、回调、登录),再回滚共享 `limitedTimeEvent`/`wucaiMigration`;调用方还引用 `starterPackOrderSnapshot/reportRookieGift` 时不得先删除这些导出。也可保留兼容共享导出,仅恢复经验证的业务行为。
3. 支付配置、回调分流与代码按兼容组回退。若仍有 `wct_` 待确认订单,继续保留转发/测试处理能力,避免已有付款无法确认。不要回退成仍依赖缺失 `starterPackConfig` 的版本。
4. 已延长期限不批量减回 24 小时;不清除版本/轮次/曝光字段,不将 `starter_packState=1` 改回 0,不回退已确认的订单 state,也不清除统计快照。代码回滚不等于业务数据倒放。
5. 只有埋点失败时,优先修复 SDK/依赖与上报问题,按订单快照核对缺失事件;不要为了重发统计重做支付或重复发奖。当前没有自动可靠补报队列。
6. 奖励在客户端发放,后端回滚不能撤回已经发出的奖励,也不会自动统一新旧客户端奖励。按订单号核对正常购买与补单,单独登记差额或漏奖处理。
## 10. 发布登记表
| 登记项 | 待填写 |
| --- | --- |
| 正式服发布前版本/备份位置 | |
| 最终后端版本、实际函数清单 | |
| 最终客户端版本、奖励确认结果 | |
| 签到 `todayClaim` 恢复及旧本地防重兼容结果 | |
| 7 项回归失败的处理/验收结论 | |
| 配置 activityId、路由、环境变量核对人 | |
| 测试服验收记录 | |
| 生产验收账号/订单与证据位置(不填密钥) | |
| 发布时间、执行人、观察窗口 | |
| 回滚版本与负责人 | |
| 最终状态:待处理/已发布/已回滚 | 待处理 |
参考资料:同目录 `signInActivity.API.md`、`signInActivity.README.md`、`signInActivity.TESTING.md`、`starterPack.README.md`、`paymentRouting.README.md`。本文已按候选代码纠正旧材料中的礼包奖励和测试回调地址配置口径;生产实际状态仍以发布前控制台核对为准。