first commit

This commit is contained in:
2026-06-02 10:42:33 +08:00
commit dd4975fd2c
1084 changed files with 442416 additions and 0 deletions

422
utils/cloud-api.js Normal file
View File

@@ -0,0 +1,422 @@
// 云端基础URL
// const BASE_URL = 'https://cloud.yuxindazhineng.com'
const BASE_URL = 'https://cloudtest.yuxindazhineng.com'
// const BASE_URL = 'https://yxdpqj.yuxindazhineng.com'
// 登录请求
export const login = async (username, password) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/login`,
method: "POST",
data: {
"username": username,
"password": password,
"login_type": "phone"
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const token = res.data.access_token
if (token) {
uni.setStorageSync('token', token)
resolve(token)
} else {
const msg = res.data.error || '检查用户名和密码'
reject(msg)
}
} else {
reject(`请求失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 历史会话请求
export const getUserConversations = async (token) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/get_user_conversations`,
method: "POST",
data: {
"access_token": token
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const conversations = res.data.conversations
resolve(conversations)
} else {
reject(`请求失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取与AI的对话内容
export const getConversationMessages = async (token, conversatio_id) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/get_conversation_messages`,
method: 'POST',
data: {
"access_token": token,
"conversation_id": conversatio_id
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const messagesInfo = res.data
if (messagesInfo) {
console.log("工作区id为", messagesInfo.workspace_id);
uni.setStorageSync('workspace_id', messagesInfo.workspace_id)
resolve(messagesInfo.messages)
} else {
const msg = res.data.error || '获取消息出错啦'
reject(msg)
}
} else {
reject(`请求失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 用户发送消息ai不接收/回复)
export const addMessageDict = async (token, role, conversation_id, message) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/add_message_dict`,
method: "POST",
data: {
"access_token": token,
"conversation_id": conversation_id,
"message": {
"role": role,
"content": message
}
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const message_id = res.data.message_id
if (message_id) {
resolve(message_id)
} else {
const msg = res.data.error || '用户发送消息出错啦'
reject(msg)
}
} else {
reject(`用户发送失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取工作区消息(非内容)
export const getWorkspaceByConversation = async (token, conversation_id) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/get_workspace_by_conversation`,
method: "POST",
data: {
"access_token": token,
"conversation_id": conversation_id
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const workspaceInfo = res.data
if (workspaceInfo?.success) {
resolve(workspaceInfo.workspace_id)
} else {
const msg = res.data.error || '获取工作区消息出错啦'
reject(msg)
}
} else {
reject(`获取工作区消息失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 创建工作区id
export const createWorkspace = async (token, name) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/create_workspace`,
method: "POST",
data: {
"access_token": token,
"name": name
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const workspaceId = res.data.workspace_id
if (workspaceId) {
resolve(workspaceId)
} else {
const msg = res.data.error || '创建工作区id出错啦'
reject(msg)
}
} else {
reject(`创建工作区id失败${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 创建新对话
export const createConversation = async (token, workspaceId, title, sender) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/create_conversation`,
method: "POST",
data: {
"access_token": token,
"workspace_id": workspaceId,
"title": title,
"sender": sender
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const conversationId = res.data.conversation_id
if (conversationId) {
resolve(conversationId)
} else {
const msg = res.data.error || '创建新对话id出错啦'
reject(msg)
}
} else {
reject(`创建新对话失败:${res.statusCode}`)
}
}
})
})
}
// 删除会话
export const deleteConversation = async (token, conversation_ids) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/delete_conversation`,
method: "POST",
data: {
"access_token": token,
"conversation_ids": conversation_ids
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond?.success) {
resolve(respond.message)
} else {
const msg = respond.failed_ids.error || '删除对话出错啦'
reject(msg)
}
} else {
reject(`删除对话失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取用户信息
export const getUserInfo = async (token) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/get_user_info`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
"access_token": token
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond?._id) {
uni.setStorageSync('userId', respond._id)
resolve(respond)
} else {
const msg = respond?.detail?.[0]?.msg || '获取用户信息出错啦'
reject(msg)
}
} else {
reject(`获取用户信息失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取用户头像自己or好友组
export const getUserAvatar = (token, userIds) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/cloud_api/get_user_avatar`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
"access_token": token,
"user_ids": userIds
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond.success) {
resolve(respond.users)
} else {
const msg = respond?.error || '获取用户头像出错啦'
reject(msg)
}
} else {
reject(`获取用户头像失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 通过用户名或邮箱来去查询用户
export const searchUsers = (token, keyword) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/cloud_api/search_users`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
"access_token": token,
"keyword": keyword
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond.success) {
resolve(respond.users)
} else {
const msg = respond?.error || '通过用户名或邮箱来去查询用户出错啦'
reject(msg)
}
} else {
reject(`通过用户名或邮箱来去查询用户失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取工作区文件目录
export const getWorkspaceList = (token, workspacesId) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_URL}/cloud_api/phone/workspace/list`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
"access_token": token,
"workspaces_id": workspacesId,
"format": "tree",
"parent_path": "",
"filter_type": ""
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond.success) {
resolve(respond.data)
} else {
const msg = respond?.error || '获取工作区文件目录出错啦'
reject(msg)
}
} else {
reject(`获取工作区文件目录失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}

234
utils/friend-api.js Normal file
View File

@@ -0,0 +1,234 @@
const BASE_Friend_URL = 'http://chat.yuxindazhineng.com'
// 获取好友列表
export const getChatFriend = async (sendId) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_Friend_URL}/api/chatList/getChatFriend`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
size: 500,
current: 1,
sendId: sendId
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
console.log("获取到的好友列表消息", respond);
if (respond?.code === 0 || respond?.msg === '成功') {
const friendList = respond.data?.records
if (friendList.length > 0) {
const receiverId = friendList[0].receiver
// 再加一层判断,防止 receiver 为空
if (receiverId) {
uni.setStorageSync('receiverId', receiverId)
}
}
resolve(friendList)
} else {
const msg = res.data.error || '获取好友列表出错啦'
reject(msg)
}
} else {
reject(`获取好友列表失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取群聊列表
export const getGroup = async (sendId) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_Friend_URL}/api/group/getGroup`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
size: 500,
current: 1,
contactId: sendId
},
success: (res) => {
if (res.statusCode === 200) {
const respond = res.data
if (respond?.code === 0 || respond?.msg === '成功') {
const groupList = respond.data?.records
resolve(groupList)
} else {
const msg = res.data.error || '获取群聊列表出错啦'
reject(msg)
}
} else {
reject(`获取群聊列表失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取与好友的对话内容
export const getFriendMessages = async (sessionId, current = 1) => {
console.log('请求的好友消息id', sessionId);
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_Friend_URL}/api/message/list`,
method: 'POST',
data: {
"sessionId": sessionId,
"size": 100,
"current": current,
"beginTime": "",
"endTime": ""
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const messagesInfo = res.data
// console.log("获取到的好友信息:",messagesInfo);
if (messagesInfo.code === 0) {
// uni.setStorageSync('currentSessionId', messagesInfo?.data
// ?.sessionId)
const messageList = messagesInfo?.data?.records || [];
const reversedList = messageList.reverse();
resolve(reversedList);
// resolve(messagesInfo?.data?.records)
} else {
const msg = res.data.error || '获取好友消息出错啦'
reject(msg)
}
} else {
reject(`请求失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取群聊的对话内容
export const getGroupMessages = async (groupId, current = 1) => {
console.log('请求的群聊消息id', groupId);
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_Friend_URL}/api/group/getGroupList`,
method: 'POST',
data: {
"current": current,
"size": 100,
"groupId": groupId,
"beginTime": "",
"endTime": ""
},
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.statusCode === 200) {
const messagesInfo = res.data
console.log("获取到的群聊信息:", messagesInfo);
if (messagesInfo.code === 0) {
// uni.setStorageSync('currentSessionId', messagesInfo?.data
// ?.groupId)
const messageList = messagesInfo?.data?.records || []
const reversedList = messageList.reverse()
resolve(reversedList)
// resolve(messagesInfo?.data?.records)
} else {
const msg = res.data.error || '获取群聊消息出错啦'
reject(msg)
}
} else {
reject(`请求失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 获取群成员
export const getGroupMemberList = async (groupId) => {
return new Promise((resolve, reject) => {
uni.request({
url: `${BASE_Friend_URL}/api/group/getGroupMemberList`,
method: "POST",
header: {
'Content-Type': 'application/json'
},
data: {
"groupId": groupId,
"current": 1,
"size": 100
},
success: (res) => {
if(res.statusCode ===200){
const respond = res.data
if(respond.code === 0){
const groupMemberList = respond.data.records
resolve(groupMemberList)
} else {
const msg = res.data.error || '获取群成员出错啦'
reject(msg)
}
}else{
reject(`请求群成员失败:${res.statusCode}`)
}
},
fail: (err) => {
const msg = err.errMsg || '网络错误'
reject(msg)
}
})
})
}
// 上传文件
export const uploadFileToServer = (filePath) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${BASE_Friend_URL}/api/file/upload`,
filePath: filePath,
name: 'file',
success: (res) => {
const data = JSON.parse(res.data)
if (data.code === 0) {
resolve(data.data)
} else {
reject(data.msg || '上传失败')
}
},
fail: (err) => {
reject(err.errMsg || '网络错误')
}
})
})
}

344
utils/friend-socket.js Normal file
View File

@@ -0,0 +1,344 @@
const WS_Friend_URL = 'wss://wss.yuxindazhineng.com'
// (工具类)连接、发送、接收、重连...
class WebSocketManager {
constructor() {
this.ws = null
this.url = `${WS_Friend_URL}/ws`
this.options = {
token: '',
UserId: '',
onMessage: null,
onError: null,
onReconnect: null,
onOpen: null,
onClose: null
}
this.reconnectAttempts = 0
this.maxReconnectAttempts = 5
this.reconnectDelay = 3000
this.reconnectTimer = null
this.heartbeatTimer = null
this.isManualClose = false
}
/**
* 初始化连接
* @param {string} url WebSocket URL
* @param {Object} options 配置选项
*/
connect(options = {}) {
this.options = {
...this.options,
...options
}
this.isManualClose = false
// 关闭现有连接
if (this.ws) {
this.close()
}
try {
// #ifdef APP-PLUS || MP-WEIXIN
this.ws = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket 连接请求已发送');
},
fail: (err) => {
console.error('WebSocket 连接失败:', err)
this.handleError(err)
}
})
// #endif
// #ifdef H5
if (!this.ws) {
this.ws = new WebSocket(this.url)
}
// #endif
this.initEventHandlers()
} catch (err) {
this.handleError(err)
}
}
/**
* 初始化事件处理
*/
initEventHandlers() {
if (!this.ws) return
// #ifdef APP-PLUS || MP-WEIXIN
// 打开连接
this.ws.onOpen((event) => {
console.log('WebSocket 已连接')
// 修复:先判断重连状态,再重置计数
if (this.reconnectAttempts > 0) {
this.options.onReconnect?.()
}
this.options.onOpen?.(event)
this.reconnectAttempts = 0
// this.startHeartbeat()
})
this.ws.onMessage((event) => {
try {
const data = JSON.parse(event.data)
console.log('收到了返回消息:', data);
this.handleMessage(data)
} catch (err) {
console.error('消息解析失败:', err)
uni.setStorageSync('currentSessionId', event.data.task_call_id)
}
})
this.ws.onError((err) => {
console.error('WebSocket 错误:', err)
this.handleError(err)
})
this.ws.onClose((event) => {
console.log('WebSocket 已关闭:', event.code, event.reason)
this.options.onClose?.(event)
// this.close()
if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect()
}
})
// #endif
// #ifdef H5
// H5 环境使用原生 WebSocket API
if (typeof WebSocket !== 'undefined') {
this.ws.onopen = (event) => {
console.log('WebSocket 已连接 (H5)')
if (this.reconnectAttempts > 0) {
this.options.onReconnect?.()
}
this.options.onOpen?.(event)
this.reconnectAttempts = 0
}
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
console.log('收到了返回消息 (H5)', data);
this.handleMessage(data)
} catch (err) {
console.error('消息解析失败 (H5):', err)
}
}
this.ws.onerror = (err) => {
console.error('WebSocket 错误 (H5):', err)
this.handleError(err)
}
this.ws.onclose = (event) => {
console.log('WebSocket 已关闭 (H5):', event.code, event.reason)
this.options.onClose?.(event)
// this.close()
if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect()
}
}
}
// #endif
}
/**
* 处理服务端推送的消息
* @param {Object} msg - 服务端返回的原始消息对象
*/
handleMessage(msg) {
this.options.onMessage?.(msg)
}
/**
* 统一发送消息方法
* 兼容 APP / 微信小程序 环境
* @param {Object} data - 要发送的消息对象
* @returns {Promise} 发送结果
*/
send(data) {
return new Promise((resolve, reject) => {
// 未连接直接拒绝
if (!this.isConnected()) {
reject(new Error('WebSocket 未连接'))
return
}
const msg = JSON.stringify(data)
console.log("发送的消息是:", msg);
// #ifdef APP-PLUS || MP-WEIXIN
// APP / 小程序环境uni-app 封装 API
this.ws.send({
data: msg,
success: () => resolve(),
fail: (err) => {
console.error('发送消息失败:', err)
reject(err)
}
})
// #endif
// #ifdef H5
// H5 环境:原生 WebSocket API
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(msg)
resolve()
} else {
reject(new Error('WebSocket 未打开'))
}
// #endif
})
}
/**
* 自动重连机制
* 指数退避重连,避免频繁重连
*/
reconnect() {
// 已有重连定时器则不重复触发
if (this.reconnectTimer) return
this.reconnectAttempts++
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`)
// 延迟执行重连,延迟时间随次数递增
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null
// 非手动关闭才重连
if (!this.isManualClose) {
this.connect(this.options)
}
}, this.reconnectDelay * this.reconnectAttempts)
}
/**
* 启动心跳定时器
* 每30秒发送一次 ping 保持连接
*/
startHeartbeat() {
this.stopHeartbeat()
this.heartbeatTimer = setInterval(() => {
if (this.isConnected()) {
this.send({
ws_event: 'ping'
}).catch(() => {
console.log('心跳发送失败,准备重连')
// 避免在已经重连过程中重复触发
if (!this.reconnectTimer && !this.isManualClose) {
this.reconnect()
}
})
} else if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
// 连接已断开但未触发 close 事件时主动重连
this.reconnect()
}
}, 30000)
}
/**
* 停止心跳定时器
*/
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
}
/**
* 统一错误处理
* @param {Error} error - 错误对象
*/
handleError(error) {
console.error('WebSocket 错误处理:', JSON.stringify(error))
this.options.onError?.(error)
}
/**
* 判断 WebSocket 是否处于已连接状态
* @returns {boolean}
*/
isConnected() {
if (!this.ws) return false
// #ifdef APP-PLUS || MP-WEIXIN
return this.ws.readyState === 1
// #endif
// #ifdef H5
return this.ws.readyState === WebSocket.OPEN
// #endif
}
/**
* 方便外部获取连接状态码
* @returns {number} 0:连接中,1:已连接,2:关闭中,3:已关闭
*/
getReadyState() {
if (!this.ws) return 3
// #ifdef APP-PLUS || MP-WEIXIN
return this.ws.readyState
// #endif
// #ifdef H5
return this.ws.readyState
// #endif
}
/**
* 手动关闭 WebSocket 连接
* 清理所有定时器、标记手动关闭、停止重连
*/
close() {
console.log('手动关闭 WebSocket 连接')
this.isManualClose = true
this.stopHeartbeat()
// 清理重连定时器
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
this.reconnectTimer = null
}
if (this.ws) {
// 移除所有事件监听,避免内存泄漏
// #ifdef APP-PLUS || MP-WEIXIN
if (this.ws.onOpen) this.ws.onOpen(null)
if (this.ws.onMessage) this.ws.onMessage(null)
if (this.ws.onError) this.ws.onError(null)
if (this.ws.onClose) this.ws.onClose(null)
// #endif
try {
// #ifdef APP-PLUS || MP-WEIXIN
this.ws.close({
success: () => console.log('连接已关闭'),
fail: (err) => console.error('关闭失败:', err)
})
// #endif
// #ifdef H5
this.ws.close()
// #endif
} catch (error) {
console.error('关闭连接出错:', error)
}
this.ws = null
}
// 重置重连计数
this.reconnectAttempts = 0
}
/**
* 更新配置项
* 合并新配置,不覆盖原有配置
* @param {Object} options - 新配置
*/
updateOptions(options) {
this.options = {
...this.options,
...options
}
}
}
// 单例实例化,全局唯一
export const friendSocketManager = new WebSocketManager()
export default friendSocketManager

366
utils/socket.js Normal file
View File

@@ -0,0 +1,366 @@
const WS_APP_URL = 'ws://cloud_test.yuxindazhineng.com'
// (工具类)连接、发送、接收、重连...
class WebSocketManager {
constructor() {
this.ws = null
this.url = `${WS_APP_URL}/cloud_api/phone/app_ws`
this.options = {
token: '',
conversationId: '',
onMessage: null,
onError: null,
onReconnect: null,
onOpen: null,
onClose: null
}
this.reconnectAttempts = 0
this.maxReconnectAttempts = 5
this.reconnectDelay = 3000
this.reconnectTimer = null
this.heartbeatTimer = null
this.isManualClose = false
}
/**
* 初始化连接
* @param {string} url WebSocket URL
* @param {Object} options 配置选项
*/
connect(options = {}) {
this.options = {
...this.options,
...options
}
this.isManualClose = false
// 关闭现有连接
if (this.ws) {
this.close()
}
try {
// #ifdef APP-PLUS || MP-WEIXIN
this.ws = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket 连接请求已发送');
},
fail: (err) => {
console.error('WebSocket 连接失败:', err)
this.handleError(err)
}
})
// #endif
// #ifdef H5
if (!this.ws) {
this.ws = new WebSocket(this.url)
}
// #endif
this.initEventHandlers()
} catch (err) {
this.handleError(err)
}
}
/**
* 初始化事件处理
*/
initEventHandlers() {
if (!this.ws) return
// #ifdef APP-PLUS || MP-WEIXIN
// 打开连接
this.ws.onOpen((event) => {
console.log('WebSocket 已连接')
// 修复:先判断重连状态,再重置计数
if (this.reconnectAttempts > 0) {
this.options.onReconnect?.()
}
this.options.onOpen?.(event)
this.reconnectAttempts = 0
// this.startHeartbeat()
})
this.ws.onMessage((event) => {
try {
const data = JSON.parse(event.data)
console.log('收到了返回消息:', data);
// uni.setStorageSync('currentSessionId', data.task_call_id)
this.handleMessage(data)
} catch (err) {
console.error('消息解析失败:', err)
uni.setStorageSync('currentSessionId', event.data.task_call_id)
}
})
this.ws.onError((err) => {
console.error('WebSocket 错误:', err)
this.handleError(err)
})
this.ws.onClose((event) => {
console.log('WebSocket 已关闭:', event.code, event.reason)
this.options.onClose?.(event)
this.stopHeartbeat()
if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect()
}
})
// #endif
// #ifdef H5
// H5 环境使用原生 WebSocket API
if (typeof WebSocket !== 'undefined') {
this.ws.onopen = (event) => {
console.log('WebSocket 已连接 (H5)')
if (this.reconnectAttempts > 0) {
this.options.onReconnect?.()
}
this.options.onOpen?.(event)
this.reconnectAttempts = 0
}
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
console.log('收到了返回消息 (H5)', data);
this.handleMessage(data)
} catch (err) {
console.error('消息解析失败 (H5):', err)
}
}
this.ws.onerror = (err) => {
console.error('WebSocket 错误 (H5):', err)
this.handleError(err)
}
this.ws.onclose = (event) => {
console.log('WebSocket 已关闭 (H5):', event.code, event.reason)
this.options.onClose?.(event)
this.stopHeartbeat()
if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnect()
}
}
}
// #endif
}
/**
* 处理服务端推送的消息
* @param {Object} msg - 服务端返回的原始消息对象
*/
handleMessage(msg) {
this.options.onMessage?.(msg)
const type = msg.type
// const data = msg.data || {}
// 连接确认:服务端认证成功响应
if (type === 'auth_ok') {
// 发送消息携带的上一次返回的验证id
// const taskCallId = data.task_call_id
// uni.setStorageSync('taskCallId', taskCallId)
console.log('认证成功socket连接成功');
return
}
// 断开连接指令:服务端主动要求断开
if (type === 'auth_fail') {
if(msg?.reason === "pc offline")
console.log("pc端不在线不可聊天");
// console.log('服务端要求断开连接')
this.isManualClose = true
this.close()
return
}
}
/**
* 统一发送消息方法
* 兼容 APP / 微信小程序 环境
* @param {Object} data - 要发送的消息对象
* @returns {Promise} 发送结果
*/
send(data) {
return new Promise((resolve, reject) => {
// 未连接直接拒绝
if (!this.isConnected()) {
reject(new Error('WebSocket 未连接'))
return
}
const msg = JSON.stringify(data)
console.log("发送的消息是:", msg);
// #ifdef APP-PLUS || MP-WEIXIN
// APP / 小程序环境uni-app 封装 API
this.ws.send({
data: msg,
success: () => resolve(),
fail: (err) => {
console.error('发送消息失败:', err)
reject(err)
}
})
// #endif
// #ifdef H5
// H5 环境:原生 WebSocket API
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(msg)
resolve()
} else {
reject(new Error('WebSocket 未打开'))
}
// #endif
})
}
/**
* 自动重连机制
* 指数退避重连,避免频繁重连
*/
reconnect() {
// 已有重连定时器则不重复触发
if (this.reconnectTimer) return
this.reconnectAttempts++
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`)
// 延迟执行重连,延迟时间随次数递增
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null
// 非手动关闭才重连
if (!this.isManualClose) {
this.connect(this.options)
}
}, this.reconnectDelay * this.reconnectAttempts)
}
/**
* 启动心跳定时器
* 每30秒发送一次 ping 保持连接
*/
startHeartbeat() {
this.stopHeartbeat()
this.heartbeatTimer = setInterval(() => {
if (this.isConnected()) {
this.send({
ws_event: 'ping'
}).catch(() => {
console.log('心跳发送失败,准备重连')
// 避免在已经重连过程中重复触发
if (!this.reconnectTimer && !this.isManualClose) {
this.reconnect()
}
})
} else if (!this.isManualClose && this.reconnectAttempts < this.maxReconnectAttempts) {
// 连接已断开但未触发 close 事件时主动重连
this.reconnect()
}
}, 30000)
}
/**
* 停止心跳定时器
*/
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
}
/**
* 统一错误处理
* @param {Error} error - 错误对象
*/
handleError(error) {
console.error('WebSocket 错误处理:', JSON.stringify(error))
this.options.onError?.(error)
}
/**
* 判断 WebSocket 是否处于已连接状态
* @returns {boolean}
*/
isConnected() {
if (!this.ws) return false
// #ifdef APP-PLUS || MP-WEIXIN
return this.ws.readyState === 1
// #endif
// #ifdef H5
return this.ws.readyState === WebSocket.OPEN
// #endif
}
/**
* 方便外部获取连接状态码
* @returns {number} 0:连接中,1:已连接,2:关闭中,3:已关闭
*/
getReadyState() {
if (!this.ws) return 3
// #ifdef APP-PLUS || MP-WEIXIN
return this.ws.readyState
// #endif
// #ifdef H5
return this.ws.readyState
// #endif
}
/**
* 手动关闭 WebSocket 连接
* 清理所有定时器、标记手动关闭、停止重连
*/
close() {
console.log('手动关闭 WebSocket 连接')
this.isManualClose = true
this.stopHeartbeat()
// 清理重连定时器
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
this.reconnectTimer = null
}
if (this.ws) {
// 移除所有事件监听,避免内存泄漏
// #ifdef APP-PLUS || MP-WEIXIN
if (this.ws.onOpen) this.ws.onOpen(null)
if (this.ws.onMessage) this.ws.onMessage(null)
if (this.ws.onError) this.ws.onError(null)
if (this.ws.onClose) this.ws.onClose(null)
// #endif
try {
// #ifdef APP-PLUS || MP-WEIXIN
this.ws.close({
success: () => console.log('连接已关闭'),
fail: (err) => console.error('关闭失败:', err)
})
// #endif
// #ifdef H5
this.ws.close()
// #endif
} catch (error) {
console.error('关闭连接出错:', error)
}
this.ws = null
}
// 重置重连计数
this.reconnectAttempts = 0
}
/**
* 更新配置项
* 合并新配置,不覆盖原有配置
* @param {Object} options - 新配置
*/
updateOptions(options) {
this.options = {
...this.options,
...options
}
}
}
// 单例实例化,全局唯一
export const socketManager = new WebSocketManager()
export default socketManager

87
utils/user-info.js Normal file
View File

@@ -0,0 +1,87 @@
// 获取账号和密码
export const getLoginInfo = () => {
return uni.getStorageSync('login_info')
}
//清除账号和密码
export const clearLoginInfo = () => {
uni.removeStorageSync('login_info')
}
// 获取存储的 token用于其他请求
export const getToken = () => {
return uni.getStorageSync('token')
}
// 清除 token退出登录
export const clearToken = () => {
uni.removeStorageSync('token')
}
// 获取会话id
export const getCurrentSessionId = () => {
const sessionId = uni.getStorageSync('currentSessionId')
// 如果是纯数字 → 直接返回
if (!isNaN(Number(sessionId))) {
return sessionId
}
// 如果是 uuid 带横杠 → 去掉横杠
return sessionId.replace(/-/g, '')
}
// 清除会话id
export const clearCurrentSessionId = () => {
uni.removeStorageSync('currentSessionId')
}
// 获取socket连接成功返回时的task_call_id
export const getTaskCallId = () => {
return uni.getStorageSync('taskCallId')
}
// 清除socket连接成功返回时的task_call_id
export const clearTaskCallId = () => {
uni.removeStorageSync('taskCallId')
}
// 获取用户id
export const getUserId = () => {
console.log("用户IdUserId", uni.getStorageSync('userId'));
return uni.getStorageSync('userId')
}
// 清除用户id
export const clearUserId = () => {
uni.removeStorageSync('userId')
}
// 获取聊天type
export const getChatType = () => {
return uni.getStorageSync('chatType')
}
// 清除聊天type
export const clearChatType = () => {
uni.removeStorageSync('chatType')
}
// 获取正在聊天的好友的receiverId
export const getReceiverId = () => {
return uni.getStorageSync('receiverId')
}
// 清除正在聊天的好友的receiverId
export const clearReceiverId = () => {
uni.removeStorageSync('receiverId')
}
// 获取当前ai会话的工作区id
export const getWorkspaceId = () => {
return uni.getStorageSync('workspace_id')
}
// 清除当前ai会话的工作区id
export const clearWorkspaceId = () => {
uni.removeStorageSync('workspace_id')
}