返回博客

字段别名方向判断陷阱:一行 alias 让条件分支永远为 false

某后台管理系统有一个字段联动逻辑:用户编辑「业务字段 A」时,服务层需要自动推导并同步写入另一张表的「字段 B」。功能上线后,用户反馈「改了 A,B 却没变」——翻遍日志,接口返回正常,数据库却纹丝不动。

代码 review 了三遍,那个 if 条件分支看起来完全正确。

现象

服务层有一段级联写入逻辑,形如:

if (snakeField === 'title_series' && extTable) {
  const code = await lookupDerivedCode(value)
  await updateExtTable('ext_title', { review_series: code })
}

接口调用链路正常,extTable 不为空,但这段代码从未执行过。ext_title.review_series 永远是初始值,无论用户怎么改「标题系列」字段。

根因

服务层在处理字段名时经历了两步转换:

// 第一步:前端 camelCase → DB snake_case
const rawSnakeField = camelToSnake(userInputField)   // 'title_series'
 
// 第二步:查别名表,兼容历史列名重命名
const snakeField = FIELD_ALIAS[rawSnakeField] || rawSnakeField
// FIELD_ALIAS['title_series'] = 'review_series'
// 所以 snakeField = 'review_series'

经过别名映射后,两个变量的语义完全不同:

| 变量 | 语义 | 应用场景 | |------|------|----------| | rawSnakeField | 业务字段名(alias KEY) | 业务逻辑分支、级联规则判断 | | snakeField | 数据库列名(alias VALUE) | SQL 写入、受保护字段集合成员判断 |

FIELD_ALIAS['title_series'] = 'review_series' 时,snakeField 已经是 'review_series'。再用它去比较 'title_series'——永远是 false

坑:alias 之后用错变量,条件永久静默失效

FIELD_ALIAS 的 key 是业务名,value 是数据库列名。映射之后:

  • 想判断「用户动了哪个业务字段」→ 必须用 rawSnakeField(alias KEY)
  • 想判断「要写哪个 DB 列」→ 才用 snakeField(alias VALUE)

snakeField === 'title_series' 判断业务意图,而 'title_series' 是 alias KEY,根本不可能出现在 snakeField 里。分支永远不执行,没有报错,没有警告,只有数据库里一列永远过期的值。

正解

一字之差,换回 rawSnakeField

- if (snakeField === 'title_series' && extTable) {
+ if (rawSnakeField === 'title_series' && extTable) {
    const code = await lookupDerivedCode(value)
    await updateExtTable('ext_title', { review_series: code })
  }

判断规则:字面量属于哪个语义域,就用那个域的变量。

// 业务字段名(alias KEY)做判断 → rawSnakeField
if (rawSnakeField === 'title_series') { /* 级联逻辑 */ }
 
// DB 列名(alias VALUE)做判断 → snakeField(这些是正确用法,不要动)
if (PROTECTED_FIELDS.includes(snakeField)) return 403
if (MAIN_TABLE_FIELDS.includes(snakeField)) writeToMain(...)
await db.update({ last_edit_field: snakeField })   // 存 DB 列名,正确
正解:字面量语义域决定用哪个变量

把别名表两侧想成两条泳道:

  • KEY 泳道(业务名)→ 只用 raw* 变量
  • VALUE 泳道(DB 列名)→ 只用 alias 后的变量

在条件分支里,先问「这个字面量是 KEY 还是 VALUE」,再选变量——两个问题一致才是对的。

排查方法

修完之后,同文件全扫——这类笔误往往成对出现,同一个作者在其他分支里可能犯了相同错误:

grep -n "snakeField ===" service.ts
grep -n "snakeField !==" service.ts
grep -n "includes(snakeField)" service.ts
grep -n "switch (snakeField)" service.ts

对每一处命中,问两个问题:右侧字面量是业务名还是 DB 列名?左侧变量是 raw 还是 aliased?不匹配就是 bug。

一句话外卖

字段别名映射后产生的两个变量语义不同,哪个变量做判断取决于右侧字面量所在的语义域,不能混用。