|
|
@@ -0,0 +1,237 @@
|
|
|
+# 用户画像驱动推荐系统设计
|
|
|
+
|
|
|
+## 1. 概述
|
|
|
+
|
|
|
+利用 AI 动态问卷引擎生成的用户画像(维度得分 + 需求),建立结构化的画像存储,并通过多维度加权匹配算法向用户推荐商品、文章和活动。
|
|
|
+
|
|
|
+## 2. 背景与现状
|
|
|
+
|
|
|
+### 2.1 已有基础设施
|
|
|
+
|
|
|
+| 组件 | 现状 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| `AiQProfile` | `userProfileJson` / `needProfileJson` JSON 存储 | 画像原始快照,不可查询 |
|
|
|
+| `ProductDimensionMapping` | 商品-维度映射表(dimensionCode, matchScore, matchReason) | 仅覆盖商品 |
|
|
|
+| `RecommendationService` | 基于标签关键词的跨表搜索 | 无维度权重计算 |
|
|
|
+| `ContentRecommendService` | 基于 dimensionCode 筛选推荐 | 权重单一,无多维度聚合 |
|
|
|
+| `RecommendationResult` | 统一推荐结果 DTO | 格式可用 |
|
|
|
+| `RecommendationLog` | 行为追踪表(show/click/consume) | 可用 |
|
|
|
+
|
|
|
+### 2.2 核心缺口
|
|
|
+
|
|
|
+- 画像维度得分未结构化存储,无法被推荐系统查询
|
|
|
+- 推荐引擎只做标签匹配,没有利用维度权重做多维度聚合评分
|
|
|
+- 文章和活动缺少统一的维度映射表
|
|
|
+
|
|
|
+## 3. 架构
|
|
|
+
|
|
|
+```
|
|
|
+┌──────────────────────────────────────────────────────────────────┐
|
|
|
+│ AI 问卷完成(AiQuestionnaireService.finish) │
|
|
|
+│ → doGenerateProfile() │
|
|
|
+│ ├─ 写 AiQProfile(原始 JSON 快照,不变) │
|
|
|
+│ └─ 同步写入 member_profile_dimension(结构化维度得分) │
|
|
|
+└──────────────────────────┬───────────────────────────────────────┘
|
|
|
+ │
|
|
|
+ ▼
|
|
|
+┌──────────────────────────────────────────────────────────────────┐
|
|
|
+│ ProfileRecommendService.recommend(memberId, types, limit) │
|
|
|
+│ │
|
|
|
+│ 1. 查 member_profile_dimension(user 维度得分 + need 需求) │
|
|
|
+│ 2. 对每种 content type: │
|
|
|
+│ a. 查对应的 dimension_mapping 表 │
|
|
|
+│ b. 计算加权匹配得分 = Σ(profile_dim × content_dim_match) │
|
|
|
+│ c. 需求加成:匹配 need 的维度 +20 分 │
|
|
|
+│ 3. 合并排序 → 取 top N │
|
|
|
+│ 4. 记录推荐日志(show) │
|
|
|
+└──────────────────────────┬───────────────────────────────────────┘
|
|
|
+ │
|
|
|
+ ▼
|
|
|
+┌──────────────────────────────────────────────────────────────────┐
|
|
|
+│ 推荐端点 /api/recommend/profile(供小程序/Web 调用) │
|
|
|
+│ 统一返回 RecommendItemVO 列表 │
|
|
|
+└──────────────────────────────────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+## 4. 数据模型
|
|
|
+
|
|
|
+### 4.1 画像维度表(新建)
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE TABLE member_profile_dimension (
|
|
|
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ member_id BIGINT NOT NULL COMMENT '家庭成员ID',
|
|
|
+ scene_id BIGINT COMMENT '来源场景ID(ai_q_scene.id)',
|
|
|
+ dimension_code VARCHAR(50) NOT NULL COMMENT '五维编码: body/mind/wisdom/wealth/social',
|
|
|
+ score INT NOT NULL COMMENT '得分 0-100',
|
|
|
+ description VARCHAR(500) COMMENT '维度描述',
|
|
|
+ evidence JSON COMMENT '证据列表 ["ev1","ev2"]',
|
|
|
+ profile_type VARCHAR(10) NOT NULL DEFAULT 'user' COMMENT 'user=用户画像, need=需求画像',
|
|
|
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_member_dim (member_id, dimension_code),
|
|
|
+ INDEX idx_member_type (member_id, profile_type)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成员画像维度得分';
|
|
|
+```
|
|
|
+
|
|
|
+**维度编码规则**:与五维体系一致 `body/mind/wisdom/wealth/social`,AI 问卷的 `dimensions_json` 中的维度键值映射到五维编码(如"肠道菌群状态"→"body")。
|
|
|
+
|
|
|
+### 4.2 内容维度映射表
|
|
|
+
|
|
|
+**商品**:复用现有 `ProductDimensionMapping`(dimensionCode, matchScore, matchReason, enabled)。
|
|
|
+
|
|
|
+**文章**(新建):
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE TABLE article_dimension_mapping (
|
|
|
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ article_id BIGINT NOT NULL,
|
|
|
+ dimension_code VARCHAR(50) NOT NULL,
|
|
|
+ match_score INT DEFAULT 50 COMMENT '关联度 0-100',
|
|
|
+ match_reason VARCHAR(200) COMMENT '关联原因',
|
|
|
+ INDEX idx_article_dim (article_id, dimension_code),
|
|
|
+ INDEX idx_dim_article (dimension_code, match_score)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章-维度映射';
|
|
|
+```
|
|
|
+
|
|
|
+**活动**(新建):
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE TABLE activity_dimension_mapping (
|
|
|
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ activity_id BIGINT NOT NULL,
|
|
|
+ dimension_code VARCHAR(50) NOT NULL,
|
|
|
+ match_score INT DEFAULT 50 COMMENT '关联度 0-100',
|
|
|
+ match_reason VARCHAR(200) COMMENT '关联原因',
|
|
|
+ INDEX idx_activity_dim (activity_id, dimension_code),
|
|
|
+ INDEX idx_dim_activity (dimension_code, match_score)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动-维度映射';
|
|
|
+```
|
|
|
+
|
|
|
+### 4.3 数据迁移
|
|
|
+
|
|
|
+- 现有 `AiQProfile` 数据不入 `member_profile_dimension`(历史数据无 dimension_code 映射)
|
|
|
+- 新问卷完成后自动同步
|
|
|
+- 现有 `Article.dimensionIds`/`dimensionWeights` 数据通过一次性迁移脚本导入 `article_dimension_mapping`
|
|
|
+- 现有 `Activity.dimensionCode`/`dimensionWeights` 同理
|
|
|
+
|
|
|
+## 5. 推荐算法
|
|
|
+
|
|
|
+### 5.1 核心公式
|
|
|
+
|
|
|
+```
|
|
|
+对每个内容项 c(类型为 t ∈ {product, article, activity}):
|
|
|
+
|
|
|
+ 基础得分 = Σ( profile_dim_score_i × mapping_match_score_i )
|
|
|
+ ─────────────────────────────────────────────
|
|
|
+ Σ( profile_dim_score_i )
|
|
|
+
|
|
|
+ 需求加成 = 20 × (该内容匹配的 need_profile 维度数)
|
|
|
+
|
|
|
+ 最终得分 = 基础得分 + 需求加成
|
|
|
+```
|
|
|
+
|
|
|
+### 5.2 步骤
|
|
|
+
|
|
|
+1. 根据 `memberId` 查询 `member_profile_dimension` 中 `profile_type='user'` 的所有维度得分
|
|
|
+2. 查询 `profile_type='need'` 的维度编码集合
|
|
|
+3. 对每种请求的 content type,查询对应的 dimension_mapping 表,JOIN 步骤 1 的维度得分
|
|
|
+4. 按公式计算每项内容的最终得分
|
|
|
+5. 按最终得分降序排列,取 top limit
|
|
|
+6. 去重(同一内容可能映射多个维度,只保留最高分条目)
|
|
|
+
|
|
|
+### 5.3 无画像时的降级
|
|
|
+
|
|
|
+- 无 `member_profile_dimension` 记录 → 按 `ContentRecommendService` 现有逻辑按 `dimensionCode` 筛选
|
|
|
+- 无维度映射的内容 → 不参与画像推荐(可走 `RecommendationService` 标签搜索兜底)
|
|
|
+
|
|
|
+## 6. 服务与接口
|
|
|
+
|
|
|
+### 6.1 ProfileRecommendService(新建)
|
|
|
+
|
|
|
+```java
|
|
|
+@Service
|
|
|
+public class ProfileRecommendService {
|
|
|
+ public List<RecommendItemVO> recommend(Long memberId, List<String> types, int limit);
|
|
|
+ // 内部方法
|
|
|
+ private List<RecommendItemVO> recommendByDimension(
|
|
|
+ Long memberId, String type, List<MemberProfileDimension> profiles, int limit);
|
|
|
+ private double calcScore(int profileScore, int matchScore, boolean isNeedMatch);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 6.2 AiQuestionnaireService 改造
|
|
|
+
|
|
|
+`doGenerateProfile()` 末尾追加同步调用:
|
|
|
+
|
|
|
+```java
|
|
|
+// 同步画像维度到 member_profile_dimension
|
|
|
+profileDimensionService.syncFromProfile(profile, session);
|
|
|
+```
|
|
|
+
|
|
|
+### 6.3 推荐端点(新增)
|
|
|
+
|
|
|
+```java
|
|
|
+@PostMapping("/api/recommend/profile")
|
|
|
+public Result<Map<String, Object>> profileRecommend(
|
|
|
+ @RequestBody Map<String, Object> params,
|
|
|
+ @RequestAttribute("userId") Long userId) {
|
|
|
+ // 参数: memberId, types(可选,默认["product","article","activity"]), limit(默认10)
|
|
|
+ // 返回: { recommendations: [{id, title, summary, coverImage, dimensionCode, recommendScore, recommendReason, badge}], hasMore: bool }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 6.4 管理端维度映射配置(新增)
|
|
|
+
|
|
|
+Web 管理端增加文章/活动的维度映射编辑:
|
|
|
+- 文章编辑页增加维度选择器(多选 + 匹配度 + 原因)
|
|
|
+- 活动编辑页同理
|
|
|
+- 复用 `ProductDimensionMapping` 的 UI 模式
|
|
|
+
|
|
|
+## 7. 推荐日志
|
|
|
+
|
|
|
+复用现有 `RecommendationLog` 表:
|
|
|
+
|
|
|
+| 字段 | 值 |
|
|
|
+|------|-----|
|
|
|
+| `contentType` | `article` / `product` / `activity` |
|
|
|
+| `action` | `show` / `click` / `consume` |
|
|
|
+| `recommendScore` | 算法计算的最终得分 |
|
|
|
+| 新增 `recommendType` | `profile`(画像驱动)/ `dimension`(维度筛选)/ `tag`(标签搜索) |
|
|
|
+
|
|
|
+`RecommendationLog` 追加 `recommend_type` 字段。
|
|
|
+
|
|
|
+## 8. 实施步骤
|
|
|
+
|
|
|
+### 阶段 1:数据层(2 个任务)
|
|
|
+
|
|
|
+1. 建表迁移:`member_profile_dimension` + `article_dimension_mapping` + `activity_dimension_mapping`
|
|
|
+2. `RecommendationLog` 追加 `recommend_type` 字段
|
|
|
+3. 现有数据迁移脚本(article/activity 维度映射)
|
|
|
+
|
|
|
+### 阶段 2:后端服务(2 个任务)
|
|
|
+
|
|
|
+4. 创建 `MemberProfileDimension` Entity + Mapper
|
|
|
+5. 创建 `ArticleDimensionMapping` / `ActivityDimensionMapping` Entity + Mapper
|
|
|
+6. 创建 `ProfileDimensionService`(画像维度同步)
|
|
|
+7. 创建 `ProfileRecommendService`(推荐引擎核心)
|
|
|
+8. 改造 `AiQuestionnaireService.doGenerateProfile()` 接入同步
|
|
|
+
|
|
|
+### 阶段 3:控制器与前端(2 个任务)
|
|
|
+
|
|
|
+9. 新增 `/api/recommend/profile` 端点
|
|
|
+10. 管理端文章/活动维度映射编辑页
|
|
|
+11. 小程序画像结果页接入推荐
|
|
|
+
|
|
|
+## 9. 约束与边界
|
|
|
+
|
|
|
+- **维度编码**:与五维体系一致(`body/mind/wisdom/wealth/social`),AI 问卷场景的 `dimensions_json` 中的键需映射到五维编码
|
|
|
+- **并发**:画像同步在 `doGenerateProfile` 末尾串行执行,不额外加锁(画像只写一次)
|
|
|
+- **降级**:画像维度为空时走原始推荐逻辑
|
|
|
+- **不涉及**:实时画像更新、协同过滤、向量检索
|
|
|
+
|
|
|
+## 10. 测试策略
|
|
|
+
|
|
|
+- 单元测试:`ProfileRecommendService` 的加权得分计算逻辑
|
|
|
+- 集成测试:画像同步 → 推荐查询 → 结果排序
|
|
|
+- 迁移测试:`mvn clean compile` + 迁移幂等性验证
|