2026-01-20 19:36:49 +08:00
|
|
|
|
import { loading, hideLoading } from "./widget";
|
2026-09-22 17:07:16 +08:00
|
|
|
|
import { buildApiUrl, getDefaultUploadUrl, isSh6CorpId, normalizeCorpId, resolveApiContext } from "./api-base-config";
|
2026-01-20 19:36:49 +08:00
|
|
|
|
|
|
|
|
|
|
const defaultOptions = {
|
|
|
|
|
|
header: {
|
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
|
},
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
timeout: 1000 * 60, // 请求超时设置为60秒
|
|
|
|
|
|
};
|
|
|
|
|
|
const tasks = [];
|
2026-09-22 17:07:16 +08:00
|
|
|
|
let miniappSessionRefresher = null;
|
|
|
|
|
|
let miniappSessionRefreshPromise = null;
|
|
|
|
|
|
|
|
|
|
|
|
export function setMiniappSessionRefresher(refresher) {
|
|
|
|
|
|
miniappSessionRefresher = typeof refresher === "function" ? refresher : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function refreshMiniappSession() {
|
|
|
|
|
|
if (!miniappSessionRefresher) return false;
|
|
|
|
|
|
if (!miniappSessionRefreshPromise) {
|
|
|
|
|
|
miniappSessionRefreshPromise = Promise.resolve()
|
|
|
|
|
|
.then(() => miniappSessionRefresher())
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
miniappSessionRefreshPromise = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
await miniappSessionRefreshPromise;
|
|
|
|
|
|
return Boolean(uni.getStorageSync("miniappSessionToken"));
|
|
|
|
|
|
}
|
2026-01-20 19:36:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 是否正在刷新 token
|
|
|
|
|
|
let isRefreshing = false;
|
|
|
|
|
|
// 重试队列
|
|
|
|
|
|
let retryQueue = [];
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理重试队列
|
|
|
|
|
|
* @param {String} token - 新的 token
|
|
|
|
|
|
*/
|
|
|
|
|
|
function processQueue(token) {
|
|
|
|
|
|
retryQueue.forEach(({ resolve, reject, config }) => {
|
|
|
|
|
|
if (token) {
|
|
|
|
|
|
// 更新 token 并重试请求
|
|
|
|
|
|
if (!config.header) config.header = {};
|
|
|
|
|
|
config.header.Authorization = `Bearer ${token}`;
|
|
|
|
|
|
uni.request({
|
|
|
|
|
|
...config,
|
|
|
|
|
|
success: (res) => resolve(res),
|
|
|
|
|
|
fail: (err) => reject(err),
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(new Error("Token 刷新失败"));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
retryQueue = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 刷新 token
|
|
|
|
|
|
* @returns {Promise<String|null>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function refreshAccessToken() {
|
|
|
|
|
|
const refreshToken = uni.getStorageSync("refreshToken");
|
|
|
|
|
|
if (!refreshToken) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isRefreshing) {
|
|
|
|
|
|
// 等待刷新完成
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
|
|
|
|
return uni.getStorageSync("accessToken");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isRefreshing = true;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await uni.request({
|
2026-06-03 10:07:43 +08:00
|
|
|
|
url: buildApiUrl("/auth/refresh"),
|
2026-01-20 19:36:49 +08:00
|
|
|
|
method: "POST",
|
|
|
|
|
|
header: {
|
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
|
},
|
|
|
|
|
|
data: {
|
|
|
|
|
|
refreshToken,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (res && res.data && res.data.success && res.data.data) {
|
|
|
|
|
|
const newToken = res.data.data.accessToken;
|
|
|
|
|
|
uni.setStorageSync("accessToken", newToken);
|
|
|
|
|
|
processQueue(newToken);
|
|
|
|
|
|
return newToken;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 刷新失败,清空队列
|
|
|
|
|
|
processQueue(null);
|
|
|
|
|
|
// 清除登录状态
|
|
|
|
|
|
uni.removeStorageSync("accessToken");
|
|
|
|
|
|
uni.removeStorageSync("refreshToken");
|
|
|
|
|
|
uni.removeStorageSync("jwtUserInfo");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("Token 刷新异常:", error);
|
|
|
|
|
|
processQueue(null);
|
|
|
|
|
|
uni.removeStorageSync("accessToken");
|
|
|
|
|
|
uni.removeStorageSync("refreshToken");
|
|
|
|
|
|
uni.removeStorageSync("jwtUserInfo");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
isRefreshing = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const request = async (options = {}, showLoading = true) => {
|
2026-09-22 17:07:16 +08:00
|
|
|
|
const sessionRetried = options.__miniappSessionRetried === true;
|
|
|
|
|
|
const requestOptions = { ...options };
|
|
|
|
|
|
delete requestOptions.__miniappSessionRetried;
|
2026-01-20 19:36:49 +08:00
|
|
|
|
// 合并用户传入的配置和默认配置
|
2026-09-22 17:07:16 +08:00
|
|
|
|
if (!requestOptions.data) requestOptions.data = {};
|
|
|
|
|
|
if (requestOptions.data.corpId) {
|
|
|
|
|
|
requestOptions.data = {
|
|
|
|
|
|
...requestOptions.data,
|
|
|
|
|
|
corpId: normalizeCorpId(requestOptions.data.corpId),
|
2026-06-11 18:13:49 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
2026-09-22 17:07:16 +08:00
|
|
|
|
const requestCorpId = requestOptions.data.corpId || resolveApiContext().corpId;
|
|
|
|
|
|
const requiresMiniappSession = isSh6CorpId(requestCorpId);
|
2026-01-20 19:36:49 +08:00
|
|
|
|
|
|
|
|
|
|
const config = {
|
|
|
|
|
|
...defaultOptions,
|
2026-09-22 17:07:16 +08:00
|
|
|
|
...requestOptions,
|
|
|
|
|
|
url: buildApiUrl(requestOptions.url, requestOptions.data.corpId),
|
2026-01-20 19:36:49 +08:00
|
|
|
|
};
|
2026-09-22 17:07:16 +08:00
|
|
|
|
const sessionToken = uni.getStorageSync("miniappSessionToken");
|
|
|
|
|
|
if (requiresMiniappSession && sessionToken) {
|
|
|
|
|
|
config.header = { ...(config.header || {}), "X-Miniapp-Session": sessionToken };
|
|
|
|
|
|
}
|
2026-01-20 19:36:49 +08:00
|
|
|
|
|
|
|
|
|
|
const key = `${JSON.stringify(config)}_${Date.now()}`;
|
|
|
|
|
|
if (showLoading) {
|
|
|
|
|
|
recordTask(key)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 发起请求
|
|
|
|
|
|
const res = await uni.request(config);
|
|
|
|
|
|
if (showLoading) {
|
|
|
|
|
|
removeTask(key)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-22 17:07:16 +08:00
|
|
|
|
if (requiresMiniappSession && res.statusCode === 401) {
|
|
|
|
|
|
uni.removeStorageSync("miniappSessionToken");
|
|
|
|
|
|
uni.removeStorageSync("miniappSessionExpiresAt");
|
|
|
|
|
|
if (
|
|
|
|
|
|
!sessionRetried &&
|
|
|
|
|
|
requestOptions.data.type !== "wxAppLogin" &&
|
|
|
|
|
|
await refreshMiniappSession()
|
|
|
|
|
|
) {
|
|
|
|
|
|
return request({ ...requestOptions, __miniappSessionRetried: true }, showLoading);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-20 19:36:49 +08:00
|
|
|
|
// 如果返回 401,尝试刷新 token
|
|
|
|
|
|
// if (res.statusCode === 401) {
|
|
|
|
|
|
// if (showLoading) {
|
|
|
|
|
|
// removeTask(key)
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// // 尝试刷新 token
|
|
|
|
|
|
// const newToken = await refreshAccessToken();
|
|
|
|
|
|
|
|
|
|
|
|
// if (newToken) {
|
|
|
|
|
|
// // 刷新成功,重试原请求
|
|
|
|
|
|
// if (!config.header) config.header = {};
|
|
|
|
|
|
// config.header.Authorization = `Bearer ${newToken}`;
|
|
|
|
|
|
|
|
|
|
|
|
// if (showLoading) {
|
|
|
|
|
|
// recordTask(key)
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// const retryRes = await uni.request(config);
|
|
|
|
|
|
|
|
|
|
|
|
// if (showLoading) {
|
|
|
|
|
|
// removeTask(key)
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// const success = retryRes && retryRes.data && retryRes.data.success === true;
|
|
|
|
|
|
// if (success) {
|
|
|
|
|
|
// return retryRes.data;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return {
|
|
|
|
|
|
// success: false,
|
|
|
|
|
|
// message: retryRes.data && retryRes.data.message ? retryRes.data.message : "请求失败",
|
|
|
|
|
|
// };
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// return {
|
|
|
|
|
|
// success: false,
|
|
|
|
|
|
// message: "登录已过期,请重新登录",
|
|
|
|
|
|
// };
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
const success = res && res.data && res.data.success === true;
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
return res.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
return {
|
2026-08-31 09:14:04 +08:00
|
|
|
|
...(res && res.data && typeof res.data === "object" ? res.data : {}),
|
2026-01-20 19:36:49 +08:00
|
|
|
|
success: false,
|
|
|
|
|
|
message: res.data && res.data.message ? res.data.message : "请求失败",
|
|
|
|
|
|
};
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.log("error:", error);
|
|
|
|
|
|
if (showLoading) {
|
|
|
|
|
|
removeTask(key)
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function recordTask(task) {
|
|
|
|
|
|
if (tasks.length === 0) {
|
|
|
|
|
|
loading("请求中...");
|
|
|
|
|
|
}
|
|
|
|
|
|
tasks.push(task);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeTask(task) {
|
|
|
|
|
|
const index = tasks.findIndex((i) => i === task);
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
tasks.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tasks.length === 0) {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default request;
|
|
|
|
|
|
|
2026-06-03 10:07:43 +08:00
|
|
|
|
export const uploadUrl = getDefaultUploadUrl();
|
2026-01-20 19:36:49 +08:00
|
|
|
|
|
2026-06-03 10:07:43 +08:00
|
|
|
|
export function getFullPath(path, corpId) {
|
2026-06-11 18:13:49 +08:00
|
|
|
|
return buildApiUrl(path, normalizeCorpId(corpId));
|
2026-01-20 19:36:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-03 10:07:43 +08:00
|
|
|
|
export function upload(path, corpId) {
|
2026-06-11 18:13:49 +08:00
|
|
|
|
const normalizedCorpId = normalizeCorpId(corpId);
|
2026-09-22 17:07:16 +08:00
|
|
|
|
const uploadCorpId = normalizedCorpId || resolveApiContext().corpId;
|
|
|
|
|
|
const sessionToken = uni.getStorageSync("miniappSessionToken");
|
2026-06-11 18:13:49 +08:00
|
|
|
|
const currentUploadUrl = buildApiUrl("/upload", normalizedCorpId);
|
2026-01-20 19:36:49 +08:00
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
uni.uploadFile({
|
2026-06-03 10:07:43 +08:00
|
|
|
|
url: currentUploadUrl,
|
2026-09-22 17:07:16 +08:00
|
|
|
|
header: isSh6CorpId(uploadCorpId) && sessionToken
|
|
|
|
|
|
? { "X-Miniapp-Session": sessionToken }
|
|
|
|
|
|
: {},
|
2026-01-20 19:36:49 +08:00
|
|
|
|
filePath: path,
|
|
|
|
|
|
name: 'file',
|
|
|
|
|
|
fileType: 'image',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const url = JSON.parse(res.data).filePath;
|
2026-06-11 18:13:49 +08:00
|
|
|
|
resolve(url ? getFullPath(url, normalizedCorpId) : '')
|
2026-01-20 19:36:49 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
resolve()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: res => {
|
|
|
|
|
|
resolve()
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|