import 'dart:convert'; import 'dart:typed_data'; import 'package:common_utils/common_utils.dart'; import 'package:get/get.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; abstract class AppSettingTool { static const String _tag = "app配置工具"; static const String _settingKey = "setting"; static const String _terminalMenuOrderKey = "terminalMenuOrder"; static const String _hiddenTerminalMenusKey = "hiddenTerminalMenus"; static const String _payTypeOrderKey = "payTypeOrder"; // 自定义缓存管理器 static final CacheManager _settingCache = CacheManager( Config( 'setting_cache', stalePeriod: const Duration(days: 7), maxNrOfCacheObjects: 10, ), ); static Future removeSettingCache() async { await _settingCache.emptyCache(); } static Future _getSetting() async { // 尝试从缓存获取 final fileInfo = await _settingCache.getFileFromCache(_settingKey); if (fileInfo != null) { final content = await fileInfo.file.readAsString(); if (ObjectUtil.isNotEmpty(content)) { return SettingEntity.fromJson(json.decode(content)); } } final app = Get.find(); final settingDao = app.dataBase.getSettingDao; final setting = await settingDao.get(); if (setting == null) { final initSysInfo = SysSetting( sysType: SysType.CANTEEN, androidId: await AppSysTool.getAndroidId(), sqlAddress: "", ); final initUserInfo = UserSetting( username: "", password: "", ); SettingEntity addObj = SettingEntity( userJson: json.encode(initUserInfo.toJson()), sysJson: json.encode(initSysInfo.toJson()), ); final addId = await settingDao.save(addObj); if (ObjectUtil.isEmpty(addId)) { throw Exception("|$_tag|_getSetting新增错误"); } addObj.id = addId; return addObj; } return setting; } static Future _setSetting({ SysSetting? sysJson, UserSetting? userJson, CanteenExtraPriceSetting? canteenExtraPriceConfig, }) async { if (ObjectUtil.isEmpty(sysJson) && ObjectUtil.isEmpty(userJson) && ObjectUtil.isEmpty(canteenExtraPriceConfig)) { return false; } final app = Get.find(); final settingDao = app.dataBase.getSettingDao; SettingEntity? setting = await settingDao.get(); if (setting == null) { logger.e("|$_tag|_setSetting新增错误settingDao.get()方法返回为null"); return false; } if (sysJson != null) { final current = Map.from(json.decode(setting.sysJson)); // sysJson 允许保存不涉及数据库结构的终端 UI 扩展配置,更新业务设置时必须保留这些键。 setting.sysJson = json.encode({...current, ...sysJson.toJson()}); } if (userJson != null) { setting.userJson = json.encode(userJson.toJson()); } if (canteenExtraPriceConfig != null) { setting.canteenExtraPriceConfigJson = json.encode( canteenExtraPriceConfig.toJson(), ); } await settingDao.edit(setting); await _settingCache.putFile( _settingKey, Uint8List.fromList(utf8.encode(json.encode(setting.toJson()))), fileExtension: 'json', ); return true; } static Future getSysSetting() async { final setting = await _getSetting(); return SysSetting.fromJson(json.decode(setting.sysJson)); } static Future getUserSetting() async { final setting = await _getSetting(); return UserSetting.fromJson(json.decode(setting.userJson)); } static Future getCanteenExtraPriceConfig() async { final setting = await _getSetting(); if (ObjectUtil.isEmpty(setting.canteenExtraPriceConfigJson)) { return null; } return CanteenExtraPriceSetting.fromJson( json.decode(setting.canteenExtraPriceConfigJson!), ); } static Future setSysSetting(SysSetting sysInfo) async { await _setSetting(sysJson: sysInfo); } static Future setUserSetting(UserSetting userInfo) async { final globalUserInfo = await getUserSetting(); if (globalUserInfo == userInfo) return; if (ObjectUtil.isEmpty(userInfo.tenantId) && ObjectUtil.isNotEmpty(globalUserInfo.tenantId)) { userInfo.tenantId = globalUserInfo.tenantId; } await _setSetting(userJson: userInfo); } static Future setCanteenExtraPriceConfig( CanteenExtraPriceSetting canteenExtraPriceConfig, ) async { final config = await getCanteenExtraPriceConfig(); if (config == canteenExtraPriceConfig) return; await _setSetting(canteenExtraPriceConfig: canteenExtraPriceConfig); } /// 读取左侧菜单的顺序和隐藏状态,未知或缺失项由控制器按默认菜单补齐。 static Future<({List order, Set hidden})> getTerminalMenuPreferences() async { final setting = await _getSetting(); final sysJson = Map.from(json.decode(setting.sysJson)); final order = (sysJson[_terminalMenuOrderKey] as List?) ?.whereType() .toList() ?? const []; final hidden = (sysJson[_hiddenTerminalMenusKey] as List?) ?.whereType() .toSet() ?? const {}; return (order: order, hidden: hidden); } /// 菜单布局属于设备级 UI 设置,直接复用 setting 表中的 sysJson,避免增加数据库字段和迁移。 static Future setTerminalMenuPreferences({ required List order, required Set hidden, }) async { final app = Get.find(); final settingDao = app.dataBase.getSettingDao; final setting = await settingDao.get(); if (setting == null) { throw StateError('终端设置尚未初始化'); } final sysJson = Map.from(json.decode(setting.sysJson)); sysJson[_terminalMenuOrderKey] = order; sysJson[_hiddenTerminalMenusKey] = hidden.toList(); setting.sysJson = json.encode(sysJson); await settingDao.edit(setting); await _settingCache.putFile( _settingKey, Uint8List.fromList(utf8.encode(json.encode(setting.toJson()))), fileExtension: 'json', ); } /// 读取支付方式的完整顺序;显隐仍由 SysSetting.usePayDisplayType 维护。 static Future> getPayTypeOrder() async { final setting = await _getSetting(); final sysJson = Map.from(json.decode(setting.sysJson)); return (sysJson[_payTypeOrderKey] as List?) ?.whereType() .toList() ?? const []; } /// 支付排序属于终端 UI 扩展配置,与现有支付显隐和默认方式一起原子保存。 static Future setPayTypePreferences({ required SysSetting sysInfo, required List order, }) async { // 在首次异步等待前生成快照,避免连续拖动时后一次修改污染本次落盘内容。 final nextSysJson = sysInfo.toJson(); final visiblePayTypes = nextSysJson['usePayDisplayType']; final defaultPayType = nextSysJson['defaultPayDisplayType']; final orderCodes = order.map((item) => item.code).toList(); final app = Get.find(); final settingDao = app.dataBase.getSettingDao; final setting = await settingDao.get(); if (setting == null) { throw StateError('终端设置尚未初始化'); } final current = Map.from(json.decode(setting.sysJson)); current['usePayDisplayType'] = visiblePayTypes; current['defaultPayDisplayType'] = defaultPayType; current[_payTypeOrderKey] = orderCodes; setting.sysJson = json.encode(current); await settingDao.edit(setting); await _settingCache.putFile( _settingKey, Uint8List.fromList(utf8.encode(json.encode(setting.toJson()))), fileExtension: 'json', ); } // 根据后台返回的数据更新到缓存 static Future updateSysInfoByService(ServiceHardware data) async { if (ObjectUtil.isEmpty(data)) { final setting = SysSetting( sysType: SysType.CANTEEN, androidId: await AppSysTool.getAndroidId(), sqlAddress: await AppFileTool.getDatabaseBackupPath(), ); await _setSetting(sysJson: setting); return; } final sysSetting = await AppSettingTool.getSysSetting(); final id = data.id; if (ObjectUtil.isNotEmpty(id)) { sysSetting.serviceId = id; } final name = data.name; if (ObjectUtil.isNotEmpty(name)) { sysSetting.serviceName = name; } final type = data.type.toString(); if (ObjectUtil.isNotEmpty(type)) { final t = SysType.values.fromCode(type); if (ObjectUtil.isNotEmpty(t)) { sysSetting.sysType = t!; } } final merchantId = data.merchantId; if (ObjectUtil.isNotEmpty(merchantId)) { sysSetting.serviceMerchantId = merchantId; } final hardGroupId = data.hardGroupId; if (ObjectUtil.isNotEmpty(hardGroupId)) { sysSetting.hardGroupId = hardGroupId; } final merchantDeptId = data.merchantDeptId; if (ObjectUtil.isNotEmpty(merchantDeptId)) { sysSetting.merchantDeptId = merchantDeptId; } final mqClientId = data.mqClientId; if (ObjectUtil.isNotEmpty(mqClientId)) { sysSetting.mqClientId = mqClientId; } final mainTopic = data.mainTopic; if (ObjectUtil.isNotEmpty(mainTopic)) { sysSetting.mainTopic = mainTopic; } final mqSocketAddress = data.mqSocketAddress; if (ObjectUtil.isNotEmpty(mqSocketAddress)) { sysSetting.mqSocketAddress = mqSocketAddress; } final mqSocketPort = data.mqSocketPort; if (ObjectUtil.isNotEmpty(mqSocketPort)) { sysSetting.mqSocketPort = mqSocketPort; } final mqUsername = data.mqUsername; if (ObjectUtil.isNotEmpty(mqUsername)) { sysSetting.mqUsername = mqUsername; } final mqPassword = data.mqPassword; if (ObjectUtil.isNotEmpty(mqPassword)) { sysSetting.mqPassword = mqPassword; } final faceAppId = data.faceAppId; if (ObjectUtil.isNotEmpty(faceAppId)) { sysSetting.faceAppId = faceAppId; } final faceSdkKey = data.faceSdkKey; if (ObjectUtil.isNotEmpty(faceSdkKey)) { sysSetting.faceSdkKey = faceSdkKey; } final faceActiveKey = data.faceActiveKey; if (ObjectUtil.isNotEmpty(faceActiveKey)) { sysSetting.faceActiveKey = faceActiveKey; } final android = data.uniqueCode; if (ObjectUtil.isNotEmpty(android)) { sysSetting.androidId = android; } sysSetting.syncTime = DateTime.now(); await _setSetting(sysJson: sysSetting); } /// 绑定设备信息 static Future bindSys(int serviceId) async { var androidId = await AppSysTool.getAndroidId(); if (ObjectUtil.isEmpty(androidId)) return false; final pa = {"id": serviceId, "uniqueCode": androidId}; try { final data = await HardApi.bind(pa); if (data == null) { logger.e("绑定设备信息pa:|${jsonEncode(pa)}|;res:$data;"); return false; } await AppSettingTool.updateSysInfoByService(data); return true; } catch (e, stack) { logger.e("|$_tag|绑定信息错误error:|$e|stack:$stack"); } return false; } }