Ai_GirlFriend/xuniYou/components/emCallKit/stores/channelManger.js

337 lines
13 KiB
JavaScript
Raw Normal View History

2026-01-31 19:15:41 +08:00
import { defineStore } from 'pinia';
import {
CALLSTATUS,
CALL_TYPES,
CALL_TYPE,
CALL_INVITE_TEXT,
} from '../contants';
import {
CALLKIT_EVENT_CODE,
CALLKIT_EVENT_TYPE,
} from '../contants/callKitEvent';
import { useInitCallKit } from '../index';
import useCallKitEvent from '../callKitManage/useCallKitEvent';
import useSendSignalMsgs from '../callKitManage/useSendSignalMsgs';
import createUid from '../utils/createUid';
const { EVENT_NAME, PUB_CHANNEL_EVENT } = useCallKitEvent();
const useAgoraChannelStore = defineStore('agoraChannelStore', {
state: () => ({
emClientInfos: {
apiUrl: '',
appKey: '',
loginUserId: '',
clientResource: '',
accessToken: '',
},
callKitStatus: {
localClientStatus: CALLSTATUS.idle, //callkit状态
channelInfos: {
channelName: '', //频道名
agoraChannelToken: '', //频道token
agoraUserId: '', //频道用户id,
callType: CALL_TYPES.SINGLE_VOICE, //0 语音 1 视频 2 多人音视频
callId: null, //会议ID
channelUsers: {}, //频道内用户
callerDevId: '', //主叫方设备ID
calleeDevId: '', //被叫方设备ID
callerIMName: '', //主叫方环信ID
calleeIMName: '', //被叫方环信ID
groupId: '', //群组ID
},
//被邀请对象 单人为string 多人为array
inviteTarget: null,
},
callKitTimer: null,
}),
actions: {
/* emClient */
initEmClientInfos(emClient) {
console.log('initEmClientInfos', emClient);
if (!emClient) return;
this.emClientInfos.apiUrl = emClient.apiUrl;
this.emClientInfos.appKey = emClient.appKey;
this.emClientInfos.loginUserId = emClient.user;
this.emClientInfos.accessToken = emClient.token;
this.emClientInfos.clientResource = emClient.clientResource;
},
/* CallKit status 管理 */
//初始化频道信息
initChannelInfos() {
this.callKitStatus.localClientStatus = CALLSTATUS.idle;
this.callKitStatus.channelInfos = {
channelName: '', //频道名
agoraChannelToken: '', //频道token
agoraUid: '', //频道用户id
callType: CALL_TYPES.SINGLE_VOICE, //0 语音 1 视频 2 多人音视频
callId: null, //会议ID
channelUsers: {}, //频道内用户
callerDevId: '', //主叫方设备ID
calleeDevId: '', //被叫方设备ID
confrontId: '', //要处理的目标ID
callerIMName: '', //主叫方环信ID
calleeIMName: '', //被叫方环信ID
groupId: '', //群组ID
};
this.callKitStatus.inviteTarget = null;
this.callKitTimer && clearTimeout(this.callKitTimer);
},
//更新localStatus
updateLocalStatus(typeCode) {
console.log('>>>>>开始变更本地状态为 typeCode', typeCode);
this.callKitStatus.localClientStatus = typeCode;
},
//更新频道信息
updateChannelInfos(msgBody) {
console.log('触发更新频道信息', msgBody);
const { from, to, ext } = msgBody || {};
const params = {
channelName:
ext.channelName || this.callKitStatus.channelInfos.channelName,
callId: ext.callId || this.callKitStatus.channelInfos.callId,
callType:
CALL_TYPE[ext.type] || this.callKitStatus.channelInfos.callType,
callerDevId: ext.callerDevId || 0,
calleeDevId: ext.calleeDevId,
callerIMName: from,
calleeIMName: to,
groupId: ext?.ext?.groupId ? ext.ext.groupId : '',
};
console.log('%c将要更新的信息内容为', 'color:red', params);
Object.assign(this.callKitStatus.channelInfos, params);
},
/* CallKit Timer */
//用作邀请信息发送之后发起计时30s。
startCallKitTimer() {
const { sendCannelMsg } = useSendSignalMsgs();
if (this.callKitTimer) {
clearTimeout(this.callKitTimer);
this.callKitTimer = null;
}
//对外发布应答事件
this.callKitTimer = setTimeout(() => {
const targetId = this.callKitStatus.inviteTarget;
//发送cannel信令
sendCannelMsg({
targetId,
callId: this.callKitStatus.channelInfos.callId,
});
const eventParams = {
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.TIMEOUT],
ext: { message: '通话超时未接听' },
callType: this.callKitStatus.channelInfos.callType,
eventHxId: targetId,
};
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
this.updateLocalStatus(CALLSTATUS.idle); //更改状态为闲置
}, 30000);
},
/* 多人会议使用 获取群组内的成员 */
getTheGroupMembers(groupId) {
const { CallKitEMClient } = useInitCallKit();
if (!groupId) return;
return new Promise((resolve, reject) => {
let pageNum = 1,
pageSize = 100;
let option = {
pageNum: pageNum,
pageSize: pageSize,
groupId: groupId,
};
CallKitEMClient.listGroupMembers(option)
.then((res) => resolve(res?.data))
.catch((err) => reject(err));
});
},
//发起音视频邀请
async sendInviteMessage(targetId, callType, groupId) {
console.log('>>>>>>>>>', targetId, callType, groupId);
const { sendInviteMsg } = useSendSignalMsgs();
//非空闲状态直接拒绝发送邀请信息[除了多人,因为涉及到多人通话中需要邀请他人入会]
if (
callType !== 2 &&
this.callKitStatus.localClientStatus !== CALLSTATUS.idle
)
return;
const channelInfors = {
channelName: `${callType}_${createUid()}`, //频道名
callId: createUid(),
inviteMsgContent: CALL_INVITE_TEXT[callType],
groupId: groupId, //只有为群聊多人邀请时这个参数才有用
};
this.callKitStatus.inviteTarget = targetId;
try {
//如果为数组就遍历发送
if (Array.isArray(targetId)) {
targetId.forEach((userId) => {
sendInviteMsg(userId, callType, channelInfors);
});
console.log('>>>>>群组多人邀请开始遍历发消息');
} else {
//非数组就单条发送
await sendInviteMsg(targetId, callType, channelInfors);
}
console.log('channelInforschannelInfors', channelInfors);
this.updateLocalStatus(CALLSTATUS.inviting);
} catch (error) {
console.log('%c邀请信息发送失败', 'color:red', error);
}
//更改部分ChannelInfos
const params = {
from: this.emClientInfos.loginUserId,
to: callType === CALL_TYPES.MULTI_VIDEO ? '' : targetId,
ext: {
channelName: channelInfors.channelName,
callId: channelInfors.callId,
type: callType,
callerDevId: this.emClientInfos.clientResource,
},
};
//如果存在群组ID则增加ext字段进入到groupId
if (callType === CALL_TYPES.MULTI_VIDEO && groupId)
params.ext.ext = { groupId };
console.log('邀请发送 callType为', callType);
this.updateChannelInfos(params);
//单人邀请开启超时挂断,多人则忽略
if (callType !== CALL_TYPES.MULTI_VIDEO) {
this.startCallKitTimer();
}
},
//【多人】在会议中邀请邀请--会议中邀请不生成新的频道信息
async inMultiChanelSendInviteMsg(targetId, callType) {
const { sendInviteMsg } = useSendSignalMsgs();
if (!targetId) throw 'targetId must pass';
if (callType === undefined || callType === null || callType < 0)
throw 'callType must pass';
if (
(Array.isArray(targetId) && targetId.length < 1) ||
targetId.length > 15
)
throw 'targetId length > 15 or length < 1';
const channelInfors = {
channelName: this.callKitStatus.channelInfos.channelName, //频道名
callId: this.callKitStatus.channelInfos.callId,
inviteMsgContent: CALL_INVITE_TEXT[callType],
groupId: this.callKitStatus.channelInfos.groupId, //只有为群聊多人邀请时这个参数才有用
};
this.callKitStatus.inviteTarget = targetId;
try {
//如果为数组就遍历发送
if (Array.isArray(targetId)) {
targetId.forEach((userId) => {
sendInviteMsg(userId, callType, channelInfors);
});
console.log('>>>>>群组多人邀请开始遍历发消息');
}
} catch (error) {
console.log('%c邀请信息发送失败', 'color:red', error);
}
},
//发送挂断信令
handleCancelCall() {
const { sendCannelMsg } = useSendSignalMsgs();
const targetId = this.callKitStatus.inviteTarget;
if (!targetId) return console.log('>>>挂断目标ID为空', targetId);
//多人遍历发送取消
if (this.callKitStatus.channelInfos.callType === CALL_TYPES.MULTI_VIDEO) {
targetId.length &&
targetId.forEach((userHxId) => {
sendCannelMsg({
targetId: userHxId,
callId: this.callKitStatus.channelInfos.callId,
});
});
//对外频道接听事件发布事件
const eventParams = {
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.CANCEL],
ext: { message: '多人音视频通话已取消' },
callType: CALL_TYPES.MULTI_VIDEO,
eventHxId: this.callKitStatus.channelInfos.groupId,
};
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
this.updateLocalStatus(CALLSTATUS.idle);
} else {
sendCannelMsg({
targetId,
callId: this.callKitStatus.channelInfos.callId,
});
//对外频道接听事件发布事件
const eventParams = {
type: CALLKIT_EVENT_TYPE[CALLKIT_EVENT_CODE.CANCEL],
ext: { message: '通话已取消' },
callType: this.callKitStatus.channelInfos.callType,
eventHxId: targetId,
};
PUB_CHANNEL_EVENT(EVENT_NAME, { ...eventParams });
this.updateLocalStatus(CALLSTATUS.idle);
}
},
//请求频道Token
/**
* @function requestRtcChannelToken
* 该方法主要作用为请求Agora Rtc频道对应的token其积极可抽象理解为拿到某频道对应的门钥匙
* !但此方法所请求的接口为环信内部Demo演示接口仅供环信内部自己使用如自己项目使用请找后端协助部署一个类似的接口服务
*/
requestRtcChannelToken() {
const { channelName } = this.callKitStatus.channelInfos;
const { apiUrl, appKey, loginUserId, accessToken } = this.emClientInfos;
const requestUrl = `${apiUrl}/token/rtcToken/v1?userAccount=${loginUserId}&channelName=${channelName}&appkey=${encodeURIComponent(
appKey
)}`;
return new Promise((resolve, reject) => {
uni.request({
url: requestUrl,
header: {
Authorization: `Bearer ${accessToken}`, //自定义请求头信息
},
success: (result) => {
console.log('>>>>>频道token已获取', result?.data);
resolve(result?.data);
},
fail: (e) => {
console.error('>>>>rtc token 获取失败', e);
uni.showToast({ icon: 'none', title: 'rtc token 请求失败' });
reject(e);
},
});
});
},
/**
* @function requestInChannelMapHxId
* 该方法作用为拿到频道内uid与环信id的映射关系例如在频道内展示uid与之对应的环信ID因此需要该接口取到uid的映射关系非必须接口
* 但此Demo多人音视频通话中有用到
* !同样此方法如果项目中需要类似的需求也请后端协助搭建类型功能接口供项目中使用
*/
//请求频道内uid映射的环信id
requestInChannelMapHxId() {
const { channelName } = this.callKitStatus.channelInfos;
const { apiUrl, appKey, loginUserId, accessToken } = this.emClientInfos;
const requestUrl = `${apiUrl}/channel/mapper?userAccount=${loginUserId}&channelName=${channelName}&appkey=${encodeURIComponent(
appKey
)}`;
console.log('>>>>requestUrl', `Bearer ${accessToken}`);
return new Promise((resolve, reject) => {
uni.request({
url: requestUrl,
header: {
Authorization: `Bearer ${accessToken}`, //自定义请求头信息
},
success: (result) => {
console.log('result', result?.data);
resolve(result?.data);
},
fail: (e) => {
console.error('>>>>rtc token 获取失败', e);
uni.showToast({ icon: 'none', title: 'rtc token 请求失败' });
reject(e);
},
});
});
},
},
});
export default useAgoraChannelStore;