Date: 2026-09-20 | Spec: spec.md | 后端分支: 029-device-trust-phone-bind
本文档是后端对前端的唯一契约。前端按此实施,无需口头追问。所有接口均为现有接口的增量,不传新字段时行为与现网完全一致(老版本 App 无感)。
ANDROID_ID(同设备+同签名跨重装稳定、免权限);退化用 storage 存自生成 UUID。deviceId)| 接口 | 位置 | 变更 |
|---|---|---|
POST /infouser/user/lodeing |
UserDTO | + deviceId(登录成功后端记录设备信任) |
POST /infouser/user/oauthLogin |
OAuthLoginDto | + deviceId(未绑定时影响返回分支,见 §2) |
POST /infouser/user/oauthBindPhone |
OAuthBindDto | + deviceId(绑定成功后端记录设备信任) |
oauthLogin 返回新增分支 deviceConfirm现有返回不变,新增一种:
| 返回(AjaxResult data) | 条件 | App 行为 |
|---|---|---|
token + 用户信息 |
三方身份已绑定 | 直接进 App(现状不变) |
{status:"needPhone", tempKey} |
未绑定,且设备无信任记录 | 现有输手机号+短信流程(不变) |
{status:"deviceConfirm", tempKey, maskedPhone} 新增 |
未绑定,且 deviceId 有信任记录 | 弹一键确认框,见 §3 |
maskedPhone:脱敏手机号,格式 前3位****后4位(如 098****4321),只读展示,不可编辑、不提供换号输入。no.oauth.tempkey.expired,需重新点三方登录拿新的。将绑定 {maskedPhone} 并登录;按钮:确认 / 取消。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 |
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。no.oauth.tempkey.expired。