|
|
@@ -0,0 +1,241 @@
|
|
|
+# 会员支付/升级页面设计(方案 B)
|
|
|
+
|
|
|
+日期:2026-08-09
|
|
|
+状态:已获用户批准(方向 B + 优惠券保留 + 原价定义确认)
|
|
|
+
|
|
|
+## 1. 背景与目标
|
|
|
+
|
|
|
+现有会员系统后端链路完整(等级 FREE/FAMILY/PREMIUM、下单、微信虚拟支付、拆单分期、优惠券、开通赠券、升级记录、权益列表),但前端把「选套餐 + 选优惠券 + 支付 + 结果」全部塞进 `upgrade.vue` 一个页面,支付确认环节不清晰、拆单分期体验差。
|
|
|
+
|
|
|
+目标:按方案 B 将支付链路拆分为「套餐选择 → 订单确认 → 支付结果」三段式,新增 `pay.vue`、`result.vue` 两个页面,改造 `upgrade.vue`,后端零改动(除新增原价配置)。
|
|
|
+
|
|
|
+### 已确认需求
|
|
|
+
|
|
|
+1. 方案 B 页面结构(index → upgrade → pay → result)
|
|
|
+2. 优惠券功能保留并修复(`createOrder` 透传 `couponId`)
|
|
|
+3. 会员价格支持「原价」定义(sys_config 驱动,仅展示用途)
|
|
|
+
|
|
|
+## 2. 页面架构与路由
|
|
|
+
|
|
|
+```
|
|
|
+pages/membership/
|
|
|
+├── index.vue 会员中心(已有,小幅优化)
|
|
|
+├── upgrade.vue 套餐选择(改造:移除支付逻辑,专注选套餐)
|
|
|
+├── pay.vue 订单确认(新增 ⭐)
|
|
|
+├── result.vue 支付结果(新增 ⭐)
|
|
|
+└── benefits.vue 我的权益(已有,保留不动)
|
|
|
+```
|
|
|
+
|
|
|
+**路由跳转:**
|
|
|
+
|
|
|
+```
|
|
|
+index ──(开通/续费)──→ upgrade ──(下一步)──→ pay?levelCode=FAMILY&period=yearly
|
|
|
+ │
|
|
|
+ (微信虚拟支付 或 模拟支付)
|
|
|
+ │
|
|
|
+ result?orderNo=xxx&status=success/fail
|
|
|
+```
|
|
|
+
|
|
|
+**pages.json 注册**(`pages/membership` 分包内追加 2 页):
|
|
|
+
|
|
|
+| path | navigationBarTitleText |
|
|
|
+|------|------------------------|
|
|
|
+| `pay` | 确认订单 |
|
|
|
+| `result` | 支付结果 |
|
|
|
+
|
|
|
+## 3. 后端变更(最小,仅原价配置)
|
|
|
+
|
|
|
+### 3.1 sys_config 新增 4 个键(单位:分)
|
|
|
+
|
|
|
+| 配置键 | 默认值 | 含义 |
|
|
|
+|--------|--------|------|
|
|
|
+| `member_fee_family_original_yearly` | `199900` | 家庭会员年费原价(分)¥1999 |
|
|
|
+| `member_fee_family_original` | `199900` | 家庭会员原价兜底(分) |
|
|
|
+| `member_fee_premium_original` | `1599900` | 高级会员年费原价(分)¥15999 |
|
|
|
+| `member_fee_premium_original_monthly` | `159900` | 高级会员月费原价(分)¥1599 |
|
|
|
+
|
|
|
+- 在 `DatabaseInitializer.java` 的 SysConfig 种子区追加 `insertSysConfigSeed`(幂等)
|
|
|
+- 原价仅作展示,不参与任何折扣/优惠券/拆单计算
|
|
|
+
|
|
|
+### 3.2 `/api/membership/plans` 接口增强
|
|
|
+
|
|
|
+`MembershipController.getPricingPlans()` 每个 plan 增加原价字段(无原价返回 null):
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "levelCode": "FAMILY",
|
|
|
+ "levelName": "家庭会员",
|
|
|
+ "levelDesc": "...",
|
|
|
+ "recommended": true,
|
|
|
+ "yearly": 131400,
|
|
|
+ "originalYearly": 199900,
|
|
|
+ "monthly": null,
|
|
|
+ "originalMonthly": null,
|
|
|
+ "quarterly": null,
|
|
|
+ "originalQuarterly": null
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- FAMILY:`originalYearly` 读 `member_fee_family_original_yearly`(兜底 `member_fee_family_original`)
|
|
|
+- PREMIUM:`originalYearly` 读 `member_fee_premium_original`,`originalMonthly` 读 `member_fee_premium_original_monthly`
|
|
|
+- 其他等级:读 `membership_levels` 表,无原价字段 → 返回 null(不展示划线价)
|
|
|
+
|
|
|
+## 4. 前端页面设计
|
|
|
+
|
|
|
+### 4.1 upgrade.vue(套餐选择页 · 改造)
|
|
|
+
|
|
|
+**保留**:
|
|
|
+- 状态卡(会员等级 / 到期时间 / 剩余天数)
|
|
|
+- 等级切换 tab(FAMILY / PREMIUM,非免费用户默认选中当前等级)
|
|
|
+- 档位选择(月/季/年,FAMILY 仅年付;PREMIUM 月付+年付)
|
|
|
+- 价格展示(含划线原价,见 4.4)
|
|
|
+- 权益对比表、开通赠券规则、升级记录
|
|
|
+
|
|
|
+**移除**:优惠券选择、支付调用、支付结果轮询(下沉到 pay.vue)
|
|
|
+
|
|
|
+**改动**:
|
|
|
+- 底部主按钮改「下一步」→ `navigateTo /pages/membership/pay?levelCode=xxx&period=xxx`
|
|
|
+- 修复 bug:等级判断用 `m.memberLevel`(现代码用 `m.levelCode`,顶层无此字段,会员状态恒显免费)
|
|
|
+- 非免费用户按钮文案「续费」
|
|
|
+
|
|
|
+### 4.2 pay.vue(订单确认页 · 新增)⭐
|
|
|
+
|
|
|
+**布局(自上而下):**
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────┐
|
|
|
+│ 订单确认(导航栏) │
|
|
|
+├─────────────────────────────┤
|
|
|
+│ 商品明细卡 │
|
|
|
+│ 家庭会员(FAMILY)· 年度 │
|
|
|
+│ 原价 ¥1999.00(划线) │
|
|
|
+│ 现价 ¥1314.00 │
|
|
|
+│ 优惠券 -¥50.00 [选择▾] │
|
|
|
+│ ────────────────────── │
|
|
|
+│ 实付 ¥1264.00 │
|
|
|
+├─────────────────────────────┤
|
|
|
+│ 拆单提示卡(仅 totalPeriods>1)│
|
|
|
+│ 💡 本订单将分 N 期支付 │
|
|
|
+│ 每期 ¥xxx,共 N 期 │
|
|
|
+├─────────────────────────────┤
|
|
|
+│ 底部固定: │
|
|
|
+│ [立即支付 ¥1264.00] │
|
|
|
+└─────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+**核心逻辑:**
|
|
|
+1. `onLoad` 读 `levelCode`、`period` → `getMembershipPlans()` 取价格(含原价)
|
|
|
+2. `getCouponList()` 加载可用优惠券 → 底部半屏 Picker(复用现有样式)
|
|
|
+3. **api.js 修复**:`createOrder(levelCode, paymentType, period, couponId)` 增加 couponId 透传
|
|
|
+4. 点击支付 → `createOrder(levelCode, 'pay', period, couponId)` → 三种分支:
|
|
|
+ - **有 `orderInfo+sign`** → `requestVirtualPayment` 调起微信支付
|
|
|
+ - **testMode 返回纯 order** → 直接走成功轮询(模拟支付)
|
|
|
+ - **拆单(totalPeriods>1)** → 支付第 1 期 → `nextOrder` 取下一期 → 逐期支付,UI 显示「第 N 期 / 共 M 期」进度
|
|
|
+5. 支付完成 → `navigateTo result?orderNo=xxx&status=success`;取消/失败 → `status=fail`
|
|
|
+
|
|
|
+**错误处理:**
|
|
|
+
|
|
|
+| 错误 | 处理 |
|
|
|
+|------|------|
|
|
|
+| 46001 | toast「该商品暂未开放购买」,返回 upgrade |
|
|
|
+| -15007 / -15005 | 登录态过期,提示重新登录 |
|
|
|
+| -15010 | 商品未发布,联系客服 |
|
|
|
+| -2 | 用户取消支付 → result fail 态 |
|
|
|
+| 其他 | 显示 errMsg → result fail 态 |
|
|
|
+
|
|
|
+### 4.3 result.vue(支付结果页 · 新增)
|
|
|
+
|
|
|
+**成功态:**
|
|
|
+
|
|
|
+```
|
|
|
+ ✅ 开通成功
|
|
|
+ 家庭会员 FAMILY
|
|
|
+ 有效期至 2027-08-09
|
|
|
+ 🎁 已赠 2 张优惠券(券名)
|
|
|
+ [查看我的权益] [返回会员中心]
|
|
|
+```
|
|
|
+
|
|
|
+**失败态:**
|
|
|
+
|
|
|
+```
|
|
|
+ ❌ 支付未完成
|
|
|
+ 原因提示(取消/失败/登录态过期)
|
|
|
+ [重新支付] [返回套餐选择]
|
|
|
+ (拆单未付清时显示:第 N 期未支付,[继续支付])
|
|
|
+```
|
|
|
+
|
|
|
+**逻辑:**
|
|
|
+- `onLoad` 读 `orderNo/status`
|
|
|
+- 成功态:轮询 `getMyMembership()` 确认 `membership.isActive`;拉取开通赠券规则(`getJoinCouponRules`)展示赠券
|
|
|
+- 失败态:拆单未付清时调 `nextOrder({orderNo})` 获取下一期支付参数,提供「继续支付」
|
|
|
+
|
|
|
+### 4.4 价格展示规范(划线原价)
|
|
|
+
|
|
|
+**展示规则:**
|
|
|
+- `originalPrice` 存在且 `> 现价` 时展示划线原价
|
|
|
+- 无原价或 `≤ 现价` 时不展示(向后兼容)
|
|
|
+- 优惠券在现价基础上抵扣:实付 = 现价 − 优惠券面额
|
|
|
+- 拆单分期按抵扣后金额判断(与后端 `createOrder` 的 `finalAmount % goodsPrice` 一致)
|
|
|
+
|
|
|
+**视觉:**
|
|
|
+```
|
|
|
+原价 ¥1999.00 (灰、删除线、22rpx)
|
|
|
+现价 ¥1314.00 (大号橙字 64rpx)
|
|
|
+[立省 ¥685] (橙底白字小标签,可选)
|
|
|
+```
|
|
|
+
|
|
|
+### 4.5 视觉规范(延续现有品牌)
|
|
|
+
|
|
|
+| 元素 | 规范 |
|
|
|
+|------|------|
|
|
|
+| 主色 | `#F97316` 暖橙渐变(会员品牌色,沿用) |
|
|
|
+| PREMIUM 区分色 | `#8B5CF6` 紫(沿用) |
|
|
|
+| 卡片 | 白底圆角 20rpx + 橙系阴影(沿用) |
|
|
|
+| 状态卡 | 橙渐变头图(沿用) |
|
|
|
+| 按钮 | 全宽圆角 44rpx,主橙 / 续费蓝 `#0EA5E9` |
|
|
|
+| 小程序限制 | 禁 `?.`、禁 Grid、`:key` 用方法、日期用 `parseDate()`、禁止 `:key` 表达式 |
|
|
|
+
|
|
|
+## 5. 数据流总览
|
|
|
+
|
|
|
+```
|
|
|
+upgrade: 选等级+档位 → navigateTo pay(levelCode, period)
|
|
|
+pay: plans取价 + couponList取券
|
|
|
+ → 用户选券 → createOrder(levelCode,'pay',period,couponId)
|
|
|
+ → orderInfo+sign ? requestVirtualPayment(逐期) : 模拟成功
|
|
|
+ → 全部付清/回调 → navigateTo result
|
|
|
+result: 轮询 getMyMembership 确认开通 → 展示成功/失败 + 赠券
|
|
|
+```
|
|
|
+
|
|
|
+## 6. 改动清单
|
|
|
+
|
|
|
+### 后端(cfc-backend)
|
|
|
+| 文件 | 改动 |
|
|
|
+|------|------|
|
|
|
+| `config/DatabaseInitializer.java` | SysConfig 种子区加 4 个原价键(insertSysConfigSeed 幂等) |
|
|
|
+| `controller/MembershipController.java` | `getPricingPlans()` 增加 originalYearly/originalMonthly/originalQuarterly 字段 |
|
|
|
+
|
|
|
+### 前端(cfc-frontend)
|
|
|
+| 文件 | 改动 |
|
|
|
+|------|------|
|
|
|
+| `utils/api.js` | `createOrder` 增加 couponId 透传参数 |
|
|
|
+| `pages/membership/upgrade.vue` | 移除支付/优惠券逻辑;按钮改「下一步」;修会员状态判断 bug;价格区展示划线原价 |
|
|
|
+| `pages/membership/pay.vue` | **新增**:订单确认 + 优惠券 Picker + 支付调用 + 拆单逐期 |
|
|
|
+| `pages/membership/result.vue` | **新增**:支付结果成功/失败态 + 赠券展示 + 续付入口 |
|
|
|
+| `pages.json` | `pages/membership` 分包注册 pay、result 两页 |
|
|
|
+
|
|
|
+## 7. 测试要点
|
|
|
+
|
|
|
+1. **testMode 全链路**:upgrade 选套餐 → pay 确认 → 模拟支付 → result 成功态(含赠券展示)
|
|
|
+2. **拆单支付**(1314元 → 按 goods_price 拆 N 期):逐期支付成功 → result 成功;中途取消 → result 失败态 + 续付入口
|
|
|
+3. **优惠券**:抵扣金额显示正确;下单请求携带 couponId;无可用券时 Picker 显示空态
|
|
|
+4. **原价展示**:FAMILY/PREMIUM 显示划线原价;无原价配置的等级不显示;原价 ≤ 现价时不显示
|
|
|
+5. **会员/非会员两态**:免费用户「立即开通」;会员「续费」,默认选中当前等级
|
|
|
+6. **错误分支**:46001、登录态过期、支付取消各走对应提示
|
|
|
+7. **回归**:`mvn clean compile` 通过;既有 benefits/index 页不受影响
|
|
|
+
|
|
|
+## 8. 范围外(YAGNI)
|
|
|
+
|
|
|
+- 不新增支付方式(仅微信虚拟支付 + testMode 模拟)
|
|
|
+- 不做自动续费 UI(后端 autoRenew 字段已有但本期不接)
|
|
|
+- 不改 `membership_levels` 表结构(原价走 sys_config,避免迁移)
|
|
|
+- 不做后端价格校验变更(createOrder 原价无关)
|