fix:参考金值,研听,研值工程通用通用工程公共能力,重点对比网络请求、登录认证、状态管理、本地存储、WebView、推送、版本更新、一键登录、主题/UI 等模块,确定后续统一整改方案
This commit is contained in:
1 parent
6997a65c96
commit
d364c6d908
46 files changed
+2060
-395
No files matched your search
@@ -51,6 +51,45 @@ class GoldAssetHolding {
|
||||
final HoldingStatus status;
|
||||
|
||||
double materialValue(double spotPrice) => weightGram * purity * spotPrice;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'metal': metal.name,
|
||||
'category': category.name,
|
||||
'purity': purity,
|
||||
'purityLabel': purityLabel,
|
||||
'weightGram': weightGram,
|
||||
'costAmount': costAmount,
|
||||
'purchaseDate': purchaseDate?.toIso8601String(),
|
||||
'channel': channel,
|
||||
'note': note,
|
||||
'createdAt': createdAt.toIso8601String(),
|
||||
'updatedAt': updatedAt.toIso8601String(),
|
||||
'status': status.name,
|
||||
};
|
||||
|
||||
factory GoldAssetHolding.fromJson(Map<String, Object?> json) {
|
||||
return GoldAssetHolding(
|
||||
id: json['id']! as String,
|
||||
name: json['name']! as String,
|
||||
metal: MetalType.values.byName(json['metal']! as String),
|
||||
category: AssetCategory.values.byName(json['category']! as String),
|
||||
purity: (json['purity']! as num).toDouble(),
|
||||
purityLabel: json['purityLabel']! as String,
|
||||
weightGram: (json['weightGram']! as num).toDouble(),
|
||||
costAmount: (json['costAmount'] as num?)?.toDouble(),
|
||||
purchaseDate: switch (json['purchaseDate']) {
|
||||
final String value when value.isNotEmpty => DateTime.parse(value),
|
||||
_ => null,
|
||||
},
|
||||
channel: json['channel'] as String?,
|
||||
note: json['note'] as String?,
|
||||
createdAt: DateTime.parse(json['createdAt']! as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt']! as String),
|
||||
status: HoldingStatus.values.byName(json['status']! as String),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HoldingValuation {
|
||||
|
||||
@@ -1,14 +1,91 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../../common/domain/metal_type.dart';
|
||||
import '../../../common/storage/preferences_storage.dart';
|
||||
import '../../../common/storage/storage_keys.dart';
|
||||
import 'asset_models.dart';
|
||||
|
||||
final mockAssetRepositoryProvider = Provider<MockAssetRepository>(
|
||||
(ref) => MockAssetRepository(),
|
||||
(ref) => MockAssetRepository(ref.watch(preferencesStorageProvider)),
|
||||
);
|
||||
|
||||
class MockAssetRepository {
|
||||
List<GoldAssetHolding> loadHoldings() {
|
||||
MockAssetRepository(this._preferences);
|
||||
|
||||
static const guestAccountScope = 'guest';
|
||||
|
||||
final PreferencesStorage _preferences;
|
||||
Future<void> _operationTail = Future<void>.value();
|
||||
|
||||
static String accountScope({String? userId, String? phone}) {
|
||||
final normalizedUserId = userId?.trim() ?? '';
|
||||
if (normalizedUserId.isNotEmpty) return 'id:$normalizedUserId';
|
||||
final normalizedPhone = phone?.trim() ?? '';
|
||||
if (normalizedPhone.isNotEmpty) return 'phone:$normalizedPhone';
|
||||
return guestAccountScope;
|
||||
}
|
||||
|
||||
static String holdingsStorageKey(String accountScope) =>
|
||||
'$storageJinzhiHoldings:${Uri.encodeComponent(accountScope)}';
|
||||
|
||||
static String deletedStorageKey(String accountScope) =>
|
||||
'$storageJinzhiHoldingsDeleted:${Uri.encodeComponent(accountScope)}';
|
||||
|
||||
List<GoldAssetHolding> loadHoldings(String accountScope) {
|
||||
if (_preferences.getBool(deletedStorageKey(accountScope))) return [];
|
||||
final encoded = _preferences.getString(holdingsStorageKey(accountScope));
|
||||
if (encoded == null || encoded.isEmpty) return _seedHoldings();
|
||||
try {
|
||||
final decoded = jsonDecode(encoded);
|
||||
if (decoded is! List) return _seedHoldings();
|
||||
return [
|
||||
for (final item in decoded)
|
||||
GoldAssetHolding.fromJson(Map<String, Object?>.from(item as Map)),
|
||||
];
|
||||
} catch (_) {
|
||||
return _seedHoldings();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveHoldings(
|
||||
String accountScope,
|
||||
List<GoldAssetHolding> holdings,
|
||||
) async {
|
||||
final encoded = jsonEncode([
|
||||
for (final holding in holdings) holding.toJson(),
|
||||
]);
|
||||
await _serialize(() async {
|
||||
await _preferences.setString(holdingsStorageKey(accountScope), encoded);
|
||||
await _preferences.remove(deletedStorageKey(accountScope));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> deleteAccountHoldings(String accountScope) async {
|
||||
await _serialize(() async {
|
||||
final scopes = {accountScope, guestAccountScope};
|
||||
await Future.wait([
|
||||
for (final scope in scopes) ...[
|
||||
_preferences.remove(holdingsStorageKey(scope)),
|
||||
_preferences.setBool(deletedStorageKey(scope), true),
|
||||
],
|
||||
// Remove the pre-account-scoping key left by earlier builds.
|
||||
_preferences.remove(storageJinzhiHoldings),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<T> _serialize<T>(Future<T> Function() operation) {
|
||||
final result = _operationTail.then((_) => operation());
|
||||
_operationTail = result.then<void>(
|
||||
(_) {},
|
||||
onError: (Object _, StackTrace _) {},
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
List<GoldAssetHolding> _seedHoldings() {
|
||||
final now = DateTime(2026, 6, 18, 16, 11);
|
||||
return [
|
||||
GoldAssetHolding(
|
||||
|
||||
Reference in new issue
Block a user