# 017 任务清单 > 顺序执行;每步完成后编译验证。 - [ ] T1 数据模型:`updatesql/sql.md` 加 `info_user_oauth` 建表(含 uk_provider_uid 唯一索引、idx_user_id)。 - [ ] T2 实体:`InfoUserOauth.java`(@TableName=info_user_oauth,字段 id/userId/provider/providerUid/createTime)。 - [ ] T3 mapper:`InfoUserOauthMapper extends BaseMapper`(无 XML)。 - [ ] T4 LoginUserDto 加 `provider` 字段。 - [ ] T5 JwtUtil.setToken(tokenKey, LoginUserDto) 加 `provider` claim(非空才写)。 - [ ] T6 配置:application.yml 加 `oauth.apple.clientId/jwks-url`、`oauth.google.clientId/tokeninfo-url`、`oauth.line.profile-url`。 - [ ] T7 依赖:ruoyi-admin/pom.xml 加 `nimbus-jose-jwt`。 - [ ] T8 OAuthVerifyService:`verify(provider, credential)` → providerUid;Apple(nimbus 验 ES256)、Google(tokeninfo)、LINE(v2/profile),校 audience/iss/exp。 - [ ] T9 DTO:OAuthLoginDto、OAuthBindDto。 - [ ] T10 InfoUserController.oauthLogin:校验→查 oauth→命中签 token / 未命中缓存 tempKey 返 needPhone。 - [ ] T11 InfoUserController.oauthBindPhone:取 providerUid + 验短信→关联/新建→写 oauth→签 token。 - [ ] T12 编译验证(JDK21):ruoyi-admin + ruoyi-system + ruoyi-common。 ## 联调待办(开发提供配置后) - [ ] application.yml 填真实 apple bundleId/clientId、google clientId、line channel。 - [ ] 三家真实 token 端到端验证(Apple 需真机/测试机签 Sign in with Apple)。 - [ ] 前端 app:登录页三按钮 + 首次绑手机流程 + i18n。 ## 代码评审修复 (2026-07-30) 评审发现并修复(均已 JDK21 编译通过): - [x] **HTTP 超时**:`OAuthVerifyService.httpGet` 原用 `HttpClients.createDefault()` 无超时,provider 慢/不可达会拖垮请求线程池 → 加 `RequestConfig`(连接 5s / 读取 10s / 获取连接 5s)。 - [x] **停用账号拦截**:`oauthBindPhone` 用 `getuser(phone)` 只滤 del_flag、不滤 status,停用账号(status≠0)能通过三方绑定绕过停用 → 加 `status!=0` 拦截(与 `lodeing` / `oauthLogin` 已绑定路径一致),文案复用 `no.user.stop`。 - [x] **Apple audience**:原 `aud.get(0)` 在多 audience 时可能漏判 → 改 `aud.contains(appleClientId)`。 - [x] **i18n 化(裸中文 → message key)**:新增 `no.oauth.*` 共 10 个 key,写入 5 份 properties(`messages` / `zh_CN` / `zh_TW` / `en_US` / `vi`);`OAuthVerifyService` 异常文案与 `InfoUserController` 提示全部走 `MessageUtils.message`,与 `lodeing` 风格一致。新增 key: - 控制器:`no.oauth.provider.blank` / `needphone` / `tempkey.missing` / `tempkey.expired` - 校验服务:`no.oauth.credential.blank` / `provider.unsupported` / `token.invalid` / `token.expired` / `audience.mismatch` / `verify.fail`(带 `{0}` 渠道名、`verify.fail` 带 `{1}` 异常详情)