key|50
网页版,笔记功能,上传图片失败。
上传图片时看着成功(图出现在块里、能预览),但保存后图片就没了。
_tooling/_b35_width_falsify.py,同一套判据只改一个变量(图片宽度档位):
_tooling/_b35_width_falsify.py
attachments.target_id
photos/42d90aa9….jpg
photos/a2f16cc3….jpg|50
反证 7 通过 / 0 失败:唯一差异就是宽度后缀,结果却相反。
两处对同一份数据的格式约定不一致:
前端(assets/js/note_editor.js 的 serialize())把宽度写进 content:
assets/js/note_editor.js
serialize()
content = (key !== '' && w > 20 && w < 100) ? (key + '|' + w) : key;
所以调过宽度(25/50/75)的图片块,content 是 photos/xxx.jpg|50。
photos/xxx.jpg|50
服务端 App\Note::cleanBlocks() 把 image 块的 content 原样保留(只去空白), 接着 claimUploads() 直接拿它当 key 去匹配:
App\Note::cleanBlocks()
claimUploads()
// app/Note.php:472 if ($b['kind'] === 'image' && $b['content'] !== '') { $keys[] = $b['content']; // ← 这里带上了 |50 } // 然后 WHERE `key` IN (?) 永远匹配不到 'photos/xxx.jpg'
于是草稿附件(target_id=0)永远认领不到真实笔记上。图片数据还在库里, 但挂在一个谁也看不到的 target_id 上,页面渲染时按块内容去取图 → 取不到 → 用户看到"图没了"。
target_id=0
同一处还有第二个受害点:Note::files() 用 $used[(string)$b['content']] 排除"已用作图片块的附件", 带后缀时也排除不掉 → 同一张图会既当图片块、又在底部附件里重复出现。
Note::files()
$used[(string)$b['content']]
只要用户调过图片大小(25/50/75%)再保存,图片就丢。 默认 100% 不受影响 —— 这也是为什么"有时候好、有时候坏",容易误判成"上传失败"。
note_blocks
fc81347
新增 App\Note::imageKey(),统一把图片块 content 剥成纯附件 key:
App\Note::imageKey()
public static function imageKey(string $content): string { $c = trim($content); if ($c === '') { return ''; } $p = explode('|', $c, 2); return trim($p[0]); }
两个受害点都改成先过它:
$keys[] = $b['content'];
$k = self::imageKey(...)
$keys
files()
$used[(string)$b['content']] = true;
$used
updateBlock() 走同一条 claimUploads(),自动被覆盖,无需单独改。
updateBlock()
修在服务端的原因(对应验收第 5 条):如果反过来让前端不写后缀,宽度功能就废了 ——渲染时要按 content 里的 |50 出图。顺带说明这是不对称实现: templates/notes/form.php:70、templates/notes/index.php:98、assets/js/note_editor.js:152 这三处本来就是剥了后缀的,只有服务端 Note.php 两处漏了。
|50
templates/notes/form.php:70
templates/notes/index.php:98
assets/js/note_editor.js:152
Note.php
主判据 _tooling/_b35_width_falsify.py —— 7 通过 / 0 失败
[P100] content=photos/f341f348e7553ae37e571a10.jpg target_id=1713 (期望 1713) [P50] content=photos/b17327fce37f49031a41f972.jpg|50 target_id=1714 (期望 1714) [OK] B. 50% → content 带 |50(宽度功能没被牺牲) [OK] B. 50% → 附件也被认领到笔记(#18 修复点) [OK] 两条路径结果一致:宽度后缀不再影响认领
修复前同一套脚本:100% → 1707 成功,50% → target_id=0 失败。
附件不重复 _tooling/_b35_dup_attach.py —— 5 通过 / 0 失败
_tooling/_b35_dup_attach.py
note id = 1719 图片块 content = photos/605d212d035ba24d3a76f0de.jpg|50 [OK] ① 图片块 content 带上了 |50(宽度功能在) [OK] ② 阅读态图片块渲染出来了 [OK] ③ 底部附件区没有重复的同名文件(应为 0 条) [OK] ④ 图片字节能取到(200) 几何:img 宽 = {'img': 61, 'fig': 990} [OK] ⑤ 50% 宽度真的生效(img 明显窄于容器)
判据有牙齿:把 claimUploads() 注入回旧形态($keys[] = $b['content'];)后立刻变红:
[P50] content=photos/c73682321ec8cbafed319b1a.jpg|50 target_id=0 (期望 1716) [FAIL] B. 50% → 附件也被认领到笔记(#18 修复点) target_id=0 期望=1716 [FAIL] 两条路径结果一致 A=1715 B=0
恢复后回到 7 通过 / 0 失败。
git push
ff818a3..fc81347
family.yangyq.net
md5sum
9a00d152cc8a7dffc3ea87092ed6374f
family
php -l
imageKey()
'photos/a.jpg' => [photos/a.jpg] 'photos/b.jpg|50' => [photos/b.jpg] ' photos/c.jpg|33 ' => [photos/c.jpg] '' => [] '|50' => [] 'photos/d.jpg|100' => [photos/d.jpg]
线上 grep -c imageKey = 3(1 处定义 + 2 处调用点),与本地一致。
grep -c imageKey
fb_test
photos
_tooling/_fb_trash/20261028-note18/photos/
fb_test_before_cleanup.sql
按之前的分工,线上登录态的冒烟测试留给您来做。
修复已提交(fc81347)并部署到 family.yangyq.net,线上 md5 与本地一致,imageKey() 六种输入形态线上实跑全部正确。
三组验证:主判据 7/0、附件不重复 5/0、注入回旧形态后 2 条 FAIL(判据有牙齿)。本地测试数据已清理,21 个测试图片移到 _tooling/_fb_trash/20261028-note18/ 而非删除。
_tooling/_fb_trash/20261028-note18/
线上登录态冒烟按分工留给用户。关闭。
No dependencies set.
The note is not visible to the blocked user.
网页版,笔记功能,上传图片失败。
现象
上传图片时看着成功(图出现在块里、能预览),但保存后图片就没了。
复现(本地真实浏览器,已抓到数据库层)
_tooling/_b35_width_falsify.py,同一套判据只改一个变量(图片宽度档位):attachments.target_idphotos/42d90aa9….jpgphotos/a2f16cc3….jpg|50反证 7 通过 / 0 失败:唯一差异就是宽度后缀,结果却相反。
根因
两处对同一份数据的格式约定不一致:
前端(
assets/js/note_editor.js的serialize())把宽度写进 content:所以调过宽度(25/50/75)的图片块,content 是
photos/xxx.jpg|50。服务端
App\Note::cleanBlocks()把 image 块的 content 原样保留(只去空白),接着
claimUploads()直接拿它当 key 去匹配:于是草稿附件(
target_id=0)永远认领不到真实笔记上。图片数据还在库里,但挂在一个谁也看不到的 target_id 上,页面渲染时按块内容去取图 → 取不到 → 用户看到"图没了"。
同一处还有第二个受害点:
Note::files()用$used[(string)$b['content']]排除"已用作图片块的附件",带后缀时也排除不掉 → 同一张图会既当图片块、又在底部附件里重复出现。
影响面
只要用户调过图片大小(25/50/75%)再保存,图片就丢。
默认 100% 不受影响 —— 这也是为什么"有时候好、有时候坏",容易误判成"上传失败"。
验收标准
attachments.target_id都等于笔记 id(都认领成功)note_blocks里图片块 content 保留宽度信息(宽度功能不能因此丢失)已修复并部署(commit
fc81347)改了什么
新增
App\Note::imageKey(),统一把图片块 content 剥成纯附件 key:两个受害点都改成先过它:
claimUploads()$keys[] = $b['content'];$k = self::imageKey(...),非空才入$keysfiles()$used[(string)$b['content']] = true;$k = self::imageKey(...),非空才入$usedupdateBlock()走同一条claimUploads(),自动被覆盖,无需单独改。修在服务端的原因(对应验收第 5 条):如果反过来让前端不写后缀,宽度功能就废了
——渲染时要按 content 里的
|50出图。顺带说明这是不对称实现:templates/notes/form.php:70、templates/notes/index.php:98、assets/js/note_editor.js:152这三处本来就是剥了后缀的,只有服务端
Note.php两处漏了。验证(本地 PHP 8.5 + MariaDB,走真实 UI,非手搓 DOM)
主判据
_tooling/_b35_width_falsify.py—— 7 通过 / 0 失败修复前同一套脚本:100% → 1707 成功,50% →
target_id=0失败。附件不重复
_tooling/_b35_dup_attach.py—— 5 通过 / 0 失败判据有牙齿:把
claimUploads()注入回旧形态($keys[] = $b['content'];)后立刻变红:恢复后回到 7 通过 / 0 失败。
部署
git push→ff818a3..fc81347family.yangyq.net已更新,md5sum与本地一致:9a00d152cc8a7dffc3ea87092ed6374ffamily、mtime 为部署时刻(未被 1980 时间戳坑 OPcache)、php -l通过imageKey()六种形态全部正确:线上
grep -c imageKey= 3(1 处定义 + 2 处调用点),与本地一致。顺带完成
fb_test测试数据已清理:笔记 6 → 2(只留 10-27 的 v52/v55),附件 25 → 4,
photos目录 4303 → 4282。21 个测试图片移到_tooling/_fb_trash/20261028-note18/photos/(没直接删),库表备份在同目录fb_test_before_cleanup.sql。按之前的分工,线上登录态的冒烟测试留给您来做。
修复已提交(
fc81347)并部署到 family.yangyq.net,线上 md5 与本地一致,imageKey()六种输入形态线上实跑全部正确。三组验证:主判据 7/0、附件不重复 5/0、注入回旧形态后 2 条 FAIL(判据有牙齿)。本地测试数据已清理,21 个测试图片移到
_tooling/_fb_trash/20261028-note18/而非删除。线上登录态冒烟按分工留给用户。关闭。