# 会员支付/升级页面实现计划(方案 B) > **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 **目标:** 将会员开通/续费链路拆分为「套餐选择 upgrade → 订单确认 pay → 支付结果 result」三段式,并支持会员价格「原价/划线价」配置展示。 **架构:** 后端仅增强 `/api/membership/plans` 返回原价字段 + sys_config 新增 4 个原价键(零表结构变更);前端新增 pay.vue、result.vue,改造 upgrade.vue(移除支付逻辑),api.js 修复 createOrder 透传 couponId。 **技术栈:** Spring Boot 2.7.18 + MyBatis-Plus(后端)/ uni-app Vue 2 Options API(小程序前端)/ 微信虚拟支付 `requestVirtualPayment` **设计文档:** `docs/superpowers/specs/2026-08-09-membership-pay-upgrade-design.md` **验证方式(本项目约定):** - 后端:`cd cfc-backend && mvn clean compile`(唯一验证方式,不做运行时验证) - 前端:提取 `` 之间的内容,写临时文件后 `node --check`)。 预期:无语法错误。 - [ ] **步骤 6:Commit** ```bash git add cfc-frontend/pages/membership/upgrade.vue git commit -m "refactor: upgrade页移除支付/优惠券逻辑,改下一步跳转pay,修复会员状态判断bug,支持划线原价" ``` --- ## 任务 5:新增 pay.vue(订单确认页) **文件:** - 创建:`cfc-frontend/pages/membership/pay.vue` **要点:** 接收 levelCode/period 参数 → plans 取价(含原价)→ couponList 取券 → 展示明细 + 优惠券 Picker → createOrder(couponId) → 微信虚拟支付(拆单逐期)→ 跳转 result。 - [ ] **步骤 1:创建 pay.vue 完整文件** ```vue ``` - [ ] **步骤 2:语法校验** 提取 script 块运行 `node --check`。 预期:无语法错误。 - [ ] **步骤 3:Commit** ```bash git add cfc-frontend/pages/membership/pay.vue git commit -m "feat: 新增订单确认页 pay.vue(明细/优惠券/拆单提示/虚拟支付逐期)" ``` --- ## 任务 6:新增 result.vue(支付结果页) **文件:** - 创建:`cfc-frontend/pages/membership/result.vue` **要点:** 读 orderNo/status → 成功态轮询确认开通 + 展示赠券;失败态展示原因 + 重新支付/续付入口(拆单未付清调 nextOrder)。 - [ ] **步骤 1:创建 result.vue 完整文件** ```vue ``` - [ ] **步骤 2:语法校验** 提取 script 块运行 `node --check`。 预期:无语法错误。 - [ ] **步骤 3:Commit** ```bash git add cfc-frontend/pages/membership/result.vue git commit -m "feat: 新增支付结果页 result.vue(成功态+赠券/失败态+续付)" ``` --- ## 任务 7:pages.json 注册 pay、result 页面 **文件:** - 修改:`cfc-frontend/pages.json`(`pages/membership` 分包,第 669-691 行) - [ ] **步骤 1:在 upgrade 页面注册后追加两页** ```json { "root": "pages/membership", "pages": [ { "path": "index", "style": { "navigationBarTitleText": "会员中心" } }, { "path": "benefits", "style": { "navigationBarTitleText": "我的权益" } }, { "path": "upgrade", "style": { "navigationBarTitleText": "开通会员" } }, { "path": "pay", "style": { "navigationBarTitleText": "确认订单" } }, { "path": "result", "style": { "navigationBarTitleText": "支付结果" } } ] }, ``` - [ ] **步骤 2:JSON 语法校验** 运行:`node -e "JSON.parse(require('fs').readFileSync('cfc-frontend/pages.json','utf8'));console.log('pages.json OK')"` 预期:`pages.json OK` - [ ] **步骤 3:Commit** ```bash git add cfc-frontend/pages.json git commit -m "feat: pages.json 注册会员 pay/result 页面" ``` --- ## 任务 8:整体验证与收尾 - [ ] **步骤 1:后端编译验证** 运行:`cd cfc-backend && mvn clean compile` 预期:`BUILD SUCCESS` - [ ] **步骤 2:前端语法校验(三个改动页面 + api.js + pages.json)** 依次对 `upgrade.vue`、`pay.vue`、`result.vue` 提取 script 块做 `node --check`;对 `pages.json` 做 JSON.parse。 预期:全部无错误。 - [ ] **步骤 3:自查设计文档覆盖度** 对照 `docs/superpowers/specs/2026-08-09-membership-pay-upgrade-design.md` 第 6 节改动清单逐项确认: - [x] DatabaseInitializer 原价键(任务 1) - [x] MembershipController plans 原价字段(任务 2) - [x] api.js createOrder couponId(任务 3) - [x] upgrade.vue 改造(任务 4) - [x] pay.vue 新增(任务 5) - [x] result.vue 新增(任务 6) - [x] pages.json 注册(任务 7) - [ ] **步骤 4:最终 Commit(如有遗漏改动)** ```bash git add -A cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java cfc-frontend/utils/api.js cfc-frontend/pages/membership/ cfc-frontend/pages.json git status git commit -m "feat: 会员支付升级页面上线(upgrade→pay→result三段式+原价+优惠券修复)" ``` > 注意:`git add -A ` 只添加列出的路径,不会带入工作区其他未提交改动(如他人 Product 相关修改)。提交前必须 `git diff --cached --stat` 核对仅含目标文件。 --- ## 自检记录(编写时已执行) **1. 规格覆盖度:** - 设计第 3.1 sys_config 原价键 → 任务 1 ✅ - 设计第 3.2 plans 接口原价字段 → 任务 2 ✅ - 设计第 4.1 upgrade 改造(移除支付/优惠券、下一步、修 bug、划线原价)→ 任务 4 ✅ - 设计第 4.2 pay.vue(明细/优惠券 Picker/拆单提示/支付调用)→ 任务 5 ✅ - 设计第 4.3 result.vue(成功+赠券/失败+续付)→ 任务 6 ✅ - 设计第 4.4 划线原价展示规则 → 任务 4 步骤 4 + 任务 5 步骤 1(showOriginal)✅ - 设计第 6 改动清单 api.js couponId → 任务 3 ✅ - pages.json 注册 → 任务 7 ✅ **2. 占位符扫描:** 无「待定/TODO/后续实现」;所有步骤含完整代码;命令精确。 **3. 类型一致性:** - `getOriginalPriceForPeriod` 返回分(int),比较用 `original > current * 100`(current 为元字符串 → 注意:`getPriceForPeriod` 返回 `(plan.yearly / 100).toFixed(0)` 是**元字符串**!showOriginalPrice 中 `current * 100` 会把字符串隐式转数字再乘——正确。但更稳妥:直接用 plan 原始分值比较。实现时可改为读取 plan 原始字段,见下方备注) - 备注:任务 4 步骤 2 的 `showOriginalPrice` 中 `current` 来自 `getPriceForPeriod()`(元字符串),`original > current * 100` 数学上等价于「原价分 > 现价分」✅;`getOriginalPriceForPeriod` 返回分,模板中 `getOriginalPriceForPeriod(p.key) / 100` 显示元 ✅ - pay.vue:`price` 存分(从 `levels[i].yearly` 取,plans 接口返回分),`priceText = (price/100).toFixed(2)` ✅;`originalPrice` 存分,`showOriginal` 用 `originalPrice > price` 比较 ✅;`couponDiscount` 折扣券按现价比例、满减按 value(分)✅;`finalPrice = price - couponDiscount`(分)✅ - `createOrder(levelCode, 'pay', period, couponId)` 与任务 3 签名一致 ✅ - `requestVirtualPayment(dto)` 接收 VirtualPayParamsDTO(orderInfo/sign/signature/currentPeriod/totalPeriods)✅ - `nextOrder({orderNo})` 返回 `{done}` 或新支付参数 ✅ - result.vue `getJoinCouponRules()` 返回 `grantLevelCode/grantQuantity/name` ✅(与 upgrade.vue 现有用法一致) - `uni.redirectTo` 用于 result 返回,避免页面栈堆积 ✅ **4. 小程序限制合规:** 无 `?.`、无 CSS Grid(全 flex)、`:key` 用 `coupon.id`(纯属性,无表达式)✅、日期用 `parseDate()` ✅、Vue 2 Options API ✅。