dimension-filter-design.md 6.9 KB

维度参数获取与筛选设计分析

概述

四个核心实体(活动、商品、文章、任务)都有"五维归属"的概念,但存储和查询方式各不相同。当前设计目标是:根据维度筛选时,只要该实体在对应维度的权重 > 0 就命中

实体维度字段对比

实体 表名 旧字段 新字段 维度筛选方式
Activity activities dimension_code (单值) dimension_weights (JSON) JSON_EXTRACT(dimension_weights, '$.{dim}') > 0 OR dimension_code = {dim}
Product products domain (单值) dimension_weights (JSON) JSON_EXTRACT(dimension_weights, '$.{dim}') > 0 OR domain = {dim}
Article articles related_dimensions (逗号分隔) dimension_weights (JSON) JSON_EXTRACT(dimension_weights, '$.{dim}') > 0 OR related_dimensions LIKE %{dim}%
Task tasks ❌ 不支持维度筛选

逐项分析

1. Activity(活动)✅ 已实现

实体字段:

private String dimensionCode;        // 旧: 单值, 如 "body"
private String dimensionWeights;     // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0}

筛选逻辑(ActivityService.list() 第53-63行):

if (dimensionCode != null && !dimensionCode.isEmpty()) {
    query.and(w -> w
        .apply("COALESCE(JSON_EXTRACT(dimension_weights, '$." + dimensionCode + "'), 0) > 0")
        .or().eq(Activity::getDimensionCode, dimensionCode)
    );
}

状态: ✅ 已实现权重感知筛选,兼容旧版 dimensionCode

2. Product(商品)✅ 已实现

实体字段:

private String domain;               // 旧: 单值, 如 "body"
private String dimensionWeights;     // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0}

筛选逻辑(ProductService.list() 第56-67行):

String dimCode = query.getDimensionCode() != null ? query.getDimensionCode() : query.getDomain();
if (dimCode != null && !dimCode.isEmpty()) {
    wrapper.and(w -> w
        .apply("COALESCE(JSON_EXTRACT(dimension_weights, '$." + dimCode + "'), 0) > 0")
        .or().eq(Product::getDomain, dimCode)
    );
}

注意: 查询参数名已统一为 dimensionCode,兼容旧版 domainProductListQueryDTO 中同时保留两个字段,优先使用 dimensionCode

状态: ✅ 已实现权重感知筛选,参数名已统一

3. Article(文章)✅ 已实现

实体字段:

private String relatedDimensions;    // 旧: 逗号分隔, 如 "body,mind"
private String dimensionIds;         // 维度ID列表JSON
private String dimensionWeights;     // 新: JSON, 如 {"body":30,"mind":0,...}

筛选逻辑(ArticleService.getPublicList() 第93-98行):

if (dimensionCode != null && !dimensionCode.trim().isEmpty()) {
    wrapper.and(w -> w
        .apply("COALESCE(JSON_EXTRACT(dimension_weights, '$." + dimensionCode.trim() + "'), 0) > 0")
        .or().like(Article::getRelatedDimensions, dimensionCode.trim())
    );
}

自动生成权重的逻辑(ArticleService 第702-706行): 当管理员设置 relatedDimensions 时,自动调用 buildEqualWeights() 生成等比例权重:

private String buildEqualWeights(String relatedDimensions) {
    String[] parts = relatedDimensions.split(",");
    int equalWeight = 100 / parts.length;
    // 为每个维度分配 equalWeight
}

状态: ✅ 已实现权重感知筛选,兼容旧版 relatedDimensions

4. Task(任务)✅ 已支持维度筛选

实体字段(新增):

private String dimensionCode;        // 新: 单值, 如 "body"
private String dimensionWeights;     // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0}

创建任务时传入(CreateTaskDTO):

private String dimensionCode;        // 五维维度
private String dimensionWeights;     // 五维权重JSON

筛选逻辑(TaskService.getTodayTasks()):

if (dimensionCode != null && !dimensionCode.isEmpty()) {
    wrapper.and(w -> w
        .apply("COALESCE(JSON_EXTRACT(dimension_weights, '$." + dimensionCode + "'), 0) > 0")
        .or().eq(Task::getDimensionCode, dimensionCode)
    );
}

调用方式:

POST /api/tasks/today
{"childId": 1003, "dimensionCode": "body"}

状态: ✅ 已实现权重感知筛选,与活动/商品/文章统一模式

数据库迁移

ALTER TABLE tasks ADD COLUMN dimension_code VARCHAR(20) COMMENT '五维维度: body/mind/wisdom/action/wealth';
ALTER TABLE tasks ADD COLUMN dimension_weights JSON COMMENT '五维权重';

迁移编号:迁移107,在 DatabaseInitializer.runMigrations() 中。| 任务 | POST /api/tasks/today | dimensionCode | {"dimensionCode":"body"} |

架构设计

JSON 权重格式

所有实体统一使用 JSON 格式存储五维权重:

{
  "body": 0,
  "mind": 30,
  "wisdom": 20,
  "action": 50,
  "wealth": 0
}
  • 权重值范围:0-100(整数)
  • 0 = 不关联该维度
  • > 0 = 关联该维度,值越大关联度越高
  • 所有权重之和不一定等于 100(可以只设置部分维度)

筛选原理

使用 MySQL 的 JSON_EXTRACT 函数:

COALESCE(JSON_EXTRACT(dimension_weights, '$.body'), 0) > 0
  • 如果 dimension_weights 为 NULL 或 JSON 中不含该 key,返回 0
  • 如果权重 > 0,表示该实体关联此维度,命中筛选

兼容旧数据

三种实体都保留了旧字段的兼容查询:

  • Activity: dimension_code 精确匹配
  • Product: domain 精确匹配
  • Article: related_dimensions LIKE 模糊匹配

前端调用方式

实体 列表接口 维度参数名 示例
活动 POST /api/activity/list dimensionCode {"dimensionCode":"body"}
商品 POST /api/product/list domain {"domain":"body"}
文章 POST /api/articles/list dimensionCode {"dimensionCode":"body"}
任务 POST /api/tasks/today dimensionCode {"childId":1003,"dimensionCode":"body"}

建议

统一参数名 ✅ 已完成

商品列表已将查询参数统一为 dimensionCode(兼容旧版 domain),与活动/文章/任务一致。

Task 增加维度支持 ✅ 已完成

Task 已增加 dimensionCode + dimensionWeights 字段,创建任务时可传入,今日任务列表支持按维度筛选。

创建/编辑时的维度设置

实体 创建接口 维度参数 说明
活动 POST /api/admin/activity/create dimensionCode + dimensionWeights 管理后台设置
商品 POST /api/product/create domain + dimensionWeights 管理后台设置
文章 POST /api/admin/articles/create relatedDimensions 自动生成 dimensionWeights
任务 POST /api/tasks/create 不支持 通过 category 间接关联