Merge branch 'refs/heads/master' into feature/REQ-2453

This commit is contained in:
yanglin 2024-07-05 11:35:25 +08:00
commit 6a351bc0c0
41 changed files with 969 additions and 297 deletions

View File

@ -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

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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);
/**
* 指定几种代办模型列表分页查询
*

View File

@ -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;
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
/**
* 业务类型
*/

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -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();

View File

@ -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));
}
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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(

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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();
}
}