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