修复用户列表

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_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<SCMessageChatPage> {
_isSystemConversation ||
SCGlobalConfig.isSystemUserId(userId) ||
userId == null ||
userId.isEmpty) {
userId.isEmpty ||
!scLooksLikeBackendUserId(userId)) {
return;
}
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_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<bool> 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<String, dynamic> data,
SCSystemInvitMessageRes invite,
) {
String _cpInviteInviterIdFromMessageData(Map<String, dynamic> 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<String, dynamic> 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<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) {
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/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<ConversationItem> {
_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<ConversationItem> {
}*/
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<ConversationItem> {
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<ConversationItem> {
"profile",
]);
return _cpInviteProfileOrNull(
id: _firstCpInviteValue([
id: _firstCpInviteBackendUserId([
nested["id"]?.toString(),
nested["userId"]?.toString(),
data["userId"]?.toString(),
@ -886,20 +885,21 @@ class _ConversationItemState extends State<ConversationItem> {
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<ConversationItem> {
"targetUserProfile",
]);
return _cpInviteProfileOrNull(
id: _firstCpInviteValue([
id: _firstCpInviteBackendUserId([
nested["id"]?.toString(),
nested["userId"]?.toString(),
data["acceptUserId"]?.toString(),
@ -956,11 +956,6 @@ class _ConversationItemState extends State<ConversationItem> {
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<ConversationItem> {
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<ConversationItem> {
String _cpInviteInviterIdFromData(Map<String, dynamic> data) {
final profile = _cpInviteInviterProfileFromData(data);
return _firstCpInviteValue([profile?.id, profile?.account]) ?? "";
return _firstCpInviteBackendUserId([profile?.id]) ?? "";
}
String _cpInviteReceiverIdFromData(Map<String, dynamic> data) {
final profile = _cpInviteReceiverProfileFromData(data);
return _firstCpInviteValue([profile?.id, profile?.account]) ?? "";
return _firstCpInviteBackendUserId([profile?.id]) ?? "";
}
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
@ -1161,4 +1161,8 @@ class _ConversationItemState extends State<ConversationItem> {
}
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');
});
}