统一手机号国家码与纯号码校验
Project CI / Frontend tests (pull_request) Successful in 30s
Project CI / Native shell tests (pull_request) Successful in 2m10s
Project CI / Backend tests (pull_request) Failing after 7s
Project CI / Repository checks (pull_request) Failing after 8s

认证请求以 countryCode 与 purePhoneNumber 替换旧 phone 字段
后端默认国家码 86 并拒绝其他国家码,复用纯手机号规范化生成 E.164
微信手机号绑定强制使用 provider 返回的国家码和纯号码并补齐前后端测试文档
This commit is contained in:
2026-07-23 13:37:23 +08:00
parent 2a80fa179a
commit 994b7119ec
25 changed files with 552 additions and 192 deletions
@@ -4398,3 +4398,9 @@
- 动态与可用性边界:`useLayoutEffect` 首次同步测量,`ResizeObserver + requestAnimationFrame` 在容器变宽、卡高变化、筛选重排和 cursor 追加后全量重排。只有当宽度、卡数和所有高度完整时才进入 absolute ready;否则保留 Grid fallback,防止卡片重叠和分页 sentinel 提前触发。DOM/Tab/读屏顺序始终不变,容器与卡片显式为 list/listitem。
- 兼容边界:保留现有 `.creation-landing__asset-waterfall` 类名、筛选、排序、cursor 分页、预览与点赞链路;只替换布局算法。该决策覆盖 2026-07-07 multi-column 及本日早先 row-major Grid 的布局部分,不改变精选仍是动态素材流的产品定位。
- 验证方式:纯函数测试锁定容器临界宽度、循环列序、列内 top 和容器高度;`src/index.test.ts` 锁定 Grid fallback 与 Masonry ready。Playwright 在同一 viewport 中变更容器宽度,核对 3/2/1 列、每列 gap、容器高度、DOM 顺序、无重叠/横溢出和 console/page error。
## 2026-07-23 手机号认证统一使用国家码与纯号码双字段
- 决策:普通手机号认证请求统一使用可选 `countryCode` 与必填 `purePhoneNumber`,省略国家码时默认中国大陆 `86`,直接替换旧 `phone` 字段。前端把浏览器 E.164 自动填充值拆成这两个字段;后端先验证国家码,再复用纯手机号规范化并生成 E.164 存储。
- 微信边界:小程序客户端仍只上传 `wechatPhoneCode`;`platform-auth` 必须要求微信成功响应中的 `phoneNumber`、`countryCode` 与 `purePhoneNumber` 均存在且非空,但只使用后两项执行国家码校验和 E.164 构造。腾讯官方仅说明境外 `phoneNumber` 会带区号,并未承诺 E.164 格式,中国号码示例中它与纯号码相同,因此不得校验 `phoneNumber == +{countryCode}{purePhoneNumber}`。微信字段缺失时失败关闭,不能使用普通请求的 `86` 默认值。
- 数据边界:认证投影与 SpacetimeDB 的 `phone_number_e164` 保持不变,不新增国家码或纯号码列,也不需要 schema 迁移或 bindings 生成。
@@ -1810,10 +1810,10 @@
## 浏览器自动填充手机号带 `+86`
- 现象:登录弹窗的手机号被浏览器回填为 `+86 1xxxxxxxxxx`,点击获取验证码或登录后返回“手机号格式不正确”。
- 原因:`autocomplete="tel"` 允许浏览器回填含国家码的完整电话号码,`inputMode="numeric"` 只提示软键盘布局,不会过滤自动填充;历史 `normalizePhoneInput` 还保留了 `+`,而后端大陆手机号边界只接受 11 位国内号码。
- 处理:手机号字段保留 `autocomplete="tel"`,不改写浏览器自动填充策略;在 `authService.normalizePhoneInput` 统一主动把 `+86 1xxxxxxxxxx` 和 `86 1xxxxxxxxxx` 收口为 `1xxxxxxxxxx`,所有认证请求和最近登录号码缓存继续共用该边界,不在单个页签临时特判。
- 验证:`npm run test -- src/services/authService.test.ts`,覆盖带 `+86`、不带加号的 `86` 和普通 11 位国内号码。
- 关联:`src/services/authService.ts`、`src/services/authService.test.ts`、`src/components/auth/LoginScreen.tsx`、`src/components/auth/BindPhoneScreen.tsx`。
- 原因:`autocomplete="tel"` 允许浏览器回填含国家码的完整电话号码,`inputMode="numeric"` 只提示软键盘布局,不会过滤自动填充;如果把完整号码和纯号码混在一个 `phone` 字段中,微信 `purePhoneNumber` 又与 `countryCode` 分开传递,后端容易在国家码丢失后把境外号码误判为 `+86`。
- 处理:手机号字段保留 `autocomplete="tel"`;`authService` 在请求前把 `+86 1xxxxxxxxxx`、`86 1xxxxxxxxxx` 拆为 `countryCode=86 + purePhoneNumber=1xxxxxxxxxx`。普通认证请求缺少 `countryCode` 时默认 `86`,但微信授权必须使用 provider 真实返回的 `countryCode + purePhoneNumber`,不能默认国家码。`module-auth` 先校验国家码,再用原纯手机号规则校验 `purePhoneNumber` 并生成 E.164;数据库仍只保存 E.164。
- 验证:`cargo test -p module-auth --manifest-path server-rs/Cargo.toml`、定向 `api-server` 认证测试和 `npm run test -- src/services/authService.test.ts src/components/auth/AuthGate.test.tsx`,覆盖省略 / 显式 `86`、境外国家码、浏览器 `+86` 自动填充以及微信 provider 国家码路径。
- 关联:`server-rs/crates/module-auth/src/domain.rs`、`server-rs/crates/module-auth/src/errors.rs`、`server-rs/crates/api-server/src/phone_auth.rs`、`server-rs/crates/api-server/src/wechat/auth.rs`、`src/services/authService.ts`、`src/components/auth/LoginScreen.tsx`。
## 本地短信收不到验证码先查 provider