144 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'package:guru_app/financial/asset/assets_model.dart';
 | ||
| import 'package:guru_app/financial/asset/assets_store.dart';
 | ||
| import 'package:guru_app/financial/financial_manager.dart';
 | ||
| import 'package:guru_app/financial/iap/iap_manager.dart';
 | ||
| import 'package:guru_app/financial/iap/iap_model.dart';
 | ||
| import 'package:guru_app/financial/igb/igb_manager.dart';
 | ||
| import 'package:guru_app/financial/igb/igb_product.dart';
 | ||
| import 'package:guru_app/financial/igc/igc_manager.dart';
 | ||
| import 'package:guru_app/financial/igc/igc_model.dart';
 | ||
| import 'package:guru_app/financial/product/product_store.dart';
 | ||
| import 'package:guru_app/financial/reward/reward_manager.dart';
 | ||
| import 'package:guru_app/financial/reward/reward_model.dart';
 | ||
| import 'package:guru_app/guru_app.dart';
 | ||
| import 'package:guru_app/inventory/db/inventory_database.dart';
 | ||
| import 'package:guru_app/inventory/inventory_manager.dart';
 | ||
| import 'package:guru_app/test/test_guru_app_creator.dart';
 | ||
| import 'package:guru_utils/collection/collectionutils.dart';
 | ||
| import 'package:guru_utils/datetime/datetime_utils.dart';
 | ||
| import 'package:guru_utils/extensions/extensions.dart';
 | ||
| import 'package:guru_utils/controller/controller.dart';
 | ||
| 
 | ||
| /// Created by Haoyi on 2022/4/7
 | ||
| 
 | ||
| mixin AssetsAware on LifecycleController {
 | ||
|   final BehaviorSubject<ProductStore<IapProduct>> _productStoreSubject =
 | ||
|       BehaviorSubject.seeded(ProductStore());
 | ||
| 
 | ||
|   ProductStore<IapProduct> get currentProductStore => _productStoreSubject.value;
 | ||
| 
 | ||
|   AssetsStore<Asset> get currentIapAssetStore => IapManager.instance.purchasedStore;
 | ||
| 
 | ||
|   AssetsStore<Asset> get currentRewardedStore => RewardManager.instance.rewardedStore;
 | ||
| 
 | ||
|   Stream<ProductStore<IapProduct>> get observableProductStore => _productStoreSubject.stream;
 | ||
| 
 | ||
|   Stream<AssetsStore<Asset>> get observableIapPurchased => IapManager.instance.observableAssetStore;
 | ||
| 
 | ||
|   Stream<AssetsStore<Asset>> get observableRewarded => RewardManager.instance.observableAssetStore;
 | ||
| 
 | ||
|   Stream<AssetsStore<Asset>> get observableAssets => FinancialManager.instance.observableAssets;
 | ||
| 
 | ||
|   int _latestRefreshIapProductTimestamp = 0;
 | ||
| 
 | ||
|   bool get isIapCanceled => IapManager.instance.latestIapCause == IapCause.canceled;
 | ||
| 
 | ||
|   bool get isIapError => IapManager.instance.latestIapCause == IapCause.error;
 | ||
| 
 | ||
|   int get currentIgcBalance => IgcManager.instance.currentBalance;
 | ||
| 
 | ||
|   Stream<int> get observableIgcBalance => IgcManager.instance.observableCurrentBalance;
 | ||
| 
 | ||
|   Stream<Map<String, InventoryItem>> get observableInventoryItems =>
 | ||
|       InventoryManager.instance.observableData;
 | ||
| 
 | ||
|   Future restorePurchases() async {
 | ||
|     return await IapManager.instance.restorePurchases();
 | ||
|   }
 | ||
| 
 | ||
|   Future clearIapAssets() async {
 | ||
|     return await IapManager.instance.clearAssetRecord();
 | ||
|   }
 | ||
| 
 | ||
|   void observeIapProducts(Set<TransactionIntent> intents) {
 | ||
|     addSubscription(IapManager.instance.observableProductDetails.listen((details) async {
 | ||
|       final productStore = await IapManager.instance.buildProducts(intents);
 | ||
|       _productStoreSubject.addEx(productStore);
 | ||
|     }));
 | ||
|   }
 | ||
| 
 | ||
|   void refreshIapProducts() async {
 | ||
|     final now = DateTimeUtils.currentTimeInMillis();
 | ||
|     if (now - _latestRefreshIapProductTimestamp > DateTimeUtils.minuteInMillis) {
 | ||
|       IapManager.instance.refreshProducts();
 | ||
|       _latestRefreshIapProductTimestamp = now;
 | ||
|     } else {
 | ||
|       Log.w("refreshIapProducts Too Frequency!", tag: "IAP");
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   Future<RewardProduct> buildRewardProduct(TransactionIntent intent) {
 | ||
|     return RewardManager.instance.buildRewardProduct(intent);
 | ||
|   }
 | ||
| 
 | ||
|   Future<IgbProduct> buildIgbProduct(TransactionIntent intent) {
 | ||
|     return IgbManager.instance.buildIgbProduct(intent);
 | ||
|   }
 | ||
| 
 | ||
|   int getInventoryBalance(String sku) {
 | ||
|     return InventoryManager.instance.getData(sku)?.balance ?? 0;
 | ||
|   }
 | ||
| 
 | ||
|   TimeSensitiveData getInventoryTimeSensitiveData(String sku) {
 | ||
|     return InventoryManager.instance.getData(sku)?.timeSensitive ?? const TimeSensitiveData();
 | ||
|   }
 | ||
| 
 | ||
|   /// 使用指定[sku]的道具,[amount]为使用数量,[action]的行为,[scene]为使用场景
 | ||
|   /// useProp最终会得到一个行为上的收益,因此这里为了方便针对道具使用进行统一的行为分析
 | ||
|   /// 这里的 [action]和[scene]最终会通过 spend_virtual_currency 事件进行统一的统计
 | ||
|   /// propCategory可以参照 [PropCategory] 中的定义
 | ||
|   /// 具体参数对照如下:
 | ||
|   ///   - **`item_name`**: [intent] 如果这里指定了使用道具的意图,那么这里就会使用这个意图,否则使用场景,
 | ||
|   ///   - **`item_category`**: [category],
 | ||
|   ///   - **`virtual_currency_name`**: [propSku],
 | ||
|   ///   - **`value`**: [amount],
 | ||
|   ///   - **`balance`**: balance,
 | ||
|   ///   - **`scene`**: [scene],
 | ||
|   ///   - **`level_name`**: levelName
 | ||
|   ///
 | ||
|   ///  返回值为是否成功使用道具,false表示道具不足,true表示使用成功
 | ||
|   ///
 | ||
|   Future<bool> useProp(
 | ||
|     String propSku,
 | ||
|     String scene, {
 | ||
|     int amount = 1,
 | ||
|     String? intent,
 | ||
|     String category = PropCategory.boosts,
 | ||
|     bool timeSensitiveOnly = false,
 | ||
|     int? transactionTs,
 | ||
|   }) async {
 | ||
|     final manifest = Manifest.action(category, scene,
 | ||
|         extras: CollectionUtils.filterOutNulls({
 | ||
|           ExtraReservedField.contentId: intent ?? scene, // 如果这里指定了使用道具的意图,那么这里就会使用这个意图,否则使用场景
 | ||
|           ExtraReservedField.transactionTs: transactionTs // 如果这里指定了交易时间,那么就会使用这个时间,否则使用当前时间
 | ||
|         }));
 | ||
|     return await InventoryManager.instance.consume(
 | ||
|         [StockItem.consumable(propSku, amount)], manifest,
 | ||
|         timeSensitiveOnly: timeSensitiveOnly);
 | ||
|   }
 | ||
| 
 | ||
|   Future<bool> requestProduct(Product product, {String from = ""}) async {
 | ||
|     if (product is IapProduct) {
 | ||
|       return await IapManager.instance.buy(product);
 | ||
|     } else if (product is IgcProduct) {
 | ||
|       return await IgcManager.instance.purchase(product);
 | ||
|     } else if (product is RewardProduct) {
 | ||
|       return await RewardManager.instance.claim(product);
 | ||
|     } else if (product is IgbProduct) {
 | ||
|       return await IgbManager.instance.redeem(product);
 | ||
|     } else {
 | ||
|       return false;
 | ||
|     }
 | ||
|   }
 | ||
| }
 |