Commit 53e8024b by Future

Merge branch 'feature-cluster' into xiaohudou_20220427

# Conflicts:
#	core/src/main/java/com/wecloud/im/action/ConversationAction.java
#	core/src/main/java/com/wecloud/im/controller/ImConversationController.java
#	core/src/main/java/com/wecloud/im/service/ImConversationService.java
#	core/src/main/java/com/wecloud/im/service/impl/ImConversationServiceImpl.java
parents ed6fd463 3d99100a
......@@ -31,6 +31,8 @@ public class ImApplicationTest {
String appSecret = KeyGenerator.getAppSecret();
int i = 1;
System.out.println(appKey);
System.out.println(appSecret);
}
@Test
......
package com.wecloud.im.action;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import com.alibaba.fastjson.JSON;
import com.wecloud.dispatch.annotation.ActionMapping;
import com.wecloud.im.param.DisbandConversationParam;
import com.wecloud.im.param.ExitChatRoomParam;
import com.wecloud.im.param.GroupChatSettingParam;
import com.wecloud.im.param.ImConversationQueryParam;
import com.wecloud.im.param.IntoChatRoomParam;
import com.wecloud.im.param.MutedGroupMemberParam;
import com.wecloud.im.param.MutedGroupParam;
import com.wecloud.im.param.SetAdminsParam;
......@@ -33,6 +21,19 @@ import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.vo.ConversationVo;
import com.wecloud.im.vo.ImConversationCreateVo;
import com.wecloud.im.ws.model.WsResponse;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
......@@ -242,4 +243,33 @@ public class ConversationAction {
result.put("result", r);
return WsResponse.ok(result);
}
/**
* 用户进入聊天室
*/
@ActionMapping("/intoChatRoom")
@ApiOperation(value = "用户进入聊天室", notes = "用户进入聊天室")
public WsResponse<Map<String, Boolean>> intoChatRoom(IntoChatRoomParam data) {
log.info("用户进入聊天室入参 {}", JSON.toJSONString(data));
Boolean r = imConversationService.intoChatRoom(data);
Map<String, Boolean> result = new HashMap<>();
result.put("result", r);
return WsResponse.ok(result);
}
/**
* 用户退出聊天室
*/
@ActionMapping("/exitRoom")
@ApiOperation(value = "用户退出聊天室", notes = "用户退出聊天室")
public WsResponse<Map<String, Boolean>> exitRoom(ExitChatRoomParam data) {
log.info("用户退出聊天室入参 {}", JSON.toJSONString(data));
Boolean r =imConversationService.exitChatRoom(data);
Map<String, Boolean> result = new HashMap<>();
result.put("result", r);
return WsResponse.ok(result);
}
}
package com.wecloud.im.chatroom.action;
import com.wecloud.dispatch.annotation.ActionMapping;
import com.wecloud.dispatch.common.BaseRequest;
import com.wecloud.dispatch.extend.ActionRequest;
import com.wecloud.im.chatroom.cache.ChatRoomCacheManager;
import com.wecloud.im.entity.ImApplication;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.entity.ImMessageOnlineSend;
import com.wecloud.im.param.ChatContentVo;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.param.MsgVo;
import com.wecloud.im.service.ImApplicationService;
import com.wecloud.im.service.ImClientService;
import com.wecloud.im.service.ImConversationService;
import com.wecloud.im.ws.enums.WsResponseCmdEnum;
import com.wecloud.im.ws.model.WsResponse;
import com.wecloud.im.ws.sender.ChannelSender;
import com.wecloud.utils.JsonUtils;
import com.wecloud.utils.SnowflakeUtil;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.netty.channel.Channel;
import io.netty.channel.socket.nio.NioSocketChannel;
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.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @Author wenzhida
* @Date 2022/4/26 21:05
* @Description 聊天室消息处理
*/
@Slf4j
@Component
@ActionMapping
@Api(value = "ws-聊天室消息处理", tags = {"ws-聊天室消息处理"})
public class ChatRoomAction {
@Autowired
private ImApplicationService imApplicationService;
@Autowired
private ImClientService imClientService;
@Autowired
private ImConversationService imConversationService;
@Autowired
private ChannelSender channelSender;
@Autowired
private ChatRoomCacheManager chatRoomCacheManager;
@ActionMapping("/chat/chatroom/send")
@ApiOperation(value = "聊天室消息发送")
public void sendMsg(ActionRequest request, ChatContentVo data, String reqId) {
if (log.isDebugEnabled()) {
log.debug("接收到参数,reqId: {},\n data: {}, ", data);
}
ImConversationQueryVo conversation = imConversationService.getCacheImConversationById(data.getToConversation());
if (conversation == null) {
log.warn("reqId: {} ,会话id: {} db 中不存在", reqId, data.getToConversation());
return;
}
// 查询发送者client
ImClient imClientSender = imClientService.getCacheImClient(request.getSenderClientId());
if (imClientSender == null) {
log.warn("根据senderClientId: {} 查找不到 imClientSender!", request.getSenderClientId());
return;
}
// 查询imApplication
ImApplication imApplication = imApplicationService.getCacheById(imClientSender.getFkAppid());
if (imApplication == null) {
log.warn("根据appId: {} 查找不到 imApplication!", imClientSender.getFkAppid());
return;
}
ImMessageOnlineSend imMessageOnlineSend = assembleImMessageOnlineSend(data, imClientSender, imApplication.getId());
// 在线用户直接发消息
sendMsgForOnline(data, imMessageOnlineSend);
// 响应发送方消息id等信息
response(reqId, imMessageOnlineSend.getMsgId(), request.getSenderChannel());
}
/**
* 发送消息给在线客户
*
* @param data
* @param imMessageOnlineSend
*/
private void sendMsgForOnline(ChatContentVo data, ImMessageOnlineSend imMessageOnlineSend) {
Map<String /** ip **/, List<String /** client的主键ID:platform **/>> onlineIpClientMap =
chatRoomCacheManager.findOnlineHostsByChatRoomId(data.getToConversation());
// 封装要推给接收方的消息
WsResponse<ImMessageOnlineSend> responseModel = new WsResponse<>();
responseModel.setCmd(WsResponseCmdEnum.ONLINE_MSG.getCmdCode());
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(imMessageOnlineSend);
responseModel.setReqId(null);
onlineIpClientMap.forEach((ip, clientIdAndPlatforms) -> {
channelSender.batchSendMsg(responseModel, ip, clientIdAndPlatforms);
});
}
/**
* 拼装发送消息体
*
* @param data
* @param imClientSender
* @param appId
* @return
*/
private ImMessageOnlineSend assembleImMessageOnlineSend(ChatContentVo data, ImClient imClientSender, Long appId) {
// 生成消息id
long messageId = SnowflakeUtil.getId();
// 封装响应的实体
ImMessageOnlineSend imMessageOnlineSend = new ImMessageOnlineSend();
imMessageOnlineSend.setMsgId(messageId);
imMessageOnlineSend.setSender(imClientSender.getClientId());
Map<String, Object> content = JsonUtils.beanCopyDeep(data, Map.class);
//action的属性无需要返回
content.remove(BaseRequest.ACTION);
imMessageOnlineSend.setContent(content);
imMessageOnlineSend.setConversationId(data.getToConversation());
imMessageOnlineSend.setCreateTime(new Date());
imMessageOnlineSend.setWithdrawTime(null);
imMessageOnlineSend.setWithdraw(false);
imMessageOnlineSend.setEvent(false);
imMessageOnlineSend.setSystemFlag(false);
imMessageOnlineSend.setType(data.getType());
imMessageOnlineSend.setAt(data.getAt());
return imMessageOnlineSend;
}
/**
* 响应发送方消息id等信息
*
* @param reqId
* @param messageId
* @param channel
*/
private void response(String reqId, long messageId, Channel channel) {
WsResponse<MsgVo> responseModel = new WsResponse<>();
ApiResult<Boolean> result = ApiResult.result(ApiCode.SUCCESS);
responseModel.setCmd(WsResponseCmdEnum.RES.getCmdCode());
responseModel.setCode(result.getCode());
responseModel.setMsg(result.getMessage());
responseModel.setData(new MsgVo(messageId));
responseModel.setReqId(reqId);
// 响应发送方
channelSender.sendMsgLocal((NioSocketChannel) channel, responseModel);
}
}
package com.wecloud.im.chatroom.cache;
import com.wecloud.im.ws.utils.RedisUtils;
import com.wecloud.utils.GetIpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 聊天室的缓存(本地缓存待实现)
*
* @author lixiaozhong
* @date 2022年01月4日 17:00:00
*/
@Service
@Slf4j
public class ChatRoomCacheManager {
/**
* redis的群聊天室房间键 key
*/
public static final String CHAT_ROOM_KEY = "chat_room_";
@Autowired
private RedisUtils redisUtils;
/**
* 成员进入聊天室
*
* @param clientId
* @param chatRoomId
* @param platform
*/
public void intoRoom(Long clientId, Long chatRoomId, Integer platform) {
redisUtils.hashset(CHAT_ROOM_KEY + chatRoomId, clientId + RedisUtils.SPLIT + platform, GetIpUtils.getlanIp(),
2, TimeUnit.DAYS);
}
/**
* 成员离开聊天室
*
* @param clientId
* @param chatRoomId
* @param platform
*/
public void exitRoom(Long clientId, Long chatRoomId, Integer platform) {
redisUtils.hashdel(CHAT_ROOM_KEY + chatRoomId, clientId + RedisUtils.SPLIT + platform);
}
/**
* 根据群ID 获取 聊天室的 在线成员的key-val
*
* @param chatRoomId
* @return 在线成员的key-val,其中key是 client的主键id:platform, val是 ip
*/
public Map<String, String> findOnlineClientsByChatRoomId(Long chatRoomId) {
return redisUtils.hashgetll(CHAT_ROOM_KEY + chatRoomId);
}
/**
* 根据群ID 获取 聊天室的 在线成员的ip以及对应的成员信息,key-val
*
* @param chatRoomId
* @return 在线成员的key-val,其中key是 ip地址, val是 client的主键id:platform
*/
public Map<String, List<String>> findOnlineHostsByChatRoomId(Long chatRoomId) {
Map<String /** client的主键ID:platform **/, String /** ip **/> onlineClientIpMap = findOnlineClientsByChatRoomId(chatRoomId);
Map<String /** ip **/, List<String /** client的主键ID:platform **/>> onlineIpClientMap = new HashMap<>();
onlineClientIpMap.forEach((clientIdAndPlatforms, ip) -> {
onlineIpClientMap.putIfAbsent(ip, new ArrayList<>());
onlineIpClientMap.get(ip).add(clientIdAndPlatforms);
});
return onlineIpClientMap;
}
}
package com.wecloud.im.controller;
import com.wecloud.im.param.ChatRoomMemberPageParam;
import com.wecloud.im.param.ExitChatRoomParam;
import com.wecloud.im.param.IntoChatRoomParam;
import com.wecloud.im.vo.ChatRoomMemberVo;
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.core.pagination.Paging;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
......@@ -50,7 +55,6 @@ public class ImConversationController extends BaseController {
@Autowired
private ImConversationService imConversationService;
/**
* 添加或修改会话名称
*/
......@@ -219,5 +223,38 @@ public class ImConversationController extends BaseController {
return ApiResult.ok(result);
}
/**
* 用户进入聊天室
*/
@PostMapping("/intoChatRoom")
@ApiOperation(value = "用户进入聊天室", notes = "用户进入聊天室")
public ApiResult<Boolean> intoChatRoom(@RequestBody IntoChatRoomParam param) {
log.info("用户进入聊天室入参 {}", JSON.toJSONString(param));
return ApiResult.ok(imConversationService.intoChatRoom(param));
}
/**
* 用户退出聊天室
*/
@PostMapping("/exitRoom")
@ApiOperation(value = "用户退出聊天室", notes = "用户退出聊天室")
public ApiResult<Boolean> exitChatRoom(@RequestBody ExitChatRoomParam param) {
log.info("用户退出聊天室入参 {}", JSON.toJSONString(param));
return ApiResult.ok(imConversationService.exitChatRoom(param));
}
/**
* 全量获取聊天室成员
*/
@PostMapping("/listChatRoomMember")
@ApiOperation(value = "全量获取聊天室成员", notes = "全量获取聊天室成员")
public ApiResult<List<ChatRoomMemberVo>> listChatRoomMember(@RequestBody ChatRoomMemberPageParam param) {
log.info("全量获取聊天室成员入参 {}", JSON.toJSONString(param));
return ApiResult.ok(imConversationService.listChatRoomMember(param));
}
}
......@@ -87,6 +87,8 @@ public class ImFriendController extends BaseController {
if(param == null) {
return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, null);
}
ImClient currentClient = imClientService.getCurrentClient();
if(currentClient == null) {
return ApiResult.fail(ApiCode.CLIENT_NOT_FOUNT, null);
......
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;
/**
* @Author wenzhida
* @Date 2022/4/27 16:42
* @Description 聊天室成员分页入参
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "聊天室成员分页入参")
public class ChatRoomMemberPageParam implements Serializable {
private static final long serialVersionUID = 3284648263835691087L;
@ApiModelProperty("聊天室房间id")
private Long chatRoomId;
}
package com.wecloud.im.param;
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 wenzhida
* @Date 2022/4/27 11:44
* @Description 用户退出聊天室入参
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "用户退出聊天室入参")
public class ExitChatRoomParam extends BaseEntity {
private static final long serialVersionUID = -3602523207000275557L;
@ApiModelProperty("聊天室id")
private Long chatRoomId;
@ApiModelProperty("离开聊天室的clientId")
private String clientId;
@ApiModelProperty("客户端平台: 1 web, 2 安卓, 3 ios, 4 pc-win, 5 pc-macOs, 需与生成sign时的值一致")
private Integer platform;
}
package com.wecloud.im.param;
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 wenzhida
* @Date 2022/4/27 11:44
* @Description 用户进入聊天室入参
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "用户进入聊天室入参")
public class IntoChatRoomParam extends BaseEntity {
private static final long serialVersionUID = -3602523207000275557L;
@ApiModelProperty("聊天室id")
private Long chatRoomId;
@ApiModelProperty("进入聊天室的clientId")
private String clientId;
@ApiModelProperty("客户端平台: 1 web, 2 安卓, 3 ios, 4 pc-win, 5 pc-macOs, 需与生成sign时的值一致")
private Integer platform;
}
......@@ -33,8 +33,14 @@ public class ImConversationCreate extends BaseEntity {
@ApiModelProperty("可选 邀请加入会话的客户端,如创建单聊,则填入对方的clientId")
private List<String> clientIds;
@ApiModelProperty("会话属性,1:单聊,2:普通群,3:万人群")
/**
* @see com.wecloud.im.sdk.enums.ChatTypeEnum
*/
@ApiModelProperty("会话属性,1:单聊,2:普通群,3:万人群, 4:聊天室")
@NotNull(message = "会话类型不能为空")
private Integer chatType;
@ApiModelProperty("客户端平台: 1 web, 2 安卓, 3 ios, 4 pc-win, 5 pc-macOs, 需与生成sign时的值一致")
private Integer platform;
}
......@@ -11,7 +11,8 @@ public enum ChatTypeEnum implements BaseEnum {
SINGLE(1, "单聊"),
NORMAL_GROUP(2, "普通群"),
THOUSAND_GROUP(3, "万人群");
THOUSAND_GROUP(3, "万人群"),
CHAT_ROOM(4, "聊天室");
private final Integer code;
private final String desc;
......
package com.wecloud.im.sdk.enums;
/**
* @Author wenzhida
* @Date 2022/4/27 10:28
* @Description 添加好友来源枚举
*/
public enum FriendSourceEnum {
/**
* 1 - 搜索
*/
SEARCH("SEARCH", "搜索"),
/**
* 2 - 群聊
*/
GROUP("GROUP", "群聊"),
/**
* 3 - 名片
*/
CARD("CARD", "名片");
FriendSourceEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
private final String code;
private final String desc;
public String getCode() {
return this.code;
}
public String getDesc() {
return this.desc;
}
}
package com.wecloud.im.service;
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;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.wecloud.im.entity.ImConversation;
import com.wecloud.im.param.ChatRoomMemberPageParam;
import com.wecloud.im.param.DisbandConversationParam;
import com.wecloud.im.param.GroupChatSettingParam;
import com.wecloud.im.param.ExitChatRoomParam;
import com.wecloud.im.param.ImConversationPageParam;
import com.wecloud.im.param.ImConversationQueryParam;
import com.wecloud.im.param.ImConversationQueryVo;
import com.wecloud.im.param.IntoChatRoomParam;
import com.wecloud.im.param.MutedGroupMemberParam;
import com.wecloud.im.param.MutedGroupParam;
import com.wecloud.im.param.SetAdminsParam;
......@@ -23,8 +20,14 @@ 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.ChatRoomMemberVo;
import com.wecloud.im.vo.ConversationVo;
import com.wecloud.im.vo.ImConversationCreateVo;
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;
/**
* 会话表 服务类
......@@ -213,5 +216,26 @@ public interface ImConversationService extends BaseService<ImConversation> {
*/
Boolean groupChatSetting(GroupChatSettingParam param);
/**
* 用户主动进入聊天室
* @param param
* @return
*/
Boolean intoChatRoom(IntoChatRoomParam param);
/**
* 用户离开聊天室
* @param param
* @return
*/
Boolean exitChatRoom(ExitChatRoomParam param);
/**
* 分页获取聊天室成员入参
* @param param
* @return
*/
List<ChatRoomMemberVo> listChatRoomMember(ChatRoomMemberPageParam param);
}
package com.wecloud.im.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Author wenzhida
* @Date 2022/4/27 16:47
* @Description 聊天室成员VO
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "聊天室成员VO")
@AllArgsConstructor
@NoArgsConstructor
public class ChatRoomMemberVo implements Serializable {
private static final long serialVersionUID = 5417994988121322697L;
@ApiModelProperty("聊天室房间id")
private Long chatRoomId;
@ApiModelProperty("客户端id")
private String clientId;
}
......@@ -169,7 +169,6 @@ public class RtcServiceImpl extends UserStateListener implements RtcService {
public Boolean leave(LeaveRtcChannelParam leaveRtcChannelParam) {
ImClient currentClient = imClientService.getCurrentClient();
this.leave(leaveRtcChannelParam, currentClient);
return true;
}
......
......@@ -9,7 +9,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
......@@ -44,7 +43,7 @@ public class JsonUtils {
try {
return OBJECT_MAPPER.readValue(strJsonBody, c);
} catch (IOException e) {
log.warn("解析json字符串失败,原字符串: {}", strJsonBody, e);
log.warn("解析json字符串失败,原字符串: {} class {}", strJsonBody, c, e);
return null;
}
}
......@@ -188,6 +187,7 @@ public class JsonUtils {
}
}
/**
* 判断字符也许是JSON
*
......
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