接口名称
批量消息管理 -(IFeishuTenantV1BatchMessage)
功能描述
批量消息管理API用于向多个用户或多个部门批量发送各类消息,支持文本、富文本、图片、群名片等多种消息类型。该接口特别适用于企业通知、营销推送、系统公告等需要同时向大量用户发送相同消息的场景。使用租户令牌访问,适用于企业应用场景,提供消息发送、撤回、进度查询和已读状态统计等完整功能。
参考文档
https://open.feishu.cn/document/server-docs/im-v1/batch_message/send-messages-in-batches
函数列表
| 函数名称 | 功能描述 | 认证方式 | HTTP 方法 |
|---|---|---|---|
| BatchSendTextMessageAsync | 批量发送文本消息 | 租户令牌 | POST |
| BatchSendRichTextMessageAsync | 批量发送富文本消息 | 租户令牌 | POST |
| BatchSendImageMessageAsync | 批量发送图片消息 | 租户令牌 | POST |
| BatchSendGroupShareMessageAsync | 批量发送群分享消息 | 租户令牌 | POST |
| RevokeMessageAsync | 撤回批量消息 | 租户令牌 | DELETE |
| GetUserReadMessageInfosAsync | 查询消息已读状态 | 租户令牌 | GET |
| GetBatchMessageProgressAsync | 查询消息处理进度 | 租户令牌 | GET |
函数详细内容
函数名称:批量发送文本消息
函数签名:
csharp
Task<FeishuApiResult<BatchMessageResult>?> BatchSendTextMessageAsync(
[Body] BatchSenderTextMessageRequest sendMessageRequest,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| sendMessageRequest | BatchSenderTextMessageRequest | ✅ 必填 | 批量发送文本消息请求体 | 见下方结构 |
| sendMessageRequest.OpenIds | List<string> | ⚪ 可选 | 用户open_id列表 | ["ou_18eac85d35a26f989317ad4f02e8bbbb", "ou_461cf042d9eedaa60d445f26dc747d5e"] |
| sendMessageRequest.UserIds | List<string> | ⚪ 可选 | 用户user_id列表 | ["7cdcc7c2", "ca51d83b"] |
| sendMessageRequest.UnionIds | List<string> | ⚪ 可选 | 用户union_id列表 | ["on_cad4860e7af114fb4ff6c5d496d1dd76"] |
| sendMessageRequest.DepartmentIds | List<string> | ⚪ 可选 | 部门ID列表 | ["3dceba33a33226", "od-5b91c9affb665451a16b90b4be367efa"] |
| sendMessageRequest.MsgType | string | ✅ 必填 | 消息类型,固定值"text" | "text" |
| sendMessageRequest.Content | MessageTextContent | ✅ 必填 | 消息内容 | 见下方结构 |
| sendMessageRequest.Content.Text | string | ✅ 必填 | 文本消息内容 | "明天上午10点召开部门例会" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"message_id": "om_dc13264520392913993dd051dba21dcf",
"invalid_open_ids": [],
"invalid_user_ids": [],
"invalid_union_ids": [],
"invalid_department_ids": ["invalid_dept_123"]
}
}常见错误响应示例:
json
{
"code": 400,
"msg": "请求参数错误",
"data": null
}json
{
"code": 403,
"msg": "无权限向该用户发送消息",
"data": null
}说明:
- 支持同时向多个用户和多个部门发送文本消息
- 至少需要提供一种类型的接收者(用户或部门)
- 返回message_id可用于后续的消息撤回或状态查询
- 无效ID列表帮助识别发送失败的原因
代码示例:
javascript
// 发送部门例会通知
async function sendDepartmentMeetingNotification() {
const meetingInfo = {
title: "部门例会通知",
content: "各位同事好,明天(周三)上午10:00在会议室A召开部门例会,请准时参加。",
targetUsers: [
"ou_18eac85d35a26f989317ad4f02e8bbbb", // 张三
"ou_461cf042d9eedaa60d445f26dc747d5e", // 李四
"ou_5g2h3j4k5l6m7n8o9p0q1r2s3t4u5v6" // 王五
],
targetDepartments: [
"od-5b91c9affb665451a16b90b4be367efa", // 研发部
"od-6a7b8c9d0e1f2a3b4c5d6e7f8g9h0i1" // 产品部
]
};
try {
const response = await feishuClient.tenantV1BatchMessage.batchSendTextMessageAsync({
open_ids: meetingInfo.targetUsers,
department_ids: meetingInfo.targetDepartments,
msg_type: "text",
content: {
text: meetingInfo.content
}
});
if (response.code === 0) {
const result = response.data;
console.log(`消息发送成功,消息ID: ${result.message_id}`);
// 分析发送结果
const analysis = analyzeBatchSendResult(result, meetingInfo);
console.log(`成功发送到 ${analysis.successfulSends} 个接收者`);
if (analysis.hasInvalidRecipients) {
console.warn(`发现无效接收者: ${JSON.stringify(analysis.invalidRecipients)}`);
// 记录无效接收者以便后续清理
await logInvalidRecipients(analysis.invalidRecipients);
}
// 设置消息发送状态监控
await setupMessageMonitoring(result.message_id);
return result;
} else {
console.error(`发送失败: ${response.msg}`);
throw new Error(`批量发送文本消息失败: ${response.msg}`);
}
} catch (error) {
console.error("发送部门例会通知时发生错误:", error.message);
throw error;
}
}
// 分析批量发送结果
function analyzeBatchSendResult(result, meetingInfo) {
const totalRequested =
meetingInfo.targetUsers.length +
meetingInfo.targetDepartments.length;
const invalidCount =
(result.invalid_open_ids?.length || 0) +
(result.invalid_user_ids?.length || 0) +
(result.invalid_union_ids?.length || 0) +
(result.invalid_department_ids?.length || 0);
const successfulSends = totalRequested - invalidCount;
const analysis = {
totalRequested,
invalidCount,
successfulSends,
hasInvalidRecipients: invalidCount > 0
};
if (analysis.hasInvalidRecipients) {
analysis.invalidRecipients = {
invalidOpenIds: result.invalid_open_ids || [],
invalidUserIds: result.invalid_user_ids || [],
invalidUnionIds: result.invalid_union_ids || [],
invalidDepartmentIds: result.invalid_department_ids || []
};
}
return analysis;
}
// 设置消息监控
async function setupMessageMonitoring(messageId) {
// 5分钟后检查发送进度
setTimeout(async () => {
const progress = await feishuClient.tenantV1BatchMessage.getBatchMessageProgressAsync(messageId);
console.log(`消息 ${messageId} 发送进度:`, progress.data);
// 1小时后检查已读状态
setTimeout(async () => {
const readStatus = await feishuClient.tenantV1BatchMessage.getUserReadMessageInfosAsync(messageId);
console.log(`消息 ${messageId} 已读状态:`, readStatus.data);
}, 60 * 60 * 1000);
}, 5 * 60 * 1000);
}函数名称:批量发送富文本消息
函数签名:
csharp
Task<FeishuApiResult<BatchMessageResult>?> BatchSendRichTextMessageAsync(
[Body] BatchSenderRichTextMessageRequest sendMessageRequest,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| sendMessageRequest | BatchSenderRichTextMessageRequest | ✅ 必填 | 批量发送富文本消息请求体 | 见下方结构 |
| sendMessageRequest.OpenIds | List<string> | ⚪ 可选 | 用户open_id列表 | ["ou_18eac85d35a26f989317ad4f02e8bbbb"] |
| sendMessageRequest.DepartmentIds | List<string> | ⚪ 可选 | 部门ID列表 | ["od-5b91c9affb665451a16b90b4be367efa"] |
| sendMessageRequest.MsgType | string | ✅ 必填 | 消息类型,固定值"post" | "post" |
| sendMessageRequest.Content | MessageRichTextContent | ✅ 必填 | 富文本消息内容 | 见下方结构 |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"message_id": "om_dc13264520392913993dd051dba21dcf",
"invalid_open_ids": [],
"invalid_department_ids": []
}
}说明:
- 支持发送格式丰富的文本消息,包含标题、列表、链接等
- 适用于发送通知公告、产品介绍等需要格式化的内容
- 富文本内容需要符合飞书的富文本格式规范
代码示例:
javascript
// 发送产品发布富文本公告
async function sendProductReleaseAnnouncement() {
const announcement = {
title: "🎉 新产品发布通知",
content: {
post: {
zh_cn: {
title: "🎉 智能办公助手 v2.0 正式发布",
content: [
[
{
tag: "text",
text: "亲爱的各位同事,\n\n我们很高兴地宣布,"
},
{
tag: "text",
text: "智能办公助手 v2.0",
style: {
bold: true,
color: "#FF0000"
}
},
{
tag: "text",
text: "今天正式发布!\n\n"
}
],
[
{
tag: "text",
text: "🚀 "
},
{
tag: "text",
text: "主要特性:",
style: {
bold: true
}
}
],
[
{
tag: "bullet",
bullet: "1"
},
{
tag: "text",
text: "智能日程管理,自动安排会议时间"
}
],
[
{
tag: "bullet",
bullet: "2"
},
{
tag: "text",
text: "AI文档生成,提升工作效率300%"
}
],
[
{
tag: "bullet",
bullet: "3"
},
{
tag: "text",
text: "团队协作优化,实时同步工作进度"
}
],
[
{
tag: "a",
text: "📖 点击查看详细文档",
href: "https://docs.company.com/assistant-v2"
}
],
[
{
tag: "text",
text: "\n\n感谢大家的支持!",
style: {
italic: true
}
}
]
]
}
}
}
};
try {
const response = await feishuClient.tenantV1BatchMessage.batchSendRichTextMessageAsync({
open_ids: await getAllEmployeeIds(),
department_ids: ["od-5b91c9affb665451a16b90b4be367efa"],
msg_type: "post",
content: announcement.content
});
if (response.code === 0) {
console.log(`产品发布公告发送成功,消息ID: ${response.data.message_id}`);
// 创建发布记录
await createAnnouncementRecord({
messageId: response.data.message_id,
title: announcement.title,
type: "product_release",
sentAt: new Date().toISOString()
});
// 设置后续跟踪
await trackAnnouncementEngagement(response.data.message_id);
return response.data;
} else {
console.error(`发送失败: ${response.msg}`);
throw new Error(`批量发送富文本消息失败: ${response.msg}`);
}
} catch (error) {
console.error("发送产品发布公告时发生错误:", error.message);
throw error;
}
}
// 获取所有员工ID
async function getAllEmployeeIds() {
// 这里应该调用获取所有员工ID的接口
return [
"ou_18eac85d35a26f989317ad4f02e8bbbb",
"ou_461cf042d9eedaa60d445f26dc747d5e"
];
}
// 跟踪公告参与度
async function trackAnnouncementEngagement(messageId) {
// 定期检查已读状态
const checkInterval = setInterval(async () => {
try {
const readStatus = await feishuClient.tenantV1BatchMessage.getUserReadMessageInfosAsync(messageId);
const readCount = parseInt(readStatus.data.read_user.read_count);
const totalCount = parseInt(readStatus.data.read_user.total_count);
const readRate = totalCount > 0 ? (readCount / totalCount * 100).toFixed(2) : 0;
console.log(`公告已读进度: ${readCount}/${totalCount} (${readRate}%)`);
// 如果大部分人都已阅读,停止检查
if (readRate >= 80) {
clearInterval(checkInterval);
console.log("公告阅读率达到80%,停止跟踪");
}
} catch (error) {
console.error("检查公告阅读状态时发生错误:", error.message);
}
}, 30 * 60 * 1000); // 每30分钟检查一次
// 24小时后自动停止跟踪
setTimeout(() => {
clearInterval(checkInterval);
}, 24 * 60 * 60 * 1000);
}函数名称:批量发送图片消息
函数签名:
csharp
Task<FeishuApiResult<BatchMessageResult>?> BatchSendImageMessageAsync(
[Body] BatchSenderMessageImageRequest sendMessageRequest,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| sendMessageRequest | BatchSenderMessageImageRequest | ✅ 必填 | 批量发送图片消息请求体 | 见下方结构 |
| sendMessageRequest.OpenIds | List<string> | ⚪ 可选 | 用户open_id列表 | ["ou_18eac85d35a26f989317ad4f02e8bbbb"] |
| sendMessageRequest.DepartmentIds | List<string> | ⚪ 可选 | 部门ID列表 | ["od-5b91c9affb665451a16b90b4be367efa"] |
| sendMessageRequest.MsgType | string | ✅ 必填 | 消息类型,固定值"image" | "image" |
| sendMessageRequest.Content | MessageImageContent | ✅ 必填 | 图片消息内容 | 见下方结构 |
| sendMessageRequest.Content.ImageKey | string | ✅ 必填 | 图片的唯一标识符 | "img_v2_041b8e4a-1234-5678-90ab-cdef12345678g" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"message_id": "om_dc13264520392913993dd051dba21dcf",
"invalid_open_ids": []
}
}说明:
- image_key需要通过图片上传接口获取
- 支持常见图片格式:JPG、PNG、GIF等
- 图片大小限制通常为10MB以内
代码示例:
javascript
// 发送公司活动照片
async function sendCompanyActivityPhotos() {
const activityInfo = {
title: "团队建设活动照片分享",
description: "上周五的团队建设活动圆满结束,以下是精彩瞬间:",
photos: [
{
image_key: "img_v2_041b8e4a-1234-5678-90ab-cdef12345678g",
description: "团队合影"
},
{
image_key: "img_v2_041b8e4a-8765-4321-ba98-fedcba987654h",
description: "游戏环节"
}
]
};
try {
// 先发送文字说明
const textResponse = await feishuClient.tenantV1BatchMessage.batchSendTextMessageAsync({
open_ids: await getActivityParticipantIds(),
msg_type: "text",
content: {
text: activityInfo.description
}
});
if (textResponse.code !== 0) {
throw new Error(`发送文字说明失败: ${textResponse.msg}`);
}
// 然后发送照片
for (const photo of activityInfo.photos) {
const response = await feishuClient.tenantV1BatchMessage.batchSendImageMessageAsync({
open_ids: await getActivityParticipantIds(),
department_ids: ["od-5b91c9affb665451a16b90b4be367efa"],
msg_type: "image",
content: {
image_key: photo.image_key
}
});
if (response.code === 0) {
console.log(`照片发送成功: ${photo.description}, 消息ID: ${response.data.message_id}`);
// 记录照片发送记录
await recordPhotoSend({
messageId: response.data.message_id,
imageKey: photo.image_key,
description: photo.description,
sentAt: new Date().toISOString()
});
} else {
console.error(`发送照片失败: ${photo.description}, 错误: ${response.msg}`);
}
}
return {
success: true,
message: "活动照片发送完成"
};
} catch (error) {
console.error("发送活动照片时发生错误:", error.message);
throw error;
}
}
// 获取活动参与者ID
async function getActivityParticipantIds() {
// 这里应该调用获取活动参与者的接口
return [
"ou_18eac85d35a26f989317ad4f02e8bbbb",
"ou_461cf042d9eedaa60d445f26dc747d5e"
];
}
// 记录照片发送记录
async function recordPhotoSend(photoInfo) {
// 这里应该调用保存照片发送记录的接口
console.log("记录照片发送:", photoInfo);
}函数名称:批量发送群分享消息
函数签名:
csharp
Task<FeishuApiResult<BatchMessageResult>?> BatchSendGroupShareMessageAsync(
[Body] BatchSenderMessageGroupShareRequest sendMessageRequest,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| sendMessageRequest | BatchSenderMessageGroupShareRequest | ✅ 必填 | 批量发送群分享消息请求体 | 见下方结构 |
| sendMessageRequest.OpenIds | List<string> | ⚪ 可选 | 用户open_id列表 | ["ou_18eac85d35a26f989317ad4f02e8bbbb"] |
| sendMessageRequest.DepartmentIds | List<string> | ⚪ 可选 | 部门ID列表 | ["od-5b91c9affb665451a16b90b4be367efa"] |
| sendMessageRequest.MsgType | string | ✅ 必填 | 消息类型,固定值"share_chat" | "share_chat" |
| sendMessageRequest.Content | MessageGroupShareContent | ✅ 必填 | 群分享消息内容 | 见下方结构 |
| sendMessageRequest.Content.ShareChatId | string | ✅ 必填 | 分享的群聊ID | "oc_5d9c3e8f7b6a5c4d3e2f1a9b8c7d6e5f" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"message_id": "om_dc13264520392913993dd051dba21dcf",
"invalid_open_ids": []
}
}说明:
- share_chat_id是要分享的群聊的唯一标识
- 接收者点击后可以查看并加入该群聊
- 适用于推广重要群聊或引导用户加入特定讨论组
代码示例:
javascript
// 推广技术交流群
async function promoteTechnicalDiscussionGroup() {
const promotionInfo = {
targetGroup: {
share_chat_id: "oc_5d9c3e8f7b6a5c4d3e2f1a9b8c7d6e5f",
name: "前端技术交流群",
description: "分享前端开发经验,讨论最新技术趋势"
},
targetAudience: {
departments: ["od-5b91c9affb665451a16b90b4be367efa"], // 研发部
individuals: [
"ou_18eac85d35a26f989317ad4f02e8bbbb" // 张三(前端负责人)
]
}
};
try {
// 先发送邀请说明
const inviteText = `🚀 ${promotionInfo.targetGroup.name}\n\n${promotionInfo.targetGroup.description}\n\n点击下方链接加入群聊,与大家一起交流学习!`;
const textResponse = await feishuClient.tenantV1BatchMessage.batchSendTextMessageAsync({
open_ids: promotionInfo.targetAudience.individuals,
department_ids: promotionInfo.targetAudience.departments,
msg_type: "text",
content: {
text: inviteText
}
});
if (textResponse.code !== 0) {
throw new Error(`发送邀请说明失败: ${textResponse.msg}`);
}
// 发送群聊分享卡片
const response = await feishuClient.tenantV1BatchMessage.batchSendGroupShareMessageAsync({
open_ids: promotionInfo.targetAudience.individuals,
department_ids: promotionInfo.targetAudience.departments,
msg_type: "share_chat",
content: {
share_chat_id: promotionInfo.targetGroup.share_chat_id
}
});
if (response.code === 0) {
console.log(`群聊推广成功,消息ID: ${response.data.message_id}`);
// 记录推广活动
await recordPromotionActivity({
messageId: response.data.message_id,
groupId: promotionInfo.targetGroup.share_chat_id,
groupName: promotionInfo.targetGroup.name,
targetCount: promotionInfo.targetAudience.individuals.length + 1, // +1 for department
promotedAt: new Date().toISOString()
});
// 设置3天后检查加入情况
setTimeout(async () => {
await checkGroupJoinStatus(promotionInfo.targetGroup.share_chat_id);
}, 3 * 24 * 60 * 60 * 1000);
return response.data;
} else {
console.error(`群聊推广失败: ${response.msg}`);
throw new Error(`批量发送群分享消息失败: ${response.msg}`);
}
} catch (error) {
console.error("推广技术交流群时发生错误:", error.message);
throw error;
}
}
// 检查群聊加入状态
async function checkGroupJoinStatus(groupId) {
try {
// 这里应该调用检查群聊成员的接口来了解新加入情况
const currentMembers = await getGroupMemberCount(groupId);
console.log(`群聊 ${groupId} 当前成员数: ${currentMembers}`);
// 记录加入情况统计
await recordJoinStatistics({
groupId,
memberCount: currentMembers,
checkDate: new Date().toISOString()
});
} catch (error) {
console.error("检查群聊加入状态时发生错误:", error.message);
}
}
// 获取群聊成员数
async function getGroupMemberCount(groupId) {
// 这里应该调用飞书获取群聊信息的接口
return 25; // 模拟返回
}
// 记录推广活动
async function recordPromotionActivity(promotionData) {
// 这里应该调用保存推广记录的接口
console.log("记录推广活动:", promotionData);
}函数名称:撤回批量消息
函数签名:
csharp
Task<FeishuNullDataApiResult?> RevokeMessageAsync(
[Path] string batch_message_id,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| batch_message_id | string | ✅ 必填 | 批量消息任务ID | "om_dc13264520392913993dd051dba21dcf" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": null
}常见错误响应示例:
json
{
"code": 404,
"msg": "消息不存在或已过期",
"data": null
}json
{
"code": 403,
"msg": "消息发送时间超过24小时,无法撤回",
"data": null
}说明:
- 只能撤回通过批量发送接口发送的消息
- 撤回有时间限制(通常是24小时内)
- 撤回操作不可逆,请谨慎使用
代码示例:
javascript
// 紧急通知撤回系统
class EmergencyNotificationSystem {
constructor() {
this.pendingRevokes = new Map(); // 存储待撤回的消息
this.revokeReasons = new Map(); // 存储撤回原因
}
// 发送可撤回的紧急通知
async sendEmergencyNotification(content, recipients, maxRetentionHours = 2) {
try {
const response = await feishuClient.tenantV1BatchMessage.batchSendTextMessageAsync({
open_ids: recipients.users || [],
department_ids: recipients.departments || [],
msg_type: "text",
content: {
text: `🚨 紧急通知(${maxRetentionHours}小时后自动撤回):\n\n${content}`
}
});
if (response.code === 0) {
const messageId = response.data.message_id;
// 记录待撤回消息
this.pendingRevokes.set(messageId, {
content,
recipients,
sentAt: new Date(),
maxRetentionHours,
autoRevoke: true
});
// 设置自动撤回定时器
const revokeTime = maxRetentionHours * 60 * 60 * 1000;
setTimeout(async () => {
await this.autoRevokeMessage(messageId);
}, revokeTime);
console.log(`紧急通知发送成功,将在${maxRetentionHours}小时后自动撤回`);
return messageId;
} else {
throw new Error(`发送紧急通知失败: ${response.msg}`);
}
} catch (error) {
console.error("发送紧急通知时发生错误:", error.message);
throw error;
}
}
// 手动撤回消息
async revokeMessage(messageId, reason = "手动撤回") {
try {
const response = await feishuClient.tenantV1BatchMessage.revokeMessageAsync(messageId);
if (response.code === 0) {
console.log(`消息 ${messageId} 撤回成功: ${reason}`);
// 记录撤回操作
await this.recordRevokeOperation({
messageId,
reason,
timestamp: new Date().toISOString(),
operator: "system"
});
// 清理待撤回记录
this.pendingRevokes.delete(messageId);
this.revokeReasons.delete(messageId);
return true;
} else {
console.error(`撤回消息失败: ${response.msg}`);
return false;
}
} catch (error) {
console.error("撤回消息时发生错误:", error.message);
return false;
}
}
// 自动撤回消息
async autoRevokeMessage(messageId) {
const messageInfo = this.pendingRevokes.get(messageId);
if (!messageInfo) {
return;
}
const reason = `消息已超过${messageInfo.maxRetentionHours}小时,自动撤回`;
const success = await this.revokeMessage(messageId, reason);
if (success) {
// 发送撤回通知
await this.sendRevokeNotification(messageInfo, reason);
}
}
// 批量撤回过期消息
async revokeExpiredMessages() {
const now = new Date();
const expiredMessages = [];
for (const [messageId, messageInfo] of this.pendingRevokes) {
const elapsedHours = (now - messageInfo.sentAt) / (1000 * 60 * 60);
if (elapsedHours >= messageInfo.maxRetentionHours) {
expiredMessages.push(messageId);
}
}
console.log(`发现 ${expiredMessages.length} 条过期消息需要撤回`);
for (const messageId of expiredMessages) {
await this.autoRevokeMessage(messageId);
}
return expiredMessages.length;
}
// 发送撤回通知
async sendRevokeNotification(messageInfo, reason) {
try {
const notification = `⚠️ 通知撤回\n\n原通知内容:${messageInfo.content.substring(0, 50)}...\n撤回原因:${reason}`;
await feishuClient.tenantV1BatchMessage.batchSendTextMessageAsync({
open_ids: messageInfo.recipients.users || [],
department_ids: messageInfo.recipients.departments || [],
msg_type: "text",
content: {
text: notification
}
});
console.log("撤回通知发送成功");
} catch (error) {
console.error("发送撤回通知时发生错误:", error.message);
}
}
// 记录撤回操作
async recordRevokeOperation(operation) {
// 这里应该调用保存撤回记录的接口
console.log("记录撤回操作:", operation);
}
}
// 使用示例
const emergencySystem = new EmergencyNotificationSystem();
// 发送紧急系统维护通知
async function sendSystemMaintenanceNotification() {
const maintenanceContent = `
系统将于今晚22:00-24:00进行维护升级,期间可能影响正常使用。
请提前保存重要工作,给您带来的不便敬请谅解。
维护内容:
1. 数据库优化
2. 安全补丁更新
3. 性能提升
`;
const recipients = {
users: ["ou_18eac85d35a26f989317ad4f02e8bbbb"],
departments: ["od-5b91c9affb665451a16b90b4be367efa"]
};
try {
const messageId = await emergencySystem.sendEmergencyNotification(
maintenanceContent,
recipients,
3 // 3小时后自动撤回
);
console.log(`系统维护通知发送成功,消息ID: ${messageId}`);
return messageId;
} catch (error) {
console.error("发送系统维护通知时发生错误:", error.message);
throw error;
}
}函数名称:查询消息已读状态
函数签名:
csharp
Task<FeishuApiResult<BatchMessageReadStatusResult>?> GetUserReadMessageInfosAsync(
[Path] string batch_message_id,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| batch_message_id | string | ✅ 必填 | 批量消息任务ID | "om_dc13264520392913993dd051dba21dcf" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"read_user": {
"read_count": "15",
"total_count": "20"
}
}
}说明:
- 返回消息的已读用户数和总用户数
- 可用于统计消息的阅读效果
- 适用于重要通知的阅读跟踪
代码示例:
javascript
// 消息阅读效果分析系统
class MessageAnalyticsSystem {
constructor() {
this.messageStats = new Map(); // 存储消息统计数据
this.analyticsReports = []; // 存储分析报告
}
// 跟踪消息阅读情况
async trackMessageReadStatus(messageId, options = {}) {
const {
checkInterval = 30 * 60 * 1000, // 30分钟检查一次
maxCheckTime = 24 * 60 * 60 * 1000, // 最多检查24小时
targetReadRate = 80 // 目标阅读率
} = options;
const startTime = Date.now();
const stats = {
messageId,
startTime: new Date().toISOString(),
checkCount: 0,
readings: []
};
this.messageStats.set(messageId, stats);
const checkIntervalId = setInterval(async () => {
const elapsed = Date.now() - startTime;
// 超过最大检查时间,停止跟踪
if (elapsed >= maxCheckTime) {
clearInterval(checkIntervalId);
await this.finalizeMessageTracking(messageId);
return;
}
await this.performReadCheck(messageId);
}, checkInterval);
// 10分钟后进行第一次检查
setTimeout(async () => {
await this.performReadCheck(messageId);
}, 10 * 60 * 1000);
return checkIntervalId;
}
// 执行阅读检查
async performReadCheck(messageId) {
try {
const response = await feishuClient.tenantV1BatchMessage.getUserReadMessageInfosAsync(messageId);
if (response.code === 0) {
const readData = response.data.read_user;
const readCount = parseInt(readData.read_count);
const totalCount = parseInt(readData.total_count);
const readRate = totalCount > 0 ? (readCount / totalCount * 100).toFixed(2) : 0;
const reading = {
timestamp: new Date().toISOString(),
readCount,
totalCount,
readRate: parseFloat(readRate)
};
const stats = this.messageStats.get(messageId);
if (stats) {
stats.checkCount++;
stats.readings.push(reading);
console.log(`消息 ${messageId} 阅读情况: ${readCount}/${totalCount} (${readRate}%)`);
// 检查是否达到目标阅读率
if (reading.readRate >= 80) {
console.log(`消息 ${messageId} 已达到80%阅读率,停止跟踪`);
clearInterval(stats.checkIntervalId);
await this.finalizeMessageTracking(messageId);
}
// 检查阅读增长情况
if (stats.readings.length >= 2) {
const prevReading = stats.readings[stats.readings.length - 2];
const growthRate = ((reading.readCount - prevReading.readCount) / prevReading.totalCount * 100).toFixed(2);
console.log(`阅读增长率: ${growthRate}%`);
}
}
return reading;
} else {
console.error(`查询阅读状态失败: ${response.msg}`);
return null;
}
} catch (error) {
console.error("检查消息阅读状态时发生错误:", error.message);
return null;
}
}
// 完成消息跟踪
async finalizeMessageTracking(messageId) {
const stats = this.messageStats.get(messageId);
if (!stats) {
return;
}
const finalReading = stats.readings[stats.readings.length - 1];
const report = {
messageId,
trackingPeriod: {
start: stats.startTime,
end: new Date().toISOString(),
checkCount: stats.checkCount
},
finalStats: {
readCount: finalReading.readCount,
totalCount: finalReading.totalCount,
readRate: finalReading.readRate
},
growthAnalysis: this.analyzeGrowth(stats.readings),
generatedAt: new Date().toISOString()
};
this.analyticsReports.push(report);
// 保存分析报告
await this.saveAnalyticsReport(report);
// 清理跟踪数据
this.messageStats.delete(messageId);
console.log(`消息 ${messageId} 跟踪完成,最终阅读率: ${finalReading.readRate}%`);
return report;
}
// 分析阅读增长情况
analyzeGrowth(readings) {
if (readings.length < 2) {
return null;
}
const first = readings[0];
const last = readings[readings.length - 1];
const totalGrowth = last.readCount - first.readCount;
const totalGrowthRate = (totalGrowth / first.totalCount * 100).toFixed(2);
// 计算平均增长率
let totalIntervalGrowth = 0;
for (let i = 1; i < readings.length; i++) {
totalIntervalGrowth += readings[i].readCount - readings[i-1].readCount;
}
const avgGrowthPerInterval = (totalIntervalGrowth / (readings.length - 1)).toFixed(2);
return {
totalGrowth,
totalGrowthRate: parseFloat(totalGrowthRate),
avgGrowthPerInterval: parseFloat(avgGrowthPerInterval),
timeToReach50Percent: this.getTimeToTarget(readings, 50),
timeToReach80Percent: this.getTimeToTarget(readings, 80)
};
}
// 计算达到目标阅读率的时间
getTimeToTarget(readings, targetRate) {
for (const reading of readings) {
if (reading.readRate >= targetRate) {
const startTime = new Date(readings[0].timestamp);
const targetTime = new Date(reading.timestamp);
return Math.round((targetTime - startTime) / (1000 * 60)); // 返回分钟数
}
}
return null; // 未达到目标
}
// 生成阅读效果报告
generateReadingReport(messageId) {
const report = this.analyticsReports.find(r => r.messageId === messageId);
if (!report) {
return null;
}
const reportText = `
📊 消息阅读效果分析报告
📋 基本信息
- 消息ID: ${report.messageId}
- 跟踪时间: ${new Date(report.trackingPeriod.start).toLocaleString()} - ${new Date(report.trackingPeriod.end).toLocaleString()}
- 检查次数: ${report.trackingPeriod.checkCount}
📈 最终统计
- 总接收人数: ${report.finalStats.totalCount}
- 已读人数: ${report.finalStats.readCount}
- 阅读率: ${report.finalStats.readRate}%
📊 增长分析
- 总增长人数: ${report.growthAnalysis?.totalGrowth || 0}
- 总增长率: ${report.growthAnalysis?.totalGrowthRate || 0}%
- 平均每次检查增长: ${report.growthAnalysis?.avgGrowthPerInterval || 0}
- 达到50%阅读率用时: ${report.growthAnalysis?.timeToReach50Percent ? report.growthAnalysis.timeToReach50Percent + '分钟' : '未达到'}
- 达到80%阅读率用时: ${report.growthAnalysis?.timeToReach80Percent ? report.growthAnalysis.timeToReach80Percent + '分钟' : '未达到'}
💡 建议
${this.generateSuggestions(report)}
📅 报告生成时间: ${new Date(report.generatedAt).toLocaleString()}
`;
return reportText.trim();
}
// 生成建议
generateSuggestions(report) {
const suggestions = [];
const readRate = report.finalStats.readRate;
if (readRate >= 90) {
suggestions.push("✅ 阅读率优秀,消息内容吸引力强");
} else if (readRate >= 80) {
suggestions.push("👍 阅读率良好,继续保持");
} else if (readRate >= 60) {
suggestions.push("⚠️ 阅读率一般,建议优化消息发送时间或内容");
} else {
suggestions.push("❌ 阅读率偏低,建议检查接收者列表或消息内容");
}
if (report.growthAnalysis?.timeToReach50Percent > 120) {
suggestions.push("🕐 建议在用户活跃时间段发送消息以提高阅读率");
}
return suggestions.join("\n");
}
// 保存分析报告
async saveAnalyticsReport(report) {
// 这里应该调用保存分析报告的接口
console.log("保存分析报告:", report);
}
}
// 使用示例
const analyticsSystem = new MessageAnalyticsSystem();
// 发送重要通知并跟踪阅读情况
async function sendImportantNotificationWithTracking() {
const messageId = "om_dc13264520392913993dd051dba21dcf"; // 假设已发送的消息ID
// 开始跟踪
await analyticsSystem.trackMessageReadStatus(messageId, {
checkInterval: 15 * 60 * 1000, // 15分钟检查一次
maxCheckTime: 12 * 60 * 60 * 1000, // 12小时
targetReadRate: 85 // 目标阅读率85%
});
console.log(`开始跟踪消息 ${messageId} 的阅读情况`);
}
// 生成并查看阅读报告
async function viewMessageReport(messageId) {
const report = analyticsSystem.generateReadingReport(messageId);
if (report) {
console.log(report);
// 可以将报告发送给管理员
await sendReportToAdmin(report);
} else {
console.log("未找到该消息的分析报告");
}
}
// 发送报告给管理员
async function sendReportToAdmin(reportText) {
// 这里应该调用发送消息给管理员的接口
console.log("发送报告给管理员:", reportText);
}函数名称:查询消息处理进度
函数签名:
csharp
Task<FeishuApiResult<BatchMessageProgressResult>?> GetBatchMessageProgressAsync(
[Path] string batch_message_id,
CancellationToken cancellationToken = default);认证:租户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| batch_message_id | string | ✅ 必填 | 批量消息任务ID | "om_dc13264520392913993dd051dba21dcf" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"batch_message_send_progress": {
"total_count": 100,
"success_count": 85,
"failed_count": 10,
"pending_count": 5,
"progress_rate": 85
},
"batch_message_recall_progress": {
"total_count": 50,
"success_count": 45,
"failed_count": 5,
"pending_count": 0,
"progress_rate": 90
}
}
}说明:
- 同时返回发送进度和撤回进度(如果进行了撤回操作)
- 包含成功、失败、待处理的详细统计
- 可用于监控大批量消息的处理状态
代码示例:
javascript
// 批量消息进度监控系统
class BatchMessageMonitor {
constructor() {
this.monitoringTasks = new Map(); // 存储监控任务
this.progressHistory = new Map(); // 存储进度历史
}
// 开始监控消息进度
async startMonitoring(messageId, options = {}) {
const {
checkInterval = 10 * 1000, // 10秒检查一次
maxMonitorTime = 5 * 60 * 1000, // 最多监控5分钟
onProgress = null, // 进度回调
onComplete = null, // 完成回调
onError = null // 错误回调
} = options;
const monitorTask = {
messageId,
startTime: Date.now(),
checkCount: 0,
checkInterval,
maxMonitorTime,
callbacks: { onProgress, onComplete, onError },
status: 'running'
};
this.monitoringTasks.set(messageId, monitorTask);
const intervalId = setInterval(async () => {
await this.checkProgress(messageId, intervalId);
}, checkInterval);
// 设置最大监控时间
const timeoutId = setTimeout(() => {
this.stopMonitoring(messageId, 'timeout');
}, maxMonitorTime);
monitorTask.intervalId = intervalId;
monitorTask.timeoutId = timeoutId;
console.log(`开始监控消息 ${messageId} 的处理进度`);
return intervalId;
}
// 检查进度
async checkProgress(messageId, intervalId) {
const task = this.monitoringTasks.get(messageId);
if (!task || task.status !== 'running') {
return;
}
try {
task.checkCount++;
const response = await feishuClient.tenantV1BatchMessage.getBatchMessageProgressAsync(messageId);
if (response.code === 0) {
const progressData = response.data;
const analysis = this.analyzeProgress(progressData);
// 记录进度历史
this.recordProgressHistory(messageId, analysis);
console.log(`消息 ${messageId} 进度更新:`, analysis);
// 调用进度回调
if (task.callbacks.onProgress) {
await task.callbacks.onProgress(analysis);
}
// 检查是否完成
if (analysis.sendProgress.isComplete || analysis.recallProgress?.isComplete) {
this.stopMonitoring(messageId, 'completed');
}
// 检查异常情况
if (analysis.hasErrors) {
if (task.callbacks.onError) {
await task.callbacks.onError(analysis);
}
}
} else {
console.error(`查询进度失败: ${response.msg}`);
if (task.callbacks.onError) {
await task.callbacks.onError({ error: response.msg });
}
}
} catch (error) {
console.error("检查消息进度时发生错误:", error.message);
if (task.callbacks.onError) {
await task.callbacks.onError({ error: error.message });
}
}
}
// 分析进度数据
analyzeProgress(progressData) {
const analysis = {
timestamp: new Date().toISOString(),
sendProgress: this.analyzeProgressItem(progressData.batch_message_send_progress),
recallProgress: progressData.batch_message_recall_progress ?
this.analyzeProgressItem(progressData.batch_message_recall_progress) : null,
hasErrors: false,
isComplete: false
};
// 检查是否有错误
if (analysis.sendProgress.failedCount > 0 ||
(analysis.recallProgress && analysis.recallProgress.failedCount > 0)) {
analysis.hasErrors = true;
}
// 检查是否完成
if (analysis.sendProgress.isComplete &&
(!analysis.recallProgress || analysis.recallProgress.isComplete)) {
analysis.isComplete = true;
}
return analysis;
}
// 分析单个进度项
analyzeProgressItem(progressItem) {
if (!progressItem) {
return null;
}
const totalCount = progressItem.total_count || 0;
const successCount = progressItem.success_count || 0;
const failedCount = progressItem.failed_count || 0;
const pendingCount = progressItem.pending_count || 0;
const progressRate = progressItem.progress_rate || 0;
const isComplete = pendingCount === 0 && (successCount + failedCount === totalCount);
const successRate = totalCount > 0 ? (successCount / totalCount * 100).toFixed(2) : 0;
const failureRate = totalCount > 0 ? (failedCount / totalCount * 100).toFixed(2) : 0;
return {
totalCount,
successCount,
failedCount,
pendingCount,
progressRate,
successRate: parseFloat(successRate),
failureRate: parseFloat(failureRate),
isComplete
};
}
// 记录进度历史
recordProgressHistory(messageId, analysis) {
if (!this.progressHistory.has(messageId)) {
this.progressHistory.set(messageId, []);
}
const history = this.progressHistory.get(messageId);
history.push(analysis);
// 保留最近50条记录
if (history.length > 50) {
history.shift();
}
}
// 停止监控
stopMonitoring(messageId, reason = 'manual') {
const task = this.monitoringTasks.get(messageId);
if (!task) {
return;
}
task.status = reason;
// 清理定时器
if (task.intervalId) {
clearInterval(task.intervalId);
}
if (task.timeoutId) {
clearTimeout(task.timeoutId);
}
// 调用完成回调
if (reason === 'completed' && task.callbacks.onComplete) {
const history = this.progressHistory.get(messageId) || [];
const finalProgress = history[history.length - 1];
task.callbacks.onComplete(finalProgress, reason);
}
console.log(`停止监控消息 ${messageId},原因: ${reason}`);
// 清理任务
this.monitoringTasks.delete(messageId);
}
// 生成进度报告
generateProgressReport(messageId) {
const history = this.progressHistory.get(messageId);
const task = this.monitoringTasks.get(messageId);
if (!history || history.length === 0) {
return null;
}
const firstProgress = history[0];
const finalProgress = history[history.length - 1];
const duration = new Date(finalProgress.timestamp) - new Date(firstProgress.timestamp);
const report = {
messageId,
monitoringDuration: Math.round(duration / 1000), // 秒
checkCount: task ? task.checkCount : history.length,
sendProgress: {
initial: firstProgress.sendProgress,
final: finalProgress.sendProgress,
improvement: this.calculateImprovement(firstProgress.sendProgress, finalProgress.sendProgress)
},
recallProgress: finalProgress.recallProgress ? {
initial: history.find(h => h.recallProgress)?.recallProgress,
final: finalProgress.recallProgress,
improvement: finalProgress.recallProgress ?
this.calculateImprovement(
history.find(h => h.recallProgress)?.recallProgress,
finalProgress.recallProgress
) : null
} : null,
timeline: history.map(h => ({
timestamp: h.timestamp,
sendProgress: h.sendProgress.progressRate,
recallProgress: h.recallProgress?.progressRate || 0
}))
};
return report;
}
// 计算改进情况
calculateImprovement(initial, final) {
if (!initial || !final) {
return null;
}
return {
successRateImprovement: final.successRate - initial.successRate,
processedCountImprovement: (final.successCount + final.failedCount) - (initial.successCount + initial.failedCount),
completionTime: final.isComplete ? "已完成" : "进行中"
};
}
}
// 使用示例
const messageMonitor = new BatchMessageMonitor();
// 监控重要通知的发送进度
async function monitorImportantNotification() {
const messageId = "om_dc13264520392913993dd051dba21dcf"; // 假设的消息ID
await messageMonitor.startMonitoring(messageId, {
checkInterval: 5 * 1000, // 5秒检查一次
maxMonitorTime: 10 * 60 * 1000, // 监控10分钟
onProgress: async (progress) => {
console.log(`发送进度: ${progress.sendProgress.progressRate}% (${progress.sendProgress.successCount}/${progress.sendProgress.totalCount})`);
// 如果发送失败率过高,发送警告
if (progress.sendProgress.failureRate > 10) {
console.warn(`⚠️ 发送失败率过高: ${progress.sendProgress.failureRate}%`);
await sendAlertNotification(progress);
}
},
onComplete: async (finalProgress, reason) => {
console.log(`监控完成,原因: ${reason}`);
// 生成进度报告
const report = messageMonitor.generateProgressReport(messageId);
if (report) {
await saveProgressReport(report);
await sendProgressReportToAdmin(report);
}
},
onError: async (error) => {
console.error("监控过程中发生错误:", error);
await sendErrorAlert(error);
}
});
}
// 发送警告通知
async function sendAlertNotification(progress) {
const alertMessage = `
⚠️ 批量消息发送异常警告
消息ID: ${progress.messageId || 'Unknown'}
发送进度: ${progress.sendProgress.progressRate}%
失败率: ${progress.sendProgress.failureRate}%
失败数量: ${progress.sendProgress.failedCount}
请及时检查处理!
`;
// 这里应该调用发送警告消息的接口
console.log("发送警告通知:", alertMessage);
}
// 保存进度报告
async function saveProgressReport(report) {
// 这里应该调用保存报告的接口
console.log("保存进度报告:", report);
}
// 发送进度报告给管理员
async function sendProgressReportToAdmin(report) {
const reportText = `
📊 批量消息处理进度报告
📋 基本信息
- 消息ID: ${report.messageId}
- 监控时长: ${Math.round(report.monitoringDuration / 60)}分钟
- 检查次数: ${report.checkCount}
📤 发送进度
- 初始进度: ${report.sendProgress.initial?.progressRate || 0}%
- 最终进度: ${report.sendProgress.final.progressRate}%
- 成功率改进: ${report.sendProgress.improvement?.successRateImprovement || 0}%
- 处理数量改进: ${report.sendProgress.improvement?.processedCountImprovement || 0}
🔙 撤回进度${report.recallProgress ? `
- 最终进度: ${report.recallProgress.final.progressRate}%
- 成功率改进: ${report.recallProgress.improvement?.successRateImprovement || 0}%` : ': 无撤回操作'}
📈 时间线
${report.timeline.map(t => `- ${new Date(t.timestamp).toLocaleTimeString()}: 发送${t.sendProgress}%${t.recallProgress > 0 ? ` 撤回${t.recallProgress}%` : ''}`).join('\n')}
📅 生成时间: ${new Date().toLocaleString()}
`;
// 这里应该调用发送报告给管理员的接口
console.log("发送进度报告:", reportText);
}