337 lines
13 KiB
JavaScript
337 lines
13 KiB
JavaScript
|
|
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;
|