主题
用户、关系链与群组 API
1. 用户资料
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
user.current_profile | 获取当前用户资料 | 无 | XHIMUserProfile |
user.batch_profiles | 批量获取用户资料 | userIDs | XHIMUserProfileBatch |
user.resolve_by_phone | 按完整手机号精确解析一个可发现用户 | phoneNumber | XHIMUserProfile |
user.update_profile | 更新当前用户资料 | XHIMUserProfileUpdate | XHIMUserProfile |
user.current_profile_with_extension | 获取当前资料和权威公开扩展 | 无 | 平台 extension-aware profile/envelope |
user.batch_profiles_with_extensions | 批量获取资料和精确对齐的平行扩展 | userIDs, 可选会话上下文 | 平台 extension-aware batch |
user.resolve_by_phone_with_extension | 精确手机号解析并返回公开扩展 | 规范 E.164 phoneNumber | 平台 extension-aware profile/envelope |
user.update_profile_with_extension | 更新资料并返回权威公开扩展 | 至少一个资料字段;扩展可选,nil 保持、空值清除 | 平台 extension-aware profile/envelope |
XHIMUserProfileBatch 同时返回找到的资料和缺失 ID;批量查询方必须按 userID 关联结果,不能依赖返回顺序。显示名称和头像采用服务端返回值,客户端不再叠加 一套独立缓存优先级。
XHIMUserProfile 同时包含两类身份:
userID是协议、数据库、好友和会话 API 使用的不透明内部主键,接入方不得把 它当作昵称或账号显示在普通页面;publicUserID是服务端生成、租户内唯一且稳定的短公开标识,只用于个人资料、 联系人确认页和客服场景的展示/复制,不替代 API 的toUserID、peerUserID等内部参数。
资料更新事件会投递给本人、有效好友以及仍共享会话的用户。聊天页已打开时, 收到 profile/sync 失效通知后应强制重新读取相关 XHIMUserProfile,再刷新可见 消息头像;不应要求用户退出聊天页后重新进入。头像控件必须让远程图片与文字 占位互斥:图片加载成功即隐藏文字,URL 变化或加载失败才恢复占位。
XHIMUserProfileUpdate 的可选字段表示“是否修改”,空字符串是一个明确值。 修改成功后以返回的完整 Profile 替换本地 UI,不要拼接旧值。
1.1 公开业务扩展
application_extension_json 是应用拥有的公开业务扩展,适合放置 “客户类型”、“前端展示标签”、“非敏感业务能力开关”等由接入应用解释的 小型元数据。它在精确陌生人资料查询中也可见, privateProfileVisible 仅继续控制实名、邮箱、手机和部门等私密资料。
安全约束:严禁放入手机号、邮箱、身份证件、实名信息、密码、 Token、会话凭证、认证状态、E2EE 密钥或任何其他个人敏感/认证数据。
更新语义固定:
- 省略 optional bytes:保持原值;
- 传入空 bytes:清除;
- 传入非空 bytes:必须是 UTF-8 JSON object,规范化后最大 16 KiB、嵌套深度小于 64。
服务端会规范化 key 顺序和 JSON 表示,成功回执才是权威值。更新事件、 Sync、PostgreSQL/SQLite 投影都使用这份规范字节;审计日志只记录 changed / bytes / cleared,不记录 JSON 内容。
底层稳定合同为 Protobuf/Core 与 C ABI。C ABI 单资料需使用 xhim_v1_client_get_current_user_profile_with_extension、 xhim_v1_client_resolve_user_by_phone_with_extension 和 xhim_v1_client_update_current_user_profile_with_extension;批量结果通过 xhim_v1_user_profile_page_get_application_extensions 取得同下标并行数组。 公开 Facade 按平台使用 envelope/平行投影,或在已有高层模型的 nullable 扩展上建立“成功即 non-null”保证;稳定 C ABI 前缀不变:
- 基础
currentUserProfile/userProfiles/resolveUserByPhone只承诺基础资料,旧 Core 上继续成功; *WithExtension(s)明确要求公开扩展,旧 Core 缺少新符号/ accessor 时稳定返回UNSUPPORTED,不会伪造空 JSON;- 新 Core 连接未声明对应能力的旧 Server 时,也必须在请求/变更前 fail-closed
UNSUPPORTED,不能只依赖 native 符号存在; - 扩展感知查询成功后,空字节才是“服务端权威地已清除”; 方法不可用/旧投影是 unknown,不是空值。
所有 Native Wrapper 都在 C 回调返回前深拷贝 profile、平行扩展和 嵌套 byte view;扩展内容不出现在 description/默认日志中。
1.2 上传头像(Apple)
Apple SDK 提供头像专用上传流程。宿主只需给出已经裁剪/压缩的本地图片,SDK 内部完成短期存储授权、上传、内容审核和资料更新,不需要 App 自己处理存储 Token:
swift
let profile = try await client.uploadCurrentUserAvatar(
fileURL: resizedJPEG,
mimeType: "image/jpeg"
) { progress in
updateAvatarProgress(progress.fractionCompleted)
}
avatarView.setURL(profile.avatarURL)头像仅接受 JPEG、PNG、HEIC 或 WebP,单文件上限 5 MiB。服务端把头像和会话 附件分配到不同 purpose,未审核完成的文件不会生成公开头像地址;稳定头像地址 再按需重定向到短期对象存储下载授权。购买方不能把普通消息附件 ID 直接拼成 头像 URL。
1.3 手机号精确查找
五个原生 Facade 同时保留基础资料入口和扩展感知入口。新的 Web/Flutter/Electron 入口也只接受规范 E.164;平台精确名称见 平台 API 映射。
iOS / macOS Swift Concurrency:
swift
let profile = try await client.resolveUser(
phoneNumber: "13800138000"
)iOS / macOS 回调:
swift
let request = client.resolveUser(
phoneNumber: "+8613800138000",
onSuccess: { profile in
showFriendConfirmation(profile)
},
onFailure: { error in
showLookupError(code: error.stableCode)
}
)
// 页面退出或开始下一次搜索时可以取消。
request.cancel()中国大陆 11 位手机号、86 开头号码和 E.164 会在客户端统一为规范 E.164 后 提交。该 API 一次只接受一个完整号码并进行精确匹配,不支持号码片段、昵称、 模糊或批量搜索。成功返回 XHIMUserProfile,前端展示 publicUserID,其中的 内部 userID 才能传给好友申请或单聊 API;返回结果不包含手机号。
常见稳定错误:
stableCode | 含义 | 客户端处理 |
|---|---|---|
invalid_phone_number / invalid_argument | 号码格式不合法 | 留在输入页并提示输入完整号码 |
not_found | 当前 App 目录中没有可返回的用户 | 统一提示“未找到用户”,不要推断账号是否注册 |
rate_limited | 账号、设备或 IP 查询过于频繁 | 按 retryAfterMilliseconds 延迟重试 |
credential_required | 当前登录凭证不可用 | 交给统一登录恢复流程,不要匿名重试 |
request_cancelled | 页面已取消本次查询 | 静默结束,不展示失败弹窗 |
Development Server 可以使用演示手机号注册目录实现该能力。Production 必须由 部署方接入生产用户目录,并提供显式可发现性开关、App/租户隔离、鉴权、多维 限流、防枚举与审计;SDK 不直接访问手机号身份表,也不会把手机号写入用户资料 或本地好友投影。未部署生产目录时,服务端不得宣称支持 user.phone_resolution。
2. 好友申请和好友关系
群成员资料页应使用带群上下文的批量资料接口:Apple 端为 userProfiles(userIDs:inConversationID:),Android 端为 userProfiles(userIds, contextConversationId),Windows 端为 GetUserProfilesInConversationAsync。服务端会同时校验请求人和目标用户的 在群状态以及 memberProfileVisible;禁止时只返回公开资料。
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
relationship.send_friend_request | 发起不带扩展的好友申请 | toUserID, introduction, source, 可选 sourceConversationID, mutationID | XHIMFriendRequest |
relationship.send_friend_request_with_extension | 发起带参与双方可见扩展的好友申请 | 基础输入 + 必填 applicationExtensionJSON | XHIMFriendRequestEnvelope / 平台强类型扩展结果 |
relationship.resolve_friend_request | 接受或拒绝 | requestID, decision, mutationID | XHIMFriendRequestResolution |
relationship.resolve_friend_request_with_extension | 接受或拒绝并保留原申请扩展 | requestID, decision, mutationID | 扩展感知 resolution |
relationship.delete_friend_requests | 从当前账号的多端申请收件箱删除记录 | requestIDs (1...100、不重复), mutationID | XHIMSocialRequestDeletionResult |
relationship.delete_friendship | 删除好友关系 | peerUserID, mutationID | XHIMFriendshipDeletion |
relationship.set_friend_remark | 设置好友备注 | peerUserID, remark, expectedRevision, mutationID | XHIMFriendRemarkChange |
relationship.set_friend_pinned | 为当前账号置顶/取消置顶好友 | peerUserID, isPinned, expectedRevision, mutationID | XHIMFriendshipPropertyChange |
relationship.set_friend_application_extension | 保存当前应用的定向 JSON 扩展 | peerUserID, json, expectedRevision, mutationID | XHIMFriendshipPropertyChange |
relationship.update_friends | 原子批量更新多个好友的统一私有属性 | targets (1...100,每项 exact revision)、至少一个 remark/isPinned/applicationExtensionJSON、mutationID | 按输入顺序的权威 friendships |
relationship.check | 批量检查好友与己方拉黑状态 | userIDs (1...500,不重复) | [XHIMRelationshipStatus] |
relationship.list_friend_requests | 分页查询好友申请 | 可选 cursor, limit | XHIMSocialPage<XHIMFriendRequest> |
relationship.list_friend_requests_with_extensions | 分页查询好友申请及权威扩展 | 可选 cursor, limit | 扩展感知申请页 |
relationship.list_friendships | 分页查询好友 | 可选 cursor, limit | XHIMSocialPage<XHIMFriendship> |
relationship.search_friend_requests | 搜索本地好友申请投影 | query, 可选状态/精确 ID/cursor | XHIMSocialPage<XHIMFriendRequest> |
relationship.search_friend_requests_with_extensions | 搜索好友申请并要求扩展投影 | 同基础搜索 | 扩展感知申请页 |
relationship.search_friendships | 按 ID、备注或同步的展示名搜索好友 | query, 可选精确 ID/cursor | XHIMSocialPage<XHIMFriendship> |
relationship.social_summary | 读取通讯录与申请角标统计 | 无 | XHIMSocialSummary |
Core/C ABI 的 relationship.update_friends 可用一个 mutation ID 为所有 目标同时修改一个或多个统一字段。目标不可重复,每项必须携带 当前权威的 exact revision;任一目标不是 active friend、revision 失配或 参数非法,整批回滚。同一目标同时修改多个字段也只把共享 revision 增加一次。空 remark 或空 extension 是显式清除;extension 必须是最多 16 KiB、深度小于 64 的 JSON object,服务端持久化 和返回 canonical JSON。响应严格保留输入顺序,只向 owner account 发送同步事件,对端不会得到备注或扩展数据。各端便捷 Facade 完成前,该能力通过稳定 C ABI xhim_v1_client_update_friends() 使用。
decision 只有 accept 和 reject。同一 mutationID 重放返回相同业务结果; 使用不同 mutation ID 重复处理已经完成的申请,会得到稳定冲突或状态错误。
applicationExtensionJSON 是好友申请的参与双方可见业务 metadata, 适合承载活动来源、CRM 线索或审批标签。非空值必须是最大 16 KiB、 嵌套深度小于 64 的 UTF-8 JSON object;成功后使用服务端返回的 canonical JSON。无关用户不会在快照、同步事件或本地投影中看到这份数据。 处理申请的回执会继续携带原申请扩展,便于二次开发保持业务上下文。
基础发送/列表/搜索/处理入口始终保持旧 Core 兼容,不读写新结构尾部。 扩展发送只能走 dedicated capability symbol;扩展感知列表和搜索必须取得 与 item 等长、同下标且 requestID 一致的平行投影。扩展感知处理会在 提交 mutation 前先做 capability preflight。任一条件不成立均返回 UNSUPPORTED/invalid_response,不回退基础 API,也不先提交再丢失回执扩展。 对应 Server capability 缺失时同样必须在 mutation 前返回 UNSUPPORTED,防止新 Core 向旧 Server 发送会被忽略的扩展字段。
XHIMFriendRequest.fromUserID/toUserID 决定申请方向。只有 toUserID == currentUserID 且状态仍为 pending 的收到申请可以显示“同意/拒绝”; 发起方只能显示“等待对方处理”。空账号、当前账号自身以及超过协议 UTF-8 长度 的账号应在调用 SDK 前拒绝,不能把 native INVALID_ARGUMENT 显示成无上下文的 “错误 1”。
申请删除不是后台物理删除:它会生成当前账号私有的同步墓碑并清掉该账号各端的 本地投影,同时保留对方/群管理员视角和服务端审核记录。批量上限为 100;部分成功 不会返回,调用要么整体提交,要么整体失败。
source 支持 profile/card/phone/account/qrCode/group。只有 group 来源必须 同时提供 sourceConversationID;服务端会校验双方是否是已加入成员以及 memberFriendRequestsAllowed。这个校验不得由前端开关代替。申请投影会保留 来源和群会话 ID,便于“通过群聊添加”的 UI 和安全审计。
toUserID 是服务端签发或客户业务系统映射的内部用户 ID,不是手机号、昵称、 publicUserID 或模糊搜索关键词。普通前端使用手机号调用 user.resolve_by_phone 解析为内部 userID,先展示服务端返回的头像、昵称和 publicUserID 供当前用户确认,再调用 relationship.send_friend_request,不要把原始手机号直接传给好友申请 API。
备注、好友置顶和业务扩展是当前账号定向的服务端字段,不会泄露给 对方。三者共用 remarkRevision(为保持源码兼容沿用该名称)作为 CAS 栅栏:每次写入后都必须使用最新返回的修订号发起下一次写入。扩展值为最多 16384 个 UTF-8 字节的完整 JSON,空值用于清除。UI 展示时直接使用返回的 remark / display 字段,不在另一个本地 Store 中覆盖服务端结果。
relationship.check 是服务端权威快照,不读本地同步投影,适合在 批量展示名片、选择群成员或发起好友申请前确认当前关系。返回顺序与 输入 userIDs 一致;未知或当前不可见账号以 isFriend=false 且 blockedByMe=false 返回,不用差异错误泄露账号是否存在。
搜索和统计直接读取当前账号已同步的加密 SQLite 投影,不会把搜索词 发送给服务端。结果是当前同步水位的弱一致快照;分页时必须保持搜索条件 不变并原样回传不透明 cursor。
3. 黑名单
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
relationship.set_block | 添加或解除拉黑,不修改扩展 | blockedUserID, isActive, mutationID | 基础 XHIMBlock |
relationship.set_block_with_extension | 设置拉黑状态并替换/清除定向扩展 | 基础输入 + 必填 applicationExtensionJSON | 扩展感知 XHIMBlock |
relationship.list_blocks | 分页查询黑名单 | 可选 cursor, limit | XHIMSocialPage<XHIMBlock> |
relationship.list_blocks_with_extensions | 分页查询黑名单并要求权威扩展 | 可选 cursor, limit | 扩展感知黑名单页 |
relationship.search_blocks_with_extensions | 搜索黑名单并要求权威扩展 | query, 可选精确 ID/cursor | 扩展感知黑名单页 |
解除拉黑使用同一个 setBlock,并把 isActive 设为 false。拉黑是否同时阻止 历史消息、好友申请或群内互动由部署方服务端策略决定;客户端只根据返回和稳定 错误展示结果。
黑名单 applicationExtensionJSON 是当前账号私有的单向属性,不会 返回给被拉黑方。nil / 未提供表示保持原值,显式空 bytes 表示清除, 非空值遵守同样的 JSON object / 16 KiB / 深度小于 64 约束。这个存在性语义 使得业务端可以只更改拉黑状态而不意外清掉扩展。 基础黑名单模型不能证明扩展为空:Android 将旧投影标记为 nullable null,Apple/Windows/HarmonyOS 使用不含扩展的基础模型;只有 *WithExtension(s) 成功结果中的空字符串/空 Data 才是权威清除。
4. 群组生命周期
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
group.create | 创建不带应用扩展的群组 | title, memberUserIDs, mutationID, 可选 initialAdministratorUserIDs | XHIMGroupChange |
group.create_with_extension | 在建群事务内初始化群公开扩展 | 基础建群输入 + 必填 applicationExtensionJSON | XHIMGroupChange |
group.change_members | 增删成员 | conversationID, addUserIDs, removeUserIDs, expectedRevision, mutationID | XHIMGroupChange |
group.leave | 当前用户退群 | conversationID, expectedRevision, mutationID | XHIMGroupLifecycleResult |
group.dismiss | 群主解散群 | conversationID, expectedRevision, mutationID | XHIMGroupLifecycleResult |
changeGroupMembers 是一个原子变更。同一用户不能同时出现在 add/remove; 接入层应在调用前去重。成员权限、群容量和是否允许直接邀请由服务端校验,客户端 不要把按钮可见性当成权限证明。
创建群聊时,memberUserIDs 只需要传“除当前账号外”的候选成员;服务端会把 当前登录用户自动加入并设为群主。推荐 UI 是:
- 固定展示“我(群主)”,不允许取消;
- 从 SDK 返回的好友列表多选成员;
- 点击加号后,手机号调用
resolveUser(phoneNumber:)精确解析; - 展示头像、昵称和
publicUserID让用户确认,再把解析后的内部userID加入候选; - 群名允许留空,由客户端按成员名生成可读预览,或由业务服务采用默认名称。
需要在建群时直接设置管理员,可把对应成员内部 ID 放入可选 initialAdministratorUserIDs。它必须是 memberUserIDs 的无重复子集,且不能 包含当前账号;服务端在创建成员的同一事务内写入角色。创建者始终是群主,不能 通过该参数把群主身份交给其他人。后续角色变更仍使用带 revision CAS 的 changeGroupGovernance。
不要让用户再次输入自己的账号,也不要把手机号直接放进 memberUserIDs。
applicationExtensionJSON 可在建群事务中初始化应用拥有的群级 公开业务元数据。非空值必须是最大 16 KiB、嵌套深度小于 64 的 UTF-8 JSON object;服务端会返回规范 JSON,客户端不得用请求原文 覆盖权威回执。该字段是公开业务 metadata,严禁存放凭证、密钥、 Token、密码或消息正文。 Native Wrapper 传入该字段时必须使用 xhim_v1_client_create_group_with_extension,群本体扩展治理必须使用 xhim_v1_client_change_group_governance_with_extension。两个符号都必须 weak-link;旧 Core 缺少符号时对外返回 unsupported,不能退回历史入口 后对已被忽略的扩展假成功。 不带扩展的基础 createGroup 仍可以连接旧 Core;显式传入空扩展也是 新能力(权威清除/初始化为 unset),不能降级到旧符号。
群主退出必须调用 dismissGroup 或先完成群主转让,普通成员退出调用 leaveGroup。不要让群主按钮调用 leaveGroup 再把服务端 403 当作网络错误。
5. 入群申请
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
group.request_join | 申请加入群组,不传扩展 | conversationID, introduction, mutationID | XHIMGroupJoinMutation |
group.request_join_with_extension | 提交带审批 metadata 的入群申请 | 基础输入 + 必填 applicationExtensionJSON | 扩展感知 XHIMGroupJoinMutation |
group.resolve_join | 管理员接受或拒绝 | requestID, decision, mutationID | XHIMGroupJoinMutation |
group.resolve_join_with_extension | 处理申请并保留原申请扩展 | requestID, decision, mutationID | 扩展感知 mutation |
group.delete_join_requests | 从当前账号的多端入群申请收件箱删除记录 | requestIDs (1...100、不重复), mutationID | XHIMSocialRequestDeletionResult |
group.list_join_requests | 分页查询入群申请 | 可选 cursor, limit | XHIMSocialPage<XHIMGroupJoinRequest> |
group.list_join_requests_with_extensions | 分页查询入群申请及审批扩展 | 可选 cursor, limit | 扩展感知入群申请页 |
group.search_join_requests | 搜索可见的入群申请 | query, 可选状态/精确 ID/cursor | XHIMSocialPage<XHIMGroupJoinRequest> |
group.search_join_requests_with_extensions | 搜索入群申请并要求扩展投影 | 同基础搜索 | 扩展感知入群申请页 |
群设置为无需审批时,服务端可以直接完成加入;客户端仍以返回的 XHIMGroupJoinMutation 和后续群成员投影为准。
入群申请扩展只在“需要审批”流程中保留,并且只对申请人和群主/ 管理员可见;普通群成员无权通过快照或列表读取。群无需审批而直接加入时, 服务端会主动丢弃这份审批 metadata,避免把审批专用信息变成长期群成员资料。 处理完成的申请回执保留原扩展;非空值同样必须是最大 16 KiB、嵌套深度 小于 64 的 JSON object。
基础入群申请 API 在旧 Core 上仍然成功;它们不读新尾部,因此对 扩展的可用性是 unknown。扩展感知入口缺少 dedicated symbol/accessor 时 fail-closed UNSUPPORTED。扩展感知结果中的空值才表示“服务端权威清除”, 不得把旧投影的 unknown 转换为空值。
6. 群治理
group.change_governance
统一输入:
conversationIDexpectedRevisionmutationIDchange: XHIMGroupGovernanceChange
支持的强类型变更:
| change | 参数 | 说明 |
|---|---|---|
setAdministrator | userID, isAdministrator | 设置或取消管理员 |
setMute | userID, 可选 mutedUntilMilliseconds | nil / 空值解除禁言 |
setAllMute | 可选 mutedUntilMilliseconds | 一次 CAS 禁言/解禁全部普通已入群成员;群主和管理员不受影响 |
transferOwnership | toUserID | 转让群主 |
setJoinApprovalRequired | Bool | 开关入群审批 |
setProfile | title, avatarURL, announcement, description | 原子更新群资料 |
setOwnNickname | nickname | 修改当前登录成员自己的群昵称;服务端忽略客户端伪造的目标成员 |
setAccessPolicy | memberProfileVisible, memberFriendRequestsAllowed | 原子设置群成员资料可见性和群内加好友权限 |
setNewMemberHistoryVisible | Bool | 设置之后入群成员是否可查看入群前历史 |
setMemberApplicationExtension | userID, applicationExtensionJSON | 群主/管理员设置指定已入群成员的业务扩展;空值清除,非空值必须是最大 16 KiB、嵌套深度小于 64 的 JSON object |
setApplicationExtension | applicationExtensionJSON | 群主/管理员完整替换群本体的公开业务扩展;空值显式清除,服务端返回规范 JSON |
group.set_application_extension 是 setApplicationExtension 的跨端稳定 api-id。 平台可以将它表达为独立便利方法,但底层只能调用 xhim_v1_client_change_group_governance_with_extension。
返回 XHIMGroupChange。setAllMute 由服务端在单一事务内更新全部普通成员, 不要在客户端逐人循环调用 setMute。群角色和 revision 可能同时改变,成功后应重新查询群资料 与成员列表。
group.set_new_member_history_visibility 不使用客户端时钟或入群时间猜测 边界。服务端在成员加入时固化当前 serverSequence,历史查询只返回 该序列之后的消息。既有成员的历史权限不会被追溯修改。
推荐客户端按照以下角色矩阵呈现操作,但服务端仍会对每次写操作独立鉴权:
| 操作 | 群主 | 管理员 | 普通成员 |
|---|---|---|---|
| 修改群资料、入群审批、成员访问/历史策略 | ✓ | ✓ | — |
| 设置群本体公开业务扩展 | ✓ | ✓ | — |
| 设置普通成员业务扩展 | ✓ | ✓ | — |
| 邀请、移除和禁言成员 | ✓ | ✓ | — |
| 设置/取消管理员、转让群主 | ✓ | — | — |
| 解散群聊 | ✓ | — | — |
| 退出群聊 | 转让或解散后 | ✓ | ✓ |
| 修改自己的群昵称、消息免打扰 | ✓ | ✓ | ✓ |
成员列表应根据 XHIMGroupMember.role 明确展示“群主”和“管理员”标记。管理员 不能管理群主,也不能管理其他管理员;普通成员不显示群管理入口,只保留自己的 群昵称和本地会话免打扰设置。权限不足返回稳定的 forbidden,客户端应刷新群资料 和成员角色,而不是继续重试。
群本体扩展与成员扩展是两个不同的字段。更新群本体扩展仍使用 exact expectedRevision 和 mutationID;重放只能接受语义相同的请求。 无关 governance action 不会修改扩展,也不允许夹带扩展实现“顺便修改”。 审计只记录 changed/bytes/cleared,不记录 JSON 内容;Webhook 和 GROUP_CHANGED Sync 事件都携带服务端权威值。
7. 群查询
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
group.list | 分页查询当前用户群组 | 可选 cursor, limit | XHIMSocialPage<XHIMGroup> |
group.list_members | 分页查询成员 | conversationID, 可选 cursor, limit | XHIMSocialPage<XHIMGroupMember> |
group.search | 按群 ID、名称或群资料搜索已加入群聊 | query, 可选精确 ID/cursor | XHIMSocialPage<XHIMGroup> |
group.search_members | 按成员 ID、群昵称、同步的展示名或入群时间范围搜索 | conversationID, query, 可选 joinedFromMilliseconds / joinedBeforeMilliseconds / 精确 ID / cursor | XHIMSocialPage<XHIMGroupMember> |
入群时间使用左闭右开区间 [from, before);0 表示不限制。 只按时间筛选时 query 可为空。该条件同样在已同步的加密本地投影上 执行,分页期间必须保持范围不变。
XHIMGroupMember.nickname 是成员在当前群内主动设置的群昵称;为空时客户端应使用 服务端联表返回的实时 displayName。avatarURL 同样来自实时用户资料,不会在成员记录中 复制一份过期快照。applicationExtensionJSON 是成员在该群内独立的受限业务扩展:服务端 只接受 JSON object,按规范 JSON 持久化;空值表示清除。群主可以修改任意已入群成员, 管理员只可修改普通成员,普通成员不能调用该治理动作。扩展内容不写入审计明文。
成员页包含群 revision fence。跨多页渲染时,如果后续页 revision 与第一页不一致,应丢弃 这一轮结果并从第一页重新读取,避免把成员变更前后的数据拼在一起。Core 的旧版群成员 C ABI 数组仍保持原有 stride;实时资料和业务扩展通过同索引的并行 profile 数组读取,兼容 已经发布的二进制消费者。
projection.check_joined_groups / projection.check_group_members 分别映射 xhim_v1_client_check_joined_group_projection / xhim_v1_client_check_group_member_projection,用于判断当前加密 SQLite 投影是否持有可验证的同步证明。它们是本地只读检查,complete=false 仍是成功结果, 原因可能是旧库没有 marker、origin replay 未完成、checkpoint/revision 不连续、成员数 或群主约束不一致。检查不会联网或自动修复,也绝不只根据行数相等推断完整。
对于没有经历完整群创建事件及后续连续 revision 的既有群,可显式调用 projection.refresh_group_members(C ABI: xhim_v1_client_refresh_group_member_projection)。Core 会使用当前 Ready 账号的认证会话, 从 /v1/social/group-members:list 空游标开始拉取无筛选的全部成员页。同一轮的 group_revision 必须非零且完全一致,user_id 必须跨页严格递增且唯一, has_more / next_after_user_id 必须结构自洽。Core 先在内存中完整缓冲,最后一页通过后 才在单个 SQLite 事务中再次校验群已加入、revision、member_count、当前用户和唯一群主, 然后替换 active member 投影并写入 COMPLETE marker;任何分页、版本、结构或身份冲突都不会 落入半套数据。
远端刷新和本地检查是两个独立操作:刷新成功直接返回新的 proof-backed snapshot; revision 在拉取或提交前变化时返回可重试的 group_member_projection_revision_stale,不会伪造“已修复”。刷新不接受搜索、角色、时间或排除过滤; 过滤成员页永远不能用于建立 COMPLETE marker。
8. Presence、正在输入和在线信令
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
user.publish_presence | 发布在线状态 | XHIMPresenceStatus, ttlMilliseconds | XHIMPresencePublication |
user.query_presence | 批量查询已授权用户在线状态和在线端 | userIDs | 与请求顺序一致的 Presence 快照数组 |
user.subscribe_presence | 原子授权并加入显式 Presence 订阅 | userIDs | 当前授权快照数组 |
user.unsubscribe_presence | 幂等移除显式 Presence 订阅 | userIDs 可为空 | 完整剩余订阅 ID |
user.list_presence_subscriptions | 读取账号级显式订阅集 | 无 | 字典序订阅 ID |
user.publish_typing | 发布输入状态 | conversationID, isTyping, ttlMilliseconds | XHIMTypingPublication |
user.query_typing | 读取成员当前输入租约 | conversationID, userID | XHIMTypingSnapshot |
user.publish_custom_signal | 发布会话内在线临时信令 | 会话、type/version、1...65536 字节载荷、TTL | XHIMCustomSignal |
Presence 和 Typing 是短暂状态,不进入可靠消息历史。接收方必须按 expiresAtMilliseconds 本地过期;“停止输入/离线”帧可能因断网丢失。
queryPresence 由服务端依据本人、好友或共同会话关系授权,不允许 客户端任意扩大查询范围。批量内任一 ID 未授权时整批失败,不返回可用于 枚举用户的部分结果。activePlatforms 使用稳定平台枚举,可区分 iOS、Android、macOS、Windows、HarmonyOS、Web 和 Linux。首次进页先主动查询, 后续使用 presenceChanged 实时事件刷新;两条链路都携带同一份 activePlatforms,不需要应用解析 WebSocket 帧。
subscribePresence 先对整批 ID 执行与 queryPresence 相同的 服务端授权,仅在整批成功后才原子合并进本地订阅集;重复 ID 会按首次出现规范化。首次订阅或退订后,presenceChanged 只投递集合中用户。空退订是幂等 no-op,但仍会选择显式模式; 如需清空,先读取 listPresenceSubscriptions 再退订全部 ID。 登出、换号或销毁会话会清空订阅并恢复服务端授权受众兼容模式; 普通断线重连保留订阅集。
queryTyping 是纯读快照,不广播事件、不推进 sequence。从未输入 或租约已过期时会返回 isTyping = false 和 sequence = 0; 正常发布与实时事件仍要求非零 sequence。activePlatforms 是当前 未过期输入租约所属端的去重排序集合。
publishCustomSignal 先由服务端校验发送者是会话成员,然后只向当前 在线成员转发。它不会出现在历史消息、离线同步或未读数中;接收端 必须在 expiresAtMilliseconds 到期后删除 UI 状态。需要可靠送达时使用 版本化自定义消息,不得自行把在线信令当作可靠通道。
9. Push 设备
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
push.register | 注册或轮换 Push Token | XHIMPushDevice | XHIMPushDeviceRegistration |
push.disable | 禁用当前设备 Push | deviceID | XHIMPushDeviceDisableResult |
XHIMPushDevice 包含 platform、deviceID、token、environment 和 locale。Token 只作为输入,错误、日志、description 和诊断不会回显。APNs/FCM/Huawei 厂商 凭证配置在服务端,不放进 SDK 客户端。
10. 多端登录设备
| api-id | 用途 | 主要输入 | 返回 |
|---|---|---|---|
session.list | 列出当前账号设备会话 | 无 | XHIMDeviceSessionPage |
session.revoke | 撤销一个设备会话 | sessionID, mutationID | XHIMDeviceSessionRevocation |
XHIMDeviceSessionPolicy 描述部署方的多端登录策略和设备上限。撤销当前会话可能 立即触发凭证失效或登出;接入方应在账号容器统一处理状态事件。
11. 社交列表刷新
收到 socialChanged 时按 socialScope 重新查询对应列表:
| scope | 重查方法 |
|---|---|
friendRequests | friendRequests |
friendships | friendships |
groups | groups |
groupMembers | 当前 scope ID 的 groupMembers |
blocks | blocks |
groupJoinRequests | groupJoinRequests |
unknown scope 或 requiresFullRequery == true 时,刷新当前页面依赖的全部社交 投影,不尝试解析未知数字值。