AI助手优化-头像和对话功能

This commit is contained in:
2026-09-29 09:45:02 +08:00
parent 80222099ff
commit f4ad2f8612
15 changed files with 990 additions and 37 deletions
+55
View File
@@ -39,6 +39,61 @@ class AuthApi {
}
}
/// 拉当前用户资料。冷启动时本地缓存的 userInfo 可能过期(在别处换过头像),
/// 用它刷一次。
Future<AuthResponse> profile() async {
try {
final res = await _dio.get('/auth/profile');
return AuthResponse.fromJson(_map(res.data));
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 更新资料。目前只有头像。
///
/// [avatar] 传 null = 不改动;[clearAvatar] 传 true = 清空。
/// 这两种语义要分清 —— 混淆会让「只改昵称」意外把头像清掉。
Future<AuthResponse> updateProfile({String? avatar, bool clearAvatar = false}) async {
try {
final res = await _dio.put('/auth/profile', data: {
'avatar': ?avatar,
if (clearAvatar) 'clearAvatar': true,
});
return AuthResponse.fromJson(_map(res.data));
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 上传头像,返回**相对路径**(再由 updateProfile 提交)。
Future<String> uploadAvatar(String filePath) async {
try {
final form = FormData.fromMap({
'file': await MultipartFile.fromFile(filePath, filename: _fileName(filePath)),
});
final res = await _dio.post<dynamic>(
'/auth/avatar',
data: form,
options: Options(receiveTimeout: const Duration(seconds: 60)),
);
final data = res.data;
if (data is Map && data['path'] != null) {
return data['path'].toString();
}
throw ApiException(-1, '上传失败:服务端没返回路径');
} on DioException catch (e) {
throw _unwrap(e);
}
}
/// 从路径取文件名,后端靠扩展名判断类型
static String _fileName(String path) {
final normalized = path.replaceAll('\\', '/');
final name = normalized.substring(normalized.lastIndexOf('/') + 1);
return name.isEmpty ? 'avatar.jpg' : name;
}
Map<String, dynamic> _map(Object? data) {
if (data is Map<String, dynamic>) return data;
throw ApiException(-1, '响应格式异常');