Files
whale-town-end/docs/api/openapi.yaml
moyin d683f0d5da feat: 邮箱冲突检测优化 v1.1.1
- 新增邮箱冲突检测:发送验证码前检查邮箱是否已被注册
- 优化用户体验:避免向已注册邮箱发送无用验证码
- 改进错误处理:返回409 Conflict状态码和明确错误信息
- 更新API文档:重新整理文档结构,突出前端开发要点
- 完善测试用例:添加邮箱冲突检测相关测试
- 版本升级:1.1.0  1.1.1

核心修改:
- src/core/login_core/login_core.service.ts: 在sendEmailVerification方法中添加邮箱存在性检查
- src/business/auth/controllers/login.controller.ts: 正确处理409冲突状态码
- docs/api/api-documentation.md: 重新整理为精简实用的前端开发文档
- docs/api/openapi.yaml: 更新版本和接口描述
- test-register-fix.ps1: 添加邮箱冲突检测测试用例
2025-12-25 18:31:36 +08:00

1140 lines
32 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: Pixel Game Server - Auth API
description: 像素游戏服务器用户认证API接口文档 - 包含验证码登录功能和邮箱冲突检测
version: 1.1.1
contact:
name: API Support
email: support@example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://localhost:3000
description: 开发环境
tags:
- name: app
description: 应用状态相关接口
- name: auth
description: 用户认证相关接口
- name: admin
description: 管理员后台相关接口
- name: user-management
description: 用户管理相关接口
paths:
/:
get:
tags:
- app
summary: 获取应用状态
description: 返回应用的基本运行状态信息,用于健康检查和监控
operationId: getAppStatus
responses:
'200':
description: 应用状态获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/AppStatusResponse'
example:
service: Pixel Game Server
version: 1.0.0
status: running
timestamp: "2025-12-25T08:00:00.000Z"
uptime: 3600
environment: development
storage_mode: database
/auth/login:
post:
tags:
- auth
summary: 用户登录
description: 支持用户名、邮箱或手机号登录
operationId: login
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginDto'
example:
identifier: testuser
password: password123
responses:
'200':
description: 登录成功
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
example:
success: true
data:
user:
id: "1"
username: testuser
nickname: 测试用户
email: test@example.com
phone: "+8613800138000"
avatar_url: https://example.com/avatar.jpg
role: 1
created_at: "2025-12-17T10:00:00.000Z"
access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
is_new_user: false
message: 登录成功
message: 登录成功
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: 用户名或密码错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/register:
post:
tags:
- auth
summary: 用户注册
description: 创建新用户账户。如果提供邮箱,需要先调用发送验证码接口获取验证码。发送验证码接口会自动检查邮箱是否已被注册,避免向已存在邮箱发送验证码。
operationId: register
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterDto'
example:
username: newuser
password: password123
nickname: 新用户
email: newuser@example.com
phone: "+8613800138001"
responses:
'201':
description: 注册成功
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterResponse'
'400':
description: 请求参数错误或验证码错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
validation_error:
summary: 参数验证错误
value:
success: false
message: "密码必须包含字母和数字长度8-128字符"
error_code: "REGISTER_FAILED"
verification_code_error:
summary: 验证码错误
value:
success: false
message: "验证码不存在或已过期"
error_code: "REGISTER_FAILED"
'409':
description: 资源冲突 - 用户名、邮箱或手机号已存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
username_exists:
summary: 用户名已存在
value:
success: false
message: "用户名已存在"
error_code: "REGISTER_FAILED"
email_exists:
summary: 邮箱已存在
value:
success: false
message: "邮箱已存在"
error_code: "REGISTER_FAILED"
phone_exists:
summary: 手机号已存在
value:
success: false
message: "手机号已存在"
error_code: "REGISTER_FAILED"
/auth/github:
post:
tags:
- auth
summary: GitHub OAuth登录
description: 使用GitHub账户登录或注册
operationId: githubOAuth
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/GitHubOAuthDto'
example:
github_id: "12345678"
username: octocat
nickname: The Octocat
email: octocat@github.com
avatar_url: https://github.com/images/error/octocat_happy.gif
responses:
'200':
description: GitHub登录成功
content:
application/json:
schema:
$ref: '#/components/schemas/GitHubOAuthResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: GitHub认证失败
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/forgot-password:
post:
tags:
- auth
summary: 发送密码重置验证码
description: 向用户邮箱或手机发送密码重置验证码
operationId: forgotPassword
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ForgotPasswordDto'
example:
identifier: test@example.com
responses:
'200':
description: 验证码发送成功
content:
application/json:
schema:
$ref: '#/components/schemas/ForgotPasswordResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: 用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/reset-password:
post:
tags:
- auth
summary: 重置密码
description: 使用验证码重置用户密码
operationId: resetPassword
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ResetPasswordDto'
example:
identifier: test@example.com
verification_code: "123456"
new_password: newpassword123
responses:
'200':
description: 密码重置成功
content:
application/json:
schema:
$ref: '#/components/schemas/CommonResponse'
'400':
description: 请求参数错误或验证码无效
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: 用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/change-password:
put:
tags:
- auth
summary: 修改密码
description: 用户修改自己的密码(需要提供旧密码)
operationId: changePassword
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChangePasswordDto'
example:
user_id: "1"
old_password: oldpassword123
new_password: newpassword123
responses:
'200':
description: 密码修改成功
content:
application/json:
schema:
$ref: '#/components/schemas/CommonResponse'
'400':
description: 请求参数错误或旧密码不正确
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: 用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/send-email-verification:
post:
tags:
- auth
summary: 发送邮箱验证码
description: 向指定邮箱发送验证码。如果邮箱已被注册,将返回冲突错误。
operationId: sendEmailVerification
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SendEmailVerificationDto'
example:
email: test@example.com
responses:
'200':
description: 验证码发送成功(真实发送模式)
content:
application/json:
schema:
$ref: '#/components/schemas/EmailVerificationResponse'
'206':
description: 测试模式:验证码已生成但未真实发送
content:
application/json:
schema:
$ref: '#/components/schemas/TestModeEmailVerificationResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: 邮箱已被注册
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
success: false
message: "邮箱已被注册,请使用其他邮箱或直接登录"
error_code: "SEND_EMAIL_VERIFICATION_FAILED"
'429':
description: 发送频率过高
content:
application/json:
schema:
$ref: '#/components/schemas/ThrottleErrorResponse'
/auth/verify-email:
post:
tags:
- auth
summary: 验证邮箱验证码
description: 使用验证码验证邮箱
operationId: verifyEmail
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EmailVerificationDto'
example:
email: test@example.com
verification_code: "123456"
responses:
'200':
description: 邮箱验证成功
content:
application/json:
schema:
$ref: '#/components/schemas/CommonResponse'
'400':
description: 验证码错误或已过期
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/resend-email-verification:
post:
tags:
- auth
summary: 重新发送邮箱验证码
description: 重新向指定邮箱发送验证码
operationId: resendEmailVerification
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SendEmailVerificationDto'
example:
email: test@example.com
responses:
'200':
description: 验证码重新发送成功
content:
application/json:
schema:
$ref: '#/components/schemas/EmailVerificationResponse'
'206':
description: 测试模式:验证码已生成但未真实发送
content:
application/json:
schema:
$ref: '#/components/schemas/TestModeEmailVerificationResponse'
'400':
description: 邮箱已验证或用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: 发送频率过高
content:
application/json:
schema:
$ref: '#/components/schemas/ThrottleErrorResponse'
/auth/verification-code-login:
post:
tags:
- auth
summary: 验证码登录
description: 使用邮箱或手机号和验证码进行登录,无需密码
operationId: verificationCodeLogin
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationCodeLoginDto'
example:
identifier: test@example.com
verification_code: "123456"
responses:
'200':
description: 验证码登录成功
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
description: 验证码错误或已过期
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationCodeLoginErrorResponse'
'404':
description: 用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/send-login-verification-code:
post:
tags:
- auth
summary: 发送登录验证码
description: 向用户邮箱或手机发送登录验证码
operationId: sendLoginVerificationCode
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SendLoginVerificationCodeDto'
example:
identifier: test@example.com
responses:
'200':
description: 验证码发送成功
content:
application/json:
schema:
$ref: '#/components/schemas/EmailVerificationResponse'
'206':
description: 测试模式:验证码已生成但未真实发送
content:
application/json:
schema:
$ref: '#/components/schemas/TestModeEmailVerificationResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: 用户不存在
content:
application/json:
schema:
$ref: '#/components/schemas/SendLoginCodeErrorResponse'
'429':
description: 发送频率过高
content:
application/json:
schema:
$ref: '#/components/schemas/ThrottleErrorResponse'
/auth/debug-verification-code:
post:
tags:
- auth
summary: 调试验证码信息
description: 获取验证码的详细调试信息(仅开发环境)
operationId: debugVerificationCode
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SendEmailVerificationDto'
example:
email: test@example.com
responses:
'200':
description: 调试信息获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/DebugVerificationCodeResponse'
/auth/debug-clear-throttle:
post:
tags:
- auth
summary: 清除限流记录
description: 清除所有限流记录(仅开发环境使用)
operationId: clearThrottle
responses:
'200':
description: 限流记录已清除
content:
application/json:
schema:
$ref: '#/components/schemas/CommonResponse'
components:
schemas:
AppStatusResponse:
type: object
properties:
service:
type: string
description: 服务名称
example: Pixel Game Server
version:
type: string
description: 版本号
example: 1.0.0
status:
type: string
description: 运行状态
example: running
timestamp:
type: string
format: date-time
description: 当前时间戳
example: "2025-12-25T08:00:00.000Z"
uptime:
type: integer
description: 运行时间(秒)
example: 3600
environment:
type: string
description: 运行环境
example: development
storage_mode:
type: string
description: 存储模式
example: database
LoginDto:
type: object
required:
- identifier
- password
properties:
identifier:
type: string
description: 登录标识符,支持用户名、邮箱或手机号
minLength: 1
maxLength: 100
example: testuser
password:
type: string
description: 用户密码
minLength: 1
maxLength: 128
example: password123
RegisterDto:
type: object
required:
- username
- password
- nickname
properties:
username:
type: string
description: 用户名,只能包含字母、数字和下划线
minLength: 1
maxLength: 50
pattern: '^[a-zA-Z0-9_]+$'
example: testuser
password:
type: string
description: 密码必须包含字母和数字长度8-128字符
minLength: 8
maxLength: 128
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
example: password123
nickname:
type: string
description: 用户昵称
minLength: 1
maxLength: 50
example: 测试用户
email:
type: string
format: email
description: 邮箱地址(可选)
example: test@example.com
phone:
type: string
description: 手机号码(可选)
example: "+8613800138000"
GitHubOAuthDto:
type: object
required:
- github_id
- username
- nickname
properties:
github_id:
type: string
description: GitHub用户ID
minLength: 1
maxLength: 100
example: "12345678"
username:
type: string
description: GitHub用户名
minLength: 1
maxLength: 50
example: octocat
nickname:
type: string
description: GitHub显示名称
minLength: 1
maxLength: 50
example: The Octocat
email:
type: string
format: email
description: GitHub邮箱地址可选
example: octocat@github.com
avatar_url:
type: string
description: GitHub头像URL可选
example: https://github.com/images/error/octocat_happy.gif
ForgotPasswordDto:
type: object
required:
- identifier
properties:
identifier:
type: string
description: 邮箱或手机号
minLength: 1
maxLength: 100
example: test@example.com
ResetPasswordDto:
type: object
required:
- identifier
- verification_code
- new_password
properties:
identifier:
type: string
description: 邮箱或手机号
minLength: 1
maxLength: 100
example: test@example.com
verification_code:
type: string
description: 6位数字验证码
pattern: '^\d{6}$'
example: "123456"
new_password:
type: string
description: 新密码必须包含字母和数字长度8-128字符
minLength: 8
maxLength: 128
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
example: newpassword123
ChangePasswordDto:
type: object
required:
- user_id
- old_password
- new_password
properties:
user_id:
type: string
description: 用户ID实际应用中应从JWT令牌中获取
example: "1"
old_password:
type: string
description: 当前密码
minLength: 1
maxLength: 128
example: oldpassword123
new_password:
type: string
description: 新密码必须包含字母和数字长度8-128字符
minLength: 8
maxLength: 128
pattern: '^(?=.*[a-zA-Z])(?=.*\d)'
example: newpassword123
SendEmailVerificationDto:
type: object
required:
- email
properties:
email:
type: string
format: email
description: 邮箱地址
example: test@example.com
EmailVerificationDto:
type: object
required:
- email
- verification_code
properties:
email:
type: string
format: email
description: 邮箱地址
example: test@example.com
verification_code:
type: string
description: 6位数字验证码
pattern: '^\d{6}$'
example: "123456"
VerificationCodeLoginDto:
type: object
required:
- identifier
- verification_code
properties:
identifier:
type: string
description: 登录标识符(邮箱或手机号)
minLength: 1
maxLength: 100
example: test@example.com
verification_code:
type: string
description: 6位数字验证码
pattern: '^\d{6}$'
example: "123456"
SendLoginVerificationCodeDto:
type: object
required:
- identifier
properties:
identifier:
type: string
description: 邮箱或手机号
minLength: 1
maxLength: 100
example: test@example.com
UserInfo:
type: object
properties:
id:
type: string
description: 用户ID
example: "1"
username:
type: string
description: 用户名
example: testuser
nickname:
type: string
description: 用户昵称
example: 测试用户
email:
type: string
format: email
description: 邮箱地址
example: test@example.com
phone:
type: string
description: 手机号码
example: "+8613800138000"
avatar_url:
type: string
description: 头像URL
example: https://example.com/avatar.jpg
role:
type: integer
description: 用户角色
example: 1
created_at:
type: string
format: date-time
description: 创建时间
example: "2025-12-17T10:00:00.000Z"
LoginResponseData:
type: object
properties:
user:
$ref: '#/components/schemas/UserInfo'
access_token:
type: string
description: 访问令牌
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
refresh_token:
type: string
description: 刷新令牌
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
is_new_user:
type: boolean
description: 是否为新用户
example: false
message:
type: string
description: 响应消息
example: 登录成功
LoginResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
$ref: '#/components/schemas/LoginResponseData'
message:
type: string
description: 响应消息
example: 登录成功
RegisterResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
$ref: '#/components/schemas/LoginResponseData'
message:
type: string
description: 响应消息
example: 注册成功
GitHubOAuthResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
$ref: '#/components/schemas/LoginResponseData'
message:
type: string
description: 响应消息
example: GitHub登录成功
ForgotPasswordResponseData:
type: object
properties:
verification_code:
type: string
description: 验证码(仅用于演示,实际应用中不应返回)
example: "123456"
ForgotPasswordResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
$ref: '#/components/schemas/ForgotPasswordResponseData'
message:
type: string
description: 响应消息
example: 验证码已发送,请查收
CommonResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
message:
type: string
description: 响应消息
example: 操作成功
ErrorResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: false
message:
type: string
description: 错误消息
example: 操作失败
error_code:
type: string
description: 错误代码
example: OPERATION_FAILED
EmailVerificationResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
type: object
properties:
sent_to:
type: string
description: 发送目标
example: test@example.com
expires_in:
type: integer
description: 过期时间(秒)
example: 300
is_test_mode:
type: boolean
description: 是否为测试模式
example: false
message:
type: string
description: 响应消息
example: 验证码已发送,请查收邮件
TestModeEmailVerificationResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: false
data:
type: object
properties:
verification_code:
type: string
description: 验证码(仅测试模式)
example: "123456"
sent_to:
type: string
description: 发送目标
example: test@example.com
expires_in:
type: integer
description: 过期时间(秒)
example: 300
is_test_mode:
type: boolean
description: 是否为测试模式
example: true
message:
type: string
description: 响应消息
example: 测试模式:验证码已生成但未真实发送
error_code:
type: string
description: 错误代码
example: TEST_MODE_ONLY
VerificationCodeLoginErrorResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: false
message:
type: string
description: 错误消息
example: 验证码错误或已过期
error_code:
type: string
description: 错误代码
example: VERIFICATION_CODE_LOGIN_FAILED
SendLoginCodeErrorResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: false
message:
type: string
description: 错误消息
example: 用户不存在
error_code:
type: string
description: 错误代码
example: SEND_LOGIN_CODE_FAILED
ThrottleErrorResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: false
message:
type: string
description: 错误消息
example: 请求过于频繁,请稍后再试
error_code:
type: string
description: 错误代码
example: TOO_MANY_REQUESTS
throttle_info:
type: object
properties:
limit:
type: integer
description: 限制次数
example: 1
window_seconds:
type: integer
description: 时间窗口(秒)
example: 60
current_requests:
type: integer
description: 当前请求次数
example: 1
reset_time:
type: string
format: date-time
description: 重置时间
example: "2025-12-25T08:01:00.000Z"
DebugVerificationCodeResponse:
type: object
properties:
success:
type: boolean
description: 请求是否成功
example: true
data:
type: object
properties:
key:
type: string
description: Redis键名
example: verification_code:email_verification:test@example.com
exists:
type: boolean
description: 是否存在
example: true
ttl:
type: integer
description: 剩余生存时间(秒)
example: 290
rawData:
type: string
description: 原始数据
example: '{"code":"123456","createdAt":1766649341250}'
parsedData:
type: object
description: 解析后的数据
properties:
code:
type: string
example: "123456"
createdAt:
type: integer
example: 1766649341250
currentTime:
type: integer
description: 当前时间戳
example: 1766649341250