Files
wechat-clock-mini/utils/auth.js
王宏豪 d7fc8c8982 init
项目初始化
2026-09-03 16:49:59 +08:00

109 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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');
}
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 };