feat(ui): Add user cache and batch fetch in VBase

- Add shared users cache object to VBase constructor
    - Add User(id) API for reactive user info lookup
    - Implement _flushUserRequests for batch POST /api/auth/users
    - Cache and sync current user into shared users cache
    - Clear users cache on logout
master
veypi 2 weeks ago
parent c8504c3f70
commit 03a0afc727

@ -11,7 +11,8 @@ export default async ($mod) => {
} }
// Initialize VBase Service // Initialize VBase Service
const vbase = new VBase('vb', $mod.scoped); // Relative path $mod.users = {}
const vbase = new VBase('vb', $mod.scoped, null, $mod.users); // Relative path
$mod.$vbase = vbase; $mod.$vbase = vbase;
// Wrap Axios: add auth header // Wrap Axios: add auth header

@ -14,7 +14,7 @@ export const Level = {
}; };
class VBase { class VBase {
constructor(scope, baseURL, login_page) { constructor(scope, baseURL, login_page, users = {}) {
if (!scope) throw new Error('VBase: scope is required'); if (!scope) throw new Error('VBase: scope is required');
if (!baseURL) baseURL = window.location.origin; if (!baseURL) baseURL = window.location.origin;
if (baseURL === '' || baseURL === '/') baseURL = window.location.origin; if (baseURL === '' || baseURL === '/') baseURL = window.location.origin;
@ -25,10 +25,16 @@ class VBase {
this.tokenKey = `vbase_token`; this.tokenKey = `vbase_token`;
this.refreshTokenKey = `vbase_refresh_token`; this.refreshTokenKey = `vbase_refresh_token`;
this.userKey = `vbase_user`; this.userKey = `vbase_user`;
this.users = users;
this._token = localStorage.getItem(this.tokenKey) || ''; this._token = localStorage.getItem(this.tokenKey) || '';
this._refreshToken = localStorage.getItem(this.refreshTokenKey) || ''; this._refreshToken = localStorage.getItem(this.refreshTokenKey) || '';
this._user = JSON.parse(localStorage.getItem(this.userKey) || 'null'); this._user = JSON.parse(localStorage.getItem(this.userKey) || 'null');
this._pendingUserIDs = new Set();
this._loadingUserIDs = new Set();
this._resolvedUserIDs = new Set();
this._pendingUserFlush = null;
this._cachePublicUser(this._user);
if (this._token) { if (this._token) {
this.fetchUser() this.fetchUser()
} }
@ -56,6 +62,7 @@ class VBase {
this._user = val; this._user = val;
if (val) localStorage.setItem(this.userKey, JSON.stringify(val)); if (val) localStorage.setItem(this.userKey, JSON.stringify(val));
else localStorage.removeItem(this.userKey); else localStorage.removeItem(this.userKey);
this._cachePublicUser(val);
} }
// ========== API 请求 ========== // ========== API 请求 ==========
@ -212,6 +219,30 @@ class VBase {
this.token = ''; this.token = '';
this.refreshToken = ''; this.refreshToken = '';
this.user = null; this.user = null;
for (const id of Object.keys(this.users)) {
delete this.users[id];
}
this._pendingUserIDs.clear();
this._loadingUserIDs.clear();
this._resolvedUserIDs.clear();
this._pendingUserFlush = null;
}
User(id) {
if (!id) return {};
if (!this.users[id]) {
this.users[id] = {};
}
if (!this._resolvedUserIDs.has(id) && !this._loadingUserIDs.has(id)) {
this._pendingUserIDs.add(id);
if (!this._pendingUserFlush) {
this._pendingUserFlush = Promise.resolve().then(() => this._flushUserRequests());
}
}
return this.users[id];
} }
isExpired(token) { isExpired(token) {
@ -341,6 +372,57 @@ class VBase {
} }
); );
} }
_cachePublicUser(user) {
if (!user?.id) return;
const cached = {
...user,
name: user.name || user.nickname || user.username || '',
icon: user.icon || user.avatar || '',
avatar: user.avatar || user.icon || '',
};
if (!this.users[cached.id]) {
this.users[cached.id] = {};
}
Object.assign(this.users[cached.id], cached);
this._resolvedUserIDs.add(cached.id);
this._loadingUserIDs.delete(cached.id);
}
async _flushUserRequests() {
const ids = [...this._pendingUserIDs].filter(Boolean);
this._pendingUserIDs.clear();
this._pendingUserFlush = null;
if (ids.length === 0) return;
for (const id of ids) {
this._loadingUserIDs.add(id);
}
try {
const res = await this.request('POST', '/api/auth/users', { ids });
const items = Array.isArray(res?.items) ? res.items : [];
const found = new Set();
for (const item of items) {
this._cachePublicUser(item);
found.add(item.id);
}
for (const id of ids) {
this._loadingUserIDs.delete(id);
if (!found.has(id)) {
this._resolvedUserIDs.add(id);
}
}
} catch (error) {
for (const id of ids) {
this._loadingUserIDs.delete(id);
}
console.warn('VBase: batch fetch users failed', error);
}
}
} }
export default VBase; export default VBase;

Loading…
Cancel
Save