소스 검색

需求抓取长文 小程序使用需求视频

wangyunpeng 3 주 전
부모
커밋
55c80a6940

+ 21 - 0
long-article-server/src/main/java/com/tzld/piaoquan/longarticle/dao/mapper/longarticle/DemandProduceContentRelationMapper.java

@@ -0,0 +1,21 @@
+package com.tzld.piaoquan.longarticle.dao.mapper.longarticle;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 需求搜索文章-执行计划对照关系表 Mapper
+ */
+public interface DemandProduceContentRelationMapper {
+
+    /**
+     * 根据账号ID和内容ID查询实验ID列表
+     *
+     * @param accountId 发布账号ID
+     * @param contentId 内容ID(对应 publishContent.sourceId)
+     * @return 实验ID列表
+     */
+    List<String> selectExperimentIdsByAccountAndContent(@Param("accountId") String accountId,
+                                                         @Param("contentId") String contentId);
+}

+ 24 - 0
long-article-server/src/main/java/com/tzld/piaoquan/longarticle/dao/mapper/longarticle/DemandSearchQueueMapper.java

@@ -0,0 +1,24 @@
+package com.tzld.piaoquan.longarticle.dao.mapper.longarticle;
+
+import com.tzld.piaoquan.longarticle.model.po.longarticle.DemandSearchQueue;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 需求搜索队列 Mapper
+ */
+public interface DemandSearchQueueMapper {
+
+    /**
+     * 根据账号和实验ID列表查询匹配视频记录
+     *
+     * @param account       账号名称(channel_level3,聚合维度)
+     * @param experimentIds 实验ID列表
+     * @param limit         返回条数上限
+     * @return 匹配的视频记录列表
+     */
+    List<DemandSearchQueue> selectByAccountAndExperiments(@Param("account") String account,
+                                                           @Param("experimentIds") List<String> experimentIds,
+                                                           @Param("limit") int limit);
+}

+ 75 - 0
long-article-server/src/main/java/com/tzld/piaoquan/longarticle/model/po/longarticle/DemandProduceContentRelation.java

@@ -0,0 +1,75 @@
+package com.tzld.piaoquan.longarticle.model.po.longarticle;
+
+import java.util.Date;
+
+public class DemandProduceContentRelation {
+    private Long id;
+
+    private String accountId;
+
+    private String contentId;
+
+    private String channelContentId;
+
+    private String experimentId;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(String accountId) {
+        this.accountId = accountId;
+    }
+
+    public String getContentId() {
+        return contentId;
+    }
+
+    public void setContentId(String contentId) {
+        this.contentId = contentId;
+    }
+
+    public String getChannelContentId() {
+        return channelContentId;
+    }
+
+    public void setChannelContentId(String channelContentId) {
+        this.channelContentId = channelContentId;
+    }
+
+    public String getExperimentId() {
+        return experimentId;
+    }
+
+    public void setExperimentId(String experimentId) {
+        this.experimentId = experimentId;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public Date getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(Date updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+}

+ 145 - 0
long-article-server/src/main/java/com/tzld/piaoquan/longarticle/model/po/longarticle/DemandSearchQueue.java

@@ -0,0 +1,145 @@
+package com.tzld.piaoquan.longarticle.model.po.longarticle;
+
+import java.util.Date;
+
+public class DemandSearchQueue {
+    private Long id;
+
+    private String account;
+
+    private String searchKey;
+
+    private String dt;
+
+    private Long videoId;
+
+    private String channelName;
+
+    private String category;
+
+    private String keyType;
+
+    private String experimentId;
+
+    private Integer status;
+
+    private String remark;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+
+    private String driveType;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getSearchKey() {
+        return searchKey;
+    }
+
+    public void setSearchKey(String searchKey) {
+        this.searchKey = searchKey;
+    }
+
+    public String getDt() {
+        return dt;
+    }
+
+    public void setDt(String dt) {
+        this.dt = dt;
+    }
+
+    public Long getVideoId() {
+        return videoId;
+    }
+
+    public void setVideoId(Long videoId) {
+        this.videoId = videoId;
+    }
+
+    public String getChannelName() {
+        return channelName;
+    }
+
+    public void setChannelName(String channelName) {
+        this.channelName = channelName;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(String category) {
+        this.category = category;
+    }
+
+    public String getKeyType() {
+        return keyType;
+    }
+
+    public void setKeyType(String keyType) {
+        this.keyType = keyType;
+    }
+
+    public String getExperimentId() {
+        return experimentId;
+    }
+
+    public void setExperimentId(String experimentId) {
+        this.experimentId = experimentId;
+    }
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public Date getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(Date updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+
+    public String getDriveType() {
+        return driveType;
+    }
+
+    public void setDriveType(String driveType) {
+        this.driveType = driveType;
+    }
+}

+ 91 - 3
long-article-server/src/main/java/com/tzld/piaoquan/longarticle/service/local/impl/CoreServiceImpl.java

@@ -112,6 +112,12 @@ public class CoreServiceImpl implements CoreService {
     @Autowired
     private VectorMatchVideoMapper vectorMatchVideoMapper;
 
+    @Autowired
+    private DemandProduceContentRelationMapper demandProduceContentRelationMapper;
+
+    @Autowired
+    private DemandSearchQueueMapper demandSearchQueueMapper;
+
     @ApolloJsonValue("${fwhGhIdList:[]}")
     private List<String> fwhGhIdList;
 
@@ -862,13 +868,17 @@ public class CoreServiceImpl implements CoreService {
         if (planAccount.getMiniprogramInsertFlag() == null || planAccount.getMiniprogramInsertFlag() != 1) {
             return Collections.emptyList();
         }
-        //获取小程序
+        // 获取小程序
         List<PublishMiniprogram> publishMiniprogramList = publicContentService.getPublishMiniprograms(publishContent);
-        //不存在则重新生成
+        // 不存在则重新生成
         if (CollectionUtils.isEmpty(publishMiniprogramList)) {
             // 如果有experimentId,优先通过queryBySourceId接口获取匹配视频生成小程序卡片
             if (StringUtils.isNotEmpty(publishContent.getExperimentId())) {
-                publishMiniprogramList = getExperimentMatchMiniprograms(publishContent, planAccount);
+                if (publishContent.getContentPoolType().contains("accountDemandPoolLevel")) {
+                    publishMiniprogramList = getDemandExperimentMatchMiniprograms(publishContent, planAccount);
+                } else {
+                    publishMiniprogramList = getExperimentMatchMiniprograms(publishContent, planAccount);
+                }
                 // 开关打开时,同时执行匹配流程获取小程序信息,将标题和封面替换至召回视频
                 if (!CollectionUtils.isEmpty(publishMiniprogramList)
                         && replaceRecallTitleCoverSwitch != null && replaceRecallTitleCoverSwitch) {
@@ -1094,6 +1104,84 @@ public class CoreServiceImpl implements CoreService {
         return publishMiniprogramList;
     }
 
+    /**
+     * 需求池匹配:通过 demand_produce_content_relation 查询实验ID,
+     * 再通过 demand_search_queue 查询匹配视频ID,生成小程序卡片
+     * 返回 null 表示无匹配结果,调用方应回退到原有流程
+     */
+    private List<PublishMiniprogram> getDemandExperimentMatchMiniprograms(PublishContent publishContent, PlanAccount planAccount) {
+        // Step 1: 通过账号ID和内容sourceId查询实验ID
+        List<String> experimentIds = demandProduceContentRelationMapper.selectExperimentIdsByAccountAndContent(
+                planAccount.getAccountId(), publishContent.getSourceId());
+        if (CollectionUtils.isEmpty(experimentIds)) {
+            log.info("getDemandExperimentMatchMiniprograms no experimentId found, accountId={}, sourceId={}",
+                    planAccount.getAccountId(), publishContent.getSourceId());
+            return null;
+        }
+        log.info("getDemandExperimentMatchMiniprograms found {} experimentIds, accountId={}, sourceId={}",
+                experimentIds.size(), planAccount.getAccountId(), publishContent.getSourceId());
+
+        // Step 2: 通过账号和实验ID查询 demand_search_queue,获取匹配视频
+        List<DemandSearchQueue> queueRecords = demandSearchQueueMapper.selectByAccountAndExperiments(
+                planAccount.getAccountName(), experimentIds, miniVideoNum);
+        if (CollectionUtils.isEmpty(queueRecords)) {
+            log.info("getDemandExperimentMatchMiniprograms no match video found, account={}, experimentIds={}",
+                    planAccount.getAccountName(), experimentIds);
+            return null;
+        }
+        log.info("getDemandExperimentMatchMiniprograms found {} queue records, account={}, experimentIds={}",
+                queueRecords.size(), planAccount.getAccountName(), experimentIds);
+
+        List<VideoDetail> videoDetails = new ArrayList<>();
+        Map<Long, String> videoExperimentMap = new LinkedHashMap<>();
+        for (DemandSearchQueue record : queueRecords) {
+            if (record.getVideoId() == null || record.getVideoId() == 0) {
+                continue;
+            }
+            VideoDetail videoDetail = VideoUtils.getVideoDetail(record.getVideoId());
+            if (videoDetail == null) {
+                log.warn("getVideoDetail null for videoId={}", record.getVideoId());
+                continue;
+            }
+            videoDetail.setSource("piaoquan");
+            videoDetail.setVideoOss(videoDetail.getOssVideoPath());
+            videoDetail.setCoverOss(videoDetail.getShareImgPath());
+            videoDetails.add(videoDetail);
+            videoExperimentMap.put(record.getVideoId(), record.getExperimentId());
+        }
+
+        if (CollectionUtils.isEmpty(videoDetails)) {
+            return null;
+        }
+
+        // 数量不足时用第一个视频补足,超出则截断(补足的视频复用第一个视频的experimentId)
+        if (videoDetails.size() < miniVideoNum) {
+            VideoDetail first = videoDetails.get(0);
+            int needAddCount = miniVideoNum - videoDetails.size();
+            for (int i = 0; i < needAddCount; i++) {
+                VideoDetail copy = new VideoDetail();
+                BeanUtils.copyProperties(first, copy);
+                videoDetails.add(copy);
+            }
+        }
+        if (videoDetails.size() > miniVideoNum) {
+            videoDetails = videoDetails.subList(0, miniVideoNum);
+        }
+
+        List<PublishMiniprogram> publishMiniprogramList = cardService.generateCards(videoDetails, planAccount, publishContent);
+        if (!CollectionUtils.isEmpty(publishMiniprogramList)) {
+            // 为每张卡片的 productionPath 拼接对应视频的 growthExtData 参数
+            appendGrowthExtDataToCards(publishMiniprogramList, videoExperimentMap);
+            for (PublishMiniprogram publishMiniprogram : publishMiniprogramList) {
+                rootSourceService.addRootSource(publishMiniprogram, planAccount, publishContent);
+                publishMiniprogramMapper.insertSelective(publishMiniprogram);
+            }
+            log.info("getDemandExperimentMatchMiniprograms success, cards={}, accountId={}, sourceId={}",
+                    publishMiniprogramList.size(), planAccount.getAccountId(), publishContent.getSourceId());
+        }
+        return publishMiniprogramList;
+    }
+
     /**
      * 为每张卡片的 productionPath 拼接对应视频的 growthExtData 参数
      * 参照 TouLiuHttpClient.sendAdFlowAddRequest 的拼接方式

+ 12 - 0
long-article-server/src/main/resources/mapper/longarticle/DemandProduceContentRelationMapper.xml

@@ -0,0 +1,12 @@
+<?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.piaoquan.longarticle.dao.mapper.longarticle.DemandProduceContentRelationMapper">
+
+    <select id="selectExperimentIdsByAccountAndContent" resultType="java.lang.String">
+        SELECT experiment_id
+        FROM demand_produce_content_relation
+        WHERE (account_id = #{accountId,jdbcType=VARCHAR} or account_id is null)
+          AND content_id = #{contentId,jdbcType=VARCHAR}
+    </select>
+
+</mapper>

+ 36 - 0
long-article-server/src/main/resources/mapper/longarticle/DemandSearchQueueMapper.xml

@@ -0,0 +1,36 @@
+<?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.piaoquan.longarticle.dao.mapper.longarticle.DemandSearchQueueMapper">
+
+    <resultMap id="BaseResultMap" type="com.tzld.piaoquan.longarticle.model.po.longarticle.DemandSearchQueue">
+        <id column="id" jdbcType="BIGINT" property="id"/>
+        <result column="account" jdbcType="VARCHAR" property="account"/>
+        <result column="search_key" jdbcType="VARCHAR" property="searchKey"/>
+        <result column="dt" jdbcType="VARCHAR" property="dt"/>
+        <result column="video_id" jdbcType="BIGINT" property="videoId"/>
+        <result column="channel_name" jdbcType="VARCHAR" property="channelName"/>
+        <result column="category" jdbcType="VARCHAR" property="category"/>
+        <result column="key_type" jdbcType="VARCHAR" property="keyType"/>
+        <result column="experiment_id" jdbcType="VARCHAR" property="experimentId"/>
+        <result column="status" jdbcType="TINYINT" property="status"/>
+        <result column="remark" jdbcType="VARCHAR" property="remark"/>
+        <result column="created_at" jdbcType="TIMESTAMP" property="createdAt"/>
+        <result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt"/>
+        <result column="drive_type" jdbcType="VARCHAR" property="driveType"/>
+    </resultMap>
+
+    <select id="selectByAccountAndExperiments" resultMap="BaseResultMap">
+        SELECT id, account, search_key, dt, video_id, channel_name, category, key_type,
+               experiment_id, status, remark, created_at, updated_at, drive_type
+        FROM demand_search_queue
+        WHERE account = #{account,jdbcType=VARCHAR}
+          AND experiment_id IN
+        <foreach collection="experimentIds" item="expId" open="(" separator="," close=")">
+            #{expId,jdbcType=VARCHAR}
+        </foreach>
+          AND video_id > 0
+        ORDER BY id DESC
+        LIMIT #{limit,jdbcType=INTEGER}
+    </select>
+
+</mapper>