Merge branch 'feature/REQ-1507' of axzsource.com:universal/infrastructure/backend/msg-center-plat into dev

This commit is contained in:
luofu 2023-11-13 17:34:10 +08:00
commit e132d8a92a
19 changed files with 216 additions and 47 deletions

View File

@ -7,6 +7,7 @@ import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.message.service.PendingMessageNewService;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.service.pending.client.PendingMessageClient;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
@ -44,6 +45,12 @@ public class PendingMessageNewController implements PendingMessageClient {
);
}
@Override
public CommonResponse<Integer> countUncompleted(PendingMessageCountUncompletedRequest request) {
Integer count = pendingMessageNewService.countUncompleted(MessageGroupNodeStatisticParam.from(request));
return CommonResponse.success(count);
}
@Override
public CommonResponse<Page<PendingMessageResponse>> pageQuery(PendingMessagePageRequest request) {
return CommonResponse.success(pendingMessageNewService.pageQuery(request));

View File

@ -32,6 +32,10 @@ public class GroupTreeNodePathDTO implements Serializable {
* 结点编码
*/
private String nodeCode;
/**
* 结点名称
*/
private String nodeName;
/**
* 结点名称格式的路径
*/
@ -50,10 +54,10 @@ public class GroupTreeNodePathDTO implements Serializable {
return nodeCodes[nodeCodes.length - 1];
}
public static GroupTreeNodePathDTO of(String nodeCode, Collection<GroupTreeNodeDTO> nodes) {
public static GroupTreeNodePathDTO of(GroupTreeNodeDTO node, Collection<GroupTreeNodeDTO> nodes) {
String nodeNamePath = formatPath(GroupTreeNodeDTO::getNodeName, nodes, NODE_NAME_PATH_SPLITER);
String nodeCodePath = formatPath(GroupTreeNodeDTO::getNodeCode, nodes, NODE_CODE_PATH_SPLITER);
return new GroupTreeNodePathDTO(nodeCode, nodeNamePath, nodeCodePath);
return new GroupTreeNodePathDTO(node.getNodeCode(), node.getNodeName(), nodeNamePath, nodeCodePath);
}
private static String formatPath(Function<GroupTreeNodeDTO, String> pathFunc, Collection<GroupTreeNodeDTO> nodes,

View File

@ -14,7 +14,6 @@ import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.utils.MessageRouterUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -121,7 +120,7 @@ public class MessageTemplateRouterDTO implements Serializable {
.map(e -> MessageRouterConfigDTO.builder().url(e.getUrl()).terminalType(e.getTerminalType()).build())
.collect(Collectors.toList());
return MessageRouteDetailDTO.builder()
.name("详情")
.name(MessageRouterUtil.DETAIL_ROUTER_DESC)
.templateCode(templateCode)
.showStrategy(bizDetailShowStrategy.getShowStrategy())
.routerConfigs(configs)

View File

@ -1,6 +1,5 @@
package cn.axzo.msg.center.message.domain.dto;
import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO;
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponse;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
@ -29,24 +28,29 @@ public class PendingMessageStatisticDTO implements Serializable {
private static final long serialVersionUID = 5171436359992401120L;
/**
* 树结点
* 叶结点编码
*/
private GroupTreeNodeDTO treeNode;
private String leafNodeCode;
/**
* 叶节点名称
*/
private String leafNodeName;
/**
* 结点对应的代办数量
*/
private Integer pendingCount;
public static PendingMessageStatisticDTO of(GroupTreeNodeDTO groupNode) {
public static PendingMessageStatisticDTO of(GroupTreeNodePathDTO groupNode) {
return PendingMessageStatisticDTO.builder()
.treeNode(groupNode)
.leafNodeName(groupNode.getNodeName())
.leafNodeCode(groupNode.getNodeCode())
.build();
}
public PendingMessageStatisticResponse toResponse() {
PendingMessageStatisticResponse response = new PendingMessageStatisticResponse();
response.setGroupCode(treeNode.getNodeCode());
response.setGroupName(treeNode.getNodeName());
response.setGroupCode(this.leafNodeCode);
response.setGroupName(this.leafNodeName);
response.setPendingCount(this.pendingCount);
return response;
}

View File

@ -4,6 +4,7 @@ import cn.axzo.core.utils.converter.BeanConverter;
import cn.axzo.msg.center.service.dto.IdentityDTO;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
@ -58,6 +59,20 @@ public class MessageGroupNodeStatisticParam implements Serializable {
return param;
}
public static MessageGroupNodeStatisticParam from(PendingMessageCountUncompletedRequest request) {
MessageGroupNodeStatisticParam param = BeanConverter.convert(request, MessageGroupNodeStatisticParam.class);
IdentityDTO identity = IdentityDTO.builder()
.id(request.getIdentityId())
.type(request.getIdentityType())
.build();
PersonDTO person = PersonDTO.builder()
.id(request.getPersonId())
.identity(identity)
.build();
param.setOperator(person);
return param;
}
@Override
public String toString() {
return JSON.toJSONString(this);

View File

@ -1,5 +1,6 @@
package cn.axzo.msg.center.message.service;
import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO;
import cn.axzo.msg.center.message.domain.param.MessageGroupNodeSaveOrUpdateParam;
import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
@ -37,12 +38,20 @@ public interface MessageGroupNodeService {
Map<String, String> leafGroupNodeCodePaths(Collection<String> leafGroupNodeCodes);
/**
* 根据结点编码查询结点信息
* 获取分类结点叶结点编码的路径
*
* @param rootNodeCode 结点编码
* @return 结点信息
* @param leafNodeCode 分类结点根结点编码
* @return 分类结点叶结点编码的路径
*/
Optional<GroupTreeNodeDTO> queryRootNode(String rootNodeCode);
Optional<GroupTreeNodePathDTO> queryLeafGroupNodeCodePath(String leafNodeCode);
/**
* 获取分类结点叶结点编码的路径
*
* @param rootNodeCodes 分类结点根结点的编码列表
* @return 分类结点叶结点编码的路径
*/
List<GroupTreeNodePathDTO> leafGroupNodeCodePathsByRootNodeCodes(Collection<String> rootNodeCodes);
/**
* 新增结点

View File

@ -46,6 +46,14 @@ public interface MessageGroupTreeNodeCacheService {
*/
Optional<GroupTreeNodePathDTO> queryLeafNodePath(String leafNodeCode);
/**
* 获取根节点对应的所有叶结点对应的树的路径
*
* @param rootNodeCode 根结点编码
* @return 路径
*/
List<GroupTreeNodePathDTO> listLeafNodePaths(String rootNodeCode);
/**
* 刷新缓存
*/

View File

@ -14,12 +14,12 @@ import java.util.Map;
public interface MessageTemplateGroupService {
/**
* 通过分组结点编码查询关联的模板编码列表
* 通过分组结点编码路径查询关联的模板编码列表
*
* @param groupNodeCode 分组结点编码
* @param leafNodePathCodes 分组结点编码路径列表
* @return 模板编码列表
*/
List<String> listMessageTemplateCodes(String groupNodeCode);
List<String> listMessageTemplateCodes(Collection<String> leafNodePathCodes);
/**
* 模板关联分类

View File

@ -1,7 +1,7 @@
package cn.axzo.msg.center.message.service;
import cn.axzo.msg.center.message.domain.dto.PendingMessageStatisticDTO;
import cn.axzo.msg.center.message.domain.dto.PendingMessageDTO;
import cn.axzo.msg.center.message.domain.dto.PendingMessageStatisticDTO;
import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
@ -21,8 +21,22 @@ import java.util.Optional;
*/
public interface PendingMessageNewService {
/**
* 分类统计待办的待处理状态的数量
*
* @param param 统计入参
* @return 分类信息及其对应的待处理的待办数量列表
*/
List<PendingMessageStatisticDTO> groupStatistic(MessageGroupNodeStatisticParam param);
/**
* 统计个人在某个应用终端的待处理的待办数量
*
* @param param 统计入参
* @return 待处理的待办数量
*/
Integer countUncompleted(MessageGroupNodeStatisticParam param);
/**
* 代办列表分页查询
*

View File

@ -76,8 +76,19 @@ public class MessageGroupNodeServiceImpl implements MessageGroupNodeService {
}
@Override
public Optional<GroupTreeNodeDTO> queryRootNode(String rootNodeCode) {
return messageGroupTreeNodeCacheService.queryRootNode(rootNodeCode);
public Optional<GroupTreeNodePathDTO> queryLeafGroupNodeCodePath(String leafNodeCode) {
return messageGroupTreeNodeCacheService.queryLeafNodePath(leafNodeCode);
}
@Override
public List<GroupTreeNodePathDTO> leafGroupNodeCodePathsByRootNodeCodes(Collection<String> rootNodeCodes) {
if (CollectionUtils.isEmpty(rootNodeCodes)) {
log.info("rootNodeCodes is empty.");
return Collections.emptyList();
}
return rootNodeCodes.stream()
.flatMap(e -> messageGroupTreeNodeCacheService.listLeafNodePaths(e).stream())
.collect(Collectors.toList());
}
@Override

View File

@ -82,6 +82,17 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
.findFirst();
}
@Override
public List<GroupTreeNodePathDTO> listLeafNodePaths(String rootNodeCode) {
if (StringUtils.isBlank(rootNodeCode)) {
log.info("leafNodeCode is blank.");
return Collections.emptyList();
}
return getLeafTreeNodePathsCache().stream()
.filter(e -> e.getNodeCodePath().startsWith(rootNodeCode))
.collect(Collectors.toList());
}
@Override
public synchronized void refreshCache() {
// 清除redis中的缓存标识
@ -175,7 +186,7 @@ public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNod
pathNodeStack.push(node);
if (CollectionUtils.isEmpty(node.getNodeChildren())) {
// 当前结点为树的叶结点逻辑上的有可能不是业务维度的叶结点
paths.add(GroupTreeNodePathDTO.of(node.getNodeCode(), reverseStack(pathNodeStack)));
paths.add(GroupTreeNodePathDTO.of(node, reverseStack(pathNodeStack)));
pathNodeStack.pop();
} else {
stack.addAll(node.getNodeChildren());

View File

@ -37,13 +37,13 @@ public class MessageTemplateGroupServiceImpl implements MessageTemplateGroupServ
private final MessageTemplateGroupDao messageTemplateGroupDao;
@Override
public List<String> listMessageTemplateCodes(String groupNodeCode) {
if (StringUtils.isBlank(groupNodeCode)) {
log.info("groupNodeCode is blank.");
public List<String> listMessageTemplateCodes(Collection<String> leafNodePathCodes) {
if (CollectionUtils.isEmpty(leafNodePathCodes)) {
log.info("groupNodePathCodes is empty.");
return Collections.emptyList();
}
return messageTemplateGroupDao.lambdaQuery()
.like(MessageTemplateGroup::getPath, groupNodeCode)
.in(MessageTemplateGroup::getPath, leafNodePathCodes)
.eq(MessageTemplateGroup::getIsDelete, 0)
.list().stream()
.map(MessageTemplateGroup::getTemplateCode)

View File

@ -329,7 +329,10 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService
private IPage<MessageBaseTemplate> pageQueryBaseTemplate(MessageTemplatePageRequest request) {
List<String> templateCodes = Lists.newArrayList();
if (StringUtils.isNotBlank(request.getGroupNodeCode())) {
templateCodes = messageTemplateGroupService.listMessageTemplateCodes(request.getGroupNodeCode());
Optional<GroupTreeNodePathDTO> nodePath = messageGroupNodeService.queryLeafGroupNodeCodePath(request.getGroupNodeCode());
templateCodes = nodePath
.map(v -> messageTemplateGroupService.listMessageTemplateCodes(Lists.newArrayList(v.getNodeCodePath())))
.orElseGet(Collections::emptyList);
if (CollectionUtils.isEmpty(templateCodes)) {
// 入参中的分类没有关联任何模板,直接返回查询结果
return null;

View File

@ -8,6 +8,7 @@ import cn.axzo.msg.center.common.exception.ServiceException;
import cn.axzo.msg.center.common.utils.PlaceholderResolver;
import cn.axzo.msg.center.dal.PendingMessageRecordDao;
import cn.axzo.msg.center.domain.entity.PendingMessageRecord;
import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO;
import cn.axzo.msg.center.message.domain.dto.MessageTemplateRouterDTO;
import cn.axzo.msg.center.message.domain.dto.PendingMessageDTO;
@ -18,7 +19,6 @@ import cn.axzo.msg.center.message.service.MessageGroupNodeService;
import cn.axzo.msg.center.message.service.MessageTemplateGroupService;
import cn.axzo.msg.center.message.service.MessageTemplateNewService;
import cn.axzo.msg.center.message.service.PendingMessageNewService;
import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO;
import cn.axzo.msg.center.service.dto.IdentityDTO;
import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO;
import cn.axzo.msg.center.service.dto.PersonDTO;
@ -78,11 +78,20 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
public List<PendingMessageStatisticDTO> groupStatistic(MessageGroupNodeStatisticParam param) {
List<String> groupTreeRootNodeCodes = messageGroupNodeService
.listGroupTreeRootNodeCodes(MessageGroupCategoryEnum.PENDING, param.getTerminalType());
return groupTreeRootNodeCodes.stream()
.flatMap(e -> statistic(e, param).stream())
List<GroupTreeNodePathDTO> leafNodePaths = messageGroupNodeService.leafGroupNodeCodePathsByRootNodeCodes(groupTreeRootNodeCodes);
return leafNodePaths.parallelStream()
.map(e -> statistic(e, param))
.collect(Collectors.toList());
}
@Override
public Integer countUncompleted(MessageGroupNodeStatisticParam param) {
List<String> groupTreeRootNodeCodes = messageGroupNodeService
.listGroupTreeRootNodeCodes(MessageGroupCategoryEnum.PENDING, param.getTerminalType());
List<GroupTreeNodePathDTO> leafNodePaths = messageGroupNodeService.leafGroupNodeCodePathsByRootNodeCodes(groupTreeRootNodeCodes);
return doStatistic(leafNodePaths, param);
}
@Override
public Page<PendingMessageResponse> pageQuery(PendingMessagePageRequest request) {
PersonDTO operator = PersonDTO.from(request.getPersonId(), request.getIdentityId(), request.getIdentityType());
@ -99,8 +108,10 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
buildPersonCondition(query, request.getWithIdentify(), request.getRoleCategory(), operator);
// 模板的分类对代办进行分组过滤
if (StringUtils.isNotBlank(request.getGroupNodeCode())) {
List<String> templateCodes = messageTemplateGroupService
.listMessageTemplateCodes(request.getGroupNodeCode());
Optional<GroupTreeNodePathDTO> nodePath = messageGroupNodeService.queryLeafGroupNodeCodePath(request.getGroupNodeCode());
List<String> templateCodes = nodePath
.map(v -> messageTemplateGroupService.listMessageTemplateCodes(Lists.newArrayList(v.getNodeCodePath())))
.orElseGet(Collections::emptyList);
query.in(CollectionUtils.isNotEmpty(templateCodes), PendingMessageRecord::getTemplateCode, templateCodes);
}
// 构建排序条件
@ -211,19 +222,21 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService {
return pendingMessage;
}
private List<PendingMessageStatisticDTO> statistic(String rootNodeCode, MessageGroupNodeStatisticParam param) {
// TODO:[cold_blade] [P0] 异常捕获处理功能实现
GroupTreeNodeDTO rootNode = messageGroupNodeService.queryRootNode(rootNodeCode)
.orElseThrow(() -> new ServiceException("groupNodeCode is invalid."));
// 外部传的是根节点但是前端希望只返回根节点的字节的
int pendingCnt = doStatistic(rootNodeCode, param);
PendingMessageStatisticDTO dto = PendingMessageStatisticDTO.of(rootNode);
dto.setPendingCount(pendingCnt);
return Collections.emptyList();
private PendingMessageStatisticDTO statistic(GroupTreeNodePathDTO leafNodePath, MessageGroupNodeStatisticParam param) {
PendingMessageStatisticDTO dto = PendingMessageStatisticDTO.of(leafNodePath);
dto.setPendingCount(doStatistic(leafNodePath, param));
return dto;
}
private int doStatistic(String groupNodeCode, MessageGroupNodeStatisticParam param) {
List<String> templateCodes = messageTemplateGroupService.listMessageTemplateCodes(groupNodeCode);
private int doStatistic(GroupTreeNodePathDTO leafNodePath, MessageGroupNodeStatisticParam param) {
return doStatistic(Lists.newArrayList(leafNodePath), param);
}
private int doStatistic(Collection<GroupTreeNodePathDTO> groupNodePaths, MessageGroupNodeStatisticParam param) {
List<String> groupNodePathCodes = groupNodePaths.stream()
.map(GroupTreeNodePathDTO::getNodeCodePath)
.collect(Collectors.toList());
List<String> templateCodes = messageTemplateGroupService.listMessageTemplateCodes(groupNodePathCodes);
if (CollectionUtils.isEmpty(templateCodes)) {
return 0;
}

View File

@ -42,7 +42,7 @@ import java.util.stream.Collectors;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class MessageRouterUtil {
public static final String DETAIL_ROUTER_DESC = "查看详情";
public static final String DETAIL_ROUTER_DESC = "详情";
/**
* 获取业务详情路由策略

View File

@ -17,6 +17,8 @@ public enum RouterCategoryEnum {
JUMP("直接跳转"),
ACTION("接口调用"),
@Deprecated
DETAIL("查看详情"),
;
private final String desc;

View File

@ -2,6 +2,7 @@ package cn.axzo.msg.center.service.pending.client;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.service.pending.client.fallback.PendingMessageClientFallback;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
@ -12,7 +13,6 @@ import cn.azxo.framework.common.model.Page;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@ -33,10 +33,25 @@ import java.util.List;
fallback = PendingMessageClientFallback.class)
public interface PendingMessageClient {
/**
* 分类统计待办的待处理状态的数量
*
* @param request 统计入参
* @return 分类信息及其对应的待处理的待办数量列表
*/
@PostMapping(value = "/pending-message/record/group/statistic", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<List<PendingMessageStatisticResponse>> groupStatistic(@RequestBody @Valid
PendingMessageStatisticRequest request);
/**
* 统计个人在某个应用终端的待处理的待办数量
*
* @param request 统计入参
* @return 待处理的待办数量
*/
@PostMapping(value = "/pending-message/record/count/uncompleted", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<Integer> countUncompleted(@RequestBody PendingMessageCountUncompletedRequest request);
/**
* 代办列表分页查询
*

View File

@ -2,11 +2,12 @@ package cn.axzo.msg.center.service.pending.client.fallback;
import cn.axzo.msg.center.service.enums.TerminalTypeEnum;
import cn.axzo.msg.center.service.pending.client.PendingMessageClient;
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessagePushRequest;
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponse;
import cn.axzo.msg.center.service.pending.request.PendingMessageStatisticRequest;
import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
import cn.axzo.msg.center.service.pending.response.PendingMessageStatisticResponse;
import cn.azxo.framework.common.model.CommonResponse;
import cn.azxo.framework.common.model.Page;
import lombok.extern.slf4j.Slf4j;
@ -31,6 +32,12 @@ public class PendingMessageClientFallback implements PendingMessageClient {
return CommonResponse.error("fall back while statistic pending message");
}
@Override
public CommonResponse<Integer> countUncompleted(PendingMessageCountUncompletedRequest request) {
log.error("fall back while counting pending message. req:{}", request);
return CommonResponse.error("fall back while counting pending message");
}
@Override
public CommonResponse<Page<PendingMessageResponse>> pageQuery(PendingMessagePageRequest request) {
log.error("fall back while page querying pending message. req:{}", request);

View File

@ -0,0 +1,47 @@
package cn.axzo.msg.center.service.pending.request;
import cn.axzo.msg.center.service.enums.AppTerminalTypeEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* @description
*
* @author cold_blade
* @date 2023/11/13
* @version 1.0
*/
@Setter
@Getter
public class PendingMessageCountUncompletedRequest implements Serializable {
private static final long serialVersionUID = 4714687359949832971L;
/**
* 当前登录账户的自然id(前端不care)
*/
@NotNull(message = "personId is required")
private Long personId;
/**
* 当前登录账户的身份id(前端不care)
*/
private Long identityId;
/**
* 当前登录账户的身份类型(前端不care)
*/
private IdentityTypeEnum identityType;
/**
* 应用终端类型
*/
private AppTerminalTypeEnum terminalType;
@Override
public String toString() {
return JSON.toJSONString(this);
}
}