Commit 1c565855 by Shadow

Merge remote-tracking branch 'origin/xiaohudou_20220427' into xiaohudou_20220427

parents 0ee2a88e 035d15fa
......@@ -3,7 +3,7 @@ package com.wecloud.im.controller;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImRtcRecord;
import com.wecloud.im.param.rtc.RtcRecordParam;
import com.wecloud.im.param.rtc.SingleRtcRecordParam;
import com.wecloud.im.service.ImRtcRecordService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
......@@ -23,18 +23,18 @@ import org.springframework.web.bind.annotation.RestController;
*/
@Slf4j
@RestController
@RequestMapping("/rtcrecord")
@Api(value = "后台通话记录列表")
@RequestMapping("/rtcRecord")
@Api(value = "通话记录控制器")
public class ImRtcRecordController extends BaseController {
@Autowired
private ImRtcRecordService rtcRecordService;
@PostMapping("/listRtcRecords")
@ApiOperation(value = "分页获取后台通话记录")
public ApiResult<Page<ImRtcRecord>> listRtcRecords(@RequestBody RtcRecordParam rtcRecordParam){
log.info("获取后台通话记录入参 {}", JSON.toJSONString(rtcRecordParam));
return ApiResult.ok(rtcRecordService.getPageImRtcRecords(rtcRecordParam));
@ApiOperation(value = "分页获取通话记录")
public ApiResult<Page<ImRtcRecord>> listRtcRecords(@RequestBody SingleRtcRecordParam singleRtcRecordParam) {
log.info("分页获取通话记录入参 {}", JSON.toJSONString(singleRtcRecordParam));
return ApiResult.ok(rtcRecordService.getPageImRtcRecords(singleRtcRecordParam));
}
}
......@@ -58,6 +58,9 @@ public class ImRtcRecord extends BaseEntity {
@ApiModelProperty("音视频结束时间")
private Date endTime;
@ApiModelProperty("通话过程中离线次数统计 用于区分异常断线使用")
private Integer offlineTimes;
@ApiModelProperty("创建时间")
private Date createTime;
......
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @author nanqianhao
......@@ -10,37 +13,52 @@ import java.io.Serializable;
* @apiNote
*/
@Data
public class RtcRecordParam implements Serializable {
@ApiModel(value = "单人rtc音视频查找记录")
public class SingleRtcRecordParam implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2176804812523348248L;
/**
* 发送者id
*/
@ApiModelProperty("发送者id")
private String fromClientId;
/**
* 接受者id
*/
@ApiModelProperty("接受者id")
private String toClientId;
/**
* 房间id
*/
@ApiModelProperty("房间id")
private String channelId;
/**
* 音视频类型
* @see com.wecloud.im.sdk.enums.CallTypeEnum
*/
@ApiModelProperty("音视频类型")
private Integer callType;
/***
* 页码
*/
@ApiModelProperty("页码")
private Integer pageNum;
/**
* 每页数量
*/
@ApiModelProperty("每页数量")
private Integer pageSize;
@ApiModelProperty("开始时间")
private Date createTimeStart;
@ApiModelProperty("结束时间")
private Date createTimeEnd;
}
package com.wecloud.im.sdk.enums;
import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
/**
* @Author Future
* @Date 2022/6/3 13:12
* @Description 单人音视频操作类型枚举
*/
public enum SingleRtcOperateTypeEnum implements BaseEnum {
/**
* 1 - 同意进入频道
*/
JOIN(1, "join"),
/**
* 2 - 拒接进入频道
*/
REJECT(2, "reject"),
/**
* 2 - 离开频道
*/
LEAVE(3, "leave"),
/**
* 4 - 异常原因断开
*/
ABNORMAL_DISCONNECT(4, "abnormalDisconnect");
SingleRtcOperateTypeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
private final Integer code;
private final String desc;
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getDesc() {
return this.desc;
}
}
......@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImRtcRecord;
import com.wecloud.im.param.rtc.CreateRtcChannelParam;
import com.wecloud.im.param.rtc.RtcRecordParam;
import com.wecloud.im.param.rtc.SingleRtcRecordParam;
import io.geekidea.springbootplus.framework.common.service.BaseService;
/**
......@@ -24,7 +24,8 @@ public interface ImRtcRecordService extends BaseService<ImRtcRecord> {
/**
* 更新音视频记录
* @param channelId
* @param type 1-同意进入频道 2-拒接进入频道 3-主动挂断(离开频道)
* @param type 1-同意进入频道 2-拒接进入频道 3-主动挂断(离开频道) 4-异常原因断开
* @see com.wecloud.im.sdk.enums.SingleRtcOperateTypeEnum
*/
void updateRtcRecord(Long channelId, Integer type);
......@@ -32,5 +33,5 @@ public interface ImRtcRecordService extends BaseService<ImRtcRecord> {
/**
* 获取通话记录
*/
Page<ImRtcRecord> getPageImRtcRecords(RtcRecordParam rtcRecordParam);
Page<ImRtcRecord> getPageImRtcRecords(SingleRtcRecordParam singleRtcRecordParam);
}
......@@ -8,14 +8,15 @@ import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImRtcRecord;
import com.wecloud.im.mapper.ImRtcRecordMapper;
import com.wecloud.im.param.rtc.CreateRtcChannelParam;
import com.wecloud.im.param.rtc.RtcRecordParam;
import com.wecloud.im.param.rtc.SingleRtcRecordParam;
import com.wecloud.im.sdk.enums.RtcStateEnum;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.sdk.enums.SingleRtcOperateTypeEnum;
import com.wecloud.im.service.ImRtcRecordService;
import com.wecloud.utils.SnowflakeUtil;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.shiro.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
......@@ -31,7 +32,6 @@ import java.util.Date;
@Service
public class ImRtcRecordServiceImpl extends BaseServiceImpl<ImRtcRecordMapper, ImRtcRecord> implements ImRtcRecordService {
@Async
@Override
public void createRtcRecord(CreateRtcChannelParam param, Long channelId, ImClient currentClient) {
......@@ -66,17 +66,21 @@ public class ImRtcRecordServiceImpl extends BaseServiceImpl<ImRtcRecordMapper, I
if (rtcRecord == null) {
return;
}
if (type == 1) {
if (SingleRtcOperateTypeEnum.JOIN.getCode().equals(type)) {
// 同意进入频道
rtcRecord.setState(RtcStateEnum.ING.getCode());
rtcRecord.setStartTime(new Date());
} else if (type == 2) {
} else if (SingleRtcOperateTypeEnum.REJECT.getCode().equals(type)) {
// 拒接进入频道
rtcRecord.setState(RtcStateEnum.END.getCode());
} else {
} else if (SingleRtcOperateTypeEnum.LEAVE.getCode().equals(type)) {
// 主动挂断(离开频道)
rtcRecord.setState(RtcStateEnum.END.getCode());
rtcRecord.setEndTime(new Date());
} else if (SingleRtcOperateTypeEnum.ABNORMAL_DISCONNECT.getCode().equals(type)) {
// 异常原因(客户端已不在线)
rtcRecord.setState(RtcStateEnum.END.getCode());
rtcRecord.setEndTime(new Date());
}
this.updateById(rtcRecord);
} catch (Exception e) {
......@@ -85,14 +89,24 @@ public class ImRtcRecordServiceImpl extends BaseServiceImpl<ImRtcRecordMapper, I
}
@Override
public Page<ImRtcRecord> getPageImRtcRecords(RtcRecordParam rtcRecordParam) {
public Page<ImRtcRecord> getPageImRtcRecords(SingleRtcRecordParam param) {
if (param.getPageNum() == null) {
throw new BusinessException("页码不可为空");
}
if (param.getPageSize() == null) {
throw new BusinessException("每页数量不可为空");
}
Long appId = SecurityUtils.getCurrentAppId();
LambdaQueryWrapper<ImRtcRecord> rtcRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
rtcRecordLambdaQueryWrapper.like(rtcRecordParam.getFromClientId()!=null,ImRtcRecord::getFromClientId,rtcRecordParam.getFromClientId());
rtcRecordLambdaQueryWrapper.like(rtcRecordParam.getToClientId()!=null,ImRtcRecord::getToClientId,rtcRecordParam.getToClientId());
rtcRecordLambdaQueryWrapper.like(rtcRecordParam.getChannelId()!=null,ImRtcRecord::getChannelId,rtcRecordParam.getChannelId());
rtcRecordLambdaQueryWrapper.eq(rtcRecordParam.getCallType()!=null,ImRtcRecord::getCallType,rtcRecordParam.getCallType());
rtcRecordLambdaQueryWrapper.eq(ImRtcRecord::getFkAppid, appId);
rtcRecordLambdaQueryWrapper.eq(param.getFromClientId() != null, ImRtcRecord::getFromClientId, param.getFromClientId());
rtcRecordLambdaQueryWrapper.eq(param.getToClientId() != null, ImRtcRecord::getToClientId, param.getToClientId());
rtcRecordLambdaQueryWrapper.eq(param.getChannelId() != null, ImRtcRecord::getChannelId, param.getChannelId());
rtcRecordLambdaQueryWrapper.eq(param.getCallType() != null, ImRtcRecord::getCallType, param.getCallType());
rtcRecordLambdaQueryWrapper.lt(param.getCreateTimeEnd() != null, ImRtcRecord::getCreateTime, param.getCreateTimeEnd());
rtcRecordLambdaQueryWrapper.gt(param.getCreateTimeStart() != null, ImRtcRecord::getCreateTime, param.getCreateTimeStart());
rtcRecordLambdaQueryWrapper.orderByDesc(ImRtcRecord::getCreateTime);
Page<ImRtcRecord> pageParam = new Page<>(rtcRecordParam.getPageNum(), rtcRecordParam.getPageSize());
Page<ImRtcRecord> pageParam = new Page<>(param.getPageNum(), param.getPageSize());
Page<ImRtcRecord> page = this.page(pageParam, rtcRecordLambdaQueryWrapper);
return page;
}
......
package com.wecloud.im.ws.cache;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.ws.utils.SpringBeanUtils;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener;
......
......@@ -43,4 +43,9 @@ public interface RtcService {
*/
Boolean candidateForward(CandidateForwardParam candidateForwardParam);
/**
* 异常断线判断处理
*/
void abnormalDisconnect();
}
package com.wecloud.rtc.service.impl;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImRtcRecord;
import com.wecloud.im.param.rtc.CandidateForwardParam;
import com.wecloud.im.param.rtc.CreateRtcChannelParam;
import com.wecloud.im.param.rtc.CreateRtcChannelResult;
......@@ -20,6 +12,7 @@ import com.wecloud.im.param.rtc.JoinRtcChannelParam;
import com.wecloud.im.param.rtc.LeaveRtcChannelParam;
import com.wecloud.im.param.rtc.RejectRtcChannelParam;
import com.wecloud.im.param.rtc.SdpForwardParam;
import com.wecloud.im.sdk.enums.SingleRtcOperateTypeEnum;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientBlacklistService;
import com.wecloud.im.service.ImClientService;
......@@ -36,11 +29,22 @@ import com.wecloud.rtc.service.MangerRtcCacheService;
import com.wecloud.rtc.service.RtcService;
import com.wecloud.rtc.service.WsRtcWrite;
import com.wecloud.utils.SnowflakeUtil;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
public class RtcServiceImpl extends UserStateListener implements RtcService {
private static final Integer MAX_OFFLINE_TIMES = 3;
@Autowired
private ImApplicationService imApplicationService;
......@@ -119,7 +123,7 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
wsRtcWrite.rtcCall(rtcCallResponse, toClient.getId());
// 创建通话记录 todo 优化为mq推送
// 创建通话记录
imRtcRecordService.createRtcRecord(createRtcChannelParam, createRtcChannelResult.getChannelId(), currentClient);
// TODO 待开发 下发安卓和ios系统推送
......@@ -153,8 +157,8 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
wsRtcWrite.clientJoin(rtcSdpForwardResponse, Long.valueOf(toClientId));
}
// 更新通话记录 todo 优化为mq推送
imRtcRecordService.updateRtcRecord(joinRtcChannelParam.getChannelId(), 1);
// 更新通话记录
imRtcRecordService.updateRtcRecord(joinRtcChannelParam.getChannelId(), SingleRtcOperateTypeEnum.JOIN.getCode());
return true;
}
......@@ -187,8 +191,8 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
mangerRtcCacheService.delChannelInfo(rejectRtcChannelParam.getChannelId());
}
// 更新通话记录 todo 优化为mq推送
imRtcRecordService.updateRtcRecord(rejectRtcChannelParam.getChannelId(), 2);
// 更新通话记录
imRtcRecordService.updateRtcRecord(rejectRtcChannelParam.getChannelId(), SingleRtcOperateTypeEnum.REJECT.getCode());
return true;
}
......@@ -198,8 +202,8 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
ImClient currentClient = imClientService.getCurrentClient();
this.leave(leaveRtcChannelParam, currentClient);
// 更新通话记录 todo 优化为mq推送
imRtcRecordService.updateRtcRecord(leaveRtcChannelParam.getChannelId(), 3);
// 更新通话记录
imRtcRecordService.updateRtcRecord(leaveRtcChannelParam.getChannelId(), SingleRtcOperateTypeEnum.LEAVE.getCode());
return true;
}
......@@ -313,6 +317,37 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
return true;
}
@Override
public void abnormalDisconnect() {
List<ImRtcRecord> records = imRtcRecordService.list(new QueryWrapper<ImRtcRecord>().lambda()
.ne(ImRtcRecord::getState, 3));
if (CollectionUtils.isEmpty(records)) {
return;
}
for (ImRtcRecord record : records) {
try {
if (record.getOfflineTimes() >= MAX_OFFLINE_TIMES) {
// 最大离线次数到阈值,做离线处理
imRtcRecordService.updateRtcRecord(record.getChannelId(), SingleRtcOperateTypeEnum.ABNORMAL_DISCONNECT.getCode());
} else {
// 判断频道内两个人是否离线 只要其中一人离线 则认定为离线 将该通话离线次数 +1
ImClient fromClient = imClientService.getCacheImClient(record.getFkAppid(), record.getFromClientId());
ImClient toClient = imClientService.getCacheImClient(record.getFkAppid(), record.getToClientId());
boolean isFromClientOnline = userStateCacheManager.isOnline(fromClient.getId());
boolean isToClientOnline = userStateCacheManager.isOnline(toClient.getId());
if (isFromClientOnline && isToClientOnline) {
// 双方均在线 跳过
continue;
}
record.setOfflineTimes(record.getOfflineTimes() + 1);
imRtcRecordService.updateById(record);
}
} catch (Exception e) {
log.info("单人音视频 {} ,处理异常 ", JSON.toJSONString(record), e);
}
}
}
/**
* 判断是否被拉黑
......
-- 在feature-cluster 2021年12月22日之后,需要执行的的sql增量脚本
-- 在feature-cluster 2021年12月22日之后,需要执行的的sql增量脚本
......@@ -185,6 +185,14 @@ CREATE TABLE `im_rtc_record`
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单人音视频聊天记录表';
ALTER TABLE im_rtc_record
ADD COLUMN `offline_times` tinyint(1) unsigned DEFAULT '0' COMMENT '通话过程中离线次数统计';
ALTER TABLE `im_rtc_record`
ADD INDEX `idx_state_create_time`(`state`, `create_time`);
ALTER TABLE im_conversation
ADD COLUMN `is_encrypt` tinyint(1) unsigned DEFAULT '0' COMMENT '是否加密聊天: 1-是 0-否';
......
......@@ -169,7 +169,7 @@ public class ResourcePathConstants {
/**
* 获取通话记录请求
*/
public static final String LIST_RTCRECORDS_REQUEST = "/api/rtcrecord/listRtcRecords";
public static final String LIST_RTC_RECORDS_REQUEST = "/api/rtcRecord/listRtcRecords";
/**
* 单次发布
......
......@@ -12,7 +12,7 @@ import com.wecloud.im.sdk.model.ImRtcRecord;
import com.wecloud.im.sdk.model.PageResult;
import com.wecloud.im.sdk.model.RtcRecordRequest;
import static com.wecloud.im.sdk.ResourcePathConstants.LIST_RTCRECORDS_REQUEST;
import static com.wecloud.im.sdk.ResourcePathConstants.LIST_RTC_RECORDS_REQUEST;
/**
* @author nanqianhao
......@@ -48,7 +48,7 @@ public class WecloudImRtcRecordOperation extends WecloudImOperation {
}
// 发送请求
RequestMessage request = new WecloudRequestMessageBuilder().setEndpoint(LIST_RTCRECORDS_REQUEST)
RequestMessage request = new WecloudRequestMessageBuilder().setEndpoint(LIST_RTC_RECORDS_REQUEST)
.setMethod(HttpMethod.POST).setParameters(param)
.setOriginalRequest(rtcRecordRequest).build();
......
......@@ -26,7 +26,6 @@ public class MultiMeetScheduled {
public void callingTimeout() {
log.info("呼叫超时处理开始...");
multiMeetService.callingTimeout();
log.info("呼叫超时处理结束...");
}
/**
......@@ -37,7 +36,6 @@ public class MultiMeetScheduled {
public void disconnect() {
log.info("异常断线处理开始...");
multiMeetService.disconnect();
log.info("异常断线处理结束...");
}
}
package io.geekidea.springbootplus.scheduled;
import com.wecloud.rtc.service.RtcService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author Future
* @Date 2022/6/3 11:43
* @Description 单人rtc定时器调度
*/
@Slf4j
@Component
public class SingleRtcScheduled {
@Resource
private RtcService rtcService;
/**
* 呼叫异常停止处理 查出正在通话的记录,检测是否在线,如未在线超过5次,按断线处理
*/
@Scheduled(cron = "*/13 * * * * ?")
public void abnormalDisconnect() {
log.info("单人音视频异常断线处理开始...");
rtcService.abnormalDisconnect();
}
}
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