feat(角色分组): 社区版移植角色分组功能 (#451)

This commit is contained in:
liusq 2023-12-01 13:36:53 +08:00 committed by GitHub
parent a5b3039be9
commit 4cb4422537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 240 additions and 0 deletions

View File

@ -15,6 +15,7 @@ import org.hswebframework.web.authorization.Dimension;
import org.hswebframework.web.authorization.simple.SimpleDimension;
import org.hswebframework.web.crud.generator.Generators;
import org.jetlinks.community.auth.enums.RoleState;
import org.jetlinks.community.auth.service.RoleGroupService;
import javax.persistence.Column;
import javax.persistence.Table;
@ -42,6 +43,11 @@ public class RoleEntity extends GenericEntity<String> implements RecordCreationE
@DefaultValue("enabled")
private RoleState state;
@Column(length = 64)
@Schema(description = "所属分组")
@DefaultValue(RoleGroupService.DEFAULT_GROUP_ID)
private String groupId;
@Column(updatable = false)
@Schema(
description = "创建者ID(只读)"

View File

@ -0,0 +1,50 @@
package org.jetlinks.community.auth.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
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 javax.persistence.Column;
import javax.persistence.Table;
import java.util.List;
@Getter
@Setter
@Table(name = "s_role_group")
@Comment("角色分组表")
@EnableEntityEvent
public class RoleGroupEntity extends GenericTreeSortSupportEntity<String> implements RecordCreationEntity {
@Column(length = 64)
@Length(min = 1, max = 64)
@Schema(description = "名称")
private String name;
@Column
@Length(max = 255)
@Schema(description = "说明")
private String description;
@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;
private List<RoleGroupEntity> children;
}

View File

@ -0,0 +1,101 @@
package org.jetlinks.community.auth.service;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.crud.query.QueryHelper;
import org.hswebframework.web.crud.service.GenericReactiveTreeSupportCrudService;
import org.hswebframework.web.exception.I18nSupportException;
import org.hswebframework.web.id.IDGenerator;
import org.jetlinks.community.auth.entity.RoleGroupEntity;
import org.jetlinks.community.auth.entity.RoleEntity;
import org.jetlinks.community.auth.web.response.RoleGroupDetailTree;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
@Service
@Slf4j
@AllArgsConstructor
public class RoleGroupService extends GenericReactiveTreeSupportCrudService<RoleGroupEntity, String> implements CommandLineRunner {
public static final String DEFAULT_GROUP_ID = "default_group";
private final RoleService roleService;
/**
* 分组下存在角色时不可删除
*/
@EventListener
public void handleEvent(EntityDeletedEvent<RoleGroupEntity> event) {
event.async(
Flux.fromIterable(event.getEntity())
.map(RoleGroupEntity::getId)
//默认分组不可删除
.filter(id -> !DEFAULT_GROUP_ID.equals(id))
.collectList()
.filter(CollectionUtils::isNotEmpty)
.flatMapMany(ids -> roleService
.createQuery()
.in(RoleEntity::getGroupId, ids)
.count()
.filter(i -> i <= 0)
.switchIfEmpty(Mono.error(() -> new I18nSupportException("error.group_role_exists"))))
);
}
public Flux<RoleGroupDetailTree> queryDetailTree(QueryParamEntity groupParam, QueryParamEntity roleParam) {
groupParam.setPaging(false);
roleParam.setPaging(false);
Flux<RoleGroupDetailTree> groupDetails = this
.query(groupParam)
.map(RoleGroupDetailTree::of);
return QueryHelper
.combineOneToMany(
groupDetails,
RoleGroupDetailTree::getGroupId,
roleService.createQuery().setParam(roleParam),
RoleEntity::getGroupId,
RoleGroupDetailTree::setRoles
);
}
@Override
public IDGenerator<String> getIDGenerator() {
return IDGenerator.SNOW_FLAKE_STRING;
}
@Override
public void setChildren(RoleGroupEntity entity, List<RoleGroupEntity> children) {
entity.setChildren(children);
}
/**
* 兼容旧数据
*/
@Override
public void run(String... args) {
//兼容旧数据,空分组即为默认分组
roleService
.createUpdate()
.set(RoleEntity::getGroupId, DEFAULT_GROUP_ID)
.isNull(RoleEntity::getGroupId)
.execute()
.subscribe(ignore -> {
},
err -> log.error("init role groupId error", err));
}
}

View File

@ -0,0 +1,48 @@
package org.jetlinks.community.auth.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 org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.annotation.Resource;
import org.hswebframework.web.authorization.annotation.SaveAction;
import org.hswebframework.web.crud.service.ReactiveCrudService;
import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
import org.jetlinks.community.auth.entity.RoleGroupEntity;
import org.jetlinks.community.auth.service.RoleGroupService;
import org.jetlinks.community.auth.web.response.RoleGroupDetailTree;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/role/group")
@Resource(id = "role-group", name = "角色组管理")
@AllArgsConstructor
@Getter
@Tag(name = "角色组管理")
public class RoleGroupController implements ReactiveServiceCrudController<RoleGroupEntity, String> {
private final RoleGroupService roleGroupService;
@PostMapping("/detail/_query/tree")
@Operation(summary = "查询分组及角色(树状)")
@SaveAction
public Flux<RoleGroupDetailTree> queryDetailTree(@RequestParam(defaultValue = "false") @Parameter(description = "true:query为角色条件,false:query为分组条件") boolean queryByRole,
@RequestBody Mono<QueryParamEntity> query) {
return Mono
.zip(queryByRole ? query : Mono.just(new QueryParamEntity()),
queryByRole ? Mono.just(new QueryParamEntity()) : query)
.flatMapMany(tp2 -> roleGroupService.queryDetailTree(tp2.getT2(), tp2.getT1()));
}
@Override
public ReactiveCrudService<RoleGroupEntity, String> getService() {
return roleGroupService;
}
}

View File

@ -0,0 +1,35 @@
package org.jetlinks.community.auth.web.response;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.jetlinks.community.auth.entity.RoleEntity;
import org.jetlinks.community.auth.entity.RoleGroupEntity;
import java.util.List;
/**
* @author gyl
* @since 2.1
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class RoleGroupDetailTree {
private String groupId;
private String groupName;
private List<RoleEntity> roles;
public static RoleGroupDetailTree of(RoleGroupEntity group) {
RoleGroupDetailTree roleGroupDetailTree = new RoleGroupDetailTree();
roleGroupDetailTree.setGroupId(group.getId());
roleGroupDetailTree.setGroupName(group.getName());
return roleGroupDetailTree;
}
}