فهرست منبع

docs: 配额设计(方案B推荐独立表) + 附录C家庭套餐用户故事补全

liaoxg 3 ماه پیش
والد
کامیت
a77c0e8401
2فایلهای تغییر یافته به همراه278 افزوده شده و 1 حذف شده
  1. 58 1
      docs/phase1-user-stories.md
  2. 220 0
      docs/quota-design.md

+ 58 - 1
docs/phase1-user-stories.md

@@ -2919,6 +2919,42 @@ dify:
 
 ### C.2 功能权限总表
 
+```
+能力              普通用户  C端¥131  家庭套餐¥686  超级¥1,314(缓)  能量师¥1,314+
+────────────────  ───────  ───────  ──────────  ──────────────  ──────────────
+生成能量盘         ✅       ✅       ✅          ✅              ✅
+查看能量盘可视化   ✅       ✅       ✅          ✅              ✅
+数字点击查看含义   ✅       ✅       ✅          ✅              ✅
+
+AI解读·主性格概要  1次/天   不限     不限        不限            不限
+AI解读·完整五区    ❌       ✅       ✅          ✅              ✅
+AI问答互动        3轮/天   不限     不限        不限            不限
+学业方向AI解读    1次/天   不限     不限        不限            不限
+
+分享图片到微信    1次/天   不限     不限        不限            不限
+导出PDF报告       ❌       ✅       ✅          ✅              ✅
+
+🔗 关系能量分析    ❌       ❌       ✅          ✅(缓)          ✅
+👫 九类关系人      ❌       ❌       ✅          ✅(缓)          ✅
+🔢 八星号码分析    ❌       ❌       ❌          ✅(缓)          ✅
+
+历史记录查看      近7天    全部    全部        全部            全部
+推广码+分销面板   ❌       ✅       ✅          ✅(缓)          ✅
+直接邀请佣金      ❌       40%      40%         40%(缓)         40%
+间接邀请积分      ❌       10积分   10积分       10积分(缓)      10积分
+团队月度分红      ❌       ❌       ❌         ❌              5%(团队销售额)
+
+申请能量师出方案  ✅       ✅       ✅          ✅              ✅
+能量师介入AI会话  ❌(被动) ❌(被动) ❌(被动)    ❌(缓/被动)     ✅(主动)
+接受人工方案付费  ✅       ✅       ✅          ✅              ✅
+能量盘批注(P2)   ❌       ✅       ✅          ✅              ✅
+客户标签分组(P2)  ❌       ❌       ❌         ❌              ✅
+
+个人资料编辑      ✅       ✅       ✅          ✅              ✅
+
+个人中心标签     "普通用户" "C端会员" "家庭套餐会员" "🏆超级会员(缓)" "能量师"
+订阅引导卡片     可见     不可见   不可见      不可见          不可见
+续费提醒         —        到期前7天  到期前7天   到期前7天        到期前7天
 ```
 能力                        普通用户       C端¥131      **超级¥1,314**   能量师¥1,314+
 ─────────────────────────  ──────────    ──────────    ──────────────  ──────────────
@@ -2988,8 +3024,29 @@ AI问答互动                    3轮/天         不限           不限
        ├─ PDF导出(不限次)
        ├─ 历史记录(全部)
        ├─ 分销面板可见
-       └─ 推广C端年费赚 40% + 5%
+       └─ 推广C端年费赚 40% 直接佣金
+```
+
+#### 场景 2.5:升级为家庭套餐(¥686)
+
 ```
+普通用户或C端用户点击"开通家庭套餐 ¥686"
+│
+├─ 微信支付 ¥686
+├─ vipType → 'family_plan', vipEndTime → +365天
+├─ 推广码自动生成(若首次付费)
+│
+└─ 解锁能力(在C端基础上增加):
+   ├─ 关系能量分析(九类关系人:夫妻/父母/子女/祖父母/孙子女/同事/朋友/上级/下级)
+   ├─ 添加关系人(不限数量)
+   ├─ 多人分析选择 + AI对话分析 + 分析记录
+   ├─ 推广任意付费产品赚 40% 直接佣金
+   └─ 间接邀请获得 10 积分/次
+```
+
+> **注意**:家庭套餐 ≠ C端补差价,是独立购买项(¥686)。非付费会员不能直接购买家庭套餐,需先成为C端会员或直接购买家庭套餐。
+
+---
 
 #### 场景 3:升级为能量师(¥1,314 / ¥1,986)
 

+ 220 - 0
docs/quota-design.md

@@ -0,0 +1,220 @@
+# user_person_quota 表结构设计
+
+> 配额体系:按不同角色控制"可咨询人数上限" + "每人每日聊天次数上限"  
+> 状态:v1.0 Draft,方案 B 推荐
+
+---
+
+## 一、业务规则回顾
+
+| 角色 | 可咨询人数 | 每人聊天次数 |
+|------|-----------|-------------|
+| 普通用户 | 3 人 | 3 次/天 |
+| C端会员 | 9 人 | 不限 |
+| 家庭套餐 | 9 人 | 不限 |
+| 超级会员 | 9 人 | 不限(暂缓) |
+| 能量师 | 不限 | 不限 |
+
+> "次数"以 API 调用 `/api/chat/send` 为准,能量盘创建本身不限次数。  
+> 数在每天 00:00 按用户本地时区(或北京时间)自动重置。
+
+---
+
+## 二、两种方案对比
+
+### 方案 A:JSON 字段(存 users 表)
+
+```sql
+ALTER TABLE users ADD COLUMN consultation_quota JSON COMMENT '咨询配额数据';
+```
+
+**JSON 结构示例:**
+
+```json
+{
+  "persons": ["张三", "李四", "王五"],
+  "dailyCounts": {
+    "2026-06-05": {"张三": 3, "李四": 1},
+    "2026-06-04": {"张三": 2}
+  },
+  "chatLimitPerDay": 3,
+  "lastResetDate": "2026-06-05"
+}
+```
+
+| 维度 | 方案 A — JSON | 方案 B — 独立表 |
+|-----|--------------|----------------|
+| 查询「已用人数」 | `JSON_LENGTH(persons)` — 快 | `SELECT COUNT(DISTINCT person_name)` — 快 |
+| 查询「今日次数」 | 需解析 JSON — 慢 | `WHERE last_chat_date = CURDATE()` — 快 |
+| 并发更新 | 整字段读写,锁冲突风险 | 单行更新,无冲突 |
+| 每日重置 | 需 JSON 重构 | `UPDATE ... SET chat_count_today = 0 WHERE last_chat_date < TODAY` |
+| 数据一致性 | 弱(应用层保证) | 强(DB 约束) |
+| 扩展性 | 差(新增字段需改 JSON schema) | 好(加字段即可) |
+| 历史分析 | 难(每日计数仅保留最近 N 天) | 易(可保留全量记录) |
+| MySQL 版本要求 | 5.7.8+(JSON 函数) | 无要求 |
+
+**结论:** 方案 A 适合快速原型,数据量小、一致性要求低的场景。  
+本项目有付费交易,配额数据直接影响用户体验,推荐方案 B。
+
+---
+
+### 方案 B:独立表 + 每日重置(推荐)
+
+```sql
+CREATE TABLE `user_consultation_quota` (
+  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
+  `user_id` BIGINT NOT NULL COMMENT '用户ID',
+  `person_name` VARCHAR(50) NOT NULL COMMENT '被咨询者姓名',
+  `chat_count_today` INT NOT NULL DEFAULT 0 COMMENT '今日已用聊天次数',
+  `last_chat_date` DATE COMMENT '最后聊天日期(用于判断是否跨天重置)',
+  `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
+  `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  UNIQUE KEY `uk_user_person` (`user_id`, `person_name`),
+  INDEX `idx_user_date` (`user_id`, `last_chat_date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+```
+
+**核心查询:**
+
+```sql
+-- 1. 查询已咨询人数(检查人数上限)
+SELECT COUNT(DISTINCT person_name) 
+FROM user_consultation_quota 
+WHERE user_id = ?
+
+-- 2. 查询某人在今日已聊天次数(检查次数上限)
+SELECT chat_count_today, last_chat_date
+FROM user_consultation_quota
+WHERE user_id = ? AND person_name = ?
+
+-- 3. 新增被咨询人(首次咨询某人)
+INSERT IGNORE INTO user_consultation_quota (user_id, person_name, last_chat_date) 
+VALUES (?, ?, CURDATE())
+
+-- 4. 聊天次数 +1(原子操作)
+UPDATE user_consultation_quota
+SET chat_count_today = chat_count_today + 1,
+    last_chat_date = CURDATE()
+WHERE user_id = ? AND person_name = ?
+  AND (last_chat_date != CURDATE() OR chat_count_today = 0)
+-- 注意:跨天时自动重置为 0 再 +1
+
+-- 5. 每日重置(定时任务 00:00)
+UPDATE user_consultation_quota
+SET chat_count_today = 0,
+    last_chat_date = CURDATE()
+WHERE last_chat_date < CURDATE()
+```
+
+**数据生命周期:**
+- 正常情况:每日 chat_count_today 归零,数据保留(历史可用于分析)
+- 建议:每月清理 30 天未访问过的 persons 记录,或保留全量用于数据挖掘
+
+---
+
+## 三、配额检查流程(后端 API 层)
+
+```
+用户调用 POST /api/chat/send
+│
+├─ 1. 查用户 vipType → 获取角色配额规则
+│   (从 sys_config 或内存缓存读取)
+│
+├─ 2. 检查人数配额
+│   ├─ SELECT COUNT(DISTINCT person_name) FROM user_consultation_quota WHERE user_id = ?
+│   ├─ IF 已用人数 >= 角色上限 → 返回 403「已达咨询人数上限」
+│   └─ IF 角色上限 = 0(不限)→ 跳过
+│
+├─ 3. 检查次数配额
+│   ├─ SELECT chat_count_today FROM user_consultation_quota 
+│   │   WHERE user_id = ? AND person_name = ?
+│   ├─ IF last_chat_date != TODAY → 视为 0 次(跨天自动重置)
+│   ├─ IF 次数 >= 角色上限 → 返回 403「今日咨询次数已用完」
+│   └─ IF 角色上限 = 0(不限)→ 跳过
+│
+├─ 4. 记录消耗
+│   ├─ INSERT IGNORE INTO user_consultation_quota (user_id, person_name, last_chat_date)
+│   └─ UPDATE user_consultation_quota SET chat_count_today = chat_count_today + 1
+│       WHERE user_id = ? AND person_name = ?
+│
+└─ 5. 继续执行业务逻辑(调用 Dify / 返回缓存结果)
+```
+
+**并发安全:** 用 `UPDATE ... WHERE` 带条件的原子更新,避免超发。  
+**幂等性:** 同一聊天消息不应重复扣配额,由消息幂等键(message_id)保证。
+
+---
+
+## 四、角色配额规则(sys_config)
+
+```sql
+INSERT INTO sys_config (`key`, `value`, `desc`, `value_type`) VALUES
+-- 可咨询人数上限(0=不限制)
+('quota.person_limit.normal', '3', '普通用户可咨询人数上限', 'number'),
+('quota.person_limit.annual', '9', 'C端会员可咨询人数上限', 'number'),
+('quota.person_limit.family', '9', '家庭套餐可咨询人数上限', 'number'),
+('quota.person_limit.super', '9', '超级会员可咨询人数上限(暂缓)', 'number'),
+('quota.person_limit.practitioner', '0', '能量师可咨询人数上限(0=不限)', 'number'),
+-- 每人每日聊天次数上限(0=不限制)
+('quota.chat_limit.normal', '3', '普通用户每人每日聊天次数上限', 'number'),
+('quota.chat_limit.annual', '0', 'C端会员每人每日聊天次数上限(0=不限)', 'number'),
+('quota.chat_limit.family', '0', '家庭套餐每人每日聊天次数上限', 'number'),
+('quota.chat_limit.super', '0', '超级会员每人每日聊天次数上限(暂缓)', 'number'),
+('quota.chat_limit.practitioner', '0', '能量师每人每日聊天次数上限', 'number'),
+-- 每日重置时间(小时,北京时间)
+('quota.reset_hour', '0', '配额每日重置小时(0=00:00)', 'number');
+```
+
+---
+
+## 五、每日重置定时任务
+
+```java
+@Scheduled(cron = "0 0 ${quota.reset_hour} * * *") // 每天 N 点执行
+public void resetDailyQuota() {
+    int resetHour = configService.getInt("quota.reset_hour", 0);
+    LocalDate today = LocalDate.now();
+    
+    // 方式1:UPDATE 批量重置(高效,适合数据量 < 100 万)
+    jdbcTemplate.update(
+        "UPDATE user_consultation_quota SET chat_count_today = 0, last_chat_date = ? " +
+        "WHERE last_chat_date < ?",
+        today, today
+    );
+    
+    // 方式2(备选):清理超过 90 天未使用的人员记录
+    jdbcTemplate.update(
+        "DELETE FROM user_consultation_quota WHERE last_chat_date < DATE_SUB(?, INTERVAL 90 DAY)",
+        today
+    );
+}
+```
+
+---
+
+## 六、与 chart_record 的关系
+
+`user_consultation_quota` 追踪"聊天次数限制",`chart_record` 存储"能量盘记录"。
+
+| 场景 | chart_record | user_consultation_quota |
+|------|-------------|------------------------|
+| 用户输入生日 → 生成能量盘 | ✅ 插入 | 不扣(能量盘创建不占配额) |
+| 用户在能量盘页发消息聊天 | ❌ 不插入 | ✅ 计数 +1 |
+| 用户首次咨询某个人 | ✅ 插入 | ✅ person_name 记录 |
+| 同一人对同一人重复咨询 | ✅ 返回历史 | ✅ chat_count_today +1 |
+
+> 人数上限以 `user_consultation_quota.person_name` 去重计数,**不依赖** chart_record,  
+> 因为用户可能在 chart_record 中注册了多人,但从未聊天。
+
+---
+
+## 七、方案选择建议
+
+| 维度 | 推荐 |
+|------|------|
+| 数据结构 | **方案 B(独立表)** |
+| 重置策略 | 每日定时 UPDATE 归零 |
+| 并发控制 | UPDATE WHERE 原子操作 |
+| 数据保留 | 保留全量,每月清理 90 天未活跃记录 |
+| sys_config | 全部参数化,支持后台动态调整 |
+| 与 chart_record | 解耦,各自承担不同职责 |