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.
 
 
 
 
 
 

205 lines
6.4 KiB

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: <String, String>{})
final Map<String, String> env;
@JsonKey(defaultValue: <DBAgent>[])
final List<DBAgent> agents;
@JsonKey(defaultValue: <String, DBMCPServer>{})
final Map<String, DBMCPServer> mcps;
@JsonKey(defaultValue: <DBProduct>[])
final List<DBProduct> products;
UserGetAppConfigResp(
{required this.env,
required this.agents,
required this.mcps,
required this.products});
factory UserGetAppConfigResp.fromJson(Map<String, dynamic> json) =>
_$UserGetAppConfigRespFromJson(json);
Map<String, dynamic> 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<String, dynamic> json) =>
_$DBAgentFromJson(json);
Map<String, dynamic> 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<String, dynamic> json) =>
_$DBMCPServerFromJson(json);
Map<String, dynamic> 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<String, dynamic> json) =>
_$DBProductFromJson(json);
Map<String, dynamic> 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; // 智能眼镜
}
}
}