ether-docs/02-DESIGN/domains/01-SPACE_AND_MDM.md

738 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 空间与主数据领域技术方案
**领域编号**: 4.1
**微服务**: ether-mdm
**最后更新**: 2026-02-14
---
## 一、领域概述
### 1.1 领域职责
空间与主数据领域是 Ether 平台的物理数字孪生底座,负责管理:
- 物理空间的层级结构(园区→楼栋→楼层→房间)
- 房产明细与权属关系
- 业主信息管理
- 巡检管理(计划、任务、记录)
- 访客管理(预约、记录、黑名单)
### 1.2 核心概念
| 概念 | 说明 | 对应实体 |
| ------------ | ---------------------------- | -------------------------- |
| **空间节点** | 物理空间的抽象,支持树形结构 | SpaceNode |
| **房间详情** | 房产的具体属性信息 | RoomDetail |
| **产权** | 房产与业主的关联关系 | Ownership |
| **业主** | 房产的所有者或使用者 | Owner |
| **巡检** | 定期检查任务体系 | InspectionPlan/Task/Record |
| **访客** | 访客预约与通行管理 | VisitorAppointment/Record |
---
## 二、领域模型
### 2.1 聚合根设计
#### SpaceNode空间节点
```java
@Entity
@Table(name = "mdm_space_node")
@Data
public class SpaceNode {
@Id
private UUID id;
private UUID projectId;
private String code; // 节点编码,唯一
private String name; // 节点名称
private SpaceNodeType nodeType; // PROJECT/BUILDING/FLOOR/ROOM/AREA
private SpaceNodeStatus status; // ACTIVE/INACTIVE
// 树形结构
private UUID parentId; // 父节点ID
private String treePath; // 路径: 1.2.3.4
private Integer level; // 层级: 0-4
private Integer sortOrder; // 排序
// 空间属性
private BigDecimal areaSqm; // 面积
private BigDecimal longitude; // 经度
private BigDecimal latitude; // 纬度
private String address; // 地址
// 扩展属性(JSONB)
private String attributes;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private UUID createdBy;
private UUID updatedBy;
}
```
**业务规则**:
- code 在同一项目下唯一
- treePath 自动生成,格式为父路径+当前ID
- 删除节点时检查是否有子节点
- 删除节点时检查是否有关联业务数据
#### RoomDetail房间详情
```java
@Entity
@Table(name = "mdm_room_detail")
@Data
public class RoomDetail {
@Id
private UUID id;
private UUID spaceNodeId; // 关联SpaceNode
// 房产属性
private String roomType; // 住宅/商铺/办公/仓库
private BigDecimal buildArea; // 建筑面积
private BigDecimal usableArea; // 使用面积
private String orientation; // 朝向
private Integer floor; // 所在楼层
private Integer roomCount; // 房间数
private Integer hallCount; // 厅数
// 状态
private RoomStatus status; // 空置/已售/已租/装修中
// 扩展属性
private String attributes;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
#### Ownership产权信息
```java
@Entity
@Table(name = "mdm_ownership")
@Data
public class Ownership {
@Id
private UUID id;
private UUID spaceNodeId; // 关联房产
private UUID ownerId; // 关联业主
// 产权属性
private OwnershipType type; // 产权/使用权/租赁权
private BigDecimal sharePercent; // 产权份额(0-100)
private LocalDate startDate; // 起始日期
private LocalDate endDate; // 结束日期(租赁)
// 证件信息
private String certType; // 房产证/购房合同/租赁合同
private String certNo; // 证件号码
private String certFileUrl; // 证件扫描件
// 状态
private OwnershipStatus status; // 有效/过期/注销
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
#### Owner业主
```java
@Entity
@Table(name = "mdm_owner")
@Data
public class Owner {
@Id
private UUID id;
private UUID projectId;
// 基本信息
private String name;
private String phone;
private String email;
private OwnerType type; // 个人/企业
// 证件信息
private String idCardType; // 身份证/护照/营业执照
private String idCardNo;
private String idCardFileUrl;
// 企业信息
private String companyName;
private String unifiedSocialCreditCode;
// 关联账户
private UUID userId; // 关联系统用户
// 状态
private OwnerStatus status;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
---
## 三、巡检管理
### 3.1 聚合根设计
#### InspectionPlan巡检计划
```java
@Entity
@Table(name = "mdm_inspection_plan")
@Data
public class InspectionPlan {
@Id
private UUID id;
private UUID projectId;
private String name;
private String description;
// 巡检类型
private InspectionType type; // DAILY/REGULAR/SPECIAL
// 周期配置
private String cronExpression; // Cron表达式
private LocalTime executeTime; // 执行时间
private Integer advanceDays; // 提前生成任务天数
// 状态
private PlanStatus status; // ENABLED/DISABLED
// 关联巡检点
@OneToMany(mappedBy = "plan", cascade = CascadeType.ALL)
private List<InspectionPoint> points;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
#### InspectionPoint巡检点
```java
@Entity
@Table(name = "mdm_inspection_point")
@Data
public class InspectionPoint {
@Id
private UUID id;
private UUID planId;
private UUID spaceNodeId; // 关联空间节点
private String name;
private String description;
private Integer sortOrder;
// 检查项(JSONB)
private String checkItems; // [{"item":"消防设施","standard":"正常","type":"BOOLEAN"}]
// 关联设备
private UUID equipmentId; // 可选,关联设备
}
```
#### InspectionTask巡检任务
```java
@Entity
@Table(name = "mdm_inspection_task")
@Data
public class InspectionTask {
@Id
private UUID id;
private UUID projectId;
private UUID planId;
private String taskNo; // 任务编号
private LocalDate planDate; // 计划日期
// 执行人
private UUID inspectorId;
private String inspectorName;
// 状态
private TaskStatus status; // PENDING/IN_PROGRESS/COMPLETED/OVERDUE
// 时间记录
private LocalDateTime startTime;
private LocalDateTime endTime;
// 结果
private TaskResult result; // NORMAL/ABNORMAL
private String remark;
// 关联记录
@OneToMany(mappedBy = "task")
private List<InspectionRecord> records;
// 异常处理
private UUID workOrderId; // 异常时关联的工单
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
#### InspectionRecord巡检记录
```java
@Entity
@Table(name = "mdm_inspection_record")
@Data
public class InspectionRecord {
@Id
private UUID id;
private UUID taskId;
private UUID pointId;
// 巡检结果
private String checkItem; // 检查项名称
private String standard; // 标准值
private String actualValue; // 实际值
private CheckResult result; // PASS/FAIL/NA
// 备注和图片
private String remark;
private String images;
// 定位
private BigDecimal longitude;
private BigDecimal latitude;
// 时间
private LocalDateTime checkTime;
}
```
### 3.2 巡检流程
```
1. 创建巡检计划
2. 定时生成巡检任务(每天凌晨)
3. 巡检人接收任务通知
4. 现场扫码签到GPS定位校验
5. 按巡检点逐项检查
6. 提交巡检结果
7. 异常自动创建工单(可选)
8. 任务完成
```
---
## 四、访客管理
### 4.1 聚合根设计
#### VisitorAppointment访客预约
```java
@Entity
@Table(name = "mdm_visitor_appointment")
@Data
public class VisitorAppointment {
@Id
private UUID id;
private UUID projectId;
// 访客信息
private String visitorName;
private String visitorPhone;
private String visitorIdCard;
private VisitorType visitorType; // VISITOR/EXPRESS/DELIVERY/MAINTENANCE
// 被访人信息
private UUID hostId;
private String hostName;
private String hostPhone;
private UUID spaceNodeId; // 访问地点
// 预约信息
private LocalDate visitDate;
private LocalTime visitTime;
private Integer duration; // 预计时长(分钟)
private String purpose;
private Integer visitorCount; // 访客人数
// 凭证
private String qrCode; // 动态二维码
private LocalDateTime qrExpireTime;
// 车牌(车辆访客)
private String plateNumber;
// 状态
private AppointmentStatus status; // PENDING/CONFIRMED/CANCELLED/COMPLETED/EXPIRED
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private UUID createdBy;
}
```
#### VisitorAccessRecord访客通行记录
```java
@Entity
@Table(name = "mdm_visitor_access_record")
@Data
public class VisitorAccessRecord {
@Id
private UUID id;
private UUID appointmentId;
// 通行信息
private LocalDateTime entryTime;
private LocalDateTime exitTime;
private String entryGate; // 入口
private String exitGate; // 出口
// 验证方式
private VerifyType verifyType; // QR_CODE/ID_CARD/FACE
private String verifyCode;
// 现场照片
private String entryPhoto;
private String exitPhoto;
// 设备信息
private String deviceId; // 门禁设备ID
// 状态
private AccessStatus status; // ENTERED/EXITED
}
```
#### VisitorBlacklist访客黑名单
```java
@Entity
@Table(name = "mdm_visitor_blacklist")
@Data
public class VisitorBlacklist {
@Id
private UUID id;
private UUID projectId;
// 访客标识(至少填一项)
private String visitorName;
private String visitorPhone;
private String visitorIdCard;
// 拉黑信息
private String reason;
private BlacklistType type; // TEMPORARY/PERMANENT
private LocalDateTime blockTime;
private LocalDateTime expireTime; // null为永久
// 操作人
private UUID operatorId;
private String operatorName;
// 状态
private BlacklistStatus status; // ACTIVE/LIFTED
}
```
### 4.2 访客流程
```
1. 业主发起预约APP/小程序)
2. 系统生成动态二维码
3. 访客收到邀请(短信/微信)
4. 访客到达现场
5. 扫码/刷身份证通行
6. 系统记录入场时间
7. 业主收到通知
8. 访客离场扫码
9. 系统记录离场时间
```
---
## 五、API 接口
### 5.1 SpaceNode API
```java
@RestController
@RequestMapping("/api/v1/mdm/space-nodes")
@Tag(name = "空间节点管理")
public class SpaceNodeController {
@PostMapping
@Operation(summary = "创建空间节点")
public Result<SpaceNodeVO> create(@RequestBody @Valid SpaceNodeCreateRequest request);
@GetMapping("/tree")
@Operation(summary = "获取空间树")
public Result<List<SpaceNodeTreeVO>> tree(@RequestParam UUID projectId);
@GetMapping("/{id}")
@Operation(summary = "获取节点详情")
public Result<SpaceNodeVO> getById(@PathVariable UUID id);
@PutMapping("/{id}")
@Operation(summary = "更新节点")
public Result<SpaceNodeVO> update(@PathVariable UUID id,
@RequestBody @Valid SpaceNodeUpdateRequest request);
@DeleteMapping("/{id}")
@Operation(summary = "删除节点")
public Result<Void> delete(@PathVariable UUID id);
@GetMapping("/{id}/children")
@Operation(summary = "获取子节点")
public Result<List<SpaceNodeVO>> getChildren(@PathVariable UUID id);
@GetMapping("/{id}/path")
@Operation(summary = "获取节点路径")
public Result<List<SpaceNodeVO>> getPath(@PathVariable UUID id);
}
```
### 5.2 Inspection API
```java
@RestController
@RequestMapping("/api/v1/mdm/inspections")
@Tag(name = "巡检管理")
public class InspectionController {
// 巡检计划
@PostMapping("/plans")
@Operation(summary = "创建巡检计划")
public Result<InspectionPlanVO> createPlan(@RequestBody @Valid InspectionPlanCreateRequest request);
@GetMapping("/plans")
@Operation(summary = "分页查询计划")
public Result<Page<InspectionPlanVO>> pagePlans(InspectionPlanQueryRequest request);
// 巡检任务
@GetMapping("/tasks")
@Operation(summary = "分页查询任务")
public Result<Page<InspectionTaskVO>> pageTasks(InspectionTaskQueryRequest request);
@PostMapping("/tasks/{id}/start")
@Operation(summary = "开始巡检")
public Result<InspectionTaskVO> startTask(@PathVariable UUID id);
@PostMapping("/tasks/{id}/complete")
@Operation(summary = "完成巡检")
public Result<InspectionTaskVO> completeTask(@PathVariable UUID id,
@RequestBody InspectionCompleteRequest request);
@PostMapping("/tasks/{id}/records")
@Operation(summary = "提交巡检记录")
public Result<Void> submitRecords(@PathVariable UUID id,
@RequestBody List<InspectionRecordSubmitRequest> records);
}
```
### 5.3 Visitor API
```java
@RestController
@RequestMapping("/api/v1/mdm/visitors")
@Tag(name = "访客管理")
public class VisitorController {
@PostMapping("/appointments")
@Operation(summary = "创建访客预约")
public Result<VisitorAppointmentVO> createAppointment(@RequestBody @Valid VisitorAppointmentCreateRequest request);
@GetMapping("/appointments/{id}/qr-code")
@Operation(summary = "获取访客二维码")
public Result<String> getQrCode(@PathVariable UUID id);
@PostMapping("/access/verify")
@Operation(summary = "验证访客凭证")
public Result<VisitorAccessResultVO> verifyAccess(@RequestBody VisitorVerifyRequest request);
@PostMapping("/access/entry")
@Operation(summary = "记录访客入场")
public Result<Void> recordEntry(@RequestBody VisitorEntryRequest request);
@PostMapping("/access/exit")
@Operation(summary = "记录访客离场")
public Result<Void> recordExit(@RequestBody VisitorExitRequest request);
@PostMapping("/blacklist")
@Operation(summary = "添加黑名单")
public Result<VisitorBlacklistVO> addBlacklist(@RequestBody @Valid BlacklistAddRequest request);
}
```
---
## 六、实现状态与差异
### 6.1 实现状态
| 功能模块 | 实现状态 | 备注 |
| ------------------- | --------- | ------------------ |
| SpaceNode | 🟢 已实现 | 基础CRUD、树形查询 |
| RoomDetail | 🟢 已实现 | 基础CRUD |
| Ownership | 🟢 已实现 | 基础CRUD |
| Owner | 🟢 已实现 | 基础CRUD |
| InspectionPlan | 🟢 已实现 | 基础CRUD |
| InspectionTask | 🟢 已实现 | 含定时任务生成 |
| VisitorAppointment | 🟢 已实现 | 含二维码生成 |
| VisitorAccessRecord | 🟢 已实现 | 基础记录 |
| VisitorBlacklist | 🟢 已实现 | 基础CRUD |
### 6.2 与设计方案的差异
| 设计项 | 设计方案 | 现有实现 | 差异分析 |
| ------------ | ---------------- | ------------------ | -------------------------------------------------------- |
| **空间坐标** | PostGIS geometry | longitude/latitude | ⚠️ 移除了PostGIS依赖简化实现 |
| **树形结构** | PostgreSQL ltree | 字符串treePath | ⚠️ 未使用ltree扩展当前数据规模可接受 |
| **巡检归属** | ether-ops | ether-mdm | ✅ 已确认:巡检点与空间节点紧密关联,归属 ether-mdm 合理 |
| **访客归属** | ether-ops | ether-mdm | ✅ 已确认:访客与业主/空间紧密关联,归属 ether-mdm 合理 |
| **设备管理** | 曾在 ether-mdm | ether-asset | ✅ 已修正:设备相关功能已迁移至 ether-asset 服务 |
### 6.3 待改进项
| 优先级 | 改进项 | 说明 |
| ------ | ---------------- | -------------------------------------------------------- |
| P2 | 优化树形查询 | 考虑引入ltree或物化路径优化 |
| P2 | 空间搜索 | 基于经纬度的附近空间查询 |
| ~~P3~~ | ~~领域边界调整~~ | ~~评估巡检、访客是否迁移到ether-ops~~ 已确认当前归属合理 |
---
## 七、数据库表结构
```sql
-- 空间节点表
CREATE TABLE mdm_space_node (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
code VARCHAR(50) NOT NULL,
name VARCHAR(100) NOT NULL,
node_type VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
parent_id UUID,
tree_path VARCHAR(1000),
level INTEGER,
sort_order INTEGER DEFAULT 0,
area_sqm NUMERIC(10,2),
longitude NUMERIC(10,6),
latitude NUMERIC(10,6),
address VARCHAR(255),
description VARCHAR(500),
attributes JSONB,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by UUID,
updated_by UUID,
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_path ON mdm_space_node(tree_path);
CREATE INDEX idx_space_node_type ON mdm_space_node(node_type);
-- 巡检计划表
CREATE TABLE mdm_inspection_plan (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
description VARCHAR(500),
type VARCHAR(20) NOT NULL,
cron_expression VARCHAR(50),
execute_time TIME,
advance_days INTEGER DEFAULT 1,
status VARCHAR(20) NOT NULL DEFAULT 'ENABLED',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by UUID,
updated_by UUID
);
-- 巡检任务表
CREATE TABLE mdm_inspection_task (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
plan_id UUID NOT NULL,
task_no VARCHAR(32) NOT NULL,
plan_date DATE NOT NULL,
inspector_id UUID,
inspector_name VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
start_time TIMESTAMP,
end_time TIMESTAMP,
result VARCHAR(20),
remark VARCHAR(500),
work_order_id UUID,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 访客预约表
CREATE TABLE mdm_visitor_appointment (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
visitor_name VARCHAR(100) NOT NULL,
visitor_phone VARCHAR(20),
visitor_id_card VARCHAR(18),
visitor_type VARCHAR(20) NOT NULL,
host_id UUID,
host_name VARCHAR(100),
host_phone VARCHAR(20),
space_node_id UUID,
visit_date DATE NOT NULL,
visit_time TIME,
duration INTEGER,
purpose VARCHAR(200),
visitor_count INTEGER DEFAULT 1,
qr_code VARCHAR(255),
qr_expire_time TIMESTAMP,
plate_number VARCHAR(20),
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by UUID
);
```
---
**文档维护**: 本领域技术方案由 ether-mdm 服务负责人维护