登录 / 注册 / 重置密码
页面路由:/login
组件:LoginPage.jsx
页面进入时
加载图形验证码(自动)
- 触发条件:
- 初始进入登录页(mode='login')→ GET /auth/login-captcha
- 切换到注册页(mode 变为 'register')→ GET /auth/register-captcha
- 切换回登录页(mode 变回 'login')→ GET /auth/login-captcha
- 前端处理:
fetchLoginCaptcha()/fetchRegisterCaptcha()→ 返回{ captchaId, svg }
点击「登录」按钮
表单提交 → 登录
POST /api/auth/login
参数:
| 参数 | 来源 | 说明 |
| phone | 国家码 + 手机号拼接 | 格式如 +86138xxxxxxx |
| password | 用户输入 | 明文传输 |
| captchaId | 图形验证码 ID | 之前 loadCaptcha 获取 |
| captchaAnswer | 用户输入 | 图形验证码答案 |
后端处理:密码 bcrypt 比对 → 生成 JWT(2h)+ RefreshToken(30天)→ 插入 refresh_tokens 表
成功响应:
{ "token": "jwt_string", "refreshToken": "hex_string", "user": { ... } }
失败:返回 400(验证码错误/密码错误)
点击「注册」按钮
1. 发送注册邮箱验证码
触发条件:点击"发送验证码"按钮(先过图形验证码)
POST /api/auth/send-register-email
参数:
| 参数 | 说明 |
| 用户输入的邮箱 | |
| captchaId | 图形验证码 ID |
| captchaAnswer | 图形验证码答案 |
后端处理:校验图形验证码 → 生成6位数字邮箱验证码(5分钟有效)→ 发送邮件
2. 表单提交 → 注册账号
POST /api/auth/register
参数:
| 参数 | 说明 |
| phone | 国家码 + 手机号 |
| nickname | 用户昵称(≥2字符) |
| password | 密码(≥8位,含字母+数字) |
| passwordConfirm | 确认密码 |
| 邮箱地址 | |
| emailCode | 邮箱验证码(6位) |
| captchaId | 图形验证码 ID |
| captchaAnswer | 图形验证码答案 |
后端处理:
1. 校验手机格式、密码格式、图形验证码、邮箱验证码
2. bcrypt 加密密码
3. 事务:插入 users + refresh_tokens + 创建默认账单(bills + bill_members)+ 7个默认分类
4. 返回 JWT + RefreshToken + user 对象
成功响应(201):
{
"token": "jwt_string",
"refreshToken": "hex_string",
"user": { "id", "phone", "nickname", "role", "is_guest", ... }
}
点击「找回密码」
1. 发送重置邮件验证码
POST /api/auth/send-reset-email
参数:
| 参数 | 说明 |
| 邮箱地址 | |
| phone | 完整手机号(国码+号码) |
| captchaId | 图形验证码 ID |
| captchaAnswer | 图形验证码答案 |
2. 表单提交 → 重置密码
POST /api/auth/reset-password
参数:
| 参数 | 说明 |
| 邮箱地址 | |
| emailCode | 邮箱验证码 |
| newPassword | 新密码 |
| newPasswordConfirm | 确认新密码 |
后端处理:校验验证码 → bcrypt 更新密码 → 返回成功
点击「游客试用」
直接登录 → 开通试用
POST /api/auth/guest-login
参数:无
后端处理:生成临时游客账号(手机号 guest_随机ID)→ 创建默认账单 + 7个分类 → 返回 JWT
特点:
- 无需任何凭证
user.is_guest = 1- 后续可通过「升级账号」变为正式账号
升级游客账号
前置:已是游客登录状态(user.is_guest === true),在注册页面显示"升级账号"
调用接口:
POST /api/auth/upgrade-guest
参数:
| 参数 | 说明 |
| phone | 新手机号 |
| password | 密码(≥8位,含字母+数字) |
| passwordConfirm | 确认密码 |
| nickname | 昵称(≥2字符) |
| 邮箱地址 | |
| emailCode | 6位邮箱验证码 |
| captchaId | 图形验证码 ID |
| captchaAnswer | 图形验证码答案 |
后端处理:校验图形验证码 + 邮箱验证码 → bcrypt 更新密码 → is_guest=0 → 新 JWT(2h)
注意:不创建新账单,复用原游客的账单数据
Auth 页面相关 Context/Flow
AuthContext
├── login(phone, password, captchaId, captchaAnswer) → POST /auth/login
├── register(payload) → POST /auth/register
├── guestLogin() → POST /auth/guest-login
├── upgradeGuest(payload) → POST /auth/upgrade-guest
├── fetchRegisterCaptcha() → GET /auth/register-captcha
├── fetchLoginCaptcha() → GET /auth/login-captcha
├── sendRegisterEmail(email, captchaId, captchaAnswer) → POST /auth/send-register-email
├── sendResetEmail(email, phone, captchaId, captchaAnswer) → POST /auth/send-reset-email
└── resetPassword(payload) → POST /auth/reset-password
错误处理汇总
| 场景 | HTTP 状态码 | 错误位置 |
| 图形验证码错误 | 400 | 验证码框顶部 |
| 手机号格式错误 | 400 | 手机号输入框下方 |
| 邮箱验证码错误/过期 | 400 | 邮箱验证区域 |
| 密码格式不符合要求 | 400 | 密码输入框下方 |
| 两次密码不一致 | 400 | 确认密码框下方 |
| 后端未启动 | ERR_NETWORK | 图形验证码区域提示 |
| 请求超时 | ECONNABORTED | 图形验证码区域提示 |