## [ERR-20260417-004] Prisma 复合唯一键的 where 子句必须用 scope_key 对象

**日期**: 2026-04-17
**类别**: Prisma / 数据模型重构
**严重度**: 低（错误信息清晰，但容易踩）

### 问题描述

把 `RobotFieldDef.key` 的单列唯一约束改成 `@@unique([scope, key])` 复合唯一后，原来的 `prisma.robotFieldDef.upsert({ where: { key: 'xxx' }, ... })` 全部报错：

```
PrismaClientValidationError: Invalid `prisma.robotFieldDef.upsert()` invocation
Unknown argument `key` in where. Valid arguments are: id, scope_key, AND, OR, ...
```

### 根本原因

Prisma 为 `@@unique([a, b])` 生成的 `WhereUniqueInput` 字段名是 **两个字段用下划线拼起来** — `scope_key`（而不是 `scope_and_key` / `scopeKey` 等）。使用时：

```ts
where: { scope_key: { scope: 'unit', key: 'supplierSn' } }
```

原来的 `key` 不再是单独的 UNIQUE，就无法作为 WhereUnique 使用。

### 绕行方案

把所有 `where: { key: ... }` 改为：
```ts
where: { scope_key: { scope: 'unit', key: ... } }
```

或者用 `findFirst` + `update`：
```ts
const existing = await prisma.robotFieldDef.findFirst({ where: { scope: 'unit', key } });
if (existing) await prisma.robotFieldDef.update({ where: { id: existing.id }, data });
```

### 预防措施

改复合唯一索引时，先全项目搜 `where: { <旧单列唯一键>:` 定位所有受影响点。这种错误只在运行时出现，TS 编译期能捕获（Prisma 类型是严格的）——所以**跑构建就能发现**，但测试类的 ts-jest 可能在实际调用时才报。

**判断规则**：改唯一约束前 `grep -rn "where: { <列名>:" .` 找出所有点。

### Metadata
- Reproducible: yes
- Related Files: backend/prisma/schema/*.prisma, testing/backend/integration/**, backend/src/**/*.service.ts
