# 平台 ID 转名称（IdToName）使用规范

## 核心原则

- 使用 "字段注解 + 方法开关注解 + AOP" 自动完成 ID 到名称的填充
- 只做展示增强：原始 ID 字段保留，不覆盖
- 名称字段必须通过 `alias` 显式指定，禁止隐式推断
- 类型必须来自统一枚举：`USER`、`DEPT`、`TENANT`、`PERSON`
- 批量场景必须去重后查询，禁止逐条远程调用

---

## 组件角色

| 组件 | 位置 | 职责 |
|------|------|------|
| `@IdToName` | 字段注解 | 标注 ID 字段，定义转换类型和名称落地字段 |
| `@EnableIdToName` | 方法注解 | 开启该方法返回值的自动填充 |
| `IdToNameType` | 枚举 | 定义 ID 类型：USER / DEPT / TENANT / PERSON |
| `IdToNameAspect` | AOP 切面 | 方法返回后自动执行批量查询与回填 |
| `BaseIdToNameClient` | Feign 客户端 | 按类型批量查询名称，顺序与入参一致 |

---

## 注解规范

### 字段注解 `@IdToName`

```java
@IdToName(alias = "userName", type = IdToNameType.USER)
private Long userId;

private String userName;
```

| 属性 | 是否必填 | 说明 |
|------|----------|------|
| `alias` | 必填 | 同对象内承载名称的字段名，为空则跳过回填 |
| `type` | 选填 | 默认 `USER`，可选 `DEPT` / `TENANT` / `PERSON` |

约束：
- 只能标注在 `Long` 类型字段上
- `alias` 必须指向同对象内已声明的可写字段
- `alias` 目标字段建议为 `String`

### 方法注解 `@EnableIdToName`

```java
@EnableIdToName
public List<DemoVO> queryList(...) { ... }
```

约束：
- 只标注在对外返回数据的方法上
- 切面在方法返回后自动处理，无需手动调用

---

## 返回值支持范围

| 返回类型 | 处理方式 |
|----------|----------|
| 单对象 `Object` | 直接处理该对象的字段 |
| 集合 `List<?>` | 遍历列表每个元素 |
| 分页 `Pagination<?>` | 从 `pagination.getData()` 取集合后处理 |

不在以上范围内的返回结构，不保证转换。

---

## AI 执行步骤（逐步指令）

### Step 1：定位入口方法
- 找到对外返回数据的 Server 或 Controller 方法
- 在方法上添加 `@EnableIdToName`

### Step 2：标注 ID 字段
- 在返回对象（VO/Bean）中找到需要转换的 ID 字段
- 为每个字段添加 `@IdToName`，指定 `type` 和 `alias`

### Step 3：准备名称承载字段
- 在同一对象中声明 `alias` 对应的 `String` 字段
- 字段必须可写（反射可 set，不能是 final）

### Step 4：按类型收集 ID（切面内部逻辑，AI 理解用）
- 扫描返回对象的所有字段
- 按 `USER / DEPT / TENANT / PERSON` 分组收集 ID 值
- 过滤 null，执行 `distinct()` 去重

### Step 5：批量查询名称（切面内部逻辑，AI 理解用）
- 对每个非空类型 ID 列表执行一次批量查询
- 生成 `Map<Long, String>` 映射（id → name）

### Step 6：回填名称（切面内部逻辑，AI 理解用）
- 再次遍历对象字段
- 根据 `type + id` 从映射取 name
- 根据 `alias` 找目标字段并写入

### Step 7：容错处理（切面内部逻辑，AI 理解用）
- 远程查询失败或返回码非成功：仅记录日志，不中断主流程
- 名称缺失时保留 null，不抛异常

---


## 禁止事项
1. 禁止仅加 `@IdToName` 不加 `@EnableIdToName`（注解不生效）
2. 禁止 `alias` 为空字符串（回填会被跳过）
3. 禁止 ID 字段为非 `Long` 类型（反射取值会 ClassCastException）
4. 禁止逐条远程查名（N+1 调用）
5. 禁止在转换失败时抛异常影响主业务返回
6. 禁止使用枚举之外的类型值

---

## AI 自检清单（生成代码后逐项验证）

- [ ] 方法是否添加 `@EnableIdToName`
- [ ] 每个需转换 ID 字段是否添加 `@IdToName`
- [ ] `alias` 是否非空且目标字段存在于同对象
- [ ] ID 字段类型是否为 `Long`
- [ ] 是否按类型分组并去重查询（无 N+1）
- [ ] 返回类型是否在支持范围内（单对象 / List / Pagination）
- [ ] 失败是否降级为日志，不中断主流程
- [ ] 原始 ID 字段是否未被修改

---

## 最小模板

```java
// VO 定义
public class DemoVO {
    @IdToName(alias = "creatorName", type = IdToNameType.USER)
    private Long creatorId;
    private String creatorName;

    @IdToName(alias = "deptName", type = IdToNameType.DEPT)
    private Long deptId;
    private String deptName;
}

// Server 方法
@EnableIdToName
public List<DemoVO> queryList(QueryBean query) {
    return demoDao.selectList(query);
}
```
