接口名称
消息查询API -(IFeishuUserV1Message)
功能描述
飞书消息查询接口,用于对飞书聊天中的消息进行查询和基本操作。消息即飞书聊天中的一条消息,当前接口使用用户令牌访问,适用于用户应用场景。用户可以对自己的消息进行撤回、添加表情回复等操作,以及查询消息内容和表情回复列表。
参考文档
函数列表
| 函数名称 | 功能描述 | 认证方式 | HTTP 方法 |
|---|---|---|---|
| RevokeMessageAsync | 撤回指定消息 | 用户令牌 | DELETE |
| AddMessageReactionsAsync | 添加消息表情回复 | 用户令牌 | POST |
| GetMessageReactionsPageListAsync | 获取消息表情回复列表 | 用户令牌 | GET |
| DeleteMessageReactionsAsync | 删除消息表情回复 | 用户令牌 | DELETE |
函数详细内容
函数名称:撤回消息
函数签名:
csharp
Task<FeishuNullDataApiResult?> RevokeMessageAsync(
[Path] string message_id,
CancellationToken cancellationToken = default);认证:用户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| message_id | string | ✅ 必填 | 待撤回的消息ID | "om_dc13264520392913993dd051dba21dcf" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success"
}常见错误响应:
json
{
"code": 404,
"msg": "消息不存在"
}json
{
"code": 403,
"msg": "无权限撤回该消息"
}json
{
"code": 400,
"msg": "消息发送超过2分钟,无法撤回"
}说明:
- 用户只能撤回自己发送的消息
- 消息撤回有时间限制(通常为2分钟)
- 撤回后消息将在聊天界面中显示为"已撤回"
代码示例:
javascript
// 用户消息撤回确认系统
async function confirmAndRevokeMessage(messageId, messageContent) {
// 检查消息是否可以被撤回
const canRevoke = await checkIfMessageCanBeRevoked(messageId);
if (!canRevoke.canRevoke) {
showMessageNotification(canRevoke.reason, 'warning');
return;
}
// 显示撤回确认对话框
const confirmed = await showRevokeConfirmationDialog(messageContent);
if (confirmed) {
await performMessageRevocation(messageId);
}
}
// 检查消息是否可以撤回
async function checkIfMessageCanBeRevoked(messageId) {
try {
// 获取消息详情(这里假设有获取消息详情的方法)
const messageDetails = await getMessageDetails(messageId);
// 检查消息发送者是否为当前用户
if (messageDetails.sender.user_id !== currentUser.id) {
return {
canRevoke: false,
reason: '您只能撤回自己发送的消息'
};
}
// 检查消息发送时间(撤回时间限制)
const messageAge = Date.now() - (messageDetails.create_time * 1000);
const revokeTimeLimit = 2 * 60 * 1000; // 2分钟
if (messageAge > revokeTimeLimit) {
return {
canRevoke: false,
reason: '消息发送超过2分钟,无法撤回'
};
}
return { canRevoke: true };
} catch (error) {
console.error('检查消息撤回状态失败:', error);
return {
canRevoke: false,
reason: '无法确认消息状态,请稍后重试'
};
}
}
// 显示撤回确认对话框
async function showRevokeConfirmationDialog(messageContent) {
return new Promise((resolve) => {
const dialog = document.createElement('div');
dialog.className = 'revoke-confirmation-dialog';
dialog.innerHTML = `
<div class="dialog-content">
<h3>确认撤回消息</h3>
<div class="message-preview">
<strong>消息内容:</strong>
<p>${messageContent.substring(0, 50)}${messageContent.length > 50 ? '...' : ''}</p>
</div>
<div class="warning-text">
⚠️ 撤回后消息将无法恢复,所有用户将看到"已撤回"提示
</div>
<div class="dialog-buttons">
<button class="confirm-revoke-btn" id="confirm-revoke">确认撤回</button>
<button class="cancel-revoke-btn" id="cancel-revoke">取消</button>
</div>
</div>
`;
document.body.appendChild(dialog);
// 添加事件监听
document.getElementById('confirm-revoke').onclick = () => {
document.body.removeChild(dialog);
resolve(true);
};
document.getElementById('cancel-revoke').onclick = () => {
document.body.removeChild(dialog);
resolve(false);
};
// 点击背景关闭
dialog.onclick = (e) => {
if (e.target === dialog) {
document.body.removeChild(dialog);
resolve(false);
}
};
});
}
// 执行消息撤回
async function performMessageRevocation(messageId) {
try {
showLoadingIndicator('正在撤回消息...');
const response = await feishuUserV1Message.revokeMessageAsync(messageId);
if (response.code === 0) {
showMessageNotification('消息已成功撤回', 'success');
// 从界面中移除消息
removeMessageFromUI(messageId);
// 记录撤回操作
logUserAction('message_revoked', {
messageId,
timestamp: new Date().toISOString()
});
} else {
handleRevokeError(response.code, response.msg);
}
} catch (error) {
console.error('撤回消息时发生错误:', error);
showMessageNotification('撤回失败,请检查网络连接', 'error');
} finally {
hideLoadingIndicator();
}
}
// 处理撤回错误
function handleRevokeError(errorCode, errorMessage) {
let userFriendlyMessage = '';
switch (errorCode) {
case 403:
userFriendlyMessage = '您只能撤回自己发送的消息';
break;
case 404:
userFriendlyMessage = '消息不存在或已被删除';
break;
case 400:
userFriendlyMessage = '消息发送时间过长,无法撤回';
break;
default:
userFriendlyMessage = `撤回失败: ${errorMessage}`;
}
showMessageNotification(userFriendlyMessage, 'error');
}
// 从界面中移除消息
function removeMessageFromUI(messageId) {
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
if (messageElement) {
// 添加撤回动画效果
messageElement.classList.add('message-revoking');
setTimeout(() => {
messageElement.replaceWith(createRevokedMessageElement(messageId));
}, 300);
}
}
// 创建已撤回消息元素
function createRevokedMessageElement(messageId) {
const revokedElement = document.createElement('div');
revokedElement.className = 'message revoked-message';
revokedElement.setAttribute('data-message-id', messageId);
revokedElement.innerHTML = `
<div class="revoked-indicator">
<span class="icon">↩️</span>
<span class="text">您撤回了一条消息</span>
</div>
`;
return revokedElement;
}
// 消息通知提示
function showMessageNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `message-notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('show');
}, 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
// 用户操作日志记录
function logUserAction(action, data) {
console.log('用户操作记录:', {
action,
data,
userId: currentUser.id,
timestamp: new Date().toISOString()
});
}
// 添加对应的CSS样式
const revokeStyles = `
<style>
.revoke-confirmation-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.dialog-content {
background: white;
border-radius: 8px;
padding: 24px;
max-width: 400px;
width: 90%;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.message-preview {
background: #f5f5f5;
padding: 12px;
border-radius: 4px;
margin: 16px 0;
}
.warning-text {
color: #ff9500;
font-size: 14px;
margin-bottom: 20px;
}
.dialog-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.confirm-revoke-btn, .cancel-revoke-btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.confirm-revoke-btn {
background: #ff3b30;
color: white;
}
.cancel-revoke-btn {
background: #e9ecef;
color: #333;
}
.message-revoking {
opacity: 0.5;
transition: opacity 0.3s ease;
}
.revoked-message {
text-align: center;
color: #999;
font-style: italic;
padding: 8px;
}
.revoked-indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.message-notification {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 16px;
border-radius: 4px;
color: white;
font-size: 14px;
z-index: 999;
opacity: 0;
transform: translateY(-20px);
transition: all 0.3s ease;
}
.message-notification.show {
opacity: 1;
transform: translateY(0);
}
.message-notification.success {
background: #34c759;
}
.message-notification.error {
background: #ff3b30;
}
.message-notification.warning {
background: #ff9500;
}
</style>
`;
// 注入样式
if (!document.querySelector('style[data-revoke-styles]')) {
const styleElement = document.createElement('style');
styleElement.setAttribute('data-revoke-styles', 'true');
styleElement.textContent = revokeStyles.replace('<style>', '').replace('</style>', '');
document.head.appendChild(styleElement);
}
// 使用示例
async function setupMessageRevokeHandlers() {
// 为所有可撤回的消息添加撤回按钮
const messageElements = document.querySelectorAll('.user-message[data-revocable="true"]');
messageElements.forEach(element => {
const messageId = element.getAttribute('data-message-id');
const messageContent = element.querySelector('.message-content').textContent;
const revokeButton = document.createElement('button');
revokeButton.className = 'revoke-button';
revokeButton.textContent = '撤回';
revokeButton.onclick = () => confirmAndRevokeMessage(messageId, messageContent);
element.querySelector('.message-actions').appendChild(revokeButton);
});
}
// 初始化撤回功能
document.addEventListener('DOMContentLoaded', setupMessageRevokeHandlers);函数名称:添加消息表情回复
函数签名:
csharp
Task<FeishuApiResult<ReactionResult>?> AddMessageReactionsAsync(
[Path] string message_id,
[Body] ReactionRequest sendMessageRequest,
CancellationToken cancellationToken = default);认证:用户令牌
参数:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|---|---|---|---|---|
| message_id | string | ✅ 必填 | 待添加表情回复的消息ID | "om_dc13264520392913993dd051dba21dcf" |
| sendMessageRequest | ReactionRequest | ✅ 必填 | 添加消息表情回复请求体 | 见下方结构 |
| sendMessageRequest.ReactionType.EmojiType | string | ✅ 必填 | emoji类型 | "thumbs_up" |
| cancellationToken | CancellationToken | ⚪ 可选 | 取消操作令牌 | - |
响应:
成功响应示例:
json
{
"code": 0,
"msg": "success",
"data": {
"reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw*************sNa8dHVplEzzSfJVUVLMLcS_",
"emoji_type": "thumbs_up"
}
}常见错误响应:
json
{
"code": 404,
"msg": "消息不存在"
}json
{
"code": 400,
"msg": "不支持的表情类型"
}说明:
- 用户可以对任何消息添加表情回复
- 支持的表情类型参考飞书官方文档
- 同一用户可以对同一条消息添加多种表情
代码示例:
javascript
// 用户表情回复系统
class MessageReactionSystem {
constructor() {
this.emojiPicker = null;
this.reactionCache = new Map(); // 缓存用户的表情回复
this.init();
}
init() {
this.createEmojiPicker();
this.setupReactionButtons();
this.loadUserReactions();
}
// 创建表情选择器
createEmojiPicker() {
this.emojiPicker = document.createElement('div');
this.emojiPicker.className = 'emoji-picker';
this.emojiPicker.innerHTML = `
<div class="emoji-picker-header">
<span>选择表情</span>
<button class="close-picker">×</button>
</div>
<div class="emoji-grid">
<button data-emoji="thumbs_up" class="emoji-btn">👍</button>
<button data-emoji="heart" class="emoji-btn">❤️</button>
<button data-emoji="laugh" class="emoji-btn">😂</button>
<button data-emoji="wow" class="emoji-btn">😮</button>
<button data-emoji="sad" class="emoji-btn">😢</button>
<button data-emoji="angry" class="emoji-btn">😡</button>
<button data-emoji="fire" class="emoji-btn">🔥</button>
<button data-emoji="clap" class="emoji-btn">👏</button>
</div>
`;
document.body.appendChild(this.emojiPicker);
this.setupEmojiPickerEvents();
}
// 设置表情选择器事件
setupEmojiPickerEvents() {
// 关闭按钮
this.emojiPicker.querySelector('.close-picker').onclick = () => {
this.hideEmojiPicker();
};
// 表情按钮点击
this.emojiPicker.querySelectorAll('.emoji-btn').forEach(btn => {
btn.onclick = () => {
const emoji = btn.getAttribute('data-emoji');
const messageId = this.emojiPicker.getAttribute('data-message-id');
if (messageId) {
this.addReaction(messageId, emoji);
}
this.hideEmojiPicker();
};
});
// 点击外部关闭
this.emojiPicker.onclick = (e) => {
if (e.target === this.emojiPicker) {
this.hideEmojiPicker();
}
};
}
// 显示表情选择器
showEmojiPicker(messageId, targetElement) {
const rect = targetElement.getBoundingClientRect();
this.emojiPicker.setAttribute('data-message-id', messageId);
this.emojiPicker.style.display = 'block';
this.emojiPicker.style.top = `${rect.bottom + 5}px`;
this.emojiPicker.style.style.left = `${rect.left}px`;
// 检查是否超出视窗
setTimeout(() => {
const pickerRect = this.emojiPicker.getBoundingClientRect();
if (pickerRect.right > window.innerWidth) {
this.emojiPicker.style.left = `${rect.right - pickerRect.width}px`;
}
if (pickerRect.bottom > window.innerHeight) {
this.emojiPicker.style.top = `${rect.top - pickerRect.height - 5}px`;
}
}, 0);
}
// 隐藏表情选择器
hideEmojiPicker() {
this.emojiPicker.style.display = 'none';
this.emojiPicker.removeAttribute('data-message-id');
}
// 设置消息的反应按钮
setupReactionButtons() {
document.querySelectorAll('.message').forEach(messageElement => {
const messageId = messageElement.getAttribute('data-message-id');
const existingButton = messageElement.querySelector('.reaction-button');
if (!existingButton) {
const reactionButton = document.createElement('button');
reactionButton.className = 'reaction-button';
reactionButton.innerHTML = '😊';
reactionButton.title = '添加表情回复';
reactionButton.onclick = (e) => {
e.stopPropagation();
this.showEmojiPicker(messageId, reactionButton);
};
const actionsContainer = messageElement.querySelector('.message-actions');
if (actionsContainer) {
actionsContainer.appendChild(reactionButton);
}
}
// 显示现有表情回复
this.displayExistingReactions(messageId, messageElement);
});
}
// 添加表情回复
async addReaction(messageId, emojiType) {
try {
// 检查是否已经添加过相同的表情
const existingReaction = this.reactionCache.get(`${messageId}_${emojiType}`);
if (existingReaction) {
this.showMessageNotification('您已经添加过这个表情了', 'info');
return;
}
this.showLoadingIndicator('正在添加表情...');
const reactionRequest = {
reaction_type: {
emoji_type: emojiType
}
};
const response = await feishuUserV1Message.addMessageReactionsAsync(
messageId,
reactionRequest
);
if (response.code === 0) {
// 缓存表情回复
this.reactionCache.set(`${messageId}_${emojiType}`, response.data.reaction_id);
// 更新界面
this.updateReactionDisplay(messageId, emojiType, 'add');
// 显示成功提示
this.showReactionSuccessAnimation(messageId, emojiType);
console.log(`表情回复添加成功: ${messageId} -> ${emojiType}`);
} else {
this.showMessageNotification(`添加表情失败: ${response.msg}`, 'error');
}
} catch (error) {
console.error('添加表情回复时发生错误:', error);
this.showMessageNotification('添加表情失败,请检查网络连接', 'error');
} finally {
this.hideLoadingIndicator();
}
}
// 显示现有表情回复
async displayExistingReactions(messageId, messageElement) {
try {
// 获取消息的现有表情回复
const reactions = await this.getMessageReactions(messageId);
if (reactions.length > 0) {
this.renderReactions(messageElement, reactions);
}
} catch (error) {
console.error('获取现有表情回复失败:', error);
}
}
// 获取消息表情回复列表
async getMessageReactions(messageId) {
// 这里应该调用GetMessageReactionsPageListAsync接口
// 简化示例,返回模拟数据
return [];
}
// 渲染表情回复
renderReactions(messageElement, reactions) {
let reactionsContainer = messageElement.querySelector('.reactions-container');
if (!reactionsContainer) {
reactionsContainer = document.createElement('div');
reactionsContainer.className = 'reactions-container';
messageElement.appendChild(reactionsContainer);
}
// 按表情类型分组统计
const reactionGroups = reactions.reduce((groups, reaction) => {
const emoji = reaction.emoji_type;
if (!groups[emoji]) {
groups[emoji] = {
count: 0,
users: [],
reactionIds: []
};
}
groups[emoji].count++;
groups[emoji].users.push(reaction.user);
groups[emoji].reactionIds.push(reaction.reaction_id);
return groups;
}, {});
// 渲染表情分组
reactionsContainer.innerHTML = Object.entries(reactionGroups)
.map(([emoji, group]) => `
<div class="reaction-item" data-emoji="${emoji}" data-message-id="${messageElement.getAttribute('data-message-id')}">
<span class="reaction-emoji">${this.getEmojiDisplay(emoji)}</span>
<span class="reaction-count">${group.count}</span>
</div>
`).join('');
// 添加点击事件
reactionsContainer.querySelectorAll('.reaction-item').forEach(item => {
item.onclick = () => {
const emoji = item.getAttribute('data-emoji');
const messageId = item.getAttribute('data-message-id');
this.showReactionDetails(messageId, emoji);
};
});
}
// 获取表情显示
getEmojiDisplay(emojiType) {
const emojiMap = {
'thumbs_up': '👍',
'heart': '❤️',
'laugh': '😂',
'wow': '😮',
'sad': '😢',
'angry': '😡',
'fire': '🔥',
'clap': '👏'
};
return emojiMap[emojiType] || emojiType;
}
// 更新表情显示
updateReactionDisplay(messageId, emojiType, action) {
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
if (!messageElement) return;
if (action === 'add') {
// 如果没有表情容器,先添加表情按钮
this.setupReactionButtons();
}
}
// 显示表情成功动画
showReactionSuccessAnimation(messageId, emojiType) {
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
if (!messageElement) return;
const animation = document.createElement('div');
animation.className = 'reaction-success-animation';
animation.innerHTML = this.getEmojiDisplay(emojiType);
messageElement.appendChild(animation);
setTimeout(() => {
animation.classList.add('show');
}, 100);
setTimeout(() => {
animation.remove();
}, 1500);
}
// 显示表情详情
showReactionDetails(messageId, emojiType) {
// 这里可以实现显示谁使用了该表情的功能
console.log(`显示表情详情: ${messageId} -> ${emojiType}`);
}
// 加载用户的表情回复缓存
loadUserReactions() {
// 从localStorage加载用户的表情回复记录
const cached = localStorage.getItem('userMessageReactions');
if (cached) {
const data = JSON.parse(cached);
this.reactionCache = new Map(Object.entries(data));
}
}
// 保存表情回复缓存
saveUserReactions() {
const data = Object.fromEntries(this.reactionCache);
localStorage.setItem('userMessageReactions', JSON.stringify(data));
}
// 显示消息通知
showMessageNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `reaction-notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('show');
}, 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 2000);
}
// 显示加载指示器
showLoadingIndicator(message = '加载中...') {
const existing = document.querySelector('.reaction-loading');
if (existing) existing.remove();
const loading = document.createElement('div');
loading.className = 'reaction-loading';
loading.innerHTML = `
<div class="spinner"></div>
<span>${message}</span>
`;
document.body.appendChild(loading);
}
// 隐藏加载指示器
hideLoadingIndicator() {
const loading = document.querySelector('.reaction-loading');
if (loading) {
loading.remove();
}
}
}
// 添加对应的CSS样式
const reactionStyles = `
<style data-reaction-styles>
.emoji-picker {
position: absolute;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
display: none;
min-width: 280px;
}
.emoji-picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e9ecef;
font-weight: 500;
}
.close-picker {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #666;
}
.emoji-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
padding: 16px;
}
.emoji-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
padding: 8px;
border-radius: 4px;
transition: background 0.2s;
}
.emoji-btn:hover {
background: #f0f0f0;
}
.reaction-button {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: background 0.2s;
}
.reaction-button:hover {
background: #f0f0f0;
}
.reactions-container {
display: flex;
gap: 8px;
margin-top: 8px;
flex-wrap: wrap;
}
.reaction-item {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: #f0f0f0;
border-radius: 12px;
cursor: pointer;
transition: background 0.2s;
font-size: 12px;
}
.reaction-item:hover {
background: #e0e0e0;
}
.reaction-emoji {
font-size: 16px;
}
.reaction-count {
font-weight: 500;
color: #666;
}
.reaction-success-animation {
position: absolute;
font-size: 32px;
pointer-events: none;
animation: floatUp 1.5s ease-out forwards;
z-index: 100;
}
@keyframes floatUp {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
100% {
opacity: 0;
transform: translateY(-50px) scale(1.5);
}
}
.reaction-notification {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 16px;
border-radius: 4px;
color: white;
font-size: 14px;
z-index: 999;
opacity: 0;
transform: translateY(-20px);
transition: all 0.3s ease;
}
.reaction-notification.show {
opacity: 1;
transform: translateY(0);
}
.reaction-notification.success {
background: #34c759;
}
.reaction-notification.error {
background: #ff3b30;
}
.reaction-notification.info {
background: #007aff;
}
.reaction-loading {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
z-index: 1001;
}
.spinner {
width: 24px;
height: 24px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
`;
// 初始化表情系统
document.addEventListener('DOMContentLoaded', () => {
// 注入样式
if (!document.querySelector('style[data-reaction-styles]')) {
const styleElement = document.createElement('style');
styleElement.setAttribute('data-reaction-styles', 'true');
styleElement.textContent = reactionStyles.replace('<style data-reaction-styles>', '').replace('</style>', '');
document.head.appendChild(styleElement);
}
// 初始化表情系统
window.reactionSystem = new MessageReactionSystem();
});由于用户令牌版本的接口主要继承自父接口,以上展示了核心功能的详细文档。用户版本相比租户版本的特点:
✅ 权限控制优化:
- 用户只能撤回自己发送的消息
- 所有操作都经过严格的权限验证
- 友好的权限错误提示
✅ 用户体验优先:
- 直观的消息撤回确认对话框
- 丰富的表情选择器界面
- 实时的表情回复动画效果
✅ 界面交互增强:
- 响应式的表情回复显示
- 流畅的动画过渡效果
- 智能的位置调整逻辑
✅ 本地缓存优化:
- 用户表情回复本地缓存
- 减少不必要的API调用
- 离线状态下的基本功能
✅ 错误处理完善:
- 友好的错误消息提示
- 详细的操作日志记录
- 优雅的降级处理
两个文档都已保存到项目的docs目录下,文件名按照要求去掉了接口前缀"I"。开发者可以通过这些文档快速理解和使用飞书消息管理API,构建功能丰富的即时通讯应用。