# 9.9 元体验会员(BASIC)权限:当前实现 vs 设计差异分析 > 日期:2026-10-23 > 设计依据:`docs/superpowers/specs/2026-09-10-basic-membership-design.md`(PRD v1.0)、`docs/superpowers/specs/2026-10-11-restore-basic-and-diet-share-design.md`(恢复规格) > 实现依据:`schema.sql`(5851-5867)、`DatabaseInitializer`(迁移 351/352/360/381)、`MembershipService`、`BasicQuotaService`、`ActivationQuotaService`、`AiGateway`、`AssessmentAppointmentService`、`HealthReportController`、`DietRecordController`、`BasicQuotaScheduledTasks`、前端 `cfc-frontend` --- ## 一、结论(TL;DR) 1. 9.9 会员 = **BASIC「体验会员」**,月付 9.9 元(`price_monthly=990`),仅月付。当前实现与两份设计规格**整体一致,但存在 1 个 P0 级功能缺陷 + 8 处口径/完整性差异**。 2. **P0:AI 对话配额没有按会员等级分流**。`AiGateway.chat()` 对所有登录用户无差别扣 `AI_CHAT` 配额(每月 3 次): - FREE 用户实际白得 3 次/月(设计为 0 次;仅前端有入口门禁,后端无门禁); - **FAMILY/PREMIUM 付费用户第 4 次起被拦截**(设计为不限次,权益对比表也承诺"不限次数")——直接损害 131.4 元/月付费用户体验; - 附带 `/nutrition/send` 配额耗尽时 NPE(`resp` 未判空)。 3. P1 三处:配额耗尽提示不回传用户(软引导文案只进了日志);健康报告上传 2 份/月配额被迁移 352 静默删除(实际变不限次,两份规格均未记录);BASIC 到期提醒定时任务遍历空列表(`listActiveBasicMemberUserIds()` 仍返回空列表)。 4. 其余为 P2 完整性问题(开通不初始化配额、FOOD_RECOGNIZE 状态查询未暴露、权益条目缺失 2 项、旧文档口径冲突)。 --- ## 二、9.9 会员当前实现的权限清单(事实源) ### 2.1 等级定义 | 字段 | 值 | 说明 | |------|-----|------| | `level_code` | `BASIC` | schema.sql:5854 / 迁移 360 | | `level_name` | `体验会员` | 前端会员页/权益页/开通页均按此展示 | | `price_monthly` | `990`(分) | 9.9 元;季付/年付为 NULL(仅月付,符合 PRD) | | `max_children` | `3` | FREE=0 / FAMILY=10 | | `max_tasks_per_day` | `3` | | | `level_order` | `2` | FREE=1 < BASIC=2 < FAMILY=3 < PREMIUM=4 < LIFETIME=5;PROVIDER=1(非会员,迁移 351/381) | | 到期判断 | `level=="BASIC"` 且 `memberExpireTime > now` | `BasicQuotaService.isActiveBasicMember()` | ### 2.2 功能开关(features,schema 与 `canUseFeature` 硬编码一致,共 11 项) | feature | 权益 | |---------|------| | `free_activities` | 免费活动 | | `free_courses` | 免费课程 | | `discount_purchase` | 优惠价购买(⚠ 无生效折扣配置,见观察 O1) | | `invite_family` | 邀请家人 | | `mascot_ai_lite` | AI 健康顾问 3 次/月 | | `ai_report_reading_lite` | 报告 AI 解读 1 次/月 | | `assessment_quota_lite` | 测评预约 1 次/月(硬门禁) | | `health_report_upload_lite` | 健康报告上传(配额已被移除 → **实际不限次**) | | `growth_plan_lite` | 基础成长计划 | | `task_template_lite` | 更多任务模板 | | `referral_commission` | 推荐佣金(2026-10-11 恢复规格新增) | ### 2.3 月度配额(`BasicQuotaService.QUOTA_LIMITS`,每月 1 号 00:00 定时刷新) | 配额类型 | 上限 | 超限行为(现状) | 集成点 | |---------|------|----------------|--------| | `AI_CHAT` | 3 次 | 软引导(当前只记日志,见 D1/D2) | `AiGateway.chat()`:184 | | `REPORT_READING` | 1 次 | 静默 return(报告上传成功但永不解读,见 O2) | `HealthReportController.triggerAiAnalysis()`:2755 | | `ASSESSMENT_QUOTA` | 1 次 | 硬门禁,抛 `BasicQuotaExhaustedException` | `AssessmentAppointmentService.createAppointment()`:34 | | `FOOD_RECOGNIZE` | 3 次 | FREE 拒绝 / BASIC 扣配额 / FAMILY+ 不限,403+toast | `DietRecordController.recognizeFood()`:26 | ### 2.4 周边链路现状 - **购买**:`/plans` 返回 BASIC(990/月);前端 `pages/membership/pay?levelCode=BASIC&period=monthly`;支付回调 → `activateMembership()` 写 `family_memberships` BASIC 记录并全家同步等级(level-agnostic,BASIC 可用)。 - **到期**:每天 4:00 `downgradeExpiredMembers()` 按 `family_memberships.end_date` 过期 → 全家降 FREE(不区分等级,BASIC 生效)。 - **前端**:AI 对话页展示配额剩余 + `UpgradeGuideModal` 软引导(PRD 符合);入口门禁 `getMyMembership().memberLevel !== 'FREE'`。 - **管理端**:`AdminBasicQuotaController` 可手动重置配额。 --- ## 三、设计 vs 实现差异对照表 | # | 差异项 | 设计要求(PRD/恢复规格) | 当前实现 | 严重度 | |---|--------|------------------------|----------|--------| | D1 | AI 对话配额等级分流 | 仅 BASIC 3 次/月;FREE 无 AI;FAMILY+ 不限 | 所有用户无差别扣配额:FREE 实得 3 次/月;FAMILY+ 第 4 次起被拦;`/nutrition/send` NPE | **P0** | | D2 | 配额耗尽软引导 | Toast/弹窗"本月额度已用完(3/3),升级 FAMILY 享不限次" | 后端仅 `log.info`;`/chat/send` 返回空 answer;文案不回传用户 | P1 | | D3 | 健康报告上传配额 | PRD:2 份/月 + `REPORT_UPLOAD_LITE` 权益(共 5 条 benefits) | 迁移 352 删除该权益与配额记录;features 仍含 `health_report_upload_lite` → 实际不限次;两份规格均未记录此变更 | P1 | | D4 | BASIC 到期提醒 | 恢复规格:`listActiveBasicMemberUserIds()` 恢复真实查询;PRD:到期前 1-5 天推送 | 仍返回 `Collections.emptyList()`(注释还写"已废弃")→ 定时任务永远空转;微信模板消息推送仍是 TODO | P1 | | D5 | 开通时初始化配额 | 恢复规格:`activateMembership` 开通 BASIC 时调 `activateBasicQuota` | 无任何调用方(死代码);靠 `consumeQuota` 懒插入兜底,功能可用但与规格不符 | P2 | | D6 | `ActivationQuotaService` 配额表 | 应含 `FOOD_RECOGNIZE` 3 次/月 | 只有 3 类(缺 `FOOD_RECOGNIZE`),与 `BasicQuotaService` 不一致 | P2 | | D7 | 食材识别进度可见 | PRD §6:显示"本月还可识别 X 次" | `/basic-quota/status/all` 只返回 3 类,不含 `FOOD_RECOGNIZE`;前端无展示 | P2 | | D8 | BASIC 权益条目 | benefits 应覆盖全部 Lite 权益 | 仅 4 条(无 `FOOD_RECOGNIZE_LITE`、无 `REPORT_UPLOAD_LITE`),权益页看不到"食材识别 3 次/月"说明 | P2 | | D9 | 旧文档口径冲突 | — | `docs/function-analysis-by-membership.md` 仍写 BASIC=家庭会员(不限量为主)、等级映射为 PROFESSIONAL/ENTERPRISE,与实现(体验会员限次、FREE/BASIC/FAMILY/PREMIUM/LIFETIME/PROVIDER)冲突 | P2(文档) | ### 观察项(非差异,需产品确认) - **O1**:BASIC features 含 `discount_purchase`,但无生效折扣配置(`member_discount_family` 仅 FAMILY 生效),对比表显示 BASIC 商品折扣"—"。声明与效果不符,建议从 BASIC features 移除或补充配置。 - **O2**:`REPORT_READING` 超限后 `triggerAiAnalysis` 静默 return,用户端无任何提示(报告上传成功但永远不解读)。 --- ## 四、P0 详解(D1:AI 对话配额无等级分流) ### 4.1 代码路径 ``` AIChatController./chat/send(无会员门禁) → AIService.sendMessage → AiGateway.chat():184 if (userId != null && userId != 0L) { basicQuotaService.consumeQuota(userId, "AI_CHAT"); // 无等级判断 if (!result.isSuccess()) return null; } ``` `consumeQuota()` 内部无等级分支:`total` 恒为 3,**任何用户**(FREE/BASIC/FAMILY/PREMIUM)第 4 次起 `used >= total` → `success=false` → `chat()` 返回 null: - `/chat/send`:`answer=""`,用户看到空白回复; - `/nutrition/send`(405-407 行):`resp.getOrDefault(...)` 未判空 → **NPE → 500**。 ### 4.2 佐证"这是缺陷而非有意设计" 1. `MembershipService.getMembershipCompareData()`(1744-1746 行)向用户承诺 FAMILY/PREMIUM"不限次数"; 2. 恢复规格风险节明确写道:"不恢复 `consumeQuota` 内原有的 `isActiveBasicMember` 前置校验(**避免误伤 FREE/FAMILY 的 AI 对话**)"——现状恰好相反(FREE/FAMILY 都被限成 3 次); 3. PRD §8.1.4:仅对 `isActiveBasicMember(userId)` 消耗配额。 ### 4.3 修复建议 在 `AiGateway.chat()` 内按 `level_order` 分流(与 `consumeFoodRecognizeQuota` 同构): ```java if (userId != null && userId != 0L) { int userOrder = membershipService.getLevelOrder(membershipService.getMemberLevel(userId)); int basicOrder = membershipService.getLevelOrder("BASIC"); if (userOrder < basicOrder) { // FREE:后端补门禁拒绝 ... } else if (userOrder == basicOrder) { // BASIC:走配额 QuotaConsumeResult r = basicQuotaService.consumeQuota(userId, "AI_CHAT"); if (!r.isSuccess()) { log...; return null; } } // FAMILY+ 直接放行,不扣配额 } ``` 配套:`/nutrition/send` 补 `resp` 判空;配额耗尽文案经 `Result` 回传前端(软引导 UpgradeGuideModal 已有,只差后端传参)。 --- ## 五、待确认 1. **D1 是否按 §4.3 方案修**(P0,建议最优先); 2. **D3 报告上传**:产品意图是"引流不限次"(回写文档、去掉 features 的 `_lite` 语义)还是恢复 2 份/月配额? 3. **D4 到期提醒推送**是否排期(需接微信模板消息); 4. **O1** `discount_purchase` 是否从 BASIC features 移除。