forked from gxwebsoft/mp-10550
276 lines
6.6 KiB
TypeScript
276 lines
6.6 KiB
TypeScript
import request from '@/utils/request';
|
||
import type { ApiResult } from '@/api';
|
||
import Taro from '@tarojs/taro';
|
||
import {SERVER_API_URL} from "@/utils/server";
|
||
import {getUserInfo} from "@/api/layout";
|
||
|
||
/**
|
||
* 扫码登录相关接口
|
||
*/
|
||
|
||
// 生成扫码token请求参数
|
||
export interface GenerateQRTokenParam {
|
||
// 客户端类型:web, app, wechat
|
||
clientType?: string;
|
||
// 设备信息
|
||
deviceInfo?: string;
|
||
// 过期时间(分钟)
|
||
expireMinutes?: number;
|
||
}
|
||
|
||
// 生成扫码token响应
|
||
export interface GenerateQRTokenResult {
|
||
// 扫码token
|
||
token: string;
|
||
// 二维码内容(通常是包含token的URL或JSON)
|
||
qrContent: string;
|
||
// 过期时间戳
|
||
expireTime: number;
|
||
// 二维码图片URL(可选)
|
||
qrImageUrl?: string;
|
||
}
|
||
|
||
// 扫码状态枚举
|
||
export enum QRLoginStatus {
|
||
PENDING = 'pending', // 等待扫码
|
||
SCANNED = 'scanned', // 已扫码,等待确认
|
||
CONFIRMED = 'confirmed', // 已确认登录
|
||
EXPIRED = 'expired', // 已过期
|
||
CANCELLED = 'cancelled' // 已取消
|
||
}
|
||
|
||
// 检查扫码状态响应
|
||
export interface QRLoginStatusResult {
|
||
// 当前状态
|
||
status: QRLoginStatus;
|
||
// 状态描述
|
||
message?: string;
|
||
// 如果已确认登录,返回JWT token
|
||
accessToken?: string;
|
||
// 用户信息
|
||
userInfo?: {
|
||
userId: number;
|
||
nickname?: string;
|
||
avatar?: string;
|
||
phone?: string;
|
||
};
|
||
// 剩余有效时间(秒)
|
||
remainingTime?: number;
|
||
}
|
||
|
||
// 确认登录请求参数
|
||
export interface ConfirmLoginParam {
|
||
// 扫码token
|
||
token: string;
|
||
// 用户ID
|
||
userId: number;
|
||
// 登录平台:web, app, wechat
|
||
platform?: string;
|
||
// 微信用户信息(当platform为wechat时)
|
||
wechatInfo?: {
|
||
openid?: string;
|
||
unionid?: string;
|
||
nickname?: string;
|
||
avatar?: string;
|
||
gender?: string;
|
||
};
|
||
// 设备信息
|
||
deviceInfo?: string;
|
||
}
|
||
|
||
// 确认登录响应
|
||
export interface ConfirmLoginResult {
|
||
// 是否成功
|
||
success: boolean;
|
||
// 消息
|
||
message: string;
|
||
// 登录用户信息
|
||
userInfo?: {
|
||
userId: number;
|
||
nickname?: string;
|
||
avatar?: string;
|
||
phone?: string;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 生成扫码登录token
|
||
*/
|
||
export async function generateQRToken(data?: GenerateQRTokenParam) {
|
||
const res = await request.post<ApiResult<GenerateQRTokenResult>>(
|
||
SERVER_API_URL + '/qr-login/generate',
|
||
{
|
||
clientType: 'wechat',
|
||
expireMinutes: 5,
|
||
...data
|
||
}
|
||
);
|
||
if (res.code === 0 && res.data) {
|
||
return res.data;
|
||
}
|
||
return Promise.reject(new Error(res.message));
|
||
}
|
||
|
||
/**
|
||
* 检查扫码登录状态
|
||
*/
|
||
export async function checkQRLoginStatus(token: string) {
|
||
const res = await request.get<ApiResult<QRLoginStatusResult>>(
|
||
SERVER_API_URL + `/qr-login/status/${token}`
|
||
);
|
||
if (res.code === 0 && res.data) {
|
||
return res.data;
|
||
}
|
||
return Promise.reject(new Error(res.message));
|
||
}
|
||
|
||
/**
|
||
* 确认扫码登录(通用接口)
|
||
*/
|
||
export async function confirmQRLogin(data: ConfirmLoginParam) {
|
||
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
||
SERVER_API_URL + '/qr-login/confirm',
|
||
data
|
||
);
|
||
if (res.code === 0 && res.data) {
|
||
return res.data;
|
||
}
|
||
return Promise.reject(new Error(res.message));
|
||
}
|
||
|
||
/**
|
||
* 微信小程序扫码登录确认(便捷接口)
|
||
*/
|
||
export async function confirmWechatQRLogin(token: string, userId: number) {
|
||
try {
|
||
// 获取微信用户信息
|
||
const userInfo = await getUserInfo();
|
||
console.log('获取微信用户信息3:', userInfo)
|
||
|
||
const data: ConfirmLoginParam = {
|
||
token,
|
||
userId,
|
||
platform: 'wechat',
|
||
wechatInfo: {
|
||
nickname: userInfo?.nickname,
|
||
avatar: userInfo?.avatar,
|
||
gender: userInfo?.sex
|
||
},
|
||
deviceInfo: await getDeviceInfo()
|
||
};
|
||
|
||
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
||
SERVER_API_URL + '/qr-login/confirm',
|
||
data
|
||
);
|
||
console.log(res,'ConfirmLoginParamResult>')
|
||
if (res.code === 0 && res.data) {
|
||
return res.data;
|
||
}
|
||
return Promise.reject(new Error(res.message));
|
||
} catch (error: any) {
|
||
return Promise.reject(new Error(error.message || '确认登录失败'));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取微信用户信息
|
||
*/
|
||
async function getUserProfile() {
|
||
return new Promise<any>((resolve, reject) => {
|
||
Taro.getUserProfile({
|
||
desc: '用于扫码登录身份确认',
|
||
success: (res) => {
|
||
resolve(res.userInfo);
|
||
},
|
||
fail: (err) => {
|
||
// 如果获取失败,尝试使用已存储的用户信息
|
||
const storedUser = Taro.getStorageSync('User');
|
||
if (storedUser) {
|
||
resolve({
|
||
nickName: storedUser.nickname,
|
||
avatarUrl: storedUser.avatar,
|
||
gender: storedUser.gender
|
||
});
|
||
} else {
|
||
reject(err);
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取设备信息
|
||
*/
|
||
async function getDeviceInfo() {
|
||
return new Promise<string>((resolve) => {
|
||
Taro.getSystemInfo({
|
||
success: (res) => {
|
||
const deviceInfo = {
|
||
platform: res.platform,
|
||
system: res.system,
|
||
version: res.version,
|
||
model: res.model,
|
||
brand: res.brand,
|
||
screenWidth: res.screenWidth,
|
||
screenHeight: res.screenHeight
|
||
};
|
||
resolve(JSON.stringify(deviceInfo));
|
||
},
|
||
fail: () => {
|
||
resolve('unknown');
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 解析二维码内容,提取token
|
||
*/
|
||
export function parseQRContent(qrContent: string): string | null {
|
||
try {
|
||
console.log('解析二维码内容1:', qrContent);
|
||
|
||
// 尝试解析JSON格式
|
||
if (qrContent.startsWith('{')) {
|
||
const parsed = JSON.parse(qrContent);
|
||
return parsed.token || parsed.qrCodeKey || null;
|
||
}
|
||
|
||
// 尝试解析URL格式
|
||
if (qrContent.includes('http')) {
|
||
const url = new URL(qrContent);
|
||
// 支持多种参数名
|
||
return url.searchParams.get('token') ||
|
||
url.searchParams.get('qrCodeKey') ||
|
||
url.searchParams.get('qr_code_key') ||
|
||
null;
|
||
}
|
||
|
||
// 尝试解析简单的key=value格式
|
||
if (qrContent.includes('=')) {
|
||
const params = new URLSearchParams(qrContent);
|
||
return params.get('token') ||
|
||
params.get('qrCodeKey') ||
|
||
params.get('qr_code_key') ||
|
||
null;
|
||
}
|
||
|
||
// 如果是以qr-login:开头的格式
|
||
if (qrContent.startsWith('qr-login:')) {
|
||
return qrContent.replace('qr-login:', '');
|
||
}
|
||
|
||
// 直接返回内容作为token(如果是32位以上的字符串)
|
||
if (qrContent.length >= 32) {
|
||
return qrContent;
|
||
}
|
||
|
||
return null;
|
||
} catch (error) {
|
||
console.error('解析二维码内容失败:', error);
|
||
return null;
|
||
}
|
||
}
|