nVisual 二次开发:节点属性体系与属性值操作速查

📅 2026/8/1 9:48:17
nVisual 二次开发:节点属性体系与属性值操作速查
适用读者需要给 nVisual 设备节点扩展自定义字段、按属性筛选设备、批量维护属性值的后端二次开发者。概述nVisual 的设备节点属性采用EAV 动态属性设计属性定义存在node_properties表属性值存在node_property_values表。给设备加字段不需要修改 nodes 表结构只需新增一条属性定义并写入属性值即可。适用场景客户要求设备加个’维保到期’字段不 ALTER TABLE配属性即可按自定义属性筛选设备如找出维保快过期的设备从 Excel 批量导入属性值用于资产盘点、维保数据回填控制属性面板的展示顺序、必填项、默认值一、基础概念两张核心表节点属性体系由两张表组成表名作用关系node_properties属性定义描述有哪些字段一个属性对应每个节点一条值node_property_values属性值描述每个节点的字段值多对一关联属性定义读写链路属性定义node_properties决定设备有哪些字段属性值node_property_values按 node_id property_id 存放每个设备的具体值。二、表字段速查node_properties 属性定义表字段类型说明idbigint属性ID自增node_type_idbigint所属节点类型决定哪种设备有这个字段必填namevarchar(255)属性名如 维保到期、IP必填default_valuevarchar(255)默认值可空positioninteger属性面板显示顺序默认1mandatoryboolean是否必填displayedboolean是否在属性面板显示is_systemboolean是否系统内置字段内置字段勿改not_editableboolean是否禁止编辑node_property_values 属性值表字段类型说明idbigint值记录ID自增node_idbigint所属节点ID必填property_idbigint所属属性ID必填valuevarchar(255)属性值统一文本存储日期和数字也存文本delete_timetimestamp逻辑删除时间非空即视为已删除重要约定属性值采用逻辑删除UPDATE 而非 DELETE所有查询必须带delete_time IS NULL条件否则会查出已删除数据。三、属性操作详解以下接口统一前缀 /wapi/v1。1. 新增属性给设备加字段每个节点类型拥有独立的属性集合。新增属性走 POST 接口name 必填缺失直接报错其余字段可选POST /wapi/v1/node_properties/{nodeTypeId} Content-Type: application/json{name:维保到期,displayed:true,position:1,mandatory:false}注意三个真实行为中文名自动映射name 若命中内置属性中文词表如设备型号会自动转成英文属性名存储重名拦截该类型下已存在同名属性时返回 duplicate_name不会创建重复字段属性创建后可随时在属性面板按 position 排序显示查询某类型已有的全部属性属性面板按此渲染GET /wapi/v1/node_properties/{nodeTypeId}/node_type_id2. 新增属性值body 使用 nodeId、propertyId、value 三件套。写入成功会级联触发创建 visNodeFields 供拓扑图元展示、WebSocket 推送、RabbitMQ 审计日志。POST /wapi/v1/node_property_values Content-Type: application/json{nodeId:1001,propertyId:999,value:2026-09-30}3. 更新属性值按值记录ID更新PUT /wapi/v1/node_property_values/{nodePropertyValueId} Content-Type: application/json{value:2027-03-31}4. 查询接口一览方法路径说明GET/node_property_values/{nodeId}/node_id查某节点全部属性值GET/node_property_values/{nodeId}/node_id/{name}/name查某节点指定属性名的值POST/node_property_values/getByNodeIdAndNameList按节点ID 属性名列表批量查询POST/node_properties/get_node_id_by_name_and_value按 属性名属性值 等值匹配反查节点ID5. 批量修改属性值一次把多个节点的同一属性设为同一个值资产盘点、维保回填常用PUT /wapi/v1/node_property_values/batch_modification Content-Type: application/json{nodeIds:[1001,1002,1003],propertyName:维保到期,propertyValue:2027-03-31}四、核心查询 SQL二次开发最常用-- 1. 查一个节点的全部属性名称值拼属性面板SELECTnp.name,npv.valueFROMnode_property_values npvLEFTJOINnode_properties npONnpv.property_idnp.idWHEREnpv.node_id12345ANDnpv.delete_timeISNULL;-- 2. 按属性名值反查节点ID按维保到期筛设备SELECTnpv.node_id,npv.valueFROMnode_property_values npvJOINnode_properties npONnpv.property_idnp.idANDnp.name维保到期WHEREnpv.value2026-12-31ANDnpv.delete_timeISNULL;-- 3. 判断属性是否已存在防重复创建LOWER 忽略大小写SELECT*FROMnode_propertiesWHEREnode_type_id10001ANDLOWER(name)LOWER(维保到期);-- 4. 按属性值全局筛选必须建复合索引CREATEINDEXidx_npv_property_valueONnode_property_values(property_id,value);五、场景示例场景 1客户要加维保到期字段客户要求设备属性面板增加维保到期日期。后端两步完成不改表结构第一步给类型 10001 创建属性POST /wapi/v1/node_properties/10001 Content-Type: application/json{name:维保到期,displayed:true,position:1}第二步给具体设备写值节点 1001、1002POST /wapi/v1/node_property_values Content-Type: application/json{nodeId:1001,propertyId:999,value:2026-09-30}{nodeId:1002,propertyId:999,value:2027-06-30}场景 2按属性筛选快过保设备资产盘点场景找出维保在 2026-12-31 前到期的所有设备。注意内置接口 get_node_id_by_name_and_value 是精确等值匹配按 namevalue 反查节点ID范围筛选需自己写 SQL精确反查等值匹配POST /wapi/v1/node_properties/get_node_id_by_name_and_value Content-Type: application/json{name:维保到期,value:2026-12-31}范围筛选需复合索引SELECTnpv.node_idFROMnode_property_values npvJOINnode_properties npONnpv.property_idnp.idANDnp.name维保到期WHEREnpv.value2026-12-31ANDnpv.delete_timeISNULL;场景 3盘点后批量回填属性值Excel 盘点后把几百台设备统一设为同一维保日期接口限制为单值批量逐批调用即可PUT /wapi/v1/node_property_values/batch_modification Content-Type: application/json{nodeIds:[1001,1002,1003,1004],propertyName:维保到期,propertyValue:2027-03-31}六、外部系统集成代码Java// 给节点批量设置属性值属性不存在自动创建 upsert幂等publicintbatchSetProperty(LongnodeTypeId,ListLongnodeIds,StringpropertyName,Stringvalue){// 1. 属性不存在则创建LOWER 匹配防大小写重复NodePropertiespropnodePropertiesRepository.getNodePropertieByNodeTypeIdAndPropertyName(nodeTypeId,propertyName);if(propnull){propnewNodeProperties();prop.setNodeTypeId(nodeTypeId);prop.setName(propertyName);prop.setDisplayed(true);propnodePropertiesRepository.save(prop);}// 2. 批量 upsert 属性值intcount0;for(LongnodeId:nodeIds){NodePropertyValuespvnodePropertyValuesRepository.getPropertyValueByNodeIdAndPropertyId(nodeId,prop.getId());if(pvnull){pvnewNodePropertyValues();pv.setNodeId(nodeId);pv.setPropertyId(prop.getId());}pv.setValue(value);nodePropertyValuesRepository.save(pv);count;}returncount;}七、踩坑记录属性值查询必须带 delete_time IS NULL否则会查出逻辑删除的数据属性名判断用 LOWER()避免 IP 和 ip 重复创建按属性值全局筛选必须建 (property_id, value) 复合索引否则千万级表全表扫描批量写入建议攒批后 saveAll逐条 save 性能差