修复用户列表

This commit is contained in:
zhx 2026-05-23 15:55:18 +08:00
parent 27f4e75cb8
commit e842c41736
5 changed files with 129 additions and 60 deletions

View File

@ -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_date_utils.dart';
import 'package:yumi/shared/tools/sc_loading_manager.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_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/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/login_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
@ -363,7 +364,8 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
_isSystemConversation || _isSystemConversation ||
SCGlobalConfig.isSystemUserId(userId) || SCGlobalConfig.isSystemUserId(userId) ||
userId == null || userId == null ||
userId.isEmpty) { userId.isEmpty ||
!scLooksLikeBackendUserId(userId)) {
return; return;
} }
SCLoadingManager.show(); SCLoadingManager.show();

View File

@ -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_broadcast_assets.dart';
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.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_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/floating_screen_manager.dart';
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart'; import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
@ -566,6 +567,18 @@ class RealTimeMessagingManager extends ChangeNotifier {
Future<bool> startConversation(V2TimConversation conversation) async { Future<bool> startConversation(V2TimConversation conversation) async {
assert(conversation != null); 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; int eTime = DateTime.now().millisecondsSinceEpoch;
if (eTime - sTime > 5000) { if (eTime - sTime > 5000) {
sTime = eTime; sTime = eTime;
@ -731,6 +744,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
} }
return false; 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) { if ((message.userID?.trim() ?? "").isEmpty) {
message.userID = peerId; message.userID = peerId;
} }
@ -769,7 +791,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
(conversation.unreadCount ?? 0) + (shouldIncrementUnread ? 1 : 0); (conversation.unreadCount ?? 0) + (shouldIncrementUnread ? 1 : 0);
conversationMap[conversationId] = conversation; conversationMap[conversationId] = conversation;
final cpData = _decodeCpSystemMessageData(message);
if (cpData != null) { if (cpData != null) {
_cpInviteLog( _cpInviteLog(
'sync C2C conversation conversationId=$conversationId ' 'sync C2C conversation conversationId=$conversationId '
@ -849,15 +870,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
data: data, data: data,
); );
if (isSender) { if (isSender) {
if (userId.isNotEmpty && !currentIds.contains(userId)) { if (scLooksLikeBackendUserId(userId) && !currentIds.contains(userId)) {
return userId; return userId;
} }
return _cpInviteReceiverIdFromMessageData(data); return _cpInviteReceiverIdFromMessageData(data);
} }
if (sender.isNotEmpty && !currentIds.contains(sender)) { if (scLooksLikeBackendUserId(sender) && !currentIds.contains(sender)) {
return sender; return sender;
} }
return _cpInviteInviterIdFromMessageData(data, invite); return _cpInviteInviterIdFromMessageData(data);
} }
String _c2cConversationDisplayName( String _c2cConversationDisplayName(
@ -1614,7 +1635,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
SCSystemInvitMessageRes invite, SCSystemInvitMessageRes invite,
) { ) {
final userId = final userId =
_firstCpInviteValue([invite.actualAccount, invite.account]) ?? ""; _firstCpInviteBackendUserId([invite.actualAccount, invite.account]) ??
"";
return SocialChatUserProfile( return SocialChatUserProfile(
id: userId, id: userId,
account: account:
@ -1657,7 +1679,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
for (final profile in profiles) { for (final profile in profiles) {
for (final value in [profile?.id, profile?.account]) { for (final value in [profile?.id, profile?.account]) {
final text = value?.trim() ?? ""; final text = value?.trim() ?? "";
if (text.isNotEmpty && !result.contains(text)) { if (scLooksLikeBackendUserId(text) && !result.contains(text)) {
result.add(text); result.add(text);
} }
} }
@ -1676,12 +1698,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
); );
} }
String _cpInviteInviterIdFromMessageData( String _cpInviteInviterIdFromMessageData(Map<String, dynamic> data) {
Map<String, dynamic> data,
SCSystemInvitMessageRes invite,
) {
final content = _cpInviteContentMap(data); final content = _cpInviteContentMap(data);
return _firstCpInviteValue([ return _firstCpInviteBackendUserId([
data["userId"]?.toString(), data["userId"]?.toString(),
data["inviterUserId"]?.toString(), data["inviterUserId"]?.toString(),
data["inviterId"]?.toString(), data["inviterId"]?.toString(),
@ -1696,26 +1715,13 @@ class RealTimeMessagingManager extends ChangeNotifier {
content["senderId"]?.toString(), content["senderId"]?.toString(),
content["fromUserId"]?.toString(), content["fromUserId"]?.toString(),
content["applyUserId"]?.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<String, dynamic> data) { String _cpInviteReceiverIdFromMessageData(Map<String, dynamic> data) {
final content = _cpInviteContentMap(data); final content = _cpInviteContentMap(data);
return _firstCpInviteValue([ return _firstCpInviteBackendUserId([
data["acceptUserId"]?.toString(), data["acceptUserId"]?.toString(),
data["receiverId"]?.toString(), data["receiverId"]?.toString(),
data["receiverUserId"]?.toString(), data["receiverUserId"]?.toString(),
@ -1726,10 +1732,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
content["receiverUserId"]?.toString(), content["receiverUserId"]?.toString(),
content["targetUserId"]?.toString(), content["targetUserId"]?.toString(),
content["toUserId"]?.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; return null;
} }
String? _firstCpInviteBackendUserId(Iterable<String?> values) {
return scFirstBackendUserId(values);
}
bool _isCpRelationInviteNoticeData(Map<String, dynamic>? 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) { void _cpInviteLog(String message) {
debugPrint('[CP][Invite] $message'); debugPrint('[CP][Invite] $message');
} }

View File

@ -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<String?> values) {
for (final value in values) {
final text = value?.trim() ?? "";
if (scLooksLikeBackendUserId(text)) {
return text;
}
}
return null;
}

View File

@ -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/tools/sc_date_utils.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.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/login_res.dart';
import 'package:yumi/shared/tools/sc_user_id_utils.dart';
import 'package:yumi/services/audio/rtm_manager.dart'; import 'package:yumi/services/audio/rtm_manager.dart';
import '../../../modules/chat/chat_route.dart'; import '../../../modules/chat/chat_route.dart';
@ -340,6 +341,9 @@ class _MessageConversationListPageState
continue; continue;
} }
final userId = conversation.userID!.trim(); final userId = conversation.userID!.trim();
if (!scLooksLikeBackendUserId(userId)) {
continue;
}
if (_conversationUserCache.containsKey(userId) || if (_conversationUserCache.containsKey(userId) ||
_loadingConversationUserIds.contains(userId)) { _loadingConversationUserIds.contains(userId)) {
continue; continue;
@ -367,6 +371,7 @@ class _MessageConversationListPageState
final userId = conversation.userID?.trim() ?? ""; final userId = conversation.userID?.trim() ?? "";
return conversation.type == ConversationType.V2TIM_C2C && return conversation.type == ConversationType.V2TIM_C2C &&
userId.isNotEmpty && userId.isNotEmpty &&
scLooksLikeBackendUserId(userId) &&
!SCGlobalConfig.isSystemConversationId(conversation.conversationID) && !SCGlobalConfig.isSystemConversationId(conversation.conversationID) &&
!SCGlobalConfig.isSystemUserId(userId) && !SCGlobalConfig.isSystemUserId(userId) &&
conversation.conversationID != "customer" && conversation.conversationID != "customer" &&
@ -411,18 +416,11 @@ class _ConversationItemState extends State<ConversationItem> {
_effectiveUserId.isNotEmpty; _effectiveUserId.isNotEmpty;
String get _effectiveUserId { String get _effectiveUserId {
final cpInviteProfile = _cpInvitePeerProfileFromLastMessage(); return scFirstBackendUserId([
final cpInvitePeerId = conversation.userID,
_firstCpInviteValue([
_cpInvitePeerUserIdFromLastMessage(), _cpInvitePeerUserIdFromLastMessage(),
cpInviteProfile?.id,
cpInviteProfile?.account,
]) ?? ]) ??
""; "";
if (cpInvitePeerId.isNotEmpty) {
return cpInvitePeerId;
}
return conversation.userID?.trim() ?? "";
} }
String get _displayAvatar { String get _displayAvatar {
@ -528,6 +526,9 @@ class _ConversationItemState extends State<ConversationItem> {
}*/ }*/
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (_isC2CConversation && _effectiveUserId.isEmpty) {
return;
}
final routeConversation = _routeConversation(); final routeConversation = _routeConversation();
if (_isC2CConversation && (routeConversation.userID?.isEmpty ?? true)) { if (_isC2CConversation && (routeConversation.userID?.isEmpty ?? true)) {
return; return;
@ -768,19 +769,17 @@ class _ConversationItemState extends State<ConversationItem> {
final messageUserId = final messageUserId =
_firstCpInviteValue([message?.userID, conversation.userID]) ?? ""; _firstCpInviteValue([message?.userID, conversation.userID]) ?? "";
if (isCurrentInviter) { if (isCurrentInviter) {
return _firstCpInviteValue([ return _firstCpInviteBackendUserId([
peerProfile?.id, peerProfile?.id,
currentIds.contains(messageUserId) ? null : messageUserId, currentIds.contains(messageUserId) ? null : messageUserId,
_cpInviteReceiverIdFromData(data), _cpInviteReceiverIdFromData(data),
peerProfile?.account,
]) ?? ]) ??
""; "";
} }
return _firstCpInviteValue([ return _firstCpInviteBackendUserId([
currentIds.contains(sender) ? null : sender, currentIds.contains(sender) ? null : sender,
peerProfile?.id, peerProfile?.id,
_cpInviteInviterIdFromData(data), _cpInviteInviterIdFromData(data),
peerProfile?.account,
]) ?? ]) ??
""; "";
} }
@ -869,7 +868,7 @@ class _ConversationItemState extends State<ConversationItem> {
"profile", "profile",
]); ]);
return _cpInviteProfileOrNull( return _cpInviteProfileOrNull(
id: _firstCpInviteValue([ id: _firstCpInviteBackendUserId([
nested["id"]?.toString(), nested["id"]?.toString(),
nested["userId"]?.toString(), nested["userId"]?.toString(),
data["userId"]?.toString(), data["userId"]?.toString(),
@ -886,20 +885,21 @@ class _ConversationItemState extends State<ConversationItem> {
content["senderId"]?.toString(), content["senderId"]?.toString(),
content["fromUserId"]?.toString(), content["fromUserId"]?.toString(),
content["applyUserId"]?.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([ account: _firstCpInviteValue([
nested["account"]?.toString(), nested["account"]?.toString(),
data["account"]?.toString(), data["account"]?.toString(),
data["applyUserAccount"]?.toString(), data["applyUserAccount"]?.toString(),
content["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([ nickname: _firstCpInviteValue([
nested["userNickname"]?.toString(), nested["userNickname"]?.toString(),
@ -943,7 +943,7 @@ class _ConversationItemState extends State<ConversationItem> {
"targetUserProfile", "targetUserProfile",
]); ]);
return _cpInviteProfileOrNull( return _cpInviteProfileOrNull(
id: _firstCpInviteValue([ id: _firstCpInviteBackendUserId([
nested["id"]?.toString(), nested["id"]?.toString(),
nested["userId"]?.toString(), nested["userId"]?.toString(),
data["acceptUserId"]?.toString(), data["acceptUserId"]?.toString(),
@ -956,11 +956,6 @@ class _ConversationItemState extends State<ConversationItem> {
content["receiverUserId"]?.toString(), content["receiverUserId"]?.toString(),
content["targetUserId"]?.toString(), content["targetUserId"]?.toString(),
content["toUserId"]?.toString(), content["toUserId"]?.toString(),
nested["actualAccount"]?.toString(),
data["acceptActualAccount"]?.toString(),
data["receiverActualAccount"]?.toString(),
content["acceptActualAccount"]?.toString(),
content["receiverActualAccount"]?.toString(),
]), ]),
account: _firstCpInviteValue([ account: _firstCpInviteValue([
nested["account"]?.toString(), nested["account"]?.toString(),
@ -968,6 +963,11 @@ class _ConversationItemState extends State<ConversationItem> {
data["receiverAccount"]?.toString(), data["receiverAccount"]?.toString(),
content["acceptAccount"]?.toString(), content["acceptAccount"]?.toString(),
content["receiverAccount"]?.toString(), content["receiverAccount"]?.toString(),
nested["actualAccount"]?.toString(),
data["acceptActualAccount"]?.toString(),
data["receiverActualAccount"]?.toString(),
content["acceptActualAccount"]?.toString(),
content["receiverActualAccount"]?.toString(),
]), ]),
nickname: _firstCpInviteValue([ nickname: _firstCpInviteValue([
nested["userNickname"]?.toString(), nested["userNickname"]?.toString(),
@ -1016,12 +1016,12 @@ class _ConversationItemState extends State<ConversationItem> {
String _cpInviteInviterIdFromData(Map<String, dynamic> data) { String _cpInviteInviterIdFromData(Map<String, dynamic> data) {
final profile = _cpInviteInviterProfileFromData(data); final profile = _cpInviteInviterProfileFromData(data);
return _firstCpInviteValue([profile?.id, profile?.account]) ?? ""; return _firstCpInviteBackendUserId([profile?.id]) ?? "";
} }
String _cpInviteReceiverIdFromData(Map<String, dynamic> data) { String _cpInviteReceiverIdFromData(Map<String, dynamic> data) {
final profile = _cpInviteReceiverProfileFromData(data); final profile = _cpInviteReceiverProfileFromData(data);
return _firstCpInviteValue([profile?.id, profile?.account]) ?? ""; return _firstCpInviteBackendUserId([profile?.id]) ?? "";
} }
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) { Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
@ -1161,4 +1161,8 @@ class _ConversationItemState extends State<ConversationItem> {
} }
return null; return null;
} }
String? _firstCpInviteBackendUserId(Iterable<String?> values) {
return scFirstBackendUserId(values);
}
} }

View File

@ -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');
});
}