/** * OmniCard AI (CRMmp) - 用户端网络请求底层封装与统一拦截器 * 严格遵循 AGENTS.md 规范与 uts 强类型规范,一套代码兼顾 App 与 Web/H5 */ import { ApiResponse } from './types.uts'; let currentBaseUrl: string = 'http://127.0.0.1:9000/api'; // #ifdef H5 currentBaseUrl = '/api'; // #endif let currentTimeout: number = 10000; export function getBaseUrl(): string { return currentBaseUrl; } export function setBaseUrl(url: string): void { currentBaseUrl = url; } export function getRequestTimeout(): number { return currentTimeout; } export function setRequestTimeout(timeout: number): void { currentTimeout = timeout; } export function getAuthToken(): string { try { const val = uni.getStorageSync('token'); return val != null ? String(val) : ''; } catch (e) { return ''; } } export function setAuthToken(token: string): void { try { uni.setStorageSync('token', token); } catch (e) {} } export function clearAuthToken(): void { try { uni.removeStorageSync('token'); } catch (e) {} } export function getTenantId(): string { try { const val = uni.getStorageSync('tenant_id'); return val != null ? String(val) : ''; } catch (e) { return ''; } } export function setTenantId(tenantId: string): void { try { uni.setStorageSync('tenant_id', tenantId); } catch (e) {} } export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; export interface RequestConfig { url: string; method?: RequestMethod; data?: any; header?: Record; timeout?: number; } /** * 构建完整的请求 URL */ function buildFullUrl(url: string): string { if (url.startsWith('http://') || url.startsWith('https://')) { return url; } const base = getBaseUrl().replace(/\/+$/, ''); const path = url.startsWith('/') ? url : `/${url}`; return `${base}${path}`; } /** * 统一处理未授权 / 401 状态 */ function handleUnauthorized(message: string): void { clearAuthToken(); uni.showToast({ title: message || '登录状态已失效,请重新登录', icon: 'none', }); uni.reLaunch({ url: '/pages/common/login', }); } /** * 网络请求底层统一封装 */ export function request(config: RequestConfig): Promise { return new Promise((resolve, reject) => { const method = config.method || 'GET'; const fullUrl = buildFullUrl(config.url); const timeout = config.timeout != null ? config.timeout : getRequestTimeout(); // 默认请求头配置 const headers: Record = { 'content-type': 'application/json', }; // 自动挂载 Authorization 与 x-tenant-id const token = getAuthToken(); if (token) { headers['Authorization'] = token.startsWith('Bearer ') ? token : `Bearer ${token}`; } const tenantId = getTenantId(); if (tenantId) { headers['x-tenant-id'] = tenantId; } // 显式传入的 header 具有更高优先级 if (config.header) { Object.assign(headers, config.header); } uni.request({ url: fullUrl, method: method as any, data: config.data, header: headers, timeout: timeout, success: (res: any) => { const statusCode = res.statusCode != null ? res.statusCode : 200; const resBody = res.data; // 1. 处理 HTTP 401 或业务 code 401 if (statusCode === 401 || (resBody != null && resBody.code === 401)) { const msg = (resBody && resBody.message) ? resBody.message : '登录状态已失效,请重新登录'; handleUnauthorized(msg); reject(new Error(msg)); return; } // 2. 处理非 200 的 HTTP 异常状态 if (statusCode < 200 || statusCode >= 300) { const errorMsg = (resBody && resBody.message) ? resBody.message : `网络请求错误 (${statusCode})`; uni.showToast({ title: errorMsg, icon: 'none', }); reject(new Error(errorMsg)); return; } // 3. 处理 Cool-Admin 标准业务状态 (code === 1000 为成功) if (resBody != null && typeof resBody === 'object' && 'code' in resBody) { const apiRes = resBody as ApiResponse; if (apiRes.code === 1000) { resolve(apiRes.data !== undefined ? apiRes.data : (apiRes as unknown as T)); } else { const failMsg = apiRes.message || '请求处理失败'; uni.showToast({ title: failMsg, icon: 'none', }); reject(new Error(failMsg)); } return; } // 4. 其它直接返回的数据格式 resolve(resBody as T); }, fail: (err: any) => { const errMsg = (err && err.errMsg) ? err.errMsg : '网络连接异常,请重试'; uni.showToast({ title: '网络连接异常,请重试', icon: 'none', }); reject(new Error(errMsg)); }, }); }); } export function httpGet(url: string, data?: any, header?: Record): Promise { return request({ url, method: 'GET', data, header }); } export function httpPost(url: string, data?: any, header?: Record): Promise { return request({ url, method: 'POST', data, header }); } export function httpPut(url: string, data?: any, header?: Record): Promise { return request({ url, method: 'PUT', data, header }); } export function httpDelete(url: string, data?: any, header?: Record): Promise { return request({ url, method: 'DELETE', data, header }); }