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 'dart:convert';
import 'dart:typed_data';
import 'package:appframe/config/constant.dart';
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/utils/login_util.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:fluwx/fluwx.dart';
import 'package:shared_preferences/shared_preferences.dart';
///
/// 微信二维码绑定弹窗
/// 内部自管理二维码生成、扫码回调、绑定请求等完整流程
///
class WechatQrBindDialog extends StatefulWidget {
@override
State<WechatQrBindDialog> createState() => _WechatQrBindDialogState();
}
class _WechatQrBindDialogState extends State<WechatQrBindDialog> {
// 0: 加载中 1: 显示二维码 2: 已扫码待确认 3: 用户取消 4: 过期/错误 5: 绑定成功
int _status = 0;
String _tip = '正在生成二维码...';
Uint8List? _qrImage;
late final Fluwx _fluwx;
late final FluwxCancelable _fluwxCancelable;
late final WechatAuthRepository _wechatAuthRepository;
late final UserAuthRepository _userAuthRepository;
@override
void initState() {
super.initState();
_fluwx = getIt.get<Fluwx>();
_wechatAuthRepository = getIt.get<WechatAuthRepository>();
_userAuthRepository = getIt.get<UserAuthRepository>();
_fluwxCancelable = _fluwx.addSubscriber(_responseListener);
_initQrCode();
}
Future<void> _initQrCode() async {
try {
var resultData = await _wechatAuthRepository.getTicket() as Map<String, dynamic>?;
if (resultData == null) {
_setError('生成二维码失败');
return;
}
if (resultData['resultCode'] != '001') {
_setError('生成二维码状态错误');
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) {
_setError('请求微信失败');
}
} catch (e) {
debugPrint('wechatQrBind initQrCode error: $e');
_setError('生成二维码异常');
}
}
void _responseListener(WeChatResponse response) async {
if (response is WeChatAuthGotQRCodeResponse) {
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
if (!mounted) return;
setState(() {
_status = 1;
_qrImage = response.qrCode;
_tip = '打开微信,扫描二维码绑定';
});
} else {
_setError('错误 $errCode');
}
} else if (response is WeChatQRCodeScannedResponse) {
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
if (!mounted) return;
setState(() {
_status = 2;
_tip = '在微信中轻触允许即可绑定';
});
} else {
_setError('错误 $errCode');
}
} else if (response is WeChatAuthByQRCodeFinishedResponse) {
int? errCode = response.errCode;
if (errCode != null && errCode == 0) {
await _doBind(response.authCode!);
} else if (errCode == -4) {
if (!mounted) return;
setState(() {
_status = 3;
_tip = '你已取消此次绑定';
});
} else {
if (!mounted) return;
setState(() {
_status = 4;
_tip = '请刷新二维码,重新绑定';
});
}
}
}
Future<void> _doBind(String code) async {
try {
var resultData = await _wechatAuthRepository.codeToSk(code) as Map<String, dynamic>?;
if (resultData == null) {
_setError('绑定请求处理失败');
return;
}
if (resultData['resultCode'] != '001') {
_setError('绑定请求状态失败');
return;
}
var data = resultData['data'] as Map<String, dynamic>;
var wechatUserCode = data['userCode'] as String;
var sharedPreferences = getIt.get<SharedPreferences>();
var visitorId = sharedPreferences.getString('auth_visitor_id') ?? '';
var visitorType = sharedPreferences.getString('auth_visitor_type') ?? '';
if (visitorId.isEmpty || visitorType.isEmpty) {
_setError('用户信息获取失败,请重新登录');
return;
}
if (visitorType != 'apple' && visitorType != 'phone') {
_setError('用户信息错误,请重新登录');
return;
}
var bindResult = await _userAuthRepository.newBinding(
visitorId,
wechatUserCode,
visitorType,
) as Map<String, dynamic>?;
if (!mounted) return;
if (bindResult != null && bindResult['code'] == 0) {
setState(() {
_status = 5;
_tip = '微信绑定成功';
});
// 延迟关闭弹窗
Future.delayed(const Duration(milliseconds: 1200), () {
if (mounted) Navigator.of(context).pop();
});
LoginUtil.handleLoginSuccess(data, 0, 'reload');
} else {
var errorMsg = bindResult?['error'] ?? '微信绑定失败';
_setError(errorMsg);
}
} catch (e) {
debugPrint('wechatQrBind doBind error: $e');
_setError('微信绑定异常');
}
}
void _setError(String tip) {
if (!mounted) return;
setState(() {
_status = 4;
_tip = tip;
});
}
Future<void> _refresh() async {
setState(() {
_status = 0;
_tip = '正在重新生成二维码...';
_qrImage = null;
});
try {
_fluwx.stopAuthByQRCode();
} catch (e) {
debugPrint('stopAuthByQRCode error: $e');
}
await _initQrCode();
}
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();
}
@override
void dispose() {
_fluwxCancelable.cancel();
try {
_fluwx.stopAuthByQRCode();
} catch (e) {
debugPrint('stopAuthByQRCode error: $e');
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
width: 300,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'微信扫码绑定',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 20),
SizedBox(
height: 230,
child: _buildContent(),
),
const SizedBox(height: 16),
// 底部按钮区域
_buildBottomActions(),
],
),
),
);
}
Widget _buildContent() {
if (_status == 0) {
// 加载中
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
width: 48,
height: 48,
child: CircularProgressIndicator(
color: Color(0xFF7691FA),
strokeWidth: 3,
),
),
const SizedBox(height: 16),
Text(
_tip,
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
textAlign: TextAlign.center,
),
],
);
} else if (_status == 1) {
// 显示二维码
return Column(
children: [
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(color: Color(0x29265ECF), width: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.memory(_qrImage!, width: 190, height: 190),
),
),
const SizedBox(height: 8),
Text(
_tip,
style: TextStyle(fontSize: 14, color: Color(0xFF333333)),
textAlign: TextAlign.center,
),
],
);
} else if (_status == 2) {
// 已扫码,等待确认
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.check_circle, color: Color(0xFF07C160), size: 64),
const SizedBox(height: 12),
Text(
'已扫码',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
Text(
_tip,
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
textAlign: TextAlign.center,
),
],
);
} else if (_status == 3) {
// 用户取消
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.cancel, color: Color(0xFFE64340), size: 64),
const SizedBox(height: 12),
Text(
'已取消绑定',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
Text(
_tip,
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
textAlign: TextAlign.center,
),
],
);
} else if (_status == 4) {
// 错误/过期
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, color: Color(0xFFE64340), size: 64),
const SizedBox(height: 12),
Text(
'绑定失败',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
Text(
_tip,
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
textAlign: TextAlign.center,
),
],
);
} else if (_status == 5) {
// 绑定成功
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.check_circle, color: Color(0xFF07C160), size: 64),
const SizedBox(height: 12),
Text(
_tip,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF07C160),
),
),
],
);
}
return SizedBox.shrink();
}
Widget _buildBottomActions() {
if (_status == 1 || _status == 2) {
// 二维码显示中,提供关闭按钮
return TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'取消',
style: TextStyle(fontSize: 15, color: Color(0xFF999999)),
),
);
} else if (_status == 3 || _status == 4) {
// 取消或错误,提供重试和关闭按钮
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: _refresh,
child: Text(
'重试',
style: TextStyle(fontSize: 15, color: Color(0xFF7691FA)),
),
),
const SizedBox(width: 16),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'关闭',
style: TextStyle(fontSize: 15, color: Color(0xFF999999)),
),
),
],
);
}
return SizedBox.shrink();
}
}
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!