日期:2026-08-09 状态:已获用户批准(方案 A + 规则确认)
平台现有会员等级体系(FREE/FAMILY/PREMIUM/PROVIDER)为订阅制,到期需续费。现新增平台终身会员(LIFETIME):用户年度在平台消费满 100 万,或推广团队销售满 200 万,经管理员审核后升级为终身会员,永久享受 FAMILY 会员全部权益,无需再续费。
同时修正现存价格来源矛盾:getPricingPlans() 目前从 sys_config 取价,而 membership_levels 表的价格字段未被真正使用。本次将会员费用(含原价)统一迁移到 membership_levels 表(membership-center 管理端的数据源)。
LIFETIME 等级 + 实时聚合统计 + 管理员审核升级status='paid' 的已支付订单,滚动 365 天窗口referralCode 关联的下线订单总额),同样滚动 365 天membership_levels 增加原价字段;getPricingPlans() 改读此表;sys_config 旧键保留但不再引用member_fee_* 键(仅代码停止引用,避免影响其他读取方)membership_levels 表)ALTER TABLE membership_levels
ADD COLUMN original_price_monthly INT COMMENT '原价-月费(分)',
ADD COLUMN original_price_quarterly INT COMMENT '原价-季费(分)',
ADD COLUMN original_price_yearly INT COMMENT '原价-年费(分)';
ensureColumn 模式)+ schema.sql 同步original_price_yearly ← member_fee_family_original_yearly(兜底 member_fee_family_original,默认 199900)original_price_yearly ← member_fee_premium_original(默认 1599900);original_price_monthly ← member_fee_premium_original_monthly(默认 159900)price_monthly/quarterly/yearly 同步用 sys_config 现值补齐(FAMILY 年费 131400、PREMIUM 年费 1316800 等,幂等 UPDATE 仅当表内为 0/NULL 时)INSERT IGNORE INTO membership_levels
(level_code, level_name, level_desc, price_monthly, price_quarterly, price_yearly,
original_price_monthly, original_price_quarterly, original_price_yearly,
features, max_children, max_tasks_per_day, ai_review_enabled, priority_support, teacher_consultation)
VALUES
('LIFETIME', '终身会员', '终身家庭会员权益,永久有效无需续费', 0, 0, 0, 0, 0, 0,
'["free_activities","free_courses","full_price_purchase","referral_commission","discount_purchase","member_activities","create_family","invite_family","purchase_commission"]',
6, 20, 1, 1, 1);
features 与 FAMILY 一致(终身 = FAMILY 权益)getPricingPlans() 改造)MembershipController.getPricingPlans()(L317)改为统一从 membership_levels 表读取价格与显示字段,不再读 sys_config:
// 每个 level 的 plan 字段:
plan.put("levelCode", level.getLevelCode());
plan.put("levelName", level.getLevelName());
plan.put("levelDesc", level.getLevelDesc());
plan.put("recommended", "FAMILY".equals(level.getLevelCode()));
plan.put("yearly", level.getPriceYearly()); // 不再从 sys_config 读
plan.put("monthly", level.getPriceMonthly());
plan.put("quarterly", level.getPriceQuarterly());
plan.put("originalYearly", level.getOriginalPriceYearly());
plan.put("originalMonthly", level.getOriginalPriceMonthly());
plan.put("originalQuarterly", level.getOriginalPriceQuarterly());
关键约束:
getPricingPlans() 跳过 levelCode='LIFETIME' 的行(不返回给小程序购买列表)MembershipLevel entity 增加 originalPriceMonthly/Quarterly/Yearly 三个字段(对应表列)表内价格字段为 NULL/0 时,plan 返回 null(前端 showOriginalPrice() 已有 original > current*100 才展示划线的逻辑,NULL/0 自然不显示划线)。
现有逻辑已满足 LIFETIME 语义,无需修改:
| 组件 | 现有行为 | LIFETIME 效果 |
|---|---|---|
MembershipService.getMemberLevel() (L767) |
仅 "FAMILY" 且 memberExpireTime 非空才检查过期 |
LIFETIME 跳过过期检查 → 永久有效 ✅ |
MembershipService.canUseFeature() (L166) |
FREE/FAMILY/PREMIUM 分支后兜底 return true |
LIFETIME 落到兜底 → 全功能放行 ✅ |
MembershipService.hasPermission() (L228) |
查 membership_levels 表 features |
LIFETIME 行 features 同 FAMILY → 权限一致 ✅ |
getMyMembership (MembershipController L65) |
返回顶层 memberLevel |
返回 LIFETIME,前端可识别 ✅ |
// 升级目标用户 = 家庭创建者(与消费归属口径一致)
User adminUser = ...; // family.getCreatorId()
String fromLevel = adminUser.getMemberLevel() != null ? adminUser.getMemberLevel() : "FREE";
adminUser.setMemberLevel("LIFETIME");
adminUser.setMemberExpireTime(null); // NULL = 永久有效
adminUser.setUpdatedAt(new Date());
userMapper.updateById(adminUser);
// 升级记录 upgradeType='lifetime'
MemberUpgradeRecord record = new MemberUpgradeRecord();
record.setUserId(adminUserId);
record.setFromLevel(fromLevel);
record.setToLevel("LIFETIME");
record.setUpgradeType("lifetime");
record.setRemark("平台终身会员:消费达标/销售达标,管理员审核授予");
memberUpgradeRecordMapper.insert(record);
MemberUpgradeRecord.upgradeType 现有枚举 pay/consumption/admin 增加 lifetime 值(字符串字段,无 DB 约束,直接复用)。
5 张订单表 UNION 聚合,过滤 status='paid' 且 paid_at >= now-365d,按用户归属求和:
| 订单表 | 用户字段 | 金额字段 | 状态字段 |
|---|---|---|---|
product_orders |
buyer_id |
total_amount |
status='paid' |
assessment_orders |
user_id |
amount |
status='paid' |
package_orders |
user_id |
price |
status='paid' |
member_subscription_order |
family_id → 家庭创建者 |
amount |
status='paid' |
activity_orders |
user_id |
total_amount |
status='paid' |
member_subscription_order无用户字段,通过family_id关联family.creator_id归属到用户(与getFamilyMembership口径一致)。
阈值:消费 ≥ 1,000,000 元(内部 100_000_000 分)。
referralCode 关联的所有下线用户(user.referrer_id = 该用户.id,一级推广关系)200_000_000 分)管理端接口 POST /api/admin/membership/lifetime-candidates:
userId / 昵称 / 家庭 / 消费额 / 销售额 / 达标类型(consumption|sales|both)paid_at 建索引即可;低频管理操作,实时计算可接受// 查询终身会员达标候选(分页)
@PostMapping("/membership/lifetime-candidates")
public Result<Page<Map<String, Object>>> lifetimeCandidates(@RequestBody Map<String, Object> params)
// 审核授予终身会员
@PostMapping("/membership/lifetime-grant")
public Result<Void> lifetimeGrant(@RequestBody Map<String, Object> params) // { userId, remark }
lifetime-grant 校验:用户存在、未是 LIFETIME;写入 user.memberLevel='LIFETIME' + memberExpireTime=null + 升级记录(见 §4);返回成功后管理端刷新列表。
/api/admin/membership/upgrade-records 过滤 toLevel='LIFETIME' 展示(复用现有接口)lifetimeCount,按 user.memberLevel='LIFETIME' 计数)loadData() 已用 memberLevel !== 'FREE' 判断非免费 → LIFETIME 自然进入"非免费"分支,显示会员状态卡 ✅membership.memberExpireTime 为 null 时)getLevelName() map 增加 LIFETIME: '终身会员'getLevelName 有对应映射则自然生效);若有「立即续费/升级」入口需对 LIFETIME 隐藏MembershipLevel entity 含 originalPrice* 3 字段;membership_levels 表含 3 原价列 + LIFETIME 行(迁移 203/204,幂等)getPricingPlans() 从 membership_levels 读价(含原价),过滤 LIFETIME,返回结构不变lifetime-candidates 返回滚动 365 天消费额 + 推广团队销售额双指标候选,阈值正确(分单位)lifetime-grant 授予后:user.memberLevel='LIFETIME'、memberExpireTime=null、升级记录 upgradeType='lifetime'getMemberLevel() 对 LIFETIME 返回 'LIFETIME' 且不因过期降级lifetimeCount?.、:key 表达式、CSS Grid、直接 new Date(string)(遵守小程序限制)member_subscription_order 按家庭创建者归属;退款订单(status='refunded')不计入(仅统计 paid)referrer_id 直接关联的下线(现有推广体系为两级,但销售口径按用户确认=「推广团队」定义为推荐码直接下线;如后续需含二级,扩展 SQL 即可)