package teamspeak // bridge-contract.md —— Go ↔ Kotlin 跨语言契约的单一事实来源。 // // 为什么需要这个文件: // gomobile 不能导出 []string / []*T / Go error,所以复杂数据全部走 JSON 字符串。 // 代价是**编译器无法校验字段名**:Go 侧把 `channelId` 写成 `channelID`, // Kotlin 侧不会编译失败,只会在运行时静默拿到默认值("" / 0 / false), // 而 Kotlin 的 `Json { ignoreUnknownKeys = true }` 让这个失效模式更隐蔽。 // // 边界上的三种数据类型(按保护强度递减,文档必须写清属于哪一类): // // A 类|gomobile 导出 struct(TextMsg / PokeEvent / Channel / Client) // 字段是编译期检查的,Go 侧改名 → Kotlin 侧编译失败。**不需要**常量保护。 // // B 类|JSON 字符串(channelJSON / clientJSON / serverInfoJSON / ...) // 完全无编译期保护,就是本文件存在的理由。字段名集中声明在下方常量里, // contract_test.go 断言常量与 struct tag 永远一致——改 tag 会让测试变红。 // // C 类|广告过但无消费方的契约(channelDetailedJSON / clientDetailedInfoJSON / // initialSyncJSON) // 已被 GetInitialSyncJSON 等导出,但 Kotlin 侧当前不解析。 // 声明字段名是为了钉住形状,避免「文档说有、实际对不上」。 // 若将来要消费,先跑 contract_test.go 确认字段名。 // // 字段名的运行时事实仍是 bridge.go / kotlin_api.go 中的 `json:"..."` tag // (encoding/json 只认 tag);本文件是声明,测试是保证两者不漂移的机制。 // BridgeContractVersion 跨语言契约版本。 // // 递增规则:任何**不兼容**变更(字段改名、类型变更、字段删除)都必须 +1。 // 只新增 Optional 字段(Kotlin 侧有默认值)不需要 +1。 // 递增后必须同步更新: // - android/.../TSBridge.kt 的 EXPECTED_CONTRACT_VERSION // - docs/bridge-contract.md const BridgeContractVersion = "1" // ─── B 类:channelJSON ⇄ ChannelInfo ────────────────────────── // 生产方 GetChannelsJSON();消费方 Repository.fetchChannels()。 const ( FieldChannelID = "id" FieldChannelName = "name" FieldChannelParentID = "parentId" FieldChannelDescription = "description" FieldChannelIsPassword = "isPassword" ) // ─── B 类:clientJSON ⇄ ClientInfo ──────────────────────────── // 生产方 GetClientsJSON();消费方 Repository.fetchClients()。 const ( FieldClientID = "id" FieldClientNickname = "nickname" FieldClientUID = "uid" FieldClientChannelID = "channelId" FieldClientServerGroups = "serverGroups" FieldClientIsSelf = "isSelf" ) // ─── B 类:serverInfoJSON ⇄ ServerInfo ──────────────────────── // 生产方 GetServerInfoJSON();消费方 Repository / ServerViewModel。 const ( FieldServerName = "name" FieldServerWelcomeMessage = "welcomeMessage" FieldServerMaxClients = "maxClients" FieldServerClientsOnline = "clientsOnline" FieldServerChannelsOnline = "channelsOnline" FieldServerUptime = "uptime" FieldServerVersion = "version" FieldServerPlatform = "platform" FieldServerCreated = "created" FieldServerIconID = "iconId" FieldServerDefaultServerGroup = "defaultServerGroup" FieldServerDefaultChannelGroup = "defaultChannelGroup" ) // ─── B 类:DownloadFileBytesJSON ⇄ TSBridge.downloadFileBytes ─ // 生产方 DownloadFileBytesJSON();消费方 FileDownloadManager。 // 失败时返回 "{}"(而非空串),消费方以 data 为空判定失败。 const ( FieldFileData = "data" FieldFileSize = "size" ) // ─── C 类:channelDetailedJSON(GetChannelsDetailedJSON)────── // 当前无 Kotlin 消费方。字段名在此钉住形状。 const ( FieldChannelDetailTopic = "topic" FieldChannelDetailOrder = "order" FieldChannelDetailCodec = "codec" FieldChannelDetailCodecQuality = "codecQuality" FieldChannelDetailNeededTalkPower = "neededTalkPower" FieldChannelDetailMaxClients = "maxClients" FieldChannelDetailMaxFamilyClients = "maxFamilyClients" FieldChannelDetailIsMaxClientsUnlimited = "isMaxClientsUnlimited" FieldChannelDetailIsMaxFamilyClientsUnlimited = "isMaxFamilyClientsUnlimited" FieldChannelDetailIsPermanent = "isPermanent" FieldChannelDetailIsSemiPermanent = "isSemiPermanent" FieldChannelDetailIsDefault = "isDefault" FieldChannelDetailIsOrdered = "isOrdered" FieldChannelDetailIconID = "iconId" FieldChannelDetailNeededModifyPower = "neededModifyPower" ) // ─── C 类:clientDetailedInfoJSON(GetClientDetailInfoJSON)─── // 当前无 Kotlin 消费方。 const ( FieldClientDetailType = "type" FieldClientDetailAway = "away" FieldClientDetailAwayMessage = "awayMessage" FieldClientDetailInputMuted = "inputMuted" FieldClientDetailOutputMuted = "outputMuted" FieldClientDetailPlatform = "platform" FieldClientDetailVersion = "version" FieldClientDetailIP = "ip" FieldClientDetailCreated = "created" FieldClientDetailLastConnected = "lastConnected" FieldClientDetailTotalConnections = "totalConnections" FieldClientDetailDescription = "description" FieldClientDetailIconID = "iconId" ) // ─── C 类:initialSyncJSON(GetInitialSyncJSON)──────────────── // 一次性返回全部初始数据的聚合契约。当前无 Kotlin 消费方 // (Repository.performInitialSync 走的是三个独立 getter 串行请求)。 const ( FieldSyncChannels = "channels" FieldSyncClients = "clients" FieldSyncSelfID = "selfId" FieldSyncSelfChannelID = "selfChannelId" FieldSyncServer = "server" ) // 消息 targetMode 取值。 // // ⚠️ 收发能力不对称(重要,别再读成三种都支持): // // TargetModePrivate(1) 仅接收。发送侧未实现——TSBridge.sendTextMessage 只实现 mode=2, // 其余返回"暂不支持该消息类型"。SDK 的 SendTextMessage 走的也是 // mode=2 路径,私聊回复需要另找 API。 // TargetModeChannel(2) 收发均支持。 // TargetModeServer(3) 仅接收。 const ( TargetModePrivate = 1 TargetModeChannel = 2 TargetModeServer = 3 ) // GetContractVersion 返回当前 bridge 契约版本。 // Kotlin 侧在建立连接前调用并与本地期望值比对。 // gomobile 不支持返回 error,版本不匹配由 Kotlin 侧抛错处理。 func GetContractVersion() string { return BridgeContractVersion }