← 返回文档导航

BeeX 商品详情推荐搜索实现说明

店铺/品牌推荐 · 同品类推荐 · 无返佣相似商品推荐 · 前后端实现细节
商品详情推荐搜索技术实现
这份文档说明当前商品详情页三类推荐如何实现:用户在详情页看到的“店铺/品牌推荐”“同品类推荐”,以及商品没有返佣或无法转链时的“相似商品推荐”。重点不是只列接口,而是把为什么这么做、各平台怎么查、缓存怎么生效、哪些情况不兜底写清楚。

1. 目标和结论

商品详情页现在不是只展示当前商品,而是要让用户在任意入口都能继续找到可购买、可返现的商品。尤其是用户粘贴的商品没有返佣、没有库存、不能转链时,不能让链路断掉,要推荐“相似可返现商品”。

当前结论 详情页统一通过 POST /api/v1/products/recommendations 获取推荐商品。后端新增 SIMILAR 场景,专门处理“当前商品无法返现时,推荐同类替代商品”的场景。

当前业务为 User-only 单层邀请返佣:所有用户业务角色都是 USER,用户之间只有一层直接邀请返佣。商品详情接口中可能带有 viewerstarLevelshareCommissionBps 等字段,前端一律把 viewer.roleUSER 处理,starLevel 不参与收益计算;邀请人可得比例以服务端当前有效规则返回的结果为准。

2. 三类推荐场景

场景scene出现位置业务含义关键原则
店铺/品牌推荐 SHOP 商品详情页“根据这个商品推荐:关联店铺/品牌商品” 尽量展示同店铺或同品牌的商品。 不做泛化兜底,避免把别的店铺商品误展示成同店铺商品。
同品类推荐 CATEGORY 商品详情页“也是推荐:根据商品品类推荐” 展示同类目下的可返现商品。 如果同类目搜空,可以退化到通用热门商品,保证页面不断流。
无返佣相似商品推荐 SIMILAR 后端已支持;H5 无返佣替代区当前仍使用 CATEGORY 根据商品标题、类目、店铺名提取关键词,推荐相同或相近商品。 优先使用商品标题提取出的核心词,再用类目/店铺兜底。

3. 整体调用流程

sequenceDiagram
    participant User as 用户
    participant H5 as 商品详情页
    participant API as BeeX API
    participant Cache as 本地/服务端缓存
    participant TK as TikTok APIs
    participant Shopee as Shopee API
    participant Lazada as Lazada API

    User->>H5: 打开商品详情
    H5->>API: POST /api/v1/products/resolve
    API-->>H5: 商品详情 + 用户可见返现 + 兼容用户上下文
    H5->>Cache: 读取推荐缓存
    Cache-->>H5: 先显示旧推荐(如有)
    H5->>API: POST /api/v1/products/recommendations scene=SHOP
    H5->>API: POST /api/v1/products/recommendations scene=CATEGORY
    alt 当前商品无返佣或不可转链
      H5->>API: POST /api/v1/products/recommendations scene=CATEGORY
      Note over H5,API: 后端支持 SIMILAR,但当前 H5 尚未切换
    end
    API->>TK: TikTok Campaign / Creator 搜索
    API->>Shopee: Shopee productOfferV2 搜索
    API->>Lazada: Lazada product feed/search
    API-->>H5: 推荐商品列表
    H5->>Cache: 写入 72 小时缓存

4. 接口协议

4.1 推荐接口

POST /api/v1/products/recommendations
Content-Type: application/json
字段类型说明
countryCodestring当前历史字段,服务端仍兼容。国家已经按数据中心隔离,业务判断不依赖它。
platformstringTIKTOKSHOPEELAZADA。为空时可以跨平台聚合。
sceneenumSHOPCATEGORYSIMILAR
keywordstring运营或前端显式传入的搜索词,优先级最高。
productNamestring当前商品标题,主要给 SIMILAR 场景提取相似商品关键词。
shopIdstring店铺 ID。Shopee 的店铺推荐必须依赖它。
shopNamestring店铺名。TikTok Campaign 缓存可以用精确店铺名匹配。
categoryIdstring平台类目 ID。品类推荐优先使用。
categoryNamestring类目名。类目 ID 缺失时用于关键词兜底。
excludeProductIdstring排除 BeeX 当前商品 ID,避免推荐列表出现自己。
excludePlatformProductIdstring排除平台商品 ID,避免跨来源重复。
pageSizenumber推荐条数,默认由后端限制。
cursorstring分页游标,当前详情推荐主要取首屏。

4.2 SHOP 请求示例

{
  "countryCode": "ID",
  "platform": "TIKTOK",
  "scene": "SHOP",
  "shopName": "LUNAVEE INDONESIA",
  "shopId": "749322114",
  "excludeProductId": "tiktok-1733045523587827027",
  "excludePlatformProductId": "1733045523587827027",
  "pageSize": 20
}

4.3 CATEGORY 请求示例

{
  "countryCode": "ID",
  "platform": "TIKTOK",
  "scene": "CATEGORY",
  "categoryId": "601493",
  "categoryName": "Sabun & Sabun Mandi",
  "excludeProductId": "tiktok-1733045523587827027",
  "excludePlatformProductId": "1733045523587827027",
  "pageSize": 20
}

4.4 SIMILAR 请求示例

{
  "countryCode": "ID",
  "platform": "TIKTOK",
  "scene": "SIMILAR",
  "productName": "Lunavée - Rose Flowers Fragrance Body Wash 400ML",
  "categoryId": "601493",
  "categoryName": "Sabun & Sabun Mandi",
  "shopName": "LUNAVEE INDONESIA",
  "excludeProductId": "tiktok-1733045523587827027",
  "excludePlatformProductId": "1733045523587827027",
  "pageSize": 20
}

5. H5 实现

商品详情页在 app/pages/product-detail.vue 内部处理三类推荐:

方法场景说明
loadShopProductRecommendations()SHOP根据当前商品的 shopId/shopName 拉店铺/品牌商品。
loadCategoryProductRecommendations()CATEGORY根据当前商品的 categoryId/categoryName 拉同品类商品。
loadUnavailablePlatformRecommendations()CATEGORY当前商品无返佣、不可购买或不可转链时,当前 H5 按类目拉替代商品。后端虽已支持 SIMILAR + productName,但尚未接入这条 H5 调用。

H5 会给推荐接口做本地缓存。缓存键包含 sceneplatform、当前商品 ID、店铺、类目、商品标题和 pageSize。这样同一商品反复进入详情时,会先显示本地缓存,再后台刷新。

为什么 SIMILAR 缓存键要包含 productName? 两个商品可能同类目、同店铺,但标题完全不同。如果 SIMILAR 不把标题放进缓存键,用户看到的“相似商品”可能来自另一个商品。

6. 后端实现

6.1 新增枚举

ProductRecommendationScene 现在包含:

public enum ProductRecommendationScene {
    SHOP,
    CATEGORY,
    SIMILAR
}

6.2 请求模型新增 productName

AffiliateProductRecommendationRequest 增加 productName,用于无返佣替代商品搜索。它不替代 keyword,而是在前端没有显式搜索词时作为关键词来源。

6.3 关键词生成规则

scene关键词优先级说明
SHOPkeyword → shopName → categoryName店铺场景优先使用店铺信息。
CATEGORYkeyword → categoryName → shopName品类场景优先使用类目信息。
SIMILARkeyword → productName → categoryName → shopName相似商品场景优先使用商品标题。

6.4 SIMILAR 的标题压缩

商品标题通常很长,比如包含促销词、规格、单位、国家名、套装词。后端不会直接拿完整标题搜索,而是用 compactSimilarKeyword() 做压缩:

  1. 按字母和数字切词。
  2. 转小写。
  3. 过滤通用词和促销词,例如 officialshopprodukpromobundlemlgramindonesia
  4. 过滤纯数字和过短词。
  5. 最多取前 4 个核心词。

这样 Lunavée - Rose Flowers Fragrance Body Wash 400ML 会被压缩为更适合搜索的核心词,而不是把无意义规格也带到平台搜索。

6.5 关键词过滤增强

后端 filterByKeyword() 现在支持两种命中方式:

  1. 完整归一化字符串包含:适合简单关键词。
  2. 所有关键词 token 都命中:适合标题里有空格、横杠、标点的场景。

匹配字段包括商品名、品牌名、类目、店铺名、平台商品 ID 和 BeeX 商品 ID。这样可以避免因为 Cool-VitaCoolvitaCool Vita 这种标点差异导致错误 miss。

7. 平台搜索策略

7.1 TikTok

scene数据来源策略
SHOPPartner Campaign 本地缓存shopId 或精确 shopName 匹配。不会用 Creator 关键词模糊搜店铺,避免跨店污染。
CATEGORYPartner Campaign 缓存 + Creator Search优先按类目找 Campaign 高返佣商品,再通过 Creator 搜索补足。
SIMILARPartner Campaign 缓存 + Creator Search先用相似关键词过滤 Campaign 缓存;如果不足,再按类目/Creator 搜索兜底。

7.2 Shopee

scene数据来源策略
SHOPShopee productOfferV2必须有 shopId。如果只有店铺名,当前不做模糊店铺搜索。
CATEGORYShopee productOfferV2优先传 productCatId,没有类目 ID 时用关键词。
SIMILARShopee productOfferV2使用从商品名压缩出来的关键词,同时尽量带类目过滤。

7.3 Lazada

Lazada 当前通过 Affiliate Product Feed/Search 类能力做推荐。SIMILAR 会把压缩后的商品关键词传给 Lazada 搜索;如果 Lazada 返回的商品缺少价格、佣金或稳定跳转链接,后端会过滤或降级展示。

Lazada 的边界 Lazada 的商品详情补全能力依赖平台接口是否能按 productId 返回价格、图片和佣金。不能拿到完整信息时,BeeX 不能在 C 端承诺返现金额,只能展示可确认的信息。

8. 过滤、缓存和兜底规则

8.1 过滤规则

推荐列表只应该展示能形成购买链路的商品。后端会过滤掉当前商品本身,并尽量过滤掉以下商品:

8.2 兜底规则

scene是否兜底原因
SHOP店铺推荐必须准确。搜不到就隐藏,不应该拿别的店铺冒充。
CATEGORY品类推荐是“猜你喜欢”,搜空时可以用热门商品保持页面连续。
SIMILAR当前商品不可返现时,目标是给用户一个继续购买的出口,可以先同类,再热门兜底。

8.3 缓存规则

H5 对推荐结果使用本地缓存,先渲染缓存,再后台刷新。这样详情页不会因为多个推荐接口同时请求而卡住主内容。

缓存内容缓存键设计理由
店铺推荐scene + platform + productId + shopId/shopName同一商品、同一店铺可复用。
品类推荐scene + platform + productId + categoryId/categoryName避免不同品类串数据。
相似推荐scene + platform + productId + productName + category/shop标题不同,相似推荐也应该不同。

9. 用户上下文与字段

用户上下文接口返回体示例:

GET /api/v1/growth/users/{userId}/role

商品详情返回体结构示例:

{
  "shareCommissionBps": 1200,
  "viewer": {
    "role": "USER",
    "starLevel": 1
  }
}

viewer.roleUSER 处理,starLevel(若字段仍在)不参与收益计算。shareCommissionBps 表达当前有效的一级邀请返佣比例。

当前约束 商品详情页展示的钱统一使用服务端返回的 cashbackMinor、活动返佣明细和当前有效规则结果,一律以服务端当前有效规则为准。

10. 代码位置和验证

10.1 后端代码

文件作用
beex-service/seahub-core/src/main/java/com/seahub/x/core/affiliate/ProductRecommendationScene.java新增 SIMILAR 场景。
beex-service/seahub-core/src/main/java/com/seahub/x/core/affiliate/AffiliateProductRecommendationRequest.java新增 productName 请求字段。
beex-service/seahub-core/src/main/java/com/seahub/x/core/affiliate/AffiliateProductService.java推荐搜索主逻辑、关键词压缩、平台路由、缓存商品过滤、兜底规则。
beex-service/seahub-core/src/main/java/com/seahub/x/core/affiliate/AffiliateProductItem.java当前仍保留 viewershareCommissionBps 兼容字段;业务角色统一按 USER 处理。

10.2 H5 代码

文件作用
beex-app-h5/app/pages/product-detail.vue详情页加载 SHOP/CATEGORY/SIMILAR 推荐,缓存推荐结果,使用详情返回的 viewer 更新本地角色。
beex-app-h5/app/types/beex-api.ts前端接口类型新增 SIMILARproductNameviewer

10.3 已做验证

10.4 还需要业务测试的点

  1. 用一个有返佣 TikTok 商品进入详情,确认店铺推荐、品类推荐正常出现。
  2. 用一个无返佣或不可转链商品进入详情,确认 SIMILAR 能返回替代商品。
  3. 用 Shopee 商品进入详情,确认有 shopId 时店铺推荐正常,没有 shopId 时不会错误兜底。
  4. 用 Lazada 商品进入详情,确认能拿到的字段展示正常,拿不到返现时不展示虚假返现金额。
  5. 确认详情页对 /api/v1/growth/users/{userId}/role 返回值一律按 USER 身份处理,收益以服务端当前有效规则为准。
  6. 若后续把无返佣替代区切换为 SIMILAR,需单独完成 H5 接线和回归测试后再把本文状态改为“已闭环”。