| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- 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;
- real_name?: string | null;
- is_verified?: string;
- verified_at?: 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;
- email_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 VerifyUserRequest {
- real_name: string;
- id_card: 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 {
- // ── 精确英文 detail 映射 ──────────────────────────────────────────
- const exactMap: Record<string, string> = {
- 'Username already exists': '该用户名已被注册,请更换用户名',
- 'Email already exists': '该邮箱已被注册,请更换邮箱或使用该邮箱登录',
- 'Invalid username or password': '用户名/手机号或者密码错误',
- 'User not found': '用户不存在',
- 'Authentication required': '请先登录',
- '该邮箱未绑定任何账户': '该邮箱未绑定任何账户,请先注册或绑定邮箱',
- }
- if (exactMap[detail]) return exactMap[detail];
- // ── 中文 detail 直接透传(后端已是中文的直接用)──────────────────
- if (detail && !/^[A-Za-z]/.test(detail)) return detail;
- // ── 按状态码兜底 ─────────────────────────────────────────────────
- if (statusCode === 400) return detail || '请求参数有误,请检查后重试';
- if (statusCode === 401) return '登录已过期,请重新登录';
- if (statusCode === 403) return detail || '没有权限执行此操作';
- if (statusCode === 404) return detail || '请求的资源不存在';
- if (statusCode === 409) return detail || '数据冲突,请检查您的输入';
- if (statusCode === 429) return detail || '操作太频繁,请稍后再试';
- if (statusCode >= 500) return '服务器繁忙,请稍后重试';
- return detail || '请求失败,请稍后重试';
- }
- /**
- * 用户注册
- * 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);
- }
- /**
- * 获取RSA公钥
- * GET /api/users/rsa-public-key
- */
- async getRSAPublicKey(): Promise<{ public_key: string }> {
- const response = await fetch(`${this.baseUrl}/users/rsa-public-key`, {
- method: 'GET',
- headers: this.getHeaders(false),
- });
- return this.handleResponse<{ public_key: string }>(response);
- }
- /**
- * 提交实名认证(发送加密数据)
- * POST /api/users/me/verify
- */
- async submitVerification(encryptedData: string): Promise<User> {
- const response = await fetch(`${this.baseUrl}/users/me/verify`, {
- method: 'POST',
- headers: this.getHeaders(true),
- body: JSON.stringify({ encrypted_data: encryptedData }),
- });
- return this.handleResponse<User>(response);
- }
- /**
- * 获取公开配置
- * GET /api/users/config/public
- */
- async getPublicConfig(): Promise<{ enable_verification_reminder: boolean }> {
- const response = await fetch(`${this.baseUrl}/users/config/public`, {
- method: 'GET',
- headers: this.getHeaders(false),
- });
- return this.handleResponse<{ enable_verification_reminder: boolean }>(response);
- }
- /** 发送短信验证码 */
- 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 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 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 || '修改失败');
- }
- }
- /** 发送邮箱验证码 */
- async sendEmailCode(email: string, scene: string = 'register'): Promise<void> {
- const response = await fetch(`${API_BASE_URL}/api/email/send-code`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ email, scene }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '发送失败,请稍后重试');
- }
- }
- /** 验证邮箱验证码(不删除,供两步流程第一步使用) */
- async verifyEmailCode(email: string, email_code: string): Promise<void> {
- const response = await fetch(`${API_BASE_URL}/api/email/verify-code`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ email, email_code }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '验证码错误');
- }
- }
- /** 邮箱验证码修改密码 */
- async resetPasswordByEmail(email: string, email_code: string, new_password: string): Promise<void> {
- const response = await fetch(`${this.baseUrl}/auth/reset-password/email`, {
- method: 'POST',
- headers: this.getHeaders(false),
- body: JSON.stringify({ email, email_code, new_password }),
- });
- if (!response.ok) {
- const err = await response.json().catch(() => ({}));
- throw new Error(err.detail || '修改失败');
- }
- }
- }
- // 导出单例
- export const userApi = new UserApiService();
|