瀏覽代碼

Merge branch 'dev-xym-we-com' of Server/long-article-recommend into master

xueyiming 9 月之前
父節點
當前提交
2de8577c2b
共有 41 個文件被更改,包括 2501 次插入90 次删除
  1. 6 0
      long-article-recommend-service/pom.xml
  2. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/Application.java
  3. 38 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/SecretEnum.java
  4. 29 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/StaffEnum.java
  5. 15 13
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/cgi/ReplyStrategyServiceEnum.java
  6. 75 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/config/MqConfig.java
  7. 2 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/crawler/AlgGhAutoreplyVideoRankDataMapper.java
  8. 34 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/crawler/PushMessageCallbackMapper.java
  9. 20 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/bo/MiniData.java
  10. 11 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/bo/ReplyInfo.java
  11. 4 46
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/cgi/BucketDataParam.java
  12. 9 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/AccessTokenParam.java
  13. 21 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/CallbackParam.java
  14. 11 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/PushMessageParam.java
  15. 11 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/WeComPushMessageParam.java
  16. 11 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/AccessTokenVo.java
  17. 14 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/PushMessageVo.java
  18. 14 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/WeComPushMessageVo.java
  19. 57 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mq/MessageCallbackCustomer.java
  20. 68 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mq/MessageCallbackProducer.java
  21. 88 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/model/PushMessageCallback.java
  22. 640 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/model/PushMessageCallbackExample.java
  23. 12 5
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/cgi/TouLiuHttpClientService.java
  24. 16 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/AccessTokenService.java
  25. 14 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/ThirdPartyService.java
  26. 12 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/WeComService.java
  27. 86 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/AccessTokenServiceImpl.java
  28. 103 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/ThirdPartyServiceImpl.java
  29. 130 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/WeComServiceImpl.java
  30. 8 5
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/BuckStrategyV1.java
  31. 254 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/PushMessageStrategyV1.java
  32. 254 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/WeComPushMessageStrategyV1.java
  33. 47 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/ThirdPartyController.java
  34. 40 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/WeComController.java
  35. 27 18
      long-article-recommend-service/src/main/resources/application-dev.yml
  36. 3 1
      long-article-recommend-service/src/main/resources/application-pre.yml
  37. 21 1
      long-article-recommend-service/src/main/resources/application-prod.yml
  38. 3 1
      long-article-recommend-service/src/main/resources/application-test.yml
  39. 5 0
      long-article-recommend-service/src/main/resources/application.yml
  40. 9 0
      long-article-recommend-service/src/main/resources/mapper/crawler/AlgGhAutoreplyVideoRankDataMapper.xml
  41. 278 0
      long-article-recommend-service/src/main/resources/mapper/crawler/PushMessageCallbackMapper.xml

+ 6 - 0
long-article-recommend-service/pom.xml

@@ -136,6 +136,12 @@
             <artifactId>poi-ooxml</artifactId>
             <version>5.2.3</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.aliyun.openservices</groupId>
+            <artifactId>ons-client</artifactId>
+            <version>1.8.4.Final</version>
+        </dependency>
     </dependencies>
 
 

+ 1 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/Application.java

@@ -24,6 +24,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
         "com.tzld.longarticle.recommend.server.web",
         "com.tzld.longarticle.recommend.server.util",
         "com.tzld.longarticle.recommend.server.repository",
+        "com.tzld.longarticle.recommend.server.mq"
 })
 @EnableAspectJAutoProxy
 @EnableApolloConfig

+ 38 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/SecretEnum.java

@@ -0,0 +1,38 @@
+package com.tzld.longarticle.recommend.server.common.enums;
+
+
+import java.util.Objects;
+
+public enum SecretEnum {
+
+    SECRET_ENUM_1("3b83574b477d4c5b8508a6e33f6e35ec", "魅力", "ml"),
+    SECRET_ENUM_2("70d342bf11a84ac7aca6b3e99541e085", "老来福", "llf");
+
+    SecretEnum(String secret, String desc, String channel) {
+        this.secret = secret;
+        this.desc = desc;
+        this.channel = channel;
+    }
+
+    public final String secret;
+    public final String desc;
+    public final String channel;
+
+    public static boolean contains(String secret) {
+        for (SecretEnum secretEnum : SecretEnum.values()) {
+            if (Objects.equals(secretEnum.secret, secret)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static SecretEnum get(String secret) {
+        for (SecretEnum secretEnum : SecretEnum.values()) {
+            if (Objects.equals(secretEnum.secret, secret)) {
+                return secretEnum;
+            }
+        }
+        return null;
+    }
+}

+ 29 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/StaffEnum.java

@@ -0,0 +1,29 @@
+package com.tzld.longarticle.recommend.server.common.enums;
+
+import java.util.Objects;
+
+public enum StaffEnum {
+
+    STAFF_ENUM_1("XinYi", "精彩内容分享官"),
+    STAFF_ENUM_2("SongYi", "精彩内容推荐官"),
+    STAFF_ENUM_3("17512006748", "精彩视频福利官");
+
+    StaffEnum(String userId, String name) {
+        this.userId = userId;
+        this.name = name;
+
+    }
+
+    public final String userId;
+    public final String name;
+
+    public static String getName(String userId) {
+        for (StaffEnum staffEnum : StaffEnum.values()) {
+            if (Objects.equals(staffEnum.userId, userId)) {
+                return staffEnum.name;
+            }
+        }
+        return null;
+    }
+
+}

+ 15 - 13
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/cgi/ReplyStrategyServiceEnum.java

@@ -2,23 +2,25 @@ package com.tzld.longarticle.recommend.server.common.enums.cgi;
 
 public enum ReplyStrategyServiceEnum {
 
-     BUCKET_STRATEGY_V1("BUCKET_STRATEGY_V1", "分桶策略v1"),
+    BUCKET_STRATEGY_V1("BUCKET_STRATEGY_V1", "分桶策略v1"),
 
-     ;
+    PUSH_MESSAGE_STRATEGY_V1("PUSH_MESSAGE_STRATEGY_V1", "第三方微信公众号推送策略V1"),
+    WE_COM_PUSH_MESSAGE_STRATEGY_V1("WE_COM_PUSH_MESSAGE_STRATEGY_V1", "企业微信推送策略V1"),
+    ;
 
-     private final String key;
-     private final String desc;
+    private final String key;
+    private final String desc;
 
     ReplyStrategyServiceEnum(String key, String desc) {
-         this.key = key;
-         this.desc = desc;
-     }
+        this.key = key;
+        this.desc = desc;
+    }
 
-     public String getKey() {
-         return key;
-     }
+    public String getKey() {
+        return key;
+    }
 
-     public String getDesc() {
-         return desc;
-     }
+    public String getDesc() {
+        return desc;
+    }
 }

+ 75 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/config/MqConfig.java

@@ -0,0 +1,75 @@
+package com.tzld.longarticle.recommend.server.config;
+
+import com.aliyun.openservices.ons.api.MessageListener;
+import com.aliyun.openservices.ons.api.PropertyKeyConst;
+import com.aliyun.openservices.ons.api.bean.ConsumerBean;
+import com.aliyun.openservices.ons.api.bean.ProducerBean;
+import com.aliyun.openservices.ons.api.bean.Subscription;
+import com.tzld.longarticle.recommend.server.mq.MessageCallbackCustomer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+@Configuration
+public class MqConfig {
+
+    @Value("${rocketmq.accessKey}")
+    private String accessKey;
+    @Value("${rocketmq.secretKey}")
+    private String secretKey;
+    @Value("${rocketmq.nameSrvAddr}")
+    private String nameSrvAddr;
+    @Value("${pushMessage.callback.topic}")
+    private String callbackTopic;
+    @Value("${pushMessage.callback.tag}")
+    private String callbackTag;
+    @Value("${pushMessage.callback.groupId}")
+    private String callbackGroupId;
+
+    @Autowired
+    private MessageCallbackCustomer messageCallbackCustomer;
+
+    public Properties getMqProperties() {
+        Properties properties = new Properties();
+        properties.setProperty(PropertyKeyConst.AccessKey, this.accessKey);
+        properties.setProperty(PropertyKeyConst.SecretKey, this.secretKey);
+        properties.setProperty(PropertyKeyConst.NAMESRV_ADDR, this.nameSrvAddr);
+        return properties;
+    }
+
+    @Bean(initMethod = "start", destroyMethod = "shutdown")
+    public ProducerBean buildProducer() {
+        ProducerBean producer = new ProducerBean();
+        producer.setProperties(getMqProperties());
+        return producer;
+    }
+
+
+    @Bean(initMethod = "start", destroyMethod = "shutdown")
+    public ConsumerBean buildConsumer() {
+        ConsumerBean consumerBean = new ConsumerBean();
+        //配置文件
+        Properties properties = getMqProperties();
+        properties.setProperty(PropertyKeyConst.ConsumeThreadNums, "20");
+        properties.setProperty(PropertyKeyConst.GROUP_ID, callbackGroupId);
+        consumerBean.setProperties(properties);
+        //订阅关系
+        Map<Subscription, MessageListener> subscriptionTable = new HashMap<>();
+        Subscription subscription = new Subscription();
+        subscription.setTopic(callbackTopic);
+        subscription.setExpression(callbackTag);
+        subscriptionTable.put(subscription, messageCallbackCustomer);
+        //订阅多个topic如上面设置
+        consumerBean.setSubscriptionTable(subscriptionTable);
+        return consumerBean;
+    }
+
+
+}

+ 2 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/crawler/AlgGhAutoreplyVideoRankDataMapper.java

@@ -31,4 +31,6 @@ public interface AlgGhAutoreplyVideoRankDataMapper {
     int updateByPrimaryKey(AlgGhAutoreplyVideoRankData row);
 
     String selectLatestDtVersionByStrategyKey(String strategyKey);
+
+    String selectLatestDtVersionByStrategyKeyAndGhId(@Param("strategyKey")String strategyKey, @Param("ghId")String ghId);
 }

+ 34 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/crawler/PushMessageCallbackMapper.java

@@ -0,0 +1,34 @@
+package com.tzld.longarticle.recommend.server.mapper.crawler;
+
+import com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback;
+import com.tzld.longarticle.recommend.server.repository.model.PushMessageCallbackExample;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface PushMessageCallbackMapper {
+    long countByExample(PushMessageCallbackExample example);
+
+    int deleteByExample(PushMessageCallbackExample example);
+
+    int deleteByPrimaryKey(Long id);
+
+    int insert(PushMessageCallback row);
+
+    int insertSelective(PushMessageCallback row);
+
+    List<PushMessageCallback> selectByExample(PushMessageCallbackExample example);
+
+    PushMessageCallback selectByPrimaryKey(Long id);
+
+    int updateByExampleSelective(@Param("row") PushMessageCallback row, @Param("example") PushMessageCallbackExample example);
+
+    int updateByExample(@Param("row") PushMessageCallback row, @Param("example") PushMessageCallbackExample example);
+
+    int updateByPrimaryKeySelective(PushMessageCallback row);
+
+    int updateByPrimaryKey(PushMessageCallback row);
+
+    int insertList(@Param("list") List<PushMessageCallback> list);
+}

+ 20 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/bo/MiniData.java

@@ -0,0 +1,20 @@
+package com.tzld.longarticle.recommend.server.model.bo;
+
+import lombok.Data;
+
+@Data
+public class MiniData {
+
+    private Integer msgType;
+
+    private String title;
+
+    private String coverUrl;
+
+    private String miniAppId;
+
+    private String miniPagePath;
+
+    private Long miniVideoId;
+
+}

+ 11 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/bo/ReplyInfo.java

@@ -0,0 +1,11 @@
+package com.tzld.longarticle.recommend.server.model.bo;
+
+import lombok.Data;
+
+@Data
+public class ReplyInfo {
+
+    private Integer msgType;
+
+    private Long miniVideoId;
+}

+ 4 - 46
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/cgi/BucketDataParam.java

@@ -1,7 +1,10 @@
 package com.tzld.longarticle.recommend.server.model.cgi;
 
+import lombok.Data;
+
 import java.util.List;
 
+@Data
 public class BucketDataParam {
 
     private String accountId;
@@ -16,51 +19,6 @@ public class BucketDataParam {
 
     private List<ArticleInfo> articleList;
 
-    public String getAccountId() {
-        return accountId;
-    }
-
-    public void setAccountId(String accountId) {
-        this.accountId = accountId;
-    }
-
-    public String getAccountName() {
-        return accountName;
-    }
-
-    public void setAccountName(String accountName) {
-        this.accountName = accountName;
-    }
-
-    public String getGhId() {
-        return ghId;
-    }
-
-    public void setGhId(String ghId) {
-        this.ghId = ghId;
-    }
-
-    public String getStrategy() {
-        return strategy;
-    }
-
-    public void setStrategy(String strategy) {
-        this.strategy = strategy;
-    }
-
-    public String getPlanId() {
-        return planId;
-    }
-
-    public void setPlanId(String planId) {
-        this.planId = planId;
-    }
-
-    public List<ArticleInfo> getArticleList() {
-        return articleList;
-    }
+    private String channel;
 
-    public void setArticleList(List<ArticleInfo> articleList) {
-        this.articleList = articleList;
-    }
 }

+ 9 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/AccessTokenParam.java

@@ -0,0 +1,9 @@
+package com.tzld.longarticle.recommend.server.model.param;
+
+import lombok.Data;
+
+@Data
+public class AccessTokenParam {
+
+    private String secret;
+}

+ 21 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/CallbackParam.java

@@ -0,0 +1,21 @@
+package com.tzld.longarticle.recommend.server.model.param;
+
+import com.tzld.longarticle.recommend.server.model.bo.ReplyInfo;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class CallbackParam {
+
+    private String ghId;
+
+    private String accessToken;
+
+    private String openId;
+
+    private Long timestamp;
+
+    private List<ReplyInfo> replyInfo;
+
+}

+ 11 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/PushMessageParam.java

@@ -0,0 +1,11 @@
+package com.tzld.longarticle.recommend.server.model.param;
+
+import lombok.Data;
+
+@Data
+public class PushMessageParam {
+
+    private String accessToken;
+
+    private String ghId;
+}

+ 11 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/param/WeComPushMessageParam.java

@@ -0,0 +1,11 @@
+package com.tzld.longarticle.recommend.server.model.param;
+
+import lombok.Data;
+
+@Data
+public class WeComPushMessageParam {
+
+    private String corpId;
+
+    private String userId;
+}

+ 11 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/AccessTokenVo.java

@@ -0,0 +1,11 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class AccessTokenVo {
+
+    private String accessToken;
+
+    private Long expires;
+}

+ 14 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/PushMessageVo.java

@@ -0,0 +1,14 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import com.tzld.longarticle.recommend.server.model.bo.MiniData;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class PushMessageVo {
+
+    private Integer groupIdx;
+
+    private List<MiniData> components;
+}

+ 14 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/WeComPushMessageVo.java

@@ -0,0 +1,14 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import com.tzld.longarticle.recommend.server.model.cgi.MsgData;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WeComPushMessageVo {
+
+    private Integer groupIndex;
+
+    private List<MsgData> msgDataList;
+}

+ 57 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mq/MessageCallbackCustomer.java

@@ -0,0 +1,57 @@
+package com.tzld.longarticle.recommend.server.mq;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.openservices.ons.api.Action;
+import com.aliyun.openservices.ons.api.ConsumeContext;
+import com.aliyun.openservices.ons.api.Message;
+import com.aliyun.openservices.ons.api.MessageListener;
+import com.tzld.longarticle.recommend.server.mapper.crawler.PushMessageCallbackMapper;
+import com.tzld.longarticle.recommend.server.model.bo.ReplyInfo;
+import com.tzld.longarticle.recommend.server.model.param.CallbackParam;
+import com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@Component
+public class MessageCallbackCustomer implements MessageListener {
+
+
+    @Autowired
+    private PushMessageCallbackMapper pushMessageCallbackMapper;
+
+    @Override
+    public Action consume(Message message, ConsumeContext consumeContext) {
+        CallbackParam param = JSONObject.parseObject(new String(message.getBody()), CallbackParam.class);
+        log.info("param = {}", param);
+        if (CollectionUtils.isEmpty(param.getReplyInfo())) {
+            log.error("CallbackParam replyInfo is empty {}", param);
+        }
+        PushMessageCallback pushMessageCallback = new PushMessageCallback();
+        pushMessageCallback.setGhId(param.getGhId());
+        pushMessageCallback.setTimestamp(param.getTimestamp());
+        pushMessageCallback.setOpenId(param.getOpenId());
+        List<PushMessageCallback> insertList = new ArrayList<>();
+        for (ReplyInfo replyInfo : param.getReplyInfo()) {
+            PushMessageCallback insertPushMessageCallback = new PushMessageCallback();
+            BeanUtils.copyProperties(pushMessageCallback, insertPushMessageCallback);
+            insertPushMessageCallback.setMsgType(replyInfo.getMsgType());
+            insertPushMessageCallback.setVideoId(replyInfo.getMiniVideoId());
+            insertList.add(insertPushMessageCallback);
+        }
+        try {
+            pushMessageCallbackMapper.insertList(insertList);
+            return Action.CommitMessage;
+        } catch (Exception e) {
+            log.error("PushMessageCallback insert error {}", pushMessageCallback);
+        }
+        return Action.ReconsumeLater;
+    }
+}

+ 68 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mq/MessageCallbackProducer.java

@@ -0,0 +1,68 @@
+package com.tzld.longarticle.recommend.server.mq;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyun.openservices.ons.api.Message;
+import com.aliyun.openservices.ons.api.SendResult;
+import com.aliyun.openservices.ons.api.bean.ProducerBean;
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.common.response.ExceptionCodeEnum;
+import com.tzld.longarticle.recommend.server.model.param.CallbackParam;
+import com.tzld.longarticle.recommend.server.service.exterior.AccessTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.nio.charset.StandardCharsets;
+
+@Slf4j
+@Component
+public class MessageCallbackProducer {
+
+    @Value("${pushMessage.callback.topic}")
+    private String TOPIC;
+
+    @Value("${pushMessage.callback.tag}")
+    private String TAG;
+
+    @Autowired
+    private ProducerBean producer;
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    public CommonResponse<Void> sendMessage(CallbackParam param) {
+        if (param == null || param.getAccessToken() == null) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "参数错误");
+        }
+        if (!accessTokenService.validateAccessToken(param.getAccessToken())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "accessToken错误或者已失效");
+        }
+        Message message = new Message();
+        message.setTopic(TOPIC);
+        message.setTag(TAG);
+        message.setBody(JSON.toJSONString(param).getBytes(StandardCharsets.UTF_8));
+        try {
+            log.info("sendMessage = {}", message);
+            producer.send(message);
+        } catch (Exception e) {
+            log.error("error send param = {}", param);
+            log.error("error", e);
+            //重试
+            retry(message);
+        }
+        return CommonResponse.success();
+    }
+
+    private void retry(Message message) {
+        for (int i = 0; i < 3; i++) {
+            try {
+                SendResult sendResult = producer.send(message);
+                log.info("sendResult = {}", sendResult);
+                return;
+            } catch (Exception e) {
+                log.error("retry send error {}", i, e);
+            }
+        }
+    }
+}

+ 88 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/model/PushMessageCallback.java

@@ -0,0 +1,88 @@
+package com.tzld.longarticle.recommend.server.repository.model;
+
+import java.util.Date;
+
+public class PushMessageCallback {
+    private Long id;
+
+    private String ghId;
+
+    private String openId;
+
+    private Long timestamp;
+
+    private Integer msgType;
+
+    private Long videoId;
+
+    private Date createTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getGhId() {
+        return ghId;
+    }
+
+    public void setGhId(String ghId) {
+        this.ghId = ghId == null ? null : ghId.trim();
+    }
+
+    public String getOpenId() {
+        return openId;
+    }
+
+    public void setOpenId(String openId) {
+        this.openId = openId == null ? null : openId.trim();
+    }
+
+    public Long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(Long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public Integer getMsgType() {
+        return msgType;
+    }
+
+    public void setMsgType(Integer msgType) {
+        this.msgType = msgType;
+    }
+
+    public Long getVideoId() {
+        return videoId;
+    }
+
+    public void setVideoId(Long videoId) {
+        this.videoId = videoId;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    @Override
+    public String toString() {
+        return "PushMessageCallback{" +
+                "id=" + id +
+                ", ghId='" + ghId + '\'' +
+                ", openId='" + openId + '\'' +
+                ", timestamp=" + timestamp +
+                ", msgType=" + msgType +
+                ", videoId=" + videoId +
+                ", createTime=" + createTime +
+                '}';
+    }
+}

+ 640 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/model/PushMessageCallbackExample.java

@@ -0,0 +1,640 @@
+package com.tzld.longarticle.recommend.server.repository.model;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class PushMessageCallbackExample {
+    protected String orderByClause;
+
+    protected boolean distinct;
+
+    protected List<Criteria> oredCriteria;
+
+    public PushMessageCallbackExample() {
+        oredCriteria = new ArrayList<>();
+    }
+
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    public List<Criteria> getOredCriteria() {
+        return oredCriteria;
+    }
+
+    public void or(Criteria criteria) {
+        oredCriteria.add(criteria);
+    }
+
+    public Criteria or() {
+        Criteria criteria = createCriteriaInternal();
+        oredCriteria.add(criteria);
+        return criteria;
+    }
+
+    public Criteria createCriteria() {
+        Criteria criteria = createCriteriaInternal();
+        if (oredCriteria.size() == 0) {
+            oredCriteria.add(criteria);
+        }
+        return criteria;
+    }
+
+    protected Criteria createCriteriaInternal() {
+        Criteria criteria = new Criteria();
+        return criteria;
+    }
+
+    public void clear() {
+        oredCriteria.clear();
+        orderByClause = null;
+        distinct = false;
+    }
+
+    protected abstract static class GeneratedCriteria {
+        protected List<Criterion> criteria;
+
+        protected GeneratedCriteria() {
+            super();
+            criteria = new ArrayList<>();
+        }
+
+        public boolean isValid() {
+            return criteria.size() > 0;
+        }
+
+        public List<Criterion> getAllCriteria() {
+            return criteria;
+        }
+
+        public List<Criterion> getCriteria() {
+            return criteria;
+        }
+
+        protected void addCriterion(String condition) {
+            if (condition == null) {
+                throw new RuntimeException("Value for condition cannot be null");
+            }
+            criteria.add(new Criterion(condition));
+        }
+
+        protected void addCriterion(String condition, Object value, String property) {
+            if (value == null) {
+                throw new RuntimeException("Value for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value));
+        }
+
+        protected void addCriterion(String condition, Object value1, Object value2, String property) {
+            if (value1 == null || value2 == null) {
+                throw new RuntimeException("Between values for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value1, value2));
+        }
+
+        public Criteria andIdIsNull() {
+            addCriterion("id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIsNotNull() {
+            addCriterion("id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdEqualTo(Long value) {
+            addCriterion("id =", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotEqualTo(Long value) {
+            addCriterion("id <>", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThan(Long value) {
+            addCriterion("id >", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("id >=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThan(Long value) {
+            addCriterion("id <", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThanOrEqualTo(Long value) {
+            addCriterion("id <=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIn(List<Long> values) {
+            addCriterion("id in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotIn(List<Long> values) {
+            addCriterion("id not in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdBetween(Long value1, Long value2) {
+            addCriterion("id between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotBetween(Long value1, Long value2) {
+            addCriterion("id not between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdIsNull() {
+            addCriterion("gh_id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdIsNotNull() {
+            addCriterion("gh_id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdEqualTo(String value) {
+            addCriterion("gh_id =", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdNotEqualTo(String value) {
+            addCriterion("gh_id <>", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdGreaterThan(String value) {
+            addCriterion("gh_id >", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdGreaterThanOrEqualTo(String value) {
+            addCriterion("gh_id >=", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdLessThan(String value) {
+            addCriterion("gh_id <", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdLessThanOrEqualTo(String value) {
+            addCriterion("gh_id <=", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdLike(String value) {
+            addCriterion("gh_id like", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdNotLike(String value) {
+            addCriterion("gh_id not like", value, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdIn(List<String> values) {
+            addCriterion("gh_id in", values, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdNotIn(List<String> values) {
+            addCriterion("gh_id not in", values, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdBetween(String value1, String value2) {
+            addCriterion("gh_id between", value1, value2, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andGhIdNotBetween(String value1, String value2) {
+            addCriterion("gh_id not between", value1, value2, "ghId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdIsNull() {
+            addCriterion("open_id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdIsNotNull() {
+            addCriterion("open_id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdEqualTo(String value) {
+            addCriterion("open_id =", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdNotEqualTo(String value) {
+            addCriterion("open_id <>", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdGreaterThan(String value) {
+            addCriterion("open_id >", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdGreaterThanOrEqualTo(String value) {
+            addCriterion("open_id >=", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdLessThan(String value) {
+            addCriterion("open_id <", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdLessThanOrEqualTo(String value) {
+            addCriterion("open_id <=", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdLike(String value) {
+            addCriterion("open_id like", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdNotLike(String value) {
+            addCriterion("open_id not like", value, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdIn(List<String> values) {
+            addCriterion("open_id in", values, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdNotIn(List<String> values) {
+            addCriterion("open_id not in", values, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdBetween(String value1, String value2) {
+            addCriterion("open_id between", value1, value2, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andOpenIdNotBetween(String value1, String value2) {
+            addCriterion("open_id not between", value1, value2, "openId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampIsNull() {
+            addCriterion("timestamp is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampIsNotNull() {
+            addCriterion("timestamp is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampEqualTo(Long value) {
+            addCriterion("timestamp =", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampNotEqualTo(Long value) {
+            addCriterion("timestamp <>", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampGreaterThan(Long value) {
+            addCriterion("timestamp >", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampGreaterThanOrEqualTo(Long value) {
+            addCriterion("timestamp >=", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampLessThan(Long value) {
+            addCriterion("timestamp <", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampLessThanOrEqualTo(Long value) {
+            addCriterion("timestamp <=", value, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampIn(List<Long> values) {
+            addCriterion("timestamp in", values, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampNotIn(List<Long> values) {
+            addCriterion("timestamp not in", values, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampBetween(Long value1, Long value2) {
+            addCriterion("timestamp between", value1, value2, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andTimestampNotBetween(Long value1, Long value2) {
+            addCriterion("timestamp not between", value1, value2, "timestamp");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeIsNull() {
+            addCriterion("msg_type is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeIsNotNull() {
+            addCriterion("msg_type is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeEqualTo(Integer value) {
+            addCriterion("msg_type =", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeNotEqualTo(Integer value) {
+            addCriterion("msg_type <>", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeGreaterThan(Integer value) {
+            addCriterion("msg_type >", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeGreaterThanOrEqualTo(Integer value) {
+            addCriterion("msg_type >=", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeLessThan(Integer value) {
+            addCriterion("msg_type <", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeLessThanOrEqualTo(Integer value) {
+            addCriterion("msg_type <=", value, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeIn(List<Integer> values) {
+            addCriterion("msg_type in", values, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeNotIn(List<Integer> values) {
+            addCriterion("msg_type not in", values, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeBetween(Integer value1, Integer value2) {
+            addCriterion("msg_type between", value1, value2, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andMsgTypeNotBetween(Integer value1, Integer value2) {
+            addCriterion("msg_type not between", value1, value2, "msgType");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdIsNull() {
+            addCriterion("video_id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdIsNotNull() {
+            addCriterion("video_id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdEqualTo(Long value) {
+            addCriterion("video_id =", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdNotEqualTo(Long value) {
+            addCriterion("video_id <>", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdGreaterThan(Long value) {
+            addCriterion("video_id >", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("video_id >=", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdLessThan(Long value) {
+            addCriterion("video_id <", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdLessThanOrEqualTo(Long value) {
+            addCriterion("video_id <=", value, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdIn(List<Long> values) {
+            addCriterion("video_id in", values, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdNotIn(List<Long> values) {
+            addCriterion("video_id not in", values, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdBetween(Long value1, Long value2) {
+            addCriterion("video_id between", value1, value2, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andVideoIdNotBetween(Long value1, Long value2) {
+            addCriterion("video_id not between", value1, value2, "videoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIsNull() {
+            addCriterion("create_time is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIsNotNull() {
+            addCriterion("create_time is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeEqualTo(Date value) {
+            addCriterion("create_time =", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotEqualTo(Date value) {
+            addCriterion("create_time <>", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeGreaterThan(Date value) {
+            addCriterion("create_time >", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeGreaterThanOrEqualTo(Date value) {
+            addCriterion("create_time >=", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeLessThan(Date value) {
+            addCriterion("create_time <", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeLessThanOrEqualTo(Date value) {
+            addCriterion("create_time <=", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIn(List<Date> values) {
+            addCriterion("create_time in", values, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotIn(List<Date> values) {
+            addCriterion("create_time not in", values, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeBetween(Date value1, Date value2) {
+            addCriterion("create_time between", value1, value2, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotBetween(Date value1, Date value2) {
+            addCriterion("create_time not between", value1, value2, "createTime");
+            return (Criteria) this;
+        }
+    }
+
+    public static class Criteria extends GeneratedCriteria {
+        protected Criteria() {
+            super();
+        }
+    }
+
+    public static class Criterion {
+        private String condition;
+
+        private Object value;
+
+        private Object secondValue;
+
+        private boolean noValue;
+
+        private boolean singleValue;
+
+        private boolean betweenValue;
+
+        private boolean listValue;
+
+        private String typeHandler;
+
+        public String getCondition() {
+            return condition;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getSecondValue() {
+            return secondValue;
+        }
+
+        public boolean isNoValue() {
+            return noValue;
+        }
+
+        public boolean isSingleValue() {
+            return singleValue;
+        }
+
+        public boolean isBetweenValue() {
+            return betweenValue;
+        }
+
+        public boolean isListValue() {
+            return listValue;
+        }
+
+        public String getTypeHandler() {
+            return typeHandler;
+        }
+
+        protected Criterion(String condition) {
+            super();
+            this.condition = condition;
+            this.typeHandler = null;
+            this.noValue = true;
+        }
+
+        protected Criterion(String condition, Object value, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.typeHandler = typeHandler;
+            if (value instanceof List<?>) {
+                this.listValue = true;
+            } else {
+                this.singleValue = true;
+            }
+        }
+
+        protected Criterion(String condition, Object value) {
+            this(condition, value, null);
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.secondValue = secondValue;
+            this.typeHandler = typeHandler;
+            this.betweenValue = true;
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue) {
+            this(condition, value, secondValue, null);
+        }
+    }
+}

+ 12 - 5
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/cgi/TouLiuHttpClientService.java

@@ -22,8 +22,15 @@ public class TouLiuHttpClientService {
 //                .build();
 //    }
 
-    public String sendAdFlowAddRequest(String baseUrl, String videoId, String putTypeOne,
-                                       String putTypeTwo, String putTypeThree, String putCarrierId) {
+    public String sendAdFlowAddRequest(String baseUrl,
+                                       String videoId,
+                                       String putScene,
+                                       String channel,
+                                       String remark,
+                                       String putTypeOne,
+                                       String putTypeTwo,
+                                       String putTypeThree,
+                                       String putCarrierId) {
         WebClient webClient = WebClient.builder()
                 .baseUrl(baseUrl)
                 .build();
@@ -31,9 +38,9 @@ public class TouLiuHttpClientService {
         // 构建请求体  
         String jsonBody = "{" +
                 "\"videoId\":\"" + videoId + "\"," +
-                "\"putScene\":\"" + "touliu" + "\"," +
-                "\"channel\":\"" + "tencentgzh" + "\"," +
-                "\"remark\":\"" + "自动" + "\"," +
+                "\"putScene\":\"" + putScene + "\"," +
+                "\"channel\":\"" + channel + "\"," +
+                "\"remark\":\"" + remark + "\"," +
                 "\"putTypeOne\":\"" + putTypeOne + "\"," +
                 "\"putTypeTwo\":\"" + putTypeTwo + "\"," +
                 "\"putTypeThree\":\"" + putTypeThree + "\"," +

+ 16 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/AccessTokenService.java

@@ -0,0 +1,16 @@
+package com.tzld.longarticle.recommend.server.service.exterior;
+
+
+import com.tzld.longarticle.recommend.server.common.enums.SecretEnum;
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.model.param.AccessTokenParam;
+import com.tzld.longarticle.recommend.server.model.vo.AccessTokenVo;
+
+public interface AccessTokenService {
+
+    CommonResponse<AccessTokenVo> getAccessToken(AccessTokenParam param);
+
+    boolean validateAccessToken(String accessToken);
+
+    SecretEnum getSecretEnum(String accessToken);
+}

+ 14 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/ThirdPartyService.java

@@ -0,0 +1,14 @@
+package com.tzld.longarticle.recommend.server.service.exterior;
+
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.model.param.CallbackParam;
+import com.tzld.longarticle.recommend.server.model.param.PushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.PushMessageVo;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public interface ThirdPartyService {
+    CommonResponse<List<PushMessageVo>> getPushMessage(PushMessageParam param);
+}

+ 12 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/WeComService.java

@@ -0,0 +1,12 @@
+package com.tzld.longarticle.recommend.server.service.exterior;
+
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.model.param.WeComPushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.WeComPushMessageVo;
+
+import java.util.List;
+
+public interface WeComService {
+
+    CommonResponse<List<WeComPushMessageVo>> getPushMessage(WeComPushMessageParam param);
+}

+ 86 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/AccessTokenServiceImpl.java

@@ -0,0 +1,86 @@
+package com.tzld.longarticle.recommend.server.service.exterior.impl;
+
+import cn.hutool.core.lang.UUID;
+import com.tzld.longarticle.recommend.server.common.enums.SecretEnum;
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.common.response.ExceptionCodeEnum;
+import com.tzld.longarticle.recommend.server.model.param.AccessTokenParam;
+import com.tzld.longarticle.recommend.server.model.vo.AccessTokenVo;
+import com.tzld.longarticle.recommend.server.service.exterior.AccessTokenService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static com.tzld.longarticle.recommend.server.common.response.ErrcodeNamespace.NS_GENERAL;
+
+@Service
+public class AccessTokenServiceImpl implements AccessTokenService {
+
+    private static final String ACCESS_TOKEN_LIST = "access_token_list";
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @Override
+    public CommonResponse<AccessTokenVo> getAccessToken(AccessTokenParam param) throws RuntimeException {
+        if (param == null || StringUtils.isEmpty(param.getSecret())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "未找到 secret");
+        }
+        if (!SecretEnum.contains(param.getSecret())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "secret 不存在");
+        }
+        AccessTokenVo accessTokenVo = new AccessTokenVo();
+        String accessToken = redisTemplate.opsForValue().get(param.getSecret());
+        if (StringUtils.isNotEmpty(accessToken)) {
+            Long expire = Long.parseLong(String.valueOf(redisTemplate.getExpire(param.getSecret(), TimeUnit.SECONDS)));
+            accessTokenVo.setAccessToken(accessToken);
+            accessTokenVo.setExpires(expire);
+            if (redisTemplate.opsForHash().size(ACCESS_TOKEN_LIST) > 20) {
+                Map<Object, Object> entries = redisTemplate.opsForHash().entries(ACCESS_TOKEN_LIST);
+                entries.forEach((k, v) -> {
+                    long timestamp = Long.parseLong((String) v);
+                    if (System.currentTimeMillis() / 1000 > timestamp) {
+                        redisTemplate.opsForHash().delete(ACCESS_TOKEN_LIST, k);
+                    }
+                });
+            }
+        } else {
+            String newAccessToken = UUID.randomUUID().toString().replace("-", "");
+            redisTemplate.opsForValue().set(param.getSecret(), newAccessToken, 2L, TimeUnit.HOURS);
+            redisTemplate.opsForValue().set(newAccessToken, param.getSecret(), 3L, TimeUnit.HOURS);
+            long expire = 7200L;
+            String timestamp = String.valueOf(System.currentTimeMillis() / 1000 + expire + 600);
+            redisTemplate.opsForHash().put(ACCESS_TOKEN_LIST, newAccessToken, timestamp);
+            accessTokenVo.setAccessToken(newAccessToken);
+            accessTokenVo.setExpires(expire);
+        }
+        return CommonResponse.success(accessTokenVo);
+    }
+
+    @Override
+    public boolean validateAccessToken(String accessToken) {
+        Object o = redisTemplate.opsForHash().get(ACCESS_TOKEN_LIST, accessToken);
+        if (o == null) {
+            return false;
+        }
+        long timestamp = Long.parseLong((String) o);
+        if (System.currentTimeMillis() / 1000 > timestamp) {
+            redisTemplate.opsForHash().delete(ACCESS_TOKEN_LIST, accessToken);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public SecretEnum getSecretEnum(String accessToken) {
+        String secret = redisTemplate.opsForValue().get(accessToken);
+        if(StringUtils.isEmpty(secret)){
+            return null;
+        }
+        return SecretEnum.get(secret);
+    }
+}

+ 103 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/ThirdPartyServiceImpl.java

@@ -0,0 +1,103 @@
+package com.tzld.longarticle.recommend.server.service.exterior.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.tzld.longarticle.recommend.server.common.enums.SecretEnum;
+import com.tzld.longarticle.recommend.server.common.enums.cgi.ReplyStrategyServiceEnum;
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.common.response.ExceptionCodeEnum;
+import com.tzld.longarticle.recommend.server.model.bo.MiniData;
+import com.tzld.longarticle.recommend.server.model.cgi.BucketDataParam;
+import com.tzld.longarticle.recommend.server.model.cgi.GroupData;
+import com.tzld.longarticle.recommend.server.model.cgi.MsgData;
+import com.tzld.longarticle.recommend.server.model.cgi.ReplyBucketData;
+import com.tzld.longarticle.recommend.server.model.param.PushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.PushMessageVo;
+import com.tzld.longarticle.recommend.server.service.exterior.AccessTokenService;
+import com.tzld.longarticle.recommend.server.service.exterior.ThirdPartyService;
+import com.tzld.longarticle.recommend.server.service.strategy.reply.ReplyStrategyService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service
+public class ThirdPartyServiceImpl implements ThirdPartyService {
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    private Map<String, ReplyStrategyService> strategyServiceMap;
+
+    @PostConstruct
+    public void init() {
+        strategyServiceMap = applicationContext.getBeansOfType(ReplyStrategyService.class);
+    }
+
+
+    @Override
+    public CommonResponse<List<PushMessageVo>> getPushMessage(PushMessageParam param) {
+        if (param == null || StringUtils.isEmpty(param.getGhId()) || StringUtils.isEmpty(param.getAccessToken())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "参数错误");
+        }
+        if (!accessTokenService.validateAccessToken(param.getAccessToken())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "accessToken错误或者已失效");
+        }
+        SecretEnum secretEnum = accessTokenService.getSecretEnum(param.getAccessToken());
+        if (secretEnum == null) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "获取secret失败");
+        }
+        String channel = secretEnum.channel;
+        List<PushMessageVo> pushMessageVoList = new ArrayList<>();
+        ReplyBucketData replyBucketData = getPushMessageData(param, channel);
+        log.info("replyBucketData={}", JSON.toJSONString(replyBucketData));
+        if (replyBucketData == null) {
+            log.error("获取推送策略数据失败");
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "系统异常,获取失败");
+        }
+        List<GroupData> groupList = replyBucketData.getGroupList();
+        for (GroupData groupData : groupList) {
+            if (CollectionUtils.isEmpty(groupData.getMsgDataList())) {
+                continue;
+            }
+            PushMessageVo pushMessageVo = new PushMessageVo();
+            List<MiniData> components = new ArrayList<>();
+            for (MsgData msgData : groupData.getMsgDataList()) {
+                MiniData miniData = new MiniData();
+                BeanUtils.copyProperties(msgData, miniData);
+                components.add(miniData);
+            }
+            pushMessageVo.setGroupIdx(groupData.getGroupIndex());
+            pushMessageVo.setComponents(components);
+            pushMessageVoList.add(pushMessageVo);
+        }
+        return CommonResponse.success(pushMessageVoList);
+    }
+
+    private ReplyBucketData getPushMessageData(PushMessageParam param, String channel) {
+        log.info("strategyServiceMap={}", JSON.toJSONString(strategyServiceMap));
+        for (Map.Entry<String, ReplyStrategyService> stringReplyStrategyServiceEntry : strategyServiceMap.entrySet()) {
+            ReplyStrategyService replyStrategyService = stringReplyStrategyServiceEntry.getValue();
+            // 使用策略层
+            if (replyStrategyService.support(ReplyStrategyServiceEnum.PUSH_MESSAGE_STRATEGY_V1)) {
+                BucketDataParam bucketDataParam = new BucketDataParam();
+                bucketDataParam.setGhId(param.getGhId());
+                bucketDataParam.setChannel(channel);
+                return replyStrategyService.getResult(bucketDataParam);
+            }
+        }
+        // 无执行策略 不会走到这里
+        return null;
+    }
+}

+ 130 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/exterior/impl/WeComServiceImpl.java

@@ -0,0 +1,130 @@
+package com.tzld.longarticle.recommend.server.service.exterior.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.tzld.longarticle.recommend.server.common.enums.StaffEnum;
+import com.tzld.longarticle.recommend.server.common.enums.cgi.ReplyStrategyServiceEnum;
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.common.response.ExceptionCodeEnum;
+import com.tzld.longarticle.recommend.server.model.bo.MiniData;
+import com.tzld.longarticle.recommend.server.model.cgi.BucketDataParam;
+import com.tzld.longarticle.recommend.server.model.cgi.GroupData;
+import com.tzld.longarticle.recommend.server.model.cgi.MsgData;
+import com.tzld.longarticle.recommend.server.model.cgi.ReplyBucketData;
+import com.tzld.longarticle.recommend.server.model.param.PushMessageParam;
+import com.tzld.longarticle.recommend.server.model.param.WeComPushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.PushMessageVo;
+import com.tzld.longarticle.recommend.server.model.vo.WeComPushMessageVo;
+import com.tzld.longarticle.recommend.server.service.exterior.WeComService;
+import com.tzld.longarticle.recommend.server.service.strategy.reply.ReplyStrategyService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.PostConstruct;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+@Slf4j
+@Service
+public class WeComServiceImpl implements WeComService {
+
+    private static final String CORP_ID = "wwa4015dc7d652a21f";
+
+    private static final int MAX_BYTES = 64;
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    private Map<String, ReplyStrategyService> strategyServiceMap;
+
+    @PostConstruct
+    public void init() {
+        strategyServiceMap = applicationContext.getBeansOfType(ReplyStrategyService.class);
+    }
+
+    @Override
+    public CommonResponse<List<WeComPushMessageVo>> getPushMessage(WeComPushMessageParam param) {
+        if (param == null || StringUtils.isEmpty(param.getCorpId()) || !CORP_ID.equals(param.getCorpId()) || StringUtils.isEmpty(param.getUserId())) {
+            return CommonResponse.create(ExceptionCodeEnum.PARAM_ERROR, "参数错误");
+        }
+        List<WeComPushMessageVo> res = new ArrayList<>();
+        ReplyBucketData replyBucketData = getPushMessageData(param);
+        List<GroupData> groupList = replyBucketData.getGroupList();
+        log.info("groupList={}", groupList);
+        if (CollectionUtils.isEmpty(groupList)) {
+            return CommonResponse.create(500, "数据异常");
+        }
+        String name = StaffEnum.getName(param.getUserId());
+        for (GroupData groupData : groupList) {
+            if (CollectionUtils.isEmpty(groupData.getMsgDataList())) {
+                continue;
+            }
+            WeComPushMessageVo weComPushMessageVo = new WeComPushMessageVo();
+            weComPushMessageVo.setGroupIndex(groupData.getGroupIndex());
+            List<MsgData> msgDataList = groupData.getMsgDataList();
+            for (MsgData msgData : msgDataList) {
+                if (msgData.getTitle().getBytes(StandardCharsets.UTF_8).length > MAX_BYTES) {
+                    String s = truncateString(msgData.getTitle(), MAX_BYTES - 3) + "...";
+                    msgData.setTitle(s);
+                }
+            }
+            MsgData msgData = new MsgData();
+            msgData.setMsgType(3);
+            msgData.setTitle(String.format("很高兴认识您!我是%s,每天给您推荐精彩视频~", name));
+            msgDataList.add(0, msgData);
+            weComPushMessageVo.setMsgDataList(msgDataList);
+            res.add(weComPushMessageVo);
+        }
+        log.info("res={}", res);
+        return CommonResponse.success(res);
+    }
+
+    private ReplyBucketData getPushMessageData(WeComPushMessageParam param) {
+        log.info("strategyServiceMap={}", JSON.toJSONString(strategyServiceMap));
+        for (Map.Entry<String, ReplyStrategyService> stringReplyStrategyServiceEntry : strategyServiceMap.entrySet()) {
+            ReplyStrategyService replyStrategyService = stringReplyStrategyServiceEntry.getValue();
+            // 使用策略层
+            if (replyStrategyService.support(ReplyStrategyServiceEnum.WE_COM_PUSH_MESSAGE_STRATEGY_V1)) {
+                BucketDataParam bucketDataParam = new BucketDataParam();
+                bucketDataParam.setGhId(param.getUserId());
+                return replyStrategyService.getResult(bucketDataParam);
+            }
+        }
+        // 无执行策略 不会走到这里
+        return null;
+    }
+
+    private String truncateString(String input, int maxBytes) {
+        if (input == null || maxBytes <= 0) {
+            return "";
+        }
+
+        byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
+        if (bytes.length <= maxBytes) {
+            return input; // 如果字节数已经在限制内,直接返回原字符串
+        }
+
+        // 截取字节数组
+        byte[] truncatedBytes = new byte[maxBytes];
+        System.arraycopy(bytes, 0, truncatedBytes, 0, maxBytes);
+
+        // 将截取的字节数组转换回字符串
+        String truncatedString = new String(truncatedBytes, StandardCharsets.UTF_8);
+
+        // 处理可能的字符截断问题
+        // 如果截取后字符串的字节数仍然大于 maxBytes,向前查找直到找到有效字符
+        while (truncatedString.getBytes(StandardCharsets.UTF_8).length > maxBytes) {
+            truncatedString = truncatedString.substring(0, truncatedString.length() - 1);
+        }
+
+        return truncatedString;
+    }
+}

+ 8 - 5
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/BuckStrategyV1.java

@@ -48,7 +48,8 @@ public class BuckStrategyV1 implements ReplyStrategyService {
 
     private static final String CDN_URL = "https://rescdn.piaoquantv.com/";
 
-    private static final String GET_SMALL_PAGE_URL = "https://api.piaoquantv.com";
+    @Value("${small_page_url}")
+    private String GET_SMALL_PAGE_URL;
 
     @Autowired
     private AlgGhAutoreplyVideoRankDataMapper algGhAutoreplyVideoRankDataMapper;
@@ -197,13 +198,14 @@ public class BuckStrategyV1 implements ReplyStrategyService {
         if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
             return smallDataCgiReplyList;
         }
-        Set<String> keys = smallDataCgiReplyList.stream().map(x -> x.getGhId() + "&" + x.getMiniVideoId()).collect(Collectors.toSet());
+        Set<String> keys = smallDataCgiReplyList.stream().map(x -> x.getGhId() + "&" + x.getMiniVideoId() + "&" + x.getSort()).collect(Collectors.toSet());
         Map<String, SmallPageUrlDetail> keyPageUrl = new HashMap<>();
-        // gh-id + videoId 复用同一page_url及落地页id
+        // gh-id + videoId + sort 复用同一page_url及落地页id
         for (String key : keys) {
             String[] keyArr = key.split("&");
             String ghId = keyArr[0];
             String videoId = keyArr[1];
+            String sort = keyArr[2];
             // 查询库里是否存在,如果存在即复用
             CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
             cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMiniVideoIdEqualTo(Long.valueOf(videoId)).andGhIdEqualTo(ghId);
@@ -211,7 +213,7 @@ public class BuckStrategyV1 implements ReplyStrategyService {
             SmallPageUrlDetail smallPageUrlDetail = new SmallPageUrlDetail();
             if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
                 // 库里不存在,调用新生成
-                String response = httpClientService.sendAdFlowAddRequest(GET_SMALL_PAGE_URL, videoId, "公众号", "自动回复小程序", "位置X", ghId);
+                String response = httpClientService.sendAdFlowAddRequest(GET_SMALL_PAGE_URL, videoId, "touliu","tencentgzh","自动","公众号", "自动回复小程序", "位置" + sort, ghId);
                 JSONObject jsonObject = JSON.parseObject(response);
                 if (jsonObject.getInteger("code").equals(0)) {
                     smallPageUrlDetail = jsonObject.getObject("data", SmallPageUrlDetail.class);
@@ -230,7 +232,7 @@ public class BuckStrategyV1 implements ReplyStrategyService {
         }
         // 处理数据
         for (CgiReplyBucketData cgiReplyBucketData : smallDataCgiReplyList) {
-            String key = cgiReplyBucketData.getGhId() + "&" + cgiReplyBucketData.getMiniVideoId();
+            String key = cgiReplyBucketData.getGhId() + "&" + cgiReplyBucketData.getMiniVideoId() + "&" + cgiReplyBucketData.getSort();
             SmallPageUrlDetail smallPageUrlDetail = keyPageUrl.get(key);
             if (Objects.isNull(smallPageUrlDetail)) {
                 log.error("setSmallPageUrl get map url is null" + JSON.toJSONString(keyPageUrl));
@@ -242,6 +244,7 @@ public class BuckStrategyV1 implements ReplyStrategyService {
         return smallDataCgiReplyList;
     }
 
+
     private List<CgiReplyBucketData> readStrategyOrderSmallData(Set<String> keyedSet) {
         List<CgiReplyBucketData> result = new ArrayList<>();
         for (String key : keyedSet) {

+ 254 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/PushMessageStrategyV1.java

@@ -0,0 +1,254 @@
+package com.tzld.longarticle.recommend.server.service.strategy.reply.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.longarticle.recommend.server.common.enums.cgi.ReplyStrategyServiceEnum;
+import com.tzld.longarticle.recommend.server.mapper.crawler.AlgGhAutoreplyVideoRankDataMapper;
+import com.tzld.longarticle.recommend.server.mapper.crawler.CgiReplyBucketDataMapper;
+import com.tzld.longarticle.recommend.server.model.cgi.*;
+import com.tzld.longarticle.recommend.server.repository.model.AlgGhAutoreplyVideoRankData;
+import com.tzld.longarticle.recommend.server.repository.model.AlgGhAutoreplyVideoRankDataExample;
+import com.tzld.longarticle.recommend.server.repository.model.CgiReplyBucketData;
+import com.tzld.longarticle.recommend.server.repository.model.CgiReplyBucketDataExample;
+import com.tzld.longarticle.recommend.server.service.cgi.TouLiuHttpClientService;
+import com.tzld.longarticle.recommend.server.service.strategy.reply.ReplyStrategyService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class PushMessageStrategyV1 implements ReplyStrategyService {
+
+    /**
+     * 实验分桶数量
+     */
+//    private static final Integer bucketNum = 10;
+
+    /**
+     * 分桶实验策略,key为策略,arr为对应桶
+     * {"base":[0,1,2,3],"stg0909-base":[4,5],"stg0909-explore1":[6,7,8],"stg0909-explore2":[9]}
+     * {"stg0909-base":[5,6],"stg0909-explore1":[7],"stg0909-explore2":[8,9]}
+     */
+    private static final String bucketStrategyConfig = "{\"3rd-party-base\":[0,1,2,3,4,5,6],\"3rd-party-explore1\":[7],\"3rd-party-explore2\":[8,9]}";
+
+    /**
+     * 自动回复使用小程序Id
+     */
+    private static final String SMALL_APP_Id = "wxbdd2a2e93d9a6e25";
+
+    private static final String CDN_URL = "https://rescdn.piaoquantv.com/";
+
+    @Value("${small_page_url}")
+    private String GET_SMALL_PAGE_URL;
+
+    @Autowired
+    private AlgGhAutoreplyVideoRankDataMapper algGhAutoreplyVideoRankDataMapper;
+    @Autowired
+    private CgiReplyBucketDataMapper cgiReplyBucketDataMapper;
+    @Autowired
+    private TouLiuHttpClientService httpClientService;
+
+    @Override
+    public ReplyBucketData getResult(BucketDataParam bucketDataParam) {
+        log.info("PushMessageStrategyV1 start");
+        // 0 获取策略key
+        JSONObject bucketStrategyConfigJsonObject = JSON.parseObject(bucketStrategyConfig);
+        Set<String> keyedSet = bucketStrategyConfigJsonObject.keySet();
+        // 1 处理文章--算法引擎--排序文章数据
+//        getWenzhangData();
+        // 2 处理小程序--读取离线数据表--获取策略排序小程序数据
+        List<CgiReplyBucketData> smallDataCgiReplyList = readStrategyOrderSmallData(keyedSet, bucketDataParam);
+        // 2.1 获取小程序落地页地址 http调用
+        smallDataCgiReplyList = setSmallPageUrl(smallDataCgiReplyList, bucketDataParam.getChannel());
+        log.info(JSON.toJSONString(smallDataCgiReplyList));
+        // 3 入库读表
+        insertSmallData(smallDataCgiReplyList, keyedSet, bucketDataParam);
+        // 4 组装分桶数据
+        return getReplyBucketData(bucketStrategyConfigJsonObject, keyedSet, bucketDataParam.getGhId());
+    }
+
+    private ReplyBucketData getReplyBucketData(JSONObject bucketStrategyConfigJsonObject, Set<String> keyedSet, String ghId) {
+        // 策略小程序数据
+        ReplyBucketData replyBucketData = new ReplyBucketData();
+        List<GroupData> groupDataList = new ArrayList<>();
+        for (String key : keyedSet) {
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo(ghId);
+            cgiReplyBucketDataExample.setOrderByClause("sort");
+            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                CgiReplyBucketDataExample cgiReplyBucketDataExampleNull = new CgiReplyBucketDataExample();
+                cgiReplyBucketDataExampleNull.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo("default");
+                cgiReplyBucketDataExampleNull.setOrderByClause("sort");
+                cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExampleNull);
+            }
+            log.info("cgiReplyBucketData={}", cgiReplyBucketData);
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                log.error("getReplyBucketData get data is null,key:" + key);
+                return null;
+            }
+
+            List<Integer> groupList = bucketStrategyConfigJsonObject.getJSONArray(key).toJavaList(Integer.class);
+            for (Integer group : groupList) {
+                GroupData groupData = new GroupData();
+                groupData.setGroupIndex(group);
+                List<MsgData> msgDataList = new ArrayList<>();
+                for (CgiReplyBucketData cgiReplyBucketDatum : cgiReplyBucketData) {
+                    MsgData msgData = new MsgData();
+                    BeanUtils.copyProperties(cgiReplyBucketDatum, msgData);
+                    if (cgiReplyBucketDatum.getMsgType().equals(1)) {
+                        msgData.setMiniAppId(SMALL_APP_Id);
+                    }
+                    msgDataList.add(msgData);
+                }
+                groupData.setMsgDataList(msgDataList);
+                groupDataList.add(groupData);
+            }
+        }
+        // groupDataList排序
+        replyBucketData.setGroupList(groupDataList.stream().sorted(Comparator.comparingInt(GroupData::getGroupIndex)).collect(Collectors.toList()));
+        return replyBucketData;
+    }
+
+
+    private void insertSmallData(List<CgiReplyBucketData> smallDataCgiReplyList, Set<String> keyedSet, BucketDataParam bucketDataParam) {
+        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
+            return;
+        }
+        for (String key : keyedSet) {
+            if ("base".equals(key)) {
+                continue;
+            }
+            List<CgiReplyBucketData> collect = smallDataCgiReplyList.stream()
+                    .filter(x -> x.getStrategy().equals(key))
+                    .filter(x -> x.getGhId().equals(bucketDataParam.getGhId()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(collect)) {
+                log.error("PushMessageStrategyV1 insertSmallData 算法排序数据异常,data:" + JSON.toJSONString(smallDataCgiReplyList));
+                continue;
+            }
+            // 清上个版本的策略数据
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMsgTypeEqualTo(1).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
+            List<CgiReplyBucketData> cgiReplyBucketData1 = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            for (CgiReplyBucketData cgiReplyBucketData : cgiReplyBucketData1) {
+                cgiReplyBucketData.setIsDelete(1);
+                cgiReplyBucketDataMapper.updateByPrimaryKeySelective(cgiReplyBucketData);
+            }
+            // 入库
+            for (CgiReplyBucketData cgiReplyBucketData : collect) {
+                cgiReplyBucketDataMapper.insertSelective(cgiReplyBucketData);
+            }
+        }
+    }
+
+    private List<CgiReplyBucketData> setSmallPageUrl(List<CgiReplyBucketData> smallDataCgiReplyList, String channel) {
+        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
+            return smallDataCgiReplyList;
+        }
+        Set<String> keys = smallDataCgiReplyList.stream().map(x -> x.getGhId() + "&" + x.getMiniVideoId() + "&" + x.getSort()).collect(Collectors.toSet());
+        Map<String, SmallPageUrlDetail> keyPageUrl = new HashMap<>();
+        // gh-id + videoId + sort 复用同一page_url及落地页id
+        for (String key : keys) {
+            String[] keyArr = key.split("&");
+            String ghId = keyArr[0];
+            String videoId = keyArr[1];
+            String sort = keyArr[2];
+            // 查询库里是否存在,如果存在即复用
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMiniVideoIdEqualTo(Long.valueOf(videoId)).andGhIdEqualTo(ghId);
+            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            SmallPageUrlDetail smallPageUrlDetail = new SmallPageUrlDetail();
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                // 库里不存在,调用新生成
+                String response = httpClientService.sendAdFlowAddRequest(GET_SMALL_PAGE_URL, videoId, "dyyjs", channel, "自动", "公众号", "自动回复小程序", "位置" + sort, ghId);
+                JSONObject jsonObject = JSON.parseObject(response);
+                if (jsonObject.getInteger("code").equals(0)) {
+                    smallPageUrlDetail = jsonObject.getObject("data", SmallPageUrlDetail.class);
+                    keyPageUrl.put(key, smallPageUrlDetail);
+                } else {
+                    log.error("httpClientService get page url error,response:" + response);
+                    throw new RuntimeException("httpClientService get page url error");
+                }
+            } else {
+                // 复用
+                CgiReplyBucketData cgiReplyBucketData1 = cgiReplyBucketData.get(0);
+                smallPageUrlDetail.setId(cgiReplyBucketData1.getPagePathUrlId());
+                smallPageUrlDetail.setUrl(cgiReplyBucketData1.getMiniPagePath());
+            }
+            keyPageUrl.put(key, smallPageUrlDetail);
+        }
+        // 处理数据
+        for (CgiReplyBucketData cgiReplyBucketData : smallDataCgiReplyList) {
+            String key = cgiReplyBucketData.getGhId() + "&" + cgiReplyBucketData.getMiniVideoId() + "&" + cgiReplyBucketData.getSort();
+            SmallPageUrlDetail smallPageUrlDetail = keyPageUrl.get(key);
+            if (Objects.isNull(smallPageUrlDetail)) {
+                log.error("setSmallPageUrl get map url is null" + JSON.toJSONString(keyPageUrl));
+                throw new RuntimeException("setSmallPageUrl get map url is null");
+            }
+            cgiReplyBucketData.setPagePathUrlId(smallPageUrlDetail.getId());
+            cgiReplyBucketData.setMiniPagePath(smallPageUrlDetail.getUrl());
+        }
+        return smallDataCgiReplyList;
+    }
+
+    private List<CgiReplyBucketData> readStrategyOrderSmallData(Set<String> keyedSet, BucketDataParam bucketDataParam) {
+        List<CgiReplyBucketData> result = new ArrayList<>();
+        for (String key : keyedSet) {
+            if ("base".equals(key)) {
+                // base作为人工控制
+                continue;
+            }
+            // 获取最新dt的策略
+            String dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKeyAndGhId(key, bucketDataParam.getGhId());
+            if (StringUtils.isEmpty(dtVersion)) {
+                bucketDataParam.setGhId("default");
+                dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKeyAndGhId(key, bucketDataParam.getGhId());
+            }
+            // 判断当前的dtVersion是否已经处理过了
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyDtEqualTo(dtVersion).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
+            long count = cgiReplyBucketDataMapper.countByExample(cgiReplyBucketDataExample);
+            if (count != 0) {
+                // 说明已处理过该dtVersion数据
+                continue;
+            }
+            // 获取最新dt数据
+            List<AlgGhAutoreplyVideoRankData> dtVerSionStrategyData = getDtVersionStrategyData(key, dtVersion, bucketDataParam.getGhId());
+            result.addAll(dtVerSionStrategyData.stream().map(x -> {
+                CgiReplyBucketData cgiReplyBucketData = new CgiReplyBucketData();
+                cgiReplyBucketData.setStrategy(key);
+                cgiReplyBucketData.setSort(x.getSort());
+                cgiReplyBucketData.setStrategyDt(x.getDtVersion());
+                cgiReplyBucketData.setGhId(x.getGhId());
+                cgiReplyBucketData.setMsgType(1);
+                cgiReplyBucketData.setTitle(x.getTitle());
+                cgiReplyBucketData.setCoverUrl(CDN_URL + x.getCoverUrl());
+                cgiReplyBucketData.setMiniAppId(SMALL_APP_Id);
+                cgiReplyBucketData.setMiniVideoId(x.getVideoId());
+                return cgiReplyBucketData;
+            }).collect(Collectors.toList()));
+        }
+        // 获取最新数据版本
+        return CollectionUtils.isEmpty(result) ? null : result;
+    }
+
+    private List<AlgGhAutoreplyVideoRankData> getDtVersionStrategyData(String key, String dtVersion, String ghId) {
+        AlgGhAutoreplyVideoRankDataExample algGhAutoreplyVideoRankDataExample = new AlgGhAutoreplyVideoRankDataExample();
+        algGhAutoreplyVideoRankDataExample.createCriteria().andIsDeleteEqualTo(0).andDtVersionEqualTo(dtVersion).andStrategyKeyEqualTo(key).andGhIdEqualTo(ghId);
+        return algGhAutoreplyVideoRankDataMapper.selectByExample(algGhAutoreplyVideoRankDataExample);
+    }
+
+    @Override
+    public Boolean support(ReplyStrategyServiceEnum key) {
+        return ReplyStrategyServiceEnum.PUSH_MESSAGE_STRATEGY_V1.equals(key);
+    }
+}

+ 254 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/strategy/reply/impl/WeComPushMessageStrategyV1.java

@@ -0,0 +1,254 @@
+package com.tzld.longarticle.recommend.server.service.strategy.reply.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.longarticle.recommend.server.common.enums.cgi.ReplyStrategyServiceEnum;
+import com.tzld.longarticle.recommend.server.mapper.crawler.AlgGhAutoreplyVideoRankDataMapper;
+import com.tzld.longarticle.recommend.server.mapper.crawler.CgiReplyBucketDataMapper;
+import com.tzld.longarticle.recommend.server.model.cgi.*;
+import com.tzld.longarticle.recommend.server.repository.model.AlgGhAutoreplyVideoRankData;
+import com.tzld.longarticle.recommend.server.repository.model.AlgGhAutoreplyVideoRankDataExample;
+import com.tzld.longarticle.recommend.server.repository.model.CgiReplyBucketData;
+import com.tzld.longarticle.recommend.server.repository.model.CgiReplyBucketDataExample;
+import com.tzld.longarticle.recommend.server.service.cgi.TouLiuHttpClientService;
+import com.tzld.longarticle.recommend.server.service.strategy.reply.ReplyStrategyService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class WeComPushMessageStrategyV1 implements ReplyStrategyService {
+
+    /**
+     * 实验分桶数量
+     */
+//    private static final Integer bucketNum = 10;
+
+    /**
+     * 分桶实验策略,key为策略,arr为对应桶
+     * {"base":[0,1,2,3],"stg0909-base":[4,5],"stg0909-explore1":[6,7,8],"stg0909-explore2":[9]}
+     * {"stg0909-base":[5,6],"stg0909-explore1":[7],"stg0909-explore2":[8,9]}
+     */
+    private static final String bucketStrategyConfig = "{\"we-com-base\":[0,1,2,3,4,5,6],\"we-com-explore1\":[7],\"we-com-explore2\":[8,9]}";
+
+    /**
+     * 自动回复使用小程序Id
+     */
+    private static final String SMALL_APP_Id = "wx7187c217efef24a7";
+
+    private static final String CDN_URL = "https://rescdn.piaoquantv.com/";
+
+    @Value("${small_page_url}")
+    private String GET_SMALL_PAGE_URL;
+
+    @Autowired
+    private AlgGhAutoreplyVideoRankDataMapper algGhAutoreplyVideoRankDataMapper;
+    @Autowired
+    private CgiReplyBucketDataMapper cgiReplyBucketDataMapper;
+    @Autowired
+    private TouLiuHttpClientService httpClientService;
+
+    @Override
+    public ReplyBucketData getResult(BucketDataParam bucketDataParam) {
+        log.info("PushMessageStrategyV1 start");
+        // 0 获取策略key
+        JSONObject bucketStrategyConfigJsonObject = JSON.parseObject(bucketStrategyConfig);
+        Set<String> keyedSet = bucketStrategyConfigJsonObject.keySet();
+        // 1 处理文章--算法引擎--排序文章数据
+//        getWenzhangData();
+        // 2 处理小程序--读取离线数据表--获取策略排序小程序数据
+        List<CgiReplyBucketData> smallDataCgiReplyList = readStrategyOrderSmallData(keyedSet, bucketDataParam);
+        // 2.1 获取小程序落地页地址 http调用
+        smallDataCgiReplyList = setSmallPageUrl(smallDataCgiReplyList);
+        log.info(JSON.toJSONString(smallDataCgiReplyList));
+        // 3 入库读表
+        insertSmallData(smallDataCgiReplyList, keyedSet, bucketDataParam);
+        // 4 组装分桶数据
+        return getReplyBucketData(bucketStrategyConfigJsonObject, keyedSet, bucketDataParam.getGhId());
+    }
+
+    private ReplyBucketData getReplyBucketData(JSONObject bucketStrategyConfigJsonObject, Set<String> keyedSet, String ghId) {
+        // 策略小程序数据
+        ReplyBucketData replyBucketData = new ReplyBucketData();
+        List<GroupData> groupDataList = new ArrayList<>();
+        for (String key : keyedSet) {
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo(ghId);
+            cgiReplyBucketDataExample.setOrderByClause("sort");
+            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                CgiReplyBucketDataExample cgiReplyBucketDataExampleNull = new CgiReplyBucketDataExample();
+                cgiReplyBucketDataExampleNull.createCriteria().andIsDeleteEqualTo(0).andStrategyEqualTo(key).andGhIdEqualTo("default");
+                cgiReplyBucketDataExampleNull.setOrderByClause("sort");
+                cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExampleNull);
+            }
+            log.info("cgiReplyBucketData={}", cgiReplyBucketData);
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                log.error("getReplyBucketData get data is null,key:" + key);
+                return null;
+            }
+
+            List<Integer> groupList = bucketStrategyConfigJsonObject.getJSONArray(key).toJavaList(Integer.class);
+            for (Integer group : groupList) {
+                GroupData groupData = new GroupData();
+                groupData.setGroupIndex(group);
+                List<MsgData> msgDataList = new ArrayList<>();
+                for (CgiReplyBucketData cgiReplyBucketDatum : cgiReplyBucketData) {
+                    MsgData msgData = new MsgData();
+                    BeanUtils.copyProperties(cgiReplyBucketDatum, msgData);
+                    if (cgiReplyBucketDatum.getMsgType().equals(1)) {
+                        msgData.setMiniAppId(SMALL_APP_Id);
+                    }
+                    msgDataList.add(msgData);
+                }
+                groupData.setMsgDataList(msgDataList);
+                groupDataList.add(groupData);
+            }
+        }
+        // groupDataList排序
+        replyBucketData.setGroupList(groupDataList.stream().sorted(Comparator.comparingInt(GroupData::getGroupIndex)).collect(Collectors.toList()));
+        return replyBucketData;
+    }
+
+
+    private void insertSmallData(List<CgiReplyBucketData> smallDataCgiReplyList, Set<String> keyedSet, BucketDataParam bucketDataParam) {
+        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
+            return;
+        }
+        for (String key : keyedSet) {
+            if ("base".equals(key)) {
+                continue;
+            }
+            List<CgiReplyBucketData> collect = smallDataCgiReplyList.stream()
+                    .filter(x -> x.getStrategy().equals(key))
+                    .filter(x -> x.getGhId().equals(bucketDataParam.getGhId()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(collect)) {
+                log.error("PushMessageStrategyV1 insertSmallData 算法排序数据异常,data:" + JSON.toJSONString(smallDataCgiReplyList));
+                continue;
+            }
+            // 清上个版本的策略数据
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMsgTypeEqualTo(1).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
+            List<CgiReplyBucketData> cgiReplyBucketData1 = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            for (CgiReplyBucketData cgiReplyBucketData : cgiReplyBucketData1) {
+                cgiReplyBucketData.setIsDelete(1);
+                cgiReplyBucketDataMapper.updateByPrimaryKeySelective(cgiReplyBucketData);
+            }
+            // 入库
+            for (CgiReplyBucketData cgiReplyBucketData : collect) {
+                cgiReplyBucketDataMapper.insertSelective(cgiReplyBucketData);
+            }
+        }
+    }
+
+    private List<CgiReplyBucketData> setSmallPageUrl(List<CgiReplyBucketData> smallDataCgiReplyList) {
+        if (CollectionUtils.isEmpty(smallDataCgiReplyList)) {
+            return smallDataCgiReplyList;
+        }
+        Set<String> keys = smallDataCgiReplyList.stream().map(x -> x.getGhId() + "&" + x.getMiniVideoId() + "&" + x.getSort()).collect(Collectors.toSet());
+        Map<String, SmallPageUrlDetail> keyPageUrl = new HashMap<>();
+        // gh-id + videoId + sort 复用同一page_url及落地页id
+        for (String key : keys) {
+            String[] keyArr = key.split("&");
+            String ghId = keyArr[0];
+            String videoId = keyArr[1];
+            String sort = keyArr[2];
+            // 查询库里是否存在,如果存在即复用
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMiniVideoIdEqualTo(Long.valueOf(videoId)).andGhIdEqualTo(ghId);
+            List<CgiReplyBucketData> cgiReplyBucketData = cgiReplyBucketDataMapper.selectByExample(cgiReplyBucketDataExample);
+            SmallPageUrlDetail smallPageUrlDetail = new SmallPageUrlDetail();
+            if (CollectionUtils.isEmpty(cgiReplyBucketData)) {
+                // 库里不存在,调用新生成
+                String response = httpClientService.sendAdFlowAddRequest(GET_SMALL_PAGE_URL, videoId, "touliu", "tencentqw", "自动", "企微", "自动回复小程序", "位置" + sort, ghId);
+                JSONObject jsonObject = JSON.parseObject(response);
+                if (jsonObject.getInteger("code").equals(0)) {
+                    smallPageUrlDetail = jsonObject.getObject("data", SmallPageUrlDetail.class);
+                    keyPageUrl.put(key, smallPageUrlDetail);
+                } else {
+                    log.error("httpClientService get page url error,response:" + response);
+                    throw new RuntimeException("httpClientService get page url error");
+                }
+            } else {
+                // 复用
+                CgiReplyBucketData cgiReplyBucketData1 = cgiReplyBucketData.get(0);
+                smallPageUrlDetail.setId(cgiReplyBucketData1.getPagePathUrlId());
+                smallPageUrlDetail.setUrl(cgiReplyBucketData1.getMiniPagePath());
+            }
+            keyPageUrl.put(key, smallPageUrlDetail);
+        }
+        // 处理数据
+        for (CgiReplyBucketData cgiReplyBucketData : smallDataCgiReplyList) {
+            String key = cgiReplyBucketData.getGhId() + "&" + cgiReplyBucketData.getMiniVideoId() + "&" + cgiReplyBucketData.getSort();
+            SmallPageUrlDetail smallPageUrlDetail = keyPageUrl.get(key);
+            if (Objects.isNull(smallPageUrlDetail)) {
+                log.error("setSmallPageUrl get map url is null" + JSON.toJSONString(keyPageUrl));
+                throw new RuntimeException("setSmallPageUrl get map url is null");
+            }
+            cgiReplyBucketData.setPagePathUrlId(smallPageUrlDetail.getId());
+            cgiReplyBucketData.setMiniPagePath(smallPageUrlDetail.getUrl());
+        }
+        return smallDataCgiReplyList;
+    }
+
+    private List<CgiReplyBucketData> readStrategyOrderSmallData(Set<String> keyedSet, BucketDataParam bucketDataParam) {
+        List<CgiReplyBucketData> result = new ArrayList<>();
+        for (String key : keyedSet) {
+            if ("base".equals(key)) {
+                // base作为人工控制
+                continue;
+            }
+            // 获取最新dt的策略
+            String dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKeyAndGhId(key, bucketDataParam.getGhId());
+            if (StringUtils.isEmpty(dtVersion)) {
+                bucketDataParam.setGhId("default");
+                dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKeyAndGhId(key, bucketDataParam.getGhId());
+            }
+            // 判断当前的dtVersion是否已经处理过了
+            CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
+            cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyDtEqualTo(dtVersion).andStrategyEqualTo(key).andGhIdEqualTo(bucketDataParam.getGhId());
+            long count = cgiReplyBucketDataMapper.countByExample(cgiReplyBucketDataExample);
+            if (count != 0) {
+                // 说明已处理过该dtVersion数据
+                continue;
+            }
+            // 获取最新dt数据
+            List<AlgGhAutoreplyVideoRankData> dtVerSionStrategyData = getDtVersionStrategyData(key, dtVersion, bucketDataParam.getGhId());
+            result.addAll(dtVerSionStrategyData.stream().map(x -> {
+                CgiReplyBucketData cgiReplyBucketData = new CgiReplyBucketData();
+                cgiReplyBucketData.setStrategy(key);
+                cgiReplyBucketData.setSort(x.getSort());
+                cgiReplyBucketData.setStrategyDt(x.getDtVersion());
+                cgiReplyBucketData.setGhId(x.getGhId());
+                cgiReplyBucketData.setMsgType(1);
+                cgiReplyBucketData.setTitle(x.getTitle());
+                cgiReplyBucketData.setCoverUrl(CDN_URL + x.getCoverUrl());
+                cgiReplyBucketData.setMiniAppId(SMALL_APP_Id);
+                cgiReplyBucketData.setMiniVideoId(x.getVideoId());
+                return cgiReplyBucketData;
+            }).collect(Collectors.toList()));
+        }
+        // 获取最新数据版本
+        return CollectionUtils.isEmpty(result) ? null : result;
+    }
+
+    private List<AlgGhAutoreplyVideoRankData> getDtVersionStrategyData(String key, String dtVersion, String ghId) {
+        AlgGhAutoreplyVideoRankDataExample algGhAutoreplyVideoRankDataExample = new AlgGhAutoreplyVideoRankDataExample();
+        algGhAutoreplyVideoRankDataExample.createCriteria().andIsDeleteEqualTo(0).andDtVersionEqualTo(dtVersion).andStrategyKeyEqualTo(key).andGhIdEqualTo(ghId);
+        return algGhAutoreplyVideoRankDataMapper.selectByExample(algGhAutoreplyVideoRankDataExample);
+    }
+
+    @Override
+    public Boolean support(ReplyStrategyServiceEnum key) {
+        return ReplyStrategyServiceEnum.WE_COM_PUSH_MESSAGE_STRATEGY_V1.equals(key);
+    }
+}

+ 47 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/ThirdPartyController.java

@@ -0,0 +1,47 @@
+package com.tzld.longarticle.recommend.server.web;
+
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.model.param.AccessTokenParam;
+import com.tzld.longarticle.recommend.server.model.param.CallbackParam;
+import com.tzld.longarticle.recommend.server.model.param.PushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.AccessTokenVo;
+import com.tzld.longarticle.recommend.server.model.vo.PushMessageVo;
+import com.tzld.longarticle.recommend.server.mq.MessageCallbackProducer;
+import com.tzld.longarticle.recommend.server.service.exterior.AccessTokenService;
+import com.tzld.longarticle.recommend.server.service.exterior.ThirdPartyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/3rdParty")
+public class ThirdPartyController {
+
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    @Autowired
+    private ThirdPartyService thirdPartyService;
+
+    @Autowired
+    private MessageCallbackProducer messageCallbackProducer;
+
+    @PostMapping("/accessToken/get")
+    public CommonResponse<AccessTokenVo> getAccessToken(@RequestBody AccessTokenParam param) {
+        return accessTokenService.getAccessToken(param);
+    }
+
+    @PostMapping("/pushMessage/get")
+    public CommonResponse<List<PushMessageVo>> getPushMessage(@RequestBody PushMessageParam param) {
+        return thirdPartyService.getPushMessage(param);
+    }
+
+    @PostMapping("/pushMessage/callback")
+    public CommonResponse<Void> pushMessageCallback(@RequestBody CallbackParam param) {
+        return messageCallbackProducer.sendMessage(param);
+    }
+}

+ 40 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/WeComController.java

@@ -0,0 +1,40 @@
+package com.tzld.longarticle.recommend.server.web;
+
+import com.tzld.longarticle.recommend.server.common.response.CommonResponse;
+import com.tzld.longarticle.recommend.server.model.param.AccessTokenParam;
+import com.tzld.longarticle.recommend.server.model.param.CallbackParam;
+import com.tzld.longarticle.recommend.server.model.param.PushMessageParam;
+import com.tzld.longarticle.recommend.server.model.param.WeComPushMessageParam;
+import com.tzld.longarticle.recommend.server.model.vo.AccessTokenVo;
+import com.tzld.longarticle.recommend.server.model.vo.PushMessageVo;
+import com.tzld.longarticle.recommend.server.model.vo.WeComPushMessageVo;
+import com.tzld.longarticle.recommend.server.mq.MessageCallbackProducer;
+import com.tzld.longarticle.recommend.server.service.exterior.AccessTokenService;
+import com.tzld.longarticle.recommend.server.service.exterior.ThirdPartyService;
+import com.tzld.longarticle.recommend.server.service.exterior.WeComService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/wecom")
+public class WeComController {
+
+
+   @Autowired
+   private WeComService weComService;
+
+
+    @PostMapping("/pushMessage/get")
+    public CommonResponse<List<WeComPushMessageVo>> getPushMessage(@RequestBody WeComPushMessageParam param) {
+        log.info("param={}", param);
+        return weComService.getPushMessage(param);
+    }
+
+}

+ 27 - 18
long-article-recommend-service/src/main/resources/application-dev.yml

@@ -1,27 +1,27 @@
 server:
-  port: 8001
+  port: 80
 
 spring:
-#  redis:
-#    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
-#    port: 6379
-#    password: Wqsd@2019
-#    timeout: 1000
-#    lettuce:
-#      pool:
-#        max-active: 8
-#        max-wait: -1
-#        max-idle: 8
-#        min-idle: 0
+  redis:
+    hostName: r-bp1ps6my7lzg8rdhwx682.redis.rds.aliyuncs.com
+    port: 6379
+    password: Wqsd@2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
   datasource:
     crawler:
-      jdbc-url: jdbc:mysql://rm-bp1159bu17li9hi94.mysql.rds.aliyuncs.com:3306/piaoquan-crawler?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
-      username: crawler
-      password: crawler123456@
-      # 测试库
-#      jdbc-url: jdbc:mysql://rm-bp1k5853td1r25g3n690.mysql.rds.aliyuncs.com:3306/piaoquan-crawler?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
+#      jdbc-url: jdbc:mysql://rm-bp1159bu17li9hi94.mysql.rds.aliyuncs.com:3306/piaoquan-crawler?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
 #      username: crawler
 #      password: crawler123456@
+      # 测试库
+      jdbc-url: jdbc:mysql://rm-bp1k5853td1r25g3n690.mysql.rds.aliyuncs.com:3306/piaoquan-crawler?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
+      username: crawler
+      password: crawler123456@
       driver-class-name: com.mysql.jdbc.Driver
       hikari:
         connection-timeout: 30000
@@ -111,4 +111,13 @@ aliyun:
 
 logging:
   file:
-    path: ./${spring.application.name}/logs/
+    path: ./${spring.application.name}/logs/
+
+
+pushMessage:
+  callback:
+    topic: 3rd_party_push_message_callback_dev
+    groupId: GID_3RD_PARTY_PUSH_MESSAGE_CALLBACK_DEV
+    tag: mini
+
+small_page_url: https://testapi.piaoquantv.com

+ 3 - 1
long-article-recommend-service/src/main/resources/application-pre.yml

@@ -161,4 +161,6 @@ aliyun:
     filter:
       log:
         project: wqsd-video
-        store: video_blacklist_security_filter_log
+        store: video_blacklist_security_filter_log
+
+small_page_url: https://testapi.piaoquantv.com

+ 21 - 1
long-article-recommend-service/src/main/resources/application-prod.yml

@@ -2,6 +2,18 @@ server:
   port: 8080
 
 spring:
+  redis:
+    hostName: r-bp1m4nvh130sfjjc6f.redis.rds.aliyuncs.com
+    port: 6379
+    password: Qingqu2019
+    timeout: 1000
+    lettuce:
+      pool:
+        max-active: 8
+        max-wait: -1
+        max-idle: 8
+        min-idle: 0
+
   datasource:
     crawler:
       jdbc-url: jdbc:mysql://rm-bp1159bu17li9hi94.mysql.rds.aliyuncs.com:3306/piaoquan-crawler?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
@@ -90,4 +102,12 @@ aliyun:
   timer:
     log:
       project: longarticle-recommend-server-test
-      logStore: timer
+      logStore: timer
+
+pushMessage:
+  callback:
+    topic: 3rd_party_push_message_callback_prod
+    groupId: GID_3RD_PARTY_PUSH_MESSAGE_CALLBACK_PROD
+    tag: mini
+
+small_page_url: https://api.piaoquantv.com

+ 3 - 1
long-article-recommend-service/src/main/resources/application-test.yml

@@ -151,4 +151,6 @@ aliyun:
     filter:
       log:
         project: wqsd-video-test
-        store: video_blacklist_security_filter_log
+        store: video_blacklist_security_filter_log
+
+small_page_url: https://testapi.piaoquantv.com

+ 5 - 0
long-article-recommend-service/src/main/resources/application.yml

@@ -55,3 +55,8 @@ apollo:
 
 mybatis:
   mapper-locations: classpath:/mapper/*.xml
+
+rocketmq:
+  accessKey: LTAI4G7puhXtLyHzHQpD6H7A
+  secretKey: nEbq3xWNQd1qLpdy2u71qFweHkZjSG
+  nameSrvAddr: http://MQ_INST_1894469520484605_BXhXuzkZ.mq-internet-access.mq-internet.aliyuncs.com:80

+ 9 - 0
long-article-recommend-service/src/main/resources/mapper/crawler/AlgGhAutoreplyVideoRankDataMapper.xml

@@ -312,4 +312,13 @@
     ORDER BY dt_version DESC
       LIMIT 1
   </select>
+
+  <select id="selectLatestDtVersionByStrategyKeyAndGhId" resultType="string" >
+    SELECT dt_version
+    FROM alg_gh_autoreply_video_rank_data
+    WHERE strategy_key = #{strategyKey} and gh_id = #{ghId}
+    GROUP BY dt_version
+    ORDER BY dt_version DESC
+      LIMIT 1
+  </select>
 </mapper>

+ 278 - 0
long-article-recommend-service/src/main/resources/mapper/crawler/PushMessageCallbackMapper.xml

@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.tzld.longarticle.recommend.server.mapper.crawler.PushMessageCallbackMapper">
+    <resultMap id="BaseResultMap" type="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback">
+        <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="gh_id" jdbcType="VARCHAR" property="ghId"/>
+        <result column="open_id" jdbcType="VARCHAR" property="openId"/>
+        <result column="timestamp" jdbcType="BIGINT" property="timestamp"/>
+        <result column="msg_type" jdbcType="INTEGER" property="msgType"/>
+        <result column="video_id" jdbcType="BIGINT" property="videoId"/>
+        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
+    </resultMap>
+    <sql id="Example_Where_Clause">
+        <where>
+            <foreach collection="oredCriteria" item="criteria" separator="or">
+                <if test="criteria.valid">
+                    <trim prefix="(" prefixOverrides="and" suffix=")">
+                        <foreach collection="criteria.criteria" item="criterion">
+                            <choose>
+                                <when test="criterion.noValue">
+                                    and ${criterion.condition}
+                                </when>
+                                <when test="criterion.singleValue">
+                                    and ${criterion.condition} #{criterion.value}
+                                </when>
+                                <when test="criterion.betweenValue">
+                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                                </when>
+                                <when test="criterion.listValue">
+                                    and ${criterion.condition}
+                                    <foreach close=")" collection="criterion.value" item="listItem" open="("
+                                             separator=",">
+                                        #{listItem}
+                                    </foreach>
+                                </when>
+                            </choose>
+                        </foreach>
+                    </trim>
+                </if>
+            </foreach>
+        </where>
+    </sql>
+    <sql id="Update_By_Example_Where_Clause">
+        <where>
+            <foreach collection="example.oredCriteria" item="criteria" separator="or">
+                <if test="criteria.valid">
+                    <trim prefix="(" prefixOverrides="and" suffix=")">
+                        <foreach collection="criteria.criteria" item="criterion">
+                            <choose>
+                                <when test="criterion.noValue">
+                                    and ${criterion.condition}
+                                </when>
+                                <when test="criterion.singleValue">
+                                    and ${criterion.condition} #{criterion.value}
+                                </when>
+                                <when test="criterion.betweenValue">
+                                    and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                                </when>
+                                <when test="criterion.listValue">
+                                    and ${criterion.condition}
+                                    <foreach close=")" collection="criterion.value" item="listItem" open="("
+                                             separator=",">
+                                        #{listItem}
+                                    </foreach>
+                                </when>
+                            </choose>
+                        </foreach>
+                    </trim>
+                </if>
+            </foreach>
+        </where>
+    </sql>
+    <sql id="Base_Column_List">
+        id
+        , gh_id, open_id, timestamp, msg_type, video_id, create_time
+    </sql>
+    <select id="selectByExample"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallbackExample"
+            resultMap="BaseResultMap">
+        select
+        <if test="distinct">
+            distinct
+        </if>
+        <include refid="Base_Column_List"/>
+        from push_message_callback
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+        <if test="orderByClause != null">
+            order by ${orderByClause}
+        </if>
+    </select>
+    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from push_message_callback
+        where id = #{id,jdbcType=BIGINT}
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
+        delete
+        from push_message_callback
+        where id = #{id,jdbcType=BIGINT}
+    </delete>
+    <delete id="deleteByExample"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallbackExample">
+        delete from push_message_callback
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+    </delete>
+    <insert id="insert" parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback">
+        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long">
+            SELECT LAST_INSERT_ID()
+        </selectKey>
+        insert into push_message_callback (gh_id, open_id, timestamp,
+        msg_type, video_id, create_time
+        )
+        values (#{ghId,jdbcType=VARCHAR}, #{openId,jdbcType=VARCHAR}, #{timestamp,jdbcType=BIGINT},
+        #{msgType,jdbcType=INTEGER}, #{videoId,jdbcType=BIGINT}, #{createTime,jdbcType=TIMESTAMP}
+        )
+    </insert>
+    <insert id="insertSelective"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback">
+        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long">
+            SELECT LAST_INSERT_ID()
+        </selectKey>
+        insert into push_message_callback
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="ghId != null">
+                gh_id,
+            </if>
+            <if test="openId != null">
+                open_id,
+            </if>
+            <if test="timestamp != null">
+                timestamp,
+            </if>
+            <if test="msgType != null">
+                msg_type,
+            </if>
+            <if test="videoId != null">
+                video_id,
+            </if>
+            <if test="createTime != null">
+                create_time,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="ghId != null">
+                #{ghId,jdbcType=VARCHAR},
+            </if>
+            <if test="openId != null">
+                #{openId,jdbcType=VARCHAR},
+            </if>
+            <if test="timestamp != null">
+                #{timestamp,jdbcType=BIGINT},
+            </if>
+            <if test="msgType != null">
+                #{msgType,jdbcType=INTEGER},
+            </if>
+            <if test="videoId != null">
+                #{videoId,jdbcType=BIGINT},
+            </if>
+            <if test="createTime != null">
+                #{createTime,jdbcType=TIMESTAMP},
+            </if>
+        </trim>
+    </insert>
+    <select id="countByExample"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallbackExample"
+            resultType="java.lang.Long">
+        select count(*) from push_message_callback
+        <if test="_parameter != null">
+            <include refid="Example_Where_Clause"/>
+        </if>
+    </select>
+    <update id="updateByExampleSelective" parameterType="map">
+        update push_message_callback
+        <set>
+            <if test="row.id != null">
+                id = #{row.id,jdbcType=BIGINT},
+            </if>
+            <if test="row.ghId != null">
+                gh_id = #{row.ghId,jdbcType=VARCHAR},
+            </if>
+            <if test="row.openId != null">
+                open_id = #{row.openId,jdbcType=VARCHAR},
+            </if>
+            <if test="row.timestamp != null">
+                timestamp = #{row.timestamp,jdbcType=BIGINT},
+            </if>
+            <if test="row.msgType != null">
+                msg_type = #{row.msgType,jdbcType=INTEGER},
+            </if>
+            <if test="row.videoId != null">
+                video_id = #{row.videoId,jdbcType=BIGINT},
+            </if>
+            <if test="row.createTime != null">
+                create_time = #{row.createTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        <if test="example != null">
+            <include refid="Update_By_Example_Where_Clause"/>
+        </if>
+    </update>
+    <update id="updateByExample" parameterType="map">
+        update push_message_callback
+        set id = #{row.id,jdbcType=BIGINT},
+        gh_id = #{row.ghId,jdbcType=VARCHAR},
+        open_id = #{row.openId,jdbcType=VARCHAR},
+        timestamp = #{row.timestamp,jdbcType=BIGINT},
+        msg_type = #{row.msgType,jdbcType=INTEGER},
+        video_id = #{row.videoId,jdbcType=BIGINT},
+        create_time = #{row.createTime,jdbcType=TIMESTAMP}
+        <if test="example != null">
+            <include refid="Update_By_Example_Where_Clause"/>
+        </if>
+    </update>
+    <update id="updateByPrimaryKeySelective"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback">
+        update push_message_callback
+        <set>
+            <if test="ghId != null">
+                gh_id = #{ghId,jdbcType=VARCHAR},
+            </if>
+            <if test="openId != null">
+                open_id = #{openId,jdbcType=VARCHAR},
+            </if>
+            <if test="timestamp != null">
+                timestamp = #{timestamp,jdbcType=BIGINT},
+            </if>
+            <if test="msgType != null">
+                msg_type = #{msgType,jdbcType=INTEGER},
+            </if>
+            <if test="videoId != null">
+                video_id = #{videoId,jdbcType=BIGINT},
+            </if>
+            <if test="createTime != null">
+                create_time = #{createTime,jdbcType=TIMESTAMP},
+            </if>
+        </set>
+        where id = #{id,jdbcType=BIGINT}
+    </update>
+    <update id="updateByPrimaryKey"
+            parameterType="com.tzld.longarticle.recommend.server.repository.model.PushMessageCallback">
+        update push_message_callback
+        set gh_id       = #{ghId,jdbcType=VARCHAR},
+            open_id     = #{openId,jdbcType=VARCHAR},
+            timestamp   = #{timestamp,jdbcType=BIGINT},
+            msg_type    = #{msgType,jdbcType=INTEGER},
+            video_id    = #{videoId,jdbcType=BIGINT},
+            create_time = #{createTime,jdbcType=TIMESTAMP}
+        where id = #{id,jdbcType=BIGINT}
+    </update>
+
+    <insert id="insertList" parameterType="java.util.List">
+        insert into push_message_callback
+        (
+        gh_id,
+        open_id,
+        timestamp,
+        msg_type,
+        video_id,
+        create_time
+        )
+        values
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.ghId,jdbcType=VARCHAR},
+            #{item.openId,jdbcType=VARCHAR},
+            #{item.timestamp,jdbcType=BIGINT},
+            #{item.msgType,jdbcType=INTEGER},
+            #{item.videoId,jdbcType=BIGINT},
+            #{item.createTime,jdbcType=TIMESTAMP}
+            )
+        </foreach>
+    </insert>
+</mapper>