Auth 模块 API 文档

基础信息

  • Base URL: /api/auth
  • 认证方式: JWT Bearer Token
  • 刷新机制: Refresh Token(30天有效期)

数据库表

users 表(相关字段)

字段类型必填说明
idtext (PK)必填10位随机ID
phonetext (UNIQUE)必填手机号
emailtext (UNIQUE)选填邮箱
nicknametext必填昵称
password_hashtext必填bcrypt加密密码
avatar_urltext选填头像URL
roletext必填角色,默认 user
statustext必填状态,默认 active
is_guestinteger必填0=正式账号, 1=游客
has_onboardedinteger必填0=未完成引导, 1=已完成
prefer_green_expenseinteger必填0=否, 1=是(支出用绿色显示)
created_atdatetime必填创建时间
updated_atdatetime必填更新时间

refresh_tokens 表

字段类型必填说明
idtext (PK)必填10位随机ID
tokentext (UNIQUE)必填刷新令牌(40位hex)
user_idtext (FK→users)必填所属用户
expires_atdatetime必填过期时间
created_atdatetime必填创建时间

接口列表


GET/api/auth/me

1. 获取当前用户信息

触发时机: 应用启动时(AuthProvider 初始化),从 localStorage 恢复会话
App 启动 → localStorage 有 accessToken → GET /auth/me 验证Token有效性
请求头: Authorization: Bearer 后端处理: 1. 从 JWT token 提取 userId 2. 查询 users 表,取字段:id, phone, nickname, email, avatar_url, prefer_green_expense, is_guest, has_onboarded, created_at, role 3. avatar_url 为空时生成默认头像(makeFallbackAvatar(phone))并回写数据库 4. 规范化 nickname(normalizeNickname响应:
{
  "user": {
    "id": "string",
    "phone": "string",
    "nickname": "string",
    "email": "string | null",
    "avatar_url": "string",
    "prefer_green_expense": 0 | 1,
    "is_guest": 0 | 1,
    "has_onboarded": 0 | 1,
    "role": "user | admin | developer",
    "created_at": "ISO8601"
  }
}
Schema 注意: avatar_url 可能为空字符串,调用方需做兜底展示
Schema 注意: email 允许 NULL,但注册时必填

POST/api/auth/register

2. 注册账号

触发时机: 在 注册页面 填写完所有信息后点击「注册」按钮
注册页 → 填写手机/密码/昵称/邮箱/验证码 → 点击"注册" → POST /auth/register
请求参数:
参数类型必填说明
phonestring必填手机号,+86 前缀或国际格式
passwordstring必填至少8位,字母+数字
passwordConfirmstring必填确认密码
nicknamestring必填昵称,至少2字符
emailstring必填邮箱
emailCodestring必填邮箱验证码(6位数字)
captchaIdstring必填图形验证码ID
captchaAnswerstring必填图形验证码答案
后端处理: 1. 校验手机格式(+86 11位 / 国际格式) 2. 校验密码格式(8位+字母+数字) 3. 校验图形验证码(内存 Map,2分钟有效期) ⚠️ P03 4. 校验邮箱验证码 5. 校验邮箱未注册 6. bcrypt 加密密码 7. 生成唯一 userId(10次碰撞检测) 8. 事务: - 插入 users 表 - 插入 refresh_tokens 表 - 创建默认账单(bills + bill_members,角色 owner) - 插入7个默认分类(categories) 9. 删除邮箱验证码记录 10. 返回 JWT(2h有效期)+ refreshToken(30天)+ user 对象
Schema 注意: 注册时创建默认账单,账单名固定为「默认账单」,owner_id 指向新用户
响应(201):
{
  "token": "jwt_string",
  "refreshToken": "hex_string",
  "user": { "id", "phone", "nickname", "email", "avatar_url", "prefer_green_expense", "is_guest", "has_onboarded", "role" }
}

POST/api/auth/login

3. 用户登录

触发时机: 在 登录页面 填写手机号+密码后点击「登录」按钮
登录页 → 输入手机号+密码+图形验证码 → 点击"登录" → POST /auth/login
请求参数:
参数类型必填说明
phonestring必填手机号
passwordstring必填密码
captchaIdstring必填图形验证码ID
captchaAnswerstring必填图形验证码答案
后端处理: 1. 校验手机格式 2. 校验图形验证码 3. 查询 users 表,比对 password_hash(bcrypt) 4. 生成 JWT(2h)+ refreshToken(30天) 5. 插入 refresh_tokens 表 6. 头像兜底 + 更新 last_ip 响应(200):
{
  "token": "jwt_string",
  "refreshToken": "hex_string",
  "user": { ... }
}

POST/api/auth/refresh

4. 刷新 Token

触发时机: 自动,由 api.js 响应拦截器在收到 401 时触发
请求返回 401 → api.js 拦截器检测到 → POST /auth/refresh { refreshToken }
  → 获取新 token → 重试原请求
请求参数:
参数类型必填说明
refreshTokenstring必填刷新令牌
后端处理: 1. 校验 refreshToken 存在且未过期 2. 校验对应用户存在 3. 生成新 JWT + 新 refreshToken 4. 事务:删除旧 token,插入新 token
Schema 注意:见 P09

POST/api/auth/logout

5. 退出登录

触发时机: 点击顶部「退出登录」按钮
点击"退出登录" → 清除 localStorage → 跳转登录页 → 同时发送 POST /auth/logout(静默,不阻塞UI)
请求参数:
参数类型必填说明
refreshTokenstring选填传则只删这条,不传则删该用户全部
后端处理: 删除 refresh_tokens 表中对应记录
POST/api/auth/guest-login

6. 游客登录

触发时机: 登录页点击「随便看看」/「游客试用」按钮
登录页 → 点击"随便看看" → POST /auth/guest-login
后端处理: 1. 生成 userId + guestPhone(GUEST_XXXXXXXX) 2. IP 限制:24小时内同 IP 最多2个游客账号 3. 事务:插入 usersis_guest=1) + 默认账单 + refresh_tokens 4. JWT 有效期 7天(与正式账号的2h不同)
Schema 注意:见 P08

POST/api/auth/upgrade-guest

7. 游客转正

触发时机: 游客账号点击「注册正式账号」/「升级」按钮,填写完整信息后提交
游客模式 → 点击"升级为正式账号" → 填写手机/密码/邮箱/验证码 → POST /auth/upgrade-guest
请求参数:
参数类型必填说明
phonestring必填新手机号
passwordstring必填新密码
passwordConfirmstring必填确认密码
nicknamestring必填昵称
emailstring必填邮箱
emailCodestring必填邮箱验证码
captchaIdstring必填图形验证码ID
captchaAnswerstring必填图形验证码答案
后端处理: 1. 确认当前用户是 is_guest=1 2. 确认新手机号+邮箱未被他用户占用 3. 校验邮箱验证码 4. bcrypt 更新密码 5. 更新 users 表:is_guest=0 6. 生成新 JWT(2h)
PUT/api/auth/me/profile

8. 修改个人资料

触发时机: 在 设置页面 修改昵称/头像/绿色支出偏好后点击保存
设置页 → 修改昵称/头像/绿色支出 → 点击"保存" → PUT /auth/me/profile
请求参数:
参数类型必填说明
nicknamestring选填至少2字符
avatar_urlstring选填头像URL
prefer_green_expenseboolean选填支出是否绿色显示
约束: phoneemail 不可修改,传了也会被后端拒绝
Schema 注意: nickname 最少2字符,不满足返回 400

POST/api/auth/me/onboard-done

9. 完成新手引导

触发时机: 完成注册后第一次引导流程,点击「开始使用」/「完成引导」按钮
注册成功 → 进入引导流程 → 完成引导 → 点击"开始使用" → POST /auth/me/onboard-done
后端处理: 更新 users.has_onboarded = 1
GET/api/auth/register-captcha

10. 获取注册图形验证码

触发时机: 进入 注册页面 时自动请求(无需用户操作)
打开注册页 → 自动 GET /auth/register-captcha → 展示 SVG 验证码图片

GET/api/auth/login-captcha

11. 获取登录图形验证码

触发时机: 进入 登录页面 时自动请求;注册页切换到登录时也触发
打开登录页 → 自动 GET /auth/login-captcha → 展示 SVG 验证码图片

POST/api/auth/send-register-email

12. 发送注册邮箱验证码

触发时机: 在 注册页面 填写邮箱后点击「发送验证码」
注册页 → 输入邮箱 → 点击"发送验证码" → POST /auth/send-register-email
请求参数:
参数类型必填说明
emailstring必填邮箱地址
captchaIdstring必填当前图形验证码ID
captchaAnswerstring必填图形验证码答案
后端处理: 1. 校验邮箱格式 2. 校验图形验证码 3. 检查邮箱未注册 4. 60秒频率限制 5. 生成6位数字验证码,发送邮件 6. 存储到内存 Map,5分钟有效
Schema 注意: 邮箱必须唯一,已注册返回 400

POST/api/auth/send-reset-email

13. 发送重置密码邮箱验证码

触发时机: 在 登录页 点击「忘记密码」,填写邮箱+手机号+图形验证码后点击「发送」
登录页 → 点击"忘记密码" → 填写邮箱+手机号+图形验证码 → 点击"发送" → POST /auth/send-reset-email
请求参数:
参数类型必填说明
emailstring必填邮箱地址
phonestring必填注册时的手机号
captchaIdstring必填图形验证码ID
captchaAnswerstring必填图形验证码答案
关键: email + phone 必须匹配数据库中同一条用户记录,否则 400

POST/api/auth/reset-password

14. 重置密码

触发时机: 在 忘记密码流程 收到邮箱验证码后,输入新密码提交
收到邮箱验证码 → 输入新密码+确认密码 → 点击"确认重置" → POST /auth/reset-password
请求参数:
参数类型必填说明
emailstring必填邮箱地址
emailCodestring必填6位邮箱验证码
newPasswordstring必填新密码(8位+字母+数字)
newPasswordConfirmstring必填确认新密码
后端处理: 1. 校验两次密码一致 2. 校验密码格式 3. 校验邮箱验证码(内存 Map,5分钟有效期) 4. bcrypt 更新 password_hash 5. 删除验证码记录

密码格式验证规则

/^(?=.*[A-Za-z])(?=.*\d).{8,}$/

手机号格式验证规则

// 中国: +86 + 11位数字
// 国际: + 5-15位数字
/^(\+86)1\d{10}$|^\+\d{5,15}$/

JWT Payload 结构

{ "userId": "string", "phone": "string", "role": "string" }

AuthContext 登录后持久化字段

localStorage.setItem('accessToken', res.data.token)
localStorage.setItem('refreshToken', res.data.refreshToken)
localStorage.setItem('user', JSON.stringify(res.data.user))

POST/api/auth/dev/reset-ip

14. 重置 IP 限制(开发者专用)

认证: 必需(仅 role = 'developer' 可访问) 后端处理: 1. 校验当前用户 role 为 developer 2. 获取请求 IP(支持 x-forwarded-for) 3. 将所有 last_ip 等于该 IP 的用户 IP 字段清空(用于解除 IP 注册限制) 响应:
{ "success": true, "count": 5 }
数据库表: users ⚠️ 注意: 仅供开发者调试使用,不可暴露给普通用户