Files
WXProgram/miniprogram/pages/index/modules/loginModule.ts
2025-10-21 21:51:51 +08:00

513 lines
16 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 { showToast } from '../../../utils/helpers';
import { UserInfo } from '../../../types';
import userService from '../../../services/userService';
import locationTrackingService from '../../../services/locationTrackingService';
import { DataModule } from './dataModule';
export class LoginModule {
private dataModule: DataModule;
private pageContext: any;
constructor(pageContext: any, dataModule: DataModule) {
this.pageContext = pageContext;
this.dataModule = dataModule;
}
/**
* 处理用户信息
*/
public async processUserInfo(userInfo: UserInfo): Promise<UserInfo> {
// 直接返回用户信息,不进行额外处理
return userInfo;
}
/**
* 登录成功后更新页面状态
*/
public updatePageAfterLogin(userInfo: UserInfo): void {
this.dataModule.updateUserInfo(userInfo);
}
/**
* 检查登录状态
*/
public checkLoginStatus(): boolean {
const globalUserInfo = userService.getGlobalUserInfo();
if (globalUserInfo) {
this.dataModule.updateUserInfo(globalUserInfo);
return true;
}
return false;
}
/**
* 用户退出登录
*/
public async logout(): Promise<void> {
try {
// 停止位置追踪服务
try {
await locationTrackingService.stopTracking();
console.log('位置追踪服务已停止');
} catch (trackingError) {
console.warn('停止位置追踪失败,但不影响退出登录:', trackingError);
}
// 注意这里不调用userService.logout()因为服务器端的logout接口会删除token
// 而用户只是签退不接单不是完全退出应用需要保持token有效
console.log('用户已退出登录本地签退保持token有效');
showToast('已退出登录');
} catch (error) {
console.error('退出登录失败:', error);
}
}
/**
* 显示手动登录模态框:如果静默登录失败后,让用户尝试手动登录
*/
public async showManualLoginModal(): Promise<boolean> {
console.log('显示手动登录模态框');
return new Promise((resolve) => {
wx.showModal({
title: '手动登录',
content: '静默登录失败,请手动登录以使用完整功能',
confirmText: '手动登录',
cancelText: '暂不',
success: async (res) => {
if (res.confirm) {
// 用户点击确定,执行手动登录
console.log('用户选择手动登录');
const success = await this.performLogin();
if (!success) {
// 登录失败,再次提供选项
this.handleLoginFailure(resolve);
} else {
resolve(success);
}
} else {
// 用户取消登录,显示关闭小程序选项
console.log('用户选择暂不登录');
this.showCloseAppOption(resolve);
}
}
});
});
}
/**
* 执行登录流程 - 调用userService的登录方法
* 静默登录失败后,只专注于登录本身,不涉及绑定、签到等复杂逻辑
*/
private async performLogin(): Promise<boolean> {
try {
// 执行微信登录
const result = await userService.wxLogin();
if (result.success && result.userInfo) {
// 登录成功,更新页面状态
this.updatePageAfterLogin(result.userInfo);
console.log('手动登录成功');
return true;
}
console.log('手动登录失败');
return false;
} catch (error) {
console.error('执行登录流程失败:', error);
return false;
}
}
/**
* 处理登录失败的情况
*/
public async handleLoginFailure(resolve: (value: boolean) => void) {
console.log('登录失败,显示重试选项');
wx.showModal({
title: '登录失败',
content: '登录遇到问题,是否重试?',
confirmText: '重新登录',
cancelText: '取消并关闭',
success: async (res) => {
if (res.confirm) {
console.log('用户选择重新登录');
const success = await this.performLogin();
if (!success) {
// 再次登录失败,递归调用以避免嵌套过深
this.handleLoginFailure(resolve);
} else {
resolve(success);
}
} else {
console.log('用户选择取消并关闭小程序');
wx.showToast({
title: '即将退出小程序',
icon: 'none',
duration: 1500,
complete: () => {
setTimeout(() => {
// 使用类型断言解决类型问题
(wx as any).exitMiniProgram();
}, 1500);
}
});
resolve(false);
}
}
});
}
/**
* 显示关闭小程序选项
*/
public showCloseAppOption(resolve: (value: boolean) => void) {
console.log('显示关闭小程序选项');
wx.showModal({
title: '确认退出',
content: '不登录将无法使用完整功能,是否退出小程序?',
confirmText: '退出小程序',
cancelText: '留在页面',
success: (res) => {
if (res.confirm) {
console.log('用户确认退出小程序');
// 使用类型断言解决类型问题
(wx as any).exitMiniProgram();
resolve(false);
} else {
console.log('用户选择留在页面');
resolve(false);
}
}
});
}
/**
* 处理签退流程
*/
public async handleSignOut(): Promise<boolean> {
try {
// 显示加载中提示
wx.showLoading({
title: '签退中...',
});
// 调用签退接口
const signOutResult = await userService.signOut();
wx.hideLoading();
if (signOutResult.success) {
console.log('签退成功:', signOutResult);
wx.showToast({
title: '签退成功',
icon: 'success',
duration: 2000
});
// 停止位置追踪服务
try {
await locationTrackingService.stopTracking();
console.log('位置追踪服务已停止');
} catch (trackingError) {
console.warn('停止位置追踪失败,但不影响签退:', trackingError);
}
// 设置用户状态为已签退
if (this.pageContext && this.pageContext.setData) {
this.pageContext.setData({
'authStatus.userStatus': 'signed_out'
});
// 更新按钮显示状态
if (this.pageContext.updateButtonDisplayStatus) {
this.pageContext.updateButtonDisplayStatus();
}
}
return true;
} else {
console.warn('签退失败:', signOutResult.message);
wx.showToast({
title: signOutResult.message || '签退失败',
icon: 'none',
duration: 2000
});
return false;
}
} catch (error) {
console.error('签退过程中发生错误:', error);
wx.hideLoading();
wx.showToast({
title: '签退失败,请重试',
icon: 'none',
duration: 2000
});
return false;
}
}
/**
* 处理签到流程
*/
public async handleSignIn(): Promise<boolean> {
try {
// 显示加载中提示
wx.showLoading({
title: '签到中...',
});
// 先获取地图位置和当前时间
let initialLocation: { latitude: number; longitude: number; timestamp: number };
try {
const location = await locationTrackingService.getCurrentLocation();
// 使用秒级时间戳除以1000取整
initialLocation = {
latitude: location.latitude,
longitude: location.longitude,
timestamp: Math.floor(Date.now() / 1000)
};
console.log('获取到签到位置数据:', initialLocation);
} catch (error) {
console.error('获取位置失败:', error);
throw new Error('获取位置失败,请检查位置权限设置');
}
// 调用实际的签到接口,传递位置数据
const signInResult = await userService.signIn(initialLocation);
wx.hideLoading();
if (signInResult.success) {
console.log('签到成功:', signInResult);
wx.showToast({
title: '签到成功',
icon: 'success',
duration: 2000
});
// 更新用户信息
if (signInResult.employeeInfo) {
// 将员工信息合并到用户信息中
const app = getApp<any>();
if (app.globalData.userInfo) {
app.globalData.userInfo = {
...app.globalData.userInfo,
name: signInResult.employeeInfo.name,
phone: signInResult.employeeInfo.phone
};
this.updatePageAfterLogin(app.globalData.userInfo);
}
}
// 设置用户状态为已签到
if (this.pageContext && this.pageContext.setData) {
this.pageContext.setData({
'authStatus.userStatus': 'signed_in'
});
// 更新按钮显示状态
if (this.pageContext.updateButtonDisplayStatus) {
this.pageContext.updateButtonDisplayStatus();
}
}
// 启动位置追踪服务WebSocket连接
try {
// 启动位置追踪服务建立WebSocket连接
await locationTrackingService.startTracking();
console.log('位置追踪服务已启动');
} catch (trackingError) {
console.warn('启动位置追踪失败,但不影响签到:', trackingError);
}
// 加载业务数据(所有登录用户)
try {
console.log('用户签到成功,开始加载业务数据');
// 获取主页面模块并加载业务数据
const mainPageModule = this.pageContext.data.mainPageModule;
if (mainPageModule && mainPageModule.loadBusinessData) {
await mainPageModule.loadBusinessData();
console.log('业务数据加载完成');
}
} catch (businessError) {
console.warn('加载业务数据失败,但不影响签到:', businessError);
}
// 如果是管理员用户,加载员工数据
if (signInResult.employeeInfo && signInResult.employeeInfo.role === 'ADMIN') {
try {
console.log('管理员用户签到成功,开始加载员工数据');
// 获取主页面模块并加载员工数据
const mainPageModule = this.pageContext.data.mainPageModule;
if (mainPageModule && mainPageModule.loadEmployeeData) {
await mainPageModule.loadEmployeeData();
console.log('员工数据加载完成');
}
} catch (employeeError) {
console.warn('加载员工数据失败,但不影响签到:', employeeError);
}
}
return true;
} else {
console.warn('签到失败:', signInResult.message);
wx.showToast({
title: signInResult.message || '签到失败',
icon: 'none',
duration: 2000
});
return false;
}
} catch (error) {
console.error('签到过程中发生错误:', error);
wx.hideLoading();
wx.showToast({
title: '签到失败,请重试',
icon: 'none',
duration: 2000
});
return false;
}
}
/**
* 处理授权登录流程
*/
public async handleAuthLogin(): Promise<boolean> {
try {
const success = await this.showManualLoginModal();
if (success) {
console.log('手动登录成功');
return true;
} else {
console.log('用户取消手动登录');
return false;
}
} catch (error) {
console.error('手动登录过程中发生错误:', error);
return false;
}
}
/**
* 判断用户是否为游客(已登录但信息不完整)
*/
public isTourist(): boolean {
const app = getApp<any>();
// 游客定义:已登录但没有完善基本信息(姓名和电话)的用户
if (app.globalData.isLoggedIn && app.globalData.userInfo) {
const userInfo = app.globalData.userInfo;
return !userInfo.name || !userInfo.phone;
}
return false;
}
/**
* 计算是否显示签到按钮(基于用户状态和角色)
*/
public shouldShowSignInButton(): boolean {
// 从页面上下文中获取数据
const pageData = this.dataModule.getData();
const authStatus = pageData.authStatus || {};
const userInfo = pageData.userInfo;
// 显示条件已获取微信code、用户状态不是已签到、且用户不是游客已绑定用户
const result = (
authStatus.hasWxCode &&
(authStatus.userStatus === 'registered' || authStatus.userStatus === 'signed_out') &&
userInfo !== null &&
userInfo.role !== 'GUEST'
);
return result;
}
/**
* 计算是否显示注册按钮(基于用户状态和角色)
*/
public shouldShowRegisterButton(): boolean {
// 从页面上下文中获取数据
const pageData = this.dataModule.getData();
const authStatus = pageData.authStatus || {};
const userInfo = pageData.userInfo;
// 显示条件已获取微信code、用户状态为未绑定、且用户是游客
const result = (
authStatus.hasWxCode &&
authStatus.userStatus === 'unregistered' &&
userInfo !== null &&
userInfo.role === 'GUEST'
);
// 调试信息已删除
return result;
}
/**
* 根据用户信息确定用户状态(从服务器获取真实状态)
*/
public async determineUserStatus(userInfo: any): Promise<'registered' | 'unregistered' | 'signed_in' | 'signed_out'> {
if (!userInfo) return 'signed_out';
try {
// 从服务器获取用户真实状态
const serverStatus = await this.getUserStatusFromServer();
if (serverStatus) {
console.log('从服务器获取的用户状态:', serverStatus);
return serverStatus;
}
// 如果服务器获取失败,使用本地逻辑作为降级方案
console.warn('服务器状态获取失败,使用本地逻辑判断');
const isRegistered = userInfo.name && userInfo.phone;
return isRegistered ? 'registered' : 'unregistered';
} catch (error) {
console.error('获取用户状态失败:', error);
// 网络错误时使用本地逻辑
const isRegistered = userInfo.name && userInfo.phone;
return isRegistered ? 'registered' : 'unregistered';
}
}
/**
* 从服务器获取用户签到状态
*/
private async getUserStatusFromServer(): Promise<'registered' | 'unregistered' | 'signed_in' | 'signed_out' | null> {
try {
// 调用服务器接口获取用户状态
const response = await userService.getUserStatus();
// 如果返回null表示服务器接口不存在
if (response === null) {
console.log('服务器状态接口不存在,跳过服务器状态获取');
return null;
}
if (response && response.status) {
// 根据服务器返回的状态映射到前端状态
switch (response.status) {
case 'signed_in':
return 'signed_in';
case 'signed_out':
return 'signed_out';
case 'registered':
return 'registered';
case 'unregistered':
return 'unregistered';
default:
return null;
}
}
return null;
} catch (error) {
console.error('获取服务器状态失败:', error);
return null;
}
}
}