文件
OmniCard/mpuniapp/common/request.uts

212 行
5.9 KiB
Plaintext

/**
* OmniCard AI (CRMmp) - 用户端网络请求底层封装与统一拦截器
* 严格遵循 AGENTS.md 规范与 uts 强类型规范,一套代码兼顾 App 与 Web/H5
*/
import { ApiResponse } from './types.uts';
let currentBaseUrl: string = 'http://127.0.0.1:9000';
// #ifdef H5
if (typeof window !== 'undefined' && window.location != null) {
const protocol = window.location.protocol != null && window.location.protocol !== '' ? window.location.protocol : 'http:';
const hostname = window.location.hostname != null && window.location.hostname !== '' ? window.location.hostname : '127.0.0.1';
currentBaseUrl = `${protocol}//${hostname}:9000`;
}
// #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<string, any>;
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<T>(config: RequestConfig): Promise<T> {
return new Promise<T>((resolve, reject) => {
const method = config.method || 'GET';
const fullUrl = buildFullUrl(config.url);
const timeout = config.timeout != null ? config.timeout : getRequestTimeout();
// 默认请求头配置
const headers: Record<string, any> = {
'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<T>;
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<T>(url: string, data?: any, header?: Record<string, any>): Promise<T> {
return request<T>({ url, method: 'GET', data, header });
}
export function httpPost<T>(url: string, data?: any, header?: Record<string, any>): Promise<T> {
return request<T>({ url, method: 'POST', data, header });
}
export function httpPut<T>(url: string, data?: any, header?: Record<string, any>): Promise<T> {
return request<T>({ url, method: 'PUT', data, header });
}
export function httpDelete<T>(url: string, data?: any, header?: Record<string, any>): Promise<T> {
return request<T>({ url, method: 'DELETE', data, header });
}