For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement plan. Steps use checkbox (
- [ ]) syntax.
Goal: 将会员等级调整为:FREE(¥0)、FAMILY(¥1,314/年)、PREMIUM(高级家庭会员 ¥13,168/年 或 ¥1,314/月),所有价格可通过 sys_config 配置,在有价格/功能差异的页面添加缴费升级提醒。
架构: 后端更新 membership_levels 种子数据 + MembershipService 处理 PREMIUM 等级逻辑 → 前端 plans/upgrade/index 页面展示三个等级 → 商城等页面添加升级提示
Tech Stack: Spring Boot 2.7 + MyBatis-Plus / uni-app Vue 2 小程序 / MySQL 8.0
参考: docs/superpowers/plans/2026-07-27-membership-fix-plan.md(之前审计的修复计划)
| 文件 | 操作 | 职责 |
|---|---|---|
cfc-backend/.../config/DatabaseInitializer.java |
修改 | 更新 seed sys_config 价格 + membership_levels 种子数据 |
cfc-backend/.../service/MembershipService.java |
修改 | 新增 PREMIUM 等级处理 |
cfc-backend/.../controller/MembershipController.java |
修改 | /api/membership/plans 返回三个等级 |
cfc-backend/src/main/resources/schema.sql |
修改 | 同步 membership_levels 表注释 |
| 文件 | 操作 | 职责 |
|---|---|---|
cfc-frontend/pages/membership/plans.vue |
修改 | 展示 FREE/FAMILY/PREMIUM 三个方案,从后端加载价格 |
cfc-frontend/pages/membership/upgrade.vue |
修改 | 展示 FAMILY + PREMIUM 两个付费选项 |
cfc-frontend/pages/membership/index.vue |
修改 | 兼容 PREMIUM 等级显示 |
cfc-frontend/utils/api.js |
修改 | 确保所有 API 封装存在 |
cfc-frontend/pages/shop/index/index.vue |
修改 | 升级文案从"家庭会员"改为通用"付费会员" |
cfc-frontend/pages/shop/checkout/checkout.vue |
修改 | 同上 |
cfc-frontend/pages/shop/cart/cart.vue |
修改 | 同上 |
cfc-frontend/pages/shop/payment/payment.vue |
修改 | 升级提示引用后端价格 |
DatabaseInitializer 中现有 sys_config 种子数据:
insertSysConfigSeed("member_fee_family", "36500", "家庭会员年费(分)"); // ← ¥365
insertSysConfigSeed("member_fee_premium", "3650", "高级会员年费(元)"); // ← 单价混乱
insertSysConfigSeed("member_discount_family", "9000", "家庭会员折扣(bps)"); // ← 9折
insertSysConfigSeed("member_discount_premium", "0.8", "高级会员折扣率"); // ← 格式不统一
insertSysConfigSeed("member_upgrade_threshold", "131400", "累计消费门槛(分)"); // ← ¥1,314
更新 sys_config 种子值以分(cents)为单位统一:
member_fee_family = 131400(¥1,314 → 按分)member_fee_premium = 1316800(¥13,168 → 按分)member_discount_family = 9000(90% = 9折,bps)member_discount_premium = 8000(80% = 8折,bps)member_upgrade_threshold = 131400(¥1,314 消费门槛,不动)添加新 sys_config 键:
member_fee_premium_monthly = 131400(高级会员月费 ¥1,314)member_fee_family_desc = "家庭会员"(展示名称)member_fee_premium_desc = "高级家庭会员"(展示名称)MembershipService.java 中的 createOrder() 里 PREMIUM 读取 member_fee_premium / member_fee_premium_monthly
找到 insertSysConfigSeed("member_fee_family", ...) 相关代码,更新为:
insertSysConfigSeed("member_fee_family", "131400", "家庭会员年费(分)");
insertSysConfigSeed("member_fee_premium", "1316800", "高级家庭会员年费(分)");
insertSysConfigSeed("member_fee_premium_monthly", "131400", "高级家庭会员月费(分)");
insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
insertSysConfigSeed("member_discount_premium", "8000", "高级家庭会员商品折扣(bps)");
insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
// 保留现有其他 seed 不变
[ ] Step 2: 编译验证
cd cfc-backend && mvn clean compile -q
[ ] Step 3: 提交
git add cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
git commit -m "chore: 更新会员价格 sys_config 种子数据
FAMILY ¥1,314/年, PREMIUM ¥13,168/年或¥1,314/月, 折扣统一bps格式"
canUseFeature() 添加 PREMIUM 权限在 MembershipService.java 的 canUseFeature() 方法中,在 FAMILY 判断之后添加 PREMIUM:
// PREMIUM 用户全部权益同 FAMILY
if ("PREMIUM".equals(level)) {
return "free_activities".equals(feature)
|| "free_courses".equals(feature)
|| "full_price_purchase".equals(feature)
|| "referral_commission".equals(feature)
|| "discount_purchase".equals(feature)
|| "member_activities".equals(feature)
|| "create_family".equals(feature)
|| "invite_family".equals(feature)
|| "purchase_commission".equals(feature);
}
[ ] Step 2: 修改 applyMemberDiscount() 支持 PREMIUM 更高折扣
public Integer applyMemberDiscount(Long userId, Integer originalPrice) {
if (originalPrice == null || originalPrice <= 0) return 0;
String level = getMemberLevel(userId);
String discountStr = null;
if ("PREMIUM".equals(level)) {
discountStr = sysConfigService.getValue("member_discount_premium");
} else if ("FAMILY".equals(level)) {
discountStr = sysConfigService.getValue("member_discount_family");
}
if (discountStr != null) {
int discountBps = Integer.parseInt(discountStr);
return originalPrice * discountBps / 10000;
}
return originalPrice;
}
[ ] Step 3: 修改 createOrder() 支持 PREMIUM 定价
在 createOrder() 的 FAMILY/PROVIDER 判断后添加:
} else if ("PREMIUM".equals(levelCode)) {
if ("monthly".equals(effectivePeriod)) {
String feeStr = sysConfigService.getValue("member_fee_premium_monthly");
amount = feeStr != null ? Integer.parseInt(feeStr) : 131400;
} else {
String feeStr = sysConfigService.getValue("member_fee_premium");
amount = feeStr != null ? Integer.parseInt(feeStr) : 1316800;
}
}
[ ] Step 4: 编译验证
cd cfc-backend && mvn clean compile -q
[ ] Step 5: 提交
git add cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java
git commit -m "feat: MembershipService 支持 PREMIUM 等级
canUseFeature/applyMemberDiscount/createOrder 增加 PREMIUM 逻辑"
/api/membership/plans 已返回所有等级当前代码(MembershipController.java 行 217-231)已调用 getAllLevels(),返回所有等级。membership_levels 表中有 FREE/FAMILY/PREMIUM(需确认后台种子数据已插入)。无需修改代码,但需确认 DB 有数据。
也可以通过 /api/membership/levels 端点验证,同样返回所有等级。
如果需要额外返回价格说明文本(如突出"推荐"标记),可扩展 MembershipController.getPricingPlans():
plan.put("recommended", "FAMILY".equals(level.getLevelCode())); // 推荐标记
plan.put("levelDesc", level.getLevelDesc());
[ ] Step 2: 提交(如需改动)
git add cfc-backend/.../controller/MembershipController.java
git commit -m "feat: /api/membership/plans 增加推荐标记和等级描述"
现状: plans.vue 硬编码 L0(注册会员¥0)、L1(❤️ 一生一世¥1,314/年)、L2(💎 久久一生¥1,314/月),价格和功能均为静态数据。
改为: 从后端 /api/membership/plans 加载三个等级,动态渲染。
<template> 使用 v-for 渲染等级卡片移除硬编码的三张卡片,改为:
<view class="plan-card" v-for="level in levels" :key="level.levelCode"
:class="{'active': level.levelCode === 'FAMILY'}"
v-if="level.levelCode !== 'FREE'">
<!-- 付费等级卡片 -->
<view class="plan-badge" v-if="level.levelCode === 'FAMILY'">推荐</view>
<view class="plan-badge premium" v-else-if="level.levelCode === 'PREMIUM'">至尊</view>
<view class="plan-header">
<text class="plan-name">{{ level.levelName }}</text>
<text class="plan-tag">{{ level.levelCode === 'FAMILY' ? '年付会员' : '年付/月付' }}</text>
</view>
<view class="plan-price">
<text class="price-amount">{{ formatPrice(level.priceYearly) }}</text>
<text class="price-unit">/年</text>
</view>
<view class="plan-features">
<view class="feature-row" v-for="(feat, idx) in parseFeatures(level.features)" :key="idx">
<text class="feature-icon">✓</text>
<text class="feature-text">{{ feat }}</text>
</view>
</view>
<button class="btn-action" :class="level.levelCode === 'FAMILY' ? 'btn-primary' : 'btn-premium'"
@click="purchasePlan(level.levelCode)">
{{ level.levelCode === 'FAMILY' ? '立即开通' : '立即订阅' }}
</button>
</view>
[ ] Step 2: 修改 <script> data 和方法
import { getMembershipPlans } from '../../utils/api'
export default {
data() {
return {
levels: [],
l0Features: ['每日 3 次 AI 问答', '基础健康百科', '社区内容浏览'],
benefits: [
{ icon: '🤖', name: 'AI 健康顾问', desc: '7×24 小时智能问答', bg: '#DBEAFE' },
{ icon: '👨👩👧👦', name: '家庭覆盖', desc: '一次付费全家守护', bg: '#D1FAE5' },
{ icon: '👨🏫', name: '专属管家', desc: '认证专业人员服务', bg: '#FEF3C7' },
{ icon: '🛒', name: '商品折扣', desc: '会员专享优惠价', bg: '#FCE7F3' },
{ icon: '💰', name: '消费返利', desc: 'P 点制终身返利', bg: '#FFEDD5' },
{ icon: '📈', name: '推广佣金', desc: 'R0-R4 阶梯奖励', bg: '#E0E7FF' }
],
faqs: [
{ q: '家庭会员适合谁?', a: '适合有 1-2 个孩子的家庭,追求性价比' },
{ q: '高级家庭会员与家庭会员的区别?', a: '享更高折扣(8折 vs 9折)、更多高级服务' },
{ q: '可以取消吗?', a: '可以,到期后降级为普通会员,数据保留 30 天' },
{ q: '消费返利如何计算?', a: '被推荐人消费 P 点 × 返利率' }
]
}
},
onLoad() {
this.loadPlans()
},
methods: {
loadPlans() {
var self = this
getMembershipPlans().then(function(res) {
if (res.code === 200 && Array.isArray(res.data)) {
self.levels = res.data
}
}).catch(function(e) {
console.error('获取方案失败', e)
})
},
formatPrice(cents) {
if (!cents && cents !== 0) return '¥0'
return '¥' + (cents / 100).toLocaleString()
},
parseFeatures(features) {
if (!features) return []
if (Array.isArray(features)) return features
try { return JSON.parse(features) } catch (e) { return [] }
},
purchasePlan(levelCode) {
uni.navigateTo({ url: '/pages/membership/upgrade?level=' + levelCode })
}
}
}
保留原有免费卡片(L0/注册会员),只替换 L1/L2 为从后端动态加载的 FAMILY/PREMIUM。
[ ] Step 3: 提交
git add cfc-frontend/pages/membership/plans.vue
git commit -m "feat: plans.vue 动态加载三个会员等级
移除硬编码价格,从后端 /api/membership/plans 获取 FREE/FAMILY/PREMIUM 数据"
现状: upgrade.vue 只有家庭会员(FAMILY)一个付费选项,显示档位选择(月/季/年)。
改为: 添加高级家庭会员(PREMIUM)选项卡,用户可在两个付费方案间切换。
在 template 的 upgrade-card 区域添加等级选择 tabs:
<!-- 等级选择 -->
<view class="level-tabs">
<view class="level-tab"
:class="selectedLevel === 'FAMILY' ? 'level-tab-active' : ''"
@click="selectLevel('FAMILY')">
<text class="level-tab-name">家庭会员</text>
<text class="level-tab-price">¥{{ getLevelYearlyPrice('FAMILY') }}/年</text>
</view>
<view class="level-tab"
:class="selectedLevel === 'PREMIUM' ? 'level-tab-active' : ''"
@click="selectLevel('PREMIUM')">
<text class="level-tab-name">高级家庭会员</text>
<text class="level-tab-price">¥{{ getLevelYearlyPrice('PREMIUM') }}/年</text>
</view>
</view>
[ ] Step 2: 修改 script data
data() {
return {
selectedLevel: 'FAMILY', // 新增:选择的等级
// ... 保留现有字段
}
},
methods: {
selectLevel(code) {
this.selectedLevel = code
},
getLevelYearlyPrice(code) {
for (var i = 0; i < this.levels.length; i++) {
if (this.levels[i].levelCode === code) {
return this.levels[i].priceYearly ? (this.levels[i].priceYearly / 100).toFixed(0) : '0'
}
}
return '0'
},
// 修改 getPriceForPeriod 根据 selectedLevel 查询
getPriceForPeriod(periodKey) {
var plan = null
for (var i = 0; i < this.levels.length; i++) {
if (this.levels[i].levelCode === this.selectedLevel) {
plan = this.levels[i]
break
}
}
if (!plan) return '0'
if (periodKey === 'monthly') return plan.priceMonthly ? (plan.priceMonthly / 100).toFixed(0) : '0'
if (periodKey === 'quarterly') return plan.priceQuarterly ? (plan.priceQuarterly / 100).toFixed(0) : '0'
if (periodKey === 'yearly') return plan.priceYearly ? (plan.priceYearly / 100).toFixed(0) : '0'
return '0'
},
// 修改 doUpgrade/doRenew 使用 selectedLevel
doUpgrade() {
var self = this
createOrder(self.selectedLevel, 'pay', self.selectedPeriod).then(function(res) {
uni.showToast({ title: '开通成功', icon: 'success' })
self.loadData()
}).catch(function(e) {
uni.showToast({ title: e.message || '开通失败', icon: 'none' })
})
},
// features 改为根据 selectedLevel 动态
// compareData 增加 PREMIUM 列
}
[ ] Step 3: 权益对比表增加第三列(PREMIUM)
<view class="compare-row header-row">
<text class="compare-cell cell-feature">权益项</text>
<text class="compare-cell cell-free">免费</text>
<text class="compare-cell cell-family">家庭会员</text>
<text class="compare-cell cell-premium active">高级会员</text>
</view>
<view class="compare-row" v-for="(item, idx) in compareData" :key="idx">
<text class="compare-cell cell-feature">{{ item.name }}</text>
<text class="compare-cell cell-free">{{ item.free }}</text>
<text class="compare-cell cell-family">{{ item.family }}</text>
<text class="compare-cell cell-premium active">{{ item.premium }}</text>
</view>
data.compareData 更新:
compareData: [
{ name: '商品折扣', free: '普通价格', family: '9折', premium: '8折' },
{ name: '家庭管理', free: '基础功能', family: '全部功能', premium: '全部功能' },
{ name: '推荐佣金', free: '不可参与', family: '最高15%', premium: '最高20%' },
{ name: '专属活动', free: '部分参与', family: '全部参与', premium: '优先参与' },
{ name: '成长报告', free: '月度', family: '每周+深度', premium: '每日+深度+专家解读' },
{ name: '任务配额', free: '每日5个', family: '不限量', premium: '不限量+优先审核' }
]
[ ] Step 4: 提交
git add cfc-frontend/pages/membership/upgrade.vue
git commit -m "feat: upgrade.vue 展示 FAMILY + PREMIUM 两个付费选项
等级切换 tabs、动态价格、权益对比三列"
现状: index.vue 已从后端动态加载等级列表,但对比表只有两列(免费 vs 家庭会员),isFamilyMember 判断当前等级。
[ ] Step 1: 修改 isFamilyMember 兼容 PREMIUM
computed: {
isMember() {
return this.membership && this.membership.levelCode
&& this.membership.levelCode !== 'FREE' && this.membership.isActive
},
familyPriceText() {
var familyLevel = this.levels.find(function(l) { return l.levelCode === 'FAMILY' })
if (familyLevel && familyLevel.priceYearly > 0) {
return this.formatPrice(familyLevel.priceYearly)
}
return '¥1,314.00'
}
}
[ ] Step 2: 等级对比表增加第三列
参照 upgrade.vue 的修改,在 compareData 中增加 premium 字段。
当前 v-for="level in levels" 已动态渲染所有等级,无需改动模板。但按钮文字需要适配:
level.levelCode === 'FAMILY' ? '开通' : level.levelCode === 'PREMIUM' ? '升级' : ''
<button class="btn-level-action"
v-if="level.levelCode !== currentLevelCode && level.levelCode !== 'FREE'"
@click="handlePurchase(level)">
{{ level.levelCode === 'FAMILY' ? '开通' : '升级' }}
</button>
[ ] Step 4: 提交
git add cfc-frontend/pages/membership/index.vue
git commit -m "feat: membership/index.vue 兼容 PREMIUM 等级显示
对比表三列, isMember 判断, 等级卡片列表"
现状: 商城页面有"开通家庭会员"的升级提示(shop/index、checkout、cart、payment)。文案写死了"家庭会员"。
改为: 文案改为"开通付费会员"或从配置读取最低付费等级名称和价格。
[ ] Step 1: shop/index/index.vue
<!-- 修改前 -->
<text class="member-banner-title">开通家庭会员</text>
<text class="member-banner-desc">享专属折扣与成长服务</text>
<!-- 修改后 -->
<text class="member-banner-title">开通付费会员</text>
<text class="member-banner-desc">享专属折扣与成长服务,最低仅 ¥{{ minMemberPrice }}/年</text>
添加 minMemberPrice:
data: { minMemberPrice: '1,314' }, // 从 levels 中找最便宜的付费等级
// loadData 中添加:
try {
var levelsRes = await getMembershipLevels()
var minPrice = null
for (var i = 0; i < (levelsRes.data || []).length; i++) {
var l = levelsRes.data[i]
if (l.levelCode !== 'FREE' && l.priceYearly && (!minPrice || l.priceYearly < minPrice)) {
minPrice = l.priceYearly
}
}
if (minPrice) that.minMemberPrice = (minPrice / 100).toLocaleString()
} catch(e) {}
[ ] Step 2: shop/checkout/checkout.vue (同样修改)
<!-- 修改前 -->
<text class="member-upgrade-text">开通家庭会员享全场商品会员专享价</text>
<!-- 修改后 -->
<text class="member-upgrade-text">开通付费会员享全场商品会员专享价</text>
[ ] Step 3: shop/cart/cart.vue (同样修改)
<!-- 修改前 -->
<text class="member-hint-text">开通家庭会员享全场商品折扣</text>
<!-- 修改后 -->
<text class="member-hint-text">开通付费会员享全场商品折扣</text>
[ ] Step 4: shop/payment/payment.vue
<!-- 修改前 -->
<text class="upgrade-card-title">支付成功!开通家庭会员更划算</text>
<!-- 修改后 -->
<text class="upgrade-card-title">支付成功!开通付费会员更划算</text>
同时修改 familyYearlyPrice 从后端获取:
// 现有 this.familyYearlyPrice 从 /api/membership/levels 获取,确认已实现
[ ] Step 5: 提交
git add cfc-frontend/pages/shop/index/index.vue cfc-frontend/pages/shop/checkout/checkout.vue cfc-frontend/pages/shop/cart/cart.vue cfc-frontend/pages/shop/payment/payment.vue
git commit -m "fix: 商城升级提示文案通用化
'开通家庭会员'改为'开通付费会员',价格从后端动态获取"
如果现有 cfc-web/src/views/admin/SysConfig.vue 可以编辑 sys_config,则不需要新建页面。确认后补充。
[ ] Step 1: 检查 SysConfig.vue 是否支持编辑 sys_config
ls cfc-web/src/views/admin/SysConfig*.vue 2>/dev/null || echo "需要新建"
[ ] Step 2: 如需新建,参考已有配置页面
(留出,视检查结果决定)
Task 1 (sys_config seed) ──→ Task 2 (MembershipService) ──→ 后端完成
│
Task 4 (plans.vue) ───────────────────────────────────────────────┘
Task 5 (upgrade.vue) ─────────────────────────────────────────────┘
Task 6 (index.vue) ───────────────────────────────────────────────┘
Task 7 (shop prompts) ── 独立,可并行 ──→ 前端完成
可并行:Task 1+2(后端连续),Task 4+5+6+7(前端皆依赖后端出数,但可先改代码)
membership_levels 表种子数据包含 FREE/FAMILY/PREMIUM 正确价格MembershipService.canUseFeature() 涵盖 PREMIUMMembershipService.applyMemberDiscount() 区分 FAMILY(9折)/PREMIUM(8折)MembershipService.createOrder() 处理 PREMIUM 月付/年付plans.vue 无硬编码价格残留upgrade.vue 可在 FAMILY/PREMIUM 间切换index.vue 对比表三列mvn clean compile 通过