Merge branch 'feature/REQ-3502'
This commit is contained in:
commit
40754a7bd0
@ -21,6 +21,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.axzo.workflow</groupId>
|
||||
<artifactId>workflow-engine-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.axzo.org</groupId>
|
||||
<artifactId>org-api</artifactId>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -302,15 +302,9 @@ public class PendingMessageNewController implements PendingMessageClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public CommonResponse<Boolean> 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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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<String, Set<PresetButtonType>> collect = this.getButtons().stream()
|
||||
|
||||
@ -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<String, Object> debugInfo;
|
||||
|
||||
private Map<String, Object> 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时, 消费这个字段
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<CardUpdateStateRequest> requestContext = CardRequestContext.create(request);
|
||||
for (List<Card> 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<CardUpdatePresetButtonRequest> requestContext = CardRequestContext.create(request);
|
||||
for (List<Card> cards : cardsCursor(request))
|
||||
firePresetButtonPressedImpl(request, requestContext, templateModel, cards);
|
||||
boolean updated = false;
|
||||
for (List<Card> 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<Card> cards) {
|
||||
void rebuildCardContent(TemplateModelV3 templateModel, List<Card> cards) {
|
||||
ArrayList<Card> 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<Card> cards;
|
||||
@ -310,6 +311,7 @@ public class CardManager {
|
||||
UpdateExecutor(CardRequestContext<?> requestContext, String operation,
|
||||
TemplateModelV3 templateModel, List<Card> cards) {
|
||||
this.updateCardLogger = cardLoggers.createLogger(requestContext);
|
||||
this.requestContext = requestContext;
|
||||
this.templateModel = templateModel;
|
||||
this.operation = operation;
|
||||
this.cards = cards;
|
||||
|
||||
@ -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<CardButtonInterceptorFactory[]> 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<ButtonStyleEnum> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String> idempotentFreeTemplateCodes = new HashSet<>();
|
||||
|
||||
boolean isIdempotentFree(String templateCode) {
|
||||
|
||||
@ -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<String> minVersion = Ref.create("");
|
||||
Ref<String> 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) {
|
||||
|
||||
@ -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<Card> cards, CardPresetButtonRequest request) {
|
||||
public void firePresetButtonPressed(List<Card> 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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Boolean> isPerformActionAvailable(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
default DecisionValue<Boolean> isActionPerformed(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
default DecisionValue<String> getActionPerformedName(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
default DecisionValue<Boolean> isVisibleOnCard(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
default DecisionValue<Boolean> isSenderShow(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
default DecisionValue<Boolean> isExecutorShow(ButtonV3 button) {
|
||||
return DecisionValue.notSure();
|
||||
}
|
||||
|
||||
CardButtonInterceptor NOT_SURE = new CardButtonInterceptor() {};
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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<AppLink> links = new ArrayList<>();
|
||||
private final Set<TerminalTypeEnum> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
|
||||
@ -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<Todo> 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<Todo> 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<Todo> todoList) {
|
||||
this.cardUpdateStateByTodoList(todoList,CardBizState.END, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片更新状态-回滚
|
||||
* 暂时不需要
|
||||
*/
|
||||
public void cardRollbackStateByTodoList(List<Todo> todoList) {
|
||||
this.cardUpdateStateByTodoList(todoList, CardBizState.ABORTED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片更新状态-撤销
|
||||
*/
|
||||
public void cardRevokeStateByTodoList(List<Todo> todoList) {
|
||||
this.cardUpdateStateByTodoList(todoList, CardBizState.REVOKED, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片更新状态-执行中
|
||||
*/
|
||||
public void cardProcessingStateByTodoList(List<Todo> todoList) {
|
||||
this.cardUpdateStateByTodoList(todoList, CardBizState.PENDING, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片更新完成状态
|
||||
*/
|
||||
private void cardUpdateStateByTodoList(List<Todo> 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<String> codes = todoList.stream().map(Todo::getTemplateCode).collect(Collectors.toSet());
|
||||
List<TemplateModelV3> byCodes = modelV3Service.getByCodes(new ArrayList<>(codes));
|
||||
Map<String, TemplateModelV3> 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<PeerPerson> buildReceivers(List<PersonDTO> 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<TodoUpdateMessage> 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<TodoUpdateMessage> 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<BizCategoryEnum> 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<String> 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());
|
||||
}
|
||||
}
|
||||
@ -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<String> SYSTEM_BUTTONS_SENDER_SHOW = Sets.newHashSet(
|
||||
BizFinalStateEnum.RETRACT.getButtonCode());
|
||||
private final static Set<String> SYSTEM_BUTTONS_EXECUTOR_SHOW = Sets.newHashSet(
|
||||
BizFinalStateEnum.PASSED.getButtonCode(),
|
||||
BizFinalStateEnum.REJECTED.getButtonCode());
|
||||
|
||||
private final Todo todo;
|
||||
private final BpmnTaskButtonVo taskInfo;
|
||||
|
||||
@Override
|
||||
public DecisionValue<Boolean> isPerformActionAvailable(ButtonV3 button) {
|
||||
BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null);
|
||||
return DecisionValue.decide(btnBizState != null && btnBizState.isButtonPerformActionAvailable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionValue<Boolean> 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<String> getActionPerformedName(ButtonV3 button) {
|
||||
BizFinalStateEnum btnBizState = findButtonClickState(button).orElse(null);
|
||||
return btnBizState == null
|
||||
? DecisionValue.notSure()
|
||||
: DecisionValue.decide(btnBizState.getActionPerformedName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionValue<Boolean> 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<Boolean> 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<Boolean> 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<ButtonVisibleScopeEnum> scopes = workflowButton.getVisibleScopes();
|
||||
return DecisionValue.decide(scopes != null
|
||||
&& scopes.contains(ButtonVisibleScopeEnum.EXECUTOR)
|
||||
&& !scopes.contains(ButtonVisibleScopeEnum.INITIATOR));
|
||||
}
|
||||
|
||||
private Optional<BizFinalStateEnum> findButtonClickState(ButtonV3 button) {
|
||||
if (button.getSource() == RouterButtonSourceEnum.CUSTOM)
|
||||
return Optional.empty();
|
||||
return BizFinalStateEnum.findButtonClickedState(button.getCode());
|
||||
}
|
||||
|
||||
private Optional<BpmnButtonMetaInfoWithVisibleScope> findWorkflowButton(ButtonV3 button) {
|
||||
List<BpmnButtonMetaInfoWithVisibleScope> workflowButtons = this.taskInfo.getButtons();
|
||||
if (workflowButtons == null)
|
||||
workflowButtons = Collections.emptyList();
|
||||
return workflowButtons.stream()
|
||||
.filter(workflowButton -> workflowButton.getBtnKey().equals(button.getCode()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private Set<String> getWorkflowHideButtonKeys() {
|
||||
List<BpmnButtonMetaInfo> workflowHiddenButtons = taskInfo.getCustomHiddenButtons();
|
||||
if (workflowHiddenButtons == null)
|
||||
workflowHiddenButtons = Collections.emptyList();
|
||||
return workflowHiddenButtons.stream()
|
||||
.map(BpmnButtonMetaInfo::getBtnKey)
|
||||
.collect(toSet());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Todo> todos) {
|
||||
if (CollectionUtils.isEmpty(todos))
|
||||
return;
|
||||
if (!cfg.isEnableTodoLog()) return;
|
||||
if (CollectionUtils.isEmpty(todos)) return;
|
||||
TodoBusinesses businesses = todoBusinessDao.getBusinesses(todos);
|
||||
ArrayList<TodoLog> logs = new ArrayList<>();
|
||||
for (Todo todo : todos) {
|
||||
List<String> 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)
|
||||
|
||||
@ -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<PushPendingMessageDTO> 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<TodoHandoverMapping> 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<Long> 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();
|
||||
|
||||
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<Todo> 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<Todo> 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<Todo> 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<String, Object> 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<Todo> updatedTodos = todoDao.listByIds(updatedTodoIds);
|
||||
todoBroadcaster.fireTodoUpdates(ctx.getName(), updatedTodos);
|
||||
todoBroadcaster.fireTodoUpdates(ctx.getName(), updatedTodos, ctx.isUpdateCard());
|
||||
}
|
||||
};
|
||||
if (!ctx.isDelayBroadcast())
|
||||
|
||||
@ -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<String, Object> 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))
|
||||
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;
|
||||
|
||||
@ -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<Todo> todos = todoDao.getByBusinessIds(Collections.singletonList(businessId));
|
||||
fireTodoUpdates(operation, todos, updateCard);
|
||||
}
|
||||
|
||||
public void fireTodoUpdates(String operation, List<Todo> todos) {
|
||||
public void fireTodoUpdates(String operation, Todo todo, boolean updateCard) {
|
||||
fireTodoUpdates(operation, Collections.singletonList(todo), updateCard);
|
||||
}
|
||||
|
||||
public void fireTodoUpdates(String operation, List<Todo> todos, boolean updateCard) {
|
||||
if (CollectionUtils.isEmpty(todos))
|
||||
return;
|
||||
List<Long> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Todo> todos) {
|
||||
public void fireTodoUpdated(String operation, List<Todo> 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());
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<ConsumerIsolation> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<MessageExt>, 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);
|
||||
}
|
||||
@ -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<MessageExt> {
|
||||
|
||||
@Autowired
|
||||
private EventConsumer eventConsumer;
|
||||
|
||||
@Override
|
||||
public void onMessage(MessageExt message) {
|
||||
log.info("CardChangeStateSyncTodoListener onMessage,message:{}", JSON.toJSONString(message));
|
||||
super.onEvent(message, eventConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package cn.axzo.msg.center.utils.desision;
|
||||
|
||||
/**
|
||||
* @author yanglin
|
||||
*/
|
||||
public enum Decision {
|
||||
NOT_SURE, DECIDED
|
||||
}
|
||||
@ -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<T> {
|
||||
|
||||
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 <T> DecisionValue<T> notSure() {
|
||||
return (DecisionValue<T>) NOT_SURE;
|
||||
}
|
||||
|
||||
public static <T> DecisionValue<T> decide(T value) {
|
||||
return new DecisionValue<>(Decision.DECIDED, value);
|
||||
}
|
||||
|
||||
public T orElse(Supplier<T> supplier) {
|
||||
return isDecided() ? value : supplier.get();
|
||||
}
|
||||
|
||||
public T orElse(T value) {
|
||||
return isDecided() ? this.value : value;
|
||||
}
|
||||
|
||||
public boolean isDecided() {
|
||||
return decision == Decision.DECIDED;
|
||||
}
|
||||
|
||||
}
|
||||
@ -148,4 +148,9 @@ public class CardInfo {
|
||||
*/
|
||||
private String subtitle;
|
||||
|
||||
public JSONObject determineBizParam() {
|
||||
if (bizParam == null)
|
||||
bizParam = new JSONObject();
|
||||
return bizParam;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,4 +30,5 @@ public class CardPresetButtonPressedMessage extends MqMessage implements Seriali
|
||||
*/
|
||||
private CardInfo cardInfo;
|
||||
|
||||
private String batchNo;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<ButtonStyleEnum> getStyles();
|
||||
|
||||
default boolean determineIsPendingShow() {
|
||||
return getPendingShow() != null && getPendingShow();
|
||||
}
|
||||
|
||||
default boolean determineIsExecutorShow() {
|
||||
return getExecutorShow() != null && getExecutorShow();
|
||||
}
|
||||
|
||||
default boolean isPerformActionAvailable() {
|
||||
return determineIsPendingShow() || getCategory() == RouterCategoryEnum.PRESET_BUTTON;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -13,10 +13,15 @@ import lombok.Getter;
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public enum BizCategoryEnum {
|
||||
public enum BizCategoryEnum implements CodeDefinition<String> {
|
||||
|
||||
FLOW("流程"), OTHER("其它"),
|
||||
;
|
||||
|
||||
private final String desc;
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<String> {
|
||||
/**
|
||||
* 已处理
|
||||
*/
|
||||
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<BizFinalStateEnum> 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);
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ public enum CardBizState implements CodeDefinition<String> {
|
||||
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")
|
||||
|
||||
;
|
||||
|
||||
|
||||
@ -11,15 +11,14 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public enum CardState implements CodeDefinition<String> {
|
||||
|
||||
CREATED("待处理"),
|
||||
SEND_SUCCESS("发送成功"),
|
||||
COMPLETED("已完成"),
|
||||
CREATED("未发送"),
|
||||
SEND_SUCCESS("已发送"),
|
||||
COMPLETED("已处理"),
|
||||
|
||||
;
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return name();
|
||||
|
||||
@ -19,11 +19,9 @@ import java.util.Objects;
|
||||
public enum PendingMessageStateEnum implements CodeDefinition<String> {
|
||||
|
||||
CREATED(0, "创建"),
|
||||
UNSENT(1, "未发送"),
|
||||
HAS_BEEN_SENT(2, "代办"),
|
||||
COMPLETED(5, "已办"),
|
||||
RETRACT(6, "已撤回"),
|
||||
DELETED(7, "已删除"),
|
||||
READ(8, "已读"),
|
||||
PROCESSING(9, "处理中")
|
||||
;
|
||||
|
||||
@ -11,6 +11,10 @@ import java.util.List;
|
||||
*/
|
||||
public interface CardContent {
|
||||
|
||||
String getBizCode();
|
||||
|
||||
String getSubBizCode();
|
||||
|
||||
String getTemplateCode();
|
||||
|
||||
CardStateInfo getStateInfo();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<ButtonRouterDTO> getButtonRouters();
|
||||
|
||||
ProposedButtons getProposedButtons();
|
||||
|
||||
}
|
||||
@ -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<ParsedButtonV3> buttons = new ArrayList<>(model.determineButtons());
|
||||
buttons.sort(Comparator.comparingInt(ParsedButtonV3::determinePriority));
|
||||
for (ParsedButtonV3 button : buttons)
|
||||
visitButton(visitor, button);
|
||||
if (template != null)
|
||||
visitor.exitTemplate(template);
|
||||
|
||||
@ -103,13 +103,8 @@ public class ParsedButtonV3 implements MessageButton, ButtonV3 {
|
||||
return priority == null ? Integer.MAX_VALUE : priority;
|
||||
}
|
||||
|
||||
public List<ButtonStyleEnum> 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<ButtonStyleEnum> getStyles() {
|
||||
if (style == null) return Collections.emptyList();
|
||||
return JSON.parseArray(style.toJSONString(), ButtonStyleEnum.class);
|
||||
}
|
||||
}
|
||||
@ -45,4 +45,8 @@ public class IdBuilder {
|
||||
return buf.stream().map(String::valueOf).collect(joining(":"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return build();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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<TodoMapper, Todo> {
|
||||
.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<Todo> 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();
|
||||
}
|
||||
}
|
||||
@ -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<MessageTempl
|
||||
return recordExt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ButtonStyleEnum> 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 {
|
||||
|
||||
@ -182,20 +182,17 @@ public class Todo extends BaseEntityExt<Todo> 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
6
pom.xml
6
pom.xml
@ -42,6 +42,7 @@
|
||||
<feign-httpclient.version>11.8</feign-httpclient.version>
|
||||
<jetbrains.version>26.0.0</jetbrains.version>
|
||||
<epic.version>1.0.0-SNAPSHOT</epic.version>
|
||||
<workflow.version>1.5.2-SNAPSHOT</workflow.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -106,6 +107,11 @@
|
||||
<artifactId>msg-center-api-v2</artifactId>
|
||||
<version>${msg-center-api-v2-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.axzo.workflow</groupId>
|
||||
<artifactId>workflow-engine-spring-boot-starter</artifactId>
|
||||
<version>${workflow.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user