| .. | ||
| data | ||
| tests | ||
| tools | ||
| admin.ts | ||
| admin.yaml | ||
| analytics.ts | ||
| analytics.yaml | ||
| cloudRise-config.example.json | ||
| cloudRise-config.publish.example.json | ||
| index.ts | ||
| index.yaml | ||
| periods.ts | ||
| periods.yaml | ||
| README.md | ||
| rules.ts | ||
| rules.yaml | ||
| stats.ts | ||
| stats.yaml | ||
百人赛 cloudRise 接入说明
2026-09-23 百人赛单独上线,以 百人赛生产发布清单 为准:相对线上 main 新增 8 个函数/模块,更新 login;黄金矿工和新版每日/周任务暂不发布。当前活动列表只返回百人赛,首次上线需要准备 activityConfigs、cloudRiseSamples、cloudRiseRuns 三个集合。样本仅使用每人每期一条的 samples 数组结构,不提供旧样本迁移。
当前配置已统一存入 activityConfigs(activityId=cloudRise),使用不可变版本。发布示例见 完整配置请求,公共规则与黄金矿工示例见 公共配置说明,已有环境升级先执行 数据迁移。
对应客户端 PRD:MatchMaster/docs/cloudRise-PRD.md(V1.1)。客户端与本仓库需配套发布。
登录开启个人活动期
百人赛改为按玩家首页请求开期。登录时读取 users.cloudRisePeriod;若当前期未到期,且对应挑战尚未失败、过期或完成三阶段,则保持期号和截止时间。没有可用期时创建新期,即使上一期原截止时间尚未到达。首次登录且未达到解锁关卡的玩家也建立个人期,原解锁门槛仍用于报名。
- 配置取 activityConfigs 中 activityId=cloudRise、status=published 且已生效、已发布的最新版本,按 effectiveFrom/publishedAt 选取。使用 durationHours、unlockLevel、pools 作为模板;旧 config.startsAt/endsAt 不再限制个人活动开放。没有模板时不开期,无效模板记录登录错误;两者都不阻断基础登录。
- 版本记录外层
enabled控制登录是否创建新个人期。先选最新已发布且已生效版本,再判断enabled;false时不开新期,也不回退旧版本。缺省按true兼容。已有未结束个人期仍可报名、推进、结算,并继续由 activityConfig/list 返回。新开期仅在后续登录触发。 - users.cloudRisePeriod 保存 periodId、configVersion、startsAt、endsAt、durationHours、unlockLevel、pools。periodId 每人每期唯一,startsAt 为首页请求开期时间,endsAt = startsAt + durationHours 小时。重复及并发登录通过原子条件更新避免重复开期。
- 登录只开期,不匹配、不占用参与记录。点击「开始」才准备并确认匹配,run.expiresAt 沿用个人期 endsAt,不重新开始倒计时。后续阶段仍手动开始。
- 开新期不覆盖 cloudRiseState,不丢弃旧奖励回执、待保存奖励和统计事件;旧轮次继续按原流程结算、归档后再参与新期。上线前已有未结束挑战沿用原期号与截止时间。
- activityConfig/list 携带 uid/token 后返回当前玩家仍开放的个人期;挑战结束或到期后不再返回这一期。列表、status 和首页刷新均不创建下一期;下一次成功登录才创建。
- 登录响应不透传 cloudRiseState/cloudRisePeriod,对外活动信息仍从列表和 cloudRise/index 读取。
已有百人赛环境升级个人期时:先发布 activityConfig/store 与新增内部模块 cloudRise/periods(methods: []),再同步发布 login、cloudRise/index、activityConfig/list 及配套客户端,无需额外新集合或批量改玩家数据。首次上线须按发布清单准备三个集合。本地改动不代表已经部署。
目录与发布
百人赛专属文件统一位于 laf-cloud/functions/cloudRise/:
cloudRise/
index.ts / .yaml 玩家接口
admin.ts / .yaml 运营配置接口
rules.ts / .yaml 内部活动规则模块
stats.ts / .yaml 生成的内部统计数据模块
periods.ts / .yaml 个人活动期查询与显式开期模块
analytics.ts / .yaml 内部埋点模块
data/cloudRiseLevelStats.json
tools/generate-cloud-rise-stats.mjs
tests/cloud-rise.test.mjs
cloudRise-config.example.json
README.md
另外必须发布目录外的 activityConfig/store.ts/.yaml、activityConfig/list.ts/.yaml。
采用现有 wx 目录的 Laf 命名约定,六个云函数名称分别为 cloudRise/index、cloudRise/admin、cloudRise/rules、cloudRise/stats、cloudRise/periods、cloudRise/analytics,与 YAML 的 name 一致。内部通过 @/cloudRise/... 引用模块;客户端玩家接口需同步使用 cloudRise/index。发布时先发布内部依赖,再发布玩家与管理接口。
rules 仅供内部导入常量与规则函数,没有默认请求处理函数,YAML 使用 methods: []。它与统计模块都需发布以支持内部导入,但不作为 HTTP 接口开放;对外玩家接口为 cloudRise/index,运营接口为校验管理密钥的 cloudRise/admin。
stats 只导出 LEVEL_STATS,没有默认请求处理函数,YAML 使用 methods: [] 关闭 HTTP 调用;仍需发布该模块供 @/cloudRise/stats 内部导入。生成器不会再生成对外处理函数,--check 校验导出与数据内容并允许格式化。若线上已发布旧版本,需要同步更新其 HTTP 方法配置。
共用的 login、userCoin、jungleTreasure 保留在原目录,继续使用原有登录、金币保存与丛林宝藏结算逻辑。数据库集合和存档格式不因本次目录整理而改变。此次仅修改本地文件,没有部署或删除线上旧接口。
云函数与数据
匹配关卡来源
客户端在 prepare_match/start/start_stage 中直接携带本地 GM_INFO.level,字段名为 levelAmount(非负安全整数,支持表单数字字符串)。服务端使用 levelAmount + 1 作为该阶段 start_level 和对手抽样中心;不修改 users.levelAmount,也不要求先调用 userLevel 上传进度。玩家进度不受对手样本起点 1960 的限制。 省略字段的旧客户端或旧离线队列仍回退到数据库进度;显式非法值返回“当前关卡数无效”。同一 matchId 重试及 confirm_match 使用已保存草稿,不会重新抽样;取消后使用新 matchId 可按最新本地进度重新匹配。活动资格、通关和奖励规则保持原状。 发布顺序:先更新 cloudRise/index 玩家接口,再发布配套客户端,避免旧服务端忽略新字段。
对手数据与精简归档
- 正式对手只存于 users.cloudRiseState.run.stages,各阶段各保存自己的 99 个对手。第三阶段时整轮合计 297 条,但单个阶段始终只有 99 条;仍保留历史阶段名单用于结算展示和恢复。
- pendingMatch.run 保留轮次元数据,stages 只含本次待确认阶段,不复制此前阶段。确认时将新阶段追加到最新正式 run,保留匹配期间到账的奖励记录;取消仅删除草稿。旧格式草稿在下次成功请求时自动精简,不重新匹配。
- 新写入 cloudRiseRuns 的终局归档不再保存各阶段 opponents,其余进度、奖池、存活人数、奖励回执和结果记录保留。users 中的正式存档不受归档精简影响,cloudRiseSamples 保持原状。
- 更新 cloudRise/index 与 cloudRise/admin 后,可通过管理员接口 /cloudRise/admin 执行历史归档精简:传 action=compact_archives、adminToken(ADMIN_TOKEN)、limit(默认 100,最多 500),后续请求将返回的 nextCursor 作为 afterId,直至 nextCursor=null。失败可原参数重试,也可从头重跑;仅移除终局归档的 stages[].opponents,不删除整条归档、不改玩家金币或正式存档。此操作会永久移除旧归档内的对手副本,如需历史名单审计应先备份。
- 本次仅提供代码与管理入口,不自动执行数据库清理;新归档无需迁移,旧客户端接口响应结构保持兼容。
可取消匹配
匹配采用两阶段提交,先发布更新后的 cloudRise/index,再发布客户端。原 start/start_stage 保留供旧客户端使用;新客户端不再通过这两个操作进入匹配。
prepare_match:传matchId、stage、levelAmount(前端已通过的主线关卡数);第一阶段传periodId,后续传runId。仅在users.cloudRiseState.pendingMatch保存待匹配名单,响应data.matching = { id, stage }。不占用playedPeriods,不改动当前 run,不产生失败样本。cancel_match:传matchId。删除对应草稿,并记录取消标识,防止在途准备请求通过并发重试恢复已取消匹配。可以重复调用;旧标识的取消不会删除新的匹配。报名窗口结束后仍可清理草稿。confirm_match:匹配人数达到 100/100 后传matchId。检查草稿、资格和活动截止时间后正式创建/推进 run。第一阶段沿用首页请求开期时的截止时间;后续阶段保留已获奖励、进度和原截止时间。重复确认不会清空游戏进度。
玩家在达到 100/100 前取消,可在报名窗口及个人挑战有效期内重新匹配。客户端持久化取消队列处理断网和重启;未确认草稿即使未及时清理,也不会消耗参加机会。匹配阶段取消不等于已进入关卡后的放弃。
| 文件/集合 | 用途 |
|---|---|
index.ts |
玩家报名、手动开始下一阶段、开局、胜负上报与状态恢复 |
rules.ts |
三阶段状态、截断离散正态权重、样本抽取、通关模拟 |
stats.ts |
由 JSON 生成的云函数可导入数据模块 |
admin.ts |
活动期配置与索引初始化 |
data/cloudRiseLevelStats.json |
CSV 解析后的原始统计,保留 1~2000 关;模拟起点上限 1960 |
activityConfigs |
用 activityId=cloudRise、recordType=version 保存每期配置版本,config 内保存开期模板,个人活动期冻结时长、门槛和奖池 |
users.cloudRisePeriod |
首页请求创建的个人期与配置快照,独立于挑战存档 |
users.cloudRiseState |
JSON 字符串;一次资格、当前轮次、阶段快照、尝试编号、真实结算的权威记录 |
cloudRiseSamples |
每个玩家每期一条记录,samples 数组保存最多三个已结算阶段;相同成绩的不同参赛记录不去重 |
cloudRiseRuns |
整轮完成、失败或超时后的归档 |
阶段目标固定为 5/7/9,一轮时长由 durationHours 配置(默认 24 小时)。各阶段奖池通过 activityConfigs.config.pools 配置,例如 [10000, 15000, 20000],按 stage1/2/3 排列,必须是三个正安全整数。旧配置缺少该字段时沿用默认 10000/15000/20000;管理接口发布时会写入默认值。
首页请求开期时冻结全部三个奖池,报名时复制到 run.pools,各阶段的 pool 从该快照取得。新配置只影响之后新建的个人期;已有个人期继续使用原快照。旧轮次没有 pools 时,使用已存在的阶段 pool 和其余阶段的旧默认值,不套用新配置。奖励仍由客户端按阶段奖池和存活人数计算,服务端负责保存。
样本文档 _id = runId,runId 由玩家 uid(users.onlyId 的字符串)和 periodId 确定。每完成一个真实阶段就立即写入,未完成任何阶段时不创建样本文档;无需等整轮结束。新结构示例:
{
"_id": "<runId>",
"schemaVersion": 2,
"uid": "1001",
"runId": "<runId>",
"periodId": "2026-09-15",
"samples": [
{ "stage": 1, "start_level": 100, "success_num": 5, "outcome": "won", "reason": "win", "endedAt": 1789459200000 },
{ "stage": 2, "start_level": 105, "success_num": 2, "outcome": "lost", "reason": "lose", "endedAt": 1789462800000 }
]
}
根字段使用 $setOnInsert 初始化,随后通过 samples.stage != 当前阶段 的原子条件追加。每个阶段最多一条,数组最多三条;并发重试和旧快照补写不会重复追加,也不会覆盖后续阶段。不同期使用不同文档,不累计成玩家的无限历史数组。
匹配只读取 schemaVersion=2 的 samples 数组,按阶段筛选后统计 start_level + success_num 的次数;每个阶段只写一次,同一成绩来自不同玩家或不同期时仍分别计权。不再读取旧平铺样本。
超时未完成阶段只保存在轮次中,不加入抽样集合。进行中阶段超时时按 success_num + 1 结算当前轮对手,更新 round 和 survivors(不含玩家本人),供前端播放晋级到下一朵云及淘汰掉落动画;玩家通关数不增加,不生成关卡尝试或奖励。阶段已获胜但等待下一阶段时超时,保留已获胜阶段及奖励快照。成功阶段的样本在后续阶段失败或超时后仍保留。
初次配置
- 按本次发布清单发布新增云函数及对应 YAML,更新
login。相对 main,userCoin和jungleTreasure没有变化,无需重复发布。 - 设置服务端环境变量
ADMIN_TOKEN,仅用于运营配置,不下发客户端。 - 对
cloudRise/admin发 POST:{ "action": "setup_indexes", "adminToken": "<运营密钥>" }。 - 参考
cloudRise-config.example.json设置periodId/startsAt/endsAt/durationHours/unlockLevel/pools。时间均为 Unix 毫秒。 - POST
cloudRise/admin:{ "action": "save", "adminToken": "<运营密钥>", "configVersion": "cloudRiseTestV1", "enabled": true, "config": { ... } }。startsAt 必须在未来;版本标识同一活动内唯一,重复同内容可安全重试,不能覆盖已有版本。
活动参数 config 内不再使用 status/enabled;公共记录外层 status=published 用于区分发布版本,外层 enabled 为布尔值,用于控制首页请求新开期(不放在 config 内)。save 接受顶层 enabled,缺省 true,read/save 响应返回 enabled。首页开期请求按最新已生效模板创建个人期,publishedAt 保留用于发布记录和版本排序。管理接口仍按原格式接受 startsAt/endsAt,effectiveFrom 等于 startsAt;endsAt 不再决定玩家个人活动期的结束时间。
save 不再覆盖当前期,必须指定新的 configVersion 发布未来配置;同一期起止时间固定,已开启期禁止修改。read 带 periodId 返回该期最近发布版本,不传则返回当前开放期;返回中增加 configVersion。原 migrate_config 操作已停用,按公共迁移脚本迁入 activityConfigs。setup_indexes 建立带 activityId 的公共版本索引,避免两个活动同名版本互相冲突。
示例中的配置为联调数值,不会自动发布。发布新模板仍需未来生效时间及新的配置版本;已经生效的模板会持续用于首页请求开期,直到新版本生效。持有未结束个人活动期的玩家登录不会重开。若直接维护数据库,可将当前最新已生效 version 记录的外层 enabled 改为 false 暂停新开期,改回 true 后在满足冷却条件的下一次首页开期请求时恢复;管理 save 仍遵守版本不可覆盖的规则,新发布版本应明确指定 enabled。
测试环境短时活动
服务端环境变量 PAYMENT_APP_ENV=test 时,durationHours 允许正数小数小时,例如 0.1 为 6 分钟(360000 毫秒)。正式环境或未设置该变量时仍要求至少 1 个整数小时。小数时长须能表示为至少 1 个整数毫秒;0、负数、非数字和溢出值仍被拒绝。请求中的 isDebug 不控制这项放宽。
配置校验、首页请求开期和玩家活动接口使用同一规则。先发布更新后的 cloudRise/periods,再发布 cloudRise/index;管理接口调用 index 的校验,无需更改请求格式。确认测试服务器已设置上述环境变量。将模板改为 0.1 后,只有新建个人期使用 6 分钟;已有未结束个人期保留原截止时间,不会被追溯缩短。
挑战时长与旧配置迁移
durationHours 正式环境为正整数小时,测试环境可使用上述小数时长,缺省为 24。首页请求开期时保存个人起止时间;匹配后写入 run.durationHours,并将 run.expiresAt 设置为个人期 endsAt。阶段切换和模板更新不会延长倒计时;旧轮次保留原 expiresAt。
旧的 migrate_config HTTP 操作已停用。请按 公共集合迁移说明 先将旧配置转入 activityConfigs;脚本保留原 durationHours,缺省补 24,不修改玩家个人截止时间或历史轮次。
前端在未参加、挑战中、等待下一阶段显示同一张总览说明图;同一期全部完成、失败或超时隐藏首页入口,下次登录重新开期;待播放结算和奖励补存独立处理。独自获胜仍显示“你和其他 0 名玩家分享了获胜奖励”。
玩家接口
所有请求通过现有 Utils.POST 携带 uid 和登录 token,地址为 cloudRise/index。这里的 uid 为 users.onlyId(正整数或其十进制字符串),客户端取登录后的 GM_INFO.userId;服务端用户查询、CAS 更新和候选池排除均使用 onlyId,样本、归档的 uid 统一保存为其字符串。Mongo _id 不作为百人赛玩家标识,其他接口仍沿用原有账号参数。
| action | 额外字段 | 行为 |
|---|---|---|
status |
无 | 恢复状态、检查超时、重试样本归档 |
start |
periodId |
本期首次报名并匹配 stage1;重复请求恢复同一名单 |
start_stage |
runId, stage |
仅在前一阶段成功后的 waiting 状态开始后续阶段 |
begin |
runId, stage, attemptId |
登记本关首次有效操作;前一关未结算不能开始另一尝试 |
finish |
同上,加 outcome |
win、lose 或 interrupted;重复编号不重复推进;成功只记录阶段结果 |
save_reward |
runId, stage, reward, coinAmount |
保存客户端计算的奖励、加奖后的余额和阶段 rewardSaved 标记;重复上报不再次写余额 |
未找到当前开放配置且玩家没有个人轮次记录时,返回 { "code": 0, "data": null, "msg": "活动未开启" },不返回目标、奖池或其他活动数据。已有个人轮次仍可恢复状态、继续个人期限内的挑战及重试结算,不受报名关闭影响;无当前时段配置时 period 为 null,targets/pools 返回个人轮次使用的目标和奖池。
成功返回 code: 1,data 含 serverNow/available/period/targets/pools/run/coinAmount/sampleSyncPending;失败返回 code: 0 与提示文案。
未报名时 data.pools 使用当前活动配置;展示个人轮次时使用 run.pools,已结算旧轮次让位给新一期时切换至新配置。period.pools 始终对应该 period 的配置。
公开阶段信息包含进度、存活数、奖励以及对手的昵称、头像、存活状态,不返回对手未来成绩和起点。登录接口也移除了原始 cloudRiseState,金币恢复沿用原有登录流程。
start_stage 必须由活动页面按钮触发。成功结算、关闭弹窗、返回首页、继续主线和状态查询都不会隐式开始下一阶段。
阶段等待期间的主线通关不计入下一阶段;点击按钮前客户端先同步主线关卡,服务端用当前 levelAmount + 1 确定起点。
前端发奖与服务端保存
当前版本遵循客户端发奖方案,后续再统一迁移到服务端发奖。阶段成功时服务端仅保存进度、存活人数和成功状态,不计算奖励、不增加金币。
客户端 CloudRiseRuntime 根据 Math.ceil(stage.pool / stage.survivors) 算出奖励,上报奖励金额及 当前客户端金币 + 奖励。服务端验证账号、阶段成功状态和整数格式,通过同一次用户文档更新保存 coinAmount、stage.reward 和 stage.rewardSaved = true,不进行余额加法或重算奖励金额。同一阶段已保存时直接返回现有状态,不覆盖之后的金币余额。
前端收到保存成功后把奖励加到本地金币并保存本地余额;请求期间有普通金币变化时,再走原有 userCoin 保存最新余额。保存失败时显示待到账并重试;同一会话响应丢失时保留待确认记录,重试成功只加一次。重新登录沿用已有的金币恢复流程,服务端 rewardSaved 标记阻止再次领取。个人期限已过但之前成功阶段的奖励尚未保存时,仍允许补存,并在补存完成前阻止替换旧轮次。
每次活动修改对 users.cloudRiseState 的完整旧值进行 CAS,首次写入用 $exists: false;冲突最多重试 5 次。终局归档等待所有成功阶段的奖励已保存,确保归档包含上报金额和保存标记。
userCoin 和 jungleTreasure 已恢复原有行为;不再使用 cloudRiseCoinTotal 或累计奖励补差,也不为百人赛改写通用金币存档与丛林结算规则。跨接口旧余额覆盖问题随未来统一服务端发奖方案处理,本版不实现跨活动余额保护。
样本或归档写入失败时,用户活动记录作为补写来源,响应标记 sampleSyncPending;后续活动请求继续重试,没有独立定时扫描任务。
匹配与兜底
- 资料候选池:
users.taskTime大于 0 且早于当前时间减三个日历月,排除玩家本人,随机取最多 500 条。月底日期向目标月月末收敛。 - 有候选资料但不足 99 条时可重复使用;头像昵称抽取与成绩抽取独立。头像保留
useravatarIcon字符串,兼容icon_9和远程头像。 - 候选池为空时返回匹配失败,首次报名不消耗资格;已生成名单永不通过重试重抽。
- 起点权重为进入人数乘离散正态权重,先在正整数上确定截断分布标准差,再对 1~1960 范围的乘积归一化。
- 有真实样本时按样本次数抽取;没有样本时逐关 Bernoulli 模拟,首次失败结束。
- 缺失通关率取最近较低的有效关卡通关率;当前 1921 及以上使用 1920 关的 0.818。原始 JSON 的缺失值保持 null。
本地验证
node laf-cloud/functions/cloudRise/tools/generate-cloud-rise-stats.mjs --check
node --test laf-cloud/functions/cloudRise/tests/cloud-rise.test.mjs
测试使用隔离的内存数据库替身,执行真实活动处理函数、抽样规则、币额更新与 CAS 冲突;其中一项直接加载相邻 MatchMaster 仓库的实际客户端服务进行联调,需要该仓库的 TypeScript 依赖。
覆盖每玩家每期样本合并、并发追加、补写失败恢复、数组样本按阶段统计、整轮三阶段、手动推进、每期一次、超时、重复结算、零分与重复样本、资料池空、样本写入失败、客户端计算上报、并发重复保存、过期补存和前后端联调。
本地预览和测试均不连接生产账号,不会自动开放或部署活动。
新旧活动期切换
当前开放配置与玩家个人轮次分别判断:旧轮次仍在挑战中,或有成功阶段奖励待保存时,继续返回旧 run,禁止同时报名新一期。旧轮次已完成、失败或超时,且奖励均已保存时,如果当前开放 periodId 不同,接口返回新一期 period、run: null 和对应 available,不再把旧结算当作当前活动。旧 run 仍保存在 users.cloudRiseState 供重试,并归档到 cloudRiseRuns;成功报名新一期后才替换存档,playedPeriods 保留。新一期首次 start 请求也可以先结算旧轮次超时,再创建新轮次。每一期必须使用不同 periodId。
换期响应的 previousRun 是已结算旧轮次的恢复记录,仅用于确认奖励保存重试(例如保存成功但客户端丢失响应),不用于当前页面和自动结算弹窗。客户端需先完成该恢复,再报名新一期;不能仅因 run 为 null 就丢弃待确认的奖励。
奖励保存参数兼容客户端 application/x-www-form-urlencoded:reward、coinAmount 可传十进制整数字符串或 JSON 数值。服务端先严格转换,再检查安全整数、正奖励及余额不小于奖励,并按数值类型持久化;空值、布尔值、数组、小数和越界整数不会被宽松转换。联调测试执行实际 Utils.POST 编码,覆盖表单上报、重复保存和丢失响应恢复。
unlockLevel 表示必须已通过的主线关卡数。例如设为 110,users.levelAmount >= 110 才可首次报名;客户端使用对应的 GM_INFO.level,未达到时首页点击提示“月光宝盒活动通过第110关后解锁”,不打开活动弹窗。已报名轮次的继续挑战和结算不受后续门槛调整影响。
百人赛数数埋点
埋点由后端生成并发送至数数,不在客户端重复上报,也不向客户端暴露机器人的预测终点人数。先发布内部模块 cloudRise/analytics(methods: []),再发布 cloudRise/index;使用服务器已有 thinkingdata-node 依赖。
| 事件 | 触发时机 | 属性 |
|---|---|---|
| cloud_rise_start | confirm_match 确认匹配;兼容 start/start_stage | stage: int 1–3;end_count: int,99 个机器人中 success_num >= target 的数量,不含玩家 |
| cloud_rise_step | 每个真实关卡结果成功保存 | stage: int;step: int 1–5/7/9;result: success/failure |
| cloud_rise_fail | 阶段首次失败 | stage: int;step: int,失败所在步骤 |
| cloud_rise_succeed | 阶段胜利且 save_reward 首次保存奖励回执 | stage: int;coin_amount: int,实际保存的阶段奖励,不是账户总余额 |
- 匹配草稿与取消匹配不发 start;begin 不发 step;重复请求、状态轮询不生成新事件。放弃、重试、关卡倒计时失败及断线中断均按已保存的失败结果发送 step(failure)+fail。个人活动期限到期仅发送 fail,不虚构一次关卡完成;已获胜阶段等待下阶段时到期,不补发该获胜阶段的 fail。
- 环境沿用现有客户端协议:isDebug=true 或字符串 true 实际代表正式版,AppID=95993f9ab6f1402a87abe5147827e5e0;false 代表测试版,AppID=40e3d5c5f2af49a4a074205564ce5dbb。请求省略该字段时回退到用户保存的 isDebug。待发送事件固定原 AppID,重试不切换项目。
- 事件随 gameplay 状态原子保存到 users.cloudRiseState.analyticsQueue;上报成功后通过 CAS 清除。上报失败或超时不影响活动结果,后续成功调用活动接口时重试。队列只保存小型事件属性,不复制对手列表;没有 openid/distinctId 时保留待报,不伪造用户身份。
- 发送采用 trackFirst,firstCheckId 由轮次、阶段、匹配批次、事件及尝试编号确定,防止并发和回包丢失造成重复计数。没有定时补报任务,玩家不再调用接口时待报事件会继续保留。
- SDK 显式 flush,等待最多 1 秒;数数后台首次事件去重处理可能延迟入库,详见 数数首次事件说明。本地测试使用发送替身,不向真实项目发送测试事件。
HomeScene 显式开期与 12 小时冷却
- 登录和
userLevel/save不再创建活动期;活动列表、状态查询也不创建活动。 - 进入 HomeScene(或首页回到前台)时刷新
activityConfig/list,用返回的openingRules检查:GM_INFO.level >= unlockLevel、无已开启个人期、距上期结束严格超过 12 小时,且配置enabled开启。没有历史期时不需要等 12 小时。 - 条件满足后 POST
cloudRise/index:{uid, token, action:"open_period", levelAmount}。levelAmount 可省略,后端不读取也不记录;关卡门槛和 12 小时间隔仅由首页判断。后端保留账号鉴权和有效启用模板的读取,只检查是否已有未结束个人期,并用 CAS 防止并发重复开期。重复请求返回同一期,不重置倒计时。 - 成功响应
{code:1,data:{serverNow,period}},period 包含 configVersion、periodId、startsAt、endsAt、durationHours、unlockLevel 和 pools;倒计时从本次请求成功创建个人期时开始。客户端接收成功回执后更新缓存、显示入口,玩家仍需点击开始才匹配。 - 失败/完成第三阶段以最终阶段 endedAt 为上期结束时间;自然超时取原 endsAt/expiresAt,不采用延迟状态同步时写入的 endedAt。已结束旧数据缺少阶段结束时间时保守使用原截止时间。
- 首页普通轮询只读,不会在停留期间自行续开;开期失败、断网或离开首页不会乐观显示入口,下次进入首页可以重试。活动规则按服务器校准时间判断,账号切换后的迟到响应会丢弃。
- 更新已有测试环境:先移除
login和userLevel的旧自动开期调用,再发布cloudRise/periods,然后cloudRise/index、activityConfig/list,最后使用新版客户端。保留旧个人活动期及奖励存档,无须迁移集合。
开期成功后的 status/start/prepare_match/confirm_match 不再使用 users.levelAmount 重复检查解锁条件,避免数据库关卡滞后导致 available=false。报名次数、阶段状态、奖励保存等原有规则保持不变。