只保存最高分,不保存每一局

当前版本的数据表以微信登录返回的 openid 作为主键,每个用户只有一行记录,字段包括昵称、最高分和达成最高分的时间。交分时执行的是“插入或更新”:只有新分数严格高于旧最高分时,才会更新分数和时间;同分或更低分不会产生写入。

这意味着排行榜展示的是每个人历史最好的一局,而不是最近一局。你后来打出更低的分数,不会把榜上的成绩拉下来;只有真正刷新纪录,才会替换。

一次交分会发生什么
情况数据库行为结果
第一次交分插入一行建立最高分记录
新分数更高更新最高分和时间榜上成绩替换
新分数相同或更低不写入保留原最高分
请求失败本地不更新下次超过本地最高分时还能再报

客户端先判断,服务端再判断一次

客户端只在“本局分数超过本地保存的最高分”时才发起上报。上报成功后,服务端返回确认的最高分,客户端再把它写回本地;如果请求失败,本地不会提前改分,所以下一局仍有机会重试。服务端并不信任客户端传来的“这是新纪录”,而是用自己的旧值再比较一次,只有更高才写库。

这层重复判断的意义是防止两种情况:一是客户端本地记录过期,二是有人直接调用接口提交低分或重复分。它不能替代完整的反作弊系统,但至少保证“榜上不会因为一次低分请求而倒退”。

榜单为什么是 6 行

服务端把列表上限固定为 6,查询按“最高分从高到低、达成时间从早到晚、openid 从小到大”排序。前两个条件决定名次,第三个条件只是让同分同时间的顺序保持稳定,避免每次刷新时同样两行来回换位置。

同分采用并列名次:如果前两名的分数相同,它们都显示第 1 名;下一名从第 3 名开始,而不是第 2 名。这是竞赛排名(1、1、3),不是密集排名(1、1、2)。榜上看到两个第 1 名时,不必怀疑显示错误。

如果你不在前 6 行,服务端仍会单独算出你的名次和最高分,放在“我的成绩”里返回;榜单主体不会因此扩成 7 行或更多。也就是说,“固定 6 行”说的是公开列表,不是“只认前 6 个人”。

昵称是怎么来的

榜上的昵称不是微信昵称,也不是用户自己输入的名字。服务端把 openid 做 SHA-256,取十六进制结果的前 8 位,拼成“奶蛙”加 8 位字符。同一个 openid 每次都会得到同一个昵称,所以成绩能稳定对应到同一个人;但昵称本身不包含 openid 原文。

这仍然是一种可关联的标识:如果同一个人多次出现,别人能看出这些成绩属于同一个昵称。它不是完全匿名,只是避免直接展示微信身份标识。排行榜公开的内容只有昵称和最高分,列表接口不返回 openid。

登录 token 和 7 天有效期

小程序通过微信登录拿到 openid 后,服务端签发一个带过期时间的 token,有效期是 7 天。客户端把 token 存在本地,之后交分和查榜都带上它;token 过期或签名不对时,接口按未登录处理,客户端会清掉缓存并重新登录一次。

登录本身不写数据库,只有第一次交分时才创建玩家记录。这样单纯打开游戏、看看榜单,不会先留下一条空记录。

想删除或更正自己的记录

如果你希望删除排行榜里的记录,可以通过联系页面说明来意。由于榜上没有公开 openid,运营者需要能定位到对应记录的信息,例如你的微信小程序身份、榜上昵称和大致成绩。删除后,该昵称和最高分不会再出现在公开列表中;再次游玩并交分时会重新建立记录。

如果你只是不想公开成绩,最直接的方式是停止交分或不再打开排行榜,而不是期待一个“隐藏但仍保留”的开关——当前版本的公开列表没有提供这种状态。涉及数据范围、保存期限和删除流程的正式说明,以隐私政策为准。

本文依据当前版本的 web/worker/api.js、web/db/schema.sql 和客户端排行榜逻辑整理。名次、昵称和留存行为可能随版本调整;实际以线上接口和隐私政策为准。