Files
wechat-clock-mini/utils/auth.js
王宏豪 1dd4fa3f41 feat(home): 实现游客模式与登录流程优化
- 添加游客模式,展示公共内容无需强制登录
- 实现游客模块展示打卡、计划、关于我们等功能介绍
- 添加产品介绍页面显示学习成长工具详情
- 优化登录流程,添加用户协议和隐私政策同意选项
- 更新角色标签将老师改为管理员,学员改为成员
- 添加注销账号功能替代退出登录
- 实现webview页面域名白名单安全验证
- 添加隐私政策和用户协议页面
- 优化页面跳转逻辑和错误提示信息
- 统一项目名称从明缘进阶场改为明缘云道场
2026-09-03 18:57:47 +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 };