Parcourir la source

update content RecommendService

reghao il y a 11 mois
Parent
commit
0b95766b1f

+ 68 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/controller/RecommendController.java

@@ -0,0 +1,68 @@
+package cn.reghao.tnb.content.app.vod.controller;
+
+import cn.reghao.jutil.jdk.string.IDObfuscation;
+import cn.reghao.jutil.web.WebResult;
+import cn.reghao.tnb.common.auth.AuthUser;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.app.vod.model.dto.UserRcmd;
+import cn.reghao.tnb.content.app.vod.model.dto.VideoRcmd;
+import cn.reghao.tnb.content.app.vod.service.RecommendService;
+import cn.reghao.tnb.user.api.iface.UserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author reghao
+ * @date 2025-03-31 14:44:11
+ */
+@Api(tags = "推荐内容接口")
+@RestController
+@RequestMapping("/api/content/rcmd")
+public class RecommendController {
+    @DubboReference(check = false)
+    private UserService userService;
+
+    private final RecommendService recommendService;
+    private final IDObfuscation userIdObfuscation;
+
+    public RecommendController(RecommendService recommendService, IDObfuscation userIdObfuscation) {
+        this.recommendService = recommendService;
+        this.userIdObfuscation = userIdObfuscation;
+    }
+
+    @AuthUser
+    @ApiOperation(value = "获取用户推荐模式", notes = "N")
+    @GetMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String getUserRcmd() {
+        long userId1 = UserContext.getUserId();
+        int mode = userService.getRecommendMode(userId1);
+        return WebResult.success(mode == 1);
+    }
+
+    @AuthUser
+    @ApiOperation(value = "设置用户推荐模式", notes = "N")
+    @PostMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String setUserRcmd(@RequestBody @Validated UserRcmd userRcmd) {
+        long userId1 = UserContext.getUserId();
+        boolean vip = userService.isVip(userId1);
+        if (userRcmd.getMode() && !vip) {
+            return WebResult.failWithMsg("此功能仅对 VIP 用户开放");
+        }
+
+        userService.setRecommendMode(userId1, userRcmd.getMode());
+        return WebResult.success();
+    }
+
+    @AuthUser
+    @ApiOperation(value = "设置用户不喜欢的视频", notes = "N")
+    @PostMapping(value = "/video", produces = MediaType.APPLICATION_JSON_VALUE)
+    public String dislikeVideo(@RequestBody @Validated VideoRcmd videoRcmd) {
+        long userId1 = UserContext.getUserId();
+        recommendService.dislikeVideo(userId1, videoRcmd.getVideoId());
+        return WebResult.success();
+    }
+}

+ 15 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/model/dto/UserRcmd.java

@@ -0,0 +1,15 @@
+package cn.reghao.tnb.content.app.vod.model.dto;
+
+import lombok.Getter;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author reghao
+ * @date 2025-03-31 14:52:38
+ */
+@Getter
+public class UserRcmd {
+    @NotNull
+    private Boolean mode;
+}

+ 15 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/model/dto/VideoRcmd.java

@@ -0,0 +1,15 @@
+package cn.reghao.tnb.content.app.vod.model.dto;
+
+import lombok.Getter;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author reghao
+ * @date 2025-03-31 15:09:04
+ */
+@Getter
+public class VideoRcmd {
+    @NotBlank
+    private String videoId;
+}

+ 2 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/service/RecommendService.java

@@ -13,6 +13,8 @@ import java.util.*;
 public interface RecommendService {
     List<VideoCard> getRecommendVideos(long userId, String nextId);
     void putRecommendVideos(long loginUser, int size);
+    void resetUserRcmd(long userId);
+    void dislikeVideo(long userId, String videoId);
     String getSimilarVideos(String videoId);
     PageScroll<VideoCard> getVideoTimeline(long userId, String nextIdStr);
     List<BannerVideo> getBannerVideos();

+ 15 - 17
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/service/impl/RecommendServiceImpl.java

@@ -1,11 +1,11 @@
 package cn.reghao.tnb.content.app.vod.service.impl;
 
-import cn.reghao.jutil.jdk.db.PageBound;
 import cn.reghao.jutil.web.WebResult;
 import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
 import cn.reghao.tnb.common.db.PageScroll;
 import cn.reghao.tnb.content.api.dto.VideoCard;
 import cn.reghao.tnb.content.api.dto.VideoPostCard;
+import cn.reghao.tnb.content.app.util.redis.ds.RedisList;
 import cn.reghao.tnb.content.app.vod.db.mapper.VideoPostMapper;
 import cn.reghao.tnb.content.app.vod.model.po.VideoPost;
 import cn.reghao.tnb.content.api.dto.BannerVideo;
@@ -49,12 +49,13 @@ public class RecommendServiceImpl implements RecommendService {
     private final VideoPostQuery videoPostQuery;
     private final UserInterestBased userInterestBased;
     private final RedisSet redisSet;
+    private final RedisList redisList;
     private final RcmdProducer rcmdProducer;
 
     public RecommendServiceImpl(RedisSortedSet sortedSet, RedisHash<VideoCard> redisHash,
                                 ContentPermission contentPermission, VideoPostMapper videoPostMapper,
-                                VideoPostQuery videoPostQuery, UserInterestBased userInterestBased, RedisSet redisSet,
-                                RcmdProducer rcmdProducer) {
+                                VideoPostQuery videoPostQuery, UserInterestBased userInterestBased,
+                                RedisSet redisSet, RedisList redisList, RcmdProducer rcmdProducer) {
         this.sortedSet = sortedSet;
         this.redisHash = redisHash;
         this.contentPermission = contentPermission;
@@ -62,6 +63,7 @@ public class RecommendServiceImpl implements RecommendService {
         this.videoPostQuery = videoPostQuery;
         this.userInterestBased = userInterestBased;
         this.redisSet = redisSet;
+        this.redisList = redisList;
         this.rcmdProducer = rcmdProducer;
     }
 
@@ -86,7 +88,7 @@ public class RecommendServiceImpl implements RecommendService {
             return;
         }
 
-        RcmdData rcmdData = new RcmdData(loginUser, pageSize*3);
+        RcmdData rcmdData = new RcmdData(loginUser, size);
         try {
             rcmdProducer.put(rcmdData);
         } catch (InterruptedException e) {
@@ -94,20 +96,16 @@ public class RecommendServiceImpl implements RecommendService {
         }
     }
 
-    public List<VideoCard> getRecommendVideos1(long userId, String nextId) {
-        Set<String> videoIds = userInterestBased.getUserInterestItems(userId);
-        List<VideoCard> videoCards = videoPostQuery.getVideoCards(new ArrayList<>(videoIds));
-
-        int pageNumber = Integer.parseInt(nextId)+1;
-        if (pageNumber > 100) {
-            return Collections.emptyList();
-        }
+    @Override
+    public void resetUserRcmd(long userId) {
+        String itemsKey = RedisKeys.getUserItemKey(userId);
+        redisSet.spop(itemsKey);
+    }
 
-        long total = sortedSet.count(RedisKey.recommendZsetKey());
-        PageBound pageBound = PageBound.get(pageNumber, pageSize, (int) total);
-        Set<String> set = sortedSet.get(RedisKey.recommendZsetKey(), pageBound.getStart(), pageBound.getEnd()-1);
-        List<VideoCard> list = redisHash.multiGet(RedisKey.recommendHashKey(), set);
-        return list;
+    @Override
+    public void dislikeVideo(long userId, String videoId) {
+        String dislikeKey = RedisKeys.getUserDislikeKey(userId);
+        redisList.add(dislikeKey, videoId);
     }
 
     public String getSimilarVideos(String videoId) {

+ 12 - 2
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/service/rcmd/RcmdConsumer.java

@@ -1,11 +1,14 @@
 package cn.reghao.tnb.content.app.vod.service.rcmd;
 
 import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
+import cn.reghao.tnb.content.app.util.redis.ds.RedisList;
 import cn.reghao.tnb.content.app.util.redis.ds.RedisSet;
 import cn.reghao.tnb.content.app.vod.service.ContentPermission;
 import cn.reghao.tnb.content.app.vod.service.VideoPostQuery;
 import cn.reghao.tnb.content.app.vod.service.rcmd.task.RcmdTask;
+import cn.reghao.tnb.user.api.iface.UserService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.PostConstruct;
@@ -20,6 +23,9 @@ import java.util.concurrent.ThreadPoolExecutor;
 @Slf4j
 @Component
 public class RcmdConsumer {
+    @DubboReference(check = false)
+    private UserService userService;
+
     private final int threads = 10;
     private final ExecutorService threadPool = ThreadPoolWrapper.threadPool("data-consumer", threads);
     private final Object monitor;
@@ -27,13 +33,15 @@ public class RcmdConsumer {
     private final VideoPostQuery videoPostQuery;
     private final UserInterestBased userInterestBased;
     private final RedisSet redisSet;
+    private final RedisList redisList;
     private final ContentPermission contentPermission;
 
     public RcmdConsumer(VideoPostQuery videoPostQuery, UserInterestBased userInterestBased, RedisSet redisSet,
-                        ContentPermission contentPermission, RcmdProducer rcmdProducer) {
+                        RedisList redisList, ContentPermission contentPermission, RcmdProducer rcmdProducer) {
         this.videoPostQuery = videoPostQuery;
         this.userInterestBased = userInterestBased;
         this.redisSet = redisSet;
+        this.redisList = redisList;
         this.contentPermission = contentPermission;
         this.monitor = rcmdProducer.getMonitor();
         this.rcmdProducer = rcmdProducer;
@@ -70,7 +78,9 @@ public class RcmdConsumer {
             if (object != null) {
                 if (object instanceof RcmdData) {
                     RcmdData rcmdData = (RcmdData) object;
-                    RcmdTask rcmdTask = new RcmdTask(rcmdData, videoPostQuery, userInterestBased, redisSet, contentPermission);
+                    int mode = userService.getRecommendMode(rcmdData.getUserId());
+                    RcmdTask rcmdTask = new RcmdTask(rcmdData, videoPostQuery, userInterestBased,
+                            redisSet, redisList, contentPermission, mode);
                     Future<?> future = threadPool.submit(rcmdTask);
                 } else {
                     log.error("Object 类型未知");

+ 4 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/service/rcmd/RedisKeys.java

@@ -5,6 +5,10 @@ package cn.reghao.tnb.content.app.vod.service.rcmd;
  * @date 2023-02-21 21:34:18
  */
 public class RedisKeys {
+    public static String getUserDislikeKey(long userId) {
+        return String.format("tnb:rcmd:dislike:user:%s", userId);
+    }
+
     public static String getUserItemKey(long userId) {
         return String.format("tnb:rcmd:items:user:%s", userId);
     }

+ 13 - 5
content/content-service/src/main/java/cn/reghao/tnb/content/app/vod/service/rcmd/task/RcmdTask.java

@@ -1,6 +1,7 @@
 package cn.reghao.tnb.content.app.vod.service.rcmd.task;
 
 import cn.reghao.tnb.content.api.dto.VideoCard;
+import cn.reghao.tnb.content.app.util.redis.ds.RedisList;
 import cn.reghao.tnb.content.app.util.redis.ds.RedisSet;
 import cn.reghao.tnb.content.app.vod.service.ContentPermission;
 import cn.reghao.tnb.content.app.vod.service.VideoPostQuery;
@@ -12,6 +13,7 @@ import lombok.extern.slf4j.Slf4j;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author reghao
@@ -21,22 +23,30 @@ import java.util.Set;
 public class RcmdTask implements Runnable {
     private final RcmdData rcmdData;
     private final RedisSet redisSet;
+    private final RedisList redisList;
     private final UserInterestBased userInterestBased;
     private final VideoPostQuery videoPostQuery;
     private final ContentPermission contentPermission;
+    private final int mode;
 
-    public RcmdTask(RcmdData rcmdData, VideoPostQuery videoPostQuery,
-                    UserInterestBased userInterestBased, RedisSet redisSet,
-                    ContentPermission contentPermission) {
+    public RcmdTask(RcmdData rcmdData, VideoPostQuery videoPostQuery, UserInterestBased userInterestBased,
+                    RedisSet redisSet, RedisList redisList, ContentPermission contentPermission, int mode) {
         this.rcmdData = rcmdData;
         this.videoPostQuery = videoPostQuery;
         this.userInterestBased = userInterestBased;
         this.redisSet = redisSet;
+        this.redisList = redisList;
         this.contentPermission = contentPermission;
+        this.mode = mode;
     }
 
     public void run() {
         long loginUser = rcmdData.getUserId();
+        String dislikeKey = RedisKeys.getUserDislikeKey(loginUser);
+        List<String> dislikeVideos = redisList.getAll(dislikeKey).stream()
+                .map(object -> (String) object)
+                .collect(Collectors.toList());
+
         int size = rcmdData.getSize();
         List<Integer> userScopes = contentPermission.getUserScopes(loginUser);
         long start = System.currentTimeMillis();
@@ -47,8 +57,6 @@ public class RcmdTask implements Runnable {
             }
 
             String itemsKey = RedisKeys.getUserItemKey(loginUser);
-            Set<String> set = userInterestBased.getUserInterestItems(loginUser);
-            List<String> videoIds1 = new ArrayList<>(set);
             List<String> videoIds = videoPostQuery.getRandomVideoIds(userScopes, size);
             if (!videoIds.isEmpty()) {
                 List<VideoCard> videoCards = videoPostQuery.getVideoCards(videoIds);