|
@@ -0,0 +1,241 @@
|
|
|
+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.stereotype.Service;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
+
|
|
|
+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 = "{\"stg0909-base\":[5,6],\"stg0909-explore1\":[7],\"stg0909-explore2\":[8,9]}";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动回复使用小程序Id
|
|
|
+ */
|
|
|
+ private static final String SMALL_APP_Id = "wxbdd2a2e93d9a6e25";
|
|
|
+
|
|
|
+ private static final String CDN_URL = "https://rescdn.piaoquantv.com/";
|
|
|
+
|
|
|
+ private static final String GET_SMALL_PAGE_URL = "https://api.piaoquantv.com";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AlgGhAutoreplyVideoRankDataMapper algGhAutoreplyVideoRankDataMapper;
|
|
|
+ @Autowired
|
|
|
+ private CgiReplyBucketDataMapper cgiReplyBucketDataMapper;
|
|
|
+ @Autowired
|
|
|
+ private TouLiuHttpClientService httpClientService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ReplyBucketData getResult(BucketDataParam bucketDataParam) {
|
|
|
+ // 0 获取策略key
|
|
|
+ JSONObject bucketStrategyConfigJsonObject = JSON.parseObject(bucketStrategyConfig);
|
|
|
+ Set<String> keyedSet = bucketStrategyConfigJsonObject.keySet();
|
|
|
+ // 1 处理文章--算法引擎--排序文章数据
|
|
|
+// getWenzhangData();
|
|
|
+ // 2 处理小程序--读取离线数据表--获取策略排序小程序数据
|
|
|
+ List<CgiReplyBucketData> smallDataCgiReplyList = readStrategyOrderSmallData(keyedSet);
|
|
|
+ // 2.1 获取小程序落地页地址 http调用
|
|
|
+ smallDataCgiReplyList = setSmallPageUrl(smallDataCgiReplyList);
|
|
|
+ log.info(JSON.toJSONString(smallDataCgiReplyList));
|
|
|
+ // 3 入库读表
|
|
|
+ insertSmallData(smallDataCgiReplyList, keyedSet);
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
+ 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) {
|
|
|
+ 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)).collect(Collectors.toList());
|
|
|
+ if (CollectionUtils.isEmpty(collect)) {
|
|
|
+ log.error("insertSmallData 算法排序数据异常,data:" + JSON.toJSONString(smallDataCgiReplyList));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 清上个版本的策略数据
|
|
|
+ CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
|
|
|
+ cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andMsgTypeEqualTo(1).andStrategyEqualTo(key);
|
|
|
+ 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()).collect(Collectors.toSet());
|
|
|
+ Map<String, SmallPageUrlDetail> keyPageUrl = new HashMap<>();
|
|
|
+ // gh-id + videoId 复用同一page_url及落地页id
|
|
|
+ for (String key : keys) {
|
|
|
+ String[] keyArr = key.split("&");
|
|
|
+ String ghId = keyArr[0];
|
|
|
+ String videoId = keyArr[1];
|
|
|
+ // 查询库里是否存在,如果存在即复用
|
|
|
+ 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, "公众号", "自动回复小程序", "位置X", 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();
|
|
|
+ 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) {
|
|
|
+ List<CgiReplyBucketData> result = new ArrayList<>();
|
|
|
+ for (String key : keyedSet) {
|
|
|
+ if ("base".equals(key)) {
|
|
|
+ // base作为人工控制
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 获取最新dt的策略
|
|
|
+ String dtVersion = algGhAutoreplyVideoRankDataMapper.selectLatestDtVersionByStrategyKey(key);
|
|
|
+ // 判断当前的dtVersion是否已经处理过了
|
|
|
+ CgiReplyBucketDataExample cgiReplyBucketDataExample = new CgiReplyBucketDataExample();
|
|
|
+ cgiReplyBucketDataExample.createCriteria().andIsDeleteEqualTo(0).andStrategyDtEqualTo(dtVersion).andStrategyEqualTo(key);
|
|
|
+ long count = cgiReplyBucketDataMapper.countByExample(cgiReplyBucketDataExample);
|
|
|
+ if (count != 0) {
|
|
|
+ // 说明已处理过该dtVersion数据
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 获取最新dt数据
|
|
|
+ List<AlgGhAutoreplyVideoRankData> dtVserSionStrategyData = getDtVersionStrategyData(key, dtVersion);
|
|
|
+ result.addAll(dtVserSionStrategyData.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) {
|
|
|
+ AlgGhAutoreplyVideoRankDataExample algGhAutoreplyVideoRankDataExample = new AlgGhAutoreplyVideoRankDataExample();
|
|
|
+ algGhAutoreplyVideoRankDataExample.createCriteria().andIsDeleteEqualTo(0).andDtVersionEqualTo(dtVersion).andStrategyKeyEqualTo(key);
|
|
|
+ return algGhAutoreplyVideoRankDataMapper.selectByExample(algGhAutoreplyVideoRankDataExample);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Boolean support(ReplyStrategyServiceEnum key) {
|
|
|
+ return ReplyStrategyServiceEnum.PUSH_MESSAGE_STRATEGY_V1.equals(key);
|
|
|
+ }
|
|
|
+}
|