You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
4.3 KiB
118 lines
4.3 KiB
import 'package:get/get.dart';
|
|
import '../../../data/models/goods.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
import 'goods_base_controller.dart';
|
|
|
|
class GoodsvipController extends GoodsBaseController {
|
|
/// 会员与算力状态(`user_getcompute`),页面顶部 VIP 卡片下展示。
|
|
/// 服务端已把自购与设备赋予的 VIP 合并好、算力也拆好了来源,这里**不做任何换算**。
|
|
final compute = Rxn<Map<String, dynamic>>();
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
refreshCompute();
|
|
}
|
|
|
|
Future<void> refreshCompute() async {
|
|
try {
|
|
final resp = await Api.getCompute();
|
|
if (resp is Map) compute.value = Map<String, dynamic>.from(resp);
|
|
} catch (_) {
|
|
// 展示项,拿不到就不显示
|
|
}
|
|
}
|
|
|
|
/// 「设备 N + 账户 M」形式的算力摘要;没数据返回 null。
|
|
String? get computeSummary {
|
|
final c = compute.value?['compute'];
|
|
if (c is! Map) return null;
|
|
final dev = (c['deviceleft'] as num?)?.toInt() ?? 0;
|
|
final usr = (c['userbalance'] as num?)?.toInt() ?? 0;
|
|
return 'computeSummary'.trParams({'device': '$dev', 'user': '$usr'});
|
|
}
|
|
|
|
/// 需要提示「VIP 快到期了」吗:剩余天数 ≤ 后台配的阈值(阈值为 0 = 后台关掉了这个提示)。
|
|
/// 判定用服务端下发的 vipwarndays,客户端不自己拍数字——运营改阈值不用发版。
|
|
bool get vipExpiringSoon {
|
|
final v = compute.value?['vip'];
|
|
final r = compute.value?['rates'];
|
|
if (v is! Map || r is! Map) return false;
|
|
final days = (r['vipwarndays'] as num?)?.toInt() ?? 0;
|
|
if (days <= 0) return false;
|
|
final exp = (v['exptime'] as num?)?.toInt() ?? 0;
|
|
if (exp <= 0) return false;
|
|
final left = exp - DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
|
return left > 0 && left <= days * 86400;
|
|
}
|
|
|
|
/// VIP 剩余天数(向上取整,至少 1);无 VIP 或已过期返回 0。
|
|
int get vipDaysLeft {
|
|
final v = compute.value?['vip'];
|
|
if (v is! Map) return 0;
|
|
final exp = (v['exptime'] as num?)?.toInt() ?? 0;
|
|
final left = exp - DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
|
if (left <= 0) return 0;
|
|
return (left / 86400).ceil();
|
|
}
|
|
|
|
/// 需要提示「算力不多了」吗:剩余总量 ≤ 后台配的阈值(阈值 0 = 不提示)。
|
|
bool get computeLow {
|
|
final c = compute.value?['compute'];
|
|
final r = compute.value?['rates'];
|
|
if (c is! Map || r is! Map) return false;
|
|
final warn = (r['computewarn'] as num?)?.toInt() ?? 0;
|
|
if (warn <= 0) return false;
|
|
final left = ((c['deviceleft'] as num?)?.toInt() ?? 0) + ((c['userbalance'] as num?)?.toInt() ?? 0);
|
|
return left <= warn;
|
|
}
|
|
|
|
/// 当前 VIP 是否来自设备(用于副标题标注来源)。
|
|
bool get vipFromDevice {
|
|
final v = compute.value?['vip'];
|
|
if (v is! Map) return false;
|
|
final self = (v['selfexptime'] as num?)?.toInt() ?? 0;
|
|
final exp = (v['exptime'] as num?)?.toInt() ?? 0;
|
|
final level = (v['level'] as num?)?.toInt() ?? 0;
|
|
return level > 0 && exp > self;
|
|
}
|
|
|
|
@override
|
|
Future<List<DBGoods>> fetchProducts() async {
|
|
// 2026-09 起在售的只有 VIP 时长与算力包;AI/Meet/Translation 是旧资源点包,
|
|
// 仍然拉取是为了让还挂在架上的存量商品能买(发货时会折成算力),但不再新建。
|
|
final types = [
|
|
UsageType.Vip,
|
|
UsageType.Compute,
|
|
UsageType.AI,
|
|
UsageType.Meet,
|
|
UsageType.Translation,
|
|
];
|
|
final Map<String, DBGoods> all = {};
|
|
for (final t in types) {
|
|
final resp = await Api.getGoods({'usagetype': t.toInt});
|
|
if (resp == null || resp is! Map) continue;
|
|
if (resp.containsKey('error') || resp.containsKey('msg')) {
|
|
final msg = resp['error'] ?? resp['msg'] ?? '服务器错误';
|
|
throw Exception(msg);
|
|
}
|
|
final container =
|
|
(resp is Map && resp['data'] is Map) ? resp['data'] : resp;
|
|
final data = (container is Map)
|
|
? (container['goodss'] ?? container['goodes'] ?? container['goods'])
|
|
: null;
|
|
if (data is List) {
|
|
final list = data
|
|
.whereType<Map<String, dynamic>>()
|
|
.map(DBGoods.fromJson)
|
|
.toList();
|
|
for (final g in list) {
|
|
all[g.id] = g;
|
|
}
|
|
}
|
|
}
|
|
return all.values.toList();
|
|
}
|
|
|
|
// Future<void> purchase(DBGoods item) async => super.purchase(item);
|
|
}
|
|
|