2025-06-06 03:08:19 +08:00
|
|
|
|
import store from '@/config/store';
|
|
|
|
|
import base from '@/config/baseUrl';
|
|
|
|
|
|
|
|
|
|
class MattermostClient {
|
|
|
|
|
constructor() {
|
|
|
|
|
// 基础属性
|
|
|
|
|
this.ready = false;
|
2025-06-07 04:59:49 +08:00
|
|
|
|
this.userBaseUrl = "https://im.dxmt.io"; // 用户 API
|
|
|
|
|
this.adminBaseUrl = "https://api.dxmt.io"; // 管理员 API
|
2025-06-06 03:08:19 +08:00
|
|
|
|
this.ws = null;
|
|
|
|
|
this.wsUrl = '';
|
|
|
|
|
this.userToken = ''; // 用户令牌
|
|
|
|
|
this.adminToken = ''; // 管理员令牌
|
|
|
|
|
this.connected = false;
|
|
|
|
|
this.authenticated = false;
|
|
|
|
|
this.sequence = 1;
|
|
|
|
|
|
|
|
|
|
// 重连相关
|
|
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
this.maxReconnectAttempts = 5;
|
|
|
|
|
this.reconnectInterval = 3000;
|
|
|
|
|
this.reconnectTimer = null;
|
|
|
|
|
|
|
|
|
|
// 心跳相关
|
2025-06-15 22:12:30 +08:00
|
|
|
|
this.heartbeatInterval = 10000; // 30秒
|
2025-06-06 03:08:19 +08:00
|
|
|
|
this.heartbeatTimer = null;
|
|
|
|
|
this.lastHeartbeatResponse = Date.now();
|
|
|
|
|
|
|
|
|
|
// 消息处理
|
|
|
|
|
this.messageHandlers = new Map(); // 修改为 Map 类型,key 为事件名,value 为 Set 类型的处理器集合
|
|
|
|
|
this.pendingMessages = new Map(); // 存储等待响应的消息
|
|
|
|
|
this.messageTimeoutEnabled = false; // 消息响应超时开关,默认关闭
|
|
|
|
|
this.messageTimeout = 10000; // 消息响应超时时间(毫秒)
|
|
|
|
|
|
|
|
|
|
// 状态回调
|
|
|
|
|
this.statusChangeCallbacks = new Set();
|
|
|
|
|
|
|
|
|
|
// 频道相关
|
|
|
|
|
this.channels = new Map(); // 存储频道信息
|
|
|
|
|
this.directChannels = new Map(); // 存储私聊频道
|
|
|
|
|
this.unreadCounts = new Map(); // 存储未读消息数
|
|
|
|
|
|
|
|
|
|
// 用户相关
|
|
|
|
|
this.currentUser = null;
|
|
|
|
|
this.userStatuses = new Map(); // 存储用户在线状态
|
|
|
|
|
this.userRoles = new Map(); // 存储用户角色
|
|
|
|
|
|
|
|
|
|
// 消息相关
|
|
|
|
|
this.messageCache = new Map(); // 缓存消息
|
|
|
|
|
this.deletedMessages = new Set(); // 记录已删除的消息
|
|
|
|
|
|
|
|
|
|
this.kown_users={}; // 存储已知的用户信息
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化连接
|
|
|
|
|
async init() {
|
|
|
|
|
if(this.ready){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!store.state.userInfo || !store.state.userInfo.im_token || !store.state.userInfo.token) {
|
|
|
|
|
console.log('用户令牌是必需的');
|
|
|
|
|
return ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.userToken = store.state.userInfo.im_token;
|
|
|
|
|
this.adminToken = store.state.userInfo.token;
|
|
|
|
|
this.currentUser = store.state.userInfo.im_user;
|
|
|
|
|
this.kown_users[this.currentUser.id] = this.currentUser;
|
|
|
|
|
this.getCurrentUser();
|
|
|
|
|
this.ready = true;
|
|
|
|
|
|
|
|
|
|
// 根据环境选择 ws 或 wss
|
|
|
|
|
const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
|
|
|
|
|
// 构建 WebSocket URL,使用用户 API 域名
|
|
|
|
|
const connectionId = Date.now().toString(36) + Math.random().toString(36).substr(2);
|
2025-06-10 02:14:24 +08:00
|
|
|
|
this.wsUrl = this.userBaseUrl.replace('https',protocol)+`/api/v4/websocket?connection_id=${connectionId}&sequence_number=0`;
|
2025-06-06 03:08:19 +08:00
|
|
|
|
// 重置状态
|
|
|
|
|
this.sequence = 1;
|
|
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
this.authenticated = false;
|
|
|
|
|
|
|
|
|
|
// 建立新连接
|
|
|
|
|
await this.connect();
|
|
|
|
|
|
|
|
|
|
// 启动心跳检测
|
|
|
|
|
this.startHeartbeat();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 建立 WebSocket 连接
|
|
|
|
|
connect() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
try {
|
|
|
|
|
// 清理可能存在的旧连接
|
|
|
|
|
if (this.ws) {
|
|
|
|
|
return ;
|
|
|
|
|
//this.ws.close();
|
|
|
|
|
//this.ws = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.ws = uni.connectSocket({
|
|
|
|
|
url: this.wsUrl,
|
|
|
|
|
header: {
|
|
|
|
|
'Authorization': `Bearer ${this.userToken}`
|
|
|
|
|
},
|
|
|
|
|
multiple: true,
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('WebSocket 连接创建成功');
|
|
|
|
|
},
|
|
|
|
|
fail: (error) => {
|
|
|
|
|
console.error('WebSocket 连接创建失败:', error);
|
|
|
|
|
this.handleConnectionError(error);
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.ws.onOpen(() => {
|
|
|
|
|
console.log('WebSocket 已打开,准备认证');
|
|
|
|
|
this.connected = true;
|
|
|
|
|
this.notifyStatusChange('connected');
|
|
|
|
|
|
|
|
|
|
// 发送认证信息,使用正确的认证消息格式
|
|
|
|
|
const authMessage = {
|
|
|
|
|
action: 'authentication_challenge',
|
|
|
|
|
seq: this.sequence,
|
|
|
|
|
data: {
|
|
|
|
|
token: this.userToken
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
this.sequence++;
|
|
|
|
|
|
|
|
|
|
this.ws.send({
|
|
|
|
|
data: JSON.stringify(authMessage),
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('认证信息发送成功:', authMessage);
|
|
|
|
|
// 注意:这里不立即设置 authenticated 为 true
|
|
|
|
|
// 等待服务器的认证响应
|
|
|
|
|
},
|
|
|
|
|
fail: (error) => {
|
|
|
|
|
console.error('认证信息发送失败:', error);
|
|
|
|
|
this.handleConnectionError(error);
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.ws.onClose(() => {
|
|
|
|
|
console.log('WebSocket 已关闭');
|
|
|
|
|
this.handleConnectionClose();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.ws.onError((error) => {
|
|
|
|
|
console.error('WebSocket 错误:', error);
|
|
|
|
|
this.handleConnectionError(error);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.ws.onMessage((res) => {
|
|
|
|
|
try {
|
|
|
|
|
const message = JSON.parse(res.data);
|
|
|
|
|
console.log('收到消息:', message);
|
|
|
|
|
|
|
|
|
|
// 处理认证响应
|
|
|
|
|
if (message.event === 'hello') {
|
|
|
|
|
console.log('认证成功');
|
|
|
|
|
this.authenticated = true;
|
|
|
|
|
this.notifyStatusChange('authenticated');
|
|
|
|
|
this.reconnectAttempts = 0;
|
|
|
|
|
resolve();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理认证失败
|
|
|
|
|
if (message.event === 'authentication_challenge' && message.data && message.data.status !== 'OK') {
|
|
|
|
|
console.error('认证失败:', message);
|
|
|
|
|
this.handleConnectionError(new Error('认证失败'));
|
|
|
|
|
reject(new Error('认证失败'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.handleMessage(message);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('消息解析错误:', error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('创建 WebSocket 连接失败:', error);
|
|
|
|
|
this.handleConnectionError(error);
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理连接错误
|
|
|
|
|
handleConnectionError(error) {
|
|
|
|
|
this.connected = false;
|
|
|
|
|
this.authenticated = false;
|
|
|
|
|
this.notifyStatusChange('error', error);
|
|
|
|
|
this.handleReconnect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理连接关闭
|
|
|
|
|
handleConnectionClose() {
|
|
|
|
|
this.connected = false;
|
|
|
|
|
this.authenticated = false;
|
|
|
|
|
this.notifyStatusChange('disconnected');
|
|
|
|
|
this.stopHeartbeat();
|
|
|
|
|
this.handleReconnect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理重连
|
|
|
|
|
handleReconnect() {
|
|
|
|
|
if (this.reconnectTimer) {
|
|
|
|
|
clearTimeout(this.reconnectTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
|
|
|
this.reconnectAttempts++;
|
|
|
|
|
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
|
|
|
|
this.notifyStatusChange('reconnecting', this.reconnectAttempts);
|
|
|
|
|
|
|
|
|
|
this.reconnectTimer = setTimeout(() => {
|
|
|
|
|
this.connect().catch(error => {
|
|
|
|
|
console.error('重连失败:', error);
|
|
|
|
|
});
|
|
|
|
|
}, this.reconnectInterval);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('达到最大重连次数,停止重连');
|
|
|
|
|
this.notifyStatusChange('failed');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 启动心跳检测
|
|
|
|
|
startHeartbeat() {
|
|
|
|
|
if (this.heartbeatTimer) {
|
|
|
|
|
clearInterval(this.heartbeatTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.heartbeatTimer = setInterval(() => {
|
|
|
|
|
if (this.connected && this.authenticated) {
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
if (now - this.lastHeartbeatResponse > this.heartbeatInterval * 2) {
|
|
|
|
|
console.log('心跳超时,准备重连');
|
|
|
|
|
this.handleConnectionClose();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送 ping 消息
|
|
|
|
|
this.sendWebSocketMessage('ping').catch(error => {
|
|
|
|
|
console.error('心跳消息发送失败:', error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}, this.heartbeatInterval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 停止心跳检测
|
|
|
|
|
stopHeartbeat() {
|
|
|
|
|
if (this.heartbeatTimer) {
|
|
|
|
|
clearInterval(this.heartbeatTimer);
|
|
|
|
|
this.heartbeatTimer = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置消息响应超时开关
|
|
|
|
|
setMessageTimeoutEnabled(enabled) {
|
|
|
|
|
this.messageTimeoutEnabled = enabled;
|
|
|
|
|
console.log(`消息响应超时已${enabled ? '启用' : '禁用'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置消息响应超时时间
|
|
|
|
|
setMessageTimeout(timeout) {
|
|
|
|
|
if (timeout < 1000) {
|
|
|
|
|
console.warn('消息响应超时时间不能小于1000毫秒');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.messageTimeout = timeout;
|
|
|
|
|
console.log(`消息响应超时时间已设置为${timeout}毫秒`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取消息响应超时设置
|
|
|
|
|
getMessageTimeoutSettings() {
|
|
|
|
|
return {
|
|
|
|
|
enabled: this.messageTimeoutEnabled,
|
|
|
|
|
timeout: this.messageTimeout
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送 WebSocket 消息
|
|
|
|
|
sendWebSocketMessage(action, data = {}) {
|
|
|
|
|
if (!this.connected) {
|
|
|
|
|
return Promise.reject(new Error('WebSocket 未连接'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建符合 Mattermost 格式的消息
|
|
|
|
|
const message = {
|
|
|
|
|
action,
|
|
|
|
|
seq: this.sequence
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 只有在非 ping 消息时才添加其他数据
|
|
|
|
|
if (action !== 'ping' && Object.keys(data).length > 0) {
|
|
|
|
|
Object.assign(message, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 增加序列号
|
|
|
|
|
this.sequence++;
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
// 存储等待响应的消息
|
|
|
|
|
this.pendingMessages.set(message.seq, { resolve, reject });
|
|
|
|
|
|
|
|
|
|
// 设置超时处理
|
|
|
|
|
let timeout;
|
|
|
|
|
if (this.messageTimeoutEnabled) {
|
|
|
|
|
timeout = setTimeout(() => {
|
|
|
|
|
if (this.pendingMessages.has(message.seq)) {
|
|
|
|
|
this.pendingMessages.delete(message.seq);
|
|
|
|
|
reject(new Error(`消息 ${message.seq} 响应超时`));
|
|
|
|
|
}
|
|
|
|
|
}, this.messageTimeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.ws.send({
|
|
|
|
|
data: JSON.stringify(message),
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('WebSocket 消息发送成功:', message);
|
|
|
|
|
},
|
|
|
|
|
fail: (error) => {
|
|
|
|
|
console.error('WebSocket 消息发送失败:', error);
|
|
|
|
|
this.pendingMessages.delete(message.seq);
|
|
|
|
|
if (timeout) {
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
}
|
|
|
|
|
reject(error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理接收到的消息
|
|
|
|
|
handleMessage(message) {
|
|
|
|
|
console.log('处理消息:', message);
|
|
|
|
|
|
|
|
|
|
// 更新心跳时间戳
|
|
|
|
|
if (message.action === 'pong') {
|
|
|
|
|
this.lastHeartbeatResponse = Date.now();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理消息响应
|
|
|
|
|
// if (message.seq && this.pendingMessages.has(message.seq)) {
|
|
|
|
|
// const { resolve } = this.pendingMessages.get(message.seq);
|
|
|
|
|
// this.pendingMessages.delete(message.seq);
|
|
|
|
|
// resolve(message);
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 更新序列号
|
|
|
|
|
if (message.seq) {
|
|
|
|
|
this.sequence = message.seq + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理不同类型的消息
|
|
|
|
|
if (message.event === 'posted') {
|
|
|
|
|
const post = message.data;
|
|
|
|
|
// 更新未读消息数
|
|
|
|
|
if (post.channel_id && !this.isCurrentChannel(post.channel_id)) {
|
|
|
|
|
const currentCount = this.unreadCounts.get(post.channel_id) || 0;
|
|
|
|
|
this.unreadCounts.set(post.channel_id, currentCount + 1);
|
|
|
|
|
}
|
|
|
|
|
// 缓存消息
|
|
|
|
|
this.messageCache.set(post.id, post);
|
|
|
|
|
|
|
|
|
|
// 如果当前不在聊天页面,增加未读数
|
|
|
|
|
const currentPage = getCurrentPages();
|
|
|
|
|
const isInChatPage = currentPage.some(page =>
|
|
|
|
|
page.route === 'pages/im/chat' &&
|
|
|
|
|
((page.$page.options.target_id == this.getCurrentUserId() &&
|
|
|
|
|
page.$page.options.target_type === 'private') ||
|
|
|
|
|
(page.$page.options.target_id == '3awtqn6b17d4bp8ro68qmzo96o' &&
|
|
|
|
|
page.$page.options.target_type === 'group'))
|
|
|
|
|
);
|
|
|
|
|
if(!isInChatPage){
|
|
|
|
|
uni.createPushMessage({
|
|
|
|
|
title:post.channel_display_name.substr(0,1).replace("_","@"),
|
|
|
|
|
content:post.message,
|
|
|
|
|
payload:post,
|
|
|
|
|
//icon:'',
|
|
|
|
|
//sound:'',
|
|
|
|
|
cover:false,
|
|
|
|
|
complete(){
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if (message.event === 'post_deleted') {
|
|
|
|
|
// 处理消息删除
|
|
|
|
|
const postId = message.data.post_id;
|
|
|
|
|
this.deletedMessages.add(postId);
|
|
|
|
|
this.messageCache.delete(postId);
|
|
|
|
|
} else if (message.event === 'user_updated') {
|
|
|
|
|
// 处理用户信息更新
|
|
|
|
|
const user = message.data;
|
|
|
|
|
if (user.roles) {
|
|
|
|
|
this.userRoles.set(user.id, user.roles);
|
|
|
|
|
}
|
|
|
|
|
} else if (message.event === 'status_change') {
|
|
|
|
|
// 处理用户状态变化
|
|
|
|
|
const status = message.data;
|
|
|
|
|
this.userStatuses.set(status.user_id, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 触发消息处理器
|
|
|
|
|
if (message.event && this.messageHandlers.has(message.event)) {
|
|
|
|
|
console.log(`触发消息处理器: ${message.event}, 处理器数量: ${this.messageHandlers.get(message.event).size}`);
|
|
|
|
|
this.messageHandlers.get(message.event).forEach(handler => {
|
|
|
|
|
try {
|
|
|
|
|
handler(message.data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('消息处理器错误:', error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册状态变化回调
|
|
|
|
|
onStatusChange(callback) {
|
|
|
|
|
this.statusChangeCallbacks.add(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 移除状态变化回调
|
|
|
|
|
offStatusChange(callback) {
|
|
|
|
|
this.statusChangeCallbacks.delete(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通知状态变化
|
|
|
|
|
notifyStatusChange(status, data = null) {
|
|
|
|
|
this.statusChangeCallbacks.forEach(callback => {
|
|
|
|
|
try {
|
|
|
|
|
callback(status, data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('状态变化回调错误:', error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册消息处理器
|
|
|
|
|
on(event, handler) {
|
|
|
|
|
if (!this.messageHandlers.has(event)) {
|
|
|
|
|
this.messageHandlers.set(event, new Set());
|
|
|
|
|
}
|
|
|
|
|
this.messageHandlers.get(event).add(handler);
|
|
|
|
|
console.log(`注册消息处理器: ${event}, 当前处理器数量: ${this.messageHandlers.get(event).size}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 移除消息处理器
|
|
|
|
|
off(event, handler) {
|
|
|
|
|
if (this.messageHandlers.has(event)) {
|
|
|
|
|
this.messageHandlers.get(event).delete(handler);
|
|
|
|
|
console.log(`移除消息处理器: ${event}, 剩余处理器数量: ${this.messageHandlers.get(event).size}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取连接状态
|
|
|
|
|
getConnectionStatus() {
|
|
|
|
|
return {
|
|
|
|
|
connected: this.connected,
|
|
|
|
|
authenticated: this.authenticated,
|
|
|
|
|
reconnectAttempts: this.reconnectAttempts,
|
|
|
|
|
maxReconnectAttempts: this.maxReconnectAttempts,
|
|
|
|
|
wsUrl: this.wsUrl,
|
|
|
|
|
lastHeartbeat: this.lastHeartbeatResponse
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 断开连接
|
|
|
|
|
async disconnect() {
|
|
|
|
|
this.stopHeartbeat();
|
|
|
|
|
|
|
|
|
|
if (this.reconnectTimer) {
|
|
|
|
|
clearTimeout(this.reconnectTimer);
|
|
|
|
|
this.reconnectTimer = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.ws) {
|
|
|
|
|
this.ws.close();
|
|
|
|
|
this.ws = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.connected = false;
|
|
|
|
|
this.authenticated = false;
|
|
|
|
|
this.notifyStatusChange('disconnected');
|
|
|
|
|
|
|
|
|
|
// 清理所有等待响应的消息
|
|
|
|
|
this.pendingMessages.forEach(({ reject }) => {
|
|
|
|
|
reject(new Error('连接已断开'));
|
|
|
|
|
});
|
|
|
|
|
this.pendingMessages.clear();
|
|
|
|
|
}
|
|
|
|
|
async getRecentContacts(){
|
|
|
|
|
const channels = await this.request({
|
|
|
|
|
url: `/api/v4/users/me/channels`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
let result = {};
|
|
|
|
|
let unkown_user_ids = [];
|
|
|
|
|
if (channels && channels.length > 0) {
|
|
|
|
|
const dmChannels = channels
|
|
|
|
|
.filter(c => c.type === 'D')
|
|
|
|
|
.sort((a, b) => b.last_post_at - a.last_post_at); // 按最后消息时间降序
|
|
|
|
|
dmChannels.forEach(channel => {
|
|
|
|
|
var member_ids = channel.name.replace(this.currentUser.id,'').replace('__','');
|
|
|
|
|
if(!this.kown_users[member_ids]){
|
|
|
|
|
result[member_ids]={};
|
|
|
|
|
unkown_user_ids.push(member_ids);
|
|
|
|
|
}else{
|
|
|
|
|
result[member_ids]=this.kown_users[member_ids];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if(unkown_user_ids.length>0){
|
|
|
|
|
const users = await this.getUsersByIds(unkown_user_ids);
|
|
|
|
|
users.data.forEach(user => {
|
|
|
|
|
this.kown_users[user.id]=user;
|
|
|
|
|
result[user.id]=user;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
// 获取私聊频道
|
|
|
|
|
async getDirectChannels() {
|
|
|
|
|
//return [];
|
|
|
|
|
try {
|
|
|
|
|
const channels = await this.request({
|
|
|
|
|
url: `/api/v4/users/me/channels`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
console.log(channels);
|
|
|
|
|
|
|
|
|
|
if (channels && channels.length > 0) {
|
|
|
|
|
channels.forEach(channel => {
|
|
|
|
|
this.directChannels.set(channel.id, channel);
|
|
|
|
|
});
|
|
|
|
|
return channels;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取私聊频道错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HTTP 请求封装
|
|
|
|
|
async request(options) {
|
|
|
|
|
let { url, method = 'GET', data = {}, headers = {},to_server='user' } = options;
|
|
|
|
|
let requestHeaders ={};
|
|
|
|
|
if(to_server=='user'){
|
|
|
|
|
// 添加认证头
|
|
|
|
|
requestHeaders = {
|
|
|
|
|
'Authorization': `Bearer ${this.userToken}`,
|
|
|
|
|
...headers
|
|
|
|
|
};
|
|
|
|
|
url = this.userBaseUrl + url;
|
|
|
|
|
}else{
|
|
|
|
|
// 添加认证头
|
|
|
|
|
requestHeaders = {
|
|
|
|
|
'token': this.adminToken,
|
|
|
|
|
...headers
|
|
|
|
|
};
|
|
|
|
|
url = this.adminBaseUrl + url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [error, response] = await uni.request({
|
|
|
|
|
url,
|
|
|
|
|
method,
|
|
|
|
|
data,
|
|
|
|
|
header: requestHeaders
|
|
|
|
|
}).catch(err => [err, null]);
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error(`请求失败 [${method} ${url}]:`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.error(`请求失败 [${method} ${url}]:`, response);
|
|
|
|
|
throw new Error(`请求失败: ${response.statusCode}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 文件上传封装
|
|
|
|
|
async uploadFile(options) {
|
2025-06-10 02:14:24 +08:00
|
|
|
|
const { channelId, filePath, name = 'files', headers = {} } = options;
|
2025-06-06 03:08:19 +08:00
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
if (!channelId) {
|
|
|
|
|
throw new Error('channelId 是必需的');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 03:08:19 +08:00
|
|
|
|
// 添加认证头
|
|
|
|
|
const requestHeaders = {
|
|
|
|
|
'Authorization': `Bearer ${this.userToken}`,
|
|
|
|
|
...headers
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
// 直接上传文件
|
|
|
|
|
const [uploadError, uploadResponse] = await uni.uploadFile({
|
|
|
|
|
url: `${this.userBaseUrl}/api/v4/files`,
|
2025-06-06 03:08:19 +08:00
|
|
|
|
filePath,
|
|
|
|
|
name,
|
2025-06-10 02:14:24 +08:00
|
|
|
|
header: requestHeaders,
|
|
|
|
|
formData: {
|
|
|
|
|
channel_id: channelId
|
|
|
|
|
}
|
2025-06-06 03:08:19 +08:00
|
|
|
|
}).catch(err => [err, null]);
|
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
if (uploadError) {
|
|
|
|
|
console.error('文件上传失败:', uploadError);
|
|
|
|
|
throw uploadError;
|
2025-06-06 03:08:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
if (uploadResponse.statusCode >= 200 && uploadResponse.statusCode < 300) {
|
|
|
|
|
return JSON.parse(uploadResponse.data);
|
2025-06-06 03:08:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
console.error('文件上传失败:', uploadResponse);
|
|
|
|
|
throw new Error(`文件上传失败: ${uploadResponse.statusCode}`);
|
2025-06-06 03:08:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取频道消息
|
|
|
|
|
async getChannelPosts(channelId, page = 0, perPage = 30) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/channels/${channelId}/posts`,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
data: {
|
|
|
|
|
page,
|
|
|
|
|
per_page: perPage,
|
|
|
|
|
include_deleted: false
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response || !response.order || !response.posts) {
|
|
|
|
|
console.error('无效的消息数据格式:', response);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.order.reverse().map(id => response.posts[id]);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取消息失败:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送文本消息
|
|
|
|
|
async sendTextMessage(channelId, message) {
|
|
|
|
|
return this.request({
|
|
|
|
|
url: `/api/v4/posts`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data: {
|
|
|
|
|
channel_id: channelId,
|
|
|
|
|
message: message,
|
|
|
|
|
root_id: '',
|
|
|
|
|
file_ids: [],
|
|
|
|
|
props: {}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送图片消息
|
|
|
|
|
async sendImageMessage(channelId, filePath) {
|
|
|
|
|
try {
|
|
|
|
|
// 1. 上传文件
|
|
|
|
|
const fileData = await this.uploadFile({
|
2025-06-10 02:14:24 +08:00
|
|
|
|
channelId,
|
2025-06-06 03:08:19 +08:00
|
|
|
|
filePath
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-10 02:14:24 +08:00
|
|
|
|
const fileId = fileData.file_infos[0].id;
|
2025-06-06 03:08:19 +08:00
|
|
|
|
|
|
|
|
|
// 2. 发送带图片的消息
|
|
|
|
|
return this.request({
|
2025-06-10 02:14:24 +08:00
|
|
|
|
url: `/api/v4/posts`,
|
2025-06-06 03:08:19 +08:00
|
|
|
|
method: 'POST',
|
|
|
|
|
data: {
|
|
|
|
|
channel_id: channelId,
|
|
|
|
|
message: '',
|
|
|
|
|
file_ids: [fileId],
|
|
|
|
|
props: {}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('发送图片消息失败:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-10 02:14:24 +08:00
|
|
|
|
async getThumbnail(file_id, width = 200, height = 200) {
|
|
|
|
|
return this.adminBaseUrl+'/api/v4/files/${file_id}/thumbnail?width=${width}&height=${height}';
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/files/${file_id}/thumbnail`,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
data: {
|
|
|
|
|
width,
|
|
|
|
|
height
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return response;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取缩略图失败:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getPreview(file_id, width = 800, height = 800) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/files/${file_id}/preview`,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
data: {
|
|
|
|
|
width,
|
|
|
|
|
height
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return response;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取预览图失败:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-06 03:08:19 +08:00
|
|
|
|
|
|
|
|
|
// 标记消息为已读
|
|
|
|
|
async markChannelAsViewed(channelId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/channels/${channelId}/view`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data: {
|
|
|
|
|
channel_id: channelId
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.unreadCounts.set(channelId, 0);
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否是当前查看的频道
|
|
|
|
|
isCurrentChannel(channelId) {
|
|
|
|
|
return this.currentChannelId === channelId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取频道的未读消息数
|
|
|
|
|
getUnreadCount(channelId) {
|
|
|
|
|
return this.unreadCounts.get(channelId) || 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置当前查看的频道
|
|
|
|
|
setCurrentChannel(channelId) {
|
|
|
|
|
this.currentChannelId = channelId;
|
|
|
|
|
// 自动标记为已读
|
|
|
|
|
if (channelId) {
|
|
|
|
|
this.markChannelAsViewed(channelId).catch(console.error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取当前用户信息
|
|
|
|
|
async getCurrentUser() {
|
|
|
|
|
// if (!this.authenticated) {
|
|
|
|
|
// throw new Error('未认证');
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
const user = await this.request({
|
|
|
|
|
url: `/api/v4/users/me`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.currentUser = user;
|
|
|
|
|
this.userRoles.set(user.id, user.roles);
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户是否是管理员
|
|
|
|
|
isAdmin(userId) {
|
|
|
|
|
const roles = this.userRoles.get(userId);
|
|
|
|
|
return roles && roles.includes('system_admin');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户是否是客服
|
|
|
|
|
isKefu(userId) {
|
|
|
|
|
const roles = this.userRoles.get(userId);
|
|
|
|
|
return roles && roles.includes('system_user');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取用户状态
|
|
|
|
|
async getUserStatus(userId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 使用用户 API 获取状态
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/users/${userId}/status`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
this.userStatuses.set(userId, response.data);
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('获取用户状态失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户状态错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取多个用户状态
|
|
|
|
|
async getUsersStatus(userIds) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/users/status/ids`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data: userIds
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
response.data.forEach(status => {
|
|
|
|
|
this.userStatuses.set(status.user_id, status);
|
|
|
|
|
});
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('获取用户状态失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户状态错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索消息
|
|
|
|
|
async searchPosts(terms, isOrSearch = false) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/posts/search`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data: {
|
|
|
|
|
terms,
|
|
|
|
|
is_or_search: isOrSearch
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('搜索消息失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('搜索消息错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除消息(管理员功能)
|
|
|
|
|
async deletePost(postId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.isAdmin(this.currentUser?.id)) {
|
|
|
|
|
throw new Error('没有权限删除消息');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/posts/${postId}`,
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
this.deletedMessages.add(postId);
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('删除消息失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除消息错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 撤回消息
|
|
|
|
|
async editPost(postId, message) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/posts/${postId}`,
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
data: {
|
|
|
|
|
message,
|
|
|
|
|
has_reactions: false
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('撤回消息失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('撤回消息错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 禁言用户(管理员功能)
|
|
|
|
|
async muteUser(userId, channelId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.isAdmin(this.currentUser?.id)) {
|
|
|
|
|
throw new Error('没有权限禁言用户');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/channels/${channelId}/members/${userId}/roles`,
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
data: {
|
|
|
|
|
roles: 'channel_user channel_muted'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('禁言用户失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('禁言用户错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解除禁言(管理员功能)
|
|
|
|
|
async unmuteUser(userId, channelId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.isAdmin(this.currentUser?.id)) {
|
|
|
|
|
throw new Error('没有权限解除禁言');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/channels/${channelId}/members/${userId}/roles`,
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
data: {
|
|
|
|
|
roles: 'channel_user'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
return response.data;
|
|
|
|
|
}
|
|
|
|
|
throw new Error('解除禁言失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解除禁言错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取被禁言用户列表(管理员功能)
|
|
|
|
|
async getMutedUsers(channelId) {
|
|
|
|
|
if (!this.authenticated) {
|
|
|
|
|
throw new Error('未认证');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.isAdmin(this.currentUser?.id)) {
|
|
|
|
|
throw new Error('没有权限查看禁言列表');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/channels/${channelId}/members`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.statusCode === 200) {
|
|
|
|
|
return response.data.filter(member =>
|
|
|
|
|
member.roles.includes('channel_muted')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
throw new Error('获取禁言列表失败');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取禁言列表错误:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查消息是否被删除
|
|
|
|
|
isMessageDeleted(postId) {
|
|
|
|
|
return this.deletedMessages.has(postId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取缓存的消息
|
|
|
|
|
getCachedMessage(postId) {
|
|
|
|
|
return this.messageCache.get(postId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清理消息缓存
|
|
|
|
|
clearMessageCache() {
|
|
|
|
|
this.messageCache.clear();
|
|
|
|
|
this.deletedMessages.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取当前用户ID
|
|
|
|
|
getCurrentUserId() {
|
|
|
|
|
return this.currentUser?.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否已认证
|
|
|
|
|
isAuthenticated() {
|
|
|
|
|
return this.authenticated;
|
|
|
|
|
}
|
|
|
|
|
async getUserInfo(username){
|
|
|
|
|
if(username.indexOf('@')!==-1){
|
|
|
|
|
return await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/email/${username}`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
}else{
|
|
|
|
|
return await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/users/${username}`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-08 21:19:07 +08:00
|
|
|
|
async getUserById(user_id){
|
|
|
|
|
if(this.kown_users[user_id]){
|
|
|
|
|
return this.kown_users[user_id];
|
|
|
|
|
}
|
|
|
|
|
var result = await this.getUsersByIds([user_id]);
|
|
|
|
|
return result[0];
|
|
|
|
|
}
|
2025-06-06 03:08:19 +08:00
|
|
|
|
async getUsersByIds(user_ids){
|
|
|
|
|
return await this.request({
|
|
|
|
|
to_server:'admin',
|
|
|
|
|
url: `/api/v4/users/ids`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data:JSON.stringify(user_ids)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
async getChannelById(channel_id){
|
|
|
|
|
return await this.request({
|
|
|
|
|
url:`/api/v4/channels/${channel_id}`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
async getChannelMembers(channel_id){
|
|
|
|
|
return await this.request({
|
|
|
|
|
url:`/api/v4/channels/${channel_id}/members`,
|
|
|
|
|
method: 'GET'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
async talk_to_user(item){
|
|
|
|
|
if (!item.type || item.type!== 'O') {
|
|
|
|
|
// 获取当前用户ID
|
|
|
|
|
const currentUserId = await this.getCurrentUserId();
|
|
|
|
|
// 获取目标用户ID
|
|
|
|
|
const targetUserId = item.id;
|
|
|
|
|
|
|
|
|
|
// 查询直接消息频道
|
|
|
|
|
const response = await this.request({
|
|
|
|
|
url: `/api/v4/channels/direct`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
data: [currentUserId, targetUserId]
|
|
|
|
|
});
|
|
|
|
|
if (response.id) {
|
|
|
|
|
// 使用返回的频道ID进行跳转
|
|
|
|
|
return `/pages/im/chat?target_id=${response.id}&target_type=private`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 如果不是私聊或查询失败,使用原始ID跳转
|
|
|
|
|
return `/pages/im/chat?target_id=${item.id}&target_type=group`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建单例
|
|
|
|
|
const mattermost = new MattermostClient();
|
|
|
|
|
export default mattermost;
|