Răsfoiți Sursa

fix(articles): 文章关联商品分页支持 + 无标签兜底首页推荐

- ProductRecommendationService: 新增 getArticleRelatedProductsFull 方法,
  返回完整候选列表(标签匹配或首页推荐兜底),原 getArticleRelatedProducts
  改为调用 full 并截取前 limit 条(向后兼容)
- ArticleController.relatedProducts: 新增 page 参数解析,全量拉取后按
  page*size 切片,与 /api/recommend/dimension-products 分页语义一致
- 无标签文章时兜底返回首页推荐(source=home_fallback),页面无内容时
  返回空列表而非重复第一页
Xiaogang Liao 1 săptămână în urmă
părinte
comite
b63bb7921a

+ 9 - 2
cfc-backend/src/main/java/com/etotem/cfc/controller/content/ArticleController.java

@@ -19,6 +19,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Collections;
 import java.util.Map;
 import com.etotem.cfc.util.ParamUtils;
 
@@ -51,6 +52,7 @@ public class ArticleController {
             return Result.error("articleId不能为空");
         }
         int size = body.get("size") != null ? Integer.parseInt(body.get("size").toString()) : 6;
+        int page = body.get("page") != null ? Math.max(1, Integer.parseInt(body.get("page").toString())) : 1;
 
         Long familyId = null;
         Long memberId = ParamUtils.getLong(body.get("memberId"), currentMemberId);
@@ -61,8 +63,13 @@ public class ArticleController {
             }
         }
 
-        List<Map<String, Object>> products = productRecommendationService.getArticleRelatedProducts(articleId, userId, familyId, memberId, size);
-        return Result.success(products);
+        // 全量候选 → 按页切片(与 /api/recommend/dimension-products 分页语义一致,保证翻页不重复、不遗漏)
+        int fetchLimit = page * size;
+        List<Map<String, Object>> all = productRecommendationService.getArticleRelatedProductsFull(articleId, userId, familyId, memberId, fetchLimit);
+        int from = (page - 1) * size;
+        int to = Math.min(from + size, all.size());
+        List<Map<String, Object>> result = from >= all.size() ? Collections.emptyList() : all.subList(from, to);
+        return Result.success(result);
     }
 
     @PostMapping("/list")

+ 24 - 5
cfc-backend/src/main/java/com/etotem/cfc/service/ProductRecommendationService.java

@@ -830,16 +830,28 @@ public class ProductRecommendationService {
      * @param limit     返回数量
      * @return 关联商品列表
      */
-    public List<Map<String, Object>> getArticleRelatedProducts(Long articleId, Long userId, Long familyId, Long memberId, int limit) {
-        if (articleId == null || limit <= 0) return Collections.emptyList();
+    /**
+     * 获取文章关联商品的完整候选列表(含标签匹配结果或首页推荐兜底)。
+     * 调用方自行切片实现分页(与 {@link #getDimensionRecommendations} 保持一致的分页语义)。
+     *
+     * @param articleId 文章ID
+     * @param userId    当前用户ID(可为空)
+     * @param familyId  家庭ID(可为空)
+     * @param memberId  成员ID(可为空,兜底首页推荐用于健康画像)
+     * @param limit     候选上限(0 表示不限制,由调用方决定分页窗口)
+     * @return 关联商品完整列表(标签匹配优先,无匹配则首页推荐兜底;均为空时返回空列表)
+     */
+    public List<Map<String, Object>> getArticleRelatedProductsFull(Long articleId, Long userId, Long familyId, Long memberId, int limit) {
+        if (articleId == null) return Collections.emptyList();
+        int fetchLimit = limit > 0 ? limit : Integer.MAX_VALUE;
 
-        // 1. 优先按文章标签匹配商品;若无标签关联商品,兜底返回首页推荐商品
-        List<Map<String, Object>> tagged = getArticleTagMatchedProducts(articleId, userId, familyId, limit);
+        // 1. 优先按文章标签匹配商品;若无标签关联商品,兜底返回首页推荐商品(与首页推荐区块同源,含健康画像过滤与个性化排序)
+        List<Map<String, Object>> tagged = getArticleTagMatchedProducts(articleId, userId, familyId, fetchLimit);
         if (tagged != null && !tagged.isEmpty()) {
             return tagged;
         }
         log.info("文章[{}]无标签关联商品,按首页推荐商品兜底返回", articleId);
-        List<Map<String, Object>> fallback = getDimensionRecommendations(null, familyId, memberId, null, limit, userId);
+        List<Map<String, Object>> fallback = getDimensionRecommendations(null, familyId, memberId, null, fetchLimit, userId);
         if (fallback == null || fallback.isEmpty()) {
             return Collections.emptyList();
         }
@@ -850,6 +862,13 @@ public class ProductRecommendationService {
         return fallback;
     }
 
+    public List<Map<String, Object>> getArticleRelatedProducts(Long articleId, Long userId, Long familyId, Long memberId, int limit) {
+        if (articleId == null || limit <= 0) return Collections.emptyList();
+        List<Map<String, Object>> all = getArticleRelatedProductsFull(articleId, userId, familyId, memberId, limit);
+        if (all.size() <= limit) return all;
+        return all.subList(0, limit);
+    }
+
     /**
      * 文章标签匹配商品推荐:通过文章标签与商品标签匹配,按 标签匹配数 > 用户权重 > 家庭权重 > 销量 排序
      *