# 元数据审核流 API 使用说明（疑似冗余 / 元数据变动）

本文档面向前端开发人员，用于开发“元数据疑似冗余/变动”审核工作台页面。

## 基本信息
- **蓝图前缀**：后端注册为 `app.register_blueprint(meta_bp, url_prefix='/api/meta')`，因此以下接口完整路径均以 `/api/meta` 开头。
- **返回格式**：统一为

```json
{
  "code": 200,
  "message": "操作成功",
  "data": {}
}
```

失败时：

```json
{
  "code": 500,
  "message": "错误原因",
  "data": null
}
```

## 业务概念

### 记录类型 `record_type`
- `redundancy`：疑似冗余（解析出的新元数据与现有元数据“存在候选但不完全一致”）。
- `change`：疑似变动（某业务领域当前已关联的元数据，与新解析结果不一致；或新解析中缺失了旧关联字段）。

### 状态 `status`
- `pending`：待处理
- `resolved`：已处理
- `ignored`：已忽略

### 处理动作 `action`
`POST /api/meta/review/resolve` 的 `action` 取值：
- `alias`：将“新解析元数据”指定为某个候选元数据的别名（建立业务领域到候选元数据的关联，并在关系上记录别名信息）。
- `create_new`：将“新解析元数据”确认为全新元数据（要求传入区分后的中文名）。
- `accept_change`：接受变动，将新快照写回目标元数据，并写入版本历史。
- `reject_change`：拒绝变动，仅标记记录已处理。
- `ignore`：忽略该记录，仅标记 ignored。

## 数据结构说明

### MetadataReviewRecord（审核记录）
字段说明（前端通常只关心以下字段）：
- `id`：审核记录ID（PG）
- `record_type`：`redundancy` / `change`
- `business_domain_id`：触发该记录的业务领域 Neo4j ID（用于回到业务领域详情页）
- `new_meta`：新解析元数据快照（示例：`{name_zh,name_en,data_type,tag_ids}`）
- `candidates`：候选列表（redundancy 场景使用；每项包含 `candidate_meta_id`、候选快照、差异字段）
- `old_meta`：旧快照（change 场景使用；包含 `meta_id` 与旧快照/差异字段）
- `status`：`pending/resolved/ignored`
- `resolution_action` / `resolution_payload`：处理动作与处理参数
- `impact_graph`：仅在 detail 接口里返回（change 场景，若能定位到 `meta_id`）

### impact_graph（影响关系图谱）
`GET /api/meta/review/detail` 在变动记录场景可能返回：

```json
{
  "nodes": [{ "id": 1, "name_zh": "...", "name_en": "...", "...": "..." }],
  "relationships": [{ "id": 10, "source": 1, "target": 2, "type": "REL" }]
}
```

该结构与现有元数据影响图谱返回一致，可直接复用前端图谱组件。

## 接口 1：审核记录列表

### URL
- `POST /api/meta/review/list`

### 请求体（JSON）
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| current | int | 否 | 页码，默认 1 |
| size | int | 否 | 每页数量，默认 10 |
| record_type | string | 否 | `redundancy` 或 `change` |
| status | string | 否 | `pending` / `resolved` / `ignored` |
| business_domain_id | int | 否 | 业务领域 Neo4j ID |
| keyword | string | 否 | 关键字，匹配 `new_meta.name_zh` 或 `new_meta.name_en`（contains） |

示例：

```json
{
  "current": 1,
  "size": 10,
  "status": "pending",
  "record_type": "redundancy",
  "keyword": "科室"
}
```

### 响应 data
```json
{
  "records": [ { "...": "MetadataReviewRecord.to_dict()" } ],
  "total": 100,
  "size": 10,
  "current": 1
}
```

### 前端建议
- 页面可默认筛选 `status=pending`。
- 列表展示建议字段：`id / record_type / new_meta.name_zh / new_meta.name_en / business_domain_id / created_at / status`。

## 接口 2：审核记录详情（含变动影响图谱）

### URL
- `GET /api/meta/review/detail?id={id}`

### 响应 data
返回审核记录完整字段（`to_dict()`）并追加 `impact_graph`：
- `record_type=change` 且能定位 `old_meta.meta_id` 时，返回 `impact_graph`；否则为 `null`。

示例：

```json
{
  "code": 200,
  "message": "操作成功",
  "data": {
    "id": 12,
    "record_type": "change",
    "business_domain_id": 345,
    "new_meta": {
      "name_zh": "HIS科室名称",
      "name_en": "HISKSMC",
      "data_type": "varchar(50)",
      "tag_ids": [1, 2]
    },
    "old_meta": {
      "meta_id": 789,
      "snapshot": {
        "id": 789,
        "name_zh": "HIS科室名称",
        "name_en": "HISKSMC",
        "data_type": "varchar(20)",
        "tag_ids": [1, 2]
      },
      "diff_fields": ["data_type"]
    },
    "impact_graph": {
      "nodes": [],
      "relationships": []
    },
    "status": "pending",
    "created_at": "2025-12-15T12:00:00.000000",
    "updated_at": "2025-12-15T12:00:00.000000"
  }
}
```

### 前端建议
- 详情页按 `record_type` 切换布局：
  - `redundancy`：展示 `candidates` 列表，给出“设为别名 / 作为新元数据”操作
  - `change`：展示 `old_meta` vs `new_meta` 差异字段，并展示 `impact_graph` 风险评估

## 接口 3：处理审核记录

### URL
- `POST /api/meta/review/resolve`

### 请求体（JSON）
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| id | int | 是 | 审核记录ID |
| action | string | 是 | `alias/create_new/accept_change/reject_change/ignore` |
| payload | object | 否 | 动作参数（见下） |
| resolved_by | string | 否 | 处理人标识（建议前端传登录用户名/工号） |
| notes | string | 否 | 处理备注 |

注意：后端会拒绝重复处理（当 `status != pending` 时返回失败）。

### action=alias（设置元数据别名关系）
用途：在 DataMeta 节点之间建立 ALIAS 关系，用于合并重复/相似的元数据。

> **注意**：此接口已更新，详细说明请参阅 [api_review_resolve_guide.md](api_review_resolve_guide.md)

payload：
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| primary_meta_id | int | 是 | 主元数据 Neo4j ID（作为别名目标） |
| alias_meta_id | int | 是 | 别名元数据 Neo4j ID（将成为别名） |

示例：

```json
{
  "id": 1001,
  "action": "alias",
  "payload": {
    "primary_meta_id": 100,
    "alias_meta_id": 200
  },
  "resolved_by": "alice",
  "notes": "将重复元数据合并为别名"
}
```

后端行为：
- 创建 `(alias_meta)-[:ALIAS]->(primary_meta)` 关系
- 将所有原先指向 `alias_meta` 的 ALIAS 关系转移到 `primary_meta`
- `primary_meta` 已有的 ALIAS 关系保持不变
- BusinessDomain 的 INCLUDES 关系不受影响
- 将审核记录标记为 `resolved`

### action=create_new（确认作为新元数据）
用途：处理 `record_type=redundancy` 的记录。

payload：
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| new_name_zh | string | 是 | 新元数据中文名（要求与现有区分） |

示例：

```json
{
  "id": 1002,
  "action": "create_new",
  "payload": { "new_name_zh": "HIS科室名称(新)" },
  "resolved_by": "alice"
}
```

后端行为：
- 在 Neo4j 创建新的 `DataMeta` 节点并关联业务领域
- 将审核记录标记为 `resolved`

### action=accept_change（接受变动）
用途：处理 `record_type=change` 的记录。

payload（可选）：
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| meta_id | int | 否 | 目标元数据 Neo4j ID；不传时使用 `old_meta.meta_id` |

示例：

```json
{
  "id": 2001,
  "action": "accept_change",
  "payload": { "meta_id": 789 },
  "resolved_by": "bob",
  "notes": "来源字段长度已调整，接受变动"
}
```

后端行为：
- 将 `new_meta` 写回目标 `DataMeta`（更新 name_zh/name_en/data_type，并尝试按 tag_ids 同步标签）
- 在 PG 写入一条 `metadata_version_history`（before/after 快照）
- 将审核记录标记为 `resolved`

### action=reject_change（拒绝变动）
用途：处理 `record_type=change` 的记录，不更新 Neo4j。

示例：

```json
{
  "id": 2002,
  "action": "reject_change",
  "resolved_by": "bob",
  "notes": "变动不合规，先不更新"
}
```

### action=ignore（忽略）
用途：任何类型记录都可忽略。

示例：

```json
{
  "id": 3001,
  "action": "ignore",
  "resolved_by": "bob"
}
```

## 前端页面交互建议（落地）
建议实现 2 个页面：
1. **审核列表页**
   - 筛选项：`status`（默认 pending）、`record_type`、`keyword`、`business_domain_id`
   - 行操作：进入详情页
2. **审核详情页**
   - `redundancy`：展示候选列表 `candidates[]`，提供：
     - “设为别名”按钮（调用 resolve alias，传 candidate_meta_id）
     - “确认新元数据”按钮（弹窗输入 new_name_zh，调用 resolve create_new）
   - `change`：展示 old/new 对比（diff_fields），展示 `impact_graph` 图谱，提供：
     - “接受变动”（resolve accept_change）
     - “拒绝变动”（resolve reject_change）
     - “忽略”（resolve ignore）

## 注意事项
- 列表接口的 `keyword` 是对 `new_meta.name_zh/name_en` 做 contains 匹配（JSONB 文本提取），适合“快速检索”；如需更复杂搜索建议前端做组合筛选。
- `resolve` 接口只允许处理 `status=pending` 的记录；已处理记录会返回失败。
- `accept_change` 会更新 Neo4j 的 `DataMeta` 属性并写版本历史（PG）；如果 `new_meta.tag_ids` 为空，则不会调整标签关系。


