---
name: "fk-field-detection"
version: "1.0.0"
origin: "captured"
generation: 0
parent_skill_ids: []
status: "stable"
description: "外键字段检测与补全技能。扫描实体/字段列表，识别以 Id 结尾的 Long 型字段，推断引用实体并自动补全 foreignKey 属性链（refModule, refEntity, refLabelField, selectorMode）。在代码生成前和代码审查时触发，确保 FK 字段在 VO/Wrapper/列表/表单四层都有正确的名称翻译和选择器组件。"
trigger_phases: ["architecture", "implementation"]
applicable_agents: ["Copilot Architect", "Copilot Code Gen Specialist", "Copilot Implementation"]
priority: 10
---

# 外键字段检测与补全 (fk-field-detection)

## 目标

在代码生成和代码审查阶段，自动识别外键字段并确保以下四层一致性：
1. **VO 层**：存在 `{fieldName}Name`（String）翻译字段
2. **Wrapper 层**：`entityVO()` 中包含引用实体查询 + 名称填充逻辑
3. **前端列表**：Option 中使用 `{fieldName}Name` 作为列 prop，而非原始 ID
4. **前端表单**：使用 `ImLookupSelector` 组件绑定 ID + 显示名称

并为下游 UX/UI 与实现阶段输出明确的 `UI handoff contract`：
- FK 字段属于 `FK/reference`
- 推荐显示状态（list / create / edit / detail / filter）
- 推荐 selectorMode（`lookup` / `select` / `cascader`）
- 推荐 display contract（`{fieldName}Name` 或推断出的 label 字段）

## 检测规则

### 规则 1：名称模式匹配

以下命名模式的 `Long` 类型字段视为候选外键：
- `*Id` 结尾（如 `enterpriseId`, `categoryId`, `parentId`, `userId`）
- `*Code` 结尾且引用外部编码体系（需人工确认）

**排除项**（不视为外键）：
- `id` — 主键本身
- `tenantId` — 租户标识（系统字段，由框架自动处理）
- `createUser`, `updateUser` — 审计字段（由 `TenantEntity`/`BaseEntity` 继承）
- `isDeleted`, `status` — 布尔/枚举状态字段

### 规则 2：引用实体推断

从字段名推断引用实体：
| 字段名模式 | 推断引用实体 | 推断 refLabelField |
|-----------|-------------|-------------------|
| `enterpriseId` | `Enterprise` | `enterpriseName` |
| `userId` / `ownerId` / `managerId` | `User` | `name` / `realName` |
| `deptId` | `Dept` | `deptName` |
| `roleId` | `Role` | `roleName` |
| `categoryId` | 需确认（Dict / Category） | `dictValue` / `categoryName` |
| `parentId` | 同实体自引用 | 同实体 name 字段 |
| `productId` | `Product` | `productName` |
| `materialId` | `Material` | `materialName` |
| `supplierId` | `Enterprise`（供应商） | `enterpriseName` |
| `customerId` | `Enterprise`（客户） | `enterpriseName` |
| `assetId` | `AssetLedger` | `assetName` |
| `locationId` | `FunctionalLocation` | `locationName` |

### 规则 3：选择器模式推断

| 条件 | selectorMode |
|------|-------------|
| 引用实体数据量 > 50 条（默认假设） | `lookup` — ImLookupSelector 弹窗 |
| 引用实体为字典/枚举（≤50 条） | `select` — el-select 下拉 |
| 引用实体为树形结构 | `cascader` — el-cascader 级联 |
| `parentId` 自引用 | `cascader` — 树形级联选择 |

### 规则 4：模块推断

从已知模块注册表推断 `refModule`：
| 实体 | refModule |
|------|----------|
| Enterprise / Bank / Qualification / Contact 等 | `partner` |
| User / Role / Dept / Dict / Menu / Param 等 | `system` |
| AssetLedger / FunctionalLocation 等 | `eam` |
| Product / Material 等 | `mdm` |

> 未知实体：标记为 `UNKNOWN`，提示用户手动指定。

## 执行流程

### 代码生成阶段 (pre-generation)

```
INPUT: 字段列表 (name, type, comment)
OUTPUT: 增强后的字段列表 (增加 foreignKey, refModule, refEntity, refLabelField, selectorMode, uiHandoff)

1. 遍历所有字段
2. 对每个 Long 类型 + *Id 命名模式的字段：
   a. 排除系统字段（id, tenantId, createUser, updateUser）
   b. 从字段名推断引用实体和模块
   c. 标记 foreignKey=true
   d. 填充 refModule, refEntity, refLabelField, selectorMode
   e. 对无法自动推断的字段，标记为 NEEDS_CONFIRM
3. 输出增强字段列表 + 需确认项 + UI handoff contract
```

### 代码审查阶段 (post-implementation)

```
INPUT: Entity 源码, VO 源码, Wrapper 源码, Option JS, Form Vue
OUTPUT: 缺陷清单 (missing VO name field, missing Wrapper translation, missing list name column, missing form selector, wrong selector mode)

1. 扫描 Entity 中所有 Long 类型 *Id 字段
2. 对每个检测到的 FK 字段，检查：
   a. VO 中是否存在 {fieldName}Name 字段 → 缺失则报 "MISSING_VO_NAME"
   b. Wrapper 中是否有对应翻译逻辑 → 缺失则报 "MISSING_WRAPPER_TRANSLATION"
   c. Option JS 中是否使用 {fieldName}Name 作为列 prop → 使用原始 ID 则报 "RAW_ID_IN_LIST"
   d. Form Vue 中是否按 selectorMode 使用正确组件 → 使用普通 input 或错误 selector 则报 "WRONG_SELECTOR_MODE"
3. 输出缺陷清单 + 修复建议 + handoff 偏差说明
```

## 输出格式

### 检测报告

```markdown
## FK 字段检测报告

| 字段 | 类型 | 引用实体 | refModule | refLabelField | selectorMode | 状态 |
|------|------|---------|----------|--------------|-------------|------|
| enterpriseId | Long | Enterprise | partner | enterpriseName | lookup | ✅ AUTO |
| categoryId | Long | Dict? | system? | dictValue? | select? | ⚠️ NEEDS_CONFIRM |
| parentId | Long | self | - | name | cascader | ✅ AUTO |

### 需确认项
- `categoryId`: 无法确定引用的是 Dict 还是独立 Category 表，请指定 refEntity。

### UI handoff contract
| 字段 | 分类 | list/detail 显示 | create/edit 组件 | filter 建议 | 备注 |
|------|------|------------------|------------------|------------|------|
| enterpriseId | FK/reference | enterpriseIdName | ImLookupSelector | lookup | 禁止显示原始 ID |
| parentId | FK/reference | parentName | cascader | cascader | 树形结构 |
```

### 四层一致性检查报告

```markdown
## FK 四层一致性检查

| 字段 | VO Name | Wrapper | List Column | Form Selector | Handoff | 状态 |
|------|---------|---------|-------------|---------------|---------|------|
| enterpriseId | ✅ enterpriseIdName | ✅ getById+setName | ✅ prop='enterpriseIdName' | ✅ ImLookupSelector | ✅ consumed | PASS |
| categoryId | ❌ 缺失 | ❌ 缺失 | ❌ 显示原始ID | ❌ el-input | ❌ ignored | FAIL (5/5) |

### 修复建议
1. VO: 添加 `private String categoryIdName;`
2. Wrapper: 添加 `DictCache.getValue(...)` 或 Service 查询
3. Option: `prop: 'categoryId'` → `prop: 'categoryIdName'`
4. Form: `<el-input>` → 按 selectorMode 改为 `<ImLookupSelector>` / `<el-select>` / cascader 组件
5. Handoff: 将 FK 字段补入 UI handoff contract，并要求下游 list/detail/form 消费该约束
```

## 触发条件

- 代码生成器 (blade-design) 处理字段列表时
- Code Gen Specialist agent 生成 CRUD 代码前
- Code Review agent 审查含 FK 字段的代码时
- Schema Guardian 检查四层一致性时
- 手动调用：用户说 "检查外键字段" / "FK 检测" / "外键一致性"
