Explorar el Código

chore: update membership price sys_config seeds

FAMILY: ¥1,314/yr, PREMIUM: ¥13,168/yr or ¥1,314/mo
Sisyphus hace 3 meses
padre
commit
a33922425d

+ 4 - 3
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -1016,14 +1016,15 @@ log.info("已添加template_id列到tasks表");
             insertSysConfigSeed("points_exchange_rate", "1", "积分兑换汇率(1积分=1分,即0.01元)");
             insertSysConfigSeed("points_deduct_cap", "0.5", "积分抵扣上限(50%)");
             insertSysConfigSeed("member_fee_standard", "1314", "标准会员年费(元)");
-            insertSysConfigSeed("member_fee_premium", "3650", "高级会员年费(元)");
+            insertSysConfigSeed("member_fee_premium", "1316800", "高级家庭会员年费(分)");
             insertSysConfigSeed("member_eligible_budget", "1314", "等值商品选择预算上限(元)");
             insertSysConfigSeed("renewal_discount_rate", "0.85", "续费折扣率");
             insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣率");
-            insertSysConfigSeed("member_discount_premium", "0.8", "高级会员折扣率");
-            insertSysConfigSeed("member_fee_family", "36500", "家庭会员年费(分)");
+            insertSysConfigSeed("member_discount_premium", "8000", "高级家庭会员商品折扣(bps)");
+            insertSysConfigSeed("member_fee_family", "131400", "家庭会员年费(分)");
             insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
             insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
+            insertSysConfigSeed("member_fee_premium_monthly", "131400", "高级家庭会员月费(分)");
             insertSysConfigSeed("commission_member_l1_rate", "2000", "推荐家庭会员L1佣金率(bps)");
             insertSysConfigSeed("commission_member_l2_rate", "500", "推荐家庭会员L2佣金率(bps)");
             insertSysConfigSeed("commission_product_profit_share", "1000", "下线商品购买利润分成(bps)");

+ 629 - 0
docs/superpowers/plans/2026-07-27-membership-fix-plan.md

@@ -0,0 +1,629 @@
+# 会员管理系统修复实施计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 修复会员管理系统审计发现的 6 个问题(2 个严重数据问题 + 4 个功能缺口)
+
+**Architecture:** 后端 DDL 补充 → 前端 API 补齐 → 前端页面补全 → 硬编码修复 → 管理端补充
+
+**Tech Stack:** Spring Boot 2.7 + MyBatis-Plus / uni-app Vue 2 小程序 / Vue 2 + Element UI 管理端
+
+**参考文档:**
+- `docs/flows/membership-flow.md` — 会员与订阅流程图
+- `docs/superpowers/specs/2026-07-09-family-membership-design.md` — 完整设计方案
+- `cfc-backend/src/main/java/com/etotem/cfc/entity/MembershipLevel.java` — membership_levels 实体
+- `cfc-backend/src/main/java/com/etotem/cfc/entity/FamilyMembership.java` — family_memberships 实体
+- `cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java` — 会员控制器
+- `cfc-backend/src/main/java/com/etotem/cfc/controller/subscription/SubscriptionController.java` — 订阅控制器
+
+---
+
+### Task 1: 补充 membership_levels 和 family_memberships 表 DDL
+
+**Files:**
+- Modify: `cfc-backend/src/main/resources/schema.sql` — 追加 CREATE TABLE
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java` — 追加迁移
+
+- [ ] **Step 1: schema.sql 追加 membership_levels 表定义**
+
+在 `member_upgrade_records` 表(行 ~1325)之后,`-- 规划师服务模块表` 之前插入:
+
+```sql
+-- 会员等级配置(FREE/FAMILY/PROVIDER)
+CREATE TABLE IF NOT EXISTS membership_levels (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    level_code VARCHAR(20) NOT NULL COMMENT '等级编码: FREE/FAMILY/PROVIDER',
+    level_name VARCHAR(50) NOT NULL COMMENT '等级名称',
+    level_desc VARCHAR(500) COMMENT '等级描述',
+    price_monthly INT DEFAULT 0 COMMENT '月费(分)',
+    price_quarterly INT DEFAULT 0 COMMENT '季费(分)',
+    price_yearly INT DEFAULT 0 COMMENT '年费(分)',
+    features TEXT COMMENT '功能权限列表(JSON)',
+    max_children INT DEFAULT 3 COMMENT '可添加孩子数量',
+    max_tasks_per_day INT DEFAULT 5 COMMENT '每日任务上限',
+    ai_review_enabled TINYINT(1) DEFAULT 0 COMMENT 'AI审核',
+    priority_support TINYINT(1) DEFAULT 0 COMMENT '优先客服',
+    teacher_consultation TINYINT(1) DEFAULT 0 COMMENT '老师咨询',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    UNIQUE KEY uk_level_code (level_code)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员等级配置';
+```
+
+- [ ] **Step 2: schema.sql 追加 family_memberships 表定义**
+
+紧跟 membership_levels 之后:
+
+```sql
+-- 家庭会员记录
+CREATE TABLE IF NOT EXISTS family_memberships (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    family_id BIGINT NOT NULL COMMENT '家庭ID',
+    level_code VARCHAR(20) NOT NULL COMMENT '会员等级: FREE/BASIC/PROFESSIONAL/ENTERPRISE',
+    start_date DATETIME NOT NULL COMMENT '开始时间',
+    end_date DATETIME NOT NULL COMMENT '到期时间',
+    payment_method VARCHAR(20) COMMENT '支付方式: wechat/alipay/card',
+    payment_status VARCHAR(20) DEFAULT 'pending' COMMENT '支付状态: pending/paid/expired/refunded',
+    order_no VARCHAR(64) COMMENT '订单号',
+    transaction_id VARCHAR(64) COMMENT '交易流水号',
+    amount INT DEFAULT 0 COMMENT '支付金额(分)',
+    auto_renew TINYINT(1) DEFAULT 0 COMMENT '自动续费',
+    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_family_id (family_id),
+    INDEX idx_level_code (level_code)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭会员记录';
+```
+
+- [ ] **Step 3: DatabaseInitializer 追加迁移 90 和 91**
+
+找到 `runMigrations()` 中最新迁移编号(搜索 `// 迁移` 确认最新号),追加:
+
+```java
+// 迁移90: 创建 membership_levels 表(会员等级配置)
+try {
+    jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS membership_levels (" +
+            "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+            "level_code VARCHAR(20) NOT NULL COMMENT '等级编码: FREE/FAMILY/PROVIDER', " +
+            "level_name VARCHAR(50) NOT NULL COMMENT '等级名称', " +
+            "level_desc VARCHAR(500) COMMENT '等级描述', " +
+            "price_monthly INT DEFAULT 0 COMMENT '月费(分)', " +
+            "price_quarterly INT DEFAULT 0 COMMENT '季费(分)', " +
+            "price_yearly INT DEFAULT 0 COMMENT '年费(分)', " +
+            "features TEXT COMMENT '功能权限列表(JSON)', " +
+            "max_children INT DEFAULT 3 COMMENT '可添加孩子数量', " +
+            "max_tasks_per_day INT DEFAULT 5 COMMENT '每日任务上限', " +
+            "ai_review_enabled TINYINT(1) DEFAULT 0 COMMENT 'AI审核', " +
+            "priority_support TINYINT(1) DEFAULT 0 COMMENT '优先客服', " +
+            "teacher_consultation TINYINT(1) DEFAULT 0 COMMENT '老师咨询', " +
+            "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+            "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+            "UNIQUE KEY uk_level_code (level_code)" +
+            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员等级配置'");
+    log.info("已创建 membership_levels 表");
+} catch (Exception e) {
+    log.warn("创建 membership_levels 表失败: {}", e.getMessage());
+}
+
+// 迁移91: 创建 family_memberships 表(家庭会员记录)
+try {
+    jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_memberships (" +
+            "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+            "family_id BIGINT NOT NULL COMMENT '家庭ID', " +
+            "level_code VARCHAR(20) NOT NULL COMMENT '会员等级', " +
+            "start_date DATETIME NOT NULL COMMENT '开始时间', " +
+            "end_date DATETIME NOT NULL COMMENT '到期时间', " +
+            "payment_method VARCHAR(20) COMMENT '支付方式', " +
+            "payment_status VARCHAR(20) DEFAULT 'pending' COMMENT '支付状态', " +
+            "order_no VARCHAR(64) COMMENT '订单号', " +
+            "transaction_id VARCHAR(64) COMMENT '交易流水号', " +
+            "amount INT DEFAULT 0 COMMENT '支付金额(分)', " +
+            "auto_renew TINYINT(1) DEFAULT 0 COMMENT '自动续费', " +
+            "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+            "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+            "INDEX idx_family_id (family_id), " +
+            "INDEX idx_level_code (level_code)" +
+            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭会员记录'");
+    log.info("已创建 family_memberships 表");
+} catch (Exception e) {
+    log.warn("创建 family_memberships 表失败: {}", e.getMessage());
+}
+```
+
+- [ ] **Step 4: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add cfc-backend/src/main/resources/schema.sql cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
+git commit -m "fix: 补充 membership_levels 和 family_memberships 表 DDL
+- schema.sql + DatabaseInitializer 迁移 90/91
+- 两个表被实体引用但无 DDL 创建语句,数据库重建时会员系统会不可用"
+```
+
+---
+
+### Task 2: 补齐缺少的前端 API 封装
+
+**Files:**
+- Modify: `cfc-frontend/utils/api.js`
+
+在 `api.js` 的会员模块区域(约行 287-306,`getMembershipLevels`/`getMyMembership` 附近)追加以下 8 个函数。在订阅模块区域(约行 1750-1755,`getSubscriptionPlans` 附近)追加缺失的 2 个。
+
+- [ ] **Step 1: 追加 membership 模块缺失的 API 封装**
+
+找到 `export const getCurrentLevel` 之后,在 `export const createOrder` 之前插入:
+
+```javascript
+// 激活试用会员
+export const activateTrial = () => {
+  return request('/api/membership/trial', 'POST', {})
+}
+
+// 获取价格计划
+export const getMembershipPlans = () => {
+  return request('/api/membership/plans', 'POST')
+}
+
+// 检查功能权限
+export const canUseFeature = (feature) => {
+  return request('/api/membership/can-use', 'POST', { feature })
+}
+
+// 应用会员折扣
+export const applyMemberDiscount = (amount) => {
+  return request('/api/membership/discount', 'POST', { amount })
+}
+```
+
+- [ ] **Step 2: 追加 subscription 模块缺失的 API 封装**
+
+找到 `export const checkBenefit` 附近,追加:
+
+```javascript
+// 我的权益列表(订阅权益使用记录)
+export const getMyBenefits = () => {
+  return request('/api/subscription/benefits/my', 'POST')
+}
+
+// 确认订阅(参数式)
+export const subscribe = (data) => {
+  return request('/api/subscription/subscribe', 'POST', data)
+}
+
+// 获取推广等级
+export const getSubscriptionTier = () => {
+  return request('/api/subscription/tier', 'POST')
+}
+```
+
+- [ ] **Step 3: 检查语法完整性**
+
+```bash
+node -e "require('./cfc-frontend/utils/api.js')" 2>&1 || true
+# 或使用 npm run build:mp-weixin 验证
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add cfc-frontend/utils/api.js
+git commit -m "fix: 补充 11 个缺失的会员/订阅 API 封装
+activateTrial / getMembershipPlans / canUseFeature / applyMemberDiscount
+getMyBenefits / subscribe / getSubscriptionTier"
+```
+
+---
+
+### Task 3: plans.vue 从后端读取价格替代硬编码
+
+**Files:**
+- Modify: `cfc-frontend/pages/membership/plans.vue`
+
+当前问题:页面硬编码了 ¥1,314/年和 ¥1,314/月 两个价格,后端 `/api/membership/plans` 已实现但未调用。
+
+- [ ] **Step 1: 修改 plans.vue 的 script 部分**
+
+在 data 中添加 `levels: []` 和 `loading: true`;在 `onLoad` 中调用 `getMembershipPlans`;使用响应式数据替换模板中的硬编码价格。
+
+找到 script 部分类似 `export default { data() { return { ... } } }` 的结构,修改为:
+
+```javascript
+import { getMembershipPlans } from '../../utils/api'
+
+export default {
+  data() {
+    return {
+      loading: true,
+      levels: [],
+      l0Features: [
+        '基础健康档案',
+        '每日任务管理',
+        '社区互动'
+      ],
+      l1Features: [],
+      l2Features: []
+    }
+  },
+  onLoad: function() {
+    this.loadPlans()
+  },
+  methods: {
+    loadPlans: function() {
+      var self = this
+      getMembershipPlans().then(function(res) {
+        if (res.code === 200 && Array.isArray(res.data)) {
+          self.levels = res.data
+          // 从后端数据填充 feature 列表(从 level.features JSON 解析或使用默认值)
+          var l1 = res.data.find(function(l) { return l.levelCode === 'FAMILY' })
+          var l2 = res.data.find(function(l) { return l.levelCode === 'PROVIDER' })
+          if (l1) {
+            self.l1Features = ['全年会员(¥' + (l1.yearly / 100).toLocaleString() + ')']
+          }
+          if (l2) {
+            self.l2Features = ['高端会员服务']
+          }
+        }
+        self.loading = false
+      }).catch(function() {
+        self.loading = false
+      })
+    },
+    // 保留原有 purchasePlan 方法不变
+    purchasePlan: function(level) {
+      uni.navigateTo({ url: '/pages/membership/upgrade?level=' + level })
+    },
+    formatPrice: function(priceInCents) {
+      if (!priceInCents) return '0'
+      return '¥' + (priceInCents / 100).toLocaleString()
+    }
+  }
+}
+```
+
+替换模板中硬编码的 `¥1,314` 为 `{{ formatPrice(planYearlyPrice) }}`。关键修改位置是计划卡片 L1 和 L2 的 `plan-price` 区域。
+
+- [ ] **Step 2: 验证模板正确引用数据**
+
+确认模板中使用 `v-for` 渲染 `levels` 数据,或至少将 L1/L2 价格绑定到从 API 获取的值而非字面量。
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add cfc-frontend/pages/membership/plans.vue
+git commit -m "fix: plans.vue 从后端 /api/membership/plans 读取价格
+替代硬编码的 ¥1,314/年 和 ¥1,314/月"
+```
+
+---
+
+### Task 4: 创建订阅管理页面(小程序)
+
+**Files:**
+- Modify: `cfc-frontend/pages.json` — 注册 `subscription` 分包
+- Create: `cfc-frontend/pages/subscription/index.vue` — 订阅管理首页(状态 + 取消 + 自动续费)
+
+订阅管理页面包含:
+- 当前订阅状态卡片(活跃/已过期/已取消 + 到期时间)
+- 自动续费开关
+- 取消订阅按钮
+- 权益列表入口
+
+- [ ] **Step 1: pages.json 注册 subscription 分包**
+
+在 `pages/membership` 分包(约行 678)之后,追加:
+
+```json
+    {
+      "root": "pages/subscription",
+      "pages": [
+        {
+          "path": "index",
+          "style": {
+            "navigationBarTitleText": "订阅管理",
+            "navigationStyle": "custom"
+          }
+        }
+      ]
+    },
+```
+
+- [ ] **Step 2: 创建 `pages/subscription/index.vue`**
+
+组件结构:
+
+```vue
+<template>
+  <view class="sub-container">
+    <!-- 自定义导航栏 -->
+    <view class="custom-nav">
+      <view class="nav-header">
+        <view class="nav-back" @click="goBack"><text class="nav-back-icon">‹</text></view>
+        <text class="nav-title">订阅管理</text>
+        <view class="nav-placeholder"></view>
+      </view>
+    </view>
+
+    <scroll-view class="sub-list" scroll-y refresher-enabled :refresher-triggered="refreshing" @refresherrefresh="loadData">
+      <!-- 当前订阅状态 -->
+      <view class="status-card" v-if="subscription">
+        <view class="status-header">
+          <text class="status-level">{{ subscription.level === 'L2' ? '💎 久久一生' : '❤️ 一生一世' }}</text>
+          <text :class="['status-badge', subActive ? 'active' : 'inactive']">{{ subActive ? '生效中' : '已失效' }}</text>
+        </view>
+        <view class="status-body">
+          <view class="status-row">
+            <text class="status-label">到期时间</text>
+            <text class="status-value">{{ formatDate(subscription.expireTime) }}</text>
+          </view>
+          <view class="status-row">
+            <text class="status-label">自动续费</text>
+            <switch :checked="subscription.autoRenew === 1" @change="onAutoRenewChange" />
+          </view>
+        </view>
+        <button class="btn-cancel" @click="onCancel" v-if="subActive">取消订阅</button>
+      </view>
+      <view class="no-sub" v-else>
+        <text class="no-sub-text">暂无有效订阅</text>
+        <button class="btn-go" @click="goPlans">查看会员计划</button>
+      </view>
+
+      <!-- 我的权益入口 -->
+      <view class="benefit-entry" @click="goBenefits">
+        <text class="benefit-title">🎁 我的权益</text>
+        <text class="benefit-arrow">›</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import { getMySubscription, setAutoRenew, cancelSubscription } from '../../utils/api'
+
+export default {
+  data() {
+    return {
+      refreshing: false,
+      subscription: null,
+      subActive: false
+    }
+  },
+  onLoad() { this.loadData() },
+  methods: {
+    loadData() {
+      var self = this
+      getMySubscription().then(function(res) {
+        if (res.code === 200 && res.data) {
+          self.subscription = res.data.subscription || null
+          self.subActive = res.data.hasActiveSubscription || false
+        }
+        self.refreshing = false
+      }).catch(function() {
+        self.refreshing = false
+      })
+    },
+    onAutoRenewChange(e) {
+      var self = this
+      setAutoRenew({ enabled: e.detail.value ? 1 : 0 }).then(function(res) {
+        if (res.code === 200) {
+          uni.showToast({ title: e.detail.value ? '已开启' : '已关闭', icon: 'success' })
+          self.loadData()
+        }
+      })
+    },
+    onCancel() {
+      var self = this
+      uni.showModal({
+        title: '确认取消',
+        content: '取消后订阅到期将不再续费,确定要取消吗?',
+        success: function(r) {
+          if (r.confirm) {
+            cancelSubscription({}).then(function(res) {
+              if (res.code === 200) {
+                uni.showToast({ title: '已取消', icon: 'success' })
+                self.loadData()
+              }
+            })
+          }
+        }
+      })
+    },
+    goBenefits() { uni.navigateTo({ url: '/pages/membership/benefits' }) },
+    goPlans() { uni.navigateTo({ url: '/pages/membership/plans' }) },
+    goBack() { uni.navigateBack() },
+    formatDate(d) { return d ? d.slice(0, 10) : '' }
+  }
+}
+</script>
+
+<style scoped>
+.sub-container { min-height: 100vh; background: #FFF7ED; }
+.custom-nav { padding-top: var(--status-bar-height); background: #fff; }
+.nav-header { display: flex; align-items: center; height: 88rpx; }
+.nav-back { width: 88rpx; height: 88rpx; display: flex; align-items: center; justify-content: center; }
+.nav-back-icon { font-size: 48rpx; color: #333; }
+.nav-title { flex: 1; text-align: center; font-size: 34rpx; font-weight: 600; color: #333; }
+.nav-placeholder { width: 88rpx; }
+.sub-list { height: calc(100vh - var(--status-bar-height) - 88rpx); padding: 20rpx; }
+.status-card { background: #fff; border-radius: 20rpx; padding: 30rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
+.status-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24rpx; }
+.status-level { font-size: 32rpx; font-weight: 600; color: #333; }
+.status-badge { font-size: 24rpx; padding: 4rpx 16rpx; border-radius: 20rpx; }
+.status-badge.active { background: #E8F5E9; color: #2E7D32; }
+.status-badge.inactive { background: #FBE9E7; color: #BF360C; }
+.status-body { display: flex; flex-direction: column; gap: 16rpx; margin-bottom: 24rpx; }
+.status-row { display: flex; justify-content: space-between; align-items: center; }
+.status-label { font-size: 28rpx; color: #666; }
+.status-value { font-size: 28rpx; color: #333; }
+.btn-cancel { width: 100%; height: 80rpx; border-radius: 40rpx; background: #fff; color: #FF4444; border: 2rpx solid #FF4444; font-size: 28rpx; display: flex; align-items: center; justify-content: center; }
+.btn-cancel:active { opacity: 0.8; }
+.no-sub { background: #fff; border-radius: 20rpx; padding: 60rpx 30rpx; text-align: center; }
+.no-sub-text { font-size: 28rpx; color: #999; margin-bottom: 24rpx; display: block; }
+.btn-go { background: #F97316; color: #fff; border: none; border-radius: 40rpx; padding: 20rpx 40rpx; font-size: 28rpx; }
+.benefit-entry { margin-top: 20rpx; background: #fff; border-radius: 16rpx; padding: 30rpx; display: flex; justify-content: space-between; align-items: center; }
+.benefit-title { font-size: 28rpx; font-weight: 500; color: #333; }
+.benefit-arrow { font-size: 36rpx; color: #ccc; }
+</style>
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add cfc-frontend/pages.json cfc-frontend/pages/subscription/index.vue
+git commit -m "feat: 创建订阅管理页面(小程序)
+pages/subscription/index.vue: 订阅状态卡片、自动续费开关、取消订阅、权益入口"
+```
+
+---
+
+### Task 5: 创建管理端订阅管理页面
+
+**Files:**
+- Modify: `cfc-web/src/router/index.js` — 添加订阅管理路由
+- Create: `cfc-web/src/views/admin/SubscriptionManage.vue` — 订阅列表 + 详情
+- Create/Modify: `cfc-web/src/api/subscription.js` — 管理端 API 封装
+
+- [ ] **Step 1: 创建管理端 API 文件 `cfc-web/src/api/subscription.js`**
+
+```javascript
+import request from '@/utils/request'
+
+// 订阅列表
+export function getSubscriptionList(params) {
+  return request({
+    url: '/api/admin/subscription/list',
+    method: 'post',
+    data: params
+  })
+}
+
+// 手动取消订阅
+export function adminCancelSubscription(data) {
+  return request({
+    url: '/api/admin/subscription/cancel',
+    method: 'post',
+    data: data
+  })
+}
+
+// 续费
+export function adminRenewSubscription(data) {
+  return request({
+    url: '/api/admin/subscription/renew',
+    method: 'post',
+    data: data
+  })
+}
+```
+
+- [ ] **Step 2: 创建 `cfc-web/src/views/admin/SubscriptionManage.vue`**
+
+使用 Element UI 表格展示订阅列表(el-table),包含字段:ID / 家庭ID / 等级 / 状态 / 到期时间 / 自动续费 / 操作(取消/续费)。
+
+- [ ] **Step 3: 添加路由**
+
+在 `router/index.js` 的会员中心路由附近追加:
+
+```javascript
+{
+  path: 'subscription-manage',
+  name: 'SubscriptionManage',
+  component: () => import('@/views/admin/SubscriptionManage.vue'),
+  meta: { title: '订阅管理', perm: 'system:config' }
+}
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add cfc-web/src/api/subscription.js cfc-web/src/views/admin/SubscriptionManage.vue cfc-web/src/router/index.js
+git commit -m "feat: 管理端订阅管理页面
+SubscriptionManage.vue: 订阅列表/取消/续费
+cfc-web/src/api/subscription.js: 管理端 API 封装"
+```
+
+---
+
+### Task 6: 小程序权益页对接后端 API
+
+**Files:**
+- Modify: `cfc-frontend/pages/membership/benefits.vue`
+
+当前 `benefits.vue` 可能使用静态数据或未调用后端。修改为调用 `getMyBenefits()` 和 `getMySubscription()` 展示权益使用记录。
+
+- [ ] **Step 1: 修改 benefits.vue script**
+
+在 data 中添加 `benefitLogs: []`,`onLoad` 中调用 `getMyBenefits()`,循环渲染权益记录。
+
+```javascript
+import { getMyBenefits, getMySubscription } from '../../utils/api'
+
+export default {
+  data() {
+    return {
+      loading: true,
+      subscription: null,
+      benefitLogs: []
+    }
+  },
+  onLoad() {
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      var self = this
+      Promise.all([
+        getMySubscription(),
+        getMyBenefits()
+      ]).then(function(results) {
+        if (results[0].code === 200) {
+          self.subscription = results[0].data.subscription
+        }
+        if (results[1].code === 200 && Array.isArray(results[1].data)) {
+          self.benefitLogs = results[1].data
+        }
+        self.loading = false
+      }).catch(function() {
+        self.loading = false
+      })
+    }
+  }
+}
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add cfc-frontend/pages/membership/benefits.vue
+git commit -m "fix: benefits.vue 对接后端 /api/subscription/benefits/my
+替代静态数据,展示权益使用记录"
+```
+
+---
+
+## 执行顺序与依赖
+
+```
+Task 1 (DDL) ──→ 无依赖,可先执行
+Task 2 (API) ──→ 无依赖,可先执行
+Task 3 (plans) ──→ 依赖 Task 2 (getMembershipPlans)
+Task 4 (订阅页) ──→ 依赖 Task 2 (getMySubscription/setAutoRenew/cancelSubscription)
+Task 5 (管理端) ──→ 无依赖,但需后端 AdminSubscriptionController 已存在(✅ 已有)
+Task 6 (权益页) ──→ 依赖 Task 2 (getMyBenefits/getMySubscription)
+
+可并行执行: Task 1 + Task 2 + Task 5
+Task 3/4/6 需在 Task 2 之后
+```
+
+## 自检清单
+
+- [ ] Task 1 补充的 DDL 字段与实体类 `MembershipLevel.java` / `FamilyMembership.java` 字段一一对应
+- [ ] Task 2 的 API 封装路径与后端 `@PostMapping` 路径一致(如 `/api/membership/trial`)
+- [ ] Task 3 无硬编码价格残留
+- [ ] Task 4 的 subscription 分包已在 pages.json 注册
+- [ ] Task 5 的管理端路由在 admin layout 的 sidebar 中有入口
+- [ ] Task 6 的 `benefits.vue` 引用 `getMyBenefits` 前已确认函数存在

+ 590 - 0
docs/superpowers/plans/2026-07-28-membership-price-tier-update.md

@@ -0,0 +1,590 @@
+# 会员等级价格体系更新实施计划
+
+> **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` | 修改 | 升级提示引用后端价格 |
+
+---
+
+## Task 1: 更新数据库种子数据 + SysConfig
+
+### 现状
+`DatabaseInitializer` 中现有 sys_config 种子数据:
+```java
+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
+```
+
+### 需要修改
+1. 更新 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 消费门槛,不动)
+
+2. 添加新 sys_config 键:
+   - `member_fee_premium_monthly` = `131400`(高级会员月费 ¥1,314)
+   - `member_fee_family_desc` = `"家庭会员"`(展示名称)
+   - `member_fee_premium_desc` = `"高级家庭会员"`(展示名称)
+
+3. `MembershipService.java` 中的 `createOrder()` 里 PREMIUM 读取 `member_fee_premium` / `member_fee_premium_monthly`
+
+- [ ] **Step 1: 修改 DatabaseInitializer sys_config 种子数据**
+
+找到 `insertSysConfigSeed("member_fee_family", ...)` 相关代码,更新为:
+
+```java
+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: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+- [ ] **Step 3: 提交**
+
+```bash
+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格式"
+```
+
+---
+
+## Task 2: 扩展 MembershipService 支持 PREMIUM 等级
+
+- [ ] **Step 1: 修改 `canUseFeature()` 添加 PREMIUM 权限**
+
+在 `MembershipService.java` 的 `canUseFeature()` 方法中,在 FAMILY 判断之后添加 PREMIUM:
+
+```java
+// 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 更高折扣**
+
+```java
+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 判断后添加:
+
+```java
+} 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: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+
+- [ ] **Step 5: 提交**
+
+```bash
+git add cfc-backend/src/main/java/com/etotem/cfc/service/MembershipService.java
+git commit -m "feat: MembershipService 支持 PREMIUM 等级
+canUseFeature/applyMemberDiscount/createOrder 增加 PREMIUM 逻辑"
+```
+
+---
+
+## Task 3: 更新 MembershipController 计划列表端点
+
+- [ ] **Step 1: 确认 `/api/membership/plans` 已返回所有等级**
+
+当前代码(MembershipController.java 行 217-231)已调用 `getAllLevels()`,返回所有等级。`membership_levels` 表中有 FREE/FAMILY/PREMIUM(需确认后台种子数据已插入)。无需修改代码,但需确认 DB 有数据。
+
+也可以通过 `/api/membership/levels` 端点验证,同样返回所有等级。
+
+**如果需要额外返回价格说明文本**(如突出"推荐"标记),可扩展 `MembershipController.getPricingPlans()`:
+
+```java
+plan.put("recommended", "FAMILY".equals(level.getLevelCode()));  // 推荐标记
+plan.put("levelDesc", level.getLevelDesc());
+```
+
+- [ ] **Step 2: 提交(如需改动)**
+
+```bash
+git add cfc-backend/.../controller/MembershipController.java
+git commit -m "feat: /api/membership/plans 增加推荐标记和等级描述"
+```
+
+---
+
+## Task 4: 更新小程序 plans.vue 展示三个等级
+
+**现状:** `plans.vue` 硬编码 L0(注册会员¥0)、L1(❤️ 一生一世¥1,314/年)、L2(💎 久久一生¥1,314/月),价格和功能均为静态数据。
+
+**改为:** 从后端 `/api/membership/plans` 加载三个等级,动态渲染。
+
+- [ ] **Step 1: 修改 `<template>` 使用 v-for 渲染等级卡片**
+
+移除硬编码的三张卡片,改为:
+
+```html
+<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 和方法**
+
+```javascript
+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: 提交**
+
+```bash
+git add cfc-frontend/pages/membership/plans.vue
+git commit -m "feat: plans.vue 动态加载三个会员等级
+移除硬编码价格,从后端 /api/membership/plans 获取 FREE/FAMILY/PREMIUM 数据"
+```
+
+---
+
+## Task 5: 更新 upgrade.vue 展示 FAMILY + PREMIUM 选项
+
+**现状:** `upgrade.vue` 只有家庭会员(FAMILY)一个付费选项,显示档位选择(月/季/年)。
+
+**改为:** 添加高级家庭会员(PREMIUM)选项卡,用户可在两个付费方案间切换。
+
+- [ ] **Step 1: 添加等级切换 tabs**
+
+在 template 的 upgrade-card 区域添加等级选择 tabs:
+
+```html
+<!-- 等级选择 -->
+<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**
+
+```javascript
+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)**
+
+```html
+<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 更新:
+```javascript
+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: 提交**
+
+```bash
+git add cfc-frontend/pages/membership/upgrade.vue
+git commit -m "feat: upgrade.vue 展示 FAMILY + PREMIUM 两个付费选项
+等级切换 tabs、动态价格、权益对比三列"
+```
+
+---
+
+## Task 6: 更新 membership/index.vue 兼容 PREMIUM
+
+**现状:** `index.vue` 已从后端动态加载等级列表,但对比表只有两列(免费 vs 家庭会员),`isFamilyMember` 判断当前等级。
+
+- [ ] **Step 1: 修改 `isFamilyMember` 兼容 PREMIUM**
+
+```javascript
+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 字段。
+
+- [ ] **Step 3: 等级卡片列表兼容 PREMIUM**
+
+当前 `v-for="level in levels"` 已动态渲染所有等级,无需改动模板。但按钮文字需要适配:
+- `level.levelCode === 'FAMILY' ? '开通' : level.levelCode === 'PREMIUM' ? '升级' : ''`
+
+```html
+<button class="btn-level-action" 
+    v-if="level.levelCode !== currentLevelCode && level.levelCode !== 'FREE'" 
+    @click="handlePurchase(level)">
+    {{ level.levelCode === 'FAMILY' ? '开通' : '升级' }}
+</button>
+```
+
+- [ ] **Step 4: 提交**
+
+```bash
+git add cfc-frontend/pages/membership/index.vue
+git commit -m "feat: membership/index.vue 兼容 PREMIUM 等级显示
+对比表三列, isMember 判断, 等级卡片列表"
+```
+
+---
+
+## Task 7: 商城页面升级提示通用化
+
+**现状:** 商城页面有"开通家庭会员"的升级提示(shop/index、checkout、cart、payment)。文案写死了"家庭会员"。
+
+**改为:** 文案改为"开通付费会员"或从配置读取最低付费等级名称和价格。
+
+- [ ] **Step 1: shop/index/index.vue**
+
+```html
+<!-- 修改前 -->
+<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`:
+```javascript
+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 (同样修改)**
+
+```html
+<!-- 修改前 -->
+<text class="member-upgrade-text">开通家庭会员享全场商品会员专享价</text>
+<!-- 修改后 -->
+<text class="member-upgrade-text">开通付费会员享全场商品会员专享价</text>
+```
+
+- [ ] **Step 3: shop/cart/cart.vue (同样修改)**
+
+```html
+<!-- 修改前 -->
+<text class="member-hint-text">开通家庭会员享全场商品折扣</text>
+<!-- 修改后 -->
+<text class="member-hint-text">开通付费会员享全场商品折扣</text>
+```
+
+- [ ] **Step 4: shop/payment/payment.vue**
+
+```html
+<!-- 修改前 -->
+<text class="upgrade-card-title">支付成功!开通家庭会员更划算</text>
+<!-- 修改后 -->
+<text class="upgrade-card-title">支付成功!开通付费会员更划算</text>
+```
+
+同时修改 `familyYearlyPrice` 从后端获取:
+```javascript
+// 现有 this.familyYearlyPrice 从 /api/membership/levels 获取,确认已实现
+```
+
+- [ ] **Step 5: 提交**
+
+```bash
+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: 商城升级提示文案通用化
+'开通家庭会员'改为'开通付费会员',价格从后端动态获取"
+```
+
+---
+
+## Task 8: 管理端会员价格配置页面(可选)
+
+如果现有 `cfc-web/src/views/admin/SysConfig.vue` 可以编辑 sys_config,则不需要新建页面。确认后补充。
+
+- [ ] **Step 1: 检查 SysConfig.vue 是否支持编辑 sys_config**
+
+```bash
+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 正确价格
+- [ ] sys_config 中所有价格键值以分(cents)为单位统一
+- [ ] `MembershipService.canUseFeature()` 涵盖 PREMIUM
+- [ ] `MembershipService.applyMemberDiscount()` 区分 FAMILY(9折)/PREMIUM(8折)
+- [ ] `MembershipService.createOrder()` 处理 PREMIUM 月付/年付
+- [ ] `plans.vue` 无硬编码价格残留
+- [ ] `upgrade.vue` 可在 FAMILY/PREMIUM 间切换
+- [ ] `index.vue` 对比表三列
+- [ ] 商城页面文案通用化
+- [ ] `mvn clean compile` 通过