# 地图数据推送接口文档

## 概述

地图数据推送接口用于处理用户推送新地图时的动作点和库区数据存储。支持库区分类（密集库区、一般库区）和动作点分层存储功能。

**重要特性：** 采用增量更新模式，在原有数据基础上进行更新：
- 库区：基于area_name判断，存在则更新，不存在则新增
- 动作点：基于station_name判断，存在则更新，不存在则新增
- 分层：基于layer_name判断，存在则更新，不存在则新增
- 确保数据的完整性和一致性，避免数据丢失风险
- 支持动作站点名称和库位名称的重复检查，自动过滤重复数据

## 接口列表

### 1. 推送地图数据

**接口地址：** `POST /api/vwed-map-data/push`

**功能说明：** 当用户推送新的地图时，将地图相关的动作点和库区数据存入数据库。支持库区分类和动作点分层存储。每个动作点包含站点名称和库位名称两个唯一标识。

**重要说明：** 采用增量更新模式，在原有数据基础上进行更新。库区通过area_name判断，动作点通过station_name判断，分层通过layer_name判断。系统会自动生成ID和layer_index，确保数据安全。

**请求参数：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| scene_id | string | 是 | 场景ID |
| storage_areas | array | 是 | 库区数据列表 |
| operate_points | array | 是 | 动作点数据列表 |

**库区数据结构（storage_areas）：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| area_name | string | 是 | 库区名称 |
| area_type | string | 是 | 库区类型（general-一般库区，dense-密集库区） |
| select_logic | integer | 是 | 选择库位逻辑（1-先进先出，2-先进后出） |
| description | string | 否 | 库区描述 |
| tags | string | 否 | 库区标签 |

**注意：** 库区ID由系统自动生成UUID，无需手动指定。

**注意：** 库区的最大容量（max_capacity）由系统根据库区类型和包含的动作点数量自动计算，无需手动指定。

**动作点数据结构（operate_points）：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| station_name | string | 是 | 动作站点名称（唯一标识） |
| storage_location_name | string | 是 | 库位名称（唯一标识） |
| area_name | string | 否 | 所属库区名称 |
| max_layers | integer | 否 | 最大层数，默认1 |
| position_x | integer | 否 | X坐标 |
| position_y | integer | 否 | Y坐标 |
| position_z | integer | 否 | Z坐标 |
| content | string | 否 | 内容 |
| tags | string | 否 | 标签 |
| description | string | 否 | 动作点描述 |
| layers | array | 否 | 分层数据 |

**注意：** 动作点ID由系统自动生成UUID，无需手动指定。站点名称和库位名称作为唯一标识，同一场景下不能重复。动作点通过area_name字段与库区关联。

**分层数据结构（layers）：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| layer_name | string | 否 | 层名称 |
| max_weight | integer | 否 | 最大承重（克） |
| max_volume | integer | 否 | 最大体积（立方厘米） |
| layer_height | integer | 否 | 层高（毫米） |
| description | string | 否 | 层描述 |
| tags | string | 否 | 层标签 |

**注意：** 层索引（layer_index）由系统自动生成，按照提交的顺序从1开始递增。

**请求示例：**

```json
{
  "scene_id": "1936411520272753371",
  "storage_areas": [
    {
      "area_name": "一般存储区B",
      "area_type": "general",
      "select_logic": 2 //1 先进先出 2 先进后出 
    },
    {
      "area_name": "一般存储区C",
      "area_type": "general",
      "select_logic": 2 //1 先进先出 2 先进后出
    }
  ],
  "operate_points": [
    {
      "station_name": "STATION-A-001",
      "area_name": "一般存储区B",
      "max_layers": 1,
      "layers": [
        {
          "layer_name": "DSA_2_1_1" // 库位名称
        }
        
      ]
    },
    {
      "station_name": "STATION-B-001",
      "area_name": "一般存储区B",
      "max_layers": 1,
      "layers": [
        {
          "layer_name": "DSA_2_1_2" //库位名称
        }
      ]
    },
    {
      "station_name": "STATION-B-004",
      "area_name": "一般存储区B",
      "max_layers": 1,
      "layers": [
        {
          "layer_name": "DSA_2_1_3" //库位名称
        }
        
      ]
    },
    {
      "station_name": "STATION-B-003",
      "area_name": "一般存储区B",
      "max_layers": 1,
      "layers": [
        {
          "layer_name": "DSA_2_1_4" //库位名称
        }
      ]
    }
  ]
}
```

**响应参数：**

| 参数名 | 类型 | 描述 |
|--------|------|------|
| code | integer | 状态码，200表示成功 |
| message | string | 响应消息 |
| data | object | 响应数据 |

**响应数据结构（data）：**

| 参数名 | 类型 | 描述 |
|--------|------|------|
| scene_id | string | 场景ID |
| storage_areas_count | integer | 创建的库区数量 |
| operate_points_count | integer | 创建的动作点数量 |
| layers_count | integer | 创建的分层数量 |
| message | string | 推送结果说明 |

**响应示例：**

**覆盖现有数据时：**
```json
{
  "code": 200,
  "message": "地图数据推送成功",
  "data": {
    "scene_id": "scene-001",
    "storage_areas_count": 2,
    "operate_points_count": 2,
    "layers_count": 2,
    "message": "推送成功，已覆盖现有数据。创建了2个库区，2个动作点，2个分层"
  }
}
```

**创建新数据时：**
```json
{
  "code": 200,
  "message": "地图数据推送成功",
  "data": {
    "scene_id": "scene-002",
    "storage_areas_count": 2,
    "operate_points_count": 2,
    "layers_count": 2,
    "message": "推送成功，创建了2个库区，2个动作点，2个分层"
  }
}
```

**有重复数据被过滤时：**
```json
{
  "code": 200,
  "message": "地图数据推送成功",
  "data": {
    "scene_id": "scene-001",
    "storage_areas_count": 2,
    "operate_points_count": 3,
    "layers_count": 4,
    "message": "推送成功，创建了2个库区，3个动作点，4个分层。检测到2个重复的站点名称已被过滤：STATION-A-001, STATION-B-001"
  }
}
```

**错误响应示例：**

```json
{
  "code": 400,
  "message": "动作站点名称不能重复",
  "data": null
}
```

```json
{
  "code": 400,
  "message": "库位名称不能重复",
  "data": null
}
```

**智能覆盖模式说明：**

- 先查询数据库是否有指定场景的现有数据
- 如果有现有数据，则先删除所有现有数据（库区、动作点、分层），然后创建新数据
- 如果没有现有数据，则直接创建新数据，提高处理效率
- 不支持增量更新，只支持全量覆盖
- 建议在推送前确认数据的完整性，避免数据丢失

**重复检查机制：**

- 同一场景下，动作站点名称（station_name）不能重复
- 同一场景下，库位名称（storage_location_name）不能重复
- 系统会自动过滤重复的动作点，并在响应中提示被过滤的数量和名称
- 支持与数据库现有数据的重复检查和请求中的重复检查

---

### 2. 查询地图数据

**接口地址：** `POST /api/vwed-map-data/query`

**功能说明：** 根据场景ID查询地图中的库区和动作点数据，支持按库区类型筛选和是否包含分层数据的选项。

**请求参数：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| scene_id | string | 是 | 场景ID |
| area_type | string | 否 | 库区类型筛选（general/dense） |
| include_layers | boolean | 否 | 是否包含分层数据，默认true |

**请求示例：**

```json
{
  "scene_id": "scene-001",
  "area_type": "dense",
  "include_layers": true
}
```

**响应参数：**

| 参数名 | 类型 | 描述 |
|--------|------|------|
| code | integer | 状态码，200表示成功 |
| message | string | 响应消息 |
| data | object | 响应数据 |

**响应数据结构（data）：**

| 参数名 | 类型 | 描述 |
|--------|------|------|
| scene_id | string | 场景ID |
| storage_areas | array | 库区数据列表 |
| operate_points | array | 动作点数据列表 |
| total_capacity | integer | 总容量 |
| used_capacity | integer | 已使用容量 |
| dense_areas_count | integer | 密集库区数量 |
| general_areas_count | integer | 一般库区数量 |
| total_layers | integer | 总分层数量 |
| occupied_layers | integer | 已占用分层数量 |

**响应示例：**

```json
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "scene_id": "scene-001",
    "storage_areas": [
      {
        "id": "area-001",
        "area_name": "密集存储区A",
        "area_code": "DENSE-A",
        "area_type": "dense",
        "scene_id": "scene-001",
        "max_capacity": 100,
        "current_usage": 0,
        "is_active": true,
        "is_maintenance": false,
        "description": "高密度存储区域，适合小件物品",
        "tags": "dense,small-items",
        "created_at": "2025-01-09 10:00:00",
        "updated_at": "2025-01-09 10:00:00",
        "is_deleted": false
      }
    ],
    "operate_points": [
      {
        "id": "point-001",
        "station_name": "STATION-A-001",
        "scene_id": "scene-001",
        "is_occupied": false,
        "is_locked": false,
        "is_disabled": false,
        "is_empty_tray": false,
        "content": "存储点1",
        "tags": "storage",
        "storage_area_id": "area-001",
        "storage_area_type": "dense",
        "area_name": "密集存储区A",
        "max_layers": 2,
        "current_layers": 0,
        "position_x": 100,
        "position_y": 200,
        "position_z": 0,
        "description": "密集区第一个动作点",
        "layers": [
          {
            "id": "layer-001",
            "operate_point_id": "point-001",
            "layer_index": 1,
            "layer_name": "第1层",
            "has_goods": false,
            "goods_content": "",
            "goods_weight": null,
            "goods_volume": null,
            "is_locked": false,
            "is_disabled": false,
            "is_reserved": false,
            "max_weight": 5000,
            "max_volume": 1000,
            "layer_height": 100,
            "goods_stored_at": null,
            "goods_retrieved_at": null,
            "last_access_at": null,
            "tags": "bottom",
            "description": "底层存储",
            "created_at": "2025-01-09 10:00:00",
            "updated_at": "2025-01-09 10:00:00",
            "is_deleted": false
          },
          {
            "id": "layer-002",
            "operate_point_id": "point-001",
            "layer_index": 2,
            "layer_name": "第2层",
            "has_goods": false,
            "goods_content": "",
            "goods_weight": null,
            "goods_volume": null,
            "is_locked": false,
            "is_disabled": false,
            "is_reserved": false,
            "max_weight": 5000,
            "max_volume": 1000,
            "layer_height": 100,
            "goods_stored_at": null,
            "goods_retrieved_at": null,
            "last_access_at": null,
            "tags": "top",
            "description": "顶层存储",
            "created_at": "2025-01-09 10:00:00",
            "updated_at": "2025-01-09 10:00:00",
            "is_deleted": false
          }
        ],
        "created_at": "2025-01-09 10:00:00",
        "updated_at": "2025-01-09 10:00:00",
        "is_deleted": false
      }
    ],
    "total_capacity": 100,
    "used_capacity": 0,
    "dense_areas_count": 1,
    "general_areas_count": 0,
    "total_layers": 2,
    "occupied_layers": 0
  }
}
```

---

### 3. 删除场景数据

**接口地址：** `DELETE /api/vwed-map-data/scene/{scene_id}`

**功能说明：** 删除指定场景的所有地图数据，包括库区、动作点和分层数据。

**路径参数：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| scene_id | string | 是 | 场景ID |

**请求示例：**

```
DELETE /api/vwed-map-data/scene/scene-001
```

**响应示例：**

```json
{
  "code": 200,
  "message": "场景数据删除成功",
  "data": null
}
```

---

### 4. 获取场景数据摘要

**接口地址：** `GET /api/vwed-map-data/scene/{scene_id}/summary`

**功能说明：** 获取指定场景的数据统计信息，包括库区和动作点的数量统计。

**路径参数：**

| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| scene_id | string | 是 | 场景ID |

**请求示例：**

```
GET /api/vwed-map-data/scene/scene-001/summary
```

**响应数据结构（data）：**

| 参数名 | 类型 | 描述 |
|--------|------|------|
| scene_id | string | 场景ID |
| total_storage_areas | integer | 总库区数量 |
| total_operate_points | integer | 总动作点数量 |
| total_capacity | integer | 总容量 |
| used_capacity | integer | 已使用容量 |
| capacity_usage_rate | float | 容量使用率（百分比） |
| dense_areas_count | integer | 密集库区数量 |
| general_areas_count | integer | 一般库区数量 |
| total_layers | integer | 总分层数量 |
| occupied_layers | integer | 已占用分层数量 |
| layer_usage_rate | float | 分层使用率（百分比） |

**响应示例：**

```json
{
  "code": 200,
  "message": "获取场景摘要成功",
  "data": {
    "scene_id": "scene-001",
    "total_storage_areas": 2,
    "total_operate_points": 2,
    "total_capacity": 150,
    "used_capacity": 45,
    "capacity_usage_rate": 30.0,
    "dense_areas_count": 1,
    "general_areas_count": 1,
    "total_layers": 3,
    "occupied_layers": 1,
    "layer_usage_rate": 33.33
  }
}
```

## 数据验证规则

### 1. 库区数据验证

- 库区ID、名称、编码不能重复
- 库区类型必须为 "general" 或 "dense"
- 最大容量不能为负数

### 2. 动作点数据验证

- 站点名称不能重复（作为唯一标识）
- 最大层数必须大于0
- 如果指定库区ID，必须在库区列表中存在
- 动作点ID由系统自动生成UUID，无需手动设置
- 库区类型字段在地图推送时自动填充，无需手动设置
- 库区名称字段在地图推送时自动填充，无需手动设置

### 3. 分层数据验证

- 层索引必须从1开始，且不能重复
- 层索引不能超过动作点的最大层数
- 分层数量不能超过最大层数
- 最大承重、最大体积、层高不能为负数

## 状态码说明

| 状态码 | 说明 |
|--------|------|
| 200 | 请求成功 |
| 400 | 请求参数错误或数据验证失败 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |

## 使用说明

### 1. 推送地图数据流程

1. 准备库区数据，包括库区类型（密集/一般）、容量等信息
2. 准备动作点数据，包括位置坐标、所属库区等信息
3. 如果动作点支持分层，准备分层数据，包括层索引、承重等信息
4. 调用推送接口，系统会自动处理数据关联和验证
5. 如果需要覆盖现有数据，设置 `is_override` 为 `true`

### 2. 查询地图数据流程

1. 指定场景ID进行查询
2. 可选择性筛选库区类型（密集/一般）
3. 可选择是否包含分层详细数据
4. 系统返回完整的地图数据和统计信息

### 3. 分层存储支持

- 每个动作点可以设置最大层数
- 每层可以独立设置承重、体积等限制
- 支持货物存取状态追踪
- 支持分层锁定和预留功能

### 4. 库区类型说明

- **密集库区（dense）**：适合小件物品的高密度存储
- **一般库区（general）**：适合大件物品的常规存储

### 5. 库区容量自动计算规则

系统会根据以下规则自动计算库区的最大容量：

#### 密集库区（dense）
- 基础容量：50（可通过环境变量 `MAP_DENSE_STORAGE_BASE_CAPACITY` 配置）
- 每个动作点增加容量：10（可通过环境变量 `MAP_DENSE_STORAGE_CAPACITY_PER_POINT` 配置）
- 分层倍数：1.5（可通过环境变量 `MAP_DENSE_STORAGE_LAYER_MULTIPLIER` 配置）

#### 一般库区（general）
- 基础容量：30（可通过环境变量 `MAP_GENERAL_STORAGE_BASE_CAPACITY` 配置）
- 每个动作点增加容量：15（可通过环境变量 `MAP_GENERAL_STORAGE_CAPACITY_PER_POINT` 配置）
- 分层倍数：1.2（可通过环境变量 `MAP_GENERAL_STORAGE_LAYER_MULTIPLIER` 配置）

#### 计算公式
```
库区容量 = 基础容量 + Σ(动作点容量)
动作点容量 = 基础点容量 × (层数 > 1 ? 分层倍数 × 层数 : 1)
```

#### 示例（基于默认配置）
- 密集库区包含2个单层动作点：50 + 10 + 10 = 70
- 密集库区包含1个双层动作点：50 + (10 × 1.5 × 2) = 80
- 一般库区包含1个三层动作点：30 + (15 × 1.2 × 3) = 84

#### 配置说明
系统支持通过环境变量动态调整库区容量计算参数，便于在不同环境中灵活配置。

### 6. 注意事项

- 动作点ID由系统自动生成32位UUID字符串，无需手动设置
- 库区ID字段使用32位字符串，建议使用UUID格式
- 站点名称作为动作点的唯一标识，不能重复
- 坐标系统使用整数，单位建议为毫米
- 重量单位为克，体积单位为立方厘米
- 软删除机制，删除操作不会物理删除数据
- 支持事务处理，确保数据一致性
- 库区容量由系统自动计算，会在推送时根据动作点配置重新计算
- 库区类型字段在地图推送时自动填充到动作点中，便于快速查询动作点所属库区类型
- 库区名称字段在地图推送时自动填充到动作点中，便于快速查询动作点所属库区名称 