20 KiB
20 KiB
空间与主数据领域技术方案
领域编号: 4.1
微服务: ether-mdm
最后更新: 2026-02-14
一、领域概述
1.1 领域职责
空间与主数据领域是 Ether 平台的物理数字孪生底座,负责管理:
- 物理空间的层级结构(园区→楼栋→楼层→房间)
- 房产明细与权属关系
- 业主信息管理
- 巡检管理(计划、任务、记录)
- 访客管理(预约、记录、黑名单)
1.2 核心概念
| 概念 | 说明 | 对应实体 |
|---|---|---|
| 空间节点 | 物理空间的抽象,支持树形结构 | SpaceNode |
| 房间详情 | 房产的具体属性信息 | RoomDetail |
| 产权 | 房产与业主的关联关系 | Ownership |
| 业主 | 房产的所有者或使用者 | Owner |
| 巡检 | 定期检查任务体系 | InspectionPlan/Task/Record |
| 访客 | 访客预约与通行管理 | VisitorAppointment/Record |
二、领域模型
2.1 聚合根设计
SpaceNode(空间节点)
@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(房间详情)
@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(产权信息)
@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(业主)
@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(巡检计划)
@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(巡检点)
@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(巡检任务)
@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(巡检记录)
@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(访客预约)
@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(访客通行记录)
@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(访客黑名单)
@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
@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
@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
@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 | 空间搜索 | 基于经纬度的附近空间查询 |
七、数据库表结构
-- 空间节点表
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 服务负责人维护