# 选图点选拦截 v2:硬封锁 + 仅本机判定(去服务器半场、去强制放行) 日期:2026-09-29 | 状态:用户拍板(方案 A:接受"服务端已删照片无法重传"限制) ## 背景与问题 v1(提交 7e5c550+0673f39)上线后两个实际问题: 1. **选图不灵敏**:`selectPredicate` 每次点选 await 判定,判定含服务器单项预检 (时间帽 2s)。弱网下每次点新照片勾选延迟 0.5~2s,用户以为没点上就反复点 (wechat_assets_picker `selectAsset` 源码:谓词返回前 `provider.selectAsset` 不执行——勾选必然等判定)。 2. **拦截形同虚设**:v1 设计了"二次点击强制选择"活口(escape hatch),配合上述 反复点击习惯,重复照片多点几次必然被强制选中——用户明确要求**重复照片不能 被选择**。 ## 方案(v2) ### 1. 点选判定只查本机(治卡顿) - `_judge` 仅保留本机 TaskStore 半场(同账号+相册+completed,filename+bytes), 删除服务器单项预检及其 2s 时间帽——点选延迟从「最多 2s 网络」降为「毫秒级 本地查询」。 - 服务器查重不消失:**入队时刻统一弹窗**(checkDuplicatesUnified,一次批量 ≤500 张)照旧——跨设备/重装场景的重复仍在点上传时拦,服务端 409 兜底不变。 - iOS 注意:`title` 为空(需 FilterOption.needTitle)时本机半场无从匹配 → 放行;iOS 点选拦截实际不生效,跨设备判定依赖入队弹窗(与 v1 服务器半场 行为的差异登记于此)。 ### 2. 硬封锁(去强制放行) - 删除 `_forced` 二次点击放行集:本机命中的重复照片**每次点击都拒选**, 判定缓存命中即秒拦,提示文案改为「这张已上传到该相册,无法重复选择」 (en: This photo has already been uploaded to this album),不再引导再点。 - SnackBar clearSnackBars 防堆积行为保留。 ### 3. 判定缓存升级为 in-flight Future 共享 - `_verdicts` 从 `Map` 改为 `Map>`, `putIfAbsent` 并发去重——同一照片双击/连点只发起一次判定(清偿 9-29 评审 登记 Minor 遗留)。`_judge` 内部全捕获不抛错,缓存 Future 无失败态。 ## 已接受的限制(用户拍板 A) - 照片在后台被删除后想重传同一文件:本机历史仍记「已传」→ 永远拦死。 解决办法:清 App 数据或换设备(罕见场景,先接受;后续可加设置开关/服务端 存活性免疫,未排期)。 ## 构造签名变化 `PickerDuplicateGate` 删除 `client` 与 `serverCheckTimeout` 入参(点选路径无 网络依赖);`multiImagePickerProvider` 相应不再注入 ApiClient。 ## 测试(enqueue_test 拦截器组重写) - 本机命中 → 拒选;**连续多次点击均拒选**(无强制路径);新提示文案; SnackBar 不堆积(第二个重复资产仅一条)。 - 不同 bytes / filename / 相册 / 账号 → 放行。 - in-flight 共享:并发两次 decideFact,fileSize 闭包仅被调用一次。 - title=null(iOS)→ 放行;fileSize 失败 → 放行;取消选择(selected=true) 恒放行且零查询(fileSize 计数为 0)。 - v1 服务器相关用例(②④⑥)随服务器半场移除而删除。 ## 影响面 `enqueue.dart`(gate + provider 注入)+ `enqueue_test.dart` + l10n 双语一处 文案。不涉及服务端、契约、其它页面。