// node.dart — 加速节点数据模型 import '../l10n/app_text.dart'; /// 节点标签(决定副标题语义胶囊)。 enum NodeTag { none, streaming, p2p } /// 单个加速节点。名称按语言单显;副标题脱敏(无红线词)。 class Node { const Node({ required this.code, required this.nameZh, required this.nameEn, required this.ping, this.uuid = '', this.tier = 'free', this.tag = NodeTag.none, this.host = '', this.port = 0, this.status = 'up', }); /// 服务端 UUID,用于 POST /v1/nodes/{uuid}/connect。 final String uuid; /// 2 字母国家码(HK/JP/SG…),界面以码块渲染,绝不用 emoji 国旗。 final String code; final String nameZh; final String nameEn; /// 节点层级:'free' | 'pro'。 final String tier; /// 客户端实测延迟(ms);0 = 尚未测得(显示 — )。 final int ping; final NodeTag tag; /// 节点入口 host/port(来自 /v1/nodes),用于客户端 TCP 实测延迟。 final String host; final int port; /// 节点状态(来自 /v1/nodes):'up' 可用;其余(如 'down')= agent 离线/不可用。 final String status; /// 不可用:status 非 'up'(节点 agent 离线,连了也会 503)。 bool get isDown => status != 'up'; /// 延迟标签:测得显示「Nms」,未测显示「—」。 String get pingLabel => ping > 0 ? '${ping}ms' : '—'; Node copyWith({int? ping}) => Node( code: code, nameZh: nameZh, nameEn: nameEn, ping: ping ?? this.ping, uuid: uuid, tier: tier, tag: tag, host: host, port: port, status: status, ); String localizedName(AppLang lang) => lang == AppLang.zh ? nameZh : nameEn; /// 副标题:有标签时显示语义文字,否则用拉丁地名(两语言通用,不串语言)。 String localizedSub(AppLang lang) { switch (tag) { case NodeTag.streaming: return lang.tagStreaming; case NodeTag.p2p: return 'P2P'; case NodeTag.none: return nameEn; } } } /// 智能选择的哨兵选中值。 const String kSmartNodeCode = 'AUTO'; /// 节点未就绪时的中性占位(非伪造服务器,仅加载窗口短暂展示)。 const Node kPlaceholderNode = Node(code: '··', nameZh: '加载中', nameEn: 'Loading', ping: 0);