From 1beb92804e8353b5bb343feb311042276dc975f4 Mon Sep 17 00:00:00 2001 From: 3y Date: Tue, 12 Apr 2022 17:44:52 +0800 Subject: [PATCH 01/22] update AccountUtils comment --- .../java/com/java3y/austin/support/utils/AccountUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java index 2d3792f..66afb12 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java @@ -24,7 +24,8 @@ public class AccountUtils { * (key:smsAccount)短信参数示例:[{"sms_10":{"url":"sms.tencentcloudapi.com","region":"ap-guangzhou","secretId":"AKIDhDUUDfffffMEqBF1WljQq","secretKey":"B4h39yWnfffff7D2btue7JErDJ8gxyi","smsSdkAppId":"140025","templateId":"11897","signName":"Java3y公众号","supplierId":10,"supplierName":"腾讯云"}}] * (key:emailAccount)邮件参数示例:[{"email_10":{"host":"smtp.qq.com","port":465,"user":"403686131@qq.com","pass":"","from":"403686131@qq.com"}}] * (key:enterpriseWechatAccount)企业微信参数示例:[{"enterprise_wechat_10":{"corpId":"wwf87603333e00069c","corpSecret":"-IFWxS2222QxzPIorNVUQn144444D915DM","agentId":10044442,"token":"rXROB3333Kf6i","aesKey":"MKZtoFxHIM44444M7ieag3r9ZPUsl"}}] - * (key:dingDingRobotAccount) 钉钉自定义机器人参数实例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] + * (key:dingDingRobotAccount) 钉钉自定义机器人参数示例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] + * (key:dingDingWorkNoticeAccount) 钉钉工作消息参数示例:[{"ding_ding_work_notice_10":{"appKey":"dingh6yyyyyyycrlbx","appSecret":"tQpvmkR863333yyyyyHP3QHyyyymy9Ao1yoL1oQX5Nlx_fYLLLlpPJWHvWKbTu","agentId":"152333383622"}}] */ public T getAccount(Integer sendAccount, String apolloKey, String prefix, T t) { String accountValues = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY); From 9420e1391915bb60c27d6534b3bd4d632ac34ab0 Mon Sep 17 00:00:00 2001 From: 3y Date: Wed, 13 Apr 2022 19:52:23 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E8=B4=A3=E4=BB=BB=E9=93=BE=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=A3=8E=E6=A0=BC=20=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java3y/austin/handler/pending/Task.java | 5 +- .../impl/action/AfterParamCheckAction.java | 2 + .../api/impl/action/AssembleAction.java | 2 + .../api/impl/action/PreParamCheckAction.java | 2 + .../service/api/impl/action/SendMqAction.java | 2 + .../api/impl/config/PipelineConfig.java | 65 ++++--------------- 6 files changed, 24 insertions(+), 54 deletions(-) diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/pending/Task.java b/austin-handler/src/main/java/com/java3y/austin/handler/pending/Task.java index 9190040..eb5f1af 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/pending/Task.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/pending/Task.java @@ -18,8 +18,9 @@ import org.springframework.stereotype.Component; /** * Task 执行器 * 0.丢弃消息 - * 1.通用去重功能 - * 2.发送消息 + * 2.屏蔽消息 + * 2.通用去重功能 + * 3.发送消息 * * @author 3y */ diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AfterParamCheckAction.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AfterParamCheckAction.java index 39216ba..8c97488 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AfterParamCheckAction.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AfterParamCheckAction.java @@ -13,6 +13,7 @@ import com.java3y.austin.service.api.impl.domain.SendTaskModel; import com.java3y.austin.support.pipeline.BusinessProcess; import com.java3y.austin.support.pipeline.ProcessContext; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; import java.util.Iterator; import java.util.List; @@ -25,6 +26,7 @@ import java.util.stream.Collectors; * 后置参数检查 */ @Slf4j +@Service public class AfterParamCheckAction implements BusinessProcess { diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java index 7114bc4..815e838 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java @@ -21,6 +21,7 @@ import com.java3y.austin.support.utils.ContentHolderUtil; import com.java3y.austin.support.utils.TaskInfoUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import java.lang.reflect.Field; import java.util.*; @@ -31,6 +32,7 @@ import java.util.*; * @description 拼装参数 */ @Slf4j +@Service public class AssembleAction implements BusinessProcess { @Autowired diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/PreParamCheckAction.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/PreParamCheckAction.java index 794e1d9..2c1d152 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/PreParamCheckAction.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/PreParamCheckAction.java @@ -9,6 +9,7 @@ import com.java3y.austin.service.api.impl.domain.SendTaskModel; import com.java3y.austin.support.pipeline.BusinessProcess; import com.java3y.austin.support.pipeline.ProcessContext; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @@ -19,6 +20,7 @@ import java.util.stream.Collectors; * @description 前置参数校验 */ @Slf4j +@Service public class PreParamCheckAction implements BusinessProcess { @Override diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/SendMqAction.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/SendMqAction.java index 8bb27df..8a4ad69 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/SendMqAction.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/SendMqAction.java @@ -13,12 +13,14 @@ import com.java3y.austin.support.utils.KafkaUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; /** * @author 3y * 将消息发送到MQ */ @Slf4j +@Service public class SendMqAction implements BusinessProcess { @Autowired diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/config/PipelineConfig.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/config/PipelineConfig.java index 5639735..662888b 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/config/PipelineConfig.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/config/PipelineConfig.java @@ -6,15 +6,15 @@ import com.java3y.austin.service.api.impl.action.AfterParamCheckAction; import com.java3y.austin.service.api.impl.action.AssembleAction; import com.java3y.austin.service.api.impl.action.PreParamCheckAction; import com.java3y.austin.service.api.impl.action.SendMqAction; +import com.java3y.austin.service.api.impl.domain.SendTaskModel; import com.java3y.austin.support.pipeline.BusinessProcess; import com.java3y.austin.support.pipeline.ProcessController; import com.java3y.austin.support.pipeline.ProcessTemplate; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * api层的pipeline配置类 @@ -23,6 +23,14 @@ import java.util.Map; @Configuration public class PipelineConfig { + @Autowired + private PreParamCheckAction preParamCheckAction; + @Autowired + private AssembleAction assembleAction; + @Autowired + private AfterParamCheckAction afterParamCheckAction; + @Autowired + private SendMqAction sendMqAction; /** * 普通发送执行流程 @@ -35,14 +43,8 @@ public class PipelineConfig { @Bean("commonSendTemplate") public ProcessTemplate commonSendTemplate() { ProcessTemplate processTemplate = new ProcessTemplate(); - ArrayList processList = new ArrayList<>(); - - processList.add(preParamCheckAction()); - processList.add(assembleAction()); - processList.add(afterParamCheckAction()); - processList.add(sendMqAction()); - - processTemplate.setProcessList(processList); + processTemplate.setProcessList(Arrays.asList(preParamCheckAction, assembleAction, + afterParamCheckAction, sendMqAction)); return processTemplate; } @@ -62,45 +64,4 @@ public class PipelineConfig { return processController; } - - /** - * 组装参数Action - * - * @return - */ - @Bean - public AssembleAction assembleAction() { - return new AssembleAction(); - } - - /** - * 前置参数校验Action - * - * @return - */ - @Bean - public PreParamCheckAction preParamCheckAction() { - return new PreParamCheckAction(); - } - - /** - * 后置参数校验Action - * - * @return - */ - @Bean - public AfterParamCheckAction afterParamCheckAction() { - return new AfterParamCheckAction(); - } - - /** - * 发送消息至MQ的Action - * - * @return - */ - @Bean - public SendMqAction sendMqAction() { - return new SendMqAction(); - } - } From 80886c2decdbd9a278719436ae9ddb57a95bff66 Mon Sep 17 00:00:00 2001 From: zcl <9928619+zhangcl1991@user.noreply.gitee.com> Date: Mon, 18 Apr 2022 08:53:20 +0000 Subject: [PATCH 03/22] =?UTF-8?q?update=20austin-handler/src/main/java/com?= =?UTF-8?q?/java3y/austin/handler/shield/impl/ShieldServiceImpl.java.=20?= =?UTF-8?q?=E5=B1=8F=E8=94=BD=E6=97=B6=E5=B0=8F=E6=97=B6=E6=95=B0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java3y/austin/handler/shield/impl/ShieldServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/shield/impl/ShieldServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/shield/impl/ShieldServiceImpl.java index 475fd1b..402b0ac 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/shield/impl/ShieldServiceImpl.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/shield/impl/ShieldServiceImpl.java @@ -11,10 +11,10 @@ import com.java3y.austin.handler.shield.ShieldService; import com.java3y.austin.support.utils.LogUtils; import com.java3y.austin.support.utils.RedisUtils; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.Date; import java.util.HashSet; @@ -60,7 +60,7 @@ public class ShieldServiceImpl implements ShieldService { * @return */ private boolean isNight() { - return Integer.valueOf(DateFormatUtils.format(new Date(), "HH")) < 8; + return LocalDateTime.now().getHour() < 8; } From e8deae88f2cbbfc533cc46688bf9f184c6692d36 Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 18 Apr 2022 19:25:21 +0800 Subject: [PATCH 04/22] =?UTF-8?q?=E5=8D=95=E6=9C=BA=E9=99=90=E6=B5=81?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeduplicationParam.java | 2 +- .../DeduplicationRuleService.java | 1 - .../build/AbstractDeduplicationBuilder.java | 2 +- .../handler/deduplication/build/Builder.java | 2 +- .../build/ContentDeduplicationBuilder.java | 2 +- .../build/FrequencyDeduplicationBuilder.java | 2 +- .../service/AbstractDeduplicationService.java | 4 +- .../service/DeduplicationService.java | 2 +- .../handler/enums/RateLimitStrategy.java | 26 +++++++ .../handler/flowcontrol/FlowControlParam.java | 39 ++++++++++ .../flowcontrol/FlowControlService.java | 20 +++++ .../impl/FlowControlServiceImpl.java | 77 +++++++++++++++++++ .../austin/handler/handler/BaseHandler.java | 29 ++++++- .../handler/handler/impl/EmailHandler.java | 10 +++ 14 files changed, 208 insertions(+), 10 deletions(-) rename austin-handler/src/main/java/com/java3y/austin/handler/{domain => deduplication}/DeduplicationParam.java (94%) create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlParam.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlService.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/impl/FlowControlServiceImpl.java diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/DeduplicationParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationParam.java similarity index 94% rename from austin-handler/src/main/java/com/java3y/austin/handler/domain/DeduplicationParam.java rename to austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationParam.java index e6c98c6..fe74595 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/domain/DeduplicationParam.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationParam.java @@ -1,4 +1,4 @@ -package com.java3y.austin.handler.domain; +package com.java3y.austin.handler.deduplication; import com.alibaba.fastjson.annotation.JSONField; import com.java3y.austin.common.domain.TaskInfo; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationRuleService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationRuleService.java index 0577827..24cb518 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationRuleService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/DeduplicationRuleService.java @@ -5,7 +5,6 @@ import com.ctrip.framework.apollo.spring.annotation.ApolloConfig; import com.java3y.austin.common.constant.AustinConstant; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.DeduplicationType; -import com.java3y.austin.handler.domain.DeduplicationParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/AbstractDeduplicationBuilder.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/AbstractDeduplicationBuilder.java index 722317f..f69c2a8 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/AbstractDeduplicationBuilder.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/AbstractDeduplicationBuilder.java @@ -3,7 +3,7 @@ package com.java3y.austin.handler.deduplication.build; import com.alibaba.fastjson.JSONObject; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.handler.deduplication.DeduplicationHolder; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/Builder.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/Builder.java index abe55e8..8631037 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/Builder.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/Builder.java @@ -1,7 +1,7 @@ package com.java3y.austin.handler.deduplication.build; import com.java3y.austin.common.domain.TaskInfo; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; /** * @author luohaojie diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/ContentDeduplicationBuilder.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/ContentDeduplicationBuilder.java index 11094a2..358c11e 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/ContentDeduplicationBuilder.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/ContentDeduplicationBuilder.java @@ -3,7 +3,7 @@ package com.java3y.austin.handler.deduplication.build; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.AnchorState; import com.java3y.austin.common.enums.DeduplicationType; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; import org.springframework.stereotype.Service; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/FrequencyDeduplicationBuilder.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/FrequencyDeduplicationBuilder.java index ae4603a..3d83962 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/FrequencyDeduplicationBuilder.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/build/FrequencyDeduplicationBuilder.java @@ -4,7 +4,7 @@ import cn.hutool.core.date.DateUtil; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.AnchorState; import com.java3y.austin.common.enums.DeduplicationType; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; import org.springframework.stereotype.Service; import java.util.Date; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java index 9c0bbc4..2dc1b6f 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java @@ -5,13 +5,14 @@ import com.java3y.austin.common.constant.AustinConstant; import com.java3y.austin.common.domain.AnchorInfo; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.handler.deduplication.DeduplicationHolder; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; import com.java3y.austin.support.utils.LogUtils; import com.java3y.austin.support.utils.RedisUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; +import java.time.LocalDateTime; import java.util.*; /** @@ -116,5 +117,4 @@ public abstract class AbstractDeduplicationService implements DeduplicationServi return result; } - } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/DeduplicationService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/DeduplicationService.java index 6eac046..a34a0b6 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/DeduplicationService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/DeduplicationService.java @@ -1,7 +1,7 @@ package com.java3y.austin.handler.deduplication.service; -import com.java3y.austin.handler.domain.DeduplicationParam; +import com.java3y.austin.handler.deduplication.DeduplicationParam; /** * @author huskey diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java b/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java new file mode 100644 index 0000000..0ada834 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java @@ -0,0 +1,26 @@ +package com.java3y.austin.handler.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +/** + * 限流枚举 + * + * @author 3y + */ +@Getter +@ToString +@AllArgsConstructor +public enum RateLimitStrategy { + + + REQUEST_RATE_LIMIT(10, "根据真实请求数限流"), + SEND_USER_NUM_RATE_LIMIT(20, "根据发送用户数请求数限流"), + ; + + private Integer code; + private String description; + + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlParam.java new file mode 100644 index 0000000..9dfa9af --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlParam.java @@ -0,0 +1,39 @@ +package com.java3y.austin.handler.flowcontrol; + +import com.google.common.util.concurrent.RateLimiter; +import com.java3y.austin.handler.enums.RateLimitStrategy; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author 3y + * @date 2022/4/18 + *

+ * 流量控制所需要的参数 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FlowControlParam { + + /** + * 限流器 + * 子类初始化的时候指定 + */ + protected RateLimiter rateLimiter; + + /** + * 限流器初始限流大小 + * 子类初始化的时候指定 + */ + protected Double rateInitValue; + + /** + * 限流的策略 + * 子类初始化的时候指定 + */ + protected RateLimitStrategy rateLimitStrategy; +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlService.java b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlService.java new file mode 100644 index 0000000..8986c6a --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/FlowControlService.java @@ -0,0 +1,20 @@ +package com.java3y.austin.handler.flowcontrol; + +import com.java3y.austin.common.domain.TaskInfo; + +/** + * @author 3y + * 流量控制服务 + */ +public interface FlowControlService { + + + /** + * 根据渠道进行流量控制 + * + * @param taskInfo + * @param flowControlParam + */ + void flowControl(TaskInfo taskInfo, FlowControlParam flowControlParam); + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/impl/FlowControlServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/impl/FlowControlServiceImpl.java new file mode 100644 index 0000000..5713a2c --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/flowcontrol/impl/FlowControlServiceImpl.java @@ -0,0 +1,77 @@ +package com.java3y.austin.handler.flowcontrol.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ctrip.framework.apollo.Config; +import com.ctrip.framework.apollo.spring.annotation.ApolloConfig; +import com.google.common.util.concurrent.RateLimiter; +import com.java3y.austin.common.constant.AustinConstant; +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.handler.enums.RateLimitStrategy; +import com.java3y.austin.handler.flowcontrol.FlowControlParam; +import com.java3y.austin.handler.flowcontrol.FlowControlService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @author 3y + * @date 2022/4/18 + */ +@Service +@Slf4j +public class FlowControlServiceImpl implements FlowControlService { + + private static final String FLOW_CONTROL_KEY = "flowControl"; + + private static final String FLOW_CONTROL_PREFIX = "flow_control_"; + + @ApolloConfig("boss.austin") + private Config config; + + + @Override + public void flowControl(TaskInfo taskInfo, FlowControlParam flowControlParam) { + RateLimiter rateLimiter = flowControlParam.getRateLimiter(); + Double rateInitValue = flowControlParam.getRateInitValue(); + + double costTime = 0; + + // 对比 初始限流值 与 配置限流值,以 配置中心的限流值为准 + Double rateLimitConfig = getRateLimitConfig(taskInfo.getSendChannel()); + if (rateLimitConfig != null && !rateInitValue.equals(rateLimitConfig)) { + rateLimiter = RateLimiter.create(rateLimitConfig); + flowControlParam.setRateInitValue(rateLimitConfig); + flowControlParam.setRateLimiter(rateLimiter); + } + if (RateLimitStrategy.REQUEST_RATE_LIMIT.equals(flowControlParam.getRateLimitStrategy())) { + costTime = rateLimiter.acquire(1); + } + if (RateLimitStrategy.SEND_USER_NUM_RATE_LIMIT.equals(flowControlParam.getRateLimitStrategy())) { + costTime = rateLimiter.acquire(taskInfo.getReceiver().size()); + } + + if (costTime > 0) { + log.info("consumer {} flow control time {}", + ChannelType.getEnumByCode(taskInfo.getSendChannel()).getDescription(), costTime); + } + } + + /** + * 得到限流值的配置 + *

+ * apollo配置样例 key:flowControl value:{"flow_control_40":1} + *

+ * 渠道枚举可看:com.java3y.austin.common.enums.ChannelType + * + * @param channelCode + */ + private Double getRateLimitConfig(Integer channelCode) { + String flowControlConfig = config.getProperty(FLOW_CONTROL_KEY, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_OBJECT); + JSONObject jsonObject = JSON.parseObject(flowControlConfig); + if (jsonObject.getDouble(FLOW_CONTROL_PREFIX + channelCode) == null) { + return null; + } + return jsonObject.getDouble(FLOW_CONTROL_PREFIX + channelCode); + } +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java index ec16778..8c21086 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java @@ -1,8 +1,12 @@ package com.java3y.austin.handler.handler; +import com.google.common.util.concurrent.RateLimiter; import com.java3y.austin.common.domain.AnchorInfo; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.AnchorState; +import com.java3y.austin.handler.enums.RateLimitStrategy; +import com.java3y.austin.handler.flowcontrol.FlowControlParam; +import com.java3y.austin.handler.flowcontrol.FlowControlService; import com.java3y.austin.support.utils.LogUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -20,12 +24,19 @@ public abstract class BaseHandler implements Handler { */ protected Integer channelCode; + /** + * 限流相关的参数 + * 子类初始化的时候指定 + */ + protected FlowControlParam flowControlParam; + @Autowired private HandlerHolder handlerHolder; @Autowired private LogUtils logUtils; - + @Autowired + private FlowControlService flowControlService; /** * 初始化渠道与Handler的映射关系 @@ -37,6 +48,7 @@ public abstract class BaseHandler implements Handler { @Override public void doHandler(TaskInfo taskInfo) { + flowControl(taskInfo); if (handler(taskInfo)) { logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_SUCCESS.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); return; @@ -44,6 +56,19 @@ public abstract class BaseHandler implements Handler { logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_FAIL.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); } + + /** + * 流量控制 + * + * @param taskInfo + */ + public void flowControl(TaskInfo taskInfo) { + // 只有子类指定了限流参数,才需要限流 + if (flowControlParam != null) { + flowControlService.flowControl(taskInfo, flowControlParam); + } + } + /** * 统一处理的handler接口 * @@ -52,4 +77,6 @@ public abstract class BaseHandler implements Handler { */ public abstract boolean handler(TaskInfo taskInfo); + + } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java index 64e71ee..4099515 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java @@ -4,10 +4,13 @@ package com.java3y.austin.handler.handler.impl; import cn.hutool.extra.mail.MailAccount; import cn.hutool.extra.mail.MailUtil; import com.google.common.base.Throwables; +import com.google.common.util.concurrent.RateLimiter; import com.java3y.austin.common.constant.SendAccountConstant; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.dto.model.EmailContentModel; import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.handler.enums.RateLimitStrategy; +import com.java3y.austin.handler.flowcontrol.FlowControlParam; import com.java3y.austin.handler.handler.BaseHandler; import com.java3y.austin.handler.handler.Handler; import com.java3y.austin.support.utils.AccountUtils; @@ -30,6 +33,13 @@ public class EmailHandler extends BaseHandler implements Handler { public EmailHandler() { channelCode = ChannelType.EMAIL.getCode(); + + // 按照请求限流,默认单机 3 qps (具体数值配置在apollo动态调整) + Double rateInitValue = Double.valueOf(3); + flowControlParam = FlowControlParam.builder().rateInitValue(rateInitValue) + .rateLimitStrategy(RateLimitStrategy.REQUEST_RATE_LIMIT) + .rateLimiter(RateLimiter.create(rateInitValue)).build(); + } @Override From dd21cfe8aed7b4ca6e5886c5cc1b46796544628b Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 18 Apr 2022 19:29:25 +0800 Subject: [PATCH 05/22] UPDATE README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9b7a3bf..9a9dbf9 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,8 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [x] 企业微信渠道接入 - [x] 夜间屏蔽次日早晨推送(xxl-job定时任务框架,另类的延时队列) - [x] 钉钉渠道接入 -- [ ] 编写单测 +- [x] 单机限流实现 +- [ ] 引入单测框架,编写部分单测用例 - [ ] 持续提高消息推送系统的影响力,让更多的业务方了解其功能,进而挖掘更多拉新和唤醒用户的玩法,提高站内的次留率和转化率 - [ ] 优化代码 - [ ] 接入微信服务号渠道 @@ -131,9 +132,9 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [ ] 接入工作流引擎实现对消息工单审核 -**近期更新时间**:2022年3月30日 +**近期更新时间**:2022年4月18日 -**近期更新功能**:钉钉群自定义机器人与工作消息渠道接入完成 +**近期更新功能**:单机限流实现 ## 项目交流 From e83ef17ba082687743164ed086483c5d764ebec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=BE=99=E8=A2=96?= <377901853@qq.com> Date: Wed, 20 Apr 2022 18:30:57 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?=E6=BB=91=E5=8A=A8=E7=AA=97=E5=8F=A3=E7=9A=84=E9=99=90=E6=B5=81?= =?UTF-8?q?=E5=8E=BB=E9=87=8D=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../limit/AbstractLimitService.java | 36 ++++++ .../deduplication/limit/LimitService.java | 24 ++++ .../limit/SimpleLimitService.java | 76 ++++++++++++ .../limit/SlideWindowLimitService.java | 68 +++++++++++ .../service/AbstractDeduplicationService.java | 68 +---------- .../service/ContentDeduplicationService.java | 8 +- .../FrequencyDeduplicationService.java | 13 ++- .../austin/support/utils/RedisUtils.java | 35 +++++- .../support/utils/SnowFlakeIdUtils.java | 108 ++++++++++++++++++ austin-web/src/main/resources/limit.lua | 17 +++ 10 files changed, 385 insertions(+), 68 deletions(-) create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/AbstractLimitService.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java create mode 100644 austin-support/src/main/java/com/java3y/austin/support/utils/SnowFlakeIdUtils.java create mode 100644 austin-web/src/main/resources/limit.lua diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/AbstractLimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/AbstractLimitService.java new file mode 100644 index 0000000..432022e --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/AbstractLimitService.java @@ -0,0 +1,36 @@ +package com.java3y.austin.handler.deduplication.limit; + +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.handler.deduplication.service.AbstractDeduplicationService; + +import java.util.*; + +/** + * @author cao + * @date 2022-04-20 12:00 + */ +public abstract class AbstractLimitService implements LimitService { + + + /** + * 获取得到当前消息模板所有的去重Key + * + * @param taskInfo + * @return + */ + protected List deduplicationAllKey(AbstractDeduplicationService service, TaskInfo taskInfo) { + List result = new ArrayList<>(taskInfo.getReceiver().size()); + for (String receiver : taskInfo.getReceiver()) { + String key = deduplicationSingleKey(service, taskInfo, receiver); + result.add(key); + } + return result; + } + + + protected String deduplicationSingleKey(AbstractDeduplicationService service, TaskInfo taskInfo, String receiver) { + + return service.deduplicationSingleKey(taskInfo, receiver); + + } +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java new file mode 100644 index 0000000..9f519f7 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java @@ -0,0 +1,24 @@ +package com.java3y.austin.handler.deduplication.limit; + +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.handler.deduplication.DeduplicationParam; +import com.java3y.austin.handler.deduplication.service.AbstractDeduplicationService; + +import java.util.Set; + +/** + * @author cao + * @date 2022-04-20 11:58 + */ +public interface LimitService { + + + /** + * @param service 去重器对象 + * @param taskInfo + * @param param 去重参数 + * @return 返回不符合条件的手机号码 + */ + Set limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param); + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java new file mode 100644 index 0000000..eaaa992 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java @@ -0,0 +1,76 @@ +package com.java3y.austin.handler.deduplication.limit; + +import cn.hutool.core.collection.CollUtil; +import com.java3y.austin.common.constant.AustinConstant; +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.handler.deduplication.DeduplicationParam; +import com.java3y.austin.handler.deduplication.service.AbstractDeduplicationService; +import com.java3y.austin.support.utils.RedisUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author cao + * @date 2022-04-20 13:41 + */ +@Service(value = "SimpleLimitService") +public class SimpleLimitService extends AbstractLimitService { + + private static final String LIMIT_TAG = "SP_"; + + @Autowired + private RedisUtils redisUtils; + + + public Set limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param) { + Set filterReceiver = new HashSet<>(taskInfo.getReceiver().size()); + // 获取redis记录 + Map readyPutRedisReceiver = new HashMap(taskInfo.getReceiver().size()); + //redis数据隔离 + List keys = deduplicationAllKey(service, taskInfo).stream().map(key -> LIMIT_TAG + key).collect(Collectors.toList()); + Map inRedisValue = redisUtils.mGet(keys); + + for (String receiver : taskInfo.getReceiver()) { + String key = LIMIT_TAG + deduplicationSingleKey(service, taskInfo, receiver); + String value = inRedisValue.get(key); + + // 符合条件的用户 + if (value != null && Integer.parseInt(value) >= param.getCountNum()) { + filterReceiver.add(receiver); + } else { + readyPutRedisReceiver.put(receiver, key); + } + } + + // 不符合条件的用户:需要更新Redis(无记录添加,有记录则累加次数) + putInRedis(readyPutRedisReceiver, inRedisValue, param.getDeduplicationTime()); + + return filterReceiver; + } + + + /** + * 存入redis 实现去重 + * + * @param readyPutRedisReceiver + */ + private void putInRedis(Map readyPutRedisReceiver, + Map inRedisValue, Long deduplicationTime) { + Map keyValues = new HashMap<>(readyPutRedisReceiver.size()); + for (Map.Entry entry : readyPutRedisReceiver.entrySet()) { + String key = entry.getValue(); + if (inRedisValue.get(key) != null) { + keyValues.put(key, String.valueOf(Integer.valueOf(inRedisValue.get(key)) + 1)); + } else { + keyValues.put(key, String.valueOf(AustinConstant.TRUE)); + } + } + if (CollUtil.isNotEmpty(keyValues)) { + redisUtils.pipelineSetEx(keyValues, deduplicationTime); + } + } + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java new file mode 100644 index 0000000..c8240fd --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java @@ -0,0 +1,68 @@ +package com.java3y.austin.handler.deduplication.limit; + +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.handler.deduplication.DeduplicationParam; +import com.java3y.austin.handler.deduplication.service.AbstractDeduplicationService; +import com.java3y.austin.support.utils.RedisUtils; +import com.java3y.austin.support.utils.SnowFlakeIdUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.scripting.support.ResourceScriptSource; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author cao + * @date 2022-04-20 11:34 + */ +@Service(value = "SlideWindowLimitService") +public class SlideWindowLimitService extends AbstractLimitService { + + private static final String LIMIT_TAG = "SW_"; + + @Autowired + private RedisUtils redisUtils; + + private SnowFlakeIdUtils snowFlakeIdUtils = new SnowFlakeIdUtils(1, 1); + + private DefaultRedisScript redisScript; + + + @PostConstruct + public void init() { + redisScript = new DefaultRedisScript(); + redisScript.setResultType(Long.class); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua"))); + } + + + /** + * @param service 去重器对象 + * @param taskInfo + * @param param 去重参数 + * @return 返回不符合条件的手机号码 + */ + @Override + public Set limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param) { + + Set filterReceiver = new HashSet<>(taskInfo.getReceiver().size()); + long nowTime = System.currentTimeMillis(); + for (String receiver : taskInfo.getReceiver()) { + String key = LIMIT_TAG + deduplicationSingleKey(service, taskInfo, receiver); + String scoreValue = String.valueOf(snowFlakeIdUtils.nextId()); + String score = String.valueOf(nowTime); + if (redisUtils.execLimitLua(redisScript, Arrays.asList(key), String.valueOf(param.getDeduplicationTime() * 1000), score, String.valueOf(param.getCountNum()), scoreValue)) { + filterReceiver.add(receiver); + } + + } + return filterReceiver; + } + + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java index 2dc1b6f..5937374 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/AbstractDeduplicationService.java @@ -1,18 +1,16 @@ package com.java3y.austin.handler.deduplication.service; import cn.hutool.core.collection.CollUtil; -import com.java3y.austin.common.constant.AustinConstant; import com.java3y.austin.common.domain.AnchorInfo; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.handler.deduplication.DeduplicationHolder; import com.java3y.austin.handler.deduplication.DeduplicationParam; +import com.java3y.austin.handler.deduplication.limit.LimitService; import com.java3y.austin.support.utils.LogUtils; -import com.java3y.austin.support.utils.RedisUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; -import java.time.LocalDateTime; import java.util.*; /** @@ -22,8 +20,11 @@ import java.util.*; */ @Slf4j public abstract class AbstractDeduplicationService implements DeduplicationService { + protected Integer deduplicationType; + protected LimitService limitService; + @Autowired private DeduplicationHolder deduplicationHolder; @@ -32,8 +33,6 @@ public abstract class AbstractDeduplicationService implements DeduplicationServi deduplicationHolder.putService(deduplicationType, this); } - @Autowired - private RedisUtils redisUtils; @Autowired private LogUtils logUtils; @@ -41,27 +40,8 @@ public abstract class AbstractDeduplicationService implements DeduplicationServi @Override public void deduplication(DeduplicationParam param) { TaskInfo taskInfo = param.getTaskInfo(); - Set filterReceiver = new HashSet<>(taskInfo.getReceiver().size()); - - // 获取redis记录 - Set readyPutRedisReceiver = new HashSet<>(taskInfo.getReceiver().size()); - List keys = deduplicationAllKey(taskInfo); - Map inRedisValue = redisUtils.mGet(keys); - - for (String receiver : taskInfo.getReceiver()) { - String key = deduplicationSingleKey(taskInfo, receiver); - String value = inRedisValue.get(key); - - // 符合条件的用户 - if (value != null && Integer.parseInt(value) >= param.getCountNum()) { - filterReceiver.add(receiver); - } else { - readyPutRedisReceiver.add(receiver); - } - } - // 不符合条件的用户:需要更新Redis(无记录添加,有记录则累加次数) - putInRedis(readyPutRedisReceiver, inRedisValue, param); + Set filterReceiver = limitService.limitFilter(this, taskInfo, param); // 剔除符合去重条件的用户 if (CollUtil.isNotEmpty(filterReceiver)) { @@ -78,43 +58,7 @@ public abstract class AbstractDeduplicationService implements DeduplicationServi * @param receiver * @return */ - protected abstract String deduplicationSingleKey(TaskInfo taskInfo, String receiver); - + public abstract String deduplicationSingleKey(TaskInfo taskInfo, String receiver); - /** - * 存入redis 实现去重 - * - * @param readyPutRedisReceiver - */ - private void putInRedis(Set readyPutRedisReceiver, - Map inRedisValue, DeduplicationParam param) { - Map keyValues = new HashMap<>(readyPutRedisReceiver.size()); - for (String receiver : readyPutRedisReceiver) { - String key = deduplicationSingleKey(param.getTaskInfo(), receiver); - if (inRedisValue.get(key) != null) { - keyValues.put(key, String.valueOf(Integer.valueOf(inRedisValue.get(key)) + 1)); - } else { - keyValues.put(key, String.valueOf(AustinConstant.TRUE)); - } - } - if (CollUtil.isNotEmpty(keyValues)) { - redisUtils.pipelineSetEx(keyValues, param.getDeduplicationTime()); - } - } - - /** - * 获取得到当前消息模板所有的去重Key - * - * @param taskInfo - * @return - */ - private List deduplicationAllKey(TaskInfo taskInfo) { - List result = new ArrayList<>(taskInfo.getReceiver().size()); - for (String receiver : taskInfo.getReceiver()) { - String key = deduplicationSingleKey(taskInfo, receiver); - result.add(key); - } - return result; - } } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/ContentDeduplicationService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/ContentDeduplicationService.java index ca14bb7..6daaddd 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/ContentDeduplicationService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/ContentDeduplicationService.java @@ -4,6 +4,9 @@ import cn.hutool.crypto.digest.DigestUtil; import com.alibaba.fastjson.JSON; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.DeduplicationType; +import com.java3y.austin.handler.deduplication.limit.LimitService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; /** @@ -14,7 +17,10 @@ import org.springframework.stereotype.Service; @Service public class ContentDeduplicationService extends AbstractDeduplicationService { - public ContentDeduplicationService() { + + @Autowired + public ContentDeduplicationService(@Qualifier("SlideWindowLimitService") LimitService limitService) { + this.limitService = limitService; deduplicationType = DeduplicationType.CONTENT.getCode(); } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/FrequencyDeduplicationService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/FrequencyDeduplicationService.java index 102a93b..9091955 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/FrequencyDeduplicationService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/service/FrequencyDeduplicationService.java @@ -3,8 +3,12 @@ package com.java3y.austin.handler.deduplication.service; import cn.hutool.core.util.StrUtil; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.enums.DeduplicationType; +import com.java3y.austin.handler.deduplication.limit.LimitService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; + /** * @author 3y * @date 2021/12/12 @@ -13,8 +17,13 @@ import org.springframework.stereotype.Service; @Service public class FrequencyDeduplicationService extends AbstractDeduplicationService { - public FrequencyDeduplicationService() { + + @Autowired + public FrequencyDeduplicationService(@Qualifier("SimpleLimitService") LimitService limitService) { + + this.limitService = limitService; deduplicationType = DeduplicationType.FREQUENCY.getCode(); + } private static final String PREFIX = "FRE"; @@ -33,7 +42,7 @@ public class FrequencyDeduplicationService extends AbstractDeduplicationService @Override public String deduplicationSingleKey(TaskInfo taskInfo, String receiver) { return PREFIX + StrUtil.C_UNDERLINE - + receiver + StrUtil.C_UNDERLINE + + receiver + StrUtil.C_UNDERLINE + taskInfo.getMessageTemplateId() + StrUtil.C_UNDERLINE + taskInfo.getSendChannel(); } diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/RedisUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/RedisUtils.java index 9d7f905..38d23b4 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/RedisUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/RedisUtils.java @@ -2,10 +2,12 @@ package com.java3y.austin.support.utils; import cn.hutool.core.collection.CollUtil; import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.AustinConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -93,7 +95,6 @@ public class RedisUtils { /** * lpush 方法 并指定 过期时间 - * */ public void lPush(String key, String value, Long seconds) { try { @@ -109,7 +110,6 @@ public class RedisUtils { /** * lLen 方法 - * */ public Long lLen(String key) { try { @@ -119,9 +119,9 @@ public class RedisUtils { } return 0L; } + /** * lPop 方法 - * */ public String lPop(String key) { try { @@ -151,4 +151,33 @@ public class RedisUtils { log.error("redis pipelineSetEX fail! e:{}", Throwables.getStackTraceAsString(e)); } } + + /** + * 执行指定的lua脚本返回执行结果 + * --KEYS[1]: 限流 key + * --ARGV[1]: 限流窗口 + * --ARGV[2]: 当前时间戳(作为score) + * --ARGV[3]: 阈值 + * --ARGV[4]: score 对应的唯一value + * + * @param redisScript + * @param keys + * @param args + * @return + */ + public Boolean execLimitLua(RedisScript redisScript, List keys, String... args) { + + try { + Long execute = redisTemplate.execute(redisScript, keys, args); + + return AustinConstant.TRUE.equals(execute.intValue()); + } catch (Exception e) { + + log.error("redis execLimitLua fail! e:{}", Throwables.getStackTraceAsString(e)); + } + + return false; + } + + } diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/SnowFlakeIdUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/SnowFlakeIdUtils.java new file mode 100644 index 0000000..925ea8b --- /dev/null +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/SnowFlakeIdUtils.java @@ -0,0 +1,108 @@ +package com.java3y.austin.support.utils; + +/** + * 雪花算法生成唯一id工具类 + * + * @author cao + * @date 2022-04-20 13:12 + */ +public class SnowFlakeIdUtils { + + /** + * 初始时间截 (2017-01-01) + */ + private final static long START_TIMESTAMP = 1483200000000L; + + /** + * 每一部分占用的位数 + */ + private final static long SEQUENCE_BIT = 12; //***占用的位数 + private final static long MACHINE_BIT = 5; //机器标识占用的位数 + private final static long DATA_CENTER_BIT = 5; //数据中心占用的位数 + + /** + * 每一部分的最大值 + */ + private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); + private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); + private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); + + /** + * 每一部分向左的位移 + */ + private final static long MACHINE_LEFT = SEQUENCE_BIT; + private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; + private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; + + private long dataCenterId; //数据中心 + private long machineId; //机器标识 + private long sequence = 0L; //*** + private long lastTimeStamp = -1L; //上一次时间戳 + + + /** + * 根据指定的数据中心ID和机器标志ID生成指定的*** + * + * @param dataCenterId 数据中心ID + * @param machineId 机器标志ID + */ + public SnowFlakeIdUtils(long dataCenterId, long machineId) { + if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) { + throw new IllegalArgumentException(String.format("DtaCenterId 不能大于 %d 或小于 0", MAX_DATA_CENTER_NUM)); + } + if (machineId > MAX_MACHINE_NUM || machineId < 0) { + throw new IllegalArgumentException(String.format("MachineId 不能大于 %d 或小于 0", MAX_MACHINE_NUM)); + } + this.dataCenterId = dataCenterId; + this.machineId = machineId; + } + + + /** + * 产生下一个ID + * + * @return + */ + public synchronized long nextId() { + long currTimeStamp = System.currentTimeMillis(); + if (currTimeStamp < lastTimeStamp) { + throw new RuntimeException("当前时间小于上一次记录的时间戳!"); + } + + if (currTimeStamp == lastTimeStamp) { + //相同毫秒内,***自增 + sequence = (sequence + 1) & MAX_SEQUENCE; + //同一毫秒的序列数已经达到最大 + if (sequence == 0L) { + currTimeStamp = getNextMill(); + } + } else { + //不同毫秒内,***置为0 + sequence = 0L; + } + + lastTimeStamp = currTimeStamp; + + return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //时间戳部分 + | dataCenterId << DATA_CENTER_LEFT //数据中心部分 + | machineId << MACHINE_LEFT //机器标识部分 + | sequence; //***部分 + } + + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @return 当前时间戳 + */ + private long getNextMill() { + + long mill = System.currentTimeMillis(); + while (mill <= lastTimeStamp) { + mill = System.currentTimeMillis(); + } + return mill; + + } + +} diff --git a/austin-web/src/main/resources/limit.lua b/austin-web/src/main/resources/limit.lua new file mode 100644 index 0000000..48b91ec --- /dev/null +++ b/austin-web/src/main/resources/limit.lua @@ -0,0 +1,17 @@ +--KEYS[1]: 限流 key +--ARGV[1]: 限流窗口,毫秒 +--ARGV[2]: 当前时间戳(作为score) +--ARGV[3]: 阈值 +--ARGV[4]: score 对应的唯一value +-- 1\. 移除开始时间窗口之前的数据 +redis.call('zremrangeByScore', KEYS[1], 0, ARGV[2]-ARGV[1]) +-- 2\. 统计当前元素数量 +local res = redis.call('zcard', KEYS[1]) +-- 3\. 是否超过阈值 +if (res == nil) or (res < tonumber(ARGV[3])) then + redis.call('zadd', KEYS[1], ARGV[2], ARGV[4]) + redis.call('expire', KEYS[1], ARGV[1]/1000) + return 0 +else + return 1 +end From 33cab31829a876ac3ad486640b2f435573ac5796 Mon Sep 17 00:00:00 2001 From: 3y Date: Wed, 20 Apr 2022 19:45:50 +0800 Subject: [PATCH 07/22] UPDATE comment --- .../handler/enums/RateLimitStrategy.java | 2 +- .../austin/handler/handler/BaseHandler.java | 37 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java b/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java index 0ada834..80ac3f1 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/enums/RateLimitStrategy.java @@ -16,7 +16,7 @@ public enum RateLimitStrategy { REQUEST_RATE_LIMIT(10, "根据真实请求数限流"), - SEND_USER_NUM_RATE_LIMIT(20, "根据发送用户数请求数限流"), + SEND_USER_NUM_RATE_LIMIT(20, "根据发送用户数限流"), ; private Integer code; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java index 8c21086..236d1f8 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/BaseHandler.java @@ -17,6 +17,12 @@ import javax.annotation.PostConstruct; * 发送各个渠道的handler */ public abstract class BaseHandler implements Handler { + @Autowired + private HandlerHolder handlerHolder; + @Autowired + private LogUtils logUtils; + @Autowired + private FlowControlService flowControlService; /** * 标识渠道的Code @@ -30,14 +36,6 @@ public abstract class BaseHandler implements Handler { */ protected FlowControlParam flowControlParam; - - @Autowired - private HandlerHolder handlerHolder; - @Autowired - private LogUtils logUtils; - @Autowired - private FlowControlService flowControlService; - /** * 初始化渠道与Handler的映射关系 */ @@ -46,17 +44,6 @@ public abstract class BaseHandler implements Handler { handlerHolder.putHandler(channelCode, this); } - @Override - public void doHandler(TaskInfo taskInfo) { - flowControl(taskInfo); - if (handler(taskInfo)) { - logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_SUCCESS.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); - return; - } - logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_FAIL.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); - } - - /** * 流量控制 * @@ -68,6 +55,18 @@ public abstract class BaseHandler implements Handler { flowControlService.flowControl(taskInfo, flowControlParam); } } + @Override + public void doHandler(TaskInfo taskInfo) { + flowControl(taskInfo); + if (handler(taskInfo)) { + logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_SUCCESS.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); + return; + } + logUtils.print(AnchorInfo.builder().state(AnchorState.SEND_FAIL.getCode()).businessId(taskInfo.getBusinessId()).ids(taskInfo.getReceiver()).build()); + } + + + /** * 统一处理的handler接口 From 8b8fe99af2212bacf0b1dbd804a6b19c36130af3 Mon Sep 17 00:00:00 2001 From: 3y Date: Wed, 20 Apr 2022 21:20:28 +0800 Subject: [PATCH 08/22] add some comment . --- .../austin/handler/deduplication/limit/LimitService.java | 1 + .../austin/handler/deduplication/limit/SimpleLimitService.java | 3 ++- .../handler/deduplication/limit/SlideWindowLimitService.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java index 9f519f7..65d8b04 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/LimitService.java @@ -14,6 +14,7 @@ public interface LimitService { /** + * 去重限制 * @param service 去重器对象 * @param taskInfo * @param param 去重参数 diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java index eaaa992..2976805 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SimpleLimitService.java @@ -13,6 +13,7 @@ import java.util.*; import java.util.stream.Collectors; /** + * 简单去重器(目前承载着 N分钟相同内容去重) * @author cao * @date 2022-04-20 13:41 */ @@ -24,7 +25,7 @@ public class SimpleLimitService extends AbstractLimitService { @Autowired private RedisUtils redisUtils; - + @Override public Set limitFilter(AbstractDeduplicationService service, TaskInfo taskInfo, DeduplicationParam param) { Set filterReceiver = new HashSet<>(taskInfo.getReceiver().size()); // 获取redis记录 diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java index c8240fd..392462d 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/deduplication/limit/SlideWindowLimitService.java @@ -17,6 +17,7 @@ import java.util.HashSet; import java.util.Set; /** + * 滑动窗口去重器(目前承载着 一天内N次相同渠道去重) * @author cao * @date 2022-04-20 11:34 */ From 408708322edf8e57e1270eaf912464b284b5d7c4 Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 25 Apr 2022 20:34:43 +0800 Subject: [PATCH 09/22] =?UTF-8?q?=E5=BC=95=E5=85=A5=E5=8D=95=E4=BE=8B?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=EF=BC=8C=E7=BC=96=E5=86=99=E5=8D=95=E4=BE=8B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/impl/service/SendServiceImplTest.java | 95 +++++++++++++++++++ austin-support/pom.xml | 5 + 2 files changed, 100 insertions(+) create mode 100644 austin-service-api-impl/src/test/java/com/java3y/austin/service/api/impl/service/SendServiceImplTest.java diff --git a/austin-service-api-impl/src/test/java/com/java3y/austin/service/api/impl/service/SendServiceImplTest.java b/austin-service-api-impl/src/test/java/com/java3y/austin/service/api/impl/service/SendServiceImplTest.java new file mode 100644 index 0000000..6e8e5cd --- /dev/null +++ b/austin-service-api-impl/src/test/java/com/java3y/austin/service/api/impl/service/SendServiceImplTest.java @@ -0,0 +1,95 @@ +package com.java3y.austin.service.api.impl.service; + +import com.java3y.austin.common.enums.RespStatusEnum; +import com.java3y.austin.common.vo.BasicResultVO; +import com.java3y.austin.service.api.domain.BatchSendRequest; +import com.java3y.austin.service.api.domain.MessageParam; +import com.java3y.austin.service.api.domain.SendRequest; +import com.java3y.austin.service.api.domain.SendResponse; +import com.java3y.austin.service.api.enums.BusinessCode; +import com.java3y.austin.service.api.impl.domain.SendTaskModel; +import com.java3y.austin.support.pipeline.BusinessProcess; +import com.java3y.austin.support.pipeline.ProcessContext; +import com.java3y.austin.support.pipeline.ProcessController; +import com.java3y.austin.support.pipeline.ProcessTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SendServiceImplTest { + + @Spy + private ProcessController processController; + + @Mock + private Map templateConfig; + + @Spy + private ProcessTemplate processTemplate; + + @Mock + private BusinessProcess businessProcess; + + @InjectMocks + private SendServiceImpl sendServiceImplUnderTest; + + @Test + void testSend() { + + // params + final SendRequest sendRequest = new SendRequest("send", 1L, + new MessageParam("13711111111", new HashMap<>(), new HashMap<>())); + + // predict result + final ProcessContext processContext = new ProcessContext<>(sendRequest.getCode(), new SendTaskModel(), false, new BasicResultVO<>( + RespStatusEnum.SUCCESS, "data")); + final SendResponse expectedResult = new SendResponse(processContext.getResponse().getStatus(), processContext.getResponse().getMsg()); + + + // stub + Map templateConfig = new HashMap<>(4); + processTemplate.setProcessList(Arrays.asList(businessProcess)); + templateConfig.put(BusinessCode.COMMON_SEND.getCode(), processTemplate); + + processController.setTemplateConfig(templateConfig); + + + // Run the test + final SendResponse result = sendServiceImplUnderTest.send(sendRequest); + + // Verify the results + assertEquals(expectedResult, result); + } + + @Test + void testBatchSend() { + // Setup + final BatchSendRequest batchSendRequest = new BatchSendRequest("code", 0L, + Arrays.asList(new MessageParam("receiver", new HashMap<>(), new HashMap<>()))); + final SendResponse expectedResult = new SendResponse("status", "msg"); + + // Configure ProcessController.process(...). + final ProcessContext processContext = new ProcessContext<>("code", null, false, new BasicResultVO<>( + RespStatusEnum.SUCCESS, "data")); + when(processController.process(new ProcessContext<>("code", null, false, new BasicResultVO<>( + RespStatusEnum.SUCCESS, "data")))).thenReturn(processContext); + + // Run the test + final SendResponse result = sendServiceImplUnderTest.batchSend(batchSendRequest); + + // Verify the results + assertEquals(expectedResult, result); + } +} diff --git a/austin-support/pom.xml b/austin-support/pom.xml index 6a9b651..96ae2e0 100644 --- a/austin-support/pom.xml +++ b/austin-support/pom.xml @@ -22,6 +22,11 @@ org.springframework.boot spring-boot-starter + + org.springframework.boot + spring-boot-starter-test + + mysql mysql-connector-java From fb24531ae23948a7eb42be05132a6caa3753d329 Mon Sep 17 00:00:00 2001 From: dqbitbucket Date: Thu, 28 Apr 2022 15:26:07 +0800 Subject: [PATCH 10/22] =?UTF-8?q?=E4=BC=98=E5=8C=96getAccount=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E9=80=9A=E8=BF=87class=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cron/handler/RefreshDingDingAccessTokenHandler.java | 2 +- .../austin/handler/handler/impl/DingDingRobotHandler.java | 2 +- .../handler/handler/impl/DingDingWorkNoticeHandler.java | 2 +- .../java3y/austin/handler/handler/impl/EmailHandler.java | 2 +- .../handler/handler/impl/EnterpriseWeChatHandler.java | 2 +- .../java3y/austin/handler/script/impl/TencentSmsScript.java | 2 +- .../java/com/java3y/austin/support/utils/AccountUtils.java | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshDingDingAccessTokenHandler.java b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshDingDingAccessTokenHandler.java index f2ce549..25f378e 100644 --- a/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshDingDingAccessTokenHandler.java +++ b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshDingDingAccessTokenHandler.java @@ -46,7 +46,7 @@ public class RefreshDingDingAccessTokenHandler { log.info("refreshAccessTokenJob#execute!"); SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> { for (int index = SendAccountConstant.START; true; index = index + SendAccountConstant.STEP) { - DingDingWorkNoticeAccount account = accountUtils.getAccount(index, SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, new DingDingWorkNoticeAccount()); + DingDingWorkNoticeAccount account = accountUtils.getAccount(index, SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, DingDingWorkNoticeAccount.class); if (account == null) { break; } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingRobotHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingRobotHandler.java index 9a1ed77..bc4d702 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingRobotHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingRobotHandler.java @@ -45,7 +45,7 @@ public class DingDingRobotHandler extends BaseHandler implements Handler { @Override public boolean handler(TaskInfo taskInfo) { try { - DingDingRobotAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.DING_DING_ROBOT_ACCOUNT_KEY, SendAccountConstant.DING_DING_ROBOT_PREFIX, new DingDingRobotAccount()); + DingDingRobotAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.DING_DING_ROBOT_ACCOUNT_KEY, SendAccountConstant.DING_DING_ROBOT_PREFIX, DingDingRobotAccount.class); DingDingRobotParam dingDingRobotParam = assembleParam(taskInfo); String httpResult = HttpUtil.post(assembleParamUrl(account), JSON.toJSONString(dingDingRobotParam)); DingDingRobotResult dingDingRobotResult = JSON.parseObject(httpResult, DingDingRobotResult.class); diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingWorkNoticeHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingWorkNoticeHandler.java index f6211bc..3ddee90 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingWorkNoticeHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/DingDingWorkNoticeHandler.java @@ -50,7 +50,7 @@ public class DingDingWorkNoticeHandler extends BaseHandler implements Handler { @Override public boolean handler(TaskInfo taskInfo) { try { - DingDingWorkNoticeAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, new DingDingWorkNoticeAccount()); + DingDingWorkNoticeAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.DING_DING_WORK_NOTICE_ACCOUNT_KEY, SendAccountConstant.DING_DING_WORK_NOTICE_PREFIX, DingDingWorkNoticeAccount.class); OapiMessageCorpconversationAsyncsendV2Request request = assembleParam(account, taskInfo); String accessToken = redisTemplate.opsForValue().get(SendAccountConstant.DING_DING_ACCESS_TOKEN_PREFIX + taskInfo.getSendAccount()); OapiMessageCorpconversationAsyncsendV2Response response = new DefaultDingTalkClient(URL).execute(request, accessToken); diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java index 4099515..f6e8a25 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EmailHandler.java @@ -62,7 +62,7 @@ public class EmailHandler extends BaseHandler implements Handler { * @return */ private MailAccount getAccountConfig(Integer sendAccount) { - MailAccount account = accountUtils.getAccount(sendAccount, SendAccountConstant.EMAIL_ACCOUNT_KEY, SendAccountConstant.EMAIL_ACCOUNT_PREFIX, new MailAccount()); + MailAccount account = accountUtils.getAccount(sendAccount, SendAccountConstant.EMAIL_ACCOUNT_KEY, SendAccountConstant.EMAIL_ACCOUNT_PREFIX, MailAccount.class); try { MailSSLSocketFactory sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatHandler.java index bee72a6..c2e9bbf 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/EnterpriseWeChatHandler.java @@ -46,7 +46,7 @@ public class EnterpriseWeChatHandler extends BaseHandler implements Handler { @Override public boolean handler(TaskInfo taskInfo) { try { - WxCpDefaultConfigImpl accountConfig = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.ENTERPRISE_WECHAT_ACCOUNT_KEY, SendAccountConstant.ENTERPRISE_WECHAT_PREFIX, new WxCpDefaultConfigImpl()); + WxCpDefaultConfigImpl accountConfig = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.ENTERPRISE_WECHAT_ACCOUNT_KEY, SendAccountConstant.ENTERPRISE_WECHAT_PREFIX, WxCpDefaultConfigImpl.class); WxCpMessageServiceImpl messageService = new WxCpMessageServiceImpl(initService(accountConfig)); WxCpMessageSendResult result = messageService.send(buildWxCpMessage(taskInfo, accountConfig.getAgentId())); if (Integer.valueOf(WxMpErrorMsgEnum.CODE_0.getCode()).equals(result.getErrCode())) { diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/TencentSmsScript.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/TencentSmsScript.java index 1a1201e..f3bb434 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/TencentSmsScript.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/TencentSmsScript.java @@ -44,7 +44,7 @@ public class TencentSmsScript implements SmsScript { @Override public List send(SmsParam smsParam) throws Exception { - TencentSmsAccount tencentSmsAccount = accountUtils.getAccount(smsParam.getSendAccount(), SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.builder().build()); + TencentSmsAccount tencentSmsAccount = accountUtils.getAccount(smsParam.getSendAccount(), SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.class); SmsClient client = init(tencentSmsAccount); SendSmsRequest request = assembleReq(smsParam, tencentSmsAccount); SendSmsResponse response = client.SendSms(request); diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java index 66afb12..42310d0 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java @@ -27,14 +27,14 @@ public class AccountUtils { * (key:dingDingRobotAccount) 钉钉自定义机器人参数示例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] * (key:dingDingWorkNoticeAccount) 钉钉工作消息参数示例:[{"ding_ding_work_notice_10":{"appKey":"dingh6yyyyyyycrlbx","appSecret":"tQpvmkR863333yyyyyHP3QHyyyymy9Ao1yoL1oQX5Nlx_fYLLLlpPJWHvWKbTu","agentId":"152333383622"}}] */ - public T getAccount(Integer sendAccount, String apolloKey, String prefix, T t) { + public T getAccount(Integer sendAccount, String apolloKey, String prefix, Class clazz) { String accountValues = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY); JSONArray jsonArray = JSON.parseArray(accountValues); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); - Object object = jsonObject.getObject(prefix + sendAccount, t.getClass()); + T object = jsonObject.getObject(prefix + sendAccount, clazz); if (object != null) { - return (T) object; + return object; } } return null; From 754309b1aaae2a0644de5f128eb93cced3871f7e Mon Sep 17 00:00:00 2001 From: 3y Date: Sat, 30 Apr 2022 09:32:32 +0800 Subject: [PATCH 11/22] update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9a9dbf9..9037f58 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [x] 夜间屏蔽次日早晨推送(xxl-job定时任务框架,另类的延时队列) - [x] 钉钉渠道接入 - [x] 单机限流实现 -- [ ] 引入单测框架,编写部分单测用例 +- [x] 引入单测框架,编写部分单测用例 - [ ] 持续提高消息推送系统的影响力,让更多的业务方了解其功能,进而挖掘更多拉新和唤醒用户的玩法,提高站内的次留率和转化率 - [ ] 优化代码 - [ ] 接入微信服务号渠道 @@ -132,9 +132,9 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [ ] 接入工作流引擎实现对消息工单审核 -**近期更新时间**:2022年4月18日 +**近期更新时间**:4月30号 -**近期更新功能**:单机限流实现 +**近期更新功能**:引入单测框架,编写部分单测用例 ## 项目交流 From aa4cdcd315fdf1c8cd3b60ef752d992c82b78479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BC=BA=E6=9E=97?= <2949079311@qq.com> Date: Sat, 7 May 2022 16:26:33 +0800 Subject: [PATCH 12/22] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8DcontentModel?= =?UTF-8?q?=E5=BE=97=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B=E8=8B=A5=E4=B8=BA?= =?UTF-8?q?=E9=9D=9EString=E5=BE=97=E5=85=B6=E4=BB=96=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java3y/austin/service/api/impl/action/AssembleAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java index 815e838..b69592d 100644 --- a/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java +++ b/austin-service-api-impl/src/main/java/com/java3y/austin/service/api/impl/action/AssembleAction.java @@ -114,7 +114,8 @@ public class AssembleAction implements BusinessProcess { if (StrUtil.isNotBlank(originValue)) { String resultValue = ContentHolderUtil.replacePlaceHolder(originValue, variables); - ReflectUtil.setFieldValue(contentModel, field, resultValue); + Object resultObj = JSON.parseObject(resultValue, field.getType()); + ReflectUtil.setFieldValue(contentModel, field, resultObj); } } From 874c1a66e65a6b2c71749bacda72aa6b27f82c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BC=BA=E6=9E=97?= <2949079311@qq.com> Date: Sat, 7 May 2022 17:57:12 +0800 Subject: [PATCH 13/22] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=8F=B7=E5=8F=91=E9=80=81=E6=A8=A1=E6=9D=BF=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/SendAccountConstant.java | 5 ++ ...ccount.java => WeChatOfficialAccount.java} | 18 ++--- .../domain/wechat/WeChatOfficialParam.java | 37 +++++++++++ .../handler/impl/OfficialAccountHandler.java | 58 +++------------- .../script/OfficialAccountService.java | 5 +- .../impl/OfficialAccountServiceImpl.java | 66 ++++++++++++++----- .../austin/support/utils/AccountUtils.java | 1 + 7 files changed, 110 insertions(+), 80 deletions(-) rename austin-common/src/main/java/com/java3y/austin/common/dto/account/{WechatOfficialAccount.java => WeChatOfficialAccount.java} (74%) create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatOfficialParam.java diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java index f5aa376..76c8bcc 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java @@ -39,6 +39,11 @@ public class SendAccountConstant { public static final String ENTERPRISE_WECHAT_ACCOUNT_KEY = "enterpriseWechatAccount"; public static final String ENTERPRISE_WECHAT_PREFIX = "enterprise_wechat_"; + /** + * 微信服务号 应用消息 账号 + */ + public static final String WECHAT_OFFICIAL_ACCOUNT_KEY = "officialAccount"; + public static final String WECHAT_OFFICIAL__PREFIX = "official_"; /** * 短信 账号 diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/account/WechatOfficialAccount.java b/austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatOfficialAccount.java similarity index 74% rename from austin-common/src/main/java/com/java3y/austin/common/dto/account/WechatOfficialAccount.java rename to austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatOfficialAccount.java index 1dfecc0..3f1a1c6 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/dto/account/WechatOfficialAccount.java +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatOfficialAccount.java @@ -19,17 +19,7 @@ import java.util.Map; @Builder @AllArgsConstructor @NoArgsConstructor -public class WechatOfficialAccount { - - /** - * 服务号关注者的openId - */ - private String openId; - - /** - * 需要使用的模板信息Id - */ - private String templateId; +public class WeChatOfficialAccount { /** * 模板消息跳转的url @@ -47,7 +37,9 @@ public class WechatOfficialAccount { private String path; /** - * 模板消息的信息载体 + * 账号相关 */ - private Map map; + private String appId; + private String secret; + private String templateId; } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatOfficialParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatOfficialParam.java new file mode 100644 index 0000000..ecd134c --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatOfficialParam.java @@ -0,0 +1,37 @@ +package com.java3y.austin.handler.domain.wechat; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; +import java.util.Set; + +/** + * @author sunql + * @date 2022年05月06日 9:56 + * + * 服务号参数 + */ +@Data +@Builder +public class WeChatOfficialParam { + /** + * 业务Id + */ + private Long messageTemplateId; + + /** + * 关注服务号得用户 + */ + private Set openIds; + + /** + * 模板消息的信息载体 + */ + private Map data; + + /** + * 发送账号 + */ + private Integer sendAccount; +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/OfficialAccountHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/OfficialAccountHandler.java index a1f274b..7e6707b 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/OfficialAccountHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/OfficialAccountHandler.java @@ -5,19 +5,15 @@ import com.google.common.base.Throwables; import com.java3y.austin.common.domain.TaskInfo; import com.java3y.austin.common.dto.model.OfficialAccountsContentModel; import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam; import com.java3y.austin.handler.handler.BaseHandler; import com.java3y.austin.handler.handler.Handler; import com.java3y.austin.handler.script.OfficialAccountService; import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; -import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Set; /** * @author zyg @@ -30,18 +26,24 @@ public class OfficialAccountHandler extends BaseHandler implements Handler { @Autowired private OfficialAccountService officialAccountService; - public OfficialAccountHandler() { channelCode = ChannelType.OFFICIAL_ACCOUNT.getCode(); } @Override public boolean handler(TaskInfo taskInfo) { + // 构建微信模板消息 + OfficialAccountsContentModel contentModel = (OfficialAccountsContentModel) taskInfo.getContentModel(); + WeChatOfficialParam officialParam = WeChatOfficialParam.builder() + .openIds(taskInfo.getReceiver()) + .messageTemplateId(taskInfo.getMessageTemplateId()) + .sendAccount(taskInfo.getSendAccount()) + .data(contentModel.getMap()) + .build(); - List mpTemplateMessages = buildTemplateMsg(taskInfo); // 微信模板消息需要记录响应结果 try { - List messageIds = officialAccountService.send(mpTemplateMessages); + List messageIds = officialAccountService.send(officialParam); log.info("OfficialAccountHandler#handler successfully messageIds:{}", messageIds); return true; } catch (Exception e) { @@ -51,45 +53,5 @@ public class OfficialAccountHandler extends BaseHandler implements Handler { return false; } - /** - * 通过taskInfo构建微信模板消息 - * - * @param taskInfo - * @return - */ - private List buildTemplateMsg(TaskInfo taskInfo) { - // 需是关注公众号的用户的OpenId - Set receiver = taskInfo.getReceiver(); - Long messageTemplateId = taskInfo.getMessageTemplateId(); - // 微信模板消息可以关联到系统业务,通过接口查询。 - String templateId = getRealWxMpTemplateId(messageTemplateId); - List wxMpTemplateMessages = new ArrayList<>(receiver.size()); - OfficialAccountsContentModel contentModel = (OfficialAccountsContentModel) taskInfo.getContentModel(); - String url = contentModel.getUrl(); - Map param = contentModel.getMap(); - - // 构建微信模板消息 - for (String openId : receiver) { - WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() - .toUser(openId) - .templateId(templateId) - .url(url) - .build(); - // WxMpTemplateData 对应模板消息 键 -- 值 -- color - param.forEach((k, v) -> templateMessage.addData(new WxMpTemplateData(k, v))); - wxMpTemplateMessages.add(templateMessage); - } - return wxMpTemplateMessages; - } - - /** - * 根据模板id获取真实的模板id - * - * @param messageTemplateId 系统业务模板id - * @return - */ - private String getRealWxMpTemplateId(Long messageTemplateId) { - return String.valueOf(messageTemplateId); - } } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/OfficialAccountService.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/OfficialAccountService.java index 868e76f..8451869 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/OfficialAccountService.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/OfficialAccountService.java @@ -1,5 +1,6 @@ package com.java3y.austin.handler.script; +import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; import java.util.List; @@ -12,10 +13,10 @@ public interface OfficialAccountService { /** * 发送模板消息 * - * @param wxMpTemplateMessages 模板消息列表 + * @param weChatOfficialParam 模板消息参数 * @return * @throws Exception */ - List send(List wxMpTemplateMessages) throws Exception; + List send(WeChatOfficialParam weChatOfficialParam) throws Exception; } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java index fe69c4b..6615be6 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java @@ -1,16 +1,23 @@ package com.java3y.austin.handler.script.impl; +import com.java3y.austin.common.constant.SendAccountConstant; +import com.java3y.austin.common.dto.account.WeChatOfficialAccount; +import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam; import com.java3y.austin.handler.script.OfficialAccountService; +import com.java3y.austin.support.utils.AccountUtils; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author zyg @@ -19,19 +26,14 @@ import java.util.List; @Slf4j public class OfficialAccountServiceImpl implements OfficialAccountService { - @Value("${wx.mp.account.appid}") - private String appId; - @Value("${wx.mp.account.secret}") - private String secret; - @Value("${wx.mp.account.token}") - private String token; - @Value("${wx.mp.account.aesKey}") - private String aesKey; - + @Autowired + private AccountUtils accountUtils; @Override - public List send(List messages) throws Exception { - WxMpService wxMpService = initService(); + public List send(WeChatOfficialParam officialParam) throws Exception { + WeChatOfficialAccount officialAccount = accountUtils.getAccount(officialParam.getSendAccount(), SendAccountConstant.WECHAT_OFFICIAL_ACCOUNT_KEY, SendAccountConstant.WECHAT_OFFICIAL__PREFIX, WeChatOfficialAccount.builder().build()); + WxMpService wxMpService = initService(officialAccount); + List messages = assembleReq(officialParam, officialAccount); List messageIds = new ArrayList<>(messages.size()); for (WxMpTemplateMessage wxMpTemplateMessage : messages) { String msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(wxMpTemplateMessage); @@ -40,18 +42,48 @@ public class OfficialAccountServiceImpl implements OfficialAccountService { return messageIds; } + /** + * 组装发送模板信息参数 + */ + private List assembleReq(WeChatOfficialParam officialParam, WeChatOfficialAccount officialAccount) { + Set receiver = officialParam.getOpenIds(); + List wxMpTemplateMessages = new ArrayList<>(receiver.size()); + + // 构建微信模板消息 + for (String openId : receiver) { + WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() + .toUser(openId) + .templateId(officialAccount.getTemplateId()) + .url(officialAccount.getUrl()) + .data(getWxMpTemplateData(officialParam.getData())) + .miniProgram(new WxMpTemplateMessage.MiniProgram(officialAccount.getMiniProgramId(), officialAccount.getPath(), false)) + .build(); + wxMpTemplateMessages.add(templateMessage); + } + return wxMpTemplateMessages; + } + + /** + * 构建模板消息参数 + * + * @return + */ + private List getWxMpTemplateData(Map data) { + List templateDataList = new ArrayList<>(data.size()); + data.forEach((k, v) -> templateDataList.add(new WxMpTemplateData(k, v))); + return templateDataList; + } + /** * 初始化微信服务号 * * @return */ - public WxMpService initService() { + public WxMpService initService(WeChatOfficialAccount officialAccount) { WxMpService wxMpService = new WxMpServiceImpl(); WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); - config.setAppId(appId); - config.setSecret(secret); - config.setToken(token); - config.setAesKey(aesKey); + config.setAppId(officialAccount.getAppId()); + config.setSecret(officialAccount.getSecret()); wxMpService.setWxMpConfigStorage(config); return wxMpService; } diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java index 66afb12..77a4fc8 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java @@ -26,6 +26,7 @@ public class AccountUtils { * (key:enterpriseWechatAccount)企业微信参数示例:[{"enterprise_wechat_10":{"corpId":"wwf87603333e00069c","corpSecret":"-IFWxS2222QxzPIorNVUQn144444D915DM","agentId":10044442,"token":"rXROB3333Kf6i","aesKey":"MKZtoFxHIM44444M7ieag3r9ZPUsl"}}] * (key:dingDingRobotAccount) 钉钉自定义机器人参数示例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] * (key:dingDingWorkNoticeAccount) 钉钉工作消息参数示例:[{"ding_ding_work_notice_10":{"appKey":"dingh6yyyyyyycrlbx","appSecret":"tQpvmkR863333yyyyyHP3QHyyyymy9Ao1yoL1oQX5Nlx_fYLLLlpPJWHvWKbTu","agentId":"152333383622"}}] + * (key:officialAccount) 微信服务号模板消息参数示例:[{"official_10":{"appId":"wxecb4693d2eef1ea7","secret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","url":"http://weixin.qq.com/download","miniProgramId":"xiaochengxuappid12345","path":"index?foo=bar"}}] */ public T getAccount(Integer sendAccount, String apolloKey, String prefix, T t) { String accountValues = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY); From 4dd3b84ad68e4992d2f6ff6615eda44510d7531f Mon Sep 17 00:00:00 2001 From: reminis <2949079311@qq.com> Date: Sat, 7 May 2022 21:17:20 +0800 Subject: [PATCH 14/22] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E5=8F=91=E9=80=81=E8=AE=A2=E9=98=85=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/SendAccountConstant.java | 6 ++ .../dto/account/WeChatMiniProgramAccount.java | 44 +++++++++ .../dto/model/MiniProgramContentModel.java | 16 +++- austin-handler/pom.xml | 7 +- .../domain/wechat/WeChatMiniProgramParam.java | 38 ++++++++ .../impl/MiniProgramAccountHandler.java | 67 +++++++++++++ .../script/MiniProgramAccountService.java | 19 ++++ .../impl/MiniProgramAccountServiceImpl.java | 93 +++++++++++++++++++ .../austin/support/utils/AccountUtils.java | 1 + .../src/main/resources/application.properties | 7 -- pom.xml | 7 ++ 11 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatMiniProgramAccount.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatMiniProgramParam.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/MiniProgramAccountHandler.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/script/MiniProgramAccountService.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java index 76c8bcc..38fd4d6 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java @@ -45,6 +45,12 @@ public class SendAccountConstant { public static final String WECHAT_OFFICIAL_ACCOUNT_KEY = "officialAccount"; public static final String WECHAT_OFFICIAL__PREFIX = "official_"; + /** + * 微信小程序 应用消息 账号 + */ + public static final String WECHAT_MINI_PROGRAM_ACCOUNT_KEY = "miniProgramAccount"; + public static final String WECHAT_MINI_PROGRAM_PREFIX = "mini_program_"; + /** * 短信 账号 */ diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatMiniProgramAccount.java b/austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatMiniProgramAccount.java new file mode 100644 index 0000000..1e8d4e5 --- /dev/null +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/account/WeChatMiniProgramAccount.java @@ -0,0 +1,44 @@ +package com.java3y.austin.common.dto.account; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * + * 小程序订阅消息参数 + *

+ * 参数示例: + * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html + * * @author sunql + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class WeChatMiniProgramAccount { + + /** + * 订阅消息模板ID + */ + private String templateId; + + /** + * 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 + */ + private String miniProgramState; + + /** + * 击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。 + */ + private String page; + + /** + * 账号相关 + */ + private String appId; + private String appSecret; + private String grantType; + +} diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/model/MiniProgramContentModel.java b/austin-common/src/main/java/com/java3y/austin/common/dto/model/MiniProgramContentModel.java index 4601781..5e15ed3 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/dto/model/MiniProgramContentModel.java +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/model/MiniProgramContentModel.java @@ -1,8 +1,22 @@ package com.java3y.austin.common.dto.model; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + /** * @author 3y */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor public class MiniProgramContentModel extends ContentModel { - + /** + * 模板消息发送的数据 + */ + Map map; } diff --git a/austin-handler/pom.xml b/austin-handler/pom.xml index 6fdac1a..7f542c6 100644 --- a/austin-handler/pom.xml +++ b/austin-handler/pom.xml @@ -46,6 +46,11 @@ com.github.binarywang weixin-java-mp + + + com.github.binarywang + weixin-java-miniapp + @@ -54,4 +59,4 @@ - \ No newline at end of file + diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatMiniProgramParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatMiniProgramParam.java new file mode 100644 index 0000000..54bab4f --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/wechat/WeChatMiniProgramParam.java @@ -0,0 +1,38 @@ +package com.java3y.austin.handler.domain.wechat; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; +import java.util.Set; + +/** + * @author sunql + * @date 2022年05月06日 15:56 + * + * 小程序参数 + */ +@Data +@Builder +public class WeChatMiniProgramParam { + /** + * 业务Id + */ + private Long messageTemplateId; + + /** + * 发送账号 + */ + private Integer sendAccount; + + /** + * 接收者(用户)的 openid + */ + private Set openIds; + + /** + * 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } } + */ + private Map data; + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/MiniProgramAccountHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/MiniProgramAccountHandler.java new file mode 100644 index 0000000..013fd15 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/MiniProgramAccountHandler.java @@ -0,0 +1,67 @@ +package com.java3y.austin.handler.handler.impl; + +import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.common.dto.model.MiniProgramContentModel; +import com.java3y.austin.common.dto.model.OfficialAccountsContentModel; +import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam; +import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam; +import com.java3y.austin.handler.handler.BaseHandler; +import com.java3y.austin.handler.handler.Handler; +import com.java3y.austin.handler.script.MiniProgramAccountService; +import com.java3y.austin.handler.script.OfficialAccountService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author sunql + * 微信小程序发送订阅消息 + */ +@Component +@Slf4j +public class MiniProgramAccountHandler extends BaseHandler implements Handler { + + @Autowired + private MiniProgramAccountService miniProgramAccountService; + + public MiniProgramAccountHandler() { + channelCode = ChannelType.MINI_PROGRAM.getCode(); + } + + @Override + public boolean handler(TaskInfo taskInfo) { + WeChatMiniProgramParam miniProgramParam = buildMiniProgramParam(taskInfo); + try { + miniProgramAccountService.send(miniProgramParam); + } catch (Exception e) { + log.error("MiniProgramAccountHandler#handler fail:{},params:{}", + Throwables.getStackTraceAsString(e), JSON.toJSONString(taskInfo)); + return false; + } + return true; + } + + /** + * 通过taskInfo构建小程序订阅消息 + * + * @param taskInfo + * @return + */ + private WeChatMiniProgramParam buildMiniProgramParam(TaskInfo taskInfo) { + // 小程序订阅消息可以关联到系统业务,通过接口查询。 + WeChatMiniProgramParam miniProgramParam = WeChatMiniProgramParam.builder() + .openIds(taskInfo.getReceiver()) + .messageTemplateId(taskInfo.getMessageTemplateId()) + .sendAccount(taskInfo.getSendAccount()) + .build(); + + MiniProgramContentModel contentModel = (MiniProgramContentModel) taskInfo.getContentModel(); + miniProgramParam.setData(contentModel.getMap()); + return miniProgramParam; + } + +} + diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/MiniProgramAccountService.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/MiniProgramAccountService.java new file mode 100644 index 0000000..7a4b6da --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/MiniProgramAccountService.java @@ -0,0 +1,19 @@ +package com.java3y.austin.handler.script; + +import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam; + +/** + * @author sunql + */ +public interface MiniProgramAccountService { + + /** + * 发送订阅消息 + * + * @param miniProgramParam 订阅消息参数 + * @return + * @throws Exception + */ + void send(WeChatMiniProgramParam miniProgramParam) throws Exception; + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java new file mode 100644 index 0000000..c6019bd --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java @@ -0,0 +1,93 @@ +package com.java3y.austin.handler.script.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.api.impl.WxMaSubscribeServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import com.java3y.austin.common.constant.SendAccountConstant; +import com.java3y.austin.common.dto.account.WeChatMiniProgramAccount; +import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam; +import com.java3y.austin.handler.script.MiniProgramAccountService; +import com.java3y.austin.support.utils.AccountUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author sunql + * @date 2022年05月06日 16:41 + */ +@Service +@Slf4j +public class MiniProgramAccountServiceImpl implements MiniProgramAccountService { + + @Autowired + private AccountUtils accountUtils; + + @Override + public void send(WeChatMiniProgramParam miniProgramParam) throws Exception { + WeChatMiniProgramAccount miniProgramAccount = accountUtils.getAccount(miniProgramParam.getSendAccount(), + SendAccountConstant.WECHAT_MINI_PROGRAM_ACCOUNT_KEY, + SendAccountConstant.WECHAT_MINI_PROGRAM_PREFIX, + WeChatMiniProgramAccount.builder().build()); + + WxMaSubscribeService wxMaSubscribeService = initService(miniProgramAccount); + List subscribeMessageList = assembleReq(miniProgramParam, miniProgramAccount); + for (WxMaSubscribeMessage subscribeMessage : subscribeMessageList) { + wxMaSubscribeService.sendSubscribeMsg(subscribeMessage); + } + } + + /** + * 组装发送模板信息参数 + */ + private List assembleReq(WeChatMiniProgramParam miniProgramParam, WeChatMiniProgramAccount miniProgramAccount) { + Set receiver = miniProgramParam.getOpenIds(); + List messageList = new ArrayList<>(receiver.size()); + + // 构建微信小程序订阅消息 + for (String openId : receiver) { + WxMaSubscribeMessage subscribeMessage = WxMaSubscribeMessage.builder() + .toUser(openId) + .data(getWxMTemplateData(miniProgramParam.getData())) + .miniprogramState(miniProgramAccount.getMiniProgramState()) + .templateId(miniProgramAccount.getTemplateId()) + .page(miniProgramAccount.getPage()) + .build(); + messageList.add(subscribeMessage); + } + return messageList; + } + + /** + * 构建订阅消息参数 + * + * @returnp + */ + private List getWxMTemplateData(Map data) { + List templateDataList = new ArrayList<>(data.size()); + data.forEach((k, v) -> templateDataList.add(new WxMaSubscribeMessage.MsgData(k, v))); + return templateDataList; + } + + /** + * 初始化微信小程序 + * + * @return + */ + private WxMaSubscribeServiceImpl initService(WeChatMiniProgramAccount miniProgramAccount) { + WxMaService wxMaService = new WxMaServiceImpl(); + WxMaDefaultConfigImpl wxMaConfig = new WxMaDefaultConfigImpl(); + wxMaConfig.setAppid(miniProgramAccount.getAppId()); + wxMaConfig.setSecret(miniProgramAccount.getAppSecret()); + wxMaService.setWxMaConfig(wxMaConfig); + return new WxMaSubscribeServiceImpl(wxMaService); + } +} diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java index 77a4fc8..fd612a2 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java @@ -27,6 +27,7 @@ public class AccountUtils { * (key:dingDingRobotAccount) 钉钉自定义机器人参数示例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] * (key:dingDingWorkNoticeAccount) 钉钉工作消息参数示例:[{"ding_ding_work_notice_10":{"appKey":"dingh6yyyyyyycrlbx","appSecret":"tQpvmkR863333yyyyyHP3QHyyyymy9Ao1yoL1oQX5Nlx_fYLLLlpPJWHvWKbTu","agentId":"152333383622"}}] * (key:officialAccount) 微信服务号模板消息参数示例:[{"official_10":{"appId":"wxecb4693d2eef1ea7","secret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","url":"http://weixin.qq.com/download","miniProgramId":"xiaochengxuappid12345","path":"index?foo=bar"}}] + * (key:miniProgramAccount) 微信服务号模板消息参数示例:[{"mini_program_10":{"appId":"wxecb4693d2eef1ea7","appSecret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","grantType":"client_credential","miniProgramState":"trial","page":"index?foo=bar"}}] */ public T getAccount(Integer sendAccount, String apolloKey, String prefix, T t) { String accountValues = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY); diff --git a/austin-web/src/main/resources/application.properties b/austin-web/src/main/resources/application.properties index 20c1b3c..28bc18d 100644 --- a/austin-web/src/main/resources/application.properties +++ b/austin-web/src/main/resources/application.properties @@ -87,10 +87,3 @@ management.endpoint.metrics.enabled=true management.endpoint.prometheus.enabled=true management.endpoints.web.exposure.include=* management.metrics.export.prometheus.enabled=true - -##################### wx mp config ##################### -##################### TODO not test by 3y,wait to apply for OfficialAccount ##################### -wx.mp.account.appid="appid" -wx.mp.account.secret="secret" -wx.mp.account.token="token" -wx.mp.account.aesKey="aesKey" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6388c08..68c2ffa 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,13 @@ ${weixin-java} + + + com.github.binarywang + weixin-java-miniapp + ${weixin-java} + + io.github.lyh200 From 97fce087959c997e56d0bdf80eb4a93b712538bd Mon Sep 17 00:00:00 2001 From: reminis <2949079311@qq.com> Date: Sat, 7 May 2022 21:31:53 +0800 Subject: [PATCH 15/22] =?UTF-8?q?fix:=E6=8F=8F=E8=BF=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/java3y/austin/support/utils/AccountUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java index fd612a2..c24218a 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/AccountUtils.java @@ -27,7 +27,7 @@ public class AccountUtils { * (key:dingDingRobotAccount) 钉钉自定义机器人参数示例:[{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}] * (key:dingDingWorkNoticeAccount) 钉钉工作消息参数示例:[{"ding_ding_work_notice_10":{"appKey":"dingh6yyyyyyycrlbx","appSecret":"tQpvmkR863333yyyyyHP3QHyyyymy9Ao1yoL1oQX5Nlx_fYLLLlpPJWHvWKbTu","agentId":"152333383622"}}] * (key:officialAccount) 微信服务号模板消息参数示例:[{"official_10":{"appId":"wxecb4693d2eef1ea7","secret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","url":"http://weixin.qq.com/download","miniProgramId":"xiaochengxuappid12345","path":"index?foo=bar"}}] - * (key:miniProgramAccount) 微信服务号模板消息参数示例:[{"mini_program_10":{"appId":"wxecb4693d2eef1ea7","appSecret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","grantType":"client_credential","miniProgramState":"trial","page":"index?foo=bar"}}] + * (key:miniProgramAccount) 微信小程序订阅消息参数示例:[{"mini_program_10":{"appId":"wxecb4693d2eef1ea7","appSecret":"6240870f4d91701640d769ba20120821","templateId":"JHUk6eE9T5Ts7a5JO3ZQqkBBrZBGn5C9iIiKNDQsk-Q","grantType":"client_credential","miniProgramState":"trial","page":"index?foo=bar"}}] */ public T getAccount(Integer sendAccount, String apolloKey, String prefix, T t) { String accountValues = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY); From 54bba799dc97c402f932e68bf0a9882c592e8cf2 Mon Sep 17 00:00:00 2001 From: 3y Date: Sat, 7 May 2022 21:38:31 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E8=B4=A6=E5=8F=B7utils?= =?UTF-8?q?=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/script/impl/MiniProgramAccountServiceImpl.java | 2 +- .../austin/handler/script/impl/OfficialAccountServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java index c6019bd..ddb2e21 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/MiniProgramAccountServiceImpl.java @@ -36,7 +36,7 @@ public class MiniProgramAccountServiceImpl implements MiniProgramAccountService WeChatMiniProgramAccount miniProgramAccount = accountUtils.getAccount(miniProgramParam.getSendAccount(), SendAccountConstant.WECHAT_MINI_PROGRAM_ACCOUNT_KEY, SendAccountConstant.WECHAT_MINI_PROGRAM_PREFIX, - WeChatMiniProgramAccount.builder().build()); + WeChatMiniProgramAccount.class); WxMaSubscribeService wxMaSubscribeService = initService(miniProgramAccount); List subscribeMessageList = assembleReq(miniProgramParam, miniProgramAccount); diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java index 6615be6..a0f68fc 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/impl/OfficialAccountServiceImpl.java @@ -31,7 +31,7 @@ public class OfficialAccountServiceImpl implements OfficialAccountService { @Override public List send(WeChatOfficialParam officialParam) throws Exception { - WeChatOfficialAccount officialAccount = accountUtils.getAccount(officialParam.getSendAccount(), SendAccountConstant.WECHAT_OFFICIAL_ACCOUNT_KEY, SendAccountConstant.WECHAT_OFFICIAL__PREFIX, WeChatOfficialAccount.builder().build()); + WeChatOfficialAccount officialAccount = accountUtils.getAccount(officialParam.getSendAccount(), SendAccountConstant.WECHAT_OFFICIAL_ACCOUNT_KEY, SendAccountConstant.WECHAT_OFFICIAL__PREFIX, WeChatOfficialAccount.class); WxMpService wxMpService = initService(officialAccount); List messages = assembleReq(officialParam, officialAccount); List messageIds = new ArrayList<>(messages.size()); From d39fc34c3471f7d1b84b73388c82af165d01a1c4 Mon Sep 17 00:00:00 2001 From: 3y Date: Sun, 8 May 2022 12:26:12 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=E6=8E=A5=E5=85=A5=20=E4=B8=AA=E6=8E=A8?= =?UTF-8?q?=EF=BC=8C=E5=88=B7=E6=96=B0token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/SendAccountConstant.java | 21 +++- .../common/dto/account/GeTuiAccount.java | 30 +++++ .../cron/dto/getui/GeTuiTokenResultDTO.java | 37 ++++++ .../cron/dto/getui/QueryTokenParamDTO.java | 40 ++++++ .../RefreshGeTuiAccessTokenHandler.java | 91 ++++++++++++++ .../handler/domain/getui/SendPushParam.java | 115 ++++++++++++++++++ .../handler/domain/getui/SendPushResult.java | 38 ++++++ 7 files changed, 367 insertions(+), 5 deletions(-) create mode 100644 austin-common/src/main/java/com/java3y/austin/common/dto/account/GeTuiAccount.java create mode 100644 austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/GeTuiTokenResultDTO.java create mode 100644 austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/QueryTokenParamDTO.java create mode 100644 austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java index f5aa376..c425dfa 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/SendAccountConstant.java @@ -12,6 +12,13 @@ package com.java3y.austin.common.constant; */ public class SendAccountConstant { + /** + * 账号约定:所有的账号都从10开始,步长为10 + */ + public static final Integer START = 10; + public static final Integer STEP = 10; + + /** * 钉钉 工作应用消息 账号 */ @@ -19,6 +26,14 @@ public class SendAccountConstant { public static final String DING_DING_WORK_NOTICE_PREFIX = "ding_ding_work_notice_"; public static final String DING_DING_ACCESS_TOKEN_PREFIX = "ding_ding_access_token_"; + /** + * 个推PUSH 消息账号 + */ + public static final String GE_TUI_ACCOUNT_KEY = "geTuiAccount"; + public static final String GE_TUI_ACCOUNT_PREFIX = "ge_tui_account_"; + public static final String GE_TUI_ACCESS_TOKEN_PREFIX = "ge_tui_access_token_"; + + /** * 邮件 账号 @@ -46,9 +61,5 @@ public class SendAccountConstant { public static final String SMS_ACCOUNT_KEY = "smsAccount"; public static final String SMS_PREFIX = "sms_"; - /** - * 账号约定:所有的账号都从10开始,步长为10 - */ - public static final Integer START = 10; - public static final Integer STEP = 10; + } diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/account/GeTuiAccount.java b/austin-common/src/main/java/com/java3y/austin/common/dto/account/GeTuiAccount.java new file mode 100644 index 0000000..994fbfe --- /dev/null +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/account/GeTuiAccount.java @@ -0,0 +1,30 @@ +package com.java3y.austin.common.dto.account; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 创建个推账号时的元信息 + * + * @author 3y + *

+ * (在调用个推的api时需要用到部分的参数) + *

+ * https://docs.getui.com/getui/start/devcenter/ + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class GeTuiAccount { + + private String appId; + + private String appKey; + + private String masterSecret; +} diff --git a/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/GeTuiTokenResultDTO.java b/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/GeTuiTokenResultDTO.java new file mode 100644 index 0000000..ef8de29 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/GeTuiTokenResultDTO.java @@ -0,0 +1,37 @@ +package com.java3y.austin.cron.dto.getui; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author 3y + * @date 2022/5/8 + *

+ * https://docs.getui.com/getui/server/rest_v2/token/ + */ +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +public class GeTuiTokenResultDTO { + + + @JSONField(name = "msg") + private String msg; + @JSONField(name = "code") + private Integer code; + @JSONField(name = "data") + private DataDTO data; + + @NoArgsConstructor + @Data + public static class DataDTO { + @JSONField(name = "expire_time") + private String expireTime; + @JSONField(name = "token") + private String token; + } +} diff --git a/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/QueryTokenParamDTO.java b/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/QueryTokenParamDTO.java new file mode 100644 index 0000000..6a1a848 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/cron/dto/getui/QueryTokenParamDTO.java @@ -0,0 +1,40 @@ +package com.java3y.austin.cron.dto.getui; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 请求token时的参数 + * @author 3y + * https://docs.getui.com/getui/server/rest_v2/token/ + */ +@NoArgsConstructor +@Data +@Builder +@AllArgsConstructor +public class QueryTokenParamDTO { + /** + * sign + */ + @JSONField(name = "sign") + private String sign; + /** + * timestamp + */ + @JSONField(name = "timestamp") + private String timestamp; + /** + * appkey + */ + @JSONField(name = "appkey") + private String appKey; +} diff --git a/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java new file mode 100644 index 0000000..e1f6ab6 --- /dev/null +++ b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java @@ -0,0 +1,91 @@ +package com.java3y.austin.cron.handler; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.SendAccountConstant; +import com.java3y.austin.common.dto.account.GeTuiAccount; +import com.java3y.austin.cron.dto.getui.QueryTokenParamDTO; +import com.java3y.austin.cron.dto.getui.GeTuiTokenResultDTO; +import com.java3y.austin.support.config.SupportThreadPoolConfig; +import com.java3y.austin.support.utils.AccountUtils; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + + +/** + * 刷新个推的token + *

+ * https://docs.getui.com/getui/server/rest_v2/token/ + * + * @author 3y + */ +@Service +@Slf4j +public class RefreshGeTuiAccessTokenHandler { + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private AccountUtils accountUtils; + + /** + * 每小时请求一次接口刷新(以防失效) + */ + @XxlJob("refreshGeTuiAccessTokenJob") + public void execute() { + log.info("refreshGeTuiAccessTokenJob#execute!"); + SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> { + for (int index = SendAccountConstant.START; true; index = index + SendAccountConstant.STEP) { + GeTuiAccount account = accountUtils.getAccount(index, SendAccountConstant.GE_TUI_ACCOUNT_KEY, SendAccountConstant.GE_TUI_ACCOUNT_PREFIX, new GeTuiAccount()); + if (account == null) { + break; + } + String accessToken = getAccessToken(account); + if (StrUtil.isNotBlank(accessToken)) { + redisTemplate.opsForValue().set(SendAccountConstant.GE_TUI_ACCESS_TOKEN_PREFIX + index, accessToken); + } + } + }); + } + + /** + * 获取 access_token + * + * @param account + * @return + */ + private String getAccessToken(GeTuiAccount account) { + String accessToken = ""; + try { + String url = "https://restapi.getui.com/v2/" + account.getAppId() + "/auth"; + String time = String.valueOf(System.currentTimeMillis()); + String digest = SecureUtil.sha256().digestHex(account.getAppKey() + time + account.getMasterSecret()); + QueryTokenParamDTO param = QueryTokenParamDTO.builder() + .timestamp(time) + .appKey(account.getAppKey()) + .sign(digest).build(); + + String body = HttpRequest.post(url).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .body(JSON.toJSONString(param)) + .timeout(20000) + .execute().body(); + GeTuiTokenResultDTO geTuiTokenResultDTO = JSON.parseObject(body, GeTuiTokenResultDTO.class); + if (geTuiTokenResultDTO.getCode().equals(0)) { + accessToken = geTuiTokenResultDTO.getData().getToken(); + } + } catch (Exception e) { + log.error("RefreshGeTuiAccessTokenHandler#getAccessToken fail:{}", Throwables.getStackTraceAsString(e)); + } + return accessToken; + } + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java new file mode 100644 index 0000000..f0a035e --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java @@ -0,0 +1,115 @@ +package com.java3y.austin.handler.domain.getui; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 推送消息的param + * @author 3y + * https://docs.getui.com/getui/server/rest_v2/push/ + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class SendPushParam { + + /** + * requestId + */ + @JSONField(name = "request_id") + private String requestId; + /** + * settings + */ + @JSONField(name = "settings") + private SettingsVO settings; + /** + * audience + */ + @JSONField(name = "audience") + private AudienceVO audience; + /** + * pushMessage + */ + @JSONField(name = "push_message") + private PushMessageVO pushMessage; + + /** + * SettingsVO + */ + @NoArgsConstructor + @Data + public static class SettingsVO { + /** + * ttl + */ + @JSONField(name = "ttl") + private Integer ttl; + } + + /** + * AudienceVO + */ + @NoArgsConstructor + @Data + @AllArgsConstructor + @Builder + public static class AudienceVO { + /** + * cid + */ + @JSONField(name = "cid") + private List cid; + } + + /** + * PushMessageVO + */ + @NoArgsConstructor + @Data + @AllArgsConstructor + @Builder + public static class PushMessageVO { + /** + * notification + */ + @JSONField(name = "notification") + private NotificationVO notification; + + /** + * NotificationVO + */ + @NoArgsConstructor + @Data + @AllArgsConstructor + @Builder + public static class NotificationVO { + /** + * title + */ + @JSONField(name = "title") + private String title; + /** + * body + */ + @JSONField(name = "body") + private String body; + /** + * clickType + */ + @JSONField(name = "click_type") + private String clickType; + /** + * url + */ + @JSONField(name = "url") + private String url; + } + } +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java new file mode 100644 index 0000000..6a14fc6 --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java @@ -0,0 +1,38 @@ +package com.java3y.austin.handler.domain.getui; + + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 发送消息后的返回值 + * @author 3y + * https://docs.getui.com/getui/server/rest_v2/common_args/?id=doc-title-1 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SendPushResult { + /** + * msg + */ + @JSONField(name = "msg") + private String msg; + /** + * code + */ + @JSONField(name = "code") + private Integer code; + /** + * data + */ + @JSONField(name = "data") + private JSONObject data; + +} From 328a6d51e5c0e3a04ac60ac111eb939e426d17c7 Mon Sep 17 00:00:00 2001 From: 3y Date: Sun, 8 May 2022 12:29:16 +0800 Subject: [PATCH 18/22] fix . --- .../austin/cron/handler/RefreshGeTuiAccessTokenHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java index e1f6ab6..b000df9 100644 --- a/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java +++ b/austin-cron/src/main/java/com/java3y/austin/cron/handler/RefreshGeTuiAccessTokenHandler.java @@ -45,7 +45,7 @@ public class RefreshGeTuiAccessTokenHandler { log.info("refreshGeTuiAccessTokenJob#execute!"); SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> { for (int index = SendAccountConstant.START; true; index = index + SendAccountConstant.STEP) { - GeTuiAccount account = accountUtils.getAccount(index, SendAccountConstant.GE_TUI_ACCOUNT_KEY, SendAccountConstant.GE_TUI_ACCOUNT_PREFIX, new GeTuiAccount()); + GeTuiAccount account = accountUtils.getAccount(index, SendAccountConstant.GE_TUI_ACCOUNT_KEY, SendAccountConstant.GE_TUI_ACCOUNT_PREFIX, GeTuiAccount.class); if (account == null) { break; } From c06577c8ae4683b38ffffd8d1e0b66d04cd1d516 Mon Sep 17 00:00:00 2001 From: 3y Date: Sun, 8 May 2022 21:51:20 +0800 Subject: [PATCH 19/22] push handler init --- .../handler/handler/impl/PushHandler.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java new file mode 100644 index 0000000..deebddd --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java @@ -0,0 +1,30 @@ +package com.java3y.austin.handler.handler.impl; + +import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.handler.handler.BaseHandler; +import com.java3y.austin.handler.handler.Handler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 通知栏消息发送处理 + *

+ * (目前具体的实现是个推服务商,安卓端已验证) + * + * @author 3y + */ +@Component +@Slf4j +public class PushHandler extends BaseHandler implements Handler { + + public PushHandler() { + channelCode = ChannelType.PUSH.getCode(); + } + + @Override + public boolean handler(TaskInfo taskInfo) { + return true; + } + +} From fafd2725c802ad4c0741dbca297725271f5de751 Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 9 May 2022 22:09:06 +0800 Subject: [PATCH 20/22] =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=AB=AF=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E4=B8=AA=E6=8E=A8=20=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dto/model/PushContentModel.java | 14 ++ .../austin/handler/domain/push/PushParam.java | 33 +++++ .../domain/push/getui/BatchSendPushParam.java | 54 +++++++ .../{ => push}/getui/SendPushParam.java | 6 +- .../{ => push}/getui/SendPushResult.java | 2 +- .../handler/handler/impl/PushHandler.java | 136 +++++++++++++++++- 6 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/push/PushParam.java create mode 100644 austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/BatchSendPushParam.java rename austin-handler/src/main/java/com/java3y/austin/handler/domain/{ => push}/getui/SendPushParam.java (95%) rename austin-handler/src/main/java/com/java3y/austin/handler/domain/{ => push}/getui/SendPushResult.java (92%) diff --git a/austin-common/src/main/java/com/java3y/austin/common/dto/model/PushContentModel.java b/austin-common/src/main/java/com/java3y/austin/common/dto/model/PushContentModel.java index abb427d..511d436 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/dto/model/PushContentModel.java +++ b/austin-common/src/main/java/com/java3y/austin/common/dto/model/PushContentModel.java @@ -1,9 +1,23 @@ package com.java3y.austin.common.dto.model; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + /** * @author 3y + * + * 通知栏消息推送 */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor public class PushContentModel extends ContentModel { + private String title; + private String content; + private String url; } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/PushParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/PushParam.java new file mode 100644 index 0000000..5ab85ca --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/PushParam.java @@ -0,0 +1,33 @@ +package com.java3y.austin.handler.domain.push; + + +import com.java3y.austin.common.domain.TaskInfo; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class PushParam { + + /** + * 调用 接口时需要的token + */ + private String token; + + /** + * 调用接口时需要的appId + */ + private String appId; + + /** + * 消息模板的信息 + */ + private TaskInfo taskInfo; + +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/BatchSendPushParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/BatchSendPushParam.java new file mode 100644 index 0000000..3afbb0a --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/BatchSendPushParam.java @@ -0,0 +1,54 @@ +package com.java3y.austin.handler.domain.push.getui; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + + +/** + * 批量推送消息的param + * + * @author 3y + * https://docs.getui.com/getui/server/rest_v2/push/ + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class BatchSendPushParam { + + /** + * audience + */ + @JSONField(name = "audience") + private AudienceVO audience; + /** + * taskid + */ + @JSONField(name = "taskid") + private String taskId; + /** + * isAsync + */ + @JSONField(name = "is_async") + private Boolean isAsync; + + /** + * AudienceVO + */ + @NoArgsConstructor + @Data + @Builder + @AllArgsConstructor + public static class AudienceVO { + /** + * cid + */ + @JSONField(name = "cid") + private Set cid; + } +} diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushParam.java similarity index 95% rename from austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java rename to austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushParam.java index f0a035e..664d748 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushParam.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushParam.java @@ -1,4 +1,4 @@ -package com.java3y.austin.handler.domain.getui; +package com.java3y.austin.handler.domain.push.getui; import com.alibaba.fastjson.annotation.JSONField; import lombok.AllArgsConstructor; @@ -6,7 +6,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; +import java.util.Set; /** * 推送消息的param @@ -65,7 +65,7 @@ public class SendPushParam { * cid */ @JSONField(name = "cid") - private List cid; + private Set cid; } /** diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushResult.java similarity index 92% rename from austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java rename to austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushResult.java index 6a14fc6..5175d27 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/domain/getui/SendPushResult.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/domain/push/getui/SendPushResult.java @@ -1,4 +1,4 @@ -package com.java3y.austin.handler.domain.getui; +package com.java3y.austin.handler.domain.push.getui; import com.alibaba.fastjson.JSONObject; diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java index deebddd..93feeef 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/handler/impl/PushHandler.java @@ -1,12 +1,32 @@ package com.java3y.austin.handler.handler.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.SendAccountConstant; import com.java3y.austin.common.domain.TaskInfo; +import com.java3y.austin.common.dto.account.GeTuiAccount; +import com.java3y.austin.common.dto.model.PushContentModel; import com.java3y.austin.common.enums.ChannelType; + +import com.java3y.austin.handler.domain.push.PushParam; +import com.java3y.austin.handler.domain.push.getui.BatchSendPushParam; +import com.java3y.austin.handler.domain.push.getui.SendPushParam; +import com.java3y.austin.handler.domain.push.getui.SendPushResult; import com.java3y.austin.handler.handler.BaseHandler; import com.java3y.austin.handler.handler.Handler; +import com.java3y.austin.support.utils.AccountUtils; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; +import java.util.Set; + /** * 通知栏消息发送处理 *

@@ -18,13 +38,127 @@ import org.springframework.stereotype.Component; @Slf4j public class PushHandler extends BaseHandler implements Handler { + private static final String BASE_URL = "https://restapi.getui.com/v2/"; + private static final String SINGLE_PUSH_PATH = "/push/single/cid"; + private static final String BATCH_PUSH_CREATE_TASK_PATH = "/push/list/message"; + private static final String BATCH_PUSH_PATH = "/push/list/cid"; + public PushHandler() { channelCode = ChannelType.PUSH.getCode(); } + @Autowired + private AccountUtils accountUtils; + @Autowired + private StringRedisTemplate redisTemplate; + + @Override public boolean handler(TaskInfo taskInfo) { - return true; + + try { + GeTuiAccount account = accountUtils.getAccount(taskInfo.getSendAccount(), SendAccountConstant.GE_TUI_ACCOUNT_KEY, SendAccountConstant.GE_TUI_ACCOUNT_PREFIX, GeTuiAccount.class); + String token = redisTemplate.opsForValue().get(SendAccountConstant.GE_TUI_ACCESS_TOKEN_PREFIX + taskInfo.getSendAccount()); + PushParam pushParam = PushParam.builder().token(token).appId(account.getAppId()).taskInfo(taskInfo).build(); + + String result; + if (taskInfo.getReceiver().size() == 1) { + result = singlePush(pushParam); + } else { + result = batchPush(createTaskId(pushParam), pushParam); + } + SendPushResult sendPushResult = JSON.parseObject(result, SendPushResult.class); + if (sendPushResult.getCode().equals(0)) { + return true; + } + // 常见的错误 应当 关联至 AnchorState,由austin后台统一透出失败原因 + log.error("PushHandler#handler fail!result:{},params:{}", JSON.toJSONString(sendPushResult), JSON.toJSONString(taskInfo)); + } catch (Exception e) { + log.error("PushHandler#handler fail!e:{},params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(taskInfo)); + } + return false; + } + + + /** + * 单推 + * @param pushParam + * @return http result + */ + private String singlePush(PushParam pushParam) { + String url = BASE_URL + pushParam.getAppId() + SINGLE_PUSH_PATH; + SendPushParam sendPushParam = assembleParam((PushContentModel) pushParam.getTaskInfo().getContentModel(), pushParam.getTaskInfo().getReceiver()); + String body = HttpRequest.post(url).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .header("token", pushParam.getToken()) + .body(JSON.toJSONString(sendPushParam)) + .timeout(2000) + .execute().body(); + return body; + } + + + /** + * 批量推送 + * + * @param taskId 个推 返回的任务Id + * @param pushParam + * @return + */ + private String batchPush(String taskId, PushParam pushParam) { + String url = BASE_URL + pushParam.getAppId() + BATCH_PUSH_PATH; + BatchSendPushParam batchSendPushParam = BatchSendPushParam.builder() + .taskId(taskId) + .isAsync(true) + .audience(BatchSendPushParam.AudienceVO.builder().cid(pushParam.getTaskInfo().getReceiver()).build()).build(); + String body = HttpRequest.post(url).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .header("token", pushParam.getToken()) + .body(JSON.toJSONString(batchSendPushParam)) + .timeout(2000) + .execute().body(); + return body; + } + + + /** + * 群推前需要构建taskId + * @param pushParam + * @return http result + */ + private String createTaskId(PushParam pushParam) { + String url = BASE_URL + pushParam.getAppId() + BATCH_PUSH_CREATE_TASK_PATH; + SendPushParam param = assembleParam((PushContentModel) pushParam.getTaskInfo().getContentModel()); + String taskId = ""; + try { + String body = HttpRequest.post(url).header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .header("token", pushParam.getToken()) + .body(JSON.toJSONString(param)) + .timeout(2000) + .execute().body(); + + taskId = JSON.parseObject(body, SendPushResult.class).getData().getString("taskId"); + } catch (Exception e) { + log.error("PushHandler#createTaskId fail :{},params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(pushParam.getTaskInfo())); + } + + return taskId; + } + + + private SendPushParam assembleParam(PushContentModel pushContentModel) { + return assembleParam(pushContentModel, null); + } + + private SendPushParam assembleParam(PushContentModel pushContentModel, Set cid) { + SendPushParam param = SendPushParam.builder() + .requestId(String.valueOf(IdUtil.getSnowflake().nextId())) + .pushMessage(SendPushParam.PushMessageVO.builder().notification(SendPushParam.PushMessageVO.NotificationVO.builder() + .title(pushContentModel.getTitle()).body(pushContentModel.getContent()).clickType("startapp").build()) + .build()) + .build(); + if (CollUtil.isNotEmpty(cid)) { + param.setAudience(SendPushParam.AudienceVO.builder().cid(cid).build()); + } + return param; } } From 9ac5c9f184eb3385b26f5bc4452d3f8dd989d3b5 Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 9 May 2022 22:12:59 +0800 Subject: [PATCH 21/22] update README.md --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9037f58..88d52dd 100644 --- a/README.md +++ b/README.md @@ -124,17 +124,16 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [x] 钉钉渠道接入 - [x] 单机限流实现 - [x] 引入单测框架,编写部分单测用例 +- [x] 接入微信服务号渠道(已有pull request代码) +- [x] 接入微信小程序渠道(已有pull request代码) +- [x] 接入PUSH渠道 +- [ ] 总体架构已完成,持续做基础建设和优化代码 - [ ] 持续提高消息推送系统的影响力,让更多的业务方了解其功能,进而挖掘更多拉新和唤醒用户的玩法,提高站内的次留率和转化率 -- [ ] 优化代码 -- [ ] 接入微信服务号渠道 -- [ ] 接入微信小程序渠道 -- [ ] 接入PUSH渠道 -- [ ] 接入工作流引擎实现对消息工单审核 -**近期更新时间**:4月30号 +**近期更新时间**:5月9号 -**近期更新功能**:引入单测框架,编写部分单测用例 +**近期更新功能**:接入个推PUSH,安卓发送推送消息 ## 项目交流 From adc937cc8eb0b8bd5dd76a576f1b43da435f26b4 Mon Sep 17 00:00:00 2001 From: 3y Date: Mon, 9 May 2022 22:20:48 +0800 Subject: [PATCH 22/22] update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 88d52dd..75297c7 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,6 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - [x] 接入微信小程序渠道(已有pull request代码) - [x] 接入PUSH渠道 - [ ] 总体架构已完成,持续做基础建设和优化代码 -- [ ] 持续提高消息推送系统的影响力,让更多的业务方了解其功能,进而挖掘更多拉新和唤醒用户的玩法,提高站内的次留率和转化率 **近期更新时间**:5月9号