diff --git a/inside-notices/pom.xml b/inside-notices/pom.xml index fc0a36ac..bbf4aa3b 100644 --- a/inside-notices/pom.xml +++ b/inside-notices/pom.xml @@ -110,6 +110,10 @@ 1.0.0-SNAPSHOT compile + + cn.axzo.im.center + im-center-api + \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/MessageSystemConfig.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/MessageSystemConfig.java index 49f7041c..113845a8 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/MessageSystemConfig.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/MessageSystemConfig.java @@ -24,4 +24,6 @@ public class MessageSystemConfig { */ @Value("${message.common.record.divide-days:-1}") private Integer dataDivideDays; + @Value("${message.common.icon.orgIcon:https://axzo-pro.oss-cn-hangzhou.aliyuncs.com/rs_app/ic_org_icon.png}") + private String orgIcon; } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/MessageModuleService.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/MessageModuleService.java index dd3a1d80..722352dc 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/MessageModuleService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/MessageModuleService.java @@ -6,6 +6,7 @@ import cn.axzo.msg.center.domain.dto.SearchModuleDTO; import cn.axzo.msg.center.domain.dto.SyncModuleDataDTO; import cn.axzo.msg.center.domain.dto.UpdateModuleDTO; import cn.axzo.msg.center.domain.entity.MessageModule; +import cn.axzo.msg.center.domain.enums.ModuleBizTypeEnum; import cn.axzo.msg.center.domain.enums.UserTypeEnum; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -51,4 +52,12 @@ public interface MessageModuleService { * @return 对应的模块列表 */ List listByModuleName(String moduleName); + + /** + * 通过bizType查询模块id列表 + * + * @param bizType 业务类型 + * @return 模块id列表 + */ + List listModuleIdByBizType(ModuleBizTypeEnum bizType); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageModuleServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageModuleServiceImpl.java index bd25ab5c..8c19edc4 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageModuleServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageModuleServiceImpl.java @@ -3,7 +3,11 @@ package cn.axzo.msg.center.inside.notices.service.impl; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; import cn.axzo.msg.center.api.enums.ReceiveTypeEnum; import cn.axzo.msg.center.dal.MessageModuleDao; -import cn.axzo.msg.center.domain.dto.*; +import cn.axzo.msg.center.domain.dto.CreateModuleDTO; +import cn.axzo.msg.center.domain.dto.MsgModuleDTO; +import cn.axzo.msg.center.domain.dto.SearchModuleDTO; +import cn.axzo.msg.center.domain.dto.SyncModuleDataDTO; +import cn.axzo.msg.center.domain.dto.UpdateModuleDTO; import cn.axzo.msg.center.domain.entity.MessageModule; import cn.axzo.msg.center.domain.enums.ModuleBizTypeEnum; import cn.axzo.msg.center.domain.enums.UserTypeEnum; @@ -20,7 +24,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; /** @@ -179,4 +188,19 @@ public class MessageModuleServiceImpl implements MessageModuleService { .filter(e -> Objects.equals(e.getModuleName(), moduleName)) .collect(Collectors.toList()); } + + @Override + public List listModuleIdByBizType(ModuleBizTypeEnum bizType) { + if (Objects.isNull(bizType)) { + log.info("bizType is null."); + return Collections.emptyList(); + } + return messageModuleDao.lambdaQuery() + .eq(MessageModule::getBizType, bizType.getCode()) + .eq(MessageModule::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .select(MessageModule::getId) + .list().stream() + .map(MessageModule::getId) + .collect(Collectors.toList()); + } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRecordServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRecordServiceImpl.java index 807fd5a6..cbc4ce60 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRecordServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRecordServiceImpl.java @@ -28,6 +28,7 @@ import cn.axzo.msg.center.domain.enums.*; import cn.axzo.msg.center.domain.request.InsideCmsReadMsgReq; import cn.axzo.msg.center.inside.notices.event.SendMessageEvent; import cn.axzo.msg.center.inside.notices.service.MessageRecordService; +import cn.axzo.msg.center.message.service.GeneralMessageMapperService; import cn.azxo.framework.common.utils.LogUtil; import cn.azxo.framework.common.utils.LogUtil.ErrorLevel; import cn.azxo.framework.common.utils.LogUtil.ErrorType; @@ -37,6 +38,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.text.StrSubstitutor; import org.springframework.beans.factory.annotation.Value; @@ -84,6 +86,9 @@ public class MessageRecordServiceImpl implements MessageRecordService { /*@Resource private IdentityProfileService identityProfileService;*/ + @Resource + private GeneralMessageMapperService generalMessageMapperService; + /** * 新增推送消息接口 * @@ -132,10 +137,11 @@ public class MessageRecordServiceImpl implements MessageRecordService { List pushMessages = new ArrayList<>(); Lists.partition(Lists.newArrayList(message.getToId()), partitionSize).forEach(toIds -> { - List messageRecords = saveBatch(basic, toIds, message.getToldIdPersonIdMap()); + Map toIdRecordMap = Maps.newHashMap(); + List messageRecords = saveBatch(basic, toIds, message.getToldIdPersonIdMap(), toIdRecordMap); + generalMessageMapperService.asyncBatchSendMessage(message, toIdRecordMap); pushMessages.addAll(messageRecords); }); - if(pushAthena) { asyncPushAthena(message, messageTemplate.getAudioFileName(), messageModule.getModuleName(), pushMessages); } @@ -158,7 +164,8 @@ public class MessageRecordServiceImpl implements MessageRecordService { @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) - public List saveBatch(MessageRecord basic, List toIds, Map toldIdPersonIdMap) { + public List saveBatch(MessageRecord basic, List toIds, Map toldIdPersonIdMap, + Map toIdRecordMap) { if (CollectionUtils.isEmpty(toIds)) { return Collections.emptyList(); } @@ -172,6 +179,7 @@ public class MessageRecordServiceImpl implements MessageRecordService { messageRecord.setToId(0L); messageRecord.setPersonId(i); pushMessages.add(messageRecord); + toIdRecordMap.put(i, messageRecord); }); } else { toIds.forEach(i -> { @@ -183,6 +191,7 @@ public class MessageRecordServiceImpl implements MessageRecordService { messageRecord.setPersonId(toldIdPersonIdMap.get(i)); } pushMessages.add(messageRecord); + toIdRecordMap.put(i, messageRecord); }); } messageRecordDao.saveBatch(pushMessages); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRelationServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRelationServiceImpl.java index 9325d114..11180058 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRelationServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/MessageRelationServiceImpl.java @@ -24,7 +24,13 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; /** @@ -188,7 +194,8 @@ public class MessageRelationServiceImpl implements MessageRelationService { } return messageRelationDao.lambdaQuery() .in(MessageRelation::getModuleId, moduleIds) - .eq(MessageRelation::getIsDelete, 0) + .eq(MessageRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .select(MessageRelation::getId) .list().stream() .map(MessageRelation::getId) .collect(Collectors.toList()); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/RawMessageRecordServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/RawMessageRecordServiceImpl.java index f6ece7fc..6e78ba58 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/RawMessageRecordServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/service/impl/RawMessageRecordServiceImpl.java @@ -45,7 +45,6 @@ import java.util.function.Function; import java.util.stream.Collectors; /** - * @description xxx * @author cold_blade * @date 2023/9/13 * @version 1.0 diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/GeneralMessageController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/GeneralMessageController.java new file mode 100644 index 00000000..a921596a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/GeneralMessageController.java @@ -0,0 +1,53 @@ +package cn.axzo.msg.center.message.controller; + +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.message.service.GeneralMessageOldService; +import cn.axzo.msg.center.message.service.GeneralMessageService; +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.axzo.msg.center.service.general.client.GeneralMessageClient; +import cn.axzo.msg.center.service.general.request.GeneralMessageOldDataStatisticRequest; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.service.general.response.GeneralMessageOldDataStatisticResponse; +import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author cold_blade + * @date 2023/10/19 + * @version 1.0 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class GeneralMessageController implements GeneralMessageClient { + + private final GeneralMessageService generalMessageService; + private final GeneralMessageOldService generalMessageOldService; + + @Override + public CommonResponse batchSend(GeneralMessageSendRequest request) { + generalMessageService.batchSendMessage(request); + return CommonResponse.success(); + } + + @Override + public CommonResponse statisticOldData( + GeneralMessageOldDataStatisticRequest request) { + return CommonResponse.success(generalMessageService.statisticOldData(request)); + } + + @Override + public CommonResponse countUnreadFromOldMessage(GeneralMessageOldDataStatisticRequest request) { + PersonDTO person = PersonDTO.from(request.getPersonId(), request.getIdentityId(), request.getIdentityType()); + return CommonResponse.success(generalMessageOldService.countUnread(person)); + } + + @Override + public CommonResponse> pageQueryOldMessage(CmsMsgQueryReq request) { + return CommonResponse.success(generalMessageOldService.pageMsgInfo(request)); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageGroupController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageGroupController.java new file mode 100644 index 00000000..631c0490 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageGroupController.java @@ -0,0 +1,56 @@ +package cn.axzo.msg.center.message.controller; + +import cn.axzo.msg.center.message.domain.param.MessageGroupNodeSaveOrUpdateParam; +import cn.axzo.msg.center.message.service.MessageGroupNodeService; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.group.client.MessageGroupClient; +import cn.axzo.msg.center.service.group.request.MessageGroupNodeAddRequest; +import cn.axzo.msg.center.service.group.request.MessageGroupNodeUpdateRequest; +import cn.axzo.msg.center.service.group.response.MessageGroupTreeNodeResponse; +import cn.azxo.framework.common.model.CommonResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 消息分类管理 + * + * @author cold_blade + * @date 2023/10/17 + * @version 1.0 + */ +@RestController +@RequiredArgsConstructor +public class MessageGroupController implements MessageGroupClient { + + private final MessageGroupNodeService messageGroupNodeService; + + @Override + public CommonResponse addNode(MessageGroupNodeAddRequest request) { + messageGroupNodeService.addGroupNode(MessageGroupNodeSaveOrUpdateParam.from(request)); + return CommonResponse.success(); + } + + @Override + public CommonResponse updateNode(MessageGroupNodeUpdateRequest request) { + messageGroupNodeService.updateGroupNode(MessageGroupNodeSaveOrUpdateParam.from(request)); + return CommonResponse.success(); + } + + @Override + public CommonResponse deleteNode(String nodeCode, Long operatorId) { + messageGroupNodeService.deleteGroupNode(operatorId, nodeCode); + return CommonResponse.success(); + } + + @Override + public CommonResponse> list(MessageCategoryEnum category) { + List groupTreeNodes = messageGroupNodeService.listGroupTree(category).stream() + .map(GroupTreeNodeDTO::toMessageGroupTreeNodeResponse) + .collect(Collectors.toList()); + return CommonResponse.success(groupTreeNodes); + } +} 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 d8e9d4d0..c24681cd 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 @@ -1,14 +1,30 @@ package cn.axzo.msg.center.message.controller; -import cn.axzo.msg.center.message.domain.param.MessageTemplateCreateParam; +import cn.axzo.core.utils.converter.BeanConverter; +import cn.axzo.msg.center.message.domain.param.MessageTemplateSaveOrUpdateParam; +import cn.axzo.msg.center.message.domain.param.RelationTemplateMapInitParam; +import cn.axzo.msg.center.message.domain.vo.RelationTemplateMapInitRequest; import cn.axzo.msg.center.message.service.MessageTemplateNewService; +import cn.axzo.msg.center.message.service.RelationTemplateMapService; import cn.axzo.msg.center.service.template.client.MessageTemplateClient; import cn.axzo.msg.center.service.template.request.MessageTemplateCreateRequest; -import cn.axzo.msg.center.service.template.request.MessageTemplateMoveToRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateStatusRequest; +import cn.axzo.msg.center.service.template.response.MessageTemplateDetailResponse; +import cn.axzo.msg.center.service.template.response.MessageTemplatePageResponse; import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; + /** * 消息模板管理 * @@ -21,17 +37,45 @@ import org.springframework.web.bind.annotation.RestController; public class MessageTemplateController implements MessageTemplateClient { private final MessageTemplateNewService messageTemplateNewService; + private final RelationTemplateMapService relationTemplateMapService; @Override - public CommonResponse addTemplate(MessageTemplateCreateRequest request) { - MessageTemplateCreateParam param = MessageTemplateCreateParam.from(request); - messageTemplateNewService.createTemplate(param); - return CommonResponse.success(param.getTemplateCode()); + public CommonResponse save(MessageTemplateCreateRequest request) { + return CommonResponse.success( + messageTemplateNewService.createTemplate(MessageTemplateSaveOrUpdateParam.from(request))); } @Override - public CommonResponse batchMove(MessageTemplateMoveToRequest request) { - // TODO: [cold_blade] [P2] 模板关联的批量移动 - return null; + public CommonResponse update(MessageTemplateUpdateRequest request) { + messageTemplateNewService.updateTemplate(MessageTemplateSaveOrUpdateParam.from(request)); + return CommonResponse.success(); + } + + @Override + public CommonResponse detail(String templateCode) { + return CommonResponse.success(messageTemplateNewService.detail(templateCode)); + } + + @Override + public CommonResponse> page(MessageTemplatePageRequest request) { + return CommonResponse.success(messageTemplateNewService.page(request)); + } + + @Override + public CommonResponse> listByCodes(Collection templateCodes) { + return CommonResponse.success(messageTemplateNewService.listByCodes(templateCodes)); + } + + @Override + public CommonResponse updateStatus(MessageTemplateUpdateStatusRequest request) { + messageTemplateNewService.updateStatus(request.getOperatorId(), request.getTemplateCode(), request.getStatus()); + return CommonResponse.success(); + } + + @PostMapping(value = "/message/template/relation/init", produces = {MediaType.APPLICATION_JSON_VALUE}) + public CommonResponse initRelationTemplateMap(@RequestBody @Valid RelationTemplateMapInitRequest request) { + RelationTemplateMapInitParam param = BeanConverter.convert(request, RelationTemplateMapInitParam.class); + relationTemplateMapService.init(param); + return CommonResponse.success(); } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateGroupRelationController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateGroupRelationController.java new file mode 100644 index 00000000..a72a10cd --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/MessageTemplateGroupRelationController.java @@ -0,0 +1,62 @@ +package cn.axzo.msg.center.message.controller; + +import cn.axzo.msg.center.common.utils.PageHelperUtil; +import cn.axzo.msg.center.message.service.MessageTemplateGroupService; +import cn.axzo.msg.center.message.service.MessageTemplateNewService; +import cn.axzo.msg.center.service.dto.MessageBaseTemplateDTO; +import cn.axzo.msg.center.service.group.client.MessageTemplateGroupRelationClient; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationMoveRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationPageRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationRemoveRequest; +import cn.axzo.msg.center.service.group.response.MessageTemplateGroupRelationResponse; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author cold_blade + * @date 2023/10/17 + * @version 1.0 + */ +@RestController +@RequiredArgsConstructor +public class MessageTemplateGroupRelationController implements MessageTemplateGroupRelationClient { + + private final MessageTemplateNewService messageTemplateNewService; + private final MessageTemplateGroupService messageTemplateGroupService; + + @Override + public CommonResponse> page(MessageTemplateGroupRelationPageRequest request) { + MessageTemplatePageRequest pageRequest = new MessageTemplatePageRequest(); + pageRequest.setPage(request.getPage()); + pageRequest.setPageSize(request.getPageSize()); + pageRequest.setTemplateName(request.getTemplateName()); + pageRequest.setGroupNodeCode(request.getNodeCode()); + Page result = messageTemplateNewService.pageBaseTemplate(pageRequest); + return CommonResponse.success(PageHelperUtil.convert(result, this::convert)); + } + + @Override + public CommonResponse move(MessageTemplateGroupRelationMoveRequest request) { + messageTemplateGroupService.move(request.getCurNodeCode(), request.getTargetNodeCode(), + request.getTemplateCodes()); + return CommonResponse.success(); + } + + @Override + public CommonResponse remove(MessageTemplateGroupRelationRemoveRequest request) { + messageTemplateGroupService.remove(request.getCurNodeCode(), request.getTemplateCodes()); + return CommonResponse.success(); + } + + private MessageTemplateGroupRelationResponse convert(MessageBaseTemplateDTO dto) { + MessageTemplateGroupRelationResponse response = new MessageTemplateGroupRelationResponse(); + response.setTitle(dto.getTitle()); + response.setTemplateCode(dto.getCode()); + response.setTemplateName(dto.getName()); + response.setCreateTimestamp(dto.getCreateTimestamp()); + return response; + } +} 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..3f0318a6 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/GroupTreeNodePathDTO.java @@ -0,0 +1,68 @@ +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 org.apache.commons.lang3.StringUtils; + +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 String parseLeafNodeCode(String nodeCodePath) { + if (StringUtils.isBlank(nodeCodePath)) { + return null; + } + String[] nodeCodes = nodeCodePath.split(NODE_CODE_PATH_SPLITER); + // 最后一个为叶节点的结点编码 + return nodeCodes[nodeCodes.length - 1]; + } + + 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..9368d462 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageGroupNodeStatisticDTO.java @@ -0,0 +1,81 @@ +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 = new MessageGroupNodeResponse(); + response.setCategory(treeNode.getCategory()); + response.setNodeCode(treeNode.getNodeCode()); + response.setNodeName(treeNode.getNodeName()); + response.setParentNodeCode(treeNode.getParentNodeCode()); + response.setPendingCount(this.pendingCount); + 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/dto/MessageTemplateDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageTemplateDTO.java index 88fafc90..37e39d7e 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageTemplateDTO.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/MessageTemplateDTO.java @@ -1,16 +1,23 @@ package cn.axzo.msg.center.message.domain.dto; import cn.axzo.msg.center.domain.entity.MessageBaseTemplate; +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import cn.axzo.msg.center.utils.JSONObjectUtil; import com.alibaba.fastjson.JSON; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.apache.commons.collections.CollectionUtils; import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @description @@ -45,9 +52,9 @@ public class MessageTemplateDTO implements Serializable { */ private String content; /** - * 卡片信息,json字串 + * 消息卡片信息标签列表 */ - private String cardContent; + private List msgCardContentItems; /** * 所属消息类型 */ @@ -60,6 +67,14 @@ public class MessageTemplateDTO implements Serializable { * 模板路由信息 */ private List routers; + /** + * 推送终端 + */ + private List pushTerminals; + /** + * APP最小版本支持,可不配 + */ + private String minAppVersion; public static MessageTemplateDTO from(MessageBaseTemplate baseTemplate, List routers) { return MessageTemplateDTO.builder() @@ -67,13 +82,24 @@ public class MessageTemplateDTO implements Serializable { .code(baseTemplate.getCode()) .title(baseTemplate.getTitle()) .content(baseTemplate.getContent()) - .cardContent(baseTemplate.getCardContent()) + .msgCardContentItems(JSONObjectUtil.parseArray(baseTemplate.getCardContent(), MessageCardContentItemDTO.class)) .msgCategory(baseTemplate.getMsgCategory()) .icon(baseTemplate.getIcon()) .routers(routers) + .pushTerminals(JSON.parseArray(baseTemplate.getPushTerminal(), PushTerminalEnum.class)) + .minAppVersion(baseTemplate.getMinAppVersion()) .build(); } + public Map toCardContentMap() { + if (CollectionUtils.isEmpty(msgCardContentItems)) { + return Collections.emptyMap(); + } + Map map = new HashMap<>(); + msgCardContentItems.forEach(e -> map.put(e.getLabel(), e.getValue())); + return map; + } + @Override public String toString() { return JSON.toJSONString(this); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/PendingMessageDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/PendingMessageDTO.java index d073790c..cc0e4727 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/PendingMessageDTO.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/PendingMessageDTO.java @@ -143,6 +143,7 @@ public class PendingMessageDTO implements Serializable { .updateTimestamp(DateFormatUtil.toTimestamp(this.updateTime)) .routers(this.routers) .routerParams(this.routerParams) + .bizExtParams("{}") .build(); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/RawMessageRouterDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/RawMessageRouterDTO.java index 1c8ea27a..d478c6be 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/RawMessageRouterDTO.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/RawMessageRouterDTO.java @@ -1,10 +1,15 @@ package cn.axzo.msg.center.message.domain.dto; import cn.axzo.msg.center.domain.entity.MessageTemplateRouter; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; import cn.axzo.msg.center.service.dto.MessageRouterDTO; +import cn.axzo.msg.center.service.dto.MessageRouterTerminalDTO; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; +import cn.axzo.msg.center.utils.MessageRouterUtil; import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -12,6 +17,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; /** * @description @@ -40,54 +47,96 @@ public class RawMessageRouterDTO implements Serializable { */ private RouterCategoryEnum category; /** - * 页面地址 OR API接口地址 + * 路由终端 */ - private String url; - /** - * 页面地址所属应用端(如果是API接口地址,请忽略改字段值) - * WEB: web端页面 - * MINI_PROGRAM: 安心筑小程序端页面 - * IOS: 原生IOS端页面 - * ANDROID: 原生Android端页面 - * WEB_VIEW: H5页面 - * WECHAT_MINI_PROGRAM: 微信小程序页面 - */ - private TerminalTypeEnum terminalType; + private List terminals; /** * 模板编码 */ private String templateCode; + /** + * 按钮样式配置 + */ + private List style; - public static RawMessageRouterDTO from(MessageTemplateRouter msgTemplateRouter) { + public static RawMessageRouterDTO from(List msgTemplateRouters) { + MessageTemplateRouter router = msgTemplateRouters.get(0); + List terminals = msgTemplateRouters.stream() + .map(RawMessageRouterDTO::convert2Terminal) + .collect(Collectors.toList()); return RawMessageRouterDTO.builder() - .desc(msgTemplateRouter.getName()) - .url(msgTemplateRouter.getUrl()) - .category(msgTemplateRouter.getCategory()) - .terminalType(msgTemplateRouter.getTerminalType()) - .templateCode(msgTemplateRouter.getTemplateCode()) + .desc(router.getName()) + .category(router.getCategory()) + .templateCode(router.getTemplateCode()) + .style(MessageRouterUtil.parseButtonStyle(router.getStyle())) + .terminals(terminals) .build(); } - public static RawMessageRouterDTO from(MessageRouterDTO router, String templateCode) { + public static RawMessageRouterDTO from(MessageRouterButtonDTO router, String templateCode) { return RawMessageRouterDTO.builder() .desc(router.getDesc()) - .url(router.getUrl()) .category(router.getCategory()) - .terminalType(router.getTerminalType()) + .terminals(router.getTerminals()) + .style(router.getStyle()) .templateCode(templateCode) .build(); } - public MessageTemplateRouter toMessageTemplateRouter() { + public MessageRouterButtonDTO toMessageRouterButton() { + return MessageRouterButtonDTO.builder() + .desc(this.desc) + .category(this.category) + .terminals(this.terminals) + .style(this.style) + .build(); + } + + public List toMessageTemplateRouters() { + return this.terminals.stream() + .map(this::convert) + .collect(Collectors.toList()); + } + + public MessageRouterDTO toMessageRouter(TerminalTypeEnum terminalType) { + MessageRouterTerminalDTO terminal = MessageRouterUtil.select(this, terminalType); + return MessageRouterDTO.builder() + .desc(this.desc) + .category(this.category) + .style(this.style) + .terminalType(terminal.getTerminalType()) + .url(terminal.getUrl()) + .build(); + } + + private MessageTemplateRouter convert(MessageRouterTerminalDTO terminal) { MessageTemplateRouter router = new MessageTemplateRouter(); - router.setName(this.desc); - router.setUrl(this.url); - router.setCategory(this.category); - router.setTerminalType(this.terminalType); - router.setTemplateCode(this.templateCode); + router.setName(desc); + router.setCategory(category); + router.setTemplateCode(templateCode); + router.setStyle(JSON.toJSONString(style)); + router.setUrl(terminal.getUrl()); + router.setTerminalType(terminal.getTerminalType()); return router; } + private static MessageRouterTerminalDTO convert2Terminal(MessageTemplateRouter router) { + return MessageRouterTerminalDTO.builder() + .url(router.getUrl()) + .terminalType(router.getTerminalType()) + .build(); + } + + public RawMessageRouterDTO deepClone() { + return RawMessageRouterDTO.builder() + .desc(this.desc) + .category(this.category) + .terminals(this.terminals.stream().map(MessageRouterTerminalDTO::deepClone).collect(Collectors.toList())) + .templateCode(this.templateCode) + .style(Lists.newArrayList(this.getStyle())) + .build(); + } + @Override public String toString() { return JSON.toJSONString(this); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/SendImMessageDTO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/SendImMessageDTO.java new file mode 100644 index 00000000..650088e9 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/dto/SendImMessageDTO.java @@ -0,0 +1,47 @@ +package cn.axzo.msg.center.message.domain.dto; + +import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/25 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SendImMessageDTO implements Serializable { + + private static final long serialVersionUID = 3385679937092148803L; + + /** + * 自然人id + */ + private Long personId; + /** + * im消息id + */ + private String imMsgId; + + public static SendImMessageDTO from(MessageDispatchResp resp) { + return SendImMessageDTO.builder() + .imMsgId(resp.getMsgid()) + .personId(Long.parseLong(resp.getPersonId())) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageGroupNodeSaveOrUpdateParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageGroupNodeSaveOrUpdateParam.java new file mode 100644 index 00000000..161a68aa --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageGroupNodeSaveOrUpdateParam.java @@ -0,0 +1,69 @@ +package cn.axzo.msg.center.message.domain.param; + +import cn.axzo.core.utils.converter.BeanConverter; +import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; +import cn.axzo.msg.center.service.group.request.MessageGroupNodeAddRequest; +import cn.axzo.msg.center.service.group.request.MessageGroupNodeUpdateRequest; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author cold_blade + * @date 2023/10/17 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageGroupNodeSaveOrUpdateParam { + + /** + * 操作者的自然人id + */ + private Long operatorId; + /** + * 父节点编码 + */ + private String parentNodeCode; + /** + * 待添加结点名称 + */ + private String nodeName; + /** + * 待修改的结点编码 + */ + private String nodeCode; + /** + * 待添加结点类型 + * GENERAL_MESSAGE_CENTER: 通知的业务中心 + * GENERAL_MESSAGE_MODULE: 消息模块 + * GENERAL_MESSAGE_CATEGORY: 消息分类 + * PENDING_MESSAGE_CENTER: 待办的业务中心 + * PENDING_MESSAGE_MODULE: 待办模块 + * PENDING_MESSAGE_CATEGORY: 待办分类 + */ + private MessageGroupNodeCategoryEnum category; + /** + * 待添加结点图标 + */ + private String icon; + + public static MessageGroupNodeSaveOrUpdateParam from(MessageGroupNodeAddRequest request) { + return BeanConverter.convert(request, MessageGroupNodeSaveOrUpdateParam.class); + } + + public static MessageGroupNodeSaveOrUpdateParam from(MessageGroupNodeUpdateRequest request) { + return BeanConverter.convert(request, MessageGroupNodeSaveOrUpdateParam.class); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} 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 deleted file mode 100644 index f99c1cbe..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateCreateParam.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.axzo.msg.center.message.domain.param; - -import cn.axzo.msg.center.service.dto.MessageRouterDTO; -import cn.axzo.msg.center.service.enums.MessageCategoryEnum; -import cn.axzo.msg.center.service.template.request.MessageTemplateCreateRequest; -import cn.axzo.msg.center.utils.UUIDUtil; -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; - -/** - * @description - * - * @author cold_blade - * @date 2023/10/5 - * @version 1.0 - */ -@Setter -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class MessageTemplateCreateParam implements Serializable { - - private static final long serialVersionUID = 6657624182580261353L; - - /** - * 模板名称 - */ - private String templateName; - /** - * 系统自动生成的模板code - */ - private String templateCode; - /** - * 所属消息类型 - * GENERAL_MESSAGE: 普通消息 - * PENDING_MESSAGE: 待办消息 - */ - private MessageCategoryEnum msgCategory; - /** - * 模板标题 - */ - private String title; - /** - * 模板类容 - */ - private String content; - /** - * 卡片信息,json字串 - */ - private String cardContent; - /** - * 模板icon - */ - private String icon; - /** - * 操作者自然人id - */ - private Long operatorId; - /** - * 路由列表 - */ - private List routers; - - public static MessageTemplateCreateParam from(MessageTemplateCreateRequest request) { - return MessageTemplateCreateParam.builder() - .templateName(request.getTemplateName()) - .templateCode(UUIDUtil.uuidString()) - .msgCategory(request.getCategory()) - .title(request.getMsgTitle()) - .content(request.getMsgContent()) - .cardContent(request.getMsgCardInfo()) - .icon(request.getMsgIcon()) - .operatorId(request.getOperatorId()) - .routers(request.getRouters()) - .build(); - } - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateSaveOrUpdateParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateSaveOrUpdateParam.java new file mode 100644 index 00000000..baa17a8e --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateSaveOrUpdateParam.java @@ -0,0 +1,118 @@ +package cn.axzo.msg.center.message.domain.param; + +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +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.service.template.request.MessageTemplateUpdateRequest; +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; + +/** + * @description + * + * @author cold_blade + * @date 2023/10/5 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageTemplateSaveOrUpdateParam implements Serializable { + + private static final long serialVersionUID = 6657624182580261353L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 模板编码 + */ + private String templateCode; + /** + * 消息分类树的叶结点的结点编码列表 + */ + private List leafGroupNodes; + /** + * 所属消息类型 + * GENERAL_MESSAGE: 普通消息 + * PENDING_MESSAGE: 待办消息 + */ + private MessageCategoryEnum msgCategory; + /** + * 模板标题 + */ + private String title; + /** + * 模板类容 + */ + private String content; + /** + * 消息卡片信息标签列表 + */ + private List msgCardContentItems; + /** + * 模板icon + */ + private String icon; + /** + * 操作者自然人id + */ + private Long operatorId; + /** + * 路由列表 + */ + private List routers; + /** + * 推送终端配置 + * B_ENTERPRISE_APP: B-安心筑企业版 + * C_WORKER_APP: C-安心筑工人版 + */ + private List pushTerminals; + + public static MessageTemplateSaveOrUpdateParam from(MessageTemplateCreateRequest request) { + return MessageTemplateSaveOrUpdateParam.builder() + .templateName(request.getTemplateName()) + .msgCategory(request.getCategory()) + .leafGroupNodes(request.getLeafGroupNodes()) + .title(request.getMsgTitle()) + .content(request.getMsgContent()) + .msgCardContentItems(request.getMsgCardContentItems()) + .icon(request.getMsgIcon()) + .operatorId(request.getOperatorId()) + .routers(request.getRouters()) + .pushTerminals(request.getPushTerminals()) + .build(); + } + + public static MessageTemplateSaveOrUpdateParam from(MessageTemplateUpdateRequest request) { + return MessageTemplateSaveOrUpdateParam.builder() + .templateName(request.getTemplateName()) + .templateCode(request.getTemplateCode()) + .leafGroupNodes(request.getLeafGroupNodes()) + .title(request.getMsgTitle()) + .content(request.getMsgContent()) + .msgCardContentItems(request.getMsgCardContentItems()) + .icon(request.getMsgIcon()) + .operatorId(request.getOperatorId()) + .routers(request.getRouters()) + .pushTerminals(request.getPushTerminals()) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/PendingMessagePushParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/PendingMessagePushParam.java index 429b0a31..61f726f7 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/PendingMessagePushParam.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/PendingMessagePushParam.java @@ -69,6 +69,10 @@ public class PendingMessagePushParam implements Serializable { * 业务描述eg:流程结点描述 */ private String bizDesc; + /** + * 业务扩展参数,JSON字符串格式 + */ + private String bizExtParams; /** * 路由参数(json string) */ diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/RelationTemplateMapInitParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/RelationTemplateMapInitParam.java new file mode 100644 index 00000000..cb1bca6a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/RelationTemplateMapInitParam.java @@ -0,0 +1,31 @@ +package cn.axzo.msg.center.message.domain.param; + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Collection; + +/** + * @author cold_blade + * @date 2023/10/27 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RelationTemplateMapInitParam { + + private Collection relationIds; + private String groupNodeCode; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/GeneralMessagePushVO.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/GeneralMessagePushVO.java new file mode 100644 index 00000000..5216d5b6 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/GeneralMessagePushVO.java @@ -0,0 +1,230 @@ +package cn.axzo.msg.center.message.domain.vo; + +import cn.axzo.msg.center.domain.entity.GeneralMessageRecord; +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.enums.RouterCategoryEnum; +import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/19 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GeneralMessagePushVO implements Serializable { + + private static final long serialVersionUID = -9017550674630922381L; + + /** + * 消息的唯一标识 + */ + private String identityCode; + /** + * 模板编码 + */ + private String templateCode; + /** + * 顶部图片 - 模板icon地址 + */ + private String cardBannerUrl; + /** + * 卡片标题标题 - 消息标题 + */ + private String cardTitle; + /** + * 详情按钮 + */ + private CardButton cardDetailButton; + /** + * 副标题 - 消息所属组织信息 + */ + private List subtitles; + /** + * 消息内容 + */ + private String cardContent; + /** + * 卡片信息 + */ + private List cardExtension; + /** + * 按钮操作区域 + */ + private List cardButtons; + /** + * 业务编码 + */ + private String bizCode; + /** + * 消息发送时间戳 + */ + private Long sendTimestamp; + + public static GeneralMessagePushVO from(GeneralMessageRecord record, String templateIcon, String orgIcon, + List routerButtons, + List cardContentItems) { + CardButton cardDetailButton = CollectionUtils.isEmpty(routerButtons) ? null : routerButtons.stream() + .filter(e -> RouterCategoryEnum.DETAIL.equals(e.getCategory())) + .findFirst() + .map(CardButton::from) + .orElse(null); + List cardButtons = CollectionUtils.isEmpty(routerButtons) ? Collections.emptyList() : + routerButtons.stream() + .filter(e -> !RouterCategoryEnum.DETAIL.equals(e.getCategory())) + .filter(MessageRouterButtonDTO::isShowOnCard) + .map(CardButton::from) + .collect(Collectors.toList()); + List cardExtension = CollectionUtils.isEmpty(cardContentItems) ? Collections.emptyList() : + cardContentItems.stream() + .map(CardExtensionItem::from) + .collect(Collectors.toList()); + List subtitles = Collections.emptyList(); + Optional subtitleOp = Subtitle.from(record, orgIcon); + if (subtitleOp.isPresent()) { + subtitles = Lists.newArrayList(subtitleOp.get()); + } + return GeneralMessagePushVO.builder() + .identityCode(record.getIdentityCode()) + .templateCode(record.getTemplateCode()) + .cardBannerUrl(templateIcon) + .cardTitle(record.getTitle()) + .cardDetailButton(cardDetailButton) + .subtitles(subtitles) + .cardContent(record.getContent()) + .cardExtension(cardExtension) + .cardButtons(cardButtons) + .bizCode(record.getBizCode()) + .sendTimestamp(record.getCreateAt().getTime()) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } + + @Setter + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class Subtitle { + + /** + * 图标 - 对应消息所属组织的图标 + */ + private String iconUrl; + /** + * 标题 - 对应消息所属组织的名称 + */ + private String title; + + static Optional from(GeneralMessageRecord record, String orgIcon) { + if (StringUtils.isBlank(record.getOrgName())) { + return Optional.empty(); + } + return Optional.of(Subtitle.builder() + .title(record.getOrgName()) + .iconUrl(orgIcon) + .build()); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } + } + + @Setter + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class CardButton { + + /** + * 按钮标题 + */ + private String title; + /** + * 按钮操作类型: JUMP - 页面跳转, ACTION - 接口调用 + */ + private String action; + /** + * 按钮样式 + */ + private Boolean isHighlight; + /** + * 按钮点击后的跳转地址 + */ + private List actionPaths; + + static CardButton from(MessageRouterButtonDTO routerButton) { + return CardButton.builder() + .title(routerButton.getDesc()) + .action(routerButton.getCategory().name()) + .isHighlight(routerButton.isHighlight()) + .actionPaths(routerButton.getTerminals().stream() + .map(e -> new ButtonAction(e.getTerminalType().name(), e.getUrl())) + .collect(Collectors.toList()) + ).build(); + } + + @Getter + @AllArgsConstructor + static class ButtonAction { + + /** + * 平台 + */ + private String platform; + /** + * 跳转地址 + */ + private String url; + } + } + + @Setter + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class CardExtensionItem { + + private String title; + + private String detail; + + static CardExtensionItem from(MessageCardContentItemDTO cardContentItem) { + return CardExtensionItem.builder() + .title(cardContentItem.getLabel()) + .detail(cardContentItem.getValue()) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/RelationTemplateMapInitRequest.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/RelationTemplateMapInitRequest.java new file mode 100644 index 00000000..f8a49139 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/vo/RelationTemplateMapInitRequest.java @@ -0,0 +1,33 @@ +package cn.axzo.msg.center.message.domain.vo; + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import java.util.Collection; + +/** + * @author cold_blade + * @date 2023/10/27 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RelationTemplateMapInitRequest { + + private Collection relationIds; + @NotBlank(message = "groupNodeCode is required") + private String groupNodeCode; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageMapperService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageMapperService.java new file mode 100644 index 00000000..c30c5165 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageMapperService.java @@ -0,0 +1,25 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.api.request.GeneralMessageReq; +import cn.axzo.msg.center.domain.entity.MessageRecord; + +import java.util.List; +import java.util.Map; + +/** + * 新老版本消息映射的相关接口 + * + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +public interface GeneralMessageMapperService { + + /** + * 异步批量发送消息 + * + * @param request 发送消息时的请求参数 + * @param toIdMessageRecordMap 接收者身份id与旧消息记录的映射关系 + */ + void asyncBatchSendMessage(GeneralMessageReq request, Map toIdMessageRecordMap); +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageOldService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageOldService.java new file mode 100644 index 00000000..b4049ed6 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageOldService.java @@ -0,0 +1,46 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.azxo.framework.common.model.Page; + +import java.util.List; + +/** + * 旧普通消息的相关接口 + * + * @author cold_blade + * @date 2023/10/24 + * @version 1.0 + */ +public interface GeneralMessageOldService { + + /** + * 统计未读的消息数量 + * 注: 该接口作为IM进入旧消息模块的入口,需要过滤双发至IM的消息 + * + * @param person 身份信息 + * @return 未读消息数量 + */ + int countUnread(PersonDTO person); + + /** + * 统计未读的消息数量 + * 注: 该接口作为IM进入旧消息模块的入口,需要过滤双发至IM的消息 + * + * @param person 身份信息 + * @param excludeMsgIds 待排除的消息记录id + * @return 未读消息数量 + */ + int countUnread(PersonDTO person, List excludeMsgIds); + + /** + * 分页查询旧的普通消息记录 + * 注: 该接口作为IM进入旧消息模块的入口,需要过滤双发至IM的消息 + * + * @param request 分页查询参数 + * @return 过滤后的旧的普通消息记录 + */ + Page pageMsgInfo(CmsMsgQueryReq request); +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageService.java new file mode 100644 index 00000000..eb2fa03a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/GeneralMessageService.java @@ -0,0 +1,32 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.message.domain.dto.SendImMessageDTO; +import cn.axzo.msg.center.service.general.request.GeneralMessageOldDataStatisticRequest; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.service.general.response.GeneralMessageOldDataStatisticResponse; + +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/19 + * @version 1.0 + */ +public interface GeneralMessageService { + + /** + * 发送消息 + * + * @param request 消息所需参数 + * @return 请求的唯一标识 + */ + List batchSendMessage(GeneralMessageSendRequest request); + + /** + * 统计旧数据 + * + * @param request 统计参数 + * @return 旧数据的未读数以及最新一条消息 + */ + GeneralMessageOldDataStatisticResponse statisticOldData(GeneralMessageOldDataStatisticRequest request); +} 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..3a328708 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageGroupNodeService.java @@ -0,0 +1,73 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.message.domain.param.MessageGroupNodeSaveOrUpdateParam; +import cn.axzo.msg.center.service.dto.GroupTreeNodeDTO; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 消息分类结点管理 + * + * @author cold_blade + * @date 2023/10/14 + * @version 1.0 + */ +public interface MessageGroupNodeService { + + /** + * 获取分类结点(叶结点)名称的路径 + * + * @param groupNodeCodePaths 分类结点(叶结点)的编码列表 + * @return 分类结点(叶结点)名称的路径 + */ + Map groupNodeNamePaths(Collection groupNodeCodePaths); + + /** + * 获取分类结点(叶结点)编码的路径 + * + * @param leafGroupNodeCodes 分类结点(叶结点)的编码列表 + * @return 分类结点(叶结点)编码的路径 + */ + Map leafGroupNodeCodePaths(Collection leafGroupNodeCodes); + + /** + * 根据结点编码查询结点信息 + * + * @param rootNodeCode 结点编码 + * @return 结点信息 + */ + Optional queryRootNode(String rootNodeCode); + + /** + * 新增结点 + * + * @param param 结点内容 + */ + void addGroupNode(MessageGroupNodeSaveOrUpdateParam param); + + /** + * 编辑结点 + * + * @param param 结点内容 + */ + void updateGroupNode(MessageGroupNodeSaveOrUpdateParam param); + + /** + * 删除结点 + * + * @param operatorId 操作人id + * @param nodeCode 待删除结点的编码 + */ + void deleteGroupNode(Long operatorId, String nodeCode); + + /** + * 查询消息/待办 或者两者的分类树 + * @param category 消息/待办 + * @return 分类树列表 + */ + List listGroupTree(MessageCategoryEnum category); +} 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..dca2c097 --- /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 listAllGroupTree(); + + /** + * 获取指定结点所在的树的根节点 + * + * @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/MessageSendTwiceRecordService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageSendTwiceRecordService.java new file mode 100644 index 00000000..144c6c4e --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageSendTwiceRecordService.java @@ -0,0 +1,21 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.api.request.GeneralMessageReq; +import cn.axzo.msg.center.service.dto.PersonDTO; + +import java.util.List; +import java.util.Map; + +/** + * 双发消息记录service + * + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +public interface MessageSendTwiceRecordService { + + List listByPerson(Long personId); + + void batchSave(Map msgRecordIdPersonIdMap); +} 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..0cd3bf30 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,6 +1,8 @@ package cn.axzo.msg.center.message.service; +import java.util.Collection; import java.util.List; +import java.util.Map; /** * 消息模板分类管理 @@ -18,4 +20,61 @@ public interface MessageTemplateGroupService { * @return 模板编码列表 */ List listMessageTemplateCodes(String groupNodeCode); + + /** + * 模板关联分类 + * + * @param templateNode 模板编码 + * @param groupNodeCodes 分类结点编码列表 + */ + void templateGroup(String templateNode, Collection groupNodeCodes); + + /** + * 模板关联分类 + * + * @param templateGroupMap 模板编码与分类结点编码列表的map + */ + void templateGroup(Map> templateGroupMap); + + /** + * 更新模板的分类关系 + * + * @param templateNode 模板编码 + * @param groupNodeCodes 新的模板的分类结点编码 + */ + void updateTemplateGroup(String templateNode, Collection groupNodeCodes); + + /** + * 解除模板关联的分类 + * + * @param templateNode 模板编码 + */ + void deleteTemplateGroup(String templateNode); + + /** + * 通过消息模板编码查询其关联的分类的path + * + * @param templateCodes 消息模板编码集合 + * @return 模板编码与分类的path列表的映射关系 + */ + Map> listMessageTemplateGroupPaths(Collection templateCodes); + + /** + * 解除模板与当前分类的关联关系 + * 注:当模板只关联一个分类时,该关联关系不能解除 + * + * @param curGroupNodeCode 当前分类结点的编码 + * @param templateCodes 待解除关连关系的模板编码 + */ + void remove(String curGroupNodeCode, Collection templateCodes); + + /** + * 解除模板与当前分类的关联关系 + * 注:当模板只关联一个分类时,该关联关系不能解除 + * + * @param srcGroupNodeCode 源分类结点的编码 + * @param tgtGroupNodeCode 目标分类结点的编码 + * @param templateCodes 待变更关连关系的模板编码 + */ + void move(String srcGroupNodeCode, String tgtGroupNodeCode, Collection templateCodes); } 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..74d94bf8 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 @@ -1,8 +1,15 @@ package cn.axzo.msg.center.message.service; import cn.axzo.msg.center.message.domain.dto.MessageTemplateDTO; -import cn.axzo.msg.center.message.domain.param.MessageTemplateCreateParam; +import cn.axzo.msg.center.message.domain.param.MessageTemplateSaveOrUpdateParam; +import cn.axzo.msg.center.service.dto.MessageBaseTemplateDTO; +import cn.axzo.msg.center.service.enums.StatusEnum; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.axzo.msg.center.service.template.response.MessageTemplateDetailResponse; +import cn.axzo.msg.center.service.template.response.MessageTemplatePageResponse; +import cn.azxo.framework.common.model.Page; +import java.util.Collection; import java.util.List; import java.util.Optional; @@ -20,7 +27,31 @@ public interface MessageTemplateNewService { * * @param param 模板内容参数 */ - void createTemplate(MessageTemplateCreateParam param); + String createTemplate(MessageTemplateSaveOrUpdateParam param); + + /** + * 编辑模板 + * + * @param param 模板内容参数 + */ + void updateTemplate(MessageTemplateSaveOrUpdateParam param); + + /** + * 通过模板便阿门查询模板详情 + * + * @param templateCode 模板编码 + * @return 模板详情信息 + */ + MessageTemplateDetailResponse detail(String templateCode); + + /** + * 更新模板状态 + * + * @param operatorId 操作人id + * @param templateCode 模板编码 + * @param status 新的模板状态 + */ + void updateStatus(Long operatorId, String templateCode, StatusEnum status); /** * 通过模板编码查询模板信息 @@ -37,4 +68,28 @@ public interface MessageTemplateNewService { * @return 模板信息 */ List listByTemplateCodes(List msgTemplateCodes); + + /** + * 分页查询模板数据 + * + * @param request 分页请求参数 + * @return 模板数据列表 + */ + Page page(MessageTemplatePageRequest request); + + /** + * 分页查询模板基础数据 + * + * @param request 分页请求参数 + * @return 模板数据列表 + */ + Page pageBaseTemplate(MessageTemplatePageRequest request); + + /** + * 通过模板编码查询对应的模板 + * + * @param templateCodes 模板编码集合 + * @return 模板列表 + */ + List listByCodes(Collection templateCodes); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateRouterService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateRouterService.java index 915b43c1..51d71044 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateRouterService.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/MessageTemplateRouterService.java @@ -1,6 +1,7 @@ package cn.axzo.msg.center.message.service; import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; import java.util.List; import java.util.Map; @@ -30,6 +31,14 @@ public interface MessageTemplateRouterService { */ void batchInsert(List routers); + /** + * 更新模板的路由信息 + * + * @param templateCode 模板编码 + * @param routers 路由列表 + */ + void updateTemplateRoutes(String templateCode, List routers); + /** * 根据消息模板编码查询配置的路由列表 * 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/RelationTemplateMapService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/RelationTemplateMapService.java new file mode 100644 index 00000000..327b90ec --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/RelationTemplateMapService.java @@ -0,0 +1,23 @@ +package cn.axzo.msg.center.message.service; + +import cn.axzo.msg.center.message.domain.param.RelationTemplateMapInitParam; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * 新老模板的关联关系service + * + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +public interface RelationTemplateMapService { + + Optional queryByRelationId(Long relationId); + + void mapRelationAndTemplate(Map map); + + void init(RelationTemplateMapInitParam param); +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageMapperServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageMapperServiceImpl.java new file mode 100644 index 00000000..46288909 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageMapperServiceImpl.java @@ -0,0 +1,146 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.msg.center.api.enums.MsgTypeEnum; +import cn.axzo.msg.center.api.request.GeneralMessageReq; +import cn.axzo.msg.center.domain.entity.MessageRecord; +import cn.axzo.msg.center.message.domain.dto.SendImMessageDTO; +import cn.axzo.msg.center.message.service.GeneralMessageMapperService; +import cn.axzo.msg.center.message.service.GeneralMessageService; +import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService; +import cn.axzo.msg.center.message.service.RelationTemplateMapService; +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.axzo.msg.center.service.enums.IdentityTypeEnum; +import cn.axzo.msg.center.service.enums.OrganizationTypeEnum; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.utils.PersonIdentityUtil; +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import jodd.util.concurrent.ThreadFactoryBuilder; +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.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class GeneralMessageMapperServiceImpl implements GeneralMessageMapperService { + + private final ThreadFactory asyncSendMsgThreadFactory = ThreadFactoryBuilder.create() + .setDaemon(true).setNameFormat("ASYNC_SEND_IM_MESSAGE_%d").get(); + private final ExecutorService asyncSendMsgExecutorService = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, + new ArrayBlockingQueue<>(1024), asyncSendMsgThreadFactory); + + private final GeneralMessageService generalMessageService; + private final RelationTemplateMapService relationTemplateMapService; + private final MessageSendTwiceRecordService messageSendTwiceRecordService; + + @Override + public void asyncBatchSendMessage(GeneralMessageReq request, Map toIdMessageRecordMap) { + log.info("do some check before send im message. relationId:[{}]", request.getRelationId()); + if (MsgTypeEnum.PENDING_MESSAGE.equals(request.getType())) { + log.info("pending message is not supported."); + return; + } + if (Objects.isNull(request.getRelationId())) { + log.info("relation id is null."); + return; + } + if (MapUtil.isEmpty(request.getToldIdPersonIdMap())) { + // 由于IM那边是根据personId来创建账户的,所以强依赖personId + log.info("toIdPersonIdMap is empty."); + return; + } + // 异步发送IM消息 + log.info("start to async send im message. relationId:[{}]", request.getRelationId()); + CompletableFuture.runAsync(() -> doBatchSendMessage(request, toIdMessageRecordMap), + asyncSendMsgExecutorService); + } + + private void doBatchSendMessage(GeneralMessageReq request, Map toIdMessageRecordMap) { + String templateCode = relationTemplateMapService.queryByRelationId(request.getRelationId()).orElse(null); + if (StringUtils.isBlank(templateCode)) { + log.info("the relationId([{}]) is not map any new message template. ", request.getRelationId()); + return; + } + log.info("start to send im message. relationId:[{}], templateCode:[{}]", request.getRelationId(), templateCode); + try { + // 发送IM消息 + GeneralMessageSendRequest sendImMsgRequest = buildSendRequest(request, templateCode, toIdMessageRecordMap.keySet()); + // IM消息的发送是基于personId+应用终端的(eg: 工人端/企业端) + List result = generalMessageService.batchSendMessage(sendImMsgRequest); + if (CollectionUtils.isEmpty(result)) { + log.info("there is not any person successfully send im message. relationId:[{}]", request.getRelationId()); + return; + } + // 记录发送了IM消息的旧消息 + recordSendImMessage(toIdMessageRecordMap, result, request.getToldIdPersonIdMap()); + } catch (Exception e) { + log.warn("broke out some exception while sending im message. relationId:[{}]", request.getRelationId(), e); + } + } + + private void recordSendImMessage(Map toIdMessageRecordMap, List sendImResult, + Map toIdPersonIdMap) { + // 成功发送了IM消息的personId集合 + Set sucSendImMsgPersonIds = sendImResult.stream() + .map(SendImMessageDTO::getPersonId).collect(Collectors.toSet()); + // 从入参中的toIdPersonIdMap中筛选发送成功的entry + Map subToIdPersonIdMap = toIdPersonIdMap.entrySet().stream() + // 过滤掉personId维度IM消息发送失败的entry + .filter(e -> sucSendImMsgPersonIds.contains(e.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + Map msgRecordIdPersonIdMap = toIdMessageRecordMap.entrySet().stream() + .filter(e -> subToIdPersonIdMap.containsKey(e.getKey())) + .collect(Collectors.toMap(e -> e.getValue().getId(), e -> subToIdPersonIdMap.get(e.getKey()))); + log.info("record message that has been send im message. msgIds:{}", msgRecordIdPersonIdMap.keySet()); + // 双发记录 + messageSendTwiceRecordService.batchSave(msgRecordIdPersonIdMap); + } + + private GeneralMessageSendRequest buildSendRequest(GeneralMessageReq request, String templateCode, + Collection subReceiverIds) { + IdentityTypeEnum identityType = PersonIdentityUtil.toIdentityType(request.getReceiveType()); + List receivers = subReceiverIds.stream() + .filter(request.getToldIdPersonIdMap()::containsKey) + .map(e -> PersonDTO.from(request.getToldIdPersonIdMap().get(e), e, identityType)) + .collect(Collectors.toList()); + OrganizationTypeEnum orgType = Objects.isNull(request.getTerminalType()) ? OrganizationTypeEnum.UNKNOWN : + OrganizationTypeEnum.valueOf(request.getTerminalType().name()); + return GeneralMessageSendRequest.builder() + .templateCode(templateCode) + .receiver(receivers) + .orgType(orgType) + .orgName(request.getTerminalName()) + .orgId(request.getTerminalId()) + .bizCode(Optional.ofNullable(request.getBizId()).map(String::valueOf).orElse("")) + .routerParams(Optional.ofNullable(request.getRouterParams()) + .map(v -> JSONObject.parseObject(JSON.toJSONString(v))) + .orElseGet(JSONObject::new)) + .bizExtParams(Optional.ofNullable(request.getMsgParams()) + .map(v -> JSONObject.parseObject(JSON.toJSONString(v))) + .orElseGet(JSONObject::new)) + .build(); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageOldServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageOldServiceImpl.java new file mode 100644 index 00000000..bd978636 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageOldServiceImpl.java @@ -0,0 +1,86 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.core.domain.PageResult; +import cn.axzo.msg.center.api.enums.MsgStateEnum; +import cn.axzo.msg.center.api.enums.MsgTypeEnum; +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; +import cn.axzo.msg.center.common.utils.PageHelperUtil; +import cn.axzo.msg.center.dal.MessageRecordDao; +import cn.axzo.msg.center.domain.entity.MessageRecord; +import cn.axzo.msg.center.domain.enums.ModuleBizTypeEnum; +import cn.axzo.msg.center.inside.notices.service.MessageCoreService; +import cn.axzo.msg.center.inside.notices.service.MessageModuleService; +import cn.axzo.msg.center.inside.notices.service.MessageRelationService; +import cn.axzo.msg.center.message.service.GeneralMessageOldService; +import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService; +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.axzo.msg.center.utils.PersonIdentityUtil; +import cn.azxo.framework.common.model.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * @author cold_blade + * @date 2023/10/24 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class GeneralMessageOldServiceImpl implements GeneralMessageOldService { + + private final MessageRecordDao messageRecordDao; + private final MessageCoreService messageCoreService; + private final MessageModuleService messageModuleService; + private final MessageRelationService messageRelationService; + private final MessageSendTwiceRecordService messageSendTwiceRecordService; + + @Override + public int countUnread(PersonDTO person) { + AssertUtil.isTrue(Objects.nonNull(person) && person.isValid(), + "session 异常, 无法执行消息统计查询!, person : " + person); + List sendTwiceMsgIds = messageSendTwiceRecordService.listByPerson(person.getId()); + return countUnread(person, sendTwiceMsgIds); + } + + @Override + public int countUnread(PersonDTO person, List excludeMsgIds) { + List moduleIds = messageModuleService.listModuleIdByBizType(ModuleBizTypeEnum.CONSTRUCTION); + List relationIds = messageRelationService.listRelationIds(moduleIds); + if (CollectionUtils.isEmpty(relationIds)) { + log.info("the is not any valid relation id."); + return 0; + } + return messageRecordDao.lambdaQuery() + .eq(MessageRecord::getType, MsgTypeEnum.GENERAL_MESSAGE) + .in(MessageRecord::getState, MsgStateEnum.unreadStates()) + .eq(MessageRecord::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .eq(MessageRecord::getToId, person.getIdentity().getId()) + .eq(MessageRecord::getReceiveType, PersonIdentityUtil.toReceiveType(person.getIdentity().getType())) + .in(MessageRecord::getRelationId, relationIds) + // 排除双发记录表中的数据 + .notIn(CollectionUtils.isNotEmpty(excludeMsgIds), MessageRecord::getId, excludeMsgIds) + .count(); + } + + @Override + public Page pageMsgInfo(CmsMsgQueryReq request) { + log.info("start to page query general message. request:{}", request); + if (CollectionUtils.isEmpty(request.getExcludeMsgIds())) { + request.setExcludeMsgIds(messageSendTwiceRecordService.listByPerson(request.getPersonId())); + } + PageResult result = messageCoreService.listMsgInfo(request); + if (CollectionUtils.isEmpty(result.getData())) { + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + return Page.toPage(request.getPage(), request.getPageSize(), result.getTotalCount(), result.getData()); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageServiceImpl.java new file mode 100644 index 00000000..0ab4e86a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/GeneralMessageServiceImpl.java @@ -0,0 +1,232 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.im.center.api.feign.MessageApi; +import cn.axzo.im.center.api.vo.req.MessageInfo; +import cn.axzo.im.center.api.vo.resp.MessageDispatchResp; +import cn.axzo.im.center.common.enums.AppTypeEnum; +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.common.exception.ServiceException; +import cn.axzo.msg.center.common.utils.PlaceholderResolver; +import cn.axzo.msg.center.dal.GeneralMessageRecordDao; +import cn.axzo.msg.center.domain.entity.GeneralMessageRecord; +import cn.axzo.msg.center.domain.enums.UserTypeEnum; +import cn.axzo.msg.center.inside.notices.config.MessageSystemConfig; +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.dto.SendImMessageDTO; +import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO; +import cn.axzo.msg.center.message.service.GeneralMessageOldService; +import cn.axzo.msg.center.message.service.GeneralMessageService; +import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService; +import cn.axzo.msg.center.message.service.MessageTemplateNewService; +import cn.axzo.msg.center.service.dto.IdentityDTO; +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.axzo.msg.center.service.enums.GeneralMessageStateEnum; +import cn.axzo.msg.center.service.enums.IdentityTypeEnum; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import cn.axzo.msg.center.service.general.request.GeneralMessageOldDataStatisticRequest; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.service.general.response.GeneralMessageOldDataStatisticResponse; +import cn.axzo.msg.center.utils.MessageRouterUtil; +import cn.axzo.msg.center.utils.PersonIdentityUtil; +import cn.axzo.msg.center.utils.UUIDUtil; +import cn.azxo.framework.common.model.Page; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author cold_blade + * @date 2023/10/19 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class GeneralMessageServiceImpl implements GeneralMessageService { + + private static final PersonDTO SYSTEM_SENDER = PersonDTO.builder() + .id(0L) + .identity(IdentityDTO.builder().id(0L).type(IdentityTypeEnum.NOT_SUPPORT).build()) + .build(); + + private static final ImmutableMap PUSH_TERMINAL_APP_MAP = ImmutableMap.of( + PushTerminalEnum.B_ENTERPRISE_APP, AppTypeEnum.CMP, + PushTerminalEnum.C_WORKER_APP, AppTypeEnum.CM + ); + + private final MessageApi messageApi; + private final MessageSystemConfig messageSystemConfig; + private final GeneralMessageRecordDao generalMessageRecordDao; + private final GeneralMessageOldService generalMessageOldService; + private final MessageTemplateNewService messageTemplateNewService; + private final MessageSendTwiceRecordService messageSendTwiceRecordService; + + @Override + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + public List batchSendMessage(GeneralMessageSendRequest request) { + // 查询模板基础信息 + MessageTemplateDTO template = messageTemplateNewService.queryByTemplateCode(request.getTemplateCode()) + .orElseThrow(() -> new ServiceException("未查询到对应的模板")); + // 构建消息记录并存储 + List messageRecords = buildMessageRecord(request, template); + generalMessageRecordDao.saveBatch(messageRecords); + // 发送IM消息 + return pushImMessage(messageRecords, template); + } + + @Override + public GeneralMessageOldDataStatisticResponse statisticOldData(GeneralMessageOldDataStatisticRequest request) { + // 查询双发的消息记录 + List sendTwiceMsgIds = messageSendTwiceRecordService.listByPerson(request.getPersonId()); + // 分页查询最新一条数据 + Page result = generalMessageOldService.pageMsgInfo(build(request, sendTwiceMsgIds)); + // 统计旧的未读普通消息数量 + int count = generalMessageOldService.countUnread(PersonDTO.from(request.getPersonId(), request.getIdentityId(), + request.getIdentityType()), sendTwiceMsgIds); + // 编排组合成界面展示的数据结构 + MessageNewRes msg = CollectionUtils.isNotEmpty(result.getList()) ? result.getList().get(0) : null; + return GeneralMessageOldDataStatisticResponse.builder() + .unreadCount(count) + .latestMsgSendTimestamp(Optional.ofNullable(msg).map(v -> v.getCreateAt().getTime()).orElse(null)) + .latestMsgContent(Optional.ofNullable(msg).map(MessageNewRes::getContent).orElse(null)) + .build(); + } + + private List buildMessageRecord(GeneralMessageSendRequest request, MessageTemplateDTO template) { + return request.getReceiver().stream() + .map(e -> buildMessageRecord(request, e, template)) + .collect(Collectors.toList()); + } + + private GeneralMessageRecord buildMessageRecord(GeneralMessageSendRequest request, PersonDTO receiver, + MessageTemplateDTO template) { + PersonDTO sender = Objects.isNull(request.getSender()) ? SYSTEM_SENDER : request.getSender(); + return GeneralMessageRecord.builder() + .identityCode(UUIDUtil.uuidString()) + .senderPersonId(sender.getId()) + .senderId(sender.getIdentity().getId()) + .senderType(sender.getIdentity().getType()) + .receiverPersonId(receiver.getId()) + .receiverId(receiver.getIdentity().getId()) + .receiverType(receiver.getIdentity().getType()) + .templateCode(template.getCode()) + .title(parseString(template.getTitle(), request.getBizExtParams())) + .content(parseString(template.getContent(), request.getBizExtParams())) + .orgType(request.getOrgType()) + .orgId(request.getOrgId()) + .orgName(request.getOrgName()) + .state(GeneralMessageStateEnum.HAS_BEEN_SENT) + .bizCode(request.getBizCode()) + .routerParams(request.getRouterParams()) + .bizExtParams(request.getBizExtParams()) + .build(); + } + + private List pushImMessage(List messageRecords, MessageTemplateDTO template) { + if (CollectionUtils.isEmpty(template.getPushTerminals())) { + // 模板未配置任何推送终端 + return Collections.emptyList(); + } + GeneralMessageRecord record = messageRecords.get(0); + List appTypes = template.getPushTerminals().stream() + .map(PUSH_TERMINAL_APP_MAP::get).collect(Collectors.toList()); + GeneralMessagePushVO message = convert(record, template); + MessageInfo msgInfo = new MessageInfo(); + msgInfo.setAppTypeList(appTypes); + msgInfo.setToPersonIdList(Sets.newHashSet(messageRecords.stream() + .map(e -> String.valueOf(e.getReceiverPersonId())) + .collect(Collectors.toSet()))); + msgInfo.setMsgHeader(record.getTitle()); + msgInfo.setMsgContent(record.getContent()); + msgInfo.setMsgTemplateId(record.getTemplateCode()); + msgInfo.setMsgTemplateContent(JSON.toJSONString(message)); + // 扩展信息 + Map ext = new HashMap<>(); + ext.put("minAppVersion", template.getMinAppVersion()); + if (Objects.nonNull(record.getOrgId()) && record.getOrgId() > 0L) { + ext.put("workspaceId", record.getOrgId().toString()); + } + msgInfo.setExtendsInfo(ext); + + ApiResult> result = messageApi.sendMessage(msgInfo); + if (result.isError() || CollectionUtils.isEmpty(result.getData())) { + log.warn("failed to batch send im message. result:{}", result); + return Collections.emptyList(); + } + return result.getData().stream() + .map(SendImMessageDTO::from) + .collect(Collectors.toList()); + } + + private GeneralMessagePushVO convert(GeneralMessageRecord record, MessageTemplateDTO template) { + // 对应模板的路由列表 + List rawRouters = template.getRouters(); + // 解析路由地址 + List routers = rawRouters.stream() + .map(e -> MessageRouterUtil.parseAndConcatRouteUrl(e, record.getRouterParams())) + .collect(Collectors.toList()); + // 转化成客户端展示的数据模型 + List routerButtons = routers.stream() + .map(RawMessageRouterDTO::toMessageRouterButton) + .collect(Collectors.toList()); + // 获取模板卡片信息 + List rawCardContentItems = template.getMsgCardContentItems(); + List cardContentItems = rawCardContentItems; + if (CollectionUtils.isNotEmpty(rawCardContentItems) && Objects.nonNull(record.getBizExtParams()) + && !record.getBizExtParams().isEmpty()) { + // 克隆,避免修改入参 + cardContentItems = cardContentItems.stream() + .map(MessageCardContentItemDTO::deepClone) + .collect(Collectors.toList()); + cardContentItems.forEach(e -> { + String value = PlaceholderResolver.getDefaultResolver() + .resolveByMap(e.getValue(), record.getBizExtParams()); + e.setValue(value); + }); + } + return GeneralMessagePushVO.from(record, template.getIcon(), messageSystemConfig.getOrgIcon(), routerButtons, cardContentItems); + } + + private String parseString(String string, JSONObject params) { + if (Objects.isNull(params)) { + return string; + } + return PlaceholderResolver.getDefaultResolver().resolveByMap(string, params); + } + + private CmsMsgQueryReq build(GeneralMessageOldDataStatisticRequest request, List excludeMsgIds) { + UserTypeEnum userType = PersonIdentityUtil.toUserType(request.getIdentityType()); + CmsMsgQueryReq req = new CmsMsgQueryReq(); + req.setMsgType(MessageCategoryEnum.GENERAL_MESSAGE.getCode()); + // 这里查询消息中心全部状态的数据 + req.setMsgStatus(0); + req.setPage(1L); + req.setPageSize(1L); + req.setPersonId(request.getPersonId()); + req.setIdentityId(request.getIdentityId()); + req.setUserType(userType.getValue()); + req.setExcludeMsgIds(excludeMsgIds); + return req; + } +} 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..97680abd --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupNodeServiceImpl.java @@ -0,0 +1,168 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; +import cn.axzo.msg.center.common.exception.ServiceException; +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.GroupTreeNodePathDTO; +import cn.axzo.msg.center.message.domain.param.MessageGroupNodeSaveOrUpdateParam; +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 cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.utils.TreeHelperUtil; +import cn.axzo.msg.center.utils.UUIDUtil; +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.Objects; +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 MessageGroupNodeDao messageGroupNodeDao; + private final MessageTemplateGroupDao messageTemplateGroupDao; + private final MessageGroupTreeNodeCacheService messageGroupTreeNodeCacheService; + + @Override + public Map groupNodeNamePaths(Collection groupNodeCodePaths) { + if (CollectionUtils.isEmpty(groupNodeCodePaths)) { + log.info("leafGroupNodeCodes is empty."); + return Collections.emptyMap(); + } + return groupNodeCodePaths.stream() + .map(GroupTreeNodePathDTO::parseLeafNodeCode) + .map(messageGroupTreeNodeCacheService::queryLeafNodePath) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toMap(GroupTreeNodePathDTO::getNodeCodePath, + 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); + } + + @Override + public void addGroupNode(MessageGroupNodeSaveOrUpdateParam param) { + GroupTreeNodeDTO parent = StringUtils.isBlank(param.getParentNodeCode()) ? null : + messageGroupTreeNodeCacheService.queryNode(param.getParentNodeCode()) + .orElseThrow(() -> new ServiceException("parentNodeCode is invalid")); + // 合法性校验 + checkCreateRule(parent, param); + // 存储 + messageGroupNodeDao.save(convert(param)); + // 刷新缓存 + messageGroupTreeNodeCacheService.refreshCache(); + } + + @Override + public void updateGroupNode(MessageGroupNodeSaveOrUpdateParam param) { + boolean updateResult = messageGroupNodeDao.lambdaUpdate() + .eq(MessageGroupNode::getCode, param.getNodeCode()) + .eq(MessageGroupNode::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .set(StringUtils.isNotBlank(param.getNodeName()), MessageGroupNode::getName, param.getNodeName()) + .set(StringUtils.isNotBlank(param.getIcon()), MessageGroupNode::getIcon, param.getIcon()) + .set(MessageGroupNode::getUpdaterId, param.getOperatorId()) + .update(); + if (updateResult) { + // 刷新缓存 + messageGroupTreeNodeCacheService.refreshCache(); + } + } + + @Override + public void deleteGroupNode(Long operatorId, String nodeCode) { + if (Objects.isNull(operatorId) || StringUtils.isBlank(nodeCode)) { + log.info("the param is invalid. operatorId:[{}], nodeCode:[{}]", operatorId, nodeCode); + return; + } + // 合法性校验 + checkDeleteRule(nodeCode); + // 删除结点 + boolean removeResult = messageGroupNodeDao.lambdaUpdate() + .eq(MessageGroupNode::getCode, nodeCode) + .remove(); + if (removeResult) { + // 刷新缓存 + messageGroupTreeNodeCacheService.refreshCache(); + } + } + + @Override + public List listGroupTree(MessageCategoryEnum category) { + return messageGroupTreeNodeCacheService.listAllGroupTree().stream() + // 查询消息/待办 OR 两者的分类树 + .filter(e -> Objects.isNull(category) + || Objects.equals(e.getCategory().getMsgCategory(), category)) + .collect(Collectors.toList()); + } + + private MessageGroupNode convert(MessageGroupNodeSaveOrUpdateParam param) { + MessageGroupNode groupNode = new MessageGroupNode(); + groupNode.setCode(UUIDUtil.uuidString()); + groupNode.setName(param.getNodeName()); + groupNode.setIcon(param.getIcon()); + groupNode.setCategory(param.getCategory()); + groupNode.setParentCode(param.getParentNodeCode()); + groupNode.setCreatorId(param.getOperatorId()); + groupNode.setUpdaterId(param.getOperatorId()); + if (TreeHelperUtil.isLeafNodeCategory(param.getCategory())) { + groupNode.setIsLeaf(1); + } + return groupNode; + } + + private void checkCreateRule(GroupTreeNodeDTO parent, MessageGroupNodeSaveOrUpdateParam param) { + if (Objects.nonNull(parent)) { + AssertUtil.isTrue(parent.getCategory().getLevel() < param.getCategory().getLevel(), "父节点的类型非法"); + } + } + + private void checkDeleteRule(String nodeCode) { + GroupTreeNodeDTO node = messageGroupTreeNodeCacheService.queryNode(nodeCode) + .orElseThrow(() -> new ServiceException(String.format("未找到指定的结点[%s]", nodeCode))); + // TODO: [cold_blade] [P2] 异常处理需要检查 + AssertUtil.isEmpty(node.getNodeChildren(), "删除失败!删除前请删除子级!"); + // 校验其结点是否被模板关联 + int cnt = messageTemplateGroupDao.lambdaQuery() + .like(MessageTemplateGroup::getPath, nodeCode) + .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .count(); + AssertUtil.isTrue(cnt == 0, "删除失败!删除前请先转移消息模版!"); + } +} 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..fa06350e --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageGroupTreeNodeCacheServiceImpl.java @@ -0,0 +1,193 @@ +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.common.redis.RedisUtil; +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 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 RedisUtil redisUtil; + private final MessageGroupNodeDao messageGroupNodeDao; + + private List allGroupTreesCache = Collections.emptyList(); + private List leafTreeNodePathsCache = Collections.emptyList(); + + @Override + public List listAllGroupTree() { + return getAllGroupTreesCache(); + } + + @Override + public Optional queryRootNode(String rootNodeCode) { + if (StringUtils.isBlank(rootNodeCode)) { + log.info("rootNodeCode is blank."); + return Optional.empty(); + } + return getAllGroupTreesCache().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 getAllGroupTreesCache().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 synchronized void refreshCache() { + // 清除redis中的缓存标识 + redisUtil.getKeyOps().delete(CACHE_KEY); + // 本地缓存初始化并更新redis中的缓存标识 + initialize(true); + } + + 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 getAllGroupTreesCache() { + if (redisUtil.getKeyOps().hasKey(CACHE_KEY)) { + // 其它结点进行了本地缓存,但是当前服务进程还未进行缓存 + if (CollectionUtils.isEmpty(allGroupTreesCache)) { + // 本地缓存初始化,不更新redis中的缓存标识 + initialize(false); + } + } else { + // 本地缓存初始化并更新redis中的缓存标识 + initialize(true); + } + return this.allGroupTreesCache; + } + + private List getLeafTreeNodePathsCache() { + if (redisUtil.getKeyOps().hasKey(CACHE_KEY)) { + // 其它结点进行了本地缓存,但是当前服务进程还未进行缓存 + if (leafTreeNodePathsCache.isEmpty()) { + // 本地缓存初始化,不更新redis中的缓存标识 + initialize(false); + } + } else { + // 本地缓存初始化并更新redis中的缓存标识 + initialize(true); + } + return leafTreeNodePathsCache; + } + + private synchronized void initialize(boolean refreshCache) { + List groupNodes = listAllValidNodesFromDB(); + allGroupTreesCache = TreeUtil.buildTree(groupNodes); + leafTreeNodePathsCache = allGroupTreesCache.stream() + .flatMap(e -> parseRootNode(e).stream()) + .collect(Collectors.toList()); + if (refreshCache) { + redisUtil.getStringOps().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(), reverseStack(pathNodeStack))); + pathNodeStack.pop(); + } else { + stack.addAll(node.getNodeChildren()); + } + } + return paths; + } + + private List reverseStack(LinkedList pathNodeStack) { + List list = Lists.newArrayList(); + for (GroupTreeNodeDTO node : pathNodeStack) { + list.add(0, node); + } + return list; + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageSendTwiceRecordServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageSendTwiceRecordServiceImpl.java new file mode 100644 index 00000000..05c66a8a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageSendTwiceRecordServiceImpl.java @@ -0,0 +1,60 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.msg.center.dal.MessageSendTwiceRecordDao; +import cn.axzo.msg.center.domain.entity.MessageSendTwiceRecord; +import cn.axzo.msg.center.message.service.MessageSendTwiceRecordService; +import cn.hutool.core.map.MapUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @description xxx + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MessageSendTwiceRecordServiceImpl implements MessageSendTwiceRecordService { + + private final MessageSendTwiceRecordDao messageSendTwiceRecordDao; + + @Override + public List listByPerson(Long personId) { + if (Objects.isNull(personId)) { + log.info("personId is null."); + return Collections.emptyList(); + } + return messageSendTwiceRecordDao.lambdaQuery() + .eq(MessageSendTwiceRecord::getReceiverPersonId, personId) + .last("LIMIT 500") + .orderByDesc(MessageSendTwiceRecord::getId) + .select(MessageSendTwiceRecord::getOriginalMsgId) + .list().stream() + .map(MessageSendTwiceRecord::getOriginalMsgId) + .collect(Collectors.toList()); + } + + @Override + public void batchSave(Map msgRecordIdPersonIdMap) { + if (MapUtil.isEmpty(msgRecordIdPersonIdMap)) { + return; + } + List records = msgRecordIdPersonIdMap.entrySet().stream() + .map(e -> { + MessageSendTwiceRecord record = new MessageSendTwiceRecord(); + record.setReceiverPersonId(e.getValue()); + record.setOriginalMsgId(e.getKey()); + return record; + }).collect(Collectors.toList()); + messageSendTwiceRecordDao.saveBatch(records); + } +} 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..d7d1045d 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 @@ -1,15 +1,26 @@ package cn.axzo.msg.center.message.service.impl; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.framework.core.util.MapUtil; +import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; 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 com.google.common.collect.Lists; 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.Objects; +import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -22,6 +33,7 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class MessageTemplateGroupServiceImpl implements MessageTemplateGroupService { + private final MessageGroupNodeService messageGroupNodeService; private final MessageTemplateGroupDao messageTemplateGroupDao; @Override @@ -37,4 +49,138 @@ 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)) { + log.info("the param is invalid. templateNode:[{}], groupNodeCodes:{}", templateNode, 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); + } + + @Override + public void templateGroup(Map> templateGroupMap) { + if (MapUtil.isEmpty(templateGroupMap)) { + log.info("the templateGroupMap is empty."); + return; + } + Set groupNodes = templateGroupMap.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + Map pathMap = messageGroupNodeService.leafGroupNodeCodePaths(groupNodes); + List rows = templateGroupMap.entrySet().stream() + .flatMap(e -> e.getValue().stream() + .filter(pathMap::containsKey) + .map(nodeCode -> { + MessageTemplateGroup group = new MessageTemplateGroup(); + group.setTemplateCode(e.getKey()); + group.setPath(pathMap.get(nodeCode)); + return group; + }) + ).collect(Collectors.toList()); + messageTemplateGroupDao.saveBatch(rows); + } + + @Override + public void updateTemplateGroup(String templateNode, Collection groupNodeCodes) { + // 先解除之前的关联关系 + deleteTemplateGroup(templateNode); + // 构建新的关联关系 + templateGroup(templateNode, groupNodeCodes); + } + + @Override + public void deleteTemplateGroup(String templateNode) { + if (StringUtils.isBlank(templateNode)) { + log.info("the templateNode is blank."); + return; + } + messageTemplateGroupDao.lambdaUpdate() + .eq(MessageTemplateGroup::getTemplateCode, templateNode) + .remove(); + } + + @Override + public Map> listMessageTemplateGroupPaths(Collection templateCodes) { + if (CollectionUtils.isEmpty(templateCodes)) { + return Collections.emptyMap(); + } + return messageTemplateGroupDao.lambdaQuery() + .in(MessageTemplateGroup::getTemplateCode, templateCodes) + .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list().stream() + .collect(Collectors.groupingBy(MessageTemplateGroup::getTemplateCode, + Collectors.mapping(MessageTemplateGroup::getPath, Collectors.toList()))); + } + + @Override + public void remove(String curGroupNodeCode, Collection templateCodes) { + // 参数校验 + AssertUtil.isTrue(StringUtils.isNotBlank(curGroupNodeCode), "curGroupNodeCode can not be blank"); + AssertUtil.notEmpty(templateCodes, "templateCodes can not be empty"); + // 通过分类结点编码获取其对应的结点编码的路径 + Map nodeCodePathMap = messageGroupNodeService + .leafGroupNodeCodePaths(Lists.newArrayList(curGroupNodeCode)); + // 通过能否成功获取到其树路径来校验其有效性 + AssertUtil.isFalse(nodeCodePathMap.isEmpty(), "curGroupNodeCode is invalid"); + // 统计指定的模板编码关联的分类数量 + Map templateCodeList = messageTemplateGroupDao.lambdaQuery() + .in(MessageTemplateGroup::getTemplateCode, templateCodes) + .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(curGroupNodeCode)) + .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value) + // 这里仅查询模板编码字段即可 + .select(MessageTemplateGroup::getTemplateCode) + .list().stream() + .map(MessageTemplateGroup::getTemplateCode) + // 内存中来做groupingBy操作 + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + // 筛选出仅关联了一个分类的模板编码 + List onlyContainsOneTemplateCodes = templateCodeList.entrySet().stream() + .filter(e -> Objects.equals(e.getValue(), 1L)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + AssertUtil.isEmpty(onlyContainsOneTemplateCodes, removeErrorStr(onlyContainsOneTemplateCodes)); + // 解除关联关系 + messageTemplateGroupDao.lambdaUpdate() + .in(MessageTemplateGroup::getTemplateCode, templateCodes) + .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(curGroupNodeCode)) + .remove(); + } + + @Override + public void move(String srcGroupNodeCode, String tgtGroupNodeCode, Collection templateCodes) { + // 参数校验 + AssertUtil.isTrue(StringUtils.isNotBlank(srcGroupNodeCode), "srcGroupNodeCode can not be blank"); + AssertUtil.isTrue(StringUtils.isNotBlank(tgtGroupNodeCode), "tgtGroupNodeCode can not be blank"); + AssertUtil.notEmpty(templateCodes, "templateCodes can not be empty"); + // 通过分类结点编码获取其对应的结点编码的路径 + Map nodeCodePathMap = messageGroupNodeService + .leafGroupNodeCodePaths(Lists.newArrayList(srcGroupNodeCode, tgtGroupNodeCode)); + // 通过能否成功获取到其树路径来校验其有效性 + AssertUtil.isTrue(StringUtils.isNotBlank(nodeCodePathMap.get(srcGroupNodeCode)), "srcGroupNodeCode is invalid"); + AssertUtil.isTrue(StringUtils.isNotBlank(nodeCodePathMap.get(tgtGroupNodeCode)), "tgtGroupNodeCode is invalid"); + // 更新分类路径与模板编码的映射关系 + messageTemplateGroupDao.lambdaUpdate() + .eq(MessageTemplateGroup::getPath, nodeCodePathMap.get(srcGroupNodeCode)) + .in(MessageTemplateGroup::getTemplateCode, templateCodes) + .eq(MessageTemplateGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .set(MessageTemplateGroup::getPath, nodeCodePathMap.get(tgtGroupNodeCode)) + .update(); + } + + private String removeErrorStr(List onlyContainsOneTemplateCodes) { + return String.format("模板:%s。解除关联后,将无关联分类!本次操作无效!", + String.join("、", onlyContainsOneTemplateCodes)); + } } 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..5191441d 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,19 +1,42 @@ 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.common.enums.TableIsDeleteEnum; +import cn.axzo.msg.center.common.redis.RedisUtil; +import cn.axzo.msg.center.common.utils.PageHelperUtil; import cn.axzo.msg.center.dal.MessageBaseTemplateDao; import cn.axzo.msg.center.domain.entity.MessageBaseTemplate; +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.RawMessageRouterDTO; -import cn.axzo.msg.center.message.domain.param.MessageTemplateCreateParam; +import cn.axzo.msg.center.message.domain.param.MessageTemplateSaveOrUpdateParam; +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.MessageTemplateRouterService; +import cn.axzo.msg.center.service.dto.MessageBaseTemplateDTO; +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.axzo.msg.center.service.template.response.MessageTemplateDetailResponse; +import cn.axzo.msg.center.service.template.response.MessageTemplatePageResponse; +import cn.axzo.msg.center.utils.JSONObjectUtil; +import cn.axzo.msg.center.utils.UUIDUtil; +import cn.azxo.framework.common.model.Page; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.google.common.collect.Lists; 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.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,19 +56,73 @@ 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 static final int MAX_NUM_ONCE_QUERY = 1000; + + private final RedisUtil redisUtil; private final MessageBaseTemplateDao messageBaseTemplateDao; + private final MessageGroupNodeService messageGroupNodeService; + 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(MessageTemplateSaveOrUpdateParam param) { + // 创建模板基础数据 + String templateCode = saveTemplate(param); + // 创建模板的路由数据 + saveTemplateRouters(param, templateCode); + // 创建模板与分类的关联关系数据 + messageTemplateGroupService.templateGroup(templateCode, param.getLeafGroupNodes()); + return templateCode; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateTemplate(MessageTemplateSaveOrUpdateParam param) { + // 更新模板基础数据 + updateBaseTemplate(param); + // 更新模板分类信息 + updateTemplateGroupRelation(param); + // 更新模板路由信息 + updateTemplateRouters(param); + } + + @Override + public MessageTemplateDetailResponse detail(String templateCode) { + if (StringUtils.isBlank(templateCode)) { + return null; } + MessageBaseTemplate baseTemplate = messageBaseTemplateDao.lambdaQuery() + .eq(MessageBaseTemplate::getCode, templateCode) + .eq(MessageBaseTemplate::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .one(); + if (Objects.isNull(baseTemplate)) { + log.info("there is not any template match the templateCode:[{}]", templateCode); + return null; + } + // 获取模板关联分类的path列表 + List groupNodePaths = messageTemplateGroupService.listMessageTemplateGroupPaths( + Lists.newArrayList(templateCode)).getOrDefault(templateCode, Collections.emptyList()); + List routers = messageTemplateRouterService.queryByTemplateCode(templateCode); + return convert(baseTemplate, groupNodePaths, routers); + } + + @Override + public void updateStatus(Long operatorId, String templateCode, StatusEnum status) { + if (Objects.isNull(operatorId) || StringUtils.isBlank(templateCode) || Objects.isNull(status)) { + log.info("the param is invalid. operatorId:[{}], code:[{}], status:[{}]", operatorId, templateCode, status); + return; + } + messageBaseTemplateDao.lambdaUpdate() + .eq(MessageBaseTemplate::getCode, templateCode) + .eq(MessageBaseTemplate::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .set(MessageBaseTemplate::getStatus, status) + .set(MessageBaseTemplate::getUpdaterId, operatorId) + .update(); } @Override @@ -87,12 +164,256 @@ public class MessageTemplateNewServiceImpl implements MessageTemplateNewService .collect(Collectors.toList()); } - private MessageBaseTemplate convert(MessageTemplateCreateParam param) { - MessageBaseTemplate template = BeanConverter.convert(param, MessageBaseTemplate.class); + @Override + public Page page(MessageTemplatePageRequest request) { + List templateCodes = Lists.newArrayList(); + if (StringUtils.isNotBlank(request.getGroupNodeCode())) { + templateCodes = messageTemplateGroupService.listMessageTemplateCodes(request.getGroupNodeCode()); + if (CollectionUtils.isEmpty(templateCodes)) { + // 入参中的分类没有关联任何模板,直接返回查询结果 + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + } + if (StringUtils.isNotBlank(request.getTemplateCode())) { + if (CollectionUtils.isNotEmpty(templateCodes) + && !templateCodes.contains(request.getTemplateCode())) { + // 分页查询的入参中没指定的模板编码与分类映射的模板编码无交集 + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + // 取两者的交集 + templateCodes = Lists.newArrayList(request.getTemplateCode()); + } + IPage pageRequest = request.toPage(); + IPage result = messageBaseTemplateDao.lambdaQuery() + .like(StringUtils.isNotBlank(request.getTemplateName()), + MessageBaseTemplate::getName, request.getTemplateName()) + .in(CollectionUtils.isNotEmpty(templateCodes), + MessageBaseTemplate::getCode, request.getTemplateCode()) + .eq(Objects.nonNull(request.getCategory()), MessageBaseTemplate::getMsgCategory, request.getCategory()) + .eq(Objects.nonNull(request.getStatus()), MessageBaseTemplate::getStatus, request.getStatus()) + .page(pageRequest); + if (CollectionUtils.isEmpty(result.getRecords())) { + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + templateCodes = result.getRecords().stream().map(MessageBaseTemplate::getCode).collect(Collectors.toList()); + Map> groupNodePaths = messageTemplateGroupService + .listMessageTemplateGroupPaths(templateCodes); + Map codeNameMap = messageGroupNodeService.groupNodeNamePaths(groupNodePaths.values().stream() + .flatMap(Collection::stream).collect(Collectors.toList())); + List records = result.getRecords().stream() + .map(e -> convert(e, groupNodePaths, codeNameMap)) + .collect(Collectors.toList()); + return Page.toPage(request.getPage(), request.getPageSize(), result.getTotal(), records); + } + + @Override + public Page pageBaseTemplate(MessageTemplatePageRequest request) { + List templateCodes = Lists.newArrayList(); + if (StringUtils.isNotBlank(request.getGroupNodeCode())) { + templateCodes = messageTemplateGroupService.listMessageTemplateCodes(request.getGroupNodeCode()); + if (CollectionUtils.isEmpty(templateCodes)) { + // 入参中的分类没有关联任何模板,直接返回查询结果 + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + } + if (StringUtils.isNotBlank(request.getTemplateCode())) { + if (CollectionUtils.isNotEmpty(templateCodes) + && !templateCodes.contains(request.getTemplateCode())) { + // 分页查询的入参中没指定的模板编码与分类映射的模板编码无交集 + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + // 取两者的交集 + templateCodes = Lists.newArrayList(request.getTemplateCode()); + } + IPage pageRequest = request.toPage(); + IPage result = messageBaseTemplateDao.lambdaQuery() + .like(StringUtils.isNotBlank(request.getTemplateName()), + MessageBaseTemplate::getName, request.getTemplateName()) + .in(CollectionUtils.isNotEmpty(templateCodes), + MessageBaseTemplate::getCode, request.getTemplateCode()) + .eq(Objects.nonNull(request.getCategory()), MessageBaseTemplate::getMsgCategory, request.getCategory()) + .eq(Objects.nonNull(request.getStatus()), MessageBaseTemplate::getStatus, request.getStatus()) + .page(pageRequest); + if (CollectionUtils.isEmpty(result.getRecords())) { + return PageHelperUtil.emptyPage(request.getPage(), request.getPageSize()); + } + List records = result.getRecords().stream() + .map(this::convert) + .collect(Collectors.toList()); + return Page.toPage(request.getPage(), request.getPageSize(), result.getTotal(), records); + } + + @Override + public List listByCodes(Collection templateCodes) { + if (CollectionUtils.isEmpty(templateCodes)) { + log.info("the templateCodes is empty."); + return Collections.emptyList(); + } + AssertUtil.isTrue(templateCodes.size() <= MAX_NUM_ONCE_QUERY, "the collection of templateCodes is too large"); + // 查询模板基础数据 + List records = messageBaseTemplateDao.lambdaQuery() + .in(MessageBaseTemplate::getCode, templateCodes) + .eq(MessageBaseTemplate::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list(); + // 查询模板的分类结点编码path + templateCodes = records.stream().map(MessageBaseTemplate::getCode).collect(Collectors.toList()); + Map> groupNodePaths = messageTemplateGroupService + .listMessageTemplateGroupPaths(templateCodes); + // 将模板分类结点编码path转化为分类名称path + Map codeNameMap = messageGroupNodeService.groupNodeNamePaths(groupNodePaths.values().stream() + .flatMap(Collection::stream).collect(Collectors.toList())); + // 转化为页面展示的数据模型 + return records.stream() + .map(e -> convert(e, groupNodePaths, codeNameMap)) + .collect(Collectors.toList()); + } + + private String saveTemplate(MessageTemplateSaveOrUpdateParam 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.getLockOps().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.getLockOps().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(MessageTemplateSaveOrUpdateParam param) { + 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.toJSONString(param.getMsgCardContentItems())); + template.setIcon(param.getIcon()); + template.setPushTerminal(JSONObjectUtil.toJSONString(param.getPushTerminals())); template.setCreatorId(param.getOperatorId()); template.setUpdaterId(param.getOperatorId()); return template; } + + private void saveTemplateRouters(MessageTemplateSaveOrUpdateParam 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); + } + + private void updateBaseTemplate(MessageTemplateSaveOrUpdateParam param) { + messageBaseTemplateDao.lambdaUpdate() + .eq(MessageBaseTemplate::getCode, param.getTemplateCode()) + .eq(MessageBaseTemplate::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .set(MessageBaseTemplate::getUpdaterId, param.getOperatorId()) + .set(StringUtils.isNotBlank(param.getTemplateName()), MessageBaseTemplate::getName, + param.getTemplateName()) + .set(CollectionUtils.isNotEmpty(param.getPushTerminals()), MessageBaseTemplate::getPushTerminal, + JSON.toJSONString(param.getPushTerminals())) + .set(StringUtils.isNotBlank(param.getTitle()), MessageBaseTemplate::getTitle, param.getTitle()) + .set(CollectionUtils.isNotEmpty(param.getMsgCardContentItems()), MessageBaseTemplate::getCardContent, + JSONObjectUtil.toJSONString(param.getMsgCardContentItems())) + .set(StringUtils.isNotBlank(param.getContent()), MessageBaseTemplate::getContent, param.getContent()) + .set(StringUtils.isNotBlank(param.getIcon()), MessageBaseTemplate::getIcon, param.getIcon()) + .update(); + } + + private void updateTemplateGroupRelation(MessageTemplateSaveOrUpdateParam param) { + if (CollectionUtils.isEmpty(param.getLeafGroupNodes())) { + return; + } + messageTemplateGroupService.updateTemplateGroup(param.getTemplateCode(), param.getLeafGroupNodes()); + } + + private void updateTemplateRouters(MessageTemplateSaveOrUpdateParam param) { + if (CollectionUtils.isEmpty(param.getRouters())) { + return; + } + messageTemplateRouterService.updateTemplateRoutes(param.getTemplateCode(), param.getRouters()); + } + + private MessageBaseTemplateDTO convert(MessageBaseTemplate record) { + return MessageBaseTemplateDTO.builder() + .name(record.getName()) + .code(record.getCode()) + .msgCategory(record.getMsgCategory()) + .title(record.getTitle()) + .content(record.getContent()) + .cardContentItems(JSONObjectUtil.parseArray(record.getCardContent(), MessageCardContentItemDTO.class)) + .icon(record.getIcon()) + .pushTerminals(JSON.parseArray(record.getPushTerminal(), PushTerminalEnum.class)) + .createTimestamp(record.getCreateAt().getTime()) + .build(); + } + + private MessageTemplatePageResponse convert(MessageBaseTemplate record, Map> groupNodePaths, + Map codeNameMap) { + MessageTemplatePageResponse response = new MessageTemplatePageResponse(); + response.setTemplateName(record.getName()); + response.setTemplateCode(record.getCode()); + response.setTitle(record.getTitle()); + response.setContent(record.getContent()); + response.setCategory(record.getMsgCategory()); + response.setStatus(record.getStatus()); + List nodeNamePaths = groupNodePaths.getOrDefault(record.getCode(), Collections.emptyList()).stream() + .filter(codeNameMap::containsKey) + .map(codeNameMap::get) + .collect(Collectors.toList()); + response.setGroupNodeNamePaths(nodeNamePaths); + return response; + } + + private MessageTemplateDetailResponse convert(MessageBaseTemplate record, List groupNodePaths, + List routers) { + // 将path解析中解析出分类结点(路径叶结点)的编码 + List groupNodeCodes = groupNodePaths.stream() + .map(GroupTreeNodePathDTO::parseLeafNodeCode) + .collect(Collectors.toList()); + // 转化为页面相关的数据模型 + List routerButtons = routers.stream() + .map(RawMessageRouterDTO::toMessageRouterButton) + .collect(Collectors.toList()); + return MessageTemplateDetailResponse.builder() + .templateName(record.getName()) + .category(record.getMsgCategory()) + .leafGroupNodes(groupNodeCodes) + .pushTerminals(JSON.parseArray(record.getPushTerminal(), PushTerminalEnum.class)) + .msgTitle(record.getTitle()) + .msgContent(record.getContent()) + .cardContentItems(JSONObjectUtil.parseArray(record.getCardContent(), MessageCardContentItemDTO.class)) + .msgIcon(record.getIcon()) + .routers(routerButtons) + .createTimestamp(record.getCreateAt().getTime()) + .updateTimestamp(record.getUpdateAt().getTime()) + .build(); + } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateRouterServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateRouterServiceImpl.java index 8a0ca09d..bab7e237 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateRouterServiceImpl.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/MessageTemplateRouterServiceImpl.java @@ -4,6 +4,7 @@ import cn.axzo.msg.center.dal.MessageTemplateRouterDao; import cn.axzo.msg.center.domain.entity.MessageTemplateRouter; import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; import cn.axzo.msg.center.message.service.MessageTemplateRouterService; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -36,9 +37,7 @@ public class MessageTemplateRouterServiceImpl implements MessageTemplateRouterSe log.warn("the template code is blank."); return Collections.emptyList(); } - return messageTemplateRouterDao.listByTemplateCode(Lists.newArrayList(templateCode)).stream() - .map(RawMessageRouterDTO::from) - .collect(Collectors.toList()); + return listByTemplateCodes(Lists.newArrayList(templateCode)); } @Override @@ -47,18 +46,49 @@ public class MessageTemplateRouterServiceImpl implements MessageTemplateRouterSe return; } List messageTemplateRouters = routers.stream() - .map(RawMessageRouterDTO::toMessageTemplateRouter) + .flatMap(e -> e.toMessageTemplateRouters().stream()) .collect(Collectors.toList()); messageTemplateRouterDao.saveBatch(messageTemplateRouters); } + @Override + public void updateTemplateRoutes(String templateCode, List routers) { + if (StringUtils.isBlank(templateCode) + || CollectionUtils.isEmpty(routers)) { + log.info("the param is invalid. templateCode:[{}], routers:{}", templateCode, routers); + return; + } + // 移除之前的路由信息 + messageTemplateRouterDao.lambdaUpdate() + .eq(MessageTemplateRouter::getTemplateCode, templateCode) + .remove(); + // 创建信息的路由信息 + List newRouters = routers.stream() + .map(e -> RawMessageRouterDTO.from(e, templateCode)) + .collect(Collectors.toList()); + batchInsert(newRouters); + } + @Override public Map> groupByTemplateCode(List templateCodes) { if (CollectionUtils.isEmpty(templateCodes)) { return Collections.emptyMap(); } - return messageTemplateRouterDao.listByTemplateCode(templateCodes).stream() - .map(RawMessageRouterDTO::from) + return listByTemplateCodes(templateCodes).stream() .collect(Collectors.groupingBy(RawMessageRouterDTO::getTemplateCode)); } + + private List listByTemplateCodes(List templateCodes) { + // 根据模板编码进行分组 + Map> routers = messageTemplateRouterDao + .listByTemplateCode(templateCodes).stream() + .collect(Collectors.groupingBy(MessageTemplateRouter::getTemplateCode)); + return routers.values().stream() + .flatMap(e -> { + // 模板路由的按钮维度 + Map> btnMap = e.stream() + .collect(Collectors.groupingBy(MessageTemplateRouter::getName)); + return btnMap.values().stream().map(RawMessageRouterDTO::from); + }).collect(Collectors.toList()); + } } 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 079a52d6..7253c6b6 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 @@ -1,21 +1,21 @@ package cn.axzo.msg.center.message.service.impl; -import cn.axzo.core.utils.converter.BeanConverter; import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; 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; @@ -27,11 +27,14 @@ import cn.axzo.msg.center.service.enums.PendingMessageStateEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import cn.axzo.msg.center.service.pending.request.PendingMessagePageRequest; 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; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; @@ -44,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; /** @@ -62,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()); @@ -169,19 +172,20 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { .findFirst() .map(MessageTemplateDTO::getRouters) .orElseGet(Collections::emptyList); - rawRouters = MessageRouterUtil.selectRouter(rawRouters, terminalType); - List routers = rawRouters.stream().map(e -> { - MessageRouterDTO router = BeanConverter.convert(e, MessageRouterDTO.class); - // 视情况替换原始URL中的参数变量 - MessageRouterUtil.parseRouteUrl(router, pendingMessageRecord.getRouterParams()); - return router; - }).collect(Collectors.toList()); + List routers = rawRouters.stream() + .map(e -> { + MessageRouterDTO router = e.toMessageRouter(terminalType); + // 视情况替换原始URL中的参数变量 + MessageRouterUtil.parseRouteUrl(router, pendingMessageRecord.getRouterParams()); + return router; + }) + .collect(Collectors.toList()); pendingMessage.setRouters(routers); // 获取模板卡片信息 String cardContent = messageTemplates.stream() .filter(e -> Objects.equals(e.getCode(), pendingMessageRecord.getTemplateCode())) .findFirst() - .map(MessageTemplateDTO::getCardContent) + .map(e -> JSON.toJSONString(e.toCardContentMap())) .orElse(null); if (StringUtils.isNotBlank(cardContent) && StringUtils.isNotBlank(pendingMessageRecord.getRouterParams())) { cardContent = PlaceholderResolver.getDefaultResolver() @@ -191,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); @@ -282,10 +283,7 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { record.setOrgId(param.getOrgId()); record.setOrgName(param.getOrgName()); // 构建业务类信息 - record.setBizCode(param.getBizCode()); - record.setSubBizCode(param.getSubBizCode()); - record.setBizDesc(param.getBizDesc()); - record.setBizCategory(param.getBizCategory()); + buildBusinessInfo(record, param); return record; } @@ -299,17 +297,28 @@ public class PendingMessageNewServiceImpl implements PendingMessageNewService { } private void buildTemplateInfo(PendingMessageRecord record, MessageTemplateDTO msgTemplate, String routeParam) { - String title = msgTemplate.getTitle(); - String content = msgTemplate.getContent(); - if (StringUtils.isNotBlank(routeParam)) { - title = PlaceholderResolver - .getDefaultResolver().resolveByMap(title, JSON.parseObject(routeParam)); - content = PlaceholderResolver - .getDefaultResolver().resolveByMap(content, JSON.parseObject(routeParam)); - } + // TODO:[cold_blade] [P3] 后续其它业务对接的时候,需要明确业务扩展字段和路由参数的分界 + JSONObject routerParamObj = JSONObjectUtil.parseObject(routeParam); + String title = PlaceholderResolver + .getDefaultResolver().resolveByMap(msgTemplate.getTitle(), routerParamObj); + String content = PlaceholderResolver + .getDefaultResolver().resolveByMap(msgTemplate.getContent(), routerParamObj); record.setTitle(title); record.setContent(content); record.setTemplateCode(msgTemplate.getCode()); - record.setRouterParams(routeParam); + record.setRouterParams(routerParamObj.toJSONString()); + } + + private void buildBusinessInfo(PendingMessageRecord record, PendingMessagePushParam param) { + record.setBizCode(param.getBizCode()); + record.setSubBizCode(param.getSubBizCode()); + record.setBizDesc(param.getBizDesc()); + 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/message/service/impl/RelationTemplateMapServiceImpl.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/RelationTemplateMapServiceImpl.java new file mode 100644 index 00000000..381a60ec --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/RelationTemplateMapServiceImpl.java @@ -0,0 +1,212 @@ +package cn.axzo.msg.center.message.service.impl; + +import cn.axzo.msg.center.common.enums.TableIsDeleteEnum; +import cn.axzo.msg.center.dal.MessageBaseTemplateDao; +import cn.axzo.msg.center.dal.RelationTemplateMapDao; +import cn.axzo.msg.center.domain.entity.MessageBaseTemplate; +import cn.axzo.msg.center.domain.entity.MessageRelation; +import cn.axzo.msg.center.domain.entity.MessageRouter; +import cn.axzo.msg.center.domain.entity.MessageTemplate; +import cn.axzo.msg.center.domain.entity.RelationTemplateMap; +import cn.axzo.msg.center.inside.notices.service.MessageRelationService; +import cn.axzo.msg.center.inside.notices.service.MessageRouterService; +import cn.axzo.msg.center.inside.notices.service.MessageTemplateService; +import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; +import cn.axzo.msg.center.message.domain.param.RelationTemplateMapInitParam; +import cn.axzo.msg.center.message.service.MessageTemplateGroupService; +import cn.axzo.msg.center.message.service.MessageTemplateRouterService; +import cn.axzo.msg.center.message.service.RelationTemplateMapService; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.dto.MessageRouterTerminalDTO; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import cn.axzo.msg.center.service.enums.RouterCategoryEnum; +import cn.axzo.msg.center.service.enums.TerminalTypeEnum; +import cn.axzo.msg.center.utils.JSONObjectUtil; +import cn.axzo.msg.center.utils.UUIDUtil; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 新老模板映射关系service + * + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RelationTemplateMapServiceImpl implements RelationTemplateMapService { + + private final MessageRouterService messageRouterService; + private final MessageRelationService messageRelationService; + private final MessageTemplateService messageTemplateService; + private final RelationTemplateMapDao relationTemplateMapDao; + private final MessageBaseTemplateDao messageBaseTemplateDao; + private final MessageTemplateGroupService messageTemplateGroupService; + private final MessageTemplateRouterService messageTemplateRouterService; + + @Override + public Optional queryByRelationId(Long relationId) { + if (Objects.isNull(relationId)) { + log.info("relationId is null"); + return Optional.empty(); + } + return Optional.ofNullable(relationTemplateMapDao.lambdaQuery() + .eq(RelationTemplateMap::getOriginalRelationId, relationId) + .eq(RelationTemplateMap::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .one() + ).map(RelationTemplateMap::getTemplateCode); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void mapRelationAndTemplate(Map map) { + if (Objects.isNull(map) || map.isEmpty()) { + return; + } + relationTemplateMapDao.lambdaUpdate() + .in(RelationTemplateMap::getOriginalRelationId, map.keySet()) + .eq(RelationTemplateMap::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .remove(); + List rows = map.entrySet().stream() + .map(e -> { + RelationTemplateMap row = new RelationTemplateMap(); + row.setOriginalRelationId(e.getKey()); + row.setTemplateCode(e.getValue()); + return row; + }).collect(Collectors.toList()); + relationTemplateMapDao.saveBatch(rows); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void init(RelationTemplateMapInitParam param) { + List allRelations = messageRelationService.getAllRelations(); + if (CollectionUtils.isNotEmpty(param.getRelationIds())) { + // 指定了需要创建并关联的relationId + allRelations = allRelations.stream() + .filter(e -> param.getRelationIds().contains(e.getId())) + .collect(Collectors.toList()); + } + // relationId与templateId的map + Map relationIdTemplateIdMap = allRelations.stream() + .collect(Collectors.toMap(MessageRelation::getId, MessageRelation::getTemplateId)); + // 获取已经建立关联关系的relationId集合 + Set mappedRelationIds = getAllOriginalRelationIds(); + // 过滤掉已经建立关联关系的relationId + relationIdTemplateIdMap = relationIdTemplateIdMap.entrySet().stream() + .filter(e -> !mappedRelationIds.contains(e.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + // 获取生效中的模板 + Map messageTemplates = messageTemplateService.getAllTemplates().stream() + .collect(Collectors.toMap(MessageTemplate::getId, Function.identity())); + // 过滤掉relationId对应的消息模板已经删除的场景 + relationIdTemplateIdMap = relationIdTemplateIdMap.entrySet().stream() + .filter(e -> messageTemplates.containsKey(e.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + // 获取真实有效且待建立关联关系的relationId对应的消息模板路由 + Map> messageRouters = messageRouterService.getRouterMapByRelationIds( + Lists.newArrayList(relationIdTemplateIdMap.keySet())).stream() + .collect(Collectors.groupingBy(MessageRouter::getRelationId)); + // 转化为新模板的数据模型wrapper + List wrappers = relationIdTemplateIdMap.entrySet().stream() + .map(e -> convert(e.getKey(), messageTemplates.get(e.getValue()), messageRouters.get(e.getKey()))) + .collect(Collectors.toList()); + // 批量插入 + messageBaseTemplateDao.saveBatch(wrappers.stream() + .map(CreateNewTemplateAndMapWrapper::getBaseTemplate).collect(Collectors.toList())); + messageTemplateRouterService.batchInsert(wrappers.stream() + .filter(e -> CollectionUtils.isNotEmpty(e.getRouters())) + .flatMap(e -> e.getRouters().stream()).collect(Collectors.toList())); + List groupNodes = Lists.newArrayList(param.getGroupNodeCode()); + Map> templateGroupNodeMap = wrappers.stream() + .map(CreateNewTemplateAndMapWrapper::getBaseTemplate) + .collect(Collectors.toMap(MessageBaseTemplate::getCode, e -> groupNodes)); + messageTemplateGroupService.templateGroup(templateGroupNodeMap); + Map relationIdTemplateCodeMap = wrappers.stream() + .collect(Collectors + .toMap(CreateNewTemplateAndMapWrapper::getRelationId, e -> e.getBaseTemplate().getCode())); + mapRelationAndTemplate(relationIdTemplateCodeMap); + } + + private CreateNewTemplateAndMapWrapper convert(Long relationId, MessageTemplate template, + List routers) { + MessageBaseTemplate baseTemplate = convert(template); + CreateNewTemplateAndMapWrapper wrapper = CreateNewTemplateAndMapWrapper.builder() + .baseTemplate(baseTemplate) + .relationId(relationId) + .build(); + if (CollectionUtils.isNotEmpty(routers)) { + wrapper.setRouters(Lists.newArrayList( + RawMessageRouterDTO.from(convert(routers), baseTemplate.getCode()))); + } + return wrapper; + } + + private MessageBaseTemplate convert(MessageTemplate srcTemplate) { + MessageBaseTemplate template = new MessageBaseTemplate(); + template.setCode(UUIDUtil.uuidString()); + template.setName("template_" + srcTemplate.getId()); + template.setMsgCategory(MessageCategoryEnum.GENERAL_MESSAGE); + template.setTitle(srcTemplate.getTitle()); + template.setContent(srcTemplate.getContent()); + template.setCardContent(JSONObjectUtil.toJSONString(null)); + template.setPushTerminal(JSONObjectUtil.toJSONString(PushTerminalEnum.values())); + template.setCreatorId(0L); + template.setUpdaterId(0L); + return template; + } + + private MessageRouterButtonDTO convert(List routers) { + return MessageRouterButtonDTO.builder() + .style(Lists.newArrayList(ButtonStyleEnum.values())) + .desc("查看详情") + .category(RouterCategoryEnum.DETAIL) + .terminals(routers.stream().map(this::convert).collect(Collectors.toList())) + .build(); + } + + private MessageRouterTerminalDTO convert(MessageRouter router) { + return MessageRouterTerminalDTO.builder() + .url(router.getRouterUrl()) + .terminalType(TerminalTypeEnum.codeOf(router.getRouterType().getCode())) + .build(); + } + + private Set getAllOriginalRelationIds() { + return relationTemplateMapDao.lambdaQuery() + .select(RelationTemplateMap::getOriginalRelationId) + .list().stream().map(RelationTemplateMap::getOriginalRelationId).collect(Collectors.toSet()); + } + + @Setter + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + private static class CreateNewTemplateAndMapWrapper { + private Long relationId; + private MessageBaseTemplate baseTemplate; + private List routers; + } +} 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 new file mode 100644 index 00000000..b84d2992 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/JSONObjectUtil.java @@ -0,0 +1,75 @@ +package cn.axzo.msg.center.utils; + +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.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @author cold_blade + * @date 2023/10/13 + * @version 1.0 + */ +@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字符串,若字符串格式不正确,抛异常 + * + * @param str 待解析的字符串 + * @return JSONObject + */ + public static JSONObject parseObject(String str) { + if (StringUtils.isBlank(str)) { + return EMPTY_JSON_OBJ; + } + return JSON.parseObject(str); + } + + /** + * 解析JSON字符串,若字符串格式不正确,抛异常 + * + * @param str 待解析的字符串 + * @return List + */ + public static List parseArray(String str, Class clazz) { + if (StringUtils.isBlank(str)) { + return Collections.emptyList(); + } + return JSON.parseArray(str, clazz); + } + + 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字符串,若字符串格式不正确,抛异常 + * + * @param str 待校验的字符串 + * @return 原字符串OR空的JSONObject + */ + public static String checkAndReturn(String str) { + return parseObject(str).toJSONString(); + } +} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/MessageRouterUtil.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/MessageRouterUtil.java index 1ec2d748..05afda6b 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/utils/MessageRouterUtil.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/MessageRouterUtil.java @@ -3,17 +3,19 @@ package cn.axzo.msg.center.utils; import cn.axzo.msg.center.common.utils.PlaceholderResolver; import cn.axzo.msg.center.message.domain.dto.RawMessageRouterDTO; import cn.axzo.msg.center.service.dto.MessageRouterDTO; +import cn.axzo.msg.center.service.dto.MessageRouterTerminalDTO; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import com.alibaba.fastjson.JSON; -import com.google.common.collect.ImmutableMap; +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.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -30,31 +32,40 @@ public final class MessageRouterUtil { // 非法路由参数的定义 private static final String INVALID_ROUTER_PARAM = "null"; - // 当前系统选取路由的优先级策略 - private static final ImmutableMap ROUTER_SELECT_ORDER = - ImmutableMap.builder() - .put(TerminalTypeEnum.WEB_VIEW, 1) - .put(TerminalTypeEnum.MINI_PROGRAM, 2) - .put(TerminalTypeEnum.ANDROID, 3) - .put(TerminalTypeEnum.IOS, 3) - .put(TerminalTypeEnum.WEB, 3) - .put(TerminalTypeEnum.WECHAT_MINI_PROGRAM, 3) - .build(); /** * 根据指定的终端类型选取合适的路由 * - * @param messageRouters 模板配置的路由列表 - * @param terminalType 指定的终端类型,可为null - * @return 合适的路由 + * @param rawMessageRouter 原始的模板路由策略 + * @param terminalType 当前请求的终端类型 + * @return 合适的路由数据 */ - public static List selectRouter(List messageRouters, - TerminalTypeEnum terminalType) { - if (CollectionUtils.isEmpty(messageRouters)) { - return Collections.emptyList(); + public static MessageRouterTerminalDTO select(RawMessageRouterDTO rawMessageRouter, TerminalTypeEnum terminalType) { + if (RouterCategoryEnum.ACTION.equals(rawMessageRouter.getCategory())) { + return rawMessageRouter.getTerminals().get(0); } - return messageRouters.stream() - .filter(e -> Objects.equals(e.getTerminalType(), terminalType)) + return rawMessageRouter.getTerminals().stream() + .filter(e -> Objects.equals(terminalType, e.getTerminalType())) + // 若没有匹配的,默认选择第一个然后由前端去进行最终判断 + .findFirst().orElseGet(() -> rawMessageRouter.getTerminals().get(0)); + } + + /** + * 根据指定的终端类型选取合适的路由 + * + * @param rawMessageRouter 原始的模板路由策略 + * @param excludeTerminalType 待排除的终端类型 + * @return 合适的路由数据 + */ + public static List selectWithout(RawMessageRouterDTO rawMessageRouter, + TerminalTypeEnum excludeTerminalType) { + if (RouterCategoryEnum.ACTION.equals(rawMessageRouter.getCategory())) { + // 如果配置路由是API调用,这与终端无关 + return rawMessageRouter.getTerminals(); + } + return rawMessageRouter.getTerminals().stream() + .filter(e -> !Objects.equals(excludeTerminalType, e.getTerminalType())) + // 若没有匹配的,默认选择第一个然后由前端去进行最终判断 .collect(Collectors.toList()); } @@ -76,9 +87,8 @@ public final class MessageRouterUtil { * @param routerParam 路由参数 */ public static void parseRouteUrl(MessageRouterDTO router, String routerParam) { - // 路由参数有效且是直接调整页面地址的路由类型 - if (isRouterParamValid(routerParam) - && RouterCategoryEnum.JUMP.equals(router.getCategory())) { + // 路由参数有效 + if (isRouterParamValid(routerParam)) { // 替换原始URL中的参数变量 String routerUrl = PlaceholderResolver .getDefaultResolver().resolveByMap(router.getUrl(), JSON.parseObject(routerParam)); @@ -86,21 +96,54 @@ public final class MessageRouterUtil { } } - private static Integer compare(RawMessageRouterDTO src, RawMessageRouterDTO tgt) { - if (Objects.isNull(tgt)) { - // NULL 默认优先级最低 - return 1; + /** + * 解析模板上配置的路由地址,将发送消息时的参数替换上去,并将路由参数追加到模板的路由地址后面,兼容APP端新老版本 + * + * @param router 路由信息 + * @param routerParam 路由参数 + * @return RawMessageRouterDTO + */ + public static RawMessageRouterDTO parseAndConcatRouteUrl(RawMessageRouterDTO router, JSONObject routerParam) { + // 路由参数有效 + if (Objects.nonNull(routerParam) && !routerParam.isEmpty()) { + // 拷贝一份,避免修改入参 + router = router.deepClone(); + router.getTerminals().forEach(e -> { + // 替换原始URL中的参数变量 + String routerUrl = PlaceholderResolver.getDefaultResolver().resolveByMap(e.getUrl(), routerParam); + // 将routerParam追加到原始的URL后面 + routerUrl = concatRouterParam(routerUrl, routerParam); + e.setUrl(routerUrl); + }); } - Integer srcOrder = ROUTER_SELECT_ORDER.get(src.getTerminalType()); - Integer tgtOrder = ROUTER_SELECT_ORDER.get(tgt.getTerminalType()); - if (Objects.isNull(srcOrder)) { - // 新增的类型未指定优先级的话,则默认最低 - return 1; + return router; + } + + private static String concatRouterParam(String originalUrl, JSONObject routerParam) { + StringBuilder paramBuilder = new StringBuilder(); + for (Map.Entry entry : routerParam.entrySet()) { + if (originalUrl.contains(entry.getKey() + "=")) { + continue; + } + paramBuilder.append("&").append(entry.getKey()).append("=").append(entry.getValue()); } - if (Objects.isNull(tgtOrder)) { - // 新增的类型未指定优先级的话,则默认最低 - return -1; + if (!originalUrl.contains("?")) { + // 替换掉第一个参数分隔符(&) + paramBuilder.replace(0, 1, "?"); } - return srcOrder.compareTo(tgtOrder); + return originalUrl + paramBuilder; + } + + /** + * 解析按钮style + * + * @param style 按钮style的JSON字串 + * @return 按钮style的枚举列表 + */ + public static List parseButtonStyle(String style) { + if (StringUtils.isBlank(style)) { + return Collections.emptyList(); + } + return JSON.parseArray(style, ButtonStyleEnum.class); } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/PersonIdentityUtil.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/PersonIdentityUtil.java new file mode 100644 index 00000000..d567fdcd --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/PersonIdentityUtil.java @@ -0,0 +1,60 @@ +package cn.axzo.msg.center.utils; + +import cn.axzo.msg.center.api.enums.ReceiveTypeEnum; +import cn.axzo.msg.center.domain.enums.UserTypeEnum; +import cn.axzo.msg.center.service.enums.IdentityTypeEnum; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Objects; + +/** + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class PersonIdentityUtil { + + public static UserTypeEnum toUserType(IdentityTypeEnum identityType) { + switch (identityType) { + case WORKER: + return UserTypeEnum.CM; + case WORKER_LEADER: + return UserTypeEnum.TEAM; + case PRACTITIONER: + return UserTypeEnum.CMP; + default: + return UserTypeEnum.NOT_IDENTITY; + } + } + + public static IdentityTypeEnum toIdentityType(ReceiveTypeEnum receiveType) { + if (Objects.isNull(receiveType)) { + return IdentityTypeEnum.NOT_SUPPORT; + } + switch (receiveType) { + case CM_WORKER: + return IdentityTypeEnum.WORKER; + case CM_LEADER: + return IdentityTypeEnum.WORKER_LEADER; + case CMP_USER: + return IdentityTypeEnum.PRACTITIONER; + default: + return IdentityTypeEnum.NOT_SUPPORT; + } + } + + public static ReceiveTypeEnum toReceiveType(IdentityTypeEnum identityType) { + switch (identityType) { + case WORKER: + return ReceiveTypeEnum.CM_WORKER; + case WORKER_LEADER: + return ReceiveTypeEnum.CM_LEADER; + case PRACTITIONER: + return ReceiveTypeEnum.CMP_USER; + default: + return ReceiveTypeEnum.NOT_IDENTITY; + } + } +} 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..4a890a6e --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/TreeHelperUtil.java @@ -0,0 +1,53 @@ +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 cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; +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 root = wrapperFunc.apply(treeNode); + convertMap.put(treeNode, root); + 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 -> { + T wrapper = wrapperFunc.apply(e); + convertMap.put(e, wrapper); + return wrapper; + }).collect(Collectors.toList())); + treeNodeStack.addAll(children); + } + return root; + } + + public static boolean isLeafNodeCategory(MessageGroupNodeCategoryEnum category) { + return MessageGroupNodeCategoryEnum.GENERAL_MESSAGE_CATEGORY.equals(category) + || MessageGroupNodeCategoryEnum.PENDING_MESSAGE_CATEGORY.equals(category); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageRecordApi.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageRecordApi.java index 2fc65bf1..afb50d09 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageRecordApi.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageRecordApi.java @@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody; import javax.validation.Valid; -@FeignClient(name = "msg-center", url = "${server.serviceUrl}", fallback = InsideMessageRecordApiFallBack.class) +@FeignClient(name = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", fallback = InsideMessageRecordApiFallBack.class) @Component public interface InsideMessageRecordApi { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageTemplateApi.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageTemplateApi.java index f77a7f37..2045687d 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageTemplateApi.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideMessageTemplateApi.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@FeignClient(name = "msg-center", url = "${server.serviceUrl}", fallback = InsideMessageTemplateApiFallBack.class) +@FeignClient(name = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", fallback = InsideMessageTemplateApiFallBack.class) @Component public interface InsideMessageTemplateApi { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideNoticesApi.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideNoticesApi.java index addc0dc8..7a338415 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideNoticesApi.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/InsideNoticesApi.java @@ -16,7 +16,7 @@ import java.util.List; /** * 站内消息相关接口 */ -@FeignClient(name = "msg-center", url = "${server.serviceUrl}", fallback = InsideNoticesApiFallBack.class) +@FeignClient(name = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", fallback = InsideNoticesApiFallBack.class) @Component public interface InsideNoticesApi { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/MessagePushApi.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/MessagePushApi.java index 8fbb477e..498333c2 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/MessagePushApi.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/MessagePushApi.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestBody; /** * 发送app push消息,底层使用友盟 */ -@FeignClient(value = "msg-center", url = "${server.serviceUrl}", fallbackFactory = MessagePushApiFallbackFactory.class) +@FeignClient(value = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", fallbackFactory = MessagePushApiFallbackFactory.class) @Component public interface MessagePushApi { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/enums/MsgStateEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/enums/MsgStateEnum.java index 2ff96325..382f97ad 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/enums/MsgStateEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/enums/MsgStateEnum.java @@ -49,4 +49,11 @@ public enum MsgStateEnum { COMPLETE ); } + + public static List unreadStates() { + return Lists.newArrayList( + HAS_BEEN_SENT, + RECEIVED + ); + } } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/request/CmsMsgQueryReq.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/request/CmsMsgQueryReq.java index 3cd15dd9..1fd8b3c5 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/request/CmsMsgQueryReq.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/request/CmsMsgQueryReq.java @@ -4,6 +4,7 @@ import cn.axzo.basics.common.page.PageRequest; import lombok.Data; import javax.validation.constraints.NotNull; +import java.util.List; /** * @author : liuchuntao @@ -58,7 +59,8 @@ public class CmsMsgQueryReq extends PageRequest { * */ private Long identityId; - - - + /** + * 待排除的消息id + */ + private List excludeMsgIds; } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/response/MessageNewRes.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/response/MessageNewRes.java index 4da66b6e..d959abfe 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/response/MessageNewRes.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/response/MessageNewRes.java @@ -92,5 +92,8 @@ public class MessageNewRes { */ private Integer oldTypeId; - + /** + * 创建时间 + */ + private Date createAt; } 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..9425101c --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/GroupTreeNodeDTO.java @@ -0,0 +1,90 @@ +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 cn.axzo.msg.center.service.group.response.MessageGroupTreeNodeResponse; +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; +import java.util.stream.Collectors; + +/** + * @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 void setNodeChildren(List nodeChildren) { + this.nodeChildren = Optional.ofNullable(nodeChildren).orElseGet(Collections::emptyList); + } + + public MessageGroupTreeNodeResponse toMessageGroupTreeNodeResponse() { + List children = Optional.ofNullable(nodeChildren) + .map(v -> v.stream().map(GroupTreeNodeDTO::toMessageGroupTreeNodeResponse).collect(Collectors.toList())) + .orElseGet(Collections::emptyList); + return MessageGroupTreeNodeResponse.builder() + .category(category) + .nodeName(nodeName) + .nodeCode(nodeCode) + .parentNodeCode(parentNodeCode) + .children(children) + .build(); + } + + public Optional getChild(String treeNodeCode) { + if (StringUtils.isBlank(treeNodeCode) || Objects.isNull(nodeChildren)) { + return Optional.empty(); + } + return nodeChildren.stream() + .filter(e -> Objects.equals(e.getNodeCode(), treeNodeCode)) + .findFirst(); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageBaseTemplateDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageBaseTemplateDTO.java new file mode 100644 index 00000000..61a730d6 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageBaseTemplateDTO.java @@ -0,0 +1,70 @@ +package cn.axzo.msg.center.service.dto; + +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +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; + +/** + * @author cold_blade + * @date 2023/10/11 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageBaseTemplateDTO implements Serializable { + + private static final long serialVersionUID = -7079227473990043416L; + + /** + * 模板名称 + */ + private String name; + /** + * 系统自动生成的模板code + */ + private String code; + /** + * 所属消息类型 + */ + private MessageCategoryEnum msgCategory; + /** + * 模板标题 + */ + private String title; + /** + * 模板类容 + */ + private String content; + /** + * 卡片信息标签列表 + */ + private List cardContentItems; + /** + * 模板icon + */ + private String icon; + /** + * 推送终端配置列表 + */ + private List pushTerminals; + /** + * 消息模板创建时间戳 + */ + private Long createTimestamp; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageCardContentItemDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageCardContentItemDTO.java new file mode 100644 index 00000000..0f01379e --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageCardContentItemDTO.java @@ -0,0 +1,46 @@ +package cn.axzo.msg.center.service.dto; + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/20 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageCardContentItemDTO implements Serializable { + + private static final long serialVersionUID = -8696875791099408327L; + + /** + * 卡片内容标签 + */ + private String label; + /** + * 卡片内容标签值 + */ + private String value; + + public MessageCardContentItemDTO deepClone() { + return MessageCardContentItemDTO.builder() + .label(this.label) + .value(this.value) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterButtonDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterButtonDTO.java new file mode 100644 index 00000000..a4bd8e0d --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterButtonDTO.java @@ -0,0 +1,68 @@ +package cn.axzo.msg.center.service.dto; + +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; +import cn.axzo.msg.center.service.enums.RouterCategoryEnum; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.commons.collections.CollectionUtils; + +import java.io.Serializable; +import java.util.List; + +/** + * @description + * + * @author cold_blade + * @date 2023/9/23 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageRouterButtonDTO implements Serializable { + + private static final long serialVersionUID = -9083376003614521781L; + + /** + * 路由描述 + */ + private String desc; + /** + * 路由分类 + * JUMP: 直接跳转 + * ACTION: 接口调用 + * DETAIL: 页面详情 + */ + private RouterCategoryEnum category; + /** + * 按钮样式配置 + * HIGH_LIGHT: 按钮高亮展示 + * OVER_CARD: 按钮显示在卡片上 + */ + private List style; + /** + * 路由终端列表,若当前按钮为ACTION,则只能配一个接口地址 + */ + private List terminals; + + public boolean isHighlight() { + return CollectionUtils.isNotEmpty(style) + && style.stream().anyMatch(ButtonStyleEnum.HIGH_LIGHT::equals); + } + + public boolean isShowOnCard() { + return CollectionUtils.isNotEmpty(style) + && style.stream().anyMatch(ButtonStyleEnum.OVER_CARD::equals); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterDTO.java index 9afba13a..85576af1 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterDTO.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterDTO.java @@ -1,5 +1,6 @@ package cn.axzo.msg.center.service.dto; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import com.alibaba.fastjson.JSON; @@ -10,6 +11,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; +import java.util.List; /** * @description @@ -51,6 +53,12 @@ public class MessageRouterDTO implements Serializable { * WECHAT_MINI_PROGRAM: 微信小程序页面 */ private TerminalTypeEnum terminalType; + /** + * 按钮样式配置 + * HIGH_LIGHT: 按钮高亮展示 + * OVER_CARD: 按钮显示在卡片 + */ + private List style; @Override public String toString() { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterTerminalDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterTerminalDTO.java new file mode 100644 index 00000000..a660b78c --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/MessageRouterTerminalDTO.java @@ -0,0 +1,53 @@ +package cn.axzo.msg.center.service.dto; + +import cn.axzo.msg.center.service.enums.TerminalTypeEnum; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageRouterTerminalDTO implements Serializable { + + private static final long serialVersionUID = 4084885624985898305L; + + /** + * 页面地址 OR API接口地址 + */ + private String url; + /** + * 页面地址所属应用端(如果是API接口地址,请忽略改字段值) + * WEB: web端页面 + * MINI_PROGRAM: 安心筑小程序端页面 + * IOS: 原生IOS端页面 + * ANDROID: 原生Android端页面 + * WEB_VIEW: H5页面 + * WECHAT_MINI_PROGRAM: 微信小程序页面 + */ + private TerminalTypeEnum terminalType; + + public MessageRouterTerminalDTO deepClone() { + return MessageRouterTerminalDTO.builder() + .terminalType(this.terminalType) + .url(this.url) + .build(); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PersonDTO.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PersonDTO.java index fad40f2d..33a108a0 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PersonDTO.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PersonDTO.java @@ -9,6 +9,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; +import java.util.Objects; /** * @description @@ -46,6 +47,11 @@ public class PersonDTO implements Serializable { .build(); } + public boolean isValid() { + return Objects.nonNull(id) && id > 0 + && Objects.nonNull(identity) && identity.isValid(); + } + @Override public String toString() { return JSON.toJSONString(this); diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/ButtonStyleEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/ButtonStyleEnum.java new file mode 100644 index 00000000..8aad56c5 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/ButtonStyleEnum.java @@ -0,0 +1,22 @@ +package cn.axzo.msg.center.service.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 按钮style枚举 + * + * @author cold_blade + * @date 2023/10/11 + * @version 1.0 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum ButtonStyleEnum { + + HIGH_LIGHT("按钮高亮展示"), OVER_CARD("按钮显示在卡片"), + ; + + private final String desc; +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/MessageGroupNodeCategoryEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/MessageGroupNodeCategoryEnum.java index 0727b216..f85abe17 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/MessageGroupNodeCategoryEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/MessageGroupNodeCategoryEnum.java @@ -15,13 +15,15 @@ import lombok.Getter; @AllArgsConstructor(access = AccessLevel.PRIVATE) public enum MessageGroupNodeCategoryEnum { - GENERAL_MESSAGE_CENTER("消息中心"), - GENERAL_MESSAGE_MODULE("消息模块"), - GENERAL_MESSAGE_CATEGORY("消息分类"), - PENDING_MESSAGE_CENTER("待办中心"), - PENDING_MESSAGE_MODULE("待办模块"), - PENDING_MESSAGE_CATEGORY("待办分类"), + GENERAL_MESSAGE_CENTER("消息中心", 1, MessageCategoryEnum.GENERAL_MESSAGE), + GENERAL_MESSAGE_MODULE("消息模块", 2, MessageCategoryEnum.GENERAL_MESSAGE), + GENERAL_MESSAGE_CATEGORY("消息分类", 3, MessageCategoryEnum.GENERAL_MESSAGE), + PENDING_MESSAGE_CENTER("待办中心", 1, MessageCategoryEnum.PENDING_MESSAGE), + PENDING_MESSAGE_MODULE("待办模块", 2, MessageCategoryEnum.PENDING_MESSAGE), + PENDING_MESSAGE_CATEGORY("待办分类", 3, MessageCategoryEnum.PENDING_MESSAGE), ; private final String desc; + private final int level; + private final MessageCategoryEnum msgCategory; } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PushTerminalEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PushTerminalEnum.java new file mode 100644 index 00000000..c52deaf6 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PushTerminalEnum.java @@ -0,0 +1,29 @@ +package cn.axzo.msg.center.service.enums; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 推送终端枚举 + * + * @author cold_blade + * @date 2023/10/11 + * @version 1.0 + */ +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public enum PushTerminalEnum { + + /** + * B-安心筑企业版 + */ + B_ENTERPRISE_APP("B-安心筑企业版"), + /** + * C-安心筑工人版 + */ + C_WORKER_APP("C-安心筑工人版"), + ; + + private final String desc; +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/RouterCategoryEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/RouterCategoryEnum.java index 04a10dcd..33e4b405 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/RouterCategoryEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/RouterCategoryEnum.java @@ -16,7 +16,9 @@ import lombok.Getter; public enum RouterCategoryEnum { JUMP("直接跳转"), - ACTION("接口调用"); + ACTION("接口调用"), + DETAIL("页面详情"), + ; private final String desc; } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/TerminalTypeEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/TerminalTypeEnum.java index 1394e4fc..f9daae5f 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/TerminalTypeEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/TerminalTypeEnum.java @@ -4,6 +4,8 @@ import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Arrays; + /** * @description * @@ -20,7 +22,12 @@ public enum TerminalTypeEnum { IOS(3, "ios"), ANDROID(4, "android"), WEB_VIEW(5, "webview"), - WECHAT_MINI_PROGRAM(6, "wechat mini-program"); + WECHAT_MINI_PROGRAM(6, "wechat mini-program"), + /** + * 所有端 + */ + ALL(-1, "all"), + ; private final Integer code; private final String desc; @@ -40,4 +47,10 @@ public enum TerminalTypeEnum { } return WEB; } + + public static TerminalTypeEnum codeOf(Integer code) { + return Arrays.stream(values()) + .filter(e -> e.code.equals(code)) + .findFirst().orElse(null); + } } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/GeneralMessageClient.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/GeneralMessageClient.java new file mode 100644 index 00000000..581798c6 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/GeneralMessageClient.java @@ -0,0 +1,68 @@ +package cn.axzo.msg.center.service.general.client; + +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.service.general.client.fallback.GeneralMessageClientFallback; +import cn.axzo.msg.center.service.general.request.GeneralMessageOldDataStatisticRequest; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.service.general.response.GeneralMessageOldDataStatisticResponse; +import cn.azxo.framework.common.model.CommonResponse; +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.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; + +/** + * 普通消息模块 + * + * @author cold_blade + * @date 2023/10/18 + * @version 1.0 + */ +@Component +@FeignClient(value = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", + fallback = GeneralMessageClientFallback.class) +public interface GeneralMessageClient { + + /** + * 发送消息 + * + * @param request 消息所需参数 + * @return 消息的唯一标识 + */ + @Deprecated + @PostMapping(value = "/general-message/send", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse batchSend(@RequestBody @Valid GeneralMessageSendRequest request); + + /** + * 统计旧消息的未读数以及最新一条消息内容 + * + * @param request 消息所需参数 + * @return 消息未读数&最新一条消息内容 + */ + @PostMapping(value = "/general-message/old-data/statistic", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse statisticOldData( + @RequestBody @Valid GeneralMessageOldDataStatisticRequest request); + + /** + * 统计旧消息的未读数 + * + * @param request 消息所需参数 + * @return 消息未读数 + */ + @PostMapping(value = "/general-message/old-data/count-unread", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse countUnreadFromOldMessage(@RequestBody @Valid GeneralMessageOldDataStatisticRequest request); + + /** + * 旧消息的分页查询入口 + * + * @param request 分页查询所需参数 + * @return 旧消息的分页列表 + */ + @PostMapping(value = "/general-message/old-data/page", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse> pageQueryOldMessage(@RequestBody @Valid CmsMsgQueryReq request); +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/fallback/GeneralMessageClientFallback.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/fallback/GeneralMessageClientFallback.java new file mode 100644 index 00000000..ac6d965e --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/client/fallback/GeneralMessageClientFallback.java @@ -0,0 +1,47 @@ +package cn.axzo.msg.center.service.general.client.fallback; + +import cn.axzo.msg.center.api.request.CmsMsgQueryReq; +import cn.axzo.msg.center.api.response.MessageNewRes; +import cn.axzo.msg.center.service.general.client.GeneralMessageClient; +import cn.axzo.msg.center.service.general.request.GeneralMessageOldDataStatisticRequest; +import cn.axzo.msg.center.service.general.request.GeneralMessageSendRequest; +import cn.axzo.msg.center.service.general.response.GeneralMessageOldDataStatisticResponse; +import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author cold_blade + * @date 2023/10/18 + * @version 1.0 + */ +@Slf4j +@Component +public class GeneralMessageClientFallback implements GeneralMessageClient { + + @Override + public CommonResponse batchSend(GeneralMessageSendRequest request) { + log.error("fall back while batch sending im message. req:{}", request); + return CommonResponse.error("fall back while batch sending im message"); + } + + @Override + public CommonResponse statisticOldData( + GeneralMessageOldDataStatisticRequest request) { + log.error("fall back while statistic old message. req:{}", request); + return CommonResponse.error("fall back while statistic old message"); + } + + @Override + public CommonResponse countUnreadFromOldMessage(GeneralMessageOldDataStatisticRequest request) { + log.error("fall back while counting unread old message. request:{}", request); + return CommonResponse.error("fall back while counting unread old message"); + } + + @Override + public CommonResponse> pageQueryOldMessage(CmsMsgQueryReq request) { + log.error("fall back while statistic old message. request:{}", request); + return CommonResponse.error("fall back while statistic old message"); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageOldDataStatisticRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageOldDataStatisticRequest.java new file mode 100644 index 00000000..04a88331 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageOldDataStatisticRequest.java @@ -0,0 +1,48 @@ +package cn.axzo.msg.center.service.general.request; + +import cn.axzo.msg.center.service.enums.IdentityTypeEnum; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GeneralMessageOldDataStatisticRequest implements Serializable { + + private static final long serialVersionUID = -7739989493953842047L; + + /** + * 自然人id + */ + @NotNull(message = "personId is required") + private Long personId; + /** + * 身份id + */ + @NotNull(message = "identityId is required") + private Long identityId; + /** + * 身份类型 + */ + @NotNull(message = "identityType is required") + private IdentityTypeEnum identityType; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageSendRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageSendRequest.java new file mode 100644 index 00000000..74595539 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/request/GeneralMessageSendRequest.java @@ -0,0 +1,79 @@ +package cn.axzo.msg.center.service.general.request; + +import cn.axzo.msg.center.service.dto.PersonDTO; +import cn.axzo.msg.center.service.enums.OrganizationTypeEnum; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.Collection; + +/** + * @description + * 发送消息 + * @author cold_blade + * @date 2023/10/18 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GeneralMessageSendRequest implements Serializable { + + private static final long serialVersionUID = -3030926259836918967L; + + /** + * 模板编码 + */ + @NotBlank(message = "templateCode is required") + private String templateCode; + /** + * 消息发送者信息,可为空,默认模板对应的IM机器人 + */ + private PersonDTO sender; + /** + * 消息接收信息 + */ + @NotEmpty(message = "receiver is required") + private Collection receiver; + /** + * 消息所属组织类型 + */ + private OrganizationTypeEnum orgType; + /** + * 消息所属组织Id + */ + private Long orgId; + /** + * 消息所属组织名称 + */ + private String orgName; + /** + * 发送消息的业务编码 + */ + private String bizCode; + /** + * 路由参数 + * 注:该字段仅存放路由相关的变量及对应的值 + */ + private JSONObject routerParams; + /** + * 业务扩展参数 + * 注:标题/内容/以及卡片等的变量及对应的值放到该字段 + */ + private JSONObject bizExtParams; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageOldDataStatisticResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageOldDataStatisticResponse.java new file mode 100644 index 00000000..f864a589 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageOldDataStatisticResponse.java @@ -0,0 +1,45 @@ +package cn.axzo.msg.center.service.general.response; + +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @description + * 普通消息记录统计数模型 + * @author cold_blade + * @date 2023/10/23 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GeneralMessageOldDataStatisticResponse implements Serializable { + + private static final long serialVersionUID = 5740922087866033787L; + + /** + * 消息的未读数 + */ + private Integer unreadCount; + /** + * 最新的一条消息的发送时间戳 + */ + private Long latestMsgSendTimestamp; + /** + * 最新的一条消息的消息内容 + */ + private String latestMsgContent; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageResponse.java new file mode 100644 index 00000000..b5c8150c --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/general/response/GeneralMessageResponse.java @@ -0,0 +1,117 @@ +package cn.axzo.msg.center.service.general.response; + +import cn.axzo.msg.center.service.dto.IdentityDTO; +import cn.axzo.msg.center.service.dto.MessageRouterDTO; +import cn.axzo.msg.center.service.enums.OrganizationTypeEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; + +/** + * @description + * 普通消息记录数模型 + * @author cold_blade + * @date 2023/10/18 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GeneralMessageResponse implements Serializable { + + private static final long serialVersionUID = 5740922087866033787L; + + /** + * 消息的唯一标识 + */ + private String identityCode; + /** + * 模板编码 + */ + private String templateCode; + /** + * 模板icon地址 + */ + private String templateIcon; + /** + * 消息标题 + */ + private String title; + /** + * 消息内容 + */ + private String content; + /** + * 卡片信息 + */ + private String cardContent; + /** + * 消息发送者自然人id + */ + private Long senderPersonId; + /** + * 消息发送者身份 + */ + private IdentityDTO senderIdentity; + /** + * 消息接收者自然人id + */ + private Long receiverPersonId; + /** + * 消息接收者身份 + */ + private IdentityDTO receiverIdentity; + /** + * 消息所属组织类型 + */ + private OrganizationTypeEnum orgType; + /** + * 消息所属组织Id + */ + private Long orgId; + /** + * 消息所属组织名称 + */ + private String orgName; + /** + * 业务编码 + */ + private String bizCode; + /** + * 业务状态描述 + */ + private String bizDesc; + /** + * 消息发送时间戳 + */ + private Long sendTimestamp; + /** + * 路由信息,可为空 + */ + private List routers; + /** + * 参数及其对应的值的JSON串 + */ + private JSONObject routerParams; + /** + * 发送终端,eg: + * B_ENTERPRISE_APP:B-安心筑企业版 + * C_WORKER_APP:C-安心筑工人版 + */ + private List pushTerminals; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageGroupClient.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageGroupClient.java index 611167eb..04c8bb65 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageGroupClient.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageGroupClient.java @@ -1,8 +1,10 @@ package cn.axzo.msg.center.service.group.client; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; import cn.axzo.msg.center.service.group.client.fallback.MessageGroupClientFallback; import cn.axzo.msg.center.service.group.request.MessageGroupNodeAddRequest; import cn.axzo.msg.center.service.group.request.MessageGroupNodeUpdateRequest; +import cn.axzo.msg.center.service.group.response.MessageGroupTreeNodeResponse; import cn.azxo.framework.common.model.CommonResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; @@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import javax.validation.Valid; +import java.util.List; /** * 消息分类管理 @@ -40,7 +43,7 @@ public interface MessageGroupClient { * @param request 待更新的结点相关数据项 */ @PostMapping(value = "/message/group/node/update", produces = {MediaType.APPLICATION_JSON_VALUE}) - CommonResponse updateStatus(@RequestBody MessageGroupNodeUpdateRequest request); + CommonResponse updateNode(@RequestBody @Valid MessageGroupNodeUpdateRequest request); /** * 删除结点 @@ -50,4 +53,13 @@ public interface MessageGroupClient { @PostMapping(value = "/message/group/node/delete", produces = {MediaType.APPLICATION_JSON_VALUE}) CommonResponse deleteNode(@RequestParam("nodeCode") String nodeCode, @RequestParam("operatorId") Long operatorId); + + /** + * 查询通知/待办的分类信息 + * + * @param category 消息分类 + */ + @PostMapping(value = "/message/group/node/list", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse> list(@RequestParam(value = "category", required = false) + MessageCategoryEnum category); } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageTemplateGroupRelationClient.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageTemplateGroupRelationClient.java new file mode 100644 index 00000000..4b96fd86 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/MessageTemplateGroupRelationClient.java @@ -0,0 +1,56 @@ +package cn.axzo.msg.center.service.group.client; + +import cn.axzo.msg.center.service.group.client.fallback.MessageGroupClientFallback; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationMoveRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationPageRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationRemoveRequest; +import cn.axzo.msg.center.service.group.response.MessageTemplateGroupRelationResponse; +import cn.azxo.framework.common.model.CommonResponse; +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.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Component +@FeignClient(value = "msg-center", url = "${server.serviceUrl:http://msg-center:8080}", + fallback = MessageGroupClientFallback.class) +public interface MessageTemplateGroupRelationClient { + + + /** + * 分页查询分类下关联的模板列表 + * + * @param request 分类结点的编码及分页参数 + * @return 模板列表 + */ + @PostMapping(value = "/message/template/group-relation/page", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse> page( + @RequestBody @Valid MessageTemplateGroupRelationPageRequest request); + + /** + * 变更模板与分类的关联关系 + * + * @param request 变更分类的相关参数 + */ + @PostMapping(value = "/message/template/group-relation/move", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse move(@RequestBody @Valid MessageTemplateGroupRelationMoveRequest request); + + /** + * 解除分类与模板的关联关系 + * + * @param request 变更分类的相关参数 + * @return 关联关系解除失败的模板编码 + */ + @PostMapping(value = "/message/template/group-relation/remove", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse remove(@RequestBody @Valid MessageTemplateGroupRelationRemoveRequest request); +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageGroupClientFallback.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageGroupClientFallback.java index 9c350661..b72f583b 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageGroupClientFallback.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageGroupClientFallback.java @@ -1,12 +1,16 @@ package cn.axzo.msg.center.service.group.client.fallback; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; import cn.axzo.msg.center.service.group.client.MessageGroupClient; import cn.axzo.msg.center.service.group.request.MessageGroupNodeAddRequest; import cn.axzo.msg.center.service.group.request.MessageGroupNodeUpdateRequest; +import cn.axzo.msg.center.service.group.response.MessageGroupTreeNodeResponse; import cn.azxo.framework.common.model.CommonResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.util.List; + /** * @description * @@ -24,7 +28,7 @@ public class MessageGroupClientFallback implements MessageGroupClient { } @Override - public CommonResponse updateStatus(MessageGroupNodeUpdateRequest request) { + public CommonResponse updateNode(MessageGroupNodeUpdateRequest request) { log.error("fall back while updating message group node. request:{}", request); return CommonResponse.error("fall back while updating message group node"); } @@ -34,4 +38,10 @@ public class MessageGroupClientFallback implements MessageGroupClient { log.error("fall back while deleting message group node. nodeCode:[{}], operatorId:[{}]", nodeCode, operatorId); return CommonResponse.error("fall back while deleting message group node"); } + + @Override + public CommonResponse> list(MessageCategoryEnum category) { + log.error("fall back while listing message group node. category:[{}]", category); + return CommonResponse.error("fall back while listing message group node"); + } } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageTemplateGroupRelationClientFallback.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageTemplateGroupRelationClientFallback.java new file mode 100644 index 00000000..36fe2be7 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/client/fallback/MessageTemplateGroupRelationClientFallback.java @@ -0,0 +1,42 @@ +package cn.axzo.msg.center.service.group.client.fallback; + +import cn.axzo.msg.center.service.group.client.MessageTemplateGroupRelationClient; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationMoveRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationPageRequest; +import cn.axzo.msg.center.service.group.request.MessageTemplateGroupRelationRemoveRequest; +import cn.axzo.msg.center.service.group.response.MessageTemplateGroupRelationResponse; +import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Slf4j +@Component +public class MessageTemplateGroupRelationClientFallback implements MessageTemplateGroupRelationClient { + + @Override + public CommonResponse> page( + MessageTemplateGroupRelationPageRequest request) { + log.error("fall back while page querying message template group relation. request:{}", request); + return CommonResponse.error("fall back while page querying message template group relation"); + } + + @Override + public CommonResponse move(MessageTemplateGroupRelationMoveRequest request) { + log.error("fall back while moving message template group relation. request:{}", request); + return CommonResponse.error("fall back while moving message template group relation"); + } + + @Override + public CommonResponse remove(MessageTemplateGroupRelationRemoveRequest request) { + log.error("fall back while removing message template group relation. request:{}", request); + return CommonResponse.error("fall back while removing message template group relation"); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeAddRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeAddRequest.java index 852c215b..fd7ffaab 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeAddRequest.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeAddRequest.java @@ -36,13 +36,14 @@ public class MessageGroupNodeAddRequest implements Serializable { */ @NotEmpty(message = "nodeName is required") private String nodeName; - /** - * 待添加结点编码 - */ - @NotEmpty(message = "nodeCode is required") - private String nodeCode; /** * 待添加结点类型 + * GENERAL_MESSAGE_CENTER: 通知的业务中心 + * GENERAL_MESSAGE_MODULE: 消息模块 + * GENERAL_MESSAGE_CATEGORY: 消息分类 + * PENDING_MESSAGE_CENTER: 待办的业务中心 + * PENDING_MESSAGE_MODULE: 待办模块 + * PENDING_MESSAGE_CATEGORY: 待办分类 */ @NotNull(message = "category is required") private MessageGroupNodeCategoryEnum category; diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeUpdateRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeUpdateRequest.java index de903960..1c33d1da 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeUpdateRequest.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageGroupNodeUpdateRequest.java @@ -39,6 +39,8 @@ public class MessageGroupNodeUpdateRequest implements Serializable { private String nodeName; /** * 结点状态 + * ENABLE: 启用 + * DISABLE: 禁用 */ private StatusEnum status; /** diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationMoveRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationMoveRequest.java new file mode 100644 index 00000000..bcfff02e --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationMoveRequest.java @@ -0,0 +1,49 @@ +package cn.axzo.msg.center.service.group.request; + +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateGroupRelationMoveRequest implements Serializable { + + private static final long serialVersionUID = 8336083457743619246L; + + /** + * 操作者的自然人id + */ + @NotNull(message = "operatorId is required") + private Long operatorId; + /** + * 模板当前所处的分类结点编码 + */ + @NotBlank(message = "curNodeCode is required") + private String curNodeCode; + /** + * 模板将要移动到的目标分类结点编码 + */ + @NotBlank(message = "targetNodeCode is required") + private String targetNodeCode; + /** + * 选中的模板编码列表 + */ + @NotEmpty(message = "templateCodes is required") + private List templateCodes; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationPageRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationPageRequest.java new file mode 100644 index 00000000..4544126c --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationPageRequest.java @@ -0,0 +1,36 @@ +package cn.axzo.msg.center.service.group.request; + +import cn.axzo.basics.common.page.PageRequest; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/16 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateGroupRelationPageRequest extends PageRequest implements Serializable { + + private static final long serialVersionUID = -4297862224638557525L; + + /** + * 分类结点编码 + */ + @NotBlank(message = "nodeCode is required") + private String nodeCode; + /** + * 模板名称 + */ + private String templateName; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationRemoveRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationRemoveRequest.java new file mode 100644 index 00000000..72f97e15 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/request/MessageTemplateGroupRelationRemoveRequest.java @@ -0,0 +1,44 @@ +package cn.axzo.msg.center.service.group.request; + +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateGroupRelationRemoveRequest implements Serializable { + + private static final long serialVersionUID = 8336083457743619246L; + + /** + * 操作者的自然人id + */ + @NotNull(message = "operatorId is required") + private Long operatorId; + /** + * 模板当前所处的分类结点编码 + */ + @NotBlank(message = "curNodeCode is required") + private String curNodeCode; + /** + * 选中的模板编码列表 + */ + @NotEmpty(message = "templateCodes is required") + private List templateCodes; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageGroupTreeNodeResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageGroupTreeNodeResponse.java new file mode 100644 index 00000000..e2b31149 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageGroupTreeNodeResponse.java @@ -0,0 +1,59 @@ +package cn.axzo.msg.center.service.group.response; + +import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; +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; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageGroupTreeNodeResponse implements Serializable { + + private static final long serialVersionUID = -6741888813327778598L; + + /** + * 结点类型 + * 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 List children; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageTemplateGroupRelationResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageTemplateGroupRelationResponse.java new file mode 100644 index 00000000..a184e65b --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/group/response/MessageTemplateGroupRelationResponse.java @@ -0,0 +1,35 @@ +package cn.axzo.msg.center.service.group.response; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateGroupRelationResponse implements Serializable { + + private static final long serialVersionUID = -5321330362977310550L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 模板编码 + */ + private String templateCode; + /** + * 消息标题 + */ + private String title; + /** + * 模板创建时间戳 + */ + private Long createTimestamp; +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/PendingMessagePushRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/PendingMessagePushRequest.java index ab8182c3..ad983d88 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/PendingMessagePushRequest.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/PendingMessagePushRequest.java @@ -68,6 +68,10 @@ public class PendingMessagePushRequest implements Serializable { * 业务描述eg:流程结点描述 */ private String bizDesc; + /** + * 业务扩展参数,JSON字符串格式 + */ + private String bizExtParams; /** * 路由参数(json string) */ diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/PendingMessageResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/PendingMessageResponse.java index 643408cf..a8461f70 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/PendingMessageResponse.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/PendingMessageResponse.java @@ -93,6 +93,10 @@ public class PendingMessageResponse implements Serializable { * 参数及其对应的值的JSON串 */ private String routerParams; + /** + * 业务扩展参数 + */ + private String bizExtParams; @Override public String toString() { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/MessageTemplateClient.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/MessageTemplateClient.java index 325c6d2f..a189a694 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/MessageTemplateClient.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/MessageTemplateClient.java @@ -2,15 +2,23 @@ package cn.axzo.msg.center.service.template.client; import cn.axzo.msg.center.service.template.client.fallback.MessageTemplateClientFallback; import cn.axzo.msg.center.service.template.request.MessageTemplateCreateRequest; -import cn.axzo.msg.center.service.template.request.MessageTemplateMoveToRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateStatusRequest; +import cn.axzo.msg.center.service.template.response.MessageTemplateDetailResponse; +import cn.axzo.msg.center.service.template.response.MessageTemplatePageResponse; import cn.azxo.framework.common.model.CommonResponse; +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.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import javax.validation.Valid; +import java.util.Collection; +import java.util.List; /** * 消息模板管理 @@ -29,15 +37,51 @@ public interface MessageTemplateClient { * 添加消息模板 * * @param request 模板数据模型 + * @return 模板编码 */ @PostMapping(value = "/message/template/add", produces = {MediaType.APPLICATION_JSON_VALUE}) - CommonResponse addTemplate(@RequestBody @Valid MessageTemplateCreateRequest request); + CommonResponse save(@RequestBody @Valid MessageTemplateCreateRequest request); /** - * 批量消息模板分类迁移 + * 编辑消息模板 * - * @param request 待迁移的关联关系id以及目标分类 + * @param request 模板数据模型 */ - @PostMapping(value = "/message/template/group/batch-move", produces = {MediaType.APPLICATION_JSON_VALUE}) - CommonResponse batchMove(@RequestBody @Valid MessageTemplateMoveToRequest request); + @PostMapping(value = "/message/template/update", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse update(@RequestBody @Valid MessageTemplateUpdateRequest request); + + /** + * 消息模板详情 + * + * @param templateCode 消息模板编码 + * @return 消息模板详情 + */ + @PostMapping(value = "/message/template/detail", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse detail(@RequestParam("templateCode") String templateCode); + + /** + * 分页查询模板 + * + * @param request 分页查询参数 + * @return 模板列表 + */ + @PostMapping(value = "/message/template/page", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse> page(@RequestBody MessageTemplatePageRequest request); + + /** + * 通过模板编码查询对应的模板 + * + * @param templateCodes 模板编码集合 + * @return 模板列表 + */ + @PostMapping(value = "/message/template/list", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse> listByCodes(@RequestParam("templateCodes") Collection templateCodes); + + /** + * 启用/禁用消息模板 + * + * @param request 模板状态 + */ + @PostMapping(value = "/message/template/update-status", produces = {MediaType.APPLICATION_JSON_VALUE}) + CommonResponse updateStatus(@RequestBody @Valid MessageTemplateUpdateStatusRequest request); } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/fallback/MessageTemplateClientFallback.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/fallback/MessageTemplateClientFallback.java index 1b79f7d7..646b9b3e 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/fallback/MessageTemplateClientFallback.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/client/fallback/MessageTemplateClientFallback.java @@ -2,11 +2,19 @@ package cn.axzo.msg.center.service.template.client.fallback; import cn.axzo.msg.center.service.template.client.MessageTemplateClient; import cn.axzo.msg.center.service.template.request.MessageTemplateCreateRequest; -import cn.axzo.msg.center.service.template.request.MessageTemplateMoveToRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplatePageRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateRequest; +import cn.axzo.msg.center.service.template.request.MessageTemplateUpdateStatusRequest; +import cn.axzo.msg.center.service.template.response.MessageTemplateDetailResponse; +import cn.axzo.msg.center.service.template.response.MessageTemplatePageResponse; import cn.azxo.framework.common.model.CommonResponse; +import cn.azxo.framework.common.model.Page; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.util.Collection; +import java.util.List; + /** * @description * @@ -18,14 +26,38 @@ import org.springframework.stereotype.Component; @Component public class MessageTemplateClientFallback implements MessageTemplateClient { @Override - public CommonResponse addTemplate(MessageTemplateCreateRequest request) { + public CommonResponse save(MessageTemplateCreateRequest request) { log.error("fall back while adding template. request:{}", request); return CommonResponse.error("fall back while adding template"); } @Override - public CommonResponse batchMove(MessageTemplateMoveToRequest request) { - log.error("fall back while batch moving to target group. request:{}", request); - return CommonResponse.error("fall back while batch moving to target group"); + public CommonResponse update(MessageTemplateUpdateRequest request) { + log.error("fall back while updating template. request:{}", request); + return CommonResponse.error("fall back while updating template"); + } + + @Override + public CommonResponse detail(String templateCode) { + log.error("fall back while query template detail. templateCode:{}", templateCode); + return CommonResponse.error("fall back while query template detail"); + } + + @Override + public CommonResponse> page(MessageTemplatePageRequest request) { + log.error("fall back while paging query template. request:{}", request); + return CommonResponse.error("fall back while paging query template"); + } + + @Override + public CommonResponse> listByCodes(Collection templateCodes) { + log.error("fall back while listing templates. templateCodes:{}", templateCodes); + return CommonResponse.error("fall back while listing template"); + } + + @Override + public CommonResponse updateStatus(MessageTemplateUpdateStatusRequest request) { + log.error("fall back while updating template status. request:{}", request); + return CommonResponse.error("fall back while updating template status"); } } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateCreateRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateCreateRequest.java index f4cec735..6699b9ab 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateCreateRequest.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateCreateRequest.java @@ -1,7 +1,9 @@ package cn.axzo.msg.center.service.template.request; -import cn.axzo.msg.center.service.dto.MessageRouterDTO; +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; import com.alibaba.fastjson.JSON; import lombok.Getter; import lombok.Setter; @@ -43,19 +45,25 @@ public class MessageTemplateCreateRequest implements Serializable { @NotNull(message = "category is required") private MessageCategoryEnum category; /** - * 消息分类结点的结点编码列表 + * 消息分类树的叶结点的结点编码列表 */ - @NotEmpty(message = "msgGroupNodeCodes is required") - private List msgGroupNodeCodes; + @NotEmpty(message = "leafGroupNodes is required") + private List leafGroupNodes; + /** + * 推送终端配置 + * B_ENTERPRISE_APP: B-安心筑企业版 + * C_WORKER_APP: C-安心筑工人版 + */ + private List pushTerminals; /** * 消息标题 */ @NotBlank(message = "msgTitle is required") private String msgTitle; /** - * 消息卡片信息 + * 消息卡片信息标签列表,可为空 */ - private String msgCardInfo; + private List msgCardContentItems; /** * 消息内容 */ @@ -68,7 +76,7 @@ public class MessageTemplateCreateRequest implements Serializable { /** * 路由策略列表 */ - private List routers; + private List routers; @Override public String toString() { diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateMoveToRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateMoveToRequest.java deleted file mode 100644 index c7bee961..00000000 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateMoveToRequest.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.axzo.msg.center.service.template.request; - -import com.alibaba.fastjson.JSON; -import lombok.Getter; -import lombok.Setter; - -import java.io.Serializable; -import java.util.List; - -/** - * @description - * - * @author cold_blade - * @date 2023/9/26 - * @version 1.0 - */ -@Setter -@Getter -public class MessageTemplateMoveToRequest implements Serializable { - - private static final long serialVersionUID = 6484726175219602690L; - - /** - * 操作者的自然人id - */ - private Long operatorId; - /** - * 模板与分类的关联关系的id列表 - */ - private List templateGroupRelationIds; - /** - * 迁移的目标分类结点编码 - */ - private String targetGroupNodeCode; - - @Override - public String toString() { - return JSON.toJSONString(this); - } -} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplatePageRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplatePageRequest.java new file mode 100644 index 00000000..0532e639 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplatePageRequest.java @@ -0,0 +1,52 @@ +package cn.axzo.msg.center.service.template.request; + +import cn.axzo.basics.common.page.PageRequest; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/13 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplatePageRequest extends PageRequest implements Serializable { + + private static final long serialVersionUID = 3501567663122621175L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 模板编码 + */ + private String templateCode; + /** + * 消息类型 + * GENERAL_MESSAGE: 普通消息 + * PENDING_MESSAGE: 待办消息 + */ + private MessageCategoryEnum category; + /** + * 分类树的结点编码 + */ + private String groupNodeCode; + /** + * 模板状态 + * ENABLE: 启用 + * DISABLE: 禁用 + */ + 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/template/request/MessageTemplateUpdateRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateUpdateRequest.java new file mode 100644 index 00000000..92020c2b --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateUpdateRequest.java @@ -0,0 +1,77 @@ +package cn.axzo.msg.center.service.template.request; + +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * @description + * + * @author cold_blade + * @date 2023/10/11 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateUpdateRequest implements Serializable { + + private static final long serialVersionUID = -2894419272913799317L; + + /** + * 操作者的自然人id + */ + @NotNull(message = "operatorId is required") + private Long operatorId; + /** + * 模板编码 + */ + @NotBlank(message = "templateCode is required") + private String templateCode; + /** + * 模板名称 + */ + private String templateName; + /** + * 消息分类树的叶结点的结点编码列表 + */ + private List leafGroupNodes; + /** + * 推送终端配置 + * B_ENTERPRISE_APP: B-安心筑企业版 + * C_WORKER_APP: C-安心筑工人版 + */ + private List pushTerminals; + /** + * 消息标题 + */ + private String msgTitle; + /** + * 消息卡片信息标签列表 + */ + private List msgCardContentItems; + /** + * 消息内容 + */ + private String msgContent; + /** + * 消息图标 + */ + private String msgIcon; + /** + * 路由策略列表 + */ + private List routers; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateUpdateStatusRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateUpdateStatusRequest.java new file mode 100644 index 00000000..3443615d --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/request/MessageTemplateUpdateStatusRequest.java @@ -0,0 +1,45 @@ +package cn.axzo.msg.center.service.template.request; + +import cn.axzo.msg.center.service.enums.StatusEnum; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/13 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateUpdateStatusRequest implements Serializable { + + private static final long serialVersionUID = 818483349559289586L; + + /** + * 操作者的自然人id + */ + @NotNull(message = "operatorId is required") + private Long operatorId; + /** + * 模板编码 + */ + @NotBlank(message = "templateCode is required") + private String templateCode; + /** + * 模板状态 + * ENABLE: 启用 + * DISABLE: 禁用 + */ + @NotNull(message = "status is required") + 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/template/response/MessageTemplateDetailResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplateDetailResponse.java new file mode 100644 index 00000000..74fb8806 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplateDetailResponse.java @@ -0,0 +1,85 @@ +package cn.axzo.msg.center.service.template.response; + +import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; +import cn.axzo.msg.center.service.dto.MessageRouterButtonDTO; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.PushTerminalEnum; +import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/11 + * @version 1.0 + */ +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageTemplateDetailResponse implements Serializable { + + private static final long serialVersionUID = 6772912658753264863L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 消息类型 + * GENERAL_MESSAGE: 普通消息 + * PENDING_MESSAGE: 待办消息 + */ + private MessageCategoryEnum category; + /** + * 消息分类树的叶结点的结点编码列表 + */ + private List leafGroupNodes; + /** + * 推送终端配置 + * B_ENTERPRISE_APP: B-安心筑企业版 + * C_WORKER_APP: C-安心筑工人版 + */ + private List pushTerminals; + /** + * 消息标题 + */ + private String msgTitle; + /** + * 卡片信息标签列表 + */ + private List cardContentItems; + /** + * 消息内容 + */ + private String msgContent; + /** + * 消息图标 + */ + private String msgIcon; + /** + * 路由策略列表 + */ + private List routers; + /** + * 模板创建时间戳 + */ + private Long createTimestamp; + /** + * 模板更新时间戳 + */ + private Long updateTimestamp; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplateListResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplateListResponse.java new file mode 100644 index 00000000..eac67c94 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplateListResponse.java @@ -0,0 +1,60 @@ +package cn.axzo.msg.center.service.template.response; + +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/17 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplateListResponse implements Serializable { + + private static final long serialVersionUID = 4748193413608569743L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 模板编码 + */ + private String templateCode; + /** + * 消息类型 + * GENERAL_MESSAGE: 普通消息 + * PENDING_MESSAGE: 待办消息 + */ + private MessageCategoryEnum category; + /** + * 消息标题 + */ + private String title; + /** + * 消息内容 + */ + private String content; + /** + * 分类路径,eg:发薪管理/发薪提醒/提醒工人 + */ + private List groupNodeNamePaths; + /** + * 模板状态 + * ENABLE: 启用 + * DISABLE: 禁用 + */ + 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/template/response/MessageTemplatePageResponse.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplatePageResponse.java new file mode 100644 index 00000000..ef0806c2 --- /dev/null +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/template/response/MessageTemplatePageResponse.java @@ -0,0 +1,60 @@ +package cn.axzo.msg.center.service.template.response; + +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; +import com.alibaba.fastjson.JSON; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; + +/** + * @author cold_blade + * @date 2023/10/12 + * @version 1.0 + */ +@Setter +@Getter +public class MessageTemplatePageResponse implements Serializable { + + private static final long serialVersionUID = 4748193413608569743L; + + /** + * 模板名称 + */ + private String templateName; + /** + * 模板编码 + */ + private String templateCode; + /** + * 消息类型 + * GENERAL_MESSAGE: 普通消息 + * PENDING_MESSAGE: 待办消息 + */ + private MessageCategoryEnum category; + /** + * 消息标题 + */ + private String title; + /** + * 消息内容 + */ + private String content; + /** + * 分类路径,eg:发薪管理/发薪提醒/提醒工人 + */ + private List groupNodeNamePaths; + /** + * 模板状态 + * ENABLE: 启用 + * DISABLE: 禁用 + */ + private StatusEnum status; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} 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; +} diff --git a/msg-center-common/src/main/java/cn/axzo/msg/center/common/redis/RedisUtil.java b/msg-center-common/src/main/java/cn/axzo/msg/center/common/redis/RedisUtil.java new file mode 100644 index 00000000..ccc5b449 --- /dev/null +++ b/msg-center-common/src/main/java/cn/axzo/msg/center/common/redis/RedisUtil.java @@ -0,0 +1,409 @@ +package cn.axzo.msg.center.common.redis; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.connection.ReturnType; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +/** + * @author cold_blade + * @date 2023/10/16 + * @version 1.0 + */ +@Slf4j +@Getter +@Component +@RequiredArgsConstructor +public class RedisUtil { + + /** + * 使用StringRedisTemplate(,其是RedisTemplate的定制化升级) + */ + private final StringRedisTemplate redisTemplate; + + private final KeyOps keyOps = new KeyOps(); + private final StringOps stringOps = new StringOps(); + private final LockOps lockOps = new LockOps(); + + /** + * key相关操作 + */ + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public class KeyOps { + + /** + * 根据key, 删除redis中的对应key-value + * 注: 若删除失败, 则返回false。 + * 若redis中,不存在该key, 那么返回的也是false。 + * 所以,不能因为返回了false,就认为redis中一定还存 + * 在该key对应的key-value。 + * + * @param key 要删除的key + * @return 删除是否成功 + */ + public Boolean delete(String key) { + log.info("delete(...) => key= {}", key); + // 返回值只可能为true/false, 不可能为null + Boolean result = redisTemplate.delete(key); + log.info("delete(...) => result= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + + /** + * 根据keys, 批量删除key-value + * + * 注: 若redis中,不存在对应的key, 那么计数不会加1, 即: + * redis中存在的key-value里,有名为a1、a2的key, + * 删除时,传的集合是a1、a2、a3,那么返回结果为2。 + * + * @param keys + * 要删除的key集合 + * @return 删除了的key-value个数 + */ + public Long delete(Collection keys) { + log.info("delete(...) => keys= {}", keys); + Long count = redisTemplate.delete(keys); + log.info("delete(...) => count= {}", count); + if (count == null) { + throw new RedisOpsResultIsNullException(); + } + return count; + } + + /** + * redis中是否存在,指定key的key-value + * + * @param key 指定的key + * @return 是否存在对应的key-value + */ + public Boolean hasKey(String key) { + log.info("hasKey(...) => key= {}", key); + Boolean result = redisTemplate.hasKey(key); + log.info("hasKey(...) => result= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + + /** + * 给指定的key对应的key-value设置: 多久过时 + * + * 注:过时后,redis会自动删除对应的key-value。 + * 注:若key不存在,那么也会返回false。 + * + * @param key 指定的key + * @param timeout 过时时间 + * @param unit timeout的单位 + * @return 操作是否成功 + */ + public Boolean expire(String key, Long timeout, TimeUnit unit) { + log.info("expire(...) => key= {}, timeout= {}, unit= {}", key, timeout, unit); + Boolean result = redisTemplate.expire(key, timeout, unit); + log.info("expire(...) => result is= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + } + + /** + * string相关操作 + * + * 提示: redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例一).png + * redis中String的数据结构可参考resources/data-structure/String(字符串)的数据结构(示例二).png + */ + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public class StringOps { + + /** + * 设置key-value + * 注: 若已存在相同的key, 那么原来的key-value会被丢弃。 + * + * @param key key + * @param value key对应的value + */ + public void set(String key, String value) { + log.info("set(...) => key= {}, value= {}", key, value); + redisTemplate.opsForValue().set(key, value); + } + + /** + * 设置key-value + * 注: 若已存在相同的key, 那么原来的key-value会被丢弃 + * + * @param key key + * @param value key对应的value + * @param timeout 过时时长 + * @param unit timeout的单位 + */ + public void setEx(String key, String value, Long timeout, TimeUnit unit) { + log.info("setEx(...) => key= {}, value= {}, timeout= {}, unit= {}", key, value, timeout, unit); + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + /** + * 若不存在key时, 向redis中添加key-value, 返回成功/失败。 + * 若存在,则不作任何操作, 返回false。 + * + * @param key key + * @param value key对应的value + * + * @return set是否成功 + */ + public Boolean setIfAbsent(String key, String value) { + log.info("setIfAbsent(...) => key= {}, value= {}", key, value); + Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value); + log.info("setIfAbsent(...) => result= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + + /** + * 若不存在key时, 向redis中添加一个(具有超时时长的)key-value, 返回成功/失败。 + * 若存在,则不作任何操作, 返回false。 + * + * @param key key + * @param value key对应的value + * @param timeout 超时时长 + * @param unit timeout的单位 + * + * @return set是否成功 + */ + public Boolean setIfAbsent(String key, String value, Long timeout, TimeUnit unit) { + log.info("setIfAbsent(...) => key= {}, value= {}, key= {}, value= {}", key, value, timeout, unit); + Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit); + log.info("setIfAbsent(...) => result= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + + /** + * 根据key,获取到对应的value值 + * 注: 若key不存在, 则返回null。 + * + * @param key key-value对应的key + * @return 该key对应的值。 + */ + public String get(String key) { + log.info("get(...) => key= {}", key); + String result = redisTemplate.opsForValue().get(key); + log.info("get(...) => result= {} ", result); + return result; + } + } + + /** + * redis分布式锁. + * + * 使用方式(示例): + * Boolean flag = false; + * String lockName = "sichuan:mianyang:fucheng:ds"; + * String lockValue = UUID.randomUUID().toString(); + * try { + * // 非阻塞获取(锁的最大存活时间采用默认值) + * flag = RedisUtil.LockOps.getLock(lockName, lockValue); + * // 非阻塞获取e.g. + * flag = RedisUtil.LockOps.getLock(lockName, lockValue, 3, TimeUnit.SECONDS); + * // 阻塞获取(锁的最大存活时间采用默认值) + * flag = RedisUtil.LockOps.getLockUntilTimeout(lockName, lockValue, 2000); + * // 阻塞获取e.g. + * flag = RedisUtil.LockOps.getLockUntilTimeout(lockName, lockValue, 2, TimeUnit.SECONDS, 2000); + * // your logic + * // ... + * } finally { + * if (flag) { + * RedisUtil.LockOps.releaseLock(lockName, lockValue); + * } + * } + */ + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public class LockOps { + + /** lua脚本, 保证 释放锁脚本 的原子性(以避免, 并发场景下, 释放了别人的锁) */ + private final String RELEASE_LOCK_LUA = "if redis.call('get',KEYS[1]) == ARGV[1] " + + "then " + + " return redis.call('del',KEYS[1]) " + + "else " + + " return 0 " + + "end "; + + /** 分布式锁默认(最大)存活时长 */ + public final long DEFAULT_LOCK_TIMEOUT = 3; + + /** DEFAULT_LOCK_TIMEOUT的单位 */ + public final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; + + /** + * 获取(分布式)锁. + * 注: 获取结果是即时返回的、是非阻塞的。 + * + * @see LockOps#getLock(String, String, Long, TimeUnit) + */ + public Boolean getLock(final String key, final String value) { + return getLock(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT); + } + + /** + * 获取(分布式)锁。 + * 若成功, 则直接返回; + * 若失败, 则进行重试, 直到成功 或 超时为止。 + * 注: 获取结果是阻塞的, 要么成功, 要么超时, 才返回。 + * + * @param retryTimeoutLimit 重试的超时时长(ms) + * 其它参数可详见: + * @see LockOps#getLock(String, String, Long, TimeUnit) + * + * @return 是否成功 + */ + public Boolean getLockUntilTimeout(final String key, final String value, final Long retryTimeoutLimit) { + return getLockUntilTimeout(key, value, DEFAULT_LOCK_TIMEOUT, DEFAULT_TIMEOUT_UNIT, + retryTimeoutLimit); + } + + /** + * 获取(分布式)锁。 + * 若成功, 则直接返回; + * 若失败, 则进行重试, 直到成功 或 超时为止。 + * 注: 获取结果是阻塞的, 要么成功, 要么超时, 才返回。 + * + * @param retryTimeoutLimit 重试的超时时长(ms) + * 其它参数可详见: + * @see LockOps#getLock(String, String, Long, TimeUnit, Boolean) + * + * @return 是否成功 + */ + public Boolean getLockUntilTimeout(final String key, final String value, + final Long timeout, final TimeUnit unit, + final Long retryTimeoutLimit) { + log.info("getLockUntilTimeout(...) => key= {}, value= {}, timeout= {}, unit= {}, " + + "retryTimeoutLimit= {}ms", key, value, timeout, unit, retryTimeoutLimit); + long startTime = Instant.now().toEpochMilli(); + long now = startTime; + do { + try { + Boolean alreadyGotLock = getLock(key, value, timeout, unit, false); + if (alreadyGotLock) { + log.info("getLockUntilTimeout(...) => consume time= {}ms, result= true", now - startTime); + return true; + } + } catch (Exception e) { + log.warn("getLockUntilTimeout(...) => try to get lock failure!", e); + } + now = Instant.now().toEpochMilli(); + } while (now < startTime + retryTimeoutLimit); + log.info("getLockUntilTimeout(...) => consume time= {}ms, result= false", now - startTime); + return false; + } + + /** + * 获取(分布式)锁 + * 注: 获取结果是即时返回的、是非阻塞的。 + * + * @see LockOps#getLock(String, String, Long, TimeUnit, Boolean) + */ + public Boolean getLock(final String key, final String value, final Long timeout, final TimeUnit unit) { + return getLock(key, value, timeout, unit, true); + } + + /** + * 获取(分布式)锁 + * 注: 获取结果是即时返回的、是非阻塞的。 + * + * @param key 锁名 + * @param value + * 锁名对应的value + * 注: value一般采用全局唯一的值, 如: requestId、uuid等。 + * 这样, 释放锁的时候, 可以再次验证value值, + * 保证自己上的锁只能被自己释放, 而不会被别人释放。 + * 当然, 如果锁超时时, 会被redis自动删除释放。 + * @param timeout + * 锁的(最大)存活时长 + * 注: 一般的, 获取锁与释放锁 都是成对使用的, 在锁在达到(最大)存活时长之前,都会被主动释放。 + * 但是在某些情况下(如:程序获取锁后,释放锁前,崩了),锁得不到释放, 这时就需要等锁过 + * 了(最大)存活时长后,被redis自动删除清理了。这样就能保证redis中不会留下死数据。 + * @param unit timeout的单位 + * @param recordLog 是否记录日志 + * + * @return 是否成功 + */ + public Boolean getLock(final String key, final String value, + final Long timeout, final TimeUnit unit, + Boolean recordLog) { + if (recordLog) { + log.info("getLock(...) => key= {}, value= {}, timeout= {}, unit= {}, recordLog= {}", + key, value, timeout, unit, recordLog); + } + Boolean result = redisTemplate.execute((RedisConnection connection) -> + connection.set(key.getBytes(StandardCharsets.UTF_8), + value.getBytes(StandardCharsets.UTF_8), + Expiration.seconds(unit.toSeconds(timeout)), + RedisStringCommands.SetOption.SET_IF_ABSENT) + ); + if (recordLog) { + log.info("getLock(...) => result= {}", result); + } + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + + /** + * 释放(分布式)锁 + * 注: 此方式能(通过value的唯一性)保证: 自己加的锁, 只能被自己释放。 + * 注: 锁超时时, 也会被redis自动删除释放。 + * + * @param key 锁名 + * @param value 锁名对应的value + * + * @return 释放锁是否成功 + */ + public Boolean releaseLock(final String key, final String value) { + log.info("releaseLock(...) => key= {}, lockValue= {}", key, value); + Boolean result = redisTemplate.execute((RedisConnection connection) -> + connection.eval(RELEASE_LOCK_LUA.getBytes(), + ReturnType.BOOLEAN, 1, + key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8)) + ); + log.info("releaseLock(...) => result= {}", result); + if (result == null) { + throw new RedisOpsResultIsNullException(); + } + return result; + } + } + + public static class RedisOpsResultIsNullException extends NullPointerException { + + private static final long serialVersionUID = 7727166544003942512L; + + public RedisOpsResultIsNullException() { + super(); + } + + public RedisOpsResultIsNullException(String message) { + super(message); + } + } +} diff --git a/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/PageHelperUtil.java b/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/PageHelperUtil.java new file mode 100644 index 00000000..12158c79 --- /dev/null +++ b/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/PageHelperUtil.java @@ -0,0 +1,31 @@ +package cn.axzo.msg.center.common.utils; + +import cn.azxo.framework.common.model.Page; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Collections; +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 PageHelperUtil { + + public static Page emptyPage(Long pageNum, Long pageSize) { + return Page.toPage(pageNum, pageSize, 0L, Collections.emptyList()); + } + + public static Page convert(Page page, Function convertFunc) { + Page tgtPage = new Page<>(); + tgtPage.setPageNum(page.getPageNum()); + tgtPage.setPageSize(page.getPageSize()); + tgtPage.setTotalElements(page.getTotalElements()); + tgtPage.setList(page.getList().stream().map(convertFunc).collect(Collectors.toList())); + return tgtPage; + } +} diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/GeneralMessageRecordDao.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/GeneralMessageRecordDao.java new file mode 100644 index 00000000..2f06d009 --- /dev/null +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/GeneralMessageRecordDao.java @@ -0,0 +1,18 @@ +package cn.axzo.msg.center.dal; + +import cn.axzo.msg.center.dal.mapper.GeneralMessageRecordMapper; +import cn.axzo.msg.center.domain.entity.GeneralMessageRecord; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @description + * @author cold_blade + * @date 2023/10/5 + * @version 1.0 + */ +@Slf4j +@Component +public class GeneralMessageRecordDao extends ServiceImpl { +} diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/MessageSendTwiceRecordDao.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/MessageSendTwiceRecordDao.java new file mode 100644 index 00000000..d570dfac --- /dev/null +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/MessageSendTwiceRecordDao.java @@ -0,0 +1,18 @@ +package cn.axzo.msg.center.dal; + +import cn.axzo.msg.center.dal.mapper.MessageSendTwiceRecordMapper; +import cn.axzo.msg.center.domain.entity.MessageSendTwiceRecord; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @description + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +@Slf4j +@Component +public class MessageSendTwiceRecordDao extends ServiceImpl { +} diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/RelationTemplateMapDao.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/RelationTemplateMapDao.java new file mode 100644 index 00000000..fcc7bdf8 --- /dev/null +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/RelationTemplateMapDao.java @@ -0,0 +1,18 @@ +package cn.axzo.msg.center.dal; + +import cn.axzo.msg.center.dal.mapper.RelationTemplateMapMapper; +import cn.axzo.msg.center.domain.entity.RelationTemplateMap; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @description + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +@Slf4j +@Component +public class RelationTemplateMapDao extends ServiceImpl { +} diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/MessageSendTwiceRecordMapper.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/MessageSendTwiceRecordMapper.java new file mode 100644 index 00000000..f350674e --- /dev/null +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/MessageSendTwiceRecordMapper.java @@ -0,0 +1,12 @@ +package cn.axzo.msg.center.dal.mapper; + +import cn.axzo.msg.center.domain.entity.MessageSendTwiceRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +public interface MessageSendTwiceRecordMapper extends BaseMapper { +} diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/RelationTemplateMapMapper.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/RelationTemplateMapMapper.java new file mode 100644 index 00000000..df72e22e --- /dev/null +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/mapper/RelationTemplateMapMapper.java @@ -0,0 +1,12 @@ +package cn.axzo.msg.center.dal.mapper; + +import cn.axzo.msg.center.domain.entity.RelationTemplateMap; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +public interface RelationTemplateMapMapper extends BaseMapper { +} diff --git a/msg-center-dal/src/main/resources/mapper/MessageRecordMapper.xml b/msg-center-dal/src/main/resources/mapper/MessageRecordMapper.xml index 2835d5ea..07b61246 100644 --- a/msg-center-dal/src/main/resources/mapper/MessageRecordMapper.xml +++ b/msg-center-dal/src/main/resources/mapper/MessageRecordMapper.xml @@ -40,7 +40,8 @@ record.extra as ext, record.router_params as routerParam, record.relation_id as relationId, - record.old_type_id as oldTypeId + record.old_type_id as oldTypeId, + record.create_at as createAt from message_record record join message_relation relation @@ -65,7 +66,12 @@ #{item} - + + and record.id not in + + #{item} + + and record.create_at > DATE_FORMAT((CURDATE() - INTERVAL 3 YEAR),'%Y-%m-%d') order by record.create_at desc,record.id desc diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/CmsMsgQueryReqDTO.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/CmsMsgQueryReqDTO.java index 186f47cd..f00d7205 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/CmsMsgQueryReqDTO.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/CmsMsgQueryReqDTO.java @@ -4,6 +4,7 @@ import cn.axzo.basics.common.page.PageRequest; import lombok.Data; import javax.validation.constraints.NotNull; +import java.util.List; @Data public class CmsMsgQueryReqDTO extends PageRequest { @@ -34,4 +35,8 @@ public class CmsMsgQueryReqDTO extends PageRequest { * 待办、消息模块类型Id - 针对待办使用 */ private Long moduleId; + /** + * 待排除的消息id + */ + private List excludeMsgIds; } diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/MessageNewResDTO.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/MessageNewResDTO.java index c5f3196e..780a0242 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/MessageNewResDTO.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/dto/MessageNewResDTO.java @@ -92,5 +92,8 @@ public class MessageNewResDTO { */ private Integer oldTypeId; - + /** + * 创建时间 + */ + private Date createAt; } diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/GeneralMessageRecord.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/GeneralMessageRecord.java index dd744f9e..fa25220f 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/GeneralMessageRecord.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/GeneralMessageRecord.java @@ -5,8 +5,14 @@ import cn.axzo.msg.center.service.enums.GeneralMessageStateEnum; import cn.axzo.msg.center.service.enums.IdentityTypeEnum; import cn.axzo.msg.center.service.enums.OrganizationTypeEnum; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import java.io.Serializable; @@ -20,27 +26,42 @@ import java.io.Serializable; */ @Setter @Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor @TableName("general_message_record") public class GeneralMessageRecord extends BaseEntity implements Serializable { private static final long serialVersionUID = -1271402884501451185L; /** - * 发送者ID + * 消息的唯一标识 + */ + private String identityCode; + /** + * 发送者自然人 ID + */ + private Long senderPersonId; + /** + * 发送者身份ID */ private Long senderId; /** - * 接收者ID + * 发送者身份类型 + */ + private IdentityTypeEnum senderType; + /** + * 接收者自然人 ID + */ + private Long receiverPersonId; + /** + * 接收者身份ID */ private Long receiverId; /** - * 接收者类型 + * 接收者身份类型 */ private IdentityTypeEnum receiverType; - /** - * 自然人 ID - */ - private Long personId; /** * 模板编码 */ @@ -76,13 +97,19 @@ public class GeneralMessageRecord extends BaseEntity imple /** * 路由参数(留存) */ - private String routerParams; + @TableField(typeHandler = FastjsonTypeHandler.class) + private JSONObject routerParams; /** - * 重试次数 + * 业务扩展参数 + */ + @TableField(typeHandler = FastjsonTypeHandler.class) + private JSONObject bizExtParams; + /** + * 推送重试次数 */ private Integer retryCount; /** - * 最终失败原因 + * 推送最终失败原因 */ private String failCause; diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageBaseTemplate.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageBaseTemplate.java index b652f869..1b8cd7dd 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageBaseTemplate.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageBaseTemplate.java @@ -2,6 +2,7 @@ package cn.axzo.msg.center.domain.entity; import cn.axzo.msg.center.domain.persistence.BaseEntity; import cn.axzo.msg.center.service.enums.MessageCategoryEnum; +import cn.axzo.msg.center.service.enums.StatusEnum; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; @@ -51,6 +52,20 @@ public class MessageBaseTemplate extends BaseEntity impleme * 模板icon */ private String icon; + /** + * 模板状态 + * ENABLE: 启用 + * DISABLE: 禁用 + */ + private StatusEnum status; + /** + * 推送终端配置 JSON字串 + */ + private String pushTerminal; + /** + * APP最小版本支持,可不配 + */ + private String minAppVersion; /** * 创建者自然人id */ diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageGroupNode.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageGroupNode.java index 75509df6..f349f496 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageGroupNode.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageGroupNode.java @@ -1,6 +1,6 @@ package cn.axzo.msg.center.domain.entity; -import cn.axzo.msg.center.domain.persistence.BaseEntity; +import cn.axzo.msg.center.domain.persistence.BaseEntityExt; import cn.axzo.msg.center.service.enums.MessageGroupNodeCategoryEnum; import cn.axzo.msg.center.service.enums.StatusEnum; import com.alibaba.fastjson.JSON; @@ -18,7 +18,7 @@ import java.io.Serializable; */ @Setter @Getter -public class MessageGroupNode extends BaseEntity implements Serializable { +public class MessageGroupNode extends BaseEntityExt implements Serializable { private static final long serialVersionUID = -4536503805143925884L; diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageSendTwiceRecord.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageSendTwiceRecord.java new file mode 100644 index 00000000..1f2457f7 --- /dev/null +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageSendTwiceRecord.java @@ -0,0 +1,36 @@ +package cn.axzo.msg.center.domain.entity; + +import cn.axzo.msg.center.domain.persistence.BaseEntityExt; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +@Setter +@Getter +@TableName("message_send_twice_record") +public class MessageSendTwiceRecord extends BaseEntityExt implements Serializable { + + private static final long serialVersionUID = 3517821492158061709L; + + /** + * 原有消息记录id + */ + private Long originalMsgId; + /** + * 接收者自然人 ID + */ + private Long receiverPersonId; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateGroup.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateGroup.java index 766c4f6a..b5b78a72 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateGroup.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateGroup.java @@ -1,6 +1,6 @@ package cn.axzo.msg.center.domain.entity; -import cn.axzo.msg.center.domain.persistence.BaseEntity; +import cn.axzo.msg.center.domain.persistence.BaseEntityExt; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; @@ -19,7 +19,7 @@ import java.io.Serializable; @Setter @Getter @TableName("message_template_group") -public class MessageTemplateGroup extends BaseEntity implements Serializable { +public class MessageTemplateGroup extends BaseEntityExt implements Serializable { private static final long serialVersionUID = 3902204750978588588L; diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateRouter.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateRouter.java index 68215289..861831c3 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateRouter.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateRouter.java @@ -1,6 +1,6 @@ package cn.axzo.msg.center.domain.entity; -import cn.axzo.msg.center.domain.persistence.BaseEntity; +import cn.axzo.msg.center.domain.persistence.BaseEntityExt; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import com.alibaba.fastjson.JSON; @@ -20,7 +20,7 @@ import java.io.Serializable; @Setter @Getter @TableName("message_template_router") -public class MessageTemplateRouter extends BaseEntity implements Serializable { +public class MessageTemplateRouter extends BaseEntityExt implements Serializable { private static final long serialVersionUID = 6549664208396593182L; @@ -44,6 +44,10 @@ public class MessageTemplateRouter extends BaseEntity imp * 路由地址 */ private String url; + /** + * 按钮style配置 + */ + private String style; @Override public String toString() { diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/PendingMessageRecord.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/PendingMessageRecord.java index e519ed29..06322e2a 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/PendingMessageRecord.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/PendingMessageRecord.java @@ -98,6 +98,10 @@ public class PendingMessageRecord extends BaseEntity imple * 业务描述eg:流程结点描述 */ private String bizDesc; + /** + * 业务扩展参数 + */ + private String bizExtParam; /** * 路由参数(留存) */ diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/RelationTemplateMap.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/RelationTemplateMap.java new file mode 100644 index 00000000..c61775f2 --- /dev/null +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/RelationTemplateMap.java @@ -0,0 +1,36 @@ +package cn.axzo.msg.center.domain.entity; + +import cn.axzo.msg.center.domain.persistence.BaseEntityExt; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author cold_blade + * @date 2023/10/21 + * @version 1.0 + */ +@Setter +@Getter +@TableName("relation_template_map") +public class RelationTemplateMap extends BaseEntityExt implements Serializable { + + private static final long serialVersionUID = 2716916154882729387L; + + /** + * 新消息模板编码 + */ + private String templateCode; + /** + * 原有的模板关联关系id + */ + private Long originalRelationId; + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/persistence/BaseEntityExt.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/persistence/BaseEntityExt.java new file mode 100644 index 00000000..580c65ef --- /dev/null +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/persistence/BaseEntityExt.java @@ -0,0 +1,45 @@ +package cn.axzo.msg.center.domain.persistence; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author cold_blade + * @date 2023/10/24 + * @version 1.0 + */ +@Setter +@Getter +public abstract class BaseEntityExt> extends Model implements Serializable { + + private static final long serialVersionUID = -420591992046546211L; + + /** + * id + */ + @TableId(type = IdType.AUTO) + protected Long id; + + /** + * 创建时间 + */ + protected Date createAt; + + /** + * 修改时间 + */ + protected Date updateAt; + + /** + * 是否删除 0正常,1删除 + */ + @TableLogic(delval = "id", value = "0") + protected Long isDelete = 0L; +} diff --git a/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/PlatServiceImpl.java b/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/PlatServiceImpl.java index 6576179a..53ec52a5 100644 --- a/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/PlatServiceImpl.java +++ b/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/PlatServiceImpl.java @@ -49,16 +49,10 @@ public class PlatServiceImpl implements PlatService { @Override @Transactional(rollbackFor = Exception.class) public void createTemplate(CreateTemplateRequestDto request) { - // FIXME: 临时方案,将渠道信息放到remark中 String remark = request.getRemark(); ChannelHandlerEnum channel = parseChannel(remark); checkCreateTemplate(request, channel); - if (StringUtils.isNotBlank(remark)) { - // 恢复remark的原始值 - request.setRemark(PlaceholderResolver.getDefaultResolver().resolve(remark, "")); - } - insertMessageApp(request.getServiceName()); insertMessageTemplate(request); @@ -69,14 +63,9 @@ public class PlatServiceImpl implements PlatService { } private ChannelHandlerEnum parseChannel(String expStr) { - if (StringUtils.isBlank(expStr)) { - return ChannelHandlerEnum.ALI_YUN; - } - // FIXME:临时方案,后期有前端资源的时候需要改造接口的出入参 - return ChannelHandlerEnum.valueOf( - PlaceholderResolver.getDefaultResolver().resolveSequenceParamName(expStr).get(0) - ); + return ChannelHandlerEnum.ALI_YUN; } + /** * 插入app */ @@ -98,26 +87,26 @@ public class PlatServiceImpl implements PlatService { * 参数校验 */ private void checkCreateTemplate(CreateTemplateRequestDto request, ChannelHandlerEnum channel) { - if(request == null){ - throw new BizException(ReturnCodeEnum.FAIL,"参数不能为空"); + if (request == null) { + throw new BizException(ReturnCodeEnum.FAIL, "参数不能为空"); } - if(request.getServiceName() == null){ - throw new BizException(ReturnCodeEnum.FAIL,"服务名称不能为空"); + if (request.getServiceName() == null) { + throw new BizException(ReturnCodeEnum.FAIL, "服务名称不能为空"); } - if(request.getMessageTemplateType() == null){ - throw new BizException(ReturnCodeEnum.FAIL,"模板类型不能为空"); + if (request.getMessageTemplateType() == null) { + throw new BizException(ReturnCodeEnum.FAIL, "模板类型不能为空"); } - if(StringUtils.isBlank(request.getTemplateNo())){ - throw new BizException(ReturnCodeEnum.FAIL,"短信模板编码不能为空"); + if (StringUtils.isBlank(request.getTemplateNo())) { + throw new BizException(ReturnCodeEnum.FAIL, "短信模板编码不能为空"); } - if(StringUtils.isBlank(request.getTemplateAliNo())){ - throw new BizException(ReturnCodeEnum.FAIL,"阿里短信模板编码不能为空"); + if (StringUtils.isBlank(request.getTemplateAliNo())) { + throw new BizException(ReturnCodeEnum.FAIL, "阿里短信模板编码不能为空"); } - if(StringUtils.isBlank(request.getTitle())){ - throw new BizException(ReturnCodeEnum.FAIL,"标题不能为空"); + if (StringUtils.isBlank(request.getTitle())) { + throw new BizException(ReturnCodeEnum.FAIL, "标题不能为空"); } - if(StringUtils.isBlank(request.getTemplateContent())){ - throw new BizException(ReturnCodeEnum.FAIL,"模板内容不能为空"); + if (StringUtils.isBlank(request.getTemplateContent())) { + throw new BizException(ReturnCodeEnum.FAIL, "模板内容不能为空"); } // 判断短信编码是否存在 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -126,12 +115,12 @@ public class PlatServiceImpl implements PlatService { // 一个内部模板可以映射多个渠道商 queryWrapper.eq(MNSChannelMessageTemplate::getInnerTemplateNo, request.getTemplateNo()); List list = mnsChannelMessageTemplateMapper.selectList(queryWrapper); - if(CollectionUtils.isNotEmpty(list)){ + if (CollectionUtils.isNotEmpty(list)) { MNSChannelMessageTemplate channelMessageTemplate = list.get(0); String templateNo = channelMessageTemplate.getTemplateNo(); String innerTemplateNo = channelMessageTemplate.getInnerTemplateNo(); String templatNo = templateNo.equals(request.getTemplateAliNo()) ? templateNo : innerTemplateNo; - throw new BizException(ReturnCodeEnum.FAIL,"模板编码已存在:" + templatNo); + throw new BizException(ReturnCodeEnum.FAIL, "模板编码已存在:" + templatNo); } } @@ -261,8 +250,8 @@ public class PlatServiceImpl implements PlatService { @Override @Transactional(rollbackFor = Exception.class) public void delete(String templateNo) { - if(StringUtils.isBlank(templateNo)){ - throw new BizException(ReturnCodeEnum.FAIL,"短信模板编码不能为空"); + if (StringUtils.isBlank(templateNo)) { + throw new BizException(ReturnCodeEnum.FAIL, "短信模板编码不能为空"); } mnsMessageTemplateMapper.deleteByTemplateNo(templateNo); diff --git a/start/src/main/java/cn/axzo/msg/center/FeignConfig.java b/start/src/main/java/cn/axzo/msg/center/FeignConfig.java new file mode 100644 index 00000000..363da982 --- /dev/null +++ b/start/src/main/java/cn/axzo/msg/center/FeignConfig.java @@ -0,0 +1,55 @@ +package cn.axzo.msg.center; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import feign.Target; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Objects; + +/** + * @author cold_blade + * @date 2023/10/19 + * @version 1.0 + */ +@Slf4j +@Component +@Profile({"dev", "test", "local"}) +public class FeignConfig implements RequestInterceptor, EnvironmentAware { + private Environment environment; + + private static String POD_NAMESPACE; + + static { + Map env = System.getenv(); + if (env != null) { + POD_NAMESPACE = env.get("MY_POD_NAMESPACE"); + } + log.info("init FeignConfig, POD_NAMESPACE value is {}", POD_NAMESPACE); + } + + @SneakyThrows + @Override + public void apply(RequestTemplate requestTemplate) { + if (POD_NAMESPACE == null) { + Target target = requestTemplate.feignTarget(); + String profile = environment.getProperty("spring.profiles.active"); + if (Objects.equals(profile, "dev")) { + requestTemplate.target("http://dev-app.axzo.cn/" + target.name()); + } else if (Objects.equals(profile, "test")) { + requestTemplate.target("http://test-api.axzo.cn/" + target.name()); + } + } + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } +} diff --git a/start/src/main/java/cn/axzo/msg/center/MsgCenterConfig.java b/start/src/main/java/cn/axzo/msg/center/MsgCenterConfig.java index 6077f8e4..00f72b58 100644 --- a/start/src/main/java/cn/axzo/msg/center/MsgCenterConfig.java +++ b/start/src/main/java/cn/axzo/msg/center/MsgCenterConfig.java @@ -3,6 +3,8 @@ package cn.axzo.msg.center; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; +import static cn.axzo.msg.center.MsgCenterConfig.IM_CENTER; + /** * @author cn * @version 1.0 @@ -10,6 +12,8 @@ import org.springframework.context.annotation.Configuration; * @date 2023/5/30 11:33 */ @Configuration -@EnableFeignClients +@EnableFeignClients(basePackages = {IM_CENTER}) public class MsgCenterConfig { + + public static final String IM_CENTER = "cn.axzo.im.center.api.feign"; } diff --git a/start/src/main/java/cn/axzo/msg/center/RedisConfiguration.java b/start/src/main/java/cn/axzo/msg/center/RedisConfiguration.java new file mode 100644 index 00000000..453472d7 --- /dev/null +++ b/start/src/main/java/cn/axzo/msg/center/RedisConfiguration.java @@ -0,0 +1,50 @@ +package cn.axzo.msg.center; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * @author cold_blade + * @date 2023/10/17 + * @version 1.0 + */ +@Configuration +public class RedisConfiguration { + + @Bean + public RedisSerializer redisKeySerializer() { + return new StringRedisSerializer(); + } + + @Bean + public RedisSerializer redisValueSerializer() { + return new StringRedisSerializer(); + } + + /** + * RedisTemplate配置 + */ + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory, + RedisSerializer redisKeySerializer, + RedisSerializer redisValueSerializer) { + StringRedisTemplate redisTemplate = new StringRedisTemplate(); + redisTemplate.setConnectionFactory(factory); + + //设置Key的序列化采用StringRedisSerializer + redisTemplate.setKeySerializer(redisKeySerializer); + redisTemplate.setHashKeySerializer(redisKeySerializer); + + //设置值的序列化 + redisTemplate.setValueSerializer(redisValueSerializer); + redisTemplate.setHashValueSerializer(redisValueSerializer); + + redisTemplate.afterPropertiesSet(); + + return redisTemplate; + } +} diff --git a/start/src/main/resources/bootstrap.yml b/start/src/main/resources/bootstrap.yml index 6178cbd1..88309f4f 100644 --- a/start/src/main/resources/bootstrap.yml +++ b/start/src/main/resources/bootstrap.yml @@ -7,7 +7,7 @@ spring: nacos: config: #nacos 服务地址 - server-addr: ${NACOS_HOST:dev-nacos.axzo.cn}:${NACOS_PORT:80} + server-addr: ${NACOS_HOST:https://dev-nacos.axzo.cn}:${NACOS_PORT:443} # 文件格式 file-extension: yaml # 指定命名空间