Commit bd25deab by tanghuan

游客登录、微信绑定、二维码扫码绑定

1 parent 6304208d
......@@ -6,6 +6,7 @@ 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:appframe/utils/login_util.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
......@@ -181,37 +182,44 @@ class LoginMainCubit extends Cubit<LoginMainState> with WidgetsBindingObserver {
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);
}
var visitor = binding == 1 ? 0 : 1;
if (visitor == 1) {
var sharedPreferences = getIt.get<SharedPreferences>();
sharedPreferences.setString('auth_visitor_type', 'apple');
sharedPreferences.setString('auth_visitor_id', credential.userIdentifier!);
}
LoginUtil.handleLoginSuccess(data, visitor, 'router');
// if (binding == 1) {
// _handleLoginSuccess(data, 0);
// } else {
// // 未绑定时,也会返回 sessionCode 和 userCode
// if (state.wechatInstalled) {
// // 已安装微信APP,直接拉起微信授权,不使用 sessionCode 和 userCode
// // 设置 appleUserIdentifier 状态,通知用户需要授权微信认证
// emit(state.copyWith(appleUserIdentifier: data['appleUid']!, showNeedWechatForApple: true));
// } else {
// // 未安装微信APP,使用 sessionCode 和 userCode
// _handleLoginSuccess(data, 1);
// }
// }
}
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: '微信授权处理失败', gravity: ToastGravity.TOP, backgroundColor: Colors.red);
return;
}
// 控制显示加载框,并启动等待微信授权回调的兜底机制
emit(state.copyWith(loading: true));
_startWechatAuthWaiting();
}
// 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: '微信授权处理失败', gravity: ToastGravity.TOP, backgroundColor: Colors.red);
// return;
// }
//
// // 控制显示加载框,并启动等待微信授权回调的兜底机制
// emit(state.copyWith(loading: true));
// _startWechatAuthWaiting();
// }
Future<void> wechatAuth() async {
emit(state.copyWith(loginType: 1));
......@@ -267,104 +275,10 @@ class LoginMainCubit extends Cubit<LoginMainState> with WidgetsBindingObserver {
}
var data = resultData['data'] as Map<String, dynamic>;
_handleLoginSuccess(data);
LoginUtil.handleLoginSuccess(data, 0, 'router');
}
}
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 className = '';
var stuName = '';
var relation = '';
var sharedPreferences = getIt.get<SharedPreferences>();
if (roles.isNotEmpty) {
var role = roles[0];
classCode = role['classCode'];
userType = role['userType'];
stuId = role['stuId'];
className = role['className'];
stuName = role['stuName'];
relation = role['relation'] ?? '';
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);
sharedPreferences.setString('auth_className', className);
sharedPreferences.setString('auth_stuName', stuName);
sharedPreferences.setString('auth_relation', relation);
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
void didChangeAppLifecycleState(AppLifecycleState appState) {
// App 回到前台时,如果仍在等待微信授权回调,给真实回调一个短窗口;
......
import 'dart:async';
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:appframe/data/repositories/phone_auth_repository.dart';
import 'package:appframe/utils/login_util.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
......@@ -97,7 +97,7 @@ class LoginPhoneCubit extends Cubit<LoginPhoneState> {
}
// 发送验证码
var result = await _phoneAuthRepository.verifyCode(phone, 0);
var result = await _phoneAuthRepository.verifyCode(phone, 1);
if (result['code'] != 0) {
Fluttertoast.showToast(msg: result['error'], gravity: ToastGravity.TOP, backgroundColor: Colors.red);
return;
......@@ -140,97 +140,15 @@ class LoginPhoneCubit extends Cubit<LoginPhoneState> {
}
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 className = '';
var stuName = '';
var relation = '';
var sharedPreferences = getIt.get<SharedPreferences>();
if (roles.isNotEmpty) {
var role = roles[0];
classCode = role['classCode'];
userType = role['userType'];
stuId = role['stuId'];
className = role['className'];
stuName = role['stuName'];
relation = role['relation'] ?? '';
// 将角色中的班级数据处理后,进行缓存
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, []);
int binding = resultData['binding'];
// binding=1 代表已绑定,不是游客
var visitor = binding == 1 ? 0 : 1;
if (visitor == 1) {
var sharedPreferences = getIt.get<SharedPreferences>();
sharedPreferences.setString('auth_visitor_type', 'phone');
sharedPreferences.setString('auth_visitor_id', phone);
}
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);
sharedPreferences.setString('auth_className', className);
sharedPreferences.setString('auth_stuName', stuName);
sharedPreferences.setString('auth_relation', relation);
router.go(
'/web',
extra: {
'sessionCode': sessionCode,
'userCode': userCode,
'classCode': classCode,
'userType': userType,
'stuId': stuId,
},
);
LoginUtil.handleLoginSuccess(data, visitor, 'router');
}
void toggleAgreed(bool value) {
......
......@@ -4,6 +4,7 @@ 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:appframe/utils/login_util.dart';
import 'package:crypto/crypto.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
......@@ -11,7 +12,6 @@ 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';
class LoginQrState extends Equatable {
final int status;
......@@ -157,97 +157,7 @@ class LoginQrCubit extends Cubit<LoginQrState> {
}
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 className = '';
var stuName = '';
var relation = '';
var sharedPreferences = getIt.get<SharedPreferences>();
if (roles.isNotEmpty) {
var role = roles[0];
classCode = role['classCode'];
userType = role['userType'];
stuId = role['stuId'];
className = role['className'];
stuName = role['stuName'];
relation = role['relation'];
// 将角色中的班级数据处理后,进行缓存
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);
sharedPreferences.setString('auth_className', className);
sharedPreferences.setString('auth_stuName', stuName);
sharedPreferences.setString('auth_relation', relation);
router.go(
'/web',
extra: {
'sessionCode': sessionCode,
'userCode': userCode,
'classCode': classCode,
'userType': userType,
'stuId': stuId,
},
);
LoginUtil.handleLoginSuccess(data, 0, 'router');
}
void goLoginMain() {
......
......@@ -6,6 +6,7 @@ import 'package:app_settings/app_settings.dart';
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:appframe/ui/widgets/wechat_qr_bind_dialog.dart';
import 'package:appframe/data/models/message/h5_message.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:appframe/services/im_service.dart';
......@@ -18,7 +19,6 @@ 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:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
......@@ -47,6 +47,8 @@ class WebState extends Equatable {
// 用来控制是否需要判断处理加群、退群等逻辑
final bool loginOpFlag;
// 没有对应身份的游客
final int? visitor;
final String? sessionCode;
final String? userCode;
final String? classCode;
......@@ -69,6 +71,9 @@ class WebState extends Equatable {
final bool chooseVideoCmdFlag;
final String chooseVideoCmdMessage;
/// wechatQrBindCmd
final bool wechatQrBindCmdFlag;
/// 用于测试监测问题
final String testMsg;
......@@ -85,6 +90,7 @@ class WebState extends Equatable {
this.showAppBar = true,
this.showBottomNavBar = false,
this.loginOpFlag = false,
this.visitor,
this.sessionCode,
this.userCode,
this.classCode,
......@@ -98,6 +104,7 @@ class WebState extends Equatable {
this.chooseImageCmdMessage = '',
this.chooseVideoCmdFlag = false,
this.chooseVideoCmdMessage = '',
this.wechatQrBindCmdFlag = false,
this.testMsg = '',
});
......@@ -114,6 +121,7 @@ class WebState extends Equatable {
bool? showAppBar,
bool? showBottomNavBar,
bool? loginOpFlag,
int? visitor,
String? sessionCode,
String? userCode,
String? classCode,
......@@ -127,6 +135,7 @@ class WebState extends Equatable {
String? chooseImageCmdMessage,
bool? chooseVideoCmdFlag,
String? chooseVideoCmdMessage,
bool? wechatQrBindCmdFlag,
String? testMsg,
}) {
return WebState(
......@@ -142,6 +151,7 @@ class WebState extends Equatable {
showAppBar: showAppBar ?? this.showAppBar,
showBottomNavBar: showBottomNavBar ?? this.showBottomNavBar,
loginOpFlag: loginOpFlag ?? this.loginOpFlag,
visitor: visitor ?? this.visitor,
sessionCode: sessionCode ?? this.sessionCode,
userCode: userCode ?? this.userCode,
classCode: classCode ?? this.classCode,
......@@ -155,6 +165,7 @@ class WebState extends Equatable {
chooseImageCmdMessage: chooseImageCmdMessage ?? this.chooseImageCmdMessage,
chooseVideoCmdFlag: chooseVideoCmdFlag ?? this.chooseVideoCmdFlag,
chooseVideoCmdMessage: chooseVideoCmdMessage ?? this.chooseVideoCmdMessage,
wechatQrBindCmdFlag: wechatQrBindCmdFlag ?? this.wechatQrBindCmdFlag,
testMsg: testMsg ?? this.testMsg,
);
}
......@@ -172,6 +183,7 @@ class WebState extends Equatable {
setting,
showAppBar,
showBottomNavBar,
visitor,
sessionCode,
userCode,
classCode,
......@@ -185,6 +197,7 @@ class WebState extends Equatable {
chooseImageCmdMessage,
chooseVideoCmdFlag,
chooseVideoCmdMessage,
wechatQrBindCmdFlag,
testMsg,
];
}
......@@ -370,6 +383,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
// 构造函数中已拦截判断未登录的情况进行了处理,所以这里不再处理未登录的情况
final String serverUrl = '${Constant.localServerUrl}/index.html'
'#/h5/login/pages/applogin?'
'visitor=${state.visitor}&'
'sessionCode=${state.sessionCode}&'
'userCode=${state.userCode}&'
'classCode=${state.classCode}&'
......@@ -381,7 +395,38 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
_controller.loadRequest(Uri.parse(serverUrl));
}
/// 微信绑定成功后,更新身份信息并重新加载 H5 页面
/// 避免销毁重建整个 WebPage,保留本地服务器和 WebView 控制器
void reloadWithIdentity({
required int visitor,
required String sessionCode,
required String userCode,
required String classCode,
required int userType,
required String stuId,
}) {
emit(state.copyWith(
loginOpFlag: true,
visitor: visitor,
sessionCode: sessionCode,
userCode: userCode,
classCode: classCode,
userType: userType,
stuId: stuId,
));
// 重新加载 H5 页面(_loadHtml 读取的是 state 中的值)
_loadHtml();
// 用新身份重新登录 IM
_loginIM();
}
Future<void> _loginIM() async {
// 游客身份不处理IM登录
if(state.visitor == 1) {
return;
}
if (Constant.needIM) {
var imService = getIt.get<ImService>();
var loginResult = await imService.login(state.userCode!);
......@@ -1070,6 +1115,21 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
_sendResponse(resp);
}
void setWechatQrBindCmdFlag(bool wechatQrBindCmdFlag) {
emit(state.copyWith(wechatQrBindCmdFlag: wechatQrBindCmdFlag));
}
void showWechatQrBindDialog(BuildContext context) {
setWechatQrBindCmdFlag(false);
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return WechatQrBindDialog();
},
);
}
void setWindowInfoCmdFlag(bool windowInfoCmdFlag, String windowInfoCmdMessage) {
emit(state.copyWith(windowInfoCmdFlag: windowInfoCmdFlag, windowInfoCmdMessage: windowInfoCmdMessage));
}
......
......@@ -116,6 +116,6 @@ class Constant {
static const String hasShownPrivacyFirstTimeKey = 'has_shown_privacy_first_time';
/// 测试阶段使用
static const bool needIM = true;
static const bool needIM = false;
static const bool needUpgrade = true;
}
......@@ -36,6 +36,8 @@ import 'package:appframe/data/repositories/message/upload_cancel_handler.dart';
import 'package:appframe/data/repositories/message/upload_file.dart';
import 'package:appframe/data/repositories/message/upload_start_handler.dart';
import 'package:appframe/data/repositories/message/vibrate_short_handler.dart';
import 'package:appframe/data/repositories/message/wechat_bind_handler.dart';
import 'package:appframe/data/repositories/message/wechat_qr_bind_handler.dart';
import 'package:appframe/data/repositories/message/video_info_handler.dart';
import 'package:appframe/data/repositories/message/wifi_info_handler.dart';
import 'package:appframe/data/repositories/message/window_info_handler.dart';
......@@ -211,6 +213,12 @@ Future<void> setupLocator() async {
/// 设置用户角色信息
getIt.registerLazySingleton<MessageHandler>(() => RoleInfoHandler(), instanceName: 'setRoleInfo');
/// 微信绑定
getIt.registerLazySingleton<MessageHandler>(() => WechatBindHandler(), instanceName: 'wechatBind');
/// 微信二维码绑定
getIt.registerLazySingleton<MessageHandler>(() => WechatQrBindHandler(), instanceName: 'wechatQrBind');
/// 设置屏幕模式
getIt.registerLazySingleton<MessageHandler>(() => ScreenHandler(), instanceName: 'setScreen');
......
import 'dart:async';
import 'package:appframe/config/locator.dart';
import 'package:appframe/data/repositories/user_auth_repository.dart';
import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:appframe/utils/login_util.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx/fluwx.dart';
import 'package:shared_preferences/shared_preferences.dart';
class WechatBindHandler extends MessageHandler {
late final Fluwx _fluwx;
late final FluwxCancelable _fluwxCancelable;
late final WechatAuthRepository _wechatAuthRepository;
late final UserAuthRepository _userAuthRepository;
// 是否正在等待微信授权回调
bool _waitingWechatAuth = false;
// 总超时定时器
Timer? _wechatAuthTimeoutTimer;
// App 回到前台后的短延时定时器
Timer? _wechatAuthResumeTimer;
WechatBindHandler() {
_fluwx = getIt.get<Fluwx>();
_wechatAuthRepository = getIt.get<WechatAuthRepository>();
_userAuthRepository = getIt.get<UserAuthRepository>();
}
@override
Future<dynamic> handleMessage(params) async {
if (!await _fluwx.isWeChatInstalled) {
// throw Exception('设备上未安装微信App,不支持微信绑定');
Fluttertoast.showToast(msg: '设备上未安装微信App,不支持微信绑定', gravity: ToastGravity.TOP, backgroundColor: Colors.red);
return null;
}
_fluwxCancelable = _fluwx.addSubscriber(_responseListener);
var authResult = await _fluwx.authBy(
which: NormalAuth(scope: 'snsapi_userinfo', state: 'wechat_bind'),
);
if (!authResult) {
_fluwxCancelable.cancel();
// throw Exception('微信授权处理失败');
Fluttertoast.showToast(msg: '微信授权处理失败', gravity: ToastGravity.TOP, backgroundColor: Colors.red);
return null;
}
// 启动等待微信授权回调的兜底机制
_startWechatAuthWaiting();
// 微信授权为异步回调,不返回结果给H5
return null;
}
void _responseListener(WeChatResponse response) async {
if (response is WeChatAuthResponse) {
// 收到正式回调,结束等待状态
_finishWechatAuthWaiting();
if (response.code == null || response.code == '') {
Fluttertoast.showToast(msg: '微信授权取消', gravity: ToastGravity.TOP);
_cleanup();
return;
}
try {
// 1. 用code换取会话信息
var resultData = await _wechatAuthRepository.codeToSk(response.code!) as Map<String, dynamic>?;
// 请求接口异常
if (resultData == null) {
Fluttertoast.showToast(msg: '绑定请求处理失败', gravity: ToastGravity.TOP);
_cleanup();
return;
}
// 状态码错误
if (resultData['resultCode'] != '001') {
Fluttertoast.showToast(msg: '绑定请求状态失败', gravity: ToastGravity.TOP);
_cleanup();
return;
}
var data = resultData['data'] as Map<String, dynamic>;
var wechatUserCode = data['userCode'] as String;
// 2. 从SharedPreferences获取当前用户信息
var sharedPreferences = getIt.get<SharedPreferences>();
var visitorId = sharedPreferences.getString('auth_visitor_id') ?? '';
var visitorType = sharedPreferences.getString('auth_visitor_type') ?? '';
if (visitorId.isEmpty || visitorType.isEmpty) {
Fluttertoast.showToast(msg: '用户信息获取失败,请重新登录', gravity: ToastGravity.TOP);
_cleanup();
return;
}
if (visitorType != 'apple' && visitorType != 'phone') {
Fluttertoast.showToast(msg: '用户信息错误,请重新登录', gravity: ToastGravity.TOP);
_cleanup();
return;
}
// 3. 调用绑定接口
var bindResult = await _userAuthRepository.newBinding(
visitorId,
wechatUserCode,
visitorType
) as Map<String, dynamic>?;
if (bindResult != null && bindResult['code'] == 0) {
// Fluttertoast.showToast(msg: '微信绑定成功', gravity: ToastGravity.TOP);
LoginUtil.handleLoginSuccess(data, 0, 'reload');
} else {
var errorMsg = bindResult?['error'] ?? '微信绑定失败';
Fluttertoast.showToast(msg: errorMsg, gravity: ToastGravity.TOP);
}
} catch (e) {
debugPrint('wechatBind error: $e');
Fluttertoast.showToast(msg: '微信绑定异常', gravity: ToastGravity.TOP);
} finally {
_cleanup();
}
}
}
// 标记进入"等待微信授权回调"状态,并启动总超时
void _startWechatAuthWaiting() {
_waitingWechatAuth = true;
_wechatAuthTimeoutTimer?.cancel();
_wechatAuthTimeoutTimer = Timer(const Duration(seconds: 30), () {
if (_waitingWechatAuth) {
_finishWechatAuthWaiting(reason: _WechatBindFinishReason.timeout);
}
});
}
// 结束等待,按场景给出提示
void _finishWechatAuthWaiting({
_WechatBindFinishReason reason = _WechatBindFinishReason.response,
}) {
if (!_waitingWechatAuth) return;
_waitingWechatAuth = false;
_wechatAuthTimeoutTimer?.cancel();
_wechatAuthResumeTimer?.cancel();
_wechatAuthTimeoutTimer = null;
_wechatAuthResumeTimer = null;
switch (reason) {
case _WechatBindFinishReason.cancel:
Fluttertoast.showToast(msg: '已取消微信授权', gravity: ToastGravity.TOP);
_cleanup();
break;
case _WechatBindFinishReason.timeout:
Fluttertoast.showToast(msg: '微信授权超时,请重试', gravity: ToastGravity.TOP);
_cleanup();
break;
case _WechatBindFinishReason.response:
// 正常收到回调时不弹提示
break;
}
}
// 清理资源
void _cleanup() {
_fluwxCancelable.cancel();
_wechatAuthTimeoutTimer?.cancel();
_wechatAuthResumeTimer?.cancel();
}
}
enum _WechatBindFinishReason {
// 收到了正式的 WeChatAuthResponse
response,
// App 已回到前台但仍无回调,判定为取消
cancel,
// 总超时
timeout,
}
import 'package:appframe/bloc/web_cubit.dart';
import 'package:appframe/services/dispatcher.dart';
class WechatQrBindHandler extends MessageHandler {
late WebCubit? _webCubit;
@override
void setCubit(WebCubit cubit) {
_webCubit = cubit;
}
void _unfollowCubit() {
_webCubit = null;
}
@override
Future<dynamic> handleMessage(params) async {
try {
_webCubit!.setWechatQrBindCmdFlag(true);
} finally {
_unfollowCubit();
}
// 二维码绑定流程为异步,不返回结果给H5
return null;
}
}
......@@ -78,7 +78,7 @@ class PhoneAuthRepository {
///
Future<dynamic> login(String phone, String verifyCode) async {
Response resp = await _appService.post(
'/api/v1/comm/phone/login',
'/api/v1/comm/phone/loginv2',
{
"phone": phone,
"verifyCode": verifyCode,
......
......@@ -64,13 +64,13 @@ class UserAuthRepository {
/// "error": "",
/// "code": 0,
/// }
Future<dynamic> newBinding(String userid, String bxeUserId) async {
Future<dynamic> newBinding(String userid, String bxeUserId, String type) async {
Response resp = await _appService.post(
'/api/v1/comm/user/newbinding',
{
"userId": userid,
"bxeUserId": bxeUserId,
"type": "apple",
"type": type,
},
);
return resp.statusCode == HttpStatus.ok ? resp.data : null;
......
......@@ -64,7 +64,8 @@ class MessageDispatcher {
h5Message.cmd.startsWith("setTitlebar") ||
h5Message.cmd == "audioPlay" ||
h5Message.cmd == "openLink" ||
h5Message.cmd == "uploadStart") {
h5Message.cmd == "uploadStart" ||
h5Message.cmd == "wechatQrBind") {
handler.setCubit(webCubit!);
handler.setMessage(message);
}
......
......@@ -161,7 +161,7 @@ class LoginMainPage extends StatelessWidget {
if (state.showAgreed) {
_showAgreementDialog(context, context.read<LoginMainCubit>());
} else if (state.showNeedWechatForApple) {
_showNeedWechatDialogForApple(context, context.read<LoginMainCubit>());
// _showNeedWechatDialogForApple(context, context.read<LoginMainCubit>());
} else if (state.showPrivacyFirstTime) {
_showPrivacyFirstTimeDialog(context, context.read<LoginMainCubit>());
}
......@@ -381,59 +381,59 @@ class LoginMainPage extends StatelessWidget {
);
}
void _showNeedWechatDialogForApple(BuildContext context, LoginMainCubit loginMainCubit) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext ctx) {
return PopScope(
canPop: false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
title: Text(
'温馨提示',
style: TextStyle(
fontSize: 17,
color: Color(0xFF000000),
// fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
content: Text.rich(
TextSpan(
text: '为了避免您之前在微信小程序的使用数据不丢失,必须绑定微信才可以继续!',
style: TextStyle(color: Color(0xFF666666), fontSize: 14),
),
),
actions: [
Center(
child: TextButton(
onPressed: () {
Navigator.of(ctx).pop('OK');
loginMainCubit.wechatAuthForApple();
},
style: TextButton.styleFrom(
foregroundColor: Color(0xFF7691FA),
textStyle: TextStyle(fontSize: 17),
minimumSize: Size.fromHeight(40),
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: Text('绑定微信'),
),
),
],
),
);
},
);
}
// void _showNeedWechatDialogForApple(BuildContext context, LoginMainCubit loginMainCubit) {
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (BuildContext ctx) {
// return PopScope(
// canPop: false,
// child: AlertDialog(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.all(
// Radius.circular(5),
// ),
// ),
// title: Text(
// '温馨提示',
// style: TextStyle(
// fontSize: 17,
// color: Color(0xFF000000),
// // fontWeight: FontWeight.bold,
// ),
// textAlign: TextAlign.center,
// ),
// content: Text.rich(
// TextSpan(
// text: '为了避免您之前在微信小程序的使用数据不丢失,必须绑定微信才可以继续!',
// style: TextStyle(color: Color(0xFF666666), fontSize: 14),
// ),
// ),
// actions: [
// Center(
// child: TextButton(
// onPressed: () {
// Navigator.of(ctx).pop('OK');
// loginMainCubit.wechatAuthForApple();
// },
// style: TextButton.styleFrom(
// foregroundColor: Color(0xFF7691FA),
// textStyle: TextStyle(fontSize: 17),
// minimumSize: Size.fromHeight(40),
// padding: EdgeInsets.zero,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.zero,
// ),
// ),
// child: Text('绑定微信'),
// ),
// ),
// ],
// ),
// );
// },
// );
// }
// 首次打开显示个人信息收集提示弹窗
Future<void> _showPrivacyFirstTimeDialog(BuildContext context, LoginMainCubit loginMainCubit) async {
......
......@@ -231,7 +231,7 @@ class LoginMainPageV3 extends StatelessWidget {
if (state.showAgreed) {
_showAgreementDialog(context, context.read<LoginMainCubit>());
} else if (state.showNeedWechatForApple) {
_showNeedWechatDialogForApple(context, context.read<LoginMainCubit>());
//_showNeedWechatDialogForApple(context, context.read<LoginMainCubit>());
} else if (state.showPrivacyFirstTime) {
_showPrivacyFirstTimeDialog(context, context.read<LoginMainCubit>());
}
......@@ -450,59 +450,59 @@ class LoginMainPageV3 extends StatelessWidget {
);
}
void _showNeedWechatDialogForApple(BuildContext context, LoginMainCubit loginMainCubit) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext ctx) {
return PopScope(
canPop: false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
title: Text(
'温馨提示',
style: TextStyle(
fontSize: 17,
color: Color(0xFF000000),
// fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
content: Text.rich(
TextSpan(
text: '为了避免您之前在微信小程序的使用数据不丢失,必须绑定微信才可以继续!',
style: TextStyle(color: Color(0xFF666666), fontSize: 14),
),
),
actions: [
Center(
child: TextButton(
onPressed: () {
Navigator.of(ctx).pop('OK');
loginMainCubit.wechatAuthForApple();
},
style: TextButton.styleFrom(
foregroundColor: Color(0xFF7691FA),
textStyle: TextStyle(fontSize: 17),
minimumSize: Size.fromHeight(40),
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: Text('绑定微信'),
),
),
],
),
);
},
);
}
// void _showNeedWechatDialogForApple(BuildContext context, LoginMainCubit loginMainCubit) {
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (BuildContext ctx) {
// return PopScope(
// canPop: false,
// child: AlertDialog(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.all(
// Radius.circular(5),
// ),
// ),
// title: Text(
// '温馨提示',
// style: TextStyle(
// fontSize: 17,
// color: Color(0xFF000000),
// // fontWeight: FontWeight.bold,
// ),
// textAlign: TextAlign.center,
// ),
// content: Text.rich(
// TextSpan(
// text: '为了避免您之前在微信小程序的使用数据不丢失,必须绑定微信才可以继续!',
// style: TextStyle(color: Color(0xFF666666), fontSize: 14),
// ),
// ),
// actions: [
// Center(
// child: TextButton(
// onPressed: () {
// Navigator.of(ctx).pop('OK');
// loginMainCubit.wechatAuthForApple();
// },
// style: TextButton.styleFrom(
// foregroundColor: Color(0xFF7691FA),
// textStyle: TextStyle(fontSize: 17),
// minimumSize: Size.fromHeight(40),
// padding: EdgeInsets.zero,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.zero,
// ),
// ),
// child: Text('绑定微信'),
// ),
// ),
// ],
// ),
// );
// },
// );
// }
/// 首次打开显示个人信息收集提示弹窗
/// 值针对iOS,Android用原生界面显示
......
......@@ -19,6 +19,7 @@ class WebPage extends StatelessWidget {
var loginOpFlag = true;
var visitor = extraData?['visitor'];
var sessionCode = extraData?['sessionCode'];
var userCode = extraData?['userCode'];
var classCode = extraData?['classCode'];
......@@ -29,6 +30,7 @@ class WebPage extends StatelessWidget {
loginOpFlag = false;
var sharedPreferences = getIt.get<SharedPreferences>();
visitor = sharedPreferences.getInt('auth_visitor');
sessionCode = sharedPreferences.getString('auth_sessionCode');
userCode = sharedPreferences.getString('auth_userCode');
classCode = sharedPreferences.getString('auth_classCode');
......@@ -41,6 +43,7 @@ class WebPage extends StatelessWidget {
final webCubit = WebCubit(
WebState(
loginOpFlag: loginOpFlag,
visitor: visitor,
sessionCode: sessionCode,
userCode: userCode,
classCode: classCode,
......@@ -171,6 +174,8 @@ class WebPage extends StatelessWidget {
context.read<WebCubit>().chooseImage(context);
} else if (state.chooseVideoCmdFlag) {
context.read<WebCubit>().chooseVideo(context);
} else if (state.wechatQrBindCmdFlag) {
context.read<WebCubit>().showWechatQrBindDialog(context);
}
},
),
......
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// 登录成功后的公共处理逻辑
class LoginUtil {
/// 处理登录成功后的数据解析、缓存持久化和路由跳转
///
/// [data] 登录接口返回的 data 字段
/// [visitor] 是否为游客(0=非游客,1=游客)
/// [loginType] 登录类型,router=跳转路由,reload=重新加载
static void handleLoginSuccess(Map<String, dynamic> data, int visitor, String loginType) {
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 className = '';
var stuName = '';
var relation = '';
var sharedPreferences = getIt.get<SharedPreferences>();
if (roles.isNotEmpty) {
var role = roles[0];
classCode = role['classCode'];
userType = role['userType'];
stuId = role['stuId'];
className = role['className'];
stuName = role['stuName'];
relation = role['relation'] ?? '';
// 将角色中的班级数据处理后,进行缓存
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.setInt('auth_visitor', visitor);
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);
sharedPreferences.setString('auth_className', className);
sharedPreferences.setString('auth_stuName', stuName);
sharedPreferences.setString('auth_relation', relation);
if (loginType == 'router') {
router.go(
'/web',
extra: {
'visitor': visitor,
'sessionCode': sessionCode,
'userCode': userCode,
'classCode': classCode,
'userType': userType,
'stuId': stuId,
},
);
} else if (loginType == 'reload') {
// 通知当前 WebCubit 更新身份信息并重新加载 H5,避免销毁重建整个页面
WebCubitHolder.instance?.reloadWithIdentity(
visitor: 0,
sessionCode: sessionCode,
userCode: userCode,
classCode: classCode,
userType: userType,
stuId: stuId,
);
}
}
}
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!