import 'package:json_annotation/json_annotation.dart'; import 'package:get/get.dart'; part 'appconfig_model.g.dart'; // 生成的文件名 /// ⚠️ 每个非空字段都带 `defaultValue`:服务端少下发一个键时用默认值, /// **而不是**整个 fromJson 抛 TypeError 让产品表/设备列表整体空白 /// (见 CLAUDE.md「客户端模型是手写镜像」那一段踩过的坑)。 /// 契约测试在 test/appconfig_model_test.dart。 @JsonSerializable() class UserGetAppConfigResp { @JsonKey(defaultValue: {}) final Map env; @JsonKey(defaultValue: []) final List agents; @JsonKey(defaultValue: {}) final Map mcps; @JsonKey(defaultValue: []) final List products; UserGetAppConfigResp( {required this.env, required this.agents, required this.mcps, required this.products}); factory UserGetAppConfigResp.fromJson(Map json) => _$UserGetAppConfigRespFromJson(json); Map toJson() => _$UserGetAppConfigRespToJson(this); } @JsonSerializable() class DBAgent { @JsonKey(defaultValue: '') final String id; @JsonKey(defaultValue: '') final String name; @JsonKey(defaultValue: '') final String avatarUrl; @JsonKey(defaultValue: '') final String description; @JsonKey(defaultValue: '') final String tag; @JsonKey(defaultValue: false) final bool isOnline; @JsonKey(defaultValue: '') final String voice; final String? welcomeMessage; final String? voiceWelcomeMessage; final String? systemPrompt; final String? tools; DBAgent({ required this.id, required this.name, required this.avatarUrl, required this.description, required this.tag, required this.isOnline, required this.voice, required this.welcomeMessage, required this.voiceWelcomeMessage, required this.systemPrompt, required this.tools, }); factory DBAgent.fromJson(Map json) => _$DBAgentFromJson(json); Map toJson() => _$DBAgentToJson(this); } @JsonSerializable() class DBMCPServer { @JsonKey(defaultValue: '') final String servername; @JsonKey(defaultValue: '') final String url; @JsonKey(defaultValue: 0) final int type; @JsonKey(defaultValue: '') final String tools; DBMCPServer({ required this.servername, required this.url, required this.type, required this.tools, }); factory DBMCPServer.fromJson(Map json) => _$DBMCPServerFromJson(json); Map toJson() => _$DBMCPServerToJson(this); } @JsonSerializable() class DBProduct { @JsonKey(defaultValue: 0) final int id; @JsonKey(defaultValue: 0) final int factoryid; /// 服务端已废弃(恒为 0),只为老包兼容保留 @JsonKey(defaultValue: 0) final int devicetype; @JsonKey(defaultValue: '') final String devicename; final String? productimage; // ⚠️ scanuuid 已于 2026-09-08 删除:设备校验早就改成按 MAC 走,绑定奖励也是按 MAC 判 // 「这台设备有没有被绑过」,跟这个 UUID 没关系。服务端仍会下发这个键(恒为空串, // 只为老 App 包不炸),新端直接不解析。 final String? version; bool? isforceupdate; /// 版本不一致即可升级(后台「产品 → 版本管理」里的开关,默认关)。 /// /// 开发/测试环境用:测试机固件常常比后台挂的还新,按「只允许升到更高版本」 /// 会永远点不动升级按钮,也没法刷回旧固件反复验证 OTA。 /// ⚠️ 可空且默认按 false 处理——老服务端不下发这个字段时必须退回严格判定, /// 绝不能反过来(那会让线上用户被降级)。 final bool? updateonmismatch; final bool? iscontinuouschat; final bool? wakeupenable; //该产品是否拥有唤醒开关控制 final bool? broadcastpeertranslate; //通话翻译是否本地播报对方翻译内容 final String? updatedescription; final String? updatepackageaddress; DBProduct({ required this.id, required this.factoryid, required this.devicetype, required this.devicename, required this.productimage, required this.version, required this.isforceupdate, //该产品固件版本是否强制更新 this.updateonmismatch, //可选:老服务端不下发这个字段,缺省即 null→按 false required this.iscontinuouschat, required this.wakeupenable, required this.broadcastpeertranslate, required this.updatedescription, required this.updatepackageaddress, }); factory DBProduct.fromJson(Map json) => _$DBProductFromJson(json); Map toJson() => _$DBProductToJson(this); } //设备类型 enum DeviceType { /// 未知设备 /// @Description 未知设备 /// @example unknown(0), //未知设备 /// 经典蓝牙耳机 /// @Description 经典蓝牙耳机 /// @example classicBluetoothHeadset(1), //经典蓝牙耳机 /// Ble蓝牙耳机 双端 /// @Description Ble蓝牙耳机 /// @example bleBluetoothDoubleHeadset(2), //Ble蓝牙耳机 双端 /// Ble蓝牙耳机 单端 /// @Description Ble蓝牙耳机 /// @example bleBluetoothSingleHeadset(3), //Ble蓝牙耳机 单端 /// 智能录音笔 /// @Description 智能录音笔 /// @example smartVoiceRecorder(4), //智能录音笔 /// 智能眼镜 /// @Description 智能眼镜 /// @example smartEye(5); //智能眼镜 /// 构造函数 const DeviceType(this.value); /// 设备类型对应的整数值 final int value; } // 为 DeviceType 添加扩展方法 extension DeviceTypeExtension on DeviceType { /// 获取设备类型的显示名称 /// @Description 根据设备类型返回对应的翻译键 String get displayName { switch (this) { case DeviceType.unknown: return "deviceTypeUnknown".tr; // 未知设备 case DeviceType.classicBluetoothHeadset: return "deviceTypeClassicBluetoothHeadset".tr; // 经典蓝牙耳机 case DeviceType.bleBluetoothDoubleHeadset: case DeviceType.bleBluetoothSingleHeadset: return "deviceTypeBleBluetoothHeadset".tr; // BLE蓝牙耳机(单端和双端都使用同一个翻译键) case DeviceType.smartVoiceRecorder: return "deviceTypeSmartVoiceRecorder".tr; // 智能录音笔 case DeviceType.smartEye: return "deviceTypeSmartEye".tr; // 智能眼镜 } } }