import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../auth/session_store.dart'; import 'models.dart'; import 'order_api.dart'; /// 授权管理:本地缓存用于主界面展示到期时间;识别入口强制服务端校验。 /// 缓存按手机号隔离(键含账号),切换账号不会读到上一账号的到期时间。 class LicenseService { static const _storage = FlutterSecureStorage(); final OrderApi orderApi; final SessionStore sessionStore; LicenseService({required this.orderApi, required this.sessionStore}); /// 主界面展示/启动加载:先读本账号本地缓存,有效则直接用; /// 无效或缺失时询问服务端(网络失败按缓存兜底,未登录按过期处理)。 Future check() async { final phone = await sessionStore.readPhone(); if (phone == null) return const LicenseStatus(active: false); final cached = await _readCache(phone); final now = DateTime.now(); if (cached != null && cached.expiresAt!.isAfter(now)) return cached; if (await sessionStore.readToken() == null) { return const LicenseStatus(active: false); } try { final remote = await orderApi.fetchLicense(); try { if (remote.active && remote.expiresAt != null) { await _writeCache(phone, remote.expiresAt!); } else if (!remote.active) { await _clearCache(phone); } } catch (_) { // 本地缓存不可用不影响授权状态展示 } return remote; } on OrderApiException { return cached ?? const LicenseStatus(active: false); } } /// 识别入口强制校验:必须走服务端且 active 才放行,失败即抛错(不进相机) Future verifyServer() async { if (await sessionStore.readToken() == null) { throw const SessionExpiredException(); } return orderApi.fetchLicense(); } /// 支付成功后立即刷新(清缓存强制走服务端,避免旧授权干扰) Future refresh() async { final phone = await sessionStore.readPhone(); if (phone != null) await _clearCache(phone); return orderApi.fetchLicense(); } Future _readCache(String phone) async { try { final raw = await _storage.read(key: _cacheKey(phone)); final t = raw == null ? null : DateTime.tryParse(raw); if (t == null || !t.isAfter(DateTime.now())) return null; return LicenseStatus(active: true, expiresAt: t); } catch (_) { return null; } } Future _writeCache(String phone, DateTime expiresAt) => _storage.write(key: _cacheKey(phone), value: expiresAt.toIso8601String()); Future _clearCache(String phone) => _storage.delete(key: _cacheKey(phone)); static String _cacheKey(String phone) => 'license_expires_at:$phone'; }