first commit
This commit is contained in:
395
AIEC-server/services/api/auth-service.js
Normal file
395
AIEC-server/services/api/auth-service.js
Normal file
@ -0,0 +1,395 @@
|
||||
/**
|
||||
* 认证服务API
|
||||
*/
|
||||
class AuthService {
|
||||
|
||||
// 本地开发: 'http://localhost:8080/api'
|
||||
// 生产环境: 'http://101.200.154.78:8080/api'
|
||||
static BASE_URL = 'http://localhost:8080/api';
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param {string} account - 手机号或邮箱
|
||||
* @param {string} type - 验证码类型:login/register
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async sendVerifyCode(account, type = 'login') {
|
||||
try {
|
||||
const response = await fetch(`${this.BASE_URL}/auth/send-code`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contact: account // 后端使用contact字段
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 后端使用code字段判断成功与否,200表示成功
|
||||
if (data.code !== 200) {
|
||||
throw new Error(data.message || '发送验证码失败');
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: data.data,
|
||||
message: data.message || '验证码发送成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Send verify code failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '网络错误,请稍后重试'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {string} account - 手机号或邮箱
|
||||
* @param {string} verifyCode - 验证码
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async login(account, verifyCode) {
|
||||
try {
|
||||
const response = await fetch(`${this.BASE_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contact: account, // 后端使用contact字段
|
||||
code: verifyCode // 后端使用code字段
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 后端使用code字段判断成功与否,200表示成功
|
||||
if (data.code !== 200) {
|
||||
throw new Error(data.message || '登录失败');
|
||||
}
|
||||
|
||||
// 后端响应的数据在data字段中
|
||||
const authData = data.data;
|
||||
if (authData && authData.token) {
|
||||
TokenManager.saveToken(authData.token, true);
|
||||
// 构建user对象
|
||||
const user = {
|
||||
id: authData.id,
|
||||
username: authData.username,
|
||||
phone: authData.phone,
|
||||
email: authData.email
|
||||
};
|
||||
TokenManager.saveUser(user, true);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: authData,
|
||||
message: data.message || '登录成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '网络错误,请稍后重试'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* @param {string} username - 用户名
|
||||
* @param {string} account - 手机号或邮箱
|
||||
* @param {string} verifyCode - 验证码
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async register(username, account, verifyCode) {
|
||||
try {
|
||||
const response = await fetch(`${this.BASE_URL}/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
// 判断是邮箱还是手机号
|
||||
...(account.includes('@') ? { email: account } : { phone: account }),
|
||||
code: verifyCode // 后端使用code字段
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 后端使用code字段判断成功与否,200表示成功
|
||||
if (data.code !== 200) {
|
||||
throw new Error(data.message || '注册失败');
|
||||
}
|
||||
|
||||
// 后端响应的数据在data字段中
|
||||
const authData = data.data;
|
||||
if (authData && authData.token) {
|
||||
TokenManager.saveToken(authData.token, true);
|
||||
// 构建user对象
|
||||
const user = {
|
||||
id: authData.id,
|
||||
username: authData.username,
|
||||
phone: authData.phone,
|
||||
email: authData.email
|
||||
};
|
||||
TokenManager.saveUser(user, true);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: authData,
|
||||
message: data.message || '注册成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Register failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '网络错误,请稍后重试'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async logout() {
|
||||
try {
|
||||
const authHeader = TokenManager.getAuthHeader();
|
||||
|
||||
const response = await fetch(`${this.BASE_URL}/auth/logout`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeader
|
||||
}
|
||||
});
|
||||
|
||||
// 无论服务器响应如何,都清除本地存储
|
||||
TokenManager.clearAll();
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('Server logout failed, but local data cleared');
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '退出登录成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
// 即使请求失败,也要清除本地数据
|
||||
TokenManager.clearAll();
|
||||
return {
|
||||
success: true,
|
||||
message: '已退出登录'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async getUserInfo() {
|
||||
try {
|
||||
const authHeader = TokenManager.getAuthHeader();
|
||||
|
||||
if (!authHeader) {
|
||||
throw new Error('未登录');
|
||||
}
|
||||
|
||||
// 从本地存储获取用户ID
|
||||
const localUser = TokenManager.getUser();
|
||||
if (!localUser || !localUser.id) {
|
||||
throw new Error('用户ID不存在');
|
||||
}
|
||||
|
||||
// 使用 /api/users/{id} 接口替代 /api/users/me
|
||||
const response = await fetch(`${this.BASE_URL}/users/${localUser.id}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeader
|
||||
}
|
||||
});
|
||||
|
||||
// 特殊处理403错误
|
||||
if (response.status === 403) {
|
||||
throw new Error('403:权限配置问题');
|
||||
}
|
||||
|
||||
// 特殊处理404错误
|
||||
if (response.status === 404) {
|
||||
throw new Error('404:接口未实现');
|
||||
}
|
||||
|
||||
// 处理401未授权
|
||||
if (response.status === 401) {
|
||||
TokenManager.clearAll();
|
||||
throw new Error('登录已过期,请重新登录');
|
||||
}
|
||||
|
||||
// 尝试解析响应
|
||||
let data;
|
||||
try {
|
||||
const text = await response.text();
|
||||
if (!text) {
|
||||
throw new Error('响应为空');
|
||||
}
|
||||
data = JSON.parse(text);
|
||||
} catch (parseError) {
|
||||
throw new Error(`解析响应失败: ${response.status}`);
|
||||
}
|
||||
|
||||
// UserController直接返回User对象,不是包装的ResponseResult
|
||||
// 所以这里data就是用户信息
|
||||
const userData = data;
|
||||
|
||||
// 确保不暴露密码等敏感信息
|
||||
if (userData) {
|
||||
delete userData.password;
|
||||
delete userData.salt;
|
||||
|
||||
// 更新本地存储的用户信息
|
||||
TokenManager.saveUser(userData, true);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: userData,
|
||||
message: data.message || '获取成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Get user info failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '网络错误,请稍后重试'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新Token
|
||||
* @returns {Promise} API响应
|
||||
*/
|
||||
static async refreshToken() {
|
||||
try {
|
||||
const refreshToken = TokenManager.getRefreshToken();
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new Error('无刷新令牌');
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.BASE_URL}/auth/refresh`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
refreshToken
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
// 刷新失败,清除所有本地数据
|
||||
TokenManager.clearAll();
|
||||
throw new Error(data.error || data.message || 'Token刷新失败');
|
||||
}
|
||||
|
||||
// Spring Boot直接返回token数据
|
||||
if (data.token) {
|
||||
TokenManager.saveToken(data.token, true);
|
||||
if (data.refreshToken) {
|
||||
TokenManager.saveRefreshToken(data.refreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: data,
|
||||
message: 'Token刷新成功'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Refresh token failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error.message || '刷新失败,请重新登录'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查认证状态
|
||||
* @returns {Promise} 认证状态
|
||||
*/
|
||||
static async checkAuthStatus() {
|
||||
try {
|
||||
// 先检查本地token
|
||||
const isTokenValid = TokenManager.isTokenValid();
|
||||
const localUser = TokenManager.getUser();
|
||||
|
||||
if (!isTokenValid) {
|
||||
console.log('本地token无效,用户未登录');
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
user: null,
|
||||
message: '未登录'
|
||||
};
|
||||
}
|
||||
|
||||
// 如果有token,尝试从服务器获取最新用户信息
|
||||
try {
|
||||
const userResult = await this.getUserInfo();
|
||||
if (userResult.success) {
|
||||
console.log('成功从服务器获取用户信息:', userResult.data);
|
||||
return {
|
||||
isAuthenticated: true,
|
||||
user: userResult.data,
|
||||
message: '已登录'
|
||||
};
|
||||
}
|
||||
} catch (userError) {
|
||||
console.warn('获取用户信息失败:', userError);
|
||||
|
||||
// 如果API调用失败但有本地数据,使用本地数据作为降级方案
|
||||
if (localUser) {
|
||||
console.log('API调用失败,使用本地缓存的用户信息:', localUser);
|
||||
return {
|
||||
isAuthenticated: true,
|
||||
user: localUser,
|
||||
message: '已登录(使用缓存)'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
user: null,
|
||||
message: '未登录'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Check auth status failed:', error);
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
user: null,
|
||||
message: '认证检查失败'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出供其他模块使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = AuthService;
|
||||
}
|
||||
Reference in New Issue
Block a user