Files
ft/CodexOutput/upserver/B3BatchMigration/M2/M2-合批正式独立方案审查-2026-09-29.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

58 lines
11 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 合批正式独立方案审查
日期:2026-09-29。审查人:原 ZCode(独立侧),按[一次性正式前审提示词](M2-合批原ZCode一次性正式前审提示词-2026-09-29.md)执行。只读客户端源码;未读/改任何服务器代码,未调用正式 101/102,未进入 Play,未打包。
## 1. 基线核验
- manifest `M2-合批正式受审基线.json` SHA256 实测 `DBC965EDBF48FADA4C1508F2662562F4FD72C01711D8E2FE2022AC4956896EF3`,与提示词一致。
- **2165/2165 项长度与 SHA256 全部一致,0 缺失 0 漂移**;12 个 expectedAbsent(新源码/测试与 .meta)全部仍不存在。开发侧[自核 JSON](M2-合批正式前审包自核.json) 与本审查独立结果一致。
- 基线含 2165 项只读快照 + 110 现存白名单文件;本文不因列入基线授权任何白名单外修改。
## 2. 总裁决:**有条件 PASS —— 批准在候选白名单 110+12 文件与方案 §2–§7 行为边界内连续编码**
无 P1 阻断。2 项 P2 为批内必须落实的精确设计点(不阻止开工),4 项 P3 登记修正。依据:①基线零漂移;②方案全部可抽验事实声明经源码核验属实(见 §3);③结构设计与既有全部裁决链(双提供方约束、纠偏、预审裁决、复核裁决)一致无回退;④外部能力缺口与客户端可编码范围正确分离;⑤奖励持久性限制如实声明未偷换协议。
## 3. 提示词风险清单逐项结论
**3.1 独立 KVUserService 是否真正摆脱旧类与 SDK 类型 —— 成立。** 方案 §2.1/2.3/2.6 的组合是充分解:LoginStage 按冻结选择注册新类(唯一注入源,`LoginStage.cs:24` 现状核实);从 `IUserService` 移除 `EntityKey entityKey`(当前为 PlayFab DTO,`UserService.cs:6,66`)使"新类零 PF 类型引用"与"实现共用契约"同时成立——这是唯一解且方案已选;Duel 三处真实访问(`EventFishingDuelPanel.cs:634-635,748`)改经 PlayFabUserService 具体能力,该文件在白名单内。`IKVUserLoadReceiver`(消费者仅 Bind)与 `IKVUserLifecycle`(消费者仅 PFLoginAct/QuitAct,复用 `CloseKVForValidationAsync`/`ObserveKVQuitAsync` 既有签名,不加第三关闭器)均有实际调用者,符合 AGENTS.md。CustomId:**接口现状确为 `{ get; }`(`UserService.cs:70`),本批加 setter 是必要的**——见 §6 审查者错误披露。
**3.2 共用存档契约必要且足够简单 —— 成立。** 消费者=台账 202 项 business-store 调用(否则每文件重复分流);`IPlayerSaveStore`/`PlayerSaveStores` 职责限于选择后读写语义、固定实例绑定与确认,未新增第二开关/Owner;KV 实现直用 KVMgr 四入口与原 handle,PF 实现委托 PlayFabMgr 原缓存/写入;公共结果不含 PF DTO。固定 store 一次绑定、40 秒单调度者(Lifetime 内)、await 前捕获/await 后验证、失败≠缺键、remoteStore=false、`WriteAndConfirmAsync` 仅 Confirmed 为真——与 KVMgr/Guard/票据既有合同吻合(`KVMgr.cs:7-51`、`PlayFabMgr.cs:202-243,323-354` 复核)。Core→Services 新依赖未引入:新文件在 Services/PlayerData 引用 Core 的 KVMgr/PlayFabMgr 是正确方向;`Core/KVMgr` 物理搬迁明确保留待办,未冒充已解决。
**3.3 台账规则与白名单覆盖 —— 独立核证通过。** 台账 224 项唯一 ID,规则分布 202 business-store / 11 retain-pf-and-used-validation / 8 retain-pf-move-formal-only / 2 confirmed-consumption / 1 retain-agg;PF 内部、AGG、受控验证调用保留不误改(样本 B3-C012 `PlayFabMgr.CompareDatas` retain ✓、B3-C087/B3-C175/B3-C179/B3-C181 与源码逐一相符 ✓)。**全客户端 83 个含 `PlayFabMgr.Instance` 的文件中 82 个在白名单内,唯一例外 `UserService.KVGameplayLoop.cs` 是按设计不改的 kv-loop 通道**;台账 224 条路径全部落在白名单内(独立复算,非引用自核 JSON)。
**3.4 本地配置 LoginDays=0 的含义 —— 表述准确。** [配置投影](M2-合批动态配置投影.json):tbfishingevent.json 1466 行(实测 `cfgData/tbfishingevent.json` 恰为 1466 行 ✓)、70 组分支、直接 LoginDays=0、TriggerPack 同 0,附 hash 与"仅本地投影"警告。方案按全部代码可达分支迁存档、不删活动、不 froze 配置,正确。
**3.5 奖励/消耗动作的可实施范围 —— 批准方案 §6 全表,含两个真实旧缺陷修复。** 逐项源码核验:邮件批量 `TakeReward` 确实"请求 `EMailState.HasRead` 后按 `HasReward` 筛选"(`InGameMailHandler.cs:448-468`,单件版 :426-431 用 `RewardTaken` 正确)——修为 RewardTaken+请求前冻结未领集合,正确且需对照回归(方案已列);`FriendService.GetInvitedListCount→SetLinkAwardData→InviteLinkRewardCount`(`FriendService.cs:29-63`)✓;两个 confirmed-consumption 恰为 `FishingBehaviorB.cs:839-857`(ConsumeBaitOnPlayfab→SyncDataImmidiate)与 `FishingTurntablePanel.cs:580-584`(ConsumeOnPlayfab)✓。Survey 尾数:现状为十六进制转数再 `%10`(`SurveyService.cs:56-69`),方案"KV 十进制 %10、PF 保留十六进制"与代码吻合。**持久性限制的声明是本方案最诚实的一段**:多键确认无事务回滚、邮件"服务端已置已领/物品未落 KV"跨系统窗口如实保留、不靠自动重放补偿——接受此边界划分:本批准"代码迁移+普通成功回归",**不批"奖励完整通关"**,最终报告须逐动作列风险(方案 §6 末段已自我设限,维持)。
**3.6 外部能力三分 —— 划分正确。** [集中问题单](M2-客户端致服务端线上合同集中问题单-2026-09-29.md)四项(新 ID 兼容、KV 等级榜聚合、跨账号公开资料、PF 好友列表+匹配替代)全部是服务端实现依赖,不是产品再决策。客户端可编码范围(独立服务/个人 KV/原游戏服按新 ID 接线)不被挂起;功能运行放行与最终通关分别受聚合/资料/好友/匹配入口约束;无猜测路由与空成功适配器。
**3.7 formal 测试宿主 —— 方向正确。** ServiceHost Program/Stubs/csproj 迁真实 KVUserService、PF SDK stub 记调用计数、不允许 stub 成功身份;对既有 90 组用例"计数可能有竞态、比较命名用例和失败数"的提醒合理。
**3.8 清账与完成声明 —— 完整。** 闲置 formal 分支静态重扫后删除(不整文件删)、PF/AGG/验证入口保留、命名与物理分层延后、"全链实证前不称 M2 完成"——与最终门控清单一启约束。
## 4. 发现清单(一次汇总)
| 编号 | 级别 | 内容 | 必要修正 |
|---|---|---|---|
| P2-1 | P2 | `LastLoginTime` 三分映射机制未定死:接口为非 nullable `DateTime`(`UserService.cs:59`),`TimeRefreshManager.GetLastLoginTime()` 现直接转发 `_userService?.LastLoginTime`(`TimeRefreshManager.cs:106-108`),服务非 null 时**永不为 null**——"缺键 MinValue 哨兵→映射 null"必须在 TimeRefreshManager 显式加 MinValue 判别,且读侧须能区分"MinValue=缺失哨兵 / parse 失败=畸形错误 / 正常值"三种;写侧"首场景每日消费完成后才写本次 LoginTime"的确切时点要落到实施清单。做错会静默破坏每日重置分支(`:74-81`) | C1/C2 内把该机制写成三行精确规则并入测试职责(缺失→null→首次登录分支;畸形→资料错误;正常→比对 DayOfYear) |
| P2-2 | P2 | KV 登录链的分析/广告初始化未列出:`OnYinDunLogin`(易盾上报)与 `InitTapJoy`(ANDROID,读 `TapJoy_Diamond`)仅挂在 PF `LoginCore`(`UserService.cs:219,223`);M31 已有 `KVTapJoyBalance` 字段但无消费决定。首批 101/102 桌面验证不受影响,移动端 KV 会缺风控上报/广告余额恢复 | 批内明确:KV 链暂缓并登记清账(推荐,因属移动端平台链),或给出 KVTapJoyBalance 消费点;写入最终门控清单,不得静默丢失 |
| P3-a | P3 | 两处阶段闸(`PFLoginAct` formalKV 早退 :98-115 与 `LoginStage.RequestNextAct` :114-115)须**同步最后解除**,避免半开状态(登录页已放行而场景闸仍拦,或反之) | 实施顺序固定为 C3 全绿后一次移除两闸 |
| P3-b | P3 | `AggUserService.cs` 在白名单但唯一关联 B3-C168 为 retain-agg(不改),条目应注明"预期零改动或移出",防止顺手扩面 | 白名单定稿时写明 |
| P3-c | P3 | `ChatService.WaitingForLogin`(PF 链 `UserService.cs:217` 调用)在 KV 链的对应时序未点名——方案 §5"先订阅或 Init 发现已授权"原则已覆盖,实施时验证 Chat 仅靠 EvtAPISvrLoginSuccess 即可初始化 | C1 验证项 |
| P3-d | P3 | 台账 `runtimeVerified:false` 全量与 `reviewStatus:candidate-not-approved` 全量正确,但最终集成时须逐项翻转为已验(含首场景 PF 调用计数=0 的解除闸证据) | C3 交付物 |
## 5. 批准范围(明确边界)
- **文件**:候选白名单 110 现存 + 12 新增,逐文件 scope 与 callIds 约束("非任意文件重构");`UserService.KVGameplayLoop.cs`、`PlayerKVEditorValidationHost.cs`、`DevIAPService.cs`、`AiHelpManager.cs`、`EventPartnerInvitedPanel.cs` 等**白名单外文件零修改**(后两者经接口取值无需改,前者属不改但回归)。
- **行为**:方案 §2(服务/DI/契约/关闭)、§3(身份资料发布)、§4(存档契约与台账 202 项)、§5(首场景与动态活动)、§6(奖励确认与两个旧缺陷修复)、§7(外部依赖只登记)。
- **阶段闸**:`FishingStage` 双闸保留至 C3 首场景 PF 调用计数=0 且合成回归全绿(P3-a)。
- **不批准**:奖励"完整通关"声明、等级榜聚合/他人资料/好友列表/匹配的功能放行、真实 Player/SignalR/线上操作验收、任何服务器代码读写、打包。
## 6. 审查者错误披露(更正记录)
本审查者上一轮报告 P3a 声称"`IUserService.CustomId` 已声明 `get; set;`"——**与源码不符**。复核事实:`UserService.cs:70` 为 `string CustomId { get; }`(getter-only),setter 仅在具体类(:131),`PFLoginAct.cs:88,168` 的下转正因接口缺 setter 而存在。主会话裁决的否决正确,本方案 §2.2 的接口 setter 修订必要。此为本审查侧单方事实错误,非开发侧文档问题;记录在案以防复发(对接口成员的读写性必须逐字符核对声明而非凭记忆)。
## 7. 批内开发规范确认
按纠偏与提示词:获批后一个编码端在 §5 边界内连续实施;常规编译/测试失败自修并记日志,不再送审;仅新线上合同、架构/协议变更、白名单越界、不可逆真实操作须停下做针对差异的补审。C4 做一次全量独立实施复核。本报告即该轮前审的完整结论——**主会话重核本报告与基线 hash 后即可逐项裁决并冻结白名单,无需再征询审查侧意见**。