Compare commits

...

2 Commits

Author SHA1 Message Date
wangdongbo
bf6106184d no message 2026-09-23 10:53:49 +08:00
wangdongbo
0ec3e570af no message 2026-09-22 17:07:16 +08:00
5 changed files with 91 additions and 24 deletions

View File

@ -17,7 +17,7 @@ import { triggerPromise } from '@/utils';
export default function useGuard() {
const { account } = storeToRefs(useAccount());
const { login } = useAccount();
const { login, hasValidMiniappSession } = useAccount();
const { resolve, promise } = triggerPromise();
const onShowEvents = ref([]);
@ -54,7 +54,7 @@ export default function useGuard() {
const page = pages[pages.length - 1];
const route = routes.find(i => page && i.path === page.route);
const requireLogin = route && route.meta && route.meta.login;
if (requireLogin && !account.value) {
if (requireLogin && (!account.value || !hasValidMiniappSession())) {
const res = await login()
if (res) {
resolve()
@ -72,4 +72,4 @@ export default function useGuard() {
})
return { useLoad, useShow }
}
}

View File

@ -11,7 +11,7 @@ import api from "@/utils/api";
import { set } from "@/utils/cache";
import { toast } from "@/utils/widget";
import useAccountStore from "@/store/account";
import { normalizeCorpId, normalizeCorpIdFields } from "@/utils/api-base-config";
import { isSh6CorpId, normalizeCorpId, normalizeCorpIdFields, resolveApiContext } from "@/utils/api-base-config";
const env = __VITE_ENV__;
const appid = env.MP_WX_APP_ID;
@ -81,7 +81,12 @@ function getAiConsultEntryUrl({ teamId, corpId, qrid }) {
async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, referenceCustomerId, type }) {
const normalizedCorpId = normalizeCorpId(corpId);
const requiresSession = isSh6CorpId(normalizedCorpId);
const redirectUrl = type === 'aiConsult' ? getAiConsultEntryUrl({ teamId, corpId: normalizedCorpId, qrid }) : '';
if (requiresSession) {
resolveApiContext(normalizedCorpId);
await login('', { forceVerify: true });
}
loading.value = true;
const res = await api("getTeamData", { teamId, corpId: normalizedCorpId, withCorpName: true });
loading.value = false;
@ -99,7 +104,7 @@ async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, re
qrid,
referenceCustomerId: referenceCustomerId || ''
});
await login('', { forceVerify: true })
if (!requiresSession) await login('', { forceVerify: true })
if (account.value && account.value.mobile) {
bindTeam({ corpUserId, externalUserId, redirectUrl })
} else {

View File

@ -4,7 +4,8 @@ import api from '@/utils/api';
import { toast } from '@/utils/widget';
import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js";
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import { normalizeCorpId, normalizeCorpIdFields } from "@/utils/api-base-config";
import { isSh6CorpId, normalizeCorpId, normalizeCorpIdFields, resolveApiContext } from "@/utils/api-base-config";
import { setMiniappSessionRefresher } from "@/utils/http";
const env = __VITE_ENV__;
export default defineStore("accountStore", () => {
@ -18,9 +19,16 @@ export default defineStore("accountStore", () => {
const teams = ref([]);
const teamsPromise = ref(null);
function hasValidMiniappSession() {
if (!isSh6CorpId(resolveApiContext().corpId)) return true;
const token = uni.getStorageSync("miniappSessionToken");
const expiresAt = Number(uni.getStorageSync("miniappSessionExpiresAt"));
return Boolean(token && (!expiresAt || expiresAt > Date.now() + 30 * 1000));
}
async function login(phoneCode = '', options = {}) {
if (loading.value) return;
if (account.value && account.value.mobile && !options.forceVerify) {
if (account.value && account.value.mobile && !options.forceVerify && hasValidMiniappSession()) {
return account.value
}
loading.value = true;
@ -38,7 +46,12 @@ export default defineStore("accountStore", () => {
});
loading.value = false
if (res.success && res.data) {
const normalizedAccount = normalizeCorpIdFields(res.data);
const { sessionToken, sessionExpiresAt, sessionKey, ...safeAccount } = res.data;
if (sessionToken) {
uni.setStorageSync("miniappSessionToken", sessionToken);
uni.setStorageSync("miniappSessionExpiresAt", sessionExpiresAt || 0);
}
const normalizedAccount = normalizeCorpIdFields(safeAccount);
account.value = normalizedAccount;
openid.value = normalizedAccount.openid;
@ -121,6 +134,8 @@ export default defineStore("accountStore", () => {
// 清空账户信息
account.value = null;
uni.removeStorageSync("miniappSessionToken");
uni.removeStorageSync("miniappSessionExpiresAt");
openid.value = "";
isIMInitialized.value = false;
@ -177,5 +192,7 @@ export default defineStore("accountStore", () => {
}
}, { immediate: true })
return { account, teams, login, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId, getTeams }
setMiniappSessionRefresher(() => login('', { forceVerify: true, silent: true }));
return { account, teams, login, hasValidMiniappSession, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId, getTeams }
})

View File

@ -12,6 +12,10 @@ export const CORP_ID_ALIAS_MAP = {
"wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA": "wwa54dfba0b5441ef1",
};
const SH6_CORP_IDS = new Set([
"wpLgjyawAAxKtBH5GVT5DVhLefg246Ag",
]);
function normalizeBaseUrl(url) {
return String(url || "").replace(/\/+$/, "");
}
@ -25,6 +29,10 @@ export function normalizeCorpId(corpId) {
return CORP_ID_ALIAS_MAP[normalizedCorpId] || normalizedCorpId;
}
export function isSh6CorpId(corpId) {
return SH6_CORP_IDS.has(normalizeCorpId(corpId));
}
export function normalizeCorpIdFields(value) {
if (Array.isArray(value)) {
return value.map((item) => normalizeCorpIdFields(item));

View File

@ -1,5 +1,5 @@
import { loading, hideLoading } from "./widget";
import { buildApiUrl, getDefaultUploadUrl, normalizeCorpId } from "./api-base-config";
import { buildApiUrl, getDefaultUploadUrl, isSh6CorpId, normalizeCorpId, resolveApiContext } from "./api-base-config";
const defaultOptions = {
header: {
@ -9,6 +9,25 @@ const defaultOptions = {
timeout: 1000 * 60, // 请求超时设置为60秒
};
const tasks = [];
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"));
}
// 是否正在刷新 token
let isRefreshing = false;
@ -94,27 +113,29 @@ async function refreshAccessToken() {
}
const request = async (options = {}, showLoading = true) => {
const sessionRetried = options.__miniappSessionRetried === true;
const requestOptions = { ...options };
delete requestOptions.__miniappSessionRetried;
// 合并用户传入的配置和默认配置
if (!options.data) options.data = {};
if (options.data.corpId) {
options.data = {
...options.data,
corpId: normalizeCorpId(options.data.corpId),
if (!requestOptions.data) requestOptions.data = {};
if (requestOptions.data.corpId) {
requestOptions.data = {
...requestOptions.data,
corpId: normalizeCorpId(requestOptions.data.corpId),
};
}
const requestCorpId = requestOptions.data.corpId || resolveApiContext().corpId;
const requiresMiniappSession = isSh6CorpId(requestCorpId);
const config = {
...defaultOptions,
...options,
url: buildApiUrl(options.url, options.data.corpId),
...requestOptions,
url: buildApiUrl(requestOptions.url, requestOptions.data.corpId),
};
// 添加 token 到请求头
// const accessToken = uni.getStorageSync("accessToken");
// if (accessToken) {
// if (!config.header) config.header = {};
// config.header.Authorization = `Bearer ${accessToken}`;
// }
const sessionToken = uni.getStorageSync("miniappSessionToken");
if (requiresMiniappSession && sessionToken) {
config.header = { ...(config.header || {}), "X-Miniapp-Session": sessionToken };
}
const key = `${JSON.stringify(config)}_${Date.now()}`;
if (showLoading) {
@ -128,6 +149,17 @@ const request = async (options = {}, showLoading = true) => {
removeTask(key)
}
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);
}
}
// 如果返回 401,尝试刷新 token
// if (res.statusCode === 401) {
// if (showLoading) {
@ -214,10 +246,15 @@ export function getFullPath(path, corpId) {
export function upload(path, corpId) {
const normalizedCorpId = normalizeCorpId(corpId);
const uploadCorpId = normalizedCorpId || resolveApiContext().corpId;
const sessionToken = uni.getStorageSync("miniappSessionToken");
const currentUploadUrl = buildApiUrl("/upload", normalizedCorpId);
return new Promise((resolve) => {
uni.uploadFile({
url: currentUploadUrl,
header: isSh6CorpId(uploadCorpId) && sessionToken
? { "X-Miniapp-Session": sessionToken }
: {},
filePath: path,
name: 'file',
fileType: 'image',