api-contract.md 5.2 KB

前端接口契约:设备信任免绑手机号(uni-app 用户端)

Date: 2026-09-20 | Spec: spec.md | 后端分支: 029-device-trust-phone-bind

本文档是后端对前端的唯一契约。前端按此实施,无需口头追问。所有接口均为现有接口的增量,不传新字段时行为与现网完全一致(老版本 App 无感)。

0. 设备唯一标识 deviceId(前端生成)

  • 定义:App 首次启动时生成的 UUID 字符串,≤64 字符,持久化保存,之后每次登录请求都带上。
  • 持久化建议
    • iOS:存 Keychain(卸载重装不丢,有现成 uni-app 插件);退化用 storage 也可,只是重装后要重新绑一次。
    • Android:优先取 ANDROID_ID(同设备+同签名跨重装稳定、免权限);退化用 storage 存自生成 UUID。
  • 传参规则:字符串、可选。空串/超长后端按未传处理。不要在未登录的其它业务接口上报。

1. 入参变更(三个现有接口加可选字段 deviceId

接口 位置 变更
POST /infouser/user/lodeing UserDTO + deviceId(登录成功后端记录设备信任)
POST /infouser/user/oauthLogin OAuthLoginDto + deviceId(未绑定时影响返回分支,见 §2)
POST /infouser/user/oauthBindPhone OAuthBindDto + deviceId(绑定成功后端记录设备信任)

2. oauthLogin 返回新增分支 deviceConfirm

现有返回不变,新增一种:

返回(AjaxResult data) 条件 App 行为
token + 用户信息 三方身份已绑定 直接进 App(现状不变)
{status:"needPhone", tempKey} 未绑定,且设备无信任记录 现有输手机号+短信流程(不变)
{status:"deviceConfirm", tempKey, maskedPhone} 新增 未绑定,且 deviceId 有信任记录 弹一键确认框,见 §3
  • maskedPhone:脱敏手机号,格式 前3位****后4位(如 098****4321),只读展示,不可编辑、不提供换号输入
  • tempKey 有效期 5 分钟,过期后确认接口返回 no.oauth.tempkey.expired,需重新点三方登录拿新的。

3. 确认弹窗规范

  • 文案示意:将绑定 {maskedPhone} 并登录;按钮:确认 / 取消
  • 确认 → 调 §4 新接口;取消 → 关闭弹窗回登录页,不产生任何绑定(用户仍可用手机号+短信登录,或换其它三方登录)。
  • 弹窗内不出现验证码输入框、不出现可编辑手机号。

4. 新接口:POST /infouser/user/oauthDeviceConfirm

用途:设备确认分支的一键绑定登录(免短信)。

// 请求体(OAuthDeviceConfirmDto)
{ "tempKey": "abc123...", "deviceId": "uuid-..." }

成功:响应结构与 oauthBindPhone 成功完全一致(msg=登录成功、user 用户信息、token),直接进 App。

失败(AjaxResult error,msg 为国际化文案):

场景 错误 key(参考) App 处理建议
tempKey 缺失 no.oauth.tempkey.missing 提示后回登录页
tempKey 过期/不存在 no.oauth.tempkey.expired 提示"请重新登录",回登录页
deviceId 缺失 / tempKey 不是设备确认凭证 / deviceId 不匹配 / 设备无信任记录 no.oauth.device.mismatch 提示后回登录页(可回落手动绑定流程)
信任手机号账号已停用 no.user.stop 提示账号停用,回登录页
期间该三方身份已被其它端绑定 无错误——直接返回 token 登录成功(容错直登,与 oauthBindPhone 一致) 直接进 App

5. LINE 登录(网页回调链路)

authorize URL 变更(唯一的必改点)state 参数从随机串改为传 deviceId

https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=...&redirect_uri=...&scope=profile%20openid&state={deviceId}

302 回跳 App(com.twanmsdyh.app://oauthLogin)分支变化

回跳参数 条件 App 行为
?token=xxx 已绑定 进 App(现状不变)
?needPhone=1&tempKey=xxx 未绑定、无设备信任 现有手机号+短信流程(不变)
?deviceConfirm=1&tempKey=xxx&maskedPhone=098****4321 新增 未绑定、state 携带的 deviceId 有信任记录 弹 §3 同款确认框,确认 → §4 接口
?error=xxx 异常 现状不变
  • maskedPhone 已做 URL 编码,App 解码后展示。
  • POST /oauthLogin(前端自拿 code 的场景,如 H5)时同样受益:body 里带 deviceId 即可,返回分支同 §2。

6. 联调自测清单

  1. 手机号+短信登录(带 deviceId)→ 卸载重装前,用 Apple 新身份登录 → 应弹 deviceConfirm(maskedPhone = 刚登录的号)。
  2. deviceConfirm 确认 → 返回 token,进 App 后"我的"页手机号 = maskedPhone 对应账号。
  3. deviceConfirm 取消 → 再用 Google 新身份登录 → 仍弹 deviceConfirm(未产生绑定)。
  4. 不带 deviceId(模拟老版本)→ 三方新身份登录 → 走 needPhone 原流程。
  5. 等 tempKey 过期(>5 分钟)后点确认 → 收到 no.oauth.tempkey.expired
  6. LINE:authorize state=deviceId → 已信任设备走 deviceConfirm 回跳分支。