Files
ft/CodexOutput/upserver/阶段4C2奖励面板点击穿透修复方案独立审查-2026-09-24.md

104 lines
17 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.
# 阶段4C2奖励面板点击穿透修复方案独立审查
2026-09-24。独立审查agent出具。审查对象:《阶段4C2奖励面板点击穿透最小修复方案-待评审-2026-09-24.md》。只读审查,未修改任何共享文档、TODO、开发日志或生产代码;未运行Unity、未连接正式服务器或测试账号。
## 一、审查范围与方法
1. 通读方案全文与《阶段4C2第三次真实尝试与奖励确认记录-2026-09-24.md》(缺陷实证),交叉核对 run 29520 的 `events.jsonl` 原始事件(confirmed→presented 相隔4.6ms、closed 时间戳,与记录一致)。
2. 逐段核对生产源码:`Client/Assets/Scripts/UI/FishingReward/FishingRewardPanel.cs`(:25-36 ConfirmKVRewardAsync/OnDisable、:158-168 Awake与KV初始化、:278-324 Start与按钮绑定、:340-364 OnShow、:428-441 KV分支与kvPresentation、:546-562 Blur/Success/ApplySuccess、:1050-1200 关闭路径与OnClickInHome/OnClosePanel/InHomePanel/OnDestroy、全文件 blocksRaycasts/interactable/alpha 写入点清点);`Client/Assets/Scripts/Services/UserService.KVFishingReward.cs`(状态机全文,重点 :176-211 绑定/段、:242-275 ConfirmRewardAsync、:276-280 PresentReward、:281-288 CancelReward);`UserService.KVFishingRewardValidation.cs` 全文检索(确认45组171断言对UI属性零依赖)。
3. 核对提取测试:`KVFishingRewardTests/PanelSlice.cs.txt`(现状切片,:38展示回调仅恢复alpha)、`PanelTests.cs.txt`(现有断言与Canvas/静态环境shim结构)。
4. 核对面板prefab `Client/Assets/ABPackage/pkgFirstPackage/Panel/FishingRewardPanel.prefab`:全部CanvasGroup序列化值(m_Alpha/m_Interactable/m_BlocksRaycasts/m_IgnoreParentGroups)与持久化onClick(m_MethodName计数)。
5. 全库grep:`interactableChildren`、`OnClickInHome`、`playButton`引用清点;controller侧文件(`GampPlay/FishingB/FishingUpdateSys.cs`、`UserService.KVGameplayLoop.cs`)定位与当前hash比对(`controller-cancel-fix-review-hashes.json`)。
6. 按 `panel-raycast-fix-review-hashes.json` 逐项计算SHA256(大写hex)与字节数核对。
## 二、hash核对表(panel-raycast-fix-review-hashes.json,基目录 C:\projects\ft,8项)
| # | 路径 | 期望SHA256 / 字节 | 实测SHA256 / 字节 | 结果 |
|---|---|---|---|---|
| 1 | Client/Assets/Scripts/UI/FishingReward/FishingRewardPanel.cs | 5289088D…97978 / 58984 | 5289088D2BD73F433D333BEB18079E363470651140CEBB064AB0AFCA80297978 / 58984 | 一致 |
| 2 | Client/Assets/Scripts/Services/UserService.KVFishingReward.cs | 40EE14EB…93F9 / 20093 | 40EE14EBFF6F3D61B6270EA01E73C48DCA0B279B924D26DE6911C0261D7293F9 / 20093 | 一致 |
| 3 | Client/Assets/Scripts/Services/UserService.KVFishingRewardValidation.cs | 4CC10F91…EF35 / 42357 | 4CC10F91095FE2F058997AA798936C556ABCC04797582CF8990E53A05E79EF35 / 42357 | 一致 |
| 4 | CodexOutput/upserver/KVFishingRewardTests/PanelSlice.cs.txt | ADDABA0B…F075 / 2085 | ADDABA0BA1B2929F978D7EB956AC6D6C6788E60C8B6079A2F6929327D52BF075 / 2085 | 一致 |
| 5 | CodexOutput/upserver/KVFishingRewardTests/PanelTests.cs.txt | 20AE11FE…9877 / 1746 | 20AE11FE854513D3053341EDB43E4CC0407127248911A899C1A60354E5B09877 / 1746 | 一致 |
| 6 | CodexOutput/upserver/阶段4C2第三次真实尝试与奖励确认记录-2026-09-24.md | 66842339…642D / 3186 | 66842339CC16E8E5BAB9BC01BFC8267289B1B21A97ABECDE97CC3F5FA44F642D / 3186 | 一致 |
| 7 | CodexOutput/upserver/阶段4C2奖励面板点击穿透最小修复方案-待评审-2026-09-24.md | 895E34B8…FB34 / 2916 | 895E34B893B2936E9AAE28BDD9EE4F055C36FE4AA3F7F13CC1977E50468FBB34 / 2916 | 一致 |
| 8 | CodexOutput/upserver/KVGameplayLoopTests/runs/29520_639258498367875839/events.jsonl | D1CCD10A…53F0 / 6181 | D1CCD10AB0DC954F2F0F71B23EA17A4D3BDFE2D70E3CF34883E3BF9BD51553F0 / 6181 | 一致 |
**8/8 全部一致(SHA256大写hex与字节数双匹配)。**
## 三、审查重点逐项回答
### 重点1:恢复三属性是否完整、必要、充分
**结论:对"点击可达"目标,`{alpha=1;blocksRaycasts=true;interactable=true}`是CanvasGroup意义上的完整、必要、充分集合;方案问项中提到的 `interactableChildren` 并非 UnityEngine.CanvasGroup 的成员(全库grep 0命中),无需也无法恢复。**
证据与行号:
- CanvasGroup的相关成员全集为 alpha / blocksRaycasts / interactable / ignoreParentGroups(UnityEngine API;代码库中无自定义扩展,`interactableChildren` 全库0命中——方案问项1的表述应纠正)。ignoreParentGroups 经prefab核对无需动:`FishingRewardPanel.prefab` 中全部CanvasGroup序列化为 `m_Alpha:1 / m_Interactable:1 / m_BlocksRaycasts:1 / m_IgnoreParentGroups:0`,且本面板栈无父级CanvasGroup在KV模式写blocksRaycasts。
- 必要性:`blocksRaycasts=false` 使GraphicRaycaster跳过该组全部Graphic(run 29520实时探测实证:可见但点击穿透到下层);`interactable=false` 使组内Selectable(Button)即使被指针命中也不响应onClick;`alpha=0` 不可见。三者缺一不可。
- 充分性(无其他拦截):全文件交互属性写入点清点——`FishingRewardPanel.cs:167`(KV初始化,置false×3)、`:1052`(OnTargetClosePanel)、`:1072`(OnClickClose)、`:1165`(OnClosePanel)、`:1266`(OnSucceed)均为关闭路径只置 `blocksRaycasts=false`,无其他置true来源;子级 `root_success` 也是CanvasGroup(:170)但仅有alpha写(:762=1、:786=0,拍照路径,且该路径按钮在KV模式本就未绑定),其blocksRaycasts保持默认true,不构成子级拦截。恢复三属性后面板回到与正常模式(非KV,出厂prefab默认值)完全一致的可点状态,而正常模式"收下"按钮是可用的——逻辑闭环。
- **限定**:以上充分性仅覆盖"点击可被按钮接收"。按钮"是否有行为"取决于监听注册,见重点3(P1)。
### 重点2:PresentReward同步show()与审计先后的时序/原子性;重复OnShow下恢复是否幂等
**结论:无新增时序或原子性风险;属性恢复幂等且有双重去重保证至多执行一次。**
证据与行号:
- 时序链:`ConfirmRewardAsync` 在票据全部确认后置 `a.Confirmed=true` 并写 fishing-reward-confirmed 审计(`UserService.KVFishingReward.cs:271-272`)→ 返回后 `ConfirmKVRewardAsync` 检查 `KVRewardViewCurrent()`(`FishingRewardPanel.cs:30`)→ `PresentReward`(:276-280)在 `:279` 同步执行 `show()`(属性恢复)**先于**写 fishing-reward-presented 审计。run 29520实测 confirmed 12:37:50.5203 → presented .5249(4.6ms),与同步执行一致。属性恢复是纯客户端视图状态,无KV写入、无服务器依赖,先后顺序不产生数据一致性影响;即使进程在show()与presented审计之间崩溃,`a.Presented=true` 已置、不会重复展示——该语义为既有设计,本修复未改变其风险面(三次属性赋值在Unity与切片shim中均不可能抛异常,不会出现"Presented已置但展示半途而废"的新情形)。
- 幂等/去重:重复OnShow时 `BindRewardPanel` 因 `a.Applying` 已置返回null(`UserService.KVFishingReward.cs:180`),面板在 `FishingRewardPanel.cs:348` 早退,第二次 `ConfirmKVRewardAsync` 根本不会发起;即使同一action二次调用PresentReward,`a.Presented`(:279)去重。既有验证已钉住该不变式:Validation `:89`(两次PresentReward后shows==1)、`:129-130`(teardown窗口后仅一次展示);PanelTests现有 `Applied==1 && Shows==1` 断言同旨。恢复语句本身(三布尔/浮点赋值)天然幂等。
### 重点3:修复后"收下"(OnClickInHome)路径能否走通
**结论:不能。拟改动1使按钮可被按下、指针事件可被Button组件接收,但 KV 模式下 `OnClickInHome` 不会被调用——监听注册被Start()的KV守卫整体跳过。这是方案未覆盖的第二个缺陷(P1)。**
证据与行号:
- 决定性证据:`FishingRewardPanel.cs:287-290` 中 Start() 的绑定块以 `if(!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)` 为门,`:292` `playButton.onClick.AddListener(OnClickInHome)` 位于该块内(:291-322)。KV模式(Enabled=true)下整块跳过——不仅playButton,targetBtn/btn_green/btn_tryagain/btn_close等全部监听与go_fishPhoto回调均未注册。
- prefab无兜底:`FishingRewardPanel.prefab` 中 `m_MethodName` 计数为0(无任何持久化onClick调用),按钮行为100%来自代码注册。
- 引用清点:`OnClickInHome` 为private,全库仅 `:292` 一处绑定(另一处同名在 `Scripts/Duel/FishingDuelRewardPanel.cs`,无关)。因此KV循环内/运行中无任何其他路径注入该监听。
- 路径本体核对(假设监听补上后):`OnClickInHome`(:1078-1091)无KV编译门;`fishID>0` 先 `OpenAppraisePopupPanel(fishID)`(会先开一个鉴定弹窗,KV模式下属未评审UI流);`OpenSupplyDrop` 因KV模式跳过 `CheckResource`(:266-269同守卫)而 `isOpenDrop==false` 恒提前返回false;`OnClosePanel`(:1162-1168)置 `blocksRaycasts=false`、发布 Reset/IsRecast 事件、`DestroyUI` → OnDisable(:36)→ `CancelReward(this,…)`(`UserService.KVFishingReward.cs:281-288`):Confirmed=true时仅置 `a.Cancelled=true` 不Fault(:287),且OnDestroy(:1177-1181)再调因Cancelled早退(:284);`InHomePanel`(:1170-1175)仅发布事件。确认后 `CheckRewardWrite` 对 `a.Confirmed` 早退(:215),无KV写入门阻塞。**即:路径上没有其他KV门,唯一断点是入口监听不存在。**
- 推论:方案验收中"真实'收下'点击闭环留待下一次用户授权运行",按当前改动在下次运行必然仍无法闭环(点击被接收但零行为)。缺陷定位需从"只恢复alpha"修正为"恢复alpha+监听未注册"两层。
### 重点4:拟改动2(shim/断言)是否充分可红绿
**结论:可行、可编译、可红绿;有一处建模局限(P3)。**
证据:现状切片 `PanelSlice.cs.txt:38` 回调仅 `canvasGroup.alpha=1`;`PanelTests.cs.txt:8` shim `public class Canvas {public float alpha;}`。补两bool字段(默认false)后:新断言"pending期间 blocksRaycasts==false && interactable==false"在修复前后均绿(shim字段默认值承担"模拟:167初始化后状态",方案已自述此建模);"pending完成后 blocksRaycasts==true && interactable==true"在旧切片(仅恢复alpha)红、切片同步为与生产相同语句后绿——红绿路径明确。编译面:纯字段新增与纯读断言,无新依赖,与既有csproj结构兼容(现有断言同为字段读+Task.Delay模式,PanelTests:2-7)。
局限(P3-2):pending不变式由shim默认值隐式承担、未建模Awake:167初始化,故无法捕获"生产:167删除两属性初始化"这类回归——属提取测试固有抽象边界,建议在切片/测试中注释固化该约定。
### 重点5:既有45组171断言与PanelTests既有断言是否有期望改变
**结论:无任何期望改变。**
证据:`UserService.KVFishingRewardValidation.cs`(45组171断言)对canvasGroup/交互属性零引用——全文相关命中仅 `:244-245`(反射调用OnDisable,不涉属性)、`:282`/`:355`("FishingRewardPanel"作为guide面板名字符串)、`:89`/`:129-130`(PresentReward去重与展示计数,观察的是shows计数而非回调内容)。拟改动1只改回调体(赋值超集、执行次数与时机不变),不在该套件观测面内。PanelTests既有4断言(`Applied==1`、pending `alpha==0`、`alpha==1`、`Shows==1`)在新回调下全部保持成立。验收要求Validation文件字节不变(hash#3已锚定),双保险。
### 重点6:静态diff判据与验收组合是否足够
**结论:对"改动面控制"判据基本足够,存在一处锚点缺口(P3-3)。**
证据:验收组合=(1)PanelTests红绿(2)45组171断言不回归(3)Editor/SERVICE_HOST双编译0错误(4)静态diff生产仅:31一行+切片/测试同步+其余受审文件字节不变。生产改动单行、切片同步为同语句,diff判据可机械执行;8项hash清单覆盖两个生产文件、验证套件、切片/测试、记录与原始events。
缺口:验收第4条文字提及"controller-cancel修复后的FishingReward.cs/Validation hash",但(a)仓库不存在字面名"FishingReward.cs"的文件,实际controller是 `Client/Assets/Scripts/GampPlay/FishingB/FishingUpdateSys.cs`;(b)该文件与 `UserService.KVGameplayLoop.cs` 均不在本8项清单;(c)`controller-cancel-fix-review-hashes.json` 记录的是那轮修复**实施前**的旧值(其中KVFishingReward.cs为3104B335/20005B,当前已是40EE14EB/20093B;KVGameplayLoop.cs 930D1FF0/40556B 与当前一致,FishingUpdateSys.cs当前为937EF7EC…/130654B)。故"其余受审文件字节不变"对controller侧两文件缺少当前值锚点,建议并入清单或另附可核对当前hash。
## 四、问题列表
| 级别 | 编号 | 问题 | 位置/证据 | 处置建议 |
|---|---|---|---|---|
| P1 | P1-1 | KV模式下按钮监听未注册:`playButton.onClick.AddListener(OnClickInHome)` 位于 `if(!Enabled)` 守卫块内被整体跳过,prefab无持久化onClick(m_MethodName=0),OnClickInHome全库仅此一处绑定。拟改动1后按钮可接收输入但onClick零订阅,"收下"无任何行为;下次授权运行的"真实收下点击闭环"按当前方案无法达成。方案将缺陷定位为"只恢复alpha"不完整。 | FishingRewardPanel.cs:287-290、:292、:291-322;FishingRewardPanel.prefab(m_MethodName计数0);OnClickInHome仅:292引用 | 实施前二选一:(a)最小扩展——KV分支同样注册playButton→OnClickInHome(属新改动面,需同流程补红绿+hash+对OnClickInHome下游的KV风险评估:OpenAppraisePopupPanel弹窗、OnClosePanel→DestroyUI→OnDisable/OnDestroy→CancelReward(已核实Confirmed后仅置Cancelled不Fault)、InHomePanel事件);(b)改写方案验收与缺陷表述:本次仅达成"可接收输入",监听缺失与收下闭环另立缺陷方案。二选一后更新方案文本再实施 |
| P2 | — | 无P2 | — | — |
| P3 | P3-1 | 方案问项1的"interactableChildren"不是UnityEngine.CanvasGroup成员(全库0命中);正确成员集为alpha/blocksRaycasts/interactable/ignoreParentGroups。ignoreParentGroups经prefab核对(m_IgnoreParentGroups=0)确认无需恢复。 | UnityEngine API;FishingRewardPanel.prefab全部CanvasGroup序列化值 | 纠正方案表述即可,不阻塞 |
| P3 | P3-2 | 切片shim默认false仅隐式模拟:167初始化,pending断言无法捕获生产初始化层回归(已知建模局限,方案已自述) | PanelSlice.cs.txt:38、PanelTests.cs.txt:8;FishingRewardPanel.cs:167 | 切片/测试加注释固化建模约定 |
| P3 | P3-3 | 验收"其余受审文件字节不变"对controller侧文件缺当前hash锚点;方案文本中文件名"FishingReward.cs"不准确(实际为GampPlay/FishingB/FishingUpdateSys.cs) | panel-raycast-fix-review-hashes.json(8项不含之);controller-cancel-fix-review-hashes.json为旧值 | 把FishingUpdateSys.cs、UserService.KVGameplayLoop.cs当前hash并入清单或附于验收 |
| P3 | P3-4 | alpha/blocksRaycasts/interactable探测值仅存在于记录MD人证转述,events.jsonl无对应机器事件 | 记录MD:13;events.jsonl(仅confirmed/presented/closed等) | 下次运行把交互属性探测纳入审计流,获得机器可核证据 |
## 五、最终结论
**有条件批准。**
- **批准部分**:拟改动1(`FishingRewardPanel.cs:31` 展示回调恢复 `{alpha=1;blocksRaycasts=true;interactable=true;}`)与拟改动2(PanelSlice/PanelTests同步)本身正确、必要、幂等、无时序/原子性风险、对既有45组171断言与PanelTests既有断言零影响、红绿可达成;hash 8/8一致;不动初始化/OnShow分段/确认时序/Fault通道的边界声明与源码相符。
- **批准条件(实施前必须满足)**:
- **C1(对应P1-1,强制)**:按问题表P1-1处置建议二选一并更新方案文本——要么扩展修复使KV模式"收下"真正可达(新改动面须同流程补审),要么明确改写验收为"仅恢复可接收输入"、将监听缺失与收下闭环另立缺陷。不得以当前文本直接进入下次授权运行,否则"收下闭环"验收必失败且易被误判为修复无效。
- **C2(对应P3-3,建议)**:补controller侧两文件当前hash锚点;顺带纠正方案中"FishingReward.cs"文件名与"interactableChildren"表述(P3-1)。
- 满足C1后,两处限定修改可按方案既有验收流程(红绿、45组171断言、双编译、静态diff+hash)实施。
## 六、边界声明
本审查为静态只读代码审查+全量hash核对+文档/事件交叉验证;未运行Unity、未运行任何测试、未连接正式服务器或测试账号、未修改任何生产代码与共享文档(唯一写入为本审查报告文件)。hash一致仅证明各文件当前字节态与方案编制快照一致,不构成对运行时行为的验证。本结论不构成对下一次真实运行(需用户授权)的授权,不构成4C2或完整4C/G4阶段关闭的依据;真实"收下"点击闭环仍须以P1-1处置后经用户授权运行实证。