英语词典与分词 · 变更及QA影响分析

2026-11-04|feature/qiyangyang/597084|Spec2.1.9

结论、范围与完整前后对照

当前不宜仅凭已有单元测试通过判定可验收。优先风险是音频补发布穿透人工草稿;其次是首次发布与异步计算竞态造成正式库持续缺分词行。问题依据是当前源码控制流与部分原方法隔离执行,未证明目标环境已经发生。

版本、范围与证据

课程中心后端 feature/qiyangyang/597084,固定 0baa9ebe8b7f9ecc46f675acbf1f56ac65378957(2026-11-04 13:57:43 +08:00);系统基线远端master 873c4f2d7c63a947216ae92bdc393b73408b6b6e(2026-11-02 19:59:22 +08:00,v3.11.2);演进对照为本分支2026-10-30 12:34:54的 e9f2fb1b58a2a0292e5a95a9cbeb6fae7f956682。master树没有本英语分词新增模块;历史审核版本不能替代系统master。

本次需求是三份在线词库/短语库/同步发布分词文档,重新读取正文与14条评论,当前Spec2.1.9全文与适用子计划按专项读取。原始返回与版本、差异清单留在证据目录。分支相对历史的264文件变化包含合入master及知识材料等其他功能,不能把264文件全部当作本英语需求;仅沿英语调用、共享资源、缓存、MQ、数据源与实际读写关系追踪。未给出实际环境部署版、真实前端仓库和C端源码,页面入口只依据PRD“公共资源空间→英文词库→单词/短语Tab”,URL未取得。

在线需求本轮实际变化:短语查重已改为原形与变形全局联合唯一;分词明确同步、发布两时机;撇号与连字符不切(it's、low-temperature)。Spec关于PRD未要求联合查重的X5已经过时;算法7.3只写连字符也应补撇号。音频恢复人工必填,与10月30日临时可空裁决有实质变化。不能沿旧用例把这些变化误判回归。

改造前完整业务机制

master没有新英语词典/分词能力,现有题目同步、发布、资源表、通用缓存和MQ是复用底座。下述“改造前”专指已实现英语功能的10月30日历史版本。

运营在教研库新增/编辑词条,归一化查重,翻译或变体作为子集合写入;编辑回待发布、保留曾发布事实。音频audioUrl可省略并登记本地资源;导入与补录同步等待TTS生成、下载上传后建词,失败可缺音频。单条发布在实体/文本锁中重读,正式库事务映射主/子/资源ID并差集删除,提交后回写教研库状态并异步清缓存。发布之外的草稿不得改变正式库。

LABS同步事务提交后直接在回调计算:抽取富文本→短语最长优先→剩余单词→每内容位落text/type序列→登记未命中关系→投补录。该版填空当数组、输入读判定区是已确认历史错误。保存剪枝要求MD5和revision;题目发布可携带教研分词快照到正式库。每日和手动全量按JY后PROD重算,正式库完全缺行不自增;详情接口用phraseRefs选择原形或特定变形。短语索引本机失效,其他进程靠TTL。

改造后完整业务机制

  1. 人工维护:单词、短语原形和已添加变体均要求audioUrl;地址登记不看后缀,但保存末端资源存在/未删除/mp3-wav门仍有效。词条保存保持待发布语义;单词完整性改为合并已有实体再算。导入固定source2、补录source3,人工source1不参与自动提交音频。
  2. 同步分词:题目与表单提交后只投ID消息;worker显式切教研库、重读最新题和表单,强制比对短语版本后计算。不随消息携带旧正文,因此“旧消息”通常读最新题;但读题后并发更新仍依赖写侧revision门。对象作答正文与分类数组正确分支,禁止读判定答案。
  3. 计算与写入:按内容位分组、每文本块独立去HTML/公式/图片,归一化后短语最长优先、词集合去重批查;输出序列保留重复和顺序,未命中以负ID占位。库存只存text/type;同正文NORMAL可剪枝(当前不再要求相同revision)。每行revision条件写后删除不在本轮的旧类型,后一步没有题级revision门。unmatched表已删除,未命中只有内存集合和补录消息。
  4. 题目发布:当前代码实际上不搬教研segments;只有教研全部分词成功时,才为正式库缺失类型创建INIT空骨架,已有行不修改。之后发发布重算MQ,worker显式切正式库、强制校版本,再按正式题正文计算并允许MD5剪枝。INIT不下发。Spec6/9.4和Plan10仍写先搬序列再重算,与代码不同。
  5. 全量与恢复:每日/手动共用watchdog锁,JY后PROD串行,按题表ID游标分页,逐题force绕过正文剪枝,单题失败继续、末尾清孤儿;PROD完全缺行仍跳过。总开关和execution急停已分别接入;execution关时既有结果仍可下发。全量不投补录,丢投已明确接受无自动恢复,不能再承诺旧台账对账。
  6. 音频状态:导入/补录先建空音频词。十分钟后Job每表取batch候选提交TTS,记录taskId/taskTime;在途按提交时间和ID取前batch。running超时取消清任务;completed保留人工音频,否则下载上传登记并在音频仍空时条件回填。正式库不复制任务列。回填后hasPublished1调用完整发布,因此“曾发布”与“当前已发布版本”混用是本轮P1。
  7. 读侧与RPC:题目下发取所有未删分词行,任一非NORMAL/坏JSON整题null,成功按内容位升序并用当前词典补ID。只有短语块失配才局部重扫,不把库存两个word合并为新phrase。详情改为wordIds+phraseIds,总数1~100按原请求长度,单库env0/1;短语一条返回原形及全variants,C端按matches.variantId定位变形。旧phraseRefs已删除,旧客户端必须同步升级;混合旧请求可能仅返回words,不能以HTTP/RPC成功认为兼容。

缓存版本机制、正常与异常边界

当前全部落库外层入口都有forceVersionCheck:同步consumer教研、发布consumer正式、全量每库轮首。此前仅检索application会漏掉worker调用,本次已用全仓反证纠正,不把“没有强制调用”列为缺陷。

正常更新:提交后INCR版本→读侧发现版本不同→丢本机entry同步重装→新句柄。普通下发按默认30秒窗口节流,写入口force绕过窗口。真实Caffeine与当前完整索引隔离执行确认 NORMAL_FORCE version=1。

异常:新版本重装时DB失败,loadSnapshot返回lastKnown旧快照,force返回void、不向调用方报告版本追平失败;进入30秒退避,readySnapshot继续给旧句柄。原类探针输出 FAILED_FORCE returnsOld=true local=1 remote=2。这是Spec明示fail-open的容错取舍,但与“绝不用旧词典落库/窗口不影响数据正确性”的保证冲突:旧句柄仍可被计算写成NORMAL。建议测试明确“保可用”与“允许持久化旧块”的不同断言,若要禁止旧块持久化,必须给写入口可判定的新鲜度结果。未跑业务DB,该项列条件风险,未当作Redis故障必然的独立产品Bug。

UNKNOWN_VERSION时后续比对直接跳过,直至TTL刷新重新取得版本;版本键读不到/INCR失败也退TTL。正常情况下新增版本能缩短可见延迟,不意味着任何故障下都秒级。180分钟refreshAfterWrite、5分钟expireAfterAccess及全局Redis过期策略是不同层;持续热点不受“5分钟无人访问淘汰”约束。测试须记录具体键、环境及预热条件,不能泛称清缓存后立即生效。

分段对照与测试影响

业务对象 历史行为 当前行为 必测结果
作答区 错读数组/判定区 读实际正文对象或分类数组 四题型、真实Schema、答案词绝不混入序列
异步分词 提交后线程直接算 MQ受理后worker算 停consumer、积压、投递失败时新正文与旧序列隔离
发布分词 搬成功快照 成功JY行驱动INIT骨架 JY未算/FAILED/正常三状态,首次发布竞态与再次发布恢复
音频 调用线程等TTS 空音频建词、Job提交回填 资源回填、人工并发、完整性、缓存、草稿不得自动发
短语详情 refs选一个变形 phraseIds带全部变形 老新客户端、变形定位、顺序、空数组和跨库ID
索引 TTL跨进程收敛 版本号+force校验 跨进程暖表、新版本DB失败/Redis失败、退避恢复
补录台账 持久关系存在但无恢复者 删除台账、承认无自动重投 不把每日刷/同MD5同步当补录恢复;明确人工处置
导入 重复跳过与同步音频 同样跳过、source修正、异步音频 CSV坏引号/非法变形重导、limit0、数据计数与音频晚到

回归执行顺序及通过门槛

第一组优先:已发布无音频词→TTS在途→人工改翻译并上传音频仅保存→平台完成;对比JY草稿和PROD旧版本,正式库非音频字段不得被自动发布。原handleInFlight/queryInFlight隔离执行已确认该分支会调publish,但真实发布事务传播目前为源码证明。

第二组优先:首次同步后暂停compute消费→立即发题→恢复消费→再跑日刷。应验证学生库最终可用,当前缺骨架门可能使segments始终null。对照JY已成功再发布可恢复。已有题改正文时另验MQ窗口不下发旧序列,不与首次无行混成一例。

第三组:batch=1,两个待音频词。首ID连续提交失败3次后服务恢复,后ID应被提交;accepted后failed反复3次应停;completed下载持续失败/查询持续异常均超过1800秒应收口。三种根因分别断言,不用“最终没有音频”一个结果代替。

第四组:唯独缺音频的完整字段导入词回填后,JY和PROD的audioUrl、completeness/complete、暖缓存同时检查;缺翻译对照仍应不完整。再覆盖RPC1/100/101项、重复IDs、旧phraseRefs、新phraseIds、变体空集合/排序及环境隔离。

其余专项矩阵包含原型字段、富文本边界、撇号/数字/IPA、跨内容位乱序、分页、权限、开关、删除、导入格式及缓存失败。均为测试设计,未在业务环境执行。源码静态、原方法探针、真实业务验证分别记录,不用自动化测试类存在证明验收。

范围限制与交付判断

本轮重点新异步链路、实际变更与旧问题受影响路径;不声称旧22项所有缓存故障模式都完整重跑。旧作答区P1、导入来源、逆序锁、完整性稀疏更新已修;旧白名单按新必填/格式约束改变口径;旧孤儿unmatched风险随表删除失效。旧CSV、参数、POS、搜索、跨内容位问题仍有当前证据。原LocalRedis投毒、旧loader失效竞态尚需专项故障注入,不以未重跑销账。

缺少业务环境部署版本、真实MySQL/Redis/MQ/TTS/OSS、前端/C端联调、全量A1/A2文件执行与权限平台注册证据。受项目AI禁止Maven限制,本轮无全项目构建/JUnit执行。已完成本地原类/原方法javac:CSV与参数、新批量Job、分词tokenizer、短语索引及音频在途分支。探针外部依赖为替身,编译桩适配失败不作业务缺陷。

两版执行时序(源码机制,非实测耗时)
历史版本全链路
当前版本全链路
已确认可达问题(1项P1、16项P2)

P1 音频补发布把曾发布对象的人工待发布草稿一并发布

条件:导入/补录无音频词已发布且有在途TTS;运营上传人工音频、改翻译并保存,未点发布;TTS随后completed。

机制:编辑保留hasPublished=1并置status=0;completed发现人工音频仍返回true;queryInFlight只检查hasPublished而非当前草稿状态,调用完整publishWord/Phrase,锁内重读当前主表、翻译、全部变体发布。

影响:未审核翻译、改名或变体配置进入PROD,待发布状态被系统改为已发布,违反Spec6只改不发布正式库完全不变。

反证和验证边界:人工音频不会被TTS覆盖;从未发布hasPublished=0确实不自动发布。Spec17自身写hasPublished=1补发布,与草稿隔离冲突,需设计裁决而不能说仅实现漏判。 当前原方法逐字抽取隔离javac探针已执行,见audio-probe/results.txt;外围服务/DAO替身,未连业务环境

EnglishAudioBackfillBizServiceImpl.java:168 · 对应证据

P2 completed下载永久失败与查询异常均绕过超时收口

条件:状态持续completed,但下载404/过期/OSS保存失败;或状态查询持续报错;提交时间超过1800秒。

机制:elapsed超时只位于null/running分支;query异常直接return,completed保存null也return并保留任务。每轮ORDER BY taskTime,id LIMIT batch重复拿最老行。

影响:该词永久无音频且不再补提交;当坏任务达到batch还会饿死后续在途任务。

反证和验证边界:null/running超时确实cancel并清理,普通一过性下载失败下轮能恢复;问题限定持续失败。 当前原方法逐字抽取隔离javac探针已执行,见audio-probe/results.txt;外围服务/DAO替身,未连业务环境

EnglishAudioBackfillBizServiceImpl.java:248 · 对应证据

P2 失败上限只统计提交抛错,平台终态失败与超时会无限重新提交

条件:平台submit一直成功,但每次合成都failed/cancelled或running超时。

机制:submit成功立即clearFailCount;终态失败和超时只清任务号、不增加失败计数;下轮pending重新提交;永远达不到maxRetry=3。

影响:同一无效词反复消耗合成配额,违反Spec17失败3次停止和配置同一行最多提交次数。

反证和验证边界:提交API连续抛错确实计数并停手7天;这不是所有失败均无上限。 静态顺向和反向调用核验;未执行Maven、未访问业务DB/TTS

EnglishAudioBackfillBizServiceImpl.java:151 · 对应证据

P2 达到提交失败上限的最早ID持续占满候选批次,后续词无法补音频

条件:最小ID的batch条IMPORT/AUTOFILL词提交连续失败达到上限,随后TTS恢复且还有更大ID待生成词。

机制:SQL先按ID升序LIMIT batch;maxRetry仅业务循环内判断后return,未推进游标/排除冷却行;7天计数TTL内每轮仍选择同一批。

影响:后续健康词最多7天也无合成任务;批次条数配置变小放大此影响。

反证和验证边界:不是数据库全量饥饿,限某表前batch被失败上限占满;7天过期或人工补齐能释放。 静态顺向和反向调用核验;未执行Maven、未访问业务DB/TTS

EnglishAudioBackfillBizServiceImpl.java:143 · 对应证据

P2 异步回填只更新音频列,完整性及教研缓存保持旧值

条件:导入词已有音标和翻译,唯一缺音频completeness=0;TTS成功回填。

机制:markAudioCompleted只set音频和清任务两列,不重算completeness;回填服务不调用教学缓存失效;补发布复制JY完整性0到PROD而不重算。

影响:即使最终音频齐全,管理筛选仍有缺失、RPC complete=false;已缓存TEACHING详情仍可无音频直到常规过期。

反证和验证边界:人工保存走mergeForCompleteness会重算;缺音标或翻译词仍应不完整,不能用它们验证本问题。 静态顺向和反向调用核验;未执行Maven、未访问业务DB/TTS

EnglishWordCoreServiceImpl.java:301 · 对应证据

P2 首次发布早于异步分词完成时,正式库缺行不能由全量自行恢复

条件:首次同步后立即发布;worker尚未完成或首次计算失败,JY无NORMAL行

机制:buildPublishableSegments空/非成功即返回空,ensureProdSegmentSkeletons直接return;发布MQ随后refreshQuestion见PROD无行直接false;每日force也共用该缺行门。JY次日成功不自动再次发布。

影响:学生库永久segments=null直到人工再次发布;不是承诺的次日全量收敛

反证和验证边界:JY正常行已就绪后发布可建INIT骨架并计算成功;再次发布可恢复 静态完整闭环,未实跑MQ/DB

QuestionBizServiceImpl.java:2099 · 对应证据

P2 正文更新后到异步执行前旧NORMAL序列仍可下发

条件:已有成功分词;LABS同步正文增加/删除英文;暂停计算消费者或投递失败

机制:AFTER_COMMIT只发ID消息,提交时未把旧行置INIT/FAILED;delivery只验NORMAL而不比题目正文/revision。worker尚未到引擎时,旧成功行被视为可信。正式库重新发布也保留已有骨架行,不降级。

影响:新正文搭配旧序列,按顺序定位可漏高亮/错位置;消息丢失持续至下次实际重算

反证和验证边界:worker成功会覆盖,已进入引擎失败会markFailed;不能把正常秒级受理当同步完成 静态控制流证明;没有声称真实客户端已错误高亮

QuestionSegmentSaveHookService.java:85 · 对应证据

P2 迟到旧任务删除新revision另一内容位成功行

条件:历史兜底MATERIAL→新表单ANSWER_AREA;旧刷新读旧正文后暂停,新同步rev2成功,再恢复旧rev1

机制:upsert/isStale仅保护同(question_id,content_type);旧type1写入可成功;dropObsoleteContentTypes重读当前type2后按ID软删,无revision条件

影响:新type2成功结果被旧type1替代;下发拿到旧内容序列

反证和验证边界:稳定题型固定同内容位时旧revision SQL门有效;需内容位集合变化,不是所有乱序均触发 静态SQL与方法完整核对;未本轮DB并发实跑

QuestionSegmentBizServiceImpl.java:406 · 对应证据

P2 数字夹英文仍作为整词查询

条件:cat2dog、3look up4

机制:isWordChar包含数字,nextToken连续吞入,isWordToken只要求至少一个英文字母

影响:与Spec7.3数字分隔冲突,漏cat/dog且可能补录cat2dog等混合词

反证和验证边界:纯1972过滤正确,low-temperature整词正确 本轮原EnglishWords+TextNormalizer隔离javac通过并执行,cat2dog/3look/up4输出已确认

EnglishWords.java:31 · 对应证据

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

条件:短语文件 translation 开引号后直到EOF未闭合,后面还有正常记录。

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

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

反证和验证边界:无真实DB/MQ/外部调用;除明确标为本轮javac的项目,均为当前源码链路核对,不复用旧实跑作为当前实跑。 本轮原完整Reader/Normalizer与原Param javac隔离执行,见 evidence/import/results.txt

EnglishImportFileReader.java:363 · 对应证据

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

条件:同一短语variants有合法及非法文本:get up / got up|起床。

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

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

反证和验证边界:无真实DB/MQ/外部调用;除明确标为本轮javac的项目,均为当前源码链路核对,不复用旧实跑作为当前实跑。 本轮原完整Reader/Normalizer与原Param javac隔离执行,见 evidence/import/results.txt

EnglishImportFileReader.java:171 · 对应证据

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

条件:english.import.enabled已开,JSON显式limit=0或-1,省略dryRun。

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

影响:整文件调用百度并建待发布词条,音频由后续回填Job统一处理;显式无效小批参数扩大到全量写入。

反证和验证边界:无真实DB/MQ/外部调用;除明确标为本轮javac的项目,均为当前源码链路核对,不复用旧实跑作为当前实跑。 本轮原完整Reader/Normalizer与原Param javac隔离执行,见 evidence/import/results.txt

EnglishDictImportBizServiceImpl.java:353 · 对应证据

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

条件:PROD提交且JY已标PUBLISHED后,clearRedisCommonCache同步抛错;例如有界队列拒绝后fallback MQ发送失败。

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

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

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

EnglishPublishBizServiceImpl.java:257 · 对应证据

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

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

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

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

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

SegmentAutoFillBizServiceImpl.java:142 · 对应证据

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

条件:HTTP pos传重复有效词性组成122字符原串

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

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

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

EnglishDictWriteTxService.java:531 · 对应证据

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

条件:已存take off,搜索take off

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

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

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

EnglishPhraseBizServiceImpl.java:129 · 对应证据

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

条件:控制台显式填limit=0(或纯0);或误填只含逗号的非空参数。

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

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

反证和验证边界:无真实DB/MQ/外部调用;除明确标为本轮javac的项目,均为当前源码链路核对,不复用旧实跑作为当前实跑。 本轮完整原Task javac,内存发布服务替身,显式0/逗号全量,150/2正常限量;见 evidence/import/results-batch.txt

EnglishBatchPublishTask.java:148 · 对应证据

分词:字段、状态、首次发布、乱序与15项矩阵

当前旧P1作答区字段错误已修复,总开关绕过已修复。仍有跨内容位旧任务删除新行、数字混词、IPA残留。新增异步化使“首次发布早于JY分词完成→PROD缺骨架→每日也跳过”成为普通操作时序;同时正文更新到计算完成期间旧NORMAL行仍可被下发。详细分级与行号见segment-findings.json,均不等于已取得线上故障。

证据范围

HEAD 0baa9ebe8b7f9ecc46f675acbf1f56ac65378957,历史 e9f2fb1b58a2a0292e5a95a9cbeb6fae7f956682,master 873c4f2d7c63a947216ae92bdc393b73408b6b6e。master树不含英语分词包,故属于master上新增能力;历史版本仅用于演进对照,不作为验收依据。当前Spec798行按1–280、281–560、561–798连续读取,Plan04/08/09/10及prd-segment-read.json读取。Plan04/10完整输出可见,初次批量Plan输出截断后已单独重读08/09。旧findings仅作为待核线索。

未跑Maven、未调用真实业务写入、未创建CodeGraph。原EnglishWords与TextNormalizer加本地commons-lang3依赖隔离javac执行:cat2dog 3look up4 => [cat2dog][3look][up4];1972 low-temperature => [low-temperature]。无DB/MQ/客户端联调证据。

业务入口与现行正常机制

业务操作是LABS同步四类英语题至课程平台,随后课程平台发布题目,下游调用标准题RPC取segments,再按正数词条ID查详情。后台直接改题不挂即时分词;每日/高权限手动全量用于重切历史题。

  1. questionBackFill在表单全部落库后hookSave(QuestionBizServiceImpl:5784)。总开关关时不发布事件;事务回滚无AFTER_COMMIT消息,提交后仅投ENGLISH_SEGMENT_SYNC_COMPUTE_EVENT,不抽正文、不等百度/TTS。无事务fallbackExecution=true立即投递。
  2. worker消息只带questionId/operatorId/requestTime;不携带正文与revision。消费者重新读教研库当前题,再forceVersionCheck(TEACHING),在显式DEFAULT数据源执行segmentAndSave。旧消息晚到通常会算最新题,不能把消息requestTime当版本门。消费者吞执行异常不重投;未命中集合非空才投补录领域事件。JSON解析在try外,非法JSON可能进入框架重试,和业务计算失败不同。
  3. 抽取先formData,若为空再output,均无则历史QuestionContent。选择题取questionInfo.material.materialContent[].text;填空/输入/完形取questionInfo.answerArea.textAnswerAreaContent(对象);数组则逐项classifyName。富文本优先unity再unityText。历史question_info.text归type1,text_fill_area.area_text归type2,历史可能双内容位。
  4. Jsoup去formula/img/script/style并留空格,document.text保留正文,IPA Unicode字符删为空格。按内容位TreeMap、同位块原顺序处理,各块独立扫描不跨块拼短语;正文指纹按位排序后用换行拼接。匹配先在token.start尝试最长短语,命中推进至短语结尾,否则整词;重复项逐次保留。每内容位单词去重后批查,未命中ID=-1,超99字符不登记补录。
  5. 实际计算入口先查execution.enabled。非force剪枝条件是现存内容位集合相同、每行NORMAL、MD5相同,当前不再要求revision相同。命中直接库存回显、未命中集合为空,不查新词典不重投补录。force绕过剪枝。readySnapshot为空则不写新NORMAL,已有不对应正文/修订的NORMAL标FAILED;索引从未就绪且原无行不建失败占位。
  6. 每内容位保存text/type快照,ID不入库;SQL唯一键(question_id,content_type),revision>=stored才能覆盖,成功清retry/error,失败登记也用同revision门。抽取失败按题型推内容位兜底,错误记录截断990字符;异常继续抛给消费者/全量单题隔离。
  7. 当前发布不搬JY segments(全仓无getQuestionSegmentDoList消费)。published完成公共后置后ensureProdSegmentSkeletons,只在JY全部行NORMAL时为PROD缺少的类型建INIT空骨架,主键decrement映射、revision取PROD题目。现存未删行一字段不改;骨架临时questionKey仍用JY值,成功重算saveResult以PROD题key改正。随后发发布重算MQ;消费者forceVersionCheck(PROD),refreshOneInProd读正式题并允许MD5剪枝。PROD无行一律跳过,不补建。
  8. 全量refreshAll先总开关再执行闸门,tryLock(0)+watchdog共用锁,先TEACHING再PROD,按题目候选id游标500页串行,每DAO动作显式切库;每题force=true,单题失败计数继续。PROD缺行日志跳过。末尾清题目不存在/已删除的孤儿分词,保留白名单外但仍存在题的行;软删除当前按ID更新。手动MQ与定时同执行体,消费者锁冲突丢弃不重投。
  9. RPC装配读取库存,key去重并CoreService每100分批。按题key归组、contentType升序,任一行不是NORMAL或JSON坏整题segments=null。按当前线程数据库选TEACHING/PROD,忽略库存历史ID,取同一短语快照;短语精确失配才对该块局部最长优先重扫,单词集合批查一次。词典异常不阻断题目而占位ID;不在读路径写回。
  10. C端契约:只在允许容器按归一化文本顺序定位;同词N次消费N项,负数只消费位置不高亮不查详情;正数按type+ID去重查详情,短语variantId仅定位返回变形。此仓不含C端实现,本次仅证明服务端序列,不声称客户端定位已验。

历史与当前对照

维度 master 历史e9f2 当前HEAD与可见结果
LABS同步 无分词新增模块 AFTER_COMMIT线程直接计算 AFTER_COMMIT只投MQ,接口不再承担计算耗时;接受与可用分离
作答区 无新增模块 fill/cloze当数组,input读判定答案 正确读对象正文/数组分类;旧P1关闭
分词库存 无 text/type与revision 保持;未命中关联表已删,只留内存集合
重复同步 无 同正文同revision才剪枝 同正文NORMAL即剪枝;非正文更新时间不再触发计算
发布 无 搬运全部NORMAL快照 不搬,按JY成功行补PROD INIT骨架后异步算;首次失败/无行恢复须再次发布
全量 无 先JY后PROD;总开关遗漏 保持串行force与缺行门,补总开关及执行闸门
词典版本 无 本地TTL 各worker单题入口和全库轮首forceVersionCheck;故障lastKnown策略由缓存专项讨论
下发 无 库存读时ID解析 保持;短语局部重扫单快照,正文一致性仍依赖写侧状态

Spec6/9.4和Plan10仍描述“发布搬行后重算”,与当前骨架机制相反;不能据此把PROD旧序列/无行都解释成当日自动收敛。Spec9.2仍写md5+修订剪枝,当前canSkip只比较MD5;应按现实现记录并将材料差异交主报告裁决。

状态与乱序、失败恢复

  • 同内容位严格旧revision:isStale/SQL拒绝,不投旧未命中;这条正常防线成立。
  • 同revision:SQL允许覆盖,未校验当前题目正文;题目update_time精度与实际并发须运行验证,不能把>=视为全部乱序已解决。
  • 跨内容位:行门无法保护题级集合删除,旧type1可清掉新type2,见SEG03。
  • 首次消息丢失:JY每日可补算;PROD若发布时无骨架每日也不补,见SEG01。
  • 补录发送/接收失败:全量仅分词,不投补录;同MD5重复同步剪枝也不投。恢复靠正文变化后再次同步、别题遇同词、手工建词发布,不能承诺“下次保存/每日自愈补录”。
  • 已算题正文变化但消息未执行:旧NORMAL没有入队失效步骤,下发仍可信,见SEG02。词典读时ID解析不能修正文序列。
  • 所有内容消失:textsByType空,结果空且accepted=true,清旧内容位,最终无行/null;此为合理删除路径,但旧任务空集合也缺题级revision守卫,能删除更新成功行(SEG03同一机制)。

可执行测试矩阵(测试设计,未执行)

编号 构造与操作 可观察通过断言
S01 选择题材料含She agrees with my opinion;词库有agree with原形及agrees with变形,LABS同步后等worker 一type1 NORMAL,正文序she/phrase/my/opinion,库存无ID,下发PROD按实际词典ID;不是接口受理true即通过
S02 填空/输入/完形各用真实answerArea对象,正文visible cat,determineInfo存hidden dog 均只type2且只visible/cat,无ClassCastException,无hidden/dog;分类组件数组classifyName另验
S03 两材料块分别look与up,库有look up;块内再放look up;同词重复 跨块不合并短语、块内合并,重复项数和顺序与正文一致
S04 富文本加粗跨节点、中文、公式图片、emoji、1972、low-temperature、it's、cat2dog、ˈfəʊnəm 标签属性/公式图片不入词;数字夹英文和IPA碎片按SEG04/05暴露差异,不能把现实现当验收正确
S05 同步事务写表单后故意回滚;另一次正常提交 回滚无compute MQ/分词写;提交仅发一次,worker异步后写;补录另观察,不等待外部服务
S06 成功题只改标题后重复同步,正文MD5相同;再真正改正文同步 前者行不更新且无补录;后者新MD5与新序列成功。隔离暂停worker读取新题RPC,复现SEG02旧序列窗口
S07 首次同步后阻塞compute消费者,立即发布,随后释放compute,再跑每日全量 应验期望PROD最终可用;当前无骨架持续null,JY成功仍不补,重复发布恢复;覆盖SEG01
S08 JY草稿有look up to,PROD仅look up,先完成JY再发布 INIT骨架不下发,发布worker后PROD序列按PROD词典look up+to;无JY ID透传
S09 旧任务rev1 type1在保存前阻塞,新rev2 type2完成后恢复;再试旧空文本任务 新type2应保留且旧任务不删;当前SEG03可违反。相同type稳定对照旧rev写必须拒绝
S10 索引冷启动失败,分别有旧同正文行、旧不同正文行、无行 同正文可保持;不同正文FAILED/null;无行不凭空NORMAL;索引恢复每日/同步可计算
S11 全量JY候选含待发布、PROD含已发布,双库词典有差异;制造一题失败 JY先PROD后,各库结果与本库词典对应,失败仅该题且后续题继续,retry/error可定位
S12 并行手动与定时;再删一道题和保留白名单外存活题 仅一轮锁持有执行,孤儿删而存活题行不误删;锁冲突不等于完成所有题
S13 总开关false分别同步/发布/全量/RPC;execution=false同样操作 总开关停分词且不下发;execution停计算而既有库存仍可下发;词典服务故障时题目主体仍返回
S14 存量快照含历史ID、同词重复、-1词/短语、坏JSON、FAILED内容位 旧ID覆盖为本库ID;重复保留;坏JSON和任一FAILED整题null;详情调用按正数唯一ID去重,负数不查
S15 先发布题后发布单词;新增跨块短语后只调下发,再跑全量 单词按缓存失效策略下次解析点亮;新跨词短语不会由读时自动合并,必须全量才改变块;短语失配可局部拆开

本矩阵为可评审测试设计;真实环境、调用方/C端容器、MQ消费控制与DB只读观察授权就绪后才能执行。没有通过Maven或生产链路验证的表述。

音频:异步状态、草稿隔离、重试与RPC

结论:新增异步链路有5项需处置,其中草稿被补发布为P1。旧19应按最新格式门槛重新裁决;旧20已修复。没有实际业务调用、数据库写入或Maven执行。以下为源码机制结论,不代表部署结果。新增原方法隔离javac探针,验证分支机制;外部服务及DAO均为替身。

版本与边界

当前0baa9ebe8b7f9ecc46f675acbf1f56ac65378957;旧e9f2fb1b58a2a0292e5a95a9cbeb6fae7f956682;最新master由主报告统一核对。音频回填Job相对旧版新增;最新Spec2.1.9包含17章异步化、5.1人工必填和12.2 phraseIds聚合契约。源AGENTS及Spec全文已读;Plan05已补读166–265、266–365、366–399,前1–165已先前完整读取,当前全文闭合。Plan02/06已读。父评审已另行完整读PRD正文/14条评论及Plan01,以父评审材料记录为准。未使用CodeGraph、未建索引。

正常机制:改造前、改造后与最终读取

旧版导入/补录调用TTS合成并等待,应用层下载/OSS上传/登记local_resource,拿资源ID后建词;失败音频为空主体仍可落库。当前导入/补录只建主体且音频为空,行source保持2/3。回填job以Redis watchdog锁保证同一轮互斥;先扫描每表音频空、任务空且创建10分钟以前的候选,word/phrase按source过滤、variant按父phrase来源二次过滤,再提交TTS并记taskId/秒级taskTime。人工来源1不自动提交。

每表在途行按taskTime/id升序取batch;查状态串行。running未超时保留、超时取消并清标记;failed/cancelled清标记。completed先保留已有人工资源;无资源才下载字节、OSS上传、登记bizType2 local_resource,随后仅在DB音频仍空的条件下回填,避免并发人工上传被覆盖。taskTime独立于updateTime,普通编辑和发布刷新updateTime不会改变超时起点。

回填后从未发布行不补发布;曾发布行调用完整单行发布。发布锁内重读当前JY,PROD一个事务显式映射主表/子表/资源ID,资源独立占行;翻译/变体差集软删;taskId清空、taskTime=0,学生库不跑合成。资源缺失/已删时loadAliveResources提前失败,不留本次半发布。无音频null允许导入/补录发布,资源集合为空时无需资源映射。RPC读取缺资源仅audioUrl=null,不因单项资源不存在拖垮整个批次。发布清PROD详情缓存,但异步回填本身没有失效TEACHING缓存。

当前RPC契约与QA边界

EnglishDictListByIdsReq已删除phraseRefs,改phraseIds。单词/短语列表可分别为空,原始列表长度合计1~100(重复也计数);env只0/1,ID必须正且按1亿边界归属同环境。split绑定单库,不自动拆分;cache查询显式source,单词/短语各按ID去重升序。短语返回原形及全部variants,由C端按下发variantId定位,故旧版“variantId归属参数错误”不再适用。缓存键有default/prod数据源段,无跨库兜底。旧调用方仅phraseRefs将等效空短语:混合请求可能仅回words,phrase-only总数0报参数错,须跨项目联调升级,不能视为兼容成功。

旧19:当前assertAudioResources仍要求mp3/wav。最新Spec5.1明确这道格式门槛,故不是已移除白名单,而是目标变更后旧缺陷不再成立;Spec5.2、Plan02“无格式校验”残留需文档统一。旧20:saveWord当前mergeForCompleteness合并existing,且缺audioUrl会被必填拦截,原始误标路径已闭合。

已核实问题

AUDIO-N01 P1 音频补发布把曾发布对象的人工待发布草稿一并发布

条件:导入/补录无音频词已发布且有在途TTS;运营上传人工音频、改翻译并保存,未点发布;TTS随后completed。

机制:编辑保留hasPublished=1并置status=0;completed发现人工音频仍返回true;queryInFlight只检查hasPublished而非当前草稿状态,调用完整publishWord/Phrase,锁内重读当前主表、翻译、全部变体发布。

最终表现:未审核翻译、改名或变体配置进入PROD,待发布状态被系统改为已发布,违反Spec6只改不发布正式库完全不变。

反证与边界:人工音频不会被TTS覆盖;从未发布hasPublished=0确实不自动发布。Spec17自身写hasPublished=1补发布,与草稿隔离冲突,需设计裁决而不能说仅实现漏判。

源码:application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:168-176;均是新增异步链路。

AUDIO-N02 P2 completed下载永久失败与查询异常均绕过超时收口

条件:状态持续completed,但下载404/过期/OSS保存失败;或状态查询持续报错;提交时间超过1800秒。

机制:elapsed超时只位于null/running分支;query异常直接return,completed保存null也return并保留任务。每轮ORDER BY taskTime,id LIMIT batch重复拿最老行。

最终表现:该词永久无音频且不再补提交;当坏任务达到batch还会饿死后续在途任务。

反证与边界:null/running超时确实cancel并清理,普通一过性下载失败下轮能恢复;问题限定持续失败。

源码:application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:248-292;均是新增异步链路。

AUDIO-N03 P2 失败上限只统计提交抛错,平台终态失败与超时会无限重新提交

条件:平台submit一直成功,但每次合成都failed/cancelled或running超时。

机制:submit成功立即clearFailCount;终态失败和超时只清任务号、不增加失败计数;下轮pending重新提交;永远达不到maxRetry=3。

最终表现:同一无效词反复消耗合成配额,违反Spec17失败3次停止和配置同一行最多提交次数。

反证与边界:提交API连续抛错确实计数并停手7天;这不是所有失败均无上限。

源码:application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:151-157;均是新增异步链路。

AUDIO-N04 P2 达到提交失败上限的最早ID持续占满候选批次,后续词无法补音频

条件:最小ID的batch条IMPORT/AUTOFILL词提交连续失败达到上限,随后TTS恢复且还有更大ID待生成词。

机制:SQL先按ID升序LIMIT batch;maxRetry仅业务循环内判断后return,未推进游标/排除冷却行;7天计数TTL内每轮仍选择同一批。

最终表现:后续健康词最多7天也无合成任务;批次条数配置变小放大此影响。

反证与边界:不是数据库全量饥饿,限某表前batch被失败上限占满;7天过期或人工补齐能释放。

源码:application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:143-146;均是新增异步链路。

AUDIO-N05 P2 异步回填只更新音频列,完整性及教研缓存保持旧值

条件:导入词已有音标和翻译,唯一缺音频completeness=0;TTS成功回填。

机制:markAudioCompleted只set音频和清任务两列,不重算completeness;回填服务不调用教学缓存失效;补发布复制JY完整性0到PROD而不重算。

最终表现:即使最终音频齐全,管理筛选仍有缺失、RPC complete=false;已缓存TEACHING详情仍可无音频直到常规过期。

反证与边界:人工保存走mergeForCompleteness会重算;缺音标或翻译词仍应不完整,不能用它们验证本问题。

源码:infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java:301-316;均是新增异步链路。

QA执行清单(未执行)

  1. 单词/短语原形/变体分别覆盖:首次提交→running→completed→JY资源回填→已发布才补PROD;PROD任务列始终空,资源ID/父外键映射一次,URI相同。
  2. 从未发布导入行回填不进PROD;曾发布在途行人工上传+改释义保存待发布,等待完成,检查草稿是否被自动发布(N01);短语变体完成也检查整个父短语草稿。
  3. 已有人工音频和下载期间并发人工音频两种窗口均不覆盖。改名必须提交人工音频,因此“改名仍空音频直接填旧TTS”正常HTTP入口不可达,不列确定问题;直接改库/其它内部入口需另证。
  4. 三类持续故障:query异常、completed下载失败、running超时,各超过1800秒。前两类不得永久占批次;running应cancel清理,随后重提交。平台failed/cancelled反复3次检查是否停手(N02/N03)。
  5. batch=1,最小ID提交失败3次后恢复服务,第二ID应能提交(N04);变体候选按父来源二次过滤也要防前批次被不可提交行占满。
  6. 唯一缺音频导入词回填后,查询JY详情/筛选/TEACHING RPC及PROD RPC,audioUrl应新值、complete应true(N05);对照确实缺音标词应false。
  7. 回填成功后注入一次补发布锁忙/资源写失败,JY保留音频、PROD仍旧;下一轮不会再选已清任务行,恢复依赖人工发布,不能声称下轮自动补发布。
  8. 未发布行在途时删除,或短语编辑移除在途变体,检查删除行是否仍被listAudioInFlight选中、是否产生孤儿资源/无意义父发布。当前在途扫描与写条件没有deleteTime/taskId守卫;普通改名音频为空已被人工必填阻断,删除并发仍需业务环境验证,不扩大为所有编辑都会错音。
  9. RPC word-only/phrase-only/混合、0/100/101长度、重复100/101、null/0/负数/跨库ID、旧phraseRefs、新phraseIds、多变体sortOrder相同、无变体[]、缺资源null;缓存关/开、坏值回源和default/prod隔离分别验。旧Plan06 variantId参数校验项已失效,改为返回集合归属与C端定位。

尚需闭合

未核实际部署、真实TTS下载响应/超时、Redis开关和DB数据;未做Dubbo客户端兼容联调;PRD与Plan01全文由父评审统一补读,Plan05本人已分段补完。报告不声称上述QA场景已运行。

原方法隔离探针实跑

audio-probe/AudioProbe.java包含从当前HEAD逐字抽取的handleInFlight、queryInFlight、publishReleased及isPublished方法;extracted-methods.txt留存抽取内容。仅外围类型、平台状态、DAO列表、发布服务与音频保存服务是最小替身,没有重写目标分支算法。Java21 javac编译及main运行成功,完整输出在audio-probe/results.txt。

  • completed、elapsed=9999(阈值1800)、saveAudio返回null:done=false、clears=0、failed=1。证明下载失败超时不收口。
  • query异常、elapsed=9999:clears=0、running=1。与下载失败是同一“超时判定仅存在running正常响应分支”根因,归N02一项。
  • 原queryInFlight加载hasPublished=1且人工音频88的词,平台completed:clears=1、publishCalls=1。证明保留人工音频后仍自动发布。该探针不运行真实publish内部、数据库事务或人工保存流程,完整草稿越权传播以当前save/publish源码闭合。

N03失败上限根因已再核:submit返回任务成功后立即clearFailCount;failed/cancelled/超时的markCleared没有increaseFailCount,故accepted→failed→重新submit不限次数,不与N02混计。

导入发布:清洗、参数、统计与补录恢复

代码基线: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,无服务写入。

图形预览100%
放大的技术图形