http.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // HTTP 请求封装
  2. import { router } from 'expo-router';
  3. import { Alert, Platform } from 'react-native';
  4. import { COMMON_HEADER, SERVICE_URL } from './config';
  5. export const SUCCESS_CODE = 0;
  6. export const SUCCESS_CODE_200 = 200;
  7. export const AUTH_INVALID = 401;
  8. export const AUTH_INVALID_2 = 403;
  9. interface RequestOptions {
  10. loading?: boolean;
  11. showMsg?: boolean;
  12. method?: 'GET' | 'POST';
  13. token?: string;
  14. silent?: boolean; // 静默模式,不显示错误提示
  15. }
  16. interface ApiResponse<T = any> {
  17. code: number;
  18. msg: string;
  19. data: T;
  20. success: boolean;
  21. count?: number;
  22. }
  23. // 存储 token
  24. let authToken: string | null = null;
  25. export const setToken = (token: string | null) => {
  26. authToken = token;
  27. };
  28. export const getToken = () => authToken;
  29. export const clearToken = async () => {
  30. authToken = null;
  31. };
  32. // 处理认证失败
  33. const handleAuthError = () => {
  34. setToken(null);
  35. // 跳转到登录页
  36. router.push('/login');
  37. };
  38. // 检查是否认证失败
  39. const isAuthError = (code: number | string) => {
  40. const numCode = Number(code);
  41. return numCode === AUTH_INVALID || numCode === AUTH_INVALID_2;
  42. };
  43. // 检查是否成功的 code
  44. const isSuccessCode = (code: number | string) => {
  45. const numCode = Number(code);
  46. return numCode === SUCCESS_CODE || numCode === SUCCESS_CODE_200;
  47. };
  48. // 显示错误提示(兼容 Web 和 Native)
  49. const showErrorMessage = (msg: string) => {
  50. const message = msg || '请求失败,请稍后重试';
  51. if (Platform.OS === 'web') {
  52. // Web 环境使用 window.alert
  53. window.alert(message);
  54. } else {
  55. // Native 环境使用 Alert.alert
  56. Alert.alert('提示', message);
  57. }
  58. };
  59. // 基础请求方法
  60. export const request = async <T = any>(
  61. url: string,
  62. data: any = {},
  63. options: RequestOptions = {}
  64. ): Promise<ApiResponse<T>> => {
  65. const { method = 'POST', silent = false } = options;
  66. const headers: Record<string, string> = {
  67. 'Content-Type': 'application/json',
  68. track: JSON.stringify(COMMON_HEADER),
  69. };
  70. if (authToken) {
  71. headers.Authentication = authToken;
  72. }
  73. const fullUrl = url.startsWith('http') ? url : `${SERVICE_URL}${url}`;
  74. try {
  75. const config: RequestInit = {
  76. method,
  77. headers,
  78. };
  79. if (method === 'GET') {
  80. const params = new URLSearchParams();
  81. Object.keys(data).forEach((key) => {
  82. if (data[key] !== undefined && data[key] !== null) {
  83. params.append(key, String(data[key]));
  84. }
  85. });
  86. const queryString = params.toString();
  87. const requestUrl = queryString ? `${fullUrl}?${queryString}` : fullUrl;
  88. const response = await fetch(requestUrl, config);
  89. const result = await response.json();
  90. // 处理 401/403 认证失败(不显示错误提示,直接跳转登录)
  91. if (isAuthError(result.code)) {
  92. handleAuthError();
  93. return {
  94. code: result.code,
  95. msg: '登录已过期,请重新登录',
  96. data: null as any,
  97. success: false,
  98. };
  99. }
  100. const success = isSuccessCode(result.code);
  101. // 非成功状态且非静默模式,显示错误提示
  102. if (!success && !silent && result.msg) {
  103. showErrorMessage(result.msg);
  104. }
  105. return {
  106. ...result,
  107. success,
  108. };
  109. } else {
  110. config.body = JSON.stringify(data);
  111. const response = await fetch(fullUrl, config);
  112. const result = await response.json();
  113. // 打印接口返回内容
  114. console.log('[HTTP Response]', fullUrl, result);
  115. // 处理 401/403 认证失败(不显示错误提示,直接跳转登录)
  116. if (isAuthError(result.code)) {
  117. handleAuthError();
  118. return {
  119. code: result.code,
  120. msg: '登录已过期,请重新登录',
  121. data: null as any,
  122. success: false,
  123. };
  124. }
  125. const success = isSuccessCode(result.code);
  126. // 非成功状态且非静默模式,显示错误提示
  127. if (!success && !silent && result.msg) {
  128. console.log('[HTTP Error]', fullUrl, 'msg:', result.msg, 'silent:', silent);
  129. showErrorMessage(result.msg);
  130. }
  131. return {
  132. ...result,
  133. success,
  134. };
  135. }
  136. } catch (error) {
  137. console.error('Request error:', error);
  138. if (!silent) {
  139. showErrorMessage('网络异常,请检查网络连接');
  140. }
  141. return {
  142. code: -1,
  143. msg: '网络异常',
  144. data: null as any,
  145. success: false,
  146. };
  147. }
  148. };
  149. // GET 请求
  150. export const get = <T = any>(url: string, params: any = {}, options: RequestOptions = {}) => {
  151. return request<T>(url, params, { ...options, method: 'GET' });
  152. };
  153. // POST 请求
  154. export const post = <T = any>(url: string, data: any = {}, options: RequestOptions = {}) => {
  155. return request<T>(url, data, { ...options, method: 'POST' });
  156. };
  157. // 带 loading 的 GET 请求
  158. export const getL = <T = any>(url: string, params: any = {}, options: RequestOptions = {}) => {
  159. return get<T>(url, params, { ...options, loading: true });
  160. };
  161. // 带 loading 的 POST 请求
  162. export const postL = <T = any>(url: string, data: any = {}, options: RequestOptions = {}) => {
  163. return post<T>(url, data, { ...options, loading: true });
  164. };