/// 设备标识(P3 计划 Task 6,A19 设备来源语义)。 /// /// 每安装一次生成一个随机 32 位十六进制串(16 字节 `Random.secure`), /// 持久化后跨进程重启稳定;随 `initUpload` 上报为可选 sourceDeviceId, /// 服务端历史(GET /uploads)据此区分上传来源设备。 /// /// 非机密(不可当作设备指纹做安全决策,服务端仅作展示归属),明文存 /// shared_preferences 足够——不占用 secure storage。卸载重装视为新设备 /// (per-install 语义,符合「客户端首次生成并持久化」的契约描述)。 library; import 'dart:math'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// 存储抽象(测试注入内存实现;生产=shared_preferences)。 abstract class DeviceIdStore { Future read(); Future write(String value); } /// shared_preferences 生产实现(单键)。 class SharedPrefsDeviceIdStore implements DeviceIdStore { const SharedPrefsDeviceIdStore(); static const String kKey = 'device.id'; @override Future read() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString(kKey); } @override Future write(String value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString(kKey, value); } } /// 设备标识:懒初始化 + 进程内缓存(并发调用共享首次生成结果)。 class DeviceId { DeviceId({DeviceIdStore? store, Random? random}) : _store = store ?? const SharedPrefsDeviceIdStore(), _random = random ?? Random.secure(); final DeviceIdStore _store; final Random _random; Future? _pending; /// 合法取值形状:32 位小写十六进制。 static final RegExp kPattern = RegExp(r'^[0-9a-f]{32}$'); /// 当前设备标识(首次调用生成并持久化;后续读缓存)。 Future value() => _pending ??= _loadOrCreate(); Future _loadOrCreate() async { String? stored; try { stored = await _store.read(); } on Object { // 存储读取失败按未生成处理:重新生成并尽力写回。 } if (stored != null && kPattern.hasMatch(stored)) { return stored; } final generated = _generate(); try { await _store.write(generated); } on Object { // 写回失败不阻断:本次进程内仍可用(下次启动重新生成—— // sourceDeviceId 可选,服务端容忍 null/变更)。 } return generated; } /// 16 随机字节 → 32 位小写十六进制。 String _generate() { final bytes = List.generate(16, (_) => _random.nextInt(256)); return hexEncode(bytes); } /// 字节 → 小写十六进制(独立函数,单测直接覆盖)。 static String hexEncode(List bytes) => bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); } /// 设备标识 Provider(全局单例语义:整个 App 共享同一 per-install 值)。 final deviceIdProvider = Provider((ref) => DeviceId());