fix(wallet): 丢弃切换账号后返回的旧余额响应

This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-30 17:14:40 +08:00
1 parent 19196ceb8e
commit 501bc66282
2 files changed
+98 -5

No files matched your search

+14 -5
View File
@@ -21,23 +21,29 @@ class WalletController extends ChangeNotifier {
String? unlockError;
String? unlockingEpisodeId;
bool _disposed = false;
int _requestSeq = 0;
int get balance => snapshot?.wallet.coinBalance ?? 0;
List<CoinLedgerEntry> get ledger => snapshot?.ledger ?? const [];
Future<void> load() async {
if (_disposed) return;
final request = ++_requestSeq;
loading = true;
error = null;
_notify();
try {
snapshot = await _repository.fetchWallet();
final result = await _repository.fetchWallet();
if (_isCurrent(request)) snapshot = result;
} on ApiException catch (exception) {
error = exception.message;
if (_isCurrent(request)) error = exception.message;
} catch (_) {
error = 'walletLoadFailed';
if (_isCurrent(request)) error = 'walletLoadFailed';
} finally {
loading = false;
_notify();
if (_isCurrent(request)) {
loading = false;
_notify();
}
}
}
@@ -75,6 +81,7 @@ class WalletController extends ChangeNotifier {
}
void reset() {
_requestSeq += 1;
snapshot = null;
loading = false;
unlocking = false;
@@ -88,6 +95,8 @@ class WalletController extends ChangeNotifier {
if (!_disposed) notifyListeners();
}
bool _isCurrent(int request) => !_disposed && request == _requestSeq;
@override
void dispose() {
_disposed = true;
+84
View File
@@ -0,0 +1,84 @@
// Author: fruit.com.cn
// 验证登出、切换请求及销毁后不会回写旧钱包。
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/wallet.dart';
import 'package:fruitshorts/core/data/repositories/commerce_repository.dart';
import 'package:fruitshorts/core/data/services/wallet_controller.dart';
const _oldBalance = 320;
const _newBalance = 50;
void main() {
test('should_discard_inflight_balance_when_identity_is_reset', () async {
final repository = _PendingCommerce();
final wallet = WalletController(repository);
final pending = wallet.load();
wallet.reset();
repository.requests.single.complete(_snapshot(_oldBalance));
await pending;
expect(wallet.snapshot, isNull);
expect(wallet.loading, isFalse);
wallet.dispose();
});
test('should_keep_new_balance_when_an_older_request_finishes_last', () async {
final repository = _PendingCommerce();
final wallet = WalletController(repository);
final oldRequest = wallet.load();
wallet.reset();
final newRequest = wallet.load();
repository.requests.last.complete(_snapshot(_newBalance));
await newRequest;
repository.requests.first.complete(_snapshot(_oldBalance));
await oldRequest;
expect(wallet.balance, _newBalance);
expect(wallet.loading, isFalse);
wallet.dispose();
});
test('should_discard_inflight_error_when_wallet_is_reset', () async {
final repository = _PendingCommerce();
final wallet = WalletController(repository);
final pending = wallet.load();
wallet.reset();
repository.requests.single.completeError(StateError('old request'));
await pending;
expect(wallet.error, isNull);
expect(wallet.snapshot, isNull);
wallet.dispose();
});
}
WalletSnapshot _snapshot(int balance) => WalletSnapshot(
wallet: CoinWallet(
coinBalance: balance,
totalRechargedCoin: balance,
totalConsumedCoin: 0,
totalRefundedCoin: 0,
),
);
class _PendingCommerce implements CommerceRepository {
final requests = <Completer<WalletSnapshot>>[];
@override
Future<WalletSnapshot> fetchWallet() {
final request = Completer<WalletSnapshot>();
requests.add(request);
return request.future;
}
@override
Future<UnlockResult> unlock(String episodeId) async {
throw UnimplementedError('not needed by wallet identity tests');
}
}