From 1f4540bad77493c7701703dfba1f33cf48e7c557 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 4 Jul 2024 09:23:16 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/group/GroupTemplateService.java | 3 +- .../message/service/group/NodeWrapper.java | 4 + .../service/group/RootNodeWrapper.java | 4 +- .../message/service/todo/LeafNodeStats.java | 51 +++++++++ .../service/todo/TodoRangeQueryService.java | 103 +++++++----------- .../axzo/msg/center/domain/entity/Todo.java | 7 +- 6 files changed, 103 insertions(+), 69 deletions(-) create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/LeafNodeStats.java diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/GroupTemplateService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/GroupTemplateService.java index 2a0107b1..92e7ef8e 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/GroupTemplateService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/GroupTemplateService.java @@ -99,8 +99,7 @@ public class GroupTemplateService { public GroupTemplates collectTemplates(List> nodes) { Set groupNodeCodes = nodes.stream() .map(ValueNode::getValue) - .map(NodeWrapper::unwrap) - .map(MessageGroupNode::getCode) + .map(NodeWrapper::getNodeCode) .collect(toSet()); return collectTemplates(groupNodeCodes); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/NodeWrapper.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/NodeWrapper.java index 17491315..2d9b1537 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/NodeWrapper.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/NodeWrapper.java @@ -42,4 +42,8 @@ public class NodeWrapper implements NodeValue { return node; } + public String getNodeCode() { + return node.getCode(); + } + } \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/RootNodeWrapper.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/RootNodeWrapper.java index d7604168..32eda67a 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/RootNodeWrapper.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/group/RootNodeWrapper.java @@ -31,7 +31,7 @@ public class RootNodeWrapper { public RootNodeWrapper(RootNode root) { this.root = root; this.code2Node = root.getValueNodes().stream() - .collect(toMap(node -> node.getValue().unwrap().getCode(), Function.identity())); + .collect(toMap(node -> node.getValue().getNodeCode(), Function.identity())); } public Optional> findValueNode(String groupNodeCode) { @@ -72,7 +72,7 @@ public class RootNodeWrapper { //noinspection unchecked ValueNode valueNode = current.tryCast(ValueNode.class); if (valueNode != null) - paths.add(valueNode.getValue().unwrap().getCode()); + paths.add(valueNode.getValue().getNodeCode()); current = current.getParent(); } Collections.reverse(paths); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/LeafNodeStats.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/LeafNodeStats.java new file mode 100644 index 00000000..b26b88e9 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/LeafNodeStats.java @@ -0,0 +1,51 @@ +package cn.axzo.msg.center.message.service.todo; + +import cn.axzo.maokai.api.vo.response.tree.ValueNode; +import cn.axzo.msg.center.domain.entity.Todo; +import cn.axzo.msg.center.message.service.group.GroupTemplates; +import cn.axzo.msg.center.message.service.group.NodeWrapper; +import cn.axzo.msg.center.service.enums.TodoType; +import lombok.RequiredArgsConstructor; + +import java.util.Collections; +import java.util.List; + +/** + * @author yanglin + */ +@RequiredArgsConstructor +class LeafNodeStats { + + // count, type, template_code + private final List stats; + private final GroupTemplates groupTemplates; + + int getCount(ValueNode node, TodoType todoType) { + return getCount(Collections.singletonList(node), todoType); + } + + int getCount(List> nodes, TodoType todoType) { + int count = 0; + for (Todo stat : stats) { + if (stat.getType() != todoType) + continue; + // 只统计叶子节点的数据, 显示列表不关心中间节点 + boolean leafNodeContainsTemplate = nodes.stream() + .map(ValueNode::getValue) + .filter(NodeWrapper::isLeaf) + .anyMatch(node -> groupTemplates + .groupContainsTemplate(node.getNodeCode(), stat.getTemplateCode())); + if (leafNodeContainsTemplate) + count += stat.getCount(); + } + return count; + } + + int getCount(TodoType todoType) { + return stats.stream() + .filter(s -> s.getType() == todoType) + .mapToInt(Todo::getCount) + .sum(); + } + +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java index f2a07615..84690357 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java @@ -15,6 +15,7 @@ import cn.axzo.msg.center.domain.entity.TodoBusiness; import cn.axzo.msg.center.domain.persistence.BaseEntityExt; import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam; import cn.axzo.msg.center.message.service.group.GroupTemplateService; +import cn.axzo.msg.center.message.service.group.GroupTemplates; import cn.axzo.msg.center.message.service.group.NodeWrapper; import cn.axzo.msg.center.message.service.impl.PendingMessageNewServiceImpl; import cn.axzo.msg.center.message.service.todo.cache.NodeStatCache; @@ -39,6 +40,7 @@ import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticRespon import cn.axzo.msg.center.service.pending.response.PersonTodoToBeDoneStatResponse; import cn.axzo.msg.center.utils.DateFormatUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -49,14 +51,12 @@ import org.springframework.stereotype.Component; import javax.annotation.Nullable; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ForkJoinPool; -import java.util.function.Function; import java.util.function.Supplier; import static cn.axzo.msg.center.inside.notices.utils.Queries.query; @@ -81,7 +81,6 @@ public class TodoRangeQueryService { private final AnalysisConfig analysisConfig; private final GroupTemplateService groupTemplateService; private final NodeStatCache nodeStatCache; - private final ForkJoinPool statExecutor = new ForkJoinPool(80); private final TodoTerminalHelper todoTerminalHelper; private final AnalysisHelper analysisHelper; @@ -144,8 +143,8 @@ public class TodoRangeQueryService { .collect(toSet()); CopiedToMeParam copiedToMeParam = request.getCopiedToMeParam(); Ref> ouCollector = Ref.create(); - OuInfo ouInfo = OuInfo.create(request.getOuId(), request.getAppTerminalType(), request.determineToDoType()); - LambdaQueryWrapper query = todoQuery(ouInfo, ouCollector) + OuInfo ouInfo = new OuInfo(request.getOuId(), request.getAppTerminalType()); + LambdaQueryWrapper query = todoQuery(query(Todo.class), ouInfo, ouCollector) // 查询的待办类型: COPIED_TO_ME, EXECUTABLE .eq(Todo::getType, request.determineToDoType()) .eq(Todo::getExecutorPersonId, request.getPersonId()) @@ -227,7 +226,7 @@ public class TodoRangeQueryService { // 如果没有传端就查询所有端的待办, 用于待办转交 if (request.getAppTerminalType() == null) nodes = groupTemplateService.getTodoGroups(AppTerminalTypeEnum.allTerminals()); - else + else // 获取可见的模版时, 不用定位到叶子节点 nodes = groupTemplateService.getTodoGroups(request.getAppTerminalType()); return groupTemplateService.collectTemplateCodes(nodes); @@ -256,40 +255,26 @@ public class TodoRangeQueryService { private PendingMessageStatisticResponseV2 countStatGroupedImpl(MessageGroupNodeStatisticParam request) { List> nodes = determineStatNodes(request); - Function, GroupStat> nodeStatFun = valueNode -> { - int executableCount = countStatByNode(request, valueNode, TodoType.EXECUTABLE); - int copiedToMeCount = countStatByNode(request, valueNode, TodoType.COPIED_TO_ME); - GroupStat groupStat = new GroupStat(); - MessageGroupNode node = valueNode.getValue().unwrap(); - groupStat.setGroupCode(node.getCode()); - groupStat.setGroupName(node.getName()); - groupStat.setGroupIcon(node.getIcon()); - groupStat.setStat(new Stat(executableCount, copiedToMeCount)); - return groupStat; - }; try { + LeafNodeStats nodeStats = countStatByNodes(request, nodes); + List stats = new ArrayList<>(); + for (ValueNode valueNode : nodes) { + GroupStat groupStat = new GroupStat(); + stats.add(groupStat); + MessageGroupNode node = valueNode.getValue().unwrap(); + groupStat.setGroupCode(node.getCode()); + groupStat.setGroupName(node.getName()); + groupStat.setGroupIcon(node.getIcon()); + groupStat.setStat(new Stat( + nodeStats.getCount(valueNode, TodoType.EXECUTABLE), + nodeStats.getCount(valueNode, TodoType.COPIED_TO_ME))); + } PendingMessageStatisticResponseV2 resp = new PendingMessageStatisticResponseV2(); - // 先提交任务 - CompletableFuture uniqueExecutableCountFuture = CompletableFuture.supplyAsync( - () -> countStatByNodes(request, nodes, TodoType.EXECUTABLE), statExecutor); - CompletableFuture uniqueCopiedToMeCountFuture = CompletableFuture.supplyAsync( - () -> countStatByNodes(request, nodes, TodoType.COPIED_TO_ME), statExecutor); - // 用一个偏大的线程池, IO密集型任务, commonPool不够用 - // 提交任务并获取结果 - List stats = statExecutor - .submit(() -> nodes - .parallelStream() - .map(nodeStatFun) - .collect(toList())) - .get(); resp.addGroupStats(stats); // 一个端可能会有重复分类 resp.setTotalStat(); - CompletableFuture.allOf(uniqueExecutableCountFuture, uniqueCopiedToMeCountFuture).join(); - // 一个端不包含重复分类 - int uniqueExecutableCount = uniqueExecutableCountFuture.get(); - int uniqueCopiedToMeCount = uniqueCopiedToMeCountFuture.get(); - resp.setUniqueTotalStat(new Stat(uniqueExecutableCount, uniqueCopiedToMeCount)); + // 为了性能, 不做去重的统计了 + resp.setUniqueTotalStat(new Stat(-1, -1)); return resp; } catch (Exception e) { log.warn("分类统计异常, request={}", request, e); @@ -307,28 +292,26 @@ public class TodoRangeQueryService { return totalStat == null ? 0 : totalStat.getPendingCount(); } - private int countStatByNode(MessageGroupNodeStatisticParam request, - ValueNode node, TodoType todoType) { - return countStatByNodes(request, Collections.singletonList(node), todoType); - } - - private int countStatByNodes(MessageGroupNodeStatisticParam request, - List> nodes, TodoType todoType) { + private LeafNodeStats countStatByNodes(MessageGroupNodeStatisticParam request, List> nodes) { List templateCodes = groupTemplateService.collectTemplateCodes(nodes); + GroupTemplates groupTemplates = groupTemplateService.collectTemplates(nodes); if (CollectionUtils.isEmpty(templateCodes)) - return 0; - OuInfo ouInfo = OuInfo.create(request.getOuId(), request.getTerminalType(), todoType); - LambdaQueryWrapper query = todoQuery(ouInfo) + return new LeafNodeStats(Collections.emptyList(), groupTemplates); + LambdaQueryWrapper query = new QueryWrapper() + // 1. group by bellow + .select("COUNT(*) AS count, template_code, type") + .lambda(); + OuInfo ouInfo = new OuInfo(request.getOuId(), request.getTerminalType()); + //noinspection unchecked + List stats = todoDao.list(todoQuery(query, ouInfo, null) .in(Todo::getTemplateCode, templateCodes) .eq(Todo::getExecutorPersonId, request.getPersonId()) .in(CollectionUtils.isNotEmpty(request.getWorkspaceIds()), Todo::getOrgId, request.getWorkspaceIds()) - .and(todoType == TodoType.EXECUTABLE, nested -> nested - .eq(Todo::getType, TodoType.EXECUTABLE) - .eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT)) - .and(todoType == TodoType.COPIED_TO_ME, nested -> nested - .eq(Todo::getType, TodoType.COPIED_TO_ME) - .eq(Todo::getState, PendingMessageStateEnum.CREATED)); - return todoDao.count(query); + .in(Todo::getType, TodoType.EXECUTABLE, TodoType.COPIED_TO_ME) + .in(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT, PendingMessageStateEnum.CREATED) + // 2. group by here + .groupBy(Todo::getTemplateCode, Todo::getType)); + return new LeafNodeStats(stats, groupTemplates); } public List> determineStatNodes(MessageGroupNodeStatisticParam request) { @@ -358,11 +341,8 @@ public class TodoRangeQueryService { .likeRight(matchBusinessTitle, TodoBusiness::getTitle, title)); } - private LambdaQueryWrapper todoQuery(OuInfo info) { - return todoQuery(info, null); - } - - private LambdaQueryWrapper todoQuery(OuInfo info, @Nullable Ref> ouCollector) { + private LambdaQueryWrapper todoQuery(LambdaQueryWrapper query, + OuInfo info, @Nullable Ref> ouCollector) { List ouIds = Collections.emptyList(); // 工人端不通过ou做查询 if (info.terminalType != AppTerminalTypeEnum.C_WORKER_APP) { @@ -372,7 +352,7 @@ public class TodoRangeQueryService { } // 1. 查询 ouId = 0 的数据 // 2. 查询ouId下面所有的平台班组id当成ouId - return query(Todo.class) + return query .eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value) .in(CollectionUtils.isNotEmpty(ouIds), Todo::getOuId, ouIds); } @@ -381,11 +361,6 @@ public class TodoRangeQueryService { private static class OuInfo { final Long ouId; final AppTerminalTypeEnum terminalType; - final boolean includeZeroOuId; - - static OuInfo create(Long ouId, AppTerminalTypeEnum terminalType, TodoType todoType) { - return new OuInfo(ouId, terminalType, todoType == TodoType.EXECUTABLE); - } } } \ No newline at end of file diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java index b05bb512..08f08346 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java @@ -16,7 +16,6 @@ import lombok.Getter; import lombok.Setter; import java.util.Date; -import java.util.HashMap; /** * @author yanglin @@ -167,6 +166,12 @@ public class Todo extends BaseEntityExt { // !! helper + /** + * 辅助字段, 用于统计数量 + */ + @TableField(exist = false) + private int count; + @Override public String toString() { return JSON.toJSONString(this); From 9f39e213ed978506cf7383870ecac3bab534efee Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 4 Jul 2024 14:15:04 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../msg/center/message/service/todo/TodoRangeQueryService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java index 84690357..acc65aab 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoRangeQueryService.java @@ -288,7 +288,7 @@ public class TodoRangeQueryService { public Integer countStat(MessageGroupNodeStatisticParam request) { PendingMessageStatisticResponseV2 nodeStat = nodeStatCache .getCacheResponseOrReload(request, () -> countStatGroupedImpl(request)); - Stat totalStat = nodeStat.getUniqueTotalStat(); + Stat totalStat = nodeStat.getTotalStat(); return totalStat == null ? 0 : totalStat.getPendingCount(); }