优化待办分类统计
This commit is contained in:
parent
bfcf3f3402
commit
1f4540bad7
@ -99,8 +99,7 @@ public class GroupTemplateService {
|
||||
public GroupTemplates collectTemplates(List<ValueNode<NodeWrapper>> nodes) {
|
||||
Set<String> groupNodeCodes = nodes.stream()
|
||||
.map(ValueNode::getValue)
|
||||
.map(NodeWrapper::unwrap)
|
||||
.map(MessageGroupNode::getCode)
|
||||
.map(NodeWrapper::getNodeCode)
|
||||
.collect(toSet());
|
||||
return collectTemplates(groupNodeCodes);
|
||||
}
|
||||
|
||||
@ -42,4 +42,8 @@ public class NodeWrapper implements NodeValue {
|
||||
return node;
|
||||
}
|
||||
|
||||
public String getNodeCode() {
|
||||
return node.getCode();
|
||||
}
|
||||
|
||||
}
|
||||
@ -31,7 +31,7 @@ public class RootNodeWrapper {
|
||||
public RootNodeWrapper(RootNode<NodeWrapper> 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<ValueNode<NodeWrapper>> findValueNode(String groupNodeCode) {
|
||||
@ -72,7 +72,7 @@ public class RootNodeWrapper {
|
||||
//noinspection unchecked
|
||||
ValueNode<NodeWrapper> 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);
|
||||
|
||||
@ -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<Todo> stats;
|
||||
private final GroupTemplates groupTemplates;
|
||||
|
||||
int getCount(ValueNode<NodeWrapper> node, TodoType todoType) {
|
||||
return getCount(Collections.singletonList(node), todoType);
|
||||
}
|
||||
|
||||
int getCount(List<ValueNode<NodeWrapper>> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<List<Long>> ouCollector = Ref.create();
|
||||
OuInfo ouInfo = OuInfo.create(request.getOuId(), request.getAppTerminalType(), request.determineToDoType());
|
||||
LambdaQueryWrapper<Todo> query = todoQuery(ouInfo, ouCollector)
|
||||
OuInfo ouInfo = new OuInfo(request.getOuId(), request.getAppTerminalType());
|
||||
LambdaQueryWrapper<Todo> query = todoQuery(query(Todo.class), ouInfo, ouCollector)
|
||||
// 查询的待办类型: COPIED_TO_ME, EXECUTABLE
|
||||
.eq(Todo::getType, request.determineToDoType())
|
||||
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
||||
@ -256,40 +255,26 @@ public class TodoRangeQueryService {
|
||||
|
||||
private PendingMessageStatisticResponseV2 countStatGroupedImpl(MessageGroupNodeStatisticParam request) {
|
||||
List<ValueNode<NodeWrapper>> nodes = determineStatNodes(request);
|
||||
Function<ValueNode<NodeWrapper>, GroupStat> nodeStatFun = valueNode -> {
|
||||
int executableCount = countStatByNode(request, valueNode, TodoType.EXECUTABLE);
|
||||
int copiedToMeCount = countStatByNode(request, valueNode, TodoType.COPIED_TO_ME);
|
||||
try {
|
||||
LeafNodeStats nodeStats = countStatByNodes(request, nodes);
|
||||
List<GroupStat> stats = new ArrayList<>();
|
||||
for (ValueNode<NodeWrapper> 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(executableCount, copiedToMeCount));
|
||||
return groupStat;
|
||||
};
|
||||
try {
|
||||
groupStat.setStat(new Stat(
|
||||
nodeStats.getCount(valueNode, TodoType.EXECUTABLE),
|
||||
nodeStats.getCount(valueNode, TodoType.COPIED_TO_ME)));
|
||||
}
|
||||
PendingMessageStatisticResponseV2 resp = new PendingMessageStatisticResponseV2();
|
||||
// 先提交任务
|
||||
CompletableFuture<Integer> uniqueExecutableCountFuture = CompletableFuture.supplyAsync(
|
||||
() -> countStatByNodes(request, nodes, TodoType.EXECUTABLE), statExecutor);
|
||||
CompletableFuture<Integer> uniqueCopiedToMeCountFuture = CompletableFuture.supplyAsync(
|
||||
() -> countStatByNodes(request, nodes, TodoType.COPIED_TO_ME), statExecutor);
|
||||
// 用一个偏大的线程池, IO密集型任务, commonPool不够用
|
||||
// 提交任务并获取结果
|
||||
List<GroupStat> 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<NodeWrapper> node, TodoType todoType) {
|
||||
return countStatByNodes(request, Collections.singletonList(node), todoType);
|
||||
}
|
||||
|
||||
private int countStatByNodes(MessageGroupNodeStatisticParam request,
|
||||
List<ValueNode<NodeWrapper>> nodes, TodoType todoType) {
|
||||
private LeafNodeStats countStatByNodes(MessageGroupNodeStatisticParam request, List<ValueNode<NodeWrapper>> nodes) {
|
||||
List<String> 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<Todo> query = todoQuery(ouInfo)
|
||||
return new LeafNodeStats(Collections.emptyList(), groupTemplates);
|
||||
LambdaQueryWrapper<Todo> query = new QueryWrapper<Todo>()
|
||||
// 1. group by bellow
|
||||
.select("COUNT(*) AS count, template_code, type")
|
||||
.lambda();
|
||||
OuInfo ouInfo = new OuInfo(request.getOuId(), request.getTerminalType());
|
||||
//noinspection unchecked
|
||||
List<Todo> 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<ValueNode<NodeWrapper>> determineStatNodes(MessageGroupNodeStatisticParam request) {
|
||||
@ -358,11 +341,8 @@ public class TodoRangeQueryService {
|
||||
.likeRight(matchBusinessTitle, TodoBusiness::getTitle, title));
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<Todo> todoQuery(OuInfo info) {
|
||||
return todoQuery(info, null);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<Todo> todoQuery(OuInfo info, @Nullable Ref<List<Long>> ouCollector) {
|
||||
private LambdaQueryWrapper<Todo> todoQuery(LambdaQueryWrapper<Todo> query,
|
||||
OuInfo info, @Nullable Ref<List<Long>> ouCollector) {
|
||||
List<Long> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Todo> {
|
||||
|
||||
// !! helper
|
||||
|
||||
/**
|
||||
* 辅助字段, 用于统计数量
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private int count;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user