This commit is contained in:
2023-12-07 14:23:26 +08:00
parent 228943fc02
commit 0760d28d36
13 changed files with 213 additions and 49 deletions

View File

@@ -371,6 +371,11 @@ function SrsRtcPlayerAsync() {
await self.pc.setLocalDescription(offer); await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) { var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft // @see https://github.com/rtcdn/rtcdn-draft
conf.apiUrl = conf.apiUrl.replace('172.16.3.17', '172.16.3.18');
conf.streamUrl = conf.streamUrl.replace('172.16.3.17', '172.16.3.18');
var data = { var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl, api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp clientip: null, sdp: offer.sdp

View File

@@ -92,7 +92,7 @@ export default {
isForce: "0", isForce: "0",
hasVideo: "1", hasVideo: "1",
callSessionId: "", callSessionId: "",
callStatus: "watting",//房间状态 callStatus: "0",//房间状态 1 未开始等待 2已开始 3已关闭
callRoomInfoRequetTimes:0,//房间拉取次数 callRoomInfoRequetTimes:0,//房间拉取次数
}; };
}, },
@@ -141,8 +141,8 @@ export default {
message: `${this.inviteName}邀请你加入${this.videoTitle}聊天`, message: `${this.inviteName}邀请你加入${this.videoTitle}聊天`,
}) })
.then(() => { .then(() => {
this.callBegin();
this.init(); this.init();
this.callStatus=1
// on confirm // on confirm
}) })
.catch(() => { .catch(() => {
@@ -160,6 +160,7 @@ export default {
this.callSessionId = uuidv4(); this.callSessionId = uuidv4();
this.sendInvite(); this.sendInvite();
this.init(); this.init();
this.callStatus=1
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
@@ -435,6 +436,19 @@ export default {
}, },
}).then(res => { }).then(res => {
if (res.status == 200) { if (res.status == 200) {
if(this.isPublish==1){
if(res.data.data.length==2){
this.callStatus=2
this.callBegin();
}
}else {
if(res.data.data.length==1){
this.callStatus=2
this.callBegin();
}
}
this.generateVideoRoomStream(res.data.data) this.generateVideoRoomStream(res.data.data)
}else { }else {
this.generateVideoRoomStream([]) this.generateVideoRoomStream([])
@@ -459,12 +473,13 @@ export default {
}) })
}, },
callBegin() { callBegin() {
let passiveId=this.roomId==this.uid?this.tuid:this.tuid
let roomInfo = { let roomInfo = {
id:uuidv4(), id:uuidv4(),
inviteId: this.roomId,//后面改成发起人的手机号或者id inviteId: this.roomId,//后面改成发起人的手机号或者id
roomId: this.roomId, roomId: this.roomId,
passiveId: this.uid, passiveId: passiveId,
isForce: this.isForce, isForce: this.isForce,
type: this.hasVideo == 1 ? "video" : "voice", type: this.hasVideo == 1 ? "video" : "voice",
} }
@@ -533,12 +548,80 @@ export default {
}, },
generateVideoRoomStream(videoList) { generateVideoRoomStream(videoList) {
this.callRoomInfoRequetTimes=this.callRoomInfoRequetTimes+1; this.callRoomInfoRequetTimes=this.callRoomInfoRequetTimes+1;
if(this.callRoomInfoRequetTimes>5){ //如果电话一直未拨通
if(videoList.length==0){ if(this.callStatus==1){
if(this.callRoomInfoRequetTimes>12){
if(videoList.length==0){
// 拉取5次房间列表之后如果房间里面视频流的数量为0则关掉房间 // 拉取5次房间列表之后如果房间里面视频流的数量为0则关掉房间
this.videoDownAction() Notify({
} message: '通讯异常,即将挂断',
duration: 2,
type: 'primary'
});
setTimeout(()=>{
this.videoDownAction()
},1500)
}
if(videoList.length==1){
// 拉取5次房间列表之后如果房间里面视频流的数量为1,并且是自己的流,则关闭房间
if(videoList[0].publishId==this.uid){
Notify({
message: '通讯异常,即将挂断',
duration: 2,
type: 'primary'
});
setTimeout(()=>{
this.videoDownAction()
},1500)
}
//如果房间里面只有一路流,并且是查岗模式,则表示正常
if(this.isPublish==0){
this.callRoomInfoRequetTimes=0;
}
}
if(videoList.length==2){
this.callRoomInfoRequetTimes=0;
}
}
} }
//如果电话打通过一次
if(this.callStatus==2){
if(this.callRoomInfoRequetTimes>2){
if(videoList.length==0){
// 拉取5次房间列表之后如果房间里面视频流的数量为0则关掉房间
Notify({
message: '通讯异常,即将挂断',
duration: 2,
type: 'primary'
});
setTimeout(()=>{
this.videoDownAction()
},1500)
}
if(videoList.length==1){
// 拉取5次房间列表之后如果房间里面视频流的数量为1,并且是自己的流,则关闭房间
if(videoList[0].publishId==this.uid){
Notify({
message: '通讯异常,即将挂断',
duration: 2,
type: 'primary'
});
setTimeout(()=>{
this.videoDownAction()
},1500)
}
//如果房间里面只有一路流,并且是查岗模式,则表示正常
if(this.isPublish==0){
this.callRoomInfoRequetTimes=0;
}
}
if(videoList.length==2){
this.callRoomInfoRequetTimes=0;
}
}
}
let hasUpdate = false; let hasUpdate = false;
if (this.oldVideoStreamList == null) { if (this.oldVideoStreamList == null) {

View File

@@ -2,6 +2,7 @@ package com.lld.im.service.call.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lld.im.common.ResponseVO; import com.lld.im.common.ResponseVO;
@@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.Date; import java.util.Date;
import java.util.List;
@Slf4j @Slf4j
@RestController @RestController
@@ -61,9 +63,25 @@ public class ImCallHistoryController {
@PostMapping("/callBegin") @PostMapping("/callBegin")
public ResponseVO callBegin(@RequestBody ImCallHistoryEntity imCallHistoryEntity) { public ResponseVO callBegin(@RequestBody ImCallHistoryEntity imCallHistoryEntity) {
//判断当前房间是否有通话
QueryWrapper<ImCallHistoryEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("session_id",imCallHistoryEntity.getSessionId());
queryWrapper.eq("status",1);
List<ImCallHistoryEntity> imCallHistoryEntityList = callHistoryMapper.selectList(queryWrapper);
//如果存在则直接返回旧的记录
if(imCallHistoryEntityList.size()>0){
return ResponseVO.successResponse(imCallHistoryEntityList.get(0));
}
// 如果不存在则插入新的记录
imCallHistoryEntity.setCreateTime(new Date()); imCallHistoryEntity.setCreateTime(new Date());
imCallHistoryEntity.setStatus("1"); imCallHistoryEntity.setStatus("1");
ResponseVO<ImUserDataEntity> inviteUserInfo = imUserService.getSingleUserInfo(imCallHistoryEntity.getRoomId(), 10000); ResponseVO<ImUserDataEntity> inviteUserInfo = imUserService.getSingleUserInfo(imCallHistoryEntity.getRoomId(), 10000);
ResponseVO<ImUserDataEntity> passiveUserInfo = imUserService.getSingleUserInfo(imCallHistoryEntity.getPassiveId(), 10000); ResponseVO<ImUserDataEntity> passiveUserInfo = imUserService.getSingleUserInfo(imCallHistoryEntity.getPassiveId(), 10000);

View File

@@ -28,5 +28,6 @@ public class ImCallHistoryEntity {
// 持续时间(秒) // 持续时间(秒)
private Integer continueSecond; private Integer continueSecond;
private Integer checkTimes;
} }

View File

@@ -39,12 +39,15 @@ public class ImCallHistoryService {
List<ImCallHistoryEntity> imCallHistoryEntityList = callHistoryMapper.selectList(queryWrapper); List<ImCallHistoryEntity> imCallHistoryEntityList = callHistoryMapper.selectList(queryWrapper);
for(ImCallHistoryEntity imCallHistory: imCallHistoryEntityList){ if(imCallHistoryEntityList.size()>0){
imCallHistory.setStatus("2"); for(ImCallHistoryEntity imCallHistory: imCallHistoryEntityList){
callHistoryMapper.updateById(imCallHistory); imCallHistory.setStatus("1");
callHistoryMapper.updateById(imCallHistory);
}
}else {
callHistoryMapper.insert(callHistory);
} }
callHistoryMapper.insert(callHistory);
} }
public List<ImCallHistoryEntity> queryListByStatus(String status){ public List<ImCallHistoryEntity> queryListByStatus(String status){
@@ -55,6 +58,62 @@ public class ImCallHistoryService {
} }
public void updateCallHistory(ImCallHistoryEntity callHistory){
callHistoryMapper.updateById(callHistory);
}
public void closeCallRoomWithError(ImCallHistoryEntity callHistory){
Date _now=new Date();
QueryWrapper<ImCallHistoryEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("session_id",callHistory.getSessionId());
queryWrapper.eq("status",1);
// end_time
ImCallHistoryEntity imCallHistory = callHistoryMapper.selectOne(queryWrapper);
if(!ObjectUtils.isEmpty(imCallHistory)){
imCallHistory.setEndTime(_now);
imCallHistory.setStatus("3");
Duration duration = Duration.between(imCallHistory.getCreateTime().toInstant(),_now.toInstant());
long seconds = TimeUnit.MILLISECONDS.toSeconds(duration.toMillis());
Integer i = Long.valueOf(seconds).intValue();
imCallHistory.setContinueSecond(i);
//TODO 结束之后通知客户端
SendMessageReq req= new SendMessageReq();
req.setAppId(10000);
req.setFromId(imCallHistory.getInviteId());
req.setToId(imCallHistory.getPassiveId());
req.setMessageTime(System.currentTimeMillis());
// req.setMessageRandom();
req.setMessageId(UUID.randomUUID().toString().replace("-", ""));
req.setImei("uniapp");
req.setClientType(1);
Map<String, Object> data = new HashMap<String, Object>();
data.put("userId", imCallHistory.getInviteId());
data.put("msgType", "CALL_"+imCallHistory.getType());
data.put("content", JSONObject.toJSONString(imCallHistory));
req.setMessageBody(JSONObject.toJSONString(data));
p2PMessageService.send(req);
callHistoryMapper.updateById(imCallHistory);
}
}
public void updateBySessionId(ImCallHistoryEntity callHistory){ public void updateBySessionId(ImCallHistoryEntity callHistory){
Date _now=new Date(); Date _now=new Date();
QueryWrapper<ImCallHistoryEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<ImCallHistoryEntity> queryWrapper = new QueryWrapper<>();

View File

@@ -26,6 +26,7 @@ public class WebConfig implements WebMvcConfigurer {
.excludePathPatterns("/v1/file/**") .excludePathPatterns("/v1/file/**")
.excludePathPatterns("/v1/room/**") .excludePathPatterns("/v1/room/**")
.excludePathPatterns("/v1/im_call_history/**") .excludePathPatterns("/v1/im_call_history/**")
.excludePathPatterns("/v1/message/listMessage")
.excludePathPatterns("/v1/message/checkSend"); .excludePathPatterns("/v1/message/checkSend");
} }

View File

@@ -30,29 +30,29 @@ public class CallHistoryTask {
ImRoomService roomService; ImRoomService roomService;
@Scheduled(cron="0/10 * * * * ? ") @Scheduled(cron="0/2 * * * * ? ")
public void executeTask(){ public void executeTask(){
List<ImCallHistoryEntity> callHistoryEntities = callHistoryService.queryListByStatus("1"); List<ImCallHistoryEntity> callHistoryEntities = callHistoryService.queryListByStatus("1");
for(ImCallHistoryEntity callHistoryEntity: callHistoryEntities){ for(ImCallHistoryEntity callHistoryEntity: callHistoryEntities){
ImRoomEntity imRoomEntity = new ImRoomEntity(); if(callHistoryEntity.getCheckTimes()>2){
imRoomEntity.setRoomId(callHistoryEntity.getRoomId()); ImRoomEntity imRoomEntity = new ImRoomEntity();
List<ImRoomEntity> serviceRoomInfo = roomService.getRoomInfo(imRoomEntity); imRoomEntity.setRoomId(callHistoryEntity.getRoomId());
if(serviceRoomInfo.size()<2){ List<ImRoomEntity> serviceRoomInfo = roomService.getRoomInfo(imRoomEntity);
log.info("{}房间里面的流少于2路关掉全部视频流并且判断异常中断",callHistoryEntity.getSessionId()); if(serviceRoomInfo.size()==0){
//房间里面的流少于2路关掉全部视频流并且判断异常中断 log.info("{}房间里面没有流,关掉全部视频流并且设置状态为异常中断",callHistoryEntity.getSessionId());
closeAll(callHistoryEntity,serviceRoomInfo); //房间里面没有流,关掉全部视频流并且判断异常中断
// closeAll(callHistoryEntity,serviceRoomInfo);
}
}else {
callHistoryEntity.setCheckTimes(callHistoryEntity.getCheckTimes()+1);
} }
} }
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void closeAll(ImCallHistoryEntity callHistoryEntity,List<ImRoomEntity> roomList){ public void closeAll(ImCallHistoryEntity callHistoryEntity,List<ImRoomEntity> roomList){
callHistoryEntity.setStatus("3"); callHistoryService.closeCallRoomWithError(callHistoryEntity);
callHistoryService.updateBySessionId(callHistoryEntity);
if(roomList.size()>0){
roomService.deleteAllUserFromRoom(roomList.get(0).getRoomId());
}
} }

View File

@@ -59,6 +59,7 @@ public class SRSStreamTask {
List<StreamEntity> streamEntityList = response.getStreams(); List<StreamEntity> streamEntityList = response.getStreams();
for (StreamEntity streamEntity:streamEntityList){ for (StreamEntity streamEntity:streamEntityList){
String webRTCUrl=streamEntity.getTcUrl()+"/"+streamEntity.getName(); String webRTCUrl=streamEntity.getTcUrl()+"/"+streamEntity.getName();
//如果流存在,则
if(StringUtils.equals(roomEntity.getWebrtcUrl(),webRTCUrl)){ if(StringUtils.equals(roomEntity.getWebrtcUrl(),webRTCUrl)){
hasStream=true; hasStream=true;
break; break;
@@ -71,7 +72,7 @@ public class SRSStreamTask {
if(!hasStream){ if(!hasStream){
roomService.addCheckTimes(roomEntity,roomEntity.getCheckTimes()+1); roomService.addCheckTimes(roomEntity,roomEntity.getCheckTimes()+1);
if(roomEntity.getCheckTimes()>4){ if(roomEntity.getCheckTimes()>4){
roomService.deleteRoomById(roomEntity.getId()); // roomService.deleteRoomById(roomEntity.getId());
} }
}else { }else {
roomService.addCheckTimes(roomEntity,0); roomService.addCheckTimes(roomEntity,0);

View File

@@ -28,10 +28,10 @@
<view v-if="showtool || showEmojitool" :style="'height:558rpx'"></view> <view v-if="showtool || showEmojitool" :style="'height:558rpx'"></view>
<view class="zfb-tk-send-tool" :style="'transform: translateY(-' + keyboardHeight + 'px)'"> <view class="zfb-tk-send-tool" :style="'transform: translateY(-' + keyboardHeight + 'px)'">
<view class="zfb-tk-send-tool-c"> <view class="zfb-tk-send-tool-c">
<view class="zfb-tk-send-tool-icon wxfont" @click="changeShowVice" <!-- <view class="zfb-tk-send-tool-icon wxfont" @click="changeShowVice"
:class="showVice ? 'jianpan' : 'yuyin2'"></view> :class="showVice ? 'jianpan' : 'yuyin2'"></view> -->
<view class="zfb-tk-send-tool-vioce" v-if="showVice"> <view class="zfb-tk-send-tool-vioce" v-if="showVice">
<view class="zfb-tk-send-tool-vioce-item" @longpress="startRecord" @touchend="endRecord">按住说话</view> <!-- <view class="zfb-tk-send-tool-vioce-item" @longpress="startRecord" @touchend="endRecord">按住说话</view> -->
</view> </view>
<view v-else class="zfb-tk-send-tool-input-box" @click="msgFocus = true"><textarea <view v-else class="zfb-tk-send-tool-input-box" @click="msgFocus = true"><textarea
@focus="showtool = false" :focus="msgFocus" class="zfb-tk-send-tool-input" @focus="showtool = false" :focus="msgFocus" class="zfb-tk-send-tool-input"
@@ -363,7 +363,6 @@
console.log("########### 此时需要发送已读回执 ###########") console.log("########### 此时需要发送已读回执 ###########")
this.$store.dispatch('sendP2PMessageReciveAck', message) this.$store.dispatch('sendP2PMessageReciveAck', message)
setTimeout(() => { setTimeout(() => {
this.$store.dispatch('updateChartHistoryFromServerLimit5', { this.$store.dispatch('updateChartHistoryFromServerLimit5', {
userId: this.talkTo.userId, userId: this.talkTo.userId,

View File

@@ -1,3 +1,6 @@
依赖安装
yarn install
核心SDK使用Typescript开发, 使用js-conditional-compile-loader插件条件编译出微信小程序端, Web网页端使用的js sdk 核心SDK使用Typescript开发, 使用js-conditional-compile-loader插件条件编译出微信小程序端, Web网页端使用的js sdk

View File

@@ -4,6 +4,7 @@
"description": "", "description": "",
"main": "src/lim.ts", "main": "src/lim.ts",
"scripts": { "scripts": {
"preview": "yarn install",
"build:weblib": "cross-env NODE_ENV=production platform=web webpack --mode=production --config ./build/webpack.lib.config.js", "build:weblib": "cross-env NODE_ENV=production platform=web webpack --mode=production --config ./build/webpack.lib.config.js",
"build:unilib": "cross-env NODE_ENV=production platform=uniapp webpack --mode=production --config ./build/webpack.lib.config.js", "build:unilib": "cross-env NODE_ENV=production platform=uniapp webpack --mode=production --config ./build/webpack.lib.config.js",
"build:wxlib": "cross-env NODE_ENV=production platform=wx webpack --mode=production --config ./build/webpack.lib.config.js", "build:wxlib": "cross-env NODE_ENV=production platform=wx webpack --mode=production --config ./build/webpack.lib.config.js",
@@ -59,4 +60,4 @@
"webpack-dev-server": "^3.8.0", "webpack-dev-server": "^3.8.0",
"webpack-merge": "^4.2.1" "webpack-merge": "^4.2.1"
} }
} }

View File

@@ -325,24 +325,8 @@ export class ImClient {
} }
public getUserConversationList(lastSequence:Long): Promise<any> {
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/conversation/syncConversationList", this.getRequestParams(), { userId: this.userId,appId:this.appId,maxLimit:100, lastSequence:lastSequence,operater:this.userId })
resolve(resp);
})
}
public queryMsgDetailsByMessageKey(messageKey:Long): Promise<any> { public getChatMessageHistoryList(uid:String,offset:any,limit:any): Promise<any> {
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/message/queryMsgDetailsByMessageKey", this.getRequestParams(), { appId:this.appId, messageKey:messageKey,operater:this.userId })
resolve(resp);
})
}
public getChatMessageHistoryList(uid:String,offset:Long,limit:Long): Promise<any> {
return new Promise((resolve, _) => { return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl); let api = new HttpApi(this.httpUrl);
let resp = api.call("/message/chatHistory", this.getRequestParams(), { userId: uid,appId:this.appId,offset:offset, limit:limit,operater:this.userId }) let resp = api.call("/message/chatHistory", this.getRequestParams(), { userId: uid,appId:this.appId,offset:offset, limit:limit,operater:this.userId })
@@ -350,6 +334,13 @@ export class ImClient {
}) })
} }
public queryConversationList():Promise<any>{
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/message/listMessage", this.getRequestParams(), { userId: this.userId,appId:this.appId,maxLimit:100, lastSequence:0,operater:this.userId })
resolve(resp);
})
}
public createRoom(room:any): Promise<any> { public createRoom(room:any): Promise<any> {
return new Promise((resolve,_)=>{ return new Promise((resolve,_)=>{
@@ -358,6 +349,7 @@ export class ImClient {
return resp; return resp;
}) })
} }
public joinRoom(room:any): Promise<any> { public joinRoom(room:any): Promise<any> {
return new Promise((resolve,_)=>{ return new Promise((resolve,_)=>{
let api = new HttpApi(this.httpUrl); let api = new HttpApi(this.httpUrl);
@@ -477,6 +469,7 @@ export class ImClient {
} }
} }
} }
export let limLogin = async (url: string, req: LoginPack, imClient: ImClient): Promise<{ success: boolean, err?: Error, conn: w3cwebsocket }> => { export let limLogin = async (url: string, req: LoginPack, imClient: ImClient): Promise<{ success: boolean, err?: Error, conn: w3cwebsocket }> => {

View File

@@ -14,4 +14,4 @@ export class MessageContent {
constructor(messageKey:Long) { constructor(messageKey:Long) {
this.messageKey = messageKey; this.messageKey = messageKey;
} }
} }