所属角色:
test-engineer基于:phase1-product-plan.md,phase1-user-stories.md,system-spec.md, 实际代码实现(Java Spring Boot + UniApp) 版本: v3.0 覆盖范围: Phase 1 全部 8 个 EPIC(含 P0/P1/P2) 总用例数: ~290+
| 层 | 范围 | 方法 | 工具 |
|---|---|---|---|
| 单元测试 | 计算引擎 calculator.js + CalculatorService.java | 纯函数,输入→输出比对 | Jest / JUnit 5 |
| 组件测试 | TriangleChart 组件渲染、AI对话交互 | UI 组件渲染 + 交互 | Vitest + happy-dom |
| 集成测试 | Controller 层 35+ 端点 | HTTP 请求→响应验证 | MockMvc / Postman |
| 集成测试 | Service 层(UserService/OrderService/CommissionService) | 模拟 Repository 验证业务逻辑 | JUnit + Mockito |
| E2E | 核心用户路径 | 真机+模拟器全链路 | Playwright / 手动 |
| 探索测试 | 边界/异常/竞态 | 破坏性思维设计测试用例 | 手动 |
| 等级 | 定义 | 响应要求 |
|---|---|---|
| S0-Critical | 核心链路阻塞(无法生成命盘/支付/AI对话/注册登录) | 立即阻塞发布 |
| S1-Major | 功能严重异常(计算错误/状态错乱/佣金算错/数据丢失/权限越界) | 24h内修复 |
| S2-Minor | 功能可用但体验缺陷(UI错位/文案错误/降级文案不佳) | 下一迭代 |
| S3-Trivial | 视觉/文案微调建议 | 积攒修复 |
| 项目 | 配置 |
|---|---|
| 小程序基础库 | 3.7.1+ |
| iOS | iPhone 13/14/15/SE, iOS 16+ |
| Android | 华为Mate/P系列、小米数字系列、OPPO Reno, Android 12+ |
| 微信版本 | 8.0+ |
| 后端服务 | Java Spring Boot, 端口 8186 |
| 数据库 | 独立的 test 数据库(非生产) |
| Dify | test 环境 Dify Chatflow API |
| 测试账号 | 账号A(普通用户)、账号B(付费VIP)、账号C(带下级) |
| 支付测试 | 微信沙箱环境 / Mock 模式 |
| 层 | 说明 |
|---|---|
| Controller 9 个 | 35+ 端点,全部 POST JSON |
| Entity 9 个 | User, Order, Commission, Withdraw, ChartRecord, ChatMessage, RatingAnnotation, Tag, SystemConfig |
| Service 12 个 | 含 DifyService 调用外部 AI, CalculatorService 双端计算 |
| 认证 | JWT token(Header Authorization: Bearer) |
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 101.1 | 合法输入生成(客户端计算) | 无 | 输入姓名"张三"、出生日期 1990-01-01 → 点击"生成命盘" | 即时显示三角形命盘页(纯前端计算,无需网络) | S0 |
| 101.2 | 年月日 picker 选择 | 无 | 分别下拉选择年/月/日 | 年份范围1900~当前年、月份1-12、日期随月份联动 | S1 |
| 101.3 | 年份边界-1900 | 无 | 选择1900年 | 可选中,正常生成命盘 | S2 |
| 101.4 | 年份边界-今年 | 无 | 选择当前年份 | 可选中,正常生成命盘 | S2 |
| 101.5 | 月份13 | 无 | 选择月份=13 | picker不可选到13 | S2 |
| 101.6 | 日期32 | 无 | 选择日期=32 | picker不可选到32 | S2 |
| 101.7 | 闰年2/29 | 无 | 年份=2000,月份=2,日期=29 | 合法输入,正常生成 | S1 |
| 101.8 | 姓名为空 | 无 | 不填姓名,直接点生成 | 可生成,命盘显示空字符串 | S2 |
| 101.9 | 不填日期的生成 | 无 | 年月日全空,点生成 | 提示"请填写完整的出生日期" | S2 |
| 101.10 | 未登录可生成 | 未登录 | 输入日期→生成命盘 | 正常生成(纯前端计算),显示结果页 | S0 |
| 101.11 | 保存命盘需登录 | 未登录生成后 | 尝试保存到记录 | 弹出微信登录引导 | S0 |
| 101.12 | 非VIP超配额后保存失败 | VIP非活跃+已生成3次 | 第4次点保存 | API返回1008错误:"今日命盘生成次数已达上限" | S0 |
| 101.13 | VIP用户无限保存 | VIP有效 | 连续保存5次 | 每次均可正常保存(无配额限制) | S0 |
| 101.14 | 跨午夜重置配额 | 非VIP 23:59 消耗3次 | 等到 00:01 后再保存 | 可正常保存(配额重置,lastQuotaDate 更新) |
S1 |
| 101.15 | 服务端同步计算 | 调用 POST /api/chart/create |
传入 birthday, 不传 chartData | 服务端自动通过 CalculatorService 计算 chartData | S1 |
| 101.16 | 客户端/服务端计算结果一致 | 同生日 | 分别客户端 calc 和服务端 calc 对比 | 15个位置 A-O 完全一致 | S1 |
| 101.17 | 弱网下客户端可看结果 | 模拟离线 | 填写后点击生成 | 前端计算正常,显示图表,保存时提示网络错误 | S1 |
| 101.18 | 月份/日期个位数补零 | 月份=1,日期=1 | 生成后检查 | D=1, E=1(正确归约) | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 102.1 | 五层十五格完整绘制 | 生成命盘→chart页 | 5层(A-E, F-I, J-L, M-N, O) 15个位置完整显示 | S0 |
| 102.2 | 布局正确性 | 检查 TriangleChart 组件渲染 | O在顶,M/N在中,F/G/H/I在底,外圈J/L/A/B/D/E | S1 |
| 102.3 | 数字卡片样式 | 检查渲染 | 每个数字用独立卡片展示 | S2 |
| 102.4 | 主性格 O 高亮 | O=主性格数字 | O 位置特殊样式(大号/高亮) | S1 |
| 102.5 | 卓越数高亮 | 构造主性格=11/22/33 | 金底+星标+特殊样式 | S1 |
| 102.6 | 姓名+生日显示 | 命盘上方 | 正确显示"张三 1990-01-01" | S2 |
| 102.7 | 底部日期显示 | 命盘下方 | 显示出生日期的单独数字方框 | S2 |
| 102.8 | 320px屏幕适配 | iPhone SE | 不溢出,数字不重叠 | S1 |
| 102.9 | 414px屏幕适配 | iPhone Plus | 布局自适应 | S2 |
| 102.10 | Android大屏适配 | 1440px宽 | 比例适配,卡片不拉伸变形 | S2 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 103.1 | 点击数字弹出含义 | 点击O(主性格) | 弹出面板:位置名称"主性格"、数字、性格特质 | S1 |
| 103.2 | 点击不同区域数字 | 依次点击F/J/A等 | 显示对应的位置名称和描述(从 CalculatorService.getNumberMeaning/PositionName 获取) | S1 |
| 103.3 | 点击空白关闭面板 | 面板打开后点空白区 | 面板关闭 | S2 |
| 103.4 | 卓越数特殊含义 | 点击主性格=22数字 | 面板显示22的特殊含义("建造大师") | S1 |
当前实现:分享功能仅限推广码复制,无图片分享/PDF导出。另见 EPIC 6 分销推广。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 201.1 | 推广码显示 | 已登录用户 | 个人中心→查看"推广码"区域 | 显示用户的 referralCode | S0 |
| 201.2 | 推广码一键复制 | 点击复制按钮 | 系统提示"复制成功" | S2 | |
| 201.3 | 分享含 ref 参数 | 能量师分享小程序 | 分享路径应包含 ?ref=推广码 |
待实现:当前仅复制文本 | P2 |
架构:Dify Chatflow 对话式 AI。非静态5区解读页面。用户输入问题→AI回复。6轮对话记忆。
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 301.1 | AI 对话入口 | 进入 chart 页 | 底部显示对话输入框 + 快捷问题(职业发展/财运分析/感情运势/健康建议) | S0 |
| 301.2 | 首次进入自动问候 | 生成命盘后进入 chart 页 | AI 发送首条消息,提及主性格数字 | S0 |
| 301.3 | 快捷问题发送 | 点击"职业发展" | 用户消息以快捷问题发送,AI 回复≤5秒 | S0 |
| 301.4 | 自定义输入 | 输入框输入文字→发送 | 正确发送至 Dify, AI 回复正常 | S0 |
| 301.5 | 空输入检查 | 输入框为空点发送 | 不发送,提示"请输入问题" | S2 |
| 301.6 | 加载状态 | 等待 AI 回复 | 显示打字中/loading 动画 | S1 |
| 301.7 | 对话历史持久化 | 发送3轮对话后刷新页面 | 历史消息正确恢复(ChatMessage 表查询) | S0 |
| 301.8 | 对话渲染 | 长文本回复 | 自动换行,可滚动查看 | S1 |
| 301.9 | 6轮记忆验证 | 连续对话6轮 | 第6轮回复能引用第1轮的上下文(Dify 对话记忆) | S1 |
| 301.10 | 滚动到底部 | 新消息到达 | scroll-view 自动滚动到底部 | S2 |
| 301.11 | Dify 不可用时降级 | Dify 服务宕机 | 显示"解读服务暂时不可用,请稍后再试" | S0 |
| 301.12 | 用户消息保存 | 查看 ChatMessage 表 | role='user', content=用户原文, chartRecordId 正确 | S1 |
| 301.13 | AI 回复保存 | 查看 ChatMessage 表 | role='ai', content=AI原文, chartRecordId 正确 | S1 |
| 301.14 | 免责声明 | 对话区域底部 | 显示"本解读由AI生成,仅供参考" | S1 |
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 302.1 | 非VIP每日1次对话限额 | 非VIP已发送1条 | 发送第2条 | 后端返回 1008:"今日AI解读次数已达上限" | S0 |
| 302.2 | VIP 无限对话 | VIP 有效 | 连续发送10条 | 均可正常回复 | S0 |
| 302.3 | 非VIP对话次数重置 | 非VIP 23:59 用完 | 00:01 后发送 | 可以发送(quota 跨日重置) | S1 |
| 302.4 | 订阅引导卡片 | 非VIP chart 页 | 底部显示"开通VIP"引导卡片 | S1 | |
| 302.5 | VIP 无引导卡片 | VIP 有效 | chart 页底部 | 不显示订阅引导 | S1 |
| 302.6 | 对话请求记录 | 每次对话 | ChatMessage 表 | 每次对话记录一条 user 消息 + 一条 ai 消息 | S1 |
定价:C端 ¥131/年(13100分);B端 种子价 ¥1,314(131400分)/ 标准价 ¥1,986(198600分),后台可配。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 401.1 | 付费入口-首页引导 | 非VIP用户首页 | 显示开通引导 | S1 | |
| 401.2 | 付费入口-个人中心 | 非VIP个人中心 | 显示"开通VIP"按钮 | S0 | |
| 401.3 | 付费入口-配额用完 | 非VIP配额耗尽 | 显示引导到支付页 | S0 | |
| 401.4 | 付费页显示价格 | C端显示"¥131/年",B端能量师显示"¥1,314/年" | S0 | ||
| 401.5 | "立即支付"按钮 | 支付页 | 按钮可点击,调用 POST /api/pay/create |
S0 | |
| 401.6 | C端创建订单参数 | 点击支付(C端) | totalFee=13100(¥131),payType='wxpay' |
S0 | |
| 401.7 | B端创建订单参数 | 点击支付(B端) | totalFee=131400(种子价¥1,314)或 totalFee=198600(标准价¥1,986) |
S0 | |
| 401.8 | Mock 支付成功 | 开发模式返回 mock=true | 显示"支付成功",自动返回 | S0 | |
| 401.9 | 真实微信支付唤起 | prepayId 返回 | uni.requestPayment() 被调用 |
S0 | |
| 401.10 | 支付成功 → VIP 升级 | 支付回调 | 调用 userService.upgradeVip(userId, 365),vipEndTime 延后365天 |
S0 | |
| 401.11 | 支付成功 → 订单状态 | 回调处理 | order.status='paid', order.paidAt 设置 |
S0 | |
| 401.12 | 续费延长 | VIP 剩余100天时续费 | vipEndTime 在原到期日 +365天(非覆盖) |
S0 | |
| 401.13 | 支付失败提示 | 模拟支付取消/失败 | 显示友好提示,不丢失已填信息 | S1 | |
| 401.14 | 支付 loading 防重入 | 点击支付 | 按钮 loading+disabled,阻止重复点击 | S1 | |
| 401.15 | 超时未支付 | 创建订单后30分钟 | 订单保持 pending 状态,不自动取消 | S2 |
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 402.1 | 个人中心显示VIP状态 | VIP 有效 | 查看个人中心 | 显示"VIP" + 到期日 vipEndTime |
S0 |
| 402.2 | VIP 已过期显示 | VIP 过期后 | 查看个人中心 | 不显示 VIP 标识,isVip() 返回 false |
S1 |
| 402.3 | 到期后自动降级 | 到期日次日 | 尝试保存命盘/AI对话 | 触发配额限制(chart≤3, chat≤1) | S0 |
| 402.4 | 续费按原价 | VIP 续费 | 操作续费 | C端 ¥131/年,B端 ¥1,314/年(种子价用户续费是否保留种子价需确认) | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 501.1 | 首次使用弹授权 | 未登录用户点"保存命盘" | 弹出微信授权 | S0 |
| 501.2 | 授权后自动注册+登录 | 同意授权 | 自动注册(user表创建),返回 JWT token | S0 |
| 501.3 | 注册时生成推广码 | 新用户注册 | referralCode 字段生成8位大写字母数字串 |
S1 |
| 501.4 | 注册时带有效推广码 | 传入 referralCode=ABCD1234 |
invitedBy = 上级用户ID |
S0 |
| 501.5 | 注册时带无效推广码 | 传入不存在的 referralCode | 报错 1002 "Invalid referral code" | S0 |
| 501.6 | 已存在用户不重复注册 | 同一 openid 再次登录 | 返回已有 token,不创建新用户 | S0 |
| 501.7 | openid 来自 code | 前端传 code | 后端 WeChatService.codeToOpenid(code) 换取 openid |
S0 |
| 501.8 | 后续自动登录 | token 有效时重开小程序 | 自动登录,不弹授权 | S0 |
| 501.9 | token 过期 | 2天后打开 | JWT 过期 → API 返回 1001 → 清除 token → 重新登录 | S1 |
| 501.10 | 拒绝授权 | 点击"拒绝" | 可继续浏览,但不记录数据(不强制登录) | S1 |
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 502.1 | "分析记录"入口 | Tab 栏 | 底部 Tab"记录" 存在 | S1 | |
| 502.2 | 列表倒序排列 | 保存3个命盘 | 查看记录列表 | 最新生成的在最上面 | S1 |
| 502.3 | 记录显示内容 | 有历史数据 | 查看 | 姓名+生日+生成时间 + 是否已解读 | S2 |
| 502.4 | 已解读标记 | 有 difyResponse 的记录 | 显示"已解读"标签 | S2 | |
| 502.5 | 点击记录跳转 | 点击任一条 | 跳转到 chart 页,正确加载 chartData+对话历史 | S1 | |
| 502.6 | 记录删除 | 长按/左滑记录 | 确认删除→调用 POST /api/chart/delete→从列表移除 |
S1 | |
| 502.7 | 删除权限验证 | 用户A 删除用户B 的记录 | API 返回 1006 "无权删除此命盘" | S1 | |
| 502.8 | 空数据状态 | 无历史记录 | 显示"暂无命盘记录"引导文案 | S2 |
实际实现:推广码注册时自动生成(8位UUID);佣金分C端比例制(40%/5%)和B端固定制(¥500/¥100);Client端显示"推广功能开发中";服务端API已完整实现。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 601.1 | 注册时自动生成推广码 | 新用户注册 | 检查 User.referralCode | 8位大写字母+数字(UUID.substring(0,8).toUpperCase) | S0 |
| 601.2 | 推广码全局唯一 | 并发注册1000用户 | 检查数据库 | 无重复 referralCode | S0 |
| 601.3 | 个人中心显示推广码 | 已登录 | 查看个人中心→推广码区域 | 显示 referralCode + 复制按钮 | S0 |
| 601.4 | 一键复制推广码 | 点击复制 | 系统提示"复制成功" | S2 |
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 602.1 | 新用户+有效推广码注册 | 注册传 referralCode="ABCD1234" | User.invitedBy = 上级ID |
S0 | |
| 602.2 | 新用户+无效推广码注册 | 注册传不存在的码 | 报错 1002,不静默降级 | S0 | |
| 602.3 | 新用户+空推广码 | 注册不传 referralCode | invitedBy = null |
S0 | |
| 602.4 | 已存在用户忽略推广码 | 老用户再次登录传推广码 | 不更新已有 invitedBy |
S0 | |
| 602.5 | 关系终身锁定 | 绑定后 referrer 不变 | 即使再传新码也不改变 | S0 | |
| 602.6 | 自邀请检测 | 自己的 referralCode 绑定自己 | 代码逻辑:传入的是别人 ID,自邀请需主动检测(目前无此逻辑) | P2 |
佣金规则分两套:C端(客户) 按比例 40%(L1)/5%(L2),定价 ¥131/年;B端(能量师) 固定金额 ¥500(L1)/¥100(L2),定价种子价 ¥1,314/标准价 ¥1,986。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 603.1 | C端:有上级→L1比例佣金 | ¥131 订单,buyer.invitedBy=A | 检查 Commission 表 | level=1, toUserId=A, amount=13100×40%=5240, status="pending" | S0 |
| 603.2 | C端:有上上级→L1+L2 | A→B→C, C付¥131 | Commission表 | L1给B=5240(40%), L2给A=655(5%) | S0 |
| 603.3 | B端:能量师订阅→L1固定佣金 | ¥1,314 订单,buyer.invitedBy=A | 检查 Commission 表 | level=1, toUserId=A, amount=50000(¥500固定) | S0 |
| 603.4 | B端:有上上级→L1+L2固定 | A→B→C, C付¥1,314 | Commission表 | L1给B=50000(¥500), L2给A=10000(¥100) | S0 |
| 603.5 | 无上级不创建佣金 | buyer.invitedBy=null | 支付成功 | 不创建任何佣金记录 | S0 |
| 603.6 | 有上级但上上级不存在 | parent.invitedBy=null | 创建 L1,跳过 L2 | S1 | |
| 603.7 | C端二级比例 5% 计算精度 | ¥131 订单,两级佣金 | L1=5240, L2=655(均为整数) | S0 | |
| 603.8 | B端固定金额不随价格变动 | 种子价¥1,314 vs 标准价¥1,986 | 两种价格的 L1 均为 ¥500(50000分) | S0 | |
| 603.9 | 重复回调幂等 | 同 outTradeNo 两次回调 | paySuccess() 第二次应幂等(目前无幂等检查,需注意) |
S0 | |
| 603.10 | 佣金状态初始值 | 创建时 | status="pending" | S1 | |
| 603.11 | 获佣金列表 API | POST /api/commission/list |
返回该用户收到的佣金列表,倒序 | S1 | |
| 603.12 | 获佣金总额 API | POST /api/commission/total |
返回 status="settled" 的佣金总和 | S1 | |
| 603.13 | 可提现余额 API | POST /api/withdraw/balance |
返回 status="available" 的佣金总和 | S1 | |
| 603.14 | 外部能量师(非站内):无佣金 | buyer 经外部能量师推荐(非站内 user),支付 ¥131 | 检查 Commission 表 | 不创建任何佣金记录(外部能量师无站内 user id) | S1 |
当前 client 端仅显示"推广功能开发中"。服务端 API 已实现。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 604.1 | 个人中心"我的推广"入口 | 任何已登录用户 | 查看个人中心 | 菜单项存在(所有用户可见,因推广码注册即有) | S1 |
| 604.2 | 推广码卡片展示 | 个人中心 | 显示 referralCode+"复制" | S0 | |
| 604.3 | 复制推广码 | 点击复制 | 粘贴板内容 = referralCode | S2 | |
| 604.4 | 佣金面板 API 返回 | POST /api/profile/commission-summary |
返回 totalEarnings + availableBalance | S1 | |
| 604.5 | 佣金明细 API | POST /api/commission/list |
返回 Commission 列表(fromUserId/level/amount/status/createdAt) | S1 |
已完整实现,非"即将开放"。
| # | 用例 | 前置 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|---|
| 605.1 | 提现申请-最低金额 | POST /api/withdraw/apply amount=9999 |
报错 1006 "Minimum withdraw is ¥100" | S0 | |
| 605.2 | 提现申请-合法金额 | amount=10000(¥100) | 创建 Withdraw 记录,status="pending" | S0 | |
| 605.3 | 提现申请-余额不足 | amount > 可用余额 | 报错 1007 "Insufficient balance" | S0 | |
| 605.4 | 提现列表 | POST /api/withdraw/list |
返回该用户所有提现记录 | S1 | |
| 605.5 | 可用余额查询 | POST /api/withdraw/balance |
返回 status="available" 的佣金总和 | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 701.1 | 添加批注 | POST /api/annotation/add |
成功创建 RatingAnnotation,status=200 | S2 |
| 701.2 | 批注列表 | POST /api/annotation/list recordId |
返回该命盘所有批注 | S2 |
| 701.3 | 删除批注 | POST /api/annotation/delete id |
批注删除,后续列表不再返回 | S2 |
| 701.4 | 批注内容持久化 | 添加→离开→重新进入 | 批注内容正确恢复 | S2 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 702.1 | 添加标签 | POST /api/annotation/tag/add |
成功创建 Tag | S2 |
| 702.2 | 标签列表 | POST /api/annotation/tag/list recordId |
返回该命盘所有标签 | S2 |
| 702.3 | 删除标签 | POST /api/annotation/tag/delete id |
标签删除 | S2 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 801.1 | system_config 表预置数据 | 检查 DB | 初始配置存在(chart_quota=3, chat_quota=1, share_quota=1 等) | S0 |
| 801.2 | 读取配置 | POST /api/admin/config |
返回全部配置 key→value HashMap | S0 |
| 801.3 | 更新配置 | POST /api/admin/config/update key+value |
写入 DB,下次读取返回新值 | S0 |
| 801.4 | 动态生效 | 更新 chart_quota=5→验证生效 | 新值立即用于配额检查 | S0 |
| 801.5 | 获取默认值 | 配置 key 不存在 | getValue(key, default) 返回默认值,不抛异常 |
S1 |
| 801.6 | 配置值为整数 | getInt(key, default) |
正确解析整数,异常时返回默认值 | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 802.1 | 管理员登录 | POST /api/admin/login username+password |
返回 admin JWT token | S0 |
| 802.2 | 登录凭证错误 | 错误密码 | 返回 1001 "Invalid admin credentials" | S0 |
| 802.3 | 用户列表 | POST /api/admin/users |
返回全部 User 列表 | S1 |
| 802.4 | 升级 VIP | POST /api/admin/user/upgrade userId |
user.vipEndTime = now+1年 | S0 |
| 802.5 | 降级 VIP | POST /api/admin/user/downgrade userId |
user.vipEndTime = null | S0 |
| 802.6 | 设置管理员 | POST /api/admin/user/set-admin userId+isAdmin |
user.isAdmin 更新 | S1 |
| 802.7 | 不存在的用户升级 | 传入 userId=-1 | 返回 1002 "User not found" | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 803.1 | 订单列表 | POST /api/admin/orders |
返回全部 Order 列表(id/userId/outTradeNo/totalFee/status/payType/paidAt) | S1 |
| 803.2 | 订单金额单位为分 | 检查 totalFee | 整型,单位分(C端¥131=13100,B端种子价¥1,314=131400) | S2 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 804.1 | 佣金列表 | POST /api/admin/commissions |
返回全部 Commission 列表(含 level/amount/status) | S1 |
| 804.2 | 所有命盘列表 | POST /api/admin/charts |
返回全部 ChartRecord 列表 | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 805.1 | 待审核提现列表 | POST /api/admin/withdraws |
返回 status="pending" 的 Withdraw 列表 | S1 |
| 805.2 | 审核通过 | POST /api/admin/withdraw/approve id |
withdraw.status="approved" | S0 |
| 805.3 | 审核拒绝 | POST /api/admin/withdraw/reject id |
withdraw.status="rejected" | S0 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| 806.1 | 统计看板 | POST /api/admin/stats |
返回 totalUsers/vipUsers/totalOrders/totalCharts/totalCommissions/totalRevenue/recentOrders | S0 |
| 806.2 | totalRevenue 计算 | 有 paid 订单 | 仅统计 status="paid" 的订单 totalFee 之和 | S0 |
| 806.3 | vipUsers 统计 | 有未过期 VIP | countByVipEndTimeAfterAndVipEndTimeIsNotNull(now) |
S0 |
| 806.4 | recentOrders 最多10条 | 订单>10 | 仅返回最近10条,按 id desc | S1 |
| # | 用例 | 输入 | 预期结果 | 等级 |
|---|---|---|---|---|
| CALC.1 | reduceToDigit 个位数 | 5 | 5 | S0 |
| CALC.2 | reduceToDigit 两位数归约 | 10 | 1 | S0 |
| CALC.3 | reduceToDigit 多位数归约 | 99 | 9 | S0 |
| CALC.4 | reduceToDigit 多级归约 | 999 → 27 → 9 | 9 | S0 |
| CALC.5 | reduceToDigit 卓越数11 | 11 | 11 | S0 |
| CALC.6 | reduceToDigit 卓越数22 | 22 | 22 | S0 |
| CALC.7 | reduceToDigit 卓越数33 | 33 | 33 | S0 |
| CALC.8 | 完整命盘-标准日期 | 1990-01-01 | A=1,B=9,C=9,D=1,E=1,F=1,G=9,H=1,I=2,J=1,K=1,L=3,M=2,N=4,O=6 | S0 |
| CALC.9 | 月份两位数 | 1985-12-25 | D=1+2=3, E=2+5=7 | S0 |
| CALC.10 | 年份特殊 | 2001-03-08 | A=2, B=0, C=0+1=1 | S0 |
| CALC.11 | 主性格卓越数 | 构造使 O=11/22/33 | 归约在卓越数停止,不继续归约 | S0 |
| CALC.12 | 全部相同数字 | 1111-11-11 | 计算正确,不失精度 | S1 |
| CALC.13 | 全部9 | 9999-09-09 | 计算正确 | S1 |
| CALC.14 | 客户端/服务端计算结果完全一致 | 1995-07-15 | 客户端 calculator.js 结果 = 服务端 CalculatorService 结果 | S0 |
| CALC.15 | getNumberMeaning 1-9 + 11/22/33 | 每个数字 | 返回正确的 title/trait/color | S1 |
| CALC.16 | getPositionName 15个位置 | A-O 每个 | 返回正确中文名称(A=年位一 ... O=主性格) | S1 |
| # | 用例 | 请求 | 预期结果 | 等级 |
|---|---|---|---|---|
| API.1 | 微信登录 | POST /api/auth/login {openid, nickname, avatarUrl} |
返回 JWT token,新用户自动注册 | S0 |
| API.2 | 微信登录+推广码 | POST /api/auth/login {openid, referralCode} |
新用户 invitedBy=上级ID | S0 |
| API.3 | 重复登录 | 同一 openid 两次登录 | 返回已有 token,不创建新用户 | S0 |
| API.4 | 无 token 调用需鉴权 API | POST /api/chart/list 无 Authorization |
返回 401 或错误 | S0 |
| API.5 | 无效 token | Authorization: Bearer invalid | 返回 1001 未授权 | S0 |
| # | 用例 | 请求 | 预期结果 | 等级 |
|---|---|---|---|---|
| API.6 | 创建命盘(服务端计算) | POST /api/chart/create {birthday, name} |
返回 ChartRecord,chartData 由服务端计算 | S0 |
| API.7 | 创建命盘(客户端传入) | POST /api/chart/create {birthday, name, chartData} |
返回 ChartRecord,chartData 使用客户端的 | S1 |
| API.8 | 创建命盘超过配额 | 非VIP第4次创建 | 报错 1008 配额超限 | S0 |
| API.9 | 命盘列表 | POST /api/chart/list |
返回该用户所有命盘(userId 从 token 获取) | S1 |
| API.10 | 命盘详情 | POST /api/chart/detail {id} |
返回 ChartRecord 含 chartData, difyResponse | S1 |
| API.11 | 删除自己的命盘 | POST /api/chart/delete {id} |
删除成功 | S1 |
| API.12 | 删除他人的命盘 | 用户A 删 用户B 的命盘 | 报错 1006 "无权删除此命盘" | S1 |
| # | 用例 | 请求 | 预期结果 | 等级 |
|---|---|---|---|---|
| API.13 | 发送消息 | POST /api/chat/send {chartRecordId, query} |
返回 AI 回复内容 | S0 |
| API.14 | 访问他人命盘 | 用户A 查看 用户B 的 chartRecordId | 报错 1006 "无权访问此命盘" | S0 |
| API.15 | 对话历史 | POST /api/chat/history {chartRecordId} |
返回 ChatMessage[],按 createdAt 升序 | S1 |
| API.16 | 空历史 | 从未对话的命盘 | 返回空数组 [] | S1 |
| API.17 | 对话超配额 | 非VIP超过1条 | 报错 1008 | S0 |
| # | 用例 | 请求 | 预期结果 | 等级 |
|---|---|---|---|---|
| API.18 | 创建订单 | POST /api/pay/create {totalFee:39800, payType:'wxpay'} |
返回 Order,status='pending' | S0 |
| API.19 | 支付回调 | POST /api/pay/notify {outTradeNo} |
order.status='paid',VIP 升级,佣金创建 | S0 |
| API.20 | 重复回调 | 同 outTradeNo 两次 | 第二次 VIP 不重复加天数,佣金不重复创建(当前无幂等,需验证) | S1 |
| API.21 | 不存在的订单回调 | outTradeNo 随机 | 报错 1005 "Order not found" | S1 |
| # | 用例 | 请求 | 预期结果 | 等级 |
|---|---|---|---|---|
| API.22 | 提现申请-低于最低 | POST /api/withdraw/apply {amount:9999} |
报错 1006 | S0 |
| API.23 | 提现申请-合法 | amount:10000 | 返回 Withdraw,status='pending' | S0 |
| API.24 | 余额不足 | amount > availableBalance | 报错 1007 | S0 |
| API.25 | 提现列表 | POST /api/withdraw/list |
返回该用户所有提现记录 | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| ERR.1 | 年月日全部为空 | 点生成 | 提示"请填写完整的出生日期" | S2 |
| ERR.2 | 月份=0 | 输入 | 提示"请输入正确的月份(1-12)" | S2 |
| ERR.3 | 月份=13 | 输入 | 提示同上 | S2 |
| ERR.4 | 日期=0 | 输入 | 提示"请输入正确的日期" | S2 |
| ERR.5 | 日期=32 | 输入 | 提示同上 | S2 |
| ERR.6 | 姓名为空 | 生成命盘 | 显示空字符串 | S2 |
| ERR.7 | 姓名超长 | 输入51字符 | 数据库 varchar(50) 截断风险(需前段限制) | S2 |
| ERR.8 | API 传不存在的 referralCode | POST /api/auth/login | 报错 1002 | S1 |
| ERR.9 | 超长 outTradeNo | 数据库 varchar(64) | 不溢出,正常写入 | S1 |
| # | 用例 | 步骤 | 预期结果 | 等级 |
|---|---|---|---|---|
| ERR.10 | Dify API 超时 | Dify 服务不可用 | DifyService.invokeChatflow 抛异常 → ChatService 捕获返回"解读服务暂时不可用" |
S1 |
| ERR.11 | 数据库连接失败 | DB 宕机 | API 返回 500,不暴露堆栈 | S1 |
| ERR.12 | 订单创建后未支付 | pending 订单一直存在 | 不影响配额,不自动取消 | S2 |
| ERR.13 | 并发创建相同 outTradeNo | 极高并发 | outTradeNo 含毫秒时间戳+UUID6位,冲突概率极低 | S1 |
| ERR.14 | 命盘ID不存在 | detail/delete 传 id=-1 | 报错 1004 "Chart record not found" | S1 |
| ERR.15 | delete 自己不存在的记录 | 无权限 delete | 报错 1006 | S1 |
| # | 用例 | 验证点 | 预期结果 | 等级 |
|---|---|---|---|---|
| ERR.16 | token 过期 | API 返回 1001 | 清除本地 token,回到登录状态 | S1 |
| ERR.17 | VIP 过期自动降级 | vipEndTime < now | isVip()=false,配额限制恢复 | S0 |
| ERR.18 | 支付重入 | 快速连点支付按钮 | payLoading=true 阻止第二次 | S1 |
| ERR.19 | 退出登录状态 | 退出→重进个人中心 | 显示未登录状态,无用户信息 | S1 |
| ERR.20 | 页面卸载未释放 | setInterval | 页面 onUnload 时 clearInterval | S1 |
| # | 检查项 | 通过标准 | 等级 |
|---|---|---|---|
| COMP.1 | 首页/解读文案 | 不含"算命、占卜、预测、占星" | S0 |
| COMP.2 | AI 解读免责声明 | 页末含"本解读由AI生成,仅供参考" | S0 |
| COMP.3 | 用户协议链接 | 可点击,打开有效页面 | S0 |
| COMP.4 | 隐私政策链接 | 可点击,打开有效页面 | S0 |
| COMP.5 | 不收集敏感信息 | 无身份证/银行卡输入 | S0 |
| COMP.6 | 支付合规 | 微信支付规范,无诱导消费/虚假倒计时 | S0 |
| COMP.7 | 分销合规 | 两级分销,无团队计酬,无入门费 | S0 |
| COMP.8 | 分销文案合规 | 无"拉人头、团队计酬、入门费"等涉传表述 | S0 |
| COMP.9 | AppID 配置正确 | manifest.json wx5ba8038ef16fb245 | S1 |
| COMP.10 | JWT Secret 不硬编码 | application.yml 配置 | S0 |
| COMP.11 | Dify API Key 不暴露前端 | 仅 Server DifyService 持有 | S0 |
| COMP.12 | 支付商户号配置化 | 不硬编码,配置注入 | S1 |
| COMP.13 | 用户数据删除 | 退出登录选项 | S2 |
| COMP.14 | API鉴权 | 所有非公开接口需 JWT token | S0 |
| COMP.15 | 命盘数据隔离 | 用户只能查看/删除自己的命盘 | S0 |
| # | 场景 | 方法 | 目标 | 等级 |
|---|---|---|---|---|
| PERF.1 | 命盘生成(客户端计算公式) | 前端计时 | <500ms | S0 |
| PERF.2 | AI 对话回复(Dify API 调用) | 全链路计时 | <5000ms | S0 |
| PERF.3 | TriangleChart 组件渲染 | 渲染计时 | <500ms | S1 |
| PERF.4 | 首页首屏加载 | 小程序启动 | <2000ms | S1 |
| PERF.5 | 历史列表渲染 | 100条记录 | 无白屏/卡顿 | S1 |
| PERF.6 | 服务端 API 响应 | 典型请求 | <200ms(不含 Dify) | S1 |
| PERF.7 | 并发用户 100 | 压测 API | 正确率 100%,P95 <500ms | S2 |
| # | 设备 | iOS/Android | 微信版本 | 重点验证 | 等级 |
|---|---|---|---|---|---|
| DEV.1 | iPhone SE (3rd) | iOS 16+ | 8.0+ | TriangleChart 适配、布局无溢出 | S1 |
| DEV.2 | iPhone 13/14 | iOS 16+ | 8.0+ | 核心流程全链路 | S0 |
| DEV.3 | iPhone 15 Pro Max | iOS 17+ | 8.0+ | 大屏适配、对话体验 | S1 |
| DEV.4 | 华为Mate 60 | Android 14 | 8.0+ | 核心流程+TriangleChart | S0 |
| DEV.5 | 小米14 | Android 14 | 8.0+ | 核心流程+支付 | S1 |
| DEV.6 | OPPO Reno 10 | Android 13 | 8.0+ | 支付流程+分销 | S1 |
上线前必须通过以下 P0 用例:
| ID | 描述 |
|---|---|
| 101.1 | 合法输入生成命盘 |
| 101.10 | 未登录可生成 |
| 101.12 | 非VIP超配额限制 |
| 101.13 | VIP用户无限生成 |
| 102.1 | 五层十五格完整 |
| 301.1 | AI对话入口存在 |
| 301.2 | 首次进入自动问候 |
| 301.3 | 快捷问题发送成功 |
| 301.7 | 对话历史持久化 |
| 301.11 | Dify不可用时降级 |
| 302.1 | 非VIP每日1次限额 |
| 302.2 | VIP无限对话 |
| 401.4 | 付费页显示¥131/年 |
| 401.6 | C端创建订单参数(13100) |
| 401.7 | B端创建订单参数(131400) |
| 401.9 | 支付成功→VIP升级 |
| 501.1 | 首次使用弹授权 |
| 501.4 | 有效推广码绑定 |
| 501.6 | 已存在用户不重复注册 |
| 601.1 | 注册时自动生成推广码 |
| 603.1 | C端比例佣金 L1=40%(¥5240) |
| 603.2 | C端比例佣金 L1+L2(40%+5%) |
| 603.3 | B端固定佣金 L1=¥500 |
| 603.4 | B端固定佣金 L1+L2(¥500+¥100) |
| 603.5 | 无上级不创建佣金 |
| 605.1 | 提现最低金额检查 |
| 605.2 | 提现申请合法金额 |
| 801.1 | 系统配置预置数据 |
| 801.4 | 配置动态生效 |
| 802.4 | 后台升级VIP |
| 805.2 | 后台提现审核通过 |
| 806.1 | 统计看板返回正确 |
| API.1 | 微信登录返回JWT |
| API.4 | 无token鉴权拒绝 |
| API.13 | 发送消息成功 |
| API.18 | 创建订单成功 |
| API.19 | 支付回调成功 |
| 数据项 | 数量 | 说明 |
|---|---|---|
| 测试微信账号A | 1 | 普通用户(无VIP) |
| 测试微信账号B | 1 | VIP有效用户 |
| 测试微信账号C | 1 | 带下级关系的用户(有佣金数据) |
| Dify test环境 | 1 | test API key + base-url |
| 数据库 test 实例 | 1 | 独立Mysql,非生产 |
| 真机设备 | ≥2台 | iOS + Android 各覆盖 |
| EPIC | 模块 | 用例数 | P0 | P1 | P2 |
|---|---|---|---|---|---|
| EPIC 1 | 命盘生成与展示 | 33 | 12 | 16 | 5 |
| EPIC 2 | 分享与导出 | 3 | 2 | 0 | 1 |
| EPIC 3 | AI 解读 | 19 | 12 | 5 | 2 |
| EPIC 4 | 付费与订阅 | 19 | 15 | 3 | 1 |
| EPIC 5 | 用户与历史 | 18 | 8 | 9 | 1 |
| EPIC 6 | 分销推广与佣金 | 29 | 18 | 9 | 2 |
| EPIC 7 | 批注与客户管理 | 7 | 0 | 0 | 7 |
| EPIC 8 | 管理后台 | 19 | 12 | 6 | 1 |
| — | 计算引擎 | 16 | 10 | 6 | 0 |
| — | 服务端API集成 | 25 | 16 | 8 | 1 |
| — | 边界与异常 | 20 | 6 | 12 | 2 |
| — | 合规安全 | 15 | 11 | 3 | 1 |
| — | 性能 | 7 | 4 | 3 | 0 |
| — | 真机兼容 | 6 | 2 | 4 | 0 |
| 合计 | 236 | 128 | 84 | 24 |
P0 用例 128 条,P1 84 条,P2 24 条,总计 236 条测试用例。 冒烟测试(P0核心 34 条)通过后方可进入发布流程。
| 变更 | 说明 |
|---|---|
| 📉 价格修正 | ¥1,314/¥1,914+¥299 → ¥398统一定价,移除种子价/标准价逻辑 |
| 🔄 佣金修正 | 固定金额¥500/¥100+40%/5% → 统一30%+10%百分比 |
| 🔄 AI架构 | 静态5区解读 → Dify Chatflow 对话式AI,6轮记忆 |
| 🔄 推广码 | 支付后生成6位码 → 注册时生成8位UUID码 |
| 📉 分销面板 | 详尽6区UI → 服务端API已实现,client显示"开发中" |
| ✅ 提现 | Phase1"即将开放" → 已完整实现(apply/approve/reject) |
| 🔄 用户模型 | vipType+practitioner+3个统计字段 → 单一vipEndTime |
| 🔄 角色系统 | 移除practitioner/annual角色,仅VIP/非VIP两级 |
| 📉 分享导出 | 10+7条 → 仅保留推广码复制 |
| ✅ 管理后台 | 新增:提现审核、统计看板、KV配置管理 |
| ✅ Dify集成 | 新增DifyService/ChatService测试覆盖 |
| 🔄 云函数测试 | 迁移为Server API集成测试(8个Controller 25+端点) |
| 📉 真机兼容 | 8设备→6设备(移除iPad/折叠屏) |
| 变更 | 说明 |
|---|---|
| 🔄 价格模型 | ¥398统一定价 → 分C端¥131/年 + B端种子价¥1,314/标准价¥1,986 |
| 🔄 佣金规则 | 统一30%+10% → C端比例40%(L1)/5%(L2) + B端固定¥500(L1)/¥100(L2) |
| 🔄 TC-603重构 | 10条旧用例→14条新用例,覆盖C端比例/B端固定/内外部推荐场景 |
| 🔄 TC-401更新 | 拆分C端/B端创建订单参数,更新定价显示用例 |
| ➕ 新增用例 | +5条(TC-401 1条 + TC-603 4条) |
| ➕ 新增P0 | +6条(TC-401.7 + TC-603.1~603.5,603.7~603.9) |
| ➕ 603.14 | 新增"外部能量师推荐(非站内user)不创建佣金"边界用例 |