fa6fb80d19
Co-authored-by: Cursor <cursoragent@cursor.com>
161 lines
5.0 KiB
TypeScript
161 lines
5.0 KiB
TypeScript
import Taro from '@tarojs/taro';
|
|
import type { UserProfile } from './api';
|
|
|
|
export type MiniWechatProfile = {
|
|
nickname?: string;
|
|
avatarUrl?: string;
|
|
};
|
|
|
|
export type MiniWechatProfileUpdate = MiniWechatProfile & {
|
|
avatarResourceId?: string;
|
|
};
|
|
|
|
export type UploadedAvatarResource = {
|
|
resourceId: string;
|
|
url: string;
|
|
bucket: string;
|
|
ossKey: string;
|
|
};
|
|
|
|
const WX_PROFILE_CACHE_KEY = 'wx_mini_profile_cache';
|
|
|
|
export function cacheWxProfile(info: MiniWechatProfile) {
|
|
if (!info.nickname && !info.avatarUrl) return;
|
|
try {
|
|
Taro.setStorageSync(WX_PROFILE_CACHE_KEY, JSON.stringify(info));
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
|
|
export function getCachedWxProfile(): MiniWechatProfile | null {
|
|
try {
|
|
const raw = Taro.getStorageSync(WX_PROFILE_CACHE_KEY);
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(String(raw)) as MiniWechatProfile;
|
|
if (!parsed?.nickname && !parsed?.avatarUrl) return null;
|
|
return parsed;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function isDefaultMiniNickname(nickname?: string | null): boolean {
|
|
if (!nickname || nickname === '访客' || nickname === '微信用户' || nickname === '用户') return true;
|
|
return /^用户\d{4}$/.test(nickname);
|
|
}
|
|
|
|
/** 是否缺少可展示的微信头像/昵称(需走 chooseAvatar + nickname 填写) */
|
|
export function needsWxProfileFill(profile: UserProfile | null | undefined): boolean {
|
|
if (!profile) return true;
|
|
return !profile.avatarUrl || isDefaultMiniNickname(profile.nickname);
|
|
}
|
|
|
|
export function mergeWxDisplayProfile(profile: UserProfile): UserProfile {
|
|
const cached = getCachedWxProfile();
|
|
if (!cached && !profile.hasWechat) return profile;
|
|
const nickname =
|
|
(!isDefaultMiniNickname(profile.nickname) ? profile.nickname : undefined) ||
|
|
cached?.nickname ||
|
|
profile.nickname ||
|
|
'微信用户';
|
|
return {
|
|
...profile,
|
|
nickname,
|
|
avatarUrl: profile.avatarUrl || cached?.avatarUrl || null,
|
|
};
|
|
}
|
|
|
|
/** 上传 chooseAvatar 临时文件到 OSS,并返回已登记到当前用户的真实资源。 */
|
|
export async function uploadAvatarTempFile(tempFilePath: string): Promise<UploadedAvatarResource> {
|
|
const { API_BASE, CLIENT_APP, getToken } = await import('./api');
|
|
const { compressWeappImageIfNeeded } = await import('./compress-image');
|
|
const token = getToken();
|
|
if (!token) throw new Error('请先登录');
|
|
|
|
const filePath = await compressWeappImageIfNeeded(tempFilePath);
|
|
|
|
const res = await Taro.uploadFile({
|
|
url: `${API_BASE}/common/resources/upload`,
|
|
filePath,
|
|
name: 'file',
|
|
formData: {
|
|
bizType: 'AVATAR',
|
|
mediaType: 'IMAGE',
|
|
},
|
|
header: {
|
|
Authorization: `Bearer ${token}`,
|
|
'X-Client-App': CLIENT_APP,
|
|
},
|
|
});
|
|
|
|
let body: {
|
|
code?: number;
|
|
message?: string;
|
|
data?: { resourceId?: string; url?: string; bucket?: string; ossKey?: string };
|
|
} = {};
|
|
try {
|
|
body = JSON.parse(String(res.data || '{}')) as typeof body;
|
|
} catch {
|
|
throw new Error('头像上传响应异常');
|
|
}
|
|
if (res.statusCode === 401 || body.code === 401) {
|
|
throw new Error(body.message || '登录已过期,请重新登录');
|
|
}
|
|
if (
|
|
res.statusCode >= 400 ||
|
|
body.code !== 0 ||
|
|
!body.data?.resourceId ||
|
|
!body.data.url ||
|
|
!body.data.bucket ||
|
|
!body.data.ossKey
|
|
) {
|
|
throw new Error(body.message || '头像上传失败');
|
|
}
|
|
return {
|
|
resourceId: body.data.resourceId,
|
|
url: body.data.url,
|
|
bucket: body.data.bucket,
|
|
ossKey: body.data.ossKey,
|
|
};
|
|
}
|
|
|
|
export async function uploadMiniWechatProfile(info: MiniWechatProfileUpdate): Promise<UserProfile | null> {
|
|
if (!info.nickname && !info.avatarUrl && !info.avatarResourceId) return null;
|
|
const { request } = await import('./api');
|
|
const updated = await request<UserProfile>('/auth/wechat/mini-profile', {
|
|
method: 'POST',
|
|
data: info,
|
|
});
|
|
cacheWxProfile({
|
|
nickname: updated?.nickname ?? info.nickname,
|
|
avatarUrl: updated?.avatarUrl ?? info.avatarUrl,
|
|
});
|
|
return updated;
|
|
}
|
|
|
|
/**
|
|
* 兼容旧调用:getUserProfile 已无法拿到真实头像昵称。
|
|
* 始终导出为函数,避免循环依赖/旧包出现 “is not a function”。
|
|
*/
|
|
export async function fetchMiniWechatUserInfo(): Promise<MiniWechatProfile> {
|
|
const cached = getCachedWxProfile();
|
|
if (cached?.nickname || cached?.avatarUrl) {
|
|
return cached;
|
|
}
|
|
// 不再弹 getUserProfile;引导走「我的」页 chooseAvatar / nickname
|
|
throw new Error('请在「我的」页点击头像完善微信头像和昵称');
|
|
}
|
|
|
|
/** 绑定后上报微信资料(优先使用已拉取的信息) */
|
|
export async function syncMiniWechatProfile(
|
|
prefetched?: MiniWechatProfile | null,
|
|
): Promise<MiniWechatProfile | null> {
|
|
if (process.env.TARO_ENV !== 'weapp') return null;
|
|
const info = prefetched?.nickname || prefetched?.avatarUrl ? prefetched : getCachedWxProfile();
|
|
if (!info?.nickname && !info?.avatarUrl) return null;
|
|
// 缓存头像 URL 可能来自历史微信资料,未经过当前 OSS 上传登记;这里只同步昵称。
|
|
if (info.nickname) await uploadMiniWechatProfile({ nickname: info.nickname });
|
|
return info;
|
|
}
|