docs: 新增 C 端套餐沉默期展示与自助延期提案 add-package-silence-extension
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 25m59s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 25m59s
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-11-07
|
||||
87
openspec/changes/add-package-silence-extension/design.md
Normal file
87
openspec/changes/add-package-silence-extension/design.md
Normal file
@@ -0,0 +1,87 @@
|
||||
## Context
|
||||
|
||||
- 后端 8-11 提供的沉默期 C 端能力(接口说明见 `junhong_cmp_fiber/docs/2026-10-27-实名即生效套餐沉默期-前端接口变更说明.md` 第 4、6、9 节):
|
||||
- 套餐历史 `GET /api/c/v1/asset/package-history` 每笔套餐带 `silence` 对象,总开关关闭或不适用时不返回。
|
||||
- `/api/c/v1/package-silence`:`GET /eligibility`、`GET /quote`、`POST /orders`、`GET /orders`、`GET /orders/:id`、`GET /records`。资产须属于当前客户。
|
||||
- C 端现状(`iteration/8-11`):
|
||||
- 套餐历史页 `pages/asset-package-history` 通过 `components/PackageHistoryNode.vue` 递归渲染主套餐与加油包,`api/modules/asset.js` 的 `normalizePackageHistoryNode` 用展开运算保留原字段,`silence` 会原样透传。
|
||||
- 接口统一走 `utils/request.js`:`code !== 0` 时 reject 原始响应(含 `code`、`msg`、`data`),`showError: false` 可关闭默认提示。
|
||||
- 支付:套餐购买走 `orderApi.create/pay`,微信用公众号 JSAPI(`wechatH5Pay`),支付宝走 `pages/alipay-payment`(`u-qrcode` 渲染二维码 + 复制链接),返回刷新用 `markPendingPaymentRefresh` / `consumePendingPaymentRefresh`。
|
||||
- 支付方式标签与过滤在 `utils/payment-methods.js`,资产信息返回 `allowed_payment_methods`。
|
||||
- 后端延期下单的支付:`POST /orders` 只返回 `qr_content`,按商户池路由:支付宝为 WAP 支付链接,微信为微信 H5 支付链接(`H5URL`),富友为扫码内容。没有返回 JSAPI 支付参数。
|
||||
- 约束:基于 `iteration/8-11`,只部署测试环境;不改停复机逻辑;不改现有套餐购买流程。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 套餐历史可见沉默期状态与截止日,可延期的套餐有入口。
|
||||
- 客户能自助完成付费延期并看到结果。
|
||||
- 金额、可选天数、新截止日全部以后端为准。
|
||||
|
||||
**Non-Goals:**
|
||||
- 特殊延期、线下登记、批量延期(后台功能)。
|
||||
- 后端改动,包括为延期订单增加微信 JSAPI 支付。
|
||||
- 生产发布。
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1 沉默期展示放在 `PackageHistoryNode` 内
|
||||
|
||||
在套餐卡片"到期时间"下方加沉默期区域,`node.silence` 为空时整块不渲染;加油包节点复用同一组件,规则一致。不新开详情页,客户在原页面即可看到状态。状态名直接用后端 `silence_status_name`,前端只按 `silence_status` 选颜色(waiting 蓝、expired 红、ended 灰)。
|
||||
|
||||
备选:在套餐历史页顶部汇总沉默期。拒绝原因:一个资产可能有多笔套餐,按笔展示更清楚。
|
||||
|
||||
### D2 新增接口模块 `api/modules/packageSilence.js`
|
||||
|
||||
与现有模块风格一致,导出 `packageSilenceApi`:`getEligibility`、`quote`、`createOrder`、`getOrders`、`getOrder`、`getRecords`。下单和资格请求 `showError: false`,由页面按错误码映射提示(D6)。订单列表默认带当前资产 `asset_identifier`,只看当前资产的订单。
|
||||
|
||||
### D3 页面结构
|
||||
|
||||
新增三个页面并在 `pages.json` 注册:
|
||||
|
||||
| 页面 | 用途 |
|
||||
|---|---|
|
||||
| `pages/silence-extension/silence-extension` | 资格、选天数、试算、选支付方式、下单 |
|
||||
| `pages/silence-orders/silence-orders` | 我的延期订单列表,点击进入详情(详情用同目录 `detail` 页) |
|
||||
| `pages/silence-records/silence-records` | 某笔套餐的沉默期流水 |
|
||||
|
||||
延期页参数 `package_usage_id`,流水页参数 `package_usage_id`。入口:套餐卡片"延期""沉默期记录";"我的延期订单"放在套餐历史页右上角或延期页底部。
|
||||
|
||||
### D4 天数选择与试算
|
||||
|
||||
优先展示 `options` 档位(每档计费天数、金额、新截止日),默认选第一档;同时提供"自定义天数"输入,输入停止 500ms 后调 `quote`,以最后一次请求结果为准(丢弃过期响应)。前端只校验 `min_days ≤ days ≤ max_days` 和整数,金额一律显示后端值(分转元,两位小数)。
|
||||
|
||||
### D5 下单幂等与支付拉起
|
||||
|
||||
- `request_id` 在客户点击"确认支付"时生成(时间戳 + 随机串),存在页面状态中;同一次提交的网络重试复用,天数或支付方式变化后清空。
|
||||
- 提交中按钮置为 loading,禁止重复提交。
|
||||
- 支付宝:复用 `openAlipayPayment` 跳 `pages/alipay-payment`,二维码与复制链接都用 `qr_content`。
|
||||
- 微信:后端返回的是微信 H5 支付链接或扫码内容,不是 JSAPI 参数。实现时先按"`qr_content` 是 http(s) 链接则 `window.location.href` 跳转,否则用 `u-qrcode` 渲染二维码并提示截图扫码"处理,见 Open Questions。
|
||||
- 拉起前 `markPendingPaymentRefresh('silence-extension')`;延期页与订单详情 `onShow` 时 `consume` 到标记即查询订单详情。
|
||||
|
||||
### D6 错误码映射
|
||||
|
||||
在 `utils/silence-errors.js` 维护 1263~1274 的客户提示文案,1265、1266 用 `data` 中的范围拼接文案;资格接口的 `reason` 也映射为文案(not_expired、no_silence、extension_disabled、no_policy、exceeds_limit、refunded、order_pending、activated、adjust_pending)。未映射的错误码回退为后端 `msg`。
|
||||
|
||||
### D7 测试
|
||||
|
||||
沿用 `tests/october-h5.test.mjs` 的运行方式新增测试:套餐卡片沉默期显示与入口条件、天数校验、`request_id` 复用规则、错误码映射、支付返回刷新。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [微信 H5 支付链接在微信内置浏览器中通常无法直接支付] → C 端主要在公众号内打开。上线测试前需要确认测试环境商户池给延期订单路由的微信渠道;若确实不能在微信内支付,短期只开放支付宝或二维码,长期需后端为延期订单提供 JSAPI 支付(另开后端变更)。
|
||||
- [总开关默认关闭,测试环境看不到沉默期] → 验证前请超级管理员在后台打开总开关(打开前有 4 项检查)。
|
||||
- [支付回跳不可靠] → 不依赖回跳,统一在 `onShow` 查订单详情;订单列表也可手动刷新。
|
||||
- [延期下单不涉及停复机] → 延期只改套餐沉默期截止日;后端在延期生效后是否复机由后端规则决定,测试环境资产已全部在只读表中,不会真实复机。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 在 `iteration/8-11` 上实现并本地 `build:h5`(development 模式,接测试后端)。
|
||||
2. 推送 `iteration/8-11` 部署测试环境(C 端 CI 目前推送未触发构建,需先查原因或手动部署)。
|
||||
3. 测试环境打开沉默期总开关,用测试资产验证:展示、资格、试算、下单、支付、流水。
|
||||
4. 回退:回滚 `iteration/8-11` 对应提交重新部署;后端关闭总开关时前端自动不显示沉默期。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 延期订单的微信支付方式:测试与生产商户池对延期订单走哪种微信渠道?是否需要后端提供 JSAPI 支付?决定前按 D5 的兜底方式实现。
|
||||
- "我的延期订单"入口位置(套餐历史页右上角 / 首页功能卡 / 延期页底部),默认放套餐历史页右上角。
|
||||
31
openspec/changes/add-package-silence-extension/proposal.md
Normal file
31
openspec/changes/add-package-silence-extension/proposal.md
Normal file
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
后端 8-11 迭代已上线"实名即生效套餐沉默期":套餐历史返回 `silence` 对象,并提供 H5 自助延期接口(`/api/c/v1/package-silence/*`)。C 端目前没有用上任何沉默期字段和接口,客户看不到沉默期状态,沉默期过期后也无法自助付费延期,只能找运营在后台办理。依据 `junhong_cmp_fiber/docs/2026-11-06-测试环境8-11新功能前端对接与权限现状.md` 第 5.1 节补齐 C 端。
|
||||
|
||||
本变更基于 `iteration/8-11` 分支,只部署测试环境。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 套餐历史每笔套餐展示沉默期:状态、截止时间、沉默期天数、累计延期天数、"调整审批中"标记;`silence` 为空时不显示,保持现有展示。
|
||||
- `silence.extendable = true` 时显示"延期"入口。
|
||||
- 新增自助延期流程:查询延期资格 → 选择或输入延期天数并试算金额与新截止日 → 选择支付方式下单 → 用返回的 `qr_content` 拉起支付或展示二维码 → 支付后刷新状态。
|
||||
- 新增"我的延期订单"列表与详情。
|
||||
- 新增套餐沉默期流水查看。
|
||||
- 沉默期错误码 1263~1274 给出客户能看懂的提示。
|
||||
- 不改现有套餐购买、钱包、实名流程;不改任何停复机相关逻辑(前端本来也不发起停复机)。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `package-silence-extension`:C 端套餐沉默期展示、自助付费延期、延期订单与沉默期流水查看、沉默期错误提示。
|
||||
|
||||
### Modified Capabilities
|
||||
(无。本仓库 `openspec/specs/` 下没有已有规格。)
|
||||
|
||||
## Impact
|
||||
|
||||
- 页面:`pages/asset-package-history`、`components/PackageHistoryNode.vue`;新增延期页、延期订单列表与详情页、沉默期流水页,并在 `pages.json` 注册。
|
||||
- 接口:新增 `api/modules/packageSilence.js`,在 `api/index.js` 导出;`api/modules/asset.js` 的套餐历史归一化保留 `silence` 字段。
|
||||
- 支付:复用 `utils/payment.js` 的支付宝支付页和待刷新标记;微信支付方式取决于后端 `qr_content` 的类型,见 design 待确认事项。
|
||||
- 后端接口(8-11 已提供,不改):`GET /api/c/v1/asset/package-history`,`GET /api/c/v1/package-silence/eligibility`、`/quote`、`/orders`、`/orders/:id`、`/records`,`POST /api/c/v1/package-silence/orders`。
|
||||
- 依赖后端总开关 `package.silence.enabled`:关闭时不返回 `silence`、延期接口返回 1274;测试环境验证前需要打开。
|
||||
@@ -0,0 +1,97 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 套餐历史展示沉默期
|
||||
|
||||
套餐历史中每笔套餐返回 `silence` 对象时,系统 SHALL 在该套餐卡片中展示沉默期信息:状态名称 `silence_status_name`、截止时间 `silence_deadline_at`、沉默期天数 `silence_period_days`、累计延期天数 `silence_extended_days`(大于 0 时)。`silence_adjust_pending = true` 时 SHALL 显示"调整审批中"。`silence` 为空或缺失时 MUST NOT 显示任何沉默期内容,其余展示保持不变。关联加油包节点同样按此规则处理。
|
||||
|
||||
#### Scenario: 待实名套餐
|
||||
- **WHEN** 套餐历史中一笔套餐的 `silence.silence_status = waiting`,截止时间为 2026-12-05 23:59:59
|
||||
- **THEN** 该套餐卡片显示"待实名"(或后端返回的"延期待实名")和截止日期 2026-12-05
|
||||
|
||||
#### Scenario: 总开关关闭
|
||||
- **WHEN** 套餐历史返回的套餐都没有 `silence` 字段
|
||||
- **THEN** 页面与改动前一致,不显示沉默期区域和延期入口
|
||||
|
||||
#### Scenario: 调整审批中
|
||||
- **WHEN** 一笔套餐的 `silence.silence_adjust_pending = true`
|
||||
- **THEN** 沉默期区域显示"调整审批中"
|
||||
|
||||
### Requirement: 延期入口
|
||||
|
||||
系统 SHALL 只在 `silence.extendable = true` 时在该套餐卡片显示"延期"入口;点击后进入该套餐的延期页面,并带上 `package_usage_id`。`extendable` 为 false 或缺失时 MUST NOT 显示入口。
|
||||
|
||||
#### Scenario: 已过期可延期
|
||||
- **WHEN** 一笔套餐 `silence.silence_status = expired` 且 `extendable = true`
|
||||
- **THEN** 卡片显示"延期"入口,点击进入延期页面
|
||||
|
||||
#### Scenario: 不可延期
|
||||
- **WHEN** 一笔套餐 `silence.extendable = false`
|
||||
- **THEN** 卡片不显示"延期"入口
|
||||
|
||||
### Requirement: 延期资格与试算
|
||||
|
||||
进入延期页面时,系统 SHALL 调用 `GET /api/c/v1/package-silence/eligibility?package_usage_id=` 获取资格。`can_extend = false` 时 SHALL 按 `reason` 展示不能办理的原因,MUST NOT 允许下单;`pending_order_no` 非空时 SHALL 提示已有未完成的延期订单,并提供进入该订单详情的入口。可以办理时 SHALL 展示当前截止日期、已过期天数、可选天数范围(`min_days`~`max_days`),并列出 `options` 中每档的计费天数、金额与新截止日期供选择;客户输入自定义天数时 SHALL 调用 `GET /quote` 试算并展示计费天数、金额与新截止日期。金额 MUST 以后端返回为准,前端只传天数、不自行计算金额。输入天数超出 `min_days`~`max_days` 时 MUST 在提交前提示并禁止下单。
|
||||
|
||||
#### Scenario: 选择档位
|
||||
- **WHEN** 资格返回 `options` 含 30 天 / 60 天两档,客户选择 60 天
|
||||
- **THEN** 页面显示 60 天档的金额(分换算为元)与新截止日期,可以下单
|
||||
|
||||
#### Scenario: 输入天数试算
|
||||
- **WHEN** 客户输入 31 天
|
||||
- **THEN** 页面调用试算接口,显示计费天数 60 天、对应金额与新截止日期
|
||||
|
||||
#### Scenario: 已有未完成订单
|
||||
- **WHEN** 资格返回 `pending_order_no` 非空
|
||||
- **THEN** 页面提示已有未完成的延期订单,不能新建,并可进入该订单详情
|
||||
|
||||
#### Scenario: 不能办理
|
||||
- **WHEN** 资格返回 `can_extend = false`、`reason = not_expired`
|
||||
- **THEN** 页面显示"套餐沉默期尚未过期,暂不需要延期",下单按钮不可用
|
||||
|
||||
### Requirement: 自助下单与支付
|
||||
|
||||
客户确认天数与支付方式后,系统 SHALL 调用 `POST /api/c/v1/package-silence/orders`,参数为 `package_usage_id`、`days`、`payment_method`、`request_id`。支付方式 MUST 只在 `wechat`、`alipay` 中,并取当前资产 `allowed_payment_methods` 的交集;交集为空时 SHALL 提示暂不支持在线延期。同一次提交的网络重试 MUST 复用同一个 `request_id`,客户重新确认(天数或支付方式变化)时 SHALL 生成新的 `request_id`。提交期间 MUST 防止重复点击。下单成功后 SHALL 用返回的 `qr_content` 拉起支付:支付宝走现有支付宝支付页(二维码 + 复制链接);微信按 design 中确定的方式处理。拉起支付后 SHALL 记录待刷新标记,客户返回时刷新订单状态。
|
||||
|
||||
#### Scenario: 支付宝下单
|
||||
- **WHEN** 客户选择 30 天、支付宝支付并确认
|
||||
- **THEN** 系统下单后进入支付宝支付页,展示 `qr_content` 二维码与复制链接
|
||||
|
||||
#### Scenario: 重复点击
|
||||
- **WHEN** 客户在下单请求返回前再次点击确认
|
||||
- **THEN** 系统不发起第二次下单请求
|
||||
|
||||
#### Scenario: 无可用支付方式
|
||||
- **WHEN** 资产 `allowed_payment_methods` 只有 `wallet`
|
||||
- **THEN** 延期页面提示暂不支持在线延期,不显示下单按钮
|
||||
|
||||
### Requirement: 支付结果与订单查看
|
||||
|
||||
系统 SHALL 提供"我的延期订单"列表(`GET /api/c/v1/package-silence/orders`,可按支付时间 `paid_start`、`paid_end` 筛选,分页加载)与详情(`GET /orders/:id`)。详情 SHALL 展示订单号、套餐、天数(输入天数与计费天数)、金额、状态名称、支付方式、下单前截止时间、预计新截止时间、支付时间、延期生效时间;状态为 `pending_payment` 且返回 `qr_content` 时 SHALL 提供"继续支付"。从支付返回后 SHALL 重新查询订单详情;状态为 `completed` 时提示延期成功并刷新套餐历史。
|
||||
|
||||
#### Scenario: 支付成功返回
|
||||
- **WHEN** 客户支付完成返回,订单状态为 `completed`
|
||||
- **THEN** 页面提示延期成功,套餐历史中该套餐显示新的截止日期
|
||||
|
||||
#### Scenario: 继续支付
|
||||
- **WHEN** 客户打开一笔 `pending_payment` 的延期订单,详情带 `qr_content`
|
||||
- **THEN** 页面显示"继续支付",点击后按原支付方式拉起支付
|
||||
|
||||
### Requirement: 沉默期流水
|
||||
|
||||
系统 SHALL 在有 `silence` 的套餐上提供"沉默期记录"入口,调用 `GET /api/c/v1/package-silence/records?package_usage_id=` 按时间展示该套餐的沉默期变化记录;无记录时显示空状态。
|
||||
|
||||
#### Scenario: 查看流水
|
||||
- **WHEN** 客户在一笔已延期过的套餐上打开沉默期记录
|
||||
- **THEN** 页面按时间列出起算、延期等记录
|
||||
|
||||
### Requirement: 沉默期错误提示
|
||||
|
||||
沉默期接口返回错误码 1263~1274 时,系统 SHALL 显示面向客户的中文提示,MUST NOT 直接展示内部错误名称。至少包括:1263 套餐当前不需要延期、1264 该套餐不支持付费延期请联系客服、1265 超过可延期期限、1266 延期天数不在允许范围(带 `min_days`~`max_days`)、1267 免费延期次数已用完、1270 暂未开放延期请联系客服、1271 已有未完成的延期订单、1274 沉默期功能未开启。其他错误码沿用通用错误提示。
|
||||
|
||||
#### Scenario: 天数超范围
|
||||
- **WHEN** 下单返回 1266,`data.min_days = 30`、`data.max_days = 180`
|
||||
- **THEN** 页面提示"延期天数需在 30~180 天之间"
|
||||
|
||||
#### Scenario: 功能未开启
|
||||
- **WHEN** 资格接口返回 1274
|
||||
- **THEN** 延期页面提示"延期功能暂未开放",不显示下单按钮
|
||||
38
openspec/changes/add-package-silence-extension/tasks.md
Normal file
38
openspec/changes/add-package-silence-extension/tasks.md
Normal file
@@ -0,0 +1,38 @@
|
||||
## 1. 接口与工具
|
||||
|
||||
- [ ] 1.1 新增 `api/modules/packageSilence.js`(eligibility、quote、createOrder、getOrders、getOrder、getRecords),在 `api/index.js` 导出
|
||||
- [ ] 1.2 确认 `normalizeAssetPackageHistory` 保留 `silence` 对象(含加油包子节点),补充布尔字段 `extendable`、`silence_adjust_pending` 归一化
|
||||
- [ ] 1.3 新增 `utils/silence-errors.js`:错误码 1263~1274 与资格 `reason` 的客户提示文案,1265/1266 拼接范围
|
||||
- [ ] 1.4 新增延期支付方式过滤:`allowed_payment_methods` 与 `wechat`、`alipay` 取交集
|
||||
|
||||
## 2. 套餐历史展示
|
||||
|
||||
- [ ] 2.1 `PackageHistoryNode.vue` 增加沉默期区域:状态名、截止日期、沉默期天数、累计延期天数、"调整审批中";`silence` 为空不渲染
|
||||
- [ ] 2.2 `extendable = true` 时显示"延期"入口,跳转延期页并带 `package_usage_id`
|
||||
- [ ] 2.3 有 `silence` 的套餐显示"沉默期记录"入口
|
||||
- [ ] 2.4 套餐历史页增加"我的延期订单"入口;从延期成功返回时刷新列表
|
||||
|
||||
## 3. 延期页
|
||||
|
||||
- [ ] 3.1 新建 `pages/silence-extension/silence-extension` 并注册 `pages.json`
|
||||
- [ ] 3.2 加载资格:不能办理时展示原因;有 `pending_order_no` 时提示并提供订单详情入口;1274 显示功能未开放
|
||||
- [ ] 3.3 展示当前截止日期、已过期天数、`options` 档位(计费天数、金额、新截止日),默认选第一档
|
||||
- [ ] 3.4 自定义天数输入:范围与整数校验,防抖调用试算,丢弃过期响应
|
||||
- [ ] 3.5 支付方式选择;无可用方式时提示暂不支持在线延期
|
||||
- [ ] 3.6 下单:生成并复用 `request_id`,提交中防重复,错误码按 1.3 提示
|
||||
- [ ] 3.7 拉起支付:支付宝走现有支付宝支付页;微信按 design D5(链接跳转或二维码);拉起前写待刷新标记
|
||||
- [ ] 3.8 `onShow` 消费待刷新标记后查询订单详情,`completed` 时提示延期成功
|
||||
|
||||
## 4. 延期订单与流水
|
||||
|
||||
- [ ] 4.1 新建延期订单列表页(分页、按支付时间筛选、默认当前资产)
|
||||
- [ ] 4.2 新建延期订单详情页:订单号、套餐、输入/计费天数、金额、状态、支付方式、截止时间变化、支付与生效时间;`pending_payment` 且有 `qr_content` 时"继续支付"
|
||||
- [ ] 4.3 新建沉默期流水页:按时间展示 `event_name`、截止时间变化、天数,空状态
|
||||
|
||||
## 5. 测试与验证
|
||||
|
||||
- [ ] 5.1 在 `tests/` 增加用例:沉默期显示与入口条件、天数校验、`request_id` 复用、错误码映射、支付返回刷新
|
||||
- [ ] 5.2 本地 `build:h5`(development 模式)通过,确认接口指向测试后端
|
||||
- [ ] 5.3 确认延期订单微信支付方式(design Open Questions),按结论调整 3.7
|
||||
- [ ] 5.4 测试环境打开沉默期总开关后,用测试资产走通:展示 → 资格 → 试算 → 下单 → 支付 → 延期生效 → 流水
|
||||
- [ ] 5.5 推送 `iteration/8-11` 部署测试环境(先排查 C 端 CI 未触发构建的原因)
|
||||
Reference in New Issue
Block a user