feat:添加会议分享功能 - 100%

This commit is contained in:
weifashi 2023-08-30 18:21:37 +08:00
parent 3a2c40a43e
commit 0c4abb5db3
9 changed files with 180 additions and 35 deletions

View File

@ -613,7 +613,10 @@ class UsersController extends AbstractController
*/ */
public function basic() public function basic()
{ {
User::auth(); $sharekey = Request::header('Sharekey');
if(empty($sharekey) || !Meeting::getShareInfo($sharekey)){
User::auth();
}
// //
$userid = Request::input('userid'); $userid = Request::input('userid');
$array = Base::json2array($userid); $array = Base::json2array($userid);
@ -1120,7 +1123,7 @@ class UsersController extends AbstractController
* - join: 加入会议有效参数meetingid (必填) * - join: 加入会议有效参数meetingid (必填)
* @apiParam {String} [meetingid] 频道ID不是数字 * @apiParam {String} [meetingid] 频道ID不是数字
* @apiParam {String} [name] 会话ID * @apiParam {String} [name] 会话ID
* @apiParam {String} [meetingsign] 签名 * @apiParam {String} [sharekey] 分享的key
* @apiParam {String} [username] 用户名称 * @apiParam {String} [username] 用户名称
* @apiParam {Array} [userids] 邀请成员 * @apiParam {Array} [userids] 邀请成员
* *
@ -1134,11 +1137,17 @@ class UsersController extends AbstractController
$meetingid = trim(Request::input('meetingid')); $meetingid = trim(Request::input('meetingid'));
$name = trim(Request::input('name')); $name = trim(Request::input('name'));
$userids = Request::input('userids'); $userids = Request::input('userids');
$meetingsign = trim(Request::input('meetingsign')); $sharekey = trim(Request::input('sharekey'));
$username = trim(Request::input('username')); $username = trim(Request::input('username'));
$user = empty($meetingsign) ? User::auth() : null; $user = null;
if(!empty($sharekey) && $type === 'join'){
if(!Meeting::getShareInfo($sharekey)){
return Base::retError('分享链接已过期');
}
}else{
$user = User::auth();
}
$isCreate = false; $isCreate = false;
// 创建、加入 // 创建、加入
if ($type === 'join') { if ($type === 'join') {
$meeting = Meeting::whereMeetingid($meetingid)->first(); $meeting = Meeting::whereMeetingid($meetingid)->first();
@ -1169,7 +1178,10 @@ class UsersController extends AbstractController
if (empty($meetingSetting['appid']) || empty($meetingSetting['app_certificate'])) { if (empty($meetingSetting['appid']) || empty($meetingSetting['app_certificate'])) {
return Base::retError('会议功能配置错误,请联系管理员'); return Base::retError('会议功能配置错误,请联系管理员');
} }
$uid = intval(str_pad( Request::header('fd'), 6, 9, STR_PAD_LEFT) . $user?->userid); $uid = intval(str_pad(Base::generatePassword(4,1), 9, 8, STR_PAD_LEFT));
if($user){
$uid = intval(str_pad(Request::header('fd'), 5, 9, STR_PAD_LEFT).$user->userid);
}
try { try {
$service = new AgoraTokenGenerator($meetingSetting['appid'], $meetingSetting['app_certificate'], $meeting->channel, $uid); $service = new AgoraTokenGenerator($meetingSetting['appid'], $meetingSetting['app_certificate'], $meeting->channel, $uid);
} catch (\Exception $e) { } catch (\Exception $e) {
@ -1198,13 +1210,40 @@ class UsersController extends AbstractController
// //
$data['appid'] = $meetingSetting['appid']; $data['appid'] = $meetingSetting['appid'];
$data['uid'] = $uid; $data['uid'] = $uid;
$data['userimg'] = $meetingsign ? 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png' : $user?->userimg; $data['userimg'] = $sharekey ? Base::fillUrl('avatar/'.$username.'.png') : $user?->userimg;
$data['nickname'] = $meetingsign ? $username : $user?->nickname; $data['nickname'] = $sharekey ? $username : $user?->nickname;
$data['token'] = $token; $data['token'] = $token;
$data['msgs'] = $msgs; $data['msgs'] = $msgs;
$data['sharelink'] = $meeting->getShareLink();
//
Meeting::setTouristInfo($data);
//
return Base::retSuccess('success', $data); return Base::retSuccess('success', $data);
} }
/**
* @api {get} api/users/meeting/tourist 16. 【会议】游客信息
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName meeting__tourist
*
* @apiParam {String} tourist_id 游客ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function meeting__tourist()
{
$touristId = trim(Request::input('tourist_id'));
if ($touristInfo = Meeting::getTouristInfo($touristId)) {
return Base::retSuccess('success', $touristInfo);
}
return Base::retError('Id不存在');
}
/** /**
* @api {get} api/users/meeting/invitation 17. 【会议】发送邀请 * @api {get} api/users/meeting/invitation 17. 【会议】发送邀请
* *

View File

@ -2,6 +2,10 @@
namespace App\Models; namespace App\Models;
use Cache;
use App\Module\Base;
use Illuminate\Support\Carbon;
/** /**
* App\Models\Meeting * App\Models\Meeting
* *
@ -30,5 +34,58 @@ namespace App\Models;
*/ */
class Meeting extends AbstractModel class Meeting extends AbstractModel
{ {
const CACHE_KEY = 'meeting_share_link_code';
const CACHE_EXPIRED_TIME = 6; // 小时
/**
* 获取分享链接
* @return mixed
*/
public function getShareLink()
{
$code = base64_encode("{$this->meetingid}" . Base::generatePassword());
Cache::put(self::CACHE_KEY.'_'.$code, [
'id' => $this->id,
'meetingid' => $this->meetingid,
'channel' => $this->channel,
], Carbon::now()->addHours(self::CACHE_EXPIRED_TIME));
return Base::fillUrl("meeting/{$this->meetingid}/".$code);
}
/**
* 获取分享信息
* @return mixed
*/
public static function getShareInfo($code)
{
if(Cache::has(self::CACHE_KEY.'_'.$code)){
return Cache::get(self::CACHE_KEY.'_'.$code);
}
return null;
}
/**
* 保存访客信息
* @return mixed
*/
public static function setTouristInfo($data)
{
Cache::put(Meeting::CACHE_KEY.'_'.$data['uid'], [
'uid' => $data['uid'],
'userimg' => $data['userimg'],
'nickname' => $data['nickname'],
], Carbon::now()->addHours(self::CACHE_EXPIRED_TIME));
}
/**
* 获取访客信息
* @return mixed
*/
public static function getTouristInfo($touristId)
{
if(Cache::has(Meeting::CACHE_KEY.'_'.$touristId)){
return Base::retSuccess('success', Cache::get(Meeting::CACHE_KEY.'_'.$touristId) );
}
return null;
}
} }

View File

@ -337,7 +337,7 @@ export default {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'users/basic', url: 'users/basic',
data: { data: {
userid: [ (uuid+"").substring(6) ] userid: [ (uuid+"").substring(5) ]
} }
}).then(({data}) => { }).then(({data}) => {
$A.eeuiAppSendMessage({ $A.eeuiAppSendMessage({

View File

@ -5,14 +5,14 @@
v-model="addShow" v-model="addShow"
:title="$L(addData.type === 'join' ? '加入会议' : '新会议')" :title="$L(addData.type === 'join' ? '加入会议' : '新会议')"
:mask-closable="false" :mask-closable="false"
:closable="!addData.meetingsign"> :closable="!addData.sharekey">
<Form ref="addForm" :model="addData" label-width="auto" @submit.native.prevent> <Form ref="addForm" :model="addData" label-width="auto" @submit.native.prevent>
<template v-if="addData.type === 'join'"> <template v-if="addData.type === 'join'">
<!-- 加入会议 --> <!-- 加入会议 -->
<FormItem v-if="addData.name" prop="userids" :label="$L('会议主题')"> <FormItem v-if="addData.name" prop="userids" :label="$L('会议主题')">
<Input v-model="addData.name" disabled/> <Input v-model="addData.name" disabled/>
</FormItem> </FormItem>
<FormItem v-if="addData.meetingsign" prop="username" :label="$L('你的姓名')"> <FormItem v-if="addData.sharekey" prop="username" :label="$L('你的姓名')">
<Input v-model="addData.username" :placeholder="$L('请输入你的姓名')"/> <Input v-model="addData.username" :placeholder="$L('请输入你的姓名')"/>
</FormItem> </FormItem>
<FormItem prop="meetingid" :label="$L('会议频道ID')"> <FormItem prop="meetingid" :label="$L('会议频道ID')">
@ -40,7 +40,7 @@
</FormItem> </FormItem>
</Form> </Form>
<div slot="footer" class="adaption"> <div slot="footer" class="adaption">
<Button type="default" @click="addShow=false" v-if="!addData.meetingsign">{{$L('取消')}}</Button> <Button type="default" @click="addShow=false" v-if="!addData.sharekey">{{$L('取消')}}</Button>
<Button type="primary" :loading="loadIng > 0" @click="onSubmit">{{$L(addData.type === 'join' ? '加入会议' : '开始会议')}}</Button> <Button type="primary" :loading="loadIng > 0" @click="onSubmit">{{$L(addData.type === 'join' ? '加入会议' : '开始会议')}}</Button>
</div> </div>
</Modal> </Modal>
@ -76,17 +76,17 @@
<Button type="primary" @click="onInvitation('open')"> <Button type="primary" @click="onInvitation('open')">
<i class="taskfont">&#xe646;</i> <i class="taskfont">&#xe646;</i>
</Button> </Button>
<Button type="primary" v-if="!addData.meetingsign" @click="meetingMini = true"> <Button type="primary" v-if="!addData.sharekey" @click="meetingMini = true">
<i class="taskfont">&#xe656;</i> <i class="taskfont">&#xe656;</i>
</Button> </Button>
<Button type="warning" v-if="!addData.meetingsign" :loading="loadIng > 0" @click="onClose"> <Button type="warning" :loading="loadIng > 0" @click="onClose">
<i class="taskfont">&#xe612;</i> <i class="taskfont">&#xe612;</i>
</Button> </Button>
</template> </template>
<template v-else> <template v-else>
<Button type="primary" @click="onInvitation('open')">{{$L('邀请')}}</Button> <Button type="primary" @click="onInvitation('open')">{{$L('邀请')}}</Button>
<Button type="primary" v-if="!addData.meetingsign" @click="meetingMini = true">{{$L('最小化')}}</Button> <Button type="primary" v-if="!addData.sharekey" @click="meetingMini = true">{{$L('最小化')}}</Button>
<Button type="warning" v-if="!addData.meetingsign" :loading="loadIng > 0" @click="onClose">{{$L('离开会议')}}</Button> <Button type="warning" :loading="loadIng > 0" @click="onClose">{{$L('离开会议')}}</Button>
</template> </template>
</div> </div>
</Modal> </Modal>
@ -113,7 +113,7 @@
</FormItem> </FormItem>
</Form> </Form>
<div slot="footer" class="adaption"> <div slot="footer" class="adaption">
<Button type="default" @click="invitationShow=false">{{$L('取消')}}</Button> <Button type="default" @click="linkCopy">{{$L('复制链接')}}</Button>
<Button type="primary" :loading="invitationLoad" @click="onInvitation('submit')">{{$L('发送邀请')}}</Button> <Button type="primary" :loading="invitationLoad" @click="onInvitation('submit')">{{$L('发送邀请')}}</Button>
</div> </div>
</Modal> </Modal>
@ -193,7 +193,6 @@ export default {
}, },
meetingWindow: { meetingWindow: {
handler(val) { handler(val) {
console.log(val)
switch (val.type) { switch (val.type) {
case 'add': case 'add':
this.addShow = val.show; this.addShow = val.show;
@ -203,10 +202,10 @@ export default {
this.addShow = val.show; this.addShow = val.show;
this.loadIng = 0; this.loadIng = 0;
this.addData.type = 'join'; this.addData.type = 'join';
if(val.meetingsign){ if(val.meetingSharekey){
this.addData.sharekey = val.meetingSharekey;
this.addData.meetingid = val.meetingid || ''; this.addData.meetingid = val.meetingid || '';
this.addData.meetingdisabled = val.meetingsign ? true : false; this.addData.meetingdisabled = val.meetingSharekey ? true : false;
this.addData.meetingsign = val.meetingsign;
} }
break; break;
case 'invitation': case 'invitation':
@ -275,6 +274,9 @@ export default {
}).then(({data}) => { }).then(({data}) => {
this.$set(this.addData, 'name', data.name); this.$set(this.addData, 'name', data.name);
this.$set(this.addData, 'meetingid', data.meetingid); this.$set(this.addData, 'meetingid', data.meetingid);
this.$set(this.addData, 'sharelink', data.sharelink);
this.$set(this.localUser, 'nickname', data.nickname);
this.$set(this.localUser, 'userimg', data.userimg);
this.$store.dispatch("saveDialogMsg", data.msgs); this.$store.dispatch("saveDialogMsg", data.msgs);
this.$store.dispatch("updateDialogLastMsg", data.msgs); this.$store.dispatch("updateDialogLastMsg", data.msgs);
delete data.name; delete data.name;
@ -294,6 +296,7 @@ export default {
video: this.addData.tracks.includes("video"), video: this.addData.tracks.includes("video"),
audio: this.addData.tracks.includes("audio"), audio: this.addData.tracks.includes("audio"),
meetingid: data.meetingid, meetingid: data.meetingid,
sharelink: data.sharelink,
alert: { alert: {
title: this.$L('温馨提示'), title: this.$L('温馨提示'),
message: this.$L('确定要离开会议吗?'), message: this.$L('确定要离开会议吗?'),
@ -338,6 +341,10 @@ export default {
onInvitation(type) { onInvitation(type) {
if (type === 'open') { if (type === 'open') {
if(this.addData.sharekey){
this.linkCopy();
return;
}
this.invitationData = { this.invitationData = {
userids: [], userids: [],
meetingid: this.addData.meetingid meetingid: this.addData.meetingid
@ -369,6 +376,10 @@ export default {
okText: '退出', okText: '退出',
onOk: async _ => { onOk: async _ => {
await this.leave() await this.leave()
if(this.addData.sharekey){
this.addShow = true;
this.loadIng = 0;
}
resolve() resolve()
} }
}); });
@ -376,7 +387,6 @@ export default {
}, },
async join(options) { async join(options) {
console.log(options)
this.loadIng++; this.loadIng++;
// //
AgoraRTC.onMicrophoneChanged = async (changedDevice) => { AgoraRTC.onMicrophoneChanged = async (changedDevice) => {
@ -521,7 +531,16 @@ export default {
if (item) { if (item) {
await this.agoraClient.unsubscribe(user, mediaType); await this.agoraClient.unsubscribe(user, mediaType);
} }
} },
linkCopy() {
this.$copyText(this.addData.sharelink).then(_ => {
$A.messageSuccess('已复制会议邀请链接');
}).catch(_ => {
$A.messageError('复制失败');
});
this.invitationShow = false;
},
} }
} }
</script> </script>

View File

@ -1,7 +1,13 @@
<template> <template>
<div class="meeting-player"> <div class="meeting-player">
<div :id="id" class="player" :style="playerStyle"></div> <div :id="id" class="player" :style="playerStyle"></div>
<UserAvatar :userid="userid" :size="36" :borderWitdh="2"/> <UserAvatar v-if="userid" :userid="userid" :size="36" :borderWitdh="2"/>
<div v-else-if="tourist.userimg" class="common-avatar avatar-wrapper">
<div class="avatar-box online">
<em style="transform: scale(1.0625);"></em>
<EAvatar :size="36" style="border: 2px solid #FFFFFF;" :src="tourist.userimg"></EAvatar>
</div>
</div>
<div class="player-state"> <div class="player-state">
<i v-if="!audio" class="taskfont">&#xe7c7;</i> <i v-if="!audio" class="taskfont">&#xe7c7;</i>
<i v-if="!video" class="taskfont">&#xe7c8;</i> <i v-if="!video" class="taskfont">&#xe7c8;</i>
@ -32,7 +38,12 @@ export default {
}, },
data() { data() {
return { return {
timer: null timer: null,
tourist: {
uid: '',
nickname: '',
userimg: '',
}
} }
}, },
mounted() { mounted() {
@ -52,8 +63,11 @@ export default {
...mapState(['cacheUserBasic']), ...mapState(['cacheUserBasic']),
userid() { userid() {
if (this.player.uid) { if (this.player.uid) {
console.log(parseInt( (this.player.uid+"").substring(6) )); if( (this.player.uid + '').indexOf('88888') !== -1 ){
return parseInt( (this.player.uid+"").substring(6) ) this.getTouristInfo();
return 0;
}
return parseInt( (this.player.uid+"").substring(5) ) || 0
} }
return 0 return 0
}, },
@ -63,11 +77,12 @@ export default {
return { return {
backgroundImage: `url("${user.userimg}")` backgroundImage: `url("${user.userimg}")`
} }
}else{ }else if(this.tourist.userimg){
return { return {
backgroundColor: '#000000' backgroundImage: `url("${this.tourist.userimg}")`
} }
} }
return null;
}, },
audio() { audio() {
return !!this.player.audioTrack return !!this.player.audioTrack
@ -103,6 +118,18 @@ export default {
console.log("Meeting Player Error", e); console.log("Meeting Player Error", e);
} }
}) })
},
getTouristInfo() {
this.$store.dispatch("call", {
url: 'users/meeting/tourist',
data: {
tourist_id: this.player.uid
}
}).then(({data}) => {
this.tourist = data.data;
}).catch(({msg}) => {
$A.modalError(msg);
});
} }
} }
} }

View File

@ -14,8 +14,8 @@ export default {
mounted() { mounted() {
this.$store.dispatch("showMeetingWindow",{ this.$store.dispatch("showMeetingWindow",{
type: "join", type: "join",
meetingsign: 'sign', meetingid: this.$route.params.meetingId,
meetingid: 10812486455, meetingSharekey: this.$route.params.sharekey,
meetingdisabled: true, meetingdisabled: true,
}) })
}, },

View File

@ -11,7 +11,7 @@ export default [
}, },
{ {
name: 'meeting', name: 'meeting',
path: '/meeting', path: '/meeting/:meetingId?/:sharekey?',
component: () => import('./pages/meeting.vue'), component: () => import('./pages/meeting.vue'),
}, },
{ {

View File

@ -112,6 +112,9 @@ export default {
'version': window.systemInfo.version || "0.0.1", 'version': window.systemInfo.version || "0.0.1",
'platform': $A.Platform, 'platform': $A.Platform,
} }
if(!state.userToken && state.meetingWindow?.meetingSharekey){
header.sharekey = state.meetingWindow.meetingSharekey;
}
if ($A.isJson(params.header)) { if ($A.isJson(params.header)) {
params.header = Object.assign(header, params.header) params.header = Object.assign(header, params.header)
} else { } else {
@ -3608,13 +3611,13 @@ export default {
* @param type * @param type
* @param meetingid * @param meetingid
*/ */
showMeetingWindow({state}, {type, meetingid, meetingdisabled, meetingsign}) { showMeetingWindow({state}, {type, meetingid, meetingdisabled, meetingSharekey}) {
state.meetingWindow = { state.meetingWindow = {
show: true, show: true,
type: type, type: type,
meetingid: meetingid, meetingid: meetingid,
meetingdisabled: meetingdisabled, meetingdisabled: meetingdisabled,
meetingsign: meetingsign meetingSharekey: meetingSharekey
}; };
}, },

View File

@ -199,7 +199,7 @@ export default {
// 审批待办未读数量 // 审批待办未读数量
approveUnreadNumber: 0, approveUnreadNumber: 0,
// 会议窗口 // 会议
meetingWindow: { meetingWindow: {
show: false, show: false,
type: "", type: "",