Files
mini-programs/src/services/loginService.ts
2026-02-08 12:29:48 +08:00

529 lines
13 KiB
TypeScript
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.
import Taro from "@tarojs/taro";
import httpService, { ApiResponse } from "./httpService";
import tokenManager from "../utils/tokenManager";
import { useUser } from "@/store/userStore";
// 微信用户信息接口
export interface WechatUserInfo {
id: string;
nickname: string;
avatar: string;
gender: number;
city: string;
province: string;
country: string;
}
// 登录响应接口
export interface LoginResponse {
success: boolean;
message: string;
token?: string;
user_info?: WechatUserInfo;
phone_update_status?: string;
existing_phone?: string;
}
// 发送短信响应接口
export interface SmsResponse {
success: boolean;
message: string;
}
// 验证验证码响应接口
export interface VerifyCodeResponse {
success: boolean;
message: string;
token?: string;
user_info?: WechatUserInfo;
}
// 用户详细信息
export interface UserStats {
followers_count: number;
following_count: number;
hosted_games_count: number;
participated_games_count: number;
}
// 手机号验证码登录接口参数
export interface PhoneLoginParams {
phone: string;
verification_code: string;
user_code: string;
}
export interface UserInfoType {
subscribe_time: string;
last_login_time: string;
create_time: string;
last_modify_time: string;
id: number;
openid: string;
user_code: string | null;
unionid: string;
session_key: string;
nickname: string;
ntrp_level: string;
occupation: string | null;
avatar_url: string;
gender: string;
country: string;
province: string;
city: string;
language: string;
phone: string;
personal_profile: string | null;
is_subscribed: string; // 如果只会是 "0" | "1",也可以写成字面量联合类型
latitude: number;
longitude: number;
stats: UserStats;
}
// 微信授权登录
// phone_code: 可选参数,如果提供则绑定手机号,否则只进行微信授权
export const wechat_auth_login = async (
phone_code?: string
): Promise<LoginResponse> => {
try {
// 先进行微信登录获取code
const login_result = await Taro.login();
if (!login_result.code) {
return {
success: false,
message: "微信登录失败",
};
}
// 构建请求参数
const requestData: any = {
code: login_result.code,
};
// 只有在提供 phone_code 时才添加到请求参数中
if (phone_code) {
requestData.phone_code = phone_code;
}
// 使用 httpService 调用微信授权接口
const auth_response = await httpService.post("user/wx_auth", requestData);
if (auth_response.code === 0) {
return {
success: true,
message: "微信登录成功",
token: auth_response.data?.token || "",
user_info: auth_response.data?.userInfo,
};
} else {
return {
success: false,
message: auth_response.message || "微信授权失败",
};
}
} catch (error) {
console.error("微信授权登录失败:", error);
return {
success: false,
message: "微信授权失败,请重试",
};
}
};
// 手机号验证码登录接口参数
export interface PhoneLoginParams {
phone: string;
verification_code: string;
user_code: string;
}
// 更新手机号接口参数
export interface ChangePhoneParams {
phone: string;
}
// 手机号验证码登录
export const phone_auth_login = async (
params: PhoneLoginParams
): Promise<LoginResponse> => {
try {
// 使用 httpService 调用验证验证码接口
const verify_response = await httpService.post("user/sms/verify", {
phone: params.phone,
code: params.verification_code,
user_code: params.user_code,
});
if (verify_response.code === 0) {
// 登录成功后,更新用户信息到 store
try {
await useUser.getState().fetchUserInfo();
await useUser.getState().checkNicknameChangeStatus();
} catch (error) {
console.error("更新用户信息到 store 失败:", error);
}
return {
success: true,
message: "登录成功",
token: verify_response.data?.token,
user_info: verify_response.data?.userInfo,
phone_update_status: verify_response.data?.phone_update_status,
existing_phone: verify_response.data?.existing_phone,
};
} else {
return {
success: false,
message: verify_response.message || "验证码错误",
};
}
} catch (error) {
console.error("手机号登录失败:", error);
return {
success: false,
message: error.message,
};
}
};
// 发送短信验证码
export const send_sms_code = async (phone: string): Promise<SmsResponse> => {
try {
const response = await httpService.post("user/sms/send", {
phone: phone,
});
// 修复响应检查逻辑:检查 code === 0 或 success === true
if (response.code === 0 || response.success === true) {
return {
success: true,
message: "验证码发送成功",
};
} else {
return {
success: false,
message: response.message || "验证码发送失败",
};
}
} catch (error) {
console.error("发送短信失败:", error);
return {
success: false,
message: error.message,
};
}
};
// 验证短信验证码
export const verify_sms_code = async (
phone: string,
code: string
): Promise<VerifyCodeResponse> => {
try {
const response = await httpService.post("user/sms/verify", {
phone: phone,
code: code,
});
return {
success: response.success,
message: response.message || "验证失败",
token: response.data?.token,
user_info: response.data?.userInfo,
};
} catch (error) {
console.error("验证验证码失败:", error);
return {
success: false,
message: error.message,
};
}
};
// 保存用户登录状态
export const save_login_state = (token: string, user_info: WechatUserInfo) => {
try {
// 使用 tokenManager 保存令牌信息设置24小时过期
const expires_at = Date.now() + 24 * 60 * 60 * 1000; // 24小时后过期
tokenManager.setToken({
accessToken: token,
expiresAt: expires_at,
});
// 保存用户信息
Taro.setStorageSync("user_info", JSON.stringify(user_info));
Taro.setStorageSync("is_logged_in", true);
Taro.setStorageSync("login_time", Date.now());
} catch (error) {
console.error("保存登录状态失败:", error);
}
};
// 清除登录状态
export const clear_login_state = () => {
try {
// 使用 tokenManager 清除令牌
// tokenManager.clearTokens();
// 清除其他登录状态
Taro.removeStorageSync("user_info");
Taro.removeStorageSync("is_logged_in");
Taro.removeStorageSync("login_time");
} catch (error) {
console.error("清除登录状态失败:", error);
}
};
// 检查是否已登录
export const check_login_status = (): boolean => {
try {
// 使用 tokenManager 检查令牌有效性
if (!tokenManager.hasValidToken()) {
clear_login_state();
return false;
}
const is_logged_in = Taro.getStorageSync("is_logged_in");
return !!is_logged_in;
} catch (error) {
return false;
}
};
// 检查令牌是否需要刷新剩余时间少于1小时时
export const should_refresh_token = (): boolean => {
try {
const remaining_time = tokenManager.getTokenRemainingTime();
const one_hour = 60 * 60 * 1000; // 1小时
return remaining_time > 0 && remaining_time < one_hour;
} catch (error) {
return false;
}
};
// 获取令牌状态信息
export const get_token_status = () => {
try {
const is_valid = tokenManager.hasValidToken();
const remaining_time = tokenManager.getTokenRemainingTime();
const is_expired = tokenManager.isTokenExpired();
return {
is_valid,
remaining_time,
is_expired,
expires_in_minutes: Math.floor(remaining_time / (60 * 1000)),
};
} catch (error) {
return {
is_valid: false,
remaining_time: 0,
is_expired: true,
expires_in_minutes: 0,
};
}
};
// 获取用户信息
export const get_user_info = (): WechatUserInfo | null => {
try {
let userinfo = Taro.getStorageSync("user_info");
if (userinfo) {
return JSON.parse(userinfo);
}
return null;
} catch (error) {
return null;
}
};
// 获取用户token
export const get_user_token = (): string | null => {
try {
// 使用 tokenManager 获取令牌
return tokenManager.getAccessToken();
} catch (error) {
return null;
}
};
// 检查微信登录状态
export const check_wechat_login = async (): Promise<boolean> => {
try {
const check_result = await Taro.checkSession();
// Taro.checkSession 返回的是 { errMsg: string }
return check_result.errMsg === "checkSession:ok";
} catch (error) {
return false;
}
};
// 刷新登录状态
export const refresh_login_status = async (): Promise<boolean> => {
try {
// 检查微信登录状态
const is_valid = await check_wechat_login();
if (!is_valid) {
// 微信登录已过期,需要重新登录
clear_login_state();
return false;
}
// 检查本地存储的登录状态
return check_login_status();
} catch (error) {
console.error("刷新登录状态失败:", error);
return false;
}
};
// 更新用户手机号
export const updateUserPhone = async (payload: ChangePhoneParams) => {
try {
const response = await httpService.post("/user/update_phone", payload);
return response;
} catch (error) {
console.error("更新用户手机号失败:", error);
throw error;
}
};
// 获取指定用户信息
export const getUserInfoById = async (id) => {
try {
const response = await httpService.post(
"/user/detail_by_id",
{ id },
{
showLoading: false,
showToast: false, // 不显示错误弹窗
}
);
return response;
} catch (error) {
console.error("获取用户信息失败:", error);
throw error;
}
};
// 关注用户
export const followUser = async (following_id) => {
try {
const response = await httpService.post("/wch_users/follow", {
following_id,
});
return response;
} catch (error) {
console.error("关注失败:", error);
throw error;
}
};
// 取消关注用户
export const unFollowUser = async (following_id) => {
try {
const response = await httpService.post("/wch_users/unfollow", {
following_id,
});
return response;
} catch (error) {
console.error("取消关注失败:", error);
throw error;
}
};
// 静默登录锁,防止并发调用
let silentLoginPromise: Promise<LoginResponse> | null = null;
// 静默登录只获取用户ID和token不需要手机号
export const silentLogin = async (): Promise<LoginResponse> => {
// 如果已经有正在进行的静默登录,直接返回该 Promise
if (silentLoginPromise) {
console.log("静默登录正在进行中,等待结果...");
return silentLoginPromise;
}
// 先检查是否已经登录
if (check_login_status()) {
console.log("已登录,跳过静默登录");
return {
success: true,
message: "已登录",
token: get_user_token() || undefined,
user_info: get_user_info() || undefined,
};
}
// 创建静默登录 Promise
silentLoginPromise = (async (): Promise<LoginResponse> => {
try {
console.log("开始执行静默登录...");
// 调用微信登录获取code
const login_result = await Taro.login();
console.log("微信登录结果:", login_result);
if (!login_result.code) {
console.error("微信登录失败未获取到code");
return {
success: false,
message: "微信登录失败",
};
}
// 调用微信授权接口,不传 phone_code静默登录
console.log("调用后端接口进行静默登录...");
const auth_response = await httpService.post(
"user/wx_auth",
{
code: login_result.code,
// 不传 phone_code实现静默登录
},
{
showLoading: false, // 静默登录不显示loading
}
);
console.log("后端接口响应:", auth_response);
if (auth_response.code === 0) {
const token = auth_response.data?.token || "";
const user_info = auth_response.data?.userInfo;
// 保存登录状态
if (token && user_info) {
console.log("保存登录状态...");
save_login_state(token, user_info);
console.log("静默登录成功,已保存登录状态");
} else {
console.warn("静默登录成功但token或user_info为空");
}
return {
success: true,
message: "静默登录成功",
token,
user_info,
};
} else {
console.error("静默登录失败:", auth_response.message);
return {
success: false,
message: auth_response.message || "静默登录失败",
};
}
} catch (error) {
console.error("静默登录异常:", error);
return {
success: false,
message: "静默登录失败,请重试",
};
} finally {
// 清除锁,允许下次调用
silentLoginPromise = null;
}
})();
return silentLoginPromise;
};