扫描范围由提交历史决定:上次审查(2026-08-17,候选 #1/#2 已落地为 ADR-0030/0031,#3/#4/#5 已否决并记录在 ADR-0022 §4 / ADR-0030,本次不再重提)之后的全部改动。 热点 = 客户模块返工(票 01-12)、商机 board / 建档字段、字典两级树、crm-rule 客户规则子域。领域词汇取自各模块 CONTEXT.md, 架构词汇按 module / interface / depth / seam / adapter / leverage / locality 口径使用。
同一份 WHERE(workspace × 内置视图 × 筛选)在 CustomerMapper 里手写了三份,board 与列表已静默分叉。商机侧同场景已有单一事实源范式可对照。
12 个集成测试类各自内联整套 DDL + MyBatis 自举,其中一份已漂移。加一列要改 12 个文件,crm-opportunity 的表结构变更也会波及客户侧测试。
6 个 45 行的 CommandLineRunner 各包装一条纯数据(菜单名/路径/排序)。@Order 全局手工编号已出现两处撞号。
列偏好 / 自定义视图 / 形态偏好三套平行 entity+mapper+service+controller。与 CONTEXT.md 既有拍板「不做全平台通用化」冲突,仅在前端出现第 4 种偏好时才值得重开。
「客户总览 / 我的客户 / 客户公海」三个 workspace 的数据集条件(内置视图 ASSIGNED/COLLABORATING/FOLLOW_UP_DUE/FOCUSED/RECENT × 公共筛选 × saved-view 条件)在 同一张 Mapper 里逐字写三遍:pageWorkspace 的 overview 分支、mine/pool 分支、以及 boardSummary。 javadoc 自述「改动须两处同步」——实际是三处。删掉三份换成一份共享片段,复杂度会集中而不是搬家。
boardSummary 相对 pageWorkspace 静默缺少 3 个条件:FOLLOW_UP_DUE(待跟进视图 EXISTS)、 industryCode、provinceCode。而 boardCards 走的是 pageWorkspace(带全这些条件)。 结果:看板列头汇总数 ≠ 同参数下卡片数。javadoc 声称「WHERE 口径与 pageWorkspace 完全一致」——代码 disagrees。 无任何测试覆盖该一致性(返工票 03 只测了 pool 拒绝)。这正是「同一份列表数据三种渲染、同一查询」约定(商机侧票 06 D-15 明文化:summary 与 cards 必须同口径,视图语义不得各写各的)在客户侧的违例。
把 pageWorkspace 与 boardSummary 两条注解 SQL 迁入同 namespace 的 XML mapper 文件, 公共 WHERE 提成 <sql id> 片段,各处 <include>。 手写 SQL 本身是正当的(子查询包裹绕 DataScopeInterceptor、RECENT 标量子查询列、saved-view 白名单列 ${} 都需要它)——要收敛的不是「写 SQL」,是「写三遍」。 顺手裁决(grilling 议题):/api/customer/page(基础分页,wrapper 版)与 /api/customer/workspace/page 同挂三个页面 tag 的双轨是否还需要。
每个集成测试类都手搓同一套 130 行自举(JdbcDataSource + MybatisConfiguration + 分页/乐观锁拦截器 + MetaObjectFillHandler + execute DDL), 再各自内联一份 CREATE TABLE customer(55+ 列)及各子表。这份自举本身是一个没人拥有的 module:12 个副本、零测试(它是测试)、无单一事实源。 deletion test:把 12 份脚手架删掉换成 1 个测试基建类,复杂度大幅集中——这正是信号本身。
客户表加一列(如 D-05 的 is_biz_negotiated)要同步改 12 个测试文件——本次已漏改 1 个。 更隐蔽的是反向依赖:CustomerMapper.pageWorkspace 的 opportunity_count 标量子查询 join 了 crm-opportunity 的表,商机表结构变更时 客户侧测试的复制 DDL 必须同步改(javadoc 已自认「商机表结构变更须同步本处与 H2 测试 SCHEMA」)。测试面不是深 module 的 interface,而是 12 个各说各话的影子 schema。
在 crm-customer/src/test/resources/ 落一份 schema.sql(customer 域全部表 + 按 H2 方言), 加一个 CustomerH2Harness 测试基建类持有自举(DataSource / MybatisConfiguration / 拦截器 / 建表执行),12 个测试类改为继承/组合它。 只做客户域,不动 lead/opportunity 的既有测试形态(那边没有内联 DDL 问题)。crm-opportunity 表的 DDL 是否也抽进 harness(跨域耦合已既成事实)作为 grilling 议题。
每个类 45 行,其中 40 行是 javadoc/样板,信息量 = 一个 PermissionModuleDescriptor 常量(菜单名/路径/组件/排序/角色)。 interface 与 implementation 几乎一样宽——典型的 shallow module。新增一个规则菜单 = 再抄一个类 + 手挑一个全局 @Order 号。 @Order 编号是全工程手工分配的隐式协议,现已两处撞号(14×2、12×2)——撞号后种子顺序不确定。
crm-rule 内 6 个种子类合并为一个 RulePermissionSeedInitializer,持有 List<PermissionModuleDescriptor>, 顺序即列表顺序。各域 javadoc 里的决策注释随数据归位。可顺带把撞号的 @Order 清掉(合并后 crm-rule 只占 1 个号)。 dict / auth / lead 各自的 Initializer 形态不同(有真实逻辑),不动。
crm-preference/CONTEXT.md 明文:「本次只做到够用的最小通用深度,不做全平台通用化打磨」。本候选与该拍板冲突, 但摩擦尚未真实化(三栈各自稳定、消费方按 scope 接入无改动诉求)。列出仅为完整性 + 标注重开条件:前端出现第 4 种偏好(如卡片密度/排序方案)时再议。
三栈的「同构」只到 CRUD 骨架层;各自领域规则(保存才落库 / 值域白名单 / 默认视图互斥 + 翻译器归业务方)不同且已各自内聚。 抽公共泛型基座会把三种差异塞进一个宽 interface——正是上次审查否决 DisplayNameEnricher 的同款理由(宽接口薄实现 = shallow module)。 deletion test 不通过:删掉任何一栈,复杂度只是搬家到另外两栈的特判里。
理由:(a) 它已经是一个活的缺陷(看板汇总 vs 卡片计数不一致,且 javadoc 撒谎说一致);(b) 修复面窄、风险低(SQL 重组,签名不变); (c) 商机侧 OpportunityViewFilter 已验证同款深化路径,等于有参照实现;(d) 它是 #2 的前置——口径唯一后,#2 的 harness 里可以直接写 「boardSummary == pageWorkspace 计数」守恒断言,把「三种渲染同一数据」从注释变成可执行契约。做完 #1 顺手补 D-15 守恒测试,再清 #2。 #3 独立小票随时可做;#4 挂起重开条件,不投入。