t-comm
Version:
专业、稳定、纯粹的工具库
393 lines (386 loc) • 16.6 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
var _createClass = require('@babel/runtime/helpers/createClass');
var qqMp_qqMiniPlugin = require('./qq-mini-plugin.js');
var qqMp_qqMp = require('./qq-mp.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck);
var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass);
var DEFAULT_MESSAGES = {
alreadyQQ: '当前已是 QQ 账号',
alreadyWx: '当前已是微信账号',
switchQQFail: '切换QQ账号失败,请稍后重试',
switchWxSuccess: '切换微信账号成功',
switchWxFail: '切换微信账号失败,请稍后重试',
switchQQRetry: '切换QQ账号失败,请重试',
switchQQSuccess: '切换QQ账号成功'
};
// ============ 实现 ============
var AccountService = /*#__PURE__*/function () {
function AccountService() {
_classCallCheck__default["default"](this, AccountService);
/** 防止 wx.onShow 重复绑定 */
this._onShowBound = false;
/** 防止"切号过程中再次点击"重复触发 */
this._switching = false;
}
// ============ 账号类型查询 ============
/** 当前是否为 QQ 账号(基于注入的 isQQAccount) */
return _createClass__default["default"](AccountService, [{
key: "isQQ",
value: function isQQ() {
return AccountService.getDeps().isQQAccount();
}
/** 「切换 QQ 账号 / 切换微信账号」按钮的动态文案 */
}, {
key: "getSwitchButtonText",
value: function getSwitchButtonText() {
return this.isQQ() ? '切换微信账号' : '切换QQ账号';
}
// ============ App 生命周期入口 ============
/**
* onLaunch / Game.bootstrap 阶段调用:
*
* 1) 初始化 qq-wxmini-plugin(QQ 用户访问微信小游戏的插件)
* 2) 若当前是 QQ 环境但本地仍是微信登录态,自动清空 storage 让游戏重走 QQ 登录
* 3) 主动绑定 wx.onShow:QQ 登录小程序返回时提取票据并换登录态
*/
}, {
key: "handleAppOnLaunch",
value: function handleAppOnLaunch() {
var deps = AccountService.getDeps();
try {
qqMp_qqMiniPlugin.initQQMiniPlugin();
} catch (e) {
console.warn('[AccountService] initQQMiniPlugin 失败', e);
}
try {
qqMp_qqMiniPlugin.clearWxLoginStorageIfQQEnv({
// 当前 loginInfo 不是 QQ 账号 → 视为微信登录态
isWxLoggedIn: function isWxLoggedIn() {
var loginInfo = deps.storage.get(deps.loginInfoStorageKey);
if (!loginInfo) return false;
return loginInfo.accType !== 'qq' && loginInfo.openid_type !== 'qq';
},
// 用注入的 storage 兜底清,避免误删 wx 全局其它业务数据
// 同时清掉可能遗留的 QQ 票据,避免下轮启动又被误识别为 QQ 登录。
clearStorage: function clearStorage() {
var _a, _b, _c, _d;
(_b = (_a = deps.storage).remove) === null || _b === void 0 ? void 0 : _b.call(_a, deps.loginInfoStorageKey);
(_d = (_c = deps.storage).remove) === null || _d === void 0 ? void 0 : _d.call(_c, AccountService.getQQTicketStorageKey(deps));
},
// 业务可选控制是否「QQ 环境下强制 QQ 登录」
shouldForceQQ: deps.shouldForceQQ
});
} catch (e) {
console.warn('[AccountService] clearWxLoginStorageIfQQEnv 失败', e);
}
this._bindWxOnShow();
}
// ============ 切号入口(供页面层调用) ============
/**
* 「切换QQ账号」点击入口
*
* 根据运行环境自动分流:
* - QQ App 环境(`checkIsQQEnv() === true`)且业务传入了 `code2QQLogin`:
* 调用 `qqPluginLogin()` 直接拿 QQ code → 交 `code2QQLogin` 探后台换登录态。
* 业务后台路径参考 [src/cocos/login/login.ts](../cocos/login/login.ts) 的 `loginMp`(`_ltype=tiploginqqproc`)。
* - 微信宿主环境(或未传 `code2QQLogin`):
* `launchQQMP` → 腾讯 QQ 小程序登录 → `wx.onShow` 提取票据 → `QueryUserInfo` 换登录态。
*/
}, {
key: "switchToQQ",
value: function switchToQQ() {
var _this = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
if (this._switching) return Promise.resolve();
if (this.isQQ()) {
// 幂等分支:已是 QQ 账号仍需要跑一次「bootstrapPlayer + onLoginSuccess」,
// 让业务侧能在登录页也命中登录成功回调(AuthPage 场景:
// 用户已是 QQ 账号 → 只需拉玩家数据 + close 登录页,不用重新走 QQ 授权)
this._switching = true;
deps.toast(messages.alreadyQQ);
return this._finishLoginOk('qq', 'already');
}
this._switching = true;
// QQ App 环境 + 业务传了后台探身入口 → 走「plugin.login() + code2QQLogin」直达路径
if (deps.code2QQLogin && qqMp_qqMiniPlugin.checkIsQQEnv()) {
return this._switchToQQViaPlugin();
}
// 微信宿主环境 → 跳腾讯 QQ 小程序,后续由 wx.onShow → _consumeQQTicket 完成
return qqMp_qqMp.launchQQMP({
qqAppId: deps.qqAppId
}).then(function () {
// 真正的登录完成在 wx.onShow → _consumeQQTicket 中
})["catch"](function (e) {
console.error('[AccountService] launchQQMP 失败', e);
deps.toast(messages.switchQQFail);
_this._switching = false;
});
}
/**
* 「切换微信账号」点击(当前账号是 QQ 时)
*
* 根据运行环境自动分流:
* - QQ App 环境(`checkIsQQEnv() === true`)且业务传入了 `code2WxLogin`:
* 调用 `wxLogin()` 拿微信 code → 交 `code2WxLogin` 探后台换登录态。
* 业务后台路径参考 [src/cocos/login/login.ts](../cocos/login/login.ts) 的 `loginMp`(`_ltype=tiploginwxproc`)。
* - 微信宿主环境(或未传 `code2WxLogin`):
* 微信小游戏本身就是微信宿主,"切回微信"等价于:清掉 QQ 登录态(loginInfo + QQ 票据缓存)
* → 让 SDK 重新用 wx.login 走微信登录。
*/
}, {
key: "switchToWx",
value: function switchToWx() {
var _this2 = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
if (this._switching) return Promise.resolve();
if (!this.isQQ()) {
// 幂等分支:与 switchToQQ 对齐(详见 switchToQQ 注释)
this._switching = true;
deps.toast(messages.alreadyWx);
return this._finishLoginOk('wx', 'already');
}
this._switching = true;
// QQ App 环境 + 业务传了后台探身入口 → 走「wx.login + code2WxLogin」直达路径
if (deps.code2WxLogin && qqMp_qqMiniPlugin.checkIsQQEnv()) {
return this._switchToWxViaCode();
}
// 微信宿主环境 → 清掉 QQ 登录残留 + 统一收尾
return Promise.resolve().then(function () {
var _a, _b;
qqMp_qqMp.clearQQTicketInfo({
storage: deps.storage,
storageKey: AccountService.getQQTicketStorageKey(deps)
});
(_b = (_a = deps.storage).remove) === null || _b === void 0 ? void 0 : _b.call(_a, deps.loginInfoStorageKey);
}).then(function () {
return _this2._finishLoginOk('wx', 'switch');
})["catch"](function (e) {
console.error('[AccountService] switchToWx 失败', e);
deps.toast(messages.switchWxFail);
_this2._switching = false;
});
}
// ============ 内部直达路径实现 ============
/**
* QQ App 环境下「切换 QQ 账号」的直达路径:插件 login 拿 code → 交业务探后台
*/
}, {
key: "_switchToQQViaPlugin",
value: function _switchToQQViaPlugin() {
var _this3 = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
return qqMp_qqMiniPlugin.qqPluginLogin().then(function (_ref) {
var code = _ref.code;
if (!deps.code2QQLogin) {
// 理论上 switchToQQ 里已拦住;这里双保险
throw new Error('code2QQLogin not configured');
}
return Promise.resolve(deps.code2QQLogin(code));
}).then(function () {
return _this3._finishLoginOk('qq', 'switch');
})["catch"](function (e) {
console.error('[AccountService] _switchToQQViaPlugin 失败', e);
deps.toast(messages.switchQQFail);
_this3._switching = false;
});
}
/**
* QQ App 环境下「切换微信账号」的直达路径:wx.login 拿 code → 交业务探后台
*/
}, {
key: "_switchToWxViaCode",
value: function _switchToWxViaCode() {
var _this4 = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
return qqMp_qqMiniPlugin.wxLogin().then(function (_ref2) {
var code = _ref2.code;
var _a, _b;
if (!deps.code2WxLogin) {
throw new Error('code2WxLogin not configured');
}
// 先清掉本地残留的 QQ 票据,避免下轮启动又被误识别为 QQ 登录
try {
qqMp_qqMp.clearQQTicketInfo({
storage: deps.storage,
storageKey: AccountService.getQQTicketStorageKey(deps)
});
(_b = (_a = deps.storage).remove) === null || _b === void 0 ? void 0 : _b.call(_a, deps.loginInfoStorageKey);
} catch (e) {
console.warn('[AccountService] clear local before wx login failed', e);
}
return Promise.resolve(deps.code2WxLogin(code));
}).then(function () {
return _this4._finishLoginOk('wx', 'switch');
})["catch"](function (e) {
console.error('[AccountService] _switchToWxViaCode 失败', e);
deps.toast(messages.switchWxFail);
_this4._switching = false;
});
}
/**
* 绑定 wx.onShow(幂等):
* - 启动时由 handleAppOnLaunch 调用一次
* - 业务页面再次需要可重复调用
*/
}, {
key: "_bindWxOnShow",
value: function _bindWxOnShow() {
var _this5 = this;
if (this._onShowBound) return;
var w = typeof wx !== 'undefined' ? wx : globalThis.wx;
if (!(w === null || w === void 0 ? void 0 : w.onShow)) return;
this._onShowBound = true;
w.onShow(function (options) {
return _this5._onWxShow(options);
});
}
/** wx.onShow 回调:处理从腾讯 QQ 小程序返回的票据 */
}, {
key: "_onWxShow",
value: function _onWxShow(options) {
var deps = AccountService.getDeps();
var ticket;
try {
ticket = qqMp_qqMp.handleQQLoginOnShow(options, {
storage: deps.storage,
// 重要:使用独立的票据 key,避免覆盖业务 loginInfo(如 cocos `loginMp` 写入的)
storageKey: AccountService.getQQTicketStorageKey(deps)
});
} catch (e) {
console.warn('[AccountService] handleQQLoginOnShow 异常', e);
}
if (!ticket) return;
this._consumeQQTicket(ticket);
}
/**
* 用 QQ 票据换取登录态 + 重启玩家服务
*
* 优先级:
* 1. 业务侧注入了 `qqTicket2Login`(推荐,如 cocos `loginMp(_ltype=tiploginqqproc, code=qqAccessToken)`)→ 走它。
* 2. 否则 fallback 到 `queryQQLoginUserInfo`(仅适用于能统一拦截 `Logininfo` 响应头的环境)。
*/
}, {
key: "_consumeQQTicket",
value: function _consumeQQTicket(ticket) {
var _this6 = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
// 优先走业务注入的「票据 → 后台探身」,该函数内部应完成 logininfo 写入。
if (typeof deps.qqTicket2Login === 'function') {
return Promise.resolve().then(function () {
return deps.qqTicket2Login(ticket);
}).then(function () {
return _this6._finishLoginOk('qq', 'switch');
})["catch"](function (e) {
console.error('[AccountService] qqTicket2Login 失败', e);
deps.toast(messages.switchQQFail);
_this6._switching = false;
});
}
return qqMp_qqMp.queryQQLoginUserInfo({
host: deps.getQQLoginUserInfoHost,
post: deps.post,
ticketInfo: ticket,
fallbackQQAppId: deps.qqAppId
}).then(function (rsp) {
if (!(rsp === null || rsp === void 0 ? void 0 : rsp.user_info)) {
deps.toast(messages.switchQQRetry);
_this6._switching = false;
return;
}
// QueryUserInfo 在响应头/cookie 里写入 QQ loginInfo(由网络层统一处理)。
return _this6._finishLoginOk('qq', 'switch');
})["catch"](function (e) {
console.error('[AccountService] _consumeQQTicket 失败', e);
deps.toast(messages.switchQQFail);
_this6._switching = false;
});
}
/**
* 统一登录成功收尾:clearPlayer → bootstrapPlayer → toast → onLoginSuccess → 释放 _switching
*
* 说明:
* - `switching` 语义与 UI 层「登录中」loading 是同一件事,因此在 bootstrap 完成前
* 保持为 true;bootstrap 结果 resolve 或 reject 都释放
* - toast 文案根据 platform 选:QQ = switchQQSuccess;WX = switchWxSuccess
* - onLoginSuccess 在 toast 之后调,业务侧派事件/关登录页由回调决定
* - bootstrapPlayer 失败仍会触发 onLoginSuccess?NO:bootstrap 抛错代表玩家数据
* 没拉到,登录不算完整成功,跳过 toast 与 onLoginSuccess,让业务侧看到 catch
*/
}, {
key: "_finishLoginOk",
value: function _finishLoginOk(platform, reason) {
var _this7 = this;
var deps = AccountService.getDeps();
var messages = AccountService._messages;
deps.clearPlayer();
return Promise.resolve(deps.bootstrapPlayer({
force: true
})).then(function () {
var _a;
deps.toast(platform === 'qq' ? messages.switchQQSuccess : messages.switchWxSuccess);
try {
(_a = deps.onLoginSuccess) === null || _a === void 0 ? void 0 : _a.call(deps, {
platform: platform,
reason: reason
});
} catch (e) {
console.error('[AccountService] onLoginSuccess 回调异常', e);
}
})["catch"](function (e) {
console.error('[AccountService] _finishLoginOk bootstrap 失败', e);
deps.toast(platform === 'qq' ? messages.switchQQFail : messages.switchWxFail);
throw e;
})["finally"](function () {
_this7._switching = false;
});
}
}], [{
key: "configure",
value:
/**
* 注入运行所需依赖(应用启动期调用一次即可)
*
* 多次调用会覆盖之前的依赖,便于测试时替换。
*/
function configure(deps, messages) {
AccountService._deps = deps;
AccountService._messages = Object.assign(Object.assign({}, DEFAULT_MESSAGES), messages || {});
}
/** 获取当前注入的依赖(未注入时抛错) */
}, {
key: "getDeps",
value: function getDeps() {
if (!AccountService._deps) {
throw new Error('[AccountService] 未初始化,请先调用 AccountService.configure(deps)');
}
return AccountService._deps;
}
/** 单例 */
}, {
key: "ins",
get: function get() {
if (!AccountService._ins) AccountService._ins = new AccountService();
return AccountService._ins;
}
// ============ 内部工具 ============
/** QQ 票据存储 key(默认 `${loginInfoStorageKey}__qq_ticket`) */
}, {
key: "getQQTicketStorageKey",
value: function getQQTicketStorageKey(deps) {
return deps.qqTicketStorageKey || "".concat(deps.loginInfoStorageKey, "__qq_ticket");
}
}]);
}();
AccountService._ins = null;
AccountService._deps = null;
AccountService._messages = Object.assign({}, DEFAULT_MESSAGES);
exports.checkIsQQEnv = qqMp_qqMiniPlugin.checkIsQQEnv;
exports.AccountService = AccountService;