113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import { getApiConfig, isCurrentApiConfig } from '../config/api';
|
|
|
|
export class ApiRequestError extends Error {
|
|
constructor(
|
|
message: string,
|
|
readonly statusCode = 0,
|
|
readonly reason = '',
|
|
) {
|
|
super(message);
|
|
this.name = 'ApiRequestError';
|
|
}
|
|
}
|
|
|
|
export interface RequestOptions {
|
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
data?: Record<string, unknown>;
|
|
header?: Record<string, string>;
|
|
timeout?: number;
|
|
upload?: { filePath: string; uploadId: string };
|
|
}
|
|
|
|
/** 底层只发请求,不触发登录;登录接口与鉴权包装共用,避免循环依赖。 */
|
|
export function requestApi<T>(
|
|
path: string,
|
|
options: RequestOptions = {},
|
|
config = getApiConfig(),
|
|
): Promise<T> {
|
|
if (!path.startsWith('/') || path.startsWith('//'))
|
|
throw new Error('接口路径不正确');
|
|
return new Promise((resolve, reject) => {
|
|
// 公开请求、上传和登录共用检查,旧环境的响应不进入上层业务回调。
|
|
const stale = () => {
|
|
if (isCurrentApiConfig(config)) return false;
|
|
reject(new ApiRequestError('接口环境已变化,请重新操作'));
|
|
return true;
|
|
};
|
|
if (stale()) return;
|
|
const success = (res: { statusCode: number; data: unknown }) => {
|
|
if (stale()) return;
|
|
const body = res.data as {
|
|
code?: number;
|
|
data?: T;
|
|
reason?: string;
|
|
} | null;
|
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
reject(
|
|
new ApiRequestError(
|
|
res.statusCode === 404
|
|
? '内容暂不可用'
|
|
: '接口请求失败,请稍后重试',
|
|
res.statusCode,
|
|
typeof body?.reason === 'string' &&
|
|
/^[A-Z_]{1,64}$/.test(body.reason)
|
|
? body.reason
|
|
: '',
|
|
),
|
|
);
|
|
} else if (
|
|
!body ||
|
|
body.code !== 0 ||
|
|
!Object.prototype.hasOwnProperty.call(body, 'data')
|
|
) {
|
|
reject(
|
|
new ApiRequestError(
|
|
'接口返回异常,请稍后重试',
|
|
res.statusCode,
|
|
),
|
|
);
|
|
} else resolve(body.data as T);
|
|
};
|
|
const fail = () => {
|
|
if (!stale())
|
|
reject(
|
|
new ApiRequestError('连接失败,请确认后端已启动及网络配置'),
|
|
);
|
|
};
|
|
if (options.upload) {
|
|
// wx.uploadFile 由运行时生成 multipart boundary,不能自行设置 JSON Content-Type。
|
|
wx.uploadFile({
|
|
url: `${config.baseUrl}${path}`,
|
|
filePath: options.upload.filePath,
|
|
name: 'file',
|
|
formData: { uploadId: options.upload.uploadId },
|
|
header: options.header,
|
|
timeout: 60000,
|
|
success(res) {
|
|
let data: unknown;
|
|
try {
|
|
data = JSON.parse(res.data);
|
|
} catch {
|
|
reject(new ApiRequestError('上传响应格式异常'));
|
|
return;
|
|
}
|
|
success({ statusCode: res.statusCode, data });
|
|
},
|
|
fail,
|
|
});
|
|
} else
|
|
wx.request({
|
|
url: `${config.baseUrl}${path}`,
|
|
method: options.method || 'GET',
|
|
data: options.data,
|
|
header: {
|
|
'Content-Type': 'application/json',
|
|
...options.header,
|
|
},
|
|
timeout: options.timeout ?? config.timeout,
|
|
success,
|
|
fail,
|
|
});
|
|
});
|
|
}
|