807 lines
36 KiB
Markdown
807 lines
36 KiB
Markdown
# Ether 空间节点管理架构设计方案
|
||
|
||
**版本**: v2.0
|
||
**设计日期**: 2026-02-16
|
||
**设计目标**: 重构空间节点管理,统一空间体系,支持地图服务对接
|
||
|
||
---
|
||
|
||
## 一、业务背景与需求分析
|
||
|
||
### 1.1 物业行业空间管理标准
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 物业项目空间层级标准模型 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
项目 (Project)
|
||
├── 住宅区域 (Residential Area)
|
||
│ ├── 分期 (Phase) ──────────────────── 可选层级,大型项目使用
|
||
│ │ ├── 楼栋 (Building)
|
||
│ │ │ ├── 单元 (Unit)
|
||
│ │ │ │ ├── 楼层 (Floor) ──────── 可选层级
|
||
│ │ │ │ │ └── 房间 (Room)
|
||
│ │ │ │ └── 房间 (Room)
|
||
│ │ │ └── 公共区域 (Public Area)
|
||
│ │ └── 地下车库 (Underground Garage)
|
||
│ │ └── 车位 (Parking Space)
|
||
│ └── 地面停车场 (Surface Parking)
|
||
│ └── 车位 (Parking Space)
|
||
├── 商业区域 (Commercial Area)
|
||
│ ├── 商铺 (Shop)
|
||
│ └── 公共区域 (Public Area)
|
||
├── 公共设施区域 (Facility Area)
|
||
│ ├── 设备房 (Equipment Room)
|
||
│ ├── 物业用房 (Property Office)
|
||
│ └── 配套设施 (Supporting Facility)
|
||
└── 室外区域 (Outdoor Area)
|
||
├── 道路 (Road)
|
||
├── 绿化带 (Green Belt)
|
||
└── 景观设施 (Landscape)
|
||
```
|
||
|
||
### 1.2 核心业务需求
|
||
|
||
| 需求分类 | 具体需求 | 优先级 |
|
||
|----------|----------|--------|
|
||
| **空间层级管理** | 支持项目→楼栋→单元→房间的层级结构 | P0 |
|
||
| **空间类型管理** | 支持楼栋、单元、房间、车位、商铺、公共区域等类型 | P0 |
|
||
| **项目统计** | 自动统计楼栋数、户数、车位数等 | P0 |
|
||
| **批量导入** | Excel批量导入楼栋、单元、房间数据 | P0 |
|
||
| **地图定位** | 支持高德地图标注点位和区域 | P1 |
|
||
| **空间编码** | 自动生成空间编码规则 | P1 |
|
||
| **业务关联** | 与工单、巡检、收费等业务模块关联 | P1 |
|
||
| **可视化展示** | 树形结构展示、地图展示 | P1 |
|
||
|
||
### 1.3 地图服务需求
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 地图服务对接需求 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
1. 点位标注 (Point)
|
||
- 楼栋位置:标注楼栋在地图上的位置
|
||
- 车位位置:标注车位在地图上的位置
|
||
- 设施位置:标注公共设施的位置
|
||
- 巡检点:标注巡检点位
|
||
|
||
2. 区域绘制 (Polygon)
|
||
- 项目边界:绘制项目的地理边界
|
||
- 区域划分:绘制管理区域边界
|
||
- 车位区域:绘制停车场区域
|
||
|
||
3. 路径规划 (Polyline)
|
||
- 巡检路线:规划巡检路线
|
||
- 巡逻路线:规划安保巡逻路线
|
||
|
||
4. 地图交互
|
||
- 点击查询:点击地图元素查看详情
|
||
- 区域筛选:在地图上框选筛选
|
||
- 距离测量:测量两点间距离
|
||
```
|
||
|
||
---
|
||
|
||
## 二、数据模型设计
|
||
|
||
### 2.1 统一空间节点模型
|
||
|
||
**设计原则**:
|
||
1. 所有空间实体统一使用 `mdm_space_node` 表
|
||
2. 通过 `node_type` 区分不同类型
|
||
3. 通过 `node_category` 区分大类(建筑、车位、设施)
|
||
4. 支持树形结构和地图数据
|
||
|
||
```sql
|
||
-- ============================================================
|
||
-- 空间节点主表 (统一管理所有空间实体)
|
||
-- ============================================================
|
||
CREATE TABLE mdm_space_node (
|
||
-- 主键
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
||
-- 基础属性
|
||
project_id UUID NOT NULL REFERENCES mdm_project(id),
|
||
code VARCHAR(50) NOT NULL, -- 空间编码
|
||
name VARCHAR(100) NOT NULL, -- 空间名称
|
||
full_name VARCHAR(500), -- 全路径名称
|
||
short_name VARCHAR(50), -- 简称
|
||
|
||
-- 类型分类
|
||
node_category VARCHAR(20) NOT NULL, -- 节点大类: BUILDING/PARKING/FACILITY/AREA
|
||
node_type VARCHAR(30) NOT NULL, -- 节点类型: BUILDING/UNIT/ROOM/PARKING/SHOP等
|
||
usage_type VARCHAR(30), -- 用途类型: RESIDENTIAL/COMMERCIAL/OFFICE等
|
||
|
||
-- 树形结构
|
||
parent_id UUID REFERENCES mdm_space_node(id),
|
||
tree_path VARCHAR(1000), -- 物理路径: id.id.id
|
||
tree_path_name VARCHAR(1000), -- 名称路径: 项目/楼栋/单元/房间
|
||
level INTEGER DEFAULT 0, -- 层级深度
|
||
sort_order INTEGER DEFAULT 0, -- 排序号
|
||
|
||
-- 状态管理
|
||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
|
||
delivery_status VARCHAR(20), -- 交付状态: UNDELIVERED/DELIVERED
|
||
decoration_status VARCHAR(20), -- 装修状态: ROUGH/FINE/UNDONE
|
||
|
||
-- 面积信息
|
||
building_area NUMERIC(10,2), -- 建筑面积(㎡)
|
||
usable_area NUMERIC(10,2), -- 使用面积(㎡)
|
||
shared_area NUMERIC(10,2), -- 公摊面积(㎡)
|
||
land_area NUMERIC(10,2), -- 占地面积(㎡)
|
||
|
||
-- 地理信息
|
||
longitude NUMERIC(10,6), -- 经度
|
||
latitude NUMERIC(10,6), -- 纬度
|
||
location GEOMETRY(Point, 4326), -- PostGIS点位
|
||
boundary GEOMETRY(Polygon, 4326), -- PostGIS区域边界
|
||
altitude NUMERIC(8,2), -- 海拔高度
|
||
floor_number INTEGER, -- 楼层号(正数地上,负数地下)
|
||
|
||
-- 地址信息
|
||
province VARCHAR(50), -- 省
|
||
city VARCHAR(50), -- 市
|
||
district VARCHAR(50), -- 区
|
||
street VARCHAR(100), -- 街道
|
||
address VARCHAR(255), -- 详细地址
|
||
|
||
-- 扩展属性(JSON)
|
||
attributes JSONB, -- 类型特定属性
|
||
|
||
-- 系统字段
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
created_by UUID,
|
||
updated_by UUID,
|
||
is_deleted BOOLEAN DEFAULT FALSE,
|
||
|
||
-- 约束
|
||
CONSTRAINT uk_space_node_project_code UNIQUE (project_id, code)
|
||
);
|
||
|
||
-- 索引
|
||
CREATE INDEX idx_space_node_project ON mdm_space_node(project_id);
|
||
CREATE INDEX idx_space_node_parent ON mdm_space_node(parent_id);
|
||
CREATE INDEX idx_space_node_type ON mdm_space_node(node_type);
|
||
CREATE INDEX idx_space_node_tree_path ON mdm_space_node(tree_path);
|
||
CREATE INDEX idx_space_node_location ON mdm_space_node USING GIST(location);
|
||
CREATE INDEX idx_space_node_boundary ON mdm_space_node USING GIST(boundary);
|
||
CREATE INDEX idx_space_node_attributes ON mdm_space_node USING GIN(attributes);
|
||
```
|
||
|
||
### 2.2 节点类型与属性定义
|
||
|
||
```java
|
||
/**
|
||
* 空间节点大类
|
||
*/
|
||
public enum SpaceNodeCategory {
|
||
BUILDING("建筑空间", "楼栋、单元、房间等建筑空间"),
|
||
PARKING("停车空间", "车位、车库等停车空间"),
|
||
FACILITY("设施空间", "设备房、公共设施等"),
|
||
AREA("区域空间", "公共区域、管理区域等");
|
||
}
|
||
|
||
/**
|
||
* 空间节点类型
|
||
*/
|
||
public enum SpaceNodeType {
|
||
// 建筑空间
|
||
BUILDING("楼栋", SpaceNodeCategory.BUILDING, 1),
|
||
UNIT("单元", SpaceNodeCategory.BUILDING, 2),
|
||
FLOOR("楼层", SpaceNodeCategory.BUILDING, 3),
|
||
ROOM("房间", SpaceNodeCategory.BUILDING, 4),
|
||
SHOP("商铺", SpaceNodeCategory.BUILDING, 2),
|
||
|
||
// 停车空间
|
||
GARAGE("车库", SpaceNodeCategory.PARKING, 1),
|
||
PARKING_AREA("停车区域", SpaceNodeCategory.PARKING, 2),
|
||
PARKING_SPACE("车位", SpaceNodeCategory.PARKING, 3),
|
||
|
||
// 设施空间
|
||
EQUIPMENT_ROOM("设备房", SpaceNodeCategory.FACILITY, 1),
|
||
PROPERTY_OFFICE("物业用房", SpaceNodeCategory.FACILITY, 1),
|
||
SECURITY_ROOM("门岗", SpaceNodeCategory.FACILITY, 1),
|
||
|
||
// 区域空间
|
||
PUBLIC_AREA("公共区域", SpaceNodeCategory.AREA, 1),
|
||
GREEN_AREA("绿化区域", SpaceNodeCategory.AREA, 1),
|
||
ROAD("道路", SpaceNodeCategory.AREA, 1);
|
||
}
|
||
```
|
||
|
||
### 2.3 类型特定属性 (JSONB)
|
||
|
||
```json
|
||
// 楼栋属性
|
||
{
|
||
"buildingType": "RESIDENTIAL", // 楼栋类型: RESIDENTIAL/COMMERCIAL/MIXED
|
||
"structureType": "FRAME", // 结构类型: FRAME/BRICK/STEEL
|
||
"totalFloors": 18, // 总层数
|
||
"undergroundFloors": 1, // 地下层数
|
||
"totalUnits": 4, // 单元数
|
||
"totalRooms": 144, // 房间数
|
||
"elevatorCount": 2, // 电梯数
|
||
"completionDate": "2020-06-30", // 竣工日期
|
||
"developer": "XX房地产开发公司" // 开发商
|
||
}
|
||
|
||
// 单元属性
|
||
{
|
||
"unitType": "STANDARD", // 单元类型
|
||
"elevatorCount": 1, // 电梯数
|
||
"stairType": "DOUBLE_RUN", // 楼梯类型
|
||
"roomsPerFloor": 2 // 每层户数
|
||
}
|
||
|
||
// 房间属性
|
||
{
|
||
"roomType": "APARTMENT", // 房间类型: APARTMENT/VILLA/SHOP/OFFICE
|
||
"layoutType": "3T2", // 户型: 3室2厅2卫
|
||
"roomCount": 3, // 卧室数
|
||
"livingRoomCount": 2, // 客厅数
|
||
"bathroomCount": 2, // 卫生间数
|
||
"kitchenCount": 1, // 厨房数
|
||
"balconyCount": 1, // 阳台数
|
||
"orientation": "SOUTH", // 朝向
|
||
"propertyCertificateNo": "沪房地徐字(2020)第0001号" // 房产证号
|
||
}
|
||
|
||
// 车位属性
|
||
{
|
||
"parkingType": "UNDERGROUND", // 车位类型: UNDERGROUND/SURFACE/MECHANICAL
|
||
"parkingCategory": "STANDARD", // 车位类别: STANDARD/LARGE/DISABLED
|
||
"vehicleType": "CAR", // 车辆类型: CAR/SUV/VAN
|
||
"hasChargingPile": false, // 是否有充电桩
|
||
"zoneCode": "A", // 区域编码
|
||
"rowNumber": 1, // 排号
|
||
"columnNumber": 5 // 列号
|
||
}
|
||
|
||
// 商铺属性
|
||
{
|
||
"shopType": "RETAIL", // 商铺类型: RETAIL/CATERING/SERVICE
|
||
"frontage": 8.5, // 门面宽度(米)
|
||
"depth": 12.0, // 进深(米)
|
||
"ceilingHeight": 4.5, // 层高(米)
|
||
"hasMezzanine": false, // 是否有夹层
|
||
"businessLicense": "XXX" // 营业执照
|
||
}
|
||
```
|
||
|
||
### 2.4 项目统计视图
|
||
|
||
```sql
|
||
-- 项目统计视图(实时计算)
|
||
CREATE OR REPLACE VIEW v_project_statistics AS
|
||
SELECT
|
||
p.id AS project_id,
|
||
p.code AS project_code,
|
||
p.name AS project_name,
|
||
|
||
-- 建筑统计
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'BUILDING' THEN sn.id END) AS total_buildings,
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'UNIT' THEN sn.id END) AS total_units,
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'ROOM' THEN sn.id END) AS total_rooms,
|
||
|
||
-- 车位统计
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'PARKING_SPACE' THEN sn.id END) AS total_parking,
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'PARKING_SPACE' AND ps.status = 'AVAILABLE' THEN sn.id END) AS available_parking,
|
||
|
||
-- 面积统计
|
||
SUM(CASE WHEN sn.node_type = 'ROOM' THEN sn.building_area ELSE 0 END) AS total_building_area,
|
||
SUM(CASE WHEN sn.node_type = 'ROOM' THEN sn.usable_area ELSE 0 END) AS total_usable_area,
|
||
|
||
-- 入住统计
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'ROOM' AND sn.status = 'OCCUPIED' THEN sn.id END) AS occupied_rooms,
|
||
COUNT(DISTINCT CASE WHEN sn.node_type = 'ROOM' AND sn.status = 'VACANT' THEN sn.id END) AS vacant_rooms,
|
||
|
||
p.updated_at
|
||
FROM mdm_project p
|
||
LEFT JOIN mdm_space_node sn ON sn.project_id = p.id AND sn.is_deleted = FALSE
|
||
GROUP BY p.id, p.code, p.name, p.updated_at;
|
||
```
|
||
|
||
---
|
||
|
||
## 三、API接口设计
|
||
|
||
### 3.1 RESTful API 规范
|
||
|
||
```
|
||
基础路径: /api/v1/mdm/space-nodes
|
||
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 空间节点 API 接口 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
1. 基础 CRUD
|
||
POST /api/v1/mdm/space-nodes 创建空间节点
|
||
PUT /api/v1/mdm/space-nodes/{id} 更新空间节点
|
||
DELETE /api/v1/mdm/space-nodes/{id} 删除空间节点
|
||
GET /api/v1/mdm/space-nodes/{id} 查询空间节点详情
|
||
|
||
2. 树形结构
|
||
GET /api/v1/mdm/space-nodes/tree 获取完整树形结构
|
||
GET /api/v1/mdm/space-nodes/roots 获取根节点列表
|
||
GET /api/v1/mdm/space-nodes/{id}/children 获取子节点列表
|
||
GET /api/v1/mdm/space-nodes/{id}/ancestors 获取祖先节点链
|
||
GET /api/v1/mdm/space-nodes/{id}/descendants 获取所有子孙节点
|
||
PUT /api/v1/mdm/space-nodes/{id}/move 移动节点到新父节点
|
||
|
||
3. 类型查询
|
||
GET /api/v1/mdm/space-nodes/buildings 获取楼栋列表
|
||
GET /api/v1/mdm/space-nodes/rooms 获取房间列表
|
||
GET /api/v1/mdm/space-nodes/parking-spaces 获取车位列表
|
||
GET /api/v1/mdm/space-nodes/shops 获取商铺列表
|
||
|
||
4. 批量操作
|
||
POST /api/v1/mdm/space-nodes/batch 批量创建
|
||
PUT /api/v1/mdm/space-nodes/batch 批量更新
|
||
DELETE /api/v1/mdm/space-nodes/batch 批量删除
|
||
POST /api/v1/mdm/space-nodes/import Excel导入
|
||
GET /api/v1/mdm/space-nodes/export/template 下载导入模板
|
||
GET /api/v1/mdm/space-nodes/export 导出数据
|
||
|
||
5. 地图相关
|
||
GET /api/v1/mdm/space-nodes/map/markers 获取地图标注点
|
||
GET /api/v1/mdm/space-nodes/map/boundaries 获取区域边界
|
||
PUT /api/v1/mdm/space-nodes/{id}/location 更新位置信息
|
||
PUT /api/v1/mdm/space-nodes/{id}/boundary 更新区域边界
|
||
|
||
6. 统计分析
|
||
GET /api/v1/mdm/space-nodes/statistics 空间统计数据
|
||
GET /api/v1/mdm/space-nodes/statistics/by-type 按类型统计
|
||
GET /api/v1/mdm/space-nodes/statistics/by-status 按状态统计
|
||
```
|
||
|
||
### 3.2 请求/响应示例
|
||
|
||
**创建楼栋**:
|
||
```json
|
||
// POST /api/v1/mdm/space-nodes
|
||
{
|
||
"projectId": "uuid",
|
||
"code": "B001",
|
||
"name": "1号楼",
|
||
"nodeCategory": "BUILDING",
|
||
"nodeType": "BUILDING",
|
||
"parentId": null,
|
||
"buildingArea": 12000.00,
|
||
"longitude": 121.473701,
|
||
"latitude": 31.230416,
|
||
"attributes": {
|
||
"buildingType": "RESIDENTIAL",
|
||
"totalFloors": 18,
|
||
"undergroundFloors": 1,
|
||
"totalUnits": 4,
|
||
"elevatorCount": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
**创建单元**:
|
||
```json
|
||
{
|
||
"projectId": "uuid",
|
||
"code": "B001-U1",
|
||
"name": "1单元",
|
||
"nodeCategory": "BUILDING",
|
||
"nodeType": "UNIT",
|
||
"parentId": "building-uuid",
|
||
"attributes": {
|
||
"unitType": "STANDARD",
|
||
"elevatorCount": 1,
|
||
"roomsPerFloor": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
**批量创建房间**:
|
||
```json
|
||
// POST /api/v1/mdm/space-nodes/batch
|
||
{
|
||
"parentId": "unit-uuid",
|
||
"nodeType": "ROOM",
|
||
"startFloor": 1,
|
||
"endFloor": 18,
|
||
"roomsPerFloor": 2,
|
||
"roomPrefix": "B001-1-",
|
||
"roomTemplate": {
|
||
"nodeCategory": "BUILDING",
|
||
"buildingArea": 89.5,
|
||
"usableArea": 72.3,
|
||
"attributes": {
|
||
"roomType": "APARTMENT",
|
||
"layoutType": "3T2",
|
||
"roomCount": 3,
|
||
"livingRoomCount": 2,
|
||
"bathroomCount": 2
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、前端界面设计
|
||
|
||
### 4.1 页面结构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 空间节点管理页面 │
|
||
├─────────────────────────────────────────────────────────────────────────────┤
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 工具栏: [新增楼栋] [批量导入] [导出] [切换视图] [地图模式] │ │
|
||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||
│ ┌───────────────────────┬─────────────────────────────────────────────────┐ │
|
||
│ │ │ │ │
|
||
│ │ 树形导航 (左侧) │ 内容区域 (右侧) │ │
|
||
│ │ │ │ │
|
||
│ │ 📁 测试小区一期 │ ┌─────────────────────────────────────────┐ │ │
|
||
│ │ ├── 🏢 1号楼 │ │ 楼栋详情: 1号楼 │ │ │
|
||
│ │ │ ├── 🚪 1单元 │ │ ─────────────────────────────────────── │ │ │
|
||
│ │ │ │ ├── 🏠 101 │ │ 编码: B001 │ │ │
|
||
│ │ │ │ ├── 🏠 102 │ │ 楼层: 18层 (地下1层) │ │ │
|
||
│ │ │ │ └── ... │ │ 单元: 4个 │ │ │
|
||
│ │ │ ├── 🚪 2单元 │ │ 户数: 144户 │ │ │
|
||
│ │ │ └── ... │ │ 电梯: 2部 │ │ │
|
||
│ │ ├── 🏢 2号楼 │ │ │ │ │
|
||
│ │ ├── 🅿️ 地下车库 │ │ [编辑] [添加单元] [批量添加房间] [删除] │ │ │
|
||
│ │ │ ├── 🅿️ A区 │ └─────────────────────────────────────────┘ │ │
|
||
│ │ │ └── 🅿️ B区 │ │ │
|
||
│ │ └── 🏪 商业街 │ ┌─────────────────────────────────────────┐ │ │
|
||
│ │ ├── 🏪 S001 │ │ 子节点列表 │ │ │
|
||
│ │ └── 🏪 S002 │ │ ─────────────────────────────────────── │ │ │
|
||
│ │ │ │ [表格: 单元列表/房间列表] │ │ │
|
||
│ │ │ └─────────────────────────────────────────┘ │ │
|
||
│ └───────────────────────┴─────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.2 地图模式
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 地图模式 │
|
||
├─────────────────────────────────────────────────────────────────────────────┤
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 工具栏: [绘制项目边界] [标注楼栋] [标注车位] [标注设施] [保存] │ │
|
||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ │ │
|
||
│ │ ┌─────┐ │ │
|
||
│ │ │ 1# │ │ │
|
||
│ │ └─────┘ ┌─────┐ │ │
|
||
│ │ │ 2# │ │ │
|
||
│ │ ┌─────┐ └─────┘ │ │
|
||
│ │ │ 3# │ │ │
|
||
│ │ └─────┘ ┌──────────────────┐ │ │
|
||
│ │ │ 地下车库入口 │ │ │
|
||
│ │ ┌─────┐ └──────────────────┘ │ │
|
||
│ │ │ 4# │ │ │
|
||
│ │ └─────┘ │ │
|
||
│ │ │ │
|
||
│ │ ═════════════════════════════════════════════════════════════════ │ │
|
||
│ │ 小区主干道 │ │
|
||
│ │ ═════════════════════════════════════════════════════════════════ │ │
|
||
│ │ │ │
|
||
│ │ 🌳🌳🌳 🌳🌳🌳 │ │
|
||
│ │ 绿化带 绿化带 │ │
|
||
│ │ │ │
|
||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ 图层控制: [☑ 楼栋] [☑ 车位] [☑ 设施] [☑ 区域边界] [☐ 巡检路线] │ │
|
||
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.3 批量导入模板
|
||
|
||
**楼栋导入模板**:
|
||
| 楼栋编码* | 楼栋名称* | 总层数 | 地下层数 | 单元数 | 建筑面积 | 经度 | 纬度 |
|
||
|-----------|-----------|--------|----------|--------|----------|------|------|
|
||
| B001 | 1号楼 | 18 | 1 | 4 | 12000 | 121.473701 | 31.230416 |
|
||
| B002 | 2号楼 | 18 | 1 | 4 | 12000 | 121.473801 | 31.230516 |
|
||
|
||
**房间导入模板**:
|
||
| 楼栋编码* | 单元编码 | 楼层* | 房间编码* | 房间名称* | 建筑面积 | 使用面积 | 户型 | 房间数 | 客厅数 | 卫生间数 |
|
||
|-----------|----------|-------|-----------|-----------|----------|----------|------|--------|--------|----------|
|
||
| B001 | U1 | 1 | B001-1-101 | 101室 | 89.5 | 72.3 | 3T2 | 3 | 2 | 2 |
|
||
| B001 | U1 | 1 | B001-1-102 | 102室 | 89.5 | 72.3 | 3T2 | 3 | 2 | 2 |
|
||
|
||
---
|
||
|
||
## 五、地图服务对接方案
|
||
|
||
### 5.1 高德地图集成架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 地图服务集成架构 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||
│ 前端应用 │────▶│ 高德地图 JS API │────▶│ 高德地图服务 │
|
||
│ (Vue3) │ │ (AMap SDK) │ │ (云端) │
|
||
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
||
│ │
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────┐ ┌──────────────────┐
|
||
│ 后端服务 │ │ 地图数据存储 │
|
||
│ (Spring Boot) │ │ (PostGIS) │
|
||
└──────────────────┘ └──────────────────┘
|
||
│ │
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────────────────────────────────────────────────────────────┐
|
||
│ 数据同步流程 │
|
||
│ │
|
||
│ 1. 前端使用高德地图SDK进行标注/绘制 │
|
||
│ 2. 获取标注点经纬度或区域坐标数组 │
|
||
│ 3. 提交到后端API保存 │
|
||
│ 4. 后端存储到PostGIS字段(location/boundary) │
|
||
│ 5. 支持空间查询(范围内搜索、距离计算等) │
|
||
└──────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 5.2 地图服务接口设计
|
||
|
||
```java
|
||
/**
|
||
* 地图服务接口
|
||
*/
|
||
public interface MapService {
|
||
|
||
/**
|
||
* 地理编码:地址转坐标
|
||
*/
|
||
GeoLocation geocode(String address);
|
||
|
||
/**
|
||
* 逆地理编码:坐标转地址
|
||
*/
|
||
String reverseGeocode(BigDecimal longitude, BigDecimal latitude);
|
||
|
||
/**
|
||
* 距离计算
|
||
*/
|
||
BigDecimal calculateDistance(BigDecimal lng1, BigDecimal lat1,
|
||
BigDecimal lng2, BigDecimal lat2);
|
||
|
||
/**
|
||
* 范围查询:查找指定范围内的空间节点
|
||
*/
|
||
List<SpaceNode> findWithinRadius(BigDecimal longitude, BigDecimal latitude,
|
||
double radiusMeters);
|
||
|
||
/**
|
||
* 多边形查询:查找多边形区域内的空间节点
|
||
*/
|
||
List<SpaceNode> findWithinPolygon(List<GeoPoint> polygon);
|
||
}
|
||
|
||
/**
|
||
* 高德地图实现
|
||
*/
|
||
@Service
|
||
public class AmapMapServiceImpl implements MapService {
|
||
|
||
@Value("${amap.api.key}")
|
||
private String apiKey;
|
||
|
||
@Value("${amap.api.geocode-url}")
|
||
private String geocodeUrl;
|
||
|
||
// ... 实现方法
|
||
}
|
||
```
|
||
|
||
### 5.3 前端地图组件
|
||
|
||
```vue
|
||
<!-- MapEditor.vue -->
|
||
<template>
|
||
<div class="map-editor">
|
||
<!-- 地图容器 -->
|
||
<div ref="mapContainer" class="map-container"></div>
|
||
|
||
<!-- 工具栏 -->
|
||
<div class="map-toolbar">
|
||
<a-space>
|
||
<a-button @click="setMode('marker')" :type="mode === 'marker' ? 'primary' : 'default'">
|
||
标注点位
|
||
</a-button>
|
||
<a-button @click="setMode('polygon')" :type="mode === 'polygon' ? 'primary' : 'default'">
|
||
绘制区域
|
||
</a-button>
|
||
<a-button @click="clearSelection">清除</a-button>
|
||
<a-button type="primary" @click="saveLocation">保存</a-button>
|
||
</a-space>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import AMapLoader from '@amap/amap-jsapi-loader';
|
||
|
||
const props = defineProps<{
|
||
modelValue?: {
|
||
longitude?: number;
|
||
latitude?: number;
|
||
boundary?: Array<{ longitude: number; latitude: number }>;
|
||
};
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
'update:modelValue': [value: any];
|
||
}>();
|
||
|
||
let map: any = null;
|
||
let marker: any = null;
|
||
let polygon: any = null;
|
||
let AMap: any = null;
|
||
|
||
onMounted(async () => {
|
||
AMap = await AMapLoader.load({
|
||
key: 'YOUR_AMAP_KEY',
|
||
version: '2.0',
|
||
plugins: ['AMap.Marker', 'AMap.Polygon', 'AMap.Geocoder']
|
||
});
|
||
|
||
map = new AMap.Map(mapContainer.value, {
|
||
zoom: 15,
|
||
center: [121.473701, 31.230416]
|
||
});
|
||
|
||
// 初始化已有标注
|
||
if (props.modelValue?.longitude && props.modelValue?.latitude) {
|
||
addMarker(props.modelValue.longitude, props.modelValue.latitude);
|
||
}
|
||
});
|
||
|
||
function addMarker(lng: number, lat: number) {
|
||
if (marker) marker.setMap(null);
|
||
marker = new AMap.Marker({
|
||
position: [lng, lat],
|
||
draggable: true
|
||
});
|
||
marker.setMap(map);
|
||
map.setCenter([lng, lat]);
|
||
}
|
||
|
||
function saveLocation() {
|
||
const result: any = {};
|
||
|
||
if (marker) {
|
||
const pos = marker.getPosition();
|
||
result.longitude = pos.lng;
|
||
result.latitude = pos.lat;
|
||
}
|
||
|
||
if (polygon) {
|
||
const path = polygon.getPath();
|
||
result.boundary = path.map((p: any) => ({
|
||
longitude: p.lng,
|
||
latitude: p.lat
|
||
}));
|
||
}
|
||
|
||
emit('update:modelValue', result);
|
||
}
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
## 六、实施计划
|
||
|
||
### 6.1 阶段划分
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||
│ 实施阶段规划 │
|
||
└─────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
阶段一:基础重构 (P0, 预计2周)
|
||
├── 数据模型重构
|
||
│ ├── 扩展 mdm_space_node 表结构
|
||
│ ├── 迁移 ParkingSpace 到 SpaceNode
|
||
│ └── 创建统计视图
|
||
├── API 接口重构
|
||
│ ├── 统一字段命名
|
||
│ ├── 完善树形操作接口
|
||
│ └── 添加批量操作接口
|
||
└── 前端页面重构
|
||
├── 树形组件集成
|
||
├── 表单字段对齐
|
||
└── 列表页面优化
|
||
|
||
阶段二:批量导入 (P0, 预计1周)
|
||
├── 导入模板设计
|
||
├── Excel解析服务
|
||
├── 数据校验逻辑
|
||
└── 导入结果反馈
|
||
|
||
阶段三:地图服务 (P1, 预计2周)
|
||
├── 高德地图SDK集成
|
||
├── 地图编辑组件
|
||
├── 位置数据存储
|
||
└── 空间查询功能
|
||
|
||
阶段四:业务关联 (P1, 预计1周)
|
||
├── 工单关联空间节点
|
||
├── 巡检点关联空间节点
|
||
├── 收费项目关联空间节点
|
||
└── 能耗计量点关联
|
||
```
|
||
|
||
### 6.2 技术依赖
|
||
|
||
| 依赖 | 版本 | 用途 |
|
||
|------|------|------|
|
||
| PostGIS | 3.x | 空间数据存储和查询 |
|
||
| 高德地图 JS API | 2.0 | 前端地图展示和编辑 |
|
||
| Apache POI | 5.x | Excel导入导出 |
|
||
| EasyExcel | 3.x | 大数据量Excel处理 |
|
||
|
||
---
|
||
|
||
## 七、风险与对策
|
||
|
||
| 风险 | 影响 | 对策 |
|
||
|------|------|------|
|
||
| 数据迁移风险 | 现有数据丢失 | 先备份,编写迁移脚本,测试验证 |
|
||
| 性能风险 | 大型项目查询慢 | 使用CTE递归优化,添加缓存 |
|
||
| 地图服务费用 | 高德API调用收费 | 控制调用频率,使用本地缓存 |
|
||
|
||
---
|
||
|
||
## 八、开发任务清单 (2026-03-23)
|
||
|
||
### 8.1 后端任务
|
||
|
||
| 任务 | 状态 | 说明 |
|
||
|------|------|------|
|
||
| 重构 SpaceNode 实体 | 待开发 | 按设计文档完善字段 |
|
||
| 重构 SpaceNodeType 枚举 | 待开发 | 添加 BUILDING/UNIT/ROOM 等 |
|
||
| 重构 SpaceNodeService | 待开发 | 树形结构操作 |
|
||
| 重构 SpaceNodeRepository | 待开发 | JPA 查询方法 |
|
||
| 重构 SpaceNodeController | 待开发 | RESTful API |
|
||
| 添加 DTO 类 | 待开发 | 创建/更新/响应 DTO |
|
||
| 编写单元测试 | 待开发 | TDD 模式 |
|
||
|
||
### 8.2 前端任务
|
||
|
||
| 任务 | 状态 | 说明 |
|
||
|------|------|------|
|
||
| 创建 space.ts 类型定义 | 待开发 | TypeScript 类型 |
|
||
| 创建 space.ts API | 待开发 | API 函数 |
|
||
| 创建空间管理页面 | 待开发 | Space.vue |
|
||
| 创建空间树形组件 | 待开发 | 左侧树导航 |
|
||
| 创建空间表单组件 | 待开发 | 新增/编辑 |
|
||
| E2E 测试验证 | 待开发 | Playwright 测试 |
|
||
|
||
### 8.3 API 接口清单
|
||
|
||
| 接口 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| 获取空间树 | GET | /api/v1/mdm/space-nodes/tree | 项目空间树 |
|
||
| 获取根节点 | GET | /api/v1/mdm/space-nodes/roots | 项目下根节点 |
|
||
| 获取节点详情 | GET | /api/v1/mdm/space-nodes/{id} | 节点详情 |
|
||
| 创建节点 | POST | /api/v1/mdm/space-nodes | 创建空间节点 |
|
||
| 更新节点 | PUT | /api/v1/mdm/space-nodes/{id} | 更新节点 |
|
||
| 删除节点 | DELETE | /api/v1/mdm/space-nodes/{id} | 删除节点 |
|
||
| 获取子节点 | GET | /api/v1/mdm/space-nodes/{id}/children | 子节点列表 |
|
||
| 批量创建 | POST | /api/v1/mdm/space-nodes/batch | 批量创建 |
|
||
| 批量导入 | POST | /api/v1/mdm/space-nodes/import | Excel导入 |
|
||
|
||
---
|
||
|
||
**文档版本**: v2.0
|
||
**最后更新**: 2026-03-23
|