389 lines
13 KiB
TypeScript
389 lines
13 KiB
TypeScript
// 认证相关API接口
|
||
import { ApiRequest } from '../request'
|
||
import type {
|
||
ApiResponse,
|
||
User,
|
||
LoginRequest,
|
||
LoginResponse,
|
||
// BackendLoginResponse,
|
||
RegisterRequest,
|
||
UserProfile,
|
||
} from '../types'
|
||
|
||
/**
|
||
* 认证API模块
|
||
*/
|
||
export class AuthApi {
|
||
// 用户登录
|
||
static async login(data: LoginRequest): Promise<ApiResponse<LoginResponse>> {
|
||
try {
|
||
// 转换参数格式:将email/phone转换为username
|
||
const loginData = {
|
||
username: data.email || data.phone || '',
|
||
password: data.password
|
||
}
|
||
|
||
console.log('🚀 发送登录请求:', { url: '/biz/user/login', data: { ...loginData, password: '***' } })
|
||
|
||
// 调用后端API
|
||
const response = await ApiRequest.post<any>('/biz/user/login', loginData)
|
||
|
||
console.log('🔍 Login API Response:', response)
|
||
console.log('🔍 Response Code:', response.code)
|
||
console.log('🔍 Response Data:', response.data)
|
||
console.log('🔍 Response Data Type:', typeof response.data)
|
||
|
||
// 处理响应格式问题 - 如果code和message是undefined,可能是响应拦截器的问题
|
||
let actualCode = response.code
|
||
let actualMessage = response.message
|
||
let actualData = response.data
|
||
|
||
// 如果response.code是undefined,检查response.data是否包含完整的API响应
|
||
if (actualCode === undefined && actualData && typeof actualData === 'object') {
|
||
// 检查是否是标准的jeecg-boot响应格式 (success, code, message, result)
|
||
if ('success' in actualData && 'code' in actualData && 'message' in actualData && 'result' in actualData) {
|
||
actualCode = actualData.code
|
||
actualMessage = actualData.message
|
||
actualData = actualData.result
|
||
console.log('🔧 修正后的响应 (jeecg-boot格式):', { code: actualCode, message: actualMessage, data: actualData })
|
||
}
|
||
// 检查是否是其他格式 (code, message, data)
|
||
else if ('code' in actualData && 'message' in actualData && 'data' in actualData) {
|
||
actualCode = actualData.code
|
||
actualMessage = actualData.message
|
||
actualData = actualData.data
|
||
console.log('🔧 修正后的响应 (标准格式):', { code: actualCode, message: actualMessage, data: actualData })
|
||
}
|
||
}
|
||
|
||
// 检查响应格式并适配
|
||
if (actualCode === 200 || actualCode === 0) {
|
||
// 如果后端返回的是完整的用户信息格式(mock数据格式)
|
||
if (actualData && actualData.user && actualData.token) {
|
||
return {
|
||
code: actualCode,
|
||
message: actualMessage,
|
||
data: actualData
|
||
} as ApiResponse<LoginResponse>
|
||
}
|
||
|
||
// 如果后端返回的是jeecg-boot格式(只包含token)
|
||
if (actualData && actualData.token) {
|
||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||
code: actualCode,
|
||
message: actualMessage || '登录成功',
|
||
data: {
|
||
user: {
|
||
id: 1, // 真实API没有返回用户ID,使用默认值
|
||
email: data.email || '',
|
||
phone: data.phone || '',
|
||
username: data.email || data.phone || '',
|
||
nickname: '用户',
|
||
avatar: '',
|
||
role: 'student',
|
||
status: 'active',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString()
|
||
},
|
||
token: actualData.token,
|
||
refreshToken: '', // 真实API没有返回refreshToken
|
||
expiresIn: 3600 // 默认1小时过期
|
||
}
|
||
}
|
||
return adaptedResponse
|
||
}
|
||
|
||
// 如果后端返回的是简化格式(BackendLoginResponse)
|
||
if (actualData && actualData.token) {
|
||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||
code: actualCode,
|
||
message: actualMessage || '登录成功',
|
||
data: {
|
||
user: {
|
||
id: actualData.id || 1,
|
||
email: data.email || '',
|
||
phone: data.phone || '',
|
||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||
nickname: '用户',
|
||
avatar: '',
|
||
role: 'student',
|
||
status: 'active',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString()
|
||
},
|
||
token: actualData.token,
|
||
refreshToken: actualData.refreshToken || '',
|
||
expiresIn: actualData.expiresIn || 3600
|
||
}
|
||
}
|
||
return adaptedResponse
|
||
}
|
||
|
||
// 如果只有token字段,直接使用token
|
||
if (typeof actualData === 'string') {
|
||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||
code: actualCode,
|
||
message: actualMessage || '登录成功',
|
||
data: {
|
||
user: {
|
||
id: 1,
|
||
email: data.email || '',
|
||
phone: data.phone || '',
|
||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||
nickname: '用户',
|
||
avatar: '',
|
||
role: 'student',
|
||
status: 'active',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString()
|
||
},
|
||
token: actualData,
|
||
refreshToken: '',
|
||
expiresIn: 3600
|
||
}
|
||
}
|
||
return adaptedResponse
|
||
}
|
||
|
||
// 如果data是null或undefined,但响应成功,可能是某些API的特殊情况
|
||
if (!actualData) {
|
||
console.warn('⚠️ API返回成功但data为空,可能需要检查API实现')
|
||
throw new Error('登录成功但未返回用户信息')
|
||
}
|
||
|
||
// 尝试处理其他可能的响应格式
|
||
console.warn('⚠️ 未知的响应格式,尝试通用处理:', actualData)
|
||
|
||
// 如果data是对象但不包含预期字段,尝试创建默认响应
|
||
if (typeof actualData === 'object') {
|
||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||
code: actualCode,
|
||
message: actualMessage || '登录成功',
|
||
data: {
|
||
user: {
|
||
id: actualData.id || actualData.userId || 1,
|
||
email: data.email || '',
|
||
phone: data.phone || '',
|
||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||
nickname: actualData.nickname || actualData.name || '用户',
|
||
avatar: actualData.avatar || '',
|
||
role: actualData.role || 'student',
|
||
status: 'active',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString()
|
||
},
|
||
token: actualData.accessToken || actualData.access_token || 'temp_token_' + Date.now(),
|
||
refreshToken: actualData.refreshToken || actualData.refresh_token || '',
|
||
expiresIn: actualData.expiresIn || actualData.expires_in || 3600
|
||
}
|
||
}
|
||
return adaptedResponse
|
||
}
|
||
}
|
||
|
||
// 如果响应格式不符合预期,抛出错误
|
||
console.error('🚨 Unexpected response format:', {
|
||
code: actualCode,
|
||
message: actualMessage,
|
||
data: actualData,
|
||
dataType: typeof actualData
|
||
})
|
||
throw new Error(actualMessage || '登录响应格式错误')
|
||
} catch (error) {
|
||
console.error('🚨 Login API Error:', error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 用户注册
|
||
static register(data: RegisterRequest): Promise<ApiResponse<User>> {
|
||
return ApiRequest.post('/auth/register', data)
|
||
}
|
||
|
||
// 用户登出
|
||
static logout(): Promise<ApiResponse<null>> {
|
||
// 尝试多个可能的登出端点
|
||
return ApiRequest.post('/auth/logout').catch(async (error) => {
|
||
// 如果 /auth/logout 失败,尝试其他可能的端点
|
||
if (error.response?.status === 404) {
|
||
try {
|
||
return await ApiRequest.post('/users/logout')
|
||
} catch (secondError) {
|
||
// 如果所有端点都失败,返回成功(客户端登出)
|
||
console.log('所有登出端点都不存在,执行客户端登出')
|
||
return {
|
||
code: 200,
|
||
message: '登出成功',
|
||
data: null
|
||
}
|
||
}
|
||
}
|
||
throw error
|
||
})
|
||
}
|
||
|
||
// 刷新Token
|
||
static refreshToken(refreshToken: string): Promise<ApiResponse<LoginResponse>> {
|
||
return ApiRequest.post('/auth/refresh', { refreshToken })
|
||
}
|
||
|
||
// 获取当前用户信息
|
||
static getCurrentUser(): Promise<ApiResponse<User>> {
|
||
return ApiRequest.get('/users/info')
|
||
}
|
||
|
||
// 更新用户资料
|
||
static updateProfile(data: Partial<UserProfile>): Promise<ApiResponse<User>> {
|
||
return ApiRequest.put('/auth/profile', data)
|
||
}
|
||
|
||
// 修改密码
|
||
static changePassword(data: {
|
||
oldPassword: string
|
||
newPassword: string
|
||
confirmPassword: string
|
||
}): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/change-password', data)
|
||
}
|
||
|
||
// 忘记密码 - 发送重置邮件
|
||
static forgotPassword(email: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/forgot-password', { email })
|
||
}
|
||
|
||
// 重置密码
|
||
static resetPassword(data: {
|
||
token: string
|
||
password: string
|
||
confirmPassword: string
|
||
}): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/reset-password', data)
|
||
}
|
||
|
||
// 发送邮箱验证码
|
||
static sendEmailVerification(email: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/send-email-verification', { email })
|
||
}
|
||
|
||
// 验证邮箱
|
||
static verifyEmail(data: {
|
||
email: string
|
||
code: string
|
||
}): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/verify-email', data)
|
||
}
|
||
|
||
// 发送手机验证码
|
||
static sendSmsVerification(phone: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/send-sms-verification', { phone })
|
||
}
|
||
|
||
// 验证手机号
|
||
static verifyPhone(data: {
|
||
phone: string
|
||
code: string
|
||
}): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/verify-phone', data)
|
||
}
|
||
|
||
// 绑定第三方账号
|
||
static bindThirdParty(data: {
|
||
provider: 'wechat' | 'qq' | 'weibo' | 'github'
|
||
code: string
|
||
state?: string
|
||
}): Promise<ApiResponse<User>> {
|
||
return ApiRequest.post('/auth/bind-third-party', data)
|
||
}
|
||
|
||
// 解绑第三方账号
|
||
static unbindThirdParty(provider: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.delete(`/auth/unbind-third-party/${provider}`)
|
||
}
|
||
|
||
// 获取第三方登录URL
|
||
static getThirdPartyLoginUrl(provider: string, redirectUrl?: string): Promise<ApiResponse<{ url: string }>> {
|
||
return ApiRequest.get(`/auth/third-party-login-url/${provider}`, { redirectUrl })
|
||
}
|
||
|
||
// 第三方登录回调
|
||
static thirdPartyLoginCallback(data: {
|
||
provider: string
|
||
code: string
|
||
state?: string
|
||
}): Promise<ApiResponse<LoginResponse>> {
|
||
return ApiRequest.post('/auth/third-party-login-callback', data)
|
||
}
|
||
|
||
// 上传头像
|
||
static uploadAvatar(file: File, onProgress?: (progress: number) => void): Promise<ApiResponse<{ url: string }>> {
|
||
return ApiRequest.upload('/auth/upload-avatar', file, onProgress)
|
||
}
|
||
|
||
// 删除账号
|
||
static deleteAccount(password: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/delete-account', { password })
|
||
}
|
||
|
||
// 获取账号安全信息
|
||
static getSecurityInfo(): Promise<ApiResponse<{
|
||
hasPassword: boolean
|
||
hasEmail: boolean
|
||
hasPhone: boolean
|
||
emailVerified: boolean
|
||
phoneVerified: boolean
|
||
twoFactorEnabled: boolean
|
||
thirdPartyAccounts: Array<{
|
||
provider: string
|
||
nickname: string
|
||
avatar?: string
|
||
bindTime: string
|
||
}>
|
||
loginHistory: Array<{
|
||
ip: string
|
||
location: string
|
||
device: string
|
||
loginTime: string
|
||
}>
|
||
}>> {
|
||
return ApiRequest.get('/auth/security-info')
|
||
}
|
||
|
||
// 启用两步验证
|
||
static enableTwoFactor(): Promise<ApiResponse<{
|
||
qrCode: string
|
||
secret: string
|
||
backupCodes: string[]
|
||
}>> {
|
||
return ApiRequest.post('/auth/enable-two-factor')
|
||
}
|
||
|
||
// 确认启用两步验证
|
||
static confirmTwoFactor(code: string): Promise<ApiResponse<{
|
||
backupCodes: string[]
|
||
}>> {
|
||
return ApiRequest.post('/auth/confirm-two-factor', { code })
|
||
}
|
||
|
||
// 禁用两步验证
|
||
static disableTwoFactor(data: {
|
||
password: string
|
||
code: string
|
||
}): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/disable-two-factor', data)
|
||
}
|
||
|
||
// 生成新的备用码
|
||
static generateBackupCodes(): Promise<ApiResponse<{
|
||
backupCodes: string[]
|
||
}>> {
|
||
return ApiRequest.post('/auth/generate-backup-codes')
|
||
}
|
||
|
||
// 验证两步验证码
|
||
static verifyTwoFactor(code: string): Promise<ApiResponse<null>> {
|
||
return ApiRequest.post('/auth/verify-two-factor', { code })
|
||
}
|
||
}
|
||
|
||
export default AuthApi
|