# Implementation Plan: 设备信任免绑手机号 **Branch**: `029-device-trust-phone-bind` | **Date**: 2026-09-20 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/029-device-trust-phone-bind/spec.md` ## Summary 同一台设备只要验证过一次手机号(短信登录 / 三方短信绑定 / 三方已绑定直登),即建立"设备→手机号"信任;之后该设备上任何未绑定三方身份登录时,返回脱敏手机号 + tempKey 让用户一键确认(免短信),确认后按现有绑定规则完成绑定并签发 token。LINE 网页回调链路通过 `state` 参数携带设备标识获得同等能力。只改后端,产出前端契约文档。 技术路径:新表 `info_user_device`(MyBatis-Plus BaseMapper,无 XML)+ 新服务 `DeviceTrustService`(upsert/查询/脱敏)+ `InfoUserController` 三接口加 `deviceId` 入参与 `deviceConfirm` 分支 + 新接口 `oauthDeviceConfirm` + `LineCallbackController` state 解析。临时凭证沿用 `oauth:bind:` 前缀,新增正交 key `oauth:bind:dev:{tempKey}` 绑定设备标识,现有解析逻辑零改动。 ## Technical Context **Language/Version**: Java 21(Maven 多模块:ruoyi-admin / ruoyi-system / ruoyi-common) **Primary Dependencies**: Spring Boot、MyBatis + MyBatis-Plus(baomidou)、Lombok、RedisCache(Spring Redis)、自写 JwtUtil(HS256 + Redis 会话)、MessageUtils(i18n messages) **Storage**: MySQL(新表 `info_user_device`,DDL 只写 `updatesql/sql.md` 人工执行);Redis(临时凭证 `oauth:bind:*`,TTL 5 分钟) **Testing**: JUnit 5 + Mockito,测试位于 `ruoyi-admin/src/test/java/com/ruoyi/app/user/`(沿用 flashdelivery 测试组织方式)。构建命令:临时 `JAVA_HOME=%USERPROFILE%\.jdks\graalvm-jdk-21.0.12+7.1`,`mvn -pl ruoyi-admin -am test -Dtest=XxxTest -DfailIfNoSpecifiedTests=false` **Target Platform**: Linux 服务器部署的 Spring Boot Web 服务(越南用户端 uni-app 的后端) **Project Type**: web-service(单体多模块 Maven) **Performance Goals**: 与现有登录接口同量级(服务端处理 < 200ms),仅新增 1 次主键查询/写入,无新增外部调用 **Constraints**: Controller 参数规范(@RequestBody DTO、DTO 无 Bean Validation 注解、校验放 Controller 用 MessageUtils);模块依赖 ruoyi-admin → ruoyi-system;SQL 不直接执行;保留文件原有 CRLF 换行风格 **Scale/Scope**: 改 3 个 Controller/DTO 既有文件 + 新增 3 个 Java 文件 + 1 个实体/Mapper + SQL + 测试 + 前端契约文档;前端 uni-app 不在范围 ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* `.specify/memory/constitution.md` 为未填写的空模板,无项目级 gates。以下以 CLAUDE.md 项目规范替代核对: | 约束 | 结果 | |---|---| | Controller 请求参数规范(DTO + @RequestBody,无 Map 入参,无 Bean Validation 注解) | PASS — 新接口用 `OAuthDeviceConfirmDto`,deviceId 校验在 Controller 内 | | 模块依赖方向 ruoyi-admin → ruoyi-system | PASS — 实体/Mapper 放 ruoyi-system,服务/编排放 ruoyi-admin | | 数据库变更写入 updatesql/sql.md 不直接执行 | PASS — 见 tasks | | Java 注释安全(块注释无 `*/` 序列) | PASS | | 已废弃代码清单(不参考不增量修改) | PASS — 不触碰 PayController/ZaloPay 等废弃代码 | | 后端 i18n 错误消息走 MessageUtils | PASS — 新增消息 key 加到 i18n 资源文件 | Phase 1 设计后复核:全部 PASS,无 Complexity Tracking 需要填写的违规项。 ## Project Structure ### Documentation (this feature) ```text specs/029-device-trust-phone-bind/ ├── plan.md # This file (/speckit-plan command output) ├── research.md # Phase 0 output (/speckit-plan command) ├── data-model.md # Phase 1 output (/speckit-plan command) ├── quickstart.md # Phase 1 output (/speckit-plan command) ├── contracts/ │ └── api-contract.md # 前端(uni-app)接口契约 └── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan) ``` ### Source Code (repository root) ```text ruoyi-admin/src/main/java/com/ruoyi/app/user/ ├── InfoUserController.java # lodeing/oauthLogin/oauthBindPhone 记录设备信任;oauthLogin 新增 deviceConfirm 分支;新接口 oauthDeviceConfirm ├── LineCallbackController.java # state 解析为 deviceId;未绑定分支查设备信任回跳 deviceConfirm;已绑定路径记录信任 ├── dto/ │ ├── OAuthLoginDto.java # +deviceId │ ├── OAuthBindDto.java # +deviceId │ └── OAuthDeviceConfirmDto.java # 新增 {tempKey, deviceId} └── service/ └── DeviceTrustService.java # 新增:recordLogin(upsert)/getTrust/maskPhone ruoyi-admin/src/test/java/com/ruoyi/app/user/ └── service/ └── DeviceTrustServiceTest.java # 设备记录/覆盖/脱敏规则 └── DeviceTrustFlowTest.java # oauthLogin 分支判定 + oauthDeviceConfirm 成功/过期/不匹配/停用路径 ruoyi-system/src/main/java/com/ruoyi/system/ ├── domain/InfoUserDevice.java # 新增实体(@TableName info_user_device) ├── domain/vo/UserDTO.java # +deviceId └── mapper/InfoUserDeviceMapper.java # 新增(MP BaseMapper,无 XML,同 InfoUserOauthMapper 模式) ruoyi-system/src/main/resources/i18n/ # 新增消息 key(多语言) updatesql/sql.md # 建表 DDL 追加(带日期注释) ``` **Structure Decision**: 单体多模块 Maven。数据层(实体/Mapper)按既有模式放 ruoyi-system;业务编排与接口改造全部在 ruoyi-admin 的 app.user 包(登录逻辑现状集中在 InfoUserController,保持就近、surgical);新增 DeviceTrustService 承载可单测的纯逻辑(upsert/查询/脱敏),Controller 只做参数校验与编排。 ## Complexity Tracking > **Fill ONLY if Constitution Check has violations that must be justified** 无违规项。