Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-C3c本人统计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

106 lines
12 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.
# M2 C3c 本人统计 KV 暂存实施增量
日期:2026-09-30。状态:**开发侧方案核查完成,待现有 C3c 集中审查裁决;未实施。**
本增量落实用户“KV 先存一份,真正联调时再确定最终读取”的方向,补充《M2-先存KV后联调的阶段移植方案-2026-09-30.md》。与原 C3c 同次审查、同份报告;不重新审已批准的 C2d/C3b,不再询问迁移方向、身份或邮件流程。原 C3c 文件及 hash 保留,不重写历史结论。
## 1. 具体改动和数据来源
生产修改仅限 `Client/Assets/Scripts/DataCenter/DataCenter.LeadboardData.cs` 的 `SetLeadboardData`、`SetLvData` 及必要 using。
| 位置 | 原调用 | KV 接线候选 | 必须保留 |
|---|---|---|---|
| SetLvData | 计算复合分数后,await SetLeadboardData | 仍在原位置传入分数;未知地区仅传全球键 | async void、isRefreshing 的原赋值位置、全部计算与已有调用者 |
| SetLeadboardData | 构造统计列表,await UpdatePlayerStatisticsAsync | 同一列表写到当前 KVUserService.SaveStore,合并等待一次 FlushAsync | Task 返回类型;正常完成及已知请求失败均结束等待;错误仍可见 |
| 非 KV 路线 | PlayFab 统计请求 | 原实现原样执行 | PF/AGG 使用方式、顺序和 SDK 参数 |
不新增注入、接口、管理器、缓存、运行开关或静态全局 store。使用已有 `userService is KVUserService` 判断;进入 KV 分支立即将 `kvUser.SaveStore` 捕获到局部变量,await 后不重新获取新会话。底层 KVMgr 不增加榜单业务依赖。
键值如下:
- `LevelB`:原计算结果的 `ToString(CultureInfo.InvariantCulture)`,十进制整数字符串。
- `<ContinentCode>LevelB`:同一分数。KVUserService 已将地区归一为 AF/AN/AS/EU/NA/OC/SA 或 null;直接消费该来源,不复制校验表、不猜地区。
- KV 地区为 null/空时不创建地区项,尤其不把空前缀拼成第二个 `LevelB`。这落实已审“未知地区不冒充全球地区榜”的边界;PF 的原地区行为保持。
- `LastLoginTimeUtc`:已有实现复用,本增量不改代码、不新增 `LastLogin` 键。
原分数算法逐行保留:建筑字符串 int.TryParse;建筑值达到 100000 时压到 99999;玩家 lv 高于配置最后一项 LevelRequired 时按原逻辑回写上限;分数为 lv×100000+建筑值。不新增负值归零、溢出处理或新校验,不修原数据缺陷。LevelB 快照不参与反向恢复 lv,也不成为新的等级真值。
## 2. 精确调用顺序和结果处理
`SetLeadboardData` 保留现有先构造完整 `List<StatisticUpdate>` 的循环。这样参数长度错误仍在写入前暴露,不因 KV 逐键写而提前产生副作用;本增量不删除仍在使用的 PlayFab 数据模型。列表构造完成后、SDK 调用前增加 KV 分支:
1. 捕获该用户的原 store。依次对统计列表调用 `store.Write(name, decimalValue)`,采用默认 remoteStore=true。
2. 每个不接受结果记录 `Debug.LogError`,内容包括统计键与错误状态,不包含存档正文、Cookie 或凭据;不调用 RequireAccepted,不转成新异常。
3. 若至少一项已接受,调用并 await **一次** `store.FlushAsync()`。所有项均未接受时,记错后直接返回;不额外刷新无关存档。
4. Flush 返回 Confirmed 则结束;Rejected/Unknown/Cancelled/Superseded/Buffered/LocalOnly 均记错误状态,然后结束。Unknown 不写成成功、不自动重试、不回退 PlayFab、不新建会话。
5. 返回原调用者,继续执行原 `isRefreshing=false`。不新增 finally、不加重入锁、不改变 async void。意外编程异常仍按原异常路径传播,不用宽泛 catch 吞掉。
6. 非 KV 分支保留原 UpdatePlayerStatisticsRequest 和 await SDK 代码。
**失败语义依据**:`Client/LocalPackages/asap.playfab.async/Runtime/Gen/PlayFabClientAsyncAPI.cs:2430` 的 UpdatePlayerStatisticsAsync 在错误回调中执行 Debug.LogError,然后 task.SetResult(null);原 SetLeadboardData 忽略该返回值。因此不能把 KV 的明确拒绝或未知结果改成抛异常/阻断后续业务,也不能以 Task 正常结束宣称远端保存成功。通过日志和测试产物区分结果,保持原公开返回合同。
### 现有存储能力的限制,需在本次审查明确接受
- `KVPlayerSaveStore.FlushAsync` 会刷新该固定 handle 的待同步内容,不只本次统计键;它还检查捕获的存档凭据。因此既有其他待写项失败也可能使本次返回非 Confirmed。复用现有合同,不新增“统计专用刷新”接口、锁或第二个 handle。
- 多键 Write/Flush 不提供事务原子性。全球/地区快照可能部分接受、部分确认;不回滚或伪造两键同时成功。两个值均由同一次原分数计算得到,下一次原刷新按原触发点重写,不新增补偿循环。
- 并发更新/注销由已有 store 处理;本增量不增强原业务并发规则。不确认到新账号,不把 Superseded 当本次已确认。
- 本人暂存不是聚合榜实现,真实 KV 读取接口能否消费此键仍待联调。这是阶段方案限定接受的存储差异,不能以本次通过解除场景闸或宣布等级榜迁移完成。
若审查认定这些限制不符合暂存边界,应一次给出具体不通过项;不得自行扩展底层事务或更改业务流程规避。
## 3. 登录时间保持现有流程
`KVUserService.PrepareProfile` 读取已有 LastLoginTimeUtc 作为前次时间,当前 LoginTime 来自认证服务器时间。`FishingStage` 完成每日初始化后先保存/刷新原档,再保存当前 UTC 往返格式 O 时间。不得为了好友查询提前覆盖前次时间。
正式 101/102 的已归档样例尚无该键,本轮不补写假历史时间。好友时间最终读 KV 还是账号数据库由真实联调确定;缺值语义和投影接线继续列在 C3c,不用客户端改排序或补当前时间掩盖。
## 4. 测试文件边界与验证方法
复用 `CodexOutput/upserver/KVGameLoadTests/ServiceHost/`,候选范围如下;不为测试抽取新的生产层。
| 文件 | 允许变更 |
|---|---|
| ServiceHost.csproj | Compile 链接完整真实 DataCenter.LeadboardData.cs,不复制/截取方法 |
| Stubs.cs | 补完整类编译所需配置、UI、事件的最小签名;PlayerData 补 InfiniteBuildingLevel;SDK 夹具加统计请求计数、参数记录及可控完成任务 |
| C3IdentityCompatibilityTests.cs | 删除 game.LeadboardData 常量替身,让 FriendService 使用真实 GameCore.LeadboardData;现有 LeadboardItemData 替身仅补 SetData 等编译签名,不测试其伪实现 |
| Program.cs | 接入同一离线执行序列;沿用上下文泵驱动真实 async void 完成 |
| C3RemainingSdkTests.cs(新增候选) | 调用真实 LeadboardData;存储结果夹具、真实 KV store/handle 与合成传输用例 |
完整 LeadboardData 包含活动奖励/配置依赖,必须让整文件通过宿主编译;未运行的活动方法依赖只提供编译签名,意外调用应明确失败,不能默认为业务成功。本批结果只声称统计写入覆盖。链接导致既有测试名称解析冲突时,只调整上述测试文件的类型限定名;不改生产业务依赖。
最小用例集(全部为待执行,不是已通过):
1. KV 全球+AS 地区:真实 SetLvData 输入 lv=12、建筑=34,写出两个 `1200034`,确认前保持原等待状态、确认后复位;KV SDK 计数为0。
2. 地区缺失:只写一次 LevelB;已有合法地区两项写入顺序全球在前。不新设地区开关。
3. 原计算:建筑99999/100000/坏字符串、lv超过配置上限;验证实际 SetLvData 输出和原 lv 回写。负值等原缺陷不在本批修正。
4. SetLeadboardData 参数数量不匹配:与原循环一致,在任何 store.Write 前失败;不增加新的参数合同。
5. 明确拒写、全部拒写、部分接受:错误可观察,无 PF 回退;至少一项接受才 Flush;部分失败不报告两键都已确认。
6. Flush 的 Confirmed、Unknown、Rejected、Cancelled、Superseded:等待结束和错误日志符合第2节;不新增 retry,不让已知结果把原 isRefreshing 卡住。
7. 延迟确认时注销/换号:真实 store 捕获旧 handle,旧操作不得写新账号或以新会话确认;同键更新与其他待写项失败按现有 store 结果记录。
8. 真实 KV store + 合成远端:检查发出的本人 UserID、键值、同步响应及新会话重新读取;不是只检查本地缓存。覆盖多键部分失败。每组标明 formalRequests=0。
9. PF、ANDROID+AGG 对照:原两项 StatisticUpdate、原 SDK 次数、Task 结束时序保持;PF 错误夹具模拟原包装器的 log+null 完成,注明包装器本身另由源码核查,并非实际联网 PF 验证。
10. 运行全部既有 ServiceHost 用例,确保引入完整真实类后 C3b 好友身份用例仍通过;Unity CLI Loop 编译验证实际配置/UI 类型,无 Play、无打包。
命令沿用 `dotnet run --project CodexOutput/upserver/KVGameLoadTests/ServiceHost/ServiceHost.csproj --no-restore` 及 `-p:AndroidProbe=true`;Unity 只用 `uloop compile --project-path C:/projects/ft/Client --wait-for-domain-reload true`。真实账号 KV 写入另在明确联调检查点执行,本次审查通过不等于允许绕过阶段闸自动进入场景。
## 5. 开发侧核查裁决及审查接续
| 项目 | 开发侧结论 | 独立审查需要裁决 |
|---|---|---|
| 用户方向 | 已明确允许先暂存,覆盖旧“不新增LevelB键”的建议 | 不重问方向,只核最小实施是否符合 |
| 抽象必要性 | 无新增生产抽象;一个文件、两个方法即可完成本人统计替换 | 认可复用 KVUserService.SaveStore 与既有批量刷新限制 |
| 业务时序/错误 | 保留原 await 和 log 后正常完成;撤销任何 RequireAccepted/throw 提议 | 核失败矩阵与原包装器一致 |
| 地区 | 使用已有已校验 ContinentCode,缺失不造第二个全球键 | 核沿用已审地区边界,PF 不改 |
| 时间 | 已有实现复用,本次零变更 | 不引入重复键或提前写入 |
| 临时数据 | 原 lv/建筑仍唯一计算源;快照不能充当全服查询 | 接受阶段限制,最终确定消费者或回收闲置写入 |
| 测试 | 完整真实类入宿主,双条件回归+CLI Loop;无生产测试抽象 | 核测试五文件边界及合成证据限制 |
**给原 ZCode 的同次审查补充**:先核原 `M2-C3c剩余SDK差异受审hash.json`,再核 `M2-C3c本人统计KV暂存增量hash.json`;结合非空好友线上事实、服务器只读核查报告及阶段方案,继续写到原约定的 `M2-C3c剩余SDK接线独立差异审查-2026-09-30.md`。列出本增量是否批准、逐项限制和允许文件;D1–D5中跨玩家事实未齐的部分明确保持未裁决,不阻挡已完整裁决的本人存储实施,也不能给未定义部分空泛PASS。不修改服务器源码,不要求重审已批准同合同内容。
收到报告后主会话核 hash、逐条记录正式裁决;通过范围内连续写测试、编码、验证并更新检查点。当前没有该报告,本文件的开发侧核查不冒充独立批准。服务器、Core/KVMgr、现有 store、KVUserService、FishingStage、好友/UI消费者、debugconfig 和场景资产均不在本增量生产修改范围。
## 6. 最终清理与当前事实
LevelB/地区快照登记为迁移期数据;联调后确定实际消费者。确无消费者时,仅移除本次造成的闲置实现;历史 KV 数据清理另按最终批准方案,不自行删档。双闸、匹配暂停等全部临时路径最终恢复并验证;当前继续保留未完成标记。命名统一与原业务缺陷等整个迁移结束后另行处理。
本检查点仅完成方案、待审基线和状态文档;没有生产/测试代码修改,没有运行新业务测试,没有线上请求、KV写入、服务器修改、Play或打包。