| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- import { authService } from './authService';
- // API Base URL - 从环境变量获取
- // 默认改为 8010,与当前后端一致。若有自定义域名/端口,请在 .env.local 配置 VITE_API_BASE_URL
- const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8010';
- // 用户信息接口(与后端API对应)
- export interface User {
- id: string;
- username: string;
- nickname: string;
- phone: string | null;
- email: string | null;
- avatar: string | null;
- registration_date: string;
- created_at: string;
- updated_at: string;
- apikey?: string; // API密钥字段
- }
- // 注册请求接口
- export interface RegisterRequest {
- username: string;
- password: string;
- nickname: string;
- email?: string;
- phone?: string;
- sms_code?: string;
- }
- // 登录响应接口
- export interface LoginResponse {
- access_token: string;
- token_type: string;
- user: User;
- }
- // 更新用户信息请求接口
- export interface UpdateUserRequest {
- nickname?: string;
- phone?: string;
- email?: string;
- avatar?: string;
- apikey?: string;
- }
- // 错误响应接口
- export interface ErrorResponse {
- detail?: string;
- message?: string;
- }
- // Token验证响应接口
- export interface TokenVerifyResponse {
- valid: boolean;
- user: User;
- }
- // 登出响应接口
- export interface LogoutResponse {
- message: string;
- user_id: string;
- }
- /**
- * 用户API服务类
- * 负责与后端用户中心API交互
- */
- class UserApiService {
- private baseUrl = `${API_BASE_URL}/api`;
- /**
- * 获取请求头(包含鉴权信息)
- */
- private getHeaders(requireAuth: boolean = false): Record<string, string> {
- const headers: Record<string, string> = {
- 'Content-Type': 'application/json'
- };
- if (requireAuth) {
- const authHeader = authService.getAuthHeader();
- if (authHeader) {
- headers['Authorization'] = authHeader;
- } else {
- throw new Error('未授权:请先登录');
- }
- }
- return headers;
- }
- /**
- * 处理API响应
- */
- private async handleResponse<T>(response: Response): Promise<T> {
- if (!response.ok) {
- // 尝试解析错误信息
- let errorMessage = `请求失败,请稍后重试`;
- try {
- const errorData: ErrorResponse = await response.json();
- const detail = errorData.detail || errorData.message || '';
- // 将后端错误信息转换为用户友好的中文提示
- errorMessage = this.translateErrorMessage(detail, response.status);
- } catch (e) {
- // 解析失败,使用默认错误信息
- }
-
- // 401 错误且不是登录接口时,清除登录状态(不调用后端API,因为Token已无效)
- if (response.status === 401 && !response.url.includes('/auth/login')) {
- authService.logout(false);
- }
-
- throw new Error(errorMessage);
- }
- return response.json();
- }
- /**
- * 将后端错误信息转换为用户友好的中文提示
- */
- private translateErrorMessage(detail: string, statusCode: number): string {
- // 注册相关错误
- if (detail === 'Username already exists') {
- return '该用户名已被注册,请更换用户名';
- }
- if (detail === 'Email already exists') {
- return '该邮箱已被注册,请更换邮箱或使用该邮箱登录';
- }
-
- // 登录相关错误
- if (detail === 'Invalid username or password') {
- return '用户名/手机号或者密码错误';
- }
-
- // 权限相关错误
- if (statusCode === 401) {
- return '登录已过期,请重新登录';
- }
- if (statusCode === 403) {
- return '没有权限执行此操作';
- }
- if (statusCode === 404) {
- return '请求的资源不存在';
- }
- if (statusCode === 409) {
- return '数据冲突,请检查您的输入';
- }
-
- // 服务器错误
- if (statusCode >= 500) {
- return '服务器繁忙,请稍后重试';
- }
-
- // 如果有detail且不是英文技术信息,直接返回
- if (detail && !/^[A-Z]/.test(detail)) {
- return detail;
- }
-
- return '请求失败,请稍后重试';
- }
- /**
- * 用户注册
- * POST /api/auth/register
- */
- async register(registerData: RegisterRequest, keyId: string | null = null): Promise<User> {
- const body = {
- ...registerData,
- // encrypted字段不需要传,后端默认为true
- ...(keyId && { key_id: keyId })
- };
-
- const response = await fetch(`${this.baseUrl}/auth/register`, {
- method: 'POST',
- headers: this.getHeaders(false),
- body: JSON.stringify(body),
- });
- return this.handleResponse<User>(response);
- }
- /**
- * 用户登录
- * POST /api/auth/login
- */
- async login(username: string, password: string, keyId: string | null = null): Promise<LoginResponse> {
- const body: any = {
- username,
- password
- // encrypted字段不需要传,后端默认为true
- };
-
- if (keyId) {
- body.key_id = keyId;
- }
- const response = await fetch(`${this.baseUrl}/auth/login`, {
- method: 'POST',
- headers: this.getHeaders(false),
- body: JSON.stringify(body),
- });
- return this.handleResponse<LoginResponse>(response);
- }
- /**
- * 获取当前用户信息
- * GET /api/users/me
- */
- async getCurrentUser(): Promise<User> {
- const response = await fetch(`${this.baseUrl}/users/me`, {
- method: 'GET',
- headers: this.getHeaders(true),
- });
- return this.handleResponse<User>(response);
- }
- /**
- * 更新当前用户信息
- * PUT /api/users/me
- */
- async updateCurrentUser(updateData: UpdateUserRequest): Promise<User> {
- const response = await fetch(`${this.baseUrl}/users/me`, {
- method: 'PUT',
- headers: this.getHeaders(true),
- body: JSON.stringify(updateData),
- });
- return this.handleResponse<User>(response);
- }
- /**
- * 删除当前用户(申请注销)
- * DELETE /api/users/me
- */
- async deleteCurrentUser(): Promise<{ message: string }> {
- const response = await fetch(`${this.baseUrl}/users/me`, {
- method: 'DELETE',
- headers: this.getHeaders(true),
- });
- return this.handleResponse<{ message: string }>(response);
- }
- /**
- * 验证Token是否有效
- * GET /api/auth/verify
- */
- async verifyToken(): Promise<TokenVerifyResponse> {
- const response = await fetch(`${this.baseUrl}/auth/verify`, {
- method: 'GET',
- headers: this.getHeaders(true),
- });
- return this.handleResponse<TokenVerifyResponse>(response);
- }
- /**
- * 刷新Token
- * POST /api/auth/refresh
- */
- async refreshToken(): Promise<LoginResponse> {
- const response = await fetch(`${this.baseUrl}/auth/refresh`, {
- method: 'POST',
- headers: this.getHeaders(true),
- });
- return this.handleResponse<LoginResponse>(response);
- }
- /**
- * 用户登出
- * POST /api/auth/logout
- */
- async logout(): Promise<LogoutResponse> {
- const response = await fetch(`${this.baseUrl}/auth/logout`, {
- method: 'POST',
- headers: this.getHeaders(true),
- });
- return this.handleResponse<LogoutResponse>(response);
- }
- /**
- * 验证验证码(不删除,供两步流程使用)
- */
- async verifySmsCode(phone: string, sms_code: string): Promise<void> {
- const response = await fetch(`${API_BASE_URL}/api/sms/verify-code`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, sms_code }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '验证码错误');
- }
- }
- /**
- * 发送短信验证码
- */
- async sendSmsCode(phone: string, scene: string = 'register'): Promise<void> {
- const response = await fetch(`${API_BASE_URL}/api/sms/send-code`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, scene }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '发送失败,请稍后重试');
- }
- }
- /**
- * 手机号+验证码登录
- */
- async loginByPhone(phone: string, sms_code: string): Promise<LoginResponse> {
- const response = await fetch(`${this.baseUrl}/auth/login/phone`, {
- method: 'POST',
- headers: this.getHeaders(false),
- body: JSON.stringify({ phone, sms_code }),
- });
- return this.handleResponse<LoginResponse>(response);
- }
- /**
- * 手机验证码修改密码
- */
- async resetPasswordByPhone(phone: string, sms_code: string, new_password: string): Promise<void> {
- const response = await fetch(`${this.baseUrl}/auth/reset-password/phone`, {
- method: 'POST',
- headers: this.getHeaders(false),
- body: JSON.stringify({ phone, sms_code, new_password }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '修改失败');
- }
- }
- }
- // 导出单例
- export const userApi = new UserApiService();
|