Commit dd886e11 by hweeeeeei

Merge branch 'feature-1.4-pushParam' of…

Merge branch 'feature-1.4-pushParam' of https://gitlab.aillo.cc/hewei/wecloud_im_server into feiwa_test

 Conflicts:
	config/src/main/resources/config/application-prod.yml
parents 7b19bae6 a90e5c83
# 四恩慈善 - 后端项目
# IM 后端项目
## 框架介绍
> 本项目使用第三方开源脚手架项目: spring-boot-plus 参考:README-zh.md
......
package io.geekidea.springbootplus.test;
import cn.hutool.core.codec.Base64;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 读取系统文件目录 生成apns字符串
*/
public class ApnsTest {
@Test
public void test() throws Exception {
File file = new File("/Users/giaogiao/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/f31cd9e6d7da1d15c57c40575f5c85db/Message/MessageTemp/e1180914825140f051a87348bda5cbb5/File/hipro_test_push.p12");
InputStream certificate = new FileInputStream(file);
String encode = Base64.encode(certificate);
}
}
......@@ -6,6 +6,8 @@ import com.turo.pushy.apns.PushType;
import com.wecloud.im.ws.sender.IosPush;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
......@@ -29,17 +31,22 @@ public class IosApnsBase64Test {
// * @param sound rtc= "call.caf"; 否则为default
Map<String, Object> customProperty = new HashMap<String, Object>(10);
String apnsCertificatePath = "frogsell_push_dev.p12";
String deviceToken = "27c93ca84bbf17d9ff8eb05df0576ac49822db2ae1c02aa0afea83b5c3861276";
// String apnsCertificatePath = "frogsell_push_dev.p12";
String deviceToken = "5b761f954efe7493de0bc751942e1a8355853771b66a512f5687ca05e7335e99";
String alertTitle = "你好333";
String alertBody = "hi333";
int badge = 1;
String topicBundleId = "com.jdw.frogsell";
String topicBundleId = "com.xteng.Hibro";
boolean contentAvailable = false;
InputStream certificate = IosPush.getApnsCertificate(apnsCertificatePath);
String encode = Base64.encode(certificate);
// InputStream certificate = IosPush.getApnsCertificate(apnsCertificatePath);
// String encode = Base64.encode(certificate);
File file = new File("/Users/giaogiao/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/f31cd9e6d7da1d15c57c40575f5c85db/Message/MessageTemp/e1180914825140f051a87348bda5cbb5/File/hipro_test_push.p12");
InputStream in = new FileInputStream(file);
String encode = Base64.encode(in);
// 解码
byte[] decode = Base64.decode(encode);
......
package io.geekidea.springbootplus.test;
import cn.hutool.crypto.digest.MD5;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.ws.model.request.ReceiveModel;
import org.springframework.util.DigestUtils;
import java.util.Date;
......@@ -24,7 +24,10 @@ public class SignTest {
*/
private static void getSign(String timestamp, String clientId, String appKey, String appSecret) {
String sign = new MD5().digestHex(timestamp + clientId + appKey + appSecret);
String data = timestamp + clientId + appKey + appSecret;
// String sign = new MD5().digestHex(data);
String sign = DigestUtils.md5DigestAsHex(data.getBytes());
System.out.println("timestamp:" + timestamp);
System.out.println("sign:" + sign);
......
package com.wecloud.im.controller;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.param.GetClientInfoParam;
import com.wecloud.im.param.GetOnlineStatusParam;
import com.wecloud.im.param.add.ImClientDeviceInfoAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAndNicknameUpdate;
import com.wecloud.im.param.add.ImClientNicknameAdd;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.vo.GetInfoListVo;
import com.wecloud.im.vo.ImOnlineStatusVo;
import com.wecloud.im.ws.service.MangerChannelService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
......@@ -46,6 +51,14 @@ public class ImClientController extends BaseController {
@Autowired
private ImApplicationService imApplicationService;
@PostMapping("/infoList")
@ApiOperation(value = "根据id获取Client的头像昵称")
public ApiResult<List<GetInfoListVo>> getInfoList(@Validated(Add.class) @RequestBody GetClientInfoParam getClientInfoParam) throws Exception {
return imClientService.getInfoList(getClientInfoParam);
}
/**
* 添加或修改推送设备信息
*/
......@@ -57,6 +70,29 @@ public class ImClientController extends BaseController {
}
@PostMapping("/updateHeadAndNickname")
@ApiOperation(value = "添加或修改头像和昵称")
public ApiResult<Boolean> updateHeadAndNickname(@Validated(Add.class) @RequestBody ImClientHeadPortraitAndNicknameUpdate imClientHeadPortraitAndNicknameUpdate) throws Exception {
boolean flag = imClientService.updateHeadAndNickname(imClientHeadPortraitAndNicknameUpdate);
return ApiResult.result(flag);
}
@PostMapping("/updateHeadPortrait")
@ApiOperation(value = "添加或修改头像")
public ApiResult<Boolean> updateHeadPortrait(@Validated(Add.class) @RequestBody ImClientHeadPortraitAdd imClientHeadPortraitAdd) throws Exception {
boolean flag = imClientService.updateHeadPortrait(imClientHeadPortraitAdd);
return ApiResult.result(flag);
}
@PostMapping("/updateNickname")
@ApiOperation(value = "添加或修改主昵称")
public ApiResult<Boolean> updateNickname(@Validated(Add.class) @RequestBody ImClientNicknameAdd imClientNicknameAdd) throws Exception {
boolean flag = imClientService.updateNickname(imClientNicknameAdd);
return ApiResult.result(flag);
}
/**
* 退出登陆
*
......
package com.wecloud.im.controller;
import com.wecloud.im.param.add.ImClientLeaveConversation;
import com.wecloud.im.param.add.ImClientToConversation;
import com.wecloud.im.param.add.ImConversationAttrUpdate;
import com.wecloud.im.param.add.ImConversationCreate;
import com.wecloud.im.param.add.ImConversationDisplayUpdate;
import com.wecloud.im.param.add.ImConversationNameUpdate;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.vo.ImConversationCreateVo;
import com.wecloud.im.vo.MyConversationListVo;
......@@ -33,6 +37,56 @@ public class ImConversationController extends BaseController {
@Autowired
private ImConversationService imConversationService;
/**
* 添加或修改会话名称
*/
@PostMapping("/saveOrUpdateName")
@ApiOperation(value = "添加或修改会话名称", notes = "权限:目前只有创建者有权限操作")
public ApiResult<Boolean> saveOrUpdateName(@RequestBody ImConversationNameUpdate imConversationNameUpdate) throws Exception {
return imConversationService.saveOrUpdateName(imConversationNameUpdate);
}
/**
* 添加或修改会话拓展字段
*/
@PostMapping("/saveOrUpdateAttr")
@ApiOperation(value = "添加或修改会话拓展字段", notes = "权限:所有client都权限操作")
public ApiResult<Boolean> saveOrUpdateAttr(@RequestBody ImConversationAttrUpdate imConversationAttrUpdate) throws Exception {
return imConversationService.saveOrUpdateAttr(imConversationAttrUpdate);
}
/**
* client退出会话
*/
@PostMapping("/leave")
@ApiOperation(value = "client退出会话", notes = "若是创建者退出,[创建者]权限将会转移给按加入会话时间排序的下一个client")
public ApiResult<Boolean> leaveConversation(@RequestBody ImClientLeaveConversation imClientToConversation) throws Exception {
return imConversationService.leaveConversation(imClientToConversation);
}
/**
* 将client从会话移除
*/
@PostMapping("/delClient")
@ApiOperation(value = "将client从会话移除", notes = "权限:目前只有创建者有权限操作")
public ApiResult<Boolean> delClientToConversation(@RequestBody ImClientToConversation imClientToConversation) throws Exception {
return imConversationService.delClientToConversation(imClientToConversation);
}
/**
* 将用户添加进会话
*/
@PostMapping("/addClient")
@ApiOperation(value = "将用户添加进会话", notes = "权限:会话中所有client都有权限操作")
public ApiResult<Boolean> addClientToConversation(@RequestBody ImClientToConversation imClientToConversation) throws Exception {
return imConversationService.addClientToConversation(imClientToConversation);
}
/**
* 创建会话
*/
......
package com.wecloud.im.controller;
import com.wecloud.im.param.ImConvMemeClientRemarkNameParam;
import com.wecloud.im.param.ImConversationMembersListParam;
import com.wecloud.im.param.add.ImConversationMemAttrUpdate;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.vo.ImConversationMemberListVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 会话成员表 控制器
*
......@@ -23,6 +34,37 @@ public class ImConversationMembersController extends BaseController {
@Autowired
private ImConversationMembersService imConversationMembersService;
/**
* 添加或修改会话成员备注
*/
@PostMapping("/updateClientRemarkName")
@ApiOperation(value = "添加或修改会话成员备注", notes = "权限:目前只有client成员自己有权限操作")
public ApiResult<Boolean> saveOrUpdateClientRemarkName(@RequestBody ImConvMemeClientRemarkNameParam imConvMemeClientRemarkNameParam) throws Exception {
return imConversationMembersService.saveOrUpdateClientRemarkName(imConvMemeClientRemarkNameParam);
}
/**
* 添加或修改会话成员拓展字段
*/
@PostMapping("/updateAttr")
@ApiOperation(value = "添加或修改会话成员拓展字段", notes = "权限:目前只有client成员自己有权限操作")
public ApiResult<Boolean> saveOrUpdateAttr(@RequestBody ImConversationMemAttrUpdate imConversationMemAttrUpdate) throws Exception {
return imConversationMembersService.saveOrUpdateAttr(imConversationMemAttrUpdate);
}
/**
* 会话中成员表列表
*/
@PostMapping("/getList")
// @OperationLog(name = "会话中成员表列表", type = OperationLogType.PAGE)
@ApiOperation(value = "获取会话中成员表列表")
public ApiResult<List<ImConversationMemberListVo>> getImConversationMembersList(@Validated @RequestBody ImConversationMembersListParam imConversationMembersListParam) throws Exception {
// Paging<ImConversationMembersQueryVo> paging = imConversationMembersService.getImConversationMembersPageList(imConversationMembersPageParam);
// return ApiResult.ok(paging);
return ApiResult.ok(imConversationMembersService.getImConversationMembersList(imConversationMembersListParam));
}
// /**
// * 添加会话成员表
......
package com.wecloud.im.controller;
import com.wecloud.im.param.rtc.CandidateForwardParam;
import com.wecloud.im.param.rtc.CreateRtcChannelParam;
import com.wecloud.im.param.rtc.CreateRtcChannelResult;
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.rtc.service.RtcService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 音视频通话 控制器
*
* @author wei
* @since 2021-10-18
*/
@Slf4j
@RestController
@RequestMapping("/rtc")
@Api(value = "音视频通话", tags = {"音视频通话"})
public class ImRtcController extends BaseController {
@Autowired
private RtcService rtcService;
/**
* 创建频道,并邀请客户端加入
*/
@PostMapping("/createAndCall")
@ApiOperation(value = "创建频道,并邀请客户端加入", notes = "创建频道,并邀请客户端加入")
public ApiResult<CreateRtcChannelResult> createAndCall(@RequestBody CreateRtcChannelParam createRtcChannelParam) throws Exception {
return rtcService.createAndCall(createRtcChannelParam);
}
@PostMapping("/join")
@ApiOperation(value = "同意进入频道", notes = "")
public ApiResult<Boolean> join(@RequestBody JoinRtcChannelParam joinRtcChannelParam) {
return rtcService.join(joinRtcChannelParam);
}
@PostMapping("/reject")
@ApiOperation(value = "拒接进入频道", notes = "")
public ApiResult<Boolean> reject(@RequestBody RejectRtcChannelParam rejectRtcChannelParam) {
return rtcService.reject(rejectRtcChannelParam);
}
@PostMapping("/leave")
@ApiOperation(value = "主动挂断(离开频道)", notes = "")
public ApiResult<Boolean> leave(@RequestBody LeaveRtcChannelParam leaveRtcChannelParam) {
return rtcService.leave(leaveRtcChannelParam);
}
@PostMapping("/sdpForward")
@ApiOperation(value = "SDP数据转发", notes = "")
public ApiResult<Boolean> sdpForward(@RequestBody SdpForwardParam sdpForwardParam) {
return rtcService.sdpForward(sdpForwardParam);
}
@PostMapping("/candidateForward")
@ApiOperation(value = "candidate候选者数据转发", notes = "")
public ApiResult<Boolean> candidateForward(@RequestBody CandidateForwardParam candidateForwardParam) {
return rtcService.candidateForward(candidateForwardParam);
}
}
......@@ -8,7 +8,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
......@@ -22,7 +22,7 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/signDemo")
@Api(value = "sign API", tags = {"获取sign(Demo)"})
@Api(value = "signAPI", tags = {"获取sign(Demo)"})
public class SignController extends BaseController {
@Autowired
......@@ -31,9 +31,9 @@ public class SignController extends BaseController {
/**
* 根据客户方生成签名字符串 验证通过则下发token
*/
@GetMapping("/get")
@ApiOperation(value = "获取sign(仅测试使用)", notes = "生成签名")
public String verify(@RequestBody GetSignParam getSignParam) throws Exception {
@PostMapping("/get")
@ApiOperation(value = "获取sign(仅提供测试调试使用)", notes = "生成签名测试,在生产环境中,此步骤需要第三方应用的服务端进行生成")
public String get(@RequestBody GetSignParam getSignParam) throws Exception {
return new MD5().digestHex(getSignParam.getTimestamp() + getSignParam.getClientId() + getSignParam.getAppKey() + getSignParam.getAppSecret());
}
......
......@@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/token")
@Api(value = "token API", tags = {"token相关"})
@Api(value = "token API", tags = {"申请token相关"})
public class TokenController extends BaseController {
@Autowired
......@@ -33,8 +33,8 @@ public class TokenController extends BaseController {
* 根据客户方生成签名字符串 验证通过则下发token
*/
@PostMapping("/verify")
@ApiOperation(value = "验证sign,并返回token", notes = "根据客户方生成签名字符串 验证通过则下发token")
public ApiResult<TokenVo> verify(@RequestBody ImTokenVerify imTokenVerify) throws Exception {
@ApiOperation(value = "根据sign申请token", notes = "校验客户方生成的签名字符串,验证通过则下发token")
public ApiResult<TokenVo> verify(@RequestBody ImTokenVerify imTokenVerify) {
return imClientLoginService.verifySign(imTokenVerify);
}
......
package com.wecloud.im.controller.serverapi;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.param.ApiImConversationMembersPageParam;
import com.wecloud.im.param.ApiImConversationMembersQueryVo;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImConversationMembersService;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 会话成员表 控制器
*
* @author wei
* @since 2021-05-07
*/
@Slf4j
@RestController
@RequestMapping("/server/imConversationMembers")
@Api(value = "服务端rest-API-会话成员表", tags = {"服务端API-会话成员表"})
public class ApiImConversationMembersController extends BaseController {
@Autowired
private ImConversationMembersService imConversationMembersService;
@Autowired
private ImApplicationService imApplicationService;
/**
* 会话成员表分页列表
*/
@PostMapping("/findList")
@OperationLog(name = "rest-api-会话成员表分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "rest-api-会话成员表分页列表")
public ApiResult<List<ApiImConversationMembersQueryVo>> getApiImConversationMembersList(@Validated @RequestBody ApiImConversationMembersPageParam apiImConversationMembersPageParam, @RequestHeader String appkey, @RequestHeader String appSecret) throws Exception {
// 根据appKey从数据库查询密钥
ImApplication imApplication = imApplicationService.getOneByAppKey(appkey);
if (imApplication == null) {
return ApiResult.result(ApiCode.FAIL, null);
}
// 校验appkey 和appSecret
if (!imApplication.getAppSecret().equals(appSecret)) {
return ApiResult.result(ApiCode.FAIL, null);
}
return imConversationMembersService.getRestApiImConversationMembersList(apiImConversationMembersPageParam, imApplication);
}
// /**
// * 添加会话成员表
// */
// @PostMapping("/add")
// @OperationLog(name = "添加会话成员表", type = OperationLogType.ADD)
// @ApiOperation(value = "添加会话成员表")
// public ApiResult<Boolean> addImConversationMembers(@Validated(Add.class) @RequestBody ImConversationMembers imConversationMembers) throws Exception {
// boolean flag = imConversationMembersService.saveImConversationMembers(imConversationMembers);
// return ApiResult.result(flag);
// }
//
// /**
// * 修改会话成员表
// */
// @PostMapping("/update")
// @OperationLog(name = "修改会话成员表", type = OperationLogType.UPDATE)
// @ApiOperation(value = "修改会话成员表")
// public ApiResult<Boolean> updateImConversationMembers(@Validated(Update.class) @RequestBody ImConversationMembers imConversationMembers) throws Exception {
// boolean flag = imConversationMembersService.updateImConversationMembers(imConversationMembers);
// return ApiResult.result(flag);
// }
//
// /**
// * 删除会话成员表
// */
// @PostMapping("/delete/{id}")
// @OperationLog(name = "删除会话成员表", type = OperationLogType.DELETE)
// @ApiOperation(value = "删除会话成员表")
// public ApiResult<Boolean> deleteImConversationMembers(@PathVariable("id") Long id) throws Exception {
// boolean flag = imConversationMembersService.deleteImConversationMembers(id);
// return ApiResult.result(flag);
// }
//
// /**
// * 获取会话成员表详情
// */
// @GetMapping("/info/{id}")
// @OperationLog(name = "会话成员表详情", type = OperationLogType.INFO)
// @ApiOperation(value = "会话成员表详情")
// public ApiResult<ImConversationMembersQueryVo> getImConversationMembers(@PathVariable("id") Long id) throws Exception {
// ImConversationMembersQueryVo imConversationMembersQueryVo = imConversationMembersService.getImConversationMembersById(id);
// return ApiResult.ok(imConversationMembersQueryVo);
// }
//
}
package com.wecloud.im.controller.serverapi;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.param.add.ImMsgSendToOnlineClient;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImMessageService;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 消息存储表 控制器
*
* @author wei
* @since 2021-04-29
*/
@Slf4j
@RestController
@RequestMapping("/server/imMessage")
@Api(value = "服务端rest-API-消息", tags = {"服务端rest-API-消息"})
public class ApiImMessageController extends BaseController {
@Autowired
private ImApplicationService imApplicationService;
@Autowired
private ImMessageService imMessageService;
/**
* 向会话中在线client,下发透传消息
*/
@PostMapping("/sendToOnlineClient")
@ApiOperation(value = "向会话中在线client,下发透传消息", notes = "应用服务端向某会话中所有client下发透传消息, 不会保存进离线消息, 仅在线client能收到")
public ApiResult<Boolean> restApiImMessageSend(@RequestBody ImMsgSendToOnlineClient imMsgSendToOnlineClient, @RequestHeader String appkey, @RequestHeader String appSecret) throws Exception {
// return imMessageService.updateMsgWithdrawById(imMsgRecall);
// 根据appKey从数据库查询密钥
ImApplication imApplication = imApplicationService.getOneByAppKey(appkey);
if (imApplication == null) {
return ApiResult.result(ApiCode.FAIL, null);
}
// 校验appkey 和appSecret
if (!imApplication.getAppSecret().equals(appSecret)) {
return ApiResult.result(ApiCode.FAIL, null);
}
return imMessageService.restApiImMessageSend(imMsgSendToOnlineClient, imApplication);
}
}
package com.wecloud.im.entity;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 消息在线推送
*
* @author wei
* @since 2021-04-29
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImApiMessageOnlineSend ")
public class ImApiMessageOnlineSend extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("内容")
private String content;
@ApiModelProperty("会话id")
private Long conversationId;
}
......@@ -56,4 +56,9 @@ public class ImClient extends BaseEntity {
@ApiModelProperty("设备推送token")
private String deviceToken;
@ApiModelProperty("头像")
private String headPortrait;
@ApiModelProperty("主昵称")
private String nickname;
}
......@@ -48,7 +48,13 @@ public class ImConversationMembers extends BaseEntity {
@ApiModelProperty("客户端id")
private Long fkClientId;
@ApiModelProperty("可选 自定义属性,供开发者扩展使用。")
private String attributes;
@NotNull(message = "单向删除(隐藏)会话, 0不显示, 1显示不能为空")
@ApiModelProperty("单向删除(隐藏)会话, 0不显示, 1显示")
private Long displayStatus;
@ApiModelProperty("会话中client的备注名")
private String clientRemarkName;
}
package com.wecloud.im.executor;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.wecloud.im.ws.model.WsConstants;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 业务处理线程池
*/
public class BusinessThreadPool {
private final static ThreadFactory BUSINESS_THREAD_FACTORY = new ThreadFactoryBuilder()
.setNamePrefix("-business-").build();
/**
* 业务处理线程池
*/
public final static ExecutorService BUSINESS_TASK_THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(WsConstants.CPU_PROCESSORS, WsConstants.CPU_PROCESSORS * 2,
60L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024 * 2), BUSINESS_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
}
......@@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.param.ImClientPageParam;
import com.wecloud.im.param.ImClientQueryVo;
import com.wecloud.im.vo.GetInfoListVo;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
import java.util.List;
/**
* 终端表 Mapper 接口
......@@ -41,4 +43,6 @@ public interface ImClientMapper extends BaseMapper<ImClient> {
int removeOldToken(@Param("appId") Long appId, @Param("deviceToken") String deviceToken);
List<GetInfoListVo> getInfoList(@Param("appId") Long appId, @Param("conversationId") Long conversationId, @Param("clientIds") List<String> clientIds);
}
......@@ -77,7 +77,7 @@ public interface ImConversationMapper extends BaseMapper<ImConversation> {
/**
* 查询已经存在的会话信息
* 查询已经存在的一对一会话信息
*
* @param clientId1
* @param clientId2
......
......@@ -4,12 +4,15 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.param.ApiImConversationMembersQueryVo;
import com.wecloud.im.param.ImConversationMembersPageParam;
import com.wecloud.im.param.ImConversationMembersQueryVo;
import com.wecloud.im.vo.ImConversationMemberListVo;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
import java.util.List;
/**
* 会话成员表 Mapper 接口
......@@ -28,6 +31,12 @@ public interface ImConversationMembersMapper extends BaseMapper<ImConversationMe
*/
ImConversationMembersQueryVo getImConversationMembersById(Serializable id);
List<ApiImConversationMembersQueryVo> getRestApiImConversationMembersList(@Param("conversationId") Long conversationId);
List<ImConversationMemberListVo> getImConversationMembersList(@Param("conversationId") Long conversationId);
/**
* 获取分页对象
*
......
......@@ -8,10 +8,11 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {
......@@ -30,10 +31,20 @@ public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {
pipeline.addLast("SingleHttpRequestHandler", channelInboundHandler);
// 连接超时管理 (判断通道是否有数据写入)
pipeline.addLast("readTimeoutHandler", new ReadTimeoutHandler(60));
// pipeline.addLast("readTimeoutHandler", new ReadTimeoutHandler(60));
// "/appws"路径 升级长连接
pipeline.addLast("appWebSocketServerotocolHandler", new WebSocketServerProtocolHandler(WsConstants.WS_URL));
/*
* 心跳机制
* observeOutput -当评估写空闲时是否应该考虑字节的消耗。默认为false。
* readerIdleTime—状态为IdleState的IdleStateEvent。当在指定的时间内没有执行读操作时,将触发READER_IDLE。指定0禁用。
* writerIdleTime—状态为IdleState的IdleStateEvent。当在指定的时间内没有执行写操作时,会触发WRITER_IDLE。指定0禁用。
* allIdleTime—状态为IdleState的IdleStateEvent。在一定时间内不读不写会触发ALL_IDLE。指定0禁用。
* unit—readerIdleTime、writeIdleTime和allIdleTime的时间单位
*/
pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
}
}
package com.wecloud.im.netty.core;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.wecloud.im.ws.model.WsConstants;
import com.wecloud.im.executor.BusinessThreadPool;
import com.wecloud.im.ws.receive.ReadWsData;
import com.wecloud.im.ws.service.MangerChannelService;
import com.wecloud.rtc.service.RtcService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description app端 长连接事件处理
......@@ -28,26 +25,23 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static final String PING = "ping";
private static final String PONG = "pong";
@Resource
private ReadWsData readWsData;
@Autowired
private RtcService rtcService;
@Resource
private MangerChannelService mangerChannelService;
private final static ThreadFactory NAMED_THREAD_FACTORY = new ThreadFactoryBuilder()
.setNamePrefix("WS-business-").build();
/**
* 耗时核心业务处理线程池
* 属于io密集型业务
* io密集型任务配置尽可能多的线程数量
*/
private final static ExecutorService TASK_THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(WsConstants.CPU_PROCESSORS * 5, WsConstants.CPU_PROCESSORS * 50,
3L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1), NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
ctx.channel().attr(MangerChannelService.READ_IDLE_TIMES).set(0);// 读空闲的计数清零
String data = msg.text();
try {
if (data.isEmpty()) {
......@@ -56,13 +50,13 @@ public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFram
/*
* 在此进入耗时业务线程池, 将不再阻塞netty的I/O线程,提高网络吞吐
*/
TASK_THREAD_POOL_EXECUTOR.execute(() ->
BusinessThreadPool.BUSINESS_TASK_THREAD_POOL_EXECUTOR.execute(() ->
execute(ctx, data)
);
} catch (Exception e) {
//返回错误
ctx.channel().writeAndFlush(new TextWebSocketFrame("error=" + e.toString() + ",data=" + data));
ctx.channel().writeAndFlush(new TextWebSocketFrame("error=" + e + ",data=" + data));
log.error(e.getMessage() + data, e);
}
}
......@@ -71,12 +65,59 @@ public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFram
String appKey = ctx.channel().attr(MangerChannelService.APP_KEY).get();
String clientId = ctx.channel().attr(MangerChannelService.CLIENT_ID).get();
try {
if (PING.equals(data)) {
log.info("收到心跳clientId:" + clientId);
ctx.channel().writeAndFlush(new TextWebSocketFrame(PONG));
return;
}
if (PONG.equals(data)) {
log.info("收到心跳应用Pong,clientId:" + clientId);
return;
}
readWsData.convertModel(data, ctx, appKey, clientId);
} catch (Exception e) {
log.error("系统繁忙data:" + data + ",appKey:" + appKey + ",clientId:" + clientId +
",channelId:" + ctx.channel().id().asShortText(), e);
",channelId:" + ctx.channel().id().asLongText(), e);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
String clientId = ctx.channel().attr(MangerChannelService.CLIENT_ID).get();
//读超时计时器
Integer readIdleTimes = ctx.channel().attr(MangerChannelService.READ_IDLE_TIMES).get();
IdleStateEvent event = (IdleStateEvent) evt;
String eventType = null;
switch (event.state()) {
case READER_IDLE:
eventType = "读空闲:readIdleTimes=" + readIdleTimes;
ctx.channel().attr(MangerChannelService.READ_IDLE_TIMES).set(readIdleTimes + 1);// 读空闲的计数加1
// 发ping
ctx.channel().writeAndFlush(new TextWebSocketFrame(PING));
break;
case WRITER_IDLE:
eventType = "写空闲";
// 不处理
break;
case ALL_IDLE:
eventType = "读写空闲";
// 不处理
break;
}
log.info(clientId + "超时事件:" + eventType);
if (readIdleTimes >= 5) {
log.info(clientId + ".读空闲超过5次关闭连接");
ctx.channel().close();
}
}
......@@ -90,13 +131,13 @@ public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// @Override
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
// log.info("uid:" + userIdByChannel + ",ws异常,channelId:" + ctx.channel().id().asShortText(), cause);
// log.info("uid:" + userIdByChannel + ",ws异常,channelId:" + ctx.channel().id().asLongText(), cause);
// }
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
log.info("连接WS成功handlerAdded,uid:" + userIdByChannel + "," + ",channelId:" + ctx.channel().id().asShortText());
log.info("连接WS成功handlerAdded,uid:" + userIdByChannel + "," + ",channelId:" + ctx.channel().id().asLongText());
}
......@@ -109,7 +150,7 @@ public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFram
// @Override
// public void channelInactive(ChannelHandlerContext ctx) {
// String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
// log.info("uid:" + userIdByChannel + "," + "channelInactive" + ",channelId:" + ctx.channel().id().asShortText());
// log.info("uid:" + userIdByChannel + "," + "channelInactive" + ",channelId:" + ctx.channel().id().asLongText());
// }
/**
......@@ -117,10 +158,16 @@ public class WsReadHandler extends SimpleChannelInboundHandler<TextWebSocketFram
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
String appKey = ctx.channel().attr(MangerChannelService.APP_KEY).get();
String clientId = ctx.channel().attr(MangerChannelService.CLIENT_ID).get();
String userIdByChannel = mangerChannelService.getInfoByChannel(ctx);
log.info("uid:" + userIdByChannel + "," + "handlerRemoved" + ",channelId:" + ctx.channel().id().asShortText());
log.info("uid:" + userIdByChannel + "," + "handlerRemoved" + ",channelId:" + ctx.channel().id().asLongText());
// 关掉连接
ctx.close();
// rtc清空缓存
rtcService.clientOffline(appKey, clientId);
}
}
......@@ -128,6 +128,7 @@ public class NettyApiRequest {
// 设置属性值 userid - channel
ctx.channel().attr(MangerChannelService.CLIENT_ID).set(clientId);
ctx.channel().attr(MangerChannelService.APP_KEY).set(appKey);
ctx.channel().attr(MangerChannelService.READ_IDLE_TIMES).set(0);// 读空闲的计数=0
// 添加长连接handler
ctx.pipeline().addLast("appImHandler", appImReadHandler);
......
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <pre>
* 会话成员表 分页参数对象
* </pre>
*
* @author wei
* @date 2021-05-07
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "ApiImConversationMembersPageParam")
public class ApiImConversationMembersPageParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "会话表id", required = true)
private Long conversationId;
}
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <pre>
* 会话成员表 查询结果对象
* </pre>
*
* @author wei
* @date 2021-05-07
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "ApiImConversationMembersQueryVo")
public class ApiImConversationMembersQueryVo implements Serializable {
private static final long serialVersionUID = 1L;
// @ApiModelProperty("唯一id")
// private Long id;
// @ApiModelProperty("加入时间")
// private Date createTime;
@ApiModelProperty("客户端id")
private String clientId;
// @ApiModelProperty("修改时间")
// private Date updateTime;
//
// @ApiModelProperty("应用appid")
// private Long fkAppid;
// @ApiModelProperty("会话表id")
// private Long fkConversationId;
}
\ No newline at end of file
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* sign
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "GetClientInfoParam")
public class GetClientInfoParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("client客户端id")
private List<String> clientIds;
@ApiModelProperty("会话id")
private Long conversationId;
}
......@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* sign
*
......@@ -12,7 +14,7 @@ import lombok.Data;
*/
@Data
@ApiModel(value = "GetSignParam")
public class GetSignParam {
public class GetSignParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("时间戳")
......
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <pre>
* 会话成员表 分页参数对象
* </pre>
*
* @author wei
* @date 2021-05-07
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "ImConvMemeClientRemarkNameParam")
public class ImConvMemeClientRemarkNameParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "会话中的client备注,展示给会话中其他client查看的", required = true)
private String clientRemarkName;
@ApiModelProperty("会话表id")
private Long conversationId;
}
package com.wecloud.im.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* <pre>
* 会话成员表 分页参数对象
* </pre>
*
* @author wei
* @date 2021-05-07
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "ImConversationMembersListParam")
public class ImConversationMembersListParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "会话表id", required = true)
private Long conversationId;
}
......@@ -15,13 +15,13 @@ import lombok.Data;
public class ImTokenVerify {
private static final long serialVersionUID = 1L;
@ApiModelProperty("时间戳")
@ApiModelProperty("时间戳,需与生成sign时的值一致")
private String timestamp;
@ApiModelProperty("client客户端id")
@ApiModelProperty("client客户端id,需与生成sign时的值一致")
private String clientId;
@ApiModelProperty("appkey")
@ApiModelProperty("appkey,需与生成sign时的值一致")
private String appKey;
@ApiModelProperty("签名sign")
......
......@@ -23,7 +23,7 @@ public class ImClientDeviceInfoAdd extends BaseEntity {
@ApiModelProperty("设备不想收到推送提醒, 1想, 0不想")
private Integer valid;
@ApiModelProperty("设备类型1:ios; 2:android")
@ApiModelProperty("设备类型1:ios; 2:android; 3:web")
private Integer deviceType;
@ApiModelProperty("设备推送token")
......
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 添加或修改推送设备信息
*
* @author wei
* @since 2021-04-27
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImClientHeadPortraitAdd")
public class ImClientHeadPortraitAdd extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("头像,uri地址")
private String headPortrait;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 添加或修改推送设备信息
*
* @author wei
* @since 2021-04-27
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImClientHeadPortraitAndNicknameUpdate")
public class ImClientHeadPortraitAndNicknameUpdate extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("头像,uri地址")
private String headPortrait;
@ApiModelProperty("主昵称")
private String nickname;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @author wei
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImClientLeaveConversation")
public class ImClientLeaveConversation extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("会话表id")
private Long conversationId;
@ApiModelProperty("会话的创建者退出时,是否需要转移给下一个client, true为转移, false为不转移直接解散")
private Boolean transfer;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 添加或修改推送设备信息
*
* @author wei
* @since 2021-04-27
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImClientNicknameAdd")
public class ImClientNicknameAdd extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主昵称")
private String nickname;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @author wei
* @since 2021年11月30日16:57:03
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImClientToConversation")
public class ImClientToConversation extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("会话表id")
private Long conversationId;
@ApiModelProperty("要操作的clientId")
private List<String> clientIds;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.HashMap;
/**
* 会话表
*
* @author wei
* @since 2021-05-07
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImConversationAttrUpdate")
public class ImConversationAttrUpdate extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("json格式,自定义属性,供开发者扩展使用。")
private HashMap attributes;
@ApiModelProperty("会话表id")
private Long conversationId;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.HashMap;
/**
* 会话表
*
* @author wei
* @since 2021-05-07
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImConversationMemAttrUpdate")
public class ImConversationMemAttrUpdate extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("json格式,自定义属性,供开发者扩展使用。")
private HashMap attributes;
@ApiModelProperty("会话表id")
private Long conversationId;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 会话表
*
* @author wei
* @since 2021-05-07
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImConversationNameUpdate")
public class ImConversationNameUpdate extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("对话的名字,可为群组命名。")
private String name;
@ApiModelProperty("会话表id")
private Long conversationId;
}
package com.wecloud.im.param.add;
import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.HashMap;
/**
* 自定义透传内容
*
* @author wei
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "ImMsgSendToOnlineClient")
public class ImMsgSendToOnlineClient extends BaseEntity {
private static final long serialVersionUID = 1L;
// @ApiModelProperty(value = "自定义透传内容", required = true)
// private String content;
@ApiModelProperty(value = "自定义透传内容 ,为任意参数名称和类型的对象,供开发者扩展使用。", required = true)
private HashMap content;
@ApiModelProperty(value = "会话id", required = true)
private Long conversationId;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "CandidateForwardParam")
public class CandidateForwardParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
/**
* 转发的候选者数据
*/
private String candidateData;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 创建频道请求参数
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "CreateRtcChannelParam")
public class CreateRtcChannelParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "被邀请的客户端ID", required = true)
private String toClient;
@ApiModelProperty(value = "客户端自定义数据", required = false)
private String attrs;
@ApiModelProperty(value = "类型: video或voice", required = true)
private String type;
@ApiModelProperty(value = "绑定的会话id,可选", required = false)
private Long conversationId;
@ApiModelProperty(value = "接收方展示的系统推送内容,可", required = false)
private String push;
@ApiModelProperty(value = "是否需要给对方发系统通知", required = true)
private Boolean pushCall;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 创建频道返回参数
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "CreateRtcChannelResult")
public class CreateRtcChannelResult implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 加入频道请求参数
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "JoinRtcChannelParam")
public class JoinRtcChannelParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 退出频道 请求
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "LeaveRtcChannelParam")
public class LeaveRtcChannelParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 拒接加入频道 请求
*
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "RejectRtcChannelParam")
public class RejectRtcChannelParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
}
package com.wecloud.im.param.rtc;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author wei
* @since 2021-04-29
*/
@Data
@ApiModel(value = "SdpForwardParam")
public class SdpForwardParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("频道id")
private Long channelId;
@ApiModelProperty("sdp转发的数据")
private String sdpData;
@ApiModelProperty("sdp类型: Offer或Answer")
private String sdpType;
}
......@@ -34,7 +34,7 @@ public class PushUtils {
}
public static void main(String[] args) {
// TODO set your appkey and master secret here
// set your appkey and master secret here
PushUtils demo = new PushUtils("your appkey", "your master secret");
try {
......@@ -52,7 +52,7 @@ public class PushUtils {
String body = "今日可能下雨🌂";
demo.sendIOSUnicast(deviceTokenIOS, titleIOS, subtitle, body);
/* TODO these methods are all available, just fill in some fields and do the test
/* these methods are all available, just fill in some fields and do the test
* demo.sendAndroidCustomizedcastFile();
* demo.sendAndroidBroadcast();
* demo.sendAndroidGroupcast();
......@@ -76,7 +76,7 @@ public class PushUtils {
broadcast.setText("Android broadcast text");
broadcast.goAppAfterOpen();
broadcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
// TODO Set 'production_mode' to 'false' if it's a test device.
// Set 'production_mode' to 'false' if it's a test device.
// For how to register a test device, please see the developer doc.
broadcast.setProductionMode();
// Set customized fields
......@@ -98,7 +98,7 @@ public class PushUtils {
*/
public void sendAndroidUnicast(String deviceToken, String unicastText, String unicastTicker, String title) throws Exception {
AndroidUnicast unicast = new AndroidUnicast(appkey, appMasterSecret);
// TODO Set your device token
// Set your device token
unicast.setDeviceToken(deviceToken);
......@@ -108,7 +108,7 @@ public class PushUtils {
unicast.setText(unicastText);
unicast.goAppAfterOpen();
unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
// TODO Set 'production_mode' to 'false' if it's a test device.
// Set 'production_mode' to 'false' if it's a test device.
// For how to register a test device, please see the developer doc.
unicast.setProductionMode();
// Set customized fields
......@@ -120,7 +120,7 @@ public class PushUtils {
public void sendAndroidGroupcast() throws Exception {
AndroidGroupcast groupcast = new AndroidGroupcast(appkey, appMasterSecret);
/* TODO
/*
* Construct the filter condition:
* "where":
* {
......@@ -150,7 +150,7 @@ public class PushUtils {
groupcast.goAppAfterOpen();
groupcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
groupcast.setChannelActivity("your channel activity");
// TODO Set 'production_mode' to 'false' if it's a test device.
// Set 'production_mode' to 'false' if it's a test device.
// For how to register a test device, please see the developer doc.
groupcast.setProductionMode();
//厂商通道相关参数
......@@ -161,7 +161,7 @@ public class PushUtils {
public void sendAndroidCustomizedcast() throws Exception {
AndroidCustomizedcast customizedcast = new AndroidCustomizedcast(appkey, appMasterSecret);
// TODO Set your alias here, and use comma to split them if there are multiple alias.
// Set your alias here, and use comma to split them if there are multiple alias.
// And if you have many alias, you can also upload a file containing these alias, then
// use file_id to send customized notification.
customizedcast.setAlias("alias", "alias_type");
......@@ -170,7 +170,7 @@ public class PushUtils {
customizedcast.setText("Android customizedcast text");
customizedcast.goAppAfterOpen();
customizedcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
// TODO Set 'production_mode' to 'false' if it's a test device.
// Set 'production_mode' to 'false' if it's a test device.
// For how to register a test device, please see the developer doc.
customizedcast.setProductionMode();
//厂商通道相关参数
......@@ -181,7 +181,7 @@ public class PushUtils {
public void sendAndroidCustomizedcastFile() throws Exception {
AndroidCustomizedcast customizedcast = new AndroidCustomizedcast(appkey, appMasterSecret);
// TODO Set your alias here, and use comma to split them if there are multiple alias.
// Set your alias here, and use comma to split them if there are multiple alias.
// And if you have many alias, you can also upload a file containing these alias, then
// use file_id to send customized notification.
String fileId = client.uploadContents(appkey, appMasterSecret, "aa" + "\n" + "bb" + "\n" + "alias");
......@@ -191,7 +191,7 @@ public class PushUtils {
customizedcast.setText("Android customizedcast text");
customizedcast.goAppAfterOpen();
customizedcast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
// TODO Set 'production_mode' to 'false' if it's a test device.
// Set 'production_mode' to 'false' if it's a test device.
// For how to register a test device, please see the developer doc.
customizedcast.setProductionMode();
//厂商通道相关参数
......@@ -202,7 +202,7 @@ public class PushUtils {
public void sendAndroidFilecast() throws Exception {
AndroidFilecast filecast = new AndroidFilecast(appkey, appMasterSecret);
// TODO upload your device tokens, and use '\n' to split them if there are multiple tokens
// upload your device tokens, and use '\n' to split them if there are multiple tokens
String fileId = client.uploadContents(appkey, appMasterSecret, "aa" + "\n" + "bb");
filecast.setFileId(fileId);
filecast.setTicker("Android filecast ticker");
......@@ -224,7 +224,7 @@ public class PushUtils {
broadcast.setAlert("今日天气", "", "今日可能下雨🌂");
broadcast.setBadge(0);
broadcast.setSound("default");
// TODO set 'production_mode' to 'true' if your app is under production mode
// set 'production_mode' to 'true' if your app is under production mode
broadcast.setTestMode();
// Set customized fields
broadcast.setCustomizedField("test", "helloworld");
......@@ -243,7 +243,7 @@ public class PushUtils {
*/
public void sendIOSUnicast(String deviceToken, String title, String subtitle, String body) throws Exception {
IOSUnicast unicast = new IOSUnicast(appkey, appMasterSecret);
// TODO Set your device token
// Set your device token
unicast.setDeviceToken(deviceToken);
//alert值设置为字符串
......@@ -253,7 +253,7 @@ public class PushUtils {
unicast.setAlert(title, subtitle, body);
unicast.setBadge(0);
unicast.setSound("default");
// TODO set 'production_mode' to 'true' if your app is under production mode
// set 'production_mode' to 'true' if your app is under production mode
unicast.setTestMode();
// Set customized fields
unicast.setCustomizedField("test", "helloworld");
......@@ -262,7 +262,7 @@ public class PushUtils {
public void sendIOSGroupcast() throws Exception {
IOSGroupcast groupcast = new IOSGroupcast(appkey, appMasterSecret);
/* TODO
/*
* Construct the filter condition:
* "where":
* {
......@@ -289,14 +289,14 @@ public class PushUtils {
groupcast.setAlert("今日天气", "subtitle", "今日可能下雨🌂");
groupcast.setBadge(0);
groupcast.setSound("default");
// TODO set 'production_mode' to 'true' if your app is under production mode
// set 'production_mode' to 'true' if your app is under production mode
groupcast.setTestMode();
client.send(groupcast);
}
public void sendIOSCustomizedcast() throws Exception {
IOSCustomizedcast customizedcast = new IOSCustomizedcast(appkey, appMasterSecret);
// TODO Set your alias and alias_type here, and use comma to split them if there are multiple alias.
// Set your alias and alias_type here, and use comma to split them if there are multiple alias.
// And if you have many alias, you can also upload a file containing these alias, then
// use file_id to send customized notification.
customizedcast.setAlias("alias", "alias_type");
......@@ -305,14 +305,14 @@ public class PushUtils {
customizedcast.setAlert("今日天气", "", "今日可能下雨🌂");
customizedcast.setBadge(0);
customizedcast.setSound("default");
// TODO set 'production_mode' to 'true' if your app is under production mode
// set 'production_mode' to 'true' if your app is under production mode
customizedcast.setTestMode();
client.send(customizedcast);
}
public void sendIOSFilecast() throws Exception {
IOSFilecast filecast = new IOSFilecast(appkey, appMasterSecret);
// TODO upload your device tokens, and use '\n' to split them if there are multiple tokens
// upload your device tokens, and use '\n' to split them if there are multiple tokens
String fileId = client.uploadContents(appkey, appMasterSecret, "aa" + "\n" + "bb");
filecast.setFileId(fileId);
//filecast.setAlert("IOS 文件播测试");
......@@ -320,7 +320,7 @@ public class PushUtils {
filecast.setAlert("今日天气", "", "今日可能下雨🌂");
filecast.setBadge(0);
filecast.setSound("default");
// TODO set 'production_mode' to 'true' if your app is under production mode
// set 'production_mode' to 'true' if your app is under production mode
filecast.setTestMode();
client.send(filecast);
}
......
package com.wecloud.im.service;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.param.GetClientInfoParam;
import com.wecloud.im.param.ImClientPageParam;
import com.wecloud.im.param.ImClientQueryVo;
import com.wecloud.im.param.add.ImClientDeviceInfoAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAndNicknameUpdate;
import com.wecloud.im.param.add.ImClientNicknameAdd;
import com.wecloud.im.vo.GetInfoListVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.BaseService;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import java.util.List;
/**
* 终端表 服务类
*
......@@ -15,6 +23,23 @@ import io.geekidea.springbootplus.framework.core.pagination.Paging;
*/
public interface ImClientService extends BaseService<ImClient> {
boolean updateHeadPortrait(ImClientHeadPortraitAdd imClientHeadPortraitAdd) throws Exception;
boolean updateHeadAndNickname(ImClientHeadPortraitAndNicknameUpdate imClientHeadPortraitAndNicknameUpdate);
/**
* 根据ids获取Client的头像昵称
*
* @param getClientInfoParam
* @return
* @throws Exception
*/
ApiResult<List<GetInfoListVo>> getInfoList(GetClientInfoParam getClientInfoParam) throws Exception;
boolean updateNickname(ImClientNicknameAdd imClientNicknameAdd) throws Exception;
/**
* 保存
*
......
package com.wecloud.im.service;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.param.ApiImConversationMembersPageParam;
import com.wecloud.im.param.ApiImConversationMembersQueryVo;
import com.wecloud.im.param.ImConvMemeClientRemarkNameParam;
import com.wecloud.im.param.ImConversationMembersListParam;
import com.wecloud.im.param.ImConversationMembersPageParam;
import com.wecloud.im.param.ImConversationMembersQueryVo;
import com.wecloud.im.param.add.ImConversationMemAttrUpdate;
import com.wecloud.im.vo.ImConversationMemberListVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.BaseService;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import java.util.List;
/**
* 会话成员表 服务类
*
......@@ -14,6 +24,32 @@ import io.geekidea.springbootplus.framework.core.pagination.Paging;
*/
public interface ImConversationMembersService extends BaseService<ImConversationMembers> {
/**
* 服务端api-会话成员表分页列表
*
* @param apiImConversationMembersPageParam
* @param imApplication
* @return
*/
ApiResult<List<ApiImConversationMembersQueryVo>> getRestApiImConversationMembersList(ApiImConversationMembersPageParam apiImConversationMembersPageParam, ImApplication imApplication);
ApiResult<Boolean> saveOrUpdateClientRemarkName(ImConvMemeClientRemarkNameParam imConvMemeClientRemarkNameParam);
/**
* 会话成员表分页列表
*
* @param imConversationMembersListParam
* @return
* @throws Exception
*/
List<ImConversationMemberListVo> getImConversationMembersList(ImConversationMembersListParam imConversationMembersListParam) throws Exception;
ApiResult<Boolean> saveOrUpdateAttr(ImConversationMemAttrUpdate imConversationMemAttrUpdate);
/**
* 保存
*
......
......@@ -4,8 +4,12 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.param.ImConversationPageParam;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.param.add.ImClientLeaveConversation;
import com.wecloud.im.param.add.ImClientToConversation;
import com.wecloud.im.param.add.ImConversationAttrUpdate;
import com.wecloud.im.param.add.ImConversationCreate;
import com.wecloud.im.param.add.ImConversationDisplayUpdate;
import com.wecloud.im.param.add.ImConversationNameUpdate;
import com.wecloud.im.vo.ImConversationCreateVo;
import com.wecloud.im.vo.MyConversationListVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
......@@ -40,6 +44,39 @@ public interface ImConversationService extends BaseService<ImConversation> {
*/
ApiResult<ImConversationCreateVo> createImConversation(ImConversationCreate imConversationCreate) throws JsonProcessingException;
/**
* 将用户添加进会话
*
* @param imClientToConversation
* @return
* @throws JsonProcessingException
*/
ApiResult<Boolean> addClientToConversation(ImClientToConversation imClientToConversation);
/**
* 将client从会话移除
*
* @param imClientToConversation
* @return
*/
ApiResult<Boolean> delClientToConversation(ImClientToConversation imClientToConversation) throws Exception;
/**
* client退出会话
*/
ApiResult<Boolean> leaveConversation(ImClientLeaveConversation imClientToConversation) throws Exception;
/**
* 添加或修改会话名称
*/
ApiResult<Boolean> saveOrUpdateName(ImConversationNameUpdate imConversationNameUpdate) throws Exception;
/**
* 添加或修改会话拓展字段
*/
ApiResult<Boolean> saveOrUpdateAttr(ImConversationAttrUpdate imConversationAttrUpdate) throws Exception;
/**
* 修改
*
......
package com.wecloud.im.service;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImMessage;
import com.wecloud.im.param.ImHistoryMessagePageParam;
import com.wecloud.im.param.add.ImMsgRecall;
import com.wecloud.im.param.add.ImMsgSendToOnlineClient;
import com.wecloud.im.param.add.ImMsgUpdate;
import com.wecloud.im.vo.ImMessageOfflineListVo;
import com.wecloud.im.vo.OfflineMsgDto;
......@@ -22,6 +25,18 @@ public interface ImMessageService extends BaseService<ImMessage> {
/**
* 下发透传消息
*
* @param imMsgSendToOnlineClient
* @return
*/
ApiResult<Boolean> restApiImMessageSend(ImMsgSendToOnlineClient imMsgSendToOnlineClient, ImApplication imApplication);
ImMessage saveImMessage(ImApplication imApplication, ImClient imClientSender, Long toConversationId, long messageId, String content);
/**
* 消息撤回 只能撤回客户端自己发送的消息
*
* @return
......
......@@ -8,11 +8,17 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.mapper.ImClientMapper;
import com.wecloud.im.param.GetClientInfoParam;
import com.wecloud.im.param.ImClientPageParam;
import com.wecloud.im.param.ImClientQueryVo;
import com.wecloud.im.param.add.ImClientDeviceInfoAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAdd;
import com.wecloud.im.param.add.ImClientHeadPortraitAndNicknameUpdate;
import com.wecloud.im.param.add.ImClientNicknameAdd;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.vo.GetInfoListVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
......@@ -27,6 +33,8 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 终端表 服务实现类
*
......@@ -43,6 +51,92 @@ public class ImClientServiceImpl extends BaseServiceImpl<ImClientMapper, ImClien
@Autowired
private ImApplicationService imApplicationService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateHeadPortrait(ImClientHeadPortraitAdd imClientHeadPortraitAdd) throws Exception {
ImClient curentClient = getCurentClient();
// shiro线程中获取当前token
JwtToken curentJwtToken = JwtUtil.getCurentJwtToken();
// 根据appKey查询appid
// ImApplication imApplication = imApplicationService.getOneByAppKey(curentJwtToken.getAppKey());
curentClient.setHeadPortrait(imClientHeadPortraitAdd.getHeadPortrait());
imClientMapper.updateById(curentClient);
// 清除client的redis缓存
deleteCacheImClient(curentClient.getFkAppid(), curentClient.getClientId());
return true;
}
@Override
public boolean updateHeadAndNickname(ImClientHeadPortraitAndNicknameUpdate imClientHeadPortraitAndNicknameUpdate) {
ImClient curentClient = getCurentClient();
// shiro线程中获取当前token
JwtToken curentJwtToken = JwtUtil.getCurentJwtToken();
// 根据appKey查询appid
// ImApplication imApplication = imApplicationService.getOneByAppKey(curentJwtToken.getAppKey());
curentClient.setHeadPortrait(imClientHeadPortraitAndNicknameUpdate.getHeadPortrait());
curentClient.setNickname(imClientHeadPortraitAndNicknameUpdate.getNickname());
imClientMapper.updateById(curentClient);
// 清除client的redis缓存
deleteCacheImClient(curentClient.getFkAppid(), curentClient.getClientId());
return true;
}
@Override
public ApiResult<List<GetInfoListVo>> getInfoList(GetClientInfoParam getClientInfoParam) throws Exception {
ImClient curentClient = getCurentClient();
// List<ImClient> imClients = this.list(new QueryWrapper<ImClient>().lambda()
// .eq(ImClient::getFkAppid, curentClient.getFkAppid())
// .in(ImClient::getClientId, getClientInfoParam.getClientId())
// );
//
// List<GetInfoListVo> getInfoListVos = new ArrayList<>();
//
// for (ImClient imClient : imClients) {
//
// GetInfoListVo getInfoListVo = new GetInfoListVo();
// getInfoListVo.setHeadPortrait(imClient.getHeadPortrait());
// getInfoListVo.setNickname(imClient.getNickname());
// getInfoListVo.setClientId(imClient.getClientId());
// getInfoListVos.add(getInfoListVo);
// }
List<GetInfoListVo> infoList = imClientMapper.getInfoList(curentClient.getFkAppid(), getClientInfoParam.getConversationId(), getClientInfoParam.getClientIds());
return ApiResult.ok(infoList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateNickname(ImClientNicknameAdd imClientNicknameAdd) throws Exception {
ImClient curentClient = getCurentClient();
// shiro线程中获取当前token
JwtToken curentJwtToken = JwtUtil.getCurentJwtToken();
// 根据appKey查询appid
// ImApplication imApplication = imApplicationService.getOneByAppKey(curentJwtToken.getAppKey());
curentClient.setNickname(imClientNicknameAdd.getNickname());
imClientMapper.updateById(curentClient);
// 清除client的redis缓存
deleteCacheImClient(curentClient.getFkAppid(), curentClient.getClientId());
return true;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveImClient(ImClient imClient) throws Exception {
......
package com.wecloud.im.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImConversationMembers;
import com.wecloud.im.mapper.ImConversationMapper;
import com.wecloud.im.mapper.ImConversationMembersMapper;
import com.wecloud.im.param.ApiImConversationMembersPageParam;
import com.wecloud.im.param.ApiImConversationMembersQueryVo;
import com.wecloud.im.param.ImConvMemeClientRemarkNameParam;
import com.wecloud.im.param.ImConversationMembersListParam;
import com.wecloud.im.param.ImConversationMembersPageParam;
import com.wecloud.im.param.ImConversationMembersQueryVo;
import com.wecloud.im.param.add.ImConversationMemAttrUpdate;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.vo.ImConversationMemberListVo;
import com.wecloud.im.ws.service.WriteDataService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 会话成员表 服务实现类
*
......@@ -26,9 +48,154 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class ImConversationMembersServiceImpl extends BaseServiceImpl<ImConversationMembersMapper, ImConversationMembers> implements ImConversationMembersService {
// private static final JsonMapper JSON_MAPPER = new JsonMapper();
@Autowired
private WriteDataService writeDataService;
@Autowired
private ImConversationMapper imConversationMapper;
@Autowired
private ImConversationMembersService imConversationMembersService;
@Autowired
private ImConversationService imConversationService;
@Autowired
private ImClientService imClientService;
@Autowired
private ImApplicationService imApplicationService;
@Autowired
private ImMessageService imMessageService;
@Autowired
private ImConversationMembersMapper imConversationMembersMapper;
@Override
public ApiResult<List<ApiImConversationMembersQueryVo>> getRestApiImConversationMembersList(ApiImConversationMembersPageParam apiImConversationMembersPageParam, ImApplication imApplication) {
List<ApiImConversationMembersQueryVo> restApiImConversationMembersList = imConversationMembersMapper.getRestApiImConversationMembersList(apiImConversationMembersPageParam.getConversationId());
return ApiResult.ok(restApiImConversationMembersList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Boolean> saveOrUpdateClientRemarkName(ImConvMemeClientRemarkNameParam imConvMemeClientRemarkNameParam) {
// shiro线程中获取当前token
JwtToken curentJwtToken = JwtUtil.getCurentJwtToken();
// 根据appKey查询application
ImApplication imApplication = imApplicationService.getOneByAppKey(curentJwtToken.getAppKey());
ImClient imClientSender = imClientService.getCurentClient();
// 查询该成员
ImConversationMembers imConversationMember = imConversationMembersService.getOne(
new QueryWrapper<ImConversationMembers>().lambda()
.eq(ImConversationMembers::getFkAppid, imApplication.getId())
.eq(ImConversationMembers::getFkConversationId, imConvMemeClientRemarkNameParam.getConversationId())
.eq(ImConversationMembers::getFkClientId, imClientSender.getId())
);
if (imConversationMember == null) {
return ApiResult.fail();
}
// // 查询该会话所有成员
// List<ImConversationMembers> membersList = imConversationMembersService.list(
// new QueryWrapper<ImConversationMembers>().lambda()
// .eq(ImConversationMembers::getFkAppid, imApplication.getId())
// .eq(ImConversationMembers::getFkConversationId, imConversationAttrUpdate.getConversationId())
// .notIn(ImConversationMembers::getId, imClientSender.getId())
// );
imConversationMember.setClientRemarkName(imConvMemeClientRemarkNameParam.getClientRemarkName());
boolean b = imConversationMembersService.updateById(imConversationMember);
if (b) {
//TODO ws下发群成员备注变动事件
return ApiResult.ok();
} else {
return ApiResult.fail();
}
}
@Override
public List<ImConversationMemberListVo> getImConversationMembersList(ImConversationMembersListParam imConversationMembersListParam) throws Exception {
return imConversationMembersMapper.getImConversationMembersList(imConversationMembersListParam.getConversationId());
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Boolean> saveOrUpdateAttr(ImConversationMemAttrUpdate imConversationMemAttrUpdate) {
// shiro线程中获取当前token
JwtToken curentJwtToken = JwtUtil.getCurentJwtToken();
// 根据appKey查询application
ImApplication imApplication = imApplicationService.getOneByAppKey(curentJwtToken.getAppKey());
ImClient imClientSender = imClientService.getCurentClient();
// 查询该成员
ImConversationMembers imConversationMember = imConversationMembersService.getOne(
new QueryWrapper<ImConversationMembers>().lambda()
.eq(ImConversationMembers::getFkAppid, imApplication.getId())
.eq(ImConversationMembers::getFkConversationId, imConversationMemAttrUpdate.getConversationId())
.eq(ImConversationMembers::getId, imClientSender.getId())
);
if (imConversationMember == null) {
return ApiResult.fail();
}
// // 查询该会话所有成员
// List<ImConversationMembers> membersList = imConversationMembersService.list(
// new QueryWrapper<ImConversationMembers>().lambda()
// .eq(ImConversationMembers::getFkAppid, imApplication.getId())
// .eq(ImConversationMembers::getFkConversationId, imConversationAttrUpdate.getConversationId())
// .notIn(ImConversationMembers::getId, imClientSender.getId())
// );
JsonMapper jsonMapper = new JsonMapper();
try {
String attributes = jsonMapper.writeValueAsString(imConversationMemAttrUpdate.getAttributes());
imConversationMember.setAttributes(attributes);
} catch (JsonProcessingException e) {
e.printStackTrace();
return ApiResult.fail();
}
boolean b = imConversationMembersService.updateById(imConversationMember);
if (b) {
//TODO ws下发群成员属性变动事件
return ApiResult.ok();
} else {
return ApiResult.fail();
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveImConversationMembers(ImConversationMembers imConversationMembers) throws Exception {
......
package com.wecloud.im.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
......@@ -14,7 +15,9 @@ import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImInboxService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.ws.model.ResponseModel;
import com.wecloud.im.ws.enums.MsgTypeEnum;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.service.WriteDataService;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
......@@ -99,7 +102,7 @@ public class ImInboxServiceImpl extends BaseServiceImpl<ImInboxMapper, ImInbox>
// 内容
HashMap<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("type", "-1009");
stringStringHashMap.put("type", MsgTypeEnum.CLIENT_RECEIVED_MSG.getUriCode() + "");
stringStringHashMap.put("receiverId", curentClient.getClientId());
// 推送给接收方
......@@ -142,7 +145,7 @@ public class ImInboxServiceImpl extends BaseServiceImpl<ImInboxMapper, ImInbox>
// 内容
HashMap<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("type", "-1010");
stringStringHashMap.put("type", MsgTypeEnum.CLIENT_READ_MSG + "");
stringStringHashMap.put("receiverId", curentClient.getClientId());
sendMsgStatus(curentClient, application, stringStringHashMap, imMsgReadStatusUpdate.getMsgIds());
......@@ -203,8 +206,8 @@ public class ImInboxServiceImpl extends BaseServiceImpl<ImInboxMapper, ImInbox>
continue;
}
// 向接收方推送
ResponseModel<ImMessageOnlineSend> responseModel = new ResponseModel<>();
responseModel.setCmd(ResponseModel.ONLINE_EVENT_MSG);
WsResponseModel<ImMessageOnlineSend> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.ONLINE_EVENT_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
......
......@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.entity.ImApiMessageOnlineSend;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImConversation;
......@@ -15,6 +16,7 @@ import com.wecloud.im.entity.ImMessageOnlineSend;
import com.wecloud.im.mapper.ImMessageMapper;
import com.wecloud.im.param.ImHistoryMessagePageParam;
import com.wecloud.im.param.add.ImMsgRecall;
import com.wecloud.im.param.add.ImMsgSendToOnlineClient;
import com.wecloud.im.param.add.ImMsgUpdate;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
......@@ -23,7 +25,8 @@ import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.vo.ImMessageOfflineListVo;
import com.wecloud.im.vo.OfflineMsgDto;
import com.wecloud.im.ws.model.ResponseModel;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.model.request.PushModel;
import com.wecloud.im.ws.sender.PushTask;
import com.wecloud.im.ws.service.WriteDataService;
......@@ -52,6 +55,8 @@ import java.util.List;
@Service
public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMessage> implements ImMessageService {
// private static final JsonMapper JSON_MAPPER = new JsonMapper();
@Autowired
private ImMessageMapper imMessageMapper;
......@@ -76,6 +81,86 @@ public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMes
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Boolean> restApiImMessageSend(ImMsgSendToOnlineClient imMsgSendToOnlineClient, ImApplication imApplication) {
// 查询该会话所有成员
List<ImConversationMembers> membersList = imConversationMembersService.list(
new QueryWrapper<ImConversationMembers>().lambda()
.eq(ImConversationMembers::getFkConversationId, imMsgSendToOnlineClient.getConversationId())
);
if (membersList.isEmpty()) {
log.info("membersList为空,toConversationId:" + imMsgSendToOnlineClient.getConversationId());
return ApiResult.fail();
}
ImApiMessageOnlineSend imApiMessageOnlineSend = new ImApiMessageOnlineSend();
imApiMessageOnlineSend.setCreateTime(new Date());
JsonMapper jsonMapper = new JsonMapper();
try {
String attributes = jsonMapper.writeValueAsString(imMsgSendToOnlineClient.getContent());
imApiMessageOnlineSend.setContent(attributes);
} catch (JsonProcessingException e) {
e.printStackTrace();
return ApiResult.fail();
}
imApiMessageOnlineSend.setConversationId(imMsgSendToOnlineClient.getConversationId());
// 遍历发送
for (ImConversationMembers conversationMembers : membersList) {
// 查询接收方
ImClient imClientReceiver = imClientService.getOne(new QueryWrapper<ImClient>().lambda()
.eq(ImClient::getFkAppid, imApplication.getId())
.eq(ImClient::getId, conversationMembers.getFkClientId()));
if (imClientReceiver == null) {
continue;
}
WsResponseModel<ImApiMessageOnlineSend> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.REST_API_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(imApiMessageOnlineSend);
responseModel.setReqId(null);
// 向接收方推送
writeDataService.write(responseModel, imApplication.getAppKey(), imClientReceiver.getClientId());
}
return null;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ImMessage saveImMessage(ImApplication imApplication, ImClient imClientSender, Long toConversationId, long messageId, String content) {
ImMessage imMessage = new ImMessage();
imMessage.setId(messageId);
imMessage.setCreateTime(new Date());
imMessage.setFkAppid(imApplication.getId());
imMessage.setSender(imClientSender.getId());
imMessage.setContent(content);
imMessage.setWithdraw(false);
imMessage.setEvent(false);
imMessage.setSystem(false);
imMessage.setSendStatus(2);
imMessage.setFkConversationId(toConversationId);
this.save(imMessage);
return imMessage;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<Boolean> updateMsgWithdrawById(ImMsgRecall imMsgRecall) {
ImClient imClientSender = imClientService.getCurentClient();
......@@ -99,6 +184,7 @@ public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMes
// 修改消息体
messageById.setWithdraw(Boolean.TRUE);
messageById.setWithdrawTime(new Date());
// 清空消息
messageById.setContent("{}");
boolean saveOk = this.updateById(messageById);
......@@ -150,8 +236,8 @@ public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMes
imMessageOnlineSend.setEvent(Boolean.TRUE);
// 向接收方推送
ResponseModel<ImMessageOnlineSend> responseModel = new ResponseModel<>();
responseModel.setCmd(ResponseModel.ONLINE_EVENT_MSG);
WsResponseModel<ImMessageOnlineSend> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.ONLINE_EVENT_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
......@@ -177,7 +263,7 @@ public class ImMessageServiceImpl extends BaseServiceImpl<ImMessageMapper, ImMes
@Transactional(rollbackFor = Exception.class)
public ApiResult<Boolean> updateMsgById(ImMsgUpdate imMsgUpdate) {
ImClient client = imClientService.getCurentClient();
// ImClient client = imClientService.getCurentClient();
/* // 判断该消息是否是该客户端发送
ImMessage messageById = this.getById(imMsgUpdate.getId());
......
package com.wecloud.im.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@ApiModel(value = "GetInfoListVo")
public class GetInfoListVo implements Serializable {
@ApiModelProperty("会话中client的备注名")
private String clientRemarkName;
@ApiModelProperty("头像")
private String headPortrait;
@ApiModelProperty("主昵称")
private String nickname;
@ApiModelProperty("clientId")
private String clientId;
@ApiModelProperty("client自己的自定义扩展属性")
private String clientAttributes;
@ApiModelProperty("会话成员列表的自定义扩展属性")
private String memberAttributes;
}
package com.wecloud.im.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@ApiModel(value = "ImConversationMemberListVo")
public class ImConversationMemberListVo implements Serializable {
@ApiModelProperty("会话中client的备注名")
private String clientRemarkName;
@ApiModelProperty("头像")
private String headPortrait;
@ApiModelProperty("主昵称")
private String nickname;
@ApiModelProperty("clientId")
private String clientId;
@ApiModelProperty("client自己的自定义扩展属性")
private String clientAttributes;
@ApiModelProperty("会话成员列表的自定义扩展属性")
private String memberAttributes;
}
......@@ -17,7 +17,7 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//RUNTIME运行时保留
@Target(ElementType.TYPE) //type描述类、接口
@Documented
public @interface ReceiveTypeAnnotation {
public @interface CmdTypeAnnotation {
WsRequestCmdEnum type();
}
package com.wecloud.im.ws.enums;
/**
* @Description ws响应类型
* @Author xxxname xxxemail.com
* @Date 2019-12-05
*/
public enum MsgTypeEnum {
// xx邀请xx加入会话 -1007
INVITE_CLIENT_JOIN_CONVERSATION(-1007),
// xx被xx移出会话 -1008
REMOVE_CLIENT_CONVERSATION(-1008),
// xx已接收某消息 -1009
CLIENT_RECEIVED_MSG(-1009),
// xx已读某条消息 -1010
CLIENT_READ_MSG(-1010),
// 你被xx拉入新会话 -1011
CLIENT_JOIN_NEW_CONVERSATION(-1011),
// 主动退出会话 -1012
LEAVE_CONVERSATION(-1012),
// 成为新群主 -1013
CONVERSATION_NEW_CREATOR(-1013),
;
private final int uriCode;
MsgTypeEnum(int uriCode) {
this.uriCode = uriCode;
}
/**
* 根据uriCode获取
*
* @param uriCode
* @return
*/
public static MsgTypeEnum getByCode(int uriCode) {
for (MsgTypeEnum wsResponsePathEnum : values()) {
if (wsResponsePathEnum.getUriCode() == uriCode) {
return wsResponsePathEnum;
}
}
return null;
}
public int getUriCode() {
return uriCode;
}
}
......@@ -15,7 +15,12 @@ public enum WsRequestCmdEnum {
/**
* ping
*/
PING(2);
PING(2),
/**
* 单人WebRTC音视频通话
*/
SINGLE_RTC(3);
private final int cmdCode;
......
......@@ -8,14 +8,35 @@ package com.wecloud.im.ws.enums;
public enum WsResponseCmdEnum {
/**
* 离线消息下发完成指令
* 服务端下发透传消息
*/
OFFLINE_MSG_SUC(100),
REST_API_MSG(6),
/**
* 主动下发消息指令
* 会话中的事件
*/
WRITE_MSG(101);
CONVERSATION_EVENT_MSG(5),
/**
* 下发在线RTC事件
*/
SINGLE_RTC_MSG(4),
/**
* 下发在线事件消息
*/
ONLINE_EVENT_MSG(3),
/**
* 下发在线基本类型消息
*/
ONLINE_MSG(2),
/**
* 响应数据类型
*/
RES(1);
private final int cmdCode;
......@@ -23,20 +44,6 @@ public enum WsResponseCmdEnum {
this.cmdCode = uriCode;
}
/**
* 根据uriCode获取
*
* @param uriCode
* @return
*/
public static WsResponseCmdEnum getByCode(int uriCode) {
for (WsResponseCmdEnum wsResponsePathEnum : values()) {
if (wsResponsePathEnum.getCmdCode() == uriCode) {
return wsResponsePathEnum;
}
}
return null;
}
public int getCmdCode() {
return cmdCode;
......
package com.wecloud.im.ws.enums;
/**
* @Description webrtc响应类型
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
public enum WsRtcResponseSubCmdEnum {
// --- 服务端响应
/**
* 接收到RTC邀请
*/
RTC_CALL(1),
/**
* 用户状态更新事件(用户加入频道)
*/
CLIENT_JOIN(2),
/**
* 用户状态更新事件(用户退出频道)
*/
CLIENT_LEAVE(3),
/**
* 用户状态更新事件(用户拒接邀请,不同意进入频道)
*/
CLIENT_REJECT(4),
/**
* SDP数据转发
*/
SDP_FORWARD(5),
/**
* candidate候选者数据转发
*/
CANDIDATE_FORWARD(6);
private final int cmdCode;
WsRtcResponseSubCmdEnum(int cmdCode) {
this.cmdCode = cmdCode;
}
public int getCmdCode() {
return cmdCode;
}
}
......@@ -12,26 +12,11 @@ import java.io.Serializable;
*/
@Data
@Accessors(chain = true)
public class ResponseModel<T> implements Serializable {
public class WsResponseModel<T> implements Serializable {
/**
* 下发在线事件消息
*/
public static final Integer ONLINE_EVENT_MSG = 3;
/**
* 下发在线基本类型消息
*/
public static final Integer ONLINE_MSG = 2;
/**
* 响应数据类型
*/
public static final Integer RES = 1;
/**
* 枚举类UriPathEnum 请求uri的编码
* 由于websocket使用同一个通道发送数据,需要区分不同类型请求
* 枚举类WsResponseCmdEnum 请求uri的编码
* 由于webSocket使用同一个通道发送数据,需要区分不同类型请求
*/
private Integer cmd;
......
......@@ -22,4 +22,10 @@ public class PushModel implements Serializable {
*/
private String subTitle;
/**
* 自定义系统推送内容
*/
private String data;
}
......@@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.ws.enums.WsRequestCmdEnum;
import com.wecloud.im.ws.model.request.ReceiveModel;
import com.wecloud.im.ws.service.WriteDataService;
import com.wecloud.im.ws.strategy.AbstractReceiveStrategy;
import com.wecloud.im.ws.strategy.ReceiveStrategyContext;
import com.wecloud.im.ws.strategy.ImCmdAbstract;
import com.wecloud.im.ws.strategy.ImCmdContext;
import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
......@@ -23,15 +23,12 @@ import javax.annotation.Resource;
public class ReadWsData {
private static final Logger log = LoggerFactory.getLogger(ReadWsData.class);
// idea此处报红 属于正常
// @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Resource
private ReceiveStrategyContext receiveStrategyContext;
private ImCmdContext receiveStrategyContext;
@Resource
private WriteDataService writeDataService;
private static final String PING = "ping";
/**
* 在此开始进入业务流程子线程,将不占netty的io线程
......@@ -42,11 +39,8 @@ public class ReadWsData {
public void convertModel(String data, ChannelHandlerContext ctx, String appKey, String clientId) throws Exception {
log.info("appWS收到data:" + data + "\nappKey+clientId:" + appKey + ":" + clientId +
",channelId:" + ctx.channel().id().asShortText());
if (PING.equals(data)) {
log.info("收到心跳clientId:" + clientId);
return;
}
",channelId:" + ctx.channel().id().asLongText());
// 解析json
JsonMapper jsonMapper = new JsonMapper();
......@@ -57,8 +51,8 @@ public class ReadWsData {
WsRequestCmdEnum wsRequestUriPathEnum = WsRequestCmdEnum.getByCode(receiveModel.getCmd());
// 使用策略模式, 根据不同类型请求调用不同实现类
AbstractReceiveStrategy receiveStrategy = receiveStrategyContext.getStrategy(wsRequestUriPathEnum);
receiveStrategy.process(receiveModel, ctx, data, appKey, clientId);
ImCmdAbstract cmdStrategy = receiveStrategyContext.getStrategy(wsRequestUriPathEnum);
cmdStrategy.process(receiveModel, ctx, data, appKey, clientId);
}
......
......@@ -57,6 +57,7 @@ public class PushTask {
private static final String title = "title";
private static final String subTitle = "subTitle";
private static final String DATA = "data";
/**
......@@ -112,6 +113,7 @@ public class PushTask {
} else {
pushModel.setTitle(pushMap.get(title));
pushModel.setSubTitle(pushMap.get(subTitle));
pushModel.setData(pushMap.get(DATA));
}
this.push(pushModel, imClientReceiver, imApplication);
......@@ -119,6 +121,11 @@ public class PushTask {
}
private void android(PushModel pushModel, ImClient imClientReceiver, ImApplication imApplication) {
if (imApplication.getAndroidPushChannel() == null) {
return;
}
// 安卓推送通道,友盟:1;firebase:2; 信鸽3
if (imApplication.getAndroidPushChannel() == 1) {
log.info("友盟");
......@@ -152,6 +159,11 @@ public class PushTask {
}
private void ios(PushModel pushModel, ImClient imClientReceiver, ImApplication imApplication) {
if (imApplication.getIosPushChannel() == null) {
return;
}
// ios推送通道,友盟:1;firebase:2; apns原生:3
if (imApplication.getIosPushChannel() == 1) {
log.info("友盟");
......@@ -202,7 +214,7 @@ public class PushTask {
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Authorization", "key=" + imApplication.getFirebaseSecret());
JSONObject json = new JSONObject();
//推送到哪台客户端机器
//推送到哪台客户端机器 设备token
json.put("to", imClientReceiver.getDeviceToken());
JSONObject info = new JSONObject();
info.put("title", pushModel.getTitle());
......@@ -210,6 +222,9 @@ public class PushTask {
//数据消息data 通知消息 notification
json.put("notification", info);
// 自定义推送内容
json.put("data", pushModel.getData());
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
jsonStr = json.toString();
wr.write(jsonStr);
......@@ -232,7 +247,9 @@ public class PushTask {
private void apnsPush(PushModel pushModel, ImClient imClientReceiver, ImApplication imApplication) {
// 查询apns证书
ImIosApns apns = imIosApnsService.getImIosApnsByAppId(imApplication.getId());
Map<String, Object> customProperty = new HashMap<String, Object>(1);
Map<String, Object> customProperty = new HashMap<String, Object>(3);
// 自定义推送内容
customProperty.put("data", pushModel.getData());
String deviceToken = imClientReceiver.getDeviceToken();
String alertTitle = pushModel.getTitle();
String alertBody = pushModel.getSubTitle();
......
......@@ -34,15 +34,17 @@ public interface MangerChannelService {
/**
* LANGUAGE
*/
AttributeKey<String> LANGUAGE = AttributeKey.valueOf("lan");
AttributeKey<String> LANGUAGE = AttributeKey.valueOf("la");
/**
* APP_VERSION
*/
// AttributeKey<String> APP_VERSION = AttributeKey.valueOf("appVersion");
// AttributeKey<String> TOKEN = AttributeKey.valueOf("TOKEN");
// AttributeKey<String> DEVICEID = AttributeKey.valueOf("DEVICEID");
// AttributeKey<String> PLATFORM = AttributeKey.valueOf("PLATFORM");
AttributeKey<String> APP_VERSION = AttributeKey.valueOf("av");
AttributeKey<String> TOKEN = AttributeKey.valueOf("to");
AttributeKey<String> DEVICEID = AttributeKey.valueOf("dc");
AttributeKey<String> PLATFORM = AttributeKey.valueOf("pt");
AttributeKey<Integer> READ_IDLE_TIMES = AttributeKey.valueOf("readIdleTimes");
/**
* 根据userID获取channel
......
package com.wecloud.im.ws.service;
import com.wecloud.im.ws.model.ResponseModel;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.model.request.ReceiveModel;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
......@@ -47,7 +47,7 @@ public interface WriteDataService {
*
* @param responseModel
*/
void write(ResponseModel responseModel, String toAppKey, String toClientId);
void write(WsResponseModel responseModel, String toAppKey, String toClientId);
}
......@@ -32,7 +32,7 @@ public class MangerChannelServiceImpl implements MangerChannelService {
* <p>
* 由于来自远程端调用下发数据 如果是群聊1000人群则调用1000次 为不要占用太多资源 需要排队下发
* 经过并发测试 200并发1000人群消息 需要调用200x1000=20w次 考虑单机cpu性能还要顾及本机api业务 设置阻塞队列
* 为避免过多占用本地io线程导致response慢,设置LinkedBlockingQueue数量多可以避免抢占,TODO (队列数量需要测试调试到最优数量 )
* 为避免过多占用本地io线程导致response慢,设置LinkedBlockingQueue数量多可以避免抢占, (队列数量需要测试调试到最优数量 )
* 最大线程数量不要设置太多 数量、优先级一定要比本地io线程低级
*
*
......@@ -108,8 +108,8 @@ public class MangerChannelServiceImpl implements MangerChannelService {
//
//
// /**
// * TODO 待完成: 根据ACK回执 以及线程等待超时机制来判断客户端是否离线和超时;
// * TODO 待完成: 发送后阻塞当前子线程2秒后获取ack回执 如客户端发起ack回执则需要主动唤醒当前子线程 立马唤醒当前子线程, 判断如果已回执则返回发送成功, 如果未回执则判断客户端是否断线或发送错误
// * 待完成: 根据ACK回执 以及线程等待超时机制来判断客户端是否离线和超时;
// * 待完成: 发送后阻塞当前子线程2秒后获取ack回执 如客户端发起ack回执则需要主动唤醒当前子线程 立马唤醒当前子线程, 判断如果已回执则返回发送成功, 如果未回执则判断客户端是否断线或发送错误
// *
// * @param msg
// * @param userId
......@@ -310,22 +310,22 @@ public class MangerChannelServiceImpl implements MangerChannelService {
NioSocketChannel nioSocketChannel = get(toAppKey, toClientId);
if (null == nioSocketChannel) {
// userCache.offline(toAppKey + toClientId);
if (log.isDebugEnabled()) {
// if (log.isDebugEnabled()) {
log.info("writeData连接为空:" + toAppKey + toClientId + "," + msg);
}
// }
return false;
}
// 判断连接是否断开
if (nioSocketChannel.isShutdown()) {
if (log.isDebugEnabled()) {
// if (log.isDebugEnabled()) {
log.info("writeData连接断开:" + toAppKey + toClientId + "," + msg + ",\nchannelId:" + nioSocketChannel.id().asLongText());
}
// }
return false;
}
if (log.isDebugEnabled()) {
// if (log.isDebugEnabled()) {
log.info("writeData:" + toAppKey + "," + toClientId + "," + msg + ",\nchannelId:" + nioSocketChannel.id().asLongText());
}
// }
ChannelFuture channelFuture = nioSocketChannel.writeAndFlush(new TextWebSocketFrame(msg));
channelFuture.addListener(
......
package com.wecloud.im.ws.service.impl;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.ws.model.ResponseModel;
import com.wecloud.im.ws.model.WsConstants;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.model.request.ReceiveModel;
import com.wecloud.im.ws.service.MangerChannelService;
import com.wecloud.im.ws.service.WriteDataService;
......@@ -14,11 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description 下发数据
......@@ -28,17 +21,17 @@ import java.util.concurrent.TimeUnit;
@Component
public class WriteDataServiceImpl implements WriteDataService {
private final static ThreadFactory WRITE_NAMED_THREAD_FACTORY = new ThreadFactoryBuilder()
.setNamePrefix("ws-WRITE-").build();
/**
* 耗时核心业务处理线程池
* 属于io密集型业务
* io密集型任务配置尽可能多的线程数量
*/
private final static ExecutorService WRITE_TASK_THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(WsConstants.CPU_PROCESSORS * 2, WsConstants.CPU_PROCESSORS * 3,
1L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), WRITE_NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
// private final static ThreadFactory WRITE_NAMED_THREAD_FACTORY = new ThreadFactoryBuilder()
// .setNamePrefix("ws-WRITE-").build();
// /**
// * 耗时核心业务处理线程池
// * 属于io密集型业务
// * io密集型任务配置尽可能多的线程数量
// */
// private final static ExecutorService WRITE_TASK_THREAD_POOL_EXECUTOR =
// new ThreadPoolExecutor(WsConstants.CPU_PROCESSORS * 2, WsConstants.CPU_PROCESSORS * 3,
// 1L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<Runnable>(), WRITE_NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
@Autowired
private MangerChannelService mangerChannelService;
......@@ -62,7 +55,7 @@ public class WriteDataServiceImpl implements WriteDataService {
@Override
public void dataAndStatus(ReceiveModel receiveModel, ApiCode apiCode, Object data, String toAppKey, String toClientId) {
ApiResult<Boolean> apiResult = ApiResult.result(apiCode);
ResponseModel responseModel = new ResponseModel();
WsResponseModel responseModel = new WsResponseModel();
responseModel.setMsg(apiResult.getMessage());
responseModel.setCmd(receiveModel.getCmd());
responseModel.setReqId(receiveModel.getReqId());
......@@ -72,10 +65,10 @@ public class WriteDataServiceImpl implements WriteDataService {
}
@Override
public void write(ResponseModel responseModel, String toAppKey, String toClientId) {
public void write(WsResponseModel responseModel, String toAppKey, String toClientId) {
WRITE_TASK_THREAD_POOL_EXECUTOR.execute(
() -> {
// WRITE_TASK_THREAD_POOL_EXECUTOR.execute(
// () -> {
JsonMapper jsonMapper = new JsonMapper();
String json = null;
......@@ -88,9 +81,9 @@ public class WriteDataServiceImpl implements WriteDataService {
mangerChannelService.writeData(json, toAppKey, toClientId);
}
);
// );
}
//}
}
......@@ -6,12 +6,12 @@ import com.wecloud.im.ws.model.request.ReceiveModel;
import io.netty.channel.ChannelHandlerContext;
/**
* @Description 接收netty不同类型请求
* @Description 处理Cmd请求
* 抽象类 策略设计模式
* @Author hewei hwei1233@163.com
* @Date 2020-01-02
*/
public abstract class AbstractReceiveStrategy {
public abstract class ImCmdAbstract {
/**
* 处理业务流程
......
......@@ -11,15 +11,15 @@ import java.util.Map;
* 维护指令码与策略实现的对应
* @Author hewei hwei1233@163.com
*/
public class ReceiveStrategyContext {
public class ImCmdContext {
private final Map<WsRequestCmdEnum, Class> strategyMap;
public ReceiveStrategyContext(Map<WsRequestCmdEnum, Class> strategyMap) {
public ImCmdContext(Map<WsRequestCmdEnum, Class> strategyMap) {
this.strategyMap = strategyMap;
}
public AbstractReceiveStrategy getStrategy(WsRequestCmdEnum wsRequestPathEnum) {
public ImCmdAbstract getStrategy(WsRequestCmdEnum wsRequestPathEnum) {
if (wsRequestPathEnum == null) {
throw new IllegalArgumentException("not fond enum");
......@@ -35,6 +35,6 @@ public class ReceiveStrategyContext {
throw new IllegalArgumentException("not fond strategy for type:" + wsRequestPathEnum.getCmdCode());
}
return (AbstractReceiveStrategy) SpringBeanUtils.getBean(aClass);
return (ImCmdAbstract) SpringBeanUtils.getBean(aClass);
}
}
package com.wecloud.im.ws.strategy;
import com.google.common.collect.Maps;
import com.wecloud.im.ws.annotation.ReceiveTypeAnnotation;
import com.wecloud.im.ws.annotation.CmdTypeAnnotation;
import com.wecloud.im.ws.enums.WsRequestCmdEnum;
import com.wecloud.im.ws.utils.ClassScanner;
import org.springframework.beans.BeansException;
......@@ -22,7 +22,7 @@ import java.util.Set;
* @Date 2020-01-02
*/
@Component
public class ReceiveStrategyProcessor implements BeanFactoryPostProcessor {
public class ImCmdStrategyProcessor implements BeanFactoryPostProcessor {
// 扫码注解的包路径
private static final String STRATEGY_PACK = "com.wecloud.im.ws.strategy.concrete";
......@@ -32,20 +32,20 @@ public class ReceiveStrategyProcessor implements BeanFactoryPostProcessor {
Map<WsRequestCmdEnum, Class> handlerMap = Maps.newHashMapWithExpectedSize(5);
// 扫码ReceiveTypeAnnotation注解的类
Set<Class<?>> classSet = ClassScanner.scan(STRATEGY_PACK, ReceiveTypeAnnotation.class);
Set<Class<?>> classSet = ClassScanner.scan(STRATEGY_PACK, CmdTypeAnnotation.class);
classSet.forEach(clazz -> {
// 获取注解中的类型值,与枚举类一一对应
WsRequestCmdEnum type = clazz.getAnnotation(ReceiveTypeAnnotation.class).type();
WsRequestCmdEnum type = clazz.getAnnotation(CmdTypeAnnotation.class).type();
handlerMap.put(type, clazz);
});
// 初始化Contenxt, 将其注册到spring容器当中
ReceiveStrategyContext context = new ReceiveStrategyContext(handlerMap);
ImCmdContext context = new ImCmdContext(handlerMap);
try {
configurableListableBeanFactory.registerResolvableDependency(Class.forName(ReceiveStrategyContext.class.getName()), context);
configurableListableBeanFactory.registerResolvableDependency(Class.forName(ImCmdContext.class.getName()), context);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
......
......@@ -15,13 +15,14 @@ import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImInboxService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.ws.annotation.ReceiveTypeAnnotation;
import com.wecloud.im.ws.annotation.CmdTypeAnnotation;
import com.wecloud.im.ws.enums.WsRequestCmdEnum;
import com.wecloud.im.ws.model.ResponseModel;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.model.request.ReceiveModel;
import com.wecloud.im.ws.sender.PushTask;
import com.wecloud.im.ws.service.WriteDataService;
import com.wecloud.im.ws.strategy.AbstractReceiveStrategy;
import com.wecloud.im.ws.strategy.ImCmdAbstract;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.shiro.util.SnowflakeUtil;
......@@ -38,10 +39,10 @@ import java.util.List;
/**
* @Description 处理app数据消息
*/
@ReceiveTypeAnnotation(type = WsRequestCmdEnum.DATA)
@CmdTypeAnnotation(type = WsRequestCmdEnum.DATA)
@Service
@Slf4j
public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
public class ImChatConcrete extends ImCmdAbstract {
private static final String TO_CONVERSATION_KEY = "toConversation";
public static final String PUSH_KEY = "push";
......@@ -71,12 +72,11 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
private ImClientService imClientService;
@Autowired
private PushTask pushTask;
private PushTask systemPush;
@Override
public void process(ReceiveModel receiveModel, ChannelHandlerContext ctx, String data, String appKey, String clientId) throws JsonProcessingException {
// String language = ctx.channel().attr(MangerChannelService.LANGUAGE).get();
// 查询imApplication
ImApplication imApplication = imApplicationService.getOneByAppKey(appKey);
......@@ -137,7 +137,7 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
// 生成消息id
long messageId = SnowflakeUtil.getId();
// 保存消息至消息表
ImMessage imMessage = saveImMessage(imApplication, imClientSender, toConversationId, messageId, content);
ImMessage imMessage = imMessageService.saveImMessage(imApplication, imClientSender, toConversationId, messageId, content);
// 封装响应的实体
ImMessageOnlineSend imMessageOnlineSend = new ImMessageOnlineSend();
......@@ -171,8 +171,8 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
}
// 向接收方推送
ResponseModel<ImMessageOnlineSend> responseModel = new ResponseModel<>();
responseModel.setCmd(ResponseModel.ONLINE_MSG);
WsResponseModel<ImMessageOnlineSend> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.ONLINE_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
......@@ -180,14 +180,15 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
responseModel.setReqId(null);
writeDataService.write(responseModel, appKey, imClientReceiver.getClientId());
// , PushType.ALERT
// 异步推送系统通知消息
pushTask.push(pushMap, imClientReceiver, imApplication);
systemPush.push(pushMap, imClientReceiver, imApplication);
}
// 响应发送方消息id等信息
ResponseModel<HashMap<String, Long>> responseModel = new ResponseModel<>();
WsResponseModel<HashMap<String, Long>> responseModel = new WsResponseModel<>();
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCmd(ResponseModel.RES);
responseModel.setCmd(WsResponseCmdEnum.RES.getCmdCode());
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
HashMap<String, Long> stringHashMap = new HashMap<>(3);
......@@ -209,21 +210,6 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
return imClientSender;
}
private ImMessage saveImMessage(ImApplication imApplication, ImClient imClientSender, Long toConversationId, long messageId, String content) {
ImMessage imMessage = new ImMessage();
imMessage.setId(messageId);
imMessage.setCreateTime(new Date());
imMessage.setFkAppid(imApplication.getId());
imMessage.setSender(imClientSender.getId());
imMessage.setContent(content);
imMessage.setWithdraw(false);
imMessage.setEvent(false);
imMessage.setSystem(false);
imMessage.setSendStatus(2);
imMessage.setFkConversationId(toConversationId);
imMessageService.save(imMessage);
return imMessage;
}
private boolean black(ReceiveModel receiveModel, String appKey, String clientUniId, ImClient imClientSender, List<ImConversationMembers> membersList) {
// 判断是否被拉黑
......@@ -232,9 +218,9 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
log.info("被对方拉黑了");
// 响应发送方
ResponseModel<HashMap<String, Long>> responseModel = new ResponseModel<>();
WsResponseModel<HashMap<String, Long>> responseModel = new WsResponseModel<>();
ApiResult<Boolean> result = ApiResult.result(ApiCode.IS_BE_BLACK);
responseModel.setCmd(ResponseModel.RES);
responseModel.setCmd(WsResponseCmdEnum.RES.getCmdCode());
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setReqId(receiveModel.getReqId());
......@@ -247,9 +233,9 @@ public class ImConcreteReceiveStrategy extends AbstractReceiveStrategy {
if (black) {
log.info("你把对方拉黑了");
// 响应发送方
ResponseModel<HashMap<String, Long>> responseModel = new ResponseModel<>();
WsResponseModel<HashMap<String, Long>> responseModel = new WsResponseModel<>();
ApiResult<Boolean> result = ApiResult.result(ApiCode.IS_TO_BLACK);
responseModel.setCmd(ResponseModel.RES);
responseModel.setCmd(WsResponseCmdEnum.RES.getCmdCode());
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setReqId(receiveModel.getReqId());
......
package com.wecloud.im.ws.strategy.concrete;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientBlacklistService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationMembersService;
import com.wecloud.im.service.ImInboxService;
import com.wecloud.im.service.ImMessageService;
import com.wecloud.im.ws.annotation.CmdTypeAnnotation;
import com.wecloud.im.ws.enums.WsRequestCmdEnum;
import com.wecloud.im.ws.model.request.ReceiveModel;
import com.wecloud.im.ws.sender.PushTask;
import com.wecloud.im.ws.service.WriteDataService;
import com.wecloud.im.ws.strategy.ImCmdAbstract;
import com.wecloud.rtc.entity.RtcSubCmd;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 处理RTC信令消息
*/
@CmdTypeAnnotation(type = WsRequestCmdEnum.SINGLE_RTC)
@Service
@Slf4j
public class SingleRtcConcrete extends ImCmdAbstract {
private static final JsonMapper JSON_MAPPER = new JsonMapper();
@Autowired
private ImClientBlacklistService imClientBlacklistService;
@Autowired
private WriteDataService writeDataService;
@Autowired
private ImMessageService imMessageService;
@Autowired
private ImInboxService imInboxService;
@Autowired
private ImApplicationService imApplicationService;
@Autowired
private ImConversationMembersService imConversationMembersService;
@Autowired
private ImClientService imClientService;
@Autowired
private PushTask systemPush;
@Override
public void process(ReceiveModel receiveModel, ChannelHandlerContext ctx, String data, String appKey, String clientId) throws JsonProcessingException {
// 指令判空
if (receiveModel.getData().get(RtcSubCmd.SUB_CMD) == null) {
return;
}
String cmd = receiveModel.getData().get(RtcSubCmd.SUB_CMD).toString();
switch (cmd) {
// //创建频道
// case RtcSubCmd.CREATE:
// break;
//
// //加入频道
// case RtcSubCmd.JOIN:
// break;
//
// //拒绝加入频道
// case RtcSubCmd.REJECT:
// break;
//
// //SDP数据转发
// case RtcSubCmd.SDP:
// break;
//
// //主动挂断(离开频道)
// case RtcSubCmd.LEAVE:
// break;
}
}
}
......@@ -22,6 +22,52 @@ public class RedisUtils {
@Autowired
private StringRedisTemplate redisTemplate;
public StringRedisTemplate redisTemplate() {
return redisTemplate;
}
/**
* 添加Key:value
*
* @param key
* @param value
*/
public void setKey(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 删除Key
*
* @param key 如果传入hash类型的key,则把整个hash中所有field删除
*/
public boolean delKey(String key) {
return redisTemplate.delete(key);
}
/**
* 获取Key
*
* @param key
*/
public String getKey(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 模糊查询
*
* @param key
* @return
*/
public Set<String> keys(String key) {
return redisTemplate.keys(key);
}
/**
* 获取hash中field对应的值
*
......@@ -29,7 +75,7 @@ public class RedisUtils {
* @param field
* @return
*/
public String hget(String key, String field) {
public String hashget(String key, String field) {
Object val = redisTemplate.opsForHash().get(key, field);
return val == null ? null : val.toString();
}
......@@ -41,7 +87,7 @@ public class RedisUtils {
* @param field
* @param value
*/
public void hset(String key, String field, String value) {
public void hashset(String key, String field, String value) {
redisTemplate.opsForHash().put(key, field, value);
}
......@@ -52,16 +98,17 @@ public class RedisUtils {
* @param key
* @param field
*/
public void hdel(String key, String field) {
public void hashdel(String key, String field) {
redisTemplate.opsForHash().delete(key, field);
}
/**
* 删除key
*
* @param key 如果传入hash类型的key,则把整个hash中所有field删除
*/
public void kdel(String key) {
public void keydel(String key) {
redisTemplate.delete(key);
}
......@@ -71,7 +118,7 @@ public class RedisUtils {
* @param key
* @return
*/
public Map<String, String> hgetll(String key) {
public Map<String, String> hashgetll(String key) {
return redisTemplate.execute((RedisCallback<Map<String, String>>) con -> {
Map<byte[], byte[]> result = con.hGetAll(key.getBytes());
if (CollectionUtils.isEmpty(result)) {
......@@ -93,7 +140,7 @@ public class RedisUtils {
* @param fields
* @return
*/
public Map<String, String> hmget(String key, List<String> fields) {
public Map<String, String> hashmget(String key, List<String> fields) {
List<String> result = redisTemplate.<String, String>opsForHash().multiGet(key, fields);
Map<String, String> ans = new HashMap<>(fields.size());
int index = 0;
......@@ -126,7 +173,7 @@ public class RedisUtils {
}
/**
* 获取指定key中存放set<String>的集合
* 获取指定key中存放set<String>的集合
*
* @param key
*/
......@@ -134,12 +181,5 @@ public class RedisUtils {
return redisTemplate.opsForSet().members(key);
}
/**
* 删除指定key缓存
*
* @param key
*/
public void deleteByKey(String key) {
redisTemplate.delete(key);
}
}
package com.wecloud.rtc.entity;
import java.io.Serializable;
public class RtcSubCmd implements Serializable {
/**
* subCmd子类型指令码
*/
public static final String SUB_CMD = "subCmd";
}
package com.wecloud.rtc.entity.redis;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* rtc 频道信息
*/
@Data
public class RtcChannelInfo implements Serializable {
@ApiModelProperty("当前房主")
private String owner;
private String appKey;
// private String appId;
@ApiModelProperty("创建时间")
private Long createTimestamp;
}
package com.wecloud.rtc.entity.redis;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* rtc 频道内的用户
*/
@Data
public class RtcJoinUser implements Serializable {
@ApiModelProperty("客户端")
private String clientId;
@ApiModelProperty("加入时间")
private Long createTimestamp;
private String sdpData;
private String sdpType;
}
package com.wecloud.rtc.entity.redis;
import lombok.Data;
import java.io.Serializable;
/**
* rtc 频道内所有用户
*/
@Data
public class RtcJoinUsers implements Serializable {
}
package com.wecloud.rtc.entity.redis;
import java.io.Serializable;
public class RtcRedisKey implements Serializable {
/**
* 维护频道信息 (kv)
*/
public static final String RTC_CHANNEL_INFO = "r:ci:%s";
/**
* 维护用户当前在线的频道ID ( kv)
* user_join_channel = ujc
* rcu:clientA = 10001
* rcu:clientB = 10001
* rcu:clientC = 10002
* rcu:clientD = 10003
*/
public static final String USER_JOIN_CHANNEL = "r:ujc:%s";
/**
* 维护频道中存在的用户 (set 集合):
* rtc_channel_users = rcu
* rcu:10001 = clientA , clientB
* rcu:10002 = clientC
* rcu:10003 = clientD
*/
public static final String RTC_CHANNEL_USERS = "r:cu:%s";
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcCallResponse extends RtcSubDataBase implements Serializable {
private String type;
private Long conversationId;
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcCandidateForwardResponse extends RtcSubDataBase implements Serializable {
/**
* 转发的候选者数据
*/
private String candidateData;
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcClientJoinResponse extends RtcSubDataBase implements Serializable {
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcClientLeaveResponse extends RtcSubDataBase implements Serializable {
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcClientRejectResponse extends RtcSubDataBase implements Serializable {
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import java.io.Serializable;
/**
* webRtc websocket下发数据封装类
*
* @param <T>
*/
@Data
public class RtcResponseBase<T> implements Serializable {
/**
* 子指令
*/
private Integer subCmd;
/**
* 根据不同子指令 不同的实体
*/
private T subData;
/**
* 自定义拓展字段
*/
private String attrs;
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class RtcSdpForwardResponse extends RtcSubDataBase implements Serializable {
/**
* channelId : 1234263457652
* clientId : 7657567
* sdpData : xxxxxxxxxxxxxxxx
* sdpType : Offer/Answer
*/
private String sdpData;
private String sdpType;
}
package com.wecloud.rtc.entity.response;
import lombok.Data;
import java.io.Serializable;
@Data
public class RtcSubDataBase implements Serializable {
private Long channelId;
private String clientId;
private Long timestamp;
}
package com.wecloud.rtc.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.wecloud.rtc.entity.redis.RtcChannelInfo;
import java.util.List;
/**
* 管理rtc频道
*/
public interface MangerRtcCacheService {
/**
* 移除频道信息
*
* @param rtcChannelId
* @return
*/
boolean delChannelInfo(Long rtcChannelId);
/**
* 频道中客户端是否为空
*
* @param rtcChannelId
* @return
*/
boolean channelIsEmpty(Long rtcChannelId);
/**
* 获取频道信息
*
* @param rtcChannelId
* @return
* @throws JsonProcessingException
*/
RtcChannelInfo getRtcChannelInfo(Long rtcChannelId) throws JsonProcessingException;
/**
* 创建一个频道
*
* @param appKey
* @param clientId
* @param rtcChannelId 雪花算法生成频道id
*/
void create(String appKey, String clientId, Long rtcChannelId) throws JsonProcessingException;
/**
* 加入频道
*/
void join(String appKey, String clientId, Long rtcChannelId);
/**
* 退出频道
*/
void leave(String appKey, String clientId, Long rtcChannelId);
/**
* 根据频道ID获取频道内所有client
*/
List<String> getClientListByRtcChannelId(Long rtcChannelId);
/**
* 根据客户端ID获取该客户端加入的频道ID
*/
Long getRtcChannelIdListByClientId(String appKey, String clientId);
/**
* 获取客户端忙线/空闲状态
*
* @param appKey
* @param clientId
* @return true:忙线,false空闲
*/
boolean getBusyStatus(String appKey, String clientId);
}
package com.wecloud.rtc.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.wecloud.im.param.rtc.CandidateForwardParam;
import com.wecloud.im.param.rtc.CreateRtcChannelParam;
import com.wecloud.im.param.rtc.CreateRtcChannelResult;
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 io.geekidea.springbootplus.framework.common.api.ApiResult;
/**
* 管理rtc频道
*/
public interface RtcService {
/**
* 客户端离线
*/
void clientOffline(String appKey, String clientId);
/**
* 创建一个频道,并向接收方发送系统推送
*/
ApiResult<CreateRtcChannelResult> createAndCall(CreateRtcChannelParam createRtcChannelParam) throws JsonProcessingException;
/**
* 加入频道
*/
ApiResult<Boolean> join(JoinRtcChannelParam joinRtcChannelParam);
/**
* 拒接加入频道
*/
ApiResult<Boolean> reject(RejectRtcChannelParam rejectRtcChannelParam);
/**
* 退出频道
*/
ApiResult<Boolean> leave(LeaveRtcChannelParam leaveRtcChannelParam);
/**
* SDP数据转发
*
* @param sdpForwardParam
* @return
*/
ApiResult<Boolean> sdpForward(SdpForwardParam sdpForwardParam);
/**
* candidate候选者数据转发
*
* @param candidateForwardParam
* @return
*/
ApiResult<Boolean> candidateForward(CandidateForwardParam candidateForwardParam);
}
package com.wecloud.rtc.service;
import com.wecloud.rtc.entity.response.RtcCallResponse;
import com.wecloud.rtc.entity.response.RtcCandidateForwardResponse;
import com.wecloud.rtc.entity.response.RtcClientJoinResponse;
import com.wecloud.rtc.entity.response.RtcClientLeaveResponse;
import com.wecloud.rtc.entity.response.RtcClientRejectResponse;
import com.wecloud.rtc.entity.response.RtcSdpForwardResponse;
/**
* WebRtc webSocket下发指令数据
*/
public interface WsRtcWrite {
/**
* 接收到RTC邀请
*/
void rtcCall(RtcCallResponse rtcCallResponse, String toAppKey, String toClientId);
/**
* 用户状态更新事件(用户加入频道)
*/
void clientJoin(RtcClientJoinResponse rtcClientJoinResponse, String toAppKey, String toClientId);
/**
* 用户状态更新事件(用户退出频道)
*/
void clientLeave(RtcClientLeaveResponse rtcClientLeaveResponse, String toAppKey, String toClientId);
/**
* 用户状态更新事件(用户拒接邀请;不同意进入频道)
*/
void clientReject(RtcClientRejectResponse rtcClientRejectResponse, String toAppKey, String toClientId);
/**
* SDP数据转发
*/
void sdpForward(RtcSdpForwardResponse rtcSdpForwardResponse, String toAppKey, String toClientId);
/**
* candidate候选者数据转发
*/
void candidateForward(RtcCandidateForwardResponse rtcCandidateForwardResponse, String toAppKey, String toClientId);
}
package com.wecloud.rtc.service.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.wecloud.im.ws.utils.RedisUtils;
import com.wecloud.rtc.entity.redis.RtcChannelInfo;
import com.wecloud.rtc.entity.redis.RtcRedisKey;
import com.wecloud.rtc.service.MangerRtcCacheService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
@Service
public class MangerRtcCacheServiceImpl implements MangerRtcCacheService {
@Autowired
private RedisUtils redisUtils;
@Override
public boolean delChannelInfo(Long rtcChannelId) {
String channelKey = String.format(RtcRedisKey.RTC_CHANNEL_INFO, rtcChannelId);
redisUtils.delKey(channelKey);
return true;
}
@Override
public boolean channelIsEmpty(Long rtcChannelId) {
List<String> clientListByRtcChannelId = getClientListByRtcChannelId(rtcChannelId);
// // 移除自己
// clientListByRtcChannelId.remove(appKey + clientId);
return clientListByRtcChannelId.isEmpty();
}
@Override
public RtcChannelInfo getRtcChannelInfo(Long rtcChannelId) throws JsonProcessingException {
// 频道信息
String channelKey = String.format(RtcRedisKey.RTC_CHANNEL_INFO, rtcChannelId);
String value = redisUtils.getKey(channelKey);
if (StringUtils.isBlank(value)) {
return null;
}
JsonMapper jsonMapper = new JsonMapper();
return jsonMapper.readValue(value, RtcChannelInfo.class);
}
@Override
public void create(String appKey, String clientId, Long rtcChannelId) throws JsonProcessingException {
// --- 频道信息
RtcChannelInfo rtcChannelInfo = new RtcChannelInfo();
rtcChannelInfo.setAppKey(appKey);
// rtcChannelInfo.setAppId("");
//当前房主
rtcChannelInfo.setOwner(clientId);
//创建时间
rtcChannelInfo.setCreateTimestamp(new Date().getTime());
String rtcChannelInfoJson = new JsonMapper().writeValueAsString(rtcChannelInfo);
// --- 保存频道信息
String channelKey = String.format(RtcRedisKey.RTC_CHANNEL_INFO, rtcChannelId);
redisUtils.setKey(channelKey, rtcChannelInfoJson);
//用户当前在线的频道ID
String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
redisUtils.setKey(userJoinChannelKey, rtcChannelId.toString());
//频道中存在的用户
String rtcChannelUsers = String.format(RtcRedisKey.RTC_CHANNEL_USERS, rtcChannelId);
redisUtils.addForSet(rtcChannelUsers, clientId);
}
@Override
public void join(String appKey, String clientId, Long rtcChannelId) {
//用户当前在线的频道ID
String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
redisUtils.setKey(userJoinChannelKey, rtcChannelId.toString());
//频道中存在的用户
String rtcChannelUsers = String.format(RtcRedisKey.RTC_CHANNEL_USERS, rtcChannelId);
redisUtils.addForSet(rtcChannelUsers, clientId);
}
@Override
public void leave(String appKey, String clientId, Long rtcChannelId) {
//用户当前在线的频道ID
String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
redisUtils.delKey(userJoinChannelKey);
//频道中存在的用户
String rtcChannelUsers = String.format(RtcRedisKey.RTC_CHANNEL_USERS, rtcChannelId);
redisUtils.removeForSet(rtcChannelUsers, clientId);
}
@Override
public List<String> getClientListByRtcChannelId(Long rtcChannelId) {
//频道中存在的用户
String rtcChannelUsers = String.format(RtcRedisKey.RTC_CHANNEL_USERS, rtcChannelId);
Set<String> forSetMembers = redisUtils.getForSetMembers(rtcChannelUsers);
return new ArrayList<>(forSetMembers);
}
@Override
public Long getRtcChannelIdListByClientId(String appKey, String clientId) {
//用户当前在线的频道ID
String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
String key = redisUtils.getKey(userJoinChannelKey);
if (StringUtils.isBlank(key)) {
return null;
}
return Long.valueOf(key);
}
@Override
public boolean getBusyStatus(String appKey, String clientId) {
//用户当前在线的频道ID
String userJoinChannelKey = String.format(RtcRedisKey.USER_JOIN_CHANNEL, appKey + clientId);
String key = redisUtils.getKey(userJoinChannelKey);
return key != null && !key.isEmpty();
}
}
package com.wecloud.rtc.service.impl;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.enums.WsRtcResponseSubCmdEnum;
import com.wecloud.im.ws.model.WsResponseModel;
import com.wecloud.im.ws.service.WriteDataService;
import com.wecloud.rtc.entity.response.RtcCallResponse;
import com.wecloud.rtc.entity.response.RtcCandidateForwardResponse;
import com.wecloud.rtc.entity.response.RtcClientJoinResponse;
import com.wecloud.rtc.entity.response.RtcClientLeaveResponse;
import com.wecloud.rtc.entity.response.RtcClientRejectResponse;
import com.wecloud.rtc.entity.response.RtcResponseBase;
import com.wecloud.rtc.entity.response.RtcSdpForwardResponse;
import com.wecloud.rtc.service.WsRtcWrite;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class WsRtcWriteImpl implements WsRtcWrite {
// private static final JsonMapper JSON_MAPPER = new JsonMapper();
@Autowired
private WriteDataService writeDataService;
@Override
public void rtcCall(RtcCallResponse rtcCallResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcCallResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.RTC_CALL.getCmdCode());
rtcResponseBase.setSubData(rtcCallResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcCallResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
@Override
public void clientJoin(RtcClientJoinResponse rtcClientJoinResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcClientJoinResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.CLIENT_JOIN.getCmdCode());
rtcResponseBase.setSubData(rtcClientJoinResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcClientJoinResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
@Override
public void clientLeave(RtcClientLeaveResponse rtcClientLeaveResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcClientLeaveResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.CLIENT_LEAVE.getCmdCode());
rtcResponseBase.setSubData(rtcClientLeaveResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcClientLeaveResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
@Override
public void clientReject(RtcClientRejectResponse rtcClientRejectResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcClientRejectResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.CLIENT_REJECT.getCmdCode());
rtcResponseBase.setSubData(rtcClientRejectResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcClientRejectResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
@Override
public void sdpForward(RtcSdpForwardResponse rtcSdpForwardResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcSdpForwardResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.SDP_FORWARD.getCmdCode());
rtcResponseBase.setSubData(rtcSdpForwardResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcSdpForwardResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
@Override
public void candidateForward(RtcCandidateForwardResponse rtcCandidateForwardResponse, String toAppKey, String toClientId) {
RtcResponseBase<RtcCandidateForwardResponse> rtcResponseBase = new RtcResponseBase<>();
rtcResponseBase.setSubCmd(WsRtcResponseSubCmdEnum.CANDIDATE_FORWARD.getCmdCode());
rtcResponseBase.setSubData(rtcCandidateForwardResponse);
// rtcResponseBase.setAttrs(rtcCallResponse.get);
// 向接收方推送
WsResponseModel<RtcResponseBase<RtcCandidateForwardResponse>> responseModel = new WsResponseModel<>();
responseModel.setCmd(WsResponseCmdEnum.SINGLE_RTC_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(rtcResponseBase);
responseModel.setReqId(null);
writeDataService.write(responseModel, toAppKey, toClientId);
}
}
......@@ -26,5 +26,31 @@
<include refid="Base_Column_List"/>
from im_client
</select>
<select id="getInfoList" resultType="com.wecloud.im.vo.GetInfoListVo">
SELECT
im_client.client_id AS clientId,
im_client.head_portrait AS headPortrait,
im_client.nickname,
(SELECT im_conversation_members.client_remark_name FROM im_conversation_members AS im_conversation_members WHERE
im_conversation_members.fk_conversation_id = #{conversationId} AND im_conversation_members.fk_client_id =
clientId ) AS clientRemarkName ,
(SELECT im_conversation_members.attributes FROM im_conversation_members AS im_conversation_members WHERE
im_conversation_members.fk_conversation_id = #{conversationId} AND im_conversation_members.fk_client_id =
clientId ) AS memberAttributes ,
im_client.attributes AS clientAttributes
FROM
im_client AS im_client
WHERE
im_client.fk_appid = #{appId}
AND im_client.client_id IN
<foreach collection="clientIds" item="clientId" index="index" open="(" close=")" separator=",">
#{clientId}
</foreach>
</select>
</mapper>
......@@ -92,14 +92,19 @@
<select id="getRepetitionConversationInfo" resultType="com.wecloud.im.entity.ImConversation">
SELECT im_conversation.*
SELECT im_conversation.*,
(SELECT COUNT(im2.id)
FROM im_conversation_members AS im2
WHERE im2.fk_conversation_id = im_conversation_members.fk_conversation_id) AS members_count
FROM im_conversation_members
INNER JOIN (SELECT *
FROM im_conversation_members
WHERE im_conversation_members.fk_client_id = #{clientId2}) AS im_conversation_members2
ON im_conversation_members.fk_conversation_id = im_conversation_members2.fk_conversation_id
INNER JOIN im_conversation ON im_conversation.id = im_conversation_members.fk_conversation_id
WHERE im_conversation_members.fk_client_id = #{clientId1} LIMIT 1
WHERE im_conversation_members.fk_client_id = #{clientId1}
HAVING members_count = 2 LIMIT 1
</select>
</mapper>
......@@ -20,5 +20,27 @@
<include refid="Base_Column_List"/>
from im_conversation_members
</select>
<select id="getRestApiImConversationMembersList"
resultType="com.wecloud.im.param.ApiImConversationMembersQueryVo">
SELECT im_client.client_id AS clientId
FROM im_conversation_members AS imConversationMembers
INNER JOIN im_client AS im_client ON im_client.id = imConversationMembers.fk_client_id
WHERE fk_conversation_id = #{conversationId}
</select>
<select id="getImConversationMembersList" resultType="com.wecloud.im.vo.ImConversationMemberListVo">
SELECT im_client.client_id as clientId,
im_conversation_members.client_remark_name as clientRemarkName,
im_client.head_portrait as headPortrait,
im_client.nickname,
im_conversation_members.attributes AS memberAttributes,
im_client.attributes AS clientAttributes
FROM im_conversation_members AS im_conversation_members
INNER JOIN im_client AS im_client ON im_client.id = im_conversation_members.fk_client_id
WHERE im_conversation_members.fk_conversation_id = #{conversationId}
</select>
</mapper>
......@@ -15,7 +15,7 @@ spring-boot-plus:
spring:
datasource:
url: jdbc:mysql://localhost:3306/wecloud_im_v1_3?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://localhost:3306/wecloud_im?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
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