# 会员管理系统修复实施计划 > **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 ``` - [ ] **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` 前已确认函数存在