2026-08-09-dimension-recommendation-remove.md 20 KB

推荐组件移除维度参数 实现计划

面向 AI 代理的工作者: 必需子技能:使用 superpowers:subagent-driven-development。步骤使用复选框(- [ ])语法来跟踪进度。

目标: 移除 DimensionProducts/Articles/Activities/Tasks 四个组件的 dimensionCode 参数,组件改为自加载全量推荐内容,父页面不再传维度参数也不负责数据加载。

架构: 组件自加载模式。后端 /api/recommend/dimension-products 支持空 dimensionCode 返回全量推荐。Article/Activity/Task 后端已支持空过滤参数,无需改动。11 个父页面移除维度传参和本地数据加载。

技术栈: Java 8 + Spring Boot 2.7.18 / uni-app Vue 2 + 微信小程序


任务 1:后端 ProductRecommendationController 支持空 dimensionCode

文件:

  • 修改:cfc-backend/src/main/java/com/etotem/cfc/controller/recommendation/ProductRecommendationController.java

  • [ ] 步骤 1:修改 Controller 校验逻辑

将 dimensionCode 为空时返回错误的逻辑改为允许空值,调用 service 时用 null:

@Operation(summary = "获取推荐商品")
@PostMapping("/dimension-products")
public Result<List<Map<String, Object>>> getDimensionProducts(@RequestBody Map<String, Object> params,
                                                              @RequestAttribute(value = "familyId", required = false) Long familyId) {
    if (familyId == null) {
        return Result.noFamily("请先创建或加入家庭");
    }
    // 去掉 dimensionCode 非空校验,允许 null 返回全量推荐
    String dimensionCode = (String) params.get("dimensionCode");

    Object memberIdObj = params.get("memberId");
    Long memberId = memberIdObj != null ? toLong(memberIdObj) : null;

    @SuppressWarnings("unchecked")
    List<Long> excludeIds = (List<Long>) params.get("excludeProductIds");

    Object limitObj = params.get("limit");
    int limit = limitObj != null ? toInt(limitObj) : 6;

    List<Map<String, Object>> result = productRecommendationService.getDimensionRecommendations(
            dimensionCode, familyId, memberId, excludeIds, limit);

    return Result.success(result);
}
  • [ ] 步骤 2:Commit

    git add cfc-backend/src/main/java/com/etotem/cfc/controller/recommendation/ProductRecommendationController.java
    git commit -m "fix: 推荐商品接口支持空 dimensionCode 返回全量"
    

任务 2:后端 ProductRecommendationService 全量查询逻辑

文件:

  • 修改:cfc-backend/src/main/java/com/etotem/cfc/service/ProductRecommendationService.java

  • [ ] 步骤 1:修改 getDimensionRecommendations 方法 — mapping 查询支持 null dimensionCode

将第 62-67 行的 mapping 查询改为 conditionally 加 dimensionCode 条件:

// 从 mapping 表获取关联商品
LambdaQueryWrapper<ProductDimensionMapping> mappingQw = new LambdaQueryWrapper<ProductDimensionMapping>()
        .eq(ProductDimensionMapping::getEnabled, 1);
if (dimensionCode != null && !dimensionCode.isEmpty()) {
    mappingQw.eq(ProductDimensionMapping::getDimensionCode, dimensionCode);
}
List<ProductDimensionMapping> mappings = dimensionMappingMapper.selectList(mappingQw);
  • 步骤 2:修改 fallback domain 查询支持 null

将第 107-118 行 fallback 查询改为 conditionally 加 domain 条件:

// fallback: 从 Product.domain 匹配
if (candidates.isEmpty()) {
    LambdaQueryWrapper<Product> domainQw = new LambdaQueryWrapper<Product>()
            .eq(Product::getStatus, "上架")
            .gt(Product::getStock, 0)
            .orderByAsc(Product::getSortOrder);
    if (dimensionCode != null && !dimensionCode.isEmpty()) {
        domainQw.eq(Product::getDomain, dimensionCode);
    }
    if (!purchasedIds.isEmpty()) {
        domainQw.notIn(Product::getId, purchasedIds);
    }
    List<Product> domainMatched = productMapper.selectList(domainQw);
    candidates.addAll(domainMatched);
}
  • 步骤 3:修改 dimensionBoost / matchScore / reason 方法 — 空 dimensionCode 时使用通用逻辑

applyDimensionBoost 方法修改为:

private double applyDimensionBoost(int baseScore, String dimensionCode, Map<String, Integer> memberScores) {
    if (dimensionCode == null || dimensionCode.isEmpty()) {
        return baseScore;
    }
    Integer score = memberScores.get(dimensionCode);
    if (score == null || score >= 70) {
        return baseScore;
    } else if (score >= 50) {
        return baseScore * 1.1;
    } else {
        return baseScore * 1.2;
    }
}

calculateMatchScore 方法修改为:

private double calculateMatchScore(Product product, String dimensionCode, Map<String, Integer> memberScores) {
    int base = 70;
    if (dimensionCode == null || dimensionCode.isEmpty()) {
        // 无维度时,用最低分维度作为参考
        Integer minScore = memberScores.values().stream().min(Integer::compareTo).orElse(null);
        if (minScore != null) {
            if (minScore < 50) base = 90;
            else if (minScore < 70) base = 80;
        }
        return base;
    }
    Integer myScore = memberScores.get(dimensionCode);
    if (myScore != null) {
        if (myScore < 50) base = 90;
        else if (myScore < 70) base = 80;
        else base = 70;
    }
    return base;
}

buildRecommendationReason 方法修改为:

private String buildRecommendationReason(Product product, String dimensionCode, Map<String, Integer> memberScores) {
    if (dimensionCode == null || dimensionCode.isEmpty()) {
        return "为您推荐的好物";
    }
    Integer score = memberScores.get(dimensionCode);
    if (score == null) {
        return "根据您的维度匹配为您推荐";
    }
    if (score < 50) {
        return "该维度得分偏低,重点推荐";
    } else if (score < 70) {
        return "该维度有提升空间,推荐关注";
    }
    return "丰富您的维度生活";
}
  • 步骤 4:编译验证

运行:

cd cfc-backend && mvn clean compile

预期:编译通过,无错误。

  • [ ] 步骤 5:Commit

    git add cfc-backend/src/main/java/com/etotem/cfc/service/ProductRecommendationService.java
    git commit -m "feat: 商品推荐支持空维度返回全量推荐"
    

任务 3:DimensionProducts.vue — 移除 dimensionCode,自加载

文件:

  • 修改:cfc-frontend/components/DimensionProducts.vue

  • [ ] 步骤 1:移除 dimensionCode prop

    props: {
    // 移除 dimensionCode
    products: { type: Array, default: function() { return [] } },
    familyId: { type: [Number, String], default: null },
    isLoggedIn: { type: Boolean, default: false },
    title: { type: String, default: '推荐商品' },
    showMore: { type: Boolean, default: true }
    },
    
  • [ ] 步骤 2:修改 loadDimensionProducts 方法,去掉 dimensionCode 参数

    loadDimensionProducts: function() {
    var self = this
    self.loading = true
    var params = {
        limit: 4
    }
    getDimensionProducts(params).then(function(res) {
        self.loading = false
        if (res.code === 200 && res.data) {
            self.selfProducts = Array.isArray(res.data) ? res.data : (res.data.records || [])
        }
    }).catch(function() {
        self.loading = false
        self.selfProducts = []
    })
    }
    
  • [ ] 步骤 3:Commit

    git add cfc-frontend/components/DimensionProducts.vue
    git commit -m "fix: DimensionProducts 移除维度参数自加载"
    

任务 4:DimensionArticles.vue — 移除 dimensionCode,新增自加载

文件:

  • 修改:cfc-frontend/components/DimensionArticles.vue

  • [ ] 步骤 1:添加 API 导入

在 script 顶部添加:

import { getFeaturedArticles } from '@/utils/api.js'
  • [ ] 步骤 2:移除 dimensionCode prop

    props: {
    // 移除 dimensionCode
    articles: { type: Array, default: function() { return [] } },
    isLoggedIn: { type: Boolean, default: false },
    colors: { type: Array, default: function() { return defaultGradientColors } }
    },
    
  • [ ] 步骤 3:添加 data 和 self-loading

    data: function() {
    return {
        selfArticles: [],
        loading: false
    }
    },
    mounted: function() {
    if (!this.articles || this.articles.length === 0) {
        this.loadArticles()
    }
    },
    
  • [ ] 步骤 4:修改 displayArticles computed,优先用 selfArticles

    displayArticles: function() {
    var self = this
    var list = (this.articles && this.articles.length > 0 ? this.articles : self.selfArticles)
    return (list || []).map(function(item) {
        item._badges = self.parseWeights(item.dimensionWeights)
        return item
    })
    }
    
  • [ ] 步骤 5:添加 loadArticles 方法

    loadArticles: function() {
    var self = this
    self.loading = true
    getFeaturedArticles({ size: 3 }).then(function(res) {
        self.loading = false
        if (res && res.code === 200 && res.data) {
            var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
            self.selfArticles = list.slice(0, 3)
        }
    }).catch(function() {
        self.loading = false
        self.selfArticles = []
    })
    }
    
  • [ ] 步骤 6:Commit

    git add cfc-frontend/components/DimensionArticles.vue
    git commit -m "fix: DimensionArticles 移除维度参数自加载"
    

任务 5:DimensionActivities.vue — 移除 dimensionCode,新增自加载

文件:

  • 修改:cfc-frontend/components/DimensionActivities.vue

  • [ ] 步骤 1:添加 API 导入

在 script 顶部添加:

import { getActivityList } from '@/utils/api.js'
  • [ ] 步骤 2:移除 dimensionCode prop 和 dimensionIcon computed

    props: {
    // 移除 dimensionCode
    activities: { type: Array, default: function() { return [] } },
    isLoggedIn: { type: Boolean, default: false }
    }
    

删除 dimensionIcon computed。

  • 步骤 3:修改 template 中的图标为固定值

{{ dimensionIcon }} 替换为固定图标 '🎯'

<!-- 头部图标 -->
<view class="section-icon-wrap">
    <text class="section-icon">🎯</text>
</view>

<!-- 空状态图标 -->
<text class="empty-icon">🎯</text>
  • [ ] 步骤 4:添加 data 和 self-loading

    data: function() {
    return {
        swiperIndex: 0,
        isMember: false,
        selfActivities: [],
        loading: false
    }
    },
    mounted: function() {
    if (!this.activities || this.activities.length === 0) {
        this.loadActivities()
    }
    }
    
  • [ ] 步骤 5:修改 displayActivities computed,优先用 selfActivities

    displayActivities: function() {
    var self = this
    var list = (this.activities && this.activities.length > 0 ? this.activities : self.selfActivities)
    return (list || []).slice(0, 3).map(function(item) {
        return {
            id: item.id,
            title: item.title,
            coverImage: item.coverImage,
            startTime: item.startTime,
            status: item.status,
            price: item.price,
            priceLabel: item.priceLabel,
            memberPrice: item.memberPrice,
            dimensionWeights: item.dimensionWeights,
            _badges: self.parseWeights(item.dimensionWeights),
            _timeText: self.formatTime(item.startTime),
            _statusText: self.statusText(item.status),
            _priceDisplay: self.formatPriceWithSymbol(item.price),
            _memberPriceDisplay: self.formatPriceWithSymbol(item.memberPrice)
        }
    })
    }
    
  • [ ] 步骤 6:添加 loadActivities 方法

    loadActivities: function() {
    var self = this
    self.loading = true
    getActivityList({ page: 1, size: 3 }).then(function(res) {
        self.loading = false
        if (res && res.code === 200 && res.data) {
            var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
            self.selfActivities = list.slice(0, 3)
        }
    }).catch(function() {
        self.loading = false
        self.selfActivities = []
    })
    }
    
  • [ ] 步骤 7:Commit

    git add cfc-frontend/components/DimensionActivities.vue
    git commit -m "fix: DimensionActivities 移除维度参数自加载"
    

任务 6:DimensionTasks.vue — 移除 dimensionCode,改用 getTodayTasks

文件:

  • 修改:cfc-frontend/components/DimensionTasks.vue

  • [ ] 步骤 1:添加 getTodayTasks 导入

    import api from '@/utils/api.js'
    // 确保 api.getTodayTasks 可用(已存在于 api.js 第 453 行)
    
  • [ ] 步骤 2:移除 dimensionCode prop

    props: {
    // 移除 dimensionCode
    tasks: { type: Array, default: function() { return [] } },
    memberId: { type: Number, default: null }
    },
    
  • [ ] 步骤 3:修改 loadTasks 方法,使用 getTodayTasks(不带 category)

    loadTasks: function() {
    var self = this
    if (!self.memberId) return
    self.loading = true
    api.getTodayTasks(self.memberId).then(function(res) {
        self.loading = false
        self.tasksData = Array.isArray(res.data) ? res.data : (res.data.records || [])
    }).catch(function() {
        self.loading = false
        self.tasksData = []
    })
    }
    
  • [ ] 步骤 4:Commit

    git add cfc-frontend/components/DimensionTasks.vue
    git commit -m "fix: DimensionTasks 移除维度参数自加载"
    

任务 7:父页面清理 — 7 个维度详情页(body/mind/wisdom/action 两个版本)

文件:

  • 修改:cfc-frontend/pages/body/member-body-detail.vue
  • 修改:cfc-frontend/pages/mind/member-mind-detail.vue
  • 修改:cfc-frontend/pages/wisdom/member-wisdom-detail.vue
  • 修改:cfc-frontend/pages/action-detail/member-action-detail.vue
  • 修改:cfc-frontend/pages/body-detail/member-body-detail.vue
  • 修改:cfc-frontend/pages/mind-detail/member-mind-detail.vue
  • 修改:cfc-frontend/pages/wisdom-detail/member-wisdom-detail.vue

统一改动模式(每个页面):

  • [ ] 步骤 1:去掉 import 中的 API

    // 移除 getTodayTasksByCategory, getActivityList, getProductsByDomain
    import { getEnergyOverview, getFamilyEnergySandbox, getChildren } from '../../utils/api.js'
    
  • [ ] 步骤 2:去掉 data 中的数据字段

    data() {
    return {
        memberId: null,
        selfId: null,
        memberInfo: null,
        sandboxData: null,
        dualDimension: null,
        // 移除 dimensionTasks, dimensionActivities, dimensionProducts
        ...
    }
    }
    
  • [ ] 步骤 3:去掉 template 中的 dimensionCode 和 props

    <!-- 今日任务 -->
    <DimensionTasks
    :memberId="memberId"
    @taskClick="onTaskClick"
    @moreTasks="goTasks" />
    
    <!-- 活动 -->
    <DimensionActivities
    :isLoggedIn="true"
    @activityClick="goActivityDetail"
    @moreActivities="goMoreActivities" />
    
    <!-- 商品 -->
    <DimensionProducts
    :isLoggedIn="true"
    @productClick="goProductDetail"
    @moreProducts="goMoreProducts" />
    
  • [ ] 步骤 4:去掉 onShow 中的数据加载调用

    // 移除 this.loadDimensionTasks()
    // 移除 this.loadDimensionActivities()
    // 移除 this.loadDimensionProducts()
    
  • [ ] 步骤 5:去掉 loadDimensionTasks / loadDimensionActivities / loadDimensionProducts 方法

删除这三个方法定义。

  • 步骤 6:对 7 个页面逐一完成上述改动

每个页面完成后立即 commit:

git add cfc-frontend/pages/body/member-body-detail.vue
git commit -m "fix: body 成员详情页移除维度参数"

按 body → mind → wisdom → action → body-detail → mind-detail → wisdom-detail 顺序逐个提交。


任务 8:index-home/index.vue — 去掉维度相关数据加载

文件:

  • 修改:cfc-frontend/pages/index-home/index.vue

  • [ ] 步骤 1:去掉 DimensionProducts 和 DimensionArticles 的 dimensionCode prop

    <DimensionArticles
    :articles="actionArticles"
    :isLoggedIn="true"
    @articleClick="goArticleDetail" />
    
    <DimensionProducts
    dimensionCode="action"  <!-- 去掉这行 -->
    ... />
    
  • [ ] 步骤 2:去掉 loadActionProducts 方法

删除 loadActionProducts 方法定义。

  • 步骤 3:去掉 loadActionArticles 方法

删除 loadActionArticles 方法定义。

  • [ ] 步骤 4:去掉 onShow 中的调用

    // 移除 this.loadActionProducts()
    // 移除 this.loadActionArticles()
    // 移除 this.loadDimensionActivities()(growth-main 已传空 dimensionCode,可保留)
    
  • [ ] 步骤 5:去掉 import 中的 getProductsByDomain, getFeaturedArticles

  • [ ] 步骤 6:去掉 data 中的 actionArticles, dimensionProducts 字段

  • [ ] 步骤 7:Commit

    git add cfc-frontend/pages/index-home/index.vue
    git commit -m "fix: index-home 移除维度参数"
    

任务 9:member-detail.vue 和 home-pages/member-home-detail.vue

文件:

  • 修改:cfc-frontend/pages/member-detail/member-detail.vue
  • 修改:cfc-frontend/pages/home-pages/member-home-detail.vue

  • [ ] 步骤 1(member-detail):去掉 DimensionTasks 的 dimensionCode="all" prop

    <DimensionTasks
    v-if="memberId"
    :memberId="memberId"
    @taskClick="onTaskClick"
    @moreTasks="goTasks" />
    

注意:member-detail 页面的 this.dimensionCode 数据属性用于页面导航逻辑(onGraphMemberTap),不要删除!只去掉组件的 prop 传参。

  • 步骤 2(member-detail):去掉 loadDimensionTasks 方法(如果存在)

检查页面是否调用 getTodayTasksByCategory 加载任务数据。如果有则删除。

  • [ ] 步骤 3(home-pages):去掉 DimensionTasks 的 dimensionCode="all" prop

    <DimensionTasks
    v-if="memberId"
    :memberId="memberId"
    @taskClick="goTasks"
    @moreTasks="goTasks" />
    
  • [ ] 步骤 4(home-pages):去掉 loadData 中的任务加载逻辑

    // 删除:
    // getTodayTasksByCategory(self.memberId, 'all').then(...)
    
  • [ ] 步骤 5(home-pages):去掉 import 中的 getTodayTasksByCategory

  • [ ] 步骤 6:Commit

    git add cfc-frontend/pages/member-detail/member-detail.vue cfc-frontend/pages/home-pages/member-home-detail.vue
    git commit -m "fix: member-detail 和 home-detail 移除任务维度参数"
    

任务 10:growth-main/index.vue — 去掉 DimensionActivities 的 dimensionCode

文件:

  • 修改:cfc-frontend/pages/growth-main/index.vue

  • [ ] 步骤 1:去掉 DimensionActivities 的 dimensionCode prop

    <DimensionActivities
    :isLoggedIn="true"
    @activityClick="goActivityDetail"
    @moreActivities="goMoreActivities" />
    
  • [ ] 步骤 2:去掉 loadDimensionActivities 方法和 onLoad 中的调用

    // 删除 loadDimensionActivities 方法
    // 删除 onLoad 中的 this.loadDimensionActivities()
    
  • [ ] 步骤 3:去掉 data 中的 dimensionActivities 字段

  • [ ] 步骤 4:去掉 import 中的 getActivityList

  • [ ] 步骤 5:Commit

    git add cfc-frontend/pages/growth-main/index.vue
    git commit -m "fix: growth-main 移除活动维度参数"
    

任务 11:验证与最终提交

  • [ ] 步骤 1:后端编译验证

    cd cfc-backend && mvn clean compile
    

预期:编译通过。

  • [ ] 步骤 2:统计所有改动

    git diff --stat
    

预期:改动涵盖:

  • cfc-backend/ — ProductRecommendationController + ProductRecommendationService
  • cfc-frontend/components/ — 4 个组件
  • cfc-frontend/pages/ — 11 个页面

  • [ ] 步骤 3:最终 Commit

    git add -A
    git commit -m "fix: 推荐组件移除维度参数(商品/文章/活动/任务)"
    
  • [ ] 步骤 4:确认

运行:

git diff HEAD~1 --stat

确认改动范围正确。