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

17 KiB
Raw Blame History

阶段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处置后经用户授权运行实证。