From 34bd2b91caf8e316397fd9cfa6acb4b2a4a284a5 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 6 Nov 2025 17:29:19 +0800 Subject: [PATCH] =?UTF-8?q?IM=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/mine/user_controller.dart | 1 + lib/controller/mine/user_info_controller.dart | 18 ++-- lib/im/im_manager.dart | 57 ++++++++++++ lib/main.dart | 2 +- lib/model/common/oss_data.dart | 64 ++++++++++++++ lib/network/api_urls.dart | 1 + lib/network/user_api.dart | 4 + lib/network/user_api.g.dart | 31 +++++++ lib/oss/oss_manager.dart | 86 +++++++++++++------ lib/pages/main/main_page.dart | 5 +- pubspec.lock | 48 +++++++++++ pubspec.yaml | 1 + 12 files changed, 274 insertions(+), 44 deletions(-) create mode 100644 lib/model/common/oss_data.dart diff --git a/lib/controller/mine/user_controller.dart b/lib/controller/mine/user_controller.dart index d9ba74f..59dc2fd 100644 --- a/lib/controller/mine/user_controller.dart +++ b/lib/controller/mine/user_controller.dart @@ -1,4 +1,5 @@ import 'package:dating_touchme_app/im/im_manager.dart'; +import 'package:dating_touchme_app/oss/oss_manager.dart'; import 'package:get/get.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import '../../network/user_api.dart'; diff --git a/lib/controller/mine/user_info_controller.dart b/lib/controller/mine/user_info_controller.dart index 17c4bd1..db9c586 100644 --- a/lib/controller/mine/user_info_controller.dart +++ b/lib/controller/mine/user_info_controller.dart @@ -1,4 +1,6 @@ import 'dart:io'; +import 'package:dating_touchme_app/oss/oss_manager.dart'; +import 'package:flustars/flustars.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -107,18 +109,10 @@ class UserInfoController extends GetxController { try { // 显示加载提示 SmartDialog.showLoading(msg: '正在处理头像...'); - - // 更新本地头像URL(实际项目中应该先上传到服务器) - // 这里直接使用本地文件路径作为临时URL - avatarUrl.value = imageFile.path; - - // 保存到本地存储 - final currentUserInfo = storage.read('userInfo') ?? {}; - if (currentUserInfo is Map) { - currentUserInfo['avatarUrl'] = imageFile.path; - await storage.write('userInfo', currentUserInfo); - } - + String objectName = '${DateUtil.getNowDateMs()}.${imageFile.path.split('.').last}'; + String imageUrl = await OSSManager.instance.uploadFile(imageFile.readAsBytesSync(), objectName); + print('上传成功,图片URL: $imageUrl'); + avatarUrl.value = imageUrl; SmartDialog.dismiss(); SmartDialog.showToast('头像设置成功'); } catch (e) { diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index c8329ee..ac278d9 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -64,6 +64,63 @@ class IMManager { print('User kicked out of IM server'); }, )); + EMClient.getInstance.chatManager.addEventHandler( + // EMChatEventHandler 对应的 key。 + "", + EMChatEventHandler( + onMessagesReceived: (messages) { + for (var msg in messages) { + switch (msg.body.type) { + case MessageType.TXT: + { + + } + break; + case MessageType.IMAGE: + { + + } + break; + case MessageType.VIDEO: + { + + } + break; + case MessageType.LOCATION: + { + + } + break; + case MessageType.VOICE: + { + + } + break; + case MessageType.FILE: + { + + } + break; + case MessageType.CUSTOM: + { + + } + break; + case MessageType.COMBINE: + { + + } + break; + case MessageType.CMD: + { + // 当前回调中不会有 CMD 类型消息,CMD 类型消息通过 `EMChatEventHandler#onCmdMessagesReceived` 回调接收 + } + break; + } + } + }, + ), + ); } catch (e) { print('Failed to register listeners: $e'); } diff --git a/lib/main.dart b/lib/main.dart index f34f3ee..36f4a85 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ void main() async { // 设置环境配置 - 根据是否为release模式 EnvConfig.setEnvironment(Environment.dev); - IMManager.instance.initialize('1165251016193374#dongwo'); + IMManager.instance.initialize('1165251016193374#demo'); // 初始化全局依赖 final networkService = NetworkService(); Get.put(networkService); diff --git a/lib/model/common/oss_data.dart b/lib/model/common/oss_data.dart new file mode 100644 index 0000000..3688253 --- /dev/null +++ b/lib/model/common/oss_data.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +/// OSS配置数据实体类 +class OssData { + final String accessKey; + final String accessKeySecret; + final String token; + final int expiredTime; + final String regionId; + final String bucketName; + final String endpoint; + + OssData({ + required this.accessKey, + required this.accessKeySecret, + required this.token, + required this.expiredTime, + required this.regionId, + required this.bucketName, + required this.endpoint, + }); + + /// 从JSON字符串创建OssData实例 + factory OssData.fromJsonString(String jsonString) { + Map json = jsonDecode(jsonString); + return OssData.fromJson(json); + } + + /// 从JSON映射创建OssData实例 + factory OssData.fromJson(Map json) { + return OssData( + accessKey: json['accessKey'] as String, + accessKeySecret: json['accessKeySecret'] as String, + token: json['token'] as String, + expiredTime: json['expiredTime'] as int, + regionId: json['regionId'] as String, + bucketName: json['bucketName'] as String, + endpoint: json['endpoint'] as String, + ); + } + + /// 转换为JSON映射 + Map toJson() { + return { + 'accessKey': accessKey, + 'accessKeySecret': accessKeySecret, + 'token': token, + 'expiredTime': expiredTime, + 'regionId': regionId, + 'bucketName': bucketName, + 'endpoint': endpoint, + }; + } + + /// 转换为JSON字符串 + String toJsonString() { + return jsonEncode(toJson()); + } + + @override + String toString() { + return 'OssData{accessKey: $accessKey, accessKeySecret: $accessKeySecret, token: $token, expiredTime: $expiredTime, regionId: $regionId, bucketName: $bucketName, endpoint: $endpoint}'; + } +} \ No newline at end of file diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index f8187df..ccf0d1b 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -10,6 +10,7 @@ class ApiUrls { static const String getMarriageInformationDetail = 'dating-agency-service/user/get/dongwo/marriage-information-detail'; static const String registerMarriageInformation = 'dating-agency-service/user/register/marriage-information'; static const String getHxUserToken = 'dating-agency-chat-audio/user/get/hx/user/token'; + static const String getApplyTempAuth = 'dating-agency-uec/get/apply-temp-auth'; // 后续可以在此添加更多API端点 } \ No newline at end of file diff --git a/lib/network/user_api.dart b/lib/network/user_api.dart index dc1d2d3..414cf9c 100644 --- a/lib/network/user_api.dart +++ b/lib/network/user_api.dart @@ -1,3 +1,4 @@ +import 'package:dating_touchme_app/model/common/oss_data.dart'; import 'package:dating_touchme_app/model/mine/login_data.dart'; import 'package:dating_touchme_app/model/mine/user_base_data.dart'; import 'package:dating_touchme_app/model/mine/user_data.dart'; @@ -37,4 +38,7 @@ abstract class UserApi { @GET(ApiUrls.getHxUserToken) Future>> getHxUserToken(); + + @GET(ApiUrls.getApplyTempAuth) + Future>> getApplyTempAuth(); } \ No newline at end of file diff --git a/lib/network/user_api.g.dart b/lib/network/user_api.g.dart index 2081fef..495cef1 100644 --- a/lib/network/user_api.g.dart +++ b/lib/network/user_api.g.dart @@ -217,6 +217,37 @@ class _UserApi implements UserApi { return httpResponse; } + @override + Future>> getApplyTempAuth() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-uec/get/apply-temp-auth', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse _value; + try { + _value = BaseResponse.fromJson( + _result.data!, + (json) => OssData.fromJson(json as Map), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/oss/oss_manager.dart b/lib/oss/oss_manager.dart index b53f386..d13109c 100644 --- a/lib/oss/oss_manager.dart +++ b/lib/oss/oss_manager.dart @@ -1,8 +1,15 @@ import 'dart:typed_data'; +import 'package:flustars/flustars.dart'; import 'package:flutter_oss_aliyun/flutter_oss_aliyun.dart'; +import 'package:dating_touchme_app/network/network_service.dart'; +import 'package:dating_touchme_app/model/common/oss_data.dart'; +import 'package:get/get.dart'; + +import '../network/user_api.dart'; class OSSManager { + static OSSManager get instance => _instance; // 静态单例实例 static final OSSManager _instance = OSSManager._internal(); bool isInit = false; @@ -13,34 +20,59 @@ class OSSManager { // 私有构造函数,防止外部直接实例化 OSSManager._internal(); + late UserApi _userApi; - void init(String stsUrl, String ossEndpoint, String bucketName) { - // 初始化 - Client.init( - stsUrl: stsUrl, - ossEndpoint: ossEndpoint, - bucketName: bucketName, - ); - isInit = true; - } + Future uploadFile( + Uint8List fileData, + String objectName, { + Function(int count, int total)? onSendProgress, + Function(int count, int total)? onReceiveProgress, + }) async { + try { + _userApi = Get.find(); + final response = await _userApi.getApplyTempAuth(); + // 调用getApplyTempAuth获取OSS临时授权 + if (response.data.isSuccess) { + Client.init( + ossEndpoint: response.data.data?.endpoint ?? '', + bucketName: response.data.data?.bucketName ?? '', + authGetter: (){ + return Auth( + accessKey: response.data.data?.accessKey ?? '', + accessSecret: response.data.data?.accessKeySecret ?? '', + secureToken: response.data.data?.token ?? '', + expire: DateUtil.formatDateMs(response.data.data?.expiredTime ?? 0) + ); + } + ); + final result = await Client().putObject( + fileData, + objectName, + option: PutRequestOption( + onSendProgress: onSendProgress != null + ? (count, total) { + onSendProgress(count, total); + } + : null, + onReceiveProgress: onReceiveProgress != null + ? (count, total) { + onReceiveProgress(count, total); + } + : null, + override: false, + aclModel: AclMode.publicRead, + storageType: StorageType.ia, + ), + ); - Future uploadFile(Uint8List fileData, String objectName, {Function(int count, int total)? onSendProgress, - Function(int count, int total)? onReceiveProgress}) async { - if (!isInit) { - throw Exception("请先初始化"); + return result.statusCode == 200 + ? 'https://${response.data.data?.bucketName}.${response.data.data?.endpoint}/$objectName' + : throw Exception("上传失败"); + } + return throw Exception("获取临时授权失败"); + }catch(e){ + print('上传失败$e'); + return throw Exception("上传失败"); } - final result = await Client().putObject(fileData, objectName, option: PutRequestOption( - onSendProgress: onSendProgress != null ? (count, total) { - onSendProgress(count, total); - } : null, - onReceiveProgress: onReceiveProgress != null ? (count, total) { - onReceiveProgress(count, total); - } : null, - override: false, - aclModel: AclMode.publicRead, - storageType: StorageType.ia, - ), - ); - return result.statusCode == 200 ? result.realUri.path : throw Exception("上传失败"); } -} \ No newline at end of file +} diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart index d0153dd..38d14d4 100644 --- a/lib/pages/main/main_page.dart +++ b/lib/pages/main/main_page.dart @@ -21,7 +21,6 @@ class MainPage extends StatefulWidget { } class _MainPageState extends State { - late UserApi _userApi; final storage = GetStorage(); PageController pageController = PageController(initialPage: 0); int currentIndex = 0; // 使用普通int替代RxInt @@ -35,9 +34,7 @@ class _MainPageState extends State { @override void initState() { super.initState(); - // 获取UserApi实例 - _userApi = Get.find(); - + // 初始化页面实例 homePage = HomePage(); discoverPage = DiscoverPage(); diff --git a/pubspec.lock b/pubspec.lock index 65e58af..eb07778 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -145,6 +145,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" + common_utils: + dependency: transitive + description: + name: common_utils + sha256: c26884339b13ff99b0739e56f4b02090c84054ed9dd3a045435cd24e7b99c2c1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" convert: dependency: transitive description: @@ -193,6 +201,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" + decimal: + dependency: transitive + description: + name: decimal + sha256: fc706a5618b81e5b367b01dd62621def37abc096f2b46a9bd9068b64c1fa36d0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.4" dio: dependency: "direct main" description: @@ -297,6 +313,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" + flustars: + dependency: "direct main" + description: + name: flustars + sha256: "7019ab8d68c0d4759ee122644d91a165d450b0492717f9e7e9d0ce277dcf664b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" flutter: dependency: "direct main" description: flutter @@ -837,6 +861,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" + rational: + dependency: transitive + description: + name: rational + sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.3" retrofit: dependency: "direct main" description: @@ -954,6 +986,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.10.1" + sp_util: + dependency: transitive + description: + name: sp_util + sha256: "9da43dce5de79c17a787d0626bf01538d63090ca32521200d22a232171c495dc" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.3" stack_trace: dependency: transitive description: @@ -986,6 +1026,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.4.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9ca1b72..dcf6aee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: easy_localization: ^3.0.8 flutter_oss_aliyun: ^6.4.2 permission_handler: ^12.0.1 + flustars: ^2.0.1 dev_dependencies: flutter_test: