Commit 3ed2f983 by 罗长华

Merge remote-tracking branch 'origin/feature-cluster' into feature-cluster

parents cf654e61 69dcde6a
......@@ -2,6 +2,7 @@ package com.wecloud.im.controller;
import com.alibaba.fastjson.JSON;
import com.wecloud.im.param.AgreeToMultiMeetParam;
import com.wecloud.im.param.HeartbeatMultiMeetParam;
import com.wecloud.im.param.InviteToMultiMeetParam;
import com.wecloud.im.param.LeaveFromMultiMeetParam;
import com.wecloud.im.param.NotAnsweredMultiMeetParam;
......@@ -81,5 +82,12 @@ public class ImMultiMeetController extends BaseController {
return ApiResult.ok();
}
@PostMapping("/heartbeat")
@ApiOperation(value = "发送心跳", notes = "发送心跳")
public ApiResult<Boolean> heartbeat(@RequestBody HeartbeatMultiMeetParam param) {
multiMeetService.heartbeat(param);
return ApiResult.ok();
}
}
......@@ -52,6 +52,9 @@ public class ImMultiRtcRoomMember extends BaseEntity {
@ApiModelProperty("呼叫时间")
private Date callTime;
@ApiModelProperty("心跳时间")
private Date heartbeatTime;
@ApiModelProperty("创建时间")
private Date createTime;
......
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @Author wenzhida
* @Date 2022/4/1 20:09
* @Description 多人音视频会议发送心跳入参
*/
@Data
@ApiModel(value = "多人音视频会议发送心跳入参")
public class HeartbeatMultiMeetParam implements Serializable {
private static final long serialVersionUID = -5000410376842426276L;
@ApiModelProperty(value = "会话id,可选", required = false)
private Long conversationId;
@ApiModelProperty(value = "多人会议房间id", required = true)
private String roomId;
}
package com.wecloud.im.service;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImMultiRtcRoomMember;
import io.geekidea.springbootplus.framework.common.service.BaseService;
......@@ -13,7 +12,7 @@ public interface ImMultiRtcRoomMemberService extends BaseService<ImMultiRtcRoomM
/**
* 改变会议成员状态
* @param currentClient
* @param fkClientId
* @param roomId
* @param state
*/
......
package com.wecloud.multimeet.service;
import com.wecloud.im.param.AgreeToMultiMeetParam;
import com.wecloud.im.param.HeartbeatMultiMeetParam;
import com.wecloud.im.param.InviteToMultiMeetParam;
import com.wecloud.im.param.LeaveFromMultiMeetParam;
import com.wecloud.im.param.NotAnsweredMultiMeetParam;
......@@ -45,8 +46,19 @@ public interface MultiMeetService {
void notAnswered(NotAnsweredMultiMeetParam param);
/**
* 心跳接收
* @param param
*/
void heartbeat(HeartbeatMultiMeetParam param);
/**
* 呼叫超时逻辑处理
*/
void callingTimeout();
/**
* 异常断线逻辑处理
*/
void disconnect();
}
package com.wecloud.multimeet.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImMultiRtcRoom;
......@@ -8,6 +9,7 @@ import com.wecloud.im.entity.ImMultiRtcRoomMember;
import com.wecloud.im.enums.MultiRtcMemberStateEnum;
import com.wecloud.im.enums.MultiRtcRoomStateEnum;
import com.wecloud.im.param.AgreeToMultiMeetParam;
import com.wecloud.im.param.HeartbeatMultiMeetParam;
import com.wecloud.im.param.InviteToMultiMeetParam;
import com.wecloud.im.param.LeaveFromMultiMeetParam;
import com.wecloud.im.param.NotAnsweredMultiMeetParam;
......@@ -122,6 +124,7 @@ public class MultiMeetServiceImpl implements MultiMeetService {
if (rtcRoomMember != null) {
// 该房间已邀请过一次,再次发起邀请
rtcRoomMember.setState(MultiRtcMemberStateEnum.CALLING.getCode());
rtcRoomMember.setCallTime(new Date());
imMultiRtcRoomMemberService.updateById(rtcRoomMember);
continue;
}
......@@ -132,6 +135,7 @@ public class MultiMeetServiceImpl implements MultiMeetService {
roomMember.setFkClientId(toClient.getId());
roomMember.setClientId(toClient.getClientId());
roomMember.setState(MultiRtcMemberStateEnum.CALLING.getCode());
roomMember.setCallTime(new Date());
roomMember.setCreateTime(new Date());
roomMember.setUpdateTime(new Date());
roomMembersToSave.add(roomMember);
......@@ -149,6 +153,8 @@ public class MultiMeetServiceImpl implements MultiMeetService {
roomMember.setFkClientId(currentClient.getId());
roomMember.setClientId(currentClient.getClientId());
roomMember.setState(MultiRtcMemberStateEnum.ANSWERED.getCode());
roomMember.setCallTime(new Date());
roomMember.setHeartbeatTime(new Date());
roomMember.setCreateTime(new Date());
roomMember.setUpdateTime(new Date());
roomMembersToSave.add(roomMember);
......@@ -266,13 +272,22 @@ public class MultiMeetServiceImpl implements MultiMeetService {
}
@Override
public void heartbeat(HeartbeatMultiMeetParam param) {
ImClient currentClient = imClientService.getCurrentClient();
imMultiRtcRoomMemberService.update(new UpdateWrapper<ImMultiRtcRoomMember>().lambda()
.eq(ImMultiRtcRoomMember::getRoomId, param.getRoomId())
.eq(ImMultiRtcRoomMember::getFkClientId, currentClient.getId())
.set(ImMultiRtcRoomMember::getHeartbeatTime, new Date()));
}
@Override
public void callingTimeout() {
// 10 秒之前的时间
Date tenSecondsBefore = DateUtils.addSeconds(new Date(), -10);
// 获取呼叫时间为10秒之前 并且状态为呼叫中的会议成员
// 60 秒之前的时间
Date beforeTime = DateUtils.addSeconds(new Date(), -60);
// 获取呼叫时间为120秒之前 并且状态为呼叫中的会议成员
List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
new QueryWrapper<ImMultiRtcRoomMember>().lambda()
.eq(ImMultiRtcRoomMember::getCallTime, tenSecondsBefore)
.lt(ImMultiRtcRoomMember::getCallTime, beforeTime)
.eq(ImMultiRtcRoomMember::getState, MultiRtcMemberStateEnum.CALLING.getCode())
);
if (CollectionUtils.isEmpty(rtcRoomMemberList)) {
......@@ -284,4 +299,23 @@ public class MultiMeetServiceImpl implements MultiMeetService {
imMultiRtcRoomMemberService.updateBatchById(rtcRoomMemberList);
}
@Override
public void disconnect() {
// 20 秒之前的时间
Date beforeTime = DateUtils.addSeconds(new Date(), -20);
// 获取最后心跳时间为20秒之前 并且状态为接听中的会议成员
List<ImMultiRtcRoomMember> rtcRoomMemberList = imMultiRtcRoomMemberService.list(
new QueryWrapper<ImMultiRtcRoomMember>().lambda()
.lt(ImMultiRtcRoomMember::getHeartbeatTime, beforeTime)
.eq(ImMultiRtcRoomMember::getState, MultiRtcMemberStateEnum.ANSWERED.getCode())
);
if (CollectionUtils.isEmpty(rtcRoomMemberList)) {
return;
}
for (ImMultiRtcRoomMember roomMember : rtcRoomMemberList) {
roomMember.setState(MultiRtcMemberStateEnum.DISCONNECT.getCode());
}
imMultiRtcRoomMemberService.updateBatchById(rtcRoomMemberList);
}
}
-- 在feature-cluster 2021年12月22日之后,需要执行的的sql增量脚本
-- 在feature-cluster 2021年12月22日之后,需要执行的的sql增量脚本
......@@ -119,6 +119,7 @@ CREATE TABLE `im_client_device`
-- 20220315 by wenzhida
DROP TABLE IF EXISTS `im_multi_rtc_room`;
CREATE TABLE `im_multi_rtc_room`
(
`id` bigint NOT NULL COMMENT '主键id',
......@@ -132,6 +133,7 @@ CREATE TABLE `im_multi_rtc_room`
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='多人音视频房间表';
DROP TABLE IF EXISTS `im_multi_rtc_room_member`;
CREATE TABLE `im_multi_rtc_room_member`
(
`id` bigint NOT NULL COMMENT '主键id',
......@@ -141,12 +143,14 @@ CREATE TABLE `im_multi_rtc_room_member`
`client_id` varchar(200) DEFAULT NULL COMMENT '客户方提供的唯一id',
`state` tinyint NOT NULL DEFAULT '1' COMMENT '房间成员状态,1:呼叫中,2:已接听,3:已拒绝,4:未接听,5:已断开',
`call_time` timestamp NULL DEFAULT NULL COMMENT '呼叫时间',
`heartbeat_time` timestamp NULL DEFAULT NULL COMMENT '心跳时间',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_fk_rtc_room_id` (`fk_rtc_room_id`) USING BTREE,
KEY `idx_room_id` (`room_id`) USING BTREE,
KEY `idx_call_time_state` (`call_time`, `state`) USING BTREE,
KEY `idx_heartbeat_time_state` (`heartbeat_time`, `state`) USING BTREE,
KEY `idx_client_id` (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='多人音视频房间成员表';
......
......@@ -29,4 +29,15 @@ public class MultiMeetScheduled {
log.info("呼叫超时处理结束...");
}
/**
* 接听中成员异常断线处理
* 每10秒执行一次 (5秒一次心跳 - 20秒未收到心跳,认为已经异常断开连接)
*/
@Scheduled(cron = "*/10 * * * * ?")
public void disconnect() {
log.info("异常断线处理开始...");
multiMeetService.disconnect();
log.info("异常断线处理结束...");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment