# 维度参数获取与筛选设计分析 ## 概述 四个核心实体(活动、商品、文章、任务)都有"五维归属"的概念,但存储和查询方式各不相同。当前设计目标是:**根据维度筛选时,只要该实体在对应维度的权重 > 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(活动)✅ 已实现 **实体字段:** ```java private String dimensionCode; // 旧: 单值, 如 "body" private String dimensionWeights; // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0} ``` **筛选逻辑(ActivityService.list() 第53-63行):** ```java 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(商品)✅ 已实现 **实体字段:** ```java private String domain; // 旧: 单值, 如 "body" private String dimensionWeights; // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0} ``` **筛选逻辑(ProductService.list() 第56-67行):** ```java 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`,兼容旧版 `domain`。`ProductListQueryDTO` 中同时保留两个字段,优先使用 `dimensionCode`。 **状态:** ✅ 已实现权重感知筛选,参数名已统一 ### 3. Article(文章)✅ 已实现 **实体字段:** ```java private String relatedDimensions; // 旧: 逗号分隔, 如 "body,mind" private String dimensionIds; // 维度ID列表JSON private String dimensionWeights; // 新: JSON, 如 {"body":30,"mind":0,...} ``` **筛选逻辑(ArticleService.getPublicList() 第93-98行):** ```java 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()` 生成等比例权重: ```java private String buildEqualWeights(String relatedDimensions) { String[] parts = relatedDimensions.split(","); int equalWeight = 100 / parts.length; // 为每个维度分配 equalWeight } ``` **状态:** ✅ 已实现权重感知筛选,兼容旧版 `relatedDimensions` ### 4. Task(任务)✅ 已支持维度筛选 **实体字段(新增):** ```java private String dimensionCode; // 新: 单值, 如 "body" private String dimensionWeights; // 新: JSON, 如 {"body":30,"mind":0,"wisdom":0,"action":70,"wealth":0} ``` **创建任务时传入(CreateTaskDTO):** ```java private String dimensionCode; // 五维维度 private String dimensionWeights; // 五维权重JSON ``` **筛选逻辑(TaskService.getTodayTasks()):** ```java if (dimensionCode != null && !dimensionCode.isEmpty()) { wrapper.and(w -> w .apply("COALESCE(JSON_EXTRACT(dimension_weights, '$." + dimensionCode + "'), 0) > 0") .or().eq(Task::getDimensionCode, dimensionCode) ); } ``` **调用方式:** ```json POST /api/tasks/today {"childId": 1003, "dimensionCode": "body"} ``` **状态:** ✅ 已实现权重感知筛选,与活动/商品/文章统一模式 ### 数据库迁移 ```sql 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 格式存储五维权重: ```json { "body": 0, "mind": 30, "wisdom": 20, "action": 50, "wealth": 0 } ``` - 权重值范围:0-100(整数) - 0 = 不关联该维度 - > 0 = 关联该维度,值越大关联度越高 - 所有权重之和不一定等于 100(可以只设置部分维度) ### 筛选原理 使用 MySQL 的 `JSON_EXTRACT` 函数: ```sql 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 间接关联 |