529 lines
13 KiB
TypeScript
529 lines
13 KiB
TypeScript
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;
|
||
};
|