From feb18c9c2e62f2c10a3da699f2775682ef40e80e Mon Sep 17 00:00:00 2001 From: luofu Date: Mon, 16 Oct 2023 11:12:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(REQ-1465):=20=E5=BE=85=E5=8A=9E=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=A1=A8=E7=BB=93=E6=9E=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景: https://jira.axzo.cn/browse/REQ-1465?goToView=1 修改: 1、待办记录表新增业务扩展参数字段 影响: 无 --- inside-notices/pom.xml | 5 + .../controller/MessageTemplateController.java | 5 +- .../PendingMessageNewController.java | 6 +- .../domain/dto/GroupTreeNodePathDTO.java | 58 ++++++ .../domain/dto/MessageGroupNodeDTO.java | 87 -------- .../dto/MessageGroupNodeStatisticDTO.java | 76 +++++++ .../param/MessageTemplateCreateParam.java | 19 +- .../service/MessageGroupNodeService.java | 41 ++++ .../message/service/MessageGroupService.java | 41 ---- .../MessageGroupTreeNodeCacheService.java | 53 +++++ .../service/MessageTemplateGroupService.java | 9 + .../service/MessageTemplateNewService.java | 2 +- .../service/PendingMessageNewService.java | 5 +- .../impl/MessageGroupNodeServiceImpl.java | 62 ++++++ .../service/impl/MessageGroupServiceImpl.java | 70 ------- .../MessageGroupTreeNodeCacheServiceImpl.java | 185 ++++++++++++++++++ .../impl/MessageTemplateGroupServiceImpl.java | 23 +++ .../impl/MessageTemplateNewServiceImpl.java | 88 +++++++-- .../impl/PendingMessageNewServiceImpl.java | 43 ++-- .../axzo/msg/center/utils/JSONObjectUtil.java | 20 ++ .../axzo/msg/center/utils/TreeHelperUtil.java | 44 +++++ .../msg/center/service/dto/GroupNodeDTO.java | 53 ----- .../center/service/dto/GroupTreeNodeDTO.java | 71 +++++++ .../common/enums/ServiceErrorCodeEnum.java | 23 +++ 24 files changed, 791 insertions(+), 298 deletions(-) create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/GroupTreeNodePathDTO.java delete mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeDTO.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeStatisticDTO.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupNodeService.java delete mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupService.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupTreeNodeCacheService.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupNodeServiceImpl.java delete mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupServiceImpl.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupTreeNodeCacheServiceImpl.java create mode 100644 inside-notices/src/main/java/cn/axzo/msg/center/utils/TreeHelperUtil.java delete mode 100644 msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupNodeDTO.java create mode 100644 msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupTreeNodeDTO.java create mode 100644 msg-center-common/src/main/java/cn/axzo/msg/center/common/enums/ServiceErrorCodeEnum.java diff --git a/inside-notices/pom.xml b/inside-notices/pom.xml index fc0a36ac..7d64c516 100644 --- a/inside-notices/pom.xml +++ b/inside-notices/pom.xml @@ -110,6 +110,11 @@ 1.0.0-SNAPSHOT compile + + + cn.axzo.pokonyan + pokonyan + \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateController.java index 564597ea..9fdc269d 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateController.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateController.java @@ -29,9 +29,8 @@ public class MessageTemplateController implements MessageTemplateClient { @Override public CommonResponse save(MessageTemplateCreateRequest request) { - MessageTemplateCreateParam param = MessageTemplateCreateParam.from(request); - messageTemplateNewService.createTemplate(param); - return CommonResponse.success(param.getTemplateCode()); + return CommonResponse.success( + messageTemplateNewService.createTemplate(MessageTemplateCreateParam.from(request))); } @Override diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/PendingMessageNewController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/PendingMessageNewController.java index 90d07020..f825ad98 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/PendingMessageNewController.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/PendingMessageNewController.java @@ -1,6 +1,6 @@ package cn.axzo.msg.center.message.controller; -import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeDTO; +import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeStatisticDTO; import cn.axzo.msg.center.message.domain.dto.PendingMessageDTO; import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam; import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam; @@ -36,10 +36,10 @@ public class PendingMessageNewController implements PendingMessageClient { @Override public CommonResponse> groupStatistic(MessageGroupNodeStatisticRequest request) { - List groupNodes = pendingMessageNewService + List groupNodes = pendingMessageNewService .groupStatistic(MessageGroupNodeStatisticParam.from(request)); return CommonResponse.success(groupNodes.stream() - .map(MessageGroupNodeDTO::toResponse) + .map(MessageGroupNodeStatisticDTO::toResponse) .collect(Collectors.toList()) ); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/GroupTreeNodePathDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/GroupTreeNodePathDTO.java new file mode 100644 index 00000000..ad2d43b4 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/GroupTreeNodePathDTO.java @@ -0,0 +1,58 @@ +package cn.axzo.msg.center.message.domain.dto; + +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import com.alibaba.fastjson.JSON; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +@Setter +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class GroupTreeNodePathDTO implements Serializable { + + private static final long serialVersionUID = -6325338437781927201L; + + private static final String NODE_NAME_PATH_SPLITER = "/"; + private static final String NODE_CODE_PATH_SPLITER = ":"; + + /** + * 结点编码 + */ + private String nodeCode; + /** + * 结点名称格式的路径 + */ + private String nodeNamePath; + /** + * 结点编码格式的路径 + */ + private String nodeCodePath; + + public static GroupTreeNodePathDTO of(String nodeCode, Collection 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); + } + + private static String formatPath(Function pathFunc, Collection nodes, + String spliter) { + return nodes.stream().map(pathFunc).collect(Collectors.joining(spliter)); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeDTO.java deleted file mode 100644 index 37bd6303..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeDTO.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.axzo.msg.center.message.domain.dto; - -import cn.axzo.core.utils.converter.BeanConverter; -import cn.axzo.msg.center.domain.entity.MessageGroupNode; -import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; -import cn.axzo.msg.center.service.pending.response.MessageGroupNodeResponse; -import com.alibaba.fastjson.JSON; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @description - * 消息分类结点DTO - * - * @author cold_blade - * @date 2023/9/26 - * @version 1.0 - */ -@Setter -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class MessageGroupNodeDTO implements Serializable { - - private static final long serialVersionUID = 5171436359992401120L; - - /** - * 结点类型 - * GENERAL_MESSAGE_CENTER: 消息中心 - * GENERAL_MESSAGE_MODULE: 消息模块 - * GENERAL_MESSAGE_CATEGORY: 消息分类 - * PENDING_MESSAGE_CENTER: 待办中心 - * PENDING_MESSAGE_MODULE: 待办模块 - * PENDING_MESSAGE_CATEGORY: 待办分类 - */ - private MessageGroupNodeCategoryEnum category; - /** - * 结点名称 - */ - private String nodeName; - /** - * 结点编码 - */ - private String nodeCode; - /** - * 父节点编码 - */ - private String parentNodeCode; - /** - * 结点对应的代办数量 - */ - private Integer pendingCount; - /** - * 子节点列表 - */ - private List children; - - public static MessageGroupNodeDTO from(MessageGroupNode groupNode) { - return MessageGroupNodeDTO.builder() - .category(groupNode.getCategory()) - .nodeName(groupNode.getName()) - .nodeCode(groupNode.getCode()) - .parentNodeCode(groupNode.getParentCode()) - .build(); - } - - public MessageGroupNodeResponse toResponse() { - MessageGroupNodeResponse response = BeanConverter.convert(this, MessageGroupNodeResponse.class); - List children = this.children.stream() - .map(MessageGroupNodeDTO::toResponse).collect(Collectors.toList()); - response.setNodeChildren(children); - return response; - } - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeStatisticDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeStatisticDTO.java new file mode 100644 index 00000000..f5cef354 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeStatisticDTO.java @@ -0,0 +1,76 @@ +package cn.axzo.msg.center.message.domain.dto; + +import cn.axzo.basics.common.model.IBaseTree; +import cn.axzo.core.utils.converter.BeanConverter; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import cn.axzo.msg.center.service.pending.response.MessageGroupNodeResponse; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @description + * 消息分类结点DTO + * + * @author cold_blade + * @date 2023/9/26 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageGroupNodeStatisticDTO implements IBaseTree, Serializable { + + private static final long serialVersionUID = 5171436359992401120L; + + /** + * 树结点 + */ + private GroupTreeNodeDTO treeNode; + /** + * 子节点列表 + */ + private List nodeChildren; + /** + * 结点对应的代办数量 + */ + private Integer pendingCount; + + public static MessageGroupNodeStatisticDTO of(GroupTreeNodeDTO groupNode) { + return MessageGroupNodeStatisticDTO.builder() + .treeNode(groupNode) + .build(); + } + + public MessageGroupNodeResponse toResponse() { + MessageGroupNodeResponse response = BeanConverter.convert(this, MessageGroupNodeResponse.class); + List children = this.nodeChildren.stream() + .map(MessageGroupNodeStatisticDTO::toResponse).collect(Collectors.toList()); + response.setNodeChildren(children); + return response; + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } + + @Override + public String getNodeCode() { + return treeNode.getNodeCode(); + } + + @Override + public String getParentNodeCode() { + return treeNode.getParentNodeCode(); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateCreateParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateCreateParam.java index b1151d5d..c516a122 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateCreateParam.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateCreateParam.java @@ -1,10 +1,10 @@ package cn.axzo.msg.center.message.domain.param; import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; -import cn.axzo.msg.center.service.dto.MessageRouterDTO; import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; import cn.axzo.msg.center.service.template.request.MessageTemplateCreateRequest; -import cn.axzo.msg.center.utils.UUIDUtil; +import cn.axzo.msg.center.utils.JSONObjectUtil; import com.alibaba.fastjson.JSON; import lombok.AllArgsConstructor; import lombok.Builder; @@ -36,9 +36,9 @@ public class MessageTemplateCreateParam implements Serializable { */ private String templateName; /** - * 系统自动生成的模板code + * 消息分类树的叶结点的结点编码列表 */ - private String templateCode; + private List leafGroupNodes; /** * 所属消息类型 * GENERAL_MESSAGE: 普通消息 @@ -69,18 +69,25 @@ public class MessageTemplateCreateParam implements Serializable { * 路由列表 */ private List routers; + /** + * 推送终端配置 + * B_ENTERPRISE_APP: B-安心筑企业版 + * C_WORKER_APP: C-安心筑工人版 + */ + private List pushTerminals; public static MessageTemplateCreateParam from(MessageTemplateCreateRequest request) { return MessageTemplateCreateParam.builder() .templateName(request.getTemplateName()) - .templateCode(UUIDUtil.uuidString()) .msgCategory(request.getCategory()) + .leafGroupNodes(request.getLeafGroupNodes()) .title(request.getMsgTitle()) .content(request.getMsgContent()) - .cardContent(request.getMsgCardInfo()) + .cardContent(JSONObjectUtil.checkAndReturn(request.getMsgCardInfo())) .icon(request.getMsgIcon()) .operatorId(request.getOperatorId()) .routers(request.getRouters()) + .pushTerminals(request.getPushTerminals()) .build(); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupNodeService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupNodeService.java new file mode 100644 index 00000000..773a1d48 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupNodeService.java @@ -0,0 +1,41 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * 消息分类结点管理 + * + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +public interface MessageGroupNodeService { + + /** + * 获取分类结点(叶结点)名称的路径 + * + * @param leafGroupNodeCodes 分类结点(叶结点)的编码列表 + * @return 分类结点(叶结点)名称的路径 + */ + Map leafGroupNodeNamePaths(Collection leafGroupNodeCodes); + + /** + * 获取分类结点(叶结点)编码的路径 + * + * @param leafGroupNodeCodes 分类结点(叶结点)的编码列表 + * @return 分类结点(叶结点)编码的路径 + */ + Map leafGroupNodeCodePaths(Collection leafGroupNodeCodes); + + /** + * 根据结点编码查询结点信息 + * + * @param rootNodeCode 结点编码 + * @return 结点信息 + */ + Optional queryRootNode(String rootNodeCode); +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupService.java deleted file mode 100644 index 66dabe02..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupService.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.axzo.msg.center.message.service; - -import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeDTO; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -/** - * 消息分类管理 - * - * @author cold_blade - * @date 2023/9/20 - * @version 1.0 - */ -public interface MessageGroupService { - - /** - * 通过结点编码查询结点信息 - * - * @param nodeCode 结点编码 - * @return 结点信息 - */ - Optional queryByNodeCode(String nodeCode); - - /** - * 查询指定结点的字节的信息 - * - * @param nodeCode 指定结点编码 - * @return 子节点列表信息 - */ - List listChildren(String nodeCode); - - /** - * 模板关联分类 - * - * @param templateNode 模板编码 - * @param pathList 分类path列表 - */ - void templateGroup(String templateNode, Collection pathList); -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupTreeNodeCacheService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupTreeNodeCacheService.java new file mode 100644 index 00000000..2b283f46 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupTreeNodeCacheService.java @@ -0,0 +1,53 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; + +import java.util.List; +import java.util.Optional; + +/** + * 消息分类树节点缓存管理 + * + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +public interface MessageGroupTreeNodeCacheService { + + /** + * 获取所有有效的分类结点树的根节点 + * + * @return 树根节点列表 + */ + List listAllRootNodes(); + + /** + * 获取指定结点所在的树的根节点 + * + * @param rootNodeCode 结点编码 + * @return 根节点 + */ + Optional queryRootNode(String rootNodeCode); + + /** + * 根据结点编码查询结点信息 + * + * @param groupNodeCode 结点编码 + * @return 结点信息 + */ + Optional queryNode(String groupNodeCode); + + /** + * 获取叶结点对应的树的路径 + * + * @param leafNodeCode 叶结点编码 + * @return 路径 + */ + Optional queryLeafNodePath(String leafNodeCode); + + /** + * 刷新缓存 + */ + void refreshCache(); +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateGroupService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateGroupService.java index f830e062..3ed0499d 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateGroupService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateGroupService.java @@ -1,5 +1,6 @@ package cn.axzo.msg.center.message.service; +import java.util.Collection; import java.util.List; /** @@ -18,4 +19,12 @@ public interface MessageTemplateGroupService { * @return 模板编码列表 */ List listMessageTemplateCodes(String groupNodeCode); + + /** + * 模板关联分类 + * + * @param templateNode 模板编码 + * @param groupNodeCodes 分类结点编码列表 + */ + void templateGroup(String templateNode, Collection groupNodeCodes); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateNewService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateNewService.java index 6836bb14..9175cd74 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateNewService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateNewService.java @@ -20,7 +20,7 @@ public interface MessageTemplateNewService { * * @param param 模板内容参数 */ - void createTemplate(MessageTemplateCreateParam param); + String createTemplate(MessageTemplateCreateParam param); /** * 通过模板编码查询模板信息 diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/PendingMessageNewService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/PendingMessageNewService.java index df132a34..be931eef 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/PendingMessageNewService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/PendingMessageNewService.java @@ -1,12 +1,11 @@ package cn.axzo.msg.center.message.service; -import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeDTO; +import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeStatisticDTO; import cn.axzo.msg.center.message.domain.dto.PendingMessageDTO; 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; 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.PendingMessageResponse; import cn.azxo.framework.common.model.Page; @@ -22,7 +21,7 @@ import java.util.Optional; */ public interface PendingMessageNewService { - List groupStatistic(MessageGroupNodeStatisticParam param); + List groupStatistic(MessageGroupNodeStatisticParam param); /** * 代办列表分页查询 diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupNodeServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupNodeServiceImpl.java new file mode 100644 index 00000000..77a9ac7a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupNodeServiceImpl.java @@ -0,0 +1,62 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO; +import cn.axzo.msg.center.message.service.MessageGroupNodeService; +import cn.axzo.msg.center.message.service.MessageGroupTreeNodeCacheService; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MessageGroupNodeServiceImpl implements MessageGroupNodeService { + + private final MessageGroupTreeNodeCacheService messageGroupTreeNodeCacheService; + + @Override + public Map leafGroupNodeNamePaths(Collection leafGroupNodeCodes) { + if (CollectionUtils.isEmpty(leafGroupNodeCodes)) { + log.info("leafGroupNodeCodes is empty."); + return Collections.emptyMap(); + } + return leafGroupNodeCodes.stream() + .map(messageGroupTreeNodeCacheService::queryLeafNodePath) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toMap(GroupTreeNodePathDTO::getNodeCode, + GroupTreeNodePathDTO::getNodeNamePath, (cur, pre) -> cur)); + } + + @Override + public Map leafGroupNodeCodePaths(Collection leafGroupNodeCodes) { + if (CollectionUtils.isEmpty(leafGroupNodeCodes)) { + log.info("leafGroupNodeCodes is empty."); + return Collections.emptyMap(); + } + return leafGroupNodeCodes.stream() + .map(messageGroupTreeNodeCacheService::queryLeafNodePath) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toMap(GroupTreeNodePathDTO::getNodeCode, + GroupTreeNodePathDTO::getNodeCodePath, (cur, pre) -> cur)); + } + + @Override + public Optional queryRootNode(String rootNodeCode) { + return messageGroupTreeNodeCacheService.queryRootNode(rootNodeCode); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupServiceImpl.java deleted file mode 100644 index 0ca69898..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupServiceImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.axzo.msg.center.message.service.impl; - -import cn.axzo.msg.center.dal.MessageGroupNodeDao; -import cn.axzo.msg.center.dal.MessageTemplateGroupDao; -import cn.axzo.msg.center.domain.entity.MessageGroupNode; -import cn.axzo.msg.center.domain.entity.MessageTemplateGroup; -import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeDTO; -import cn.axzo.msg.center.message.service.MessageGroupService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * @author cold_blade - * @date 2023/10/6 - * @version 1.0 - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class MessageGroupServiceImpl implements MessageGroupService { - - private final MessageGroupNodeDao messageGroupNodeDao; - private final MessageTemplateGroupDao messageTemplateGroupDao; - - @Override - public Optional queryByNodeCode(String nodeCode) { - if (StringUtils.isBlank(nodeCode)) { - return Optional.empty(); - } - MessageGroupNode groupNode = messageGroupNodeDao.lambdaQuery() - .eq(MessageGroupNode::getCode, nodeCode) - .eq(MessageGroupNode::getIsDelete, 0) - .one(); - return Optional.ofNullable(groupNode).map(MessageGroupNodeDTO::from); - } - - @Override - public List listChildren(String nodeCode) { - return messageGroupNodeDao.lambdaQuery() - .eq(MessageGroupNode::getParentCode, nodeCode) - .eq(MessageGroupNode::getIsDelete, 0) - .list().stream() - .map(MessageGroupNodeDTO::from) - .collect(Collectors.toList()); - } - - @Override - public void templateGroup(String templateNode, Collection pathList) { - if (StringUtils.isBlank(templateNode) - || CollectionUtils.isEmpty(pathList)) { - return; - } - List rows = pathList.stream() - .map(e -> { - MessageTemplateGroup group = new MessageTemplateGroup(); - group.setTemplateCode(templateNode); - group.setPath(e); - return group; - }).collect(Collectors.toList()); - messageTemplateGroupDao.saveBatch(rows); - } -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupTreeNodeCacheServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupTreeNodeCacheServiceImpl.java new file mode 100644 index 00000000..ddb47614 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupTreeNodeCacheServiceImpl.java @@ -0,0 +1,185 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.basics.common.util.TreeUtil; +import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; +import cn.axzo.msg.center.dal.MessageGroupNodeDao; +import cn.axzo.msg.center.domain.entity.MessageGroupNode; +import cn.axzo.msg.center.message.domain.dto.GroupTreeNodePathDTO; +import cn.axzo.msg.center.message.service.MessageGroupTreeNodeCacheService; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import cn.axzo.pokonyan.config.redis.RedisUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MessageGroupTreeNodeCacheServiceImpl implements MessageGroupTreeNodeCacheService { + + private static final String CACHE_KEY = "msg-center:message:group:cache-key"; + private static final String CACHE_VALUE = "msg-center:message:group:cache-val"; + private static final long CACHE_KEY_TIMEOUT_DAYS = 1; + + private final MessageGroupNodeDao messageGroupNodeDao; + + private List allGroupTreeRootNodesCache = Collections.emptyList(); + private List leafTreeNodePathsCache = Collections.emptyList(); + + @Override + public List listAllRootNodes() { + return getAllGroupTreeRootNodesCache(); + } + + @Override + public Optional queryRootNode(String rootNodeCode) { + if (StringUtils.isBlank(rootNodeCode)) { + log.info("rootNodeCode is blank."); + return Optional.empty(); + } + return getAllGroupTreeRootNodesCache().stream() + .filter(e -> Objects.equals(e.getNodeCode(), rootNodeCode)) + .findFirst(); + } + + @Override + public Optional queryNode(String groupNodeCode) { + if (StringUtils.isBlank(groupNodeCode)) { + log.info("groupNodeCode is blank."); + return Optional.empty(); + } + return getAllGroupTreeRootNodesCache().stream() + .map(e -> findTreeNode(e, groupNodeCode)) + .filter(Objects::nonNull) + .findFirst(); + } + + @Override + public Optional queryLeafNodePath(String leafNodeCode) { + if (StringUtils.isBlank(leafNodeCode)) { + log.info("leafNodeCode is blank."); + return Optional.empty(); + } + return getLeafTreeNodePathsCache().stream() + .filter(e -> Objects.equals(e.getNodeCode(), leafNodeCode)) + .findFirst(); + } + + @Override + public void refreshCache() { + // 清除redis中的缓存标识 + RedisUtil.KeyOps.delete(CACHE_KEY); + // 清除本地缓存 + allGroupTreeRootNodesCache = Collections.emptyList(); + leafTreeNodePathsCache = Collections.emptyList(); + } + + private GroupTreeNodeDTO findTreeNode(GroupTreeNodeDTO root, String treeNodeCode) { + if (Objects.equals(root.getNodeCode(), treeNodeCode)) { + return root; + } + LinkedList queue = new LinkedList<>(root.getNodeChildren()); + while (!queue.isEmpty()) { + GroupTreeNodeDTO node = queue.pop(); + Optional childOp = node.getChild(treeNodeCode); + if (childOp.isPresent()) { + return childOp.get(); + } + queue.addAll(node.getNodeChildren()); + } + return null; + } + + private List getAllGroupTreeRootNodesCache() { + if (RedisUtil.KeyOps.hasKey(CACHE_KEY)) { + // 其它结点进行了本地缓存,但是当前服务进程还未进行缓存 + if (CollectionUtils.isEmpty(allGroupTreeRootNodesCache)) { + // 本地缓存初始化,不更新redis中的缓存标识 + initialize(false); + } + } else { + // 本地缓存初始化并更新redis中的缓存标识 + initialize(true); + } + return this.allGroupTreeRootNodesCache; + } + + private List getLeafTreeNodePathsCache() { + if (RedisUtil.KeyOps.hasKey(CACHE_KEY)) { + // 其它结点进行了本地缓存,但是当前服务进程还未进行缓存 + if (leafTreeNodePathsCache.isEmpty()) { + // 本地缓存初始化,不更新redis中的缓存标识 + initialize(false); + } + } else { + // 本地缓存初始化并更新redis中的缓存标识 + initialize(true); + } + return leafTreeNodePathsCache; + } + + private synchronized void initialize(boolean refreshCache) { + List groupNodes = listAllValidNodesFromDB(); + allGroupTreeRootNodesCache = TreeUtil.buildTree(groupNodes); + leafTreeNodePathsCache = allGroupTreeRootNodesCache.stream() + .flatMap(e -> parseRootNode(e).stream()) + .collect(Collectors.toList()); + if (refreshCache) { + RedisUtil.StringOps.setIfAbsent(CACHE_KEY, CACHE_VALUE, CACHE_KEY_TIMEOUT_DAYS, TimeUnit.DAYS); + } + } + + private List listAllValidNodesFromDB() { + return messageGroupNodeDao.lambdaQuery() + .eq(MessageGroupNode::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list().stream() + .map(this::convert) + .collect(Collectors.toList()); + } + + private GroupTreeNodeDTO convert(MessageGroupNode groupNode) { + return GroupTreeNodeDTO.builder() + .nodeName(groupNode.getName()) + .nodeCode(groupNode.getCode()) + .category(groupNode.getCategory()) + .parentNodeCode(groupNode.getParentCode()) + .isLeaf(groupNode.getIsLeaf()) + .status(groupNode.getStatus()) + .build(); + } + + private List parseRootNode(GroupTreeNodeDTO root) { + List paths = Lists.newArrayList(); + LinkedList pathNodeStack = new LinkedList<>(); + LinkedList stack = new LinkedList<>(); + stack.push(root); + while (!stack.isEmpty()) { + GroupTreeNodeDTO node = stack.pop(); + pathNodeStack.push(node); + if (CollectionUtils.isEmpty(node.getNodeChildren())) { + // 当前结点为树的叶结点(逻辑上的,有可能不是业务维度的叶结点) + paths.add(GroupTreeNodePathDTO.of(node.getNodeCode(), pathNodeStack)); + pathNodeStack.pop(); + } else { + stack.addAll(node.getNodeChildren()); + } + } + return paths; + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateGroupServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateGroupServiceImpl.java index cfcd1b34..24288fc0 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateGroupServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateGroupServiceImpl.java @@ -2,14 +2,18 @@ package cn.axzo.msg.center.message.service.impl; import cn.axzo.msg.center.dal.MessageTemplateGroupDao; import cn.axzo.msg.center.domain.entity.MessageTemplateGroup; +import cn.axzo.msg.center.message.service.MessageGroupNodeService; import cn.axzo.msg.center.message.service.MessageTemplateGroupService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -22,6 +26,7 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class MessageTemplateGroupServiceImpl implements MessageTemplateGroupService { + private final MessageGroupNodeService messageGroupNodeService; private final MessageTemplateGroupDao messageTemplateGroupDao; @Override @@ -37,4 +42,22 @@ public class MessageTemplateGroupServiceImpl implements MessageTemplateGroupServ .map(MessageTemplateGroup::getTemplateCode) .collect(Collectors.toList()); } + + @Override + public void templateGroup(String templateNode, Collection groupNodeCodes) { + if (StringUtils.isBlank(templateNode) + || CollectionUtils.isEmpty(groupNodeCodes)) { + return; + } + Map pathMap = messageGroupNodeService.leafGroupNodeCodePaths(groupNodeCodes); + List rows = groupNodeCodes.stream() + .filter(pathMap::containsKey) + .map(e -> { + MessageTemplateGroup group = new MessageTemplateGroup(); + group.setTemplateCode(templateNode); + group.setPath(pathMap.get(e)); + return group; + }).collect(Collectors.toList()); + messageTemplateGroupDao.saveBatch(rows); + } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateNewServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateNewServiceImpl.java index 5a149989..50da105e 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateNewServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateNewServiceImpl.java @@ -1,18 +1,24 @@ package cn.axzo.msg.center.message.service.impl; -import cn.axzo.core.utils.converter.BeanConverter; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.msg.center.common.enums.ServiceErrorCodeEnum; import cn.axzo.msg.center.dal.MessageBaseTemplateDao; import cn.axzo.msg.center.domain.entity.MessageBaseTemplate; import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO; import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; import cn.axzo.msg.center.message.domain.param.MessageTemplateCreateParam; +import cn.axzo.msg.center.message.service.MessageTemplateGroupService; import cn.axzo.msg.center.message.service.MessageTemplateNewService; import cn.axzo.msg.center.message.service.MessageTemplateRouterService; +import cn.axzo.msg.center.utils.JSONObjectUtil; +import cn.axzo.msg.center.utils.UUIDUtil; +import cn.axzo.pokonyan.config.redis.RedisUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.List; @@ -33,19 +39,24 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class MessageTemplateNewServiceImpl implements MessageTemplateNewService { + private static final String SAVE_TEMPLATE_MUTEX_KEY = "msg-center:template:save:%s"; + private static final long TRY_LOCK_TIMEOUT_MILLS = 1000; + private static final int RETRY_CNT_MAX = 3; + private final MessageBaseTemplateDao messageBaseTemplateDao; + private final MessageTemplateGroupService messageTemplateGroupService; private final MessageTemplateRouterService messageTemplateRouterService; @Override - public void createTemplate(MessageTemplateCreateParam param) { - // TODO: [cold_blade] [P2] 模板与分类的关系 - messageBaseTemplateDao.save(convert(param)); - if (CollectionUtils.isNotEmpty(param.getRouters())) { - List routers = param.getRouters().stream() - .map(e -> RawMessageRouterDTO.from(e, param.getTemplateCode())) - .collect(Collectors.toList()); - messageTemplateRouterService.batchInsert(routers); - } + @Transactional(rollbackFor = Exception.class) + public String createTemplate(MessageTemplateCreateParam param) { + // 创建模板基础数据 + String templateCode = saveTemplate(param); + // 创建模板的路由数据 + saveTemplateRouters(param, templateCode); + // 创建模板与分类的关联关系数据 + messageTemplateGroupService.templateGroup(templateCode, param.getLeafGroupNodes()); + return templateCode; } @Override @@ -87,12 +98,65 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService .collect(Collectors.toList()); } + private String saveTemplate(MessageTemplateCreateParam param) { + String templateCode = UUIDUtil.uuidString(); + MessageBaseTemplate template = convert(param); + boolean result = doSaveTemplate(template, templateCode); + int retryCnt = 0; + while (!result && retryCnt++ < RETRY_CNT_MAX) { + // 默认重试{@cod RETRY_CNT_MAX}次,若{@code RETRY_CNT_MAX}次后依然失败就报错 + templateCode = UUIDUtil.uuidString(); + result = doSaveTemplate(template, templateCode); + } + AssertUtil.isTrue(result, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc()); + return templateCode; + } + + private boolean doSaveTemplate(MessageBaseTemplate template, String templateCode) { + String requestId = UUIDUtil.uuidRawString(); + try { + String opKey = String.format(SAVE_TEMPLATE_MUTEX_KEY, templateCode); + boolean lockResult = RedisUtil.LockOps.getLockUntilTimeout(opKey, requestId, TRY_LOCK_TIMEOUT_MILLS); + AssertUtil.isTrue(lockResult, ServiceErrorCodeEnum.SYSTEM_BUSY.getDesc()); + if (isTemplateExist(templateCode)) { + return false; + } + template.setCode(templateCode); + messageBaseTemplateDao.save(template); + return true; + } finally { + RedisUtil.LockOps.releaseLock(SAVE_TEMPLATE_MUTEX_KEY, requestId); + } + } + + private boolean isTemplateExist(String templateCode) { + return messageBaseTemplateDao.lambdaQuery() + .eq(MessageBaseTemplate::getCode, templateCode) + .eq(MessageBaseTemplate::getIsDelete, 0) + .count() > 0; + } + private MessageBaseTemplate convert(MessageTemplateCreateParam param) { - MessageBaseTemplate template = BeanConverter.convert(param, MessageBaseTemplate.class); + MessageBaseTemplate template = new MessageBaseTemplate(); template.setName(param.getTemplateName()); - template.setCode(param.getTemplateCode()); + template.setMsgCategory(param.getMsgCategory()); + template.setTitle(param.getTitle()); + template.setContent(param.getContent()); + template.setCardContent(JSONObjectUtil.checkAndReturn(param.getCardContent())); + template.setIcon(param.getIcon()); + template.setPushTerminal(JSONObjectUtil.toJSONString(param.getPushTerminals())); template.setCreatorId(param.getOperatorId()); template.setUpdaterId(param.getOperatorId()); return template; } + + private void saveTemplateRouters(MessageTemplateCreateParam param, String templateCode) { + if (CollectionUtils.isEmpty(param.getRouters())) { + return; + } + List routers = param.getRouters().stream() + .map(e -> RawMessageRouterDTO.from(e, templateCode)) + .collect(Collectors.toList()); + messageTemplateRouterService.batchInsert(routers); + } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/PendingMessageNewServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/PendingMessageNewServiceImpl.java index fd9312ba..027e62d8 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/PendingMessageNewServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/PendingMessageNewServiceImpl.java @@ -5,16 +5,17 @@ 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.MessageGroupNodeDTO; +import cn.axzo.msg.center.message.domain.dto.MessageGroupNodeStatisticDTO; import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO; import cn.axzo.msg.center.message.domain.dto.PendingMessageDTO; import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; import cn.axzo.msg.center.message.domain.param.MessageGroupNodeStatisticParam; import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam; -import cn.axzo.msg.center.message.service.MessageGroupService; +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.MessageRouterDTO; import cn.axzo.msg.center.service.dto.PersonDTO; @@ -29,6 +30,7 @@ import cn.axzo.msg.center.service.pending.response.PendingMessageResponse; import cn.axzo.msg.center.utils.JSONObjectUtil; import cn.axzo.msg.center.utils.MessageRouterUtil; import cn.axzo.msg.center.utils.OrderFieldParseUtil; +import cn.axzo.msg.center.utils.TreeHelperUtil; import cn.axzo.msg.center.utils.UUIDUtil; import cn.azxo.framework.common.model.Page; import com.alibaba.fastjson.JSON; @@ -45,10 +47,10 @@ import org.springframework.stereotype.Service; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Stack; import java.util.stream.Collectors; /** @@ -63,13 +65,13 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class PendingMessageNewServiceImpl implements PendingMessageNewService { - private final MessageGroupService messageGroupService; private final PendingMessageRecordDao pendingMessageRecordDao; + private final MessageGroupNodeService messageGroupNodeService; private final MessageTemplateNewService messageTemplateNewService; private final MessageTemplateGroupService messageTemplateGroupService; @Override - public List groupStatistic(MessageGroupNodeStatisticParam param) { + public List groupStatistic(MessageGroupNodeStatisticParam param) { return param.getGroupNodeCodes().stream() .flatMap(e -> statistic(e, param).stream()) .collect(Collectors.toList()); @@ -193,26 +195,23 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { return pendingMessage; } - private List statistic(String rootNodeCode, MessageGroupNodeStatisticParam param) { - MessageGroupNodeDTO groupNode = messageGroupService.queryByNodeCode(rootNodeCode) + private List statistic(String rootNodeCode, MessageGroupNodeStatisticParam param) { + GroupTreeNodeDTO rootNode = messageGroupNodeService.queryRootNode(rootNodeCode) .orElseThrow(() -> new ServiceException("groupNodeCode is invalid.")); - MessageGroupNodeDTO root = groupNode; - Stack stack = new Stack<>(); - stack.push(groupNode); - List children; + MessageGroupNodeStatisticDTO rootWrapper = TreeHelperUtil.wrapper(rootNode, MessageGroupNodeStatisticDTO::of); + LinkedList stack = new LinkedList<>(); + stack.push(rootWrapper); + MessageGroupNodeStatisticDTO wrapper; while (!stack.isEmpty()) { - // TODO: [cold_blade] [P3] 优化树形分类结点的统计效率 - groupNode = stack.pop(); - statistic(groupNode, param); - children = messageGroupService.listChildren(groupNode.getNodeCode()); - groupNode.setChildren(children); - children.forEach(stack::push); + wrapper = stack.pop(); + statistic(wrapper, param); + stack.addAll(wrapper.getNodeChildren()); } // 外部传的是根节点,但是前端希望只返回根节点的字节的 - return root.getChildren(); + return rootWrapper.getNodeChildren(); } - private void statistic(MessageGroupNodeDTO groupNode, MessageGroupNodeStatisticParam param) { + private void statistic(MessageGroupNodeStatisticDTO groupNode, MessageGroupNodeStatisticParam param) { List templateCodes = messageTemplateGroupService.listMessageTemplateCodes(groupNode.getNodeCode()); if (CollectionUtils.isEmpty(templateCodes)) { groupNode.setPendingCount(0); @@ -298,6 +297,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { } private void buildTemplateInfo(PendingMessageRecord record, MessageTemplateDTO msgTemplate, String routeParam) { + // TODO:[cold_blade] [P3] 后续其它业务对接的时候,需要明确业务扩展字段和路由参数的分界 JSONObject routerParamObj = JSONObjectUtil.parseObject(routeParam); String title = PlaceholderResolver .getDefaultResolver().resolveByMap(msgTemplate.getTitle(), routerParamObj); @@ -316,4 +316,9 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { record.setBizCategory(param.getBizCategory()); record.setBizExtParam(JSONObjectUtil.checkAndReturn(param.getBizExtParams())); } + + private static class GroupTreeNodeWrapper { + private GroupTreeNodeDTO treeNode; + private int pendingCnt; + } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/JSONObjectUtil.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/JSONObjectUtil.java index d22d57cc..a5770691 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/utils/JSONObjectUtil.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/JSONObjectUtil.java @@ -4,8 +4,12 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; +import java.util.Objects; + /** * @author cold_blade * @date 2023/10/13 @@ -14,6 +18,8 @@ import org.apache.commons.lang3.StringUtils; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class JSONObjectUtil { private static final JSONObject EMPTY_JSON_OBJ = new JSONObject(); + private static final String EMPTY_JSON_OBJ_STR = "{}"; + private static final String EMPTY_JSON_ARR_STR = "[]"; /** * 解析JSON字符串,若字符串格式不正确,抛异常 @@ -28,6 +34,20 @@ public final class JSONObjectUtil { return JSON.parseObject(str); } + public static String toJSONString(Object obj) { + if (Objects.isNull(obj)) { + return EMPTY_JSON_OBJ_STR; + } + return JSON.toJSONString(obj); + } + + public static String toJSONString(Collection objCollection) { + if (CollectionUtils.isEmpty(objCollection)) { + return EMPTY_JSON_ARR_STR; + } + return JSON.toJSONString(objCollection); + } + /** * 校验字符串是否为有效的JSON字符串,若字符串格式不正确,抛异常 * diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/TreeHelperUtil.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/TreeHelperUtil.java new file mode 100644 index 00000000..0a8a57e0 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/TreeHelperUtil.java @@ -0,0 +1,44 @@ +package cn.axzo.msg.center.utils; + +import cn.axzo.basics.common.model.IBaseTree; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import com.google.common.collect.Maps; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/16 + * @version 1.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class TreeHelperUtil { + + public static , O> T wrapper(GroupTreeNodeDTO treeNode, + Function wrapperFunc) { + AssertUtil.notNull(treeNode, "treeNode is null"); + AssertUtil.notNull(wrapperFunc, "wrapperFunc is null"); + + final Map convertMap = Maps.newHashMap(); + T wrapper = wrapperFunc.apply(treeNode); + convertMap.put(treeNode, wrapper); + LinkedList treeNodeStack = new LinkedList<>(); + treeNodeStack.push(treeNode); + while (!treeNodeStack.isEmpty()) { + treeNode = treeNodeStack.pop(); + List children = treeNode.getNodeChildren(); + convertMap.get(treeNode).setNodeChildren(children.stream() + .map(e -> convertMap.put(e, wrapperFunc.apply(e))) + .collect(Collectors.toList())); + treeNodeStack.addAll(children); + } + return wrapper; + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupNodeDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupNodeDTO.java deleted file mode 100644 index 256ada15..00000000 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupNodeDTO.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.axzo.msg.center.service.dto; - -import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; -import cn.axzo.msg.center.service.enums.StatusEnum; -import com.alibaba.fastjson.JSON; -import lombok.Getter; -import lombok.Setter; - -import java.io.Serializable; - -/** - * @description - * - * @author cold_blade - * @date 2023/9/23 - * @version 1.0 - */ -@Setter -@Getter -public class GroupNodeDTO implements Serializable { - - private static final long serialVersionUID = -5935469947222698608L; - - /** - * 模板分组结点名称 - */ - private String name; - /** - * 模板分组结点名称code - */ - private String code; - /** - * 结点类型 - */ - private MessageGroupNodeCategoryEnum category; - /** - * 父节点 - */ - private String parentCode; - /** - * 是否为叶节点 - */ - private Integer isLeaf; - /** - * 状态 - */ - private StatusEnum status; - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupTreeNodeDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupTreeNodeDTO.java new file mode 100644 index 00000000..187101b5 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupTreeNodeDTO.java @@ -0,0 +1,71 @@ +package cn.axzo.msg.center.service.dto; + +import cn.axzo.basics.common.model.IBaseTree; +import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class GroupTreeNodeDTO implements IBaseTree, Serializable { + + private static final long serialVersionUID = -3244632155934087302L; + + /** + * 模板分组结点名称 + */ + private String nodeName; + /** + * 模板分组结点名称code + */ + private String nodeCode; + /** + * 结点类型 + */ + private MessageGroupNodeCategoryEnum category; + /** + * 父节点 + */ + private String parentNodeCode; + /** + * 是否为叶节点 + */ + private Integer isLeaf; + /** + * 状态 + */ + private StatusEnum status; + /** + * 子节点列表 + */ + @Builder.Default + private List nodeChildren = Collections.emptyList(); + + public Optional getChild(String treeNodeCode) { + if (StringUtils.isBlank(treeNodeCode)) { + return Optional.empty(); + } + return getNodeChildren().stream() + .filter(e -> Objects.equals(e.getNodeCode(), treeNodeCode)) + .findFirst(); + } +} diff --git a/msg-center-common/src/main/java/cn/axzo/msg/center/common/enums/ServiceErrorCodeEnum.java b/msg-center-common/src/main/java/cn/axzo/msg/center/common/enums/ServiceErrorCodeEnum.java new file mode 100644 index 00000000..e84dab97 --- /dev/null +++ b/msg-center-common/src/main/java/cn/axzo/msg/center/common/enums/ServiceErrorCodeEnum.java @@ -0,0 +1,23 @@ +package cn.axzo.msg.center.common.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum ServiceErrorCodeEnum { + + SYSTEM_BUSY("E0002", "系统繁忙,请稍后重试"), + + PARAM_IS_INVALID("E0020", "参数异常"), + ; + + private final String code; + private final String desc; +}