Просмотр исходного кода

平台推荐排序公式 v2: 30 天衰减 SUM 替代 3 天 AVG

- 逐日得分: rovn × pow(exposure/5000, 0.5), 去除其他rovn × log 压缩
- 聚合得分: 30 天加权 SUM, α=0.9 (半衰期 ~6.6 天)
- content_platform_video 加 rovn/exposure 列, 同步任务读 ODPS 第 5/6 列回写
- 解决单天爆款 (如立夏 vid=68600076) 持续霸榜问题

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
刘立冬 18 часов назад
Родитель
Сommit
608a5a8151

+ 1 - 1
api-module/src/main/java/com/tzld/piaoquan/api/dao/mapper/contentplatform/ext/ContentPlatformPlanMapperExt.java

@@ -94,7 +94,7 @@ public interface ContentPlatformPlanMapperExt {
                                            @Param("oldStatus") Integer oldStatus,
                                            @Param("now") Long now);
 
-    List<ContentPlatformVideoAgg> getVideoAggList(@Param("dtList") List<String> dtList);
+    List<ContentPlatformVideoAgg> getVideoAggList(@Param("aggDt") String aggDt, @Param("dtList") List<String> dtList);
 
     List<ContentPlatformVideo> getVideoListByIds(@Param("videoIds") List<Long> videoIds);
 

+ 37 - 3
api-module/src/main/java/com/tzld/piaoquan/api/job/contentplatform/ContentPlatformVideoJob.java

@@ -49,7 +49,7 @@ public class ContentPlatformVideoJob {
     @Autowired
     private ContentPlatformAccountMapper accountMapper;
 
-    @Value("${video.agg.days:3}")
+    @Value("${video.agg.days:30}")
     private Integer videoAggDays;
 
     @ApolloJsonValue("${video.recommend.score.config:{}}")
@@ -61,7 +61,7 @@ public class ContentPlatformVideoJob {
         if (StringUtils.hasText(param)) {
             aggDt = param;
         }
-        List<String> dtList = DateUtil.getBeforeDays(aggDt, null, videoAggDays);
+        List<String> dtList = DateUtil.getBeforeDays(aggDt, aggDt, videoAggDays - 1);
         // 轮询查询大数据获取最近 videoAggDays 天视频
         for (String dt : dtList) {
             String sql = String.format("SELECT * FROM loghubods.wecom_cooperation_video_candidate_pool WHERE dt=%s;", dt);
@@ -81,12 +81,16 @@ public class ContentPlatformVideoJob {
                     String title = (String) record.get(2);
                     String videoUrl = (String) record.get(3);
                     Double score = Double.parseDouble((String) record.get(4));
+                    Long exposure = parseLongSafe(record.get(5));
+                    Double rovn = parseDoubleSafe(record.get(6));
                     item.setDt(dt);
                     item.setVideoId(videoId);
                     item.setCategory(category);
                     item.setTitle(title);
                     item.setVideo(videoUrl);
                     item.setScore(score);
+                    item.setExposure(exposure);
+                    item.setRovn(rovn);
                     item.setCreateTimestamp(now);
                     saveList.add(item);
                 }
@@ -157,7 +161,7 @@ public class ContentPlatformVideoJob {
             planMapperExt.deleteContentPlatformVideoAgg(aggDt);
         }
         // 聚合最近14天视频
-        List<ContentPlatformVideoAgg> saveAggList = planMapperExt.getVideoAggList(dtList);
+        List<ContentPlatformVideoAgg> saveAggList = planMapperExt.getVideoAggList(aggDt, dtList);
         if (CollectionUtils.isNotEmpty(saveAggList)) {
             Long now = System.currentTimeMillis();
             for (ContentPlatformVideoAgg item : saveAggList) {
@@ -438,6 +442,36 @@ public class ContentPlatformVideoJob {
         return bdScore.setScale(2, RoundingMode.HALF_UP).doubleValue();
     }
 
+    private Long parseLongSafe(Object v) {
+        if (v == null) {
+            return 0L;
+        }
+        try {
+            String s = v.toString().trim();
+            if (s.isEmpty() || "null".equalsIgnoreCase(s) || "NaN".equalsIgnoreCase(s)) {
+                return 0L;
+            }
+            return (long) Double.parseDouble(s);
+        } catch (NumberFormatException e) {
+            return 0L;
+        }
+    }
+
+    private Double parseDoubleSafe(Object v) {
+        if (v == null) {
+            return 0.0;
+        }
+        try {
+            String s = v.toString().trim();
+            if (s.isEmpty() || "null".equalsIgnoreCase(s) || "NaN".equalsIgnoreCase(s)) {
+                return 0.0;
+            }
+            double d = Double.parseDouble(s);
+            return Double.isNaN(d) ? 0.0 : d;
+        } catch (NumberFormatException e) {
+            return 0.0;
+        }
+    }
 
     private List<ContentPlatformAccount> getAllContentPlatformAccount() {
         ContentPlatformAccountExample example = new ContentPlatformAccountExample();

+ 22 - 0
api-module/src/main/java/com/tzld/piaoquan/api/model/po/contentplatform/ContentPlatformVideo.java

@@ -17,6 +17,10 @@ public class ContentPlatformVideo {
 
     private Double score;
 
+    private Double rovn;
+
+    private Long exposure;
+
     private Integer status;
 
     private Long createTimestamp;
@@ -87,6 +91,22 @@ public class ContentPlatformVideo {
         this.score = score;
     }
 
+    public Double getRovn() {
+        return rovn;
+    }
+
+    public void setRovn(Double rovn) {
+        this.rovn = rovn;
+    }
+
+    public Long getExposure() {
+        return exposure;
+    }
+
+    public void setExposure(Long exposure) {
+        this.exposure = exposure;
+    }
+
     public Integer getStatus() {
         return status;
     }
@@ -125,6 +145,8 @@ public class ContentPlatformVideo {
         sb.append(", cover=").append(cover);
         sb.append(", video=").append(video);
         sb.append(", score=").append(score);
+        sb.append(", rovn=").append(rovn);
+        sb.append(", exposure=").append(exposure);
         sb.append(", status=").append(status);
         sb.append(", createTimestamp=").append(createTimestamp);
         sb.append(", updateTimestamp=").append(updateTimestamp);

+ 1 - 1
api-module/src/main/java/com/tzld/piaoquan/api/service/contentplatform/impl/ContentPlatformApiServiceImpl.java

@@ -52,7 +52,7 @@ public class ContentPlatformApiServiceImpl implements ContentPlatformApiService
     @Autowired
     private RedisUtils redisUtils;
 
-    @Value("${video.min.score:0.6}")
+    @Value("${video.min.score:0.5}")
     private Double videoMinScore;
 
     @Override

+ 1 - 1
api-module/src/main/java/com/tzld/piaoquan/api/service/contentplatform/impl/ContentPlatformPlanServiceImpl.java

@@ -105,7 +105,7 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
     @Value("${vlog.share.appType:11}")
     private String shareAppType;
 
-    @Value("${video.min.score:0.6}")
+    @Value("${video.min.score:0.5}")
     private Double videoMinScore;
 
     @Value("${video.title.search.max.count:500}")

+ 37 - 7
api-module/src/main/resources/mapper/contentplatform/ContentPlatformVideoMapper.xml

@@ -10,6 +10,8 @@
     <result column="cover" jdbcType="VARCHAR" property="cover" />
     <result column="video" jdbcType="VARCHAR" property="video" />
     <result column="score" jdbcType="DOUBLE" property="score" />
+    <result column="rovn" jdbcType="DOUBLE" property="rovn" />
+    <result column="exposure" jdbcType="BIGINT" property="exposure" />
     <result column="status" jdbcType="INTEGER" property="status" />
     <result column="create_timestamp" jdbcType="BIGINT" property="createTimestamp" />
     <result column="update_timestamp" jdbcType="BIGINT" property="updateTimestamp" />
@@ -73,7 +75,7 @@
     </where>
   </sql>
   <sql id="Base_Column_List">
-    id, dt, video_id, category, title, cover, video, score, `status`, create_timestamp, 
+    id, dt, video_id, category, title, cover, video, score, rovn, exposure, `status`, create_timestamp,
     update_timestamp
   </sql>
   <select id="selectByExample" parameterType="com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVideoExample" resultMap="BaseResultMap">
@@ -110,13 +112,13 @@
     </if>
   </delete>
   <insert id="insert" parameterType="com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVideo">
-    insert into content_platform_video (id, dt, video_id, 
-      category, title, cover, 
-      video, score, `status`, 
+    insert into content_platform_video (id, dt, video_id,
+      category, title, cover,
+      video, score, rovn, exposure, `status`,
       create_timestamp, update_timestamp)
-    values (#{id,jdbcType=BIGINT}, #{dt,jdbcType=VARCHAR}, #{videoId,jdbcType=BIGINT}, 
-      #{category,jdbcType=VARCHAR}, #{title,jdbcType=VARCHAR}, #{cover,jdbcType=VARCHAR}, 
-      #{video,jdbcType=VARCHAR}, #{score,jdbcType=DOUBLE}, #{status,jdbcType=INTEGER}, 
+    values (#{id,jdbcType=BIGINT}, #{dt,jdbcType=VARCHAR}, #{videoId,jdbcType=BIGINT},
+      #{category,jdbcType=VARCHAR}, #{title,jdbcType=VARCHAR}, #{cover,jdbcType=VARCHAR},
+      #{video,jdbcType=VARCHAR}, #{score,jdbcType=DOUBLE}, #{rovn,jdbcType=DOUBLE}, #{exposure,jdbcType=BIGINT}, #{status,jdbcType=INTEGER},
       #{createTimestamp,jdbcType=BIGINT}, #{updateTimestamp,jdbcType=BIGINT})
   </insert>
   <insert id="insertSelective" parameterType="com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVideo">
@@ -146,6 +148,12 @@
       <if test="score != null">
         score,
       </if>
+      <if test="rovn != null">
+        rovn,
+      </if>
+      <if test="exposure != null">
+        exposure,
+      </if>
       <if test="status != null">
         `status`,
       </if>
@@ -181,6 +189,12 @@
       <if test="score != null">
         #{score,jdbcType=DOUBLE},
       </if>
+      <if test="rovn != null">
+        #{rovn,jdbcType=DOUBLE},
+      </if>
+      <if test="exposure != null">
+        #{exposure,jdbcType=BIGINT},
+      </if>
       <if test="status != null">
         #{status,jdbcType=INTEGER},
       </if>
@@ -225,6 +239,12 @@
       <if test="record.score != null">
         score = #{record.score,jdbcType=DOUBLE},
       </if>
+      <if test="record.rovn != null">
+        rovn = #{record.rovn,jdbcType=DOUBLE},
+      </if>
+      <if test="record.exposure != null">
+        exposure = #{record.exposure,jdbcType=BIGINT},
+      </if>
       <if test="record.status != null">
         `status` = #{record.status,jdbcType=INTEGER},
       </if>
@@ -249,6 +269,8 @@
       cover = #{record.cover,jdbcType=VARCHAR},
       video = #{record.video,jdbcType=VARCHAR},
       score = #{record.score,jdbcType=DOUBLE},
+      rovn = #{record.rovn,jdbcType=DOUBLE},
+      exposure = #{record.exposure,jdbcType=BIGINT},
       `status` = #{record.status,jdbcType=INTEGER},
       create_timestamp = #{record.createTimestamp,jdbcType=BIGINT},
       update_timestamp = #{record.updateTimestamp,jdbcType=BIGINT}
@@ -280,6 +302,12 @@
       <if test="score != null">
         score = #{score,jdbcType=DOUBLE},
       </if>
+      <if test="rovn != null">
+        rovn = #{rovn,jdbcType=DOUBLE},
+      </if>
+      <if test="exposure != null">
+        exposure = #{exposure,jdbcType=BIGINT},
+      </if>
       <if test="status != null">
         `status` = #{status,jdbcType=INTEGER},
       </if>
@@ -301,6 +329,8 @@
       cover = #{cover,jdbcType=VARCHAR},
       video = #{video,jdbcType=VARCHAR},
       score = #{score,jdbcType=DOUBLE},
+      rovn = #{rovn,jdbcType=DOUBLE},
+      exposure = #{exposure,jdbcType=BIGINT},
       `status` = #{status,jdbcType=INTEGER},
       create_timestamp = #{createTimestamp,jdbcType=BIGINT},
       update_timestamp = #{updateTimestamp,jdbcType=BIGINT}

+ 7 - 4
api-module/src/main/resources/mapper/contentplatform/ext/ContentPlatformPlanMapperExt.xml

@@ -163,11 +163,11 @@
     </select>
 
     <insert id="batchInsertContentPlatformVideo">
-        insert into content_platform_video (dt, video_id, category, title, cover, video, score, create_timestamp)
+        insert into content_platform_video (dt, video_id, category, title, cover, video, score, rovn, exposure, create_timestamp)
         values
         <foreach collection="records" item="item" separator=",">
             (#{item.dt}, #{item.videoId}, #{item.category}, #{item.title}, #{item.cover}, #{item.video}, #{item.score},
-            #{item.createTimestamp})
+            #{item.rovn}, #{item.exposure}, #{item.createTimestamp})
         </foreach>
     </insert>
 
@@ -260,9 +260,12 @@
 
     <select id="getVideoAggList"
             resultType="com.tzld.piaoquan.api.model.po.contentplatform.ContentPlatformVideoAgg">
-        SELECT t.video_id, t.category, t.title, t.cover, t.video, round(t.avg_score, 3) as score
+        SELECT t.video_id, t.category, t.title, t.cover, t.video, round(t.weighted_score, 3) as score
         FROM (
-        SELECT video_id, category, title, cover, video, AVG(score) OVER (PARTITION BY video_id) AS avg_score,
+        SELECT video_id, category, title, cover, video,
+            SUM(IFNULL(rovn, 0) * POW(GREATEST(IFNULL(exposure, 0) / 5000.0, 1.0), 0.5)
+                * POWER(0.9, DATEDIFF(STR_TO_DATE(#{aggDt}, '%Y%m%d'), STR_TO_DATE(dt, '%Y%m%d'))))
+                OVER (PARTITION BY video_id) AS weighted_score,
             ROW_NUMBER() OVER (PARTITION BY video_id ORDER BY dt DESC) AS rn
         FROM content_platform_video
         where dt in