# Phase 1 用户故事与验收标准 > 基于 `phase1-product-plan.md` 与 `1.0产品对齐方案.md`,每个用户故事包含:标题、描述、验收标准、优先级 --- ## EPIC 1:命盘生成与展示(P0) ### US-1.1 生日输入与咨询发起 **作为** 数字能量师/C端用户 **我希望** 输入姓名和出生年月日,发起一次命盘咨询 **以便** 获取命盘展示和AI解读服务 **验收标准(输入表单):** 1. 输入页面包含:**被咨询者姓名**(选填,即命盘主人姓名)、出生年份(4位数字)、月份(1-12)、日期(1-31) 2. 月份和日期支持数字键盘输入,也有下拉选择器 3. 验证:年份必须为 1900-当前年份、月份 1-12、日期根据月份合法性验证 4. 输入无效时显示具体错误提示(如"请输入正确的出生日期") 5. "想了解的问题" 输入框限制 100 字以内(输入时实时显示剩余字数) 6. 点击"开始咨询"后,调用后端 `POST /api/consultation/start` 发起咨询 7. **已登录用户自动填入自己生日**(用户注册时已填 `birth_date`),但允许修改(能量师可输入客户生日) **验收标准(咨询会话管理):** 7. 后端按照 **userId + birthday** 唯一确定一条咨询记录(同用户+同生日始终返回同一条记录) 8. 同一用户对同一生日重复点击"开始咨询",后端直接返回已有的咨询记录 + 完整聊天历史,**不创建新记录**;前端 toast 提示"已找到您之前的咨询记录" 9. 输入的生日是**全新**生日时,前端弹出确认对话框:"此生日将开启全新的命盘咨询,确认吗?" 10. 用户确认后,后端计算完整的数字命盘(24个位置),创建新咨询记录,并返回结果 11. 用户取消确认,停留在首页,不跳转、不创建 12. 点击"开始咨询"到命盘渲染完成,总耗时不超过 3 秒 13. 命盘计算全部由后端 `CalculatorService` 完成,前端只负责展示(`calculator.js` 保留为降级兜底,不作为主路径) 14. 命盘页面 URL 可分享 ### US-1.2 三角形命盘可视化展示 **作为** 数字能量师 **我希望** 看到一个清晰、美观的数字三角形,每个位置都标有数字 **以便** 直接用于客户讲解 **验收标准:** 1. 页面展示完整的24个数字位置(8个底部输入位 A-H + 7个三角内部位 I-O + 9个外部三组位 P-X) 2. 内部三角形布局严格遵循(新字母命名): ``` O / \ M N / \ / \ I J K L ``` 3. 底部输入框布局:日(2位)| 月(2位)| 年(4位),从左到右排列(已改为"日月年"顺序) 4. 外部三组以T形线段连接三角内部对应位置: - 左侧组(R, P, Q)对应 21-40 岁,由 I, J, M 计算 - 右侧组(U, S, T)对应 61+ 岁,由 K, L, N 计算 - 顶部组(X, V, W)对应 41-60 岁,由 M, N, O 计算 5. 外部三组使用独立的计算值,非内部数字的直接重复 6. 每个数字使用单独的圆形/方形卡片展示,字体清晰易读 7. 五区使用不同背景色区分(左区/中区/右区/父源/母源/主性格) 8. 数字颜色:1-9 每个数字有独立颜色(参考能量数字配色) 9. 卓越数(11/22/33)用特殊标记突出 10. 底部显示姓名和出生日期信息 ### US-1.3 命盘数字位置命名与计算规则(对照表) **作为** 开发团队 **我希望** 有一份完整的位置命名与计算规则对照表作为开发基准 **以便** 前后端计算逻辑一致,避免歧义 #### 1. 完整字母命名表(A-X) ##### 输入层(8位)—— 生日原始数字 | 字母 | 来源 | 示例 1990-01-15 | 说明 | |------|------|----------------|------| | A | 年份千位 | 1 | 年[0] | | B | 年份百位 | 9 | 年[1] | | C | 年份十位 | 9 | 年[2] | | D | 年份个位 | 0 | 年[3] | | E | 月份十位 | 0 | 月[0] | | F | 月份个位 | 1 | 月[1] | | G | 日期十位 | 1 | 日[0] | | H | 日期个位 | 5 | 日[1] | ##### 内部计算层(7位) | 字母 | 公式 | 中文名 | 旧名对照 | 视觉层级 | |------|------|-------|---------|---------| | I | `reduce(E+F)` | 月能量 | 旧 F | 底部左1 | | J | `reduce(G+H)` | 日能量 | 旧 G | 底部左2 | | K | `reduce(A+B)` | 年前半 | 旧 H | 底部右1 | | L | `reduce(C+D)` | 年后半 | 旧 I | 底部右2 | | M | `reduce(I+J)` | 青年综合数 | 不变 | 中层左 | | N | `reduce(K+L)` | 晚年综合数 | 不变 | 中层右 | | O | `reduce(M+N)` | **主性格数** | 不变 | 顶层 | ##### 外部三组(9位) | 字母 | 公式 | 中文名 | 年龄区间 | 所属组 | |------|------|-------|---------|-------| | P | `reduce(I+M)` | 左侧左子(left-L) | 21-40 | 左侧 | | Q | `reduce(J+M)` | 左侧右子(left-R) | 21-40 | 左侧 | | R | `reduce(P+Q)` | **左侧主数(left)** | 21-40 | 左侧 | | S | `reduce(K+N)` | 右侧左子(right-L) | 61+ | 右侧 | | T | `reduce(L+N)` | 右侧右子(right-R) | 61+ | 右侧 | | U | `reduce(S+T)` | **右侧主数(right)** | 61+ | 右侧 | | V | `reduce(M+O)` | 顶部左子(top-L) | 41-60 | 顶部 | | W | `reduce(N+O)` | 顶部右子(top-R) | 41-60 | 顶部 | | X | `reduce(V+W)` | **顶部主数(top)** | 41-60 | 顶部 | #### 2. 视觉布局示意 ``` X ↙ ↘ V W ↙ ↘ M ───────────── N ↙ ↘ ↙ ↘ P Q S T ↙ ↘ ↙ ↘ I ─────────── J K ─────────── L R (21-40) U (61+) 底部输入: 日 G H | 月 E F | 年 A B C D ``` #### 3. 外部三组年龄映射 | 组 | 位置 | 年龄区间 | 关联内部位 | |----|------|---------|----------| | 左侧组 (R, P, Q) | 三角形左外侧 | **21-40岁** | 基于 I, J, M | | 顶部组 (X, V, W) | 三角形顶外侧 | **41-60岁** | 基于 M, N, O | | 右侧组 (U, S, T) | 三角形右外侧 | **61+岁** | 基于 K, L, N | #### 4. 计算精度说明 - `reduce(N)` 定义:各位数字相加归约至个位,但 11/22/33 保留为卓越数 - 所有 `+` 操作符均先执行普通加法,再调用 `reduce()` 归约 - 外部三组**必须使用独立的归约计算**,不得直接引用内部数字值 #### 5. 后端 API 返回结构(`POST /api/consultation/start` 响应示例) ```json { "recordId": 42, "isNew": false, "chartData": { "positions": { "I": 1, "J": 2, "K": 3, "L": 4, "M": 5, "N": 6, "O": 7, "P": 8, "Q": 9, "R": 1, "S": 2, "T": 3, "U": 4, "V": 5, "W": 6, "X": 7 }, "mainCharacter": 7, "isMasterNumber": false }, "messages": [] } ``` --- ### US-1.4 数字点击查看含义 **作为** 数字能量师 **我希望** 点击三角形中的每个数字可以查看该位置的含义和能量解读 **以便** 快速向客户解释命盘 **验收标准(交互):** 1. 点击三角形中任一数字卡片(A-X 任一位置),从屏幕底部弹出该位置的详细面板 2. 点击效果分为两层: - 轻点数字卡片 → 弹出解释面板 - 面板弹出时,数字卡片高亮状态(边框发光 + 放大 1.1 倍) 3. 打开新面板时自动关闭前一个面板 4. 关闭方式:点击面板外部遮罩区 / 点击关闭图标 / 再次点击同一数字 5. 关闭面板后高亮状态取消 **验收标准(面板内容):** 6. 面板标题区域显示位置名称(如"主性格")、字母编号(如 O)、对应数字 7. 面板内容按 Tab 切换展示: - **Tab 1 - 含义**:该位置的核心能量含义(如"主性格:代表一个人与生俱来的天赋和性格特质") - **Tab 2 - 特征**:该数字在该位置的具体性格/运势描述(如"O=7 代表分析力强、追求真理") - **Tab 3 - 建议**:该位置该数字的注意事项和提升建议 8. 面板底部显示数字的能量强弱 / 吉凶指示(如"吉数 ★★★★☆") 9. 若该位置数字与相邻位置形成特定组合,面板顶部提示"该数字与 X 形成 XX 组合",可点击查看组合含义 10. 面板内容中的"组合"和"特征"描述,后续可通过管理后台配置 **验收标准(交互细节):** 11. 面板高度不超过屏幕 60%,内容可滚动 12. 面板弹出动画:从底部平滑滑入(300ms ease-out) 13. 连续快速点击不同数字时,面板内容直接替换,不重复弹入动画 14. 面板支持手势下滑关闭(drag-to-dismiss) --- ## EPIC 2:分享与导出(P0) ### US-2.1 命盘分享图片 **作为** 数字能量师 **我希望** 将命盘生成为一张精美的图片,分享到微信或保存到相册 **以便** 发给客户或在朋友圈展示 **验收标准(入口与权限):** 1. 分享按钮位于命盘展示页底部,始终可见 2. 点击分享按钮,底部弹出分享方式选择菜单: - "保存到相册" - "分享给微信好友" - "分享到朋友圈" 3. 免费用户每日限制分享 1 次,点击后显示剩余次数(如"今日还剩 1 次") 4. 免费用户用完今日次数后,按钮置灰,提示"升级能量师享无限次分享" 5. 已付费用户不限次数,不显示剩余次数 **验收标准(分享卡片生成):** 6. 分享卡片在客户端生成(使用 canvas 绘图),不依赖后端 7. 卡片内容包含: - 顶部:品牌 Logo + "数字能量命理分析" 标题 - 中部:三角命盘图(颜色和样式与 app 内一致) - 底部:用户姓名 + 出生日期 + 生成日期 - 右下角:小字水印(平台名称/二维码) 8. 卡片设计使用样式指南中的品牌色系统(主色 #B8860B / 金色系) 9. 横向卡片比例 4:3,宽度适配主流手机屏幕(建议 1080px 基准) **验收标准(保存与分享):** 10. 保存到相册:调用 `uni.saveImageToPhotosAlbum`,保存成功后 toast 提示"已保存到相册" 11. 分享给微信好友:调用 `uni.share`(或小程序原生转发),携带卡片图片和默认文案"看看你的数字能量命盘" 12. 分享到朋友圈:调用小程序端朋友圈分享 API(走官方渠道) 13. 分享后返回 app 时,若为免费用户则扣除当日次数,显示更新后的剩余次数 **验收标准(限制与风控):** 14. 免费用户每日分享**到微信/朋友圈**的次数在服务端记录,防止客户端篡改 15. 分享次数限制仅对"分享到微信/朋友圈"生效;**保存到相册**不限制次数(客户端canvas生成无法强控) 16. 每日 0 点重置次数,后端接口 `POST /api/user/share-quota` 返回当日剩余次数 17. 分享卡片不得包含用户微信号、手机号等敏感信息 ### US-2.2 命盘PDF导出 **作为** 数字能量师 **我希望** 将命盘导出为 PDF 文件,可以直接打印或微信发送 **以便** 客户获得正式的纸质/电子版报告 **验收标准(权限与入口):** 1. "导出 PDF" 按钮位于命盘展示页顶部操作栏(仅已付费用户可见) 2. 未付费用户点击不可见或置灰 + 提示"升级能量师可导出 PDF 报告" 3. 点击后出现加载指示器(loading + 进度百分比),防止重复点击 4. 生成过程不超过 5 秒,超过 5 秒则显示"生成较慢,请稍候…" **验收标准(PDF 内容与排版):** 5. PDF 采用 A4 竖版(210mm × 297mm),页边距上下 15mm、左右 12mm 6. 第一页内容按以下布局: - 头部:品牌 Logo + "数字能量命理分析报告" 标题(居中) - 副标题:生成日期 + 报告编号(格式:YYYYMMDD-XXXXX) - 用户信息区:姓名 / 出生日期 / 主性格数字 - 三角命盘图:占页面 40% 高度,清晰可辨各位置数字 - 表格区:24 个位置的字母编号、数字值、位置名称三列展示 - 左中右三区标注:左侧(21-40 岁)/ 顶部(41-60 岁)/ 右侧(61+ 岁) 7. PDF 使用品牌色系(金色 #B8860B 为标题色,深棕色 #4A3728 为正文字体) 8. PDF 支持中文显示,字体嵌入(或使用系统黑体/宋体) 9. 如有命盘批注内容(US-7.1),在 PDF 第二页以附录形式呈现 **验收标准(导出流程):** 10. PDF 由后端生成(通过 iText 或 Apache PDFBox 库),前端仅发起请求 11. 前端调用 `POST /api/export/pdf` 传入 `chartId`,后端返回 PDF 文件流 12. 前端接收文件流后,使用 `uni.openDocument` 打开微信文件预览 13. 微信文件预览界面支持:转发给好友 / 保存到本地 / 发送到电脑 14. 导出记录保存在数据库 `export_logs` 表,便于统计 **验收标准(限制):** 15. 已付费用户导出 PDF **不限制次数** 16. 同一命盘重复导出不重新生成,直接返回已缓存的文件(缓存有效期 7 天) --- ## EPIC 3:AI 解读(P0) ### US-3.1 AI 解读展示 **作为** 数字能量师 **我希望** 在命盘生成后,看到由AI自动生成的完整文字解读 **以便** 直接发给客户或稍作润色后使用,节省自己查资料写解读的时间 **验收标准:** 1. 命盘展示页底部有"查看AI解读"按钮,点击跳转到AI解读页 2. 解读内容通过 **Dify Workflow**(工作流)生成,技术架构详见**附录B** 3. 前端调用后端 `POST /api/chart/interpret`,后端转发至 Dify Workflow API(`POST /v1/workflows/run`) 4. 首次加载等待时间 < 10 秒(含 Dify Workflow 执行时间);超过 8 秒显示"AI 正在深度分析中,请稍候…" 5. 加载过程中显示骨架屏或loading动画,避免用户以为卡死 6. 解读内容至少包含以下章节: - **主性格解读**:顶端数字的核心特质、性格描述、代表人物 - **左区(21-40岁)**:早年运势、成长环境(对应 P/Q/R 位置) - **顶部(41-60岁)**:中年事业、人际关系(对应 V/W/X 位置) - **右区(61+岁)**:晚年成就、财运趋势(对应 S/T/U 位置) 7. 每个章节独立卡片展示,可折叠展开 8. 解读内容基于命盘的实际数字,同一数字对不同命盘解读不同(位置差异) 9. 页面底部显示"本解读由AI生成,仅供参考"的免责声明 10. Dify Workflow 输出解析:后端收到 Dify 返回的 `data.outputs` JSON 后,原样或稍作格式化后返回前端 ### US-3.2 AI 解读的免费/付费控制 **作为** 平台运营者 **我希望** 控制AI解读功能的免费与付费边界 **以便** 激励用户订阅C端年费或能量师年费 **验收标准:** 1. 未付费用户点击"查看AI解读"时,仅显示**主性格概要**(1段文字) 2. 未付费用户每日可查看主性格概要 1 次(**此配额与 US-3.4 的 3 轮 AI 问答独立计数,互不消耗**) 3. 五区完整解读仅在已付费后可用(C端¥131或能量师¥1,314+) 4. 解读页底部显示"¥131 开通完整解读"引导卡片(免费用户可见) 5. 已付费用户可无限次查看完整解读 6. 每次解读请求记录到数据库,用于成本控制和限流 ### US-3.4 AI 交互问答(免费/付费控制) **作为** 平台运营者 **我希望** 控制AI问答互动功能的免费次数,且限定在同一个命盘上 **以便** 防止用户通过切换生日绕过每日限制,同时保证体验清晰可预期 **验收标准(配额控制):** 1. 未付费用户每日可进行 3 轮 AI 问答互动(每日配额全局统一,不按命盘拆分) 2. 此 3 轮互动**绑定到当前咨询的命盘上**,用户不能通过创建多个生日命盘来获得额外免费次数 3. 已付费用户不受次数限制 4. 用户在命盘页顶部看到剩余次数提示:"今日 {{usedChats}}/{{maxChats}} 轮" 5. 当用户在已用满3次的命盘上新建**另一个生日**的咨询时: - 前端弹出确认对话框 - 对话框提示:"此生日将开启全新的命盘咨询,今日剩余互动次数仍为 0 次,确定吗?" - 用户确认后创建新咨询,次数不重置 6. 超出每日次数后,AI 输入框和快捷问题按钮隐藏,显示引导卡片:"今日 AI 解读次数已用完,开通会员享无限次" 7. 每日次数在 `lastQuotaDate` 跨日时自动归零 **验收标准(Dify Chatflow 集成):** 8. 问答交互通过 **Dify Chatflow**(对话工作流)实现,详见**附录B** 9. 前端调用 `POST /api/chat/send` 发送用户消息,传入 `{ chartId, message }` 10. 后端 `ChatService` 将消息转发至 Dify Chatflow API(`POST /v1/chat-messages`) 11. Chatflow 保持多轮对话上下文(通过 Dify 的 `conversation_id`),无需后端自行维护会话历史 12. 后端在首次向 Dify 发送消息时,将命盘 24 个数字(A-X)作为 Chatflow 的 `inputs` 传入,后续轮次无需重复传入 13. Dify Chatflow 返回的答案中,如果包含数字能量学专业术语,后端不做二次处理,直接透传 14. 后端每次问答调用前检查用户配额(未付费用户当日≤3轮),超限则不调用 Dify,直接返回错误码 ### US-3.3 解读内容缓存 **作为** 系统 **我希望** 同一命盘的重复解读请求不重复调用LLM API **以便** 控制成本,避免同一命盘每次查看都消耗API费用 **验收标准:** 1. 首次生成解读后,将解读内容与命盘ID绑定存储到数据库 2. 后续同一命盘再次查看解读时,直接从数据库读取,不调用LLM 3. 用户可在AI解读页右上角菜单中点击"重新生成解读",覆盖旧的缓存内容(重新调用 Dify Workflow) 4. 解读缓存永久保留,不自动过期 5. 重新生成时弹出确认框:"重新生成将覆盖已有解读内容,确定吗?" --- ## EPIC 4:付费与订阅(P0) ### US-4.1 能量师付费订阅 **作为** 数字能量师 **我希望** 看到清晰的定价方案并进行年费支付 **以便** 获得AI完整解读、分销推广等全部功能 **验收标准:** 1. 付费入口:未付费用户在首页/命盘页/解读页/分享时均会看到升级引导 2. 付费页展示: - 功能对比表(普通用户 vs 能量师) - 价格:种子价 ¥1,314 / 标准价 ¥1,986(通过 `POST /api/pricing/current` 获取) - 种子价提示:根据 `seedReason` 展示不同文案 - `eligible`:"仅剩 XX 个种子名额" + "有效期至 YYYY-MM-DD" - `not_founder_code` / `no_referrer`:"种子价仅限创始人邀请用户" - `expired`:"种子价活动已结束" - `quota_full`:"种子名额已满" - "立即开通"支付按钮 3. 通过微信支付完成订阅 4. 支付成功后,用户状态即时更新(`vipEndTime += 365天`) 5. 支付成功后,自动生成专属6位推广码(如无已有) 6. 支付成功后,自动跳转到分销面板(或引导页) 7. 支付失败显示友好提示 ### US-4.2 种子价自动判断 **作为** 平台 **我希望** 系统自动判断用户是否享受种子价 **以便** 运营策略自动化,无需人工干预 **种子会员定义:** 种子会员 = 通过扫描**创始人码**注册并付费成为能量师的用户。创始人码 = **没有推荐人的能量师**(`invitedBy IS NULL` 且 `vipType='practitioner'`)的推广码。 > 即:用户 U 的直接推荐人 A 满足 `A.invitedBy IS NULL AND A.vipType='practitioner'` 时,U 扫的是创始人码。 **验收标准:** 1. 用户在支付页看到的价格由后端接口 `POST /api/pricing/current` 返回,前端不做判断 2. `isSeedPrice = true` 需**同时满足三重条件**: - **条件① 注册渠道**:用户的直接推荐人(`invitedBy`)是创始人(`invitedBy IS NULL AND vipType='practitioner'`)的自然注册用户(无推荐人,`invitedBy IS NULL`)**不享受种子价** - **条件② 有效期**:当前时间 ≤ `sys_config.pricing.practitioner.seed_period_end`(种子价有效期截止时间) - **条件③ 名额**:已通过创始人码付费的能量师记录数 < `sys_config.pricing.practitioner.seed_limit`(默认 300) 3. 三个条件任一不满足 → `isSeedPrice = false`,返回标准价 4. 种子价名额计数方式:统计 `orders` 表中 `product_type='practitioner'` AND `status='paid'` AND `is_seed_price=true` 的记录数 5. 订单创建时将种子价状态锁定(`is_seed_price` 写入订单,不因后续条件变化而改变) 6. 有效期过期后,即使名额未满,所有新用户看到标准价 7. 名额满后,即使仍在有效期内,所有新用户看到标准价 8. 种子价名额上限和有效期均可在管理后台动态修改(`pricing.practitioner.seed_limit`、`pricing.practitioner.seed_period_end`) ### US-4.3 订阅状态与续费 **作为** 付费能量师 **我希望** 在个人中心看到我的订阅状态、到期日,并能续费 **以便** 管理我的会员身份 **验收标准:** 1. 个人中心显示:订阅状态(能量师/C端会员/已过期/普通用户) 2. 显示到期日期,到期前 7 天显示续费提醒 3. 到期后自动降级为普通用户,分销面板入口隐藏 4. 续费操作按**标准价**执行(种子价仅限首次,不限能量师或C端) 5. 续费成功后有效期在原到期日基础上延长1年 ### US-4.4 年费升级能量师(升级定价与流程) **作为** C端年费用户 **我希望** 从年费升级为能量师时,按已付年费的剩余价值抵扣差价 **以便** 不需要重复支付已经买过的部分 **验收标准(升级定价):** 1. 用户现有 `vipType='annual'` 且 `vipEndTime > now`,可在个人中心点击"升级能量师" 2. 升级价格由后端计算,公式: ``` 已付年费金额 = 该用户最近一笔 annual 订单的 amount(默认 ¥131) 已用天数 = 从 annual 支付成功日到当前日的天数 剩余价值 = 已付年费金额 × (365 - 已用天数) / 365 升级价格 = 当前能量师定价(种子/标准)− 剩余价值 ``` 3. 计算示例:用户 B 在 1月1日付 ¥131,第100天升级(种子价 ¥1,314): ``` 剩余价值 = ¥131 × (365-100)/365 = ¥95.11 升级价格 = ¥1,314 − ¥95.11 = ¥1,218.89 ``` 4. 升级价格 < 0(理论极限)→ 按 ¥0.01 收取 5. 升级后 `vipType` 从 `'annual'` 变更为 `'practitioner'` 6. 升级后 `vipEndTime` 按能量师续费规则处理(在原能量师到期日 +365天,不叠加年费剩余天数) 7. 升级不享受种子价(种子价仅限通过创始人码首次购买 practitioner,见 US-4.2) 8. 升级后推广码不变(如之前已有推广码) **验收标准(前端流程):** 9. 个人中心 → 升级能量师 → 展示升级详情卡片: - 原年费支付金额:¥131 - 年费剩余价值:¥95.11("已使用 100/365 天") - 能量师当前定价:¥1,314 - 应付差价:¥1,218.89 - 支付按钮:"支付 ¥1,218.89" 10. 升级详情卡片下方附带**权益对比**(精简表格),说明能量师相比年费额外获得的能力:推广能量师赚固定佣金(¥500+¥100)、客户批注与标签管理等 11. 点击支付 → 调 `POST /api/pay/create` 传 `{ productType: 'practitioner', isUpgrade: true, previousOrderId: xxx }` 12. 支付成功后调 `paySuccess()` → `vipType` 更新 + 佣金结算(见 US-6.3 升级场景) **验收标准(接口):** 13. `POST /api/pricing/upgrade` 新增接口:传入 `userId`,返回升级价格详情 14. `POST /api/pay/create` 新增字段: - `isUpgrade: boolean` - `previousOrderId: Long`(原年费订单ID,用于佣金补差) --- ## EPIC 5:用户注册与资料(P1) ### US-5.1 微信登录 + 注册信息完善 **作为** 用户 **我希望** 通过微信授权一键登录,随后补充生日和性别完成注册 **以便** 使用命盘分析和未来的能量匹配社交功能 **验收标准(微信授权登录):** 1. 首次使用点击登录按钮时,弹出微信授权(`uni.login` 获取 code) 2. 授权后自动获取微信昵称和头像(`uni.getUserProfile`),用户可修改 3. 用户拒绝授权昵称/头像时,仍然可以使用 code 登录,昵称默认为"微信用户" 4. 后续使用自动登录(token 未过期时跳过授权) 5. 首次登录后端返回 `{ token, isNewUser: true, profileIncomplete: true }` 6. 老用户登录返回 `{ token, isNewUser: false, profileIncomplete: false }` 7. 老用户(旧版未填生日/性别的)返回 `{ token, profileIncomplete: true }`,引导补全资料 **验收标准(注册信息完善页):** 8. 新用户或资料不全的用户,微信授权后自动跳转到**注册完善页**(非首页) 9. 注册完善页表单包含以下字段: | 字段 | 必填 | 预填 | 说明 | |------|------|------|------| | 头像 | 是 | 微信头像 | 可点击更换(从相册选择) | | 昵称 | 是 | 微信昵称 | 1-20 字 | | 性别 | 是 | 空 | 男 / 女,radio 选择 | | 出生日期 | 是 | 空 | 年月日选择器,同命盘生日 | | 个人简介 | 否 | 空 | 限 100 字,用于社交展示 | | 所在城市 | 否 | 空 | 微信定位或手动选择 | | 兴趣标签 | 否 | 空 | 多选:读书/运动/音乐/旅行/禅修/创业/心理学/玄学 | | 想认识 | 否 | 空 | 单选:不限 / 朋友 / 导师 / 同修 | 10. 点击"提交"调用 `POST /api/profile/complete` 保存所有资料 11. 提交后标记用户 `profile_complete = true` 12. 提交成功后跳转到首页,进入正常使用流程 13. 资料不完整的用户在个人中心显示引导 banner:"请完善个人资料,开启能量匹配" 14. 用户可随时在个人中心 → 编辑资料 修改所有字段 **验收标准(后端与数据库变更):** 15. `users` 表新增字段: | 字段 | 类型 | 说明 | |------|------|------| | `gender` | `TINYINT` | 0=未知, 1=男, 2=女 | | `birth_date` | `DATE` | 出生日期(用于命盘+社交) | | `bio` | `VARCHAR(200)` | 个人简介 | | `city` | `VARCHAR(50)` | 所在城市 | | `tags` | `VARCHAR(200)` | 兴趣标签,逗号分隔 | | `looking_for` | `TINYINT` | 0=不限, 1=朋友, 2=导师, 3=同修 | | `profile_complete` | `TINYINT(1)` | 0=资料未完善, 1=资料已完善 | | `birth_year` | `INT` | 出生年份(冗余,从 birth_date 提取) | | `birth_month` | `INT` | 出生月份(冗余) | | `birth_day` | `INT` | 出生日期(冗余) | 16. `POST /api/auth/login` 响应增加 `profileIncomplete` 字段 17. 新增 `POST /api/profile/complete` 接口,接收所有注册字段 18. 新增 `POST /api/profile/update` 接口,允许修改任意字段 19. `POST /api/profile/info` 响应增加全部新字段 **验收标准(老用户兼容):** 20. 已注册的老用户登录时,如果 `profile_complete = false`,登录后显示完善引导 21. 老用户不强制立即完善,可以正常使用命盘功能 22. 老用户在个人中心中看到"完善资料"入口和引导 ### US-5.2 历史咨询记录 **作为** 能量师 **我希望** 查看我过去的所有咨询记录 **以便** 回顾和继续之前的咨询 **验收标准:** 1. 个人中心页包含"我的咨询"列表 2. 列表按最后互动时间倒序排列 3. 每条记录显示:姓名、生日、咨询时间、最后一条消息摘要 4. 点击记录可跳转到对应的命盘展示页,并恢复完整的聊天历史 5. 支持单条删除 6. 同一用户+同一生日永远返回同一条咨询记录(`(userId, birthday)` 联合唯一) 7. 免费用户仅能看到近 7 天记录,已付费用户可见全部 ### US-5.3 个人资料编辑(为陌生社交准备) **作为** 用户 **我希望** 在个人中心编辑我的昵称、头像、简介、标签、城市等资料 **以便** 未来通过能量匹配结识志同道合的朋友 **验收标准:** 1. 个人中心新增"编辑资料"入口 2. 编辑页包含:头像、昵称、性别、生日、简介、城市、兴趣标签、想认识 3. 资料字段定义同 US-5.1 注册完善页 4. 修改后调用 `POST /api/profile/update` 5. 修改成功后本地更新并提示"保存成功" 6. **陌生社交广场和能量匹配功能不属于 Phase 1,此处仅预留用户资料字段** 注:能量匹配社交将根据用户的命盘主性格数(O)、外部三组数(R/U/X)等计算能量兼容度,推荐匹配用户。此功能在 Phase 2 规划。 --- ## EPIC 6:分销推广与佣金(P0-P1) ### US-6.1 推广码生成(付费后生成) **作为** 付费用户(C端 / 能量师) **我希望** 在首次付费后自动获得专属推广码 **以便** 分享给微信好友来发展下级并赚取佣金 **验收标准:** 1. 推广码在**注册时不生成**,仅在**首次支付成功后**生成(¥131年费 / ¥1,314+能量师费任一触发) 2. 推广码规则:6位大写字母+数字,字符集去掉易混淆的 `O/0/I/1`(即 `ABCDEFGHJKLMNPQRSTUVWXYZ23456789`) 3. 推广码冲突时自动重新生成,保证全局唯一 4. 推广码永久有效,不随年费到期失效 5. 普通用户(未付费)没有推广码,也没有分销入口 6. 推广码展示在分销面板顶部,支持一键复制和生成推广海报 7. 推广码下游用于:分享带参注册(US-6.2)和佣金结算(US-6.3 / US-6.4) --- ### US-6.2 分享带参注册与上下级绑定 **作为** 平台 **我希望** 当用户通过推广链接/扫码打开小程序时,自动记录上下级关系 **以便** 为佣金分成打好基础 **验收标准:** **分享链路:** 1. 推广链接格式:`/pages/index/index?ref=ABC123`(6位推广码) 2. 分享卡片 / 小程序码同样携带 `ref` 参数 **前端缓存策略:** 3. 用户通过链接打开小程序时: - `onLaunch(options)` 读取 `options.query.ref`,转为大写后写入缓存 `pending_referrer` - **每次点击新链接都覆盖**已有值,以用户**最后一次点击的链接**为准(新点击意图覆盖旧的) 4. `pending_referrer` **永不过期**,直到注册成功后才清除 5. 用户注册(微信授权登录)时,从缓存读取 `pending_referrer` 传入注册接口 **后端注册逻辑(`UserService.loginOrRegister()`):** 6. 用户不存在(新用户)+ `referrerCode` 有效 → `invitedBy = 上级ID` 7. 用户不存在 + `referrerCode` 无效 → `invitedBy = null`(静默降级,不报错) 8. 用户不存在 + `referrerCode` 为空 → `invitedBy = null`(正常注册) 9. 用户不存在 + `referrerCode` 为**自己的新openid** → `invitedBy = null`(防止自邀请) 10. 用户**已存在** → 忽略所有 `referrerCode`,不覆盖已有 `invitedBy`(关系一次性锁定) 11. `referrerCode` 查询不到对应上级 → 静默跳过,不抛异常 **数据库更新:** 12. 绑定成功后,如果上级存在,`inviter.directCount += 1`(实时更新统计字段) **边界与异常:** 13. 用户未登录直接生成命盘(纯前端计算)→ 允许,不强制登录 14. 登录中途中断(拒绝授权)→ 不清除 `pending_referrer`,下次触发登录仍携带 15. 注册接口网络失败 → 保留 `pending_referrer`,重试时继续携带 16. 同一设备切换微信账号 → 新的 openid 按新用户处理,正常绑定关系 --- ### US-6.3 B端佣金结算(能量师→能量师,固定金额) **作为** 平台 **我希望** 当能量师A的下级B升级为能量师时,自动结算固定金额佣金 **以便** 能量师获得推广收益 **验收标准:** **触发条件:** 1. 订单 `productType = "practitioner"` 且支付成功 → 进入 B端佣金结算流程 2. 订单 `productType = "annual"` → 走 US-6.4 C端佣金(互斥分支) **佣金计算——按推荐人身份分两种情况:** 3. **推荐人 A 是能量师(`vipType='practitioner'`)** → 按标准固定金额: | 级别 | 默认值 | 配置键 | |------|--------|--------| | L1(直接上级) | ¥500 | `commission.practitioner.l1` | | L2(上上级) | ¥100 | `commission.practitioner.l2` | 4. **推荐人 A 是 C端年费用户(`vipType='annual'`)** → L1 拿半额: | 级别 | 默认值 | 配置键 | |------|--------|--------| | L1(直接上级) | ¥200 | `commission.practitioner.l1_cend` | | L2(上上级) | ¥100(不变) | `commission.practitioner.l2` | 5. 推荐人 A 既不是能量师也不是 C端年费(`vipType=null` 等异常情况)→ L1/L2 不创建佣金 6. 佣金单位为**分**(避免浮点精度问题) **升级场景佣金补差(`isUpgrade=true`):** 7. 当订单标记 `isUpgrade=true`(从年费升级到能量师),L1/L2 佣金需**扣除**已支付过的年费佣金: ``` 应发佣金 = 本次应发金额 − 该买家此前 annual 订单已支付给同一收款人的佣金总额 ``` 8. 计算逻辑: - 查询 `commissions` 表中,同一 `buyer_id` + 同一 `收款人` 且 `product_type='annual'` 的所有佣金之和 - 从本次 practitioner 佣金中减去该金额 - 若差值 ≤ 0(理论情况),则本条佣金不创建 9. 示例——首次直接购买能量师(非升级,对比参考): ``` 用户C为新用户(无年费记录),通过上级A(能量师)链接直接购买能量师 ¥1,314 isUpgrade = false → 全额发放:L1 = ¥500,L2 = ¥100 无年费佣金可扣除 ``` 10. 示例——年费升级补差: ``` 用户B第1天付¥131(annual),上级A获¥52.40(40%) 用户B第100天升级能量师(种子价¥1,314),上级A是能量师→应发¥500 实际发放:¥500 − ¥52.40 = ¥447.60 ``` 10. 示例——C端推荐人升级补差: ``` 用户B第1天付¥131(annual),上级A(C端)获¥52.40(40%) 用户B第100天升级能量师,上级A是C端→应发半额¥200 实际发放:¥200 − ¥52.40 = ¥147.60 ``` **收款人资格:** 11. L1:`buyer.invitedBy` 对应的上级存在 → 按上述规则计算 12. L2:上级的 `invitedBy` 对应的上上级存在 → 按上述规则计算(L2 不因身份打折,仅 L1 打折) 13. 收款人不存在时 → 静默跳过,不报错 **状态与幂等:** 14. 佣金 `status` 直接写入 `"settled"`(即时到账,无 pending 冷静期) 15. 同一 `outTradeNo` 重复回调 → 幂等处理,不创建重复佣金 16. 佣金固定金额不受种子价/标准价差异影响(C端半额同样固定) **统计更新:** 17. L1 创建时:`inviter.directCount += 1`,`inviter.convertedCount += 1` 18. L2 创建时:`inviter2.indirectCount += 1` **配置项(后台可配):** | 配置键 | 默认值 | 说明 | |--------|--------|------| | `commission.practitioner.l1` | `50000` | B端一级佣金—能量师推荐(分) | | `commission.practitioner.l1_cend` | `20000` | B端一级佣金—C端推荐(分) | | `commission.practitioner.l2` | `10000` | B端二级佣金(分) | --- ### US-6.4 C端佣金结算(年费推荐,比例分成) **作为** 平台 **我希望** 当C端用户B通过上级A的链接注册并付费 ¥131 时,自动按比例分成 **以便** 激励所有付费用户推广 **验收标准:** **触发条件:** 1. 订单 `productType = "annual"` 且支付成功 → 进入 C端佣金结算流程 **佣金计算:** 2. 直接佣金(L1):`totalFee × directRate / 10000`,从 `sys_config` 读取 `commission.annual.direct_rate`(默认 `4000` = 40%) 3. 上级佣金(L2):`totalFee × upstreamRate / 10000`,从 `sys_config` 读取 `commission.annual.upstream_rate`(默认 `500` = 5%) 4. 金额计算使用**整数截断**(非四舍五入),剩余零头归平台 > **注意**:年费佣金在后续用户升级能量师时可能被部分抵扣(见 US-6.3 升级补差逻辑)。`paySuccess()` 记录原始佣金(用于后续补差计算),补差逻辑在升级时执行。
> 因此 C端佣金的 `status` 仍直接写入 `"settled"`,不需要等待升级再结算。若后续升级,再由 US-6.3 扣除已付金额。 **收款人资格(与 B端不同):** 5. L1:`buyer.invitedBy` 对应的上级**必须已付费**(`referralCode != null`),才创建佣金 6. L2:上级的 `invitedBy` 对应的上上级**必须已付费**(`referralCode != null`),才创建佣金 7. C端佣金**不要求**收款人是"能量师"(practitioner),只要曾经付费即可 **状态与幂等:** 8. 佣金 `status` 直接写入 `"settled"` 9. 幂等处理同 US-6.3 **与 B端的差异:** 10. C端佣金不更新 `directCount/convertedCount/indirectCount`(这些统计仅用于B端"升级能量师") 11. C端佣金比例灵活(后台可配 0%~100%),B端固定金额 **每笔 ¥131 分配示例:** | 分配对象 | 比例 | 金额 | |---------|------|------| | 直接上级 | 40% | ¥52.40 | | 上上级 | 5% | ¥6.55 | | 平台 | 55% | ¥72.05 | **配置项(后台可配):** | 配置键 | 默认值 | 说明 | |--------|--------|------| | `commission.annual.direct_rate` | `4000` | C端直接佣金比例(万分比,4000=40%) | | `commission.annual.upstream_rate` | `500` | C端上级佣金比例(万分比,500=5%) | --- ### US-6.5 分销面板 **作为** 能量师 **我希望** 在分销面板中查看我的推广收益和团队数据 **以便** 追踪推广效果,激励持续推广 **验收标准:** **入口权限:** 1. 个人中心菜单项"💰 我的推广"→ 跳转到分销面板 2. **仅付费用户可见**(`vipEndTime > now`),普通用户看不到入口 3. 付费到期后入口自动隐藏 **页面结构(自上而下):** **① 推广码卡片:** 4. 显示推广码 `我的推广码: ABC123` + 复制按钮 5. "生成推广海报"按钮 → 调用后端生成含小程序码的海报 6. 无推广码(极端情况)→ 显示"推广码生成中…" **② 收益统计卡片(三列等宽):** 7. 总收益:累计所有佣金总额(`status = settled`) 8. **累计收益**:当前总收益金额(Phase 1 不提现,故不称"可提现"以免误解;页面加注"提现功能即将开放") 9. 今日新增:当日 00:00 至今产生的佣金总额 10. 金额以元为单位,保留两位小数(后端存储分,前端 `/100`) **③ 团队统计(三列):** 11. 直接下级:`directCount` 12. 间接下级:`indirectCount` 13. 升级下级:`convertedCount` 14. 零数据时显示引导文案:"暂无推广数据,分享推广码给好友开始赚取佣金" **④ 佣金明细列表(最近50条):** 15. 显示:时间(MM-DD HH:mm)、来源(匿名"用户****")、级别(L1/L2标签)、金额(+¥XXX.XX)、状态 16. 按时间倒序排列 17. 空数据时显示"暂无佣金记录"引导 **⑤ 推广工具区:** 18. "📱 生成推广海报" → 合成含小程序码的分享海报 19. "🔗 复制推广链接" → 复制带 ref 参数的小程序路径到剪贴板 20. "📊 提现申请" → **置灰不可点击**,显示"即将开放"(Phase 1 不做提现) **页面状态:** 21. **Loading 状态**:各区块显示骨架屏脉冲动画 22. **错误状态**:顶部显示错误提示条 + "点击重试"按钮 23. **空数据状态**:佣金列表/团队统计显示引导文案 24. **下拉刷新**:重新请求所有数据 **后端 API 需求:** | 接口 | 说明 | |------|------| | `POST /api/profile/info` | 含 `referralCode`(已有) | | `POST /api/commission/list` | 佣金明细列表(已有) | | `POST /api/commission/stats` | **新增**:总收益+可提现+今日新增+团队统计 | --- ### US-6.6 C端年费订阅入口 **作为** 免费用户(C端) **我希望** 在AI解读页底部看到 ¥131 开通完整解读的引导 **以便** 一键订阅获取完整服务 **验收标准:** **入口位置(用户已确认选项1——命盘解读页底部):** 1. `chart/index.vue` 底部(聊天区下方)固定显示订阅卡片(免费用户) 2. 已付费用户(C端或能量师) → 不显示订阅卡片,改为显示"✅ 已开通"徽章+生成推广海报入口 **交互流程(未登录):** 3. 显示完整订阅卡片:🔮 AI解读会员 ¥131/年,列出权益(无限AI解读、个性化报告、推广赚佣金) 4. 点击"登录后开通 ¥131" → 先跳登录页,登录后自动跳转支付页 **交互流程(已登录未付费):** 5. 显示同上"立即开通 ¥131"按钮 6. 点击 → 跳转支付页 `product=annual`,金额 ¥131 **交互流程(已付费):** 7. 订阅卡片替换为灰色小徽章"✅ 已开通AI解读 | 有效期至 2027-05-28" 8. 已付费用户显示"📱 生成推广海报"入口 9. 能量师(practitioner)显示"✅ 已开通能量师" **支付页适配:** 10. `payment/index.vue` 支持 URL 参数 `product`: - `product=annual` → 显示 C端年费 ¥131 和对应权益列表 - `product=practitioner` → 显示能量师价格(通过 API 获取种子/标准价) 11. `createOrder` 接口接收 `productType` 参数 **后端适应:** 12. Order `productType` 字段新增枚举值:`"annual"` | `"practitioner"` | `"practitioner_plan"`(人工方案) 13. User `vipType` 字段(`"annual"` | `"practitioner"` | `null`) 14. `paySuccess()` 佣金结算按 `productType` 分支: - `annual` → US-6.4 C端年费佣金 - `practitioner` → US-6.3 B端佣金(含推荐人身份判断 + isUpgrade 补差) - `practitioner_plan` → US-9.5/9.6 人工方案佣金(通过 CommerceService 结算,平台留存模型) --- ## EPIC 9:学业方向分析与人工方案(P1) > **场景**:家长为孩子选择学业方向,通过AI解读初步了解孩子的天赋倾向,如需深度方案可申请能量师出方案。 > **佣金模型**:人工方案采用**平台佣金留存模型**(区别于年费的固定金额/比例模型),所有分销佣金从平台留存部分支出。 > **商城底座**:引入 lilishop(`iwt/lilishop`)作为商城引擎,本期通过 `CommerceService` 接口层预留对接,暂不实现实际商城付费。 --- ### US-9.1 学业方向AI深度解读 **作为** 家长 **我希望** 输入孩子的生日后,看到针对学业方向的AI深度解读 **以便** 了解孩子的天赋倾向,为学业规划提供参考 **验收标准:** **入口:** 1. 命盘解读页底部增加"🎓 学业方向分析"按钮(位于通用解读下方第二行) 2. 点击后调用新接口 `POST /api/chart/academic-orientation` **AI 解读内容:** 3. 后端转发至 Dify **学业方向专用 Workflow**,命盘24个数字(A-X)作为 `inputs` 4. 解读内容至少包含以下章节: - **天赋倾向**:基于主性格数字+外部三组数的自然天赋分析 - **适合方向**:文科倾向/理科倾向/艺术特长/体育潜能等,用百分比表示匹配度 - **学习特征**:专注力、理解方式、学习节奏偏好(基于J/K/L位置分析) - **亲子沟通建议**:针对该命盘类型的孩子,应采用的沟通和教育方式 - **关键期提醒**:V/W/X中年区对应的升学/职业选择重要节点 5. 每个章节独立卡片展示,可折叠展开 6. 页面底部显示"💡 以上分析由AI生成,如需人工深度方案,可申请能量师出方案" **付费控制:** 7. 免费用户每日可查看 1 次学业方向解读(与 US-3.2 主性格概要**独立配额**,互不消耗) 8. 已付费用户(C端/能量师)不限次 9. 超出次数后显示引导卡片:"今日学业分析次数已用完,开通会员享无限次" **技术架构:** 10. 在 Dify 平台新增学业方向 Workflow,Workflow 输入为 24 个命盘数字(A-X),输出为结构化 JSON 11. 后端 `DifyService` 新增方法 `interpretAcademicOrientation(AcademicRequest request)` 12. 解读结果缓存同 US-3.3(同一命盘+同一天内重复请求返回缓存内容) --- ### US-9.2 申请能量师出方案 **作为** 家长 **我希望** 在AI学业解读后,申请能量师为孩子出具深度人工方案 **以便** 获得比AI更个性化的专业指导 **验收标准:** **入口与表单:** 1. AI学业解读页底部固定"申请能量师出方案"按钮 2. 通用AI解读页底部也显示"💼 申请人工深度方案"按钮 3. 点击后弹出半屏表单,包含: - **需求类型**:学业方向 / 职业规划 / 亲子关系 / 其他(单选) - **具体需求描述**:文本输入框,限300字 - **期望价格范围**:下拉选项(¥50-99 / ¥100-299 / ¥300-499 / ¥500-999 / 面议) 4. 提交后生成一条"方案需求单"记录 **需求分配:** 5. 如果用户有 `invitedBy` 且上级是能量师(`vipType='practitioner'`)→ 自动把需求单分配给该能量师 6. 如果没有上级能量师 → 显示"暂未开放系统分配,请通过推荐链接找到专属能量师" 7. 分配后能量师收到通知(US-8.3 消息通知或在能量师工作台看到) **数据库新增:** 8. `plan_requests` 表:`id, userId, chartId, requestType, description, budgetRange, assignedPractitionerId, status(pending/accepted/negotiating/paid/completed/cancelled), price, createdAt, updatedAt` --- ### US-9.3 能量师介入AI会话 **作为** 能量师 **我希望** 看到向我咨询用户的AI会话,并可以发起介入 **以便** 在用户需要时主动提供专业建议,促成人工方案 **验收标准:** **触发条件:** 1. 仅 `invitedBy` 关系链中的能量师(B推荐A,A是B的上级)才能介入B的AI会话 2. 能量师在"能量师工作台"看到下级用户的咨询记录列表(来自 `invitedBy` 关系链,仅显示有实际咨询的用户) 3. 列表显示:用户头像/昵称/命盘日期/最后活跃时间 **介入流程:** 4. 能量师点击某个用户 → 进入"咨询监看"页面(只读模式查看AI聊天记录) 5. 页面底部有"介入会话"按钮 6. 点击后,用户侧聊天界面出现系统消息:**"🔔 能量师 张三 已进入本次咨询"** 7. 能量师侧出现输入框,可发送文字消息 8. 用户侧聊天流中,能量师消息显示为:**"👤 能量师张三:消息内容"**(绿色气泡,区别于AI的灰色气泡) 9. AI继续正常回答,能量师和AI的回答在聊天中交替显示 **权限边界:** 10. 用户随时可"请出能量师"(在消息长按菜单中选择"结束能量师介入") 11. 用户主动关闭后,能量师侧显示"用户已结束本次协同咨询" 12. 每次介入在 `chat_interventions` 表记录 **数据库新增:** 13. `chat_interventions` 表:`id, sessionId, practitionerId, startTime, endTime, endedBy(user/practitioner)` 14. `chat_messages` 表新增 `senderType` 枚举:`ai` / `user` / `practitioner` / `system` / `proposal` --- ### US-9.4 方案价格协商 **作为** 能量师 **我希望** 在聊天中向用户发送方案提议,并可与用户协商价格 **以便** 双方达成一致后完成付费 **验收标准:** **出方案提议:** 1. 能量师在聊天输入框左侧有"📋 出方案"按钮 2. 点击弹出结构化表单: - 方案标题(限50字) - 方案描述(限500字) - 方案价格(手动输入,单位元,整数,范围受系统配置限制) 3. 发送后在聊天中显示方案卡片(嵌入消息格式) **方案卡片交互:** 4. 卡片包含:标题、描述、价格(¥XXX)、能量师名称 5. 用户侧卡片有三个操作按钮: - **"💰 接受并支付"** → 进入 US-9.5 支付流程 - **"💬 议价"** → 弹出输入框,用户输入期望价格 - **"❌ 不感兴趣"** → 卡片标记为已拒绝,通知能量师 6. 用户议价后,能量师侧收到新消息:"用户希望价格改为 ¥XXX" 7. 能量师可: - 接受新价 → 发送更新后的方案卡片(价格更新) - 坚持原价 → 回复文字说明 - 提出折中价 → 发送新的方案卡片 **状态管理:** 8. 一条 `plan_requests` 记录对应多轮协商历史 9. 每次更新价格或状态在 `plan_request_logs` 表记录 10. 最大协商轮次 5 轮(超限后只能接受或拒绝当前价格) **数据库新增:** 11. `plan_request_logs` 表:`id, planRequestId, action(propose/counter/accept/reject), oldPrice, newPrice, message, operatorId, createdAt` --- ### US-9.5 方案付费与交付 **作为** 家长 **我希望** 接受能量师方案报价后在线支付,并收到完整的方案报告 **以便** 获得专业的学业指导 **验收标准:** **支付:** 1. 用户点击"接受并支付" → 弹出半屏支付确认页 2. 支付确认页显示:服务名称、能量师名称、协商价格、微信支付按钮 3. 点击支付 → `createOrder(productType="practitioner_plan", planRequestId=xxx)` 4. 订单 `productType = "practitioner_plan"`(新增类型) 5. 支付回调 → 调用 `CommerceService.onPaymentSuccess()` 处理平台佣金计算 **平台佣金计算(StubCommerceService):** 6. 读取分类佣金率:`commerce.category.practitioner_plan.commission_rate`(默认 `3000` = 30%) 7. 平台佣金 = 总价 × commission_rate / 10000(单位分) 8. 能量师应结算 = 总价 - 平台佣金 9. 如有上级推荐 → 从平台佣金中提取分销佣金: - 直接上级提成 = 平台佣金 × `commission.practitioner_plan.referral_rate` / 10000(默认 `2000` = 20%) - 上上级提成 = 平台佣金 × `commission.practitioner_plan.upstream_rate` / 10000(默认 `500` = 5%) **示例计算:** ``` 方案价 ¥299,平台佣金率 30% 平台佣金 = ¥299 × 30% = ¥89.70 能量师结算 = ¥299 - ¥89.70 = ¥209.30 有直接上级:分销佣金 = ¥89.70 × 20% = ¥17.94 平台净留 = ¥89.70 - ¥17.94 = ¥71.76 ``` **交付:** 10. 能量师收到支付成功通知 11. 能量师工作台出现"方案交付入口" 12. 交付支持三种方式: - **文字方案**:富文本编辑器输入,保存到 `plan_deliveries.text_content` - **PDF方案**:上传PDF文件(与US-2.2共用PDF生成能力) - **图文报告**:混合内容,包含命盘截图+文字解读 13. 交付后用户收到通知 + 聊天显示"📄 您的学业方案已交付" 14. 用户可查看/下载方案,平台不额外限制次数 **评价与完成:** 15. 用户确认接收方案 → 状态 `completed` 16. 用户可对能量师服务进行评分(1-5星)+ 文字评价 17. 评价写入 `practitioner_ratings` 表 **数据库新增:** 18. `plan_deliveries` 表:`id, planRequestId, deliveryType(text/pdf/mixed), textContent, fileUrl, createdAt` 19. `practitioner_ratings` 表:`id, planRequestId, userId, practitionerId, score(1-5), comment, createdAt` --- ### US-9.6 人工方案分销佣金(平台留存口径) **作为** 平台 **我希望** 当用户购买人工方案时,从平台佣金中自动结算分销佣金 **以便** 激励推广者推荐用户给能量师 **验收标准:** **触发条件:** 1. 订单 `productType = "practitioner_plan"` 且支付成功 → 进入分佣流程 2. 佣金来自平台留存部分(不是从能量师结算金额中扣除) **佣金参数:** | 参数 | 默认值 | 配置键 | |------|--------|--------| | 平台佣金率 | 30% | `commerce.category.practitioner_plan.commission_rate` | | 直接上级分销比例 | 20%(占平台佣金) | `commission.practitioner_plan.referral_rate` | | 上上级分销比例 | 5%(占平台佣金) | `commission.practitioner_plan.upstream_rate` | **计算逻辑:** 3. `platformCommission = totalFee × commission_rate / 10000` 4. `referralCommission = platformCommission × referral_rate / 10000` 5. `upstreamCommission = platformCommission × upstream_rate / 10000` 6. 收款人资格同 US-6.4(须已付费用户才可收款) **状态与幂等:** 7. 佣金 `status` 写入 `"settled"`(即时到账) 8. 同一 `outTradeNo` 重复回调 → 幂等处理 **示例:** ``` 方案价 ¥299(29,900分) 平台佣金率 30% 平台佣金 = 29,900 × 30% = 8,970分 = ¥89.70 直接上级佣金 = 8,970 × 20% = 1,794分 = ¥17.94 上上级佣金 = 8,970 × 5% = 448分 = ¥4.48 能量师结算 = 29,900 - 8,970 = 20,930分 = ¥209.30 平台净留 = 8,970 - 1,794 - 448 = 6,728分 = ¥67.28 ``` --- ### CommerceService 接口层(商城预留) > 本期不实现 lilishop 对接,先定义接口 + 桩实现。 **接口:** ```java public interface CommerceService { /** 创建商品(能量师上架服务) */ String createProduct(CommerceProductDTO product); /** 创建订单 */ String createOrder(CommerceOrderDTO order); /** 订单支付回调处理 */ void onPaymentSuccess(String orderSn, String payOrderNo); /** 查询订单 */ CommerceOrderDTO getOrder(String orderSn); /** 获取店铺结算信息 */ CommerceSettlementDTO getSettlement(String storeId); /** 记录分销订单 */ void recordDistribution(String orderSn); } ``` **Phase 1 桩实现(StubCommerceService):** | 方法 | 实现策略 | |------|---------| | `createProduct` | 写入本地 `commerce_goods` 映射表 | | `createOrder` | 走现有本地 `orders` 表 + 标记 `commerceReady=false` | | `onPaymentSuccess` | 执行本地佣金计算逻辑(US-9.5 验收标准6-9) | | `getSettlement` | 从本地佣金表聚合统计 | | `recordDistribution` | 走现有 `commissions` 表逻辑 | **Phase 2+ 对接 lilishop:** - `createProduct` → lilishop Goods API - `createOrder` → lilishop Order API - `onPaymentSuccess` → 创建 StoreFlow + DistributionOrder - `getSettlement` → lilishop Bill API - `recordDistribution` → lilishop Distribution API **数据库新增:** ```sql -- lilishop 商品映射表(预留) CREATE TABLE `commerce_goods` ( `id` bigint PRIMARY KEY AUTO_INCREMENT, `store_id` varchar(32) NOT NULL COMMENT '能量师storeId(= userId)', `product_type` varchar(32) NOT NULL COMMENT '商品类型:practitioner_plan', `lilishop_goods_id` varchar(32) DEFAULT NULL COMMENT 'lilishop商品ID(Phase 2 填充)', `category_path` varchar(255) DEFAULT NULL COMMENT 'lilishop分类路径', `price` bigint NOT NULL COMMENT '价格(分)', `status` varchar(16) DEFAULT 'active' COMMENT '状态', `created_at` datetime NOT NULL, `updated_at` datetime DEFAULT NULL ); ``` --- ### US-7.1 命盘批注 **作为** 能量师 **我希望** 在命盘图上添加文字批注或标记 **以便** 为每个客户记录个性化的解读要点 **验收标准(入口与权限):** 1. "编辑批注"按钮位于命盘展示页右上角菜单中(仅已付费用户可见) 2. 未付费用户看不到该按钮 3. **批注模式建议方案(开发选其一):** - **方案A(推荐)**:在命盘页面底部展开批注编辑面板,命盘操作和数字点击互不干扰,无需切换模式。用户点击数字后可快速插入 `【位置X】` 标签到批注文本 - **方案B(备选)**:点击"编辑批注"进入专用模式,命盘页面其他操作暂时禁用,底部显示批注编辑区域 4. 批注内容支持富文本(加粗、斜体、分段、列表),最大 2000 字 5. 批注内容与 `chartId` 绑定,不同命盘各自独立 6. 批注支持按命盘位置打标签,格式示例:`【主性格O】天生领导者,数字1能量突出` 7. 同一位置的多条批注自动折叠,点击展开 8. 保存批注调用 `POST /api/chart/annotation/save`,传入 `chartId` + `content` 9. 保存成功后 toast 提示"批注已保存",编辑区域收起 10. 取消编辑时,若有未保存内容,弹出确认框"有未保存的内容,确定放弃?" **验收标准(批注展示):** 11. 已有批注的命盘,在数字卡片上方显示小黄点标记(🟡) 12. 退出批注模式后,命盘页底部显示批注摘要区域(展开/折叠),显示最近 3 条批注 13. 点击"查看全部批注"跳到批注完整列表页 14. 批注列表页按位置分组,按创建时间倒序排列 15. 导出的 PDF(US-2.2)第二页包含批注内容 16. 批注支持删除(长按批注 → 确认删除) **验收标准(后端与存储):** 17. 新增 `chart_annotations` 表: | 字段 | 类型 | 说明 | |------|------|------| | `id` | `BIGINT PK` | 主键 | | `chart_id` | `BIGINT` | 关联命盘记录 | | `user_id` | `BIGINT` | 批注人(能量师) | | `position` | `VARCHAR(2)` | 关联位置字母(A-X),可为 null | | `content` | `TEXT` | 批注内容(富文本 HTML) | | `created_at` | `DATETIME` | 创建时间 | | `updated_at` | `DATETIME` | 最后修改时间 | 18. 新增接口: - `POST /api/annotation/save` — 保存/更新批注 - `POST /api/annotation/list` — 获取命盘批注列表(按 chartId) - `POST /api/annotation/delete` — 删除单条批注 ### US-7.2 客户标签分组 **作为** 能量师 **我希望** 为命盘记录添加简单的标签或分组 **以便** 对客户进行分类管理 **验收标准(标签创建与选择):** 1. 发起咨询时(生日输入页确认弹窗中)显示"添加标签(选填)"区域 2. 标签区域包含:标签输入框 + "新建"按钮 + 已创建的标签列表(可点击选择) 3. 新建标签:输入 2-8 字,自动分配随机颜色,点击确定后存入用户标签库 4. 已有标签以胶囊样式展示:`[🟢 老客户]` `[🟠 朋友推荐]` `[🔵 线上引流]` 5. 一个命盘最多绑 3 个标签,超出提示"最多选择 3 个标签" **验收标准(标签展示与筛选):** 6. 命盘列表中,每条记录右侧展示标签胶囊 7. 历史列表顶部增加标签筛选栏,可选择一个或多个标签组合筛选: - 未选择任何标签时:显示所有记录 - 选择标签时:AND 逻辑(同时包含所有选中标签的记录) - 支持"全部"按钮一键清除筛选 8. 已打标签的命盘记录,在列表中有标签标识,方便快速识别 **验收标准(标签管理):** 9. 个人中心 → 我的标签:展示该能量师创建的所有标签 10. 标签管理操作: - 编辑标签名称 - 修改标签颜色(从预设 8 色中选择) - 删除标签(删除后将移除所有关联命盘上的该标签) 11. 删除标签时弹出确认:"删除后,所有关联命盘上的该标签将被移除,确定?" **验收标准(后端与存储):** 12. 新增 `user_labels` 表: | 字段 | 类型 | 说明 | |------|------|------| | `id` | `BIGINT PK` | 主键 | | `user_id` | `BIGINT` | 能量师 ID | | `name` | `VARCHAR(20)` | 标签名称 | | `color` | `VARCHAR(7)` | 颜色值(如 #4CAF50) | | `created_at` | `DATETIME` | 创建时间 | 13. 新建关联表 `chart_labels`(命盘-标签多对多): | 字段 | 类型 | 说明 | |------|------|------| | `id` | `BIGINT PK` | 主键 | | `chart_id` | `BIGINT` | 命盘记录 ID | | `label_id` | `BIGINT` | 标签 ID | | `created_at` | `DATETIME` | 关联时间 | 14. 新增接口: - `POST /api/label/create` — 新建标签 - `POST /api/label/list` — 获取标签列表 - `POST /api/label/update` — 编辑标签名称/颜色 - `POST /api/label/delete` — 删除标签 - `POST /api/chart/label/set` — 为命盘设置标签 - `POST /api/chart/label/list` — 获取命盘标签列表(按 chartId) --- ## EPIC 8:管理后台(P0-P1) ### US-8.1 系统配置管理 **作为** 运营管理员 **我希望** 在管理后台可视化编辑定价和佣金参数 **以便** 灵活调整运营策略,无需开发介入 --- #### 8.1.1 验收标准 **DDL & Entity:** 1. 新建 `sys_config` 表,结构如下: ```sql CREATE TABLE `sys_config` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `config_key` VARCHAR(64) NOT NULL COMMENT '配置键(点分命名法)', `config_value` VARCHAR(255) DEFAULT NULL COMMENT '配置值(始终使用字符串存储)', `description` VARCHAR(255) DEFAULT NULL COMMENT '中文说明', `value_type` VARCHAR(20) NOT NULL DEFAULT 'string' COMMENT 'int | price | percent | string', `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_config_key` (`config_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` 2. 预置 9 条种子数据(见下,含新增的 `seed_period_end`)。 **后端:SysConfigService + 内存缓存:** 3. `@PostConstruct loadAllToCache()`:启动时将全量配置读入 `ConcurrentHashMap` 4. 对外提供 `getString(key, default)`、`getInt(key, default)` 两个读方法,O(1) 取值 5. `key` 不存在时 → 返回代码中传入的 `defaultValue`,**永不抛异常** 6. `updateConfig(key, value)`:更新 DB + 同步写入缓存,**无需重启** 7. `batchUpdate(Map)`:循环调用 `updateConfig`,整个操作在 `@Transactional` 中 **管理后台 API:** 8. `POST /api/admin/config/list` → 返回 `List`(含全部字段) 9. `POST /api/admin/config/update` → 接收 `{ "configKey": "value", ... }` → 批量更新 **小程序端 API:`POST /api/pricing/current`:** ```json // Request: 无参数 // Response: { "code": 0, "data": { "seedPrice": 131400, // pricing.practitioner.seed(分) "standardPrice": 198600, // pricing.practitioner.standard(分) "annualFee": 13100, // pricing.annual(分) "isSeedPrice": true, // 当前用户是否能享受种子价(三重条件判断) "seedReason": "eligible", // "eligible"=三条件均满足 | "not_founder_code"=非创始人码 | "expired"=有效期已过 | "quota_full"=名额已满 | "no_referrer"=无推荐人(自然注册) "remainingSeats": 298, // 种子名额余量 "seedLimit": 300, // 名额上限 "seedPeriodEnd": "2027-05-31T23:59:59" // 种子价有效期截止时间 } } ``` 10. `isSeedPrice` 的计算逻辑(三重条件,全部满足才为 `true`): - 条件①:当前用户的直接推荐人(`invitedBy`)存在,且该推荐人 `invitedBy IS NULL AND vipType='practitioner'`(即推荐人是创始人) - 条件②:当前时间 ≤ `pricing.practitioner.seed_period_end` - 条件③:已通过创始人码付费的能量师数 < `seedLimit`(统计 `orders` 中 `product_type='practitioner'` AND `status='paid'` AND `is_seed_price=true` 的记录数) - 自然注册用户(`invitedBy IS NULL`)→ 条件①不满足 → `isSeedPrice=false` - `seedReason` 字段用于前端展示不同提示文案(如"种子价已过期"、"种子名额已满"、"需通过创始人邀请注册"等) **管理后台页面(`src/views/settings/index.vue`):** 11. 左侧菜单新增"系统配置"入口,路由 `/settings`,图标 ⚙️ 12. 页面分为三个卡片区域,每个卡片顶部有"恢复初始值"按钮(仅重置单个卡片内的字段为种子数据): - hover 时 tooltip 显示具体初始值(如"恢复为 ¥500") - 点击后弹出确认框:"将恢复本卡片内所有字段为初始值,确定吗?" **定价配置卡片:** 13. 五个输入项:能量师种子价、标准价、种子价名额上限、种子价有效期截止时间、C端年费 14. 用户输入的是**元(整数)**,提交时自动 ×100 转为分(有效期除外) 15. 种子价名额上限下方实时显示"当前已使用:X / Y"(调用 `countByProductTypeAndStatus` + `isSeedPrice=true`) 16. 修改数量时,"已使用"比例随之动态变化 17. 种子价有效期使用 `el-date-picker`(datetime 类型),显示格式 `YYYY-MM-DD HH:mm:ss`,存储格式为 ISO 8601 字符串 18. 有效期下方显示当前状态:"🟢 有效期剩余 XX 天" 或 "🔴 已过期"(根据当前时间与 `seed_period_end` 比较) 19. 定价卡片顶部注释:"种子价三重条件:①扫创始人码注册 ②在有效期内 ③名额未满,三者同时满足才生效" **B端佣金配置卡片:** 17. **三个输入项**: - L1 佣金—能量师推荐(¥500) - L1 佣金—C端推荐(¥200,新增) - L2 佣金(¥100,不限推荐人身份) 18. 输入单位为元(整数),提交时 ×100 转为分 19. 每个输入项下方实时显示比例计算: - "L1(能量师推荐)¥500 = 种子价 38.1% / 标准价 25.2%" - "L1(C端推荐)¥200 = 种子价 15.2% / 标准价 10.1%" - "L2 ¥100 = 种子价 7.6% / 标准价 5.0%" 20. 比例随定价或佣金值的修改**实时联动更新** **C端佣金配置卡片:** 21. 两个输入项:直接佣金比例(40%)、上级佣金比例(5%) 22. 输入单位为"%"(整数,如输入"40"表示40%),提交时转为万分比 ×100 23. 下方实时显示金额分配预览: - "每笔 ¥131:直接 ¥52.40(40.0%)/ 上级 ¥6.55(5.0%)/ 平台 ¥72.05(55.0%)" 24. 费率修改时金额分配**实时联动更新** **保存逻辑:** 25. 点击"保存全部配置"按钮 → 触发 `POST /api/admin/config/update` 26. 所有字段批量提交,不逐个保存 27. 保存前做前端校验:金额 > 0、比例 0-100%、名额上限 > 0 28. 保存成功 → ElMessage.success("配置已保存,已实时生效") 29. 保存失败 → ElMessage.error("保存失败:" + 错误详情) 30. 前端无 `el-form` 的 reset 行为 —— 用户手动刷新页面可恢复到上次保存的值 **错误与边界:** 31. 网络失败时显示"保存失败,请重试",页面不跳转,已修改的值保留在输入框中 32. 同时两个管理员保存 → 后保存的覆盖先保存的,不做冲突检测(Phase 1 简化) 33. 输入非法值时(如空字符串、负数),`el-input-number` 的 min 属性直接阻止输入 34. 所有金额字段不允许出现小数(元为整数输入,单位转换在后端) --- #### 8.1.2 后端接口规格 **`POST /api/admin/config/list`** ```json // Response: { "code": 0, "data": [ { "id": 1, "configKey": "pricing.practitioner.seed", "configValue": "131400", "description": "能量师种子价(分)", "valueType": "price", "updatedAt": "2026-05-28T10:00:00" } // ... 共 9 条(基础定价4+有效期1+佣金3+佣金1,详见种子数据SQL) ] } ``` **`POST /api/admin/config/update`** ```json // Request: { "pricing.practitioner.seed": "131400", "commission.practitioner.l1": "60000" } // Response: { "code": 0 } ``` **`POST /api/admin/config/reset`**(可选,单条重置) ```json // Request: { "configKey": "commission.practitioner.l1" } // Response: { "code": 0, "data": { "configKey": "commission.practitioner.l1", "configValue": "50000" } } ``` --- #### 8.1.3 后端类设计 | 类名 | 位置 | 说明 | |------|------|------| | `SysConfig` | `entity/` | JPA 实体 | | `SysConfigRepository` | `repository/` | JPA 接口 | | `SysConfigService` | `service/` | 配置 CRUD + 内存缓存 | | `AdminConfigController` | `controller/` | `/api/admin/config/*` 端点 | | `PricingController` | `controller/` | `/api/pricing/current` 小程序端端点 | | `OrderRepository` | `repository/` | 新增 `countByProductTypeAndStatus()` | **`SysConfigService` 核心代码:** ```java @Service public class SysConfigService { private final ConcurrentHashMap cache = new ConcurrentHashMap<>(); private final SysConfigRepository repository; @PostConstruct public void loadAllToCache() { cache.clear(); repository.findAllByOrderByConfigKeyAsc() .forEach(cfg -> cache.put(cfg.getConfigKey(), cfg.getConfigValue())); } public String getString(String key, String defaultValue) { return cache.getOrDefault(key, defaultValue); } public int getInt(String key, int defaultValue) { String val = cache.get(key); if (val == null) return defaultValue; try { return Integer.parseInt(val); } catch (NumberFormatException e) { return defaultValue; } } @Transactional public void updateConfig(String key, String value) { SysConfig cfg = repository.findByConfigKey(key) .orElseGet(() -> new SysConfig(key, value, "auto", "string")); cfg.setConfigValue(value); cfg.setUpdatedAt(LocalDateTime.now()); repository.save(cfg); cache.put(key, value); } public void batchUpdate(Map configs) { configs.forEach(this::updateConfig); } } ``` --- #### 8.1.4 管理后台前端规格 | 文件 | 操作 | 说明 | |------|------|------| | `src/views/settings/index.vue` | **新增** | 系统配置页面(~200行) | | `src/router/index.ts` | 修改 | 新增 `/settings` 路由 | | `src/layout/index.vue` | 修改 | 新增菜单项 | **侧边栏菜单新增:** ```vue 系统配置 ``` **页面组件结构:** ``` settings/index.vue ├── el-card (header-toolbar: "系统配置" + [保存全部配置]) ├── el-card (定价配置) │ ├── el-form-item: 能量师种子价 → el-input-number(min=0, max=999999, step=100) │ ├── el-form-item: 能量师标准价 → el-input-number(...) │ ├── el-form-item: 种子价名额上限 → el-input-number(min=1, max=99999, step=50) │ ├── el-form-item: 种子价有效期 → el-date-picker(type="datetime", format="YYYY-MM-DD HH:mm:ss") │ ├── 注记:种子价三重条件(创始人码+有效期+名额) │ └── el-form-item: C端年费 → el-input-number(...) ├── el-card (B端佣金配置) │ ├── el-form-item: L1 佣金(能量师推荐)→ el-input-number │ ├── el-form-item: L1 佣金(C端推荐)→ el-input-number │ ├── el-form-item: L2 佣金 → el-input-number │ └── 实时比例显示 └── el-card (C端佣金配置) ├── el-form-item: 直接佣金比例 → el-input-number(min=0, max=10000, step=500) ├── el-form-item: 上级佣金比例 → el-input-number(min=0, max=5000, step=100) └── 实时分配预览 ├── el-card (人工方案配置 — EPIC 9) │ ├── el-form-item: 平台佣金率 → el-input-number(min=0, max=10000, step=500) │ │ └── 注记:方案价 × 佣金率 = 平台收入,剩余结算给能量师 │ ├── el-form-item: 分销佣金占比(直接) → el-input-number(min=0, max=10000, step=500) │ │ └── 注记:从平台佣金中提取 × 此比例 = 直接上级佣金 │ ├── el-form-item: 分销佣金占比(上级) → el-input-number(min=0, max=5000, step=100) │ │ └── 注记:从平台佣金中提取 × 此比例 = 上上级佣金 │ ├── el-form-item: 方案最低价 → el-input-number(min=0, max=999999, step=100) │ ├── el-form-item: 方案最高价 → el-input-number(min=0, max=999999, step=100) │ │ └── 实时预览示例:方案价¥299→佣金¥89.70→能量师¥209.30→分销¥17.94 │ └── el-form-item: 最大协商轮次 → el-input-number(min=1, max=20, step=1) ``` **表单校验规则:** | 字段 | 校验规则 | |------|---------| | 所有金额(元) | > 0,整数,≤ 999999 | | 比例(%) | 0-100,整数 | | 名额上限 | ≥ 1,≤ 99999 | --- #### 8.1.5 种子数据 SQL ```sql INSERT INTO `sys_config` (`config_key`, `config_value`, `description`, `value_type`) VALUES ('pricing.practitioner.seed', '131400', '能量师种子价(分)', 'price'), ('pricing.practitioner.standard', '198600', '能量师标准价(分)', 'price'), ('pricing.practitioner.seed_limit', '300', '种子价名额上限', 'int'), ('pricing.practitioner.seed_period_end', '2027-05-31T23:59:59', '种子价有效期截止时间(ISO 8601)', 'datetime'), ('pricing.annual', '13100', 'C端年费(分)', 'price'), ('commission.practitioner.l1', '50000', 'B端一级佣金-能量师推荐(分)', 'price'), ('commission.practitioner.l1_cend', '20000', 'B端一级佣金-C端推荐(分)', 'price'), ('commission.practitioner.l2', '10000', 'B端二级佣金(分)', 'price'), ('commission.annual.direct_rate', '4000', 'C端直接佣金比例(万分比)', 'percent'), ('commission.annual.upstream_rate', '500', 'C端上级佣金比例(万分比)', 'percent'), ('commerce.category.practitioner_plan.commission_rate', '3000', '人工方案平台佣金率(万分比)', 'percent'), ('commission.practitioner_plan.referral_rate', '2000', '人工方案分销佣金占比(万分比)', 'percent'), ('commission.practitioner_plan.upstream_rate', '500', '人工方案上级佣金占比(万分比)', 'percent'), ('plan_request.enabled', 'true', '人工方案功能开关', 'bool'), ('plan_request.max_negotiation_rounds', '5', '最大协商轮次', 'int'), ('plan_request.auto_cancel_hours', '72', '协商超时自动取消(小时)', 'int'), ('plan_request.min_price', '5000', '方案最低价(分)', 'price'), ('plan_request.max_price', '999900', '方案最高价(分)', 'price'); ``` --- #### 8.1.6 测试用例 | # | 场景 | 步骤 | 预期 | |---|------|------|------| | 1 | 初始加载 | 打开系统配置页 | 17个字段显示正确的默认值 | | 2 | 修改定价 | 种子价改为 ¥2,000 → 保存 | 下次 `POST /api/pricing/current` 返回 seedPrice=200000 | | 3 | 修改佣金 | L1 改为 ¥600 → 保存 | 新订单的佣金 amount=60000 | | 4 | 比例联动 | 直接佣金改为 50% | 下方显示"每笔¥131:直接¥65.50 / 上级¥6.55 / 平台¥58.95" | | 5 | 比例联动反向 | 种子价改为 ¥2,000 | B端卡片"L1占种子价%"从38.1%变为25.0% | | 6 | 输入校验 | 在金额框输入 -100 | el-input-number 阻止输入,值保持为 0 | | 7 | 保存失败 | 网络断开后保存 | 显示"保存失败",输入值保留不丢失 | | 8 | 保存后立即生效 | 改种子价为 ¥2,000 → 保存 → 新用户打开支付页 | 显示 ¥2,000 | | 9 | 缓存同步 | API 直接改 DB → 调用 getInt() | 返回值仍为旧值(下次 loadAllToCache 或调用 updateConfig 后更新) | | 10 | 种子名额展示 | 3笔已支付 practitioner 种子价订单 | 显示"已使用:3 / 300" | | 11 | 种子有效期展示 | 有效期设为未来日期 | 显示"🟢 有效期剩余 XX 天" | | 12 | 种子有效期过期 | 有效期设为过去日期 | 显示"🔴 已过期" | --- ### US-8.2 订单管理 **作为** 运营管理员 **我希望** 查看所有订单记录,按产品类型和状态筛选 **以便** 核对收入和对账 --- #### 8.2.1 验收标准 **后端 API(补全 `AdminController.listOrders()`):** 1. `POST /api/admin/orders` → 接收筛选参数,返回分页订单列表 ```json // Request: { "productType": "", // 筛选:"" 全部 | "practitioner" | "annual" "status": "", // 筛选:"" 全部 | "paid" | "pending" "page": 1, "pageSize": 20 } // Response: { "code": 0, "data": { "list": [ { "id": 1, "userId": 42, "outTradeNo": "202605280001", "totalFee": 131400, "productType": "practitioner", "isSeedPrice": true, "status": "paid", "payType": "wxpay", "paidAt": "2026-05-28T10:00:00", "createdAt": "2026-05-28T09:59:00" } ], "total": 128, "page": 1, "pageSize": 20 } } ``` 2. 后端在 `OrderRepository` 中新增分页+动态筛选查询方法(使用 `Specification` 或 `@Query` 动态拼接) **前端页面:** 3. 表格列:ID、订单号、用户ID、产品类型("能量师" / "年费")、金额(¥格式)、种子价标记、状态(已支付/待支付)、支付时间 4. 产品类型使用 el-tag 展示:能量师→蓝色,年费→绿色 5. 种子价订单显示 🌱 种子 el-tag(橙色),hover 提示"通过创始人码注册" 6. 状态使用 el-tag:已支付→success,待支付→warning 7. 表格上方的工具栏包含两个筛选器: - 产品类型:el-select(全部 / 能量师 / C端年费) - 状态:el-select(全部 / 已支付 / 待支付) 8. 筛选器 change 时重新请求接口 9. 底部分页组件:显示总条数,支持切换页码 **当前代码待补全:** - `AdminController.listOrders()` 目前返回 `Result.success(null)` → 需替换为真实数据 - `Order` 实体和 `OrderRepository` 已有,但缺 `productType` 和 `isSeedPrice` 字段 → 需新增 - 订单表 `orders` 需增加 `product_type` 和 `is_seed_price` 列 - 分页需引入 `Pageable` + `Specification`(或简单用 `PageRequest`) --- #### 8.2.2 后端类变更 | 类名 | 变更 | 说明 | |------|------|------| | `Order.java` | 新增字段 | `productType` (String)、`isSeedPrice` (Boolean) | | `OrderRepository` | 新增方法 | `countByProductTypeAndStatus(productType, status)` | | `OrderRepository` | 新增方法 | `findAll(Specification, Pageable)` 或 `findByProductTypeAndStatus` | | `AdminController` | 重写 `listOrders()` | 支持筛选+分页 | --- ### US-8.3 佣金查看 **作为** 运营管理员 **我希望** 查看所有佣金记录和统计 **以便** 了解分销推广效果 --- #### 8.3.1 验收标准 **后端 API(增强现有):** 1. `POST /api/admin/commissions` → 支持分页和按级别筛选 ```json // Request: { "level": 0, // 0: 全部 | 1: L1 | 2: L2 "page": 1, "pageSize": 20 } // Response: { "code": 0, "data": { "list": [ { "id": 1, "orderId": 101, "fromUserId": 42, "toUserId": 10, "level": 1, "amount": 50000, "status": "settled", "remark": "直接推荐升级能量师", "createdAt": "2026-05-28T10:00:00" } ], "total": 56, "page": 1, "pageSize": 20, "summary": { "settledTotal": 350000, // 已结算总佣金(分) "pendingTotal": 0, // 待结算总佣金(分,Phase 1 始终为 0) "grandTotal": 350000, // 总佣金(分) "countByLevel": { // 各级别笔数 "1": 12, "2": 8 } } } } ``` 2. 当前 `AdminController.listAllCommissions()` 返回 `commissionRepository.findAll()`(无分页)→ 需重写 **前端页面(增强现有 `commissions/index.vue`):** 3. 顶部统计卡片(已有框架)→ 增强: - 已结算佣金:¥3,500.00(绿色) - 待结算佣金:¥0.00(黄色,Phase 1 恒为 0,保留字段显示) - 总佣金:¥3,500.00 - **新增第四个卡片**:总笔数(L1: 12 笔 / L2: 8 笔) 4. 表格列(增强现有):ID、订单ID、来源用户ID、获得用户ID、级别(L1/L2 el-tag)、金额(¥格式)、备注(remark 字段)、状态、创建时间 5. 新增级别筛选器:el-select(全部 / L1 / L2),筛选时重新请求接口 6. 底部分页:当前 `:total="commissions.length"` 是前端假分页,改为后端真分页 7. 备注列显示 remark 内容(如"直接推荐升级能量师"、"C端年费直接推荐佣金 (40%)") --- #### 8.3.2 前后端差距分析 | 当前状态 | 目标状态 | 工作量 | |---------|---------|--------| | `AdminController.listAllCommissions()` 无分页,无筛选 | 分页 + level 筛选 + summary 统计 | M | | `CommissionRepository` 无分页方法 | 新增 `findAll(Specification, Pageable)` | S | | 前端假分页(全量拉取) | 后端真分页 | M | | 前端无 level 筛选 | 新增 el-select 筛选器 | S | | 前端无备注列 | 新增 remark 列 | S | | 前端无笔数统计卡片 | 新增第四个统计卡片 | S | --- ### US-8.4 提现管理(推迟至 Phase 2) **已决策:推迟,当前不做。** --- ## 开发优先级总表 | 用户故事 | 优先级 | 预估工期 | 依赖 | |----------|--------|---------|------| | US-1.1 生日输入与咨询发起 | P0 | 3天 | US-5.1(需登录)| | US-1.2 三角形可视化 | P0 | 3天 | US-1.1 | | US-1.4 数字点击查看含义 | P2 | 1天 | US-1.2 | | US-2.1 分享图片 | P0 | 2天 | US-1.2 | | US-2.2 PDF导出 | P0 | 2天 | US-1.2 | | US-3.1 AI解读展示 | P0 | 2天 | US-1.1 + Dify Workflow配置(附录B) | | US-3.2 AI解读免费/付费控制 | P0 | 1天 | US-3.1 | | US-3.3 解读内容缓存 | P0 | 1天 | US-3.1 | | US-3.4 AI交互问答(免费/付费控制) | P0 | 2天 | US-1.1 + Dify Chatflow配置(附录B) | | US-4.1 能量师付费订阅 | P0 | 2天 | US-5.1 | | US-4.2 种子价自动判断 | P0 | 1天 | US-8.1 | | US-4.3 订阅状态与续费 | P0 | 1天 | US-4.1 | | US-4.4 年费升级能量师(升级定价) | P1 | 1.5天 | US-4.1 + US-4.2 | | US-5.1 微信登录+注册信息完善 | P1 | 3天 | 无 | | US-5.2 历史咨询记录 | P1 | 2天 | US-5.1 | | US-5.3 个人资料编辑(社交预留) | P2 | 1天 | US-5.1 | | US-6.1 推广码生成 | P0 | 1天 | US-4.1 | | US-6.2 分享带参注册与上下级绑定 | P0 | 2天 | US-5.1 | | US-6.3 B端佣金结算(含升级补差+C端半额) | P0 | 3天 | US-4.1 + US-6.2 + US-4.4 + US-8.1 | | US-6.4 C端佣金结算 | P0 | 1天 | US-6.6 + US-6.3 + US-8.1 | | US-6.5 分销面板 | P1 | 2天 | US-6.1 + US-6.3 | | US-6.6 C端年费订阅入口 | P0 | 1.5天 | US-3.2 + US-4.1 | | US-7.1 批注功能 | P2 | 2天 | P0完成 | | US-7.2 客户分组 | P2 | 2天 | P0完成 | | US-8.1 系统配置管理 | **P0** | **2天** | **无(独立基础设施)** | | US-8.2 订单管理 | P1 | 1.5天 | US-4.1 + Order 新增字段 | | US-8.3 佣金查看 | P1 | 1天 | US-6.3 | | US-9.1 学业方向AI解读 | P1 | 2天 | US-1.1 + Dify学业Workflow | | US-9.2 申请能量师出方案 | P1 | 1.5天 | US-5.1 + US-9.1 | | US-9.3 能量师介入AI会话 | P1 | 2天 | US-5.1 + US-3.4 | | US-9.4 方案价格协商 | P1 | 1.5天 | US-9.3 | | US-9.5 方案付费与交付 | P1 | 2天 | US-9.4 + CommerceService 接口 | | US-9.6 人工方案分销佣金 | P1 | 1天 | US-9.5 + US-8.1 | --- ## 咨询+命盘依赖图 ``` US-5.1 微信登录 │ ▼ US-1.1 生日输入与咨询发起(后端查重(userId+birthday)→确认弹窗→后台计算→返回展示) │ ├──► US-1.2 三角形可视化(24位置新命名 I-X) ├──► US-3.4 AI交互问答(每日3轮,绑定当前命盘) │ ▼ US-5.2 历史咨询记录(按 userId+birthday 唯一归组,可恢复聊天历史) ``` ## 分销相关依赖图 ``` US-8.1 系统配置管理(独立基础设施——最先完成) │ ├──► US-4.2 种子价判断 ├──► US-6.3 B端佣金(读取 L1/L2 金额) └──► US-6.4 C端佣金(读取直接/上级比例) US-5.1 微信登录 │ ▼ US-6.2 分享带参注册(需要 openid) │ ▼ 绑定上下级关系 │ US-4.1 能量师付费 + US-6.6 C端年费订阅 │ │ ▼ ▼ US-6.1 推广码生成 US-6.4 C端佣金结算 │ │ ▼ │ US-6.3 B端佣金结算 ◄──────────────┘(共享 paySuccess() 分支) │ ▼ US-6.5 分销面板(前端展示所有数据) ``` ## EPIC 9 依赖图(学业方向+人工方案) ``` US-8.1 系统配置(人工方案参数——独立基础设施) │ ▼ US-9.1 学业方向AI解读(Dify学业Workflow) │ ▼ US-9.2 申请能量师出方案 │ ├──(有上级能量师)──► US-9.3 能量师介入AI会话 │ │ └──(无上级)→ 提示找推荐链接 │ ▼ US-9.4 方案价格协商 │ ▼ US-9.5 方案付费与交付 │ │ │ ▼ │ US-9.6 人工方案分销佣金 │ ▼ CommerceService 接口层(lilishop预留) ``` ## 排期建议 | 周次 | 交付内容 | |------|---------| | **Week 1** | US-5.1 登录+注册完善 → US-8.1 系统配置(独立基础设施) | | **Week 2** | US-1.1 咨询发起(依赖US-5.1登录) → US-1.2 三角可视化 → US-1.3 计算规则实现(附录A Step 1-6) | | **Week 3** | US-3.1 AI解读 + Dify Workflow配置(附录B) → US-3.3 缓存 → US-3.2 免费/付费控制 | | **Week 4** | US-3.4 AI问答 + Dify Chatflow配置(附录B) → US-2.1 分享图片 → US-2.2 PDF导出 | | **Week 5** | US-4.1 能量师付费 + US-4.2 种子价 + US-6.6 C端订阅入口 → **US-9.1 学业Dify Workflow配置** | | **Week 6** | US-6.2 带参注册 → US-6.1 推广码 → US-6.3 B端佣金(含升级补差) | | **Week 7** | US-6.4 C端佣金 → US-6.5 分销面板 + US-8.2 订单管理 → **US-9.2 需求单 + US-9.3 介入会话** | | **Week 8** | 联调测试 + US-8.3 佣金查看 + US-4.3 订阅状态/续费 + US-4.4 升级能量师 + BUG修复 | | **Week 9+** | **US-9.4 价格协商 + US-9.5 付费交付 + US-9.6 分佣 → CommerceService 接口层** | > **Phase 1 总预估:约 8 周(含联调测试)** > 排期原则:先做核心流程(登录→命盘→AI),再做支付分销。US-8.1(配置系统)作为基础设施优先完成,保障后续所有定价依赖。 > P2 功能(US-1.4 数字点击含义 / US-5.3 资料编辑 / US-7.x 批注与标签)可在 Phase 1 后期或 Phase 2 迭代。 --- ## 附录A:【新计算规则】代码文件变更清单 > 以下清单基于新命名体系(A-X)和咨询会话管理需求,列出所有需修改的代码文件及变更内容。 ### A.1 后端 Java | 文件 | 变更类型 | 变更内容 | |------|---------|---------| | `CalculatorService.java` | 重写 | 内部7位从 `F,G,H,I,M,N,O` → `I,J,K,L,M,N,O`;新增外部9位 `P,Q,R,S,T,U,V,W,X` 的独立计算;新增 `calculateFullTriangle()` 返回全部24个位置 | | `ChartService.java` | 改造 | 新增 `startConsultation(userId, birthday, name, questions)` 方法,含:①查 userId+birthday 是否已有记录;②已有则直接返回+聊天历史;③无则服务器端调 `CalculatorService` 计算+创建记录+可选首次AI解读 | | `ChartController.java` | 改造 | 保留 `/api/chart/create`(兼容旧版);**新增 `POST /api/consultation/start` → 调 `ChartService.startConsultation()`** | | `ChartRecord.java` | 微调 | 新增 `lastInteractionAt` 字段(`LocalDateTime`,用于列表排序);加 `@Table(uniqueConstraints=...)` 定义 `(userId, birthday)` 联合唯一 | | `ChartRecordRepository.java` | 新增 | `Optional findByUserIdAndBirthday(Long userId, String birthday)` | | `UserService.java` | 微调 | `checkDailyQuota()` 和 `consumeQuota()` 已有逻辑不变(3次/天全局计数),但新增注释明确"非VIP每日3次绑定当前命盘,切换生日不重置" | | `ProfileController.java` | 微调 | 咨询列表接口按 `lastInteractionAt` 倒序 | | `DifyService.java` | **新增** | Dify API 客户端,封装 `POST /v1/workflows/run`(命盘解读)和 `POST /v1/chat-messages`(AI问答)两个接口;含签名、错误重试、超时处理,详见**附录B** | | `ChatService.java` | 改造 | 原直调 LLM 逻辑改为调 `DifyService`;解读生成走 `DifyService.runWorkflow()`;问答走 `DifyService.sendChatMessage()`;在调用前检查配额(US-3.4) | ### A.2 前端 Vue / JS | 文件 | 变更类型 | 变更内容 | |------|---------|---------| | `client/utils/calculator.js` | 废弃/降级 | `calculateTriangle()` 保留为离线兜底,不再作为主路径调用;内部命名改为 `I,J,K,L,M,N,O`;新增 `calculateFullTriangle()` 含外部 P-X;主入口加注释 `@deprecated 请使用后端计算` | | `client/stores/chart.js` | 改造 | `computeChart()` 不再调 `calculateTriangle()`,改为调 `consultationApi.start()`;返回数据中的 `positions` 直接写入 `currentChart`;不再调 `analyzeTriangle()`(由后端返回) | | `client/components/TriangleChart.vue` | 重写 matrix() | `innerBottom` 从 `[d.F,d.G,d.H,d.I]` → `[d.I,d.J,d.K,d.L]`;`outerLeft` 从 `{main:d.M, sub:[d.F,d.G]}` → `{main:d.R, sub:[d.P,d.Q]}`;`outerRight` 从 `{main:d.N, sub:[d.H,d.I]}` → `{main:d.U, sub:[d.S,d.T]}`;`outerTop` 从 `{main:d.O, sub:[d.M,d.N]}` → `{main:d.X, sub:[d.V,d.W]}` | | `client/pages/index/index.vue` | 改造 | `onAnalyze()` 改为:①调 `POST /api/consultation/start`(传 birthday + name + questions);②若返回 `isNew=true` 且 `requireConfirmation=true` 则弹确认框;③确认后再次请求;④成功后跳转 `chart/index`;⑤"想了解的问题"加 `maxlength=100` + 实时字数显示 | | `client/utils/api.js` | 新增 | `consultationApi`:`start(data)` → `POST /api/consultation/start`;`detail(id)` → `POST /api/consultation/detail` | | `client/pages/chart/index.vue` | 微调 | 适配新的 API 返回结构(`chartData.positions` 含全部 I-X);`onMounted` 加载历史消息逻辑不变 | | `client/pages/records/index.vue` | 微调 | 列表按 `lastInteractionAt` 排序;详情页恢复聊天历史逻辑不变 | ### A.3 数据库 | 变更 | 说明 | |------|------| | `chart_records` 表加联合唯一索引 | `UNIQUE INDEX uk_user_birthday (user_id, birthday)`,确保同用户+同生日只有一条咨询记录 | | `chart_records` 表加字段 | `last_interaction_at DATETIME`,默认等于 `created_at`,每次 AI 聊天时更新,用于列表排序 | | `chart_records` 表现有字段 | `chart_data` 列存储的 JSON 结构从 7个旧位置 → 16个新位置(内部 I-O + 外部 P-X) | ### A.4 变更执行顺序 ``` Step 1: US-1.3(本对照表)→ 开发团队通读,确保理解命名对应关系 Step 2: 数据库迁移 → 加索引+字段 Step 3: CalculatorService.java → 重写为新命名+外部计算 Step 4: ChartService.java + ChartController.java → 新增 startConsultation() Step 5: stores/chart.js + api.js → 前端调后端新接口(主路径走后端;后端不可用时 calculator.js 降级兜底,仅展示离线计算版命盘,不提供AI解读和咨询记录) Step 6: TriangleChart.vue → matrix() 改用新命名+独立外部值 Step 7: pages/index/index.vue → 确认弹窗 + 100字限制 Step 8: Dify 配置 → 8a. 在 Dify 平台创建 Workflow(命盘解读)和 Chatflow(AI问答) 8b. 上传知识库文档(数字能量学语料) 8c. 发布工作流,获取 API Key Step 9: DifyService.java + ChatService.java → 集成 Dify API Step 10: 联调 → 前后端打通 end-to-end,含 Dify 工作流调试验收 ``` --- ## 附录B:Dify AI 架构设计 > Dify 是开源 LLM 应用开发平台,提供可视化工作流编排、RAG 知识库、模型管理等能力。 > 本项目使用 Dify 的两个应用类型:**Workflow**(命盘解读生成)和 **Chatflow**(AI交互问答)。 ### B.1 整体架构 ``` ┌─────────────────────────────────────────────────────────┐ │ 微信小程序前端 │ │ POST /api/chart/interpret POST /api/chat/send │ └────────────────────┬─────────────────────┬──────────────┘ │ │ ┌────────────────────▼─────────────────────▼──────────────┐ │ Java 后端 (Spring Boot) │ │ │ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ChatController│ │ChartController│ │ChatService │ │ │ └──────┬──────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ └──────────┬──────┘ │ │ │ │ │ │ │ ┌──────────▼──────────┐ ┌───────────▼────────┐ │ │ │ DifyService.java │ │ DifyService.java │ │ │ │ runWorkflow() │ │ sendChatMessage() │ │ │ └──────────┬──────────┘ └───────────┬─────────┘ │ └────────────────────┼─────────────────────────┼───────────┘ │ │ ┌────────────────────▼─────────────────────────▼───────────┐ │ Dify API (自建部署 / SaaS) │ │ │ │ ┌──────────────────┐ ┌──────────────────────┐ │ │ │ Workflow(解读) │ │ Chatflow(问答) │ │ │ │ │ │ │ │ │ │ 开始 → 知识检索 │ │ 开始 → 接收消息 │ │ │ │ → LLM生成解读 │ │ → 知识检索 → LLM回答 │ │ │ │ → 格式化输出 │ │ → 返回答案 │ │ │ └────────┬─────────┘ └──────────┬───────────┘ │ │ │ │ │ │ └──────────┬───────────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ 知识库(RAG) │ │ │ │ ├─ 主性格含义库 │ │ │ │ ├─ 组合数字含义库 │ │ │ │ ├─ 三区年龄解读库 │ │ │ │ └─ 常见命盘案例库 │ │ │ └─────────────────────┘ │ └──────────────────────────────────────────────────────────┘ ``` **关键原则:** - Java 后端是**最薄的一层**,只做:参数校验 → 配额检查 → 调用 Dify API → 透传结果 - 所有 AI 逻辑(prompt 设计、知识检索策略、输出格式)都在 Dify 工作流中配置 - 修改解读模板或知识内容不需要改 Java 代码,只需要在 Dify 平台更新工作流 ### B.2 Dify Workflow(命盘解读)—— 一次性解读生成 **用途:** US-3.1 AI解读展示 **Dify 应用类型:** **Workflow(工作流)**——单轮执行,无需维护对话历史 **触发方式:** API 调用,blocking 模式 **输入变量:** | 变量名 | 类型 | 说明 | |--------|------|------| | `chart` | `object` | 24个命盘位置的值 `{ A:8, B:1, ..., X:5 }` | | `name` | `string` | 用户姓名 | | `birthday` | `string` | 出生日期 `YYYY-MM-DD` | **工作流节点编排:** ``` [开始节点] ↓ [知识检索 1] 查主性格(O)含义 ↓ [知识检索 2] 查左区(P,R)21-40岁解读 ↓ [知识检索 3] 查顶部(V,X)41-60岁解读 ↓ [知识检索 4] 查右区(S,U)61+岁解读 ↓ [知识检索 5] 查特殊组合(如 I+J=11, M+O 等) ↓ [LLM 节点] 系统提示词: "你是一个专业的数字能量学分析师。 根据以下命盘数据和检索到的知识,生成完整的命盘解读。 必须严格按照以下结构输出..." ↓ [代码节点 / 输出解析]:确保输出 JSON 结构正确 ↓ [结束节点] → 输出 `{ sections: [...], summary: "..." }` ``` **输出格式(`data.outputs`):** ```json { "sections": [ { "title": "主性格解读", "position": "O", "value": 6, "content": "主性格数字 6 代表…", "keywords": ["责任心", "家庭", "关爱"] }, { "title": "左区(21-40岁)", "positions": ["P", "Q", "R"], "values": [8, 3, 2], "content": "左区数字组合 8-3-2 代表…" }, { "title": "顶部(41-60岁)", "positions": ["V", "W", "X"], "values": [4, 1, 5], "content": "顶部数字组合 4-1-5 代表…" }, { "title": "右区(61+岁)", "positions": ["S", "T", "U"], "values": [6, 8, 5], "content": "右区数字组合 6-8-5 代表…" } ], "summary": "整体命盘评价…", "combination_notes": ["数字 I(4)+J(3)=7,形成…", "M(7)+O(6)=13→4,形成…"] } ``` ### B.3 Dify Chatflow(AI交互问答)—— 多轮对话 **用途:** US-3.4 AI交互问答 **Dify 应用类型:** **Chatflow(对话工作流)**——多轮对话,自动维护 `conversation_id` **触发方式:** API 调用,streaming 模式(打字机效果) **第一轮消息传入的上下文变量:** ```json { "inputs": { "chart": { "A":8, "B":1, "C":9, "D":6, "E":3, "F":0, "G":1, "H":4, "I":4, "J":3, "K":6, "L":5, "M":7, "N":2, "O":6, "P":8, "Q":3, "R":2, "S":6, "T":8, "U":5, "V":4, "W":1, "X":5 }, "user_name": "张三", "birthday": "1996-03-14" }, "query": "我的主性格数字6代表什么?", "user": "uid_123", "response_mode": "streaming" } ``` **后续轮次:** 只传 `query` + `conversation_id`,无需重复传入命盘数据 **Chatflow 节点编排:** ``` [开始节点] ↓ [问题分类节点] ├─ 问数字含义 → [知识检索] → [LLM 回答] ├─ 问运势建议 → [知识检索] → [LLM 回答] └─ 其他问题 → [LLM 直接回答] ↓ [LLM 节点] 系统提示词: "你是一个数字能量学咨询助手。 用户的命盘数据已作为上下文变量传入。 请基于用户的命盘数据和知识库内容回答问题。 如果问题涉及具体数字,请引用该数字在命盘中的位置和含义。" ↓ [结束节点] → 输出答案文本 ``` ### B.4 知识库设计 需要在 Dify 平台创建的知识库文档: | 文档名称 | 内容 | 用途 | |----------|------|------| | **数字能量学基础** | 数字 1-9 的基础能量含义、吉凶属性 | LLM 基础理解 | | **主性格数字解读** | 每个主性格数字(1-9)的详细性格特质、优缺点、代表人物 | 主性格解读 | | **位置含义对照表** | 每个位置(A-X)代表的人生领域和能量属性 | 各章节解读 | | **组合数字解读** | 常见数字组合(如11/22/33/13/14等)的特殊含义 | 组合分析 | | **三区年龄段解读** | 21-40 / 41-60 / 61+ 三个年龄段的典型命理模式 | 三区解读 | | **常见命盘案例** | 典型命盘 + 完整解读示例(Few-shot 示例) | 提升解读质量 | **文档格式建议:** - 每个文档的结构化程度越高越好,推荐 Markdown 表格 + 标题层级 - 示例格式: ```markdown # 主性格数字 7 ## 核心特质 - 关键词:分析力、哲学思维、追求真理 - 能量属性:精神导向,向内探索 ## 性格描述 主性格数字7的人天生具有强烈的分析能力和探索精神… ## 优势 - 逻辑思维强 - 善于发现规律 - 独立自主 ## 劣势 - 容易多疑 - 社交上偏孤僻 - 过度分析导致行动迟缓 ## 代表人物 - 爱因斯坦、达芬奇 ``` ### B.5 Java 后端集成 **`DifyService.java` 核心接口:** ```java @Service public class DifyService { // 配置(从 application.yml 读取) @Value("${dify.api-base}") private String apiBase; // e.g. "https://api.dify.ai/v1" @Value("${dify.workflow-api-key}") private String workflowApiKey; // 命盘解读 Workflow 密钥 @Value("${dify.chatflow-api-key}") private String chatflowApiKey; // AI问答 Chatflow 密钥 /** * 调用 Dify Workflow 生成命盘解读(US-3.1) * @param chartData 24个数字的 Map * @param userName 用户姓名 * @param birthday 出生日期 * @return 结构化解读结果(sections + summary) */ public InterpretationResult runInterpretation( Map chartData, String userName, String birthday ) { // 1. 构造 inputs // 2. POST /v1/workflows/run (blocking) // 3. 解析 data.outputs 为 InterpretationResult // 4. 超时 15s,重试 1 次 } /** * 调用 Dify Chatflow 发送消息(US-3.4) * @param chartData 24个数字(仅首轮传入) * @param conversationId 已有会话ID(后续轮次) * @param query 用户问题 * @param userId 用户标识 * @param isFirstRound 是否首轮 * @return Dify 流式响应或阻塞响应 */ public ChatResponse sendChatMessage( Map chartData, String conversationId, String query, String userId, boolean isFirstRound ) { // 1. 首轮时传入 inputs(chart 数据) // 2. POST /v1/chat-messages (streaming) // 3. 返回 SSE 流或阻塞结果 } } ``` **`ChatService.java` 改造要点:** ```java @Service public class ChatService { public ChatResponse sendMessage(Long userId, Long chartId, String message) { // 1. 检查用户配额(US-3.4) if (!isVip(userId) && getTodayQuota(userId) >= 3) { throw new BusinessException("今日AI问答次数已用完"); } // 2. 获取命盘数据 ChartRecord record = chartRecordRepository.findById(chartId); Map chartData = record.getChartData(); // 3. 调用 Dify(Chatflow) String conversationId = record.getDifyConversationId(); boolean isFirstRound = (conversationId == null); ChatResponse response = difyService.sendChatMessage( chartData, conversationId, message, userId.toString(), isFirstRound ); // 4. 首次对话保存 conversationId if (isFirstRound) { record.setDifyConversationId(response.getConversationId()); chartRecordRepository.save(record); } // 5. 消耗配额 consumeQuota(userId); return response; } } ``` **`application.yml` 新增配置:** ```yaml dify: api-base: https://api.dify.ai/v1 workflow-api-key: app-xxxxx # 命盘解读 Workflow chatflow-api-key: app-yyyyy # AI问答 Chatflow timeout: 15000 # 单次调用超时 15s ``` ### B.6 Dify 部署模式建议 | 模式 | 适用阶段 | 说明 | |------|---------|------| | **Dify SaaS(cloud.dify.ai)** | 开发/测试 | 快速上手,无需自建,有免费额度 | | **自建部署(Docker)** | 生产 | 数据不出域,可控成本,建议生产环境使用 | 自建部署参考:`https://github.com/langgenius/dify` ### B.7 开发流程建议 1. **先配知识库**:在 Dify 平台创建知识库,上传数字能量学语料文档 2. **再搭 Workflow**:创建命盘解读工作流,配置节点链,用测试数据调通 3. **再搭 Chatflow**:创建 AI 问答对话工作流,测试多轮对话 4. **Java 集成**:开发 `DifyService.java`,调通 Workflow 和 Chatflow 两个 API 5. **联调验收**:前端通过后端调用 Dify,验证解读质量和问答效果 ### B.8 与 US 的对应关系 | Dify 组件 | 对应 US | 备注 | |-----------|---------|------| | Workflow(命盘解读) | US-3.1 AI解读展示、US-3.2 付费控制 | Workflow 输出由 US-3.2 决定是否全文展示 | | **Workflow(学业方向)** | **US-9.1 学业方向AI解读** | **新增专用Workflow,输入24个A-X数字,输出学业方向分析JSON** | | Chatflow(AI问答) | US-3.4 AI交互问答 | 配额控制在 Java 后端,不经过 Dify;能量师介入消息不走Dify | | 知识库 | US-3.1、US-3.4、US-9.1 | 三个工作流共用同一套知识库 | | 无(纯后端) | US-3.3 解读内容缓存、US-9.5 CommerceService | 缓存逻辑在 Java 后端,不涉及 Dify | --- ## 附录C:三类用户角色权限对照表 > 覆盖系统所有功能的权限边界,按角色逐一对比。 ### C.1 概览 | | 普通用户 | C端年费用户 | 能量师用户 | |--|---------|------------|-----------| | **年费** | ¥0 | ¥131 | ¥1,314(种子)/ ¥1,986(标准) | | **数据库标记** | `vipType=NULL` | `vipType='annual'` | `vipType='practitioner'` | | **到期降级** | — | → 普通用户 | → 普通用户 | | **续费价格** | — | 标准价 ¥131 | 标准价 ¥1,986(种子价仅限创始人码首次购买) | | **定位** | 浏览体验 | 给自己看,轻度社交 | 给客户看,商业工具 | ### C.2 功能权限总表 ``` 能力 普通用户 C端¥131 能量师¥1,314+ ───────────────────────── ────────── ────────── ────────────── 生成命盘 ✅ ✅ ✅ 查看命盘可视化 ✅ ✅ ✅ 数字点击查看含义 ✅ ✅ ✅ AI解读·主性格概要 1次/天 不限 不限 AI解读·完整五区 ❌ ✅ ✅ AI问答互动 3轮/天 不限 不限 学业方向AI解读 1次/天 不限 不限 分享图片到微信 1次/天 不限 不限 导出PDF报告 ❌ 不限 不限 历史记录查看 近7天 全部 全部 推广码+分销面板 ❌ ✅ ✅ 推广C端年费得佣金 ❌ 40%+5%分成 40%+5%分成 推广能量师年费得佣金 ❌ ❌ ¥500 + ¥100 申请能量师出方案 ✅ ✅ ✅ 能量师介入AI会话 ❌(被动) ❌(被动) ✅(主动) 接受人工方案付费 ✅ ✅ ✅ 命盘批注(P2) ❌ ✅ ✅ 客户标签分组(P2) ❌ ❌ ✅ 个人资料编辑 ✅ ✅ ✅ 个人中心标签 "普通用户" "C端会员" "能量师" 订阅引导卡片 可见 不可见 不可见 续费提醒 — 到期前7天 到期前7天 ``` ### C.3 按场景的用户体验流程 #### 场景 1:首次进入 ``` 所有用户 │ ├─ 微信登录 → 完善资料(昵称/头像/生日/性别) │ └─ 输入生日 → 生成命盘 → 查看命盘图 → 查看主性格概要(免费1次) │ ├─ 遇到付费墙(完整解读/PDF/无限问答) │ └─ 看到 ¥131 开通引导卡片 │ └─ 保持免费用户,每日 3 轮问答 + 1 次分享 ``` #### 场景 2:升级为 C端年费(¥131) ``` 普通用户点击"开通 ¥131" │ ├─ 微信支付 ¥131 ├─ vipType → 'annual', vipEndTime → +365天 ├─ 推广码自动生成 │ └─ 解锁能力: ├─ AI完整解读(无限次) ├─ AI问答(不限轮数) ├─ 分享图片(不限次) ├─ PDF导出(不限次) ├─ 历史记录(全部) ├─ 分销面板可见 └─ 推广C端年费赚 40% + 5% ``` #### 场景 3:升级为能量师(¥1,314 / ¥1,986) ``` 用户点击"开通能量师" │ ├─ 通过创始人码注册 + 有效期内 + 名额未满 → 种子价 ¥1,314 ├─ 其他情况 → 标准价 ¥1,986 ├─ 微信支付 → vipType → 'practitioner', vipEndTime → +365天 ├─ 推广码自动生成(若首次付费) │ └─ 解锁能力(在C端基础上增加): ├─ 推广能量师年费赚 ¥500 + ¥100 ├─ 客户标签分组(P2) └─ 命盘批注(P2,C端也有但用途不同) ``` ### C.4 关键决策点 **能量师 vs C端——AI能力完全一致。** 两者在AI解读和问答上没有任何区别(都不限次、完整内容),差异只在于: | 维度 | C端¥131 | 能量师¥1,314+ | |------|---------|--------------| | 分销产品 | 仅 C端年费 | C端年费 + 能量师年费 | | 佣金模式 | 比例(40%+5%) | 比例 + 固定(¥500+¥100) | | 种子价资格 | ❌ | ✅(需扫创始人码+有效期内+名额未满) | | 客户管理 | ❌ 不需要 | ✅ 批注+标签 | | 使用场景 | 自己看命理 | 为客户解读 | 这意味着如果能量师**不打算做推广**,¥1,314+相比¥131的额外价值只有批注和标签(P2功能)。这可能影响高客单价转化策略——需要在能量师权益中强化"商业工具"的价值感知。 **普通用户的命盘创建无限。** 目前仅限制了AI问答3轮/天和AI解读1次/天,但普通用户可创建任意数量的命盘。每个命盘可获得一次免费主性格概要,理论上可通过不停创建新命盘获取多次概要。此场景消耗成本较低(1段文本),暂不设限。 ### C.5 数据库字段 ```sql -- users 表相关字段 vipType: NULL 'annual' 'practitioner' vipEndTime: NULL 2027-05-29 2027-05-29 referralCode: NULL 'A3X7K9' 'B2Y4M8' profile_complete: true/false true/false true/false -- 关联表 ┌─ orders ─────────────────────┐ │ user_id, product_type, │ │ amount, status, invited_by │ └──────────────────────────────┘ ┌─ commissions ────────────────┐ │ order_id, level, amount, │ │ status (pending/settled) │ └──────────────────────────────┘ ``` ### C.6 与 US 映射 | 约束规则 | 所在用户故事 | |---------|------------| | AI解读:普通用户仅主性格概要1次/天 | US-3.2 | | AI问答:普通用户3轮/天全局计数 | US-3.4 | | 分享图片:普通用户1次/天 | US-2.1 | | PDF导出:仅付费用户可用,不限次 | US-2.2 | | 历史记录:普通用户仅7天 | US-5.2 | | 推广码:仅付费后生成 | US-6.1 | | 分销面板:仅付费用户可见 | US-6.5 | | C端佣金:上级须已付费才结算 | US-6.4 | | 批注:仅已付费用户 | US-7.1 | | 标签:仅能量师(practitioner)可用 | US-7.2 | | 学业方向AI解读:普通用户1次/天 | US-9.1 | | 能量师介入AI会话:仅上级能量师可介入 | US-9.3 | | 人工方案分佣:平台留存模型 | US-9.6 |