统一修复说明 · 完整修复代码逻辑 · 验证与部署前置 · 最终独立复审
| 改动ID | 关联问题/类型 | 业务改动 | 改动前行为 | 修复代码逻辑 | 完整代码位置 | 验证前置与操作 | 预期结果/观察点 | 已执行证据 | 待验证项 | 影响与回归范围 | DDL/配置/上线依赖 | 交付状态 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CHG-001 | AUDIO-N01 / P1正式问题 | 音频补发布把曾发布对象的人工待发布草稿一并发布 | 编辑保留hasPublished=1并置status=0;completed发现人工音频仍返回true;queryInFlight只检查hasPublished而非当前草稿状态,调用完整publishWord/Phrase,锁内重读当前主表、翻译、全部变体发布。 | 音频自动发布使用独立入口,在与人工保存/发布相同的实体锁内重读status和hasPublished;只有当前PUBLISHED且曾发布才复用完整发布。草稿返回未发布结果,PROD旧版本保留。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:108 public PublishResultBo publishAudioWord code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:119 public PublishResultBo publishAudioPhrase code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:218 private void publishReleased | 已发布无音频词有在途TTS;人工上传音频、修改释义仅保存;恢复TTS完成。 | 人工草稿保持待发布;PROD名称/翻译/变体不变;无草稿且当前已发布的对照可补发布。 | 新增EnglishAudioPublishGuardRegressionTest(未执行);锁外旧快照/锁内草稿路径已静态核对。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 词条保存、补录、音频、双库发布 | 两库先建outbox;注册englishDictOutboxReplay。 | 已给完整代码;业务验收待验证 |
| CHG-002 | AUDIO-N02 / P2正式问题 | completed下载永久失败与查询异常均绕过超时收口 | elapsed超时只位于null/running分支;query异常直接return,completed保存null也return并保留任务。每轮ORDER BY taskTime,id LIMIT batch重复拿最老行。 | 总超时判定前置到queryTask之前,覆盖query异常、completed下载失败、running/null全部分支;cancel异常被单独接住,仍计失败并清在途任务。预算内下载失败保持任务,下轮重试。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:262 private boolean handleInFlight | 分别持续query异常、completed下载失败、running;提交时间超过1800秒,再测预算内下载失败。 | 所有超时分支都结束当前尝试并记录失败;cancel异常不阻清理;预算内失败保留任务。 | verification/red-audio.txt -> green-audio.txt;4个超时分支及预算内失败保留实跑。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | TTS状态、下载上传、超时与批次 | 沿用音频回填Job与超时配置。 | 已给完整代码;业务验收待验证 |
| CHG-003 | AUDIO-N03 / P2正式问题 | 失败上限只统计提交抛错,平台终态失败与超时会无限重新提交 | submit成功立即clearFailCount;终态失败和超时只清任务号、不增加失败计数;下轮pending重新提交;永远达不到maxRetry=3。 | submit成功不再清计数;终态failed/cancelled与超时每个已结束尝试计一次,只有成功回填或检测到人工已补齐时清计数。提交API抛错仍计数,7天冷却保持现有契约。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:161 private void submit( code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:262 private boolean handleInFlight | submit一直成功而每次终态failed/cancelled或超时;连续三轮;成功/人工补齐另做对照。 | 失败跨尝试累计,到上限停止提交;只有成功完成才清计数。 | verification/green-audio.txt:终态累计、成功清零;submit计数路径静态核对。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 重试预算、Redis计数、平台配额 | 失败计数7天冷却;验证真实Redis。 | 已给完整代码;业务验收待验证 |
| CHG-004 | AUDIO-N04 / P2正式问题 | 达到提交失败上限的最早ID持续占满候选批次,后续词无法补音频 | SQL先按ID升序LIMIT batch;maxRetry仅业务循环内判断后return,未推进游标/排除冷却行;7天计数TTL内每轮仍选择同一批。 | 三张表增加严格id>afterId的DAO查询入口。每张表用独立Redis游标跨调度推进;页满推进到最后候选,页不足归零,达到失败上限/非自动出身变形也不能永久占住最早一页。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:115 private List<EnglishWordDo> pendingWords code/infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java:260 listAudioPendingSubmitAfterId | batch=1,最小ID连续失败达到上限,恢复TTS后还有第二个健康ID;变体前页均人工来源另验。 | 游标继续前进,后ID能被提交;完整一圈回到起点,不被最早失败页永久占满。 | 接口/实现/调用三处均已给出;未实跑Redis游标和真实数据库分页。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 三表分页、游标、来源过滤 | 新增afterId查询及Redis游标,三表独立。 | 已给完整代码;业务验收待验证 |
| CHG-005 | AUDIO-N05 / P2正式问题 | 异步回填只更新音频列,完整性及教研缓存保持旧值 | markAudioCompleted只set音频和清任务两列,不重算completeness;回填服务不调用教学缓存失效;补发布复制JY完整性0到PROD而不重算。 | 单词条件更新音频时同一SQL重算完整性:word、phonetic非空,存在活翻译且无空词性/译文。音频回填和TEACHING缓存失效、受控补发布事件同JY事务提交;变形按phraseId失效聚合详情。 | code/infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java:307 public boolean markAudioCompleted code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishAudioCompletionTxService.java:25 public boolean complete | 唯一缺音频的词回填成功;先预热JY详情缓存;缺音标的词作为对照。 | 字段齐全词completeness=1且RPC complete=true;教研暖缓存通过outbox失效;仍缺字段词保持0。 | SQL/事务及outbox调用静态核对;完整性数据库UPDATE、缓存实际消费未执行。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 音频回填事务、完整性、JY/PROD缓存 | 两库outbox DDL及持续重放Job。 | 已给完整代码;业务验收待验证 |
| CHG-006 | SEG01 / P2正式问题 | 首次发布早于异步分词完成时,正式库缺行不能由全量自行恢复 | buildPublishableSegments空/非成功即返回空,ensureProdSegmentSkeletons直接return;发布MQ随后refreshQuestion见PROD无行直接false;每日force也共用该缺行门。JY次日成功不自动再次发布。 | 首次发布不要求JY已有NORMAL结果。按正式题型先在JY保留唯一身份,再递减映射PROD骨架;发布及全量刷新缺行路径都调用同一成对建行服务。PROD禁止自行reserveIdentity/自增。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentSkeletonBizService.java:19 public void ensurePair code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentStateTxService.java:29 public QuestionSegmentDo reserveTeachingIdentity code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/SegmentRefreshBizServiceImpl.java:191 private boolean refreshQuestion | 首次同步后暂停compute消费者立即发布;随后恢复消费者,再跑全量;对照已有成功行发布。 | JY/PROD INIT身份成对,不依赖首次NORMAL才建;随后可算成NORMAL,正式库不自由自增。 | JY身份->PROD映射->INIT->刷新接线静态核对;跨库ID实值与调度未跑。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 首次发布、历史缺行、ID映射、全量 | 部署全部分词修复;来源元数据恢复Job持续调度。 | 已给完整代码;业务验收待验证 |
| CHG-007 | SEG02 / P2正式问题 | 正文更新后到异步执行前旧NORMAL序列仍可下发 | AFTER_COMMIT只发ID消息,提交时未把旧行置INIT/FAILED;delivery只验NORMAL而不比题目正文/revision。worker尚未到引擎时,旧成功行被视为可信。正式库重新发布也保留已有骨架行,不降级。 | 保存事务内仅登记事件,提交后独立失信,派生失败不回滚主体。输出生成前记录稳定canonical原FormData输入hash;分词在同一次原输入读取中取得文本、输入hash、本库formDataId/configId,并只绑定确由该输入生成的完整输出JSON hash。缓存URL/输入hash/formId及scalar完整输出hash来自同一OutputData行;输出hash在loadDB解析原输出后生成并存为scalar,Redis默认序列化丢嵌套null时不重算。RPC选URL时固定不可变凭据随本次请求传给delivery。库存四项身份与环境都相同才下发,v1响应不能被后读v2分词替换。缺身份安全省略;恢复Job必须每分钟持续调度,真实重生成、驱逐缓存、force重算,不能完整一轮后停用,绝不给旧URL现场贴新hash。总开关/执行急停在Job取锁前阻断,服务及重生成前复检。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentSaveHookService.java:61 public void hookSave code/application/src/main/java/cn/jojo/cc/biz/service/impl/schema/process/oss/GenerateOutPutDataService.java:84 String segmentSourceHash code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/SegmentPayloadIdentity.java:9 public record SegmentPayloadIdentity code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/DefaultSegmentTextExtractor.java:103 public RichTextSource readRichTextSource code/api/api-server/src/main/java/cn/jojo/cc/server/service/impl/QuestionServiceImpl.java:1847 SelectedSegmentOutput.capture code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentDeliveryBizService.java:76 Map<String, SelectedSegmentOutput> selectedOutputs code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentOutputIdentityRecoveryBizService.java:76 private void recover | 已有成功分词,正文v1改v2;暂停消费者。另先选择v1输出,再发布/重算v2,最后装配响应。 | 实际所选输出来源与分词不一致则segments=null;同v2正常仍下发;题目主体不因派生失败回滚。 | verification/red-response-binding.txt -> green-response-binding.txt:完整生产Delivery/Identity/SelectedOutput+逐字RPC fill方法,外围替身。8项包括先选v1再DB/分词v2必须null、同v2正常、旧缓存拒绝、canonical null/数组/对象序。真实生成器/OSS/双库Job未跑。 verification/green-cache-roundtrip.txt使用真实BO/default serializer验证Redis往返null丢失下scalar仍一致,旧缺scalar拒绝;green-job-pause.txt执行完整Job,暂停0锁/0服务调用,正常对照1/1。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 同步/发布、缓存、输出生成、RPC | 输出来源DDL、元数据cache字段、持续恢复Job。 | 已给完整代码;业务验收待验证 |
| CHG-008 | SEG03 / P2正式问题 | 迟到旧任务删除新revision另一内容位成功行 | upsert/isStale仅保护同(question_id,content_type);旧type1写入可成功;dropObsoleteContentTypes重读当前type2后按ID软删,无revision条件 | 外层先检查executionEnabled,再经独立代理compute事务SELECT真实cc_new_question WHERE del_flag=0 FOR UPDATE,拒绝旧revision,改用锁内重读题目。跨内容位写入/空集合删除同题锁事务。异常先退出代理回滚释放锁,再由外层调用另一个REQUIRES_NEW事务重锁、核当前revision及可用正文指纹,登记FAILED/retry_count/last_error,绝不让失败登记随原事务回滚或持原锁再自锁。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentTxService.java:22 public <T> T compute code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentTxService.java:29 public void recordFailure code/infrastructure/repository/src/main/java/cn/jojo/cc/core/dao/english/QuestionSegmentDao.java:20 lockQuestion( | 旧rev1/MATERIAL任务停在写锁前;新正文rev2/ANSWER_AREA成功后恢复;另用旧空结果试删除。 | 锁内重读真实修订号,拒绝整个旧写删;不插旧类型、不删新类型;失败诊断单独提交。 | verification/red-segment-wave.txt -> green-segment-wave.txt,真实Spring事务拦截器的事件序列为begin/lock/rollback/begin/lock/failed/commit;内存事务manager及DAO替身,非真实MySQL锁/持久化验证。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 跨内容位并发、事务、失败收口 | 真实MySQL题行锁及双源事务需联调。 | 已给完整代码;业务验收待验证 |
| CHG-009 | SEG04 / P2正式问题 | 数字夹英文仍作为整词查询 | isWordChar包含数字,nextToken连续吞入,isWordToken只要求至少一个英文字母 | 数字统一按分隔符;nextToken、isWordToken、边界判定共用ASCII字母规则,避免cat2dog、3look/up4进入词典。撇号和hyphen仍仅两侧字母时保留。 | code/common/src/main/java/cn/jojo/cc/common/utils/EnglishWords.java:31 public static boolean isWordChar | 正文含cat2dog、3look up4、1972、low-temperature、it's。 | 数字作为分隔,cat/dog/look/up独立;纯数字忽略;hyphen和撇号保留合法词内连接。 | verification/red-words.txt -> green-words.txt(实际编译完整生产EnglishWords)。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 词边界、短语首尾、补录输入 | 共享EnglishWords同步改动。 | 已给完整代码;业务验收待验证 |
| CHG-010 | 01 / P2正式问题 | CSV未闭合引号仍吞后续短语,且修文件重导不能纠正已写错误译文 | readRecords不检查EOF的inQuotes,直接addRecord;readPhrases将后续物理行作为允许换行的translation,rows有效且invalid为空。importOnePhrase正常创建后,重导因主词已存在直接skip。 | CSV在EOF仍处于引号内时整文件拒绝,并报记录起始物理行;只允许字段开头开引号,闭引号后只允许逗号/换行/EOF。有效引号换行/双引号转义保留;记录行号改为起始物理行。文件在解析完成后才进入写入。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:324 private List<Record> readRecords | CSV译文开启引号未闭合,后面跟合法记录;对照引号内合法换行和双引号转义。 | EOF拒绝畸形文件,不能吞后行后正常写库;合法CSV解析不回归。 | verification/red-import.txt -> green-import.txt;完整Reader、Normalizer实际隔离javac。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 文件解析、整批导入、重导 | 无新增基础设施。 | 已给完整代码;业务验收待验证 |
| CHG-011 | 02 / P2正式问题 | 非法变形不应部分入库,避免幂等跳过堵死修文件重导 | 非法项只continue内层循环;主短语和got up仍进入rows并写库。修成got up|gotten up重导时,importOnePhrase先getByNormalized命中已建主词而直接false。 | 一个非空variant非法就break且整行不进入rows;撤销该无效行的文件内firstSeen占用,后续同key修正行仍能导入。合法去重及与原形同串的变体丢弃保持。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:163 boolean invalidVariant | get up带got up|起床混合变体;修正为got up|gotten up后重导。 | 非法变体使该短语整行零写;修好后可首次完整入库,避免残缺行占幂等键。 | verification/green-import.txt实际拒整行;新增JUnit覆盖同文件修正同key,未运行。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 变体原子接受、幂等重导 | 无新增基础设施。 | 已给完整代码;业务验收待验证 |
| CHG-012 | 03 / P2正式问题 | 导入显式非法limit仍退化为全量真写 | Param.parse直接getInteger,无正数校验;execute只在limit>0截断,dryRun默认false。显式无效小批参数变成整文件执行。 | 下载前校验显式limit必须为JSON整数类型,严格>0且<=Integer.MAX_VALUE;0、负数、小数、数字字符串、null、溢出拒绝。只有省略limit表示全文件。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictImportBizServiceImpl.java:353 Integer limit = null | import开关开启,显式limit=0/-1/null/小数/溢出/字符串,再测省略及合法正数。 | 非法值在下载/百度/写库前拒绝;省略才表示全量;正数只处理前N合法候选。 | Param原嵌套类隔离javac,green-import.txt;JUnit另列小数/溢出/null/string未运行。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 导入参数、真写范围、dryRun | 保持原XxlJob入口。 | 已给完整代码;业务验收待验证 |
| CHG-013 | 06 / P2正式问题 | 缓存失效异常被吞成发布成功,下一次批量也不再扫描该词 | PROD写成后先把JY status置PUBLISHED;缓存失效异常仅catch日志,无补偿身份。批量按status=UNPUBLISHED扫描,因此下轮不再拿到该行。clearRedisCommonCache内部异步丢投也不一定抛回。 | 发布PROD主从数据与失效事件同一PROD事务提交,JY改为PUBLISHED不会丢补偿身份。Job按retry_after扫描JY和PROD两库,使用真实sendN同步等broker确认;失败留待办并退避,短语版本推进用可抛错可靠入口。delivered_time保留确认记录。内部PublishResultBo保留pending,BatchPublishResultBo增加pendingCacheCount(包含于PROD successCount),Job显式记录待失效数;HTTP Boolean保持既有契约,仅表示PROD提交及失效待办登记,不表示跨实例消费完成。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictPublishProServiceHelper.java:141 englishDictOutboxCoreService.enqueue("WORD_CACHE" code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictOutboxBizService.java:28 public int replay code/api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishDictOutboxJobHandler.java:15 @XxlJob | PROD提交后让broker/Redis失效失败,或退出进程;恢复后跑outbox Job,模拟重复投递。 | 待办与业务事务同提交;失败不丢,确认后记回执;重放幂等。Boolean只确认PROD提交,不冒称消费可见。 | 完整DDL/DO/DAO/Core/Biz/Job/真实调用已接线,未部署DDL/注册Job/运行broker。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | PROD事务、MQ、缓存、重放 | 两库outbox DDL;englishDictOutboxReplay持续注册。 | 已给完整代码;业务验收待验证 |
| CHG-014 | 10 / P2正式问题 | 补录重复消费和重试仍先查百度,再幂等复用已有词 | autoFillOne首步queryBaiduSafely,随后文本锁内saveWordForAutoFill才查已有(含删除行)。TTS已移到异步回填,不再逐次生成或上传。 | 文本锁内先查活词,再查含删除行;存在则直接复用,不查百度。仅确实不存在才查询百度并保存。文本锁释放后进入实体锁内验证自动补录、从未发布且auto_publish_eligible=1,才发布。新建补录置1,任何人工saveWord清0;不再用createTime==updateTime推断未编辑。新列在JY/PROD部署,存量默认0且禁止用旧审计时间回填资格。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/SegmentAutoFillBizServiceImpl.java:143 private void autoFillOne code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:132 public PublishResultBo publishAutoFillWord | 对已发布、人工草稿、导入草稿、已删同词重复消费;新词竞争另验。 | 先幂等预读,已有词不再调百度;锁内再查封并发;人工接管词不自动发布。 | verification/red-eligibility.txt -> green-eligibility.txt:同秒flag0/null旧代码误发布、修复后拒绝;原方法逐字提取,外围替身。百度先幂等路径与人工清flag静态核对;真实百度/MQ未跑。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 补录配额、幂等、人工发布闸门 | auto_publish_eligible迁移先行,历史默认0。 | 已给完整代码;业务验收待验证 |
| CHG-015 | 13 / P2正式问题 | 复合词性验证归一化但落库仍写超长原串 | NotBlank无Size;isSupportedPos临时归一化n.并接受;buildTranslationWrites仍存item.getPos,超过VARCHAR96。 | 翻译实际落库值改为PartOfSpeechEnum的规范串,与原校验使用同一函数;重复n.等输入不再以超长原串写VARCHAR96。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictWriteTxService.java:533 target.setPos(PartOfSpeechEnum.normalizeKnownCodes | pos为n./重复40次;再用全部20种合法词性与未知词性。 | 合法串规范化后实际落库,规范结果不超96;未知值拒绝;不能只校验后写原串。 | 静态确认写入点已用规范值;未执行真实MySQL严格模式。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 保存、词性回显、列宽 | 不扩VARCHAR96,不改枚举业务口径。 | 已给完整代码;业务验收待验证 |
| CHG-016 | 14 / P2正式问题 | 管理搜索未按保存规则压缩连续空白 | 保存归一化单空格,搜索只trim,再按normalized列eq。 | 单词与短语管理精确搜索都使用保存时相同TextNormalizer:小写、trim、连续空白压单空格。 | code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPhraseBizServiceImpl.java:129 TextNormalizer.normalize(bo.getPhrase()), bo.getStatus() code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishWordBizServiceImpl.java:116 TextNormalizer.normalize(bo.getWord()), bo.getStatus() | 保存take off,搜索 take OFF 等连续内部空白;再测空条件、部分take与原词。 | 搜索与保存共用normalize,归一化后精确匹配;不扩大为模糊或变体搜索。 | TextNormalizer复用和两个调用点静态核对;未运行管理API。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 单词/短语分页搜索、查重 | 无新增基础设施。 | 已给完整代码;业务验收待验证 |
| CHG-017 | 23 / P2正式问题 | 新批量发布Job接受limit=0和空参数段,意外发布全部待发布词 | isNumeric接受0,parse返回limit0,nextBatchSize把<=0解释为无限;只含逗号的raw.split结果无有效键,也返回type=all/limit0。本类注释明确配错抛错,但未区分“省略limit”与“显式0”。 | 纯数字/键值limit都严格正整数,非空参数必须至少解析一个有效键值;显式0和纯逗号参数在任何publish调用前失败。空参数及未给limit仍按原规则全量。 | code/api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishBatchPublishTask.java:190 private static int positiveLimit | 批量发布250词,传limit=0/纯0/仅逗号/负数/非法type;对照150词和2短语。 | 所有非法显式参数零发布;省略才全量;合法150/2严格限量。 | verification/red-batch.txt -> green-batch.txt;完整Task实际隔离javac,非法参数零写,150/2限量正确。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 批量发布参数、跨库真写 | 保留原Job与HTTP契约。 | 已给完整代码;业务验收待验证 |
| CHG-018 | COND-IPA / 条件边界 | COND-IPA | 见原分析条件风险;不把策略歧义冒充已发生故障。 | 识别Unicode IPA字符块及有边界的/ASCII音标/;跳过括号中可识别IPA,不把and/or中的斜杠当音标起点;[ordinary words]保留。无法可靠区分无标记纯ASCII音标,明确保留。 DefaultSegmentTextExtractor保留原IPA token交统一扫描器处理,避免先剔IPA字母留下ASCII碎片;旧fixture已相应迁移。 | code/common/src/main/java/cn/jojo/cc/common/utils/EnglishWords.java:195 private static int phoneticSpanEnd | 正文含ˈfəʊnəm、and/or、普通[cat],另构造有明确标记的纯ASCII音标。 | 可识别音标整token排除,无f/n/m残片;and/or及普通正文保留;无标记纯ASCII歧义明确保留边界。 | verification/green-words.txt覆盖/kæt/、[dɒɡ]、/kat/、and/or与普通方括号;未标记纯ASCII仍是条件边界。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 富文本抽取、词边界、补录 | 输入若为纯ASCII音标需明确语义标记。 | 已给完整代码;业务验收待验证 |
| CHG-019 | COND-INDEX / 条件边界 | COND-INDEX | 见原分析条件风险;不把策略歧义冒充已发生故障。 | 保留read readySnapshot的旧索引降级;持久化分词专用freshSnapshotForWrite强制版本比对,再确认句柄version等于实际远端,Redis不可读/重装失败/unknown版本返回null跳过落库及补录。 | code/application/src/main/java/cn/jojo/cc/biz/cache/english/EnglishPhraseIndexCacheService.java:190 public PhraseIndexSnapshot freshSnapshotForWrite code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentBizServiceImpl.java:269 freshSnapshotForWrite(segmentSource) | 远端短语版本变更,重装DB失败/Redis版本不可读;对照查询读与落库写。 | 读可降级旧表;写侧无法证明新鲜度则不持久化旧块;恢复后重新计算。 | 读写入口分离和校验静态核对;Redis失败与多实例重装未实跑。 | JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成;具体探针只证明列明分支。 | 索引、保存/发布/全量写入口 | 版本检查配置与重装退避;真实故障注入待验。 | 已给完整代码;业务验收待验证 |
| CHG-020 | REV-R1 / 复审补充回归 | 题表锁字段契约 | 旧题表错用delete_time | 使用cc_new_question.del_flag,保持新分词表delete_time。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 题表锁字段契约:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 使用cc_new_question.del_flag,保持新分词表delete_time。 | verification/green-sql-contract.txt(文件已存在) | SQLite仅验证列/筛选契约,不证明MySQL FOR UPDATE锁。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-021 | REV-R2 / 复审补充回归 | 派生失败不回滚题目同步 | 同步事务内派生写失败污染rollback-only | hook事务内只发事件;提交后独立失信,失败仍投计算;响应来源门保护读侧。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 派生失败不回滚题目同步:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | hook事务内只发事件;提交后独立失信,失败仍投计算;响应来源门保护读侧。 | verification/green-segment-wave.txt(文件已存在) | 题目同步提交、派生表不可用、消费者暂停需业务联调。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-022 | REV-R3 / 复审补充回归 | 失败登记在原事务退出后提交 | FAILED写后throw导致一起回滚 | 计算Tx回滚释放题锁后,新Tx重锁核revision/指纹再登记FAILED。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 失败登记在原事务退出后提交:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 计算Tx回滚释放题锁后,新Tx重锁核revision/指纹再登记FAILED。 | verification/green-segment-wave.txt(文件已存在) | 真实Spring+内存事务manager不等于真实JTA/MySQL并发。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-023 | REV-R5 / 复审补充回归 | 同秒人工接管保护 | createTime==updateTime无法证明未编辑 | 显式auto_publish_eligible:历史0、新补录1、人工保存清0;锁内读资格。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 同秒人工接管保护:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 显式auto_publish_eligible:历史0、新补录1、人工保存清0;锁内读资格。 | verification/green-eligibility.txt(文件已存在) | 两库新列与历史迁移;完整人工保存链需IDE和业务验证。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-024 | SOURCE-01 / 复审补充回归 | 响应选择后发生更新 | 仅核新DB正文仍会给旧响应配新序列 | 生成来源、输出scalar hash、本次选定URL/来源和分词来源绑定;v1/v2不同即省略。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 响应选择后发生更新:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 生成来源、输出scalar hash、本次选定URL/来源和分词来源绑定;v1/v2不同即省略。 | verification/green-response-binding.txt(文件已存在) | 真实输出生成/资源跨库/缓存失效链尚未联调。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-025 | SOURCE-02 / 复审补充回归 | 缓存JSON往返保留身份 | nested null丢失可能使同一正常输出hash失配 | 从同一OutputData生成scalar输出hash并缓存,capture只复制scalar。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 缓存JSON往返保留身份:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 从同一OutputData生成scalar输出hash并缓存,capture只复制scalar。 | verification/green-cache-roundtrip.txt(文件已存在) | 旧无scalar缓存安全失信;须真实生成和驱逐恢复,不贴新DBhash。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |
| CHG-026 | SOURCE-03 / 复审补充回归 | 持续恢复与急停 | 把一次迁移误当未来发布的永久恢复者 | 恢复Job持续调度;每库50滚动扫描;执行前和每题重生成前检查总开关/急停。 | 见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json | 持续恢复与急停:按对应验证脚本/VERIFICATION所列前置执行,并覆盖正常对照。 | 恢复Job持续调度;每库50滚动扫描;执行前和每题重生成前检查总开关/急停。 | VERIFICATION.md(文件已存在) | 正常扫描周期按ceil(N/50)+1个成功调度轮估算;锁跳过/暂停/失败不保证固定墙钟上界。 | 来源一致性、事务隔离、旧数据滚动升级 | 见VERIFICATION.md;持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。 | 代码与复审结论见最终记录;不等于业务通过 |