Merge branch 'refs/heads/master' into feature/REQ-2453
This commit is contained in:
commit
6a351bc0c0
@ -17,9 +17,11 @@ import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -30,11 +32,10 @@ import java.util.Set;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* 待办消息的相关业务类配置
|
||||
* @author cold_blade
|
||||
* @date 2023/8/31
|
||||
* @version 1.0
|
||||
* @description 待办消息的相关业务类配置
|
||||
* @date 2023/8/31
|
||||
*/
|
||||
@Setter
|
||||
@RefreshScope
|
||||
@ -141,7 +142,7 @@ public class PendingMessageBizConfig {
|
||||
private int nodeStatCacheMaxRequestNodeCodeSize = 10;
|
||||
|
||||
@Getter
|
||||
private int nodeStatCacheDataExpireHours = 8;
|
||||
private int nodeStatCacheDataExpireHours = 48;
|
||||
|
||||
public boolean determineOldMsgStatCacheOn() {
|
||||
return isOldMsgStatCacheOn();
|
||||
@ -186,11 +187,28 @@ public class PendingMessageBizConfig {
|
||||
}
|
||||
|
||||
public List<Long> fetchMessageGroupTreeNodeIds(AppTerminalTypeEnum appTerminalType) {
|
||||
if (Objects.isNull(appTerminalType)) {
|
||||
return Collections.emptyList();
|
||||
return fetchMessageGroupTreeNodeIds(Collections.singletonList(appTerminalType));
|
||||
}
|
||||
|
||||
public List<Long> fetchMessageGroupTreeNodeIds(List<AppTerminalTypeEnum> terminals) {
|
||||
Set<Long> nodeIds = new HashSet<>();
|
||||
for (AppTerminalTypeEnum terminal : terminals) {
|
||||
List<Long> ids = msgGroupConfig.getOrDefault(terminal, Collections.emptyList());
|
||||
nodeIds.addAll(ids);
|
||||
}
|
||||
// TODO: [cold_blade] [P2] 需要配置各个应用终端映射的业务中心分类的结点id
|
||||
return msgGroupConfig.getOrDefault(appTerminalType, Collections.emptyList());
|
||||
return new ArrayList<>(nodeIds);
|
||||
}
|
||||
|
||||
public Map<Long, AppTerminalTypeEnum> fetchNodeId2Terminal() {
|
||||
if (msgGroupConfig == null || msgGroupConfig.isEmpty())
|
||||
return Collections.emptyMap();
|
||||
Map<Long, AppTerminalTypeEnum> nodeId2Terminal = new HashMap<>();
|
||||
for (Map.Entry<AppTerminalTypeEnum, List<Long>> e : msgGroupConfig.entrySet()) {
|
||||
for (Long id : e.getValue()) {
|
||||
nodeId2Terminal.put(id, e.getKey());
|
||||
}
|
||||
}
|
||||
return nodeId2Terminal;
|
||||
}
|
||||
|
||||
@Setter
|
||||
|
||||
@ -24,9 +24,11 @@ import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageQueryRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticForWorkerRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PersonTodoToBeDoneStatRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.RevokePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.SetHideRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdateBusinessFinalBizStateRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdatePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.response.AnalysisPage;
|
||||
@ -35,6 +37,7 @@ import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageSimpleDTO;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponseV2;
|
||||
import cn.axzo.msg.center.service.pending.response.PersonTodoToBeDoneStatResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import cn.azxo.framework.common.model.Page;
|
||||
@ -107,6 +110,20 @@ public class PendingMessageNewController implements PendingMessageClient {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResponse<PersonTodoToBeDoneStatResponse> personTodoToBeDoneStat(PersonTodoToBeDoneStatRequest request) {
|
||||
log.info("personTodoToBeDoneStat, request={}", JSON.toJSONString(request));
|
||||
PersonTodoToBeDoneStatResponse resp = todoRangeQueryService.personTodoToBeDoneStat(request);
|
||||
return CommonResponse.success(resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResponse<Integer> handover(TodoHandoverRequest request) {
|
||||
log.info("handover, request={}", JSON.toJSONString(request));
|
||||
int count = todoManager.handover(request);
|
||||
return CommonResponse.success(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResponse<List<PendingMessageResponse>> getPendingMessageByAppWorker(PendingMessageFixedTemplatePageRequest request) {
|
||||
log.info("getPendingMessageByAppWorker, request={}", JSON.toJSONString(request));
|
||||
|
||||
@ -10,7 +10,6 @@ import cn.axzo.msg.center.domain.entity.MessageTemplateGroup;
|
||||
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
|
||||
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
|
||||
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.MessageGroupCategoryEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -21,6 +20,7 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
@ -52,22 +52,20 @@ public class GroupTemplateService {
|
||||
return new RootNodeWrapper(TreeBuilder.build(nodes, true));
|
||||
}
|
||||
|
||||
public List<ValueNode<NodeWrapper>> getTodoGroups(AppTerminalTypeEnum terminal) {
|
||||
Set<Long> configuredIds = new HashSet<>(cfg.fetchMessageGroupTreeNodeIds(terminal));
|
||||
ArrayList<ValueNode<NodeWrapper>> configuredNodes = new ArrayList<>();
|
||||
getGroupRoot().unwrap().walkDown(node -> {
|
||||
//noinspection unchecked
|
||||
ValueNode<NodeWrapper> valueNode = node.tryCast(ValueNode.class);
|
||||
if (valueNode == null)
|
||||
return true;
|
||||
MessageGroupNode groupNode = valueNode.getValue().unwrap();
|
||||
if (!MessageGroupCategoryEnum.PENDING.isGroupCategory(groupNode.getCategory()))
|
||||
return true;
|
||||
if (configuredIds.contains(groupNode.getId()))
|
||||
configuredNodes.add(valueNode);
|
||||
return true;
|
||||
});
|
||||
return configuredNodes;
|
||||
public List<ValueNode<NodeWrapper>> getTodoGroups(AppTerminalTypeEnum terminals) {
|
||||
return getTodoGroups(Collections.singletonList(terminals));
|
||||
}
|
||||
|
||||
public List<ValueNode<NodeWrapper>> getTodoGroups(List<AppTerminalTypeEnum> terminals) {
|
||||
Set<Long> configuredIds = new HashSet<>(cfg.fetchMessageGroupTreeNodeIds(terminals));
|
||||
if (configuredIds.isEmpty())
|
||||
return Collections.emptyList();
|
||||
RootNodeWrapper root = getGroupRoot();
|
||||
return configuredIds.stream()
|
||||
.map(root::findValueNode)
|
||||
.map(nodeOpt -> nodeOpt.orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public List<ValueNode<NodeWrapper>> collectLeafNodes(List<ValueNode<NodeWrapper>> nodes) {
|
||||
@ -137,4 +135,23 @@ public class GroupTemplateService {
|
||||
return new GroupTemplates(root, groups);
|
||||
}
|
||||
|
||||
public TemplateGroups getTemplateGroups(Collection<String> templateCodes) {
|
||||
if (CollectionUtils.isEmpty(templateCodes))
|
||||
return new TemplateGroups();
|
||||
List<MessageTemplateGroup> groups = messageTemplateGroupDao.lambdaQuery()
|
||||
.eq(MessageTemplateGroup::getIsDelete, 0)
|
||||
.in(MessageTemplateGroup::getTemplateCode, templateCodes)
|
||||
.list();
|
||||
if (CollectionUtils.isEmpty(groups))
|
||||
return new TemplateGroups();
|
||||
ArrayList<String> nodeCodes = new ArrayList<>();
|
||||
for (MessageTemplateGroup group : groups)
|
||||
nodeCodes.addAll(group.getPathSegments());
|
||||
List<MessageGroupNode> nodes = messageGroupNodeDao.lambdaQuery()
|
||||
.eq(MessageGroupNode::getIsDelete, 0)
|
||||
.in(MessageGroupNode::getCode, nodeCodes)
|
||||
.list();
|
||||
return new TemplateGroups(groups, nodes);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
package cn.axzo.msg.center.message.service.group;
|
||||
|
||||
import cn.axzo.maokai.api.util.Ref;
|
||||
import cn.axzo.maokai.api.vo.response.tree.Node;
|
||||
import cn.axzo.maokai.api.vo.response.tree.RootNode;
|
||||
import cn.axzo.maokai.api.vo.response.tree.ValueNode;
|
||||
import cn.axzo.msg.center.common.utils.BizAssertions;
|
||||
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -36,6 +38,21 @@ public class RootNodeWrapper {
|
||||
return Optional.ofNullable(code2Node.get(groupNodeCode));
|
||||
}
|
||||
|
||||
public Optional<ValueNode<NodeWrapper>> findValueNode(Long id) {
|
||||
Ref<ValueNode<NodeWrapper>> ref = Ref.create();
|
||||
unwrap().walkDown(node -> {
|
||||
//noinspection unchecked
|
||||
ValueNode<NodeWrapper> valueNode = node.tryCast(ValueNode.class);
|
||||
if (valueNode == null)
|
||||
return true;
|
||||
MessageGroupNode groupNode = valueNode.getValue().unwrap();
|
||||
if (id.equals(groupNode.getId()))
|
||||
ref.set(valueNode);
|
||||
return ref.isNull();
|
||||
});
|
||||
return Optional.ofNullable(ref.get());
|
||||
}
|
||||
|
||||
public List<ValueNode<NodeWrapper>> getNodes(Collection<String> groupNodeCodes) {
|
||||
return groupNodeCodes.stream()
|
||||
.map(this::findValueNode)
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package cn.axzo.msg.center.message.service.group;
|
||||
|
||||
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
|
||||
import cn.axzo.msg.center.domain.entity.MessageTemplateGroup;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class TemplateGroups {
|
||||
|
||||
private final List<MessageTemplateGroup> groups;
|
||||
private final List<MessageGroupNode> nodes;
|
||||
|
||||
TemplateGroups() {
|
||||
this(Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public List<MessageGroupNode> collectPathNodes(String templateCode) {
|
||||
List<String> collectedNodeCodes = groups.stream()
|
||||
.filter(group -> group.getTemplateCode().equals(templateCode))
|
||||
.map(MessageTemplateGroup::getPathSegments)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct()
|
||||
.collect(toList());
|
||||
return collectedNodeCodes.stream()
|
||||
.map(this::findNode)
|
||||
.map(node -> node.orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private Optional<MessageGroupNode> findNode(String nodeCode) {
|
||||
return nodes.stream()
|
||||
.filter(node -> node.getCode().equals(nodeCode))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
}
|
||||
@ -190,7 +190,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
|
||||
}
|
||||
|
||||
private List<GroupTreeNodePathDTO> parseRootNode(GroupTreeNodeDTO root) {
|
||||
List<GroupTreeNodePathDTO> paths = Lists.newArrayList();
|
||||
List<GroupTreeNodePathDTO> paths = new ArrayList<>();
|
||||
List<GroupTreeNodeDTO> stack = new ArrayList<>();
|
||||
parse(root, stack, paths);
|
||||
return paths;
|
||||
@ -212,7 +212,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
|
||||
}
|
||||
|
||||
private List<GroupTreeNodeDTO> reverseStack(LinkedList<GroupTreeNodeDTO> pathNodeStack) {
|
||||
List<GroupTreeNodeDTO> list = Lists.newArrayList();
|
||||
List<GroupTreeNodeDTO> list = new ArrayList<>();
|
||||
for (GroupTreeNodeDTO node : pathNodeStack) {
|
||||
list.add(0, node);
|
||||
}
|
||||
|
||||
@ -997,17 +997,14 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
|
||||
}
|
||||
|
||||
private List<Long> appendExecutorOuIdExpr(LambdaQueryChainWrapper<PendingMessageRecord> query, Long ouId) {
|
||||
List<Long> ouIds = determineOuIds(ouId, true);
|
||||
List<Long> ouIds = determineOuIds(ouId);
|
||||
query.in(PendingMessageRecord::getOuId, ouIds);
|
||||
return ouIds;
|
||||
}
|
||||
|
||||
@NotEmpty
|
||||
public List<Long> determineOuIds(Long ouId, boolean includeZeroOuId) {
|
||||
public List<Long> determineOuIds(Long ouId) {
|
||||
List<Long> ouIds = new ArrayList<>();
|
||||
// 1. 查询 ouId = 0 的数据
|
||||
if (includeZeroOuId)
|
||||
ouIds.add(0L);
|
||||
if (ouId == null)
|
||||
return ouIds;
|
||||
ouIds.add(ouId);
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
package cn.axzo.msg.center.message.service.todo;
|
||||
|
||||
import cn.axzo.msg.center.dal.TodoBusinessDao;
|
||||
import cn.axzo.msg.center.dal.TodoDao;
|
||||
import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.domain.entity.TodoBusiness;
|
||||
import cn.axzo.msg.center.service.pending.request.AnalysisInfo;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
class AnalysisHelper {
|
||||
|
||||
private final TodoBusinessDao todoBusinessDao;
|
||||
private final TodoDao todoDao;
|
||||
|
||||
TodoBusiness getAnalyzeBusiness(AnalysisInfo analysis) {
|
||||
if (analysis == null) return null;
|
||||
return todoBusinessDao.getById(analysis.getBusinessId());
|
||||
}
|
||||
|
||||
Todo getAnalyzeTodo(PendingMessagePageRequest request, AnalysisInfo analysis) {
|
||||
if (analysis.getTodoId() != null)
|
||||
return todoDao.getById(analysis.getTodoId());
|
||||
if (analysis.getTodoCode() != null)
|
||||
return todoDao.lambdaQuery()
|
||||
.eq(Todo::getIdentityCode, analysis.getTodoCode())
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
if (analysis.getBizCode() != null) {
|
||||
return todoDao.lambdaQuery()
|
||||
.eq(Todo::getBizCode, analysis.getBizCode())
|
||||
.eq(Todo::getExecutorPersonId, request.getPersonId())
|
||||
.last("LIMIT 1")
|
||||
.one();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,6 +4,8 @@ import cn.axzo.basics.common.exception.ServiceException;
|
||||
import cn.axzo.maokai.api.util.Ref;
|
||||
import cn.axzo.maokai.api.vo.response.tree.ValueNode;
|
||||
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
|
||||
import cn.axzo.msg.center.dal.TodoBusinessDao;
|
||||
import cn.axzo.msg.center.dal.TodoBusinesses;
|
||||
import cn.axzo.msg.center.dal.TodoDao;
|
||||
import cn.axzo.msg.center.dal.mapper.TodoBusinessMapper;
|
||||
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
|
||||
@ -16,19 +18,25 @@ import cn.axzo.msg.center.message.service.group.GroupTemplateService;
|
||||
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;
|
||||
import cn.axzo.msg.center.message.service.todo.manage.TodoTerminalHelper;
|
||||
import cn.axzo.msg.center.message.service.todo.mybatis.CollectSQLInterceptor;
|
||||
import cn.axzo.msg.center.message.service.todo.mybatis.CollectSQLInterceptor.CollectSqlConfig;
|
||||
import cn.axzo.msg.center.message.service.todo.pagequery.PageQuerySort;
|
||||
import cn.axzo.msg.center.message.service.todo.queryanalyze.SimpleAnalyzer;
|
||||
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
|
||||
import cn.axzo.msg.center.service.enums.TodoType;
|
||||
import cn.axzo.msg.center.service.pending.request.AnalysisInfo;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest.CopiedToMeParam;
|
||||
import cn.axzo.msg.center.service.pending.request.PersonTodoToBeDoneStatRequest;
|
||||
import cn.axzo.msg.center.service.pending.response.AnalysisPage;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponseV2;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponseV2.GroupStat;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponseV2.Stat;
|
||||
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.metadata.IPage;
|
||||
@ -44,6 +52,7 @@ import java.time.LocalDateTime;
|
||||
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;
|
||||
@ -64,6 +73,7 @@ import static java.util.stream.Collectors.toSet;
|
||||
@RequiredArgsConstructor
|
||||
public class TodoRangeQueryService {
|
||||
|
||||
private final TodoBusinessDao todoBusinessDao;
|
||||
private final TodoBusinessMapper todoBusinessMapper;
|
||||
private final TodoDao todoDao;
|
||||
private final PendingMessageNewServiceImpl pendingMessageNewServiceImpl;
|
||||
@ -71,7 +81,9 @@ public class TodoRangeQueryService {
|
||||
private final AnalysisConfig analysisConfig;
|
||||
private final GroupTemplateService groupTemplateService;
|
||||
private final NodeStatCache nodeStatCache;
|
||||
private final ForkJoinPool statExecutor = new ForkJoinPool(20);
|
||||
private final ForkJoinPool statExecutor = new ForkJoinPool(80);
|
||||
private final TodoTerminalHelper todoTerminalHelper;
|
||||
private final AnalysisHelper analysisHelper;
|
||||
|
||||
// !! page query
|
||||
|
||||
@ -88,6 +100,8 @@ public class TodoRangeQueryService {
|
||||
pageResult = pageQueryTodo(request, templateCodes);
|
||||
List<PendingMessageResponse> messages = todoRespBuilder
|
||||
.convertAdapter2MessageResponse(pageResult.getList(), request.getTerminalType());
|
||||
if (request.determineQueryTemplateTerminals())
|
||||
todoTerminalHelper.populateTemplateTerminals(messages);
|
||||
messages.forEach(message -> message.setQueryType(request.getQueryType()));
|
||||
AnalysisPage<PendingMessageResponse> respResult = pageResult.copyAs(messages);
|
||||
respResult.addAnalysis(pageResult.getAnalysis());
|
||||
@ -109,7 +123,7 @@ public class TodoRangeQueryService {
|
||||
.in(TodoBusiness::getTemplateCode, templateCodes)
|
||||
.eq(request.getBizFinalState() != null, TodoBusiness::getBizFinalState, request.getBizFinalState());
|
||||
PageQuerySort.TODO_BUSINESS.appendSortExpr(request, query);
|
||||
return queryAndAnalysis(getAnalyzeBusiness(request), () -> {
|
||||
return queryAndAnalysis(request, () -> {
|
||||
IPage<TodoBusiness> page = todoBusinessMapper.selectPage(request.toPage(), query);
|
||||
List<PendingRecordAdapter> messages = todoRespBuilder.buildBusinessAdapters(page.getRecords());
|
||||
return createAnalysisResult(request.getAnalysisToken(), page, messages);
|
||||
@ -157,7 +171,7 @@ public class TodoRangeQueryService {
|
||||
// 用于查询的模版会通过一个单独的字段打印出来, 用于排查问题
|
||||
.in(Todo::getTemplateCode, templateCodes);
|
||||
PageQuerySort.TODO.appendSortExpr(request, query);
|
||||
return queryAndAnalysis(getAnalyzeTodo(request), () -> {
|
||||
return queryAndAnalysis(request, () -> {
|
||||
IPage<Todo> page = todoDao.page(request.toPage(), query);
|
||||
List<PendingRecordAdapter> messages = todoRespBuilder.buildTodoAdapters(page.getRecords());
|
||||
return createAnalysisResult(request.getAnalysisToken(), page, messages);
|
||||
@ -165,20 +179,28 @@ public class TodoRangeQueryService {
|
||||
}
|
||||
|
||||
private AnalysisPage<PendingRecordAdapter> queryAndAnalysis(
|
||||
Object analyzeItem, Supplier<AnalysisPage<PendingRecordAdapter>> pageQuery) {
|
||||
if (analyzeItem != null)
|
||||
CollectSQLInterceptor.enableCollectSQL();
|
||||
PendingMessagePageRequest request, Supplier<AnalysisPage<PendingRecordAdapter>> pageQuery) {
|
||||
AnalysisInfo analysis = request.getA();
|
||||
Object analyzeItem = analysisHelper.getAnalyzeBusiness(analysis);
|
||||
if (analyzeItem == null)
|
||||
analyzeItem = analysisHelper.getAnalyzeTodo(request, analysis);
|
||||
if (analyzeItem != null || analysis.isCollectSql())
|
||||
CollectSQLInterceptor.enableCollectSQL(new CollectSqlConfig(true));
|
||||
try {
|
||||
Ref<String> execSQL = Ref.create();
|
||||
AnalysisPage<PendingRecordAdapter> result = pageQuery.get();
|
||||
execSQL.set(CollectSQLInterceptor.getSQL().orElse(null));
|
||||
// !! analysis
|
||||
result.addAnalysis("analyzeItem", analyzeItem);
|
||||
result.addAnalysis("query", execSQL::get);
|
||||
Object finalAnalyzeItem = analyzeItem;
|
||||
result.addAnalysis("eval", () -> {
|
||||
String sql = execSQL.get();
|
||||
if (sql == null || analyzeItem == null) return null;
|
||||
return new SimpleAnalyzer(sql).analyze(analyzeItem);
|
||||
if (sql == null || finalAnalyzeItem == null) return null;
|
||||
SimpleAnalyzer simpleAnalyzer = new SimpleAnalyzer(sql);
|
||||
HashMap<String, Object> analyzeResult = new HashMap<>();
|
||||
analyzeResult.put("evalItem", finalAnalyzeItem);
|
||||
analyzeResult.put("evalResult", simpleAnalyzer.analyze(finalAnalyzeItem));
|
||||
return analyzeResult;
|
||||
});
|
||||
return result;
|
||||
} finally {
|
||||
@ -195,35 +217,39 @@ public class TodoRangeQueryService {
|
||||
return analysisResult;
|
||||
}
|
||||
|
||||
// !! analyze helper
|
||||
|
||||
private TodoBusiness getAnalyzeBusiness(PendingMessagePageRequest request) {
|
||||
if (request.getBusinessId() != null && request.getBusinessId() > 0)
|
||||
return todoBusinessMapper.selectById(request.getBusinessId());
|
||||
return null;
|
||||
}
|
||||
|
||||
private Todo getAnalyzeTodo(PendingMessagePageRequest request) {
|
||||
if (request.getTodoId() != null && request.getTodoId() > 0)
|
||||
return todoDao.getById(request.getTodoId());
|
||||
else if (StringUtils.isNotBlank(request.getTodoCode()))
|
||||
return todoDao.findTodoByCode(request.getTodoCode()).orElse(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分类获取可见的模版编码
|
||||
*/
|
||||
public List<String> determineVisibleTemplateCodes(PendingMessagePageRequest request) {
|
||||
if (!request.determineGroupNodeCodes().isEmpty())
|
||||
return groupTemplateService.collectTemplateCodes(request.determineGroupNodeCodes());
|
||||
// 获取可见的模版时, 不用定位到叶子节点
|
||||
List<ValueNode<NodeWrapper>> nodes = groupTemplateService.getTodoGroups(request.getAppTerminalType());
|
||||
List<ValueNode<NodeWrapper>> nodes;
|
||||
// 如果没有传端就查询所有端的待办, 用于待办转交
|
||||
if (request.getAppTerminalType() == null)
|
||||
nodes = groupTemplateService.getTodoGroups(AppTerminalTypeEnum.allTerminals());
|
||||
else
|
||||
// 获取可见的模版时, 不用定位到叶子节点
|
||||
nodes = groupTemplateService.getTodoGroups(request.getAppTerminalType());
|
||||
return groupTemplateService.collectTemplateCodes(nodes);
|
||||
}
|
||||
|
||||
// !! stat
|
||||
|
||||
public PersonTodoToBeDoneStatResponse personTodoToBeDoneStat(PersonTodoToBeDoneStatRequest request) {
|
||||
List<Todo> allTodos = todoDao.getToBeDone(request.getPersonId(), request.getOuId());
|
||||
List<Todo> todos = todoTerminalHelper.maybeFilterByTerminal(allTodos, request.getTerminals());
|
||||
TodoBusinesses businesses = todoBusinessDao.getBusinesses(todos);
|
||||
PersonTodoToBeDoneStatResponse resp = new PersonTodoToBeDoneStatResponse();
|
||||
for (BizCategoryEnum category : BizCategoryEnum.values()) {
|
||||
List<Todo> categoryTodos = businesses.getTodoByCategory(category);
|
||||
resp.addStat(new PersonTodoToBeDoneStatResponse.Stat(category, categoryTodos.size()));
|
||||
}
|
||||
// 有些待办模版可能没有设置分类
|
||||
if (request.isFilterByAllTerminals())
|
||||
resp.setDebugCount(allTodos.size() - todos.size());
|
||||
return resp;
|
||||
}
|
||||
|
||||
public PendingMessageStatisticResponseV2 countStatGrouped(MessageGroupNodeStatisticParam request) {
|
||||
return nodeStatCache.getCacheResponseOrReload(request, () -> countStatGroupedImpl(request));
|
||||
}
|
||||
@ -340,7 +366,7 @@ public class TodoRangeQueryService {
|
||||
List<Long> ouIds = Collections.emptyList();
|
||||
// 工人端不通过ou做查询
|
||||
if (info.terminalType != AppTerminalTypeEnum.C_WORKER_APP) {
|
||||
ouIds = pendingMessageNewServiceImpl.determineOuIds(info.ouId, info.includeZeroOuId);
|
||||
ouIds = pendingMessageNewServiceImpl.determineOuIds(info.ouId);
|
||||
if (ouCollector != null)
|
||||
ouCollector.set(ouIds);
|
||||
}
|
||||
|
||||
@ -19,7 +19,6 @@ class StateAdvanceBuilder {
|
||||
private final LambdaQueryChainWrapper<Todo> query;
|
||||
private final LambdaQueryChainWrapper<Todo> noStateQuery;
|
||||
private final LambdaUpdateChainWrapper<Todo> update;
|
||||
private boolean sendPullNotification = true;
|
||||
|
||||
StateAdvanceBuilder(TodoDao todoDao) {
|
||||
this.query = todoDao.lambdaQuery();
|
||||
@ -66,8 +65,4 @@ class StateAdvanceBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
StateAdvanceBuilder setSendPullNotification(boolean sendPullNotification) {
|
||||
this.sendPullNotification = sendPullNotification;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,31 @@
|
||||
package cn.axzo.msg.center.message.service.todo.manage;
|
||||
|
||||
import cn.axzo.basics.common.BeanMapper;
|
||||
import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum;
|
||||
import cn.axzo.basics.common.exception.ServiceException;
|
||||
import cn.axzo.framework.rocketmq.utils.TraceUtils;
|
||||
import cn.axzo.msg.center.api.mq.PresetButtonPressedMessage;
|
||||
import cn.axzo.msg.center.common.utils.BizAssertions;
|
||||
import cn.axzo.msg.center.dal.TodoBusinessDao;
|
||||
import cn.axzo.msg.center.dal.TodoBusinesses;
|
||||
import cn.axzo.msg.center.dal.TodoDao;
|
||||
import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.domain.entity.TodoBusiness;
|
||||
import cn.axzo.msg.center.domain.entity.TodoHandoverMapping;
|
||||
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
|
||||
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
|
||||
import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO;
|
||||
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
|
||||
import cn.axzo.msg.center.message.service.MessageTemplateNewService;
|
||||
import cn.axzo.msg.center.message.service.todo.manage.broadcast.TodoBroadcaster;
|
||||
import cn.axzo.msg.center.message.service.todo.manage.broadcast.TodoMqBroadcaster;
|
||||
import cn.axzo.msg.center.message.service.todo.manage.event.HandoverEvent;
|
||||
import cn.axzo.msg.center.message.service.todo.manage.event.NewTodoEvent;
|
||||
import cn.axzo.msg.center.mq.MqMessageRecord;
|
||||
import cn.axzo.msg.center.mq.MqMessageType;
|
||||
import cn.axzo.msg.center.mq.MqProducer;
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
|
||||
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
|
||||
import cn.axzo.msg.center.service.enums.TodoType;
|
||||
@ -28,6 +36,7 @@ import cn.axzo.msg.center.service.pending.request.CompletePendingMessageRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.RevokePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.SetHideRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdateBusinessFinalBizStateRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdatePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO;
|
||||
@ -35,7 +44,6 @@ import cn.axzo.msg.center.service.util.JSONUtils;
|
||||
import cn.axzo.msg.center.utils.DateFormatUtil;
|
||||
import cn.axzo.msg.center.utils.QueryFormatter;
|
||||
import cn.axzo.msg.center.utils.UUIDUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||
import com.google.common.base.Supplier;
|
||||
@ -53,11 +61,15 @@ import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
@ -71,6 +83,7 @@ public class TodoManager {
|
||||
private final TodoDao todoDao;
|
||||
private final TodoRecordBuilder todoRecordBuilder;
|
||||
private final TodoLogger todoLogger;
|
||||
private final TodoMqBroadcaster todoMqBroadcaster;
|
||||
private final MessageTemplateNewService messageTemplateNewService;
|
||||
private final MqProducer mqProducer;
|
||||
private final ErrorAssembler errorAssembler;
|
||||
@ -166,6 +179,74 @@ public class TodoManager {
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
// !! handover
|
||||
|
||||
/**
|
||||
* 交接
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Integer handover(TodoHandoverRequest request) {
|
||||
BizAssertions.assertNotBlank(request.getFromPersonName(), "交接来源人自然人姓名不能为空");
|
||||
BizAssertions.assertNotEmpty(request.getTodoIdentityCodes(), "需要转交的待办编码不能为空");
|
||||
List<Todo> candidates = todoDao.getByCodes(request.getTodoIdentityCodes());
|
||||
// 只处理未完成的
|
||||
candidates = candidates.stream()
|
||||
.filter(i -> i.getState() == PendingMessageStateEnum.HAS_BEEN_SENT)
|
||||
.collect(toList());
|
||||
// 只交接业务待办, 审批待办的转交审批自己已经闭环
|
||||
TodoBusinesses businesses = todoBusinessDao.getBusinesses(candidates);
|
||||
List<Todo> srcTodos = businesses.getTodoByCategory(BizCategoryEnum.OTHER);
|
||||
TodoRequestContext parentCtx = TodoRequestContext.create("handover", request)
|
||||
.addLogContent("todoCount", srcTodos.size());
|
||||
// 完成待办
|
||||
List<Long> srcIds = srcTodos.stream()
|
||||
.map(BaseEntityExt::getId)
|
||||
.collect(toList());
|
||||
todoDao.lambdaUpdate()
|
||||
.in(Todo::getId, srcIds)
|
||||
.in(Todo::getState,
|
||||
PendingMessageStateEnum.HAS_BEEN_SENT,
|
||||
PendingMessageStateEnum.PROCESSING)
|
||||
.set(Todo::getState, PendingMessageStateEnum.RETRACT)
|
||||
.update();
|
||||
// 创建新的待办
|
||||
List<Todo> destTodos = new ArrayList<>(srcTodos.size());
|
||||
for (Todo srcTodo : srcTodos) {
|
||||
// 复制待办内容
|
||||
Todo destTodo = BeanMapper.copyBean(srcTodo, Todo.class);
|
||||
destTodos.add(destTodo);
|
||||
// 重置下面的属性
|
||||
destTodo.setId(null);
|
||||
destTodo.setCreateAt(null);
|
||||
destTodo.setUpdateAt(null);
|
||||
destTodo.setIdentityCode(UUIDUtil.uuidString());
|
||||
// 身份id设置为0, 去身份
|
||||
destTodo.setExecutorId(0L);
|
||||
destTodo.setExecutorType(IdentityTypeEnum.NOT_SUPPORT);
|
||||
destTodo.setOuId(request.getToOuId());
|
||||
destTodo.setExecutorPersonId(request.getToPersonId());
|
||||
destTodo.setExecutorName(request.determineToPersonName());
|
||||
}
|
||||
todoDao.saveBatch(destTodos);
|
||||
todoBroadcaster.fireTodoUpdates("handover", srcTodos);
|
||||
todoBroadcaster.fireTodoUpdates("handover", destTodos);
|
||||
// build handover mappings
|
||||
List<TodoHandoverMapping> mappings = new ArrayList<>();
|
||||
for (int i = 0; i < srcTodos.size(); i++) {
|
||||
mappings.add(new TodoHandoverMapping(
|
||||
srcTodos.get(i).getId(),
|
||||
destTodos.get(i).getId()));
|
||||
}
|
||||
// save logs
|
||||
parentCtx.addLogContent("handoverMappings", mappings);
|
||||
todoLogger.logTodosUpdated(parentCtx.copy().addLogContent(
|
||||
"todoAction", "setComplete -> COMPLETED"), srcTodos);
|
||||
todoLogger.logTodosUpdated(parentCtx.copy().addLogContent(
|
||||
"todoAction", "create -> HAS_BEEN_SENT"), destTodos);
|
||||
applicationContext.publishEvent(new HandoverEvent(request, srcTodos, destTodos));
|
||||
return srcTodos.size();
|
||||
}
|
||||
|
||||
// !! complete
|
||||
|
||||
/**
|
||||
@ -383,14 +464,22 @@ public class TodoManager {
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean batchSetProcessing(List<String> identityCodes, List<String> subBizCodes) {
|
||||
log.info("batch set processing...,request:{}", JSONUtil.toJsonStr(identityCodes));
|
||||
BizAssertions.assertTrue(CollectionUtils.isNotEmpty(identityCodes), "identityCodes列表不能为空");
|
||||
BizAssertions.assertTrue(
|
||||
CollectionUtils.isNotEmpty(identityCodes) || CollectionUtils.isNotEmpty(subBizCodes) ,
|
||||
"identityCodes或subBizCodes列表不能为空");
|
||||
List<Todo> todos;
|
||||
if (CollectionUtils.isNotEmpty(identityCodes))
|
||||
todos = todoDao.getByIdentityCodes(identityCodes);
|
||||
else
|
||||
todos = todoDao.getBySubBizCodes(subBizCodes, true);
|
||||
Boolean updated = todoDao.batchSetExecutableProcessing(identityCodes, subBizCodes);
|
||||
if (updated) {
|
||||
List<Todo> updatedTodos = todoDao.getByIdentityCodes(identityCodes);
|
||||
TodoRequestContext ctx = TodoRequestContext.create("batchSetProcessing", identityCodes);
|
||||
todoLogger.logSetTodoProcessing(ctx, updatedTodos);
|
||||
todoBroadcaster.fireTodoUpdates("batchSetProcessing", updatedTodos);
|
||||
HashMap<String, Object> request = new HashMap<>();
|
||||
request.put("identityCodes", identityCodes);
|
||||
request.put("subBizCodes", subBizCodes);
|
||||
TodoRequestContext ctx = TodoRequestContext.create("batchSetProcessing", request);
|
||||
todoLogger.logSetTodoProcessing(ctx, todos);
|
||||
todoBroadcaster.fireTodoUpdates("batchSetProcessing", todos);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
@ -451,6 +540,7 @@ public class TodoManager {
|
||||
// 支持重复发mq消息
|
||||
if (isAdvancedOrCompleted) {
|
||||
sendMqMessageOnPresetButtonPressed(ctx, request, todo);
|
||||
todoBroadcaster.fireTodoUpdates("presetButtonPressed", todo);
|
||||
// 如果不是重复发送, 就只记一条日志. 如果是重复发送, 就单独记录一条日志
|
||||
if (!advanceResult.isAdvanced())
|
||||
todoLogger.logTodoUpdated(ctx, todo);
|
||||
@ -466,18 +556,12 @@ public class TodoManager {
|
||||
*/
|
||||
private void sendMqMessageOnPresetButtonPressed(
|
||||
TodoRequestContext ctx, PresetButtonPressedRequest request, Todo todo) {
|
||||
TodoBusiness business = todoBusinessDao.getById(todo.getTodoBusinessId());
|
||||
PresetButtonPressedMessage message = new PresetButtonPressedMessage();
|
||||
BeanUtils.copyProperties(business, message);
|
||||
BeanUtils.copyProperties(todo, message);
|
||||
message.setBtnPressedRequestNo(ctx.getRequestNo());
|
||||
message.setPresetButtonType(request.getPresetButtonType());
|
||||
message.setPromoterOuId(business.getOuId());
|
||||
message.setPromoterWorkspaceId(business.getOrgId());
|
||||
message.setPromoterWorkspaceName(business.getOrgName());
|
||||
message.setExecutorOuId(todo.getOuId());
|
||||
message.setExecutorWorkspaceId(todo.getOrgId());
|
||||
message.setExecutorWorkspaceName(todo.getOrgName());
|
||||
TodoBusinesses businesses = todoBusinessDao.getBusinesses(todo);
|
||||
message.setTodoInfo(todoMqBroadcaster.createTodoInfo(businesses, todo));
|
||||
ctx.addLogContent("shardingKey", todo.getTemplateCode());
|
||||
try {
|
||||
mqProducer.send(MqMessageRecord
|
||||
@ -553,13 +637,18 @@ public class TodoManager {
|
||||
return new StateAdvanceResult(false, noStateTodos.business, Collections.emptyList());
|
||||
}
|
||||
boolean updated = builder.getUpdate().update();
|
||||
// race condition
|
||||
if (updated && builder.isSendPullNotification())
|
||||
todoBroadcaster.fireTodoUpdates(ctx.getName(), advanceTodos.todos);
|
||||
if (updated)
|
||||
if (updated) {
|
||||
ctx.addLogContent("stateAdvanced", true);
|
||||
else
|
||||
Set<Long> updatedTodoIds = advanceTodos.todos.stream()
|
||||
.map(BaseEntityExt::getId)
|
||||
.collect(toSet());
|
||||
if (!updatedTodoIds.isEmpty()) {
|
||||
List<Todo> updatedTodos = todoDao.listByIds(updatedTodoIds);
|
||||
todoBroadcaster.fireTodoUpdates(ctx.getName(), updatedTodos);
|
||||
}
|
||||
} else {
|
||||
advanceFailLogger.get();
|
||||
}
|
||||
return new StateAdvanceResult(updated, advanceTodos.business, advanceTodos.todos);
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,11 @@ public class TodoRequestContext {
|
||||
addLogContent("request", request);
|
||||
}
|
||||
|
||||
private TodoRequestContext(String name, String requestNo) {
|
||||
this.name = name;
|
||||
this.requestNo = requestNo;
|
||||
}
|
||||
|
||||
public static TodoRequestContext create(String name, Object request) {
|
||||
return new TodoRequestContext(name, UUIDUtil.uuidString(), request);
|
||||
}
|
||||
@ -50,6 +55,12 @@ public class TodoRequestContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TodoRequestContext copy() {
|
||||
TodoRequestContext copy = new TodoRequestContext(name, requestNo);
|
||||
copy.logContents.putAll(logContents);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package cn.axzo.msg.center.message.service.todo.manage;
|
||||
|
||||
import cn.axzo.maokai.api.vo.response.tree.ValueNode;
|
||||
import cn.axzo.msg.center.domain.entity.MessageGroupNode;
|
||||
import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
|
||||
import cn.axzo.msg.center.message.service.group.GroupTemplateService;
|
||||
import cn.axzo.msg.center.message.service.group.NodeWrapper;
|
||||
import cn.axzo.msg.center.message.service.group.TemplateGroups;
|
||||
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TodoTerminalHelper {
|
||||
|
||||
private final GroupTemplateService groupTemplateService;
|
||||
private final PendingMessageBizConfig cfg;
|
||||
|
||||
public List<Todo> maybeFilterByTerminal(List<Todo> todos, @Nullable List<AppTerminalTypeEnum> terminals) {
|
||||
if (CollectionUtils.isEmpty(terminals))
|
||||
return todos;
|
||||
List<ValueNode<NodeWrapper>> todoGroups = groupTemplateService.getTodoGroups(terminals);
|
||||
List<String> templateCodes = groupTemplateService.collectTemplateCodes(todoGroups);
|
||||
return todos.stream()
|
||||
.filter(todo -> templateCodes.contains(todo.getTemplateCode()))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public void populateTemplateTerminals(List<PendingMessageResponse> responses) {
|
||||
if (CollectionUtils.isEmpty(responses)) return;
|
||||
List<String> templateCodes = responses.stream()
|
||||
.map(PendingMessageResponse::getTemplateCode)
|
||||
.collect(toList());
|
||||
TemplateGroups templateGroups = groupTemplateService.getTemplateGroups(templateCodes);
|
||||
Map<Long, AppTerminalTypeEnum> nodeId2Terminal = cfg.fetchNodeId2Terminal();
|
||||
Function<String, List<AppTerminalTypeEnum>> collector = templateCode -> {
|
||||
ArrayList<AppTerminalTypeEnum> terminals = new ArrayList<>();
|
||||
for (MessageGroupNode node : templateGroups.collectPathNodes(templateCode)) {
|
||||
if (nodeId2Terminal.containsKey(node.getId()))
|
||||
terminals.add(nodeId2Terminal.get(node.getId()));
|
||||
}
|
||||
return terminals;
|
||||
};
|
||||
for (PendingMessageResponse response : responses) {
|
||||
List<AppTerminalTypeEnum> terminals = collector.apply(response.getTemplateCode());
|
||||
response.setTemplateTerminals(terminals);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -8,6 +8,7 @@ import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.inside.notices.config.ImMessageProps;
|
||||
import cn.hutool.core.thread.NamedThreadFactory;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
package cn.axzo.msg.center.message.service.todo.manage.event;
|
||||
|
||||
import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Getter
|
||||
public class HandoverEvent extends ApplicationEvent {
|
||||
|
||||
private final TodoHandoverRequest request;
|
||||
private final List<Todo> srcTodos;
|
||||
private final List<Todo> destTodos;
|
||||
|
||||
public HandoverEvent(TodoHandoverRequest request,
|
||||
List<Todo> srcTodos, List<Todo> destTodos) {
|
||||
super(request);
|
||||
this.request = request;
|
||||
this.srcTodos = srcTodos;
|
||||
this.destTodos = destTodos;
|
||||
}
|
||||
|
||||
public int todoCount() {
|
||||
return srcTodos.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
HashMap<String, Object> fields = new HashMap<>();
|
||||
fields.put("request", request);
|
||||
fields.put("srcTodos", srcTodos);
|
||||
fields.put("destTodos", destTodos);
|
||||
fields.put("todoCount", todoCount());
|
||||
return JSON.toJSONString(fields);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
package cn.axzo.msg.center.notices.client.config;
|
||||
package cn.axzo.msg.center.message.service.todo.mybatis;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLLimit;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
|
||||
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
|
||||
import com.baomidou.mybatisplus.core.MybatisParameterHandler;
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.zaxxer.hikari.pool.ProxyStatement;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
@ -20,11 +23,13 @@ import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Slf4j
|
||||
@Intercepts({@Signature(
|
||||
type = StatementHandler.class,
|
||||
method = "prepare",
|
||||
@ -55,7 +60,29 @@ public class BeautifulPaginationInterceptor implements Interceptor, Ordered {
|
||||
if (limitPresent.get())
|
||||
return invocation.proceed();
|
||||
else {
|
||||
return delegate.intercept(invocation);
|
||||
Object result = delegate.intercept(invocation);
|
||||
if (result == null)
|
||||
CollectSQLInterceptor.maybeBuildSqlSelf(() -> tryBuildSql(invocation));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static String tryBuildSql(Invocation invocation) {
|
||||
Connection connection = (Connection) (invocation.getArgs()[0]);
|
||||
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
|
||||
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
|
||||
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
|
||||
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
|
||||
try (PreparedStatement statement = connection.prepareStatement(boundSql.getSql())) {
|
||||
if (!(statement instanceof ProxyStatement))
|
||||
return null;
|
||||
MybatisParameterHandler parameterHandler = new MybatisParameterHandler(
|
||||
mappedStatement, boundSql.getParameterObject(), boundSql);
|
||||
parameterHandler.setParameters(statement);
|
||||
return SQLAccessor.getSql((ProxyStatement) statement);
|
||||
} catch (Exception e) {
|
||||
log.warn("error - tryBuildSql", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import org.apache.ibatis.plugin.Signature;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
@ -21,10 +22,10 @@ import java.util.Optional;
|
||||
})
|
||||
public class CollectSQLInterceptor implements Interceptor {
|
||||
|
||||
private static final ThreadLocal<SQLInfo> LOCAL = new ThreadLocal<>();
|
||||
private static final ThreadLocal<CollectSqlConfig> LOCAL = new ThreadLocal<>();
|
||||
|
||||
public static void enableCollectSQL() {
|
||||
LOCAL.set(new SQLInfo());
|
||||
public static void enableCollectSQL(CollectSqlConfig value) {
|
||||
LOCAL.set(value);
|
||||
}
|
||||
|
||||
public static void disableCollectSQL() {
|
||||
@ -32,22 +33,40 @@ public class CollectSQLInterceptor implements Interceptor {
|
||||
}
|
||||
|
||||
public static Optional<String> getSQL() {
|
||||
SQLInfo info = LOCAL.get();
|
||||
CollectSqlConfig info = LOCAL.get();
|
||||
return info == null ? Optional.empty() : Optional.ofNullable(info.sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Object result = invocation.proceed();
|
||||
SQLInfo info = LOCAL.get();
|
||||
CollectSqlConfig info = LOCAL.get();
|
||||
// collect for first stmt
|
||||
if (info != null && StringUtils.isBlank(info.sql))
|
||||
info.sql = SQLAccessor.getSQL(invocation);
|
||||
if (info != null && StringUtils.isBlank(info.sql)) {
|
||||
try {
|
||||
info.sql = SQLAccessor.getSQL(invocation);
|
||||
} catch (Exception e) {
|
||||
log.warn("error get sql", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void maybeBuildSqlSelf(Supplier<String> provider) {
|
||||
CollectSqlConfig info = LOCAL.get();
|
||||
// collect for first stmt
|
||||
if (info != null && StringUtils.isBlank(info.sql) && info.tryBuildSqlSelf) {
|
||||
try {
|
||||
info.sql = provider.get();
|
||||
} catch (Exception e) {
|
||||
log.warn("error build sql self", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class SQLInfo {
|
||||
public static class CollectSqlConfig {
|
||||
private final boolean tryBuildSqlSelf;
|
||||
private String sql;
|
||||
}
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@ package cn.axzo.msg.center.message.service.todo.mybatis;
|
||||
import com.mysql.cj.jdbc.ClientPreparedStatement;
|
||||
import com.zaxxer.hikari.pool.ProxyStatement;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.plugin.Invocation;
|
||||
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
|
||||
import org.apache.ibatis.plugin.Invocation;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -53,6 +53,15 @@ class SQLAccessor {
|
||||
return ((ClientPreparedStatement) stmt).asSql();
|
||||
}
|
||||
|
||||
public static String getSql(ProxyStatement proxy) throws Exception {
|
||||
Statement stmt = getStmtFromProxy(proxy);
|
||||
if (stmt == null)
|
||||
return null;
|
||||
if (!(stmt instanceof ClientPreparedStatement))
|
||||
return null;
|
||||
return ((ClientPreparedStatement) stmt).asSql();
|
||||
}
|
||||
|
||||
private static Statement getStmtFromProxy(ProxyStatement proxy) {
|
||||
Field delegateField = ReflectionUtils.findField(proxy.getClass(), "delegate");
|
||||
if (delegateField == null) return null;
|
||||
|
||||
@ -22,6 +22,11 @@ public class SqlInterceptorConfig {
|
||||
return new CollectSQLInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeautifulPaginationInterceptor paginationInterceptor2() {
|
||||
return new BeautifulPaginationInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
LogExecSQLInterceptor logExecSQLInterceptor(PendingMessageBizConfig cfg) {
|
||||
return new LogExecSQLInterceptor(cfg);
|
||||
|
||||
@ -40,9 +40,9 @@ public class YoumengTemplateClient {
|
||||
private final MessageBaseTemplateMapper messageBaseTemplateMapper;
|
||||
|
||||
private final ExecutorService executor = new ThreadPoolExecutor(
|
||||
15, 30,
|
||||
30, 50,
|
||||
10, TimeUnit.MINUTES,
|
||||
new ArrayBlockingQueue<>(3000),
|
||||
new ArrayBlockingQueue<>(10000),
|
||||
new NamedThreadFactory(getClass().getSimpleName()));
|
||||
|
||||
public void asyncSend(Long templateId, List<YoumengPush> pushes) {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package cn.axzo.msg.center.api;
|
||||
|
||||
import cn.axzo.msg.center.api.fallback.LoggingFallbackFactory;
|
||||
import cn.axzo.msg.center.api.request.v3.MessageSendReqV3;
|
||||
import cn.axzo.msg.center.api.response.v3.MessageSendRespV3;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
@ -18,18 +17,10 @@ import javax.validation.Valid;
|
||||
@Component
|
||||
@FeignClient(
|
||||
value = "msg-center",
|
||||
url = "${server.serviceUrl:http://msg-center:8080}",
|
||||
fallbackFactory = MessageAPIV3.FallbackFactory.class)
|
||||
url = "${server.serviceUrl:http://msg-center:8080}")
|
||||
public interface MessageAPIV3 {
|
||||
|
||||
@RequestMapping(value = "api/message/v3/send", method = RequestMethod.POST)
|
||||
CommonResponse<MessageSendRespV3> send(@RequestBody @Valid MessageSendReqV3 req);
|
||||
|
||||
class FallbackFactory extends LoggingFallbackFactory {
|
||||
|
||||
public FallbackFactory() {
|
||||
super(MessageAPIV3.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,10 @@
|
||||
package cn.axzo.msg.center.api.mq;
|
||||
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
import cn.axzo.msg.center.service.enums.BizFinalStateEnum;
|
||||
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
|
||||
import cn.axzo.msg.center.service.enums.PresetButtonType;
|
||||
import cn.axzo.msg.center.service.enums.TodoType;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
@ -30,164 +22,9 @@ public class PresetButtonPressedMessage extends MqMessage implements Serializabl
|
||||
* REJECT: 待办驳回
|
||||
*/
|
||||
private PresetButtonType presetButtonType;
|
||||
/**
|
||||
* 消息的唯一标识
|
||||
*/
|
||||
private String identityCode;
|
||||
|
||||
/**
|
||||
* 源模板编码. 比如抄送, 保存审批流的待办模版code
|
||||
* 待办信息
|
||||
*/
|
||||
private String srcTemplateCode;
|
||||
|
||||
/**
|
||||
* 待办内容模板编码. 比如抄送, 保存审批流的待办模版code或抄送的待办模版code
|
||||
*/
|
||||
private String templateCode;
|
||||
|
||||
/**
|
||||
* 关联业务编码
|
||||
*/
|
||||
private String bizCode;
|
||||
|
||||
/**
|
||||
* 流程类待办的流程结点编码
|
||||
*/
|
||||
private String subBizCode;
|
||||
|
||||
/**
|
||||
* 待办类别. EXECUTABLE: 可执行的; COPIED_TO_ME: 抄送给我的
|
||||
*/
|
||||
private TodoType type;
|
||||
|
||||
/**
|
||||
* 待办状态
|
||||
*/
|
||||
private PendingMessageStateEnum state;
|
||||
|
||||
/**
|
||||
* 执行者的自然人ID
|
||||
*/
|
||||
private Long executorPersonId;
|
||||
|
||||
/**
|
||||
* 执行者ID
|
||||
*/
|
||||
private Long executorId;
|
||||
|
||||
/**
|
||||
* 执行者姓名
|
||||
*/
|
||||
private String executorName;
|
||||
|
||||
/**
|
||||
* 请求批次号
|
||||
*/
|
||||
private String requestNo;
|
||||
|
||||
/**
|
||||
* 执行者身份
|
||||
*/
|
||||
private IdentityTypeEnum executorType;
|
||||
|
||||
/**
|
||||
* 消息所属组织类型
|
||||
*/
|
||||
private OrganizationTypeEnum orgType;
|
||||
|
||||
/**
|
||||
* 业务描述eg:流程结点描述
|
||||
*/
|
||||
private String bizDesc;
|
||||
|
||||
/**
|
||||
* 业务标签
|
||||
*/
|
||||
private String bizFlag;
|
||||
|
||||
/**
|
||||
* 消息标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 发起者ID
|
||||
*/
|
||||
private Long promoterId;
|
||||
|
||||
/**
|
||||
* 发起者的自然人ID
|
||||
*/
|
||||
private Long promoterPersonId;
|
||||
|
||||
/**
|
||||
* 发起者姓名
|
||||
*/
|
||||
private String promoterName;
|
||||
|
||||
/**
|
||||
* 发起者身份 WORKER:工人,WORKER_LEADER:班组长,PRACTITIONER:从业人员,REGULATOR:监管人员,OPERATOR:运营人员
|
||||
*/
|
||||
private IdentityTypeEnum promoterType;
|
||||
|
||||
/**
|
||||
* 业务状态
|
||||
*/
|
||||
private BizFinalStateEnum bizFinalState;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private BizCategoryEnum bizCategory;
|
||||
|
||||
/**
|
||||
* 业务扩展参数
|
||||
*/
|
||||
private JSONObject bizExtParam;
|
||||
|
||||
/**
|
||||
* 路由参数
|
||||
*/
|
||||
private JSONObject routerParams;
|
||||
|
||||
/**
|
||||
* 待办的截止时间
|
||||
*/
|
||||
private Date deadline;
|
||||
|
||||
/**
|
||||
* 执行人单位id
|
||||
*/
|
||||
private Long executorOuId;
|
||||
|
||||
/**
|
||||
* 执行人工作台id
|
||||
*/
|
||||
private Long executorWorkspaceId;
|
||||
|
||||
/**
|
||||
* 执行人工作台名称
|
||||
*/
|
||||
private String executorWorkspaceName;
|
||||
|
||||
/**
|
||||
* 发起人单位id
|
||||
*/
|
||||
private Long promoterOuId;
|
||||
|
||||
/**
|
||||
* 发起人工作台id
|
||||
*/
|
||||
private Long promoterWorkspaceId;
|
||||
|
||||
/**
|
||||
* 发起人工作台名称
|
||||
*/
|
||||
private String promoterWorkspaceName;
|
||||
|
||||
private TodoInfo todoInfo;
|
||||
}
|
||||
@ -4,6 +4,9 @@ import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* 应用终端类型枚举
|
||||
@ -38,4 +41,23 @@ public enum AppTerminalTypeEnum {
|
||||
;
|
||||
|
||||
private final String desc;
|
||||
}
|
||||
|
||||
public boolean isManageTerminal() {
|
||||
return this == B_ENTERPRISE_APP || this == CMS_WEB_PC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 管理终端类型列表
|
||||
*/
|
||||
public static List<AppTerminalTypeEnum> manageTerminals() {
|
||||
return Arrays.asList(B_ENTERPRISE_APP, CMS_WEB_PC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 所有终端
|
||||
*/
|
||||
public static List<AppTerminalTypeEnum> allTerminals() {
|
||||
return Arrays.asList(AppTerminalTypeEnum.values());
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,9 +15,11 @@ import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageQueryRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticForWorkerRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PersonTodoToBeDoneStatRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.RevokePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.SetHideRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdateBusinessFinalBizStateRequest;
|
||||
import cn.axzo.msg.center.service.pending.request.UpdatePendingMessageByIdRequest;
|
||||
import cn.axzo.msg.center.service.pending.response.AnalysisPage;
|
||||
@ -26,6 +28,7 @@ import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageSimpleDTO;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponseV2;
|
||||
import cn.axzo.msg.center.service.pending.response.PersonTodoToBeDoneStatResponse;
|
||||
import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import cn.azxo.framework.common.model.Page;
|
||||
@ -54,9 +57,12 @@ public interface PendingMessageClient {
|
||||
/**
|
||||
* 分类统计待办的待处理状态的数量
|
||||
*
|
||||
* <p>用groupStatisticV2替代
|
||||
*
|
||||
* @param request 统计入参
|
||||
* @return 分类信息及其对应的待处理的待办数量列表
|
||||
*/
|
||||
@Deprecated
|
||||
@PostMapping(value = "/pending-message/record/group/statistic", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
CommonResponse<List<PendingMessageStatisticResponse>> groupStatistic(
|
||||
@RequestBody @Valid PendingMessageStatisticRequest request);
|
||||
@ -99,6 +105,21 @@ public interface PendingMessageClient {
|
||||
@PostMapping(value = "/pending-message/record/page", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
CommonResponse<AnalysisPage<PendingMessageResponse>> pageQuery(@RequestBody @Valid PendingMessagePageRequest request);
|
||||
|
||||
/**
|
||||
* 统计个人未完成的待办数量
|
||||
*/
|
||||
@PostMapping(value = "/pending-message/record/personTodoToBeDoneStat", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
CommonResponse<PersonTodoToBeDoneStatResponse> personTodoToBeDoneStat(
|
||||
@RequestBody @Valid PersonTodoToBeDoneStatRequest request);
|
||||
|
||||
/**
|
||||
* 交接未完成的业务待办
|
||||
*
|
||||
* @return 交接的数量
|
||||
*/
|
||||
@PostMapping(value = "/pending-message/record/handover", produces = {MediaType.APPLICATION_JSON_VALUE})
|
||||
CommonResponse<Integer> handover(@RequestBody @Valid TodoHandoverRequest request);
|
||||
|
||||
/**
|
||||
* 指定几种代办模型列表分页查询
|
||||
*
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package cn.axzo.msg.center.service.pending.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
public class AnalysisInfo {
|
||||
|
||||
private String todoCode;
|
||||
private Long todoId;
|
||||
private Long businessId;
|
||||
private String bizCode;
|
||||
private boolean collectSql;
|
||||
|
||||
}
|
||||
@ -142,14 +142,17 @@ public class PendingMessagePageRequest extends PageRequest implements Serializab
|
||||
*/
|
||||
private YesOrNo supportBatchProcess;
|
||||
|
||||
/**
|
||||
* 是否返回待办模版对应的终端列表
|
||||
*/
|
||||
private Boolean queryTemplateTerminals;
|
||||
|
||||
// !! 用于排查问题
|
||||
private String analysisToken;
|
||||
private String todoCode;
|
||||
private Long todoId;
|
||||
private Long businessId;
|
||||
private AnalysisInfo a = new AnalysisInfo();
|
||||
|
||||
public boolean isAnalyzeTodo() {
|
||||
return (todoId != null && todoId > 0) || StringUtils.isNotBlank(todoCode);
|
||||
public boolean determineQueryTemplateTerminals() {
|
||||
return queryTemplateTerminals != null && queryTemplateTerminals;
|
||||
}
|
||||
|
||||
public PendingMessageRoleCategoryEnum determineRoleCategory() {
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.axzo.msg.center.service.pending.request;
|
||||
|
||||
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
public class PersonTodoToBeDoneStatRequest {
|
||||
@NotNull(message = "personId is required")
|
||||
private Long personId;
|
||||
private Long ouId;
|
||||
private List<AppTerminalTypeEnum> terminals;
|
||||
|
||||
public boolean isFilterByAllTerminals() {
|
||||
if (CollectionUtils.isEmpty(terminals)) return false;
|
||||
return new HashSet<>(terminals).equals(new HashSet<>(AppTerminalTypeEnum.allTerminals()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package cn.axzo.msg.center.service.pending.request;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
public class TodoHandoverRequest {
|
||||
|
||||
/**
|
||||
* 交接来源自然人id
|
||||
*/
|
||||
@NotNull(message = "fromPersonId is required")
|
||||
private Long fromPersonId;
|
||||
|
||||
/**
|
||||
* 交接来源人自然人姓名
|
||||
*/
|
||||
@NotBlank(message = "fromPersonName is required")
|
||||
private String fromPersonName;
|
||||
|
||||
/**
|
||||
* 交接来源单位id
|
||||
*/
|
||||
@NotNull(message = "fromOuId is required")
|
||||
private Long fromOuId;
|
||||
|
||||
/**
|
||||
* 交接目标自然人id
|
||||
*/
|
||||
@NotNull(message = "toPersonId is required")
|
||||
private Long toPersonId;
|
||||
|
||||
/**
|
||||
* 交接目标自然人姓名
|
||||
*/
|
||||
private String toPersonName;
|
||||
|
||||
/**
|
||||
* 交接目标单位id
|
||||
*/
|
||||
@NotNull(message = "toOuId is required")
|
||||
private Long toOuId;
|
||||
|
||||
/**
|
||||
* 需要转交的待办编码
|
||||
*/
|
||||
@NotEmpty(message = "identityCodes is required")
|
||||
private List<String> todoIdentityCodes;
|
||||
|
||||
public String determineFromPersonName() {
|
||||
return fromPersonName == null ? "" : fromPersonName;
|
||||
}
|
||||
|
||||
public String determineToPersonName() {
|
||||
return toPersonName == null ? "" : toPersonName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@ import cn.axzo.msg.center.service.dto.ButtonRouterDTO;
|
||||
import cn.axzo.msg.center.service.dto.DetailRouterDTO;
|
||||
import cn.axzo.msg.center.service.dto.IdentityDTO;
|
||||
import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO;
|
||||
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
import cn.axzo.msg.center.service.enums.BizFinalStateEnum;
|
||||
import cn.axzo.msg.center.service.enums.MessageCategoryEnum;
|
||||
@ -15,6 +16,11 @@ import cn.axzo.msg.center.service.enums.TodoQueryType;
|
||||
import cn.axzo.msg.center.service.enums.TodoType;
|
||||
import cn.axzo.msg.center.service.template.response.MessageDetailStyle;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@ -27,10 +33,11 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* 代办消息记录页面展示数模型
|
||||
* @author cold_blade
|
||||
* @version 1.0
|
||||
* @description 代办消息记录页面展示数模型
|
||||
* @date 2023/9/23
|
||||
* @version 1.0
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@ -218,13 +225,18 @@ public class PendingMessageResponse implements Serializable, TodoButtonProvider
|
||||
|
||||
/**
|
||||
* 查询类型
|
||||
* HAS_BEEN_SENT: 待处理
|
||||
* COMPLETED: 已处理
|
||||
* SEND_BY_ME: 我发起
|
||||
* COPIED_TO_ME: 抄送我
|
||||
* HAS_BEEN_SENT: 待处理
|
||||
* COMPLETED: 已处理
|
||||
* SEND_BY_ME: 我发起
|
||||
* COPIED_TO_ME: 抄送我
|
||||
*/
|
||||
private TodoQueryType queryType;
|
||||
|
||||
/**
|
||||
* 待办模版编码配置的终端类型列表
|
||||
*/
|
||||
private List<AppTerminalTypeEnum> templateTerminals;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package cn.axzo.msg.center.service.pending.response;
|
||||
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Data
|
||||
public class PersonTodoToBeDoneStatResponse {
|
||||
|
||||
/**
|
||||
* 统计数据
|
||||
*/
|
||||
private List<Stat> stats = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 排查问题用
|
||||
*/
|
||||
private Integer debugCount;
|
||||
|
||||
public void addStat(Stat stat) {
|
||||
stats.add(stat);
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Stat {
|
||||
|
||||
/**
|
||||
* 分类
|
||||
* FLOW: 流程审批
|
||||
* OTHER: 业务
|
||||
*/
|
||||
private BizCategoryEnum bizCategory;
|
||||
|
||||
/**
|
||||
* 未完成的待办数量
|
||||
*/
|
||||
private Integer pendingCount;
|
||||
|
||||
public String getCategoryName() {
|
||||
return bizCategory.getDesc();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import cn.azxo.framework.common.model.CommonResponse;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -80,6 +81,18 @@ public class BizAssertions {
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotBlank(String content, String message, Object... args) {
|
||||
if (StringUtils.isBlank(content)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertBlank(String content, String message, Object... args) {
|
||||
if (StringUtils.isNotBlank(content)) {
|
||||
throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T assertResponse(CommonResponse<T> response) {
|
||||
return assertResponse(response, "error resp={}", JSON.toJSONString(response));
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
@ -46,16 +47,18 @@ public class TodoBusinessDao extends ServiceImpl<TodoBusinessMapper, TodoBusines
|
||||
}
|
||||
|
||||
public TodoBusinesses getBusinesses(List<Todo> todos) {
|
||||
if (CollectionUtils.isEmpty(todos))
|
||||
return new TodoBusinesses(emptyList(), emptyList());
|
||||
// 获取对应的待办业务
|
||||
Set<Long> businessIds = todos.stream()
|
||||
.map(Todo::getTodoBusinessId)
|
||||
.collect(toSet());
|
||||
return new TodoBusinesses(getByIds(businessIds));
|
||||
return new TodoBusinesses(getByIds(businessIds), todos);
|
||||
}
|
||||
|
||||
private List<TodoBusiness> getByIds(Collection<Long> ids) {
|
||||
if (CollectionUtils.isEmpty(ids))
|
||||
return Collections.emptyList();
|
||||
return emptyList();
|
||||
return lambdaQuery()
|
||||
.in(TodoBusiness::getId, ids)
|
||||
.list();
|
||||
|
||||
@ -3,12 +3,15 @@ package cn.axzo.msg.center.dal;
|
||||
import cn.axzo.msg.center.domain.entity.Todo;
|
||||
import cn.axzo.msg.center.domain.entity.TodoBusiness;
|
||||
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
|
||||
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
@ -17,10 +20,23 @@ import static java.util.stream.Collectors.toMap;
|
||||
public class TodoBusinesses {
|
||||
|
||||
private final Map<Long, TodoBusiness> id2Business;
|
||||
private final List<Todo> todos;
|
||||
|
||||
TodoBusinesses(List<TodoBusiness> businesses) {
|
||||
TodoBusinesses(List<TodoBusiness> businesses, List<Todo> todos) {
|
||||
id2Business = businesses.stream()
|
||||
.collect(toMap(BaseEntityExt::getId, Function.identity()));
|
||||
this.todos = todos;
|
||||
}
|
||||
|
||||
public List<Todo> getTodoByCategory(BizCategoryEnum category) {
|
||||
if (todos == null)
|
||||
return Collections.emptyList();
|
||||
return todos.stream()
|
||||
.filter(todo -> {
|
||||
TodoBusiness business = findBusiness(todo).orElse(null);
|
||||
return business != null && business.getBizCategory() == category;
|
||||
})
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public Optional<TodoBusiness> findBusiness(Todo todo) {
|
||||
@ -32,4 +48,4 @@ public class TodoBusinesses {
|
||||
public Optional<TodoBusiness> findBusiness(Long businessId) {
|
||||
return Optional.ofNullable(id2Business.get(businessId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,25 @@ import static java.util.stream.Collectors.toSet;
|
||||
@Component
|
||||
public class TodoDao extends ServiceImpl<TodoMapper, Todo> {
|
||||
|
||||
public List<Todo> getByCodes(List<String> codes) {
|
||||
if (CollectionUtils.isEmpty(codes))
|
||||
return Collections.emptyList();
|
||||
return lambdaQuery()
|
||||
.in(Todo::getIdentityCode, codes)
|
||||
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
||||
.list();
|
||||
}
|
||||
|
||||
public List<Todo> getToBeDone(Long personId, @Nullable Long ouId) {
|
||||
return lambdaQuery()
|
||||
.eq(Todo::getExecutorPersonId, personId)
|
||||
.eq(ouId != null, Todo::getOuId, ouId)
|
||||
.eq(Todo::getType, TodoType.EXECUTABLE)
|
||||
.eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT)
|
||||
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
||||
.list();
|
||||
}
|
||||
|
||||
public Optional<Todo> findTodoByCode(String todoCode) {
|
||||
if (StringUtils.isBlank(todoCode))
|
||||
return Optional.empty();
|
||||
@ -42,6 +61,17 @@ public class TodoDao extends ServiceImpl<TodoMapper, Todo> {
|
||||
return Optional.ofNullable(todo);
|
||||
}
|
||||
|
||||
public List<Todo> getBySubBizCodes(List<String> subBizCodes, boolean advanceableOnly) {
|
||||
if (CollectionUtils.isEmpty(subBizCodes))
|
||||
return Collections.emptyList();
|
||||
return lambdaQuery()
|
||||
.in(Todo::getSubBizCode, subBizCodes)
|
||||
.in(advanceableOnly, Todo::getState,
|
||||
PendingMessageStateEnum.HAS_BEEN_SENT, PendingMessageStateEnum.PROCESSING)
|
||||
.eq(Todo::getIsDelete, TableIsDeleteEnum.NORMAL.value)
|
||||
.list();
|
||||
}
|
||||
|
||||
public List<Todo> getBySubBizCode(String subBizCode) {
|
||||
return lambdaQuery()
|
||||
.eq(Todo::getSubBizCode, subBizCode)
|
||||
|
||||
@ -7,6 +7,8 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description
|
||||
@ -32,6 +34,10 @@ public class MessageTemplateGroup extends BaseEntityExt<MessageTemplateGroup> im
|
||||
*/
|
||||
private String path;
|
||||
|
||||
public List<String> getPathSegments() {
|
||||
return Arrays.asList(path.split(":"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
|
||||
@ -3,6 +3,7 @@ package cn.axzo.msg.center.domain.entity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
@ -11,11 +12,17 @@ import java.util.Date;
|
||||
public class PendingRecordExt {
|
||||
|
||||
private MigrateOu migrateOu = new MigrateOu();
|
||||
|
||||
/**
|
||||
* 是否是从pending_message_record表迁移的数据
|
||||
*/
|
||||
private Boolean isMigratedFromPendingMessage;
|
||||
|
||||
/**
|
||||
* 交接信息
|
||||
*/
|
||||
private List<TodoHandoverMapping> handoverMappings;
|
||||
|
||||
@Data
|
||||
public static class MigrateOu {
|
||||
// 最原始的ouId
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package cn.axzo.msg.center.domain.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class TodoHandoverMapping {
|
||||
private final Long srcTodoId;
|
||||
private final Long destTodoId;
|
||||
}
|
||||
@ -9,7 +9,6 @@ import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@ -25,8 +24,7 @@ import java.util.Set;
|
||||
public class MybatisInterceptorAnalyzeProcessor implements BeanPostProcessor {
|
||||
|
||||
private static final Set<Class<?>> EXPECTED_INTERCEPTORS = Sets.newHashSet(
|
||||
MybatisPlusCryptInterceptor.class,
|
||||
BeautifulPaginationInterceptor.class);
|
||||
MybatisPlusCryptInterceptor.class);
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(
|
||||
|
||||
@ -21,11 +21,6 @@ import org.springframework.stereotype.Component;
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public BeautifulPaginationInterceptor paginationInterceptor2() {
|
||||
return new BeautifulPaginationInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EntityMetaObjectHandler EntityMetaObjectHandler() {
|
||||
return new EntityMetaObjectHandler();
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.axzo.msg.center.message.service.todo.manage;
|
||||
|
||||
import cn.axzo.msg.center.MsgCenterApplication;
|
||||
import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.Commit;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@SpringBootTest(classes = MsgCenterApplication.class)
|
||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||
class TodoManagerTest {
|
||||
|
||||
private final TodoManager todoManager;
|
||||
|
||||
@Test @Commit
|
||||
void foo() {
|
||||
TodoHandoverRequest request = new TodoHandoverRequest();
|
||||
request.setFromPersonId(16068L);
|
||||
request.setFromOuId(5140L);
|
||||
request.setFromPersonName("假杨林");
|
||||
request.setToPersonId(9000394L);
|
||||
request.setToOuId(5140L);
|
||||
todoManager.handover(request);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package cn.axzo.msg.center.service.pending.client;
|
||||
|
||||
import cn.axzo.msg.center.MsgCenterApplication;
|
||||
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
|
||||
import cn.axzo.msg.center.service.pending.response.AnalysisPage;
|
||||
import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
@SpringBootTest(classes = MsgCenterApplication.class)
|
||||
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||
class PendingMessageClientTest {
|
||||
private final PendingMessageClient pendingMessageClient;
|
||||
|
||||
@Test
|
||||
void foo() {
|
||||
@Language("JSON") String str = "{\n" +
|
||||
" \"personId\": 22375,\n" +
|
||||
" \"roleCategory\": \"EXECUTOR\",\n" +
|
||||
" \"msgState\": \"HAS_BEEN_SENT\",\n" +
|
||||
" \"ouId\": 7314,\n" +
|
||||
" \"queryTemplateTerminals\": true,\n" +
|
||||
" \"a\": {\n" +
|
||||
" \"collectSql\": true\n" +
|
||||
" },\n" +
|
||||
" \"page\": 1,\n" +
|
||||
" \"pageSize\": 100\n" +
|
||||
"}";
|
||||
PendingMessagePageRequest request = JSON.parseObject(str, PendingMessagePageRequest.class);
|
||||
CommonResponse<AnalysisPage<PendingMessageResponse>> analysisPageCommonResponse = pendingMessageClient.pageQuery(request);
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user