login_qr_cubit.dart 7.14 KB
import 'dart:convert';
import 'dart:typed_data';

import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import 'package:crypto/crypto.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluwx/fluwx.dart';
import 'package:shared_preferences/shared_preferences.dart';

class LoginQrState extends Equatable {
  final int status;
  final Uint8List? image;
  final String tip;
  final bool showSnackBar;
  final String snackBarMsg;

  const LoginQrState({
    this.status = 0,
    this.image,
    this.tip = '',
    this.showSnackBar = false,
    this.snackBarMsg = '',
  });

  LoginQrState copyWith({
    int? status,
    Uint8List? image,
    String? tip,
    bool? showSnackBar,
    String? snackBarMsg,
  }) {
    return LoginQrState(
      status: status ?? this.status,
      image: image ?? this.image,
      tip: tip ?? this.tip,
      showSnackBar: showSnackBar ?? this.showSnackBar,
      snackBarMsg: snackBarMsg ?? this.snackBarMsg,
    );
  }

  @override
  List<Object?> get props => [
        status,
        image,
        tip,
        showSnackBar,
        snackBarMsg,
      ];
}

class LoginQrCubit extends Cubit<LoginQrState> {
  late final Fluwx _fluwx;
  late final FluwxCancelable _fluwxCancelable;
  late final WechatAuthRepository _wechatAuthRepository;

  LoginQrCubit(super.initialState) {
    _fluwx = getIt.get<Fluwx>();
    _fluwxCancelable = _fluwx.addSubscriber(_responseListener);
    _wechatAuthRepository = getIt.get<WechatAuthRepository>();

    init();
  }

  Future<void> init() async {
    // sdk_ticket
    var resultData = await _wechatAuthRepository.getTicket() as Map<String, dynamic>?;
    // 请求接口异常
    if (resultData == null) {
      emit(state.copyWith(showSnackBar: true, snackBarMsg: '生成二维码失败'));
      emit(state.copyWith(showSnackBar: false));
      return;
    }

    // 状态码错误
    if (resultData['resultCode'] != '001') {
      emit(state.copyWith(showSnackBar: true, snackBarMsg: '生成二维码状态错误'));
      emit(state.copyWith(showSnackBar: false));
      return;
    }

    var sdkTicket = resultData['data'];

    // 当前时间戳
    var timestamp = DateTime.now().millisecondsSinceEpoch;
    var signature = _sig(Constant.wxAppId, '$timestamp', sdkTicket, '$timestamp');

    var authResult = await _fluwx.authBy(
      which: QRCode(
        appId: Constant.wxAppId,
        scope: 'snsapi_userinfo',
        nonceStr: '$timestamp',
        timestamp: '$timestamp',
        signature: signature,
      ),
    );

    if (!authResult) {
      emit(state.copyWith(showSnackBar: true, snackBarMsg: '请求微信失败'));
      emit(state.copyWith(showSnackBar: false));
      return;
    }
  }

  void _responseListener(WeChatResponse response) async {
    if (response is WeChatAuthGotQRCodeResponse) {
      int? errCode = response.errCode;
      if (errCode != null && errCode == 0) {
        emit(state.copyWith(
          status: 1,
          image: response.qrCode,
          tip: '打开微信,扫描二维码登录',
        ));
      } else {
        emit(state.copyWith(tip: '错误 $errCode'));
      }
    } else if (response is WeChatQRCodeScannedResponse) {
      int? errCode = response.errCode;
      if (errCode != null && errCode == 0) {
        emit(state.copyWith(
          status: 2,
          tip: '在微信中轻触允许即可登录',
        ));
      } else {
        emit(state.copyWith(tip: '错误 $errCode'));
      }
    } else if (response is WeChatAuthByQRCodeFinishedResponse) {
      int? errCode = response.errCode;
      if (errCode != null && errCode == 0) {
        _doLogin(response.authCode!);
      } else {
        emit(state.copyWith(
          status: 3,
          tip: '你已取消此次登录',
        ));
      }
    }
  }

  String _sig(String appId, String nonceStr, String sdkTicket, String timestamp) {
    String str = 'appid=$appId&noncestr=$nonceStr&sdk_ticket=$sdkTicket&timestamp=$timestamp';
    return sha1.convert(utf8.encode(str)).toString();
  }

  Future<void> _doLogin(String code) async {
    var resultData = await _wechatAuthRepository.codeToSk(code) as Map<String, dynamic>?;

    // 请求接口异常
    if (resultData == null) {
      emit(state.copyWith(showSnackBar: true, snackBarMsg: '登录请求处理失败'));
      emit(state.copyWith(showSnackBar: false));
      return;
    }

    // 状态码错误
    if (resultData['resultCode'] != '001') {
      emit(state.copyWith(showSnackBar: true, snackBarMsg: '登录请求状态失败'));
      emit(state.copyWith(showSnackBar: false));
      return;
    }

    var data = resultData['data'] as Map<String, dynamic>;
    var roles = data['roles'];
    // 过滤出家长角色的数据
    roles.removeWhere((element) => element['userType'] != 2);

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

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

    var sharedPreferences = getIt.get<SharedPreferences>();
    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);

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

  void goLoginMain() {
    router.go('/loginMain');
  }

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