配置: - manifest.json mp-weixin.appid = wx40ca4b02d4d1cb58(构建产物已自动同步) - .env 新增 [sms] 段:mock / mock_code,手机号登录验证码改为配置驱动 (Auth::login 不再硬编码 123456;mock=0 时返回 2010 提示接入短信) - 服务端 .env [wechat] appid/secret 已就位并与小程序一致 加固: - 新增 utils/wxlogin.js:微信登录单飞(防静默登录与手动登录抢同一个一次性 code) + 40029/40163 自动换新 code 重试一次 - Auth::wxlogin 失败时回传 wx_errcode,供客户端判断是否重试 - 登录失败自动展开手机号兜底通道 新增 tests/config_check.js:一键核对 appid 三处一致性 / 接口连通 / HTTPS 证书 / 短信开关 验证:config_check 8/8 + 163 项断言回归全通过
82 行
2.2 KiB
JavaScript
82 行
2.2 KiB
JavaScript
/**
|
||
* 微信登录统一入口
|
||
* 解决两个线上问题:
|
||
* 1) code 一次性(5 分钟、单次有效)——静默登录与手动登录若同时发起会互相抢 code
|
||
* → 用「单飞(in-flight)」保证同一时刻只有一个换取流程
|
||
* 2) 40029(invalid code)——可能来自 code 已被消费/过期
|
||
* → 自动用全新 code 重试一次
|
||
*/
|
||
import api from './api.js';
|
||
import { getToken, setToken, setUser } from './request.js';
|
||
|
||
let inflight = null;
|
||
|
||
/** 取微信一次性 code */
|
||
export function getWxCode() {
|
||
return new Promise((resolve, reject) => {
|
||
uni.login({
|
||
provider: 'weixin',
|
||
success: (res) => (res && res.code ? resolve(res.code) : reject(new Error('微信未返回 code'))),
|
||
fail: () => reject(new Error('未检测到微信环境,请在小程序内打开'))
|
||
});
|
||
});
|
||
}
|
||
|
||
function isCodeInvalid(err) {
|
||
const code = err && err.data ? Number(err.data.wx_errcode) : 0;
|
||
if (code === 40029 || code === 40163) return true;
|
||
return /invalid code|code been used|40029/i.test((err && err.message) || '');
|
||
}
|
||
|
||
/**
|
||
* 用 code 换 token;遇到 code 失效自动换新 code 重试一次
|
||
*/
|
||
async function exchange(payload, retry) {
|
||
const code = await getWxCode();
|
||
try {
|
||
const data = await api.wxLogin(Object.assign({}, payload, { code: code }));
|
||
setToken(data.token);
|
||
setUser(data.userinfo);
|
||
return data;
|
||
} catch (err) {
|
||
if (retry && isCodeInvalid(err)) {
|
||
return exchange(payload, false);
|
||
}
|
||
throw err;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 静默登录(进主页时调用):已登录直接返回,失败不抛错
|
||
*/
|
||
export function ensureSilentLogin() {
|
||
if (getToken()) return Promise.resolve(null);
|
||
if (inflight) return inflight;
|
||
|
||
inflight = exchange({}, true)
|
||
.catch(() => null)
|
||
.then((data) => {
|
||
inflight = null;
|
||
return data;
|
||
});
|
||
return inflight;
|
||
}
|
||
|
||
/**
|
||
* 主动授权登录(点击「微信一键登录」)
|
||
*/
|
||
export function wxAuthorizeLogin(payload) {
|
||
if (inflight) {
|
||
// 复用在途结果,避免抢同一个 code
|
||
return inflight.then((data) => {
|
||
if (data) return data;
|
||
return exchange(payload || {}, true);
|
||
});
|
||
}
|
||
inflight = exchange(payload || {}, true).then(
|
||
(data) => { inflight = null; return data; },
|
||
(err) => { inflight = null; throw err; }
|
||
);
|
||
return inflight;
|
||
}
|