From 84c6e48f0ee83f448ec5ff1ba5d40f20af35e8b8 Mon Sep 17 00:00:00 2001 From: ayan Date: Tue, 18 Oct 2022 17:24:38 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E4=BC=A0=E8=BE=93=E5=8D=8F=E8=AE=AE=E7=9A=84?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8D=8F=E8=AE=AE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/web/ProtocolSupportController.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java index fd17e40e..ae36548f 100644 --- a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.Getter; import org.hswebframework.utils.StringUtils; +import org.hswebframework.web.api.crud.entity.QueryParamEntity; import org.hswebframework.web.authorization.annotation.Authorize; import org.hswebframework.web.authorization.annotation.QueryAction; import org.hswebframework.web.authorization.annotation.Resource; @@ -32,7 +33,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; +import java.util.Comparator; import java.util.List; @RestController @@ -174,4 +178,29 @@ public class ProtocolSupportController public Flux allUnits() { return Flux.fromIterable(ValueUnits.getAllUnit()); } + + + @GetMapping("/supports/{transport}") + @Authorize(merge = false) + @Operation(summary = "获取支持指定传输协议的消息协议") + public Flux getSupportTransportProtocols(@PathVariable String transport, + @Parameter(hidden = true) QueryParamEntity query) { + return protocolSupports + .getProtocols() + .collectMap(ProtocolSupport::getId) + .flatMapMany(protocols -> service.createQuery() + .setParam(query) + .fetch() + .index() + .flatMap(tp2 -> Mono + .justOrEmpty(protocols.get(tp2.getT2().getId())) + .filterWhen(support -> support + .getSupportedTransport() + .filter(t -> t.isSame(transport)) + .hasElements()) + .map(ProtocolInfo::of) + .map(protocolInfo -> Tuples.of(tp2.getT1(), protocolInfo)))) + .sort(Comparator.comparingLong(Tuple2::getT1)) + .map(Tuple2::getT2); + } } From 9795f7a11afe795393ec246cad9f430104f6ffef Mon Sep 17 00:00:00 2001 From: ayan Date: Tue, 18 Oct 2022 17:25:57 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=8F=9C=E5=8D=95=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=88=9D=E5=A7=8B=E5=8C=96=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MenuAuthenticationInitializeService.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 jetlinks-manager/authentication-manager/src/main/java/org/jetlinks/community/auth/initialize/MenuAuthenticationInitializeService.java diff --git a/jetlinks-manager/authentication-manager/src/main/java/org/jetlinks/community/auth/initialize/MenuAuthenticationInitializeService.java b/jetlinks-manager/authentication-manager/src/main/java/org/jetlinks/community/auth/initialize/MenuAuthenticationInitializeService.java new file mode 100644 index 00000000..1f3c718d --- /dev/null +++ b/jetlinks-manager/authentication-manager/src/main/java/org/jetlinks/community/auth/initialize/MenuAuthenticationInitializeService.java @@ -0,0 +1,122 @@ +package org.jetlinks.community.auth.initialize; + +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.hswebframework.web.api.crud.entity.QueryParamEntity; +import org.hswebframework.web.authorization.DefaultDimensionType; +import org.hswebframework.web.authorization.Permission; +import org.hswebframework.web.authorization.events.AuthorizationInitializeEvent; +import org.hswebframework.web.authorization.simple.SimpleAuthentication; +import org.hswebframework.web.authorization.simple.SimplePermission; +import org.hswebframework.web.system.authorization.api.entity.ActionEntity; +import org.hswebframework.web.system.authorization.api.entity.PermissionEntity; +import org.hswebframework.web.system.authorization.defaults.service.DefaultPermissionService; +import org.jetlinks.community.auth.entity.MenuEntity; +import org.jetlinks.community.auth.entity.MenuView; +import org.jetlinks.community.auth.service.DefaultMenuService; +import org.jetlinks.community.auth.service.request.MenuGrantRequest; +import org.jetlinks.community.auth.web.request.AuthorizationSettingDetail; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@AllArgsConstructor +@Component +public class MenuAuthenticationInitializeService { + + private final DefaultMenuService menuService; + + private final DefaultPermissionService permissionService; + + /** + * 根据角色配置的菜单权限来重构权限信息 + * + * @param event 权限初始化事件 + */ + @EventListener + public void refactorPermission(AuthorizationInitializeEvent event) { + if (event.getAuthentication().getDimensions().isEmpty()) { + return; + } + event.async( + Mono + .zip( + // T1: 权限定义列表 + permissionService + .createQuery() + .where(PermissionEntity::getStatus, 1) + .fetch() + .collectMap(PermissionEntity::getId, Function.identity()), + // T2: 菜单定义列表 + menuService + .createQuery() + .where(MenuEntity::getStatus, 1) + .fetch() + .collectList(), + // T3: 角色赋予的菜单列表 + menuService + .getGrantedMenus(QueryParamEntity.of(), event + .getAuthentication() + .getDimensions()) + .collectList() + .filter(CollectionUtils::isNotEmpty) + ) + .flatMapIterable(tp3 -> { + Map permissions = tp3.getT1(); + List menus = tp3.getT2(); + List grantedMenus = tp3.getT3(); + MenuGrantRequest request = new MenuGrantRequest(); + request.setTargetType(DefaultDimensionType.role.getId()); + request.setTargetId("merge"); + request.setMenus(grantedMenus); + AuthorizationSettingDetail detail = request.toAuthorizationSettingDetail(menus); + return detail + .getPermissionList() + .stream() + .map(per -> { + PermissionEntity entity = permissions.get(per.getId()); + if (entity == null || per.getActions() == null) { + return null; + } + + Set actions; + if (CollectionUtils.isEmpty(entity.getActions())) { + actions = new HashSet<>(); + } else { + Set defActions = entity + .getActions() + .stream() + .map(ActionEntity::getAction) + .collect(Collectors.toSet()); + actions = new HashSet<>(per.getActions()); + actions.retainAll(defActions); + } + + return SimplePermission + .builder() + .id(entity.getId()) + .name(entity.getName()) + .options(entity.getProperties()) + .actions(actions) + .build(); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + }) + .collectList() + .filter(CollectionUtils::isNotEmpty) + .doOnNext(mapping -> { + SimpleAuthentication authentication = new SimpleAuthentication(); + authentication.setUser(event.getAuthentication().getUser()); + authentication.setPermissions(mapping); + event.setAuthentication(event.getAuthentication().merge(authentication)); + }) + ); + + } + +} From d5d3c0dd9d18a73b788362e1c4f1b8ba58182db5 Mon Sep 17 00:00:00 2001 From: ayan Date: Tue, 18 Oct 2022 17:26:52 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BA=A7=E5=93=81=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/entity/DeviceCategory.java | 30 ----- .../device/entity/DeviceCategoryEntity.java | 79 +++++++++++ .../device/service/DeviceCategoryService.java | 80 +++++++++++ .../device/web/DeviceCategoryController.java | 125 ++++++++---------- 4 files changed, 215 insertions(+), 99 deletions(-) delete mode 100644 jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategory.java create mode 100755 jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategoryEntity.java create mode 100755 jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/DeviceCategoryService.java diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategory.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategory.java deleted file mode 100644 index 11c4242f..00000000 --- a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategory.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.jetlinks.community.device.entity; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; -import org.hswebframework.web.api.crud.entity.GenericTreeSortSupportEntity; - -import java.util.List; - -@Getter -@Setter -public class DeviceCategory extends GenericTreeSortSupportEntity { - - @Schema(description = "ID") - private String id; - - @Schema(description = "标识") - private String key; - - @Schema(description = "名称") - private String name; - - @Schema(description = "父节点标识") - private String parentId; - - @Schema(description = "子节点") - private List children; - - -} diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategoryEntity.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategoryEntity.java new file mode 100755 index 00000000..8ae16e08 --- /dev/null +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/entity/DeviceCategoryEntity.java @@ -0,0 +1,79 @@ +package org.jetlinks.community.device.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType; +import org.hswebframework.ezorm.rdb.mapping.annotation.Comment; +import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue; +import org.hswebframework.web.api.crud.entity.GenericTreeSortSupportEntity; +import org.hswebframework.web.api.crud.entity.RecordCreationEntity; +import org.hswebframework.web.crud.annotation.EnableEntityEvent; +import org.hswebframework.web.crud.generator.Generators; +import org.hswebframework.web.validator.CreateGroup; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import java.sql.JDBCType; +import java.util.List; + +@Getter +@Setter +@Table(name = "dev_product_category") +@Comment("产品分类信息表") +@EnableEntityEvent +public class DeviceCategoryEntity extends GenericTreeSortSupportEntity implements RecordCreationEntity { + + @Override + @Id + @Column(length = 64, updatable = false) + @GeneratedValue(generator = Generators.SNOW_FLAKE) + @NotBlank(message = "ID不能为空", groups = CreateGroup.class) + @Pattern(regexp = "^[0-9a-zA-Z_\\-|]+$", message = "ID只能由数字,字母,下划线和中划线组成", groups = CreateGroup.class) + public String getId() { + return super.getId(); + } + + @Schema(description = "标识") + @Column(nullable = false,length = 64) + @NotBlank(message = "标识不能为空", groups = CreateGroup.class) + @GeneratedValue(generator = Generators.SNOW_FLAKE) + @Pattern(regexp = "^[0-9a-zA-Z_\\-]+$", message = "分类标识只能由数字,字母,下划线和中划线组成") + private String key; + + @Schema(description = "名称") + @Column(nullable = false) + @NotBlank + private String name; + + @Schema(description = "说明") + @Column + private String description; + + @Schema(description = "子节点") + private List children; + + @Schema(description = "物模型") + @Column + @ColumnType(javaType = String.class, jdbcType = JDBCType.CLOB) + private String metadata; + + @Column(updatable = false) + @Schema( + description = "创建者ID(只读)" + , accessMode = Schema.AccessMode.READ_ONLY + ) + private String creatorId; + + @Column(updatable = false) + @DefaultValue(generator = Generators.CURRENT_TIME) + @Schema( + description = "创建时间(只读)" + , accessMode = Schema.AccessMode.READ_ONLY + ) + private Long createTime; +} diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/DeviceCategoryService.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/DeviceCategoryService.java new file mode 100755 index 00000000..f312790e --- /dev/null +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/DeviceCategoryService.java @@ -0,0 +1,80 @@ +package org.jetlinks.community.device.service; + +import com.alibaba.fastjson.JSON; +import org.hswebframework.web.api.crud.entity.TreeSupportEntity; +import org.hswebframework.web.crud.service.GenericReactiveTreeSupportCrudService; +import org.hswebframework.web.id.IDGenerator; +import org.jetlinks.community.device.entity.DeviceCategoryEntity; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; +import org.springframework.util.StreamUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Service +public class DeviceCategoryService extends GenericReactiveTreeSupportCrudService implements CommandLineRunner { + + @Override + public IDGenerator getIDGenerator() { + return IDGenerator.MD5; + } + + private static final String category_splitter = "-"; + @Override + public void setChildren(DeviceCategoryEntity entity, List children) { + entity.setChildren(children); + } + + @Override + public void run(String... args) { + this + .createQuery() + .fetchOne() + .switchIfEmpty(initDefaultData().then(Mono.empty())) + .subscribe(); + } + + + static void rebuild(String parentId, List children) { + if (children == null) { + return; + } + for (DeviceCategoryEntity child : children) { + String id = child.getId(); + child.setId(parentId + category_splitter + id +category_splitter); + child.setParentId(parentId +category_splitter); + rebuild(parentId + category_splitter + id, child.getChildren()); + } + } + + private Mono initDefaultData() { + return Mono + .fromCallable(() -> { + ClassPathResource resource = new ClassPathResource("device-category.json"); + + try (InputStream stream = resource.getInputStream()) { + String json = StreamUtils.copyToString(stream, StandardCharsets.UTF_8); + + List all = JSON.parseArray(json, DeviceCategoryEntity.class); + + List root = TreeSupportEntity.list2tree(all, DeviceCategoryEntity::setChildren); + + for (DeviceCategoryEntity category : root) { + String id = category.getId(); + category.setId(category_splitter + id + category_splitter); + category.setParentId(category_splitter + category.getParentId() + category_splitter); + rebuild(category_splitter + id, category.getChildren()); + } + return root; + } + + }) + .flatMap(all -> save(Flux.fromIterable(all))) + .then(); + } +} diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/DeviceCategoryController.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/DeviceCategoryController.java index 820513ed..4a7b9b49 100644 --- a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/DeviceCategoryController.java +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/DeviceCategoryController.java @@ -1,85 +1,72 @@ package org.jetlinks.community.device.web; -import com.alibaba.fastjson.JSON; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.hswebframework.web.api.crud.entity.TreeSupportEntity; -import org.jetlinks.community.device.entity.DeviceCategory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.util.StreamUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import reactor.core.publisher.Flux; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.hswebframework.web.api.crud.entity.QueryNoPagingOperation; +import org.hswebframework.web.api.crud.entity.QueryParamEntity; +import org.hswebframework.web.api.crud.entity.TreeSupportEntity; +import org.hswebframework.web.authorization.annotation.Authorize; +import org.hswebframework.web.authorization.annotation.Resource; +import org.hswebframework.web.crud.service.ReactiveCrudService; +import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController; +import org.jetlinks.community.device.entity.DeviceCategoryEntity; +import org.jetlinks.community.device.service.DeviceCategoryService; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; @RestController @RequestMapping("/device/category") @Slf4j -@Tag(name = "设备分类目录") -public class DeviceCategoryController { +@Tag(name = "产品分类管理") +@AllArgsConstructor +@Resource(id="device-category",name = "产品分类") +public class DeviceCategoryController implements ReactiveServiceCrudController { - static List statics; - - - static void rebuild(String parentId, List children) { - if (children == null) { - return; - } - for (DeviceCategory child : children) { - String id = child.getId(); - child.setId(parentId + "|" + id + "|"); - child.setParentId(parentId + "|"); - rebuild(parentId + "|" + id, child.getChildren()); - } - } - - static { - try { - ClassPathResource resource = new ClassPathResource("device-category.json"); - String json = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8); - - List all = JSON.parseArray(json, DeviceCategory.class); - - List root = TreeSupportEntity.list2tree(all, DeviceCategory::setChildren); - - for (DeviceCategory category : root) { - String id = category.getId(); - - category.setId("|" + id + "|"); - category.setParentId("|" + category.getParentId() + "|"); - rebuild("|" + id, category.getChildren()); - } - - statics = all; - - } catch (Exception e) { - statics = new ArrayList<>(); - DeviceCategoryController.log.error(e.getMessage(), e); - } - } + private final DeviceCategoryService categoryService; @GetMapping - @Operation(summary = "获取全部分类目录") - public Flux getAllCategory() { - return Flux.fromIterable(statics); + @QueryNoPagingOperation(summary = "获取全部分类") + @Authorize(merge = false) + public Flux getAllCategory(@Parameter(hidden = true) QueryParamEntity query) { + return this + .categoryService + .createQuery() + .setParam(query) + .fetch(); } - @GetMapping("/_query/no-paging") - @Operation(summary = "获取全部分类目录") - public Flux getAllCategory2() { - return Flux.fromIterable(statics); - } - - @GetMapping("/_tree") - @Operation(summary = "获取全部分类目录(树结构)") - public Flux getAllCategoryTree() { - return Flux.fromIterable(TreeSupportEntity.list2tree(statics, DeviceCategory::setChildren)); + @QueryNoPagingOperation(summary = "获取全部分类(树结构)") + @Authorize(merge = false) + public Flux getAllCategoryTree(@Parameter(hidden = true) QueryParamEntity query) { + return this + .categoryService + .createQuery() + .setParam(query) + .fetch() + .collectList() + .flatMapMany(all-> Flux.fromIterable(TreeSupportEntity.list2tree(all, DeviceCategoryEntity::setChildren))); + } + + + @PostMapping("/_tree") + @QueryNoPagingOperation(summary = "获取全部分类(树结构)") + @Authorize(merge = false) + public Flux getAllCategoryTreeByQueryParam(@RequestBody Mono query) { + return this + .categoryService + .query(query) + .collectList() + .flatMapMany(all-> Flux.fromIterable(TreeSupportEntity.list2tree(all, DeviceCategoryEntity::setChildren))); + } + + @Override + public ReactiveCrudService getService() { + return categoryService; } } From 7d7a6f2d93471445f943c156e6cd21e44885820f Mon Sep 17 00:00:00 2001 From: ayan Date: Tue, 18 Oct 2022 17:39:44 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reference/DataReferenceManager.java | 2 + .../service/LocalProtocolSupportService.java | 51 +++++++------ .../service/ProtocolSupportHandler.java | 73 +++++++++++++++++++ .../device/web/ProtocolSupportController.java | 30 ++++++++ 4 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/ProtocolSupportHandler.java diff --git a/jetlinks-components/common-component/src/main/java/org/jetlinks/community/reference/DataReferenceManager.java b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/reference/DataReferenceManager.java index ca4c3061..faf173da 100644 --- a/jetlinks-components/common-component/src/main/java/org/jetlinks/community/reference/DataReferenceManager.java +++ b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/reference/DataReferenceManager.java @@ -24,6 +24,8 @@ public interface DataReferenceManager { String TYPE_NETWORK = "network"; //数据类型:关系配置 String TYPE_RELATION = "relation"; + //数据类型:消息协议 + String TYPE_PROTOCOL = "protocol"; /** * 判断指定数据类型的数据是否已经被其他地方所引用 diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalProtocolSupportService.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalProtocolSupportService.java index 7d4cad9f..236a06e5 100644 --- a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalProtocolSupportService.java +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/LocalProtocolSupportService.java @@ -1,48 +1,55 @@ package org.jetlinks.community.device.service; +import lombok.extern.slf4j.Slf4j; import org.hswebframework.web.crud.service.GenericReactiveCrudService; -import org.hswebframework.web.exception.BusinessException; import org.hswebframework.web.exception.NotFoundException; import org.jetlinks.community.device.entity.ProtocolSupportEntity; -import org.jetlinks.supports.protocol.management.ProtocolSupportLoader; +import org.jetlinks.community.reference.DataReferenceManager; import org.jetlinks.supports.protocol.management.ProtocolSupportManager; +import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service +@Slf4j public class LocalProtocolSupportService extends GenericReactiveCrudService { @Autowired private ProtocolSupportManager supportManager; @Autowired - private ProtocolSupportLoader loader; + private DataReferenceManager referenceManager; + + @Override + public Mono deleteById(Publisher idPublisher) { + return Flux.from(idPublisher) + .flatMap(id -> supportManager.remove(id).thenReturn(id)) + .as(super::deleteById); + } public Mono deploy(String id) { return findById(Mono.just(id)) - .switchIfEmpty(Mono.error(NotFoundException::new)) - .map(ProtocolSupportEntity::toDeployDefinition) - .flatMap(def->loader.load(def).thenReturn(def)) - .onErrorMap(err->new BusinessException("无法加载协议:"+err.getMessage(),err)) - .flatMap(supportManager::save) - .flatMap(r -> createUpdate() - .set(ProtocolSupportEntity::getState, 1) - .where(ProtocolSupportEntity::getId, id) - .execute()) - .map(i -> i > 0); + .switchIfEmpty(Mono.error(NotFoundException::new)) + .flatMap(r -> createUpdate() + .set(ProtocolSupportEntity::getState, 1) + .where(ProtocolSupportEntity::getId, id) + .execute()) + .map(i -> i > 0); } public Mono unDeploy(String id) { - return findById(Mono.just(id)) - .switchIfEmpty(Mono.error(NotFoundException::new)) - .map(ProtocolSupportEntity::toUnDeployDefinition) - .flatMap(supportManager::save) - .flatMap(r -> createUpdate() - .set(ProtocolSupportEntity::getState, 0) - .where(ProtocolSupportEntity::getId, id) - .execute()) - .map(i -> i > 0); + // 消息协议被使用时,不能禁用 + return referenceManager + .assertNotReferenced(DataReferenceManager.TYPE_PROTOCOL, id) + .then(findById(Mono.just(id))) + .switchIfEmpty(Mono.error(NotFoundException::new)) + .flatMap(r -> createUpdate() + .set(ProtocolSupportEntity::getState, 0) + .where(ProtocolSupportEntity::getId, id) + .execute()) + .map(i -> i > 0); } } diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/ProtocolSupportHandler.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/ProtocolSupportHandler.java new file mode 100644 index 00000000..83f45ae2 --- /dev/null +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/service/ProtocolSupportHandler.java @@ -0,0 +1,73 @@ +package org.jetlinks.community.device.service; + +import lombok.AllArgsConstructor; +import org.hswebframework.web.crud.events.EntityBeforeDeleteEvent; +import org.hswebframework.web.crud.events.EntityCreatedEvent; +import org.hswebframework.web.crud.events.EntityModifyEvent; +import org.hswebframework.web.crud.events.EntitySavedEvent; +import org.hswebframework.web.exception.BusinessException; +import org.jetlinks.community.device.entity.ProtocolSupportEntity; +import org.jetlinks.community.reference.DataReferenceManager; +import org.jetlinks.core.ProtocolSupport; +import org.jetlinks.supports.protocol.management.ProtocolSupportLoader; +import org.jetlinks.supports.protocol.management.ProtocolSupportManager; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Collection; + +/** + * 协议事件处理类. + * + * @author zhangji 2022/4/1 + */ +@Component +@AllArgsConstructor +public class ProtocolSupportHandler { + private final DataReferenceManager referenceManager; + private ProtocolSupportLoader loader; + private ProtocolSupportManager supportManager; + + //禁止删除已有网关使用的协议 + @EventListener + public void handleProtocolDelete(EntityBeforeDeleteEvent event) { + event.async( + Flux.fromIterable(event.getEntity()) + .flatMap(protocol -> referenceManager + .assertNotReferenced(DataReferenceManager.TYPE_PROTOCOL, protocol.getId())) + ); + } + + @EventListener + public void handleCreated(EntityCreatedEvent event) { + event.async(reloadProtocol(event.getEntity())); + } + + @EventListener + public void handleSaved(EntitySavedEvent event) { + event.async(reloadProtocol(event.getEntity())); + } + + @EventListener + public void handleModify(EntityModifyEvent event) { + event.async(reloadProtocol(event.getAfter())); + } + + // 重新加载协议 + private Mono reloadProtocol(Collection protocol) { + return Flux + .fromIterable(protocol) + .filter(entity -> entity.getState() != null) + .map(entity -> entity.getState() == 1 ? entity.toDeployDefinition() : entity.toUnDeployDefinition()) + .flatMap(def -> loader + //加载一下检验是否正确,然后就卸载 + .load(def) + .doOnNext(ProtocolSupport::dispose) + .thenReturn(def)) + .onErrorMap(err -> new BusinessException("error.unable_to_load_protocol", 500, err.getMessage())) + .flatMap(supportManager::save) + .then(); + } +} diff --git a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java index ae36548f..170d1038 100644 --- a/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java +++ b/jetlinks-manager/device-manager/src/main/java/org/jetlinks/community/device/web/ProtocolSupportController.java @@ -13,6 +13,7 @@ import org.hswebframework.web.authorization.annotation.QueryAction; import org.hswebframework.web.authorization.annotation.Resource; import org.hswebframework.web.authorization.annotation.SaveAction; import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController; +import org.hswebframework.web.exception.BusinessException; import org.jetlinks.community.device.entity.ProtocolSupportEntity; import org.jetlinks.community.device.service.LocalProtocolSupportService; import org.jetlinks.community.device.web.protocol.ProtocolDetail; @@ -20,6 +21,7 @@ import org.jetlinks.community.device.web.protocol.ProtocolInfo; import org.jetlinks.community.device.web.protocol.TransportInfo; import org.jetlinks.community.device.web.request.ProtocolDecodeRequest; import org.jetlinks.community.device.web.request.ProtocolEncodeRequest; +import org.jetlinks.community.protocol.TransportDetail; import org.jetlinks.core.ProtocolSupport; import org.jetlinks.core.ProtocolSupports; import org.jetlinks.core.message.codec.Transport; @@ -203,4 +205,32 @@ public class ProtocolSupportController .sort(Comparator.comparingLong(Tuple2::getT1)) .map(Tuple2::getT2); } + + @GetMapping("/{id}/transport/{transport}") + @Authorize(merge = false) + @Operation(summary = "获取消息协议对应的传输协议信息") + public Mono getTransportDetail(@PathVariable @Parameter(description = "协议ID") String id, + @PathVariable @Parameter(description = "传输协议") String transport) { + return protocolSupports + .getProtocol(id) + .onErrorMap(e -> new BusinessException("error.unable_to_load_protocol_by_access_id", 404, id)) + .flatMapMany(protocol -> protocol + .getSupportedTransport() + .filter(trans -> trans.isSame(transport)) + .distinct() + .flatMap(_transport -> TransportDetail.of(protocol, _transport))) + .singleOrEmpty(); + } + + + @PostMapping("/{id}/detail") + @QueryAction + @Operation(summary = "获取协议详情") + public Mono protocolDetail(@PathVariable String id) { + return protocolSupports + .getProtocol(id) + .onErrorMap(e -> new BusinessException("error.unable_to_load_protocol_by_access_id", 404, id)) + .flatMap(ProtocolDetail::of); + } + }