From e842c41736e88c0af548e51dc438b7f4af19d286 Mon Sep 17 00:00:00 2001 From: zhx Date: Sat, 23 May 2026 15:55:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8=E6=88=B7=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/chat/message_chat_page.dart | 4 +- lib/services/audio/rtm_manager.dart | 84 ++++++++++++------- lib/shared/tools/sc_user_id_utils.dart | 17 ++++ .../msg/message_conversation_list_page.dart | 64 +++++++------- test/sc_user_id_utils_test.dart | 20 +++++ 5 files changed, 129 insertions(+), 60 deletions(-) create mode 100644 lib/shared/tools/sc_user_id_utils.dart create mode 100644 test/sc_user_id_utils_test.dart diff --git a/lib/modules/chat/message_chat_page.dart b/lib/modules/chat/message_chat_page.dart index 49e5eca..2b37e0c 100644 --- a/lib/modules/chat/message_chat_page.dart +++ b/lib/modules/chat/message_chat_page.dart @@ -20,6 +20,7 @@ import 'package:yumi/app/constants/sc_emoji_datas.dart'; import 'package:yumi/shared/tools/sc_date_utils.dart'; import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/tools/sc_message_utils.dart'; +import 'package:yumi/shared/tools/sc_user_id_utils.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart'; @@ -363,7 +364,8 @@ class _SCMessageChatPageState extends State { _isSystemConversation || SCGlobalConfig.isSystemUserId(userId) || userId == null || - userId.isEmpty) { + userId.isEmpty || + !scLooksLikeBackendUserId(userId)) { return; } SCLoadingManager.show(); diff --git a/lib/services/audio/rtm_manager.dart b/lib/services/audio/rtm_manager.dart index 300dd73..83211f7 100644 --- a/lib/services/audio/rtm_manager.dart +++ b/lib/services/audio/rtm_manager.dart @@ -17,6 +17,7 @@ import 'package:yumi/shared/tools/sc_room_utils.dart'; import 'package:yumi/shared/tools/sc_cp_relation_broadcast_assets.dart'; import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart'; import 'package:yumi/shared/tools/sc_room_rocket_reward_popup_ack.dart'; +import 'package:yumi/shared/tools/sc_user_id_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; @@ -566,6 +567,18 @@ class RealTimeMessagingManager extends ChangeNotifier { Future startConversation(V2TimConversation conversation) async { assert(conversation != null); + final userId = conversation.userID?.trim() ?? ""; + if (conversation.type == ConversationType.V2TIM_C2C && + !_isSystemConversation(conversation) && + conversation.conversationID != "customer" && + conversation.conversationID != "article" && + !scLooksLikeBackendUserId(userId)) { + _cpInviteLog( + 'skip startConversation reason=invalid_user_id ' + 'userId=$userId conversationID=${conversation.conversationID}', + ); + return false; + } int eTime = DateTime.now().millisecondsSinceEpoch; if (eTime - sTime > 5000) { sTime = eTime; @@ -731,6 +744,15 @@ class RealTimeMessagingManager extends ChangeNotifier { } return false; } + final cpData = _decodeCpSystemMessageData(message); + if (_isCpRelationInviteNoticeData(cpData) && + !scLooksLikeBackendUserId(peerId)) { + _cpInviteLog( + 'skip C2C conversation sync reason=invalid_peer ' + 'peerId=$peerId msgId=${message.msgID} sender=${message.sender}', + ); + return false; + } if ((message.userID?.trim() ?? "").isEmpty) { message.userID = peerId; } @@ -769,7 +791,6 @@ class RealTimeMessagingManager extends ChangeNotifier { (conversation.unreadCount ?? 0) + (shouldIncrementUnread ? 1 : 0); conversationMap[conversationId] = conversation; - final cpData = _decodeCpSystemMessageData(message); if (cpData != null) { _cpInviteLog( 'sync C2C conversation conversationId=$conversationId ' @@ -849,15 +870,15 @@ class RealTimeMessagingManager extends ChangeNotifier { data: data, ); if (isSender) { - if (userId.isNotEmpty && !currentIds.contains(userId)) { + if (scLooksLikeBackendUserId(userId) && !currentIds.contains(userId)) { return userId; } return _cpInviteReceiverIdFromMessageData(data); } - if (sender.isNotEmpty && !currentIds.contains(sender)) { + if (scLooksLikeBackendUserId(sender) && !currentIds.contains(sender)) { return sender; } - return _cpInviteInviterIdFromMessageData(data, invite); + return _cpInviteInviterIdFromMessageData(data); } String _c2cConversationDisplayName( @@ -1614,7 +1635,8 @@ class RealTimeMessagingManager extends ChangeNotifier { SCSystemInvitMessageRes invite, ) { final userId = - _firstCpInviteValue([invite.actualAccount, invite.account]) ?? ""; + _firstCpInviteBackendUserId([invite.actualAccount, invite.account]) ?? + ""; return SocialChatUserProfile( id: userId, account: @@ -1657,7 +1679,7 @@ class RealTimeMessagingManager extends ChangeNotifier { for (final profile in profiles) { for (final value in [profile?.id, profile?.account]) { final text = value?.trim() ?? ""; - if (text.isNotEmpty && !result.contains(text)) { + if (scLooksLikeBackendUserId(text) && !result.contains(text)) { result.add(text); } } @@ -1676,12 +1698,9 @@ class RealTimeMessagingManager extends ChangeNotifier { ); } - String _cpInviteInviterIdFromMessageData( - Map data, - SCSystemInvitMessageRes invite, - ) { + String _cpInviteInviterIdFromMessageData(Map data) { final content = _cpInviteContentMap(data); - return _firstCpInviteValue([ + return _firstCpInviteBackendUserId([ data["userId"]?.toString(), data["inviterUserId"]?.toString(), data["inviterId"]?.toString(), @@ -1696,26 +1715,13 @@ class RealTimeMessagingManager extends ChangeNotifier { content["senderId"]?.toString(), content["fromUserId"]?.toString(), content["applyUserId"]?.toString(), - data["actualAccount"]?.toString(), - data["applyActualAccount"]?.toString(), - data["applyUserActualAccount"]?.toString(), - data["inviterActualAccount"]?.toString(), - data["account"]?.toString(), - content["applyActualAccount"]?.toString(), - content["applyUserActualAccount"]?.toString(), - content["inviterActualAccount"]?.toString(), - content["applyUserAccount"]?.toString(), - invite.actualAccount, - invite.account, - content["actualAccount"]?.toString(), - content["account"]?.toString(), ]) ?? ""; } String _cpInviteReceiverIdFromMessageData(Map data) { final content = _cpInviteContentMap(data); - return _firstCpInviteValue([ + return _firstCpInviteBackendUserId([ data["acceptUserId"]?.toString(), data["receiverId"]?.toString(), data["receiverUserId"]?.toString(), @@ -1726,10 +1732,6 @@ class RealTimeMessagingManager extends ChangeNotifier { content["receiverUserId"]?.toString(), content["targetUserId"]?.toString(), content["toUserId"]?.toString(), - data["acceptActualAccount"]?.toString(), - data["receiverActualAccount"]?.toString(), - content["acceptActualAccount"]?.toString(), - content["receiverActualAccount"]?.toString(), ]) ?? ""; } @@ -2019,6 +2021,30 @@ class RealTimeMessagingManager extends ChangeNotifier { return null; } + String? _firstCpInviteBackendUserId(Iterable values) { + return scFirstBackendUserId(values); + } + + bool _isCpRelationInviteNoticeData(Map? data) { + if (data == null) { + return false; + } + final content = _cpInviteContentMap(data); + final noticeType = + _firstCpInviteValue([ + data["noticeType"]?.toString(), + data["type"]?.toString(), + content["noticeType"]?.toString(), + content["type"]?.toString(), + ])?.toUpperCase() ?? + ""; + if (noticeType == SCSysytemMessageType.CP_BUILD.name) { + return true; + } + return _cpInviteApplyIdFromData(data).isNotEmpty && + _cpInviteResultFromMessageData(data) != null; + } + void _cpInviteLog(String message) { debugPrint('[CP][Invite] $message'); } diff --git a/lib/shared/tools/sc_user_id_utils.dart b/lib/shared/tools/sc_user_id_utils.dart new file mode 100644 index 0000000..87652d3 --- /dev/null +++ b/lib/shared/tools/sc_user_id_utils.dart @@ -0,0 +1,17 @@ +bool scLooksLikeBackendUserId(String? value) { + final text = value?.trim() ?? ""; + if (text.isEmpty) { + return false; + } + return RegExp(r'^\d{15,}$').hasMatch(text); +} + +String? scFirstBackendUserId(Iterable values) { + for (final value in values) { + final text = value?.trim() ?? ""; + if (scLooksLikeBackendUserId(text)) { + return text; + } + } + return null; +} diff --git a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart index 2c72d1c..52470d2 100644 --- a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart +++ b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart @@ -22,6 +22,7 @@ import 'package:yumi/shared/data_sources/models/enum/sc_sysytem_message_type.dar import 'package:yumi/shared/tools/sc_date_utils.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; +import 'package:yumi/shared/tools/sc_user_id_utils.dart'; import 'package:yumi/services/audio/rtm_manager.dart'; import '../../../modules/chat/chat_route.dart'; @@ -340,6 +341,9 @@ class _MessageConversationListPageState continue; } final userId = conversation.userID!.trim(); + if (!scLooksLikeBackendUserId(userId)) { + continue; + } if (_conversationUserCache.containsKey(userId) || _loadingConversationUserIds.contains(userId)) { continue; @@ -367,6 +371,7 @@ class _MessageConversationListPageState final userId = conversation.userID?.trim() ?? ""; return conversation.type == ConversationType.V2TIM_C2C && userId.isNotEmpty && + scLooksLikeBackendUserId(userId) && !SCGlobalConfig.isSystemConversationId(conversation.conversationID) && !SCGlobalConfig.isSystemUserId(userId) && conversation.conversationID != "customer" && @@ -411,18 +416,11 @@ class _ConversationItemState extends State { _effectiveUserId.isNotEmpty; String get _effectiveUserId { - final cpInviteProfile = _cpInvitePeerProfileFromLastMessage(); - final cpInvitePeerId = - _firstCpInviteValue([ + return scFirstBackendUserId([ + conversation.userID, _cpInvitePeerUserIdFromLastMessage(), - cpInviteProfile?.id, - cpInviteProfile?.account, ]) ?? ""; - if (cpInvitePeerId.isNotEmpty) { - return cpInvitePeerId; - } - return conversation.userID?.trim() ?? ""; } String get _displayAvatar { @@ -528,6 +526,9 @@ class _ConversationItemState extends State { }*/ return GestureDetector( onTap: () async { + if (_isC2CConversation && _effectiveUserId.isEmpty) { + return; + } final routeConversation = _routeConversation(); if (_isC2CConversation && (routeConversation.userID?.isEmpty ?? true)) { return; @@ -768,19 +769,17 @@ class _ConversationItemState extends State { final messageUserId = _firstCpInviteValue([message?.userID, conversation.userID]) ?? ""; if (isCurrentInviter) { - return _firstCpInviteValue([ + return _firstCpInviteBackendUserId([ peerProfile?.id, currentIds.contains(messageUserId) ? null : messageUserId, _cpInviteReceiverIdFromData(data), - peerProfile?.account, ]) ?? ""; } - return _firstCpInviteValue([ + return _firstCpInviteBackendUserId([ currentIds.contains(sender) ? null : sender, peerProfile?.id, _cpInviteInviterIdFromData(data), - peerProfile?.account, ]) ?? ""; } @@ -869,7 +868,7 @@ class _ConversationItemState extends State { "profile", ]); return _cpInviteProfileOrNull( - id: _firstCpInviteValue([ + id: _firstCpInviteBackendUserId([ nested["id"]?.toString(), nested["userId"]?.toString(), data["userId"]?.toString(), @@ -886,20 +885,21 @@ class _ConversationItemState extends State { content["senderId"]?.toString(), content["fromUserId"]?.toString(), content["applyUserId"]?.toString(), - nested["actualAccount"]?.toString(), - data["actualAccount"]?.toString(), - data["applyActualAccount"]?.toString(), - data["applyUserActualAccount"]?.toString(), - data["inviterActualAccount"]?.toString(), - content["applyActualAccount"]?.toString(), - content["applyUserActualAccount"]?.toString(), - content["inviterActualAccount"]?.toString(), ]), account: _firstCpInviteValue([ nested["account"]?.toString(), data["account"]?.toString(), data["applyUserAccount"]?.toString(), content["applyUserAccount"]?.toString(), + nested["actualAccount"]?.toString(), + data["actualAccount"]?.toString(), + data["applyActualAccount"]?.toString(), + data["applyUserActualAccount"]?.toString(), + data["inviterActualAccount"]?.toString(), + content["actualAccount"]?.toString(), + content["applyActualAccount"]?.toString(), + content["applyUserActualAccount"]?.toString(), + content["inviterActualAccount"]?.toString(), ]), nickname: _firstCpInviteValue([ nested["userNickname"]?.toString(), @@ -943,7 +943,7 @@ class _ConversationItemState extends State { "targetUserProfile", ]); return _cpInviteProfileOrNull( - id: _firstCpInviteValue([ + id: _firstCpInviteBackendUserId([ nested["id"]?.toString(), nested["userId"]?.toString(), data["acceptUserId"]?.toString(), @@ -956,11 +956,6 @@ class _ConversationItemState extends State { content["receiverUserId"]?.toString(), content["targetUserId"]?.toString(), content["toUserId"]?.toString(), - nested["actualAccount"]?.toString(), - data["acceptActualAccount"]?.toString(), - data["receiverActualAccount"]?.toString(), - content["acceptActualAccount"]?.toString(), - content["receiverActualAccount"]?.toString(), ]), account: _firstCpInviteValue([ nested["account"]?.toString(), @@ -968,6 +963,11 @@ class _ConversationItemState extends State { data["receiverAccount"]?.toString(), content["acceptAccount"]?.toString(), content["receiverAccount"]?.toString(), + nested["actualAccount"]?.toString(), + data["acceptActualAccount"]?.toString(), + data["receiverActualAccount"]?.toString(), + content["acceptActualAccount"]?.toString(), + content["receiverActualAccount"]?.toString(), ]), nickname: _firstCpInviteValue([ nested["userNickname"]?.toString(), @@ -1016,12 +1016,12 @@ class _ConversationItemState extends State { String _cpInviteInviterIdFromData(Map data) { final profile = _cpInviteInviterProfileFromData(data); - return _firstCpInviteValue([profile?.id, profile?.account]) ?? ""; + return _firstCpInviteBackendUserId([profile?.id]) ?? ""; } String _cpInviteReceiverIdFromData(Map data) { final profile = _cpInviteReceiverProfileFromData(data); - return _firstCpInviteValue([profile?.id, profile?.account]) ?? ""; + return _firstCpInviteBackendUserId([profile?.id]) ?? ""; } Set _cpInviteInviterIdentityValues(Map data) { @@ -1161,4 +1161,8 @@ class _ConversationItemState extends State { } return null; } + + String? _firstCpInviteBackendUserId(Iterable values) { + return scFirstBackendUserId(values); + } } diff --git a/test/sc_user_id_utils_test.dart b/test/sc_user_id_utils_test.dart new file mode 100644 index 0000000..1373809 --- /dev/null +++ b/test/sc_user_id_utils_test.dart @@ -0,0 +1,20 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:yumi/shared/tools/sc_user_id_utils.dart'; + +void main() { + test('backend user id detector rejects display accounts', () { + expect(scLooksLikeBackendUserId('323456'), isFalse); + expect(scLooksLikeBackendUserId('223456'), isFalse); + expect(scLooksLikeBackendUserId('4569421795454615555'), isTrue); + }); + + test('first backend user id skips CP invite display accounts', () { + final userId = scFirstBackendUserId([ + '323456', + '223456', + '4569421795454615555', + ]); + + expect(userId, '4569421795454615555'); + }); +}