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

11 KiB
Raw Blame History

M2 合批正式独立方案审查

日期:2026-09-29。审查人:原 ZCode(独立侧),按一次性正式前审提示词执行。只读客户端源码;未读/改任何服务器代码,未调用正式 101/102,未进入 Play,未打包。

1. 基线核验

  • manifest M2-合批正式受审基线.json SHA256 实测 DBC965EDBF48FADA4C1508F2662562F4FD72C01711D8E2FE2022AC4956896EF3,与提示词一致。
  • 2165/2165 项长度与 SHA256 全部一致,0 缺失 0 漂移;12 个 expectedAbsent(新源码/测试与 .meta)全部仍不存在。开发侧自核 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 的含义 —— 表述准确。 配置投影: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 外部能力三分 —— 划分正确。 集中问题单四项(新 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 后即可逐项裁决并冻结白名单,无需再征询审查侧意见。