面向 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 + 微信小程序
文件:
修改: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 返回全量"
文件:
修改: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);
将第 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);
}
将 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 "丰富您的维度生活";
}
运行:
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: 商品推荐支持空维度返回全量推荐"
文件:
修改: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 移除维度参数自加载"
文件:
修改: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 移除维度参数自加载"
文件:
修改: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。
将 {{ 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 移除维度参数自加载"
文件:
修改: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 移除维度参数自加载"
文件:
cfc-frontend/pages/body/member-body-detail.vuecfc-frontend/pages/mind/member-mind-detail.vuecfc-frontend/pages/wisdom/member-wisdom-detail.vuecfc-frontend/pages/action-detail/member-action-detail.vuecfc-frontend/pages/body-detail/member-body-detail.vuecfc-frontend/pages/mind-detail/member-mind-detail.vuecfc-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 方法
删除这三个方法定义。
每个页面完成后立即 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 顺序逐个提交。
文件:
修改: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 方法定义。
删除 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 移除维度参数"
文件:
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 传参。
检查页面是否调用 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 移除任务维度参数"
文件:
修改: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 移除活动维度参数"
[ ] 步骤 1:后端编译验证
cd cfc-backend && mvn clean compile
预期:编译通过。
[ ] 步骤 2:统计所有改动
git diff --stat
预期:改动涵盖:
cfc-backend/ — ProductRecommendationController + ProductRecommendationServicecfc-frontend/components/ — 4 个组件cfc-frontend/pages/ — 11 个页面
[ ] 步骤 3:最终 Commit
git add -A
git commit -m "fix: 推荐组件移除维度参数(商品/文章/活动/任务)"
[ ] 步骤 4:确认
运行:
git diff HEAD~1 --stat
确认改动范围正确。