# API v2 MES 模块接口文档

本文档面向前端和业务评审，描述当前已经开放的 `MES` 公开接口，以及每个接口当前真实可用的字段。

## 基本约定

- Base URL: `/api/v2/mes`
- 认证：所有接口都需要登录。
- 人员身份：后端使用 `request.user.employee` 作为当前员工身份。
- 多商户隔离：所有接口只允许访问当前员工所属商户的数据。
- 创建审计：`created_by` 由当前登录用户自动写入。
- 操作审计：`operator` 由当前登录员工自动写入。
- `extra`：自由结构 JSON 字段，后端不解释业务内容。

## 枚举定义

### 产能单位 `capacity_unit`

| 值 | 显示值 |
|------|------|
| `1` | `米` |
| `2` | `码` |

### 生产指派状态 `status`

| 值 | 显示值 |
|------|------|
| `1` | `Draft` |
| `2` | `已发布` |
| `3` | `已接收` |
| `4` | `已取消` |
| `5` | `已完工` |

## 返回对象

### DeviceCategory 对象

```json
{
  "id": 1,
  "merchant": 10,
  "name": "打印机",
  "created_by": {
    "id": 5,
    "username": "admin"
  },
  "operator": {
    "id": 20,
    "name": "张三",
    "merchant_id": 10
  },
  "created_at": "2026-05-08T12:00:00+08:00",
  "updated_at": "2026-05-08T12:00:00+08:00"
}
```

字段说明：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | integer | 主键 |
| `merchant` | integer | 所属商户 ID |
| `name` | string | 设备分类名称 |
| `created_by` | object | 创建用户信息，包含 `id`、`username` |
| `operator` | object | 操作员工信息，包含 `id`、`name`、`merchant_id` |
| `created_at` | datetime string | 创建时间 |
| `updated_at` | datetime string | 更新时间 |

### Device 对象

```json
{
  "id": 100,
  "merchant": 10,
  "category": 1,
  "category_name": "打印机",
  "name": "A-01",
  "peak_capacity": 120,
  "capacity_unit": 1,
  "capacity_unit_label": "米",
  "extra": {
    "capacity": {
      "per_hour": 120
    }
  },
  "created_by": {
    "id": 5,
    "username": "admin"
  },
  "operator": {
    "id": 20,
    "name": "张三",
    "merchant_id": 10
  },
  "created_at": "2026-05-08T12:00:00+08:00",
  "updated_at": "2026-05-08T12:00:00+08:00"
}
```

字段说明：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | integer | 主键 |
| `merchant` | integer | 所属商户 ID |
| `category` | integer | 设备分类 ID |
| `category_name` | string | 设备分类名称 |
| `name` | string | 设备名称 |
| `peak_capacity` | integer | 峰值产能，必须大于 `0` |
| `capacity_unit` | integer | 产能单位枚举值 |
| `capacity_unit_label` | string | 产能单位显示值 |
| `extra` | object/null | 自由结构 JSON 扩展字段 |
| `created_by` | object | 创建用户信息，包含 `id`、`username` |
| `operator` | object | 操作员工信息，包含 `id`、`name`、`merchant_id` |
| `created_at` | datetime string | 创建时间 |
| `updated_at` | datetime string | 更新时间 |

### ProductionAssignment 对象

```json
{
  "id": 200,
  "merchant": 10,
  "device": 100,
  "device_name": "A-01",
  "content_type": 45,
  "object_id": 9001,
  "assigner": {
    "id": 20,
    "name": "张三",
    "merchant_id": 10
  },
  "assignee": {
    "id": 21,
    "name": "李四",
    "merchant_id": 10
  },
  "production_quantity": 300,
  "status": 1,
  "status_label": "Draft",
  "extra": {
    "batch": "A1"
  },
  "created_by": {
    "id": 5,
    "username": "admin"
  },
  "operator": {
    "id": 20,
    "name": "张三",
    "merchant_id": 10
  },
  "created_at": "2026-05-08T12:00:00+08:00",
  "updated_at": "2026-05-08T12:00:00+08:00"
}
```

字段说明：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | integer | 主键 |
| `merchant` | integer | 所属商户 ID |
| `device` | integer | 设备 ID |
| `device_name` | string | 设备名称 |
| `content_type` | integer | 关联业务对象类型 ID |
| `object_id` | integer | 关联业务对象主键 |
| `assigner` | object | 指派者员工信息，包含 `id`、`name`、`merchant_id` |
| `assignee` | object/null | 被指派人员工信息，包含 `id`、`name`、`merchant_id`，可空 |
| `production_quantity` | integer | 生产数量，必须大于 `0`，单位跟随设备 `capacity_unit` |
| `status` | integer | 状态枚举值 |
| `status_label` | string | 状态显示值 |
| `extra` | object/null | 自由结构 JSON 扩展字段 |
| `created_by` | object | 创建用户信息，包含 `id`、`username` |
| `operator` | object | 操作员工信息，包含 `id`、`name`、`merchant_id` |
| `created_at` | datetime string | 创建时间 |
| `updated_at` | datetime string | 更新时间 |

## 设备分类接口

### 设备分类列表

- URL: `/api/v2/mes/device-categories/`
- Method: `GET`

查询参数：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 按名称模糊匹配 |

### 创建设备分类

- URL: `/api/v2/mes/device-categories/`
- Method: `POST`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 设备分类名称 |

### 设备分类详情

- URL: `/api/v2/mes/device-categories/{category_id}/`
- Method: `GET`

### 更新设备分类

- URL: `/api/v2/mes/device-categories/{category_id}/`
- Method: `PATCH`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 设备分类名称 |

### 删除设备分类

- URL: `/api/v2/mes/device-categories/{category_id}/`
- Method: `DELETE`

错误示例：

```json
{
  "detail": "设备分类已被设备引用，不能删除"
}
```

## 设备接口

### 设备列表

- URL: `/api/v2/mes/devices/`
- Method: `GET`

查询参数：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 按设备名称模糊匹配 |
| `category` | integer | 否 | 按设备分类 ID 过滤 |
| `extra_path` | string | 否 | `extra` 中的 JSON 路径，使用 `.` 分隔，例如 `capacity.per_hour` |
| `extra_value` | string | 否 | 与 `extra_path` 配套使用，支持 JSON 字面量字符串，例如 `120`、`"A1"`、`true` |

说明：

- `extra_path` 和 `extra_value` 必须同时传入。
- 当前仅支持精确匹配。
- 后端不解释 `extra` 的业务语义，只按路径和值匹配。

### 创建设备

- URL: `/api/v2/mes/devices/`
- Method: `POST`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 设备名称 |
| `category` | integer | 是 | 设备分类 ID |
| `peak_capacity` | integer | 是 | 峰值产能，必须大于 `0` |
| `capacity_unit` | integer | 否 | 产能单位，省略时默认 `1=米` |
| `extra` | object/null | 否 | 自由结构 JSON 扩展字段 |

### 设备详情

- URL: `/api/v2/mes/devices/{device_id}/`
- Method: `GET`

### 更新设备

- URL: `/api/v2/mes/devices/{device_id}/`
- Method: `PATCH`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 否 | 设备名称 |
| `category` | integer | 否 | 设备分类 ID |
| `peak_capacity` | integer | 否 | 峰值产能，必须大于 `0` |
| `capacity_unit` | integer | 否 | 产能单位 |
| `extra` | object/null | 否 | 传 `null` 可清空扩展字段 |

补充说明：

- 模型层历史迁移曾使用一次性兼容默认值处理旧数据，但前端不应依赖省略 `peak_capacity` 让系统自动补值。

### 删除设备

- URL: `/api/v2/mes/devices/{device_id}/`
- Method: `DELETE`

- 成功返回 `204 No Content`

## 生产指派接口

### 生产指派列表

- URL: `/api/v2/mes/production-assignments/`
- Method: `GET`

查询参数：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device` | integer | 否 | 按设备 ID 过滤 |
| `content_type` | integer | 否 | 按关联对象类型 ID 过滤 |
| `object_id` | integer | 否 | 按关联对象 ID 过滤 |
| `status` | integer | 否 | 按状态过滤 |

### 创建生产指派

- URL: `/api/v2/mes/production-assignments/`
- Method: `POST`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device` | integer | 是 | 设备 ID |
| `content_type` | integer | 是 | 关联业务对象类型 ID |
| `object_id` | integer | 是 | 关联业务对象主键 |
| `assigner` | integer | 是 | 指派者员工 ID |
| `assignee` | integer/null | 否 | 被指派人员工 ID，可空 |
| `production_quantity` | integer | 是 | 生产数量，必须大于 `0` |
| `status` | integer | 否 | 状态，省略时默认 `1=Draft` |
| `extra` | object/null | 否 | 自由结构 JSON 扩展字段 |

说明：

- `device`、`assigner`、`assignee` 必须属于当前商户。
- `production_quantity` 单位跟随设备 `capacity_unit`。

### 生产指派详情

- URL: `/api/v2/mes/production-assignments/{assignment_id}/`
- Method: `GET`

### 更新生产指派

- URL: `/api/v2/mes/production-assignments/{assignment_id}/`
- Method: `PATCH`

请求字段：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `device` | integer | 否 | 设备 ID |
| `assignee` | integer/null | 否 | 被指派人员工 ID，可空 |
| `production_quantity` | integer | 否 | 生产数量，必须大于 `0` |
| `status` | integer | 否 | 目标状态 |
| `extra` | object/null | 否 | 扩展字段，传 `null` 可清空 |

状态流转说明：

- 主流程：`Draft -> 已发布 -> 已接收 -> 已完工`
- `Draft` 可以改为 `已取消`
- `已发布` 可以改为 `已取消`
- `已接收` 和 `已完工` 不能改为 `已取消`
- `已取消` 和 `已完工` 视为终态，不能回退

错误示例：

```json
{
  "detail": "当前状态不允许变更为目标状态"
}
```

### 删除生产指派

- URL: `/api/v2/mes/production-assignments/{assignment_id}/`
- Method: `DELETE`
