login_main_cubit.dart 10.1 KB
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:appframe/data/repositories/user_auth_repository.dart';
import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx/fluwx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';

class LoginMainState extends Equatable {
  final bool agreed;
  final bool showAgreed;
  final bool showNeedWechatForApple;
  final int loginType; // 1=Wechat,2=Apple
  final String appleUserIdentifier;
  final bool loading;
  final bool wechatInstalled;

  const LoginMainState({
    this.agreed = false,
    this.showAgreed = false,
    this.showNeedWechatForApple = false,
    this.loginType = 0,
    this.appleUserIdentifier = '',
    this.loading = false,
    this.wechatInstalled = false,
  });

  LoginMainState copyWith({
    bool? agreed,
    bool? showAgreed,
    bool? showNeedWechatForApple,
    int? loginType,
    String? appleUserIdentifier,
    bool? loading,
    bool? wechatInstalled,
  }) {
    return LoginMainState(
      agreed: agreed ?? this.agreed,
      showAgreed: showAgreed ?? this.showAgreed,
      showNeedWechatForApple: showNeedWechatForApple ?? this.showNeedWechatForApple,
      loginType: loginType ?? this.loginType,
      appleUserIdentifier: appleUserIdentifier ?? this.appleUserIdentifier,
      loading: loading ?? this.loading,
      wechatInstalled: wechatInstalled ?? this.wechatInstalled,
    );
  }

  @override
  List<Object?> get props => [
        agreed,
        showAgreed,
        showNeedWechatForApple,
        loginType,
        appleUserIdentifier,
        loading,
        wechatInstalled,
      ];
}

class LoginMainCubit extends Cubit<LoginMainState> {
  late final Fluwx _fluwx;
  late final FluwxCancelable _fluwxCancelable;
  late final WechatAuthRepository _wechatAuthRepository;
  late final UserAuthRepository _userAuthRepository;

  LoginMainCubit(super.initialState) {
    _fluwx = getIt.get<Fluwx>();

    // 处理微信安装检测
    _fluwx.isWeChatInstalled.then((value) {
      emit(state.copyWith(wechatInstalled: value));
    });

    _fluwxCancelable = _fluwx.addSubscriber(_responseListener);
    _wechatAuthRepository = getIt.get<WechatAuthRepository>();
    _userAuthRepository = getIt.get<UserAuthRepository>();
  }

  void toggleAgreed(bool value) {
    emit(state.copyWith(agreed: value));
  }

  void confirmAgreed() {
    emit(state.copyWith(agreed: true, showAgreed: false));
    if (state.loginType == 1) {
      wechatAuth();
    } else if (state.loginType == 2) {
      appleAuth();
    }
  }

  void cancelAgreed() {
    emit(state.copyWith(showAgreed: false));
  }

  ///
  /// 通过 Apple 登录
  ///
  void appleAuth() async {
    emit(state.copyWith(loginType: 2));

    if (!state.agreed) {
      emit(state.copyWith(showAgreed: true));
      return;
    }

    AuthorizationCredentialAppleID credential = await SignInWithApple.getAppleIDCredential(
      scopes: [
        AppleIDAuthorizationScopes.email,
        AppleIDAuthorizationScopes.fullName,
      ],
    );

    debugPrint('用户唯一标识: ${credential.userIdentifier}');
    debugPrint('用户邮箱: ${credential.email}');
    debugPrint('用户姓名: ${credential.givenName} ${credential.familyName}');
    // 应将 credential.authorizationCode 发送给后端服务器,由后端与 Apple 服务器验证该码的有效性
    debugPrint('授权码 (authorizationCode): ${credential.authorizationCode}');
    debugPrint('身份令牌 (identityToken): ${credential.identityToken}');

    if (credential.userIdentifier == null) {
      Fluttertoast.showToast(msg: '授权失败', backgroundColor: Colors.red);
      return;
    }

    ///
    /// 处理 credential,发送到服务器获取用户信息。有关联用户信息,则登录成功;没有关联用户信息,则弹出提示框提示用户授权微信认证
    ///
    var resultData = await _userAuthRepository.appleLogin(
        credential.userIdentifier!, credential.authorizationCode, credential.identityToken!) as Map<String, dynamic>?;
    if (resultData == null) {
      Fluttertoast.showToast(msg: '登录请求处理失败', backgroundColor: Colors.red);
      return;
    }
    if (resultData['code'] != 0) {
      Fluttertoast.showToast(msg: resultData['error'], backgroundColor: Colors.red);
      return;
    }

    var data = resultData['data'] as Map<String, dynamic>;
    int binding = resultData['binding'];
    if (binding == 1) {
      _handleLoginSuccess(data);
    } else {
      // 未绑定时,也会返回 sessionCode 和 userCode
      if (state.wechatInstalled) {
        // 已安装微信APP,直接拉起微信授权,不使用 sessionCode 和 userCode
        // 设置 appleUserIdentifier 状态,通知用户需要授权微信认证
        emit(state.copyWith(appleUserIdentifier: data['appleUid']!, showNeedWechatForApple: true));
      } else {
        // 未安装微信APP,使用 sessionCode 和 userCode
        _handleLoginSuccess(data);
      }
    }
  }

  Future<void> wechatAuthForApple() async {
    emit(state.copyWith(showNeedWechatForApple: false, loginType: 2));

    var authResult = await _fluwx.authBy(
      which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_test'),
    );

    if (!authResult) {
      Fluttertoast.showToast(msg: '微信授权处理失败', backgroundColor: Colors.red);
      return;
    }

    // 控制显示加载框
    emit(state.copyWith(loading: true));
  }

  Future<void> wechatAuth() async {
    emit(state.copyWith(loginType: 1));

    if (!state.agreed) {
      emit(state.copyWith(showAgreed: true));
      return;
    }

    var authResult = await _fluwx.authBy(
      which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_sdk_test'),
    );

    if (!authResult) {
      Fluttertoast.showToast(msg: '微信授权处理失败', backgroundColor: Colors.red);
      return;
    }

    // 控制显示加载框
    emit(state.copyWith(loading: true));
  }

  void goLoginPhone() {
    router.go('/loginPhone');
  }

  void goLoginQr() {
    router.go('/loginQr');
  }

  void _responseListener(WeChatResponse response) async {
    if (response is WeChatAuthResponse) {
      if (response.code == null || response.code == '') {
        emit(state.copyWith(loading: false));
        return;
      }

      var resultData = await _wechatAuthRepository.codeToSk(response.code!) as Map<String, dynamic>?;

      // 请求接口异常
      if (resultData == null) {
        Fluttertoast.showToast(msg: '登录请求处理失败', backgroundColor: Colors.red);
        return;
      }

      // 状态码错误
      if (resultData['resultCode'] != '001') {
        Fluttertoast.showToast(msg: '登录请求状态失败', backgroundColor: Colors.red);
        return;
      }

      var data = resultData['data'] as Map<String, dynamic>;
      _handleLoginSuccess(data);
    }
  }

  void _handleLoginSuccess(Map<String, dynamic> data) {
    var roles = data['roles'];
    // 过滤出家长角色的数据
    if (roles?.isNotEmpty ?? false) {
      roles.removeWhere((element) => element['userType'] != 2);
    } else {
      roles = [];
    }

    var sessionCode = data['sessionCode'];
    var userCode = data['userCode'];
    var classCode = '';
    var userType = 0;
    var stuId = '';

    var sharedPreferences = getIt.get<SharedPreferences>();

    if (roles.isNotEmpty) {
      var role = roles[0];
      classCode = role['classCode'];
      userType = role['userType'];
      stuId = role['stuId'];

      List<String> classIdList = [];
      for (var role in roles) {
        classIdList.add(role['classCode'] as String);
      }
      debugPrint('classCodeIds:-------------- $classIdList');
      sharedPreferences.setStringList(Constant.classIdSetKey, classIdList);
    } else {
      sharedPreferences.setStringList(Constant.classIdSetKey, []);
    }

    var preUserCode = sharedPreferences.getString('pre_userCode') ?? '';
    if (userCode != preUserCode) {
      sharedPreferences.setString('pre_userCode', userCode);
      sharedPreferences.setString('pre_classCode', classCode);
      sharedPreferences.setInt('pre_userType', userType);
      sharedPreferences.setString('pre_stuId', stuId);
    } else {
      var preClassCode = sharedPreferences.getString('pre_classCode') ?? '';
      var preUserType = sharedPreferences.getInt('pre_userType') ?? 0;
      var preStuId = sharedPreferences.getString('pre_stuId') ?? '';

      if (preClassCode != '' &&
          roles.any((element) =>
              element['classCode'] == preClassCode &&
              element['userType'] == preUserType &&
              element['stuId'] == preStuId)) {
        classCode = preClassCode;
        userType = preUserType;
        stuId = preStuId;
      } else {
        sharedPreferences.setString('pre_userCode', userCode);
        sharedPreferences.setString('pre_classCode', classCode);
        sharedPreferences.setInt('pre_userType', userType);
        sharedPreferences.setString('pre_stuId', stuId);
      }
    }

    sharedPreferences.setString('auth_sessionCode', sessionCode);
    sharedPreferences.setString('auth_userCode', userCode);
    sharedPreferences.setString('auth_classCode', classCode);
    sharedPreferences.setInt('auth_userType', userType);
    sharedPreferences.setString('auth_stuId', stuId);

    debugPrint('loginType: ${state.loginType} appleUid: ${state.appleUserIdentifier}');
    // 针对 Apple 登录
    if (state.loginType == 2 && state.appleUserIdentifier.isNotEmpty) {
      // appleUserIdentifier未绑定,则进行绑定
      _userAuthRepository.newBinding(state.appleUserIdentifier, userCode);
    }

    router.go(
      '/web',
      extra: {
        'sessionCode': sessionCode,
        'userCode': userCode,
        'classCode': classCode,
        'userType': userType,
        'stuId': stuId,
      },
    );
  }

  @override
  Future<void> close() {
    _fluwxCancelable.cancel();
    return super.close();
  }
}