# 空间与主数据领域技术方案 **领域编号**: 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 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 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 create(@RequestBody @Valid SpaceNodeCreateRequest request); @GetMapping("/tree") @Operation(summary = "获取空间树") public Result> tree(@RequestParam UUID projectId); @GetMapping("/{id}") @Operation(summary = "获取节点详情") public Result getById(@PathVariable UUID id); @PutMapping("/{id}") @Operation(summary = "更新节点") public Result update(@PathVariable UUID id, @RequestBody @Valid SpaceNodeUpdateRequest request); @DeleteMapping("/{id}") @Operation(summary = "删除节点") public Result delete(@PathVariable UUID id); @GetMapping("/{id}/children") @Operation(summary = "获取子节点") public Result> getChildren(@PathVariable UUID id); @GetMapping("/{id}/path") @Operation(summary = "获取节点路径") public Result> 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 createPlan(@RequestBody @Valid InspectionPlanCreateRequest request); @GetMapping("/plans") @Operation(summary = "分页查询计划") public Result> pagePlans(InspectionPlanQueryRequest request); // 巡检任务 @GetMapping("/tasks") @Operation(summary = "分页查询任务") public Result> pageTasks(InspectionTaskQueryRequest request); @PostMapping("/tasks/{id}/start") @Operation(summary = "开始巡检") public Result startTask(@PathVariable UUID id); @PostMapping("/tasks/{id}/complete") @Operation(summary = "完成巡检") public Result completeTask(@PathVariable UUID id, @RequestBody InspectionCompleteRequest request); @PostMapping("/tasks/{id}/records") @Operation(summary = "提交巡检记录") public Result submitRecords(@PathVariable UUID id, @RequestBody List records); } ``` ### 5.3 Visitor API ```java @RestController @RequestMapping("/api/v1/mdm/visitors") @Tag(name = "访客管理") public class VisitorController { @PostMapping("/appointments") @Operation(summary = "创建访客预约") public Result createAppointment(@RequestBody @Valid VisitorAppointmentCreateRequest request); @GetMapping("/appointments/{id}/qr-code") @Operation(summary = "获取访客二维码") public Result getQrCode(@PathVariable UUID id); @PostMapping("/access/verify") @Operation(summary = "验证访客凭证") public Result verifyAccess(@RequestBody VisitorVerifyRequest request); @PostMapping("/access/entry") @Operation(summary = "记录访客入场") public Result recordEntry(@RequestBody VisitorEntryRequest request); @PostMapping("/access/exit") @Operation(summary = "记录访客离场") public Result recordExit(@RequestBody VisitorExitRequest request); @PostMapping("/blacklist") @Operation(summary = "添加黑名单") public Result 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 服务负责人维护