Ver código fonte

add 地图区域管理

18339543638 4 anos atrás
pai
commit
1defca306a

+ 2 - 1
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceTagEntity.java

@@ -25,7 +25,8 @@ import java.util.Date;
 @Setter
 @Table(name = "dev_device_tags", indexes = {
     @Index(name = "dev_dev_id_idx", columnList = "device_id"),
-    @Index(name = "dev_tag_idx", columnList = "device_id,key,value")
+    @Index(name = "dev_tag_idx", columnList = "device_id,key,value"),
+    @Index(name = "dev_tag_unique",columnList = "device_id,key",unique = true)
 })
 @EnableEntityEvent
 public class DeviceTagEntity extends GenericEntity<String> {

+ 3 - 24
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/geo/GeoEntity.java

@@ -1,15 +1,8 @@
 package org.jetlinks.community.device.entity.geo;
 
-import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
-import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
-import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
-import org.hswebframework.web.validator.CreateGroup;
-import javax.persistence.*;
-import javax.validation.constraints.NotNull;
 import java.io.Serializable;
-import java.sql.JDBCType;
-import java.util.*;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -18,26 +11,12 @@ import java.util.*;
  * @createTime 2021年09月27日 10:54:00
  */
 @Data
-@Table(name = "geo")
 public class GeoEntity  implements Serializable{
 
-    @Column
-    @NotNull(message = "类型不为空", groups = String.class)
-    @Schema(description = "类型")
-    private String type;
 
+    private String type;
 
-    @Column
-    @JsonCodec
-    @ColumnType(jdbcType = JDBCType.VARCHAR,javaType = Geometry.class)
-    @NotNull(message = "地图信息不为空", groups = CreateGroup.class)
-    @Schema(description = "地图信息")
     private Geometry geometry;
 
-    @Column
-    @JsonCodec
-    @NotNull(message = "实体属性不能为空", groups = CreateGroup.class)
-    @Schema(description = "实体属性")
-    @ColumnType(jdbcType = JDBCType.VARCHAR,javaType = Property.class)
-    private Property properties;
+    private GeoProperty properties;
 }

+ 85 - 0
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/geo/GeoProperty.java

@@ -0,0 +1,85 @@
+package org.jetlinks.community.device.entity.geo;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
+import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
+import org.hswebframework.web.api.crud.entity.GenericEntity;
+import org.hswebframework.web.validator.CreateGroup;
+import javax.persistence.Column;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.sql.JDBCType;
+import java.util.*;
+@JsonSerialize
+@Table(name = "dev_geo_property",indexes = {
+    @Index(name = "deviceUnique",columnList = "object_id,object_type,from")
+})
+@Data
+public  class GeoProperty extends GenericEntity<String> {
+    @Column
+    @Schema(description = "属性名称")
+    private String name;
+
+    @Column
+    @NotBlank(message = "对象id不能为空", groups = CreateGroup.class)
+    @Schema(description = "对象id")
+    private String objectId;
+
+    @Column
+    @NotBlank(message = "对象类型不能为空", groups = CreateGroup.class)
+    @Schema(description = "对象类型")
+    private String objectType;
+
+    @Column
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    @JsonCodec
+    private List<String > acroutes;
+
+    @Column
+    private String adcode;
+
+    @Column
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    @JsonCodec
+    private List<String > center;
+
+    @Column
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    @JsonCodec
+    private List<String > centroid;
+
+    @Column
+    private int childrenNum;
+
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    @JsonCodec
+    private Map<String,Object> parent;
+
+    @Column
+    private String parentId;
+
+    @Column
+    private String deviceName;
+
+    @Column
+    private String from;
+
+    @Column
+    private String productId;
+
+    @Column
+    private String productName;
+
+    @Column
+    @JsonIgnoreProperties(allowSetters = true)
+    @JsonCodec
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    @Schema(description = "地图信息")
+    private Geometry geometry;
+
+}

+ 0 - 41
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/geo/Property.java

@@ -1,41 +0,0 @@
-package org.jetlinks.community.device.entity.geo;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.*;
-@Data
-@JsonSerialize
-public  class Property implements Serializable {
-
-    private String id;
-
-    private String name;
-
-    private String objectId;
-
-    private String objectType;
-
-    private List<String > acroutes;
-
-    private String adcode;
-
-    private List<String > center;
-
-    private List<String > centroid;
-
-    private int childrenNum;
-
-    private Map<String,Object> parent;
-
-    private String parentId;
-
-    private String deviceName;
-
-    private String from;
-
-    private String productId;
-
-    private String productName;
-}

+ 6 - 5
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/response/DeviceDetail.java

@@ -152,11 +152,12 @@ public class DeviceDetail {
                 setOnlineTime(tp.getT2());
                 setOfflineTime(tp.getT3());
                 setAddress(tp.getT1());
-                with(tp.getT4()
-                       .getTags()
-                       .stream()
-                       .map(DeviceTagEntity::of)
-                       .collect(Collectors.toList()));
+                //标签单独提取出来,不在物模型中存在
+//                with(tp.getT4()
+//                       .getTags()
+//                       .stream()
+//                       .map(DeviceTagEntity::of)
+//                       .collect(Collectors.toList()));
                 Map<String, Object> cachedConfigs = tp.getT5().getAllValues();
                 cachedConfiguration.putAll(cachedConfigs);
 //                cachedConfigs.forEach(configuration::putIfAbsent);

+ 66 - 0
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/DeviceTagsService.java

@@ -0,0 +1,66 @@
+package org.jetlinks.community.device.service;
+
+import lombok.AllArgsConstructor;
+import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
+import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
+import org.hswebframework.web.crud.service.GenericReactiveCrudService;
+import org.jetlinks.community.device.entity.DeviceTagEntity;
+import org.jetlinks.community.device.entity.geo.GeoProperty;
+import org.jetlinks.core.device.DeviceRegistry;
+import org.jetlinks.core.metadata.types.GeoType;
+import org.reactivestreams.Publisher;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName DeviceTagsService.java
+ * @Description TODO
+ * @createTime 2021年09月29日 10:55:00
+ */
+@Service
+@AllArgsConstructor
+public class DeviceTagsService  extends GenericReactiveCrudService<DeviceTagEntity, String> {
+
+    private final LocalDeviceInstanceService instanceService;
+    private final LocalDeviceProductService productService;
+    private final ReactiveRepository<GeoProperty, String> propertyRepository;
+    @Override
+    public Mono<SaveResult> save(Publisher<DeviceTagEntity> entityPublisher) {
+        return Mono.from(entityPublisher)
+            //判断地理位置
+            .doOnNext(entity->{
+                if (judgementGeo(entity)) {
+                    GeoProperty geoProperty = new GeoProperty();
+                    instanceService.findById(entity.getDeviceId())
+                        .flatMap(device->{
+                            geoProperty.setFrom("tag");
+                            geoProperty.setObjectId(device.getId());
+                            geoProperty.setObjectType("device");
+                            geoProperty.setDeviceName(device.getName());
+                            return productService.findById(device.getProductId());
+                        })
+                        .doOnNext(product->{
+                            geoProperty.setProductName(product.getName());
+                            geoProperty.setProductId(product.getId());
+                        })
+                        .subscribe(ignore->{
+                            propertyRepository.createQuery()
+                                .where(GeoProperty::getFrom,"tag")
+                                .where(GeoProperty::getObjectType,"device")
+                                .where(GeoProperty::getObjectId,geoProperty.getObjectId())
+                                .count()
+                                .flatMap(count->count==0?propertyRepository.save(geoProperty):Mono.empty())
+                                .subscribe();
+                        });
+                }
+            })
+            .flatMap(entity->this.getRepository().save(entity));
+    }
+
+    //判断是否为设备坐标标签
+    private boolean judgementGeo(DeviceTagEntity entity){
+        return GeoType.ID.equals(entity.getType())&&"coordinate".equals(entity.getKey());
+    }
+}

+ 32 - 0
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalGeoPropertyService.java

@@ -0,0 +1,32 @@
+package org.jetlinks.community.device.service;
+
+import org.hswebframework.web.crud.service.GenericReactiveCrudService;
+import org.jetlinks.community.device.entity.geo.GeoProperty;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName LocalGeoService.java
+ * @Description TODO
+ * @createTime 2021年09月27日 11:20:00
+ */
+@Service
+public class LocalGeoPropertyService extends GenericReactiveCrudService<GeoProperty, String> {
+
+    /**
+     * 更新子节点数量
+     * @param id
+     * @return
+     */
+    public Mono<Void> updateChildrenNum(String id){
+        return  Mono.just(id)
+            .zipWith(Mono.defer(()->this.createQuery().where(GeoProperty::getParentId,id).count()))
+            .map(tp2->
+                this.createUpdate()
+                    .where(GeoProperty::getId,id)
+                    .set(GeoProperty::getChildrenNum,tp2.getT2()))
+            .then();
+    }
+}

+ 0 - 16
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalGeoService.java

@@ -1,16 +0,0 @@
-package org.jetlinks.community.device.service;
-
-import org.hswebframework.web.crud.service.GenericReactiveCrudService;
-import org.jetlinks.community.device.entity.geo.GeoEntity;
-import org.springframework.stereotype.Service;
-
-/**
- * @author lifang
- * @version 1.0.0
- * @ClassName LocalGeoService.java
- * @Description TODO
- * @createTime 2021年09月27日 11:20:00
- */
-@Service
-public class LocalGeoService extends GenericReactiveCrudService<GeoEntity, String> {
-}

+ 3 - 4
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/data/GeoData.java

@@ -1,10 +1,9 @@
 package org.jetlinks.community.device.service.data;
 
 import cn.hutool.json.JSONUtil;
-import com.alibaba.fastjson.JSON;
 import lombok.Data;
 import org.jetlinks.community.device.entity.geo.Geometry;
-import org.jetlinks.community.device.entity.geo.Property;
+import org.jetlinks.community.device.entity.geo.GeoProperty;
 
 /**
  * @author lifang
@@ -20,13 +19,13 @@ public class GeoData {
 
     private Geometry geometry;
 
-    private Property properties;
+    private GeoProperty properties;
 
     public void setGeometry(String geometry) {
         this.geometry = JSONUtil.toBean(geometry,Geometry.class);
     }
 
     public void setProperties(String properties) {
-        this.properties = JSONUtil.toBean(properties,Property.class);
+        this.properties = JSONUtil.toBean(properties, GeoProperty.class);
     }
 }

+ 106 - 92
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/DeviceInstanceController.java

@@ -3,10 +3,10 @@ package org.jetlinks.community.device.web;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
-import org.hswebframework.ezorm.rdb.exception.DuplicateKeyException;
 import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
 import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
 import org.hswebframework.reactor.excel.ReactorExcel;
@@ -28,6 +28,7 @@ import org.jetlinks.community.device.response.DeviceDeployResult;
 import org.jetlinks.community.device.response.DeviceDetail;
 import org.jetlinks.community.device.response.ImportDeviceInstanceResult;
 import org.jetlinks.community.device.service.DeviceConfigMetadataManager;
+import org.jetlinks.community.device.service.DeviceTagsService;
 import org.jetlinks.community.device.service.LocalDeviceInstanceService;
 import org.jetlinks.community.device.service.LocalDeviceProductService;
 import org.jetlinks.community.device.service.data.DeviceDataService;
@@ -47,8 +48,10 @@ import org.jetlinks.core.message.Message;
 import org.jetlinks.core.message.MessageType;
 import org.jetlinks.core.message.RepayableDeviceMessage;
 import org.jetlinks.core.metadata.*;
+import org.jetlinks.core.metadata.types.GeoType;
 import org.springframework.core.io.buffer.DataBufferFactory;
 import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.data.util.Lazy;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
@@ -61,14 +64,10 @@ import reactor.core.scheduler.Schedulers;
 import reactor.util.function.Tuple2;
 import reactor.util.function.Tuple4;
 import reactor.util.function.Tuples;
-
 import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -96,6 +95,7 @@ public class DeviceInstanceController implements
 
     private final DeviceConfigMetadataManager metadataManager;
 
+    private final DeviceTagsService tagsService;
     @SuppressWarnings("all")
     public DeviceInstanceController(LocalDeviceInstanceService service,
                                     DeviceRegistry registry,
@@ -103,7 +103,8 @@ public class DeviceInstanceController implements
                                     ImportExportService importExportService,
                                     ReactiveRepository<DeviceTagEntity, String> tagRepository,
                                     DeviceDataService deviceDataService,
-                                    DeviceConfigMetadataManager metadataManager) {
+                                    DeviceConfigMetadataManager metadataManager,
+                                    DeviceTagsService tagsService) {
         this.service = service;
         this.registry = registry;
         this.productService = productService;
@@ -111,6 +112,7 @@ public class DeviceInstanceController implements
         this.tagRepository = tagRepository;
         this.deviceDataService = deviceDataService;
         this.metadataManager = metadataManager;
+        this.tagsService=tagsService;
     }
 
 
@@ -207,12 +209,13 @@ public class DeviceInstanceController implements
     //新建设备
     @PostMapping
     @Operation(summary = "新建设备")
+    @Override
     public Mono<DeviceInstanceEntity> add(@RequestBody Mono<DeviceInstanceEntity> payload) {
         return Mono
             .zip(payload, Authentication.currentReactive(), this::applyAuthentication)
             .flatMap(entity -> service.insert(Mono.just(entity)).thenReturn(entity))
+            .onErrorMap(DuplicateKeyException.class, err -> new BusinessException("设备ID已存在", err))
             .onErrorMap(e->new BusinessException("服务器繁忙,请稍后重试",e));
-//            .onErrorMap(DuplicateKeyException.class, err -> new BusinessException("设备ID已存在", err));
     }
 
     /**
@@ -259,8 +262,8 @@ public class DeviceInstanceController implements
     public Mono<DeviceProperty> getDeviceLatestProperty(@PathVariable @Parameter(description = "设备ID") String deviceId,
                                                         @PathVariable @Parameter(description = "属性ID") String property) {
         return deviceDataService.queryEachOneProperties(deviceId, QueryParamEntity.of(), property)
-                                .take(1)
-                                .singleOrEmpty()
+            .take(1)
+            .singleOrEmpty()
             ;
     }
 
@@ -325,10 +328,10 @@ public class DeviceInstanceController implements
     public Mono<Void> deleteDeviceTag(@PathVariable @Parameter(description = "设备ID") String deviceId,
                                       @PathVariable @Parameter(description = "标签ID") String tagId) {
         return tagRepository.createDelete()
-                            .where(DeviceTagEntity::getDeviceId, deviceId)
-                            .and(DeviceTagEntity::getId, tagId)
-                            .execute()
-                            .then();
+            .where(DeviceTagEntity::getDeviceId, deviceId)
+            .and(DeviceTagEntity::getId, tagId)
+            .execute()
+            .then();
     }
 
     /**
@@ -343,7 +346,7 @@ public class DeviceInstanceController implements
     @Operation(summary = "批量删除设备")
     public Mono<Integer> deleteBatch(@RequestBody Mono<List<String>> idList) {
         return idList.flatMapMany(Flux::fromIterable)
-                     .as(service::deleteById);
+            .as(service::deleteById);
     }
 
     /**
@@ -372,9 +375,9 @@ public class DeviceInstanceController implements
     @Operation(summary = "批量激活设备")
     public Mono<Integer> deployBatch(@RequestBody Mono<List<String>> idList) {
         return idList.flatMapMany(service::findById)
-                     .as(service::deploy)
-                     .map(DeviceDeployResult::getTotal)
-                     .reduce(Math::addExact);
+            .as(service::deploy)
+            .map(DeviceDeployResult::getTotal)
+            .reduce(Math::addExact);
     }
 
     /**
@@ -400,8 +403,8 @@ public class DeviceInstanceController implements
     @Operation(summary = "获取设备全部标签数据")
     public Flux<DeviceTagEntity> getDeviceTags(@PathVariable @Parameter(description = "设备ID") String deviceId) {
         return tagRepository.createQuery()
-                            .where(DeviceTagEntity::getDeviceId, deviceId)
-                            .fetch();
+            .where(DeviceTagEntity::getDeviceId, deviceId)
+            .fetch();
     }
 
     //保存设备标签
@@ -416,7 +419,7 @@ public class DeviceInstanceController implements
                 tag.setDeviceId(deviceId);
                 tag.tryValidate();
             })
-            .as(tagRepository::save)
+            .as(tagsService::save)
             .thenMany(getDeviceTags(deviceId));
     }
 
@@ -433,9 +436,9 @@ public class DeviceInstanceController implements
             registry.getProduct(productId).flatMap(DeviceProductOperator::getMetadata),
             //配置
             metadataManager.getDeviceConfigMetadataByProductId(productId)
-                           .flatMapIterable(ConfigMetadata::getProperties)
-                           .collectList()
-                           .defaultIfEmpty(Collections.emptyList())
+                .flatMapIterable(ConfigMetadata::getProperties)
+                .collectList()
+                .defaultIfEmpty(Collections.emptyList())
         );
     }
 
@@ -464,12 +467,12 @@ public class DeviceInstanceController implements
             .buffer(100)//每100条数据保存一次
             .publishOn(Schedulers.single())
             .concatMap(buffer ->
-                           Mono.zip(
-                               service.save(Flux.fromIterable(buffer).map(Tuple2::getT1)),
-                               tagRepository
-                                   .save(Flux.fromIterable(buffer).flatMapIterable(Tuple2::getT2))
-                                   .defaultIfEmpty(SaveResult.of(0, 0))
-                           ))
+                Mono.zip(
+                    service.save(Flux.fromIterable(buffer).map(Tuple2::getT1)),
+                    tagRepository
+                        .save(Flux.fromIterable(buffer).flatMapIterable(Tuple2::getT2))
+                        .defaultIfEmpty(SaveResult.of(0, 0))
+                ))
             .map(res -> ImportDeviceInstanceResult.success(res.getT1()))
             .onErrorResume(err -> Mono.just(ImportDeviceInstanceResult.error(err)));
     }
@@ -482,16 +485,16 @@ public class DeviceInstanceController implements
                                              ServerHttpResponse response,
                                              @PathVariable @Parameter(description = "文件格式,支持csv,xlsx") String format) throws IOException {
         response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
-                                  "attachment; filename=".concat(URLEncoder.encode("设备导入模版." + format, StandardCharsets.UTF_8
-                                      .displayName())));
+            "attachment; filename=".concat(URLEncoder.encode("设备导入模版." + format, StandardCharsets.UTF_8
+                .displayName())));
         return getDeviceProductDetail(productId)
             .map(tp4 -> DeviceExcelInfo.getTemplateHeaderMapping(tp4.getT3().getTags(), tp4.getT4()))
             .defaultIfEmpty(DeviceExcelInfo.getTemplateHeaderMapping(Collections.emptyList(), Collections.emptyList()))
             .flatMapMany(headers ->
-                             ReactorExcel.<DeviceExcelInfo>writer(format)
-                                 .headers(headers)
-                                 .converter(DeviceExcelInfo::toMap)
-                                 .writeBuffer(Flux.empty()))
+                ReactorExcel.<DeviceExcelInfo>writer(format)
+                    .headers(headers)
+                    .converter(DeviceExcelInfo::toMap)
+                    .writeBuffer(Flux.empty()))
             .doOnError(err -> log.error(err.getMessage(), err))
             .map(bufferFactory::wrap)
             .as(response::writeWith);
@@ -506,8 +509,8 @@ public class DeviceInstanceController implements
                              @Parameter(hidden = true) QueryParamEntity parameter,
                              @PathVariable @Parameter(description = "文件格式,支持csv,xlsx") String format) throws IOException {
         response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
-                                  "attachment; filename=".concat(URLEncoder.encode("设备实例." + format, StandardCharsets.UTF_8
-                                      .displayName())));
+            "attachment; filename=".concat(URLEncoder.encode("设备实例." + format, StandardCharsets.UTF_8
+                .displayName())));
         parameter.setPaging(false);
         parameter.toNestQuery(q -> q.is(DeviceInstanceEntity::getProductId, productId));
         return getDeviceProductDetail(productId)
@@ -523,44 +526,44 @@ public class DeviceInstanceController implements
                         .collect(Collectors.toList())
                 ))
             .defaultIfEmpty(Tuples.of(DeviceExcelInfo.getExportHeaderMapping(Collections.emptyList(),
-                                                                             Collections.emptyList()),
-                                      Collections.emptyList()))
+                Collections.emptyList()),
+                Collections.emptyList()))
             .flatMapMany(headerAndConfigKey ->
-                             ReactorExcel.<DeviceExcelInfo>writer(format)
-                                 .headers(headerAndConfigKey.getT1())
-                                 .converter(DeviceExcelInfo::toMap)
-                                 .writeBuffer(
-                                     service.query(parameter)
-                                            .flatMap(entity -> {
-                                                DeviceExcelInfo exportEntity = FastBeanCopier.copy(entity, new DeviceExcelInfo(), "state");
-                                                exportEntity.setState(entity.getState().getText());
-                                                return registry
-                                                    .getDevice(entity.getId())
-                                                    .flatMap(deviceOperator -> deviceOperator
-                                                        .getSelfConfigs(headerAndConfigKey.getT2())
-                                                        .map(Values::getAllValues))
-                                                    .doOnNext(configs -> exportEntity
-                                                        .getConfiguration()
-                                                        .putAll(configs))
-                                                    .thenReturn(exportEntity);
-                                            })
-                                            .buffer(200)
-                                            .flatMap(list -> {
-                                                Map<String, DeviceExcelInfo> importInfo = list
-                                                    .stream()
-                                                    .collect(Collectors.toMap(DeviceExcelInfo::getId, Function.identity()));
-                                                return tagRepository.createQuery()
-                                                                    .where()
-                                                                    .in(DeviceTagEntity::getDeviceId, importInfo.keySet())
-                                                                    .fetch()
-                                                                    .collect(Collectors.groupingBy(DeviceTagEntity::getDeviceId))
-                                                                    .flatMapIterable(Map::entrySet)
-                                                                    .doOnNext(entry -> importInfo
-                                                                        .get(entry.getKey())
-                                                                        .setTags(entry.getValue()))
-                                                                    .thenMany(Flux.fromIterable(list));
-                                            })
-                                     , 512 * 1024))//缓冲512k
+                ReactorExcel.<DeviceExcelInfo>writer(format)
+                    .headers(headerAndConfigKey.getT1())
+                    .converter(DeviceExcelInfo::toMap)
+                    .writeBuffer(
+                        service.query(parameter)
+                            .flatMap(entity -> {
+                                DeviceExcelInfo exportEntity = FastBeanCopier.copy(entity, new DeviceExcelInfo(), "state");
+                                exportEntity.setState(entity.getState().getText());
+                                return registry
+                                    .getDevice(entity.getId())
+                                    .flatMap(deviceOperator -> deviceOperator
+                                        .getSelfConfigs(headerAndConfigKey.getT2())
+                                        .map(Values::getAllValues))
+                                    .doOnNext(configs -> exportEntity
+                                        .getConfiguration()
+                                        .putAll(configs))
+                                    .thenReturn(exportEntity);
+                            })
+                            .buffer(200)
+                            .flatMap(list -> {
+                                Map<String, DeviceExcelInfo> importInfo = list
+                                    .stream()
+                                    .collect(Collectors.toMap(DeviceExcelInfo::getId, Function.identity()));
+                                return tagRepository.createQuery()
+                                    .where()
+                                    .in(DeviceTagEntity::getDeviceId, importInfo.keySet())
+                                    .fetch()
+                                    .collect(Collectors.groupingBy(DeviceTagEntity::getDeviceId))
+                                    .flatMapIterable(Map::entrySet)
+                                    .doOnNext(entry -> importInfo
+                                        .get(entry.getKey())
+                                        .setTags(entry.getValue()))
+                                    .thenMany(Flux.fromIterable(list));
+                            })
+                        , 512 * 1024))//缓冲512k
             .doOnError(err -> log.error(err.getMessage(), err))
             .map(bufferFactory::wrap)
             .as(response::writeWith);
@@ -575,8 +578,8 @@ public class DeviceInstanceController implements
                              @Parameter(hidden = true) QueryParamEntity parameter,
                              @PathVariable @Parameter(description = "文件格式,支持csv,xlsx") String format) throws IOException {
         response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
-                                  "attachment; filename=".concat(URLEncoder.encode("设备实例." + format, StandardCharsets.UTF_8
-                                      .displayName())));
+            "attachment; filename=".concat(URLEncoder.encode("设备实例." + format, StandardCharsets.UTF_8
+                .displayName())));
         return ReactorExcel.<DeviceExcelInfo>writer(format)
             .headers(DeviceExcelInfo.getExportHeaderMapping(Collections.emptyList(), Collections.emptyList()))
             .converter(DeviceExcelInfo::toMap)
@@ -603,8 +606,8 @@ public class DeviceInstanceController implements
         return Mono
             .zip(registry.getDevice(deviceId), shadow)
             .flatMap(tp2 -> tp2.getT1()
-                               .setConfig(DeviceConfigKey.shadow, tp2.getT2())
-                               .thenReturn(tp2.getT2()));
+                .setConfig(DeviceConfigKey.shadow, tp2.getT2())
+                .thenReturn(tp2.getT2()));
     }
 
     //获取设备影子
@@ -649,10 +652,10 @@ public class DeviceInstanceController implements
         return param
             .flatMapMany(request -> deviceDataService
                 .aggregationPropertiesByDevice(deviceId,
-                                               request.getQuery(),
-                                               request
-                                                   .getColumns()
-                                                   .toArray(new DeviceDataService.DevicePropertyAggregation[0]))
+                    request.getQuery(),
+                    request
+                        .getColumns()
+                        .toArray(new DeviceDataService.DevicePropertyAggregation[0]))
             )
             .map(AggregationData::values);
     }
@@ -718,8 +721,8 @@ public class DeviceInstanceController implements
             entity.setWhere(where);
             entity.includes("id");
             return service.query(entity)
-                          .flatMap(device -> registry.getDevice(device.getId()))
-                          .cache();
+                .flatMap(device -> registry.getDevice(device.getId()))
+                .cache();
         });
         return messages
             .flatMap(message -> {
@@ -766,13 +769,24 @@ public class DeviceInstanceController implements
     public Mono<Void> updateMetadata(@PathVariable String id,
                                      @RequestBody Mono<String> metadata) {
         return metadata
-            .flatMap(metadata_ -> service
+            .doOnNext(metadata_ -> service
                 .createUpdate()
                 .set(DeviceInstanceEntity::getDeriveMetadata, metadata_)
                 .where(DeviceInstanceEntity::getId, id)
                 .execute()
                 .then(registry.getDevice(id))
-                .flatMap(device -> device.updateMetadata(metadata_)))
+                .doOnNext(device -> device.updateMetadata(metadata_))
+                .flatMap(DeviceOperator::getMetadata)
+                .doOnNext(metadataMono -> {
+                    //查看是否存在地理位置
+                    List<PropertyMetadata> properties = metadataMono.getProperties();
+                    List<PropertyMetadata> tags = metadataMono.getTags();
+                    //属性中存在地理位置
+                    Set<PropertyMetadata> proCollection =
+                        properties.stream().filter(propertyMetadata -> propertyMetadata.getValueType().getType().equals(GeoType.ID)).collect(Collectors.toSet());
+                    //标签中存在地理位置
+                    Set<PropertyMetadata> tagCollection = tags.stream().filter(propertyMetadata -> propertyMetadata.getValueType().getType().equals(GeoType.ID)).collect(Collectors.toSet());
+                }))
             .then();
     }
 
@@ -786,11 +800,11 @@ public class DeviceInstanceController implements
             .getDevice(id)
             .flatMap(DeviceOperator::resetMetadata)
             .then(service
-                      .createUpdate()
-                      .setNull(DeviceInstanceEntity::getDeriveMetadata)
-                      .where(DeviceInstanceEntity::getId, id)
-                      .execute()
-                      .then());
+                .createUpdate()
+                .setNull(DeviceInstanceEntity::getDeriveMetadata)
+                .where(DeviceInstanceEntity::getId, id)
+                .execute()
+                .then());
     }
 
 

+ 30 - 15
jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/GeoController.java

@@ -1,27 +1,28 @@
 package org.jetlinks.community.device.web;
 
-import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.AllArgsConstructor;
 import org.hswebframework.web.api.crud.entity.PagerResult;
 import org.hswebframework.web.api.crud.entity.QueryParamEntity;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.annotation.Resource;
-import org.hswebframework.web.bean.FastBeanCopier;
 import org.hswebframework.web.crud.service.ReactiveCrudService;
 import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
 import org.jetlinks.community.device.entity.geo.GeoEntity;
-import org.jetlinks.community.device.service.LocalGeoService;
-import org.jetlinks.community.device.service.data.GeoData;
+import org.jetlinks.community.device.entity.geo.GeoProperty;
+import org.jetlinks.community.device.service.LocalGeoPropertyService;
 import org.jetlinks.community.device.web.request.GeoPayload;
+import org.jetlinks.core.metadata.types.GeoShape;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author lifang
@@ -36,33 +37,47 @@ import java.util.List;
 @Resource(id = "geo-manager", name = "地理位置管理")
 @Tag(name = "地理位置管理")
 @AllArgsConstructor
-public class GeoController  implements ReactiveServiceCrudController<GeoEntity, String> {
-    private final LocalGeoService geoService;
+public class GeoController  implements ReactiveServiceCrudController<GeoProperty, String> {
+    private final LocalGeoPropertyService geoService;
 
 
     @PostMapping("/_search/geo.json")
-    public Mono<?> search(@RequestBody Mono<QueryParamEntity> filter){
+    public Mono<?> search(@RequestBody Mono<QueryParamEntity> filter, @RequestBody GeoShape shape){
 
         return geoService.queryPager(filter)
             .map(PagerResult::getData)
             .map(result->{
                 GeoPayload geoPayload = new GeoPayload();
-                geoPayload.setFeatures(result);
+//                geoPayload.setFeatures(result);
                 return geoPayload;
             });
     }
 
+    /**
+     * 区域管理新增
+     * @param publisher
+     * @return
+     */
     @PostMapping("/geo.json")
     public Mono<Void> save(@RequestBody GeoPayload publisher){
-        return Mono.just(publisher)
-            .map(GeoPayload::getFeatures)
-            .map(Mono::just)
-            .flatMap(geoService::insertBatch)
-            .then();
+        return Mono.just(publisher.getFeatures())
+            .map(features->{
+                features.forEach(geo-> geo.getProperties().setGeometry(geo.getGeometry()));
+                List<GeoProperty> collect = features.stream().map(GeoEntity::getProperties).collect(Collectors.toList());
+                return geoService
+                    .insertBatch(Mono.just(collect))
+                    .map(ignore->
+                        Flux.fromStream(collect.stream())
+                            .filter(geoProperty -> StrUtil.isNotEmpty(geoProperty.getParentId()))
+                            .map(GeoProperty::getParentId)
+                            .collect(Collectors.toSet())
+                            .doOnNext(ids->ids.forEach(geoService::updateChildrenNum))
+                    );
+            }).then();
     }
 
     @Override
-    public ReactiveCrudService<GeoEntity, String> getService() {
+    public ReactiveCrudService<GeoProperty, String> getService() {
         return geoService;
     }
 }