This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# 第 1 批 H5 对接实施与验证记录
|
||||
|
||||
2026-10-13:客户端功能已实现。仅对接 `/api/c/v1`;不处理代理注册、密码规则和后台接口。真实微信授权、短信绑定及后端 A/B 资产通知范围尚需测试环境联调,未部署或提交代码。
|
||||
|
||||
## 已实现
|
||||
|
||||
- 客户手机号、资产是否绑号、号码归属、绑定提示分别保存;登录字段按当前令牌和资产隔离,退出/过期/切换时清理,旧 phoneBindStatus 不再作为权威状态。
|
||||
- 四种绑定模式使用原 bind-phone 入参;资产首次绑号可输入与客户号不同的号码,1192 提示管理员并禁用当前提交,1184 保留后端错误。强制绑定成功仍要求手动重登。
|
||||
- 客户换号只更新后端确认的客户号码,不改资产关联;归属事实失效,下一次真实登录重新确认。换号的原号来源不再放进 URL。
|
||||
- asset/info 的共享返回入口接收一次性临期提醒,首页使用可关闭横幅。0 天正常显示,使用服务器等级和上海时区。强制绑定页不为预填请求 asset/info,避免提前消费提醒后跨重登丢失。
|
||||
- 旧首页通知弹窗排除临期项,通知中心保留。全部已读改 POST,单条已读保持 PUT;成功后重新读取列表和计数,失败不乐观清空,延迟响应不能覆盖新的资产上下文。
|
||||
|
||||
## 验证证据
|
||||
|
||||
| 证据 | 结果 | 说明 |
|
||||
|---|---|---|
|
||||
| `npm run test:h5-integration` | 14/14 通过 | 实际 store/API 模块及页面 setup,使用 Vue 响应性;原生 API、生命周期和后端响应为模拟 |
|
||||
| `npm run build:h5` | 通过 | uni-app H5 开发配置构建 |
|
||||
| `npm run build:h5:production` | 通过 | uni-app H5 生产配置构建 |
|
||||
| `openspec validate update-october-batch1-h5-integration --strict` | 通过 | 提案、需求和场景结构校验 |
|
||||
| `git diff --check` | 通过 | 默认 Git 换行配置下未发现补丁空白错误;Windows 换行提示不影响构建 |
|
||||
| 本地浏览器,390×844 | 通过 | 0 天红色横幅、上海到期时间、关闭、绑定客户号预填、编辑不同号码、1192 联系管理员弹窗及禁用提交,无页面运行错误 |
|
||||
|
||||
测试文件:[tests/october-h5.test.mjs](../../../tests/october-h5.test.mjs)。覆盖绑定模式/成功后的分类更新、登录提示开关、状态持久化/隔离、换号不迁移资产、1192/1184、强制绑定不提前消费提醒、共享接收/并发、0~15 天/上海边界、旧弹窗过滤、POST/PUT 请求方法、通知失败与切换、模拟 A/B 全部已读以及 401 状态清理。
|
||||
|
||||
本地渲染复验:先完成生产构建,再运行以下命令。使用独立浏览器会话和虚构号码,所有业务接口改写到本地模拟服务。
|
||||
|
||||
```powershell
|
||||
node tests/mock-h5-preview.mjs
|
||||
```
|
||||
|
||||
另开一个终端:
|
||||
|
||||
```powershell
|
||||
agent-browser --session october-h5-ui --headed false --init-script ./tests/mock-h5-browser-init.js open http://127.0.0.1:48138/pages/index/index
|
||||
agent-browser --session october-h5-ui set viewport 390 844
|
||||
agent-browser --session october-h5-ui snapshot -i
|
||||
agent-browser --session october-h5-ui close
|
||||
```
|
||||
|
||||
模拟服务固定在 `127.0.0.1:48138`,init 脚本仅在该地址注入;检查后使用 Ctrl+C 停止服务。该模拟接口故意重复返回提醒用于检查前端防重复,不能证明后端每日一次规则。
|
||||
|
||||
## 仍需真实联调
|
||||
|
||||
1. 微信公众号真实登录应返回三个布尔字段;尤其验证客户无号/资产有号时只给客户绑定,以及客户已有号时资产可绑定不同号码。
|
||||
2. 确认 asset/info.bound_phone 仍表示当前客户手机号,与登录布尔事实区分;换号后资产号码保持原值。若实际响应与对接文档冲突,先确认契约,不猜测接口。
|
||||
3. 同客户 A/B 两个临期资产分别扫码进入,确认服务器每天只返回一次、临期列表/未读数仅计当前资产,以及 A 上 POST 全部已读不改变 B 的临期通知。
|
||||
4. 完整回归真实支付、实名认证、套餐订购以及运营/风险换货候选弹窗。本次没有修改这些接口或候选组件,构建成功不替代真实业务验收。
|
||||
|
||||
完成结果已按实际验证范围回写 [tasks.md](tasks.md),真实联调项保留未勾选;不归档历史提案。
|
||||
@@ -0,0 +1,92 @@
|
||||
## Context
|
||||
|
||||
项目为 Vue 3 / uni-app H5。`openspec/specs` 当前没有已归档基线,因此本提案在现有相关 capability 名称下使用 `ADDED Requirements` 建立本次需求,不对不存在的基线使用 `MODIFIED`。`phone-change-migration` 保留历史 capability 名称以便追踪,但行为明确改为“只更换客户手机号”,不表示仍支持迁移。
|
||||
|
||||
接口行为以 2026-10-12 的第 1 批对接说明和用户 2026-10-13 的答复为准。仓库未提供说明中引用的 `docs/admin-openapi.yaml`,不因此回退旧的换号或全部已读约定。
|
||||
|
||||
| 代码证据 | 问题 | 处理方向 |
|
||||
|---|---|---|
|
||||
| `pages/login/login.vue` 只读取 `need_bind_phone` | 登录的资产绑定事实未保存 | 保存三个布尔字段,按当前登录资产隔离 |
|
||||
| `pages/index/index.vue` 用本地手机号优先覆盖 `bound_phone`,并据此计算 `alreadyBindPhone` | 客户手机号与资产关联混为一体,旧缓存可覆盖服务端 | 拆分客户手机号、资产绑定/归属、是否需要提示 |
|
||||
| `pages/bind/bind.vue` 成功后无条件 `setPhoneBindStatus` | 只给客户绑号也可能被记为资产换号 | 按本次绑定对象刷新,不推断资产号码 |
|
||||
| `pages/change-phone/change-phone.vue` 把 `newPhone` 写入资产缓存 | 与新文档“只换客户手机号”冲突 | 仅更新客户手机号,保留资产关联 |
|
||||
| 首页、TrafficCard、绑定页等分别请求 `asset/info` | 一次性提醒可能先被非展示调用方取走 | 在 API 返回入口集中接收,再由资产信息区展示 |
|
||||
| 首页 `loadUnreadNotifications` 展示所有未读项 | 仍以旧临期通知触发弹窗 | 从旧弹窗来源排除临期项,通知中心不删除 |
|
||||
| `api/modules/notification.js` 的 `markAllRead` 使用 PUT | 与新文档方法不一致 | 明确切换 POST,不影响 `markRead` 的 PUT |
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals:正确区分客户与资产绑定、支持文档四种流程、客户换号不改变资产关联、一次性临期提醒不丢失/不重复、通知全部已读方法正确。
|
||||
- Non-Goals:代理注册及密码规则、后台接口、全局通知重构、新增登录方式、重新设计支付或实名流程。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 分离客户与资产手机号状态
|
||||
|
||||
`asset_phone_bound` 只表示当前资产存在有效手机号关联,`asset_phone_is_own` 只表示该关联是否属于当前客户;二者不提供号码。`need_bind_phone` 是后端计算的提示开关,不能由前端用手机号为空、归属为 false 或本地缓存重新计算。
|
||||
|
||||
现有八月份接口定义 `docs/产品迭代8月份/11.md` 将 `asset/info.bound_phone` 描述为“当前登录用户绑定的手机号”。新对接说明声明字段为兼容性新增、未改变该字段,故保留其客户手机号用途:可作为绑定预填和更换客户手机号的原号来源,但不能作为资产关联是否存在的证据。若实施联调的实际响应与这一未变字段约定冲突,先向用户说明并确认,不猜测新增字段或号码。
|
||||
|
||||
在用户状态中记录当前登录资产的三个布尔字段;在退出、切换账号或资产时清理/更换。原 `phoneBindStatus` 不再作为服务端资产事实或客户原手机号的权威来源,不能让历史的 `bound: true` 绕过当前状态,也不能跨客户复用。未获取新字段时保持“未知”,不把缺失默认为已绑定,也不新增 OAuth 自动重登来刷新事实。
|
||||
|
||||
### 2. 按四种场景处理绑定
|
||||
|
||||
| 客户手机号 | 资产手机号 | 表单与成功后的处理 |
|
||||
|---|---|---|
|
||||
| 无 | 无 | 验证一次输入号码,由后端同时写入客户与资产 |
|
||||
| 有 | 无 | 提示“给当前资产绑定手机号”;客户号码可预填且可编辑,允许提交不同号码,不据此改客户手机号 |
|
||||
| 无 | 有 | 提示“绑定客户手机号”;仍使用既有 bind-phone 请求,成功后只更新客户状态,不覆盖资产关联 |
|
||||
| 有 | 有 | 不要求绑定;资产号码属于他人也不要求改绑或暴露号码 |
|
||||
|
||||
是否触发登录后的绑定提示仍取决于 `need_bind_phone`;关闭强制开关时不产生自动跳转。首页功能入口分别展示资产绑定状态与客户换号能力,不能因客户已有手机号就禁用资产首次绑号。
|
||||
|
||||
对于 `1192`,展示联系管理员的明确提示,停止再次提交当前资产改绑请求;不自动重试、不换接口解除、不显示资产号码。`1184` 等其他错误按返回消息提示,失败不更新状态。请求仍使用 `{ phone, code }`,不凭空新增绑定对象参数。
|
||||
|
||||
保留强制绑定完成后回登录页、保留 identifier 并由用户手动重登的现有行为;普通绑定成功返回上一页。成功后的状态更新必须对应本次实际绑定对象,不能用“请求成功”统一代表客户号和资产号均已修改。
|
||||
|
||||
实施时避免强制绑定页为预填发起 asset/info:该页优先使用已有客户手机号;尚无客户号码时保留可输入表单,由后端判定实际绑定对象。`asset_phone_bound=true` 且 `need_bind_phone=true` 明确对应“只给客户绑号”。普通绑定页继续使用 asset/info 预填。这保证强制绑定后的手动重登不会丢弃已被预填请求消费的每日提醒,也不需要猜测跨令牌的客户身份。
|
||||
|
||||
### 3. 客户换号不迁移资产
|
||||
|
||||
保留原/新手机号和验证码的现有表单以及 `change_phone_old`、`change_phone_new` 场景。成功后只更新服务端确认的客户手机号,不调用 `setPhoneBindStatus(identifier, newPhone)`,不显示“全部资产换绑成功”或旧事务迁移文案。
|
||||
|
||||
原资产仍可保持已绑定,但其号码可能不再属于客户;例如原 `asset_phone_is_own=true` 在客户改成不同号码后可能为 false。若响应不提供更新后的归属事实,不能把旧 true 留作最新事实,应失效该归属状态,等下一次真实登录响应确认;不借此要求资产改绑。
|
||||
|
||||
### 4. 一次性临期提醒使用横幅及共享接收
|
||||
|
||||
首页就是当前 H5 的资产信息展示页,本次不新增资产详情路由。选择可关闭横幅展示临期信息,避免与已有风险换货、运营及支付弹窗争抢显示。
|
||||
|
||||
原样保留 `expiry_reminder.remaining_days`、`expires_at`、`level`、`level_name`;使用服务端等级映射 red / purple / pink 的样式,保留文本提示,不能只靠颜色传达紧急程度。`remaining_days=0` 必须正常展示。日期展示遵循 Asia/Shanghai,不能用客户端当前时区重算剩余自然日或等级。
|
||||
|
||||
`assetApi.getInfo` 成功返回处集中接收非空提醒并交给共享状态,同次资产进入期间的并发响应不允许用“未返回字段”的响应覆盖已经接收的提醒。共享状态隔离当前客户上下文与 identifier,避免切换资产/退出后显示旧资产或他人的提醒。提醒到达时若资产信息区尚不可见,应先暂存,待对应资产可见时再展示,不能静默丢弃。
|
||||
|
||||
后端负责“客户 + 资产 + 上海自然日”的每日一次;前端只防止同一次接收被多个组件重复展示,不增加每日定时推送、补发请求或长期缓存复播。关闭/已展示的提醒在当天后续无字段响应时不得重新出现。审核所有 `asset/info` 调用方,特别是绑定前预填及首页 TrafficCard 的请求顺序;账户身份无法确定的跨重登暂存不得当作可信提醒直接展示,若现有接口无法保证安全传递,应在实施前向用户说明具体阻碍。
|
||||
|
||||
横幅展示/关闭不自动标记通知已读:提醒不含 notification_id,不能按标题、日期或列表位置猜测 ID,也不能调用 read-all 消掉新通知。临期通知继续由通知中心管理阅读状态。
|
||||
|
||||
### 5. 通知仅调整临期来源与全部已读方法
|
||||
|
||||
从首页旧通知弹窗来源排除临期项,沿用既有 `category=expiry` 识别方式;不删除通知中心临期项,不改变其他未读通知的顺序和展示。`popup-candidates` 的风险换货/运营链路不受影响。
|
||||
|
||||
列表、未读数保持原入参,不增加文档未定义的 identifier/asset_id;后端基于当前认证资产过滤临期通知,并非所有通知都只属于当前资产。切换资产后刷新列表和计数,不将 A 的未读结果拼接到 B。
|
||||
|
||||
全部已读使用 POST,保留通知中心主动触发;失败不提前清除未读状态。成功后以刷新结果对齐本地列表/未读数,不能把其他资产的临期通知置已读。单条已读继续 PUT。风险换货、运营弹窗及临期横幅均不得自动调用全部已读。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 一次性提醒在任何 asset/info 读取时就可能被后端消费:集中接收并回归首页并发、绑定预填、其他页面进入,不能只给首页 getInfo 的响应加判断。
|
||||
- 客户号码与资产号码不同:服务端布尔事实和客户号分离,历史本地缓存不再覆盖服务端。
|
||||
- 没有最新 YAML:按已确认的新文档实施;如实际契约缺字段或与四场景不符,提供响应结构差异(不含真实号码/令牌)并先询问用户。
|
||||
- 尚未归档的旧提案仍含相反规则:本提案显式声明覆盖范围,不顺带改写或归档历史提案。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 提案批准后,先完成状态拆分及旧缓存失效,再调整绑定/换号行为。
|
||||
2. 加入临期提醒共享接收和横幅,排除首页旧临期弹窗来源。
|
||||
3. 切换全部已读为 POST,回归当前资产的列表与未读数。
|
||||
4. 完成任务中的场景验证及 H5 构建,记录实际结果;不以静态构建代替真实接口联调。
|
||||
5. 需要回退时只回退本次前端改动;服务端新契约不变,不能恢复“客户换号即资产迁移”的旧假设作为兼容策略。
|
||||
|
||||
## Open Questions
|
||||
|
||||
提案范围和优先契约已由用户确认,无需再次决定代理注册或 read-all 方法。实施前仍应检查接口是否实际提供所需字段及一次性提醒跨页面传递条件;只在发现具体矛盾时报告并询问,不擅自扩展接口。
|
||||
@@ -0,0 +1,56 @@
|
||||
# Change: 对接十月份第 1 批 H5 接口变更
|
||||
|
||||
本提案仅覆盖 `/api/c/v1` 的现有 H5 功能。用户于 2026-10-13 批准执行;实现和验证结果见 [IMPLEMENTATION.md](IMPLEMENTATION.md)。
|
||||
|
||||
## Why
|
||||
|
||||
[第 1 批前端对接说明](../../../docs/10月份修改/第1批-8-9月迭代前端对接说明.md)中的 F1、M9 改变了手机号关联和临期提醒的行为。当前代码仍混用客户手机号与资产绑定状态、在客户换号后覆盖资产本地手机号,并从未读通知触发临期弹窗,可能出现错误的绑定提示和重复提醒。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 接收并保存公众号登录返回的 `asset_phone_bound`、`asset_phone_is_own`、`need_bind_phone`,区分客户手机号与当前资产手机号关联,不能再用一个“已绑定”缓存表达两者。
|
||||
- 按 F1 的四种场景调整绑定页文案、绑定入口及提交后的状态处理;客户已有手机号、资产无号时,允许给资产绑定不同号码。
|
||||
- 对 `bind-phone` 的 `1192` 提示“该资产已绑定手机号,如需更换请联系管理员”,不提供重试当前绑定请求的操作;保留 `1184` 的既有错误处理。
|
||||
- **BREAKING(替代旧前端假设)**:`change-phone` 只修改客户手机号,不更新任何资产手机号关联;停止将新客户手机号写入资产绑定缓存。
|
||||
- 接收 `asset/info.expiry_reminder`,在首页资产信息区展示可关闭横幅,展示后端提供的剩余天数、到期时间和等级;无字段时不自行计算或补发提醒。
|
||||
- 统一接收各 `asset/info` 调用返回的一次性提醒,防止首页子组件或其他调用方先获得字段后将其丢弃;不为提醒新增轮询或额外请求。
|
||||
- 首页旧未读通知弹窗不再以临期通知作为弹窗依据;临期通知仍保留在通知中心,其他通知和既有运营/风险换货弹窗不变。
|
||||
- **BREAKING(请求方法切换)**:全部已读改为 `POST /api/c/v1/notifications/read-all`,不再发送旧 `PUT` 或自动回退;通知列表和未读数沿用现有入参,由后端过滤当前资产的临期通知。
|
||||
|
||||
## Scope and confirmed decisions
|
||||
|
||||
用户于 2026-10-13 确认:不处理代理注册;手机号行为以当前对接说明为准;全部已读以新文档的 `POST` 为准。
|
||||
|
||||
| 接口 | 本次处理 | 不变部分 |
|
||||
|---|---|---|
|
||||
| `POST /api/c/v1/auth/wechat-login` | 消费两个资产绑定布尔字段及新的绑定提示语义 | OAuth、令牌、资产校验流程 |
|
||||
| `POST /api/c/v1/auth/bind-phone` | 四种绑定场景、1192、成功后的分类状态更新 | `{ phone, code }`、验证码接口与场景 |
|
||||
| `POST /api/c/v1/auth/change-phone` | 客户换号,不再假设资产关联迁移 | 原/新手机号及验证码表单、接口入参 |
|
||||
| `GET /api/c/v1/asset/info` | 接收一次性 `expiry_reminder`,区分手机号用途 | 原资产、套餐、实名、支付字段 |
|
||||
| `GET /api/c/v1/notifications` | 回归当前资产临期通知的可见性 | 分页、排序、通知中心展示 |
|
||||
| `GET /api/c/v1/notifications/unread-count` | 使用后端当前上下文计数 | 原请求参数、未读入口 |
|
||||
| `POST /api/c/v1/notifications/read-all` | 从旧 PUT 切换为 POST | 用户主动点击“全部已读”的入口 |
|
||||
|
||||
明确排除:
|
||||
|
||||
- 代理注册页面、接口封装、密码新规则及注册 `status=3`。
|
||||
- 所有 `/api/admin`、`/api/auth` 的变更,以及后台订单、审批、换货、导入、预警、导出功能。
|
||||
- 新增小程序登录入口;`miniapp-login` 的同名字段仅作为后端共用契约背景,本 H5 继续使用公众号登录。
|
||||
- 修改 C 端订单、退款接口、WiFi 密码规则、支付路由及实名流程。
|
||||
- 新增后端接口、手机号关联解除/迁移接口,或从布尔字段推测资产实际号码。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `index-phone-bind-gate`、`phone-bind-association-prefill`、`phone-change-migration`、`c-notification-reminders`、`personal-notifications`。
|
||||
- Affected code: `pages/login/login.vue`、`pages/index/index.vue`、`pages/bind/bind.vue`、`pages/change-phone/change-phone.vue`、`components/FunctionCard.vue`、`components/DeviceStatusCard.vue`、`api/modules/asset.js`、`api/modules/notification.js`、`store/user.js`、`store/asset.js`、`utils/phone-bind.js`。
|
||||
- 检查但不预设必须修改:`components/TrafficCard.vue`、`components/RenewalPaymentPopup.vue`、其他 `assetApi.getInfo` 调用方及通知中心的刷新逻辑。
|
||||
- 不增加依赖,不执行后端或数据迁移。
|
||||
|
||||
## Existing change conflicts
|
||||
|
||||
- [update-phone-bind-association-flow](../update-phone-bind-association-flow/proposal.md):其“不同于客户主号则拒绝”“换号迁移全部资产关联”“用 bound_phone 判断资产关联”的旧约定,被本提案的 F1 约定替代;不覆盖其历史文件及完成标记。
|
||||
- [update-july-h5-c-iteration](../update-july-h5-c-iteration/proposal.md):仅替代临期通知作为首页弹窗来源的约定,保留通知中心、其他通知和套餐到期信息展示。
|
||||
- [add-personal-notifications](../add-personal-notifications/proposal.md):仅切换全部已读方法及遵循当前资产临期通知范围,不把单条已读的 PUT 一并改为 POST。
|
||||
- [add-h5-popup-candidate-flow](../add-h5-popup-candidate-flow/proposal.md):保留候选弹窗与单条已读链路;临期横幅独立展示,不调用全部已读来关闭任何弹窗。
|
||||
|
||||
技术选择、字段依据及风险见 [design.md](design.md),执行及验收清单见 [tasks.md](tasks.md)。
|
||||
@@ -0,0 +1,63 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Expiry reminders SHALL use the optional asset-info response
|
||||
|
||||
H5 SHALL 仅以 `GET /api/c/v1/asset/info` 返回的可选 `expiry_reminder` 作为本次临期主动提醒来源,在首页资产信息区展示可关闭横幅。剩余天数、到期时间、等级及等级名称 SHALL 使用 `remaining_days`、`expires_at`、`level`、`level_name`,SHALL NOT 用旧未读临期通知或客户端计算补发提醒。
|
||||
|
||||
#### Scenario: Asset info includes a reminder
|
||||
|
||||
- **WHEN** 当前资产响应含有效 expiry_reminder
|
||||
- **THEN** H5 SHALL 展示该资产临期横幅及后端提供的提醒信息
|
||||
|
||||
#### Scenario: Asset info omits the optional field
|
||||
|
||||
- **WHEN** 响应无 expiry_reminder,且当前进入没有已接收的待展示提醒
|
||||
- **THEN** H5 SHALL 不显示临期横幅,也不从 is_expiring 或通知列表补发
|
||||
|
||||
### Requirement: Reminder presentation SHALL preserve server calendar and levels
|
||||
|
||||
H5 SHALL 正确显示 0~15 天,按后端 `red`(0~3)、`purple`(4~7)、`pink`(8~15)设置视觉等级,并提供文字信息。到期时间 SHALL 按 Asia/Shanghai 展示,SHALL NOT 按客户端日期重新判定等级或剩余自然日。
|
||||
|
||||
#### Scenario: Zero remaining days
|
||||
|
||||
- **WHEN** 后端返回 remaining_days=0、level=red
|
||||
- **THEN** H5 SHALL 正常展示 0 天及红色等级,不能把 0 当作缺失
|
||||
|
||||
#### Scenario: Client timezone differs from Shanghai
|
||||
|
||||
- **WHEN** 用户设备的本地时区不是 Asia/Shanghai
|
||||
- **THEN** H5 SHALL 保留后端天数和等级,到期时间使用上海时区呈现
|
||||
|
||||
### Requirement: One-time reminder delivery SHALL survive competing asset-info consumers
|
||||
|
||||
H5 SHALL 在 asset/info 的共享成功返回入口接收一次性提醒,按当前客户上下文及资产隔离并暂存至对应资产可见。后端负责每日一次,前端 SHALL 防止同次接收被重复展示,SHALL NOT 因并发空字段响应覆盖已接收提醒、使用额外轮询或在关闭后复播。
|
||||
|
||||
#### Scenario: Child component receives the only reminder response
|
||||
|
||||
- **WHEN** TrafficCard 等调用方先获得 expiry_reminder,首页后续响应未返回该字段
|
||||
- **THEN** H5 SHALL 通过共享接收状态在当前资产信息区展示该提醒,不将其丢弃
|
||||
|
||||
#### Scenario: Same asset is re-entered after dismissal
|
||||
|
||||
- **WHEN** 用户关闭提醒后再次进入同一资产,后端不再返回字段
|
||||
- **THEN** H5 SHALL 不重播先前已展示提醒
|
||||
|
||||
#### Scenario: User switches customer or asset
|
||||
|
||||
- **WHEN** 用户退出、切换客户或进入另一资产
|
||||
- **THEN** H5 SHALL 不显示旧上下文提醒;其他资产有新提醒时 SHALL 独立处理
|
||||
|
||||
### Requirement: Expiry notifications SHALL remain in the notification center without driving popups
|
||||
|
||||
H5 SHALL 从首页旧未读通知弹窗来源排除临期项,同时保留通知中心临期列表、未读数和既有阅读操作。临期横幅展示或关闭 SHALL NOT 自动标记通知已读,其他通知及候选弹窗链路 SHALL 保持不变。
|
||||
|
||||
#### Scenario: Unread expiry notification exists without a new reminder
|
||||
|
||||
- **WHEN** 通知列表返回未读临期项,但 asset/info 无本次提醒
|
||||
- **THEN** H5 SHALL 不以该项触发旧首页临期弹窗
|
||||
- **AND** 通知中心 SHALL 仍可展示和阅读该项
|
||||
|
||||
#### Scenario: User closes an expiry banner
|
||||
|
||||
- **WHEN** 用户关闭临期横幅
|
||||
- **THEN** H5 SHALL 不猜测 notification_id、不发送自动单条已读或全部已读请求
|
||||
@@ -0,0 +1,36 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Phone guidance SHALL use the current login asset facts
|
||||
|
||||
H5 SHALL 保存 `POST /api/c/v1/auth/wechat-login` 的 `asset_phone_bound`、`asset_phone_is_own`、`need_bind_phone`,分别表达资产是否绑号、号码归属和是否提示绑定。状态 SHALL 按当前账号及资产隔离;客户 `bound_phone` 和旧本地缓存 SHALL NOT 替代资产布尔事实。
|
||||
|
||||
#### Scenario: Customer has a phone but the asset is unbound
|
||||
|
||||
- **WHEN** 客户有手机号,登录返回 `asset_phone_bound=false` 和 `need_bind_phone=true`
|
||||
- **THEN** H5 SHALL 引导用户给资产绑定号码
|
||||
- **AND** SHALL NOT 因客户手机号非空或旧缓存 bound=true 跳过提示
|
||||
|
||||
#### Scenario: Asset belongs to another phone and guidance is not required
|
||||
|
||||
- **WHEN** 登录返回 `asset_phone_bound=true`、`asset_phone_is_own=false`、`need_bind_phone=false`
|
||||
- **THEN** H5 SHALL 认为资产已有手机号,不要求重新绑定
|
||||
- **AND** SHALL NOT 推测或显示关联号码
|
||||
|
||||
#### Scenario: Account or asset context changes
|
||||
|
||||
- **WHEN** 用户退出、切换账号或切换资产
|
||||
- **THEN** H5 SHALL 清理或更换绑定事实,不能复用上一上下文的布尔值或手机号缓存
|
||||
|
||||
### Requirement: Automatic binding guidance SHALL follow need_bind_phone
|
||||
|
||||
H5 SHALL 仅按后端 `need_bind_phone` 触发自动绑定提示,SHALL NOT 根据手机号为空或 `asset_phone_is_own=false` 擅自增加自动跳转;缺失字段 SHALL 保持未知而非视为绑定成功。
|
||||
|
||||
#### Scenario: Force binding is disabled
|
||||
|
||||
- **WHEN** 强制绑定关闭,登录返回 `need_bind_phone=false`
|
||||
- **THEN** H5 SHALL 正常进入首页,不自动跳转绑定页
|
||||
|
||||
#### Scenario: Guidance is required
|
||||
|
||||
- **WHEN** 登录返回 `need_bind_phone=true`
|
||||
- **THEN** H5 SHALL 进入现有强制绑定流程,并按资产事实区分给客户绑号与给资产绑号
|
||||
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Notification queries SHALL preserve backend current-asset expiry scope
|
||||
|
||||
H5 SHALL 保持 `GET /api/c/v1/notifications` 与 `GET /api/c/v1/notifications/unread-count` 的现有入参,使用后端返回的当前资产临期通知及未读计数。H5 SHALL NOT 扩大范围查询所有资产、为这些接口新增未声明参数,或将普通通知一并误过滤为仅当前资产。
|
||||
|
||||
#### Scenario: Customer views asset A
|
||||
|
||||
- **WHEN** 客户在资产 A 的认证上下文读取列表和未读数
|
||||
- **THEN** H5 SHALL 展示后端结果,其中临期提醒按 A 过滤
|
||||
- **AND** SHALL NOT 混入资产 B 的旧列表或计数
|
||||
|
||||
#### Scenario: Current asset changes
|
||||
|
||||
- **WHEN** 当前认证资产由 A 切换为 B
|
||||
- **THEN** H5 SHALL 刷新列表和未读数,防止 A 的延迟响应覆盖 B
|
||||
|
||||
### Requirement: Mark-all-read SHALL use POST and preserve other assets' expiry notifications
|
||||
|
||||
H5 SHALL 在用户主动点击通知中心“全部已读”时调用 `POST /api/c/v1/notifications/read-all`,SHALL NOT 使用或自动回退旧 PUT。其他资产的临期通知 SHALL 由后端保留未读;客户端成功后 SHALL 按服务端刷新结果更新当前视图,失败 SHALL 保留未读状态。单条已读 SHALL 继续使用 `PUT /api/c/v1/notifications/{id}/read`。
|
||||
|
||||
#### Scenario: User marks all notifications read on asset A
|
||||
|
||||
- **WHEN** 客户在 A 上主动执行全部已读
|
||||
- **THEN** H5 SHALL 发送 POST 并对齐当前视图
|
||||
- **AND** 资产 B 的临期通知 SHALL 保持未读
|
||||
|
||||
#### Scenario: Mark-all-read fails
|
||||
|
||||
- **WHEN** POST 全部已读请求失败
|
||||
- **THEN** H5 SHALL 不提前清空未读计数或把本地项全部标成已读,也不改用 PUT 重试
|
||||
|
||||
#### Scenario: User reads one notification
|
||||
|
||||
- **WHEN** 用户执行单条已读操作
|
||||
- **THEN** H5 SHALL 继续发送对应通知的 PUT 请求,不受 read-all 方法切换影响
|
||||
|
||||
#### Scenario: Reminder or popup is displayed
|
||||
|
||||
- **WHEN** 临期横幅、运营候选或风险换货弹窗展示或关闭
|
||||
- **THEN** H5 SHALL 不自动调用 read-all 来结束展示链路
|
||||
@@ -0,0 +1,54 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Binding SHALL distinguish customer and asset phone targets
|
||||
|
||||
H5 SHALL 按 F1 四种场景组织绑定表单,继续使用 `POST /api/c/v1/auth/bind-phone` 的 `{ phone, code }` 与既有验证码场景。客户号码预填 SHALL 可编辑,SHALL NOT 要求未绑定资产只能绑定客户主号,也 SHALL NOT 用资产布尔字段推导实际号码。
|
||||
|
||||
#### Scenario: Neither the customer nor the asset has a phone
|
||||
|
||||
- **WHEN** 客户和资产都无手机号,用户输入号码并成功验证
|
||||
- **THEN** H5 SHALL 按后端同时绑定客户和资产的结果完成流程
|
||||
|
||||
#### Scenario: Only the customer has a phone
|
||||
|
||||
- **WHEN** 客户已有号码,资产未关联号码
|
||||
- **THEN** H5 SHALL 提示给当前资产绑号,允许提交与客户号码不同的号码
|
||||
- **AND** 成功后 SHALL NOT 把输入的资产号码当成客户的新号码
|
||||
|
||||
#### Scenario: Only the asset has a phone
|
||||
|
||||
- **WHEN** 客户无号码,资产已有号码,用户成功提交客户绑定
|
||||
- **THEN** H5 SHALL 仅更新客户手机号状态,不覆盖或改变资产关联
|
||||
|
||||
#### Scenario: Both the customer and the asset have phones
|
||||
|
||||
- **WHEN** 客户和资产都已有号码
|
||||
- **THEN** H5 SHALL 不要求绑定,无论资产号码是否属于当前客户
|
||||
|
||||
### Requirement: Asset phone conflicts SHALL direct the user to an administrator
|
||||
|
||||
H5 SHALL 对 `bind-phone` 的 `code=1192` 提示“该资产已绑定手机号,如需更换请联系管理员”,SHALL NOT 重试当前资产改绑请求、暴露号码或调用后台解除接口。其他错误 SHALL 保留后端消息,失败 SHALL NOT 更新绑定成功状态。
|
||||
|
||||
#### Scenario: Binding returns 1192
|
||||
|
||||
- **WHEN** 后端拒绝资产改绑并返回 1192
|
||||
- **THEN** H5 SHALL 显示联系管理员提示并停止当前改绑操作,不提供同请求重试
|
||||
|
||||
#### Scenario: Phone is owned by another customer
|
||||
|
||||
- **WHEN** 绑定返回既有 1184 错误
|
||||
- **THEN** H5 SHALL 保留相应后端错误提示,不误报为绑定成功或转换成 1192
|
||||
|
||||
### Requirement: Binding completion SHALL preserve existing navigation
|
||||
|
||||
H5 SHALL 保留强制绑定成功后回登录页、恢复 identifier 并等待用户手动重登的行为,以及普通绑定成功返回上一页的行为。状态更新 SHALL 对应本次客户/资产实际绑定对象,SHALL NOT 一律将输入号码写入资产缓存。
|
||||
|
||||
#### Scenario: Mandatory binding succeeds
|
||||
|
||||
- **WHEN** 用户从强制绑定入口成功提交
|
||||
- **THEN** H5 SHALL 返回登录页,保留 identifier 并提示手动重登,不自动 OAuth 重登
|
||||
|
||||
#### Scenario: Ordinary binding succeeds
|
||||
|
||||
- **WHEN** 用户从首页普通入口成功提交
|
||||
- **THEN** H5 SHALL 返回上一页,按后端结果更新相关状态,不强制返回登录页
|
||||
@@ -0,0 +1,27 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Customer phone changes SHALL leave asset phone associations unchanged
|
||||
|
||||
H5 SHALL 按当前 F1 契约调用 `POST /api/c/v1/auth/change-phone`,只更换客户手机号。原/新号码及验证码入参、`change_phone_old` / `change_phone_new` 场景 SHALL 保持不变;H5 SHALL NOT 将客户新号写入资产关联缓存或宣称所有资产关联已迁移。
|
||||
|
||||
#### Scenario: Customer changes to a different phone
|
||||
|
||||
- **WHEN** 客户换号成功,后端确认新客户手机号
|
||||
- **THEN** H5 SHALL 更新客户手机号及后续换号的原号来源
|
||||
- **AND** SHALL 保持资产关联不变,不调用新号写入资产绑定缓存的逻辑
|
||||
|
||||
#### Scenario: Asset ownership fact becomes stale
|
||||
|
||||
- **WHEN** 客户更换为不同号码,原归属事实为 true,响应未返回最新资产归属
|
||||
- **THEN** H5 SHALL 失效旧 `asset_phone_is_own` 事实,等真实登录响应确认
|
||||
- **AND** SHALL NOT 将资产误标为未绑定或要求重新绑定
|
||||
|
||||
### Requirement: Customer phone change failures SHALL preserve existing state
|
||||
|
||||
H5 SHALL 在换号失败时展示后端错误并留在表单,保留原客户及资产状态;SHALL NOT 套用旧“全部资产事务迁移/回滚”的假设或固定文案。
|
||||
|
||||
#### Scenario: Change-phone returns a business error
|
||||
|
||||
- **WHEN** 换号请求失败
|
||||
- **THEN** H5 SHALL 显示实际错误消息并留在页面
|
||||
- **AND** SHALL NOT 更新客户号码、资产号码或缓存
|
||||
@@ -0,0 +1,46 @@
|
||||
## 1. 状态拆分与绑定入口
|
||||
|
||||
- [x] 1.1 保存登录的 asset_phone_bound / asset_phone_is_own / need_bind_phone,按当前账号与资产隔离,在退出/切换时清理。
|
||||
- [x] 1.2 区分客户手机号、资产关联和提示状态;bound_phone 只沿用客户手机号用途,不用它推断资产关联。
|
||||
- [x] 1.3 移除首页对旧 phoneBindStatus 的优先信任,确保历史缓存不能覆盖服务端状态或跨客户复用。
|
||||
- [x] 1.4 调整首页手机号展示和 FunctionCard 绑定入口:客户有号但资产无号仍可绑定;资产归属 false 不表示未绑定。
|
||||
- [x] 1.5 保留 need_bind_phone 驱动的登录提示,强制开关关闭时不额外自动跳转。
|
||||
|
||||
## 2. 绑定与客户换号
|
||||
|
||||
- [x] 2.1 实现 F1 四种绑定场景的表单文案、可编辑预填、成功后的分类状态更新;不新增请求字段。
|
||||
- [x] 2.2 对 1192 显示联系管理员提示并停止当前改绑重试,保留 1184 及其他后端错误提示;失败不更新绑定缓存。
|
||||
- [x] 2.3 保留强制绑定成功后的手动重登、identifier 保留,以及普通绑定成功返回上一页的行为。
|
||||
- [x] 2.4 换号只更新客户手机号,删除新号写入资产缓存的行为;不再声明全部关联迁移/事务回滚。
|
||||
- [x] 2.5 换号后更新客户原号来源并失效可能过期的 asset_phone_is_own;不将原资产标记未绑定,不自动要求改绑。
|
||||
|
||||
## 3. 临期提醒
|
||||
|
||||
- [x] 3.1 保留 expiry_reminder 的可选语义及四个字段,并在 assetApi.getInfo 的共同返回入口接收一次性提醒。
|
||||
- [x] 3.2 实现当前账号/资产隔离的临时共享提醒状态;同次进入的后续空字段响应不能覆盖已接收的提醒。
|
||||
- [x] 3.3 在首页资产信息区显示可关闭横幅,按后端 red / purple / pink 展示,正确处理 0 天及上海时区的到期时间。
|
||||
- [x] 3.4 检查 TrafficCard、绑定预填、RenewalPaymentPopup 及其他 getInfo 调用方;强制绑定页不为预填请求 asset/info,避免跨重登传递一次性提醒。
|
||||
- [x] 3.5 从首页旧通知弹窗来源排除临期通知,通知中心保留;不影响其他通知及风险换货/运营弹窗。
|
||||
- [x] 3.6 不为临期提醒新增轮询、每日补发、无字段补弹或自动已读请求。
|
||||
|
||||
## 4. 当前资产通知与全部已读
|
||||
|
||||
- [x] 4.1 将 markAllRead 切换为 POST /api/c/v1/notifications/read-all,不回退 PUT;单条已读继续 PUT。
|
||||
- [x] 4.2 保持列表/未读数入参不变,切换资产后刷新相关结果,禁止旧资产结果覆盖新资产。
|
||||
- [x] 4.3 全部已读成功后按服务端结果对齐当前列表/计数,失败保留未读状态;不在任何提醒/弹窗展示链路调用 read-all。
|
||||
|
||||
## 5. 验证与交付
|
||||
|
||||
- [x] 5.1 通过自动/模拟测试验证四种绑定模式及强制开关关闭;真实微信授权验收另见 IMPLEMENTATION.md。
|
||||
- [x] 5.2 通过模拟测试和本地浏览器验证不同号码输入、1192 联系管理员且不重试、1184 提示、失败状态不变。
|
||||
- [x] 5.3 通过自动/模拟测试验证换号只更新客户、资产关联保持、归属失效及旧缓存/账号/资产隔离。
|
||||
- [x] 5.4 通过自动/模拟测试验证字段缺省、0/3/4/7/8/15 天及等级、上海日期边界、重复进入和 A/B 上下文;浏览器检查 0 天及关闭行为。
|
||||
- [x] 5.5 通过共享接收和首页脚本测试验证并发/其他调用方先返回提醒不丢失,以及旧首页弹窗只排除临期通知。
|
||||
- [ ] 5.6 用同一客户的 A/B 两资产验证列表、未读数与 A 上 POST 全部已读;B 的临期通知仍保持未读,普通通知范围不被前端扩大过滤。
|
||||
- [ ] 5.7 验证单条已读仍 PUT、read-all 失败不提前清空、运营/风险换货弹窗和支付/实名/套餐展示不回归。
|
||||
- [x] 5.8 执行 npm run build:h5、npm run build:h5:production,均通过;实施记录明确区分模拟验证与待真实联调场景。
|
||||
- [x] 5.9 执行 openspec validate update-october-batch1-h5-integration --strict,校验通过;仅按实际完成结果更新本任务清单。
|
||||
- [x] 5.10 新增并执行 npm run test:h5-integration,14 组自动/模拟测试全部通过。
|
||||
- [x] 5.11 使用本地模拟预览检查移动端临期横幅、绑定表单预填/不同号输入及 1192 弹窗/禁用操作;不发送真实业务请求。
|
||||
|
||||
5.6 的客户端 A/B 隔离逻辑、5.7 的单条 PUT 和失败不提前清空均已模拟测试通过;真实后端范围及完整支付/实名/候选弹窗回归未完成,不勾选。
|
||||
Reference in New Issue
Block a user