refactor: 优化

This commit is contained in:
zhouhao
2025-01-02 16:26:43 +08:00
parent 35ad482eb3
commit 358f740e63
5 changed files with 293 additions and 176 deletions

View File

@@ -0,0 +1,69 @@
/*
*
* * Copyright 2020 http://www.hswebframework.org
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.hswebframework.web.api.crud.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 javax.persistence.Column;
/**
* 支持树形结构,排序的实体类,要使用树形结构,排序功能的实体类直接继承该类
*/
@Getter
@Setter
public abstract class ExtendableTreeSortSupportEntity<PK> extends ExtendableEntity<PK>
implements TreeSortSupportEntity<PK> {
/**
* 父级类别
*/
@Column(name = "parent_id", length = 64)
@Comment("父级ID")
@Schema(description = "父节点ID")
private PK parentId;
/**
* 树结构编码,用于快速查找, 每一层由4位字符组成,用-分割
* 如第一层:0001 第二层:0001-0001 第三层:0001-0001-0001
*/
@Column(name = "path", length = 128)
@Comment("树路径")
@Schema(description = "树结构路径")
@Length(max = 128, message = "目录层级太深")
private String path;
/**
* 排序索引
*/
@Column(name = "sort_index", precision = 32)
@Comment("排序序号")
@Schema(description = "排序序号")
private Long sortIndex;
@Column(name = "_level", precision = 32)
@Comment("树层级")
@Schema(description = "树层级")
private Integer level;
}

View File

@@ -78,37 +78,40 @@ public class TreeUtils {
(helper, node) -> {
PK parentId = parentIdGetter.apply(node);
return ObjectUtils.isEmpty(parentId)
|| helper.getNode(parentId) == null;
|| helper.getNode(parentId) == null;
});
}
/**
* 列表结构转为树结构,并返回根节点集合
*
* @param dataList 数据集合
* @param childConsumer 子节点消费接口,用于设置子节点
* @param predicateFunction 根节点判断函数,传入helper,获取一个判断是否为节点的函数
* @param <N> 元素类型
* @param <PK> 主键类型
* @param dataList 数据集合
* @param childConsumer 子节点消费接口,用于设置子节点
* @param rootPredicate 根节点判断函数,传入helper,获取一个判断是否为节点的函数
* @param <N> 元素类型
* @param <PK> 主键类型
* @return 根节点集合
*/
public static <N, PK> List<N> list2tree(Collection<N> dataList,
Function<N, PK> idGetter,
Function<N, PK> parentIdGetter,
BiConsumer<N, List<N>> childConsumer,
BiPredicate<TreeSupportEntity.TreeHelper<N, PK>, N> predicateFunction) {
BiPredicate<TreeSupportEntity.TreeHelper<N, PK>, N> rootPredicate) {
Objects.requireNonNull(dataList, "source list can not be null");
Objects.requireNonNull(childConsumer, "child consumer can not be null");
Objects.requireNonNull(predicateFunction, "root predicate function can not be null");
Objects.requireNonNull(rootPredicate, "root predicate function can not be null");
int size = dataList.size();
if (size == 0) {
return new ArrayList<>(0);
}
// id,node
Map<PK, N> cache = Maps.newHashMapWithExpectedSize(dataList.size());
Map<PK, N> cache = Maps.newHashMapWithExpectedSize(size);
// parentId,children
Map<PK, List<N>> treeCache = dataList
.stream()
.peek(node -> cache.put(idGetter.apply(node), node))
.filter(e -> parentIdGetter.apply(e) != null)
.collect(Collectors.groupingBy(parentIdGetter));
.stream()
.peek(node -> cache.put(idGetter.apply(node), node))
.filter(e -> parentIdGetter.apply(e) != null)
.collect(Collectors.groupingBy(parentIdGetter));
TreeSupportEntity.TreeHelper<N, PK> helper = new TreeSupportEntity.TreeHelper<N, PK>() {
@Override
@@ -122,13 +125,18 @@ public class TreeUtils {
}
};
return dataList
.stream()
//设置每个节点的子节点
.peek(node -> childConsumer.accept(node, treeCache.get(idGetter.apply(node))))
//获取根节点
.filter(node -> predicateFunction.test(helper, node))
.collect(Collectors.toList());
List<N> list = new ArrayList<>(treeCache.size());
for (N node : dataList) {
//设置每个节点的子节点
childConsumer.accept(node, treeCache.get(idGetter.apply(node)));
//获取根节点
if (rootPredicate.test(helper, node)) {
list.add(node);
}
}
return list;
}
}

View File

@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
* @see GenericReactiveTreeSupportCrudService
*/
public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K>, K>
extends ReactiveCrudService<E, K> {
extends ReactiveCrudService<E, K> {
/**
* 动态查询并将查询结构转为树形结构
@@ -54,10 +54,10 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<List<E>> queryResultToTree(QueryParamEntity paramEntity) {
return query(paramEntity)
.collectList()
.map(list -> TreeSupportEntity.list2tree(list,
this::setChildren,
this::createRootNodePredicate));
.collectList()
.map(list -> TreeSupportEntity.list2tree(list,
this::setChildren,
this::createRootNodePredicate));
}
/**
@@ -69,55 +69,78 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<List<E>> queryIncludeChildrenTree(QueryParamEntity paramEntity) {
return queryIncludeChildren(paramEntity)
.collectList()
.map(list -> TreeSupportEntity.list2tree(list,
this::setChildren,
this::createRootNodePredicate));
.collectList()
.map(list -> TreeSupportEntity.list2tree(list,
this::setChildren,
this::createRootNodePredicate));
}
/**
* 查询指定ID的实体以及对应的全部子节点
*
* @param idList ID集合
* @return 树形结构
* @return 包含子节点的所有节点
*/
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Flux<E> queryIncludeChildren(Collection<K> idList) {
Set<String> duplicateCheck = new HashSet<>();
return queryIncludeChildren(findById(idList));
}
return findById(idList)
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.where()
//使用path快速查询
.like$("path", e.getPath())
.fetch(),
Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
/**
* 根据实体流查询全部子节点(包含原节点)
*
* @param entities 实体流
* @return 包含子节点的所有节点
* @since 4.0.18
*/
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Flux<E> queryIncludeChildren(Flux<E> entities) {
Set<String> duplicateCheck = new HashSet<>();
return entities
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.where()
//使用path快速查询
.like$("path", e.getPath())
.fetch(),
Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
}
/**
* 查询指定ID的实体以及对应的全部父节点
*
* @param idList ID集合
* @return 树形结构
* @return 包含父节点的所有节点
*/
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Flux<E> queryIncludeParent(Collection<K> idList) {
return queryIncludeParent(findById(idList));
}
/**
* 根据实体流查询全部父节点(包含原节点)
*
* @param entities 实体流
* @return 包含父节点的所有节点
* @since 4.0.18
*/
@Transactional(readOnly = true, transactionManager = TransactionManagers.reactiveTransactionManager)
default Flux<E> queryIncludeParent(Flux<E> entities) {
Set<String> duplicateCheck = new HashSet<>();
return findById(idList)
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.where()
//where ? like path and path !='' and path not null
.accept(Terms.Like.reversal("path", e.getPath(), false, true))
.notEmpty("path")
.notNull("path")
.fetch(), Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
return entities
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.where()
//where ? like path and path !='' and path not null
.accept(Terms.Like.reversal("path", e.getPath(), false, true))
.notEmpty("path")
.notNull("path")
.fetch(), Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
}
/**
@@ -131,23 +154,23 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
Set<String> duplicateCheck = new HashSet<>();
return query(queryParam)
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.as(q -> {
if (CollectionUtils.isNotEmpty(queryParam.getIncludes())) {
q.select(queryParam.getIncludes().toArray(new String[0]));
}
if (CollectionUtils.isNotEmpty(queryParam.getExcludes())) {
q.selectExcludes(queryParam.getExcludes().toArray(new String[0]));
}
return q;
})
.where()
.like$("path", e.getPath())
.fetch()
,Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
.concatMap(e -> !StringUtils.hasText(e.getPath()) || !duplicateCheck.add(e.getPath())
? Mono.just(e)
: createQuery()
.as(q -> {
if (CollectionUtils.isNotEmpty(queryParam.getIncludes())) {
q.select(queryParam.getIncludes().toArray(new String[0]));
}
if (CollectionUtils.isNotEmpty(queryParam.getExcludes())) {
q.selectExcludes(queryParam.getExcludes().toArray(new String[0]));
}
return q;
})
.where()
.like$("path", e.getPath())
.fetch()
, Integer.MAX_VALUE)
.distinct(TreeSupportEntity::getId);
}
@Override
@@ -166,12 +189,12 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Transactional(transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<Integer> insertBatch(Publisher<? extends Collection<E>> entityPublisher) {
return this
.getRepository()
.insertBatch(new TreeSortServiceHelper<>(this)
.prepare(Flux.from(entityPublisher)
.flatMapIterable(Function.identity()))
// .doOnNext(e -> e.tryValidate(CreateGroup.class))
.buffer(getBufferSize()));
.getRepository()
.insertBatch(new TreeSortServiceHelper<>(this)
.prepare(Flux.from(entityPublisher)
.flatMapIterable(Function.identity()))
// .doOnNext(e -> e.tryValidate(CreateGroup.class))
.buffer(getBufferSize()));
}
default int getBufferSize() {
@@ -181,7 +204,7 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Deprecated
default Mono<E> applyTreeProperty(E ele) {
if (StringUtils.hasText(ele.getPath()) ||
ObjectUtils.isEmpty(ele.getParentId())) {
ObjectUtils.isEmpty(ele.getParentId())) {
return Mono.just(ele);
}
@@ -198,57 +221,57 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
return Mono.empty();
}
return this
.queryIncludeChildren(Collections.singletonList(id))
.doOnNext(e -> {
if (Objects.equals(ele.getParentId(), e.getId())) {
throw new ValidationException.NoStackTrace("parentId", "error.tree_entity_cyclic_dependency");
}
})
.then(Mono.just(ele));
.queryIncludeChildren(Collections.singletonList(id))
.doOnNext(e -> {
if (Objects.equals(ele.getParentId(), e.getId())) {
throw new ValidationException.NoStackTrace("parentId", "error.tree_entity_cyclic_dependency");
}
})
.then(Mono.just(ele));
}
@Deprecated
default Mono<Collection<E>> checkParentId(Collection<E> source) {
Set<K> idSet = source
.stream()
.map(TreeSupportEntity::getId)
.filter(e -> !ObjectUtils.isEmpty(e))
.collect(Collectors.toSet());
.stream()
.map(TreeSupportEntity::getId)
.filter(e -> !ObjectUtils.isEmpty(e))
.collect(Collectors.toSet());
if (idSet.isEmpty()) {
return Mono.just(source);
}
Set<K> readyToCheck = source
.stream()
.map(TreeSupportEntity::getParentId)
.filter(e -> !ObjectUtils.isEmpty(e) && !idSet.contains(e))
.collect(Collectors.toSet());
.stream()
.map(TreeSupportEntity::getParentId)
.filter(e -> !ObjectUtils.isEmpty(e) && !idSet.contains(e))
.collect(Collectors.toSet());
if (readyToCheck.isEmpty()) {
return Mono.just(source);
}
return this
.createQuery()
.select("id")
.in("id", readyToCheck)
.fetch()
.doOnNext(e -> readyToCheck.remove(e.getId()))
.then(Mono.fromSupplier(() -> {
if (!readyToCheck.isEmpty()) {
throw new ValidationException(
.createQuery()
.select("id")
.in("id", readyToCheck)
.fetch()
.doOnNext(e -> readyToCheck.remove(e.getId()))
.then(Mono.fromSupplier(() -> {
if (!readyToCheck.isEmpty()) {
throw new ValidationException(
"error.tree_entity_parent_id_not_exist",
Collections.singletonList(
new ValidationException.Detail(
"parentId",
"error.tree_entity_parent_id_not_exist",
Collections.singletonList(
new ValidationException.Detail(
"parentId",
"error.tree_entity_parent_id_not_exist",
readyToCheck))
);
}
return source;
}));
readyToCheck))
);
}
return source;
}));
}
@@ -274,14 +297,14 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Override
@Transactional(rollbackFor = Throwable.class,
transactionManager = TransactionManagers.reactiveTransactionManager)
transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<SaveResult> save(Publisher<E> entityPublisher) {
return new TreeSortServiceHelper<>(this)
.prepare(Flux.from(entityPublisher))
.prepare(Flux.from(entityPublisher))
// .doOnNext(e -> e.tryValidate(CreateGroup.class))
.buffer(getBufferSize())
.flatMap(this.getRepository()::save)
.reduce(SaveResult::merge);
.buffer(getBufferSize())
.flatMap(this.getRepository()::save)
.reduce(SaveResult::merge);
}
@@ -306,12 +329,12 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Transactional(transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<Integer> updateById(K id, Mono<E> entityPublisher) {
return this
.findById(id)
.map(e -> this
.save(entityPublisher.doOnNext(data -> data.setId(id)))
.map(SaveResult::getTotal))
.defaultIfEmpty(Mono.just(0))
.flatMap(Function.identity());
.findById(id)
.map(e -> this
.save(entityPublisher.doOnNext(data -> data.setId(id)))
.map(SaveResult::getTotal))
.defaultIfEmpty(Mono.just(0))
.flatMap(Function.identity());
}
@Override
@@ -324,11 +347,11 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@Transactional(transactionManager = TransactionManagers.reactiveTransactionManager)
default Mono<Integer> deleteById(Publisher<K> idPublisher) {
return this
.findById(Flux.from(idPublisher))
.concatMap(e -> StringUtils.hasText(e.getPath())
? getRepository().createDelete().where().like$(e::getPath).execute()
: getRepository().deleteById(e.getId()),Integer.MAX_VALUE)
.as(MathFlux::sumInt);
.findById(Flux.from(idPublisher))
.concatMap(e -> StringUtils.hasText(e.getPath())
? getRepository().createDelete().where().like$(e::getPath).execute()
: getRepository().deleteById(e.getId()), Integer.MAX_VALUE)
.as(MathFlux::sumInt);
}
IDGenerator<K> getIDGenerator();
@@ -360,18 +383,18 @@ public interface ReactiveTreeSortEntityService<E extends TreeSortSupportEntity<K
@SuppressWarnings("all")
default ReactiveDelete createDelete() {
return ReactiveCrudService.super
.createDelete()
.onExecute((delete, executor) -> this
.queryIncludeChildren(delete.toQueryParam(QueryParamEntity::new)
.<QueryParamEntity>includes("id", "path", "parentId"))
.map(TreeSupportEntity::getId)
.buffer(200)
.concatMap(list -> getRepository()
.createDelete()
.where()
.in("id", list)
.execute(), Integer.MAX_VALUE)
//.concatWith(executor)
.reduce(0, Math::addExact));
.createDelete()
.onExecute((delete, executor) -> this
.queryIncludeChildren(delete.toQueryParam(QueryParamEntity::new)
.<QueryParamEntity>includes("id", "path", "parentId"))
.map(TreeSupportEntity::getId)
.buffer(200)
.concatMap(list -> getRepository()
.createDelete()
.where()
.in("id", list)
.execute(), Integer.MAX_VALUE)
//.concatWith(executor)
.reduce(0, Math::addExact));
}
}

View File

@@ -25,6 +25,20 @@ import java.util.concurrent.atomic.AtomicReference;
public class FastBeanCopierTest {
@Test
public void testExtendableToExtendable() {
ExtendableEntity source = new ExtendableEntity();
source.setName("test");
source.setExtension("age", 123);
source.setExtension("color", Color.RED);
ExtendableEntity e = FastBeanCopier.copy(source, new ExtendableEntity());
Assert.assertEquals(source.getName(), e.getName());
Assert.assertEquals(source.getExtension("age"), e.getExtension("age"));
Assert.assertEquals(source.getExtension("color"), e.getExtension("color"));
}
@Test
public void testToExtendable() {
Source source = new Source();

View File

@@ -26,6 +26,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;
import java.util.*;
import java.util.function.Function;
@@ -33,7 +34,7 @@ import java.util.stream.Collectors;
@Slf4j
public class DefaultReactiveAuthenticationInitializeService
implements ReactiveAuthenticationInitializeService {
implements ReactiveAuthenticationInitializeService {
@Autowired
private ReactiveUserService userService;
@@ -63,25 +64,25 @@ public class DefaultReactiveAuthenticationInitializeService
return userEntityMono.flatMap(user -> {
SimpleAuthentication authentication = new SimpleAuthentication();
authentication.setUser(SimpleUser
.builder()
.id(user.getId())
.name(user.getName())
.username(user.getUsername())
.userType(user.getType())
.build());
.builder()
.id(user.getId())
.name(user.getName())
.username(user.getUsername())
.userType(user.getType())
.build());
return initPermission(authentication)
.defaultIfEmpty(authentication)
.onErrorResume(err -> {
log.warn(err.getMessage(), err);
return Mono.just(authentication);
})
.flatMap(auth -> {
AuthorizationInitializeEvent event = new AuthorizationInitializeEvent(auth);
return event
.publish(eventPublisher)
.then(Mono.fromSupplier(event::getAuthentication));
});
.defaultIfEmpty(authentication)
.onErrorResume(err -> {
log.warn(err.getMessage(), err);
return Mono.just(authentication);
})
.flatMap(auth -> {
AuthorizationInitializeEvent event = new AuthorizationInitializeEvent(auth);
return event
.publish(eventPublisher)
.then(Mono.fromSupplier(event::getAuthentication));
});
});
}
@@ -90,26 +91,28 @@ public class DefaultReactiveAuthenticationInitializeService
.filter(dimension -> dimension.getType() != null)
.groupBy(d -> d.getType().getId(), (Function<Dimension, Object>) Dimension::getId)
.flatMap(group ->
group.collectList()
.flatMapMany(list -> settingRepository
.createQuery()
.where(AuthorizationSettingEntity::getState, 1)
.and(AuthorizationSettingEntity::getDimensionType, group.key())
.in(AuthorizationSettingEntity::getDimensionTarget, list)
.fetch()));
group.collectList()
.flatMapMany(list -> settingRepository
.createQuery()
.where(AuthorizationSettingEntity::getState, 1)
.and(AuthorizationSettingEntity::getDimensionType, group.key())
.in(AuthorizationSettingEntity::getDimensionTarget, list)
.fetch()));
}
protected Mono<Authentication> initPermission(SimpleAuthentication authentication) {
return Flux.fromIterable(dimensionProviders)
.flatMap(provider -> provider.getDimensionByUserId(authentication.getUser().getId()))
.cast(Dimension.class)
//去重?还是合并?
.distinct(dis -> Tuples.of(dis.getType().getId(), dis.getId()))
.doOnNext(authentication::addDimension)
.collectList()
.then(Mono.defer(() -> Mono
.zip(getAllPermission(),
getSettings(authentication.getDimensions()).collect(Collectors.groupingBy(AuthorizationSettingEntity::getPermission)),
(_p, _s) -> handlePermission(authentication, _p, _s)
)));
.zip(getAllPermission(),
getSettings(authentication.getDimensions()).collect(Collectors.groupingBy(AuthorizationSettingEntity::getPermission)),
(_p, _s) -> handlePermission(authentication, _p, _s)
)));
}
@@ -145,9 +148,9 @@ public class DefaultReactiveAuthenticationInitializeService
.stream()
.map(conf -> {
DataAccessConfig config = builderFactory
.create()
.fromMap(conf.toMap())
.build();
.create()
.fromMap(conf.toMap())
.build();
if (config == null) {
log.warn("unsupported data access:{}", conf.toMap());
}
@@ -205,11 +208,11 @@ public class DefaultReactiveAuthenticationInitializeService
protected Mono<Map<String, PermissionEntity>> getAllPermission() {
return permissionRepository
.createQuery()
.where(PermissionEntity::getStatus, 1)
.fetch()
.collect(Collectors.toMap(PermissionEntity::getId, Function.identity()))
.switchIfEmpty(Mono.just(Collections.emptyMap()));
.createQuery()
.where(PermissionEntity::getStatus, 1)
.fetch()
.collect(Collectors.toMap(PermissionEntity::getId, Function.identity()))
.switchIfEmpty(Mono.just(Collections.emptyMap()));
}
}