Преглед на файлове

feat: add product recommendation rule engine design and plan
design: unified recommendation rule engine for 5 business rules
plan: 6-phase implementation roadmap with milestones

iwt преди 2 дни
родител
ревизия
8afabf5715

+ 162 - 0
docs/superpowers/plans/2026-09-17-product-recommendation-rule-engine-plan.md

@@ -0,0 +1,162 @@
+# 商品推荐规则引擎 — 实现计划
+
+> 基于 `docs/superpowers/specs/2026-09-17-product-recommendation-rule-engine-design.md`
+
+## 阶段 0:数据库迁移
+
+**目标**:创建 `recommendation_rules` 表,同步 schema.sql
+
+**步骤:**
+1. 在 `cfc-backend/src/main/resources/schema.sql` 末尾追加 `CREATE TABLE recommendation_rules (...)` 定义(含索引)
+2. 在 `DatabaseInitializer.java` 末尾的 `runMigrations()` 中添加新迁移(迁移编号递增,当前最大编号搜索确认)
+3. 执行 `mvn clean compile` 验证无编译错误
+
+**文件:**
+- `cfc-backend/src/main/resources/schema.sql`
+- `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java`
+
+---
+
+## 阶段 1:新实体 + Mapper + Service
+
+**目标**:基础 CRUD 能力
+
+**步骤:**
+1. 新建 `entity/RecommendationRule.java`(对应 recommendation_rules 表,字段见设计文档 3.1)
+2. 新建 `mapper/RecommendationRuleMapper.java`(使用 MyBatis-Plus,提供按 scene、rule_type、enabled 查询方法)
+3. 新建 `service/RecommendationRuleService.java`
+   - `listRules(sceneCode, page, size)` — 分页列表
+   - `getRuleById(id)` — 详情
+   - `createRule(rule)` — 创建,含参数校验(rule_type 1-5、priority>0、trigger/target 非空等)
+   - `updateRule(id, rule)` — 更新
+   - `deleteRule(id)` — 删除(设 enabled=0,软停用)
+   - `batchUpdateEnabled(ids, enabled)` — 批量启用/停用
+4. 新建 `controller/recommendation/RecommendationRuleAdminController.java`
+   - `POST /api/admin/recommendation-rules/list`
+   - `POST /api/admin/recommendation-rules/create`
+   - `POST /api/admin/recommendation-rules/update`
+   - `POST /api/admin/recommendation-rules/delete`
+   - `POST /api/admin/recommendation-rules/batch-enable`
+5. 运行 `mvn clean compile` 验证
+
+---
+
+## 阶段 2:推荐引擎核心实现
+
+**目标**:`RecommendationRuleEngine.recommend(ctx, limit)` 完成 5 种规则计算
+
+**步骤:**
+1. 新建 `service/RecommendationRuleEngine.java`(实现 `RecommendationRuleEngine` 接口)
+
+2. 内部方法设计:
+   - `loadRules(String sceneCode)` — 加载匹配的启用规则,按 priority DESC
+   - `queryPurchasedProducts(RecommendationContext ctx)` — 查购买记录,返回 `Map<Long, Date>`(productId → 最早购买日期)
+   - `buildBaseCandidates(RecommendationContext ctx)` — 按场景构建候选池(保留现有维度页推荐逻辑)
+   - `applyRules(List<RecommendationRule> rules, Set<Long> purchasedIds, List<BaseCandidate> candidates)` — 应用所有规则
+     - 先处理规则4(排除类,从高 priority 开始)
+     - 再处理规则1(排除类)
+     - 再处理规则3(推荐类)
+     - 再处理规则5(推荐类 + boost)
+     - 再处理规则2(定时复购类)
+   - `sortAndTruncate(List<BaseCandidate> candidates, int limit)` — 排序截断
+
+3. 保留 `ProductRecommendationService.getDimensionRecommendations` 现有逻辑作为 `buildBaseCandidates` 的基础实现,但不再直接调用 `getPurchasedProductIds`,改由引擎统一处理
+
+4. 在 `ProductRecommendationController.getDimensionProducts` 中:
+   - 保留 Controller 接口签名不变
+   - 内部改为调用 `engine.recommend(ctx, limit)`,将 `RecommendedProduct` 映射为现有 `Map<String, Object>` 格式返回
+
+5. 运行 `mvn clean compile` 验证
+
+---
+
+## 阶段 3:RepurchaseReminderService 改造
+
+**目标**:规则2 配置从 `recommendation_rules` 读取,旧 `repurchase_reminder_config` 不再被代码引用
+
+**步骤:**
+1. 修改 `RepurchaseReminderService.scanAndCreateReminders()`
+   - 旧逻辑:读 `repurchase_reminder_config` 表
+   - 新逻辑:读 `recommendation_rules` 表中 `rule_type=2 AND enabled=1` 的规则
+   - 映射关系:
+     - `trigger_product_id` → 触发商品
+     - `delay_days` → 复购延迟天数
+     - `target_product_id` → 复购提醒的商品(与 trigger 相同或不同均可)
+     - `max_reminders` → `recommendation_rules` 表暂无此字段,暂时复用 `repurchase_reminder_record` 中的已发送次数判断
+2. 保留定时任务 `@Scheduled(cron = "0 0 9 * * ?")`,不修改触发时间
+3. 运行 `mvn clean compile` 验证
+
+---
+
+## 阶段 4:新增推荐场景接口(可选)
+
+**目标**:商城首页、商品详情页、下单成功页的推荐接口
+
+**步骤:**
+1. 新增 `POST /api/recommend/mall-home`
+   - 入参:`{ "limit": 10 }`
+   - 内部:`engine.recommend(ctx, limit)` 其中 `scene="mall_home"`
+2. 新增 `POST /api/recommend/product-detail`
+   - 入参:`{ "productId": 123, "limit": 6 }`
+   - 内部:`engine.recommend(ctx, limit)` 其中 `scene="product_detail"`, `currentProductId=123`
+3. 新增 `POST /api/recommend/after-order`
+   - 入参:`{ "orderId": 456, "limit": 6 }`
+   - 内部:`engine.recommend(ctx, limit)` 其中 `scene="after_order"`
+4. 修改 `ProductRecommendationService.getReportRelatedProducts`
+   - 内部改为调用 `engine.recommend(ctx, limit)` 其中 `scene="report_related"`
+5. 运行 `mvn clean compile` 验证
+
+---
+
+## 阶段 5:前端管理端页面
+
+**目标**:cfc-web 新增「商品推荐规则」管理页面
+
+**步骤:**
+1. 在 `cfc-web/src/views/` 下新建 `RecommendationRules.vue`
+   - 页面标题:商品推荐规则
+   - 功能:列表展示 + 新建/编辑弹窗 + 启用/停用开关
+   - 字段:rule_type(类型下拉)、rule_name、trigger_product_id、target_product_id、priority、enabled(开关)、scene_codes(多选)
+2. 在 `cfc-web/src/router/` 下新增路由 `/admin/recommendation-rules`
+3. 在 `cfc-web/src/api/` 下封装推荐规则接口
+4. 运行 `npm run build` 验证编译通过
+
+---
+
+## 阶段 6:单元测试与集成验证
+
+**目标**:规则计算正确,降级场景正常
+
+**步骤:**
+1. 编写 `ProductRecommendationEngineTest`(或 `RecommendationRuleEngineTest`)
+   - 测试5种规则各自命中场景
+   - 测试 priority 排序
+   - 测试 scope family vs individual
+   - 测试 time_window_days 边界
+   - 测试降级:规则表为空时仍能返回候选(退化为全局已购排除)
+2. 运行 `mvn clean test`
+3. 运行 `mvn clean compile` 最终验证
+4. 如有运行时条件,在测试库 `192.168.16.251:3306` 执行迁移,验证接口返回正常
+
+---
+
+## 变更文件清单
+
+| 新增文件 | 说明 |
+|---------|------|
+| `cfc-backend/.../entity/RecommendationRule.java` | 新实体 |
+| `cfc-backend/.../mapper/RecommendationRuleMapper.java` | Mapper |
+| `cfc-backend/.../service/RecommendationRuleService.java` | CRUD Service |
+| `cfc-backend/.../service/RecommendationRuleEngine.java` | 引擎核心 |
+| `cfc-backend/.../controller/recommendation/RecommendationRuleAdminController.java` | 管理端 Controller |
+| `cfc-web/src/views/RecommendationRules.vue` | 管理端页面 |
+| `cfc-web/src/api/recommendationRule.js` | 管理端 API 封装 |
+
+| 修改文件 | 说明 |
+|---------|------|
+| `cfc-backend/.../service/ProductRecommendationService.java` | 改为调用引擎 |
+| `cfc-backend/.../service/RepurchaseReminderService.java` | 规则2 配置改读 recommendation_rules |
+| `cfc-backend/.../config/DatabaseInitializer.java` | 新增 DDL 迁移 |
+| `cfc-backend/src/main/resources/schema.sql` | 新增 recommendation_rules 表定义 |
+| `cfc-web/src/router/index.js` | 新增路由 |
+| `docs/superpowers/api/API_REFERENCE.md` | 同步新增接口文档 |

+ 321 - 0
docs/superpowers/specs/2026-09-17-product-recommendation-rule-engine-design.md

@@ -0,0 +1,321 @@
+# 商品推荐规则引擎设计文档
+
+## 1. 概述
+
+统一商品推荐的 5 种业务规则,引入统一规则引擎,由配置表 `recommendation_rules` 驱动。现有实现(已购90天排除 + 定时复购提醒)并入新引擎,所有场景(维度页/商城首页/商品详情/下单成功/报告关联)统一走一套计算逻辑,配置在后台管理端。
+
+## 2. 核心目标
+
+1. **统一入口**:所有推荐场景通过同一个引擎计算,输入输出一致
+2. **配置驱动**:规则均由 `recommendation_rules` 表配置,运营可在 Web 管理端增删改,无需改代码
+3. **降级兼容**:规则表为空时退化为现有硬编码行为,保证不出现空结果
+4. **最小侵入**:现有接口/服务改造仅改调用引擎,核心业务逻辑不改
+5. **覆盖全场景**:5 种规则全覆盖,且易扩展新规则
+
+## 3. 数据模型
+
+### 3.1 新表 `recommendation_rules`
+
+| 字段 | 类型 | 主键 | 说明 |
+|------|------|------|------|
+| `id` | BIGINT | PK | 主键,AUTO_INCREMENT |
+| `rule_type` | TINYINT | — | 规则类型 1-5 |
+| `rule_name` | VARCHAR(100) | — | 运营可读名称 |
+| `trigger_product_id` | BIGINT | — | 单商品触发(规则1/2/3/4) |
+| `trigger_product_ids` | VARCHAR(1000) | — | 多商品触发 JSON 数组(规则5,如 `"[1,2,3]"`) |
+| `target_product_id` | BIGINT | — | 目标商品ID(被推荐/排除/重点推) |
+| `priority` | INT | — | 优先级分,越大越先处理 |
+| `enabled` | TINYINT | — | 启用 1/0 |
+| `time_window_days` | INT | — | 购买回溯窗口(天),默认 null=不限 |
+| `delay_days` | INT | — | 复购延迟天数(规则2) |
+| `scope` | VARCHAR(20) | — | `family`/`individual` |
+| `scene_codes` | VARCHAR(500) | — | 生效场景 JSON 数组,如 `"[\"dimension_page\",\"product_detail\"]"` |
+| `created_at` | DATETIME | — | 创建时间 |
+| `updated_at` | DATETIME | — | 更新时间 |
+
+### 3.2 Index 建议
+
+- `idx_rules_enabled`(`enabled`, `rule_type`)
+- `idx_rules_scene`(`scene_codes`, `enabled`)
+- `idx_rules_priority`(`priority`, `enabled`)
+
+### 3.3 MyBatis-Plus Mapper 注解示例(仅示意)
+
+```java
+@Mapper
+public interface RecommendationRuleMapper {
+    @Select("SELECT * FROM recommendation_rules WHERE enabled = 1 AND FIND_IN_SET(?, scene_codes)")
+    List<RecommendationRule> selectByScene(String sceneCode);
+
+    @Select("SELECT * FROM recommendation_rules WHERE enabled = 1 AND rule_type = ? ORDER BY priority DESC")
+    List<RecommendationRule> selectByRuleType(Integer ruleType);
+
+    @Select("SELECT * FROM recommendation_rules WHERE enabled = 1 AND rule_type = ? AND FIND_IN_SET(?, trigger_product_ids)")
+    List<RecommendationRule> selectByProductIds(Integer ruleType, String productIdsJson);
+}
+```
+
+## 4. 引擎设计
+
+### 4.1 入口接口
+
+```java
+/**
+ * 推荐上下文
+ */
+public class RecommendationContext {
+    private Long userId;          // 登录用户ID
+    private Long familyId;        // 家庭ID(scene 为 family 时必填)
+    private Long memberId;        // 成员ID(scope=individual 时)
+    private String scene;         // 生效场景代码:dimension_page, mall_home, product_detail, after_order, report_related
+    private Long currentProductId; // 商品详情页/下单页的当前商品ID
+    private List<Long> excludeProductIds; // 用户自行排除的商品ID(如已选/不喜欢)
+    private Integer limit;        // 返回上限,默认6
+}
+```
+
+```java
+/**
+ * 推荐结果项
+ */
+public class RecommendedProduct {
+    private Long productId;
+    private String name;
+    private String coverImage;
+    private Integer price; // 分
+    private String reason; // 推荐理由
+    private Double matchScore; // 匹配分
+    private Integer ruleId; // 触发该结果的规则ID
+    private Integer ruleType; // 规则类型 1-5
+    private Integer priority; // 该规则优先级
+    private String source; // 来源标识:engine/campaign/...
+}
+```
+
+```java
+/**
+ * 引擎核心方法
+ */
+public interface RecommendationRuleEngine {
+    /**
+     * 计算推荐
+     * @param ctx 推荐上下文
+     * @param limit 若覆盖默认limit,可传;否则用 ctx.limit
+     * @return 推荐列表
+     */
+    List<RecommendedProduct> recommend(RecommendationContext ctx, Integer limit);
+}
+```
+
+### 4.2 处理流程(按 priority 降序遍历规则)
+
+```
+1. 加载规则
+   - 根据 scene 匹配 `scene_codes`(精确匹配 JSON 中的字符串)
+   - enabled=1,按 priority DESC 排序
+
+2. 查购买记录
+   - scope=family:查 family_members 关联的订单(ProductOrder 中 familyId 匹配)
+   - scope=individual:查 buyerId(userId) 的订单
+   - 状态过滤:status in ("已支付", "completed")
+   - 时间窗口:time_window_days 为 null 或 返回的时间差 <= time_window_days
+     若为 null,默认使用全局配置 default_purchased_exclude_days(默认 90)
+
+3. 构建候选池(场景基础候选)
+   - dimension_page → 维度匹配商品(已有 ProductRecommendationService 逻辑)
+   - mall_home      → 全量上架在售商品(product status=on_shelf, stock>0)
+   - product_detail → 从规则3/5 中命中的 targetProductId
+   - after_order    → 从规则3/5 中命中的 targetProductId
+   - report_related → 报告提取的弱维度商品(已有 extractDimensionNeedsFromHealth/FromCognitive)
+
+4. 应用规则(按 priority 从高到低遍历已加载的规则)
+
+   4.1 规则4(购买A不推B)
+       - 条件:已购买 trigger_product_id
+       - 动作:从候选池移除 target_product_id
+       - 若未命中则跳过
+
+   4.2 规则1(已购不推)
+       - 条件:已购买 trigger_product_id(或全局默认已购窗口内任意商品)
+       - 动作:从候选池移除 target_product_id
+       - 若未命中则跳过(已购默认由 step2/3 在全局层面完成,规则1只作特定覆盖)
+
+   4.3 规则3(购买A推B)
+       - 条件:已购买 trigger_product_id
+       - 动作:将 target_product_id 加入候选池 + reason="根据您的购买记录推荐"
+       - 若候选池已有该productId,则不重复添加,仅更新 reason
+
+   4.4 规则5(系列重点推)
+       - 条件:已购买 trigger_product_ids 中的 ALL 个商品
+       - 动作:将 target_product_id 加入候选池 + priority 加 10(提升排序位置)
+
+   4.5 规则2(定时复购)
+       - 条件:已购买 trigger_product_id 且(当前日期 - 购买日期) >= delay_days
+       - 动作:将 target_product_id 加入候选池 + reason="该商品您已购买,可考虑复购"
+       - 建议 delay_days 默认值:30
+
+5. 结果排序 & 截断
+   - 按 (priority DESC, matchScore DESC) 排序
+   - 截断 limit(默认6)
+
+6. 返回结果
+   - List<RecommendedProduct>,每项含 productId, name, coverImage, price, reason, matchScore, ruleId, ruleType, priority, source
+```
+
+### 4.3 规则语义表
+
+| 规则 | rule_type | 触发 | 动作 | 关键字段 |
+|------|-----------|------|------|---------|
+| 1 已购不推 | 1 | 购买 trigger_product(或全局已购窗口内任意) | 从候选排除 target_product | trigger_product_id, target_product_id, time_window_days |
+| 2 定时复购 | 2 | 购买 trigger_product 且距今>=delay_days | target_product 加入候选(复购) | delay_days, target_product_id |
+| 3 购买A推B | 3 | 购买 trigger_product(A) | target_product(B) 加入候选 | trigger_product_id, target_product_id |
+| 4 购买A不推B | 4 | 购买 trigger_product(A) | 从候选排除 target_product(B) | trigger_product_id, target_product_id |
+| 5 系列重点推 | 5 | 购买 trigger_product_ids 全部 | target_product 加入候选 + boost | trigger_product_ids, target_product_id |
+
+### 4.4 全局默认已购排除
+
+- 系统级配置 `default_purchased_exclude_days`(默认 90 天),存入 application.yml 或系统参数表
+- 引擎在 step2 查购买记录阶段,scope=family 时查 family 成员订单,scope=individual 时查 buyerId 订单
+- 无论规则1是否配置,所有候选商品在进入 step4 前都已过滤掉回溯窗口内的已购商品(作为兜底)
+- 规则1 仅作“特定商品覆盖”:如 trigger_product_id=123,time_window_days=180,则仅将商品123 的购买记录从候选排除(放宽或收窄默认 90 天)
+
+## 5. 现有实现迁移
+
+| 现有代码/组件 | 迁移动作 |
+|--------------|----------|
+| `ProductRecommendationService.getDimensionRecommendations` | 改为调用 `engine.recommend(ctx.scene="dimension_page")`,ctx.limit=6 |
+| `getPurchasedProductIds(familyId, 90)` 硬编码 90 天 | 移除该方法,改为在引擎内读 `default_purchased_exclude_days` |
+| `RepurchaseReminderService.scanAndCreateReminders` @Scheduled | **保留**(不做改动,但内部配置读取改为从规则表读;rule_type=2 的 config 从 recommendation_rules 读,旧 `repurchase_reminder_config` 表数据迁移),外部定时任务仍可继续生成 reminder 记录 |
+| `repurchase_reminder_config` 表 | **保留**(点击/购买追踪表),不做结构变更,仅在新引擎规则2 中通过 rule_type 匹配 |
+| `ContentRecommendService.getPurchasedProductIds` | 复用引擎的全局已购排除能力 |
+| API Controller(维度页推荐) | 不改接口签名,内部改调用 engine.recommend,返回结果自动映射为 Map<String,Object> 格式 |
+| 新增 API 接口(可选) | 若前端需要更多场景,可在 `POST /api/recommend/*` 下新增,内部同样调用 engine.recommend |
+
+## 6. 管理端(cfc-web)
+
+### 6.1 页面:商品推荐规则
+
+- **列表**:分页 + rule_type / enabled / priority 筛选 + 排序
+- **新建/编辑**:
+  - rule_type:1-5 下拉
+  - rule_name:文本
+  - trigger_product_id / trigger_product_ids:商品选择器(支持搜索、多选)
+  - target_product_id:目标商品选择器
+  - priority:数字输入(整数,默认 10)
+  - enabled:开关
+  - time_window_days:整数(天,默认 90)
+  - delay_days:整数(天,默认 30,仅规则2)
+  - scope:single/family 单选
+  - scene_codes:多行文本/标签选择器(可选:dimension_page, mall_home, product_detail, after_order, report_related)
+
+### 6.2 管理端 API
+
+- `GET /api/admin/recommendation-rules` - 分页列表
+- `POST /api/admin/recommendation-rules` - 新建
+- `PUT /api/admin/recommendation-rules/{id}` - 编辑
+- `DELETE /api/admin/recommendation-rules/{id}` - 物理删除(慎用,建议设 enabled=0 停用)
+- `POST /api/admin/recommendation-rules/batch-enable` - 批量启用/停用
+
+## 7. 容错与降级
+
+1. **规则表为空时**:引擎不抛出异常,直接进入“场景基础候选”阶段(即现有行为),全局已购排除默认 90 天,返回已有候选列表。
+
+2. **规则配置冲突**:同一规则 type 存在多条启用记录时,按 priority 降序处理,较早的生效。运营若需调整顺序,在管理端修改 priority 即可。
+
+3. **trigger_product_id 不存在**:引擎检查 trigger_product_id 是否存在于 products 表中,不存在则跳过该规则,记录 warn 日志。
+
+4. **scene_codes 匹配失败**:
+   - 引擎首先尝试精确匹配 `FIND_IN_SET(sceneCode, scene_codes)`
+   - 若未匹配,再尝试前缀匹配(如 sceneCode 为 "dimension_page",scene_codes 包含 "dimension" 也视为匹配)
+   - 作为最后兜底,若 scene_codes 为空字符串 `""`,视为“所有场景生效”
+
+5. **时间窗口边界**:
+   - time_window_days=0 表示“只计算今日购买”
+   - time_window_days<0 视为异常,引擎取绝对值但标记日志警告
+
+## 8. 测试要点
+
+1. **规则单独生效**:
+   - 规则1:已购特定商品(180天)从候选排除
+   - 规则2:30天后出现复购推荐
+   - 规则3:购买A后推B
+   - 规则4:购买A后不推B
+   - 规则5:购买A+B+C系列后重点推X
+
+2. **优先级排序**:
+   - 验证 priority 从高到低的处理顺序
+   - 验证同 priority 时,matchScore DESC
+
+3. **scope family/individual**:
+   - family:查 familyId 关联订单
+   - individual:查 buyerId(userId) 订单
+
+4. **time_window_days 边界**:0、90、365 各自行为
+
+5. **降级测试**:
+   - 删除所有规则记录,验证系统仍能返回推荐(退化为全局已购排除 + 基础候选)
+   - 验证旧定时任务仍正常工作
+
+6. **编译验证**:`mvn clean compile`
+
+## 9. 变更清单
+
+### 9.1 数据库
+
+```sql
+-- 新建表(迁移编号自行在 DatabaseInitializer 末尾递增)
+CREATE TABLE recommendation_rules (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    rule_type TINYINT NOT NULL COMMENT '1-5',
+    rule_name VARCHAR(100) COMMENT '运营名称',
+    trigger_product_id BIGINT COMMENT '触发商品ID',
+    trigger_product_ids VARCHAR(1000) COMMENT '触发商品ID JSON 数组',
+    target_product_id BIGINT COMMENT '目标商品ID',
+    priority INT DEFAULT 10 COMMENT '优先级,越大越优',
+    enabled TINYINT DEFAULT 1 COMMENT '启用 1/0',
+    time_window_days INT COMMENT '回溯窗口(天)',
+    delay_days INT COMMENT '复购延迟天数',
+    scope VARCHAR(20) COMMENT 'family/individual',
+    scene_codes VARCHAR(500) COMMENT '生效场景 JSON 数组',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+-- 建议索引
+CREATE INDEX idx_rules_enabled ON recommendation_rules(enabled);
+CREATE INDEX idx_rules_scene ON recommendation_rules(scene_codes);
+CREATE INDEX idx_rules_priority ON recommendation_rules(priority);
+```
+
+### 9.2 Java 代码变更
+
+- 新增 `entity/RecommendationRule.java`
+- 新增 `mapper/RecommendationRuleMapper.java`
+- 新增 `service/RecommendationRuleService.java`(CRUD + 规则校验)
+- 新增 `service/RecommendationRuleEngine.java`(核心计算逻辑)
+- 新增 `controller/recommendation/RecommendationRuleAdminController.java`(管理端 API)
+- 修改 `service/ProductRecommendationService.java` → 改为调用 engine.recommend
+- 修改 `service/RepurchaseReminderService.java` → rule_type=2 配置读取改为从 recommendation_rules
+- 删除/废弃 `repurchase_reminder_config` 业务逻辑(保留表结构)
+
+### 9.3 API 变更(后端)
+
+- 维度页推荐:`POST /api/recommend/dimension-products` 不变,内部实现改调用 engine
+- 新增(如需要):`POST /api/recommend/mall-home`、`POST /api/recommend/product-detail`、`POST /api/recommend/after-order`、`POST /api/recommend/report-related`
+
+### 9.4 Web管理端
+
+- 新增页面:`/admin/recommendation-rules`(Vue 2 + Element UI,遵循项目现有设计规范)
+- 权限:role=admin 或 role=teacher
+
+## 10. 完成标准
+
+- [ ] 新表 `recommendation_rules` 建表 + schema.sql 同步
+- [ ] `RecommendationRuleMapper` + `RecommendationRuleService` 实现
+- [ ] `RecommendationRuleEngine.recommend()` 完整实现(5种规则流程)
+- [ ] `RepurchaseReminderService` 规则2 配置读取改造
+- [ ] `ProductRecommendationService` 改为调用 engine
+- [ ] 管理端 CRUD 页面 + API
+- [ ] 单元测试:5种规则各自命中/未命中 + priority 排序 + scope family/individual
+- [ ] 集成测试:mvn clean compile 通过
+- [ ] 文档同步:`docs/superpowers/specs/2026-09-17-product-recommendation-rule-engine-design.md`