Skip to content

用户、关系链与群组 API

1. 用户资料

api-id用途主要输入返回
user.current_profile获取当前用户资料XHIMUserProfile
user.batch_profiles批量获取用户资料userIDsXHIMUserProfileBatch
user.resolve_by_phone按完整手机号精确解析一个可发现用户phoneNumberXHIMUserProfile
user.update_profile更新当前用户资料XHIMUserProfileUpdateXHIMUserProfile
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 的 toUserIDpeerUserID 等内部参数。

资料更新事件会投递给本人、有效好友以及仍共享会话的用户。聊天页已打开时, 收到 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_extensionxhim_v1_client_resolve_user_by_phone_with_extensionxhim_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, mutationIDXHIMFriendRequest
relationship.send_friend_request_with_extension发起带参与双方可见扩展的好友申请基础输入 + 必填 applicationExtensionJSONXHIMFriendRequestEnvelope / 平台强类型扩展结果
relationship.resolve_friend_request接受或拒绝requestID, decision, mutationIDXHIMFriendRequestResolution
relationship.resolve_friend_request_with_extension接受或拒绝并保留原申请扩展requestID, decision, mutationID扩展感知 resolution
relationship.delete_friend_requests从当前账号的多端申请收件箱删除记录requestIDs (1...100、不重复), mutationIDXHIMSocialRequestDeletionResult
relationship.delete_friendship删除好友关系peerUserID, mutationIDXHIMFriendshipDeletion
relationship.set_friend_remark设置好友备注peerUserID, remark, expectedRevision, mutationIDXHIMFriendRemarkChange
relationship.set_friend_pinned为当前账号置顶/取消置顶好友peerUserID, isPinned, expectedRevision, mutationIDXHIMFriendshipPropertyChange
relationship.set_friend_application_extension保存当前应用的定向 JSON 扩展peerUserID, json, expectedRevision, mutationIDXHIMFriendshipPropertyChange
relationship.update_friends原子批量更新多个好友的统一私有属性targets (1...100,每项 exact revision)、至少一个 remark/isPinned/applicationExtensionJSON、mutationID按输入顺序的权威 friendships
relationship.check批量检查好友与己方拉黑状态userIDs (1...500,不重复)[XHIMRelationshipStatus]
relationship.list_friend_requests分页查询好友申请可选 cursor, limitXHIMSocialPage<XHIMFriendRequest>
relationship.list_friend_requests_with_extensions分页查询好友申请及权威扩展可选 cursor, limit扩展感知申请页
relationship.list_friendships分页查询好友可选 cursor, limitXHIMSocialPage<XHIMFriendship>
relationship.search_friend_requests搜索本地好友申请投影query, 可选状态/精确 ID/cursorXHIMSocialPage<XHIMFriendRequest>
relationship.search_friend_requests_with_extensions搜索好友申请并要求扩展投影同基础搜索扩展感知申请页
relationship.search_friendships按 ID、备注或同步的展示名搜索好友query, 可选精确 ID/cursorXHIMSocialPage<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 只有 acceptreject。同一 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=falseblockedByMe=false 返回,不用差异错误泄露账号是否存在。

搜索和统计直接读取当前账号已同步的加密 SQLite 投影,不会把搜索词 发送给服务端。结果是当前同步水位的弱一致快照;分页时必须保持搜索条件 不变并原样回传不透明 cursor

3. 黑名单

api-id用途主要输入返回
relationship.set_block添加或解除拉黑,不修改扩展blockedUserID, isActive, mutationID基础 XHIMBlock
relationship.set_block_with_extension设置拉黑状态并替换/清除定向扩展基础输入 + 必填 applicationExtensionJSON扩展感知 XHIMBlock
relationship.list_blocks分页查询黑名单可选 cursor, limitXHIMSocialPage<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, 可选 initialAdministratorUserIDsXHIMGroupChange
group.create_with_extension在建群事务内初始化群公开扩展基础建群输入 + 必填 applicationExtensionJSONXHIMGroupChange
group.change_members增删成员conversationID, addUserIDs, removeUserIDs, expectedRevision, mutationIDXHIMGroupChange
group.leave当前用户退群conversationID, expectedRevision, mutationIDXHIMGroupLifecycleResult
group.dismiss群主解散群conversationID, expectedRevision, mutationIDXHIMGroupLifecycleResult

changeGroupMembers 是一个原子变更。同一用户不能同时出现在 add/remove; 接入层应在调用前去重。成员权限、群容量和是否允许直接邀请由服务端校验,客户端 不要把按钮可见性当成权限证明。

创建群聊时,memberUserIDs 只需要传“除当前账号外”的候选成员;服务端会把 当前登录用户自动加入并设为群主。推荐 UI 是:

  1. 固定展示“我(群主)”,不允许取消;
  2. 从 SDK 返回的好友列表多选成员;
  3. 点击加号后,手机号调用 resolveUser(phoneNumber:) 精确解析;
  4. 展示头像、昵称和 publicUserID 让用户确认,再把解析后的内部 userID 加入候选;
  5. 群名允许留空,由客户端按成员名生成可读预览,或由业务服务采用默认名称。

需要在建群时直接设置管理员,可把对应成员内部 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, mutationIDXHIMGroupJoinMutation
group.request_join_with_extension提交带审批 metadata 的入群申请基础输入 + 必填 applicationExtensionJSON扩展感知 XHIMGroupJoinMutation
group.resolve_join管理员接受或拒绝requestID, decision, mutationIDXHIMGroupJoinMutation
group.resolve_join_with_extension处理申请并保留原申请扩展requestID, decision, mutationID扩展感知 mutation
group.delete_join_requests从当前账号的多端入群申请收件箱删除记录requestIDs (1...100、不重复), mutationIDXHIMSocialRequestDeletionResult
group.list_join_requests分页查询入群申请可选 cursor, limitXHIMSocialPage<XHIMGroupJoinRequest>
group.list_join_requests_with_extensions分页查询入群申请及审批扩展可选 cursor, limit扩展感知入群申请页
group.search_join_requests搜索可见的入群申请query, 可选状态/精确 ID/cursorXHIMSocialPage<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

统一输入:

  • conversationID
  • expectedRevision
  • mutationID
  • change: XHIMGroupGovernanceChange

支持的强类型变更:

change参数说明
setAdministratoruserID, isAdministrator设置或取消管理员
setMuteuserID, 可选 mutedUntilMillisecondsnil / 空值解除禁言
setAllMute可选 mutedUntilMilliseconds一次 CAS 禁言/解禁全部普通已入群成员;群主和管理员不受影响
transferOwnershiptoUserID转让群主
setJoinApprovalRequiredBool开关入群审批
setProfiletitle, avatarURL, announcement, description原子更新群资料
setOwnNicknamenickname修改当前登录成员自己的群昵称;服务端忽略客户端伪造的目标成员
setAccessPolicymemberProfileVisible, memberFriendRequestsAllowed原子设置群成员资料可见性和群内加好友权限
setNewMemberHistoryVisibleBool设置之后入群成员是否可查看入群前历史
setMemberApplicationExtensionuserID, applicationExtensionJSON群主/管理员设置指定已入群成员的业务扩展;空值清除,非空值必须是最大 16 KiB、嵌套深度小于 64 的 JSON object
setApplicationExtensionapplicationExtensionJSON群主/管理员完整替换群本体的公开业务扩展;空值显式清除,服务端返回规范 JSON

group.set_application_extensionsetApplicationExtension 的跨端稳定 api-id。 平台可以将它表达为独立便利方法,但底层只能调用 xhim_v1_client_change_group_governance_with_extension

返回 XHIMGroupChangesetAllMute 由服务端在单一事务内更新全部普通成员, 不要在客户端逐人循环调用 setMute。群角色和 revision 可能同时改变,成功后应重新查询群资料 与成员列表。

group.set_new_member_history_visibility 不使用客户端时钟或入群时间猜测 边界。服务端在成员加入时固化当前 serverSequence,历史查询只返回 该序列之后的消息。既有成员的历史权限不会被追溯修改。

推荐客户端按照以下角色矩阵呈现操作,但服务端仍会对每次写操作独立鉴权:

操作群主管理员普通成员
修改群资料、入群审批、成员访问/历史策略
设置群本体公开业务扩展
设置普通成员业务扩展
邀请、移除和禁言成员
设置/取消管理员、转让群主
解散群聊
退出群聊转让或解散后
修改自己的群昵称、消息免打扰

成员列表应根据 XHIMGroupMember.role 明确展示“群主”和“管理员”标记。管理员 不能管理群主,也不能管理其他管理员;普通成员不显示群管理入口,只保留自己的 群昵称和本地会话免打扰设置。权限不足返回稳定的 forbidden,客户端应刷新群资料 和成员角色,而不是继续重试。

群本体扩展与成员扩展是两个不同的字段。更新群本体扩展仍使用 exact expectedRevisionmutationID;重放只能接受语义相同的请求。 无关 governance action 不会修改扩展,也不允许夹带扩展实现“顺便修改”。 审计只记录 changed/bytes/cleared,不记录 JSON 内容;Webhook 和 GROUP_CHANGED Sync 事件都携带服务端权威值。

7. 群查询

api-id用途主要输入返回
group.list分页查询当前用户群组可选 cursor, limitXHIMSocialPage<XHIMGroup>
group.list_members分页查询成员conversationID, 可选 cursor, limitXHIMSocialPage<XHIMGroupMember>
group.search按群 ID、名称或群资料搜索已加入群聊query, 可选精确 ID/cursorXHIMSocialPage<XHIMGroup>
group.search_members按成员 ID、群昵称、同步的展示名或入群时间范围搜索conversationID, query, 可选 joinedFromMilliseconds / joinedBeforeMilliseconds / 精确 ID / cursorXHIMSocialPage<XHIMGroupMember>

入群时间使用左闭右开区间 [from, before);0 表示不限制。 只按时间筛选时 query 可为空。该条件同样在已同步的加密本地投影上 执行,分页期间必须保持范围不变。

XHIMGroupMember.nickname 是成员在当前群内主动设置的群昵称;为空时客户端应使用 服务端联表返回的实时 displayNameavatarURL 同样来自实时用户资料,不会在成员记录中 复制一份过期快照。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, ttlMillisecondsXHIMPresencePublication
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, ttlMillisecondsXHIMTypingPublication
user.query_typing读取成员当前输入租约conversationID, userIDXHIMTypingSnapshot
user.publish_custom_signal发布会话内在线临时信令会话、type/version、1...65536 字节载荷、TTLXHIMCustomSignal

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 = falsesequence = 0; 正常发布与实时事件仍要求非零 sequence。activePlatforms 是当前 未过期输入租约所属端的去重排序集合。

publishCustomSignal 先由服务端校验发送者是会话成员,然后只向当前 在线成员转发。它不会出现在历史消息、离线同步或未读数中;接收端 必须在 expiresAtMilliseconds 到期后删除 UI 状态。需要可靠送达时使用 版本化自定义消息,不得自行把在线信令当作可靠通道。

9. Push 设备

api-id用途主要输入返回
push.register注册或轮换 Push TokenXHIMPushDeviceXHIMPushDeviceRegistration
push.disable禁用当前设备 PushdeviceIDXHIMPushDeviceDisableResult

XHIMPushDevice 包含 platform、deviceID、token、environment 和 locale。Token 只作为输入,错误、日志、description 和诊断不会回显。APNs/FCM/Huawei 厂商 凭证配置在服务端,不放进 SDK 客户端。

10. 多端登录设备

api-id用途主要输入返回
session.list列出当前账号设备会话XHIMDeviceSessionPage
session.revoke撤销一个设备会话sessionID, mutationIDXHIMDeviceSessionRevocation

XHIMDeviceSessionPolicy 描述部署方的多端登录策略和设备上限。撤销当前会话可能 立即触发凭证失效或登出;接入方应在账号容器统一处理状态事件。

11. 社交列表刷新

收到 socialChanged 时按 socialScope 重新查询对应列表:

scope重查方法
friendRequestsfriendRequests
friendshipsfriendships
groupsgroups
groupMembers当前 scope ID 的 groupMembers
blocksblocks
groupJoinRequestsgroupJoinRequests

unknown scope 或 requiresFullRequery == true 时,刷新当前页面依赖的全部社交 投影,不尝试解析未知数字值。

XHIM 客户端 SDK 与服务端文档