215 lines
5.9 KiB
Dart
215 lines
5.9 KiB
Dart
import 'dart:convert';
|
||
import 'dart:io';
|
||
|
||
import 'package:json_annotation/json_annotation.dart';
|
||
|
||
/// Created by Haoyi on 2021/7/1
|
||
|
||
part "manifest.g.dart";
|
||
|
||
class DetailsReservedType {
|
||
static const String igc = "igc";
|
||
static const String igb = "igb";
|
||
}
|
||
|
||
class DetailsAttr {
|
||
static const permanent = 1;
|
||
static const consumable = 2;
|
||
}
|
||
|
||
class ExtraReservedField {
|
||
static const String scene = "__scene";
|
||
static const String offerId = "__offer_id";
|
||
static const String basePlanId = "__base_plan_id";
|
||
static const String sales = "__sales";
|
||
static const String rate = "__rate";
|
||
static const String contentId = "__contentId";
|
||
static const String barterId = "__barterId";
|
||
static const String transactionTs = "__transaction_ts";
|
||
}
|
||
|
||
class DetailsReservedField {
|
||
static const String type = "__type";
|
||
static const String sku = "__sku";
|
||
static const String amount = "__amount";
|
||
static const String name = "__name";
|
||
static const String icon = "__icon";
|
||
static const String attr = "__attr";
|
||
}
|
||
|
||
class ManifestStringConvert implements JsonConverter<Manifest, String> {
|
||
const ManifestStringConvert();
|
||
|
||
@override
|
||
Manifest fromJson(String json) {
|
||
if (json.isEmpty) {
|
||
return Manifest.empty;
|
||
}
|
||
final map = jsonDecode(json);
|
||
return Manifest.fromJson(map);
|
||
}
|
||
|
||
@override
|
||
String toJson(Manifest manifest) {
|
||
return jsonEncode(manifest);
|
||
}
|
||
}
|
||
|
||
const ManifestStringConvert manifestStringConvert = ManifestStringConvert();
|
||
|
||
@JsonSerializable(constructor: "_")
|
||
class Details {
|
||
@JsonKey(name: "bundle", defaultValue: <String, dynamic>{})
|
||
final Map<String, dynamic> bundle;
|
||
|
||
const Details._({this.bundle = const <String, dynamic>{}});
|
||
|
||
Details.define(String type, int amount,
|
||
{String? sku, Map<String, dynamic> params = const <String, dynamic>{}})
|
||
: bundle = <String, dynamic>{
|
||
DetailsReservedField.sku: sku ?? type,
|
||
DetailsReservedField.type: type,
|
||
DetailsReservedField.amount: amount
|
||
};
|
||
|
||
factory Details.fromJson(Map<String, dynamic> json) => _$DetailsFromJson(json);
|
||
|
||
int get amount => bundle[DetailsReservedField.amount] ?? 0;
|
||
|
||
String get type => bundle[DetailsReservedField.type] ?? "unknown";
|
||
|
||
String get sku => bundle[DetailsReservedField.sku] ?? type;
|
||
|
||
String? get name => bundle[DetailsReservedField.name] ?? "unknown";
|
||
|
||
int get attr => bundle[DetailsReservedField.attr] ?? DetailsAttr.consumable;
|
||
|
||
Map<String, dynamic> toJson() => _$DetailsToJson(this);
|
||
|
||
void merge(Details details) {
|
||
bundle.addAll(details.bundle);
|
||
}
|
||
|
||
bool containsKey(String name) {
|
||
return bundle.containsKey(name);
|
||
}
|
||
|
||
void setInt(String key, int value) {
|
||
bundle[key] = value.toString();
|
||
}
|
||
|
||
void setDouble(String key, double value) {
|
||
bundle[key] = value.toString();
|
||
}
|
||
|
||
void setString(String key, String value) {
|
||
bundle[key] = value;
|
||
}
|
||
|
||
void setBool(String key, bool value) {
|
||
bundle[key] = value;
|
||
}
|
||
|
||
int? getInt(String key) {
|
||
final value = bundle[key];
|
||
if (value == null) {
|
||
return null;
|
||
}
|
||
return int.parse(value);
|
||
}
|
||
|
||
double? getDouble(String key) {
|
||
final value = bundle[key];
|
||
if (value == null) {
|
||
return null;
|
||
}
|
||
return double.parse(value);
|
||
}
|
||
|
||
String? getString(String key) {
|
||
return bundle[key];
|
||
}
|
||
|
||
bool? getBool(String key) {
|
||
final value = bundle[key];
|
||
if (value == null) {
|
||
return null;
|
||
}
|
||
return value == true;
|
||
}
|
||
|
||
Future<void> forEach(void Function(String key, String value) f) async {
|
||
for (var entry in bundle.entries) {
|
||
f.call(entry.key, entry.value);
|
||
}
|
||
}
|
||
}
|
||
|
||
@JsonSerializable(explicitToJson: true)
|
||
class Manifest {
|
||
@JsonKey(name: "category", defaultValue: "")
|
||
final String category;
|
||
|
||
@JsonKey(name: "extras", defaultValue: <String, dynamic>{})
|
||
final Map<String, dynamic> extras;
|
||
|
||
@JsonKey(name: "details", defaultValue: <Details>[])
|
||
final List<Details> details;
|
||
|
||
String get scene => extras[ExtraReservedField.scene] ?? "";
|
||
|
||
String? get basePlanId => extras[ExtraReservedField.basePlanId];
|
||
|
||
String? get offerId => extras[ExtraReservedField.offerId];
|
||
|
||
/// 在 TransactionMethod为IAP时,specific 对应的是 IAP 的 SKU,
|
||
/// 如一些特定含有 ID的物品,如用 IGC购买了某个道具,这里面的 contentId 就是道具的 ID
|
||
String get contentId => extras[ExtraReservedField.contentId] ?? "";
|
||
|
||
// 如果你Barter进行购买,这个字段会有值
|
||
String get barterId => extras[ExtraReservedField.barterId] ?? "";
|
||
|
||
int? get transactionTs => extras[ExtraReservedField.transactionTs];
|
||
|
||
Manifest.barter(this.category, String contentId, String barterId,
|
||
{Map<String, dynamic> extras = const <String, dynamic>{}, this.details = const <Details>[]})
|
||
: assert(contentId.isNotEmpty),
|
||
assert(barterId.isNotEmpty),
|
||
extras = {
|
||
...extras,
|
||
...{ExtraReservedField.contentId: contentId, ExtraReservedField.barterId: barterId}
|
||
};
|
||
|
||
Manifest.action(this.category, String scene,
|
||
{Map<String, dynamic> extras = const <String, dynamic>{}, this.details = const <Details>[]})
|
||
: assert(scene.isNotEmpty),
|
||
extras = {
|
||
...extras,
|
||
...{ExtraReservedField.scene: scene}
|
||
};
|
||
|
||
const Manifest(this.category,
|
||
{this.extras = const <String, dynamic>{}, this.details = const <Details>[]});
|
||
|
||
Manifest offer(String offerId, String basePlanId) {
|
||
return Manifest(category,
|
||
extras: {
|
||
...extras,
|
||
...{ExtraReservedField.offerId: offerId, ExtraReservedField.basePlanId: basePlanId}
|
||
},
|
||
details: details);
|
||
}
|
||
|
||
factory Manifest.fromJson(Map<String, dynamic> json) => _$ManifestFromJson(json);
|
||
|
||
static const Manifest empty = Manifest("empty");
|
||
|
||
Map<String, dynamic> toJson() => _$ManifestToJson(this);
|
||
}
|
||
|
||
extension DefaultManifestExtension on Manifest {
|
||
int get igcAmount => details
|
||
.map((details) => details.type == DetailsReservedType.igc ? details.amount : 0)
|
||
.reduce((value, element) => element + value);
|
||
}
|