# Feature Spec: 账号登录 > Status: `Active` · Map: `5.x 账号 [P1+]` · Phase: `P1` > 规范:[../feature-design.md](../feature-design.md) > 关联:[profile-bootstrap.md](profile-bootstrap.md) --- ## 1. 功能定义 | 字段 | 内容 | |---|---| | Name | 账号登录(手机号+密码) | | Purpose | 跨设备持久用户身份;未登录不可生成/查看生日衍生结果 | | Business Goal | 数据归属清晰;为会员与深度版付费打底 | | In | Out | |---|---| | 注册 / 登录 / 登出 / me | 微信 OAuth、短信 OTP(后续) | | 设备身份绑定到已注册账号 | 游客可看完整报告 | | Bearer Session | 运营 Admin 登录(独立) | --- ## 2. 用户价值 1. 换手机后仍能看到自己的档案与报告。 2. 登录后输入生日一次生成、各页直读。 3. N/A(登录本身免费)。 --- ## 3. 用户角色 | Actor | 能力 | |---|---| | Visitor | 仅设备身份;可打开登录页;**不可**建档出结果 | | User(已注册) | CRUD 本人档案与 TA;读已生成报告 | | VIP / DeepAccess | 同前,权益不变 | --- ## 4. 用户流程 ```text 进入结果页或建档 ↓ 未登录? → /login 注册或登录 ↓ Device 绑定到账号 User ↓ 进入档案 / 结果页(读缓存报告) ``` --- ## 5. 页面设计 | 路由 | 页面 | |---|---| | `/login` | LoginPage(注册/登录切换) | | `/mine` | 展示账号手机号尾号 + 退出 | --- ## 6. 页面状态规范 | 状态 | UI | |---|---| | Loading | 登录中 | | Error | 密码错误 / 手机号已注册 | | Normal | 跳转 redirect 或 /mine | --- ## 7. Business Rules | ID | Rule | |---|---| | R1 | **临时开放:** 任意非空手机号 + 任意密码均可登录;未注册则自动建号写入 DB | | R2 | 登录/注册统一走 OpenLogin;不校验历史密码,仍会更新 password_hash 记录本次输入 | | R3 | 登录成功后:签发 session;device_identities.user_id 改绑到账号 | | R4 | 生成/查看档案与报告 API 必须已注册(users.phone 非空)且有效 session 或已绑设备账号 | | R5 | 登出作废 session;设备可再登录其他账号 | | R6 | 正式环境恢复校验前须改回本 Spec | | R7 | 新账号未填昵称时,服务端用「意象词+场景词」词库(100×100)随机生成,如心语岛、微光谷 | | R8 | 账号昵称可在个人档案页修改(PATCH `/auth/me`);首页与各页「自己」展示位统一用昵称(合盘/问答/星座/档案卡) | | R9 | 账号头像可在「我的」页更换:相册或拍照上传;服务端持久化;GET `/auth/me` 返回 `avatar_url`;未设置时 UI 用愈心小人正面默认头像(`public/default-avatar.png`,源图 `public/logo/愈心小人1.png`) | --- ## 8. 数据模型影响 - `users.phone` UNIQUE · `users.password_hash` · `users.nickname` · `users.avatar_url` - `user_sessions(token, user_id, expires_at)` - 本地文件:`data/avatars/{user_id}.{jpg|png|webp}`(进程相对路径) --- ## 9. API 需求 | Method | Path | 说明 | |---|---|---| | POST | `/api/v1/auth/register` | 注册(可带 nickname) | | POST | `/api/v1/auth/login` | 登录 | | POST | `/api/v1/auth/logout` | 登出 | | GET | `/api/v1/auth/me` | 当前账号(含 avatar_url) | | PATCH | `/api/v1/auth/me` | 更新昵称 `{ nickname }` | | POST | `/api/v1/auth/me/avatar` | multipart `file`;jpeg/png/webp;≤2MB → 更新 avatar_url | | GET | `/api/v1/media/avatars/:file` | 公开读头像文件 | --- ## 10. 权限设计 | 能力 | Visitor | User | |---|---|---| | register/login | ✓ | ✓ | | profiles / reports 写读 | ✗ | ✓ | | 上传头像 | ✗ | ✓ | | 读头像媒体 | ✓ | ✓ | --- ## 11. 埋点 经统一 `track()`(见 [analytics.md](analytics.md) · [ops-analytics.md](ops-analytics.md))。 **非** P1 核心漏斗 5 事件;接入后走自有 `POST /analytics/events`(及可选 GA)。 禁止参数含手机号、明文密码、头像绝对 URL / `user_id`。 | Event | 触发 | 可选 params | |---|---|---| | `auth_register` | 账号新建成功(含 OpenLogin 自动建号,`is_new=true`) | `source`: login_page | | `auth_login` | 已有账号登录成功(`is_new=false`) | `source`: login_page | | `auth_logout` | 用户在「我的」点退出且本地 session 已清 | `surface`: mine | | `avatar_sheet_opened` | 「我的」打开更换头像 sheet | `surface`: mine | | `avatar_upload_succeeded` | 头像上传 API 成功且 UI 刷新为新图 | `source`: album \| camera | | `avatar_upload_failed` | 头像上传失败(校验/网络/服务端) | `source`: album \| camera · `reason`: too_large \| bad_type \| network \| server \| unknown | | `nickname_updated` | PATCH 昵称成功 | `surface`: profile | `page_view` 对 `/login` · `/mine` 由路由自动采集,本 Spec 不重复定义。 **实现状态:** 事件名以本表为准;H5 已挂 Login / Mine / Profile(`track` → 自有 ingest + 可选 GA)。 --- ## 12. 测试验收标准 | Given | When | Then | |---|---|---| | 新设备 | 未登录 POST portrait | 401 | | 注册成功 | 建档 | 200 且生成报告包 | | 登录另一设备 | list profiles | 见同一账号数据 | | 已登录设备 | POST logout 后无 Bearer 再 GET /auth/me | 401(设备已解绑,刷新不再显示登录态) | --- ## 13. AI 开发前检查 - [x] lexicon:登录/注册/账号 - [x] 不碰 Admin Auth