优化待办分类统计
This commit is contained in:
parent
bfcf3f3402
commit
1f4540bad7
@ -99,8 +99,7 @@ public class GroupTemplateService {
|
|||||||
public GroupTemplates collectTemplates(List<ValueNode<NodeWrapper>> nodes) {
|
public GroupTemplates collectTemplates(List<ValueNode<NodeWrapper>> nodes) {
|
||||||
Set<String> groupNodeCodes = nodes.stream()
|
Set<String> groupNodeCodes = nodes.stream()
|
||||||
.map(ValueNode::getValue)
|
.map(ValueNode::getValue)
|
||||||
.map(NodeWrapper::unwrap)
|
.map(NodeWrapper::getNodeCode)
|
||||||
.map(MessageGroupNode::getCode)
|
|
||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
return collectTemplates(groupNodeCodes);
|
return collectTemplates(groupNodeCodes);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,4 +42,8 @@ public class NodeWrapper implements NodeValue {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNodeCode() {
|
||||||
|
return node.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ public class RootNodeWrapper {
|
|||||||
public RootNodeWrapper(RootNode<NodeWrapper> root) {
|
public RootNodeWrapper(RootNode<NodeWrapper> root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.code2Node = root.getValueNodes().stream()
|
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) {
|
public Optional<ValueNode<NodeWrapper>> findValueNode(String groupNodeCode) {
|
||||||
@ -72,7 +72,7 @@ public class RootNodeWrapper {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
ValueNode<NodeWrapper> valueNode = current.tryCast(ValueNode.class);
|
ValueNode<NodeWrapper> valueNode = current.tryCast(ValueNode.class);
|
||||||
if (valueNode != null)
|
if (valueNode != null)
|
||||||
paths.add(valueNode.getValue().unwrap().getCode());
|
paths.add(valueNode.getValue().getNodeCode());
|
||||||
current = current.getParent();
|
current = current.getParent();
|
||||||
}
|
}
|
||||||
Collections.reverse(paths);
|
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.domain.persistence.BaseEntityExt;
|
||||||
import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam;
|
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.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.group.NodeWrapper;
|
||||||
import cn.axzo.msg.center.message.service.impl.PendingMessageNewServiceImpl;
|
import cn.axzo.msg.center.message.service.impl.PendingMessageNewServiceImpl;
|
||||||
import cn.axzo.msg.center.message.service.todo.cache.NodeStatCache;
|
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.service.pending.response.PersonTodoToBeDoneStatResponse;
|
||||||
import cn.axzo.msg.center.utils.DateFormatUtil;
|
import cn.axzo.msg.center.utils.DateFormatUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -49,14 +51,12 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
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 java.util.function.Supplier;
|
||||||
|
|
||||||
import static cn.axzo.msg.center.inside.notices.utils.Queries.query;
|
import static cn.axzo.msg.center.inside.notices.utils.Queries.query;
|
||||||
@ -81,7 +81,6 @@ public class TodoRangeQueryService {
|
|||||||
private final AnalysisConfig analysisConfig;
|
private final AnalysisConfig analysisConfig;
|
||||||
private final GroupTemplateService groupTemplateService;
|
private final GroupTemplateService groupTemplateService;
|
||||||
private final NodeStatCache nodeStatCache;
|
private final NodeStatCache nodeStatCache;
|
||||||
private final ForkJoinPool statExecutor = new ForkJoinPool(80);
|
|
||||||
private final TodoTerminalHelper todoTerminalHelper;
|
private final TodoTerminalHelper todoTerminalHelper;
|
||||||
private final AnalysisHelper analysisHelper;
|
private final AnalysisHelper analysisHelper;
|
||||||
|
|
||||||
@ -144,8 +143,8 @@ public class TodoRangeQueryService {
|
|||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
CopiedToMeParam copiedToMeParam = request.getCopiedToMeParam();
|
CopiedToMeParam copiedToMeParam = request.getCopiedToMeParam();
|
||||||
Ref<List<Long>> ouCollector = Ref.create();
|
Ref<List<Long>> ouCollector = Ref.create();
|
||||||
OuInfo ouInfo = OuInfo.create(request.getOuId(), request.getAppTerminalType(), request.determineToDoType());
|
OuInfo ouInfo = new OuInfo(request.getOuId(), request.getAppTerminalType());
|
||||||
LambdaQueryWrapper<Todo> query = todoQuery(ouInfo, ouCollector)
|
LambdaQueryWrapper<Todo> query = todoQuery(query(Todo.class), ouInfo, ouCollector)
|
||||||
// 查询的待办类型: COPIED_TO_ME, EXECUTABLE
|
// 查询的待办类型: COPIED_TO_ME, EXECUTABLE
|
||||||
.eq(Todo::getType, request.determineToDoType())
|
.eq(Todo::getType, request.determineToDoType())
|
||||||
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
||||||
@ -227,7 +226,7 @@ public class TodoRangeQueryService {
|
|||||||
// 如果没有传端就查询所有端的待办, 用于待办转交
|
// 如果没有传端就查询所有端的待办, 用于待办转交
|
||||||
if (request.getAppTerminalType() == null)
|
if (request.getAppTerminalType() == null)
|
||||||
nodes = groupTemplateService.getTodoGroups(AppTerminalTypeEnum.allTerminals());
|
nodes = groupTemplateService.getTodoGroups(AppTerminalTypeEnum.allTerminals());
|
||||||
else
|
else
|
||||||
// 获取可见的模版时, 不用定位到叶子节点
|
// 获取可见的模版时, 不用定位到叶子节点
|
||||||
nodes = groupTemplateService.getTodoGroups(request.getAppTerminalType());
|
nodes = groupTemplateService.getTodoGroups(request.getAppTerminalType());
|
||||||
return groupTemplateService.collectTemplateCodes(nodes);
|
return groupTemplateService.collectTemplateCodes(nodes);
|
||||||
@ -256,40 +255,26 @@ public class TodoRangeQueryService {
|
|||||||
|
|
||||||
private PendingMessageStatisticResponseV2 countStatGroupedImpl(MessageGroupNodeStatisticParam request) {
|
private PendingMessageStatisticResponseV2 countStatGroupedImpl(MessageGroupNodeStatisticParam request) {
|
||||||
List<ValueNode<NodeWrapper>> nodes = determineStatNodes(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);
|
|
||||||
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 {
|
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(
|
||||||
|
nodeStats.getCount(valueNode, TodoType.EXECUTABLE),
|
||||||
|
nodeStats.getCount(valueNode, TodoType.COPIED_TO_ME)));
|
||||||
|
}
|
||||||
PendingMessageStatisticResponseV2 resp = new PendingMessageStatisticResponseV2();
|
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.addGroupStats(stats);
|
||||||
// 一个端可能会有重复分类
|
// 一个端可能会有重复分类
|
||||||
resp.setTotalStat();
|
resp.setTotalStat();
|
||||||
CompletableFuture.allOf(uniqueExecutableCountFuture, uniqueCopiedToMeCountFuture).join();
|
// 为了性能, 不做去重的统计了
|
||||||
// 一个端不包含重复分类
|
resp.setUniqueTotalStat(new Stat(-1, -1));
|
||||||
int uniqueExecutableCount = uniqueExecutableCountFuture.get();
|
|
||||||
int uniqueCopiedToMeCount = uniqueCopiedToMeCountFuture.get();
|
|
||||||
resp.setUniqueTotalStat(new Stat(uniqueExecutableCount, uniqueCopiedToMeCount));
|
|
||||||
return resp;
|
return resp;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("分类统计异常, request={}", request, e);
|
log.warn("分类统计异常, request={}", request, e);
|
||||||
@ -307,28 +292,26 @@ public class TodoRangeQueryService {
|
|||||||
return totalStat == null ? 0 : totalStat.getPendingCount();
|
return totalStat == null ? 0 : totalStat.getPendingCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countStatByNode(MessageGroupNodeStatisticParam request,
|
private LeafNodeStats countStatByNodes(MessageGroupNodeStatisticParam request, List<ValueNode<NodeWrapper>> nodes) {
|
||||||
ValueNode<NodeWrapper> node, TodoType todoType) {
|
|
||||||
return countStatByNodes(request, Collections.singletonList(node), todoType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countStatByNodes(MessageGroupNodeStatisticParam request,
|
|
||||||
List<ValueNode<NodeWrapper>> nodes, TodoType todoType) {
|
|
||||||
List<String> templateCodes = groupTemplateService.collectTemplateCodes(nodes);
|
List<String> templateCodes = groupTemplateService.collectTemplateCodes(nodes);
|
||||||
|
GroupTemplates groupTemplates = groupTemplateService.collectTemplates(nodes);
|
||||||
if (CollectionUtils.isEmpty(templateCodes))
|
if (CollectionUtils.isEmpty(templateCodes))
|
||||||
return 0;
|
return new LeafNodeStats(Collections.emptyList(), groupTemplates);
|
||||||
OuInfo ouInfo = OuInfo.create(request.getOuId(), request.getTerminalType(), todoType);
|
LambdaQueryWrapper<Todo> query = new QueryWrapper<Todo>()
|
||||||
LambdaQueryWrapper<Todo> query = todoQuery(ouInfo)
|
// 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)
|
.in(Todo::getTemplateCode, templateCodes)
|
||||||
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
||||||
.in(CollectionUtils.isNotEmpty(request.getWorkspaceIds()), Todo::getOrgId, request.getWorkspaceIds())
|
.in(CollectionUtils.isNotEmpty(request.getWorkspaceIds()), Todo::getOrgId, request.getWorkspaceIds())
|
||||||
.and(todoType == TodoType.EXECUTABLE, nested -> nested
|
.in(Todo::getType, TodoType.EXECUTABLE, TodoType.COPIED_TO_ME)
|
||||||
.eq(Todo::getType, TodoType.EXECUTABLE)
|
.in(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT, PendingMessageStateEnum.CREATED)
|
||||||
.eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT))
|
// 2. group by here
|
||||||
.and(todoType == TodoType.COPIED_TO_ME, nested -> nested
|
.groupBy(Todo::getTemplateCode, Todo::getType));
|
||||||
.eq(Todo::getType, TodoType.COPIED_TO_ME)
|
return new LeafNodeStats(stats, groupTemplates);
|
||||||
.eq(Todo::getState, PendingMessageStateEnum.CREATED));
|
|
||||||
return todoDao.count(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ValueNode<NodeWrapper>> determineStatNodes(MessageGroupNodeStatisticParam request) {
|
public List<ValueNode<NodeWrapper>> determineStatNodes(MessageGroupNodeStatisticParam request) {
|
||||||
@ -358,11 +341,8 @@ public class TodoRangeQueryService {
|
|||||||
.likeRight(matchBusinessTitle, TodoBusiness::getTitle, title));
|
.likeRight(matchBusinessTitle, TodoBusiness::getTitle, title));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<Todo> todoQuery(OuInfo info) {
|
private LambdaQueryWrapper<Todo> todoQuery(LambdaQueryWrapper<Todo> query,
|
||||||
return todoQuery(info, null);
|
OuInfo info, @Nullable Ref<List<Long>> ouCollector) {
|
||||||
}
|
|
||||||
|
|
||||||
private LambdaQueryWrapper<Todo> todoQuery(OuInfo info, @Nullable Ref<List<Long>> ouCollector) {
|
|
||||||
List<Long> ouIds = Collections.emptyList();
|
List<Long> ouIds = Collections.emptyList();
|
||||||
// 工人端不通过ou做查询
|
// 工人端不通过ou做查询
|
||||||
if (info.terminalType != AppTerminalTypeEnum.C_WORKER_APP) {
|
if (info.terminalType != AppTerminalTypeEnum.C_WORKER_APP) {
|
||||||
@ -372,7 +352,7 @@ public class TodoRangeQueryService {
|
|||||||
}
|
}
|
||||||
// 1. 查询 ouId = 0 的数据
|
// 1. 查询 ouId = 0 的数据
|
||||||
// 2. 查询ouId下面所有的平台班组id当成ouId
|
// 2. 查询ouId下面所有的平台班组id当成ouId
|
||||||
return query(Todo.class)
|
return query
|
||||||
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
||||||
.in(CollectionUtils.isNotEmpty(ouIds), Todo::getOuId, ouIds);
|
.in(CollectionUtils.isNotEmpty(ouIds), Todo::getOuId, ouIds);
|
||||||
}
|
}
|
||||||
@ -381,11 +361,6 @@ public class TodoRangeQueryService {
|
|||||||
private static class OuInfo {
|
private static class OuInfo {
|
||||||
final Long ouId;
|
final Long ouId;
|
||||||
final AppTerminalTypeEnum terminalType;
|
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 lombok.Setter;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author yanglin
|
* @author yanglin
|
||||||
@ -167,6 +166,12 @@ public class Todo extends BaseEntityExt<Todo> {
|
|||||||
|
|
||||||
// !! helper
|
// !! helper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 辅助字段, 用于统计数量
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private int count;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return JSON.toJSONString(this);
|
return JSON.toJSONString(this);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user