auth_store.dart 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import 'package:chicken_farm/apis/index.dart';
  2. import 'package:chicken_farm/core/config/app_config.dart';
  3. import 'package:chicken_farm/core/permissions/permission_keys.dart';
  4. import 'package:chicken_farm/core/services/navigation_service.dart';
  5. import 'package:chicken_farm/core/utils/jwt_token.dart';
  6. import 'package:chicken_farm/core/utils/logger.dart';
  7. import 'package:chicken_farm/core/utils/service_checker.dart';
  8. import 'package:chicken_farm/core/utils/toast.dart';
  9. import 'package:chicken_farm/modes/auth/login_model.dart';
  10. import 'package:chicken_farm/modes/user/user_model.dart';
  11. import 'package:chicken_farm/routes/app_routes.dart';
  12. import 'package:flutter_riverpod/flutter_riverpod.dart';
  13. import 'package:go_router/go_router.dart';
  14. /// 认证状态枚举
  15. enum AuthState { authenticated, unauthenticated, loading }
  16. /// 用户认证信息状态
  17. class AuthInfo {
  18. final AuthState state;
  19. final String? token;
  20. final UserModel? user;
  21. final List<String>? permissions;
  22. final List<String>? roles;
  23. AuthInfo({
  24. required this.state,
  25. this.token,
  26. this.user,
  27. this.permissions,
  28. this.roles,
  29. });
  30. /// 创建已认证状态
  31. AuthInfo.authenticated({
  32. required String token,
  33. required UserModel user,
  34. List<String>? permissions,
  35. List<String>? roles,
  36. }) : this(
  37. state: AuthState.authenticated,
  38. token: token,
  39. user: user,
  40. permissions: permissions,
  41. roles: roles,
  42. );
  43. /// 创建未认证状态
  44. AuthInfo.unauthenticated()
  45. : this(
  46. state: AuthState.unauthenticated,
  47. token: null,
  48. user: null,
  49. permissions: const [],
  50. roles: const [],
  51. );
  52. /// 创建加载状态
  53. AuthInfo.loading()
  54. : this(
  55. state: AuthState.loading,
  56. token: null,
  57. user: null,
  58. permissions: const [],
  59. roles: const [],
  60. );
  61. /// 复制对象并更新部分字段
  62. AuthInfo copyWith({
  63. AuthState? state,
  64. String? token,
  65. UserModel? user,
  66. List<String>? permissions,
  67. List<String>? roles,
  68. }) {
  69. return AuthInfo(
  70. state: state ?? this.state,
  71. token: token ?? this.token,
  72. user: user ?? this.user,
  73. permissions: permissions ?? this.permissions,
  74. roles: roles ?? this.roles,
  75. );
  76. }
  77. }
  78. class AuthStore extends StateNotifier<AuthInfo> {
  79. AuthStore() : super(AuthInfo.unauthenticated()) {
  80. _init();
  81. }
  82. /// 初始化认证状态
  83. Future<void> _init() async {
  84. state = AuthInfo.loading();
  85. try {
  86. // 检查是否启用了离线模式
  87. if (AppConfig.isOffline) {
  88. // 在离线模式下,创建一个具有养殖相关权限的虚拟用户
  89. final offlineUser = UserModel()
  90. ..userId = 0
  91. ..userName = 'offline_user'
  92. ..nickName = '离线用户';
  93. // 设置养殖相关的权限
  94. final breedingPermissions = [
  95. PermissionKeys.bindChicken,
  96. PermissionKeys.cageChange,
  97. PermissionKeys.chickenWeighing,
  98. PermissionKeys.chickenCulling,
  99. PermissionKeys.chickenCulling,
  100. ];
  101. state = AuthInfo.authenticated(
  102. token: 'offline_token',
  103. user: offlineUser,
  104. permissions: breedingPermissions,
  105. roles: ['breeding_operator'],
  106. );
  107. logger.i('离线模式已启用,创建离线用户: $state');
  108. return;
  109. }
  110. final token = await JwtToken.getToken();
  111. if (token != null) {
  112. // 检查网络连接状态
  113. final isConnected = await ServiceChecker().forceCheckService();
  114. if (isConnected) {
  115. // 有网络连接,强制获取最新用户信息
  116. try {
  117. final userInfo = await apis.loginApi.getInfo();
  118. if (userInfo == null) {
  119. throw Exception('用户信息获取失败');
  120. }
  121. // 保存用户信息到本地存储,供离线时使用
  122. await JwtToken.saveUserInfo(userInfo);
  123. state = AuthInfo.authenticated(
  124. token: token,
  125. user: userInfo.user!,
  126. permissions: userInfo.permissions,
  127. roles: userInfo.roles,
  128. );
  129. logger.i('在线模式下已登录 state: $state');
  130. } catch (e) {
  131. // 网络连接但是获取用户信息失败,可能是token过期
  132. logger.e('在线模式下获取用户信息失败: $e');
  133. await JwtToken.clear();
  134. state = AuthInfo.unauthenticated();
  135. }
  136. } else {
  137. // 无网络连接,使用离线模式
  138. try {
  139. // 尝试从本地存储获取上次登录的用户信息
  140. final cachedUserInfo = await JwtToken.getUserInfo();
  141. if (cachedUserInfo != null && cachedUserInfo.user != null) {
  142. state = AuthInfo.authenticated(
  143. token: token,
  144. user: cachedUserInfo.user!,
  145. permissions: cachedUserInfo.permissions ?? [],
  146. roles: cachedUserInfo.roles ?? [],
  147. );
  148. logger.i('离线模式下使用缓存用户信息: $state');
  149. } else {
  150. // 没有缓存的用户信息,不能进行离线认证
  151. logger.w('离线模式下无缓存用户信息,无法进行离线认证');
  152. state = AuthInfo.unauthenticated();
  153. }
  154. } catch (e) {
  155. logger.e('离线模式初始化失败: $e');
  156. state = AuthInfo.unauthenticated();
  157. }
  158. }
  159. } else {
  160. state = AuthInfo.unauthenticated();
  161. }
  162. } catch (e) {
  163. logger.e('认证初始化失败: $e');
  164. state = AuthInfo.unauthenticated();
  165. }
  166. }
  167. /// 登录操作
  168. Future<bool> login(LoginModel loginModel) async {
  169. state = AuthInfo.loading();
  170. try {
  171. loginModel.clientId = AppConfig.clientId;
  172. final authResult = await apis.loginApi.login(loginModel);
  173. final token = authResult.accessToken;
  174. await JwtToken.setToken(token, authResult.refreshToken);
  175. // 获取用户信息
  176. final userInfo = await apis.loginApi.getInfo();
  177. if (userInfo == null) {
  178. throw Exception('用户信息获取失败');
  179. }
  180. // 保存用户信息到本地存储,供离线时使用
  181. await JwtToken.saveUserInfo(userInfo);
  182. state = AuthInfo.authenticated(
  183. token: token,
  184. user: userInfo.user!,
  185. permissions: userInfo.permissions,
  186. roles: userInfo.roles,
  187. );
  188. logger.i('登录成功 state: $state');
  189. return true;
  190. } catch (e) {
  191. logger.e('登录失败: $e');
  192. await JwtToken.clear();
  193. state = AuthInfo.unauthenticated();
  194. ToastUtil.errorAlert(e.toString());
  195. return false;
  196. }
  197. }
  198. /// 登出操作
  199. Future<void> logout() async {
  200. try {
  201. await apis.loginApi.logout();
  202. } catch (e) {
  203. // 即使API调用失败也要清除本地状态
  204. logger.e('Logout API call failed: $e');
  205. } finally {
  206. await JwtToken.clear();
  207. state = AuthInfo.unauthenticated();
  208. if (NavigationService.navigatorKey.currentState != null &&
  209. NavigationService.navigatorKey.currentContext != null) {
  210. NavigationService.navigatorKey.currentContext!.goNamed(
  211. AppRouteNames.login,
  212. );
  213. }
  214. }
  215. }
  216. /// 刷新token
  217. Future<void> refreshToken() async {
  218. try {
  219. final refreshToken = await JwtToken.getRefreshToken();
  220. if (refreshToken != null) {
  221. final authResult = await apis.loginApi.refreshToken(refreshToken);
  222. final newToken = authResult.accessToken;
  223. await JwtToken.setToken(newToken, authResult.refreshToken);
  224. state = state.copyWith(token: newToken);
  225. }
  226. } catch (e) {
  227. // 刷新失败则登出
  228. await logout();
  229. rethrow;
  230. }
  231. }
  232. /// 重新获取用户信息
  233. Future<void> getUserInfo() async {
  234. try {
  235. final userInfo = await apis.loginApi.getInfo();
  236. if (userInfo == null) {
  237. throw Exception('用户信息获取失败');
  238. }
  239. // 更新本地存储的用户信息
  240. await JwtToken.saveUserInfo(userInfo);
  241. state = state.copyWith(
  242. user: userInfo.user,
  243. permissions: userInfo.permissions,
  244. roles: userInfo.roles,
  245. );
  246. } catch (e) {
  247. rethrow;
  248. }
  249. }
  250. /// 检查是否有特定权限
  251. bool hasPermission(String permission) {
  252. return state.permissions?.contains(permission) ?? false;
  253. }
  254. /// 检查是否有特定角色
  255. bool hasRole(String role) {
  256. return state.roles?.contains(role) ?? false;
  257. }
  258. /// 是否是超级管理员
  259. bool isSuperAdmin() {
  260. if (state.user == null) return false;
  261. return state.user!.userName == "admin" ||
  262. (state.roles?.contains("super_admin") ?? false);
  263. }
  264. }
  265. // 添加 Provider 实例
  266. final authStoreProvider = StateNotifierProvider<AuthStore, AuthInfo>(
  267. (ref) => AuthStore(),
  268. );