# 好友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完整评审,不能局部开工。本轮仅文档,无服务端或客户端源码修改、无线上请求。