- 替换开发环境API基础URL为本地服务地址 - 实现签名URL本地缓存机制,解决微信按URL缓存图片导致重复下载问题 - 添加缓存清理功能,包括过期清理和按需删除特定缓存项 - 集成阿里云OSS上传功能,支持直传减少服务器带宽占用 - 优化头像地址处理逻辑,统一使用签名缓存避免地址频繁变化 - 移除过时的OSS上传示例文件oss-upload.js - 更新课程页面实现跳转第三方小程序功能 - 调整打卡记录图片地址获取方式,统一使用新的签名缓存逻辑 - 优化图片上传流程,增加压缩处理和批量上传支持 - 完善认证模块中的缓存清理逻辑,切换账号时清除相关图片缓存 - 添加后端环境变量配置模板和代理文档说明
111 lines
3.2 KiB
JavaScript
111 lines
3.2 KiB
JavaScript
const api = require('./api');
|
||
|
||
let loginPromise = null;
|
||
|
||
function getToken() {
|
||
return wx.getStorageSync('token') || '';
|
||
}
|
||
|
||
function setToken(token) {
|
||
wx.setStorageSync('token', token);
|
||
}
|
||
|
||
function clearToken() {
|
||
wx.removeStorageSync('token');
|
||
// 顺带清掉签名 URL 缓存:换账号后不该复用上一个人的图片地址
|
||
api.clearSignedCache();
|
||
}
|
||
|
||
function wxLogin(retries) {
|
||
const remain = typeof retries === 'number' ? retries : 3;
|
||
return new Promise((resolve, reject) => {
|
||
wx.login({
|
||
success(res) {
|
||
if (res && res.code) {
|
||
resolve(res.code);
|
||
} else {
|
||
reject(new Error((res && res.errMsg) || '登录失败'));
|
||
}
|
||
},
|
||
fail: reject
|
||
});
|
||
}).catch((err) => {
|
||
// 冷启动时微信登录服务可能尚未就绪,指数退避重试
|
||
if (remain > 0) {
|
||
const delay = 400 + (4 - remain) * 200; // 首次 600ms,依次 800ms、1000ms
|
||
return new Promise((resolve) => setTimeout(resolve, delay))
|
||
.then(() => wxLogin(remain - 1));
|
||
}
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
// 整个微信登录流程失败时重新取 code 再试一次,覆盖冷启动首个网络请求偶发失败
|
||
function wechatLogin(retries) {
|
||
const remain = typeof retries === 'number' ? retries : 1;
|
||
return wxLogin()
|
||
.then((code) => api.request({
|
||
url: '/api/v1/auth/wechat-login',
|
||
method: 'POST',
|
||
data: { code },
|
||
auth: false
|
||
}))
|
||
.catch((err) => {
|
||
if (remain > 0) {
|
||
return new Promise((resolve) => setTimeout(resolve, 500)).then(() => wechatLogin(remain - 1));
|
||
}
|
||
throw err;
|
||
});
|
||
}
|
||
|
||
// 单例登录:App.onLaunch 与首屏页面 onShow 可能几乎同时调用,
|
||
// 共享同一个进行中的登录 Promise,避免重复 wx.login / wechat-login 造成竞态。
|
||
// 关键:登录失败时必须清空单例,让下一次调用能重新发起,
|
||
// 否则冷启动 wx.login 偶发失败后,后续页面永远拿不到 token。
|
||
function ensureLogin(refresh) {
|
||
const existing = getToken();
|
||
if (existing && !refresh) {
|
||
return Promise.resolve(existing);
|
||
}
|
||
if (loginPromise) {
|
||
return loginPromise;
|
||
}
|
||
const p = wechatLogin()
|
||
.then((res) => {
|
||
if (res && res.success) {
|
||
setToken(res.token);
|
||
return res.token;
|
||
}
|
||
// need_phone:未绑定手机号时 resolve(null),由页面引导去「我的」绑定
|
||
return null;
|
||
})
|
||
.finally(() => {
|
||
// 无论成功失败,完成后都清掉单例,让下一次调用可以重新发起
|
||
if (loginPromise === p) {
|
||
loginPromise = null;
|
||
}
|
||
});
|
||
loginPromise = p;
|
||
return p;
|
||
}
|
||
|
||
function loginWithPhone(phoneCode) {
|
||
return wxLogin()
|
||
.then((code) => api.request({
|
||
url: '/api/v1/auth/bind-phone',
|
||
method: 'POST',
|
||
data: { code, phone_code: phoneCode },
|
||
auth: false
|
||
}))
|
||
.then((res) => {
|
||
if (res && res.success) {
|
||
setToken(res.token);
|
||
return res;
|
||
}
|
||
const error = new Error((res && res.error) || '手机号登录失败');
|
||
error.error = (res && res.error) || '手机号登录失败';
|
||
throw error;
|
||
});
|
||
}
|
||
|
||
module.exports = { getToken, setToken, clearToken, wxLogin, ensureLogin, loginWithPhone }; |