消息回执
本文档适用于开发以下平台应用:iOS、Android、macOS、Windows。
功能简介
消息已读回执,是指用户在会话中发送一条消息后,得知其他用户已读或未读此消息。本功能可用于在企业办公等需要实时知晓消息是否已经被阅读的场景。

本文档介绍了如何使用 ZIM SDK 的接口,实现发送一条附带回执的消息,以及查看和应答回执详情等功能。
ZIM SDK 目前支持发送“单聊”会话和“群组”会话的消息回执(仅支持普通消息和富媒体消息),暂不支持发送“房间”会话的消息回执。
实现流程
消息发送端通过 ZIM SDK 发送一条消息,并通过设置 ZIMMessageSendConfig 的 hasReceipt 字段标记该消息是否需要带回执,接收端可根据消息的回执状态 receiptStatus 判断该消息当前是否带回执,或者回执处于正在进行中还是已完成,从而渲染不同的 UI 效果,而接收端可根据不同的场景进行不同的已读方式。

发送一条附带回执的消息
如果客户端 A 想要向客户端 B 发送一条附带回执的消息:
- 客户端 A 和 客户端 B 登录 ZIM SDK;
 
- 客户端 A 调用 sendMessage 或 sendMediaMessage 接口,向客户端 B 发送一条消息(仅支持“单聊”会话和“群组”会话的 ZIMTextMessage、 ZIMImageMessage、 ZIMFileMessage、 ZIMAudioMessage、 ZIMVideoMessage、 ZIMCombineMessage 和 ZIMMultipleMessage),并设置 ZIMMessageSendConfig 的 
hasReceipt字段为 true; 
- 客户端 A 调用 sendMessage 或 sendMediaMessage 接口,向客户端 B 发送一条消息(仅支持“单聊”会话和“群组”会话的 ZIMTextMessage、 ZIMImageMessage、 ZIMFileMessage、 ZIMAudioMessage 和 ZIMVideoMessage),并设置 ZIMMessageSendConfig 的 
hasReceipt字段为 true; 
- 客户端 B 通过监听相关回调( onPeerMessageReceived 或 onGroupMessageReceived )会收到一条 
receiptStatus为PROCESSING的消息。 
接收端对消息回执进行已读操作
已读操作分为消息已读和会话已读。
消息已读
消息已读,是指接收端收到对方发送的附带回执的消息,可对该消息设置已读,已读成功,发送方将会收到该消息已被读的通知。
- 消息可为单条消息或批量消息,但是消息发送端与接收端必须在同一会话内,不允许跨会话。
 - 如需对会话的历史消息进行相关操作,需先完成查询历史消息,对历史消息的回执状态进行判断,详情请参考 查询历史消息。
 
- 客户端 B 通过相关回调( onPeerMessageReceived 或 onGroupMessageReceived )收到客户端 A 发送的一条附带回执的消息;
 - 客户端 B 根据回调的 
receiptStatus字段判断该消息的回执状态。如果是该字段为PROCESSING,表示该消息处于“回执中”,开发者可根据自己的业务逻辑,调用 sendMessageReceiptsRead 接口将该消息设置为已读。 - 客户端 B 通过 ZIMMessageReceiptsReadSentCallback 得知设置是否成功。
 - 客户端 A 通过 ZIMEventHandler 的 onMessageReceiptChanged 收到该消息被设置为消息已读的回调通知。开发者可根据这个回调,在客户端 A 实现将此消息设置为已读的业务逻辑。
 
会话已读
会话已读,是指接收端将指定会话内所有已接收的对方消息都设置为已读。
- 目前 ZIM SDK 只允许在单聊会话实现此功能;
 - 此功能只作用于设置已读之前获取的消息,不对设置之后的消息生效。
 - 此功能建议在用户从会话列表页进入到会话时使用,不推荐在同一会话中与 sendMessageReceiptsRead 接口混用。
 - 如需对会话的历史消息进行相关操作,需先完成查询历史消息,对历史消息的回执状态进行判断,详情请参考 查询历史消息。
 
- 客户端 B 根据回调 onPeerMessageReceived 的 
receiptStatus字段判断该消息的回执状态。如果是该字段为PROCESSING,则表示该消息处于回执中,开发者可根据自己的业务逻辑,调用 sendConversationMessageReceiptRead 接口将会话内客户端 A 已发送的所有消息都设置为已读。 - 客户端 B 通过 ZIMConversationMessageReceiptReadSentResult 得知设置是否成功。
 - 客户端 A 通过 ZIMEventHandler 的 onConversationMessageReceiptChanged 收到该消息被设置为会话已读的回调通知,开发者可根据这个回调,实现该会话所有对方发的消息都设置为已读的逻辑。开发者可根据这个回调,在客户端 A 实现自己发的所有消息都被客户端 B 设置为已读的业务逻辑。
 
更多功能
获取回执已读时间
通过 ZIMMessageReceiptInfo.readTime 可获取消息的回执已读时间,实现阅后即焚等密聊业务场景。
支持的接口:queryMessageReceiptsInfo、onMessageReceiptChanged。
- 对于消息发送者:当该消息被会话内成员全部已读时,已读时间才会有值,且为最后一个成员已读时的服务端时间戳,否则为 0。
 - 对于消息接收者:已读时间为调用 sendMessageReceiptsRead 成功时的服务端时间戳,否则为 0。
 - 获取回执已读时间暂不支持“会话已读”。
 - 2.22.0 版本开始支持。
 
批量查询消息的回执状态、已读用户数和未读用户数
当需要查询一条或一批消息的回执状态、已读用户数和未读未读用户数时,可以调用 queryMessageReceiptsInfo 接口查询,通过 ZIMMessageReceiptsInfoQueriedResult 获取相关信息。
- 如果是查询其他用户发送的信息,得到的已读用户数和未读用户数都是 0。
 - 如需对会话的历史消息进行相关操作,需先完成查询历史消息,对历史消息的回执状态进行判断,详情请参考 查询历史消息。
 
查询群组里自己发送的消息的已读和未读成员列表
ZIM SDK 支持查询群组里自己发送的消息的已读成员列表和未读成员列表。
查询已读成员列表
当需要查询有哪些成员读了自己发送的消息,可调用 queryGroupMessageReceiptReadMemberList 接口查询具体成员列表。
如需对会话的历史消息进行相关操作,需先完成查询历史消息,对历史消息的回执状态进行判断,详情请参考 查询历史消息。
查询未读成员列表
当需要查询还有哪些成员未读自己发送的消息,可调用 queryGroupMessageReceiptUnreadMemberList 接口查询具体成员列表。
- 若 SDK 版本低于 2.16.0,当群成员人数大于 100 时,此接口不会返回具体未读成员列表。如需使用此功能,可联系 ZEGO 技术支持。
 - 如需对会话的历史消息进行相关操作,需先完成查询历史消息,对历史消息的回执状态进行判断,详情请参考 查询历史消息。
 
zim.setEventHandler(new ZIMEventHandler() {
    @Override
    public void onMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
        // 对方设置了消息的已读回执
    }
    @Override
    public void onConversationMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
        // 对方设置会话的已读回执
    }
})
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
String conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = new ZIMTextMessage("test");
ZIMMessageSendConfig sendConfig = new ZIMMessageSendConfig();
sendConfig.hasReceipt = true;    // 设置消息带回执
zim.sendMessage(message, conversationID, ZIMConversationType.PEER,sendConfig, new ZIMMessageSentCallback() {
            @Override
            public void onMessageAttached(ZIMMessage message) {}
            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
                }
            }
        });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
List<ZIMMessage> messages = new ArrayList<>();
messages.add(message);
zim.sendMessageReceiptsRead(messages, conversationID, ZIMConversationType.PEER, 
        new ZIMMessageReceiptsReadSentCallback() {
            @Override
            public void onMessageReceiptsReadSent(String conversationID, ZIMConversationType conversationType, 
ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
             if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                // 消息已读的回调
             }
            }
        });
// 会话已读
zim.sendConversationMessageReceiptRead(conversationID, ZIMConversationType.PEER, 
            new ZIMConversationMessageReceiptReadSentCallback() {
            @Override
            public void onConversationMessageReceiptReadSent(String conversationID, 
                ZIMConversationType conversationType, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 会话已读成功,开发者可通过监听这个回调,把这个会话内对方发的消息都设置为已读的标志。
                }
            }
        });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> messages = new ArrayList<>();
messages.add(message);
zim.queryMessageReceiptsInfo(messages, conversationID, ZIMConversationType.PEER, new ZIMMessageReceiptsInfoQueriedCallback() {
            @Override
            public void onMessageReceiptsInfoQueried(ArrayList<ZIMMessageReceiptInfo> infos, 
                ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 查询到这一批消息的状态和数量,遍历 infos 获取对应的消息 ID 和 count
                }
            }
        });
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
zim.queryGroupMessageReceiptReadMemberList(message, groupID, config, 
    new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList,
             int nextFlag, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                   // 查询到对应的成员列表
                }
            }
        });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
zim.queryGroupMessageReceiptUnreadMemberList(message, groupID, config, 
    new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList,
             int nextFlag, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                   // 查询到对应的成员列表
                }
            }
        });zim.setEventHandler(new ZIMEventHandler() {
    @Override
    public void onMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
        // 对方设置了消息的已读回执
    }
    @Override
    public void onConversationMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
        // 对方设置会话的已读回执
    }
})
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
String conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = new ZIMTextMessage("test");
ZIMMessageSendConfig sendConfig = new ZIMMessageSendConfig();
sendConfig.hasReceipt = true;    // 设置消息带回执
zim.sendMessage(message, conversationID, ZIMConversationType.PEER,sendConfig, new ZIMMessageSentCallback() {
            @Override
            public void onMessageAttached(ZIMMessage message) {}
            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
                }
            }
        });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
List<ZIMMessage> messages = new ArrayList<>();
messages.add(message);
zim.sendMessageReceiptsRead(messages, conversationID, ZIMConversationType.PEER, 
        new ZIMMessageReceiptsReadSentCallback() {
            @Override
            public void onMessageReceiptsReadSent(String conversationID, ZIMConversationType conversationType, 
ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
             if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                // 消息已读的回调
             }
            }
        });
// 会话已读
zim.sendConversationMessageReceiptRead(conversationID, ZIMConversationType.PEER, 
            new ZIMConversationMessageReceiptReadSentCallback() {
            @Override
            public void onConversationMessageReceiptReadSent(String conversationID, 
                ZIMConversationType conversationType, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 会话已读成功,开发者可通过监听这个回调,把这个会话内对方发的消息都设置为已读的标志。
                }
            }
        });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> messages = new ArrayList<>();
messages.add(message);
zim.queryMessageReceiptsInfo(messages, conversationID, ZIMConversationType.PEER, new ZIMMessageReceiptsInfoQueriedCallback() {
            @Override
            public void onMessageReceiptsInfoQueried(ArrayList<ZIMMessageReceiptInfo> infos, 
                ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    // 查询到这一批消息的状态和数量,遍历 infos 获取对应的消息 ID 和 count
                }
            }
        });
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
zim.queryGroupMessageReceiptReadMemberList(message, groupID, config, 
    new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList,
             int nextFlag, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                   // 查询到对应的成员列表
                }
            }
        });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
zim.queryGroupMessageReceiptUnreadMemberList(message, groupID, config, 
    new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList,
             int nextFlag, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                   // 查询到对应的成员列表
                }
            }
        });- (void)zim:(ZIM *)zim messageReceiptChanged:(NSArray<ZIMMessageReceiptInfo *> *)infos{
     // 对方设置了消息的已读回执
}
- (void)zim:(ZIM *)zim conversationMessageReceiptChanged:(NSArray<ZIMMessageReceiptInfo *> *)infos{
    // 对方设置会话的已读回执
}
NSString *conversationID = @"xxx" ; // 会话 ID
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
ZIMTextMessage *message = [[ZIMTextMessage alloc] init];
ZIMMessageSendConfig *sendConfig = [[ZIMMessageSendConfig alloc]init];
sendConfig.hasReceipt = true;    // 设置消息带回执
[self.zim sendMessage:cmdMsg toUserID:toUserID conversationType:type config:config notification:notification callback:^((ZIMMessage * _Nonnull message, ZIMError * _Nonnull errorInfo)) {
    // 开发者可以通过该回调监听消息是否发送成功。
    if (errorInfo.code == 0) {
        // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
    }
}];
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
NSMutableArray<ZIMMessage *> *messageList = [[NSMutableArray alloc] init];
[[ZIM getInstance] sendMessageReceiptsRead:messageList conversationID:@"conversationID" conversationType:conversationType callback:^(NSString * _Nonnull conversationID, ZIMConversationType conversationType, NSArray<NSNumber *> * _Nonnull errorMessageIDs, ZIMError * _Nonnull errorInfo) {
     // 设置消息已读的回调
}];
// 会话已读
[[ZIM getInstance] sendConversationMessageReceiptRead:@"conversationID" conversationType:conversationType callback:^(NSString * _Nonnull conversationID, ZIMConversationType conversationType, ZIMError * _Nonnull errorInfo) {
    // 设置会话已读的回调
}];
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
NSMutableArray<ZIMMessage *> *messageList = [[NSMutableArray alloc] init];
[[ZIM getInstance] queryMessageReceiptsInfoByMessageList:messageList conversationID:conversationID conversationType:conversationType callback:^(NSArray<ZIMMessageReceiptInfo *> * _Nonnull infos, NSArray<NSNumber *> * _Nonnull errorMessageIDs, ZIMError * _Nonnull errorInfo) {
     // 查询到这一批消息的状态和数量,遍历 infos 获取对应    的消息 ID 和 count
}];
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig *config =  [[ZIMGroupMessageReceiptMemberQueryConfig alloc]init];
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
[[ZIM getInstance] queryGroupMessageReceiptReadMemberListByMessage:message groupID:groupID config:config callback:^(NSString * _Nonnull groupID, NSArray<ZIMGroupMemberInfo *> * _Nonnull userList, unsigned int nextFlag, ZIMError * _Nonnull errorInfo)    {
    // 查询到对应的成员列表
}];
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
[[ZIM getInstance] queryGroupMessageReceiptUnreadMemberListByMessage:message groupID:groupID config:config callback:^(NSString * _Nonnull groupID, NSArray<ZIMGroupMemberInfo *> * _Nonnull userList, unsigned int nextFlag, ZIMError * _Nonnull errorInfo) {
    // 查询到对应的成员列表
}];- (void)zim:(ZIM *)zim messageReceiptChanged:(NSArray<ZIMMessageReceiptInfo *> *)infos{
     // 对方设置了消息的已读回执
}
- (void)zim:(ZIM *)zim conversationMessageReceiptChanged:(NSArray<ZIMMessageReceiptInfo *> *)infos{
    // 对方设置会话的已读回执
}
NSString *conversationID = @"xxx" ; // 会话 ID
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
ZIMTextMessage *message = [[ZIMTextMessage alloc] init];
ZIMMessageSendConfig *sendConfig = [[ZIMMessageSendConfig alloc]init];
sendConfig.hasReceipt = true;    // 设置消息带回执
[self.zim sendMessage:cmdMsg toUserID:toUserID conversationType:type config:config notification:notification callback:^((ZIMMessage * _Nonnull message, ZIMError * _Nonnull errorInfo)) {
    // 开发者可以通过该回调监听消息是否发送成功。
    if (errorInfo.code == 0) {
        // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
    }
}];
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
NSMutableArray<ZIMMessage *> *messageList = [[NSMutableArray alloc] init];
[[ZIM getInstance] sendMessageReceiptsRead:messageList conversationID:@"conversationID" conversationType:conversationType callback:^(NSString * _Nonnull conversationID, ZIMConversationType conversationType, NSArray<NSNumber *> * _Nonnull errorMessageIDs, ZIMError * _Nonnull errorInfo) {
     // 设置消息已读的回调
}];
// 会话已读
[[ZIM getInstance] sendConversationMessageReceiptRead:@"conversationID" conversationType:conversationType callback:^(NSString * _Nonnull conversationID, ZIMConversationType conversationType, ZIMError * _Nonnull errorInfo) {
    // 设置会话已读的回调
}];
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
NSMutableArray<ZIMMessage *> *messageList = [[NSMutableArray alloc] init];
[[ZIM getInstance] queryMessageReceiptsInfoByMessageList:messageList conversationID:conversationID conversationType:conversationType callback:^(NSArray<ZIMMessageReceiptInfo *> * _Nonnull infos, NSArray<NSNumber *> * _Nonnull errorMessageIDs, ZIMError * _Nonnull errorInfo) {
     // 查询到这一批消息的状态和数量,遍历 infos 获取对应    的消息 ID 和 count
}];
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig *config =  [[ZIMGroupMessageReceiptMemberQueryConfig alloc]init];
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
[[ZIM getInstance] queryGroupMessageReceiptReadMemberListByMessage:message groupID:groupID config:config callback:^(NSString * _Nonnull groupID, NSArray<ZIMGroupMemberInfo *> * _Nonnull userList, unsigned int nextFlag, ZIMError * _Nonnull errorInfo)    {
    // 查询到对应的成员列表
}];
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
[[ZIM getInstance] queryGroupMessageReceiptUnreadMemberListByMessage:message groupID:groupID config:config callback:^(NSString * _Nonnull groupID, NSArray<ZIMGroupMemberInfo *> * _Nonnull userList, unsigned int nextFlag, ZIMError * _Nonnull errorInfo) {
    // 查询到对应的成员列表
}];// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
zim::ZIMMessageSendConfig sendConfig;
zim::ZIMPushConfig pushConfig;
pushConfig.content = "win_push_content";
pushConfig.payload = "win_push_extended_data";
pushConfig.title = "win_push_title";
sendConfig.priority = zim::ZIM_MESSAGE_PRIORITY_MEDIUM;
sendConfig.pushConfig = &pushConfig;
sendConfig.hasReceipt = true; // 设置消息带回执
auto smessage = std::make_shared<zim::ZIMTextMessage>("test message");
auto notification = std::make_shared<zim::ZIMMessageSendNotification>(
    [=](const std::shared_ptr<zim::ZIMMessage> &message) { 
        // 消息已入库的通知
    });
zim_->sendMessage(
    std::static_pointer_cast<zim::ZIMMessage>(smessage), userID,
    zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER, sendConfig, notification,
    [=](const std::shared_ptr<zim::ZIMMessage> &cb_message, const zim::ZIMError &errorInfo) {
        if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
            // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
        }
    });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
std::vector<std::shared_ptr<zim::ZIMMessage>> messages;
messages.emplace_back(message);
zim_->sendMessageReceiptsRead(
    messages, conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::string &conversationID, zim::ZIMConversationType conversationType,
        const std::vector<long long> &errorMessageIDs,
        const zim::ZIMError &errorInfo) { 
        // 消息已读的回调
    });
// 会话已读
zim_->sendConversationMessageReceiptRead(
    conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::string &conversationID, zim::ZIMConversationType conversationType,
        const zim::ZIMError &errorInfo) { 
            // 会话已读的回调,开发者可通过监听这个回调返回的错误码,把这个会话内对方发的消息都设置为已读的标志。
    });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
std::vector<std::shared_ptr<zim::ZIMMessage>> messages;
messages.emplace_back(message);
zim_->queryMessageReceiptsInfo(
    messages, conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::vector<zim::ZIMMessageReceiptInfo> &infos,
        std::vector<long long> errorMessageIDs, const zim::ZIMError &errorInfo) {});
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
zim::ZIMGroupMessageReceiptMemberQueryConfig readMemberQueryConfig;
readMemberQueryConfig.count = 10;    // 需要查询的用户数量。
readMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
zim_->queryGroupMessageReceiptReadMemberList(
    message, "group_id", readMemberQueryConfig,
    [=](const std::string &groupID, const std::vector<zim::ZIMGroupMemberInfo> &userList,
        unsigned int nextFlag, const zim::ZIMError &errorInfo) {
                if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
                   // 查询到对应的成员列表
                }
        });
// 未读用户列表
zim::ZIMGroupMessageReceiptMemberQueryConfig unreadMemberQueryConfig;
unreadMemberQueryConfig.count = 10;    // 需要查询的用户数量。
unreadMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
zim_->queryGroupMessageReceiptUnreadMemberList(
    message, "group_id", unreadMemberQueryConfig,
    [=](const std::string &groupID, const std::vector<zim::ZIMGroupMemberInfo> &userList,
        unsigned int nextFlag, const zim::ZIMError &errorInfo) {
                if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
                   // 查询到对应的成员列表
                }
        });// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
zim::ZIMMessageSendConfig sendConfig;
zim::ZIMPushConfig pushConfig;
pushConfig.content = "win_push_content";
pushConfig.payload = "win_push_extended_data";
pushConfig.title = "win_push_title";
sendConfig.priority = zim::ZIM_MESSAGE_PRIORITY_MEDIUM;
sendConfig.pushConfig = &pushConfig;
sendConfig.hasReceipt = true; // 设置消息带回执
auto smessage = std::make_shared<zim::ZIMTextMessage>("test message");
auto notification = std::make_shared<zim::ZIMMessageSendNotification>(
    [=](const std::shared_ptr<zim::ZIMMessage> &message) { 
        // 消息已入库的通知
    });
zim_->sendMessage(
    std::static_pointer_cast<zim::ZIMMessage>(smessage), userID,
    zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER, sendConfig, notification,
    [=](const std::shared_ptr<zim::ZIMMessage> &cb_message, const zim::ZIMError &errorInfo) {
        if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
            // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
        }
    });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
std::vector<std::shared_ptr<zim::ZIMMessage>> messages;
messages.emplace_back(message);
zim_->sendMessageReceiptsRead(
    messages, conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::string &conversationID, zim::ZIMConversationType conversationType,
        const std::vector<long long> &errorMessageIDs,
        const zim::ZIMError &errorInfo) { 
        // 消息已读的回调
    });
// 会话已读
zim_->sendConversationMessageReceiptRead(
    conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::string &conversationID, zim::ZIMConversationType conversationType,
        const zim::ZIMError &errorInfo) { 
            // 会话已读的回调,开发者可通过监听这个回调返回的错误码,把这个会话内对方发的消息都设置为已读的标志。
    });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
std::vector<std::shared_ptr<zim::ZIMMessage>> messages;
messages.emplace_back(message);
zim_->queryMessageReceiptsInfo(
    messages, conversationID, zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER,
    [=](const std::vector<zim::ZIMMessageReceiptInfo> &infos,
        std::vector<long long> errorMessageIDs, const zim::ZIMError &errorInfo) {});
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
zim::ZIMGroupMessageReceiptMemberQueryConfig readMemberQueryConfig;
readMemberQueryConfig.count = 10;    // 需要查询的用户数量。
readMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
zim_->queryGroupMessageReceiptReadMemberList(
    message, "group_id", readMemberQueryConfig,
    [=](const std::string &groupID, const std::vector<zim::ZIMGroupMemberInfo> &userList,
        unsigned int nextFlag, const zim::ZIMError &errorInfo) {
                if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
                   // 查询到对应的成员列表
                }
        });
// 未读用户列表
zim::ZIMGroupMessageReceiptMemberQueryConfig unreadMemberQueryConfig;
unreadMemberQueryConfig.count = 10;    // 需要查询的用户数量。
unreadMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
zim_->queryGroupMessageReceiptUnreadMemberList(
    message, "group_id", unreadMemberQueryConfig,
    [=](const std::string &groupID, const std::vector<zim::ZIMGroupMemberInfo> &userList,
        unsigned int nextFlag, const zim::ZIMError &errorInfo) {
                if (errorInfo.code == zim::ZIMErrorCode::ZIM_ERROR_CODE_SUCCESS) {
                   // 查询到对应的成员列表
                }
        });ZIMEventHandler.onMessageReceiptChanged = (zim, infos) {
    // 对方设置了消息的已读回执
};
ZIMEventHandler.onConversationMessageReceiptChanged = (zim, infos) {
    // 对方设置会话的已读回执
};
//用户 A 发送一条消息,并带上回执,以单聊文本消息为例
String conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = ZIMTextMessage(message: "test");
ZIMMessageSendConfig sendConfig = ZIMMessageSendConfig();
sendConfig.hasReceipt = true; //设置消息带回执
ZIM
    .getInstance()!
    .sendMessage(
        message, conversationID, ZIMConversationType.peer, sendConfig)
    .then((result) {
    // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
    })
    .catchError((onError) {
    });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
  List<ZIMMessage> messages = [];
  ZIM
      .getInstance()!
      .sendMessageReceiptsRead(
          messages, conversationID, ZIMConversationType.peer)
      .then((result) {
        // 消息已读的回调
      })
      .catchError((onError) {
      });
// 会话已读
  ZIM
      .getInstance()!
      .sendConversationMessageReceiptRead(
          conversationID, ZIMConversationType.peer)
      .then((value) {
        // 会话已读成功,开发者可通过监听这个回调,把这个会话内对方发的消息都设置为已读的标志。
      })
      .catchError((onError) {
      });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> messages = [];
  ZIM
      .getInstance()!
      .queryMessageReceiptsInfo(
          messages, conversationID, ZIMConversationType.peer)
      .then((value) {
        // 查询到这一批消息的状态和数量,遍历 infos 获取对应的消息 ID 和 count
      })
      .catchError((onError) {});
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config =
      ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
  ZIM
      .getInstance()!
      .queryGroupMessageReceiptReadMemberList(message, groupID, config)
      .then((result) {
          // 查询到对应的成员列表
      })
      .catchError((onError) {
      });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
  ZIM
      .getInstance()!
      .queryGroupMessageReceiptUnreadMemberList(message, groupID, config)
      .then((result) {
          // 查询到对应的成员列表
      })
      .catchError((onError) {
      });ZIMEventHandler.onMessageReceiptChanged = (zim, infos) {
    // 对方设置了消息的已读回执
};
ZIMEventHandler.onConversationMessageReceiptChanged = (zim, infos) {
    // 对方设置会话的已读回执
};
//用户 A 发送一条消息,并带上回执,以单聊文本消息为例
String conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = ZIMTextMessage(message: "test");
ZIMMessageSendConfig sendConfig = ZIMMessageSendConfig();
sendConfig.hasReceipt = true; //设置消息带回执
ZIM
    .getInstance()!
    .sendMessage(
        message, conversationID, ZIMConversationType.peer, sendConfig)
    .then((result) {
    // 这里表示发送消息成功,message 的 receiptStatus 会为 PROCESSING,业务层可根据这个标志实现展示回执中(消息未读)的逻辑。
    })
    .catchError((onError) {
    });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
  List<ZIMMessage> messages = [];
  ZIM
      .getInstance()!
      .sendMessageReceiptsRead(
          messages, conversationID, ZIMConversationType.peer)
      .then((result) {
        // 消息已读的回调
      })
      .catchError((onError) {
      });
// 会话已读
  ZIM
      .getInstance()!
      .sendConversationMessageReceiptRead(
          conversationID, ZIMConversationType.peer)
      .then((value) {
        // 会话已读成功,开发者可通过监听这个回调,把这个会话内对方发的消息都设置为已读的标志。
      })
      .catchError((onError) {
      });
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> messages = [];
  ZIM
      .getInstance()!
      .queryMessageReceiptsInfo(
          messages, conversationID, ZIMConversationType.peer)
      .then((value) {
        // 查询到这一批消息的状态和数量,遍历 infos 获取对应的消息 ID 和 count
      })
      .catchError((onError) {});
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config =
      ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
  ZIM
      .getInstance()!
      .queryGroupMessageReceiptReadMemberList(message, groupID, config)
      .then((result) {
          // 查询到对应的成员列表
      })
      .catchError((onError) {
      });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
  ZIM
      .getInstance()!
      .queryGroupMessageReceiptUnreadMemberList(message, groupID, config)
      .then((result) {
          // 查询到对应的成员列表
      })
      .catchError((onError) {
      });ZIM.GetInstance().onMessageReceiptChanged = (ZIM zim,
                                         List<ZIMMessageReceiptInfo> infos) =>
{
    // 消息的回执状态变化
};
ZIM.GetInstance().onConversationMessageReceiptChanged = (ZIM zim, List<ZIMMessageReceiptInfo> infos) =>
{
    // 单聊会话的回执状态变化
};
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
string conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = new ZIMTextMessage("test");
ZIMMessageSendConfig sendConfig = new ZIMMessageSendConfig();
sendConfig.hasReceipt = true;    // 设置消息带回执
ZIMMessageSendNotification notification = new ZIMMessageSendNotification();
ZIM.GetInstance().SendMessage(message, "conversationID", ZIMConversationType.Peer, sendConfig, notification,
    (ZIMMessage message, ZIMError errorInfo) => { });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
List<ZIMMessage> messages = new List<ZIMMessage>();
messages.Add(message);
ZIM.GetInstance().SendMessageReceiptsRead(messages, "conversationID", ZIMConversationType.Peer, (string conversationID, ZIMConversationType conversationType,
                   List<long> errorMessageIDs, ZIMError errorInfo) =>
{
    //发送消息已读的回调
});
// 会话已读
ZIM.GetInstance().SendConversationMessageReceiptRead("conversationID", ZIMConversationType.Peer, (string conversationID, ZIMConversationType conversationType,
                           ZIMError errorInfo) =>
{
    //发送会话消息已读的回调
});
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> queryMessages = new List<ZIMMessage>();
queryMessages.Add(message);
ZIM.GetInstance().QueryMessageReceiptsInfo(queryMessages, "conversationID", ZIMConversationType.Peer, (List<ZIMMessageReceiptInfo> infos, List<long> errorMessageIDs,
                           ZIMError errorInfo) =>
{ });
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
ZIM.GetInstance().QueryGroupMessageReceiptReadMemberList(message, "groupID", config, (string groupID, List<ZIMGroupMemberInfo> userList,
                           uint nextFlag, ZIMError errorInfo) =>
{ });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig groupMessageReceiptMemberQueryConfig = new ZIMGroupMessageReceiptMemberQueryConfig();
groupMessageReceiptMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
groupMessageReceiptMemberQueryConfig.count = 10;    // 需要查询的用户数量。
ZIM.GetInstance().QueryGroupMessageReceiptUnreadMemberList(message, "groupID", groupMessageReceiptMemberQueryConfig, (string groupID, List<ZIMGroupMemberInfo> userList,
                           uint nextFlag, ZIMError errorInfo) => { });ZIM.GetInstance().onMessageReceiptChanged = (ZIM zim,
                                         List<ZIMMessageReceiptInfo> infos) =>
{
    // 消息的回执状态变化
};
ZIM.GetInstance().onConversationMessageReceiptChanged = (ZIM zim, List<ZIMMessageReceiptInfo> infos) =>
{
    // 单聊会话的回执状态变化
};
// 用户 A 发送一条消息,并带上回执,以单聊文本消息为例
string conversationID = "xxx" ; // 会话 ID
ZIMTextMessage message = new ZIMTextMessage("test");
ZIMMessageSendConfig sendConfig = new ZIMMessageSendConfig();
sendConfig.hasReceipt = true;    // 设置消息带回执
ZIMMessageSendNotification notification = new ZIMMessageSendNotification();
ZIM.GetInstance().SendMessage(message, "conversationID", ZIMConversationType.Peer, sendConfig, notification,
    (ZIMMessage message, ZIMError errorInfo) => { });
// 用户 B 接收到回执,并做已读操作,选择以下任一接口即可
// 消息已读
List<ZIMMessage> messages = new List<ZIMMessage>();
messages.Add(message);
ZIM.GetInstance().SendMessageReceiptsRead(messages, "conversationID", ZIMConversationType.Peer, (string conversationID, ZIMConversationType conversationType,
                   List<long> errorMessageIDs, ZIMError errorInfo) =>
{
    //发送消息已读的回调
});
// 会话已读
ZIM.GetInstance().SendConversationMessageReceiptRead("conversationID", ZIMConversationType.Peer, (string conversationID, ZIMConversationType conversationType,
                           ZIMError errorInfo) =>
{
    //发送会话消息已读的回调
});
// (可选)查询一批消息的回执状态、未读用户数和已读用户数
List<ZIMMessage> queryMessages = new List<ZIMMessage>();
queryMessages.Add(message);
ZIM.GetInstance().QueryMessageReceiptsInfo(queryMessages, "conversationID", ZIMConversationType.Peer, (List<ZIMMessageReceiptInfo> infos, List<long> errorMessageIDs,
                           ZIMError errorInfo) =>
{ });
// (可选)查询某一条群消息的已读群成员列表和未读群成员列表
// 已读用户列表
ZIMGroupMessageReceiptMemberQueryConfig config = new ZIMGroupMessageReceiptMemberQueryConfig();
config.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
config.count = 10;    // 需要查询的用户数量。
ZIM.GetInstance().QueryGroupMessageReceiptReadMemberList(message, "groupID", config, (string groupID, List<ZIMGroupMemberInfo> userList,
                           uint nextFlag, ZIMError errorInfo) =>
{ });
// 未读用户列表
ZIMGroupMessageReceiptMemberQueryConfig groupMessageReceiptMemberQueryConfig = new ZIMGroupMessageReceiptMemberQueryConfig();
groupMessageReceiptMemberQueryConfig.nextFlag = 0;    // 查询的 flag ,初始时填 0,后续填从 callback 里返回的 flag。
groupMessageReceiptMemberQueryConfig.count = 10;    // 需要查询的用户数量。
ZIM.GetInstance().QueryGroupMessageReceiptUnreadMemberList(message, "groupID", groupMessageReceiptMemberQueryConfig, (string groupID, List<ZIMGroupMemberInfo> userList,
                           uint nextFlag, ZIMError errorInfo) => { });// 1、注册回调
// 对方设置了消息的已读回执
zim.on('messageReceiptChanged', (zim: ZIM, data: ZIMEventOfMessageReceiptChangedResult) => {
    console.log('messageReceiptChanged', data);
});
// 对方设置了会话的已读回执
zim.on('conversationMessageReceiptChanged', (zim: ZIM, data: ZIMEventOfMessageReceiptChangedResult) => {
    console.log('conversationMessageReceiptChanged', data);
});// 1、注册回调
// 对方设置了消息的已读回执
zim.on('messageReceiptChanged', (zim: ZIM, data: ZIMEventOfMessageReceiptChangedResult) => {
    console.log('messageReceiptChanged', data);
});
// 对方设置了会话的已读回执
zim.on('conversationMessageReceiptChanged', (zim: ZIM, data: ZIMEventOfMessageReceiptChangedResult) => {
    console.log('conversationMessageReceiptChanged', data);
});// 1、注册回调
// 对方设置了消息的已读回执
zim.onMessageReceiptChanged((data) => {
    console.log('messageReceiptChanged', data);
});
// 对方设置了会话的已读回执
zim.onConversationMessageReceiptChanged((data) => {
    console.log('conversationMessageReceiptChanged', data);
});// 1、注册回调
// 对方设置了消息的已读回执
zim.onMessageReceiptChanged((data) => {
    console.log('messageReceiptChanged', data);
});
// 对方设置了会话的已读回执
zim.onConversationMessageReceiptChanged((data) => {
    console.log('conversationMessageReceiptChanged', data);
});// 2、用户 A 发送一条消息给用户 B,并带上回执,以单聊文本消息为例
const userID_A = "xxxx" ;    // 用户 A 的 ID
const userID_B = "xxxx" ;    // 用户 B 的 ID
const messageObj: ZIMMessage = { type: 1, message: '文本回执消息' }
const config: ZIMMessageSendConfig = {
    priority: 1,    // 消息优先级,取值为 低:1 默认, 中:2, 高:3
    hasReceipt: true    // 设置消息带回执
}
const notification: ZIMMessageSendNotification = {
    onMessageAttached: (message: ZIMMessage) => {
        // todo: Loading
    }
}
zim.sendMessage(messageObj, userID_B, 0, config, notification)
    .then((res: ZIMMessageSentResult) => {
        // 发送成功
    })
    .catch((err: ZIMError) => {
        // 发送失败
    });
// 3、用户 B 接收到带回执的消息,并做已读操作,选择以下任一接口即可
// 3.1 消息已读
const messages: ZIMMessage[] = [];    // 从 queryHistoryMessage 查询,或者从 peerMessageReceived 接收
zim.sendMessageReceiptsRead(messages, userID_A, 0)    
    .then((res: ZIMMessageReceiptsReadSentResult) => {
        // 操作成功,设置已读失败的消息通过 res.errorMessageIDs 返回
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 3.2、会话已读
zim.sendConversationMessageReceiptRead(userID_A, 0)
    .then((res: ZIMConversationMessageReceiptReadSentResult) => {
        // 操作成功,用户 B 可把这个会话内用户 A 发的消息都标志为已读
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 4、(可选)用户 A 查询一批消息的回执状态、未读用户数和已读用户数
const messages: ZIMMessage[] = []; // 从 queryHistoryMessage 查询
zim.queryMessageReceiptsInfo(messages, userID_B, 0)    
    .then((res: ZIMMessageReceiptsInfoQueriedResult) => {
        // 操作成功,查询失败的消息通过 res.errorMessageIDs 返回
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 5、(可选)查询某一条群消息的已读群成员列表和未读群成员列表
const groupMsgObj: ZIMMessage = {}    // 从 queryHistoryMessage 查询
const queryConfig: ZIMGroupMessageReceiptMemberQueryConfig = {
    count: 10,    // 需要查询的用户数量
    nextFlag: 0    // 查询的 flag,初始时填 0,后续填从 Promise 里返回的 nextFlag
}
// 5.1 群已读用户列表
zim.queryGroupMessageReceiptReadMemberList(groupMsgObj, groupMsgObj.conversationID, queryConfig)
    .then((res: ZIMGroupMessageReceiptMemberListQueriedResult) => {
        // 操作成功
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 5.2 群未读用户列表
zim.queryGroupMessageReceiptUnreadMemberList(groupMsgObj, groupMsgObj.conversationID, queryConfig)
    .then((res: ZIMGroupMessageReceiptMemberListQueriedResult) => {
        // 操作成功
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });// 2、用户 A 发送一条消息给用户 B,并带上回执,以单聊文本消息为例
const userID_A = "xxxx" ;    // 用户 A 的 ID
const userID_B = "xxxx" ;    // 用户 B 的 ID
const messageObj: ZIMMessage = { type: 1, message: '文本回执消息' }
const config: ZIMMessageSendConfig = {
    priority: 1,    // 消息优先级,取值为 低:1 默认, 中:2, 高:3
    hasReceipt: true    // 设置消息带回执
}
const notification: ZIMMessageSendNotification = {
    onMessageAttached: (message: ZIMMessage) => {
        // todo: Loading
    }
}
zim.sendMessage(messageObj, userID_B, 0, config, notification)
    .then((res: ZIMMessageSentResult) => {
        // 发送成功
    })
    .catch((err: ZIMError) => {
        // 发送失败
    });
// 3、用户 B 接收到带回执的消息,并做已读操作,选择以下任一接口即可
// 3.1 消息已读
const messages: ZIMMessage[] = [];    // 从 queryHistoryMessage 查询,或者从 peerMessageReceived 接收
zim.sendMessageReceiptsRead(messages, userID_A, 0)    
    .then((res: ZIMMessageReceiptsReadSentResult) => {
        // 操作成功,设置已读失败的消息通过 res.errorMessageIDs 返回
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 3.2、会话已读
zim.sendConversationMessageReceiptRead(userID_A, 0)
    .then((res: ZIMConversationMessageReceiptReadSentResult) => {
        // 操作成功,用户 B 可把这个会话内用户 A 发的消息都标志为已读
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 4、(可选)用户 A 查询一批消息的回执状态、未读用户数和已读用户数
const messages: ZIMMessage[] = []; // 从 queryHistoryMessage 查询
zim.queryMessageReceiptsInfo(messages, userID_B, 0)    
    .then((res: ZIMMessageReceiptsInfoQueriedResult) => {
        // 操作成功,查询失败的消息通过 res.errorMessageIDs 返回
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 5、(可选)查询某一条群消息的已读群成员列表和未读群成员列表
const groupMsgObj: ZIMMessage = {}    // 从 queryHistoryMessage 查询
const queryConfig: ZIMGroupMessageReceiptMemberQueryConfig = {
    count: 10,    // 需要查询的用户数量
    nextFlag: 0    // 查询的 flag,初始时填 0,后续填从 Promise 里返回的 nextFlag
}
// 5.1 群已读用户列表
zim.queryGroupMessageReceiptReadMemberList(groupMsgObj, groupMsgObj.conversationID, queryConfig)
    .then((res: ZIMGroupMessageReceiptMemberListQueriedResult) => {
        // 操作成功
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
// 5.2 群未读用户列表
zim.queryGroupMessageReceiptUnreadMemberList(groupMsgObj, groupMsgObj.conversationID, queryConfig)
    .then((res: ZIMGroupMessageReceiptMemberListQueriedResult) => {
        // 操作成功
    })
    .catch((err: ZIMError) => {
        // 操作失败
    });
