diff --git a/inside-notices/pom.xml b/inside-notices/pom.xml index 14e26e33..bcd781b0 100644 --- a/inside-notices/pom.xml +++ b/inside-notices/pom.xml @@ -21,6 +21,10 @@ + + cn.axzo.workflow + workflow-engine-spring-boot-starter + cn.axzo.org org-api diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/event/card/CardChangeStateSyncTodoEventHandler.java b/inside-notices/src/main/java/cn/axzo/msg/center/event/card/CardChangeStateSyncTodoEventHandler.java deleted file mode 100644 index 9400c8ad..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/event/card/CardChangeStateSyncTodoEventHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.axzo.msg.center.event.card; - -import cn.axzo.framework.rocketmq.Event; -import cn.axzo.framework.rocketmq.EventConsumer; -import cn.axzo.framework.rocketmq.EventHandler; -import cn.axzo.msg.center.api.mq.CardPresetButtonPressedMessage; -import cn.axzo.msg.center.message.service.todo.TodoWithCardWrapper; -import cn.axzo.msg.center.message.service.todo.manage.TodoManager; -import cn.axzo.msg.center.notices.common.constans.CommonConstants; -import cn.axzo.msg.center.service.enums.MqMessageType; -import com.alibaba.fastjson.JSON; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.stereotype.Component; - -import java.util.Objects; - -/** - * @author xudawei@axzo.cn - * @date 2024/11/07 - * @desc 卡片预设MQ消费 - */ -@Slf4j -@Component -@RequiredArgsConstructor -public class CardChangeStateSyncTodoEventHandler implements EventHandler, InitializingBean { - - private final EventConsumer eventConsumer; - - private final TodoWithCardWrapper todoWithCardWrapper; - - private final TodoManager todoManager; - - - @Override - public void onEvent(Event event, EventConsumer.Context context) { - if (!MqMessageType.CARD_PRESET_BUTTON_PRESSED.getEventName().equalsIgnoreCase(event.getEventCode().getName())) { - return; - } - - log.info("CardChangeStateSyncTodoEventHandler-start - handle mq event, event={}", JSON.toJSONString(event)); - try { - long start = System.currentTimeMillis(); - handleMqMessage(event); - long end = System.currentTimeMillis(); - log.warn("CardChangeStateSyncTodoEventHandler-handle mq event, used={}ms, event={}", end - start, JSON.toJSONString(event)); - } catch (Exception e) { - log.warn("CardChangeStateSyncTodoEventHandler-error - handle mq event, event={}", JSON.toJSONString(event), e); - } - - } - /** - * 业务逻辑 - */ - private void handleMqMessage(Event event) { - //解析数据 - CardPresetButtonPressedMessage payload = event.normalizedData(CardPresetButtonPressedMessage.class); - if (Objects.isNull(payload)) { - return; - } - if (Objects.isNull(payload.getCardInfo())) { - return; - } - if (!CommonConstants.TODO_SYSN_CARD_APP_CODE.equalsIgnoreCase(payload.getCardInfo().getAppCode())) { - return; - } - Long operatorId = Objects.nonNull(payload.getOperatorId()) ? payload.getOperatorId() : 0L; - todoWithCardWrapper.fireTodoWhenPresetButtonPressedByCard(todoManager, payload.getPresetButtonType(), payload.getCardInfo(), operatorId); - - } - - @Override - public void afterPropertiesSet() { - Event.EventCode eventCode = new Event.EventCode(MqMessageType.CARD_PRESET_BUTTON_PRESSED.getEventModel(), MqMessageType.CARD_PRESET_BUTTON_PRESSED.getEventName()); - eventConsumer.registerHandler(eventCode, this); - } -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/event/outer/PushYouMengMessageHandler.java b/inside-notices/src/main/java/cn/axzo/msg/center/event/outer/PushYouMengMessageHandler.java index 34cd20dc..17c6f101 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/event/outer/PushYouMengMessageHandler.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/event/outer/PushYouMengMessageHandler.java @@ -135,6 +135,12 @@ public class PushYouMengMessageHandler implements EventHandler, InitializingBean return; } + MessageHistoryUpdatedPayload.MsgBody msgBody = messageBody.resolveMsgBody(); + if (msgBody == null) { + log.warn("push-handler, 模板code:{}, msgBody为空 [不是普通消息], event: {}", card.getTemplateCode(), event); + return; + } + youMengMessageService.sendPushMessage(MsgBody4Guest.builder() .ty(0) .f("0") diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/DiagnosisProps.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/DiagnosisProps.java index 9dac53c7..47ad7de6 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/DiagnosisProps.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/DiagnosisProps.java @@ -34,9 +34,9 @@ public class DiagnosisProps implements EnvironmentAware { @Setter @Getter public static class RowCount { - private int warningThreshold = 6000; + private int warningThreshold = 1500; private boolean enable = true; - private int periodMaxWarnTimes = 3; + private int periodMaxWarnTimes = 2; /** * For test purpose diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/PendingMessageBizConfig.java b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/PendingMessageBizConfig.java index ceab3700..6643b65a 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/PendingMessageBizConfig.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/inside/notices/config/PendingMessageBizConfig.java @@ -173,6 +173,15 @@ public class PendingMessageBizConfig { @Getter private int todoTitleSearchMaxSize = 5000; + @Getter + private boolean enableTodoLog = true; + + @Getter + private String workflowIMChannelAppMinVersionCmp = ""; + + @Getter + private String workflowIMChannelAppMinVersionCm = ""; + @Getter private boolean requestReplayWindowEnabled = true; 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 7e4d95d5..55dbd6a6 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 @@ -302,15 +302,9 @@ public class PendingMessageNewController implements PendingMessageClient { } @Override + @Deprecated public CommonResponse setHide(SetHideRequest req) { - log.info("setHide, request={}", JSON.toJSONString(req)); - Boolean response = null; - try { - response = todoManager.setHide(req); - return CommonResponse.success(response); - } finally { - log.info("setHide. request={}, response={}", req, response); - } + return CommonResponse.success(true); } @Override diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/WorkflowButtonSyncClientController.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/WorkflowButtonSyncClientController.java index 36e524c8..455a4a90 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/WorkflowButtonSyncClientController.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/controller/WorkflowButtonSyncClientController.java @@ -8,6 +8,7 @@ import cn.axzo.msg.center.service.domain.UrlConfig; import cn.axzo.msg.center.service.enums.MessageCategoryEnum; import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; +import cn.axzo.msg.center.service.enums.WebPageOpenStrategy; import cn.axzo.msg.center.service.pending.client.WorkflowButtonSyncClient; import cn.axzo.msg.center.service.pending.request.WorkflowSyncButtonsRequest; import cn.axzo.msg.center.service.pending.request.WorkflowSyncButtonsRequest.WorkflowButton; @@ -96,12 +97,14 @@ public class WorkflowButtonSyncClientController implements WorkflowButtonSyncCli if (button.getCategory() == RouterCategoryEnum.JUMP) { UrlConfig urlConfig = new UrlConfig(); urlConfig.setDefaultUrl(workflowButton.getUrl()); + urlConfig.setWebOpenStrategy(WebPageOpenStrategy.DRAWER); update.setUrlConfig(urlConfig); update.setApiUrl(""); } else { String apiUrl = workflowButton.getUrl(); if (apiUrl == null) apiUrl = ""; update.setApiUrl(apiUrl); + update.setUrlConfig(new UrlConfig()); } messageTemplateButtonV3Dao.updateById(update); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateV3SaveOrUpdateParam.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateV3SaveOrUpdateParam.java index 3698a715..f6dfeaa1 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateV3SaveOrUpdateParam.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/domain/param/MessageTemplateV3SaveOrUpdateParam.java @@ -175,18 +175,6 @@ public class MessageTemplateV3SaveOrUpdateParam implements Serializable { * 校验 */ public void checkCreate() { - //1 流程待办,暂不能使用IM通道 - switch (this.getMsgCategory()) { - case GENERAL_MESSAGE: - break; - case IM_MESSAGE_CARD: - break; - case BIZ_PENDING_MESSAGE: - break; - case APPROVAL_PENDING_MESSAGE: - boolean contains = CollectionUtils.isNotEmpty(this.getChannels()) && this.getChannels().contains(MessageChannel.IM); - BizAssertions.assertFalse(contains, "审批待办,暂不能使用IM通道"); - } //2 同一个预设按钮类型,只能有一个 if (CollectionUtils.isNotEmpty(this.getButtons())) { Map> collect = this.getButtons().stream() 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 index 52b87328..fbc0fb88 100644 --- 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 @@ -7,6 +7,7 @@ import cn.axzo.msg.center.service.domain.UrlConfig; import cn.axzo.msg.center.service.dto.MessageCardContentItemDTO; import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.PresetButtonType; +import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import cn.axzo.msg.center.service.pending.card.domain.CardElementConfig; @@ -89,6 +90,12 @@ public class GeneralMessagePushVO implements Serializable { * 业务编码 */ private String bizCode; + + /** + * 子业务编码 + */ + private String subBizCode; + /** * 消息发送时间戳 */ @@ -111,6 +118,8 @@ public class GeneralMessagePushVO implements Serializable { private Map debugInfo; + private Map extInfo; + public void addDebugInfo(String name, Object value) { if (debugInfo == null) debugInfo = new HashMap<>(); @@ -236,6 +245,11 @@ public class GeneralMessagePushVO implements Serializable { */ private UrlConfig urlConfig; + /** + * 按钮来源 + */ + private RouterButtonSourceEnum source; + /** * 调用api的地址. action=ACTION时, 消费这个字段 */ diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardExtInfo.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardExtInfo.java new file mode 100644 index 00000000..626bc077 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardExtInfo.java @@ -0,0 +1,32 @@ +package cn.axzo.msg.center.message.service.card; + +import com.alibaba.fastjson.JSONObject; + +import javax.validation.constraints.NotNull; + +/** + * @author yanglin + */ +public class CardExtInfo { + + private final JSONObject extInfo; + + public CardExtInfo(JSONObject bizParam) { + JSONObject extInfo = bizParam.getJSONObject("extInfo"); + if (extInfo == null) { + extInfo = new JSONObject(); + bizParam.put("extInfo", extInfo); + } + this.extInfo = extInfo; + } + + public void addExtInfo(String key, Object value) { + extInfo.put(key, value); + } + + @NotNull + public JSONObject getJsonObject() { + return extInfo; + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardManager.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardManager.java index df6bea78..2a4da9fc 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardManager.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardManager.java @@ -135,7 +135,7 @@ public class CardManager { if (cardProps.isDeleteCardsWhenSendFail()) { cardSupport.deleteCardIdempotent(request); cardDao.deleteCards(sendModel.getCards()); - sendLogger.reloadAndLogCards("send:fail:deleteCards"); + sendLogger.reloadAndLogCards("send:fail:deleteCards", e); } }); throw MiscUtils.wrapException(e); @@ -152,11 +152,9 @@ public class CardManager { public void updateState(CardUpdateStateRequest request) { request.validate(); TemplateModelV3 templateModel = cardSupport.ensureImChannelPresent(request.getTemplateCode()); - boolean updated = false; // 不要放到for里面去了 CardRequestContext requestContext = CardRequestContext.create(request); for (List cards : cardsCursor(request)) { - updated = true; UpdateExecutor executor = new UpdateExecutor(requestContext, "updateState", templateModel, cards); executor.update(card -> { Card update = executor.createUpdate(card); @@ -165,10 +163,9 @@ public class CardManager { update.setBizState(request.getBizState()); }); } - BizAssertions.assertTrue(updated, "未找到任何需要更新的卡片, request={}", request); } - void setActionPerformed(SetActionPerformedRequest request) { + public void setActionPerformed(SetActionPerformedRequest request) { TemplateModelV3 templateModel = cardSupport.ensureImChannelPresent(request.getTemplateCode()); MessageTemplateButtonV3 button = templateModel.findButton(request.getButtonCode()).orElse(null); BizAssertions.assertNotNull(button, "找不到对应的按钮. buttonCode={}", request.getButtonCode()); @@ -194,11 +191,15 @@ public class CardManager { "卡片已是终状态, 无法'{}'", request.getPresetButtonType().getDesc()); } - public void firePresetButtonPressed(CardUpdatePresetButtonRequest request) { + public boolean firePresetButtonPressed(CardUpdatePresetButtonRequest request) { TemplateModelV3 templateModel = cardSupport.ensureImChannelPresent(request.getTemplateCode()); CardRequestContext requestContext = CardRequestContext.create(request); - for (List cards : cardsCursor(request)) - firePresetButtonPressedImpl(request, requestContext, templateModel, cards); + boolean updated = false; + for (List cards : cardsCursor(request)) { + UpdateStateResult result = firePresetButtonPressedImpl(request, requestContext, templateModel, cards); + updated = result == UpdateStateResult.UPDATED || updated; + } + return updated; } private UpdateStateResult firePresetButtonPressedImpl( @@ -214,8 +215,10 @@ public class CardManager { UpdateStateResult result = executor.update(new SetActionPerformedBuilder( executor, button, CardBizState.fromPresetButton(request.getPresetButtonType()))); if (result == UpdateStateResult.UPDATED) { - cardBroadcaster.firePresetButtonPressed(executor.updatedCards, request); + cardBroadcaster.firePresetButtonPressed(executor.updatedCards, request, requestContext); executor.updateCardLogger.reloadAndLogCards("presetButtonPressed:mq:success"); + } else { + executor.updateCardLogger.reloadAndLogCards("presetButtonPressed:cardNoUpdate"); } return result; } @@ -251,7 +254,7 @@ public class CardManager { })); } - private void rebuildCardContent(TemplateModelV3 templateModel, List cards) { + void rebuildCardContent(TemplateModelV3 templateModel, List cards) { ArrayList updates = new ArrayList<>(); for (Card card : cardDao.reloadCards(cards)) { Card update = new Card(); @@ -261,9 +264,6 @@ public class CardManager { BizAssertions.assertTrue(cardTemplate.isUpdatable(), "模板不支持更新, templateCode={}", card.getTemplateCode()); GeneralMessagePushVO cardContent = cardParser.parseCardContent(cardTemplate, card); - // 保留原来的title和content, 避免模版变了但缺少bizParam导致数据不完整 - cardContent.setCardTitle(card.getTitle()); - cardContent.setCardContent(card.getContent()); cardContent.addDebugInfo("bizCode", card.getBizCode()); cardContent.addDebugInfo("subBizCode", card.getSubBizCode()); update.setCardContent(cardContent); @@ -301,6 +301,7 @@ public class CardManager { private class UpdateExecutor { final CardLogger updateCardLogger; + final CardRequestContext requestContext; final TemplateModelV3 templateModel; final String operation; final List cards; @@ -310,6 +311,7 @@ public class CardManager { UpdateExecutor(CardRequestContext requestContext, String operation, TemplateModelV3 templateModel, List cards) { this.updateCardLogger = cardLoggers.createLogger(requestContext); + this.requestContext = requestContext; this.templateModel = templateModel; this.operation = operation; this.cards = cards; diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardParser.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardParser.java index 44bb74de..e7fd68fb 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardParser.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardParser.java @@ -4,6 +4,8 @@ import cn.axzo.msg.center.inside.notices.config.MessageSystemConfig; import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO; import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO.CardButton; import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO.CardExtensionItem; +import cn.axzo.msg.center.message.service.card.interceptor.CardButtonInterceptor; +import cn.axzo.msg.center.message.service.card.interceptor.CardButtonInterceptorFactory; import cn.axzo.msg.center.message.service.impl.v3.AppLink; import cn.axzo.msg.center.message.service.impl.v3.NativeAppLinkUrlConfigVisitor; import cn.axzo.msg.center.message.service.impl.v3.V3ExtPopulator; @@ -12,8 +14,8 @@ import cn.axzo.msg.center.service.domain.UrlConfigWalker; import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.CardState; import cn.axzo.msg.center.service.enums.KVContentType; +import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; -import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import cn.axzo.msg.center.service.pending.card.domain.CardButtonStates; import cn.axzo.msg.center.service.pending.card.domain.CardElementConfig; import cn.axzo.msg.center.service.pending.request.CardContent; @@ -25,8 +27,9 @@ import cn.axzo.msg.center.service.pending.response.v3.model.ParsedGroupV3; import cn.axzo.msg.center.service.pending.response.v3.model.ParsedKV; import cn.axzo.msg.center.service.pending.response.v3.model.ParsedTemplateV3; import cn.axzo.msg.center.service.pending.response.v3.model.PersonInfo; -import lombok.RequiredArgsConstructor; +import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; import java.util.ArrayList; @@ -38,12 +41,23 @@ import java.util.List; * @author yanglin */ @Component -@RequiredArgsConstructor class CardParser { private final MessageSystemConfig messageSystemConfig; private final V3ExtPopulator v3ExtPopulator; private final CardProps cardProps; + private final CardButtonInterceptorFactory[] interceptorFactories; + + CardParser(MessageSystemConfig messageSystemConfig, + V3ExtPopulator v3ExtPopulator, + CardProps cardProps, + ObjectProvider interceptorFactoryProvider) { + this.messageSystemConfig = messageSystemConfig; + this.v3ExtPopulator = v3ExtPopulator; + this.cardProps = cardProps; + this.interceptorFactories = interceptorFactoryProvider + .getIfAvailable(() -> new CardButtonInterceptorFactory[0]); + } GeneralMessagePushVO parseCardContent(CardTemplate cardTemplate, CardContent card) { ParsedTemplateV3 template = cardTemplate.getTemplate(); @@ -55,6 +69,8 @@ class CardParser { super.addDebugInfo(name, value); } }; + bizBody.setBizCode(card.getBizCode()); + bizBody.setSubBizCode(card.getSubBizCode()); bizBody.setCardStyleCode(template.getCardStyleCode()); bizBody.setTemplateCode(template.getCode()); bizBody.setCardBannerUrl(template.getIcon()); @@ -76,6 +92,7 @@ class CardParser { bizBody.addDebugInfo("bizState", stateInfo.getBizState()); bizBody.addDebugInfo("cardState", stateInfo.getCardState()); bizBody.addDebugInfo("isUpdatable", cardTemplate.isUpdatable()); + CardButtonInterceptor buttonInterceptor = getButtonInterceptor(card); ParsedModelV3Walker.walkDown(cardTemplate.getParsedModel(), new ParsedModel3Visitor() { @Override public void visitTemplateCardUrlConfig(UrlConfig urlConfig) { @@ -113,16 +130,25 @@ class CardParser { */ @Override public void visitButton(ParsedButtonV3 button) { - List styles = button.parseStyle(); - if (!styles.contains(ButtonStyleEnum.OVER_CARD)) + if (bizBody.getCardButtons() != null + && bizBody.getCardButtons().size() >= cardProps.getMaxSendingCardButtonsCount()) return; - boolean isActionPerformed = CardButtonStates - .create(card.getButtonStates()) - .isButtonActionPerformed(button); + Boolean isVisibleOnCard = buttonInterceptor + .isVisibleOnCard(button) + .orElse(() -> button.getStyles().contains(ButtonStyleEnum.OVER_CARD)); + if (!isVisibleOnCard) return; + boolean isPerformActionAvailable = buttonInterceptor + .isPerformActionAvailable(button) + .orElse(button.isPerformActionAvailable()); + Boolean isActionPerformed = buttonInterceptor + .isActionPerformed(button) + .orElse(CardButtonStates + .create(card.getButtonStates()) + .isButtonActionPerformed(button)); if (card.getStateInfo().getCardState() == CardState.COMPLETED - && button.isPerformActionAvailable() + && isPerformActionAvailable && !isActionPerformed) return; @@ -131,21 +157,23 @@ class CardParser { CardButton imButton = new CardButton(); bizBody.getCardButtons().add(imButton); - imButton.setTitle(isActionPerformed ? button.getActionPerformedName() : button.getName()); + String actionPerformed = buttonInterceptor + .getActionPerformedName(button) + .orElse(button.getActionPerformedName()); + imButton.setSource(button.getSource()); + imButton.setTitle(isActionPerformed ? actionPerformed : button.getName()); imButton.setAction(button.getCategory().name()); - imButton.setIsHighlight(styles.contains(ButtonStyleEnum.HIGH_LIGHT)); - imButton.setExecutorShow(button.getExecutorShow()); + imButton.setIsHighlight(button.getStyles().contains(ButtonStyleEnum.HIGH_LIGHT)); + imButton.setSenderShow(buttonInterceptor.isSenderShow(button).orElse(false)); + imButton.setExecutorShow(buttonInterceptor.isExecutorShow(button).orElse(button.getExecutorShow())); imButton.setActionPerformed(isActionPerformed); + boolean isSystemButton = button.getSource() == RouterButtonSourceEnum.SYSTEM; if (button.getUrlConfig().hasUrl()) imButton.setUrlConfig(button.getUrlConfig()); imButton.setButtonCode(button.getCode()); imButton.setPresetButtonType(button.getPresetButtonType()); - imButton.setApiUrl(button.getApiUrl()); - if (RouterCategoryEnum.ACTION.equals(button.getCategory())) { - AppLink appLink = new AppLink(TerminalTypeEnum.WEB_VIEW.name(), button.getApiUrl()); - imButton.setActionPaths(Collections.singletonList(appLink)); - } else if (button.getUrlConfig().hasUrl()) { + if (button.getUrlConfig().hasUrl() && !isSystemButton) { imButton.setActionPaths(getNativeAppLinks(button.getUrlConfig())); } } @@ -160,6 +188,9 @@ class CardParser { } }); + JSONObject extInfo = new CardExtInfo(card.getBizParam()).getJsonObject(); + if (!extInfo.isEmpty()) + bizBody.setExtInfo(extInfo); return bizBody; } @@ -170,4 +201,13 @@ class CardParser { return visitor.getLinks(); } + private CardButtonInterceptor getButtonInterceptor(CardContent card) { + for (CardButtonInterceptorFactory factory : interceptorFactories) { + CardButtonInterceptor interceptor = factory.create(card); + if (interceptor != null) + return interceptor; + } + return CardButtonInterceptor.NOT_SURE; + } + } \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardProps.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardProps.java index 2b29ee84..d6b56809 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardProps.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardProps.java @@ -22,6 +22,8 @@ public class CardProps { private boolean deleteCardsWhenSendFail = true; private boolean enableCardIdempotent = true; private int updateCardBatchSize = 200; + // IM最多显示3个, 可能会存在仅发起人可见的按钮1个, 3 + 1 = 4, 避免消息超长 + private int maxSendingCardButtonsCount = 4; private Set idempotentFreeTemplateCodes = new HashSet<>(); boolean isIdempotentFree(String templateCode) { diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardSupport.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardSupport.java index 14004a75..a69a9423 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardSupport.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/CardSupport.java @@ -1,6 +1,5 @@ package cn.axzo.msg.center.message.service.card; -import cn.axzo.basics.common.exception.ServiceException; import cn.axzo.framework.jackson.utility.JSON; import cn.axzo.im.center.api.vo.PersonAccountAttribute; import cn.axzo.im.center.api.vo.req.SendTemplateMessageParam; @@ -11,10 +10,12 @@ import cn.axzo.msg.center.common.utils.BizAssertions; import cn.axzo.msg.center.dal.CardIdempotentDao; import cn.axzo.msg.center.domain.entity.Card; import cn.axzo.msg.center.domain.entity.CardIdempotent; +import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig; import cn.axzo.msg.center.message.domain.dto.TemplateModelV3; import cn.axzo.msg.center.message.domain.vo.GeneralMessagePushVO; import cn.axzo.msg.center.message.service.card.domain.CardGroup; import cn.axzo.msg.center.message.service.card.domain.CardSendModel; +import cn.axzo.msg.center.message.service.card.exception.CardIdempotentException; import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser; import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service; import cn.axzo.msg.center.message.service.impl.v3.UrlParser; @@ -23,6 +24,7 @@ import cn.axzo.msg.center.nimpush.device.PushDeviceSnapshots; import cn.axzo.msg.center.push.PushData; import cn.axzo.msg.center.service.domain.card.AppVersionConfig; import cn.axzo.msg.center.service.dto.PeerPerson; +import cn.axzo.msg.center.service.enums.MessageCategoryEnum; import cn.axzo.msg.center.service.enums.MessageChannel; import cn.axzo.msg.center.service.pending.request.CardContent; import cn.axzo.msg.center.service.pending.request.CardSendRequest; @@ -60,6 +62,7 @@ public class CardSupport { private final NimPushService nimPushService; private final CardIdempotentDao cardIdempotentDao; private final CardProps cardProps; + private final PendingMessageBizConfig cfg; public static String getBizIdPrefix(String templateCode) { return IdBuilder.builder() @@ -82,7 +85,7 @@ public class CardSupport { cardIdempotentDao.save(idempotent); } catch (DuplicateKeyException e) { log.warn("重复创建卡片, request={}", request); - throw new ServiceException(403, String.format("重复创建卡片: %s", idempotent)); + throw new CardIdempotentException(403, String.format("重复创建卡片: %s", idempotent)); } } @@ -178,15 +181,22 @@ public class CardSupport { private String determineMinAppVersion(ParsedModelV3 model, AppTypeEnum appType) { - Ref minVersion = Ref.create(""); + Ref ref = Ref.create(""); ParsedModelV3Walker.walkDown(model, new ParsedModel3Visitor() { @Override public void visitAppVersionConfig(AppVersionConfig cfg) { if (cfg.getAppType() == appType) - minVersion.set(cfg.getMinVersion()); + ref.set(cfg.getMinVersion()); } }); - return minVersion.get(); + String minVersion = ref.get(); + if (StringUtils.isBlank(minVersion) + && model.getTemplate().getMsgCategory() == MessageCategoryEnum.APPROVAL_PENDING_MESSAGE) { + return appType == AppTypeEnum.CMP + ? cfg.getWorkflowIMChannelAppMinVersionCmp() + : cfg.getWorkflowIMChannelAppMinVersionCm(); + } + return minVersion; } TemplateModelV3 ensureImChannelPresent(String templateCode) { diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/broadcast/CardBroadcaster.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/broadcast/CardBroadcaster.java index 7dfc7c30..7c725bab 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/broadcast/CardBroadcaster.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/broadcast/CardBroadcaster.java @@ -5,10 +5,12 @@ import cn.axzo.msg.center.api.mq.CardInfo; import cn.axzo.msg.center.api.mq.CardPresetButtonPressedMessage; import cn.axzo.msg.center.dal.CardDao; import cn.axzo.msg.center.domain.entity.Card; +import cn.axzo.msg.center.message.service.card.CardRequestContext; import cn.axzo.msg.center.mq.MqMessageRecord; import cn.axzo.msg.center.mq.MqProducer; import cn.axzo.msg.center.service.enums.MqMessageType; import cn.axzo.msg.center.service.pending.request.CardPresetButtonRequest; +import cn.axzo.msg.center.service.util.IdBuilder; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -24,18 +26,25 @@ public class CardBroadcaster { private final CardDao cardDao; private final MqProducer mqProducer; - public void firePresetButtonPressed(List cards, CardPresetButtonRequest request) { + public void firePresetButtonPressed(List cards, + CardPresetButtonRequest request, + CardRequestContext requestContext) { for (Card card : cardDao.reloadCards(cards)) { CardPresetButtonPressedMessage message = new CardPresetButtonPressedMessage(); message.setPresetButtonType(request.getPresetButtonType()); message.setOperatorId(request.getOperatorId()); message.setOperatorName(request.getOperatorName()); message.setCardInfo(BeanMapper.copyBean(card, CardInfo.class)); + message.setBatchNo(requestContext.getBatchNo()); mqProducer.send(MqMessageRecord .builder(MqMessageType.CARD_PRESET_BUTTON_PRESSED, message) - .messageKey(card.getId()) + .messageKey(card.getIdentityCode()) .operatorId(request.getOperatorId()) - .shardingKey(card.getTemplateCode()) + .shardingKey(IdBuilder.builder() + .append(card.getTemplateCode()) + .append(card.getBizCode()) + .append(card.getSubBizCode()) + .build()) .build()); } } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/exception/CardIdempotentException.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/exception/CardIdempotentException.java new file mode 100644 index 00000000..3875c854 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/exception/CardIdempotentException.java @@ -0,0 +1,18 @@ +package cn.axzo.msg.center.message.service.card.exception; + +import cn.axzo.basics.common.exception.ServiceException; + +/** + * @author yanglin + */ +public class CardIdempotentException extends ServiceException { + + public CardIdempotentException(Integer code, String msg) { + super(code, msg); + } + + public CardIdempotentException(String msg) { + super(msg); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptor.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptor.java new file mode 100644 index 00000000..7847ace9 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptor.java @@ -0,0 +1,37 @@ +package cn.axzo.msg.center.message.service.card.interceptor; + +import cn.axzo.msg.center.service.ButtonV3; +import cn.axzo.msg.center.utils.desision.DecisionValue; + +/** + * @author yanglin + */ +public interface CardButtonInterceptor { + + default DecisionValue isPerformActionAvailable(ButtonV3 button) { + return DecisionValue.notSure(); + } + + default DecisionValue isActionPerformed(ButtonV3 button) { + return DecisionValue.notSure(); + } + + default DecisionValue getActionPerformedName(ButtonV3 button) { + return DecisionValue.notSure(); + } + + default DecisionValue isVisibleOnCard(ButtonV3 button) { + return DecisionValue.notSure(); + } + + default DecisionValue isSenderShow(ButtonV3 button) { + return DecisionValue.notSure(); + } + + default DecisionValue isExecutorShow(ButtonV3 button) { + return DecisionValue.notSure(); + } + + CardButtonInterceptor NOT_SURE = new CardButtonInterceptor() {}; + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptorFactory.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptorFactory.java new file mode 100644 index 00000000..17d41cbe --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/card/interceptor/CardButtonInterceptorFactory.java @@ -0,0 +1,15 @@ +package cn.axzo.msg.center.message.service.card.interceptor; + +import cn.axzo.msg.center.service.pending.request.CardContent; + +import javax.annotation.Nullable; + +/** + * @author yanglin + */ +public interface CardButtonInterceptorFactory { + + @Nullable + CardButtonInterceptor create(CardContent card); + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/v3/NativeAppLinkUrlConfigVisitor.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/v3/NativeAppLinkUrlConfigVisitor.java index 7e2fa681..a81b6f73 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/v3/NativeAppLinkUrlConfigVisitor.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/impl/v3/NativeAppLinkUrlConfigVisitor.java @@ -6,7 +6,9 @@ import cn.axzo.msg.center.service.enums.TerminalTypeEnum; import lombok.Getter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * @author yanglin @@ -15,6 +17,7 @@ import java.util.List; public class NativeAppLinkUrlConfigVisitor implements UrlConfigVisitor { private final List links = new ArrayList<>(); + private final Set terminals = new HashSet<>(); @Override public void visitAppManagerAndroid(AppUrl android) { @@ -41,7 +44,9 @@ public class NativeAppLinkUrlConfigVisitor implements UrlConfigVisitor { } private void addLink(AppLink link) { - if (!links.contains(link)) + if (!terminals.contains(link.getPlatform())) { links.add(link); + terminals.add(link.getPlatform()); + } } } \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/ModelV2PropsPopulator.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/ModelV2PropsPopulator.java index 575b9746..56c4b532 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/ModelV2PropsPopulator.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/ModelV2PropsPopulator.java @@ -89,7 +89,7 @@ public class ModelV2PropsPopulator implements ParsedModel3Visitor { buttonV2.setDesc(buttonV3.getName()); buttonV2.setCategory(buttonV3.getCategory()); buttonV2.setPresetButtonType(buttonV3.getPresetButtonType()); - buttonV2.setStyle(buttonV3.parseStyle()); + buttonV2.setStyle(buttonV3.getStyles()); buttonV2.setExecutorShow(buttonV3.getExecutorShow()); buttonV2.setPendingShow(buttonV3.getPendingShow()); buttonV2.setKey(buttonV3.getCode()); diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoWithCardWrapper.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoWithCardWrapper.java deleted file mode 100644 index 37542fd7..00000000 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/TodoWithCardWrapper.java +++ /dev/null @@ -1,324 +0,0 @@ -package cn.axzo.msg.center.message.service.todo; - -import cn.axzo.basics.common.exception.ServiceException; -import cn.axzo.msg.center.api.mq.CardInfo; -import cn.axzo.msg.center.dal.TodoDao; -import cn.axzo.msg.center.domain.entity.Todo; -import cn.axzo.msg.center.message.domain.dto.TemplateModelV3; -import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam; -import cn.axzo.msg.center.message.service.card.CardManager; -import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service; -import cn.axzo.msg.center.message.service.todo.manage.TodoLogger; -import cn.axzo.msg.center.message.service.todo.manage.TodoManager; -import cn.axzo.msg.center.message.service.todo.manage.TodoRequestContext; -import cn.axzo.msg.center.notices.common.constans.CommonConstants; -import cn.axzo.msg.center.service.dto.PeerPerson; -import cn.axzo.msg.center.service.dto.PersonDTO; -import cn.axzo.msg.center.service.enums.CardBizState; -import cn.axzo.msg.center.service.enums.PresetButtonType; -import cn.axzo.msg.center.service.pending.request.CardSendRequest; -import cn.axzo.msg.center.service.pending.request.CardUpdatePresetButtonRequest; -import cn.axzo.msg.center.service.pending.request.CardUpdateStateRequest; -import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest; -import cn.axzo.msg.center.service.util.IdBuilder; -import com.alibaba.excel.util.StringUtils; -import com.alibaba.fastjson.JSON; -import com.google.common.base.Throwables; -import com.google.common.collect.Sets; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.stereotype.Component; - -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.function.Function; -import java.util.stream.Collectors; - -/** - * @author xudawei@axzo.cn - * @date 2024/12/12 - * @desc 待办与卡片关联包装 - */ -@Slf4j -@Component -@RequiredArgsConstructor -public class TodoWithCardWrapper { - - private final CardManager cardManager; - - private final TodoLogger todoLogger; - - private final TodoDao todoDao; - - private final ModelV3Service modelV3Service; - - /** - * 卡片预设按钮,同步待办 - * 注:卡片预设同步待办状态 - * @param presetButtonType 预设类型 - * @param cardInfo 卡片 - */ - public void fireTodoWhenPresetButtonPressedByCard(TodoManager todoManager, PresetButtonType presetButtonType, CardInfo cardInfo, Long operatorId) { - if (Objects.isNull(presetButtonType) || Objects.isNull(cardInfo)) { - return; - } - if (StringUtils.isBlank(cardInfo.getTemplateCode()) || StringUtils.isBlank(cardInfo.getBizCode())) { - return; - } - - log.info("TodoWithCardWrapper#fireTodoWhenPresetButtonPressedByCard start,presetButtonType:{},cardInfo:{}", presetButtonType, JSON.toJSONString(cardInfo)); - TodoRequestContext ctx = TodoRequestContext.create("cardPresetTodo", cardInfo); - ctx.addLogContent("presetButtonType", presetButtonType); - ctx.addLogContent("operatorId", operatorId); - //1 查询待办(Todo对象),根据templateCode/bizCode/subBizCode/receiverPresonId/receiverOuId/receiverWorkspaceId 理论上只能查询一条[待办]记录 - List todoList = todoDao.findByCondition(cardInfo.getTemplateCode(), cardInfo.getBizCode(), cardInfo.getSubBizCode(), cardInfo.getReceiverPersonId(), cardInfo.getReceiverOuId(), cardInfo.getReceiverWorkspaceId()); - try { - if (CollectionUtils.isEmpty(todoList)) { - return; - } - //2 同步待办预设状态, - for (Todo todo : todoList) { - PresetButtonPressedRequest presetButtonPressedRequest = this.buildPresetButtonPressedRequest(todo.getIdentityCode(), presetButtonType, operatorId); - todoManager.firePresetButtonPressed( presetButtonPressedRequest, false); - - ctx.addLogContent("fireTodoWhenPresetButtonPressedByCard", "success"); - todoLogger.logTodosUpdated(ctx, Collections.singletonList(todo)); - } - } catch (Exception e) { - log.warn("TodoWithCardWrapper#fireTodoWhenPresetButtonPressedByCard exception,presetButtonType:{},cardInfo:{}", presetButtonType, JSON.toJSONString(cardInfo),e); - ctx.addLogContent("exception", Throwables.getStackTraceAsString(e)); - todoLogger.logTodosUpdated(ctx, todoList); - } - } - - /** - * 构建预设按钮对象 - */ - private PresetButtonPressedRequest buildPresetButtonPressedRequest(String identityCode, PresetButtonType presetButtonType, Long operatorId) { - return PresetButtonPressedRequest.builder() - .identityCode(identityCode) - .presetButtonType(presetButtonType) - .operatorId(operatorId) - .build(); - } - - /** - * 点击预设按钮-同步卡片 - * 注:待办预设同步卡片状态 - */ - public void fireCardWhenPresetButtonPressedByTodo(PresetButtonPressedRequest request, Todo todo, boolean isSyncCard) { - //是否同步卡片信息,true:同步;false:不同步 - if (!isSyncCard) { - return; - } - log.info("TodoWithCardWrapper#fireCardWhenPresetButtonPressedByTodo start,request:{},todo:{}", JSON.toJSONString(request), JSON.toJSONString(todo)); - TodoRequestContext ctx = TodoRequestContext.create("todoPresetCard", request); - - - if (!this.isContainImChannel(todo.getTemplateCode())) { - log.info("fireCardWhenPresetButtonPressedByTodo#notContainImChannel,templateCode:{}", todo.getTemplateCode()); - return; - } - log.info("fireCardWhenPresetButtonPressedByTodo#isContainImChannel,templateCode:{}", todo.getTemplateCode()); - try { - //1 构建对象 - CardUpdatePresetButtonRequest cardRequest = this.buildCardUpdatePresetButtonRequest(request, todo); - //2 同步卡片 - cardManager.firePresetButtonPressed(cardRequest); - - ctx.addLogContent("fireCardWhenPresetButtonPressedByTodo", "success"); - todoLogger.logTodosUpdated(ctx, Collections.singletonList(todo)); - } catch (Exception e) { - log.warn("TodoWithCardWrapper#fireCardWhenPresetButtonPressedByTodo exception,request:{},todo:{}", JSON.toJSONString(request), JSON.toJSONString(todo),e); - ctx.addLogContent("exception", Throwables.getStackTraceAsString(e)); - todoLogger.logTodosUpdated(ctx, Collections.singletonList(todo)); - } - } - - /** - * 构建对象 - */ - private CardUpdatePresetButtonRequest buildCardUpdatePresetButtonRequest(PresetButtonPressedRequest request, Todo todo) { - CardUpdatePresetButtonRequest cardRequest = new CardUpdatePresetButtonRequest(); - cardRequest.setAppCode(CommonConstants.TODO_SYSN_CARD_APP_CODE); - cardRequest.setTemplateCode(todo.getTemplateCode()); - cardRequest.setBizCode(todo.getBizCode()); - cardRequest.setSubBizCode(todo.getSubBizCode()); - cardRequest.setReceivers(Sets.newHashSet( - PeerPerson.newPeerPerson(todo.getExecutorPersonId() - , todo.getOuId() - , todo.getReceiverWorkspaceId()))); - - cardRequest.setPresetButtonType(request.getPresetButtonType()); - cardRequest.setOperatorId(request.getOperatorId()); - return cardRequest; - } - - /** - * 发送卡片信息 - */ - public void send(PendingMessagePushParam param,List todos) { - log.info("TodoWithCardWrapper#send start,param:{}", JSON.toJSONString(param)); - TodoRequestContext ctx = TodoRequestContext.create("todoSyncCardSend", param); - if (!this.isContainImChannel(param.getTemplateCode())) { - log.info("send#notContainImChannel,templateCode:{}", param.getTemplateCode()); - return; - } - log.info("send#isContainImChannel,templateCode:{}", param.getTemplateCode()); - try { - //1 构建对象 - CardSendRequest cardSendRequest = this.buildCardSendRequest(param); - ctx.addLogContent(JSON.toJSONString(cardSendRequest)); - //2 发送 - cardManager.send(cardSendRequest); - - ctx.addLogContent("sendCard", "success"); - todoLogger.logTodosUpdated(ctx, todos); - } catch (Exception e) { - log.warn("TodoWithCardWrapper#send,param:{}", JSON.toJSONString(param), e); - ctx.addLogContent("exception", Throwables.getStackTraceAsString(e)); - todoLogger.logTodosUpdated(ctx, todos); - } - } - - /** - * 是否包含IM通道 - */ - private boolean isContainImChannel(String templateCode) { - TemplateModelV3 templateModel = modelV3Service.findEnabledByCode(templateCode) - .orElseThrow(() -> new ServiceException( - String.format("Can't find template. templateCode=%s", templateCode))); - return templateModel.getTemplate().isContainImChannel(); - } - - /** - * 卡片更新状态-完成 - */ - public void cardCompleteStateByTodoList(List todoList) { - this.cardUpdateStateByTodoList(todoList,CardBizState.END, true); - } - - /** - * 卡片更新状态-回滚 - * 暂时不需要 - */ - public void cardRollbackStateByTodoList(List todoList) { - this.cardUpdateStateByTodoList(todoList, CardBizState.ABORTED, false); - } - - /** - * 卡片更新状态-撤销 - */ - public void cardRevokeStateByTodoList(List todoList) { - this.cardUpdateStateByTodoList(todoList, CardBizState.REVOKED, false); - } - - /** - * 卡片更新状态-执行中 - */ - public void cardProcessingStateByTodoList(List todoList) { - this.cardUpdateStateByTodoList(todoList, CardBizState.PENDING, false); - } - - /** - * 卡片更新完成状态 - */ - private void cardUpdateStateByTodoList(List todoList, CardBizState bizState, boolean setCardCompleted) { - log.info("TodoWithCardWrapper#cardUpdateStateByTodoList start,todoList:{},bizState:{},setCardCompleted:{}", JSON.toJSONString(todoList), bizState, setCardCompleted); - if (CollectionUtils.isEmpty(todoList)) { - return; - } - TodoRequestContext ctx = TodoRequestContext.create("todoSyncCardState", todoList); - ctx.addLogContent("bizState", bizState); - ctx.addLogContent("cardState", bizState); - ctx.addLogContent("setCardCompleted", setCardCompleted); - - try { - Set codes = todoList.stream().map(Todo::getTemplateCode).collect(Collectors.toSet()); - List byCodes = modelV3Service.getByCodes(new ArrayList<>(codes)); - Map modelV3Map = byCodes.stream().collect(Collectors.toMap(TemplateModelV3::getTemplateCode, Function.identity())); - - for (Todo todo : todoList) { - TemplateModelV3 templateModelV3 = modelV3Map.get(todo.getTemplateCode()); - if (Objects.isNull(templateModelV3) || Objects.isNull(templateModelV3.getTemplate()) - || StringUtils.isBlank(templateModelV3.getTemplate().getCode()) ||!templateModelV3.getTemplate().isContainImChannel()) { - log.info("cardUpdateStateByTodoList#notContainImChannel,templateModelV3:{}", JSON.toJSONString(templateModelV3)); - continue; - } - log.info("cardUpdateStateByTodoList#isContainImChannel,templateModelV3:{}", JSON.toJSONString(templateModelV3)); - //1 构建对象 - CardUpdateStateRequest updateStateRequest = this.buildCardUpdateStateRequest(todo, bizState, setCardCompleted); - //2 更新状态 - cardManager.updateState(updateStateRequest); - ctx.addLogContent("cardUpdateStateByTodoList", "success"); - todoLogger.logTodosUpdated(ctx, Collections.singletonList(todo)); - } - } catch (Exception e) { - log.warn("TodoWithCardWrapper#cardCompleteState,todoList:{}", JSON.toJSONString(todoList), e); - ctx.addLogContent("exception", Throwables.getStackTraceAsString(e)); - todoLogger.logTodosUpdated(ctx, todoList); - } - } - - /** - * 构建卡片状态更新对象 - */ - private CardUpdateStateRequest buildCardUpdateStateRequest(Todo todo, CardBizState bizState, boolean setCardCompleted) { - CardUpdateStateRequest updateStateRequest = new CardUpdateStateRequest(); - - updateStateRequest.setBizState(bizState); - updateStateRequest.setCardCompleted(setCardCompleted); - - updateStateRequest.setAppCode(CommonConstants.TODO_SYSN_CARD_APP_CODE); - updateStateRequest.setTemplateCode(todo.getTemplateCode()); - updateStateRequest.setBizCode(todo.getBizCode()); - updateStateRequest.setSubBizCode(todo.getSubBizCode()); - updateStateRequest.setReceivers(Sets.newHashSet(PeerPerson.newPeerPerson(todo.getExecutorPersonId(), todo.getOuId(), todo.getReceiverWorkspaceId())));//TODO - return updateStateRequest; - } - - /** - * 构建对象 - */ - private CardSendRequest buildCardSendRequest(PendingMessagePushParam param) { - CardSendRequest sendRequest = new CardSendRequest(); - sendRequest.setAppCode(CommonConstants.TODO_SYSN_CARD_APP_CODE); - sendRequest.setTemplateCode(param.getTemplateCode()); - sendRequest.setBizCode(param.getBizCode()); - sendRequest.setSubBizCode(param.getSubBizCode()); - if (Objects.nonNull(param.getPromoter()) && Objects.nonNull(param.getPromoter().getId())) { - sendRequest.setSender(PeerPerson.newPeerPerson(param.getPromoter().getId() - , Objects.nonNull(param.getPromoterOuId()) ? param.getPromoterOuId() : null - , Objects.nonNull(param.getPromoterWorkspaceId()) ? param.getPromoterWorkspaceId() : null)); - } - - sendRequest.setReceivers(this.buildReceivers(param.getExecutor(), param.getOuId(), param.getWorkspaceId())); - if (StringUtils.isNotBlank(param.getBizExtParams())) { - sendRequest.setBizParam(JSON.parseObject(param.getBizExtParams())); - } - if (StringUtils.isNotBlank(param.getRouterParams())) { - sendRequest.setRouterParam(JSON.parseObject(param.getRouterParams())); - } - - sendRequest.setIdempotentCode(IdBuilder.builder() - .append(param.getBizCode()) - .append(param.getSubBizCode()) - .build()); - - return sendRequest; - } - - private Set buildReceivers(List personDTOS, Long ouId, Long workspaceId) { - if (CollectionUtils.isEmpty(personDTOS)) { - return Sets.newHashSet(); - } - return personDTOS.stream().map(item -> PeerPerson.newPeerPerson(item.getId(), ouId, workspaceId)).collect(Collectors.toSet()); - } - - -} diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/CardPresetButtonSyncTodoListener.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/CardPresetButtonSyncTodoListener.java new file mode 100644 index 00000000..fbf7bbe2 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/CardPresetButtonSyncTodoListener.java @@ -0,0 +1,45 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.msg.center.api.mq.CardPresetButtonPressedMessage; +import cn.axzo.msg.center.mq.ConsumerIsolation; +import cn.axzo.msg.center.mq.IsolationMQListener; +import cn.axzo.msg.center.mq.RocketMQConfig; +import cn.axzo.msg.center.service.enums.MqMessageType; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RocketMQMessageListener( + consumeThreadMax = 2, + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = RocketMQConfig.MSG_CENTER_TOPIC, + consumerGroup = "GID_topic_card_preset_button_sync_todo_${spring.profiles.active}" +) +public class CardPresetButtonSyncTodoListener extends IsolationMQListener { + + private final TodoSyncCardService todoSyncCardService; + + CardPresetButtonSyncTodoListener(TodoSyncCardService todoSyncCardService) { + super(ConsumerIsolation.CARD_PRESET_BUTTON_CLICKED_SYNC_TODO, + MqMessageType.CARD_PRESET_BUTTON_PRESSED); + this.todoSyncCardService = todoSyncCardService; + } + + @Override + public void onEventImpl(Event event, EventConsumer.Context context) { + CardPresetButtonPressedMessage message = event.normalizedData(CardPresetButtonPressedMessage.class); + log.info("received CardPresetButtonPressedMessage: {}", message); + todoSyncCardService.syncCardPresetButtonPressed(message); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoPresetButtonSyncCardListener.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoPresetButtonSyncCardListener.java new file mode 100644 index 00000000..f3339443 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoPresetButtonSyncCardListener.java @@ -0,0 +1,45 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.msg.center.api.mq.PresetButtonPressedMessage; +import cn.axzo.msg.center.mq.ConsumerIsolation; +import cn.axzo.msg.center.mq.IsolationMQListener; +import cn.axzo.msg.center.mq.RocketMQConfig; +import cn.axzo.msg.center.service.enums.MqMessageType; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RocketMQMessageListener( + consumeThreadMax = 2, + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = RocketMQConfig.MSG_CENTER_TOPIC, + consumerGroup = "GID_topic_todo_preset_button_sync_card_${spring.profiles.active}" +) +public class TodoPresetButtonSyncCardListener extends IsolationMQListener { + + private final TodoSyncCardService todoSyncCardService; + + TodoPresetButtonSyncCardListener(TodoSyncCardService todoSyncCardService) { + super(ConsumerIsolation.TODO_PRESET_BUTTON_CLICKED_SYNC_CARD, + MqMessageType.TODO_PRESET_BUTTON_PRESSED); + this.todoSyncCardService = todoSyncCardService; + } + + @Override + public void onEventImpl(Event event, EventConsumer.Context context) { + PresetButtonPressedMessage message = event.normalizedData(PresetButtonPressedMessage.class); + log.info("received PresetButtonPressedMessage: {}", message); + todoSyncCardService.syncTodoPresetButtonPressed(message); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardBizListener.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardBizListener.java new file mode 100644 index 00000000..608afac1 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardBizListener.java @@ -0,0 +1,51 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.msg.center.api.mq.TodoUpdateMessage; +import cn.axzo.msg.center.mq.ConsumerIsolation; +import cn.axzo.msg.center.mq.IsolationMQListener; +import cn.axzo.msg.center.mq.RocketMQConfig; +import cn.axzo.msg.center.service.enums.BizCategoryEnum; +import cn.axzo.msg.center.service.enums.MqMessageType; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RocketMQMessageListener( + consumeThreadMax = 2, + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = RocketMQConfig.MSG_CENTER_TOPIC, + consumerGroup = "GID_topic_todo_sync_card_biz_${spring.profiles.active}" +) +class TodoSyncCardBizListener extends IsolationMQListener { + + private final TodoSyncCardService todoSyncCardService; + + TodoSyncCardBizListener(TodoSyncCardService todoSyncCardService) { + super(ConsumerIsolation.TODO_SYNC_CARD_BIZ, MqMessageType.TODO_STATE_UPDATE); + this.todoSyncCardService = todoSyncCardService; + } + + @Override + public void onEventImpl(Event event, EventConsumer.Context context) { + TodoUpdateMessage message; + try { + message = event.normalizedData(TodoUpdateMessage.class); + } catch (Exception e) { + log.warn("parse message error. event={}", event, e); + return; + } + if (message.getUpdatedTodo().getBizCategory() == BizCategoryEnum.OTHER) + todoSyncCardService.onMessage(event, message); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardFlowListener.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardFlowListener.java new file mode 100644 index 00000000..39c903c7 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardFlowListener.java @@ -0,0 +1,51 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.msg.center.api.mq.TodoUpdateMessage; +import cn.axzo.msg.center.mq.ConsumerIsolation; +import cn.axzo.msg.center.mq.IsolationMQListener; +import cn.axzo.msg.center.mq.RocketMQConfig; +import cn.axzo.msg.center.service.enums.BizCategoryEnum; +import cn.axzo.msg.center.service.enums.MqMessageType; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RocketMQMessageListener( + consumeThreadMax = 2, + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = RocketMQConfig.MSG_CENTER_TOPIC, + consumerGroup = "GID_topic_todo_sync_card_flow_${spring.profiles.active}" +) +class TodoSyncCardFlowListener extends IsolationMQListener { + + private final TodoSyncCardService todoSyncCardService; + + TodoSyncCardFlowListener(TodoSyncCardService todoSyncCardService) { + super(ConsumerIsolation.TODO_SYNC_CARD_FLOW, MqMessageType.TODO_STATE_UPDATE); + this.todoSyncCardService = todoSyncCardService; + } + + @Override + public void onEventImpl(Event event, EventConsumer.Context context) { + TodoUpdateMessage message; + try { + message = event.normalizedData(TodoUpdateMessage.class); + } catch (Exception e) { + log.warn("parse message error. event={}", event, e); + return; + } + if (message.getUpdatedTodo().getBizCategory() == BizCategoryEnum.FLOW) + todoSyncCardService.onMessage(event, message); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardService.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardService.java new file mode 100644 index 00000000..bd661d7a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardService.java @@ -0,0 +1,318 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.msg.center.api.mq.CardPresetButtonPressedMessage; +import cn.axzo.msg.center.api.mq.PresetButtonPressedMessage; +import cn.axzo.msg.center.api.mq.TodoUpdateMessage; +import cn.axzo.msg.center.dal.MessageTemplateV3Dao; +import cn.axzo.msg.center.dal.TodoBusinessDao; +import cn.axzo.msg.center.dal.TodoDao; +import cn.axzo.msg.center.domain.entity.MessageTemplateV3; +import cn.axzo.msg.center.domain.entity.Todo; +import cn.axzo.msg.center.domain.entity.TodoBusiness; +import cn.axzo.msg.center.message.service.card.CardExtInfo; +import cn.axzo.msg.center.message.service.card.CardManager; +import cn.axzo.msg.center.message.service.card.exception.CardIdempotentException; +import cn.axzo.msg.center.message.service.todo.manage.TodoLogger; +import cn.axzo.msg.center.message.service.todo.manage.TodoManager; +import cn.axzo.msg.center.message.service.todo.manage.TodoRequestContext; +import cn.axzo.msg.center.service.dto.PeerPerson; +import cn.axzo.msg.center.service.enums.BizCategoryEnum; +import cn.axzo.msg.center.service.enums.BizFinalStateEnum; +import cn.axzo.msg.center.service.enums.CardBizState; +import cn.axzo.msg.center.service.enums.CodeDefinition; +import cn.axzo.msg.center.service.enums.MessageChannel; +import cn.axzo.msg.center.service.enums.PendingMessageStateEnum; +import cn.axzo.msg.center.service.pending.request.CardSendRequest; +import cn.axzo.msg.center.service.pending.request.CardUpdatePresetButtonRequest; +import cn.axzo.msg.center.service.pending.request.CardUpdateStateRequest; +import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest; +import cn.axzo.msg.center.service.pending.response.CardSendResponse; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Sets; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.Optional; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class TodoSyncCardService { + + private static final ThreadLocal TODO_MESSAGE = new ThreadLocal<>(); + + private static final String APP_CODE = "msg-center:todo"; + private static final String TODO_IDENTITY_CODE_BIZ_PARAM_KEY = "todoIdentityCode"; + private static final String TODO_TYPE_BIZ_PARAM_KEY = "todoType"; + private static final String TODO_CATEGORY_BIZ_PARAM_KEY = "todoCategory"; + private static final String TODO_EXECUTOR_WORKSPACE_ID_BIZ_PARAM_KEY = "todoExecutorWorkspaceId"; + private static final String TODO_PROMOTER_WORKSPACE_ID_BIZ_PARAM_KEY = "todoPromoterWorkspaceId"; + + private final TodoDao todoDao; + private final TodoBusinessDao todoBusinessDao; + private final CardManager cardManager; + private final TodoLogger todoLogger; + private final MessageTemplateV3Dao messageTemplateV3Dao; + private final TodoManager todoManager; + private final TransactionTemplate transactionTemplate; + + void onMessage(Event event, TodoUpdateMessage message) { + log.info("received TodoUpdateMessage: {}", message); + try { + TODO_MESSAGE.set(message); + sync(event, message); + } catch (Exception e) { + log.warn("onMessage error, message={}", message, e); + throw new RuntimeException(e); + } finally { + TODO_MESSAGE.remove(); + } + } + + public static Optional getTodoUpdateMessage() { + return Optional.ofNullable(TODO_MESSAGE.get()); + } + + private void sync(Event event, TodoUpdateMessage message) { + if (!message.isUpdateCard()) { + log.warn("not update card, message={}", message); + return; + } + Todo todo = todoDao.findTodoByCode(message.getUpdatedTodo().getIdentityCode()) + .orElse(null); + if (todo == null) { + log.warn("[sync] todo not found, message={}", message); + return; + } + MessageTemplateV3 template = messageTemplateV3Dao + .findByCode(todo.getTemplateCode()) + .orElse(null); + if (template == null) { + log.warn("template not found, message={}", message); + return; + } + if (!template.determineChannels().contains(MessageChannel.IM)) { + log.warn("template not support IM, message={}", message); + return; + } + TodoBusiness business = todoBusinessDao.getBusinesses(todo).findBusiness(todo).orElse(null); + if (business == null) { + log.warn("business not found, message={}", message); + return; + } + if (todo.getState() == PendingMessageStateEnum.HAS_BEEN_SENT + || (todo.getState() == PendingMessageStateEnum.CREATED && business.getBizFinalState() == null)) + sendCard(event, business, todo); + else if (!TodoManager.OP_FIRE_PRESET_BUTTON_PRESSED.equals(message.getOperation())) + updateCardState(event, business, todo); + } + + private void sendCard(Event event, TodoBusiness business, Todo todo) { + CardSendRequest request = new CardSendRequest(); + request.setAppCode(APP_CODE); + request.setTemplateCode(todo.getTemplateCode()); + request.setBizCode(todo.getBizCode()); + request.setIdempotentCode(todo.getIdentityCode()); + request.setSubBizCode(todo.getSubBizCode()); + Long promoterPersonId = business.getPromoterPersonId(); + if (promoterPersonId == null) promoterPersonId = 0L; + Long ouId = business.getOuId(); + if (ouId == null) ouId = 0L; + request.setSender(PeerPerson.create(promoterPersonId, ouId, business.getOrgId())); + request.setReceivers(Sets.newHashSet(PeerPerson.create( + todo.getExecutorPersonId(), todo.getOuId(), todo.getOrgId()))); + JSONObject bizParam = todo.bizParam(); + CardExtInfo cardExtInfo = new CardExtInfo(bizParam); + cardExtInfo.addExtInfo(TODO_IDENTITY_CODE_BIZ_PARAM_KEY, todo.getIdentityCode()); + cardExtInfo.addExtInfo(TODO_TYPE_BIZ_PARAM_KEY, todo.getType().getCode()); + cardExtInfo.addExtInfo(TODO_CATEGORY_BIZ_PARAM_KEY, business.getBizCategory().getCode()); + cardExtInfo.addExtInfo(TODO_EXECUTOR_WORKSPACE_ID_BIZ_PARAM_KEY, todo.getReceiverWorkspaceId()); + cardExtInfo.addExtInfo(TODO_PROMOTER_WORKSPACE_ID_BIZ_PARAM_KEY, business.getOrgId()); + request.setBizParam(bizParam); + request.setRouterParam(todo.routerParam()); + request.setReturnCards(true); + TodoRequestContext ctx = TodoRequestContext + .create("sendCard", event) + .addLogContent("sendCardRequest", request); + try { + log.info("sendCard: todo={}", todo); + CardSendResponse response = cardManager.send(request); + ctx.addLogContent("sendCardResponse", response); + todoLogger.logTodoUpdated(ctx, todo); + log.info("sendCard: success, todo={}", todo); + } catch (CardIdempotentException ignored) { + log.warn("sendCard: idempotent, todo={}", todo); + } catch (Exception e) { + log.warn("sendCard: error, todo={}", todo, e); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("exception", e), todo); + } + } + + private void updateCardState(Event event, TodoBusiness business, Todo todo) { + TodoCardUpdateStateRequest request = new TodoCardUpdateStateRequest(); + request.setTodo(todo); + request.setTodoBusiness(business); + request.setAppCode(APP_CODE); + request.setTemplateCode(todo.getTemplateCode()); + request.setBizCode(todo.getBizCode()); + request.setSubBizCode(todo.getSubBizCode()); + request.setReceivers(Sets.newHashSet(PeerPerson.create( + todo.getExecutorPersonId(), todo.getOuId(), todo.getOrgId()))); + if (todo.getState() != PendingMessageStateEnum.PROCESSING) { + request.setBizState(business.getBizCategory() == BizCategoryEnum.FLOW + ? determineCardBizStateForFlowTodo(business) + : determineCardBizStateForBizTodo(todo)); + } + request.setCardCompleted(todo.getState() == PendingMessageStateEnum.COMPLETED); + TodoRequestContext ctx = TodoRequestContext + .create("updateCardState", event) + .addLogContent("updateCardStateRequest", request) + .addLogContent("todo", todo); + if (request.isValid()) { + try { + cardManager.updateState(request); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("updateCardStateResult", "success"), todo); + } catch (Exception e) { + log.warn("updateCardState: error, todo={}", todo, e); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("exception", e), todo); + } + } else { + log.info("updateCardState: invalid request, todo={}", todo); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("updateCardStateResult", "invalid update card state request"), todo); + } + } + + void syncCardPresetButtonPressed(CardPresetButtonPressedMessage message) { + log.info("onCardPresetButtonPressed: {}", message); + if (!APP_CODE.equals(message.getCardInfo().getAppCode())) { + log.info("[syncCardPresetButtonPressed] onCardPresetButtonPressed: not msg-center:todo, message={}", message); + return; + } + String identityCode = new CardExtInfo(message.getCardInfo().determineBizParam()) + .getJsonObject().getString(TODO_IDENTITY_CODE_BIZ_PARAM_KEY); + if (StringUtils.isBlank(identityCode)) { + log.warn("[syncCardPresetButtonPressed] onCardPresetButtonPressed: identityCode is blank, message={}", message); + return; + } + Todo todo = todoDao.findTodoByCode(identityCode).orElse(null); + if (todo == null) { + log.info("[syncCardPresetButtonPressed] onCardPresetButtonPressed: todo not found, message={}", message); + return; + } + PresetButtonPressedRequest request = new PresetButtonPressedRequest(); + request.setIdentityCode(identityCode); + request.setPresetButtonType(message.getPresetButtonType()); + request.setOperatorId(message.getOperatorId()); + request.setOperatorName(message.getOperatorName()); + TodoRequestContext ctx = TodoRequestContext.create( + "syncCardPresetButtonPressed:finished", message.getBatchNo(), message); + try { + execTransactional(() -> { + if (todoManager.firePresetButtonPressed(request, false)) + todoLogger.logTodoUpdated(ctx, todo); + }); + } catch (Exception e) { + log.warn("[syncCardPresetButtonPressed] onCardPresetButtonPressed: error, message={}", message, e); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("exception", e), todo); + } + } + + void syncTodoPresetButtonPressed(PresetButtonPressedMessage message) { + Todo todo = todoDao.findTodoByCode(message.getTodoInfo().getIdentityCode()).orElse(null); + if (todo == null) { + log.warn("[syncTodoPresetButtonPressed] todo not found, message={}", message); + return; + } + MessageTemplateV3 template = messageTemplateV3Dao + .findByCode(todo.getTemplateCode()) + .orElse(null); + if (template == null) { + log.warn("[syncTodoPresetButtonPressed] template not found, message={}", message); + return; + } + if (!template.determineChannels().contains(MessageChannel.IM)) { + log.warn("[syncTodoPresetButtonPressed] template not support IM, message={}", message); + return; + } + CardUpdatePresetButtonRequest request = new CardUpdatePresetButtonRequest(); + request.setAppCode(APP_CODE); + request.setTemplateCode(todo.getTemplateCode()); + request.setBizCode(todo.getBizCode()); + request.setSubBizCode(todo.getSubBizCode()); + request.setReceivers(Sets.newHashSet(PeerPerson.create( + todo.getExecutorPersonId(), todo.getOuId(), todo.getReceiverWorkspaceId()))); + request.setPresetButtonType(message.getPresetButtonType()); + request.setOperatorId(0L); + request.setOperatorName(""); + TodoRequestContext ctx = TodoRequestContext.create("syncTodoPresetButtonPressed:finished", message); + try { + execTransactional(() -> { + if (cardManager.firePresetButtonPressed(request)) + todoLogger.logTodoUpdated(ctx, todo); + }); + } catch (Exception e) { + log.warn("[syncTodoPresetButtonPressed] error, message={}", message, e); + todoLogger.logTodoUpdated(ctx.copy().addLogContent("exception", e), todo); + } + } + + private static CardBizState determineCardBizStateForBizTodo(Todo todo) { + if (todo.getState() == PendingMessageStateEnum.COMPLETED) + return CardBizState.COMPLETED; + if (todo.getState() == PendingMessageStateEnum.PROCESSING) + return CardBizState.PROCESSING; + if (todo.getState() == PendingMessageStateEnum.RETRACT) + return CardBizState.REVOKED; + return null; + } + + private static CardBizState determineCardBizStateForFlowTodo(TodoBusiness business) { + if (business.getBizFinalState() == BizFinalStateEnum.COMPLETED) + return CardBizState.COMPLETED; + if (business.getBizFinalState() == BizFinalStateEnum.RETRACT) + return CardBizState.REVOKED; + if (business.getBizFinalState() == BizFinalStateEnum.PASSED) + return CardBizState.AGREED; + if (business.getBizFinalState() == BizFinalStateEnum.REJECTED) + return CardBizState.REJECTED; + if (business.getBizFinalState() == BizFinalStateEnum.ABORTED) + return CardBizState.ABORTED; + return null; + } + + public static Optional findTodoBizCategory(JSONObject bizParam) { + if (bizParam == null) + return Optional.empty(); + String category = new CardExtInfo(bizParam) + .getJsonObject().getString(TODO_CATEGORY_BIZ_PARAM_KEY); + return CodeDefinition.findByCode(BizCategoryEnum.class, category); + } + + public static Optional findTodoIdentityCode(JSONObject bizParam) { + if (bizParam == null) + return Optional.empty(); + String identityCode = new CardExtInfo(bizParam) + .getJsonObject().getString(TODO_IDENTITY_CODE_BIZ_PARAM_KEY); + return Optional.ofNullable(identityCode); + } + + @Setter + @Getter + private static class TodoCardUpdateStateRequest extends CardUpdateStateRequest { + private TodoBusiness todoBusiness; + private Todo todo; + } + + private void execTransactional(Runnable runnable) { + transactionTemplate.executeWithoutResult(unused -> runnable.run()); + } +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptor.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptor.java new file mode 100644 index 00000000..6e2894ac --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptor.java @@ -0,0 +1,147 @@ +package cn.axzo.msg.center.message.service.todo.card.workflow; + +import cn.axzo.msg.center.domain.entity.Todo; +import cn.axzo.msg.center.message.service.card.interceptor.CardButtonInterceptor; +import cn.axzo.msg.center.service.ButtonV3; +import cn.axzo.msg.center.service.enums.BizFinalStateEnum; +import cn.axzo.msg.center.service.enums.PendingMessageStateEnum; +import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; +import cn.axzo.msg.center.utils.desision.DecisionValue; +import cn.axzo.workflow.common.enums.BpmnProcessTaskResultEnum; +import cn.axzo.workflow.common.enums.ButtonVisibleScopeEnum; +import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo.BpmnButtonMetaInfoWithVisibleScope; +import com.google.common.collect.Sets; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static java.util.stream.Collectors.toSet; + +/** + * @author yanglin + */ +@Slf4j +@RequiredArgsConstructor +class WorkflowTodoCardButtonInterceptor implements CardButtonInterceptor { + + private final static Set SYSTEM_BUTTONS_SENDER_SHOW = Sets.newHashSet( + BizFinalStateEnum.RETRACT.getButtonCode()); + private final static Set SYSTEM_BUTTONS_EXECUTOR_SHOW = Sets.newHashSet( + BizFinalStateEnum.PASSED.getButtonCode(), + BizFinalStateEnum.REJECTED.getButtonCode()); + + private final Todo todo; + private final BpmnTaskButtonVo taskInfo; + + @Override + public DecisionValue isPerformActionAvailable(ButtonV3 button) { + BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null); + return DecisionValue.decide(btnBizState != null && btnBizState.isButtonPerformActionAvailable()); + } + + @Override + public DecisionValue isActionPerformed(ButtonV3 button) { + return DecisionValue.decide(isSystemButtonActionPerformed(button)); + } + + private boolean isSystemButtonActionPerformed(ButtonV3 button) { + return isExecutorActionPerformed(button) || isSenderActionPerformed(button); + } + + private boolean isExecutorActionPerformed(ButtonV3 button) { + BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null); + if (btnBizState == null) + return false; + if (btnBizState == BizFinalStateEnum.PASSED + && taskInfo.getExecutorTaskResult() == BpmnProcessTaskResultEnum.APPROVED) + return true; + return btnBizState == BizFinalStateEnum.REJECTED + && taskInfo.getExecutorTaskResult() == BpmnProcessTaskResultEnum.REJECTED; + } + + private boolean isSenderActionPerformed(ButtonV3 button) { + BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null); + if (btnBizState == null) + return false; + return btnBizState == BizFinalStateEnum.RETRACT + && taskInfo.getInitiatorTaskResult() == BpmnProcessTaskResultEnum.CANCELED; + } + + @Override + public DecisionValue getActionPerformedName(ButtonV3 button) { + BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null); + return btnBizState == null + ? DecisionValue.notSure() + : DecisionValue.decide(btnBizState.getActionPerformedName()); + } + + @Override + public DecisionValue isVisibleOnCard(ButtonV3 button) { + // 进行中隐藏所有按钮 + if (todo.getState() == PendingMessageStateEnum.PROCESSING) + return DecisionValue.decide(false); + // 显示失效按钮 + if (isSystemButtonActionPerformed(button)) + return DecisionValue.notSure(); + if (getWorkflowHideButtonKeys().contains(button.getCode())) + return DecisionValue.decide(false); + if (button.getSource() == RouterButtonSourceEnum.CUSTOM) + return DecisionValue.notSure(); + BpmnButtonMetaInfoWithVisibleScope workflowButton = findWorkflowButton(button).orElse(null); + // 如果找不到流程按钮, 说明不需要显示按钮,否则是否显示由待办模版控制 + return workflowButton == null ? DecisionValue.decide(false) : DecisionValue.notSure(); + } + + @Override + public DecisionValue isSenderShow(ButtonV3 button) { + if (button.getSource() == RouterButtonSourceEnum.CUSTOM) + return DecisionValue.decide(false); + return DecisionValue.decide(SYSTEM_BUTTONS_SENDER_SHOW.contains(button.getCode())); + } + + @Override + public DecisionValue isExecutorShow(ButtonV3 button) { + if (button.getSource() == RouterButtonSourceEnum.CUSTOM) + return DecisionValue.notSure(); + if (SYSTEM_BUTTONS_EXECUTOR_SHOW.contains(button.getCode())) + return DecisionValue.decide(true); + BpmnButtonMetaInfoWithVisibleScope workflowButton = findWorkflowButton(button).orElse(null); + if (workflowButton == null) + return DecisionValue.notSure(); + List scopes = workflowButton.getVisibleScopes(); + return DecisionValue.decide(scopes != null + && scopes.contains(ButtonVisibleScopeEnum.EXECUTOR) + && !scopes.contains(ButtonVisibleScopeEnum.INITIATOR)); + } + + private Optional findButtonClickState(ButtonV3 button) { + if (button.getSource() == RouterButtonSourceEnum.CUSTOM) + return Optional.empty(); + return BizFinalStateEnum.findButtonClickedState(button.getCode()); + } + + private Optional findWorkflowButton(ButtonV3 button) { + List workflowButtons = this.taskInfo.getButtons(); + if (workflowButtons == null) + workflowButtons = Collections.emptyList(); + return workflowButtons.stream() + .filter(workflowButton -> workflowButton.getBtnKey().equals(button.getCode())) + .findFirst(); + } + + private Set getWorkflowHideButtonKeys() { + List workflowHiddenButtons = taskInfo.getCustomHiddenButtons(); + if (workflowHiddenButtons == null) + workflowHiddenButtons = Collections.emptyList(); + return workflowHiddenButtons.stream() + .map(BpmnButtonMetaInfo::getBtnKey) + .collect(toSet()); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptorFactory.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptorFactory.java new file mode 100644 index 00000000..90aea463 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/card/workflow/WorkflowTodoCardButtonInterceptorFactory.java @@ -0,0 +1,76 @@ +package cn.axzo.msg.center.message.service.todo.card.workflow; + +import cn.axzo.msg.center.api.mq.TodoUpdateMessage; +import cn.axzo.msg.center.common.utils.BizAssertions; +import cn.axzo.msg.center.dal.TodoBusinessDao; +import cn.axzo.msg.center.dal.TodoDao; +import cn.axzo.msg.center.domain.entity.Todo; +import cn.axzo.msg.center.domain.entity.TodoBusiness; +import cn.axzo.msg.center.message.service.card.interceptor.CardButtonInterceptor; +import cn.axzo.msg.center.message.service.card.interceptor.CardButtonInterceptorFactory; +import cn.axzo.msg.center.message.service.todo.card.TodoSyncCardService; +import cn.axzo.msg.center.message.service.todo.manage.TodoLogger; +import cn.axzo.msg.center.message.service.todo.manage.TodoRequestContext; +import cn.axzo.msg.center.service.enums.BizCategoryEnum; +import cn.axzo.msg.center.service.pending.request.CardContent; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; +import cn.axzo.workflow.starter.api.WorkflowCoreService; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Nullable; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +class WorkflowTodoCardButtonInterceptorFactory implements CardButtonInterceptorFactory { + + private final TodoBusinessDao todoBusinessDao; + private final TodoDao todoDao; + private final WorkflowCoreService workflowCoreService; + private final TodoLogger todoLogger; + + @Override @Nullable + public CardButtonInterceptor create(CardContent card) { + BizCategoryEnum category = TodoSyncCardService.findTodoBizCategory(card.getBizParam()).orElse(null); + if (category != BizCategoryEnum.FLOW) + return null; + String identityCode = TodoSyncCardService.findTodoIdentityCode(card.getBizParam()).orElse(null); + if (identityCode == null) + return null; + Todo todo = todoDao.findTodoByCode(identityCode).orElse(null); + if (todo == null) { + log.warn("todo not found. identityCode={}", card.getBizParam()); + return null; + } + BpmnTaskButtonVo taskInfo = fetchWorkflowButtons(todo); + log.info("fetchWorkflowButtons, todoIdentityCode={}, workflowTaskInfo: {}", + todo.getIdentityCode(), JSON.toJSONString(taskInfo)); + String contextName = "syncWorkflowButtons"; + TodoUpdateMessage todoMessage = TodoSyncCardService.getTodoUpdateMessage().orElse(null); + if (todoMessage != null) + contextName += ":" + todoMessage.getOperation(); + TodoRequestContext ctx = TodoRequestContext.create(contextName, card) + .addLogContent("workflowTaskInfo", taskInfo); + todoLogger.logTodoUpdated(ctx, todo); + return new WorkflowTodoCardButtonInterceptor(todo, taskInfo); + } + + BpmnTaskButtonVo fetchWorkflowButtons(Todo todo) { + TodoBusiness business = todoBusinessDao.getBusinesses(todo).findBusiness(todo).orElse(null); + BizAssertions.assertNotNull(business, "todo business not found. todoIdentityCode={}", todo.getIdentityCode()); + BpmnTaskButtonSearchDTO workflowRequest = new BpmnTaskButtonSearchDTO(); + workflowRequest.setProcessInstanceId(todo.getBizCode()); + workflowRequest.setTaskId(todo.getSubBizCode()); + workflowRequest.setInitiatorPersonId(business.getPromoterPersonId()); + workflowRequest.setExecutorPersonId(todo.getExecutorPersonId()); + return workflowCoreService.findProcessSingleTaskButtons(workflowRequest); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoLogger.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoLogger.java index c4b34227..6fa11f62 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoLogger.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoLogger.java @@ -2,10 +2,12 @@ package cn.axzo.msg.center.message.service.todo.manage; import cn.axzo.msg.center.dal.TodoBusinessDao; import cn.axzo.msg.center.dal.TodoBusinesses; +import cn.axzo.msg.center.dal.TodoDao; import cn.axzo.msg.center.dal.TodoLogDao; import cn.axzo.msg.center.domain.entity.Todo; import cn.axzo.msg.center.domain.entity.TodoBusiness; import cn.axzo.msg.center.domain.entity.TodoLog; +import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig; import cn.axzo.msg.center.service.enums.BizCategoryEnum; import cn.axzo.msg.center.service.enums.PendingMessageStateEnum; import cn.axzo.msg.center.service.enums.TodoLogType; @@ -18,6 +20,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static java.util.stream.Collectors.toList; + /** * @author yanglin */ @@ -27,8 +31,11 @@ public class TodoLogger { private final TodoLogDao todoLogDao; private final TodoBusinessDao todoBusinessDao; + private final TodoDao todoDao; + private final PendingMessageBizConfig cfg; void logBusinessUpdated(TodoRequestContext ctx, TodoBusiness business) { + if (!cfg.isEnableTodoLog()) return; TodoLog log = createBusinessLog(ctx, business); log.setContext(ctx.getName()); log.addLogContents(ctx.getLogContents()); @@ -55,16 +62,20 @@ public class TodoLogger { logTodosUpdated(ctx, todos); } - void logTodoUpdated(TodoRequestContext ctx, Todo todo) { + public void logTodoUpdated(TodoRequestContext ctx, Todo todo) { logTodosUpdated(ctx, Collections.singletonList(todo)); } public void logTodosUpdated(TodoRequestContext ctx, List todos) { - if (CollectionUtils.isEmpty(todos)) - return; + if (!cfg.isEnableTodoLog()) return; + if (CollectionUtils.isEmpty(todos)) return; TodoBusinesses businesses = todoBusinessDao.getBusinesses(todos); ArrayList logs = new ArrayList<>(); - for (Todo todo : todos) { + List todoCodes = todos.stream() + .map(Todo::getIdentityCode) + .distinct().collect(toList()); + // reload to get the up-to-date update time + for (Todo todo : todoDao.getByCodes(todoCodes)) { logs.add(createTodoLog(ctx, businesses, todo) .addLogContents(ctx.getLogContents())); } @@ -101,6 +112,8 @@ public class TodoLogger { log.setSubBizCode(todo.getSubBizCode()); log.setRequestNo(ctx.getRequestNo()); log.setContext(ctx.getName()); + log.addLogContent("todoState", todo.getState()); + log.addLogContent("todoUpdateTime", todo.getUpdateAt().getTime()); String category = businesses.findBusiness(todo) .map(TodoBusiness::getBizCategory) .map(Enum::name) diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoManager.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoManager.java index 8e3b527f..98433de7 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoManager.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoManager.java @@ -21,7 +21,6 @@ import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam; import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service; import cn.axzo.msg.center.message.service.replay.RequestInfo; import cn.axzo.msg.center.message.service.replay.RequestReplayService; -import cn.axzo.msg.center.message.service.todo.TodoWithCardWrapper; import cn.axzo.msg.center.message.service.todo.manage.broadcast.TodoBroadcaster; import cn.axzo.msg.center.message.service.todo.manage.broadcast.TodoMqBroadcaster; import cn.axzo.msg.center.message.service.todo.manage.event.HandoverEvent; @@ -42,13 +41,12 @@ import cn.axzo.msg.center.service.pending.request.CompletePendingMessageRequest; import cn.axzo.msg.center.service.pending.request.PresetButtonPressedRequest; import cn.axzo.msg.center.service.pending.request.RevokeByTemplateCodeRequest; import cn.axzo.msg.center.service.pending.request.RevokePendingMessageByIdRequest; -import cn.axzo.msg.center.service.pending.request.SetHideRequest; import cn.axzo.msg.center.service.pending.request.TodoHandoverRequest; import cn.axzo.msg.center.service.pending.request.UpdateBusinessFinalBizStateRequest; import cn.axzo.msg.center.service.pending.request.UpdatePendingMessageByIdRequest; import cn.axzo.msg.center.service.pending.response.PushPendingMessageDTO; +import cn.axzo.msg.center.service.util.IdBuilder; import cn.axzo.msg.center.service.util.JSONUtils; -import cn.axzo.msg.center.utils.DateFormatUtil; import cn.axzo.msg.center.utils.QueryFormatter; import cn.axzo.msg.center.utils.UUIDUtil; import com.alibaba.fastjson.JSONObject; @@ -62,7 +60,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; import org.springframework.dao.DuplicateKeyException; @@ -72,7 +69,6 @@ import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -91,6 +87,8 @@ import static java.util.stream.Collectors.toSet; @RequiredArgsConstructor public class TodoManager { + public static final String OP_FIRE_PRESET_BUTTON_PRESSED = "firePresetButtonPressed"; + private final TodoBusinessDao todoBusinessDao; private final TodoDao todoDao; private final TodoRecordBuilder todoRecordBuilder; @@ -103,7 +101,6 @@ public class TodoManager { private final ApplicationContext applicationContext; private final TodoBroadcaster todoBroadcaster; private final TransactionTemplate transactionTemplate; - private final TodoWithCardWrapper todoWithCardWrapper; private final RequestReplayService requestReplayService; public List send(PendingMessagePushParam request) { @@ -184,7 +181,7 @@ public class TodoManager { if (StringUtils.isBlank(business.getSampleTodoCode()) && request.determineTodoType() == TodoType.EXECUTABLE) todoBusinessDao.updateSampleTodCode(business.getId(), sample.getIdentityCode()); - todoBroadcaster.fireTodoUpdates("send", todos); + todoBroadcaster.fireTodoUpdates("send", todos, true); // 记录日志 // @formatter:off ctx.addLogContent("templateTitle", templateModel.getTemplate().getTitle()) @@ -200,7 +197,6 @@ public class TodoManager { todoLogger.logBusinessUpdated(ctx, business); todoLogger.logTodosUpdated(ctx, todos); applicationContext.publishEvent(new NewTodoEvent(this, templateModel, todos)); - todoWithCardWrapper.send(request, todos); return todos.stream() .map(todo -> new PushPendingMessageDTO( @@ -264,8 +260,8 @@ public class TodoManager { destTodo.setExecutorName(request.determineToPersonName()); } todoDao.saveBatch(destTodos); - todoBroadcaster.fireTodoUpdates("handover", srcTodos); - todoBroadcaster.fireTodoUpdates("handover", destTodos); + todoBroadcaster.fireTodoUpdates("handover", srcTodos, true); + todoBroadcaster.fireTodoUpdates("handover", destTodos, true); // build handover mappings List mappings = new ArrayList<>(); for (int i = 0; i < srcTodos.size(); i++) { @@ -299,7 +295,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoCompleted(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardCompleteStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -310,7 +305,9 @@ public class TodoManager { public boolean completeById(CompletePendingMessageByIdRequest request) { Set ids = request.determineIds(); BizAssertions.assertNotEmpty(ids, "待办id不能为空"); - TodoRequestContext ctx = TodoRequestContext.create("completeById", request); + TodoRequestContext ctx = TodoRequestContext + .create("completeById", request) + .delayBroadcast(true); StateAdvanceResult advanceResult = advanceState(ctx, execAdvanceBuilder() .in(Todo::getId, ids.toArray(new Object[0])) .set(Todo::getState, PendingMessageStateEnum.COMPLETED)); @@ -324,10 +321,12 @@ public class TodoManager { "businessUpdated", businessUpdated); todoLogger.logBusinessUpdated(ctx, advanceResult.getBusiness()); } - if (advanceResult.isAdvanced()) { + if (advanceResult.isAdvanced()) todoLogger.logTodoCompleted(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardCompleteStateByTodoList(advanceResult.getAdvancedTodos()); - } + if (businessUpdated) + todoBroadcaster.fireTodoUpdates("completeById", advanceResult.getBusinessId(), true); + else + advanceResult.broadcast(); return advanceResult.isAdvanced() || businessUpdated; } @@ -354,12 +353,13 @@ public class TodoManager { "businessUpdated", businessUpdated); todoLogger.logBusinessUpdated(ctx, advanceResult.getBusiness()); } - if (advanceResult.isAdvanced()) { + if (advanceResult.isAdvanced()) todoLogger.logTodoCompleted(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardCompleteStateByTodoList(advanceResult.getAdvancedTodos()); - } + if (businessUpdated) + todoBroadcaster.fireTodoUpdates("completeByBizCode", advanceResult.getBusinessId(), true); + else + advanceResult.broadcast(); - advanceResult.broadcast(); return advanceResult.isAdvanced() || businessUpdated; } @@ -398,8 +398,7 @@ public class TodoManager { TodoRequestContext ctx = TodoRequestContext.create("updateBusinessFinalBizState", request) .addLogContent("updated", updated); todoLogger.logBusinessUpdated(ctx, business); - List todos = todoDao.getByBusinessIds(Collections.singletonList(business.getId())); - todoBroadcaster.fireTodoUpdates("updateBusinessFinalBizState", todos); + todoBroadcaster.fireTodoUpdates("updateBusinessFinalBizState", business.getId(), true); } return updated; } @@ -418,7 +417,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoCompleted(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardCompleteStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -437,8 +435,7 @@ public class TodoManager { return false; } todoLogger.logTodoRollback(ctx, advanceResult.getAdvancedTodos()); - todoBroadcaster.fireTodoUpdates("rollbackBySubBizCode", advanceResult.getAdvancedTodos()); -// todoWithCardWrapper.cardRollbackStateByTodoList(advanceResult.getAdvancedTodos()); + todoBroadcaster.fireTodoUpdates("rollbackBySubBizCode", advanceResult.getAdvancedTodos(), true); return true; } @@ -458,7 +455,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoRevoked(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardRevokeStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -474,7 +470,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoRevoked(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardRevokeStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -490,7 +485,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoRevoked(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardRevokeStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -507,7 +501,6 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoRevoked(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardRevokeStateByTodoList(advanceResult.getAdvancedTodos()); return true; } @@ -520,37 +513,11 @@ public class TodoManager { if (!advanceResult.isAdvanced()) return false; todoLogger.logTodoRevoked(ctx, advanceResult.getAdvancedTodos()); - todoWithCardWrapper.cardRevokeStateByTodoList(advanceResult.getAdvancedTodos()); return true; } // !! update - /** - * 将待办设置为隐藏, 隐藏有时间期限 - */ - @Transactional(rollbackFor = Exception.class) - public boolean setHide(SetHideRequest request) { - BizAssertions.assertTrue(StringUtils.isNotBlank(request.getSubBizCode()), "subBizCode不能为空"); - List todos = todoDao.getBySubBizCode(request.getSubBizCode()); - if (todos.isEmpty()) - return false; - int seconds = request.getHideSeconds() == null - ? cfg.getPendingSetHideSeconds() : request.getHideSeconds(); - Date expireTime = DateTime.now().plusSeconds(seconds).toDate(); - boolean updated = todoDao.setExecutableHide(request.getSubBizCode(), expireTime); - if (updated) { - List updatedTodos = todos.stream() - .filter(todo -> todo.getState() == PendingMessageStateEnum.HAS_BEEN_SENT) - .collect(toList()); - TodoRequestContext ctx = TodoRequestContext.create("setHide", request) - .addLogContent("expiredTime", expireTime.getTime()) - .addLogContent("readableExpiredTime", DateFormatUtil.toReadableString(expireTime)); - todoLogger.logTodosUpdated(ctx, updatedTodos); - } - return updated; - } - /** * 将待办设置为执行中 */ @@ -571,8 +538,7 @@ public class TodoManager { request.put("subBizCodes", subBizCodes); TodoRequestContext ctx = TodoRequestContext.create("batchSetProcessing", request); todoLogger.logSetTodoProcessing(ctx, todos); - todoBroadcaster.fireTodoUpdates("batchSetProcessing", todos); - todoWithCardWrapper.cardProcessingStateByTodoList(todos); + todoBroadcaster.fireTodoUpdates("batchSetProcessing", todos, true); } return updated; } @@ -609,7 +575,7 @@ public class TodoManager { .addLogContent("updatedRouterParam", routerParam) .addLogContent("updatedTemplateCode", request.getTemplateCode()); todoLogger.logBusinessUpdated(ctx, business); - //TODO todoWithCardWrapper.send + todoBroadcaster.fireTodoUpdates("updateBusinessById", business.getId(), true); } return updated; } @@ -620,31 +586,23 @@ public class TodoManager { * 点击预设按钮 */ @Transactional(rollbackFor = Exception.class) - public boolean firePresetButtonPressed(PresetButtonPressedRequest request, boolean isSyncCard) { + public boolean firePresetButtonPressed(PresetButtonPressedRequest request, boolean syncCardState) { Todo todo = todoDao.findTodoByCode(request.getIdentityCode()).orElse(null); if (todo == null) return false; - TodoRequestContext ctx = TodoRequestContext.create("firePresetButtonPressed", request) - .addLogContent("presetButtonType", request.getPresetButtonType()); + TodoRequestContext ctx = TodoRequestContext.create(OP_FIRE_PRESET_BUTTON_PRESSED, request) + .addLogContent("presetButtonType", request.getPresetButtonType()) + .addLogContent("syncCardState", syncCardState); + if (!syncCardState) + ctx.disableUpdateCard(); StateAdvanceResult advanceResult = advanceState(ctx, execAdvanceBuilder() .eq(Todo::getIdentityCode, request.getIdentityCode()) .set(Todo::getState, PendingMessageStateEnum.COMPLETED)); - // isExecCompleted 可以排除是抄送待办的情况 - boolean isAdvancedOrCompleted = advanceResult.isAdvanced() || todo.isExecCompleted(); - // 支持重复发mq消息 - if (isAdvancedOrCompleted) { + if (advanceResult.isAdvanced()) { sendMqMessageOnPresetButtonPressed(ctx, request, todo); - todoBroadcaster.fireTodoUpdates("presetButtonPressed", todo); - todoWithCardWrapper.fireCardWhenPresetButtonPressedByTodo(request, todo, isSyncCard); - - // 如果不是重复发送, 就只记一条日志. 如果是重复发送, 就单独记录一条日志 - if (!advanceResult.isAdvanced()) - todoLogger.logTodoUpdated(ctx, todo); - } - // 如果不是重复发送, 就只记一条日志. 所以这个记录日志不能提前 - if (advanceResult.isAdvanced()) todoLogger.logTodoCompleted(ctx, advanceResult.getAdvancedTodos()); - return isAdvancedOrCompleted; + } + return advanceResult.isAdvanced(); } /** @@ -662,9 +620,13 @@ public class TodoManager { try { mqProducer.send(MqMessageRecord .builder(MqMessageType.TODO_PRESET_BUTTON_PRESSED, message) - .messageKey(todo.getId()) + .messageKey(todo.getIdentityCode()) .operatorId(request.getOperatorId()) - .shardingKey(todo.getTemplateCode()) + .shardingKey(IdBuilder.builder() + .append(todo.getTemplateCode()) + .append(todo.getBizCode()) + .append(todo.getSubBizCode()) + .build()) .build()); ctx.addLogContent("sendMqMessage", ImmutableMap.of("isSuccess", "true")); } catch (Exception e) { @@ -692,6 +654,7 @@ public class TodoManager { return false; ImmutableMap request = ImmutableMap.of("personId", personId, "identityCode", identityCode); TodoRequestContext ctx = TodoRequestContext.create("setCopiedToMeRead", request) + .disableUpdateCard() .addLogContent("state", PendingMessageStateEnum.READ) .addLogContent("isSetAllRead", StringUtils.isBlank(identityCode)); StateAdvanceResult advanceResult = advanceState(ctx, copiedAdvanceBuilder() @@ -724,7 +687,7 @@ public class TodoManager { "currentStateSample", noStateBusinessTodos.sampleTodo()); // @formatter:on if (cfg.isLogAdvanceTodoStateFail()) - todoLogger.logTodosUpdated(ctx, noStateBusinessTodos.todos); + todoLogger.logTodosUpdated(ctx.copy(String.format("%s:fail", ctx.getName())), noStateBusinessTodos.todos); // throw an error? dunno log.warn("尝试推进待办状态, 但是 {}. ctx={}, query={}, currentStateSample={}", failReason, ctx, QueryFormatter.format(builder.getQuery()), noStateBusinessTodos.sampleTodo()); @@ -745,7 +708,7 @@ public class TodoManager { broadcastHandler = () -> { if (!updatedTodoIds.isEmpty()) { List updatedTodos = todoDao.listByIds(updatedTodoIds); - todoBroadcaster.fireTodoUpdates(ctx.getName(), updatedTodos); + todoBroadcaster.fireTodoUpdates(ctx.getName(), updatedTodos, ctx.isUpdateCard()); } }; if (!ctx.isDelayBroadcast()) diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoRequestContext.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoRequestContext.java index e83539ea..1e324bfa 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoRequestContext.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/TodoRequestContext.java @@ -3,7 +3,9 @@ package cn.axzo.msg.center.message.service.todo.manage; import cn.axzo.msg.center.common.utils.BizAssertions; import cn.axzo.msg.center.utils.UUIDUtil; import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; import java.util.HashMap; import java.util.LinkedHashMap; @@ -18,6 +20,7 @@ public class TodoRequestContext { private final String requestNo; private final Map logContents = new LinkedHashMap<>(); private boolean delayBroadcast = false; + private boolean updateCard = true; private TodoRequestContext(String name, String requestNo, Object request) { this.name = name; @@ -35,7 +38,8 @@ public class TodoRequestContext { } public static TodoRequestContext create(String name, String requestNo, Object request) { - return new TodoRequestContext(name, requestNo, request); + String finalRequestNo = StringUtils.isBlank(requestNo) ? UUIDUtil.uuidString() : requestNo; + return new TodoRequestContext(name, finalRequestNo, request); } public TodoRequestContext addLogContent(String name, Object... fields) { @@ -53,15 +57,28 @@ public class TodoRequestContext { return this; } + public TodoRequestContext disableUpdateCard() { + this.updateCard = false; + return this; + } + public TodoRequestContext addLogContent(String name, Object value) { if (name == null || value == null) return this; - if (!logContents.containsKey(name)) - logContents.put(name, value); + if (!logContents.containsKey(name)) { + if (value instanceof Throwable) + logContents.put(name, Throwables.getStackTraceAsString((Throwable) value)); + else + logContents.put(name, value); + } return this; } public TodoRequestContext copy() { + return copy(this.name); + } + + public TodoRequestContext copy(String name) { TodoRequestContext copy = new TodoRequestContext(name, requestNo); copy.logContents.putAll(logContents); return copy; diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoBroadcaster.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoBroadcaster.java index a2d6729d..fcd2adec 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoBroadcaster.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoBroadcaster.java @@ -26,11 +26,16 @@ public class TodoBroadcaster { private final TodoPullBroadcaster todoPullBroadcaster; private final TodoDao todoDao; - public void fireTodoUpdates(String operation, Todo todo) { - fireTodoUpdates(operation, Collections.singletonList(todo)); + public void fireTodoUpdates(String operation, Long businessId, boolean updateCard) { + List todos = todoDao.getByBusinessIds(Collections.singletonList(businessId)); + fireTodoUpdates(operation, todos, updateCard); } - public void fireTodoUpdates(String operation, List todos) { + public void fireTodoUpdates(String operation, Todo todo, boolean updateCard) { + fireTodoUpdates(operation, Collections.singletonList(todo), updateCard); + } + + public void fireTodoUpdates(String operation, List todos, boolean updateCard) { if (CollectionUtils.isEmpty(todos)) return; List todoIds = todos.stream() @@ -42,8 +47,8 @@ public class TodoBroadcaster { log.warn("发送待办通知时, 查询不到最新的待办信息. todoIds={}", JSON.toJSONString(todoIds)); return; } - todoMqBroadcaster.fireTodoUpdated(operation, todos); - todoPullBroadcaster.fireTodoChanged(todos); + todoMqBroadcaster.fireTodoUpdated(operation, upToDateTodos, updateCard); + todoPullBroadcaster.fireTodoChanged(upToDateTodos); } } \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoMqBroadcaster.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoMqBroadcaster.java index b2a58dce..40b283ae 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoMqBroadcaster.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/manage/broadcast/TodoMqBroadcaster.java @@ -11,6 +11,7 @@ import cn.axzo.msg.center.message.service.todo.manage.TodoExt; import cn.axzo.msg.center.mq.MqMessageRecord; import cn.axzo.msg.center.mq.MqProducer; import cn.axzo.msg.center.service.enums.MqMessageType; +import cn.axzo.msg.center.service.util.IdBuilder; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -26,20 +27,25 @@ public class TodoMqBroadcaster { private final MqProducer mqProducer; private final TodoBusinessDao todoBusinessDao; - public void fireTodoUpdated(String operation, List todos) { + public void fireTodoUpdated(String operation, List todos, boolean updateCard) { TodoBusinesses businesses = todoBusinessDao.getBusinesses(todos); for (Todo todo : todos) - fireTodoUpdated(operation, businesses, todo); + fireTodoUpdated(operation, businesses, todo, updateCard); } - private void fireTodoUpdated(String operation, TodoBusinesses businesses, Todo todo) { + private void fireTodoUpdated(String operation, TodoBusinesses businesses, Todo todo, boolean updateCard) { TodoUpdateMessage message = new TodoUpdateMessage(); message.setOperation(operation); message.setUpdatedTodo(createTodoInfo(businesses, todo)); + message.setUpdateCard(updateCard); mqProducer.send(MqMessageRecord .builder(MqMessageType.TODO_STATE_UPDATE, message) - .messageKey(todo.getId()) - .shardingKey(todo.getTemplateCode()) + .messageKey(todo.getIdentityCode()) + .shardingKey(IdBuilder.builder() + .append(todo.getTemplateCode()) + .append(todo.getBizCode()) + .append(todo.getSubBizCode()) + .build()) .build()); } diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/BeautifulPaginationInterceptor.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/BeautifulPaginationInterceptor.java index 3f7c9999..8f1f8677 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/BeautifulPaginationInterceptor.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/BeautifulPaginationInterceptor.java @@ -18,6 +18,7 @@ import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.springframework.core.Ordered; @@ -36,12 +37,13 @@ import java.util.concurrent.atomic.AtomicBoolean; args = {Connection.class, Integer.class})}) public class BeautifulPaginationInterceptor implements Interceptor, Ordered { + private static final DefaultReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory(); private final PaginationInterceptor delegate = new CustomPaginationInterceptor(); @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); - MetaObject metaObject = SystemMetaObject.forObject(statementHandler); + MetaObject metaObject = systemMetaForObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType() || StatementType.CALLABLE == mappedStatement.getStatementType()) { @@ -70,7 +72,7 @@ public class BeautifulPaginationInterceptor implements Interceptor, Ordered { private static String tryBuildSql(Invocation invocation) { Connection connection = (Connection) (invocation.getArgs()[0]); StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); - MetaObject metaObject = SystemMetaObject.forObject(statementHandler); + MetaObject metaObject = systemMetaForObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); try (PreparedStatement statement = connection.prepareStatement(boundSql.getSql())) { @@ -86,6 +88,13 @@ public class BeautifulPaginationInterceptor implements Interceptor, Ordered { } } + public static MetaObject systemMetaForObject(Object object) { + return MetaObject.forObject(object, + SystemMetaObject.DEFAULT_OBJECT_FACTORY, + SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, + REFLECTOR_FACTORY); + } + private static class CustomPaginationInterceptor extends PaginationInterceptor { public CustomPaginationInterceptor() { diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/rowcountwarn/ProxyStatement.java b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/rowcountwarn/ProxyStatement.java index 30f5cd75..6f96ba29 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/rowcountwarn/ProxyStatement.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/message/service/todo/mybatis/rowcountwarn/ProxyStatement.java @@ -63,7 +63,7 @@ public class ProxyStatement extends StatementWrapper { private boolean canWarnPeriodically() { int maxWarnTimes = props.getRowCount().getPeriodMaxWarnTimes(); - return rowCount % 2000 == 0 && periodWarnTimes <= maxWarnTimes; + return rowCount % 100 == 0 && periodWarnTimes <= maxWarnTimes; } @Override diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/mq/ConsumerIsolation.java b/inside-notices/src/main/java/cn/axzo/msg/center/mq/ConsumerIsolation.java new file mode 100644 index 00000000..8574bac9 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/mq/ConsumerIsolation.java @@ -0,0 +1,29 @@ +package cn.axzo.msg.center.mq; + +/** + * @author yanglin + */ +public enum ConsumerIsolation { + + TODO_SYNC_CARD_BIZ, + TODO_SYNC_CARD_FLOW, + TODO_PRESET_BUTTON_CLICKED_SYNC_CARD, + CARD_PRESET_BUTTON_CLICKED_SYNC_TODO + + ; + + private static final ThreadLocal INSTANCE = new ThreadLocal<>(); + + public static void setIsolation(ConsumerIsolation isolation) { + INSTANCE.set(isolation); + } + + public static ConsumerIsolation getIsolation() { + return INSTANCE.get(); + } + + public static void clearIsolation() { + INSTANCE.remove(); + } + +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/mq/IsolationMQListener.java b/inside-notices/src/main/java/cn/axzo/msg/center/mq/IsolationMQListener.java new file mode 100644 index 00000000..bfcc56d3 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/mq/IsolationMQListener.java @@ -0,0 +1,56 @@ +package cn.axzo.msg.center.mq; + +import cn.axzo.framework.rocketmq.BaseListener; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.msg.center.service.enums.MqMessageType; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author yanglin + */ +public abstract class IsolationMQListener extends BaseListener + implements RocketMQListener, EventHandler, InitializingBean { + + @Autowired + private EventConsumer eventConsumer; + private final ConsumerIsolation isolation; + private final Event.EventCode eventCode; + + protected IsolationMQListener(ConsumerIsolation isolation, MqMessageType mqMessageType) { + this(isolation, mqMessageType.getEventCode()); + } + + protected IsolationMQListener(ConsumerIsolation isolation, Event.EventCode eventCode) { + this.isolation = isolation; + this.eventCode = eventCode; + } + + @Override + public void onMessage(MessageExt message) { + ConsumerIsolation.setIsolation(isolation); + try { + super.onEvent(message, eventConsumer); + } finally { + ConsumerIsolation.clearIsolation(); + } + } + + @Override + public final void onEvent(Event event, EventConsumer.Context context) { + if (ConsumerIsolation.getIsolation() == isolation) + onEventImpl(event, context); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandler(eventCode, this); + } + + public abstract void onEventImpl(Event event, EventConsumer.Context context); +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/mq/RocketMQConfig.java b/inside-notices/src/main/java/cn/axzo/msg/center/mq/RocketMQConfig.java index f7b87a3f..94df03a4 100644 --- a/inside-notices/src/main/java/cn/axzo/msg/center/mq/RocketMQConfig.java +++ b/inside-notices/src/main/java/cn/axzo/msg/center/mq/RocketMQConfig.java @@ -11,7 +11,6 @@ import cn.axzo.framework.rocketmq.RocketMQEventProducer.RocketMQMessageMeta; import cn.axzo.framework.rocketmq.utils.TraceUtils; import cn.axzo.msg.center.api.mq.MqMessage; import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig; -import com.alibaba.fastjson.JSON; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import lombok.RequiredArgsConstructor; @@ -41,8 +40,9 @@ import java.util.function.Consumer; public class RocketMQConfig { public static final String APP_NAME = "MSG-CENTER"; + public static final String MSG_CENTER_TOPIC = "topic_msg_center_${spring.profiles.active}"; - @Value("topic_msg_center_${spring.profiles.active}") + @Value(MSG_CENTER_TOPIC) private String topic; @Bean @@ -126,7 +126,7 @@ public class RocketMQConfig { eventConsumer.onEvent(value, EventConsumer.Context.builder() .msgId(message.getMsgId()) .ext(ImmutableMap.of("topic", topic)) - .headers(Maps.transformValues(headers, header -> Optional.ofNullable(header).map(String::getBytes).orElse(new byte[] {}))) + .headers(Maps.transformValues(headers, header -> Optional.ofNullable(header).map(String::getBytes).orElse(new byte[]{}))) .lagSupplier(() -> partitionLag) .maxAllowElapsedMillis(cfg.getMsgCenterMqSelfConsumeMaxExecMs()) .build()); @@ -140,26 +140,4 @@ public class RocketMQConfig { }); } - /** - * 卡片变更,同步状态至待办TODO - */ - @Slf4j - @Component - @RocketMQMessageListener(topic = "topic_msg_center_${spring.profiles.active}", - consumerGroup = "GID_topic_card_change_state_sync_todo_${spring.application.name}_${spring.profiles.active}", - consumeMode = ConsumeMode.ORDERLY, - nameServer = "${rocketmq.name-server}" - ) - public static class CardChangeStateSyncTodoListener extends BaseListener implements RocketMQListener { - - @Autowired - private EventConsumer eventConsumer; - - @Override - public void onMessage(MessageExt message) { - log.info("CardChangeStateSyncTodoListener onMessage,message:{}", JSON.toJSONString(message)); - super.onEvent(message, eventConsumer); - } - } - } \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/Decision.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/Decision.java new file mode 100644 index 00000000..fa20786a --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/Decision.java @@ -0,0 +1,8 @@ +package cn.axzo.msg.center.utils.desision; + +/** + * @author yanglin + */ +public enum Decision { + NOT_SURE, DECIDED +} \ No newline at end of file diff --git a/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/DecisionValue.java b/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/DecisionValue.java new file mode 100644 index 00000000..54ac8b24 --- /dev/null +++ b/inside-notices/src/main/java/cn/axzo/msg/center/utils/desision/DecisionValue.java @@ -0,0 +1,41 @@ +package cn.axzo.msg.center.utils.desision; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.function.Supplier; + +/** + * @author yanglin + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class DecisionValue { + + private static final DecisionValue NOT_SURE = new DecisionValue<>(Decision.NOT_SURE, null); + + private final Decision decision; + @Getter private final T value; + + @SuppressWarnings("unchecked") + public static DecisionValue notSure() { + return (DecisionValue) NOT_SURE; + } + + public static DecisionValue decide(T value) { + return new DecisionValue<>(Decision.DECIDED, value); + } + + public T orElse(Supplier supplier) { + return isDecided() ? value : supplier.get(); + } + + public T orElse(T value) { + return isDecided() ? this.value : value; + } + + public boolean isDecided() { + return decision == Decision.DECIDED; + } + +} \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardInfo.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardInfo.java index f7ecef1c..1558e478 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardInfo.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardInfo.java @@ -148,4 +148,9 @@ public class CardInfo { */ private String subtitle; + public JSONObject determineBizParam() { + if (bizParam == null) + bizParam = new JSONObject(); + return bizParam; + } } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardPresetButtonPressedMessage.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardPresetButtonPressedMessage.java index 79fabe22..402a002b 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardPresetButtonPressedMessage.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/CardPresetButtonPressedMessage.java @@ -30,4 +30,5 @@ public class CardPresetButtonPressedMessage extends MqMessage implements Seriali */ private CardInfo cardInfo; + private String batchNo; } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/TodoUpdateMessage.java b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/TodoUpdateMessage.java index 3cb4bce4..f7be9bdc 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/TodoUpdateMessage.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/api/mq/TodoUpdateMessage.java @@ -1,5 +1,6 @@ package cn.axzo.msg.center.api.mq; +import com.alibaba.fastjson.JSON; import lombok.Data; import lombok.EqualsAndHashCode; @@ -9,6 +10,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class TodoUpdateMessage extends MqMessage { + /** * 什么操作导致这次变化 */ @@ -17,4 +19,15 @@ public class TodoUpdateMessage extends MqMessage { * 待办信息 */ private TodoInfo updatedTodo; + + /** + * 是否需要更新卡片 + */ + private boolean updateCard; + + @Override + public String toString() { + return JSON.toJSONString(this); + } + } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/ButtonV3.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/ButtonV3.java index 6a801540..4593e67c 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/ButtonV3.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/ButtonV3.java @@ -1,12 +1,17 @@ package cn.axzo.msg.center.service; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.PresetButtonType; +import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; +import cn.axzo.msg.center.service.pending.response.MessageButton; + +import java.util.List; /** * @author yanglin */ -public interface ButtonV3 { +public interface ButtonV3 extends MessageButton { String getName(); @@ -22,14 +27,14 @@ public interface ButtonV3 { RouterCategoryEnum getCategory(); + RouterButtonSourceEnum getSource(); + + List getStyles(); + default boolean determineIsPendingShow() { return getPendingShow() != null && getPendingShow(); } - default boolean determineIsExecutorShow() { - return getExecutorShow() != null && getExecutorShow(); - } - default boolean isPerformActionAvailable() { return determineIsPendingShow() || getCategory() == RouterCategoryEnum.PRESET_BUTTON; } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/domain/UrlConfig.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/domain/UrlConfig.java index 7dd29897..6f7ab0aa 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/domain/UrlConfig.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/domain/UrlConfig.java @@ -1,5 +1,6 @@ package cn.axzo.msg.center.service.domain; +import cn.axzo.framework.jackson.utility.JSON; import cn.axzo.msg.center.service.domain.url.AppUrl; import cn.axzo.msg.center.service.domain.url.WebUrl; import cn.axzo.msg.center.service.enums.WebPageOpenStrategy; @@ -17,6 +18,8 @@ import lombok.Setter; @Getter public class UrlConfig { + private String sameUrlForAllPlatforms; + /** * PC(OMS) */ @@ -129,4 +132,14 @@ public class UrlConfig { getOrCreateAppWorker().setIos(appUrl); } + public void setWebOpenStrategy(WebPageOpenStrategy openStrategy) { + if (pcCms != null) pcCms.setOpenStrategy(openStrategy); + if (pcOms != null) pcOms.setOpenStrategy(openStrategy); + if (pcGaGeneral != null) pcGaGeneral.setOpenStrategy(openStrategy); + } + + public UrlConfig copy() { + return JSON.parseObject(JSON.toJSONString(this), UrlConfig.class); + } + } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PeerPerson.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PeerPerson.java index c08e0697..8ae90686 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PeerPerson.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/dto/PeerPerson.java @@ -26,7 +26,7 @@ public class PeerPerson { return person; } - public static PeerPerson newPeerPerson(Long personId, Long ouId, Long workspaceId) { + public static PeerPerson create(Long personId, Long ouId, Long workspaceId) { PeerPerson person = new PeerPerson(); person.setPersonId(personId); person.setOuId(ouId); diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizCategoryEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizCategoryEnum.java index e3085def..7deed910 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizCategoryEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizCategoryEnum.java @@ -13,10 +13,15 @@ import lombok.Getter; */ @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) -public enum BizCategoryEnum { +public enum BizCategoryEnum implements CodeDefinition { FLOW("流程"), OTHER("其它"), ; private final String desc; + + @Override + public String getCode() { + return name(); + } } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizFinalStateEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizFinalStateEnum.java index e3e9ab2b..5b7ad802 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizFinalStateEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/BizFinalStateEnum.java @@ -3,14 +3,15 @@ package cn.axzo.msg.center.service.enums; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Optional; /** - * @description - * 业务终态的状态枚举,包含审批流的相关状态 - * * @author cold_blade - * @date 2023/11/7 * @version 1.0 + * @description 业务终态的状态枚举,包含审批流的相关状态 + * @date 2023/11/7 */ @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) @@ -19,28 +20,42 @@ public enum BizFinalStateEnum implements CodeDefinition { /** * 已处理 */ - COMPLETED("https://static.axzo.cn/fe-static/uni-icon/public/seal-1%23_%241699341908382.png"), + COMPLETED("", "", "https://static.axzo.cn/fe-static/uni-icon/public/seal-1%23_%241699341908382.png"), /** * 已撤销 */ - RETRACT("https://static.axzo.cn/fe-static/uni-icon/native/%E5%8E%9F%E7%A8%BF%23_%241711338477975.png"), + RETRACT("BPMN_REVOCATION", "已撤销", "https://static.axzo.cn/fe-static/uni-icon/native/%E5%8E%9F%E7%A8%BF%23_%241711338477975.png"), /** * 已通过 */ - PASSED("https://static.axzo.cn/fe-static/uni-icon/public/seal-4%23_%241699341908374.png"), + PASSED("BPMN_APPROVE", "已同意", "https://static.axzo.cn/fe-static/uni-icon/public/seal-4%23_%241699341908374.png"), /** * 已拒绝 */ - REJECTED("https://static.axzo.cn/fe-static/uni-icon/public/seal-2%23_%241699341908381.png"), + REJECTED("BPMN_REJECT", "已驳回", "https://static.axzo.cn/fe-static/uni-icon/public/seal-2%23_%241699341908381.png"), /** * 已终止 */ - ABORTED("https://axzo-public.oss-cn-chengdu.aliyuncs.com/%E5%8D%B0%E7%AB%A0-%E8%AF%A6%E6%83%85end.png"); + ABORTED("", "", "https://axzo-public.oss-cn-chengdu.aliyuncs.com/%E5%8D%B0%E7%AB%A0-%E8%AF%A6%E6%83%85end.png"); + private final String buttonCode; + private final String actionPerformedName; private final String icon; @Override public String getCode() { return name(); } + + public static Optional findButtonClickedState(String buttonCode) { + for (BizFinalStateEnum value : values()) { + if (value.buttonCode.equals(buttonCode)) + return Optional.of(value); + } + return Optional.empty(); + } + + public boolean isButtonPerformActionAvailable() { + return StringUtils.isNotBlank(actionPerformedName); + } } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardBizState.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardBizState.java index 4963681a..6aa90263 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardBizState.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardBizState.java @@ -26,7 +26,7 @@ public enum CardBizState implements CodeDefinition { ABORTED("已中止", true, "https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_aborted_20241220.png"), COMPLETED("已处理", true, "https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_completed_20241220.png"), END("已完结", true, "https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_end_20241220.png"), - IN_PROGRESS("进行中", false, "https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_inprogress_20241220.png") + PROCESSING("进行中", false, "https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_inprogress_20241220.png") ; diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardState.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardState.java index 425f86ea..c0112524 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardState.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/CardState.java @@ -11,15 +11,14 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public enum CardState implements CodeDefinition { - CREATED("待处理"), - SEND_SUCCESS("发送成功"), - COMPLETED("已完成"), + CREATED("未发送"), + SEND_SUCCESS("已发送"), + COMPLETED("已处理"), ; private final String name; - @Override public String getCode() { return name(); diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PendingMessageStateEnum.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PendingMessageStateEnum.java index 485bcdd1..6d196df5 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PendingMessageStateEnum.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/enums/PendingMessageStateEnum.java @@ -19,11 +19,9 @@ import java.util.Objects; public enum PendingMessageStateEnum implements CodeDefinition { CREATED(0, "创建"), - UNSENT(1, "未发送"), HAS_BEEN_SENT(2, "代办"), COMPLETED(5, "已办"), RETRACT(6, "已撤回"), - DELETED(7, "已删除"), READ(8, "已读"), PROCESSING(9, "处理中") ; diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardContent.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardContent.java index 1f2c2aaf..2cde4294 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardContent.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardContent.java @@ -11,6 +11,10 @@ import java.util.List; */ public interface CardContent { + String getBizCode(); + + String getSubBizCode(); + String getTemplateCode(); CardStateInfo getStateInfo(); diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardUpdateStateRequest.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardUpdateStateRequest.java index 8b13e39f..629a793a 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardUpdateStateRequest.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/request/CardUpdateStateRequest.java @@ -24,16 +24,18 @@ public class CardUpdateStateRequest extends CardUpdateRequest { private Boolean cardCompleted; public void validate() { - if (!determineIsCardCompleted() && bizState == null) - throw new ServiceException("cardCompleted 和 bizState 不能同时为空"); + if (!isValid()) throw new ServiceException("cardCompleted 和 bizState 不能同时为空"); + } + + public boolean isValid() { + return determineIsCardCompleted() || bizState != null; } public boolean determineIsCardCompleted() { return cardCompleted != null && cardCompleted; } - @Override - public String toString() { + @Override public String toString() { return JSON.toJSONString(this); } diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/TodoButtonProvider.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/TodoButtonProvider.java deleted file mode 100644 index ec92244a..00000000 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/TodoButtonProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.axzo.msg.center.service.pending.response; - -import cn.axzo.msg.center.api.custombutton.ProposedButtons; -import cn.axzo.msg.center.service.dto.ButtonRouterDTO; - -import java.util.List; - -/** - * @author yanglin - */ -@Deprecated -public interface TodoButtonProvider { - - List getButtonRouters(); - - ProposedButtons getProposedButtons(); - -} \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/ParsedModelV3Walker.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/ParsedModelV3Walker.java index d5ed8f0d..33582ee4 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/ParsedModelV3Walker.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/ParsedModelV3Walker.java @@ -15,6 +15,7 @@ import cn.axzo.msg.center.service.pending.response.v3.model.ParsedTemplateV3; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; +import java.util.Comparator; /** * @author yanglin @@ -40,7 +41,9 @@ public class ParsedModelV3Walker { } for (ParsedGroupV3 group : new ArrayList<>(model.determineGroups())) visitGroup(visitor, group); - for (ParsedButtonV3 button : new ArrayList<>(model.determineButtons())) + ArrayList buttons = new ArrayList<>(model.determineButtons()); + buttons.sort(Comparator.comparingInt(ParsedButtonV3::determinePriority)); + for (ParsedButtonV3 button : buttons) visitButton(visitor, button); if (template != null) visitor.exitTemplate(template); diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/model/ParsedButtonV3.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/model/ParsedButtonV3.java index f308b86f..b22df160 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/model/ParsedButtonV3.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/pending/response/v3/model/ParsedButtonV3.java @@ -103,13 +103,8 @@ public class ParsedButtonV3 implements MessageButton, ButtonV3 { return priority == null ? Integer.MAX_VALUE : priority; } - public List parseStyle() { - if (style == null) return Collections.emptyList(); - return JSON.parseArray(style.toJSONString(), ButtonStyleEnum.class); - } - public boolean hasStyle(ButtonStyleEnum style) { - return parseStyle().contains(style); + return getStyles().contains(style); } @Override @@ -126,4 +121,10 @@ public class ParsedButtonV3 implements MessageButton, ButtonV3 { public PresetButtonType getPresetBtnType() { return presetButtonType; } + + @Override + public List getStyles() { + if (style == null) return Collections.emptyList(); + return JSON.parseArray(style.toJSONString(), ButtonStyleEnum.class); + } } \ No newline at end of file diff --git a/msg-center-api/src/main/java/cn/axzo/msg/center/service/util/IdBuilder.java b/msg-center-api/src/main/java/cn/axzo/msg/center/service/util/IdBuilder.java index cacb9fd7..fc1a727d 100644 --- a/msg-center-api/src/main/java/cn/axzo/msg/center/service/util/IdBuilder.java +++ b/msg-center-api/src/main/java/cn/axzo/msg/center/service/util/IdBuilder.java @@ -45,4 +45,8 @@ public class IdBuilder { return buf.stream().map(String::valueOf).collect(joining(":")); } + @Override + public String toString() { + return build(); + } } \ No newline at end of file diff --git a/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/BizAssertions.java b/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/BizAssertions.java index 3a4dc9b2..42dbb8c0 100644 --- a/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/BizAssertions.java +++ b/msg-center-common/src/main/java/cn/axzo/msg/center/common/utils/BizAssertions.java @@ -21,6 +21,10 @@ import java.util.Objects; @Slf4j public class BizAssertions { + public static ServiceException fail(String message, Object... args) { + return new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + /** * 断言为NULL */ diff --git a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/TodoDao.java b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/TodoDao.java index 4e39eeba..0a44c4af 100644 --- a/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/TodoDao.java +++ b/msg-center-dal/src/main/java/cn/axzo/msg/center/dal/TodoDao.java @@ -17,9 +17,7 @@ import org.springframework.stereotype.Component; import javax.annotation.Nullable; import java.util.Collections; -import java.util.Date; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -157,24 +155,4 @@ public class TodoDao extends ServiceImpl { .set(Todo::getState, PendingMessageStateEnum.PROCESSING) .update(); } - - public boolean setExecutableHide(String subBizCode, Date expireTime) { - return lambdaUpdate() - .eq(Todo::getType, TodoType.EXECUTABLE) - .eq(Todo::getSubBizCode, subBizCode) - .eq(Todo::getState, PendingMessageStateEnum.HAS_BEEN_SENT) - .set(Todo::getHideUntil, expireTime) - .update(); - } - - public List findByCondition(String templateCode, String bizCode, String subBizCode, Long executorPersonId, Long ouId, Long workspaceId) { - return lambdaQuery() - .eq(StringUtils.isNotBlank(templateCode), Todo::getTemplateCode, templateCode) - .eq(StringUtils.isNotBlank(bizCode), Todo::getBizCode, bizCode) - .eq(StringUtils.isNotBlank(subBizCode), Todo::getSubBizCode, subBizCode) - .eq(Objects.nonNull(executorPersonId), Todo::getExecutorPersonId, executorPersonId) - .eq(Objects.nonNull(ouId), Todo::getOuId, ouId) - .eq(Objects.nonNull(workspaceId), Todo::getReceiverWorkspaceId, workspaceId) - .list(); - } } \ No newline at end of file diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateButtonV3.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateButtonV3.java index c836328f..f5a892fb 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateButtonV3.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/MessageTemplateButtonV3.java @@ -3,6 +3,7 @@ package cn.axzo.msg.center.domain.entity; import cn.axzo.msg.center.domain.utils.IgnorePropsJsonTypeHandler; import cn.axzo.msg.center.service.ButtonV3; import cn.axzo.msg.center.service.domain.UrlConfig; +import cn.axzo.msg.center.service.enums.ButtonStyleEnum; import cn.axzo.msg.center.service.enums.PresetButtonType; import cn.axzo.msg.center.service.enums.RouterButtonSourceEnum; import cn.axzo.msg.center.service.enums.RouterCategoryEnum; @@ -14,6 +15,9 @@ import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; +import java.util.Collections; +import java.util.List; + /** * @author yanglin */ @@ -102,6 +106,17 @@ public class MessageTemplateButtonV3 extends BaseEntityWithOperator getStyles() { + if (style == null) return Collections.emptyList(); + return JSON.parseArray(style.toJSONString(), ButtonStyleEnum.class); + } + + @Override + public String key() { + return code; + } + @Setter @Getter public static class RecordExt { diff --git a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java index 926085de..29c0bc74 100644 --- a/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java +++ b/msg-center-domain/src/main/java/cn/axzo/msg/center/domain/entity/Todo.java @@ -182,20 +182,17 @@ public class Todo extends BaseEntityExt implements MessageEntity { return orgId; } - /** - * 执行待办是否已经完成(处理) - */ - public boolean isExecCompleted() { - return type == TodoType.EXECUTABLE && state == PendingMessageStateEnum.COMPLETED; - } - @Override public JSONObject bizParam() { - return bizExtParam == null ? new JSONObject() : bizExtParam; + if (bizExtParam == null) + bizExtParam = new JSONObject(); + return bizExtParam; } @Override public JSONObject routerParam() { - return routerParams == null ? new JSONObject() : routerParams; + if (routerParams == null) + routerParams = new JSONObject(); + return routerParams; } } \ No newline at end of file diff --git a/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/MnsLimiter.java b/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/MnsLimiter.java index a231b04c..4cfbc8bf 100644 --- a/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/MnsLimiter.java +++ b/msg-notices/msg-notices-service/src/main/java/cn/axzo/msg/center/notices/service/impl/MnsLimiter.java @@ -60,6 +60,7 @@ public class MnsLimiter { error = String.format("验证码短信: 每 %s 分钟发送给同一个手机号的重复内容不能超过 %s 条。 请勿重试发送!!", props.getVerifyCodeLimitWindowMinutes(), limitCount); } + error += " [" + request.getPhoneNo() + "]"; if (alreadySendCount + 1 > limitCount) { log.warn("mns rate limited, {}, request={}", error, request); throw new ServiceException(ReturnCodeEnum.FAIL.getCode(), error); diff --git a/pom.xml b/pom.xml index 32786ce7..1478aa15 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ 11.8 26.0.0 1.0.0-SNAPSHOT + 1.5.2-SNAPSHOT @@ -106,6 +107,11 @@ msg-center-api-v2 ${msg-center-api-v2-version} + + cn.axzo.workflow + workflow-engine-spring-boot-starter + ${workflow.version} + diff --git a/start/src/test/java/cn/axzo/msg/center/message/service/card/CardManagerTest.java b/start/src/test/java/cn/axzo/msg/center/message/service/card/CardManagerTest.java new file mode 100644 index 00000000..601f9dbb --- /dev/null +++ b/start/src/test/java/cn/axzo/msg/center/message/service/card/CardManagerTest.java @@ -0,0 +1,34 @@ +package cn.axzo.msg.center.message.service.card; + +import cn.axzo.msg.center.MsgCenterApplication; +import cn.axzo.msg.center.common.utils.BizAssertions; +import cn.axzo.msg.center.dal.CardDao; +import cn.axzo.msg.center.domain.entity.Card; +import cn.axzo.msg.center.message.domain.dto.TemplateModelV3; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Collections; + +/** + * @author yanglin + */ +@SpringBootTest(classes = MsgCenterApplication.class) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class CardManagerTest { + + private final CardManager cardManager; + private final CardSupport cardSupport; + private final CardDao cardDao; + + @Test + void rebuildCardContent() { + TemplateModelV3 templateModel = cardSupport.ensureImChannelPresent("8733f93de8db49699a78eda5a342763c"); + Card card = cardDao.findCardByBizMessageId("c0d696b2178f442f9488cfc74c518042").orElse(null); + BizAssertions.assertNotNull(card, "card not found"); + cardManager.rebuildCardContent(templateModel, Collections.singletonList(card)); + } + +} \ No newline at end of file diff --git a/start/src/test/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardServiceTest.java b/start/src/test/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardServiceTest.java new file mode 100644 index 00000000..5c4db1a4 --- /dev/null +++ b/start/src/test/java/cn/axzo/msg/center/message/service/todo/card/TodoSyncCardServiceTest.java @@ -0,0 +1,26 @@ +package cn.axzo.msg.center.message.service.todo.card; + +import cn.axzo.msg.center.MsgCenterApplication; +import cn.axzo.msg.center.api.mq.CardPresetButtonPressedMessage; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +/** + * @author yanglin + */ +@SpringBootTest(classes = MsgCenterApplication.class) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +class TodoSyncCardServiceTest { + + private final TodoSyncCardService todoSyncCardService; + + @Test + void syncCardPresetButtonPressed() { + String json = "{\"cardInfo\": {\"appCode\": \"msg-center:todo\", \"batchNo\": \"fd301a68203a430cbc6e6928c0164fe5\", \"bizCode\": \"test-14\", \"bizMessageId\": \"2e1119e4a96345049870558d53939700\", \"bizParam\": {\"assistLogName\": \"测试\", \"extInfo\": {\"todoType\": \"EXECUTABLE\", \"todoCategory\": \"OTHER\", \"todoPromoterWorkspaceId\": 0, \"todoIdentityCode\": \"80978834809443febcf579b0eecf560b\", \"todoExecutorWorkspaceId\": 0}}, \"buttonStates\": [{\"buttonCode\": \"f6a77000fc50487ab9be215201bf233a\", \"isActionPerformed\": true}], \"cardContent\": {\"bizCode\": \"test-14\", \"cardStyleCode\": \"common_style_001\", \"updateTime\": 1737025348622, \"cardContent\": \"系统提示,无需关注\", \"debugInfo\": {\"isUpdatable\": true, \"bizCode\": \"test-14\", \"bizState\": \"AGREED\", \"subBizCode\": \"aaaa\", \"cardState\": \"COMPLETED\"}, \"templateCode\": \"7aceab1f31ec4570a98cdaf7f8940c61\", \"sendTimestamp\": 1737025348622, \"cardTitle\": \"系统提示\", \"extInfo\": {\"todoType\": \"EXECUTABLE\", \"todoCategory\": \"OTHER\", \"todoPromoterWorkspaceId\": 0, \"todoIdentityCode\": \"80978834809443febcf579b0eecf560b\", \"todoExecutorWorkspaceId\": 0}, \"cardBannerUrl\": \"\", \"subBizCode\": \"aaaa\", \"stateImage\": \"https://axzo-public.oss-cn-chengdu.aliyuncs.com/msg-center/todo_card_state/card_biz_state_agreed_20241220.png\", \"cardButtons\": [{\"presetButtonType\": \"AGREE\", \"actionPerformed\": true, \"senderShow\": false, \"buttonCode\": \"f6a77000fc50487ab9be215201bf233a\", \"isHighlight\": true, \"action\": \"PRESET_BUTTON\", \"source\": \"CUSTOM\", \"title\": \"已同意\", \"executorShow\": true}]}, \"content\": \"系统提示,无需关注\", \"id\": 167756, \"identityCode\": \"726de22e936c4f3bb640693e5f2c0968\", \"imTaskId\": 756527, \"isSenderRobot\": \"YES\", \"receiverAppType\": \"CMP\", \"receiverOuId\": 10616, \"receiverPersonId\": 9000399522, \"receiverWorkspaceId\": 0, \"routerParam\": {}, \"senderAppType\": \"SYSTEM\", \"senderOuId\": 0, \"senderPersonId\": 6678911, \"senderWorkspaceId\": 0, \"state\": null, \"subBizCode\": \"aaaa\", \"subtitle\": \"\", \"templateCode\": \"7aceab1f31ec4570a98cdaf7f8940c61\", \"title\": \"系统提示\"}, \"messageId\": \"490ddb1e-5879-4088-bbda-7ca8f15bd4f2\", \"messageSendTime\": 1737025348697, \"messageSendTimeStr\": \"2025-01-16 19:02:28\", \"operatorId\": 9000399522, \"operatorName\": \"罗福\", \"presetButtonType\": \"AGREE\"}"; + CardPresetButtonPressedMessage message = JSON.parseObject(json, CardPresetButtonPressedMessage.class); + todoSyncCardService.syncCardPresetButtonPressed(message); + } + +} \ No newline at end of file