## API：开版暂存（明道云）查询

### 概述
该接口用于查询 `api_v1.models.MDYPlateOrderStaging` 中的“开版暂存”数据。

- **只读**：仅提供 `list/retrieve`（不支持创建/更新/删除）
- **分页**：LimitOffset（`limit` / `offset`）
- **默认排序**：按 `ctime` 倒序（`-ctime`），并将 `ctime = null` 的记录排在最后
- **过滤**：支持按“内部字段名”的普通 query param 过滤（对前端隐藏 JSONField 实现细节）
- **返回结构**：`raw` 与 `related` 两个 JSONField 在输出时会做“可读化/碾平”处理

### Endpoint
- **List**：`GET /api/v1/mdy-plate-order-staging/`
- **Detail**：`GET /api/v1/mdy-plate-order-staging/{id}/`

### 权限与认证
- 当前接口权限行为 **遵循项目 DRF 全局配置**。
- 如启用 `DjangoModelPermissions`，则需要用户具备 `api_v1.view_mdyplateorderstaging` 权限。

### Query Params
#### 分页
- **limit**：返回条数
- **offset**：偏移量

#### 过滤
- **design_no**：设计编号（精确匹配）
- **style_name__icontains**：款号名称（模糊匹配）

> 该接口会拒绝任何以 `raw__` / `related__` 开头的参数（避免前端绕过白名单）。

示例：

```http
GET /api/v1/mdy-plate-order-staging/?limit=20&offset=0&design_no=82724
```

### 默认排序规则
接口固定按以下规则排序（不开放 `ordering=` 参数）：

1. `ctime` 倒序：`-ctime`（`nulls_last=True`）
2. `id` 倒序：`-id`

### 返回字段说明（results[] 内的对象）
- **id**：本地数据库主键
- **mdy_rowid**：明道云 rowid（唯一）
- **ctime**：明道云系统字段 ctime（模型字段，便于排序）
- **utime**：明道云系统字段 utime（模型字段）
- **created_at / updated_at**：本地记录创建/更新时间（ModelBase）
- **design_no**：便捷字段，等价于 `raw` 中的设计编号值
- **raw**：对 `MDYPlateOrderStaging.raw` 的“可读化/碾平”输出
- **related**：对 `MDYPlateOrderStaging.related` 的“碾平”输出（list[dict]）

### raw / related 的碾平规则
#### raw
输出时会把明道云 `controlId -> 字段中文名`，并做基础 normalize：

- **已知字段映射**：来自 `flower.utils.mingdaoyun.mappings.plate_order_field_definitions`
  - 输出 key 使用字段中文名（例如 `设计编号`、`款号名称`）
  - value 会尝试把字符串 JSON（如 `"[]"`、`"[{...}]"`）解析成 Python/JSON 对象
- **系统字段**：若 `raw` 内存在 `rowid/ctime/utime`，会一并输出
- **未映射字段不丢失**：如果 `raw` 中存在未在 `plate_order_field_definitions` 里声明的字段，仍会保留，key 使用原始 `controlId` 字符串

#### related
`related` 在入库阶段已经是“单条关联记录一个 dict”的结构（`list[dict]`）。

输出时：
- 对每个 dict 的 value 做一次 normalize（同上）
- 如果出现非 dict 的异常元素，兜底返回 `{ "value": <原值> }` 便于排查

### 响应示例
```json
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": 123,
      "mdy_rowid": "66xxxxxxxxxxxxxxxxxxxxxx",
      "ctime": "2025-12-17T03:20:00Z",
      "utime": "2025-12-17T03:21:00Z",
      "created_at": "2025-12-17T03:30:00Z",
      "updated_at": "2025-12-17T03:30:00Z",
      "design_no": "82724",
      "raw": {
        "rowid": "66xxxxxxxxxxxxxxxxxxxxxx",
        "ctime": "2025-12-17 11:20:00",
        "utime": "2025-12-17 11:21:00",
        "设计编号": "82724",
        "款号名称": "某款号",
        "62d52f4b8d2972284492dd99": "（未映射字段示例：以 controlId 原样返回）"
      },
      "related": [
        {
          "source": "drawing",
          "source_name": "画图",
          "worksheet_id": "668ba100fb551c850214066b",
          "rowid": "66yyyyyyyyyyyyyyyyyyyyyy",
          "设计师名字": "张三",
          "电脑位置": "D:/xxx"
        }
      ]
    }
  ]
}
```

### 性能说明
- `design_no` 过滤对应 PostgreSQL 表达式索引（见迁移 `api_v1/migrations/0006_mdy_plate_order_staging_raw_id_index.py`），用于加速按设计编号检索。

### 实现位置
- View/Serializer：`api_v1/views/mingdaoyun/plate_order_staging.py`
- 路由注册：`api_v1/urls.py`（`mdy-plate-order-staging`）
- 字段映射：`flower/utils/mingdaoyun/mappings.py`（`plate_order_field_definitions`）
- 碾平工具：`flower/utils/mingdaoyun/relations.py`（`flatten_row_by_field_definitions` / `normalize_mdy_value`）

### 相关文档（同步如何跑）
- 同步任务/command/是否定时：`docs/mdy_plate_order_sync.md`
