|
@@ -135,6 +135,39 @@ public class ProductRecommendationService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 3.5 fallback: 当 dimension_mapping 表无数据时,从 Product.domain 匹配
|
|
|
|
|
+ if (candidates.isEmpty()) {
|
|
|
|
|
+ LambdaQueryWrapper<Product> domainQw = new LambdaQueryWrapper<Product>()
|
|
|
|
|
+ .eq(Product::getStatus, "on_shelf")
|
|
|
|
|
+ .gt(Product::getStock, 0)
|
|
|
|
|
+ .orderByAsc(Product::getSortOrder);
|
|
|
|
|
+ if (dimensionCode != null && !dimensionCode.isEmpty()) {
|
|
|
|
|
+ domainQw.eq(Product::getDomain, dimensionCode);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!purchasedIds.isEmpty()) {
|
|
|
|
|
+ domainQw.notIn(Product::getId, purchasedIds);
|
|
|
|
|
+ }
|
|
|
|
|
+ SortUtil.applySort(domainQw);
|
|
|
|
|
+ List<Product> domainMatched = productMapper.selectList(domainQw);
|
|
|
|
|
+ for (Product p : domainMatched) {
|
|
|
|
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
|
|
+ item.put("id", p.getId());
|
|
|
|
|
+ item.put("productId", p.getId());
|
|
|
|
|
+ item.put("name", p.getName());
|
|
|
|
|
+ item.put("coverImage", p.getCoverImage());
|
|
|
|
|
+ item.put("price", p.getPrice());
|
|
|
|
|
+ item.put("memberPrice", p.getPrice());
|
|
|
|
|
+ item.put("productType", p.getProductType());
|
|
|
|
|
+ item.put("source", "dimension");
|
|
|
|
|
+ item.put("url", "/pages/discover/product-detail/product-detail?id=" + p.getId());
|
|
|
|
|
+ String reason = buildRecommendationReason(p, dimensionCode, memberScores);
|
|
|
|
|
+ item.put("reason", reason);
|
|
|
|
|
+ double matchScore = calculateMatchScore(p, dimensionCode, memberScores);
|
|
|
|
|
+ item.put("matchScore", matchScore);
|
|
|
|
|
+ candidates.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 4. 关联关系推荐(动态:画像缺口、共购、复购)
|
|
// 4. 关联关系推荐(动态:画像缺口、共购、复购)
|
|
|
List<Map<String, Object>> assocResults = queryAssociationRecommendations(
|
|
List<Map<String, Object>> assocResults = queryAssociationRecommendations(
|
|
|
dimensionCode, familyId, memberId, purchasedIds, excludeProductIds, limit - candidates.size());
|
|
dimensionCode, familyId, memberId, purchasedIds, excludeProductIds, limit - candidates.size());
|