Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-好友KV资料服务端接口设计-待完整审核-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

120 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.
# 好友KV资料服务端接口设计(待完整审核)
2026-09-30。用户授权:好友数据可以从服务器取得,客户端侧可设计接口,这部分由服务端开发修改。本文件是**拟实施合同**,不是当前线上已有字段的说明。客户端Agent不修改服务器;所有相关内容完整审核通过后才编码。
## 1. 方案选择与范围
| 方案 | 影响 | 选择 |
|---|---|---|
| 扩展原friend/GetFriendsList,随好友一次返回KV公开资料 | 保留已有路由、认证和friends字符串数组,新增一个资料字段即可承接原Profile语义 | 采用 |
| 新增独立批量KV公开资料路由 | 增加一次请求和关系列表/资料的组合步骤,改变原一次取得好友资料的链路 | 本次不采用 |
| 客户端逐个读好友私有KV | 已被隔离合同与实测否定 | 禁止 |
本次设计只覆盖**好友列表中的KV资料**,落实LevelB及LastLogin的服务器读取;不将设计范围扩成新的泛化资料管理器。SearchFriend公开资料、等级榜、公告、匹配仍在同一C3c各自范围,不能因本接口定稿声称它们都已通过。
## 2. 请求合同
```http
POST /friend/GetFriendsList
Content-Type: application/json
Cookie: 当前登录会话(沿现有认证)
{}
```
- 服务器从认证会话取得本人UserID,沿原好友关系来源获取本人好友列表。
- 请求不接受用于选择本人账号的playerId/playFabId/userId,不接受任意目标ID列表;客户端不提供好友私有KV访问凭据。
- 只投影该原好友集合允许公开的资料,不返回原始NewPlayerDataDic、完整KV或认证数据。
- 不改原好友关系建立、分页/数量边界和关系结果顺序,不新增好友增删动作。一次客户端请求完成关系和资料取得;服务端可用已有内部能力批量读取,不能回调客户端逐好友取档。
## 3. 返回合同
外层沿原`code/msg/friends`;成功仍`code=1`,friends仍为**JSON字符串数组**。每个字符串解码后,原PlayFabId/DisplayName/AvatarUrl/Level四字段保留原兼容含义,不改名、不把Level塞入复合分数。本次增加`Profile`字段,供迁移后的原PF好友消费者使用。
无好友成功:
```json
{"code":1,"msg":"","friends":[]}
```
一名好友的成功示例(合成说明,不是101/102真实数据):
```json
{
"code": 1,
"msg": "",
"friends": [
"{\"PlayFabId\":\"102\",\"DisplayName\":\"Angler#102\",\"AvatarUrl\":\"\",\"Level\":12,\"Profile\":{\"DisplayName\":\"Angler#102\",\"AvatarUrl\":\"\",\"Statistics\":[{\"Name\":\"LevelB\",\"Value\":1200034}],\"LastLogin\":\"2026-09-30T01:00:00.0000000Z\"}}"
]
}
```
新增Profile是普通业务DTO,不要求服务器引用PlayFab SDK。包含下列字段:
| 字段 | JSON类型 | 值的来源与规则 |
|---|---|---|
| Profile | object或null,字段必须出现 | 可正常取得目标KV公开资料时为对象;已明确目标资料实体不存在时为null。读取超时/服务失败不能伪装null |
| Profile.DisplayName | string或null | 对应好友KV的DisplayName;缺键为null,空串保留。客户端沿原GetDefaultName规则处理空昵称,不额外写回KV |
| Profile.AvatarUrl | string或null | 对应好友KV的AvatarUrl;缺键为null,不填虚构头像 |
| Profile.Statistics | array或null,字段必须出现 | 当前只投影LevelB。正常完成KV读取后生成数组;无LevelB键则为[]。null仅表示明确不存在统计资料集合,不用于掩盖读取失败 |
| Statistics[].Name | string | 本次固定LevelB,不将地区统计混入好友全局分数 |
| Statistics[].Value | JSON整数(Int32范围) | 从该好友KV的LevelB十进制整数字符串解析;不从普通Level/建筑重新计算,不更改原负值等规则 |
| Profile.LastLogin | UTC时间字符串或null,字段必须出现 | 从该好友KV的LastLoginTimeUtc读取,保留真实时刻并以UTC往返格式O返回;缺键为null,不用查询时间或DB时间静默回填 |
外层四字段用于原接口消费者兼容,新增Profile明确承接KV迁移后的资料来源;客户端本次原PF好友映射读取Profile里的名字/头像/统计/时间,身份取外层PlayFabId。外层Level仍是普通等级,不在本接口改造中顺便修其他消费者或将其当GetPlayLv真实等级的证明。重复的名字/头像字段是保留现有wire兼容所需,不新增第二个持久化副本;服务器不为Profile另存缓存真值。
### 为什么保留Profile/Statistics两层
原FriendService分别判断Profile==null、Statistics==null,并且“统计集合存在但没有LevelB”会保留好友、分数初始化为0。只加两个nullable平铺字段无法同时表达这些原行为。新增这一资料DTO只服务于FriendService一个实际调用点,保留原形状,可复用现有PF结果模型在客户端内部映射;不新增Provider/管理器或运行开关。
## 4. 缺值、失败和旧客户端兼容
| 条件 | 服务端拟合同 | 客户端原语义 |
|---|---|---|
| 正常有效资料 | Profile对象、Statistics含真实LevelB、真实时间 | 原过滤/降序/名次/事件流程 |
| 目标资料实体确认不存在 | 保留关系项,Profile=null | 沿原Profile==null跳过,不由服务端偷偷删除好友关系 |
| 统计集合明确不存在 | Profile.Statistics=null | 沿原Statistics==null跳过 |
| 有效KV读取完成但没有LevelB键 | Statistics=[] | 沿原查找循环保留value=0;这是原逻辑,不新增Level=1或Level×100000兜底 |
| 没有LastLoginTimeUtc键 | LastLogin=null,好友不因时间缺失被过滤 | 保留原Nullable值;伙伴邀请原.Value异常风险仍存在,不能声称此账号已通关 |
| 有键但LevelB/时间格式损坏,或某好友KV读取失败 | 本次请求失败;建议HTTP500,外层code=0、friends=null、msg为非敏感错误类别 | 按原SDK请求失败记错/返回null,不发布伪空列表;不返回部分成功掩盖无法判定的数据 |
| 原关系服务的业务失败 | 保留原失败HTTP/业务码,不改成code=1 | 客户端成功条件必须code=1;其他结果走原失败映射 |
| 认证失效 | 原401/403认证处理保持 | 沿原CustomServerMgr会话处理,不改登录流程 |
| 旧服务端只返回四字段,没有Profile字段 | 这表示新合同尚未部署,不能解码成“Profile=null”假装成功 | 映射前检查字段存在性,记录协议不匹配;不把整张好友表静默清空 |
新失败响应示例:`{"code":0,"msg":"KV_PROFILE_READ_FAILED","friends":null}`,配HTTP500;格式损坏类别为KV_PROFILE_DATA_INVALID。此为待审设计,不宣称线上已有该错误码。沿原失败语义处理,不增加客户端重试或新的用户业务流程。
旧客户端忽略新增Profile;原四字段类型和friends字符串数组不变。服务端在上线前需验证真实旧调用者能忽略新增字段。不得通过新增客户端多重开关兼容不完整部署,也不复活开发冻结作为产品行为。
## 5. 数据生成、缓存与时间边界
- LevelB仍由本人客户端在原SetLvData触发点计算和写本人KV;服务端读取已保存统计,不在好友请求里按当前等级重算。全球/地区统计写合同另与聚合侧对齐。
- LastLoginTimeUtc已由本人客户端每日初始化后按现有流程保存。本接口只读取、不更新该键;本人的旧时间仍先用于每日刷新。
- 此阶段遵循用户先存KV的方向;返回语义明确为该键保存的登录时刻,不声称它覆盖所有“认证成功但首场景尚未完成”的登录。这个与PF元数据更新时点的阶段差异须由同一次C3c完整审查明确记录,不擅自改本人写入时点消除差异。
- 当前101/102缺LevelB和LastLoginTimeUtc,不能把它们当“正常完整资料”验收样例。字段缺失测试可以使用;正常路径须在已审本人写入完成后再联调验证,测试夹具不得冒充正式结果。
- 新增Profile不以原四字段资料缓存替代KV读取;本次设计不新增缓存层。若服务器复用现有缓存,必须保持与已确认KV写入的可见性一致,并在联调报告给出实际结果,不能仅返回旧缓存的level=1推导LevelB。
## 6. 客户端接线候选(审核通过后执行)
仅`FriendService.cs.GetFriendList`的数据获取前段及必要的`FriendModel.cs`响应DTO:
1. KV用户调用原FriendRequest("GetFriendsList", 空对象);PF/AGG原SDK分支保持。
2. 校验外层成功、friends类型及每个Profile字段是否存在,逐项解析原JSON字符串;禁止把不兼容响应当空好友成功。
3. 映射外层PlayFabId到FriendPlayFabId,Profile各字段到原PlayerProfileModel/Statistics;不调用PF SDK获取缺失值。
4. 随后沿原FriendService消费段:两项null跳过、自身排除、默认名、统计查找、SetPlayInfo、排序、名次、事件和AGG计数。async void、错误处理和UI消费者不改。
不修改AddFriend/AcceptFriend/DeleteFriend/SearchFriend普通请求,不删除这些业务接口中的目标ID。GetPlayInfo/GetPlayLv完整远端替代仍需对应资料接口合同,本次不以局部缓存修复先行编码。
## 7. 服务端交付与联合验收
服务端开发负责当前线上仓库的关系查询、内部按好友UserID读取KV、上述公开字段投影、错误响应及部署。客户端Agent只提供设计与后续客户端接线,不修改D:/ft/server/N3-Server或远程服务端代码。
请服务端开发及审核侧一次核对本拟合同,返回实际实现版本及以下脱敏样例:
1. 两个有效好友、无好友、Profile不存在、Statistics=null、LevelB缺键、时间缺键;旧关系和顺序保留。
2. LevelB=1200034在原UI解码12/34;不同值按原客户端规则排序,服务端不另分配好友名次。
3. 字符串LevelB和UTC时间损坏、单项KV超时、关系查询失败、401/403,不返回假空列表或部分成功。
4. 本人KV写确认后好友查询返回该统计和时间;101/102互查目标身份正确。测试不改变正式等级/奖励,只在已审授权范围使用真实流程或隔离夹具。
5. 原四字段消费者兼容、迁移后的真实FriendService/伙伴邀请消费者、KV分支SDK计数0、原PF对照。
本方案的待审差异明确为:新增Profile公开DTO、缺值映射、请求级错误映射、时间阶段语义。用户授权设计不等于已审核通过;纳入同一C3c完整评审,不能局部开工。本轮仅文档,无服务端或客户端源码修改、无线上请求。