http-client.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * 集成测试 HTTP 客户端
  3. * 使用 axios 替代 Playwright 的 APIRequestContext,确保可在 Vitest 中运行
  4. */
  5. import axios, { AxiosInstance, AxiosResponse } from 'axios';
  6. const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
  7. let httpClient: AxiosInstance;
  8. let authToken: string | null = null;
  9. /** 初始化 HTTP 客户端 */
  10. export function initHttpClient(): void {
  11. httpClient = axios.create({
  12. baseURL: BASE_URL,
  13. timeout: 30000,
  14. headers: { 'Content-Type': 'application/json' },
  15. });
  16. }
  17. /** 释放 HTTP 客户端 */
  18. export function disposeHttpClient(): void {
  19. httpClient = null as any;
  20. }
  21. /** 设置认证 token */
  22. export function setAuthToken(token: string): void {
  23. authToken = token;
  24. }
  25. /** 获取当前 token */
  26. export function getAuthToken(): string | null {
  27. return authToken;
  28. }
  29. /** API 响应类型 */
  30. export interface ApiResponse {
  31. code: number;
  32. message: string;
  33. data: any;
  34. }
  35. /** 解析 API 响应 */
  36. function parseResponse(response: AxiosResponse): ApiResponse {
  37. const body = response.data;
  38. if (body && typeof body === 'object' && body.code !== undefined) {
  39. return { code: body.code, message: body.message ?? '', data: body.data };
  40. }
  41. if (body && typeof body === 'object' && body.success === true) {
  42. return { code: 0, message: '', data: body.data };
  43. }
  44. return { code: response.status, message: '', data: body };
  45. }
  46. /** GET 请求 */
  47. export async function apiGet(path: string, params?: Record<string, string>): Promise<ApiResponse> {
  48. const config: any = {};
  49. if (authToken) {
  50. config.headers = { Authorization: `Bearer ${authToken}` };
  51. }
  52. if (params) {
  53. config.params = params;
  54. }
  55. try {
  56. const response = await httpClient.get(path, config);
  57. return parseResponse(response);
  58. } catch (error: any) {
  59. if (error.response) {
  60. return parseResponse(error.response);
  61. }
  62. return { code: -1, message: error.message, data: null };
  63. }
  64. }
  65. /** POST 请求 */
  66. export async function apiPost(path: string, data?: any): Promise<ApiResponse> {
  67. const config: any = {};
  68. if (authToken) {
  69. config.headers = { Authorization: `Bearer ${authToken}` };
  70. }
  71. try {
  72. const response = await httpClient.post(path, data, config);
  73. return parseResponse(response);
  74. } catch (error: any) {
  75. if (error.response) {
  76. return parseResponse(error.response);
  77. }
  78. return { code: -1, message: error.message, data: null };
  79. }
  80. }
  81. /** PUT 请求 */
  82. export async function apiPut(path: string, data?: any): Promise<ApiResponse> {
  83. const config: any = {};
  84. if (authToken) {
  85. config.headers = { Authorization: `Bearer ${authToken}` };
  86. }
  87. try {
  88. const response = await httpClient.put(path, data, config);
  89. return parseResponse(response);
  90. } catch (error: any) {
  91. if (error.response) {
  92. return parseResponse(error.response);
  93. }
  94. return { code: -1, message: error.message, data: null };
  95. }
  96. }
  97. /** DELETE 请求 */
  98. export async function apiDelete(path: string): Promise<ApiResponse> {
  99. const config: any = {};
  100. if (authToken) {
  101. config.headers = { Authorization: `Bearer ${authToken}` };
  102. }
  103. try {
  104. const response = await httpClient.delete(path, config);
  105. return parseResponse(response);
  106. } catch (error: any) {
  107. if (error.response) {
  108. return parseResponse(error.response);
  109. }
  110. return { code: -1, message: error.message, data: null };
  111. }
  112. }
  113. /** 创建测试用户(集成测试专用) */
  114. export async function createTestUser(): Promise<{ token: string; userId: string }> {
  115. initHttpClient();
  116. const phone = `138${String(Date.now()).slice(-8)}`;
  117. await apiPost('/api/auth/send-code', { phone });
  118. const loginRes = await apiPost('/api/auth/login', { phone, code: '123456' });
  119. if (loginRes.code !== 0 || !loginRes.data?.token) {
  120. throw new Error('创建测试用户失败');
  121. }
  122. setAuthToken(loginRes.data.token);
  123. return { token: loginRes.data.token, userId: loginRes.data.userId || '1' };
  124. }