9.9 KiB
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
- 提案批准后,先完成状态拆分及旧缓存失效,再调整绑定/换号行为。
- 加入临期提醒共享接收和横幅,排除首页旧临期弹窗来源。
- 切换全部已读为 POST,回归当前资产的列表与未读数。
- 完成任务中的场景验证及 H5 构建,记录实际结果;不以静态构建代替真实接口联调。
- 需要回退时只回退本次前端改动;服务端新契约不变,不能恢复“客户换号即资产迁移”的旧假设作为兼容策略。
Open Questions
提案范围和优先契约已由用户确认,无需再次决定代理注册或 read-all 方法。实施前仍应检查接口是否实际提供所需字段及一次性提醒跨页面传递条件;只在发现具体矛盾时报告并询问,不擅自扩展接口。