Przeglądaj źródła

修改http请求为https

刘清 1 miesiąc temu
rodzic
commit
e9c5b15f08

+ 24
- 0
lib/core/constants/api_constants.dart Wyświetl plik

@@ -0,0 +1,24 @@
1
+class ApiConstants {
2
+  // 开发环境
3
+  static const String baseUrl = 'https://afanai.top:10004';  // 实际restful接口地址
4
+  // static const String baseUrl = 'http://10.0.2.2:8000';  // Android模拟器
5
+  // static const String baseUrl = 'http://localhost:8000';  // iOS模拟器
6
+  
7
+  // 认证相关接口
8
+  static const String login = '/api/v1/auth/login';
9
+  static const String register = '/api/v1/auth/register';
10
+  static const String logout = '/api/v1/auth/logout';
11
+  static const String getCurrentUser = '/api/v1/auth/me';
12
+  
13
+  // 用户管理接口
14
+  static const String getUserProfile = '/api/v1/users/me';
15
+  static const String updateUserProfile = '/api/v1/users/me';
16
+  
17
+  // 工具方法
18
+  static String getLoginUrl() => '$baseUrl$login';
19
+  static String getRegisterUrl() => '$baseUrl$register';
20
+  static String getLogoutUrl() => '$baseUrl$logout';
21
+  static String getCurrentUserUrl() => '$baseUrl$getCurrentUser';
22
+  static String getUserProfileUrl() => '$baseUrl$getUserProfile';
23
+  static String getUpdateProfileUrl() => '$baseUrl$updateUserProfile';
24
+}

+ 44
- 0
lib/core/utils/crypto_utils.dart Wyświetl plik

@@ -0,0 +1,44 @@
1
+// lib/core/utils/crypto_utils.dart
2
+import 'dart:convert';
3
+import 'package:crypto/crypto.dart';
4
+import 'package:encrypt/encrypt.dart' as encrypt;
5
+import 'dart:math';
6
+
7
+class CryptoUtils {
8
+  // 生成盐值
9
+  static String generateSalt([int length = 16]) {
10
+    final random = Random.secure();
11
+    final saltBytes = List<int>.generate(length, (i) => random.nextInt(256));
12
+    return base64.encode(saltBytes);
13
+  }
14
+  
15
+  // SHA256哈希(用于前端预处理)
16
+  static String sha256Hash(String input) {
17
+    final bytes = utf8.encode(input);
18
+    final digest = sha256.convert(bytes);
19
+    return digest.toString();
20
+  }
21
+  
22
+  // 生成客户端密钥(前端加密用)
23
+  static String generateClientKey() {
24
+    final random = Random.secure();
25
+    final keyBytes = List<int>.generate(32, (i) => random.nextInt(256));
26
+    return base64.encode(keyBytes);
27
+  }
28
+  
29
+  // AES加密(如果需要前端加密)
30
+  static Map<String, String> aesEncrypt(String plainText, String key) {
31
+    final iv = encrypt.IV.fromSecureRandom(16);
32
+    final encrypter = encrypt.Encrypter(
33
+      encrypt.AES(encrypt.Key.fromBase64(key))
34
+    );
35
+    
36
+    final encrypted = encrypter.encrypt(plainText, iv: iv);
37
+    
38
+    return {
39
+      'data': encrypted.base64,
40
+      'iv': iv.base64,
41
+      'key': key, // 实际项目中,key应该通过其他方式传输
42
+    };
43
+  }
44
+}

+ 96
- 0
lib/data/datasources/remote/api_client.dart Wyświetl plik

@@ -0,0 +1,96 @@
1
+import 'dart:convert';
2
+import 'package:http/http.dart' as http;
3
+import 'package:shared_preferences/shared_preferences.dart';
4
+import './secure_http_client.dart';
5
+
6
+class ApiClient {
7
+  final http.Client _httpClient;
8
+  final SharedPreferences _prefs;
9
+  
10
+  ApiClient(this._prefs) : _httpClient = SecureHttpClient.createSecureClient();
11
+  
12
+  // 获取认证头
13
+  Map<String, String> _getHeaders({bool withAuth = true}) {
14
+    final headers = {
15
+      'Content-Type': 'application/json',
16
+      'Accept': 'application/json',
17
+    };
18
+    
19
+    if (withAuth) {
20
+      final token = _prefs.getString('access_token');
21
+      if (token != null && token.isNotEmpty) {
22
+        headers['Authorization'] = 'Bearer $token';
23
+      }
24
+    }
25
+    
26
+    return headers;
27
+  }
28
+  
29
+  // 通用POST请求
30
+  Future<http.Response> post(
31
+    String url, 
32
+    Map<String, dynamic> body, 
33
+    {bool withAuth = false}
34
+  ) async {
35
+    try {
36
+      final response = await _httpClient.post(
37
+        Uri.parse(url),
38
+        headers: _getHeaders(withAuth: withAuth),
39
+        body: json.encode(body),
40
+      );
41
+      return response;
42
+    } catch (e) {
43
+      throw Exception('网络请求失败: $e');
44
+    }
45
+  }
46
+  
47
+  // 通用GET请求
48
+  Future<http.Response> get(
49
+    String url, 
50
+    {bool withAuth = true}
51
+  ) async {
52
+    try {
53
+      final response = await _httpClient.get(
54
+        Uri.parse(url),
55
+        headers: _getHeaders(withAuth: withAuth),
56
+      );
57
+      return response;
58
+    } catch (e) {
59
+      throw Exception('网络请求失败: $e');
60
+    }
61
+  }
62
+  
63
+  // 通用PUT请求
64
+  Future<http.Response> put(
65
+    String url, 
66
+    Map<String, dynamic> body, 
67
+    {bool withAuth = true}
68
+  ) async {
69
+    try {
70
+      final response = await _httpClient.put(
71
+        Uri.parse(url),
72
+        headers: _getHeaders(withAuth: withAuth),
73
+        body: json.encode(body),
74
+      );
75
+      return response;
76
+    } catch (e) {
77
+      throw Exception('网络请求失败: $e');
78
+    }
79
+  }
80
+  
81
+  // 保存token
82
+  Future<void> saveToken(String token) async {
83
+    await _prefs.setString('access_token', token);
84
+  }
85
+  
86
+  // 清除token
87
+  Future<void> clearToken() async {
88
+    await _prefs.remove('access_token');
89
+  }
90
+  
91
+  // 检查是否已登录
92
+  bool isLoggedIn() {
93
+    final token = _prefs.getString('access_token');
94
+    return token != null && token.isNotEmpty;
95
+  }
96
+}

+ 40
- 0
lib/data/datasources/remote/secure_http_client.dart Wyświetl plik

@@ -0,0 +1,40 @@
1
+// lib/data/datasources/remote/secure_http_client.dart
2
+import 'dart:io';
3
+import 'package:http/http.dart' as http;
4
+import 'package:http/io_client.dart' as http;
5
+
6
+class SecureHttpClient {
7
+  static http.Client createSecureClient() {
8
+    // 生产环境:严格的证书验证
9
+    if (const bool.fromEnvironment('dart.vm.product')) {
10
+      final securityContext = SecurityContext.defaultContext;
11
+      
12
+      // 你可以添加自定义根证书(如果需要)
13
+      // securityContext.setTrustedCertificates('path/to/certificate.pem');
14
+      
15
+      final httpClient = HttpClient(context: securityContext);
16
+      
17
+      // 配置安全策略
18
+      httpClient.badCertificateCallback = 
19
+        (X509Certificate cert, String host, int port) {
20
+          // 生产环境严格验证
21
+          return false; // 拒绝无效证书
22
+        };
23
+      
24
+      return http.IOClient(httpClient);
25
+    } 
26
+    // 开发环境:宽松的验证
27
+    else {
28
+      final httpClient = HttpClient();
29
+      
30
+      httpClient.badCertificateCallback = 
31
+        (X509Certificate cert, String host, int port) {
32
+          // 开发环境允许自签名证书
33
+          // print('警告:使用自签名证书 - $host:$port');
34
+          return true;
35
+        };
36
+      
37
+      return http.IOClient(httpClient);
38
+    }
39
+  }
40
+}

+ 3
- 3
lib/data/models/auth/login_request.dart Wyświetl plik

@@ -1,15 +1,15 @@
1 1
 class LoginRequest {
2
-  final String email;
2
+  final String username;  // 改为username
3 3
   final String password;
4 4
 
5 5
   LoginRequest({
6
-    required this.email,
6
+    required this.username,
7 7
     required this.password,
8 8
   });
9 9
 
10 10
   Map<String, dynamic> toJson() {
11 11
     return {
12
-      'email': email,
12
+      'username': username,
13 13
       'password': password,
14 14
     };
15 15
   }

+ 9
- 6
lib/data/models/auth/register_request.dart Wyświetl plik

@@ -1,22 +1,25 @@
1 1
 class RegisterRequest {
2
+  final String username;
2 3
   final String email;
3 4
   final String password;
4
-  final String name;
5
-  final String? phone;
5
+  final String passwordConfirm;  // 添加密码确认
6
+  final String? fullName;  // 改为fullName
6 7
 
7 8
   RegisterRequest({
9
+    required this.username,
8 10
     required this.email,
9 11
     required this.password,
10
-    required this.name,
11
-    this.phone,
12
+    required this.passwordConfirm,
13
+    this.fullName,
12 14
   });
13 15
 
14 16
   Map<String, dynamic> toJson() {
15 17
     return {
18
+      'username': username,
16 19
       'email': email,
17 20
       'password': password,
18
-      'name': name,
19
-      'phone': phone,
21
+      'password_confirm': passwordConfirm,
22
+      'full_name': fullName,
20 23
     };
21 24
   }
22 25
 }

+ 47
- 0
lib/data/models/auth/secure_login_request.dart Wyświetl plik

@@ -0,0 +1,47 @@
1
+import '../../../core/utils/crypto_utils.dart';
2
+
3
+class SecureLoginRequest {
4
+  final String username;
5
+  final String passwordHash; // 客户端哈希后的密码
6
+  final String salt; // 盐值
7
+  final String? clientKey; // 客户端密钥(用于加密传输)
8
+  
9
+  SecureLoginRequest({
10
+    required this.username,
11
+    required this.passwordHash,
12
+    required this.salt,
13
+    this.clientKey,
14
+  });
15
+  
16
+  Map<String, dynamic> toJson() {
17
+    return {
18
+      'username': username,
19
+      'password_hash': passwordHash,
20
+      'salt': salt,
21
+      'client_key': clientKey,
22
+    };
23
+  }
24
+  
25
+  // 工厂方法:创建安全的登录请求
26
+  factory SecureLoginRequest.create({
27
+    required String username,
28
+    required String password,
29
+  }) {
30
+    // 生成盐值
31
+    final salt = CryptoUtils.generateSalt();
32
+    
33
+    // 密码加盐哈希
34
+    final passwordWithSalt = password + salt;
35
+    final passwordHash = CryptoUtils.sha256Hash(passwordWithSalt);
36
+    
37
+    // 生成客户端密钥(可选,用于进一步加密)
38
+    final clientKey = CryptoUtils.generateClientKey();
39
+    
40
+    return SecureLoginRequest(
41
+      username: username,
42
+      passwordHash: passwordHash,
43
+      salt: salt,
44
+      clientKey: clientKey,
45
+    );
46
+  }
47
+}

+ 27
- 0
lib/data/models/auth/token_response.dart Wyświetl plik

@@ -0,0 +1,27 @@
1
+import '../user.dart';
2
+
3
+class TokenResponse {
4
+  final String accessToken;
5
+  final String tokenType;
6
+  final int? expiresIn;
7
+  final String? refreshToken;
8
+  final User user;
9
+
10
+  TokenResponse({
11
+    required this.accessToken,
12
+    required this.user,
13
+    this.tokenType = 'bearer',
14
+    this.expiresIn,
15
+    this.refreshToken,
16
+  });
17
+
18
+  factory TokenResponse.fromJson(Map<String, dynamic> json) {
19
+    return TokenResponse(
20
+      accessToken: json['access_token'] ?? '',
21
+      tokenType: json['token_type'] ?? 'bearer',
22
+      expiresIn: json['expires_in'],
23
+      refreshToken: json['refresh_token'],
24
+      user: User.fromJson(json['user']),
25
+    );
26
+  }
27
+}

+ 47
- 21
lib/data/models/user.dart Wyświetl plik

@@ -1,29 +1,43 @@
1 1
 class User {
2
-  final String id;
2
+  final int id;  // 改为int,匹配API
3
+  final String username;  // 改为username
3 4
   final String email;
4
-  final String name;
5
-  final String? avatarUrl;
5
+  final String? fullName;  // 改为fullName
6
+  final String? avatar;
6 7
   final String? phone;
7
-  final DateTime? createdAt;
8
+  final bool isActive;
9
+  final bool isVerified;
10
+  final DateTime createdAt;
11
+  final DateTime? lastLogin;
8 12
 
9 13
   User({
10 14
     required this.id,
15
+    required this.username,
11 16
     required this.email,
12
-    required this.name,
13
-    this.avatarUrl,
17
+    this.fullName,
18
+    this.avatar,
14 19
     this.phone,
15
-    this.createdAt,
20
+    this.isActive = true,
21
+    this.isVerified = false,
22
+    required this.createdAt,
23
+    this.lastLogin,
16 24
   });
17 25
 
18 26
   factory User.fromJson(Map<String, dynamic> json) {
19 27
     return User(
20
-      id: json['id'] ?? '',
28
+      id: json['id'] ?? 0,
29
+      username: json['username'] ?? '',
21 30
       email: json['email'] ?? '',
22
-      name: json['name'] ?? '',
23
-      avatarUrl: json['avatar_url'],
31
+      fullName: json['full_name'],
32
+      avatar: json['avatar'],
24 33
       phone: json['phone'],
34
+      isActive: json['is_active'] ?? true,
35
+      isVerified: json['is_verified'] ?? false,
25 36
       createdAt: json['created_at'] != null
26 37
           ? DateTime.parse(json['created_at'])
38
+          : DateTime.now(),
39
+      lastLogin: json['last_login'] != null
40
+          ? DateTime.parse(json['last_login'])
27 41
           : null,
28 42
     );
29 43
   }
@@ -31,29 +45,41 @@ class User {
31 45
   Map<String, dynamic> toJson() {
32 46
     return {
33 47
       'id': id,
48
+      'username': username,
34 49
       'email': email,
35
-      'name': name,
36
-      'avatar_url': avatarUrl,
37
-      'phone': phone,
38
-      'created_at': createdAt?.toIso8601String(),
50
+      'full_name': fullName,
51
+      'avatar': avatar,
52
+      'is_active': isActive,
53
+      'is_verified': isVerified,
54
+      'created_at': createdAt.toIso8601String(),
55
+      'last_login': lastLogin?.toIso8601String(),
39 56
     };
40 57
   }
41 58
 
59
+  // 添加一个getter来兼容之前的name字段
60
+  // String get name => fullName ?? username;
61
+
42 62
   User copyWith({
43
-    String? id,
63
+    int? id,
64
+    String? username,
44 65
     String? email,
45
-    String? name,
46
-    String? avatarUrl,
47
-    String? phone,
66
+    String? fullName,
67
+    String? avatar,
68
+    bool? isActive,
69
+    bool? isVerified,
48 70
     DateTime? createdAt,
71
+    DateTime? lastLogin,
49 72
   }) {
50 73
     return User(
51 74
       id: id ?? this.id,
75
+      username: username ?? this.username,
52 76
       email: email ?? this.email,
53
-      name: name ?? this.name,
54
-      avatarUrl: avatarUrl ?? this.avatarUrl,
55
-      phone: phone ?? this.phone,
77
+      fullName: fullName ?? this.fullName,
78
+      avatar: avatar ?? this.avatar,
79
+      isActive: isActive ?? this.isActive,
80
+      isVerified: isVerified ?? this.isVerified,
56 81
       createdAt: createdAt ?? this.createdAt,
82
+      lastLogin: lastLogin ?? this.lastLogin,
57 83
     );
58 84
   }
59 85
 }

+ 219
- 69
lib/data/repositories/auth_repository.dart Wyświetl plik

@@ -1,97 +1,247 @@
1
-import '../datasources/local/shared_prefs.dart';
1
+import 'dart:convert';
2
+import 'package:shared_preferences/shared_preferences.dart';
3
+import '../../core/constants/api_constants.dart';
4
+import '../../core/utils/crypto_utils.dart';
5
+import '../datasources/remote/api_client.dart';
6
+import '../models/auth/secure_login_request.dart';
2 7
 import '../models/user.dart';
3 8
 import '../models/auth/login_request.dart';
4 9
 import '../models/auth/register_request.dart';
10
+import '../models/auth/token_response.dart';
5 11
 import '../models/api_response.dart';
6 12
 
7 13
 class AuthRepository {
8
-  final SharedPrefs localDataSource;
9
-
10
-  AuthRepository({required this.localDataSource});
11
-
12
-  // 模拟登录
14
+  final ApiClient _apiClient;
15
+  final SharedPreferences _prefs;
16
+  
17
+  AuthRepository({
18
+    required ApiClient apiClient,
19
+    required SharedPreferences prefs,
20
+  }) : _apiClient = apiClient, _prefs = prefs;
21
+  
22
+  // 登录(需要使用安全登录替代)
13 23
   Future<ApiResponse<User>> login(LoginRequest request) async {
14
-    await Future.delayed(const Duration(seconds: 1));
15
-    
16
-    // 模拟成功登录
17
-    if (request.email == 'test@example.com' && request.password == '123456') {
18
-      final user = User(
19
-        id: '1',
20
-        email: request.email,
21
-        name: '测试用户',
22
-        avatarUrl: 'https://ui-avatars.com/api/?name=测试用户',
23
-        phone: '13800138000',
24
-        createdAt: DateTime.now(),
24
+    try {
25
+      final response = await _apiClient.post(
26
+        ApiConstants.getLoginUrl(),
27
+        request.toJson(),
28
+        withAuth: false,
25 29
       );
26 30
       
27
-      // 保存token和用户数据
28
-      await localDataSource.setAuthToken('mock_jwt_token_${user.id}');
29
-      await localDataSource.setUserData(user.toJson().toString());
30
-      
31
+      if (response.statusCode == 200) {
32
+        final tokenResponse = TokenResponse.fromJson(
33
+          json.decode(response.body)
34
+        );
35
+        
36
+        // 保存token
37
+        await _apiClient.saveToken(tokenResponse.accessToken);
38
+        
39
+        return ApiResponse<User>(
40
+          success: true,
41
+          message: '登录成功',
42
+          data: tokenResponse.user,
43
+        );
44
+      } else {
45
+        final error = json.decode(response.body);
46
+        return ApiResponse<User>(
47
+          success: false,
48
+          message: error['detail'] ?? '登录失败',
49
+        );
50
+      }
51
+    } catch (e) {
31 52
       return ApiResponse<User>(
32
-        success: true,
33
-        message: '登录成功',
34
-        data: user,
53
+        success: false,
54
+        message: '登录失败: $e',
35 55
       );
36
-    } else {
56
+    }
57
+  }
58
+  
59
+  // 注册(需要使用安全注册替代)
60
+  Future<ApiResponse<User>> register(RegisterRequest request) async {
61
+    try {
62
+      final response = await _apiClient.post(
63
+        ApiConstants.getRegisterUrl(),
64
+        request.toJson(),
65
+        withAuth: false,
66
+      );
67
+      
68
+      if (response.statusCode == 201) {
69
+        final tokenResponse = TokenResponse.fromJson(
70
+          json.decode(response.body)
71
+        );
72
+        
73
+        // 保存token
74
+        await _apiClient.saveToken(tokenResponse.accessToken);
75
+        
76
+        return ApiResponse<User>(
77
+          success: true,
78
+          message: '注册成功',
79
+          data: tokenResponse.user,
80
+        );
81
+      } else {
82
+        final error = json.decode(response.body);
83
+        return ApiResponse<User>(
84
+          success: false,
85
+          message: error['detail'] ?? '注册失败',
86
+        );
87
+      }
88
+    } catch (e) {
37 89
       return ApiResponse<User>(
38 90
         success: false,
39
-        message: '邮箱或密码错误',
91
+        message: '注册失败: $e',
40 92
       );
41 93
     }
42 94
   }
95
+  
96
+  // 安全登录方法
97
+  Future<ApiResponse<User>> secureLogin(LoginRequest request) async {
98
+    try {
99
+      // 创建安全登录请求
100
+      final secureRequest = SecureLoginRequest.create(
101
+        username: request.username,
102
+        password: request.password,
103
+      );
43 104
 
44
-  // 模拟注册
45
-  Future<ApiResponse<User>> register(RegisterRequest request) async {
46
-    await Future.delayed(const Duration(seconds: 1));
47
-    
48
-    final user = User(
49
-      id: DateTime.now().millisecondsSinceEpoch.toString(),
50
-      email: request.email,
51
-      name: request.name,
52
-      phone: request.phone,
53
-      createdAt: DateTime.now(),
54
-    );
55
-    
56
-    // 保存token和用户数据
57
-    await localDataSource.setAuthToken('mock_jwt_token_${user.id}');
58
-    await localDataSource.setUserData(user.toJson().toString());
59
-    
60
-    return ApiResponse<User>(
61
-      success: true,
62
-      message: '注册成功',
63
-      data: user,
64
-    );
105
+      print(ApiConstants.getLoginUrl());
106
+      print(secureRequest.toJson());
107
+      
108
+      final response = await _apiClient.post(
109
+        ApiConstants.getLoginUrl(),
110
+        secureRequest.toJson(),
111
+        withAuth: false,
112
+      );
113
+      print(response.statusCode);
114
+      
115
+      // ... 处理响应
116
+      if (response.statusCode == 200) {
117
+        final tokenResponse = TokenResponse.fromJson(
118
+          json.decode(response.body)
119
+        );
120
+        
121
+        // 保存token
122
+        await _apiClient.saveToken(tokenResponse.accessToken);
123
+        
124
+        return ApiResponse<User>(
125
+          success: true,
126
+          message: '登录成功',
127
+          data: tokenResponse.user,
128
+        );
129
+      } else {
130
+        final error = json.decode(response.body);
131
+        return ApiResponse<User>(
132
+          success: false,
133
+          message: error['detail'] ?? '登录失败',
134
+        );
135
+      }
136
+    } catch (e) {
137
+      return ApiResponse<User>(
138
+        success: false,
139
+        message: '登录失败: $e',
140
+      );
141
+    }
65 142
   }
66
-
67
-  // 检查登录状态
68
-  Future<bool> isLoggedIn() async {
69
-    final token = localDataSource.getAuthToken();
70
-    return token != null && token.isNotEmpty;
143
+  
144
+  // 安全注册方法
145
+  Future<ApiResponse<User>> secureRegister(RegisterRequest request) async {
146
+    try {
147
+      // 创建安全注册数据
148
+      final salt = CryptoUtils.generateSalt();
149
+      final passwordHash = CryptoUtils.sha256Hash(request.password + salt);
150
+      
151
+      final registerData = {
152
+        'username': request.username,
153
+        'email': request.email,
154
+        'password_hash': passwordHash,
155
+        'salt': salt,
156
+        'full_name': request.fullName,
157
+        'password_confirm': request.passwordConfirm,
158
+      };
159
+      
160
+      final response = await _apiClient.post(
161
+        ApiConstants.getRegisterUrl(),
162
+        registerData,
163
+        withAuth: false,
164
+      );
165
+      
166
+      // ... 处理响应
167
+      if (response.statusCode == 201) {
168
+        final tokenResponse = TokenResponse.fromJson(
169
+          json.decode(response.body)
170
+        );
171
+        
172
+        // 保存token
173
+        await _apiClient.saveToken(tokenResponse.accessToken);
174
+        
175
+        return ApiResponse<User>(
176
+          success: true,
177
+          message: '注册成功',
178
+          data: tokenResponse.user,
179
+        );
180
+      } else {
181
+        final error = json.decode(response.body);
182
+        return ApiResponse<User>(
183
+          success: false,
184
+          message: error['detail'] ?? '注册失败',
185
+        );
186
+      }
187
+    } catch (e) {
188
+      return ApiResponse<User>(
189
+        success: false,
190
+        message: '注册失败: $e',
191
+      );
192
+    }
71 193
   }
72 194
 
73 195
   // 获取当前用户
74
-  Future<User?> getCurrentUser() async {
75
-    final userData = localDataSource.getUserData();
76
-    if (userData != null) {
77
-      try {
78
-        // 这里简单处理,实际应该解析JSON
79
-        return User(
80
-          id: '1',
81
-          email: 'test@example.com',
82
-          name: '测试用户',
196
+  Future<ApiResponse<User>> getCurrentUser() async {
197
+    try {
198
+      final response = await _apiClient.get(
199
+        ApiConstants.getCurrentUserUrl(),
200
+        withAuth: true,
201
+      );
202
+      
203
+      if (response.statusCode == 200) {
204
+        final userData = json.decode(response.body);
205
+        return ApiResponse<User>(
206
+          success: true,
207
+          message: '获取成功',
208
+          data: User.fromJson(userData),
209
+        );
210
+      } else {
211
+        return ApiResponse<User>(
212
+          success: false,
213
+          message: '获取用户信息失败',
83 214
         );
84
-      } catch (e) {
85
-        return null;
86 215
       }
216
+    } catch (e) {
217
+      return ApiResponse<User>(
218
+        success: false,
219
+        message: '获取失败: $e',
220
+      );
87 221
     }
88
-    return null;
89 222
   }
90
-
91
-  // 注销
223
+  
224
+  // 登出
92 225
   Future<bool> logout() async {
93
-    await localDataSource.removeAuthToken();
94
-    await localDataSource.removeUserData();
95
-    return true;
226
+    try {
227
+      final response = await _apiClient.post(
228
+        ApiConstants.getLogoutUrl(),
229
+        {},
230
+        withAuth: true,
231
+      );
232
+      
233
+      if (response.statusCode == 200) {
234
+        await _apiClient.clearToken();
235
+        return true;
236
+      }
237
+      return false;
238
+    } catch (e) {
239
+      return false;
240
+    }
241
+  }
242
+  
243
+  // 检查登录状态
244
+  Future<bool> isLoggedIn() async {
245
+    return _apiClient.isLoggedIn();
96 246
   }
97 247
 }

+ 62
- 36
lib/data/repositories/user_repository.dart Wyświetl plik

@@ -1,50 +1,76 @@
1
-import '../datasources/local/shared_prefs.dart';
1
+import 'dart:convert';
2
+import '../../core/constants/api_constants.dart';
3
+import '../datasources/remote/api_client.dart';
2 4
 import '../models/user.dart';
3 5
 import '../models/api_response.dart';
4 6
 
5 7
 class UserRepository {
6
-  final SharedPrefs localDataSource;
7
-
8
-  UserRepository({required this.localDataSource});
9
-
10
-  // 模拟获取用户信息
8
+  final ApiClient _apiClient;
9
+  
10
+  UserRepository({required ApiClient apiClient}) : _apiClient = apiClient;
11
+  
12
+  // 获取用户资料
11 13
   Future<ApiResponse<User>> getUserProfile() async {
12
-    await Future.delayed(const Duration(seconds: 1));
13
-    
14
-    final userData = localDataSource.getUserData();
15
-    if (userData != null) {
16
-      final user = User(
17
-        id: '1',
18
-        email: 'test@example.com',
19
-        name: '测试用户',
20
-        avatarUrl: 'https://ui-avatars.com/api/?name=测试用户',
21
-        phone: '13800138000',
22
-        createdAt: DateTime.now(),
14
+    try {
15
+      final response = await _apiClient.get(
16
+        ApiConstants.getUserProfileUrl(),
17
+        withAuth: true,
23 18
       );
24 19
       
20
+      if (response.statusCode == 200) {
21
+        final userData = json.decode(response.body);
22
+        return ApiResponse<User>(
23
+          success: true,
24
+          message: '获取成功',
25
+          data: User.fromJson(userData),
26
+        );
27
+      } else {
28
+        return ApiResponse<User>(
29
+          success: false,
30
+          message: '获取用户资料失败',
31
+        );
32
+      }
33
+    } catch (e) {
25 34
       return ApiResponse<User>(
26
-        success: true,
27
-        message: '获取成功',
28
-        data: user,
35
+        success: false,
36
+        message: '获取失败: $e',
29 37
       );
30 38
     }
31
-    
32
-    return ApiResponse<User>(
33
-      success: false,
34
-      message: '未登录',
35
-    );
36 39
   }
37
-
38
-  // 模拟更新用户信息
40
+  
41
+  // 更新用户资料
39 42
   Future<ApiResponse<User>> updateUserProfile(User user) async {
40
-    await Future.delayed(const Duration(seconds: 1));
41
-    
42
-    await localDataSource.setUserData(user.toJson().toString());
43
-    
44
-    return ApiResponse<User>(
45
-      success: true,
46
-      message: '更新成功',
47
-      data: user,
48
-    );
43
+    try {
44
+      final updateData = {
45
+        'full_name': user.fullName,
46
+        'email': user.email,
47
+      };
48
+      
49
+      final response = await _apiClient.put(
50
+        ApiConstants.getUpdateProfileUrl(),
51
+        updateData,
52
+        withAuth: true,
53
+      );
54
+      
55
+      if (response.statusCode == 200) {
56
+        final userData = json.decode(response.body);
57
+        return ApiResponse<User>(
58
+          success: true,
59
+          message: '更新成功',
60
+          data: User.fromJson(userData),
61
+        );
62
+      } else {
63
+        final error = json.decode(response.body);
64
+        return ApiResponse<User>(
65
+          success: false,
66
+          message: error['detail'] ?? '更新失败',
67
+        );
68
+      }
69
+    } catch (e) {
70
+      return ApiResponse<User>(
71
+        success: false,
72
+        message: '更新失败: $e',
73
+      );
74
+    }
49 75
   }
50 76
 }

+ 13
- 2
lib/injection_container.dart Wyświetl plik

@@ -1,6 +1,8 @@
1 1
 import 'package:get_it/get_it.dart';
2 2
 import 'package:shared_preferences/shared_preferences.dart';
3
+import 'package:http/http.dart' as http;
3 4
 import 'data/datasources/local/shared_prefs.dart';
5
+import 'data/datasources/remote/api_client.dart';
4 6
 import 'data/repositories/auth_repository.dart';
5 7
 import 'data/repositories/user_repository.dart';
6 8
 import 'presentation/providers/auth_provider.dart';
@@ -13,12 +15,21 @@ Future<void> init() async {
13 15
   final sharedPreferences = await SharedPreferences.getInstance();
14 16
   sl.registerLazySingleton(() => sharedPreferences);
15 17
   
18
+  // HTTP Client
19
+  sl.registerLazySingleton(() => http.Client());
20
+  
16 21
   // Data sources
17 22
   sl.registerLazySingleton(() => SharedPrefs(sl()));
23
+  sl.registerLazySingleton(() => ApiClient(sl()));
18 24
   
19 25
   // Repositories
20
-  sl.registerLazySingleton(() => AuthRepository(localDataSource: sl()));
21
-  sl.registerLazySingleton(() => UserRepository(localDataSource: sl()));
26
+  sl.registerLazySingleton(() => AuthRepository(
27
+    apiClient: sl(),
28
+    prefs: sl(),
29
+  ));
30
+  sl.registerLazySingleton(() => UserRepository(
31
+    apiClient: sl(),
32
+  ));
22 33
   
23 34
   // Providers
24 35
   sl.registerFactory(() => AuthProvider(authRepository: sl()));

+ 5
- 5
lib/presentation/providers/auth_provider.dart Wyświetl plik

@@ -23,19 +23,19 @@ class AuthProvider with ChangeNotifier {
23 23
   Future<void> _loadCurrentUser() async {
24 24
     final user = await authRepository.getCurrentUser();
25 25
     if (user != null) {
26
-      _user = user;
26
+      _user = user.data;
27 27
       notifyListeners();
28 28
     }
29 29
   }
30 30
   
31
-  Future<void> login(String email, String password) async {
31
+  Future<void> login(String username, String password) async {
32 32
     _isLoading = true;
33 33
     _error = null;
34 34
     notifyListeners();
35 35
     
36 36
     try {
37 37
       final response = await authRepository.login(
38
-        LoginRequest(email: email, password: password),
38
+        LoginRequest(username: username, password: password),
39 39
       );
40 40
       
41 41
       if (response.success && response.data != null) {
@@ -52,14 +52,14 @@ class AuthProvider with ChangeNotifier {
52 52
     }
53 53
   }
54 54
   
55
-  Future<void> register(String email, String password, String name, String? phone) async {
55
+  Future<void> register(String username, String email, String password, String passwordConfirm, String? phone) async {
56 56
     _isLoading = true;
57 57
     _error = null;
58 58
     notifyListeners();
59 59
     
60 60
     try {
61 61
       final response = await authRepository.register(
62
-        RegisterRequest(email: email, password: password, name: name, phone: phone),
62
+        RegisterRequest(username: username, email: email, password: password, passwordConfirm: passwordConfirm),
63 63
       );
64 64
       
65 65
       if (response.success && response.data != null) {

+ 5
- 9
lib/presentation/screens/auth/login_screen.dart Wyświetl plik

@@ -65,17 +65,13 @@ class _LoginScreenState extends State<LoginScreen> {
65 65
                 ),
66 66
                 const SizedBox(height: 40),
67 67
                 AppTextField(
68
-                  controller: _emailController,
69
-                  labelText: '邮箱地址',
70
-                  hintText: '请输入邮箱',
71
-                  keyboardType: TextInputType.emailAddress,
72
-                  prefixIcon: const Icon(Icons.email_outlined),
68
+                  controller: _emailController,  // 改为username
69
+                  labelText: '用户名',
70
+                  hintText: '请输入用户名',
71
+                  prefixIcon: const Icon(Icons.person_outline),
73 72
                   validator: (value) {
74 73
                     if (value == null || value.isEmpty) {
75
-                      return '请输入邮箱地址';
76
-                    }
77
-                    if (!value.contains('@')) {
78
-                      return '请输入有效的邮箱地址';
74
+                      return '请输入用户名';
79 75
                     }
80 76
                     return null;
81 77
                   },

+ 2
- 1
lib/presentation/screens/auth/register_screen.dart Wyświetl plik

@@ -155,9 +155,10 @@ class _RegisterScreenState extends State<RegisterScreen> {
155 155
                     onPressed: () async {
156 156
                       if (_formKey.currentState!.validate()) {
157 157
                         await authProvider.register(
158
+                          _nameController.text,
158 159
                           _emailController.text.trim(),
159 160
                           _passwordController.text,
160
-                          _nameController.text,
161
+                          _passwordController.text,
161 162
                           _phoneController.text.isNotEmpty
162 163
                               ? _phoneController.text
163 164
                               : null,

+ 1
- 1
lib/presentation/screens/home/home_screen.dart Wyświetl plik

@@ -58,7 +58,7 @@ class HomeScreen extends StatelessWidget {
58 58
                               children: [
59 59
                                 Text(
60 60
                                   authProvider.isAuthenticated
61
-                                      ? '您好,${authProvider.user?.name}'
61
+                                      ? '您好,${authProvider.user?.fullName}'
62 62
                                       : '您好,游客',
63 63
                                   style: const TextStyle(
64 64
                                     fontSize: 18,

+ 9
- 9
lib/presentation/screens/profile/profile_detail_screen.dart Wyświetl plik

@@ -23,8 +23,8 @@ class _ProfileDetailScreenState extends State<ProfileDetailScreen> {
23 23
   void initState() {
24 24
     super.initState();
25 25
     final userProvider = Provider.of<UserProvider>(context, listen: false);
26
-    _currentUser = userProvider.user ?? User(id: '', email: '', name: '');
27
-    _nameController = TextEditingController(text: _currentUser.name);
26
+    _currentUser = userProvider.user ?? User(id: -1, email: '', username: '', createdAt: DateTime.now());
27
+    _nameController = TextEditingController(text: _currentUser.username);
28 28
     _emailController = TextEditingController(text: _currentUser.email);
29 29
     _phoneController = TextEditingController(text: _currentUser.phone ?? '');
30 30
   }
@@ -75,10 +75,10 @@ class _ProfileDetailScreenState extends State<ProfileDetailScreen> {
75 75
                     CircleAvatar(
76 76
                       radius: 60,
77 77
                       backgroundColor: Colors.blue[100],
78
-                      backgroundImage: _currentUser.avatarUrl != null
79
-                          ? NetworkImage(_currentUser.avatarUrl!)
78
+                      backgroundImage: _currentUser.avatar != null
79
+                          ? NetworkImage(_currentUser.avatar!)
80 80
                           : null,
81
-                      child: _currentUser.avatarUrl == null
81
+                      child: _currentUser.avatar == null
82 82
                           ? const Icon(
83 83
                               Icons.person,
84 84
                               size: 80,
@@ -175,10 +175,10 @@ class _ProfileDetailScreenState extends State<ProfileDetailScreen> {
175 175
                 onPressed: () async {
176 176
                   if (_formKey.currentState!.validate()) {
177 177
                     final updatedUser = _currentUser.copyWith(
178
-                      name: _nameController.text.trim(),
179
-                      phone: _phoneController.text.trim().isNotEmpty
180
-                          ? _phoneController.text.trim()
181
-                          : null,
178
+                      fullName: _nameController.text.trim(),
179
+                      // phone: _phoneController.text.trim().isNotEmpty
180
+                      //     ? _phoneController.text.trim()
181
+                      //     : null,
182 182
                     );
183 183
                     
184 184
                     await userProvider.updateUserProfile(updatedUser);

+ 7
- 5
lib/presentation/screens/profile/profile_screen.dart Wyświetl plik

@@ -75,10 +75,10 @@ class _ProfileContentState extends State<_ProfileContent> {
75 75
                           CircleAvatar(
76 76
                             radius: 50,
77 77
                             backgroundColor: Colors.blue[100],
78
-                            backgroundImage: authProvider.user?.avatarUrl != null
79
-                                ? NetworkImage(authProvider.user!.avatarUrl!)
78
+                            backgroundImage: authProvider.user?.avatar != null
79
+                                ? NetworkImage(authProvider.user!.avatar!)
80 80
                                 : null,
81
-                            child: authProvider.user?.avatarUrl == null
81
+                            child: authProvider.user?.avatar == null
82 82
                                 ? const Icon(
83 83
                                     Icons.person,
84 84
                                     size: 60,
@@ -111,7 +111,7 @@ class _ProfileContentState extends State<_ProfileContent> {
111 111
                     ),
112 112
                     const SizedBox(height: 16),
113 113
                     Text(
114
-                      authProvider.user?.name ?? '用户',
114
+                      authProvider.user?.fullName ?? '用户',
115 115
                       style: const TextStyle(
116 116
                         fontSize: 22,
117 117
                         fontWeight: FontWeight.bold,
@@ -232,7 +232,9 @@ class _ProfileContentState extends State<_ProfileContent> {
232 232
                         subtitle: '安全退出当前账号',
233 233
                         onTap: () async {
234 234
                           await authProvider.logout();
235
-                          Navigator.of(context).pushReplacementNamed('/');
235
+                          if (mounted) {
236
+                            Navigator.of(context).pushReplacementNamed('/');
237
+                          }
236 238
                         },
237 239
                       ),
238 240
                     ],

+ 3
- 1
lib/presentation/screens/splash_screen.dart Wyświetl plik

@@ -14,7 +14,9 @@ class SplashScreenState extends State<SplashScreen> {
14 14
   @override
15 15
   void initState() {
16 16
     super.initState();
17
-    _initializeApp();
17
+    WidgetsBinding.instance.addPostFrameCallback((_) {
18
+      _initializeApp();
19
+    });
18 20
   }
19 21
 
20 22
   Future<void> _initializeApp() async {

+ 48
- 0
pubspec.lock Wyświetl plik

@@ -9,6 +9,14 @@ packages:
9 9
       url: "https://pub.flutter-io.cn"
10 10
     source: hosted
11 11
     version: "2.7.0"
12
+  asn1lib:
13
+    dependency: transitive
14
+    description:
15
+      name: asn1lib
16
+      sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024"
17
+      url: "https://pub.flutter-io.cn"
18
+    source: hosted
19
+    version: "1.6.5"
12 20
   async:
13 21
     dependency: transitive
14 22
     description:
@@ -49,6 +57,22 @@ packages:
49 57
       url: "https://pub.flutter-io.cn"
50 58
     source: hosted
51 59
     version: "1.19.1"
60
+  convert:
61
+    dependency: transitive
62
+    description:
63
+      name: convert
64
+      sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
65
+      url: "https://pub.flutter-io.cn"
66
+    source: hosted
67
+    version: "3.1.2"
68
+  crypto:
69
+    dependency: "direct main"
70
+    description:
71
+      name: crypto
72
+      sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
73
+      url: "https://pub.flutter-io.cn"
74
+    source: hosted
75
+    version: "3.0.7"
52 76
   cupertino_icons:
53 77
     dependency: "direct main"
54 78
     description:
@@ -57,6 +81,14 @@ packages:
57 81
       url: "https://pub.flutter-io.cn"
58 82
     source: hosted
59 83
     version: "1.0.8"
84
+  encrypt:
85
+    dependency: "direct main"
86
+    description:
87
+      name: encrypt
88
+      sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
89
+      url: "https://pub.flutter-io.cn"
90
+    source: hosted
91
+    version: "5.0.3"
60 92
   fake_async:
61 93
     dependency: transitive
62 94
     description:
@@ -144,6 +176,14 @@ packages:
144 176
       url: "https://pub.flutter-io.cn"
145 177
     source: hosted
146 178
     version: "0.18.1"
179
+  js:
180
+    dependency: transitive
181
+    description:
182
+      name: js
183
+      sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
184
+      url: "https://pub.flutter-io.cn"
185
+    source: hosted
186
+    version: "0.7.2"
147 187
   leak_tracker:
148 188
     dependency: transitive
149 189
     description:
@@ -272,6 +312,14 @@ packages:
272 312
       url: "https://pub.flutter-io.cn"
273 313
     source: hosted
274 314
     version: "2.1.8"
315
+  pointycastle:
316
+    dependency: "direct main"
317
+    description:
318
+      name: pointycastle
319
+      sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
320
+      url: "https://pub.flutter-io.cn"
321
+    source: hosted
322
+    version: "3.9.1"
275 323
   provider:
276 324
     dependency: "direct main"
277 325
     description:

+ 4
- 0
pubspec.yaml Wyświetl plik

@@ -16,6 +16,10 @@ dependencies:
16 16
   flutter_svg: ^2.0.9
17 17
   intl: ^0.18.1
18 18
   get_it: ^7.6.4
19
+  # 安全相关依赖
20
+  crypto: ^3.0.3        # SHA256等哈希算法
21
+  encrypt: ^5.0.1       # AES加密
22
+  pointycastle: ^3.7.3  # 更多加密算法
19 23
 
20 24
 dev_dependencies:
21 25
   flutter_test: