# 导入 / 发布 / 补录专项影响复核

代码基线：`0baa9ebe8b7f9ecc46f675acbf1f56ac65378957`；旧基线：`e9f2fb1b58a2a0292e5a95a9cbeb6fae7f956682`；master：`873c4f2d7c63a947216ae92bdc393b73408b6b6e`。未运行 Maven，未调用真实DB、MQ、百度、TTS，未改源码，未建CodeGraph。

当前结论：8项旧问题仍可由当前代码定位；导入来源、补录逆序锁已修；丢投恢复已在Spec中改为明确接受无兜底，不能再按旧恢复承诺计算同一个代码缺陷。

## 前后机制与影响

相对master：增加词/短语主子表、后台维护、单正式库事务发布和分词补录。运营写教研库，单条发布复制主/子/资源并映射ID；学生下发按词面从正式词典读时解析ID。待发布内容不会因为导入而自动进入正式库。

相对e9f2fb1b：导入与补录删除同步TTS等待，建行音频空，任务统一回填；单词导入增加百度前锁外预筛，锁内权威复查仍在；短语source固定IMPORT；补录发布移至文本锁外消除ID/文本反序；删除unmatched意味着没有持久化待补词台账。

发布事务边界仍是PROD单事务→JY条件回写/曾发布保护→失效缓存。JY回写失败后补登hasPublished，两个写回都失败仍需人工；失效失败在已发布状态之后，批量重跑不会自动补发。当前持锁顺序ID→归一化文本→音频URI；多文本排序，事务外壳持锁覆盖提交。60秒固定租期仍是运行耗时假设，不能当无限互斥保证。

## 仍存问题（每项均以当前源码重新核验）

### 01 CSV未闭合引号仍吞后续短语，且修文件重导不能纠正已写错误译文

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:363-365`。

触发：短语文件 translation 开引号后直到EOF未闭合，后面还有正常记录。

机制：readRecords不检查EOF的inQuotes，直接addRecord；readPhrases将后续物理行作为允许换行的translation，rows有效且invalid为空。importOnePhrase正常创建后，重导因主词已存在直接skip。

最终影响：look up译文变成“查找\nget up,起床”，get up整条丢失；修文件重导无法恢复look up已落的错误译文。

验证：本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 02 非法变形不应部分入库，避免幂等跳过堵死修文件重导

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:171-173`。

触发：同一短语variants有合法及非法文本：get up / got up|起床。

机制：非法项只continue内层循环；主短语和got up仍进入rows并写库。修成got up|gotten up重导时，importOnePhrase先getByNormalized命中已建主词而直接false。

最终影响：报告“变形非法”同时建成残缺语义；修文件重导仍无法加入gotten up，后续题面该变形漏短语命中。

验证：本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 03 导入显式非法limit仍退化为全量真写

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictImportBizServiceImpl.java:353-355`。

触发：english.import.enabled已开，JSON显式limit=0或-1，省略dryRun。

机制：Param.parse直接getInteger，无正数校验；execute只在limit>0截断，dryRun默认false。显式无效小批参数变成整文件执行。

最终影响：整文件查询百度并建待发布词条，音频由后续回填Job处理；违背Plan07“配错一律FAIL”及limit用于小批试跑的约定。

验证：本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 06 缓存失效异常被吞成发布成功，下一次批量也不再扫描该词

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:257-259`。

触发：PROD提交且JY已标PUBLISHED后，clearRedisCommonCache同步抛错；例如有界队列拒绝后fallback MQ发送失败。

机制：PROD写成后先把JY status置PUBLISHED；缓存失效异常仅catch日志，无补偿身份。批量按status=UNPUBLISHED扫描，因此下轮不再拿到该行。clearRedisCommonCache内部异步丢投也不一定抛回。

最终影响：PROD已新而缓存仍旧，直到实际TTL/手工重发/另一次有效失效；批量报告成功无法证明学生已看到新内容。

验证：当前源码静态核验

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 10 补录重复消费和重试仍先查百度，再幂等复用已有词

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/SegmentAutoFillBizServiceImpl.java:142-144`。

触发：同词多题的延迟MQ或消费重放；库中已经有已发布词、人工草稿、导入草稿或已删词。

机制：autoFillOne首步queryBaiduSafely，随后文本锁内saveWordForAutoFill才查已有（含删除行）。TTS已移到异步回填，不再逐次生成或上传。

最终影响：重复投递、发布失败重试、已有人工草稿或删除词都仍消耗百度配额；不会重复建词；本轮不再声称重复TTS/OSS。

验证：当前源码静态核验

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 13 复合词性验证归一化但落库仍写超长原串

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictWriteTxService.java:531-533`。

触发：HTTP pos传重复有效词性组成122字符原串

机制：NotBlank无Size；isSupportedPos临时归一化n.并接受；buildTranslationWrites仍存item.getPos，超过VARCHAR96。

最终影响：严格MySQL模式DataTooLong导致整个词条保存失败；宽松模式可能截断。

验证：当前源码静态核验

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 14 管理搜索未按保存规则压缩连续空白

定位：`application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPhraseBizServiceImpl.java:129-131`。

触发：已存take off，搜索take  off

机制：保存归一化单空格，搜索只trim，再按normalized列eq。

最终影响：允许的归一化精确搜索无法命中。大小写是否出错取决于DB collation，不用其做确定反例。

验证：当前源码静态核验

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

### 23 新批量发布Job接受limit=0和空参数段，意外发布全部待发布词

定位：`api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishBatchPublishTask.java:148-150`。

触发：控制台显式填limit=0（或纯0）；或误填只含逗号的非空参数。

机制：isNumeric接受0，parse返回limit0，nextBatchSize把<=0解释为无限；只含逗号的raw.split结果无有效键，也返回type=all/limit0。本类注释明确配错抛错，但未区分“省略limit”与“显式0”。

最终影响：试跑配置错误会直接跨库发布所有待发布内容，可能把未完成审核的人工草稿送至学生端；没有dryRun与任务撤回可兜。

验证：本轮完整原Task javac，内存发布服务替身，显式0/逗号全量，150/2正常限量；见 evidence/import/results-batch.txt

反证/边界：无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。

## 旧项处理

- 04：已修复。EnglishDictWriteTxService.savePhraseForImport显式IMPORT，避免异步回填按source误跳过。
- 11：已修复。autoFillOne先完成callWithWordLock，再publishWord；不再持文本锁进入ID锁。
- 07：改设计后保留风险，不计当前违约代码缺陷。Spec9.4/12.3/17.2承认丢投无自动兜底；Plan08 Goal/提交后可恢复投递仍矛盾。
- 08：随unmatched删除失去原触发条件。无待清理unmatched表，旧先删segment后删unmatched失败机制不存在；分词孤儿清理另由分词专项审核。

## unmatched删除后的真实恢复责任

发送阶段：EnglishDictChangeListener仅发一次MQ；抛错仅日志，无入重试。进程在提交后回调前退出同样没有持久待办。消费本体逐词持久化失败才进Redis重试列表；队满5000或Redis写失败只有日志。重试job每轮最多100、最多3次后淘汰，无台账可再扫描。

每日/手动全量刷只重算分词，不投补录；同正文重复LABS同步会剪枝，不等价于再投。恢复实际只有正文变化后同步、其它题首次遇到同词、人工建词发布。读时解析只负责“已建词从-1变ID”，不能把从未补出来的词自动创建。Spec9.4/12.3/17.2已接受这些边界；Plan08仍承诺“提交后可恢复”“MQ发送失败进入持久化重试”，需同步裁决，不能用Plan旧承诺声称实现成功。

## 统计与验收边界

格式非法/文件内重复不进入execute，结束三计数仅覆盖截断后的合法候选。全非法文件回0/0/0，错误在warn日志；本轮没有冒认它们已入失败数。百度故障被queryDictSafely/queryBaiduSafely吞为null并降级建词，算新建而非失败；重导跳过已有壳，不会自动补音标翻译。异步回填仅补音频，不能补其它字段。Spec8.2/12.3仍写“百度失败记词失败/失败明细”，与当前降级结果相冲突。

最新phrase PRD已明确跨原形/变形全局联合查重；Spec5.4第3条及16.1 X5仍称PRD未要求，此差异已过时，不应再列为待业务裁决。词性13/20与复合串、翻译999/3000等差异依旧显式存在。

## 可执行验证场景

1. CSV `phrase,translation
look up,"查找
get up,起床
`：必须观察解析仅look up且译文吞后行、invalid空；导入修文件再跑依旧跳过look up。
2. get up变形`got up|起床`：解析同时有valid row与invalid；首次建主表+got up，修成got up|gotten up再导被主表存在短路。
3. import.enabled=true，显式limit=0/-1且省略dryRun：当前会处理全部合法候选；对照limit20仅前20，dryRun不查库不外呼。
4. 待发布250词，batch传limit=0,type=words：当前100/100/100扫描250；`0`或`,,,`处理两类全量；对照150仅150；负数/非法type无写并FAIL。
5. 已发布词重复消费同补录词3次：统计百度query3次，建词0，自动发布0，TTS调用0；与新导入同词重导百度query0区分。
6. JY发布成功后注入缓存失效抛错：断言JY PUBLISHED、PROD新值、任务成功；下一轮batch不选该词，暖缓存仍旧。
7. 保存合法音频词条但pos=`n./`重复40次：normalizeKnownCodes接受并压成n.，实际buildTranslationWrites写原串；超过96列宽在严格DB应失败；避免把Req未限长误当枚举规范化已落库。
8. 库有take off，搜索`take  off`：trim后仍双空格，精确eq无法命中；对照take off命中。大小写是否命中还取决于DB排序规则，不能无DB证据泛称大小写必失败。
9. 模拟提交后MQ丢投：正常行已有、词典缺词；每日/手动重算及同正文同步均不应被声称补投；正文改变重新同步才有新消息。
10. 两线程补录与人工编辑：确认旧文本锁释放后才拿ID锁，无旧反序环；同时验证人工源草稿不自动发布、已删除词不重建。

本轮隔离证据：`evidence/import/results.txt`（原完整Reader/Normalizer与原Param）；`evidence/import/results-batch.txt`（完整原Task、发布服务内存替身）。探针为本地javac，无服务写入。
