修改bug

This commit is contained in:
zhx 2026-05-23 20:43:29 +08:00
parent f025c18211
commit b045b3b784
3 changed files with 221 additions and 47 deletions

View File

@ -1275,11 +1275,15 @@ class _MessageItem extends StatelessWidget {
),
),
),
if (cpStatusLineText != null) _buildCpStatusLine(cpStatusLineText),
if (cpRelationFormedCard != null)
cpRelationFormedCard
else if (cpStatusLineText != null)
_buildCpStatusLine(cpStatusLineText)
else
Padding(
padding: EdgeInsets.only(
top: cpStatusLineText == null ? 0 : 10.w,
),
child: cpRelationFormedCard,
)
else if (cpStatusLineText == null)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@ -1647,12 +1651,14 @@ class _MessageItem extends StatelessWidget {
return null;
}
final state = _cpInviteStateFromData(data);
if (state == _CpInviteMessageState.pending ||
state == _CpInviteMessageState.accepted) {
if (state == _CpInviteMessageState.pending) {
return null;
}
final localizations = SCAppLocalizations.of(context)!;
final relationType = _cpRelationTypeFromMessageData(data);
if (state == _CpInviteMessageState.accepted) {
return _cpAcceptedStatusLineText(relationType);
}
if (state == _CpInviteMessageState.expired) {
if (relationType != "CP") {
return "The ${scCpRelationPossessiveLabel(relationType)} invitation timed out.";
@ -1670,6 +1676,19 @@ class _MessageItem extends StatelessWidget {
: "You declined the other party's $relationLabel invitation.";
}
String _cpAcceptedStatusLineText(String relationType) {
final localizations = SCAppLocalizations.of(context)!;
switch (scNormalizeCpRelationType(relationType)) {
case "BROTHER":
return "Have become sworn brothers";
case "SISTERS":
return "Have become sisters";
case "CP":
default:
return localizations.closeFriendsHaveBecome;
}
}
void _showCpInviteDialogFromMessage(
Map<String, dynamic> data,
SCSystemInvitMessageRes? cpInvite,

View File

@ -370,11 +370,15 @@ class _MessageItem extends StatelessWidget {
),
),
),
if (cpStatusLineText != null) _buildCpStatusLine(cpStatusLineText),
if (cpRelationFormedCard != null)
cpRelationFormedCard
else if (cpStatusLineText != null)
_buildCpStatusLine(cpStatusLineText)
else
Padding(
padding: EdgeInsets.only(
top: cpStatusLineText == null ? 0 : 10.w,
),
child: cpRelationFormedCard,
)
else if (cpStatusLineText == null)
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
@ -834,10 +838,6 @@ class _MessageItem extends StatelessWidget {
if (data == null) {
return null;
}
final noticeType = data["noticeType"]?.toString() ?? "";
if (noticeType == SCSysytemMessageType.CP_BUILD.name) {
return null;
}
final state = _cpInviteStateFromData(data);
if (state == _CpInviteMessageState.pending || !_isCpInvitePayload(data)) {
return null;
@ -846,10 +846,10 @@ class _MessageItem extends StatelessWidget {
if (state == _CpInviteMessageState.expired) {
return localizations.closeFriendInviteTimedOut;
}
final inviterView = _isCurrentUserCpInviter(data);
if (state == _CpInviteMessageState.accepted) {
return null;
return _cpAcceptedStatusLineText(_cpRelationTypeFromMessageData(data));
}
final inviterView = _isCurrentUserCpInviter(data);
return inviterView
? localizations.otherDeclinedCloseFriendInvite
: localizations.youDeclinedCloseFriendInvite;
@ -1147,6 +1147,20 @@ class _MessageItem extends StatelessWidget {
], fallback: "");
}
String _cpRelationTypeFromMessageData(Map<String, dynamic> data) {
final invite = _cpInviteFromMessageData(data);
final content = _cpContentMap(data);
return scNormalizeCpRelationType(
_firstNonBlankString([
invite?.relationType,
data["relationType"]?.toString(),
data["relation_type"]?.toString(),
content["relationType"]?.toString(),
content["relation_type"]?.toString(),
], fallback: "CP"),
);
}
Map<String, dynamic> _cpContentMap(Map<String, dynamic> data) {
final content = data["content"];
try {
@ -1202,6 +1216,19 @@ class _MessageItem extends StatelessWidget {
}
}
String _cpAcceptedStatusLineText(String relationType) {
final localizations = SCAppLocalizations.of(context)!;
switch (scNormalizeCpRelationType(relationType)) {
case "BROTHER":
return "Have become sworn brothers";
case "SISTERS":
return "Have become sisters";
case "CP":
default:
return localizations.closeFriendsHaveBecome;
}
}
String _cpDisplayName({
String? nickname,
String? account,

View File

@ -33,14 +33,25 @@ import 'package:yumi/shared/business_logic/models/res/sc_sign_in_reward_res.dart
import 'package:yumi/shared/business_logic/models/res/sc_sign_in_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_violation_handle_res.dart';
import 'package:yumi/shared/business_logic/repositories/user_repository.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart'
show BaseNetworkClient;
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
import 'package:yumi/shared/tools/sc_mobile_login_context.dart';
import 'package:yumi/shared/tools/sc_room_profile_cache.dart';
class _CpRequestCacheEntry<T> {
const _CpRequestCacheEntry(this.value, this.createdAt);
final T value;
final DateTime createdAt;
bool isFresh(Duration ttl) => DateTime.now().difference(createdAt) < ttl;
}
class SCAccountRepository implements SocialChatUserRepository {
static SCAccountRepository? _instance;
static const Duration _cpRelationRequestCacheTtl = Duration(seconds: 2);
SCAccountRepository._internal();
@ -48,6 +59,12 @@ class SCAccountRepository implements SocialChatUserRepository {
return _instance ??= SCAccountRepository._internal();
}
final Map<String, _CpRequestCacheEntry<List<CPRes>>> _cpPairsCache = {};
final Map<String, Future<List<CPRes>>> _cpPairsInFlight = {};
final Map<String, _CpRequestCacheEntry<SCCpCabinRes>> _cpCabinCache = {};
final Map<String, Future<SCCpCabinRes>> _cpCabinInFlight = {};
int _cpRelationCacheEpoch = 0;
void _cpDebugLog(String message) {
if (kDebugMode) {
debugPrint('[CP][API] $message');
@ -78,6 +95,52 @@ class SCAccountRepository implements SocialChatUserRepository {
'levels=${config.levels.map((level) => "{level=${level.level}, name=${level.levelName}, required=${level.requiredIntimacyValue}, rights=${level.rights.length}, unlocked=${level.unlocked}}").toList()}}';
}
String _currentCpCacheSessionKey() {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? '';
if (currentUserId.isNotEmpty) {
return currentUserId;
}
final token = AccountStorage().getToken().trim();
return token.isEmpty ? 'anonymous' : token;
}
String _normalizedCpRelationType(String relationType) {
final normalized = relationType.trim().toUpperCase();
return normalized.isEmpty ? 'CP' : normalized;
}
String _cpPairsCacheKey(String relationType) {
return '${_currentCpCacheSessionKey()}|${_normalizedCpRelationType(relationType)}';
}
String _cpCabinCacheKey(String userId, String relationType) {
return '${_currentCpCacheSessionKey()}|${userId.trim()}|${_normalizedCpRelationType(relationType)}';
}
T? _freshCpCacheValue<T>(
Map<String, _CpRequestCacheEntry<T>> cache,
String key,
) {
final entry = cache[key];
if (entry == null) {
return null;
}
if (entry.isFresh(_cpRelationRequestCacheTtl)) {
return entry.value;
}
cache.remove(key);
return null;
}
void _clearCpRelationRequestCache() {
_cpRelationCacheEpoch++;
_cpPairsCache.clear();
_cpPairsInFlight.clear();
_cpCabinCache.clear();
_cpCabinInFlight.clear();
}
@override
Future<SocialChatLoginRes> getUser(String id) async {
final result = await http.get(
@ -1272,23 +1335,55 @@ class SCAccountRepository implements SocialChatUserRepository {
///user/cp-relationship/pair
@override
Future<List<CPRes>> cpRelationshipPairs({String relationType = 'CP'}) async {
_cpDebugLog('pair request relationType=$relationType');
try {
final result = await http.get<List<CPRes>>(
"/user/cp-relationship/pair",
queryParams: {"relationType": relationType},
fromJson:
(json) => (json as List).map((e) => CPRes.fromJson(e)).toList(),
);
_cpDebugLog(
'pair response relationType=$relationType count=${result.length} '
'pairs=${result.map(_cpPairSummary).toList()}',
);
return result;
} catch (error) {
_cpDebugLog('pair failed relationType=$relationType error=$error');
rethrow;
final normalizedRelationType = _normalizedCpRelationType(relationType);
final cacheKey = _cpPairsCacheKey(normalizedRelationType);
final cached = _freshCpCacheValue(_cpPairsCache, cacheKey);
if (cached != null) {
_cpDebugLog('pair cache hit relationType=$normalizedRelationType');
return cached;
}
final inFlight = _cpPairsInFlight[cacheKey];
if (inFlight != null) {
_cpDebugLog('pair join in-flight relationType=$normalizedRelationType');
return inFlight;
}
_cpDebugLog('pair request relationType=$normalizedRelationType');
final requestEpoch = _cpRelationCacheEpoch;
late final Future<List<CPRes>> trackedRequest;
trackedRequest = http
.get<List<CPRes>>(
"/user/cp-relationship/pair",
queryParams: {"relationType": normalizedRelationType},
fromJson:
(json) => (json as List).map((e) => CPRes.fromJson(e)).toList(),
)
.then((result) {
if (requestEpoch == _cpRelationCacheEpoch) {
_cpPairsCache[cacheKey] = _CpRequestCacheEntry(
result,
DateTime.now(),
);
}
_cpDebugLog(
'pair response relationType=$normalizedRelationType count=${result.length} '
'pairs=${result.map(_cpPairSummary).toList()}',
);
return result;
})
.catchError((error) {
_cpDebugLog(
'pair failed relationType=$normalizedRelationType error=$error',
);
throw error;
})
.whenComplete(() {
if (_cpPairsInFlight[cacheKey] == trackedRequest) {
_cpPairsInFlight.remove(cacheKey);
}
});
_cpPairsInFlight[cacheKey] = trackedRequest;
return trackedRequest;
}
///user/cp-relationship/send-apply
@ -1308,6 +1403,7 @@ class SCAccountRepository implements SocialChatUserRepository {
allowNullBody: true,
fromJson: (json) => json,
);
_clearCpRelationRequestCache();
_cpDebugLog('sendApply success data=$parm');
return true;
} catch (error) {
@ -1330,6 +1426,7 @@ class SCAccountRepository implements SocialChatUserRepository {
allowNullBody: true,
fromJson: (json) => json,
);
_clearCpRelationRequestCache();
_cpDebugLog('processApply success data=$parm');
return true;
} catch (error) {
@ -1355,6 +1452,7 @@ class SCAccountRepository implements SocialChatUserRepository {
allowNullBody: true,
fromJson: (json) => json,
);
_clearCpRelationRequestCache();
_cpDebugLog('dismissApply success query=$parm');
return true;
} catch (error) {
@ -1413,22 +1511,52 @@ class SCAccountRepository implements SocialChatUserRepository {
String userId, {
String relationType = 'CP',
}) async {
final query = {"userId": userId, "relationType": relationType};
_cpDebugLog('cabin request query=$query');
try {
final result = await http.get<SCCpCabinRes>(
"/user/cp/cabin",
queryParams: query,
fromJson: (json) => SCCpCabinRes.fromJson(json),
);
_cpDebugLog(
'cabin response query=$query result=${_cpCabinSummary(result)}',
);
return result;
} catch (error) {
_cpDebugLog('cabin failed query=$query error=$error');
rethrow;
final normalizedRelationType = _normalizedCpRelationType(relationType);
final query = {"userId": userId, "relationType": normalizedRelationType};
final cacheKey = _cpCabinCacheKey(userId, normalizedRelationType);
final cached = _freshCpCacheValue(_cpCabinCache, cacheKey);
if (cached != null) {
_cpDebugLog('cabin cache hit query=$query');
return cached;
}
final inFlight = _cpCabinInFlight[cacheKey];
if (inFlight != null) {
_cpDebugLog('cabin join in-flight query=$query');
return inFlight;
}
_cpDebugLog('cabin request query=$query');
final requestEpoch = _cpRelationCacheEpoch;
late final Future<SCCpCabinRes> trackedRequest;
trackedRequest = http
.get<SCCpCabinRes>(
"/user/cp/cabin",
queryParams: query,
fromJson: (json) => SCCpCabinRes.fromJson(json),
)
.then((result) {
if (requestEpoch == _cpRelationCacheEpoch) {
_cpCabinCache[cacheKey] = _CpRequestCacheEntry(
result,
DateTime.now(),
);
}
_cpDebugLog(
'cabin response query=$query result=${_cpCabinSummary(result)}',
);
return result;
})
.catchError((error) {
_cpDebugLog('cabin failed query=$query error=$error');
throw error;
})
.whenComplete(() {
if (_cpCabinInFlight[cacheKey] == trackedRequest) {
_cpCabinInFlight.remove(cacheKey);
}
});
_cpCabinInFlight[cacheKey] = trackedRequest;
return trackedRequest;
}
///user/cp/cabin/value