35 KiB
35 KiB
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 统一空间节点模型
设计原则:
- 所有空间实体统一使用
mdm_space_node表 - 通过
node_type区分不同类型 - 通过
node_category区分大类(建筑、车位、设施) - 支持树形结构和地图数据
-- ============================================================
-- 空间节点主表 (统一管理所有空间实体)
-- ============================================================
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 节点类型与属性定义
/**
* 空间节点大类
*/
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)
// 楼栋属性
{
"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 项目统计视图
-- 项目统计视图(实时计算)
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 请求/响应示例
创建楼栋:
// 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
}
}
创建单元:
{
"projectId": "uuid",
"code": "B001-U1",
"name": "1单元",
"nodeCategory": "BUILDING",
"nodeType": "UNIT",
"parentId": "building-uuid",
"attributes": {
"unitType": "STANDARD",
"elevatorCount": 1,
"roomsPerFloor": 2
}
}
批量创建房间:
// 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 地图服务接口设计
/**
* 地图服务接口
*/
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 前端地图组件
<!-- 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调用收费 | 控制调用频率,使用本地缓存 |
文档版本: v2.0
最后更新: 2026-02-16