Commit 8fe1a8c
異體字落地替換 S2:Codes UI 全表串接
執行 docs/CHAR_VARIANT_MAP_TEXT_COLUMN_ROLLOUT_PLAN.md 的 S2。Codes UI 的 5 條寫入路徑
(performStore/performUpdate/performProposalStore/performProposalUpdate/
proposalUpdateExisting)全部掛上落地替換;Blade 與 React 共用 perform*,一次涵蓋兩個入口。
## 掛鉤與通知
- 替換一律只碰「要寫入的值」,不碰「用來定位既有列的條件」(D7 第四類):
performUpdate 的 $conditions 來自 URL 主鍵、在 extractFormData 之前就組好;
提案路徑的主鍵查重則在替換之後,所以查重看到的是替換後的值。
- 成功與**失敗**分支都 flash 通知。失敗分支特別重要:唯一鍵衝突/「未偵測到任何修改內容」
可能是替換自己造成的,而 withInput() 回填的是**替換前**的原始輸入——不附通知的話
使用者會看到「你什麼都沒改」而完全無從理解。
## D7「兩形並存」的去重(實作時發現,原計畫漏了)
`ALTNAME_DATA.c_alt_name_chn` 同時是**文本主鍵成員**又在替換範圍內(strict),而
ALTNAME_DATA 是 Codes UI 可寫的表——這是 Codes UI 裡唯一會踩到 D7 的形狀。
D6 不回溯,所以既有列的主鍵可能還是變體形,只用替換後的值查重會錯過它、鑄出語義重複的
第二列,而資料庫唯一鍵擋不住(不同字形是不同鍵值)——**比不替換更糟**。
正確做法(前兩版都是錯的,記錄下來避免重演):
- ❌ 只探「輸入值 + 替換後值」兩形:對照是多對一,既有列可能是**另一個**變體
(既有「菁客」、新輸入「靑客」,兩者都歸一成「青客」)。
- ❌ 列舉所有等價字形再逐一查:查詢次數等於等價字形數,而為避免組合爆炸設的上限
**本身是正確性缺口**(超過就退回只比對正規形=完全失去去重),且可由合法對照表資料
觸發(一個參考字 6 個變體、主鍵含 2 個這樣的字就是 7×7=49)。
- ✅ 把**不在替換範圍內**的主鍵欄固定在 SQL 條件裡取回那一小群候選列,再在 PHP 端把
它們的文本主鍵歸一後比對:精確(不管對照表什麼形狀)、只要一次查詢、無上限可調。
待審提案那一側同理——`hasActiveCreateProposalConflict()` 是拿 resource_id 做完全相等比對,
S2 之前留下的 pending 提案帶變體形 resource_id、新提案帶歸一後的 ⇒ 不會衝突 ⇒ 兩筆並存、
依序核准就落成兩種字形的兩筆列。改以 resource_id 的**位置式 LIKE 樣式**收斂 + lazyById() 分批。
兩個陷阱:前導前綴在 production 的 ALTNAME_DATA 會失效(主鍵第一欄就是可替換的文字欄);
cursor() 在 PDO MySQL 預設 buffered query 下記憶體並非有界。
## 其他實質修正
- **缺表降級**:落地替換現在掛在 Codes UI 全部寫入路徑上,若因缺表拋錯會讓**整個代碼表的
寫入功能 500**——為加值功能讓核心錄入停擺是錯的取捨。只在「表不存在」時降級(確定性、
可快取、reset() 會清),其餘錯誤一律往上拋。早期寫法對所有 Throwable 降級並快取空 map,
後果是一次瞬時錯誤就讓整個 process 不再替換,只留一行 warning。
- **新增 VariantMappingException**:QueryException 繼承 PDOException 繼承 RuntimeException,
而 assertWritable() 內部會查表——catch(\RuntimeException) 會把資料庫錯誤當成驗證失敗、
把原始 SQL 顯示給使用者,而且該次寫入被靜默跳過而不是誠實 500。
- **proposalUpdateExisting 的 excludeId 改用權威來源** operation.resource_id,而非使用者
送出的 body id(body 的 id 可能被改、可能是空字串 ⇒ (int) 變 0 ⇒ 不排除舊邊 ⇒ 合法修改
被誤報成環)。
- **getKeyColumns() 的方法內 static cache 改成可重置**:那是既有的測試隔離地雷(測試會為
同一個表名建不同的合成 schema,一旦被快取成錯的主鍵欄,後面的測試就會拿到污染值,
症狀是與該測試無關的「請確認主鍵欄位已填寫完整」)。生產語義不變。
## 既有測試的調整
CodesCharVariantMapAuditTest 原本把 c_reference_char 設成「新參考字」(4 個字),新 guard 會
擋下多字元對照。改成單一字元——該值本來就不是合法的「參考字」,而該測試的主體是稽核紀錄。
## 驗證
三輪 review agent + 六輪 codex。實測鑑別力(neuter 對應機制後會紅的測試數):
替換 hook 6/22、通知 5、去重 4、提案側去重 1、定位器 1。
全量 phpunit 2845 tests/15190 assertions 全過;php-cs-fixer(已刪 cache、用 dist config)0 fixes。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent a821dea commit 8fe1a8c
8 files changed
Lines changed: 1403 additions & 32 deletions
File tree
- app
- Exceptions
- Http/Controllers
- Services
- docs
- tests
- Feature
- Unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
36 | 51 | | |
37 | 52 | | |
38 | 53 | | |
| |||
183 | 198 | | |
184 | 199 | | |
185 | 200 | | |
186 | | - | |
| 201 | + | |
187 | 202 | | |
188 | 203 | | |
189 | 204 | | |
| |||
208 | 223 | | |
209 | 224 | | |
210 | 225 | | |
211 | | - | |
| 226 | + | |
212 | 227 | | |
213 | 228 | | |
214 | 229 | | |
215 | | - | |
| 230 | + | |
216 | 231 | | |
217 | 232 | | |
218 | 233 | | |
219 | | - | |
| 234 | + | |
220 | 235 | | |
221 | 236 | | |
222 | 237 | | |
| |||
233 | 248 | | |
234 | 249 | | |
235 | 250 | | |
236 | | - | |
| 251 | + | |
237 | 252 | | |
238 | 253 | | |
239 | 254 | | |
| |||
243 | 258 | | |
244 | 259 | | |
245 | 260 | | |
| 261 | + | |
246 | 262 | | |
247 | 263 | | |
248 | 264 | | |
| |||
335 | 351 | | |
336 | 352 | | |
337 | 353 | | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
344 | 359 | | |
345 | 360 | | |
346 | 361 | | |
| |||
351 | 366 | | |
352 | 367 | | |
353 | 368 | | |
354 | | - | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
361 | 374 | | |
362 | 375 | | |
363 | 376 | | |
364 | 377 | | |
365 | 378 | | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
366 | 414 | | |
367 | 415 | | |
368 | 416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
135 | | - | |
136 | | - | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
137 | 150 | | |
138 | 151 | | |
139 | 152 | | |
| |||
186 | 199 | | |
187 | 200 | | |
188 | 201 | | |
189 | | - | |
| 202 | + | |
190 | 203 | | |
191 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
192 | 210 | | |
193 | 211 | | |
194 | 212 | | |
| |||
264 | 282 | | |
265 | 283 | | |
266 | 284 | | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
267 | 295 | | |
268 | 296 | | |
269 | 297 | | |
| |||
317 | 345 | | |
318 | 346 | | |
319 | 347 | | |
| 348 | + | |
320 | 349 | | |
321 | 350 | | |
322 | 351 | | |
| |||
0 commit comments