Files
AI-Health/health_app/lib/providers/auth_provider.dart
MingNian 13714d9ed8 feat: 应用内通知系统 + 结构化手术史/用药等相关改动
- 新增用户通知 outbox 流水线(EfUserNotificationPipeline)与后台投递 worker
- 通知中心页面及前端通知服务接入
- 健康指标异常、用药/运动提醒等事件统一产出站内通知
- 健康档案结构化手术史、用药提醒扫描、医生/用户端点等配套调整
- AppDbContext 注册通知相关实体
2026-06-21 21:06:29 +08:00

162 lines
5.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:developer';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart';
import '../core/api_client.dart';
import '../core/local_database.dart';
class UserInfo {
final String id;
final String phone;
final String role;
final String? name;
final String? avatarUrl;
UserInfo({required this.id, required this.phone, this.role = 'User', this.name, this.avatarUrl});
}
class AuthState {
final UserInfo? user;
final bool isLoggedIn;
final bool isLoading;
const AuthState({this.user, this.isLoggedIn = false, this.isLoading = true});
}
final authProvider = NotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
final localDbProvider = Provider<LocalDatabase>((ref) => LocalDatabase.instance);
final apiClientProvider = Provider<ApiClient>((ref) {
return ApiClient(db: ref.watch(localDbProvider));
});
class AuthNotifier extends Notifier<AuthState> {
@override
AuthState build() {
_checkAuth();
return const AuthState(isLoggedIn: false, isLoading: false);
}
Future<void> _checkAuth() async {
final db = ref.read(localDbProvider);
final refresh = await db.read('refresh_token');
if (refresh == null) return; // 无token保持 isLoggedIn: false
state = const AuthState(isLoading: true);
try {
final response = await Dio(BaseOptions(baseUrl: baseUrl))
.post('/api/auth/refresh', data: {'refreshToken': refresh});
final data = response.data['data'];
if (data != null) {
await db.write('access_token', data['accessToken']);
await db.write('refresh_token', data['refreshToken']);
final u = data['user'] as Map<String, dynamic>?;
state = AuthState(isLoggedIn: true, isLoading: false,
user: UserInfo(id: '', phone: '', role: u?['role'] ?? 'User'),
);
_loadProfile();
} else {
state = const AuthState(isLoggedIn: false, isLoading: false);
}
} catch (_) {
state = const AuthState(isLoggedIn: false, isLoading: false);
}
}
Future<void> _loadProfile() async {
// Admin 不查 profile无 User 记录)
if (state.user?.role == 'Admin') return;
try {
final api = ref.read(apiClientProvider);
final response = await api.get('/api/user/profile');
final user = response.data['data'];
if (user != null) {
state = AuthState(isLoggedIn: true, isLoading: false,
user: UserInfo(
id: user['id'] ?? '', phone: user['phone'] ?? '',
role: user['role'] ?? state.user?.role ?? 'User',
name: user['name'], avatarUrl: user['avatarUrl'],
),
);
}
} catch (e) {
log('[Auth] loadProfile: $e');
}
}
Future<void> refreshProfile() => _loadProfile();
/// 发送验证码
Future<({String? error, String? devCode})> sendSms(String phone) async {
try {
final api = ref.read(apiClientProvider);
final response = await api.post('/api/auth/send-sms', data: {'phone': phone});
return (error: null, devCode: response.data['data']?['devCode'] as String?);
} catch (e) {
return (error: '发送失败: $e', devCode: null);
}
}
/// 注册(新用户,需选身份)
Future<String?> register(String phone, String code, String name, String doctorId) async {
try {
final api = ref.read(apiClientProvider);
final response = await api.post('/api/auth/register', data: {
'phone': phone, 'smsCode': code, 'name': name, 'doctorId': doctorId,
});
final data = response.data['data'];
if (data == null) return response.data['message'] ?? '注册失败';
await api.saveTokens(data['accessToken'], data['refreshToken']);
final user = data['user'];
state = AuthState(isLoggedIn: true, isLoading: false,
user: UserInfo(id: user['id'] ?? '', phone: user['phone'] ?? '', role: user['role'] ?? 'User'),
);
return null;
} catch (e) {
return '注册失败: $e';
}
}
/// 登录(已有账号)
Future<String?> login(String phone, String code) async {
try {
final api = ref.read(apiClientProvider);
final response = await api.post('/api/auth/login', data: {'phone': phone, 'smsCode': code});
final data = response.data['data'];
if (data == null) return response.data['message'] ?? '登录失败';
await api.saveTokens(data['accessToken'], data['refreshToken']);
final user = data['user'];
state = AuthState(isLoggedIn: true, isLoading: false,
user: UserInfo(
id: user['id'] ?? '', phone: user['phone'] ?? '',
role: user['role'] ?? 'User', name: user['name'],
avatarUrl: user['avatarUrl'],
),
);
return null;
} catch (e) {
return '登录失败: $e';
}
}
/// 登出
Future<void> logout() async {
final api = ref.read(apiClientProvider);
final db = ref.read(localDbProvider);
final refresh = await db.read('refresh_token');
if (refresh != null) {
try { await api.post('/api/auth/logout', data: {'refreshToken': refresh}); } catch (e) { log('[Auth] logout: $e'); }
}
await api.clearTokens();
state = const AuthState(isLoggedIn: false, isLoading: false);
}
}
/// 便捷:当前用户角色
final userRoleProvider = Provider<String>((ref) {
return ref.watch(authProvider).user?.role ?? 'User';
});