Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-等级榜读取与本人统计键接口设计-待完整审核-2026-09-30.md
Liubing\LB 6813da149a feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。
保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。

验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。
包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
2026-09-30 19:19:05 +08:00

130 lines
10 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-30。依据用户本轮授权:等级榜读取接口可由本端设计;本人LevelB可以存KV。本文给出供服务端实现、客户端接线的拟合同,**不宣称线上已有此路由,也不构成编码批准**。与好友接口设计合并进入现有C3c完整审核,不逐字段另开审核。服务器代码由服务端开发修改。
## 1. 两个名称究竟指什么
- **本人统计键**:每个玩家自己的KV里,存原排行榜统计分数的键名。不是新UserID,也不是整张排行榜。
- **等级榜读取入口**:向服务器请求全球/地区排名列表的HTTP接口。服务器在内部读取各玩家的对应KV统计,返回玩家ID、分数、排名与公开资料。客户端不逐个读取他人的私有KV。
原代码均在`Client/Assets/Scripts/DataCenter/DataCenter.LeadboardData.cs`:158行SetLeadboardData写统计;171行GetLeadboardData读榜;189行SetLvData计算分数;220行GetLevelLeadboardData消费榜单。原来分别调用UpdatePlayerStatisticsAsync与GetLeaderboardAsync,这两个KV分支需要替换,原PF分支保留。
## 2. 本人统计键:沿用原统计名称
| 本人KV键 | 含义 | 示例值(KV字符串) |
|---|---|---|
| LevelB | 全球等级榜分数,也是好友资料中的LevelB来源 | "1200034" |
| ASLevelB | 亚洲等级榜分数;AS是当前玩家ContinentCode | "1200034" |
| AFLevelB / ANLevelB / EULevelB / NALevelB / OCLevelB / SALevelB | 其他有效地区对应的统计名 | 与该次全球分数相同 |
正常更新只写全球键和本人当前地区键,不写全部地区键,不删除原来可能存在的其他地区统计,不为账号增加新ID。
原公式:`LevelB = lv * 100000 + InfiniteBuildingLevelInt`。等级12、建筑34就是1200034。建筑字符串TryParse失败沿原值0;建筑值>=100000沿原上限99999;等级沿原地图配置上限;不增加负值钳制或其他业务修正。UI仍除以100000显示等级、取模显示建筑等级。
本人写入的线格式示例(合成说明,不是要求写101/102正式档):
```json
{"data":{"LevelB":"1200034","ASLevelB":"1200034"}}
```
沿现有`POST /playerdata/Set`本人认证合同,值为InvariantCulture十进制Int32字符串。**实际客户端调用仍经既有KV管理器/固定存档句柄**;此示例不授权绕过存档层从CustomServerMgr调用playerdata/Set,不添加目标账号参数。读本人时仍使用playerdata/Get的keys。
地区来源只用KVUserService已校验的ContinentCode(AF/AN/AS/EU/NA/OC/SA)。地区缺失沿已审未知地区边界:保留全球能力,不杜撰地区,不把空前缀当第二个全球统计或将全球榜冒充地区榜。具体UI保持既有裁决,不增加新开关。此条不修改PF分支。
SetLvData、AwaitSetLvData原调用点、300秒节奏、async void、isRefreshing赋值顺序及原已知网络失败记日志后完成的语义保持。禁止复活已撤回的“临时副本+通用Flush刷新所有待写项”方案;本人两键更新的具体存档层调用及完成/失败边界必须在C3c完整方案中落到方法后才能实施。一次请求不等于已证实事务,不声称两键原子性。
## 3. 读取入口:拟新增POST /leaderboard/GetLeaderboard
固定基址`http://s101.local:18080/`。使用当前登录会话的认证Cookie;不接收调用者提供的本人ID,不接受目标玩家ID数组。
```json
{"statisticName":"LevelB","startPosition":0,"maxResultsCount":50}
```
| 请求字段 | 合同 |
|---|---|
| statisticName | 必填;LevelB或上表七个地区统计键,区分大小写;不开放任意私有KV键查询 |
| startPosition | 必填非负整数,0表示第一名;现有客户端固定0 |
| maxResultsCount | 必填整数,拟支持1–100;现有客户端默认50,不增加翻页UI |
地区请求仅将statisticName换为例如ASLevelB;不另传region形成两个地区来源。未知统计名或无效范围返回HTTP400,不能偷偷切换全球榜或改请求数。
选用独立等级榜路由,是因为原客户端已有的`event/GetLeaderboards`属于活动榜,分数、奖励及返回语义不同。新接口复用已有CustomServerPost传输,已核正式租约接受该规范相对路径;不需要增加HTTP客户端、账号会话、Provider或管理器。调用传相对路径`leaderboard/GetLeaderboard`,不带前导斜杠。
## 4. 成功返回与原结果映射
```json
{
"code": 1,
"msg": "",
"leaderboard": [
{
"playFabId": "102",
"position": 0,
"statValue": 1200034,
"profile": {
"displayName": "Angler#102",
"avatarUrl": "",
"lastLogin": "2026-09-30T01:00:00.0000000Z"
}
}
]
}
```
示例仅说明格式。字段playFabId暂保留兼容名称,**值必须是新UserID(登录PlayerId)**。服务器不必引用PlayFab SDK。
| 返回字段 | 原客户端映射及来源 |
|---|---|
| leaderboard | 必须为数组;空榜成功为[],不以null冒充空榜 |
| playFabId | PlayerLeaderboardEntry.PlayFabId;目标玩家新UserID字符串 |
| position | PlayerLeaderboardEntry.Position;全榜从0起的位置,非每页重新从0计算;UI仍Position+1 |
| statValue | PlayerLeaderboardEntry.StatValue;所选统计键的Int32分数,不是普通等级 |
| profile | 必须为对象;映射PlayerLeaderboardEntry.Profile,原消费者会直接访问 |
| profile.displayName | 该玩家KV的DisplayName,缺键null、空串保留;沿原客户端默认名,不在服务器虚构 |
| profile.avatarUrl | 该玩家KV的AvatarUrl,缺键null |
| profile.lastLogin | 该玩家KV的LastLoginTimeUtc,缺键null;UTC往返格式O,阶段时点边界沿好友接口设计 |
profile内上述三个字段必须出现,可为null。不能把缺整个profile字段解释成三个字段都缺。LastLogin缺键不新增过滤;不使用请求时间伪造登录时间。旧GetLeaderboardRequest虽然要求ShowLocations,但此方法返回仅由GetLevelLeadboardData消费,已核其及LeadboardItemData均不读取Locations;本合同不为未消费字段增加新的地理资料来源,也不修改任何原PF请求约束。
## 5. 服务端聚合与错误边界
1. 按请求的统计键取得各玩家**已保存的统计值**,返回排名切片;地区榜按对应地区统计键读取,不在查询时按当前地区再筛选或重新算lv/建筑,以免更改原历史统计含义。读取不写KV、不更新登录时间、不变好友关系。
2. 拟按分数降序,高复合分数在前。原客户端把排名交给PF,源码不能证明PF后台统计的同分次序、更新聚合方式(覆盖/最大值等)和重置配置。因此本方案**不另定同分按ID、最大值覆盖或新周期重置规则**;服务端实施时须对照原统计配置/既有证据保持一致,并作为同一完整审核和联调的核验项。仅键名对齐不能宣称统计语义已完全等价。
3. 正常读取确认没有该统计键的玩家不产生该榜条目,不把缺键变成0分或普通Level=1。整个榜无记录或起始位置超出结果,成功返回[]。
4. 已存在的统计值格式非法、读取失败或聚合失败,本次请求失败,不静默删掉玩家、不返回假空榜或不完整成功榜。拟HTTP500,code=0、leaderboard=null,msg分别为KV_STAT_DATA_INVALID / KV_LEADERBOARD_READ_FAILED。公开资料读取失败也不能填空值伪装“缺键”;存在但损坏的时间按数据错误处理。
5. 401/403沿现有认证失效处理。HTTP200但code非1、必填结构缺失或非法时,客户端结果映射记错并返回null,沿原SDK已知失败语义;不回退PF、不加入重试或新UI。
6. 本人更新成功后的查询须能反映该统计更新,不能把旧普通level缓存当统计值。若服务端使用索引或缓存,应交付实际可见性与原统计更新语义的比较证据;本文不要求每次全表扫描,也不为客户端新增轮询或等待。
无数据成功示例:
```json
{"code":1,"msg":"","leaderboard":[]}
```
失败示例(HTTP500):
```json
{"code":0,"msg":"KV_LEADERBOARD_READ_FAILED","leaderboard":null}
```
## 6. 客户端候选范围与验收
生产候选仍收敛到DataCenter.LeadboardData.cs:SetLeadboardData按原两统计名写本人KV,GetLeadboardData在KV分支调用新路由并映射原GetLeaderboardResult;必要存档注入在同文件。复用现有JSON与结果类型,不新增泛化层。若现有IPlayerSaveStore不能表达原两键完成语义,先把实际差异补入同一C3c白名单,不私自扩层。
GetLevelLeadboardData、SocialLeadboardItem及各业务触发点保持原流程:默认名、SetPlayInfo、缓存赋值、PFRankDataEvent顺序不变;原空榜不刷新缓存/不发事件的条件也保留,不顺带修复。等级榜与活动榜分开验证,原PF/AGG仍走原路径。
审核后测试至少覆盖:
- 两个统计键与同值序列化、原分数计算上下限与解析失败;全球/七地区/未知地区;不冲刷无关待写项。
- 0条、1条、50条、超过50条及分页位置;分数与名次映射;同分与原配置一致;缺键、非法统计、服务失败和401/403。
- 名字/头像/时间缺键及协议缺字段,默认名与原错误路径;空榜保留原UI行为。
- 本人写后服务器聚合读取、好友LevelB投影读取指向同一键;101/102真实联调不改等级/奖励造数据,隔离夹具与正式证据分开记录。
- KV分支相关PlayFabClientAsyncAPI调用次数为0;原PF对照、活动榜无回归。真实Unity验证只用CLI Loop,不打包。
## 7. 本检查点结论
本人键名、值格式和等级榜路由/请求/响应已形成可评审的具体设计,不再要求用户解释“什么叫入口/统计键”或重选KV存储方向。与[好友KV资料接口设计](M2-好友KV资料服务端接口设计-待完整审核-2026-09-30.md)一并纳入C3c;新路由部署、原PF统计配置对齐、具体两键写调用和完整编码白名单尚不因此自动获批。其他公开资料、公告、匹配的剩余合同仍在原批次内,不因本设计完成而销账。
本轮只读客户端与既有审核证据、更新文档;没有修改客户端/测试/服务器源码,没有正式网络请求。原工作树保留。正式地址、审核后编码、全部通关前恢复临时冻结及最终只清理本次造成的无消费者实现等约束保持。