Browse Source

多终端同时在线后端改造(开发中)

master
xsx 3 years ago
parent
commit
9c0b8a963f
  1. 24
      im-client/src/main/java/com/bx/imclient/config/RedisConfig.java
  2. 6
      im-client/src/main/java/com/bx/imclient/listener/MessageListener.java
  3. 19
      im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java
  4. 77
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  5. 12
      im-client/src/main/java/com/bx/imclient/task/PullSendResultGroupMessageTask.java
  6. 13
      im-client/src/main/java/com/bx/imclient/task/PullSendResultPrivateMessageTask.java
  7. 3
      im-commom/src/main/java/com/bx/imcommon/contant/IMConstant.java
  8. 2
      im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java
  9. 42
      im-commom/src/main/java/com/bx/imcommon/model/GroupMessageInfo.java
  10. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMHeartbeatInfo.java
  11. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMLoginInfo.java
  12. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java
  13. 4
      im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java
  14. 42
      im-commom/src/main/java/com/bx/imcommon/model/PrivateMessageInfo.java
  15. 3
      im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java
  16. 6
      im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java
  17. 8
      im-platform/src/main/java/com/bx/implatform/contant/Constant.java
  18. 8
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  19. 8
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  20. 28
      im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java
  21. 31
      im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java
  22. 13
      im-platform/src/main/java/com/bx/implatform/enums/FileType.java
  23. 14
      im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java
  24. 19
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  25. 1
      im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java
  26. 2
      im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java
  27. 1
      im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java
  28. 8
      im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java
  29. 1
      im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java
  30. 2
      im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java
  31. 11
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  32. 22
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  33. 8
      im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java
  34. 8
      im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java
  35. 26
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java
  36. 64
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  37. 43
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java
  38. 60
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  39. 19
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  40. 45
      im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java
  41. 40
      im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java
  42. 41
      im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java
  43. 23
      im-server/src/main/java/com/bx/imserver/config/RedisConfig.java
  44. 4
      im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java
  45. 4
      im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java
  46. 8
      im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java
  47. 18
      im-server/src/main/java/com/bx/imserver/netty/processor/HeartbeatProcessor.java
  48. 19
      im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java
  49. 8
      im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java
  50. 10
      im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java
  51. 10
      im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java
  52. 6
      im-ui/src/api/enums.js
  53. 12
      im-ui/src/components/chat/ChatMessageItem.vue

24
im-client/src/main/java/com/bx/imclient/config/RedisConfig.java

@ -1,5 +1,6 @@
package com.bx.imclient.config;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
@ -9,6 +10,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@ -17,16 +19,13 @@ import javax.annotation.Resource;
@Configuration("IMRedisConfig")
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean("IMRedisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置值(value)的序列化采用jackson2JsonRedisSerializer
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
// 设置值(value)的序列化采用FastJsonRedisSerializer
redisTemplate.setValueSerializer(fastJsonRedisSerializer());
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer());
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
@ -34,16 +33,9 @@ public class RedisConfig {
return redisTemplate;
}
@Bean
public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
public FastJsonRedisSerializer fastJsonRedisSerializer(){
FastJsonRedisSerializer <Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
return fastJsonRedisSerializer;
}
}

6
im-client/src/main/java/com/bx/imclient/listener/MessageListener.java

@ -1,10 +1,10 @@
package com.bx.imclient.listener;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
public interface MessageListener {
public interface MessageListener<T> {
void process(SendResult result);
void process(IMSendResult<T> result);
}

19
im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java

@ -1,12 +1,15 @@
package com.bx.imclient.listener;
import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.annotation.IMListener;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
@ -16,12 +19,22 @@ public class MessageListenerMulticaster {
@Autowired(required = false)
private List<MessageListener> messageListeners = Collections.emptyList();
public void multicast(IMListenerType type, SendResult result){
public void multicast(IMListenerType listenerType, IMSendResult result){
for(MessageListener listener:messageListeners){
IMListener annotation = listener.getClass().getAnnotation(IMListener.class);
if(annotation!=null && (annotation.type().equals(IMListenerType.ALL) || annotation.type().equals(type))){
if(annotation!=null && (annotation.type().equals(IMListenerType.ALL) || annotation.type().equals(listenerType))){
// 将data转回对象类型
if(result.getData() instanceof JSONObject){
Type superClass = listener.getClass().getGenericInterfaces()[0];
Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
JSONObject data = (JSONObject)result.getData();
result.setData(data.toJavaObject(type));
}
// 回调到调用方处理
listener.process(result);
}
}
}
}

77
im-client/src/main/java/com/bx/imclient/sender/IMSender.java

@ -1,10 +1,11 @@
package com.bx.imclient.sender;
import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -30,57 +31,52 @@ public class IMSender {
public void sendPrivateMessage(IMPrivateMessage<?> message) {
for (Integer terminal : message.getRecvTerminals()) {
// 获取对方连接的channelId
String key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString());
Integer serverId = (Integer) redisTemplate.opsForValue().get(key);
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString());
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
// 如果对方在线,将数据存储至redis,等待拉取推送
if (serverId != null) {
IMRecvInfo[] recvInfos = new IMRecvInfo[message.getDatas().size()];
String sendKey = String.join(":",RedisKey.IM_UNREAD_PRIVATE_QUEUE,serverId.toString());
for (int i = 0; i < message.getDatas().size(); i++) {
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE, serverId.toString());
IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setSendResult(message.getSendResult());
recvInfo.setSender(message.getSender());
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getRecvId(), terminal)));
recvInfo.setData(message.getDatas().get(i));
recvInfos[i]=recvInfo;
}
redisTemplate.opsForList().rightPushAll(sendKey, recvInfos);
} else if(message.getSendResult()){
recvInfo.setData(message.getData());
redisTemplate.opsForList().rightPush(sendKey, recvInfo);
} else if (message.getSendResult()) {
// 回复消息状态
for (int i = 0; i < message.getDatas().size(); i++) {
SendResult result = new SendResult();
IMSendResult result = new IMSendResult();
result.setSender(message.getSender());
result.setReceiver(new IMUserInfo(message.getRecvId(), terminal));
result.setCode(IMSendCode.NOT_ONLINE.code());
result.setData(message.getDatas().get(i));
result.setData(message.getData());
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result);
}
}
// 推送给自己的其他终端
if (message.getSendToSelf() && !message.getSender().getTerminal().equals(terminal)) {
if(message.getSendToSelf()){
for (Integer terminal : IMTerminalType.codes()) {
if (message.getSender().getTerminal().equals(terminal)) {
continue;
}
// 获取终端连接的channelId
key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString());
serverId = (Integer) redisTemplate.opsForValue().get(key);
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString());
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
// 如果终端在线,将数据存储至redis,等待拉取推送
if (serverId != null) {
String sendKey = String.join(":",RedisKey.IM_UNREAD_PRIVATE_QUEUE, serverId.toString());
IMRecvInfo[] recvInfos = new IMRecvInfo[message.getDatas().size()];
for (int i = 0; i < message.getDatas().size(); i++) {
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE, serverId.toString());
IMRecvInfo recvInfo = new IMRecvInfo();
// 自己的消息不需要回推消息结果
recvInfo.setSendResult(false);
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setSender(message.getSender());
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getSender().getId(),terminal)));
recvInfo.setData(message.getDatas().get(i));
recvInfos[i]=recvInfo;
}
redisTemplate.opsForList().rightPushAll(sendKey, recvInfos);
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getSender().getId(), terminal)));
recvInfo.setData(message.getData());
redisTemplate.opsForList().rightPush(sendKey, recvInfo);
}
}
}
}
public void sendGroupMessage(IMGroupMessage<?> message) {
@ -90,14 +86,14 @@ public class IMSender {
Map<Integer, List<IMUserInfo>> serverMap = new ConcurrentHashMap<>();
for (Integer terminal : message.getRecvTerminals()) {
message.getRecvIds().parallelStream().forEach(id -> {
String key = String.join(":",RedisKey.IM_USER_SERVER_ID, id.toString(),terminal.toString());
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, id.toString(), terminal.toString());
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
if (serverId != null) {
List<IMUserInfo> list = serverMap.computeIfAbsent(serverId,o->Collections.synchronizedList(new LinkedList<>()));
list.add(new IMUserInfo(id,terminal));
List<IMUserInfo> list = serverMap.computeIfAbsent(serverId, o -> Collections.synchronizedList(new LinkedList<>()));
list.add(new IMUserInfo(id, terminal));
} else {
// 加入离线列表
offLineUsers.add(new IMUserInfo(id,terminal));
offLineUsers.add(new IMUserInfo(id, terminal));
}
});
}
@ -110,13 +106,13 @@ public class IMSender {
recvInfo.setSendResult(message.getSendResult());
recvInfo.setData(message.getData());
// 推送至队列
String key = String.join(":",RedisKey.IM_UNREAD_GROUP_QUEUE,entry.getKey().toString());
String key = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE, entry.getKey().toString());
redisTemplate.opsForList().rightPush(key, recvInfo);
}
// 对离线用户回复消息状态
if(message.getSendResult()){
if (message.getSendResult()) {
for (IMUserInfo offLineUser : offLineUsers) {
SendResult result = new SendResult();
IMSendResult result = new IMSendResult();
result.setSender(message.getSender());
result.setReceiver(offLineUser);
result.setCode(IMSendCode.NOT_ONLINE.code());
@ -124,26 +120,25 @@ public class IMSender {
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, result);
}
}
// 推送给自己的其他终端
if (message.getSendToSelf()) {
for (Integer terminal : message.getRecvTerminals()) {
if(terminal.equals(message.getSender().getTerminal())){
for (Integer terminal : IMTerminalType.codes()) {
if (terminal.equals(message.getSender().getTerminal())) {
continue;
}
// 获取终端连接的channelId
String key = String.join(":",RedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString());
Integer serverId = (Integer) redisTemplate.opsForValue().get(key);
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString());
Integer serverId = (Integer)redisTemplate.opsForValue().get(key);
// 如果终端在线,将数据存储至redis,等待拉取推送
if (serverId != null) {
IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code());
recvInfo.setSender(message.getSender());
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getSender().getId(),terminal)));
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getSender().getId(), terminal)));
// 自己的消息不需要回推消息结果
recvInfo.setSendResult(false);
recvInfo.setData(message.getData());
String sendKey = String.join(":",RedisKey.IM_UNREAD_GROUP_QUEUE,serverId.toString());
String sendKey = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE, serverId.toString());
redisTemplate.opsForList().rightPush(sendKey, recvInfo);
}
}
@ -151,7 +146,7 @@ public class IMSender {
}
public Boolean isOnline(Long userId) {
String key = String.join(":",RedisKey.IM_USER_SERVER_ID,userId.toString(),"*");
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), "*");
return !redisTemplate.keys(key).isEmpty();
}
}

12
im-client/src/main/java/com/bx/imclient/task/PullSendResultGroupMessageTask.java

@ -1,9 +1,10 @@
package com.bx.imclient.task;
import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
@ -23,9 +24,10 @@ public class PullSendResultGroupMessageTask extends AbstractPullMessageTask{
@Override
public void pullMessage() {
String key = RedisKey.IM_RESULT_GROUP_QUEUE;
SendResult result = (SendResult)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(result != null) {
String key = IMRedisKey.IM_RESULT_GROUP_QUEUE;
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject != null) {
IMSendResult result = jsonObject.toJavaObject(IMSendResult.class);
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result);
}
}

13
im-client/src/main/java/com/bx/imclient/task/PullSendResultPrivateMessageTask.java

@ -1,9 +1,10 @@
package com.bx.imclient.task;
import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -28,9 +29,11 @@ public class PullSendResultPrivateMessageTask extends AbstractPullMessageTask{
@Override
public void pullMessage() {
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
SendResult result = (SendResult)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(result != null) {
String key = IMRedisKey.IM_RESULT_PRIVATE_QUEUE;
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject != null) {
IMSendResult result = jsonObject.toJavaObject(IMSendResult.class);
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result);
}
}

3
im-commom/src/main/java/com/bx/imcommon/contant/Constant.java → im-commom/src/main/java/com/bx/imcommon/contant/IMConstant.java

@ -1,7 +1,7 @@
package com.bx.imcommon.contant;
public class Constant {
public class IMConstant {
// 在线状态过期时间 600s
public static final long ONLINE_TIMEOUT_SECOND = 600;
@ -9,5 +9,4 @@ public class Constant {
public static final long ALLOW_RECALL_SECOND = 300;
}

2
im-commom/src/main/java/com/bx/imcommon/contant/RedisKey.java → im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java

@ -1,6 +1,6 @@
package com.bx.imcommon.contant;
public class RedisKey {
public class IMRedisKey {
// im-server最大id,从0开始递增
public final static String IM_MAX_SERVER_ID = "im:max_server_id";

42
im-commom/src/main/java/com/bx/imcommon/model/GroupMessageInfo.java

@ -1,42 +0,0 @@
package com.bx.imcommon.model;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import java.util.Date;
@Data
public class GroupMessageInfo {
/*
* 消息id
*/
private Long id;
/*
* 群聊id
*/
private Long groupId;
/*
* 发送者id
*/
private Long sendId;
/*
* 消息内容
*/
private String content;
/*
* 消息内容类型 具体枚举值由应用层定义
*/
private Integer type;
/**
* 发送时间
*/
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

2
im-commom/src/main/java/com/bx/imcommon/model/HeartbeatInfo.java → im-commom/src/main/java/com/bx/imcommon/model/IMHeartbeatInfo.java

@ -3,5 +3,5 @@ package com.bx.imcommon.model;
import lombok.Data;
@Data
public class HeartbeatInfo {
public class IMHeartbeatInfo {
}

2
im-commom/src/main/java/com/bx/imcommon/model/LoginInfo.java → im-commom/src/main/java/com/bx/imcommon/model/IMLoginInfo.java

@ -3,7 +3,7 @@ package com.bx.imcommon.model;
import lombok.Data;
@Data
public class LoginInfo {
public class IMLoginInfo {
private String accessToken;
}

2
im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java

@ -38,7 +38,7 @@ public class IMPrivateMessage<T> {
/**
* 消息内容
*/
private List<T> datas;
private T data;
}

4
im-commom/src/main/java/com/bx/imcommon/model/SendResult.java → im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java

@ -3,7 +3,7 @@ package com.bx.imcommon.model;
import lombok.Data;
@Data
public class SendResult {
public class IMSendResult<T> {
/**
* 发送方
@ -23,6 +23,6 @@ public class SendResult {
/*
* 消息内容
*/
private Object data;
private T data;
}

42
im-commom/src/main/java/com/bx/imcommon/model/PrivateMessageInfo.java

@ -1,42 +0,0 @@
package com.bx.imcommon.model;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import java.util.Date;
@Data
public class PrivateMessageInfo {
/*
* 消息id
*/
private long id;
/*
* 发送者id
*/
private Long sendId;
/*
* 接收者id
*/
private Long recvId;
/*
* 发送内容
*/
private String content;
/*
* 消息内容类型 具体枚举值由应用层定义
*/
private Integer type;
/**
* 发送时间
*/
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

3
im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java

@ -20,10 +20,9 @@ public class MinIoClientConfig {
@Bean
public MinioClient minioClient(){
// 注入minio 客户端
MinioClient client = MinioClient.builder()
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return client;
}
}

6
im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java

@ -38,7 +38,7 @@ public class RedisConfig extends CachingConfigurerSupport {
@Primary
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置值(value)的序列化采用jackson2JsonRedisSerializer
@ -52,8 +52,8 @@ public class RedisConfig extends CachingConfigurerSupport {
}
@Bean
public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题

8
im-platform/src/main/java/com/bx/implatform/contant/Constant.java

@ -8,13 +8,5 @@ public class Constant {
public static final long MAX_FILE_SIZE = 10*1024*1024;
// 群聊最大人数
public static final long MAX_GROUP_MEMBER = 500;
// accessToken 过期时间(半小时)
public static final Integer ACCESS_TOKEN_EXPIRE = 30 * 60;
// refreshToken 过期时间(7天)
public static final Integer REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60 ;
// accessToken 加密秘钥
// refreshToken 加密秘钥
public static final String ACCESS_TOKEN_SECRET = "MIIBIjANBgkq";
public static final String REFRESH_TOKEN_SECRET = "IKDiqVmn0VFU";
}

8
im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java

@ -1,11 +1,11 @@
package com.bx.implatform.controller;
import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IGroupMessageService;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.dto.GroupMessageDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@ -27,7 +27,7 @@ public class GroupMessageController {
@PostMapping("/send")
@ApiOperation(value = "发送群聊消息",notes="发送群聊消息")
public Result<Long> sendMessage(@Valid @RequestBody GroupMessageVO vo){
public Result<Long> sendMessage(@Valid @RequestBody GroupMessageDTO vo){
return ResultUtils.success(groupMessageService.sendMessage(vo));
}
@ -47,7 +47,7 @@ public class GroupMessageController {
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
public Result<List<GroupMessageInfo>> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId,
public Result<List<GroupMessageVO>> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId,
@NotNull(message = "页码不能为空") @RequestParam Long page,
@NotNull(message = "size不能为空") @RequestParam Long size){
return ResultUtils.success( groupMessageService.findHistoryMessage(groupId,page,size));

8
im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java

@ -1,11 +1,11 @@
package com.bx.implatform.controller;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.result.Result;
import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IPrivateMessageService;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.dto.PrivateMessageDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@ -25,7 +25,7 @@ public class PrivateMessageController {
@PostMapping("/send")
@ApiOperation(value = "发送消息",notes="发送私聊消息")
public Result<Long> sendMessage(@Valid @RequestBody PrivateMessageVO vo){
public Result<Long> sendMessage(@Valid @RequestBody PrivateMessageDTO vo){
return ResultUtils.success(privateMessageService.sendMessage(vo));
}
@ -48,7 +48,7 @@ public class PrivateMessageController {
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
public Result<List<PrivateMessageInfo>> recallMessage(@NotNull(message = "好友id不能为空") @RequestParam Long friendId,
public Result<List<PrivateMessageVO>> recallMessage(@NotNull(message = "好友id不能为空") @RequestParam Long friendId,
@NotNull(message = "页码不能为空") @RequestParam Long page,
@NotNull(message = "size不能为空") @RequestParam Long size){
return ResultUtils.success( privateMessageService.findHistoryMessage(friendId,page,size));

28
im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java

@ -0,0 +1,28 @@
package com.bx.implatform.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
@ApiModel("群聊消息VO")
public class GroupMessageDTO {
@NotNull(message="群聊id不可为空")
@ApiModelProperty(value = "群聊id")
private Long groupId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容")
private String content;
@NotNull(message="消息类型不可为空")
@ApiModelProperty(value = "消息类型")
private Integer type;
}

31
im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java

@ -0,0 +1,31 @@
package com.bx.implatform.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
@ApiModel("私聊消息VO")
public class PrivateMessageDTO {
@NotNull(message="接收用户id不可为空")
@ApiModelProperty(value = "接收用户id")
private Long recvId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容")
private String content;
@NotNull(message="消息类型不可为空")
@ApiModelProperty(value = "消息类型")
private Integer type;
}

13
im-platform/src/main/java/com/bx/implatform/enums/FileType.java

@ -9,24 +9,15 @@ public enum FileType {
private Integer code;
private final Integer code;
private String desc;
private final String desc;
FileType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static FileType fromCode(Integer code){
for (FileType typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String description() {
return desc;

14
im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java

@ -7,25 +7,15 @@ public enum MessageStatus {
ALREADY_READ(1,"已读"),
RECALL(2,"已撤回");
private Integer code;
private final Integer code;
private String desc;
private final String desc;
MessageStatus(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static MessageStatus fromCode(Integer code){
for (MessageStatus typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String description() {
return desc;
}

19
im-platform/src/main/java/com/bx/implatform/enums/MessageType.java

@ -6,8 +6,9 @@ public enum MessageType {
TEXT(0,"文字"),
FILE(1,"文件"),
IMAGE(2,"图片"),
VIDEO(3,"视频"),
TIP(10,"系统提示"),
AUDIO(3,"音频"),
VIDEO(4,"视频"),
RECALL(10,"撤回"),
RTC_CALL(101,"呼叫"),
RTC_ACCEPT(102,"接受"),
@ -17,25 +18,15 @@ public enum MessageType {
RTC_HANDUP(106,"挂断"),
RTC_CANDIDATE(107,"同步candidate");
private Integer code;
private final Integer code;
private String desc;
private final String desc;
MessageType(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static MessageType fromCode(Integer code){
for (MessageType typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String description() {
return desc;
}

1
im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java

@ -21,7 +21,6 @@ public enum ResultCode {
private int code;
private String msg;
// 构造方法
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;

2
im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java

@ -71,7 +71,7 @@ public class GlobalExceptionHandler {
public Result handleValidationExceptionHandler(MethodArgumentNotValidException exception) {
BindingResult bindResult = exception.getBindingResult();
String msg;
if (bindResult != null && bindResult.hasErrors()) {
if (bindResult.hasErrors()) {
msg = bindResult.getAllErrors().get(0).getDefaultMessage();
if (msg.contains("NumberFormatException")) {
msg = "参数类型错误!";

1
im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java

@ -7,7 +7,6 @@ import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
@Component
@ServletComponentScan

8
im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java

@ -10,9 +10,9 @@ import java.io.*;
public class CacheHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody;
private HttpServletRequest request;
private final HttpServletRequest request;
public CacheHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
public CacheHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
@ -48,10 +48,6 @@ public class CacheHttpServletRequestWrapper extends HttpServletRequestWrapper {
};
}
public byte[] getRequestBody() {
return requestBody;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));

1
im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java

@ -14,7 +14,6 @@ import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
;
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {

2
im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java

@ -17,7 +17,7 @@ public class XssInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 检查参数
Map<String, String[]> paramMap = request.getParameterMap();
for(String[] values:paramMap.values()){

11
im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java

@ -4,10 +4,9 @@ import com.bx.imclient.annotation.IMListener;
import com.bx.imclient.listener.MessageListener;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.imcommon.model.SendResult;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.imcommon.model.IMSendResult;
import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.enums.MessageType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@ -15,14 +14,14 @@ import org.springframework.data.redis.core.RedisTemplate;
@Slf4j
@IMListener(type = IMListenerType.GROUP_MESSAGE)
public class GroupMessageListener implements MessageListener {
public class GroupMessageListener implements MessageListener<GroupMessageVO> {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Override
public void process(SendResult result){
GroupMessageInfo messageInfo = (GroupMessageInfo) result.getData();
public void process(IMSendResult<GroupMessageVO> result){
GroupMessageVO messageInfo = result.getData();
// 保存该用户已拉取的最大消息id
if(result.getCode().equals(IMSendCode.SUCCESS.code())) {
String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,messageInfo.getGroupId().toString(),result.getReceiver().getId().toString());

22
im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java

@ -1,42 +1,32 @@
package com.bx.implatform.listener;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bx.imclient.IMClient;
import com.bx.imclient.annotation.IMListener;
import com.bx.imclient.listener.MessageListener;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.imcommon.model.SendResult;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.imcommon.model.IMSendResult;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.enums.MessageStatus;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.service.IPrivateMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.Date;
@Slf4j
@IMListener(type = IMListenerType.PRIVATE_MESSAGE)
public class PrivateMessageListener implements MessageListener {
public class PrivateMessageListener implements MessageListener<PrivateMessageVO> {
@Autowired
private IPrivateMessageService privateMessageService;
@Autowired
private IMClient imClient;
@Override
public void process(SendResult result){
PrivateMessageInfo messageInfo = (PrivateMessageInfo) result.getData();
IMSendCode resultCode = IMSendCode.fromCode(result.getCode());
public void process(IMSendResult<PrivateMessageVO> result){
PrivateMessageVO messageInfo = result.getData();
// 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态
if(resultCode.equals(IMSendCode.SUCCESS)){
if(result.getCode().equals(IMSendCode.SUCCESS.code())){
UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId())
.eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code())

8
im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java

@ -1,9 +1,9 @@
package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.implatform.entity.GroupMessage;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.entity.GroupMessage;
import com.bx.implatform.dto.GroupMessageDTO;
import java.util.List;
@ -11,11 +11,11 @@ import java.util.List;
public interface IGroupMessageService extends IService<GroupMessage> {
Long sendMessage(GroupMessageVO vo);
Long sendMessage(GroupMessageDTO vo);
void recallMessage(Long id);
void pullUnreadMessage();
List<GroupMessageInfo> findHistoryMessage(Long groupId, Long page, Long size);
List<GroupMessageVO> findHistoryMessage(Long groupId, Long page, Long size);
}

8
im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java

@ -1,20 +1,20 @@
package com.bx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.dto.PrivateMessageDTO;
import java.util.List;
public interface IPrivateMessageService extends IService<PrivateMessage> {
Long sendMessage(PrivateMessageVO vo);
Long sendMessage(PrivateMessageDTO vo);
void recallMessage(Long id);
List<PrivateMessageInfo> findHistoryMessage(Long friendId, Long page,Long size);
List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page,Long size);
void pullUnreadMessage();

26
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java

@ -1,7 +1,9 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.entity.GroupMember;
@ -71,8 +73,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
*/
@Override
public List<GroupMember> findByUserId(Long userId) {
QueryWrapper<GroupMember> memberWrapper = new QueryWrapper();
memberWrapper.lambda().eq(GroupMember::getUserId, userId)
LambdaQueryWrapper<GroupMember> memberWrapper = Wrappers.lambdaQuery();
memberWrapper.eq(GroupMember::getUserId, userId)
.eq(GroupMember::getQuit,false);
return this.list(memberWrapper);
}
@ -86,8 +88,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
*/
@Override
public List<GroupMember> findByGroupId(Long groupId) {
QueryWrapper<GroupMember> memberWrapper = new QueryWrapper();
memberWrapper.lambda().eq(GroupMember::getGroupId, groupId);
LambdaQueryWrapper<GroupMember> memberWrapper = Wrappers.lambdaQuery();
memberWrapper.eq(GroupMember::getGroupId, groupId);
return this.list(memberWrapper);
}
@ -101,11 +103,11 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@Cacheable(key="#groupId")
@Override
public List<Long> findUserIdsByGroupId(Long groupId) {
QueryWrapper<GroupMember> memberWrapper = new QueryWrapper();
memberWrapper.lambda().eq(GroupMember::getGroupId, groupId)
LambdaQueryWrapper<GroupMember> memberWrapper = Wrappers.lambdaQuery();
memberWrapper.eq(GroupMember::getGroupId, groupId)
.eq(GroupMember::getQuit,false);
List<GroupMember> members = this.list(memberWrapper);
return members.stream().map(m->m.getUserId()).collect(Collectors.toList());
return members.stream().map(GroupMember::getUserId).collect(Collectors.toList());
}
@ -118,8 +120,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@CacheEvict(key = "#groupId")
@Override
public void removeByGroupId(Long groupId) {
UpdateWrapper<GroupMember> wrapper = new UpdateWrapper();
wrapper.lambda().eq(GroupMember::getGroupId,groupId)
LambdaUpdateWrapper<GroupMember> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(GroupMember::getGroupId,groupId)
.set(GroupMember::getQuit,true);
this.update(wrapper);
}
@ -134,8 +136,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@CacheEvict(key = "#groupId")
@Override
public void removeByGroupAndUserId(Long groupId, Long userId) {
UpdateWrapper<GroupMember> wrapper = new UpdateWrapper<>();
wrapper.lambda().eq(GroupMember::getGroupId,groupId)
LambdaUpdateWrapper<GroupMember> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(GroupMember::getGroupId,groupId)
.eq(GroupMember::getUserId,userId)
.set(GroupMember::getQuit,true);
this.update(wrapper);

64
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java

@ -1,10 +1,12 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.contant.Constant;
import com.bx.imcommon.model.GroupMessageInfo;
import com.bx.imcommon.contant.IMConstant;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.imcommon.model.IMGroupMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.implatform.contant.RedisKey;
@ -22,7 +24,7 @@ import com.bx.implatform.service.IGroupService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.dto.GroupMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@ -30,37 +32,31 @@ import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, GroupMessage> implements IGroupMessageService {
@Autowired
private IGroupService groupService;
@Autowired
private IGroupMemberService groupMemberService;
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private IMClient imClient;
/**
* 发送群聊消息(与mysql所有交换都要进行缓存)
*
* @param vo
* @param dto 群聊消息
* @return 群聊id
*/
@Override
public Long sendMessage(GroupMessageVO vo) {
public Long sendMessage(GroupMessageDTO dto) {
UserSession session = SessionContext.getSession();
Group group = groupService.getById(vo.getGroupId());
Group group = groupService.getById(dto.getGroupId());
if(group == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"群聊不存在");
}
@ -73,20 +69,20 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊里面,无法发送消息");
}
// 保存消息
GroupMessage msg = BeanUtils.copyProperties(vo, GroupMessage.class);
GroupMessage msg = BeanUtils.copyProperties(dto, GroupMessage.class);
msg.setSendId(session.getUserId());
msg.setSendTime(new Date());
this.save(msg);
// 不用发给自己
userIds = userIds.stream().filter(id->!session.getUserId().equals(id)).collect(Collectors.toList());
// 群发
GroupMessageInfo msgInfo = BeanUtils.copyProperties(msg, GroupMessageInfo.class);
IMGroupMessage sendMessage = new IMGroupMessage();
GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage);
log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),vo.getGroupId(),vo.getContent());
log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),dto.getGroupId(),dto.getContent());
return msg.getId();
}
@ -108,7 +104,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
if(!msg.getSendId().equals(session.getUserId())){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"这条消息不是由您发送,无法撤回");
}
if(System.currentTimeMillis() - msg.getSendTime().getTime() > Constant.ALLOW_RECALL_SECOND * 1000){
if(System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"消息已发送超过5分钟,无法撤回");
}
// 判断是否在群里
@ -123,17 +119,25 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
List<Long> userIds = groupMemberService.findUserIdsByGroupId(msg.getGroupId());
// 不用发给自己
userIds = userIds.stream().filter(uid->!session.getUserId().equals(uid)).collect(Collectors.toList());
GroupMessageInfo msgInfo = BeanUtils.copyProperties(msg, GroupMessageInfo.class);
msgInfo.setType(MessageType.TIP.code());
GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class);
msgInfo.setType(MessageType.RECALL.code());
String content = String.format("'%s'撤回了一条消息",member.getAliasName());
msgInfo.setContent(content);
msgInfo.setSendTime(new Date());
IMGroupMessage sendMessage = new IMGroupMessage();
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
sendMessage.setSendToSelf(false);
imClient.sendGroupMessage(sendMessage);
// 推给自己其他终端
msgInfo.setContent("你撤回了一条消息");
sendMessage.setSendToSelf(true);
sendMessage.setRecvIds(Collections.emptyList());
sendMessage.setRecvTerminals(Collections.emptyList());
imClient.sendGroupMessage(sendMessage);
log.info("撤回群聊消息,发送id:{},群聊id:{},内容:{}",session.getUserId(),msg.getGroupId(),msg.getContent());
}
@ -142,7 +146,6 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
/**
* 异步拉取群聊消息通过websocket异步推送
*
* @return
*/
@Override
public void pullUnreadMessage() {
@ -152,13 +155,13 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 获取群聊已读的最大消息id,只推送未读消息
String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,member.getGroupId().toString(),session.getUserId().toString());
Integer maxReadedId = (Integer)redisTemplate.opsForValue().get(key);
QueryWrapper<GroupMessage> wrapper = new QueryWrapper();
wrapper.lambda().eq(GroupMessage::getGroupId,member.getGroupId())
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId,member.getGroupId())
.gt(GroupMessage::getSendTime,member.getCreatedTime())
.ne(GroupMessage::getSendId, session.getUserId())
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code());
if(maxReadedId!=null){
wrapper.lambda().gt(GroupMessage::getId,maxReadedId);
wrapper.gt(GroupMessage::getId,maxReadedId);
}
wrapper.last("limit 100");
List<GroupMessage> messages = this.list(wrapper);
@ -167,8 +170,8 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
}
// 推送
for (GroupMessage message:messages ){
GroupMessageInfo msgInfo = BeanUtils.copyProperties(message, GroupMessageInfo.class);
IMGroupMessage sendMessage = new IMGroupMessage();
GroupMessageVO msgInfo = BeanUtils.copyProperties(message, GroupMessageVO.class);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
// 只推给自己当前终端
sendMessage.setRecvIds(Collections.singletonList(session.getUserId()));
@ -191,11 +194,11 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
* @return 聊天记录列表
*/
@Override
public List<GroupMessageInfo> findHistoryMessage(Long groupId, Long page, Long size) {
public List<GroupMessageVO> findHistoryMessage(Long groupId, Long page, Long size) {
page = page > 0 ? page:1;
size = size > 0 ? size:10;
Long userId = SessionContext.getSession().getUserId();
Long stIdx = (page-1)* size;
long stIdx = (page-1)* size;
// 群聊成员信息
GroupMember member = groupMemberService.findByGroupAndUserId(groupId,userId);
if(member == null || member.getQuit()){
@ -210,10 +213,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
.last("limit "+stIdx + ","+size);
List<GroupMessage> messages = this.list(wrapper);
List<GroupMessageInfo> messageInfos = messages.stream().map(m->{
GroupMessageInfo info = BeanUtils.copyProperties(m, GroupMessageInfo.class);
return info;
}).collect(Collectors.toList());
List<GroupMessageVO> messageInfos = messages.stream().map(m->BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}",userId,groupId,messageInfos.size());
return messageInfos;
}

43
im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java

@ -1,6 +1,7 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.implatform.contant.Constant;
import com.bx.implatform.contant.RedisKey;
@ -30,8 +31,8 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@ -55,8 +56,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
/**
* 创建新群聊
*
* @Param groupName 群聊名称
* @return
* @param groupName 群聊名称
* @return 群聊信息
**/
@Transactional
@Override
@ -89,8 +90,8 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
/**
* 修改群聊信息
*
* @Param GroupVO 群聊信息
* @return
* @param vo 群聊信息
* @return 群聊信息
**/
@CacheEvict(value = "#vo.getId()")
@Transactional
@ -120,8 +121,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
/**
* 删除群聊
*
* @Param groupId 群聊id
* @return
* @param groupId 群聊id
**/
@Transactional
@CacheEvict(value = "#groupId")
@ -145,7 +145,6 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
* 退出群聊
*
* @param groupId 群聊id
* @return
*/
@Override
public void quitGroup(Long groupId) {
@ -165,7 +164,6 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
*
* @param groupId 群聊id
* @param userId 用户id
* @return
*/
@Override
public void kickGroup(Long groupId, Long userId) {
@ -200,7 +198,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
* 根据id查找群聊并进行缓存
*
* @param groupId 群聊id
* @return
* @return 群聊实体
*/
@Cacheable(value = "#groupId")
@Override
@ -220,7 +218,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
/**
* 查询当前用户的所有群聊
*
* @return
* @return 群聊信息列表
**/
@Override
public List<GroupVO> findGroups() {
@ -228,29 +226,27 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
// 查询当前用户的群id列表
List<GroupMember> groupMembers = groupMemberService.findByUserId(session.getUserId());
if(groupMembers.isEmpty()){
return Collections.EMPTY_LIST;
return new LinkedList<>();
}
// 拉取群列表
List<Long> ids = groupMembers.stream().map((gm -> gm.getGroupId())).collect(Collectors.toList());
QueryWrapper<Group> groupWrapper = new QueryWrapper();
groupWrapper.lambda().in(Group::getId, ids);
List<Long> ids = groupMembers.stream().map((GroupMember::getGroupId)).collect(Collectors.toList());
LambdaQueryWrapper<Group> groupWrapper = Wrappers.lambdaQuery();
groupWrapper.in(Group::getId, ids);
List<Group> groups = this.list(groupWrapper);
// 转vo
List<GroupVO> vos = groups.stream().map(g -> {
return groups.stream().map(g -> {
GroupVO vo = BeanUtils.copyProperties(g, GroupVO.class);
GroupMember member = groupMembers.stream().filter(m -> g.getId().equals(m.getGroupId())).findFirst().get();
vo.setAliasName(member.getAliasName());
vo.setRemark(member.getRemark());
return vo;
}).collect(Collectors.toList());
return vos;
}
/**
* 邀请好友进群
*
* @Param GroupInviteVO 群id好友id列表
* @return
**/
@Override
public void invite(GroupInviteVO vo) {
@ -265,7 +261,6 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
if(vo.getFriendIds().size() + size > Constant.MAX_GROUP_MEMBER){
throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊人数不能大于"+Constant.MAX_GROUP_MEMBER+"人");
}
// 找出好友信息
List<Friend> friends = friendsService.findFriendByUserId(session.getUserId());
List<Friend> friendsList = vo.getFriendIds().stream().map(id ->
@ -277,7 +272,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
List<GroupMember> groupMembers = friendsList.stream()
.map(f -> {
Optional<GroupMember> optional = members.stream().filter(m->m.getUserId().equals(f.getFriendId())).findFirst();
GroupMember groupMember = optional.isPresent()? optional.get():new GroupMember();
GroupMember groupMember = optional.orElseGet(GroupMember::new);
groupMember.setGroupId(vo.getGroupId());
groupMember.setUserId(f.getFriendId());
groupMember.setAliasName(f.getFriendNickName());
@ -302,11 +297,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
@Override
public List<GroupMemberVO> findGroupMembers(Long groupId) {
List<GroupMember> members = groupMemberService.findByGroupId(groupId);
List<GroupMemberVO> vos = members.stream().map(m->{
GroupMemberVO vo = BeanUtils.copyProperties(m,GroupMemberVO.class);
return vo;
}).collect(Collectors.toList());
return vos;
return members.stream().map(m->BeanUtils.copyProperties(m,GroupMemberVO.class)).collect(Collectors.toList());
}
}

60
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -1,12 +1,14 @@
package com.bx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.contant.Constant;
import com.bx.imcommon.contant.IMConstant;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.entity.PrivateMessage;
import com.bx.implatform.enums.MessageStatus;
import com.bx.implatform.enums.MessageType;
@ -18,7 +20,7 @@ import com.bx.implatform.service.IPrivateMessageService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.dto.PrivateMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -40,31 +42,31 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
/**
* 发送私聊消息
*
* @param vo 私聊消息vo
* @param dto 私聊消息
* @return 消息id
*/
@Override
public Long sendMessage(PrivateMessageVO vo) {
public Long sendMessage(PrivateMessageDTO dto) {
UserSession session = SessionContext.getSession();
Boolean isFriends = friendService.isFriend(session.getUserId(), vo.getRecvId());
Boolean isFriends = friendService.isFriend(session.getUserId(), dto.getRecvId());
if (!isFriends) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不是对方好友,无法发送消息");
}
// 保存消息
PrivateMessage msg = BeanUtils.copyProperties(vo, PrivateMessage.class);
PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
msg.setSendId(session.getUserId());
msg.setStatus(MessageStatus.UNREAD.code());
msg.setSendTime(new Date());
this.save(msg);
// 推送消息
PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(true);
sendMessage.setDatas(Collections.singletonList(msgInfo));
sendMessage.setData(msgInfo);
imClient.sendPrivateMessage(sendMessage);
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), vo.getRecvId(), vo.getContent());
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent());
return msg.getId();
}
@ -83,23 +85,30 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
if (!msg.getSendId().equals(session.getUserId())) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "这条消息不是由您发送,无法撤回");
}
if (System.currentTimeMillis() - msg.getSendTime().getTime() > Constant.ALLOW_RECALL_SECOND * 1000) {
if (System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回");
}
// 修改消息状态
msg.setStatus(MessageStatus.RECALL.code());
this.updateById(msg);
// 推送消息
PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class);
msgInfo.setType(MessageType.TIP.code());
PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
msgInfo.setType(MessageType.RECALL.code());
msgInfo.setSendTime(new Date());
msgInfo.setContent("对方撤回了一条消息");
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(false);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(false);
imClient.sendPrivateMessage(sendMessage);
// 推给自己其他终端
msgInfo.setContent("你撤回了一条消息");
sendMessage.setSendToSelf(true);
sendMessage.setDatas(Collections.singletonList(msgInfo));
sendMessage.setRecvTerminals(Collections.emptyList());
imClient.sendPrivateMessage(sendMessage);
log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent());
}
@ -114,7 +123,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
* @return 聊天记录列表
*/
@Override
public List<PrivateMessageInfo> findHistoryMessage(Long friendId, Long page, Long size) {
public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) {
page = page > 0 ? page : 1;
size = size > 0 ? size : 10;
Long userId = SessionContext.getSession().getUserId();
@ -130,7 +139,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
.last("limit " + stIdx + "," + size);
List<PrivateMessage> messages = this.list(wrapper);
List<PrivateMessageInfo> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageInfo.class)).collect(Collectors.toList());
List<PrivateMessageVO> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageVO.class)).collect(Collectors.toList());
log.info("拉取聊天记录,用户id:{},好友id:{},数量:{}", userId, friendId, messageInfos.size());
return messageInfos;
}
@ -147,22 +156,23 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
throw new GlobalException(ResultCode.PROGRAM_ERROR, "用户未建立连接");
}
// 获取当前用户所有未读消息
QueryWrapper<PrivateMessage> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(PrivateMessage::getRecvId, session.getUserId())
LambdaQueryWrapper<PrivateMessage> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(PrivateMessage::getRecvId, session.getUserId())
.eq(PrivateMessage::getStatus, MessageStatus.UNREAD);
List<PrivateMessage> messages = this.list(queryWrapper);
// 上传至redis,等待推送
if (!messages.isEmpty()) {
List<PrivateMessageInfo> messageInfos = messages.stream().map(m -> BeanUtils.copyProperties(m, PrivateMessageInfo.class)).collect(Collectors.toList());
for(PrivateMessage message:messages){
PrivateMessageVO msgInfo = BeanUtils.copyProperties(message, PrivateMessageVO.class);
// 推送消息
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(session.getUserId());
sendMessage.setRecvTerminals(Collections.singletonList(session.getTerminal()));
sendMessage.setSendToSelf(false);
sendMessage.setDatas(messageInfos);
sendMessage.setData(msgInfo);
imClient.sendPrivateMessage(sendMessage);
log.info("拉取未读私聊消息,用户id:{},数量:{}", session.getUserId(), messageInfos.size());
}
log.info("拉取未读私聊消息,用户id:{},数量:{}", session.getUserId(), messages.size());
}
}

19
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -1,7 +1,9 @@
package com.bx.implatform.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.implatform.config.JwtProperties;
@ -114,7 +116,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
* 用户注册
*
* @param dto 注册dto
* @return
*/
@Override
public void register(RegisterDTO dto) {
@ -136,8 +137,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
*/
@Override
public User findUserByName(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(User::getUserName,username);
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(User::getUserName,username);
return this.getOne(queryWrapper);
}
@ -145,7 +146,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
* 更新用户信息好友昵称和群聊昵称等冗余信息也会更新
*
* @param vo 用户信息vo
* @return
*/
@Transactional
@Override
@ -184,7 +184,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
user.setHeadImage(vo.getHeadImage());
user.setHeadImageThumb(vo.getHeadImageThumb());
this.updateById(user);
log.info("用户信息更新,用户:{}}",user.toString());
log.info("用户信息更新,用户:{}}", user);
}
@ -196,17 +196,14 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
*/
@Override
public List<UserVO> findUserByNickName(String nickname) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.like(User::getNickName,nickname)
.last("limit 20");
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.like(User::getNickName,nickname).last("limit 20");
List<User> users = this.list(queryWrapper);
List<UserVO> vos = users.stream().map(u-> {
return users.stream().map(u-> {
UserVO vo = BeanUtils.copyProperties(u,UserVO.class);
vo.setOnline(imClient.isOnline(u.getId()));
return vo;
}).collect(Collectors.toList());
return vos;
}

45
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java

@ -3,7 +3,7 @@ package com.bx.implatform.service.impl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.model.IMPrivateMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imcommon.model.PrivateMessageInfo;
import com.bx.implatform.vo.PrivateMessageVO;
import com.bx.implatform.config.ICEServer;
import com.bx.implatform.config.ICEServerConfig;
import com.bx.implatform.contant.RedisKey;
@ -13,7 +13,6 @@ import com.bx.implatform.service.IWebrtcService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.session.WebrtcSession;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@ -48,18 +47,18 @@ public class WebrtcServiceImpl implements IWebrtcService {
String key = getSessionKey(session.getUserId(), uid);
redisTemplate.opsForValue().set(key, webrtcSession, 12, TimeUnit.HOURS);
// 向对方所有终端发起呼叫
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_CALL.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(offer);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
}
@ -75,20 +74,20 @@ public class WebrtcServiceImpl implements IWebrtcService {
String key = getSessionKey(session.getUserId(), uid);
redisTemplate.opsForValue().set(key, webrtcSession, 12, TimeUnit.HOURS);
// 向发起人推送接受通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_ACCEPT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(answer);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经接受会话,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals((Collections.singletonList(webrtcSession.getCallerTerminal())));
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
}
@ -100,19 +99,19 @@ public class WebrtcServiceImpl implements IWebrtcService {
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向发起人推送拒绝通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_REJECT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经拒绝会话,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
}
@ -122,17 +121,17 @@ public class WebrtcServiceImpl implements IWebrtcService {
// 删除会话信息
removeWebrtcSession(session.getUserId(), uid);
// 向对方所有终端推送取消通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_ACCEPT.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
}
@ -145,19 +144,19 @@ public class WebrtcServiceImpl implements IWebrtcService {
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向发起方推送通话失败信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_FAILED.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经会话失败,中止呼叫
sendMessage.setSendToSelf(true);
sendMessage.setSendResult(false);
sendMessage.setRecvTerminals(Collections.singletonList(webrtcSession.getCallerTerminal()));
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
@ -171,19 +170,19 @@ public class WebrtcServiceImpl implements IWebrtcService {
// 删除会话信息
removeWebrtcSession(uid, session.getUserId());
// 向对方推送挂断通话信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_HANDUP.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
Integer terminal = getTerminalType(uid, webrtcSession);
sendMessage.setRecvTerminals(Collections.singletonList(terminal));
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
// 通知对方取消会话
imClient.sendPrivateMessage(sendMessage);
}
@ -194,20 +193,20 @@ public class WebrtcServiceImpl implements IWebrtcService {
// 查询webrtc会话
WebrtcSession webrtcSession = getWebrtcSession(session.getUserId(), uid);
// 向发起方推送同步candidate信令
PrivateMessageInfo messageInfo = new PrivateMessageInfo();
PrivateMessageVO messageInfo = new PrivateMessageVO();
messageInfo.setType(MessageType.RTC_CANDIDATE.code());
messageInfo.setRecvId(uid);
messageInfo.setSendId(session.getUserId());
messageInfo.setContent(candidate);
IMPrivateMessage<PrivateMessageInfo> sendMessage = new IMPrivateMessage<>();
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
Integer terminal = getTerminalType(uid, webrtcSession);
sendMessage.setRecvTerminals(Collections.singletonList(terminal));
sendMessage.setDatas(Collections.singletonList(messageInfo));
sendMessage.setData(messageInfo);
imClient.sendPrivateMessage(sendMessage);
}

40
im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java

@ -1,28 +1,42 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data
@ApiModel("群聊消息VO")
public class GroupMessageVO {
@NotNull(message="群聊id不可为空")
@ApiModelProperty(value = "群聊id")
/*
* 消息id
*/
private Long id;
/*
* 群聊id
*/
private Long groupId;
/*
* 发送者id
*/
private Long sendId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容")
/*
* 消息内容
*/
private String content;
@NotNull(message="消息类型不可为空")
@ApiModelProperty(value = "消息类型")
/*
* 消息内容类型 具体枚举值由应用层定义
*/
private Integer type;
/**
* 发送时间
*/
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

41
im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java

@ -1,31 +1,42 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.bx.imcommon.serializer.DateToLongSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data
@ApiModel("私聊消息VO")
public class PrivateMessageVO {
/*
* 消息id
*/
private long id;
@NotNull(message="接收用户id不可为空")
@ApiModelProperty(value = "接收用户id")
private Long recvId;
/*
* 发送者id
*/
private Long sendId;
/*
* 接收者id
*/
private Long recvId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容")
/*
* 发送内容
*/
private String content;
@NotNull(message="消息类型不可为空")
@ApiModelProperty(value = "消息类型")
/*
* 消息内容类型 IMCmdType
*/
private Integer type;
/**
* 发送时间
*/
@JsonSerialize(using = DateToLongSerializer.class)
private Date sendTime;
}

23
im-server/src/main/java/com/bx/imserver/config/RedisConfig.java

@ -1,5 +1,6 @@
package com.bx.imserver.config;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
@ -26,9 +27,9 @@ public class RedisConfig {
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置值(value)的序列化采用jackson2JsonRedisSerializer
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
// 设置值(value)的序列化采用FastJsonRedisSerializer
redisTemplate.setValueSerializer(fastJsonRedisSerializer());
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer());
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
@ -36,17 +37,11 @@ public class RedisConfig {
return redisTemplate;
}
@Bean
public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
public FastJsonRedisSerializer fastJsonRedisSerializer(){
FastJsonRedisSerializer <Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
return fastJsonRedisSerializer;
}
}

4
im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java

@ -1,6 +1,6 @@
package com.bx.imserver.netty;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.model.IMSendInfo;
import com.bx.imserver.constant.ChannelAttrKey;
@ -76,7 +76,7 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
UserChannelCtxMap.removeChannelCtx(userId,terminal);
// 用户下线
RedisTemplate redisTemplate = SpringContextHolder.getBean("redisTemplate");
String key = String.join(":",RedisKey.IM_USER_SERVER_ID,userId.toString(), terminal.toString());
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userId.toString(), terminal.toString());
redisTemplate.delete(key);
log.info("断开连接,userId:{},终端类型:{}",userId,terminal);
}

4
im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java

@ -1,6 +1,6 @@
package com.bx.imserver.netty;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
@ -39,7 +39,7 @@ public class IMServerGroup implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 初始化SERVER_ID
String key = RedisKey.IM_MAX_SERVER_ID;
String key = IMRedisKey.IM_MAX_SERVER_ID;
serverId = redisTemplate.opsForValue().increment(key,1);
// 启动服务
for(IMServer imServer:imServers){

8
im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java

@ -1,12 +1,12 @@
package com.bx.imserver.netty.processor;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imcommon.model.IMSendInfo;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
import com.bx.imserver.netty.UserChannelCtxMap;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
@ -58,13 +58,13 @@ public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo>
private void sendResult(IMRecvInfo recvInfo,IMUserInfo receiver,IMSendCode sendCode){
if(recvInfo.getSendResult()) {
SendResult result = new SendResult();
IMSendResult result = new IMSendResult();
result.setSender(recvInfo.getSender());
result.setReceiver(receiver);
result.setCode(sendCode.code());
result.setData(recvInfo.getData());
// 推送到结果队列
String key = RedisKey.IM_RESULT_GROUP_QUEUE;
String key = IMRedisKey.IM_RESULT_GROUP_QUEUE;
redisTemplate.opsForList().rightPush(key, result);
}
}

18
im-server/src/main/java/com/bx/imserver/netty/processor/HeartbeatProcessor.java

@ -1,10 +1,10 @@
package com.bx.imserver.netty.processor;
import cn.hutool.core.bean.BeanUtil;
import com.bx.imcommon.contant.Constant;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMConstant;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.model.HeartbeatInfo;
import com.bx.imcommon.model.IMHeartbeatInfo;
import com.bx.imcommon.model.IMSendInfo;
import com.bx.imserver.constant.ChannelAttrKey;
import com.bx.imserver.netty.ws.WebSocketServer;
@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class HeartbeatProcessor extends AbstractMessageProcessor<HeartbeatInfo> {
public class HeartbeatProcessor extends AbstractMessageProcessor<IMHeartbeatInfo> {
@Autowired
@ -30,7 +30,7 @@ public class HeartbeatProcessor extends AbstractMessageProcessor<HeartbeatInfo>
RedisTemplate<String,Object> redisTemplate;
@Override
public void process(ChannelHandlerContext ctx, HeartbeatInfo beatInfo) {
public void process(ChannelHandlerContext ctx, IMHeartbeatInfo beatInfo) {
// 响应ws
IMSendInfo sendInfo = new IMSendInfo();
sendInfo.setCmd(IMCmdType.HEART_BEAT.code());
@ -46,16 +46,16 @@ public class HeartbeatProcessor extends AbstractMessageProcessor<HeartbeatInfo>
Long userId = ctx.channel().attr(userIdAttr).get();
AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
Integer ternimal = ctx.channel().attr(terminalAttr).get();
String key = String.join(":",RedisKey.IM_USER_SERVER_ID,userId.toString(),ternimal.toString());
redisTemplate.expire(key, Constant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userId.toString(),ternimal.toString());
redisTemplate.expire(key, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
}
}
@Override
public HeartbeatInfo transForm(Object o) {
public IMHeartbeatInfo transForm(Object o) {
HashMap map = (HashMap)o;
HeartbeatInfo heartbeatInfo = BeanUtil.fillBeanWithMap(map, new HeartbeatInfo(), false);
IMHeartbeatInfo heartbeatInfo = BeanUtil.fillBeanWithMap(map, new IMHeartbeatInfo(), false);
return heartbeatInfo;
}
}

19
im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java

@ -2,17 +2,16 @@ package com.bx.imserver.netty.processor;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.bx.imcommon.contant.Constant;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMConstant;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.model.IMSendInfo;
import com.bx.imcommon.model.IMSessionInfo;
import com.bx.imcommon.model.LoginInfo;
import com.bx.imcommon.model.IMLoginInfo;
import com.bx.imcommon.util.JwtUtil;
import com.bx.imserver.constant.ChannelAttrKey;
import com.bx.imserver.netty.IMServerGroup;
import com.bx.imserver.netty.UserChannelCtxMap;
import com.bx.imserver.netty.ws.WebSocketServer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
@ -26,7 +25,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class LoginProcessor extends AbstractMessageProcessor<LoginInfo> {
public class LoginProcessor extends AbstractMessageProcessor<IMLoginInfo> {
@Autowired
RedisTemplate<String,Object> redisTemplate;
@ -35,7 +34,7 @@ public class LoginProcessor extends AbstractMessageProcessor<LoginInfo> {
private String accessTokenSecret;
@Override
synchronized public void process(ChannelHandlerContext ctx, LoginInfo loginInfo) {
synchronized public void process(ChannelHandlerContext ctx, IMLoginInfo loginInfo) {
if(!JwtUtil.checkSign(loginInfo.getAccessToken(),accessTokenSecret)){
ctx.channel().close();
log.warn("用户token校验不通过,强制下线,token:{}",loginInfo.getAccessToken());
@ -66,8 +65,8 @@ public class LoginProcessor extends AbstractMessageProcessor<LoginInfo> {
AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf("HEARTBEAt_TIMES");
ctx.channel().attr(heartBeatAttr).set(0L);
// 在redis上记录每个user的channelId,15秒没有心跳,则自动过期
String key = String.join(":",RedisKey.IM_USER_SERVER_ID,userId.toString(), terminal.toString());
redisTemplate.opsForValue().set(key, IMServerGroup.serverId, Constant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userId.toString(), terminal.toString());
redisTemplate.opsForValue().set(key, IMServerGroup.serverId, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
// 响应ws
IMSendInfo sendInfo = new IMSendInfo();
sendInfo.setCmd(IMCmdType.LOGIN.code());
@ -76,9 +75,9 @@ public class LoginProcessor extends AbstractMessageProcessor<LoginInfo> {
@Override
public LoginInfo transForm(Object o) {
public IMLoginInfo transForm(Object o) {
HashMap map = (HashMap)o;
LoginInfo loginInfo = BeanUtil.fillBeanWithMap(map, new LoginInfo(), false);
IMLoginInfo loginInfo = BeanUtil.fillBeanWithMap(map, new IMLoginInfo(), false);
return loginInfo;
}
}

8
im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java

@ -1,12 +1,12 @@
package com.bx.imserver.netty.processor;
import com.bx.imcommon.contant.RedisKey;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imcommon.model.IMSendInfo;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imcommon.model.SendResult;
import com.bx.imcommon.model.IMSendResult;
import com.bx.imserver.netty.UserChannelCtxMap;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
@ -51,13 +51,13 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor<IMRecvInfo
private void sendResult(IMRecvInfo recvInfo,IMSendCode sendCode){
if(recvInfo.getSendResult()) {
SendResult result = new SendResult();
IMSendResult result = new IMSendResult();
result.setSender(recvInfo.getSender());
result.setReceiver(recvInfo.getReceivers().get(0));
result.setCode(sendCode.code());
result.setData(recvInfo.getData());
// 推送到结果队列
String key = RedisKey.IM_RESULT_PRIVATE_QUEUE;
String key = IMRedisKey.IM_RESULT_PRIVATE_QUEUE;
redisTemplate.opsForList().rightPush(key, result);
}
}

10
im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java

@ -1,6 +1,7 @@
package com.bx.imserver.task;
import com.bx.imcommon.contant.RedisKey;
import com.alibaba.fastjson.JSONObject;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imserver.netty.IMServerGroup;
@ -22,9 +23,10 @@ public class PullUnreadGroupMessageTask extends AbstractPullMessageTask {
@Override
public void pullMessage() {
// 从redis拉取未读消息
String key = String.join(":",RedisKey.IM_UNREAD_GROUP_QUEUE,IMServerGroup.serverId+"");
IMRecvInfo recvInfo = (IMRecvInfo)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(recvInfo != null){
String key = String.join(":", IMRedisKey.IM_UNREAD_GROUP_QUEUE,IMServerGroup.serverId+"");
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject != null){
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE);
processor.process(recvInfo);
}

10
im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java

@ -1,7 +1,8 @@
package com.bx.imserver.task;
import com.bx.imcommon.contant.RedisKey;
import com.alibaba.fastjson.JSONObject;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.model.IMRecvInfo;
import com.bx.imserver.netty.IMServerGroup;
@ -24,9 +25,10 @@ public class PullUnreadPrivateMessageTask extends AbstractPullMessageTask {
@Override
public void pullMessage() {
// 从redis拉取未读消息
String key = String.join(":",RedisKey.IM_UNREAD_PRIVATE_QUEUE ,IMServerGroup.serverId+"");
IMRecvInfo recvInfo = (IMRecvInfo)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(recvInfo != null) {
String key = String.join(":", IMRedisKey.IM_UNREAD_PRIVATE_QUEUE ,IMServerGroup.serverId+"");
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS);
if(jsonObject!=null){
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE);
processor.process(recvInfo);
}

6
im-ui/src/api/enums.js

@ -1,5 +1,11 @@
const MESSAGE_TYPE = {
TEXT: 0,
FILE:1,
IMAGE:2,
AUDIO:3,
VIDEO:4,
RECALL:10,
RTC_CALL: 101,
RTC_ACCEPT: 102,
RTC_REJECT: 103,

12
im-ui/src/components/chat/ChatMessageItem.vue

@ -1,7 +1,7 @@
<template>
<div class="chat-msg-item">
<div class="chat-msg-tip" v-show="msgInfo.type==10">{{msgInfo.content}}</div>
<div class="chat-msg-normal" v-show="msgInfo.type!=10" :class="{'chat-msg-mine':mine}">
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.RECALL">{{msgInfo.content}}</div>
<div class="chat-msg-normal" v-show="msgInfo.type!=$enums.MESSAGE_TYPE.RECALL" :class="{'chat-msg-mine':mine}">
<div class="head-image">
<head-image :url="headImage" :id="msgInfo.sendId"></head-image>
</div>
@ -11,14 +11,14 @@
<chat-time :time="msgInfo.sendTime"></chat-time>
</div>
<div class="chat-msg-bottom" @contextmenu.prevent="showRightMenu($event)">
<span class="chat-msg-text" v-if="msgInfo.type==0" v-html="$emo.transform(msgInfo.content)"></span>
<div class="chat-msg-image" v-if="msgInfo.type==1">
<span class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT" v-html="$emo.transform(msgInfo.content)"></span>
<div class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.FILE">
<div class="img-load-box" v-loading="loading" element-loading-text="上传中.." element-loading-background="rgba(0, 0, 0, 0.4)">
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" @click="showFullImageBox()" />
</div>
<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span>
</div>
<div class="chat-msg-file" v-if="msgInfo.type==2">
<div class="chat-msg-file" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE">
<div class="chat-file-box" v-loading="loading">
<div class="chat-file-info">
<el-link class="chat-file-name" :underline="true" target="_blank" type="primary" :href="data.url">{{data.name}}</el-link>
@ -30,7 +30,7 @@
</div>
<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span>
</div>
<div class="chat-msg-voice" v-if="msgInfo.type==3" @click="handlePlayVoice()">
<div class="chat-msg-voice" v-if="msgInfo.type==$enums.MESSAGE_TYPE.AUDIO" @click="handlePlayVoice()">
<audio controls :src="JSON.parse(msgInfo.content).url"></audio>
</div>
</div>

Loading…
Cancel
Save